diff options
author | Dieter Hametner <dh (plus) vdr (at) gekrumbel (dot) de> | 2007-10-21 15:56:00 +0000 |
---|---|---|
committer | Dieter Hametner <dh (plus) vdr (at) gekrumbel (dot) de> | 2007-10-21 15:56:00 +0000 |
commit | 0fea7f3368ab6ca3f056c26151d30e978a2f2eee (patch) | |
tree | 8fe0c6eb5c3cd46ecf68a1cd52cc224b0349ed2d /recman.cpp | |
parent | 8ac9758dd9dc502c64c06c3339b6a60a7d54e39c (diff) | |
download | vdr-plugin-live-0fea7f3368ab6ca3f056c26151d30e978a2f2eee.tar.gz vdr-plugin-live-0fea7f3368ab6ca3f056c26151d30e978a2f2eee.tar.bz2 |
- renamed recordings.h/cpp to recman.h/cpp. Preparations for
recordings updates.
Diffstat (limited to 'recman.cpp')
-rw-r--r-- | recman.cpp | 464 |
1 files changed, 464 insertions, 0 deletions
diff --git a/recman.cpp b/recman.cpp new file mode 100644 index 0000000..eb993d5 --- /dev/null +++ b/recman.cpp @@ -0,0 +1,464 @@ +#include <unistd.h> +#include <cstring> +#include <string> +#include <sstream> +#include <fstream> +#include <stack> + +#include "stdext.h" +#include "tools.h" + +#include "epg_events.h" +#include "recman.h" + + +using namespace std::tr1; +using namespace std; + +namespace vdrlive { + + /** + * Implementation of class RecordingsManager: + */ + weak_ptr< RecordingsManager > RecordingsManager::m_recMan; + shared_ptr< RecordingsTree > RecordingsManager::m_recTree; + shared_ptr< RecordingsList > RecordingsManager::m_recList; + int RecordingsManager::m_recordingsState = 0; + + // The RecordingsManager holds a VDR lock on the + // Recordings. Additionally the singleton instance of + // RecordingsManager is held in a weak pointer. If it is not in + // use any longer, it will be freed automaticaly, which leads to a + // release of the VDR recordings lock. Upon requesting access to + // the RecordingsManager via LiveRecordingsManger function, first + // the weak ptr is locked (obtaining a shared_ptr from an possible + // existing instance) and if not successfull a new instance is + // created, which again locks the VDR Recordings. + // + // RecordingsManager provides factory methods to obtain other + // recordings data structures. The data structures returned keep if + // needed the instance of RecordingsManager alive until destructed + // themselfs. This way the use of LIVE::recordings is straight + // forward and does hide the locking needs from the user. + + RecordingsManager::RecordingsManager() : + m_recordingsLock(&Recordings) + { + } + + RecordingsTreePtr RecordingsManager::GetRecordingsTree() const + { + RecordingsManagerPtr recMan = EnsureValidData(); + if (! recMan) { + return RecordingsTreePtr(recMan, shared_ptr< RecordingsTree >()); + } + return RecordingsTreePtr(recMan, m_recTree); + } + + RecordingsListPtr RecordingsManager::GetRecordingsList(bool ascending) const + { + RecordingsManagerPtr recMan = EnsureValidData(); + if (! recMan) { + return RecordingsListPtr(recMan, shared_ptr< RecordingsList >()); + } + return RecordingsListPtr(recMan, shared_ptr< RecordingsList >(new RecordingsList(m_recList, ascending))); + } + + RecordingsListPtr RecordingsManager::GetRecordingsList(time_t begin, time_t end, bool ascending) const + { + RecordingsManagerPtr recMan = EnsureValidData(); + if (! recMan) { + return RecordingsListPtr(recMan, shared_ptr< RecordingsList >()); + } + return RecordingsListPtr(recMan, shared_ptr< RecordingsList >(new RecordingsList(m_recList, ascending))); + } + + string RecordingsManager::Md5Hash(cRecording const * recording) const + { + return "recording_" + MD5Hash(recording->FileName()); + } + + cRecording const * RecordingsManager::GetByMd5Hash(string const & hash) const + { + if (!hash.empty()) { + for (cRecording* rec = Recordings.First(); rec != 0; rec = Recordings.Next(rec)) { + if (hash == Md5Hash(rec)) + return rec; + } + } + return 0; + } + + bool RecordingsManager::IsArchived(cRecording const * recording) + { + string filename = recording->FileName(); + + string vdrFile = filename + "/001.vdr"; + if (0 == access(vdrFile.c_str(), R_OK)) + return false; + + filename += "/dvd.vdr"; + return (0 == access(filename.c_str(), R_OK)); + } + + string const RecordingsManager::GetArchiveId(cRecording const * recording) + { + string filename = recording->FileName(); + + filename += "/dvd.vdr"; + ifstream dvd(filename.c_str()); + + if (dvd) { + string archiveDisc; + string videoDisc; + dvd >> archiveDisc; + if ("0000" == archiveDisc) { + dvd >> videoDisc; + return videoDisc; + } + return archiveDisc; + } + return ""; + } + + string const RecordingsManager::GetArchiveDescr(cRecording const * recording) + { + string archived; + if (IsArchived(recording)) { + archived += " ["; + archived += tr("On archive DVD No."); + archived += ": "; + archived += GetArchiveId(recording); + archived += "]"; + } + return archived; + } + + RecordingsManagerPtr RecordingsManager::EnsureValidData() + { + // Get singleton instance of RecordingsManager. 'this' is not + // an instance of shared_ptr of the singleton + // RecordingsManager, so we obtain it in the overall + // recommended way. + RecordingsManagerPtr recMan = LiveRecordingsManager(); + if (! recMan) { + // theoretically this code is never reached ... + esyslog("[LIVE]: lost RecordingsManager instance while using it!"); + return RecordingsManagerPtr(); + } + + // StateChanged must be executed every time, so not part of + // the short cut evaluation in the if statement below. + bool stateChanged = Recordings.StateChanged(m_recordingsState); + if ((!m_recTree) || (!m_recList) || stateChanged) { + if (stateChanged) { + m_recTree.reset(); + m_recList.reset(); + } + if (stateChanged || !m_recTree) { + m_recTree = shared_ptr< RecordingsTree >(new RecordingsTree(recMan)); + } + if (!m_recTree) { + esyslog("[LIVE]: creation of recordings tree failed!"); + return RecordingsManagerPtr(); + } + if (stateChanged || !m_recList) { + m_recList = shared_ptr< RecordingsList >(new RecordingsList(RecordingsTreePtr(recMan, m_recTree))); + } + if (!m_recList) { + esyslog("[LIVE]: creation of recordings list failed!"); + return RecordingsManagerPtr(); + } + } + return recMan; + } + + + /** + * Implementation of class RecordingsItem: + */ + RecordingsItem::RecordingsItem(string const & name, RecordingsItemPtr parent) : + m_name(name), + m_entries(), + m_parent(parent) + { + } + + RecordingsItem::~RecordingsItem() + { + } + + + /** + * Implementation of class RecordingsItemDir: + */ + RecordingsItemDir::RecordingsItemDir(const string& name, int level, RecordingsItemPtr parent) : + RecordingsItem(name, parent), + m_level(level) + { + esyslog("REC: C: dir %s -> %s", name.c_str(), parent ? parent->Name().c_str() : "ROOT"); + } + + RecordingsItemDir::~RecordingsItemDir() + { + esyslog("REC: D: dir %s", Name().c_str()); + } + + + /** + * Implementation of class RecordingsItemRec: + */ + RecordingsItemRec::RecordingsItemRec(const string& id, const string& name, const cRecording* recording, RecordingsItemPtr parent) : + RecordingsItem(name, parent), + m_recording(recording), + m_id(id) + { + esyslog("REC: C: rec %s -> %s", name.c_str(), parent->Name().c_str()); + } + + RecordingsItemRec::~RecordingsItemRec() + { + esyslog("REC: D: rec %s", Name().c_str()); + } + + time_t RecordingsItemRec::StartTime() const + { + return m_recording->start; + } + + + /** + * Implementation of class RecordingsTree: + */ + RecordingsTree::RecordingsTree(RecordingsManagerPtr recMan) : + m_maxLevel(0), + m_root(new RecordingsItemDir("", 0, RecordingsItemPtr())) + { + // esyslog("DH: ****** RecordingsTree::RecordingsTree() ********"); + for (cRecording* recording = Recordings.First(); recording != 0; recording = Recordings.Next(recording)) { + if (m_maxLevel < recording->HierarchyLevels()) { + m_maxLevel = recording->HierarchyLevels(); + } + + RecordingsItemPtr dir = m_root; + string name(recording->Name()); + + // esyslog("DH: recName = '%s'", recording->Name()); + int level = 0; + size_t index = 0; + size_t pos = 0; + do { + pos = name.find('~', index); + if (pos != string::npos) { + string dirName(name.substr(index, pos - index)); + index = pos + 1; + RecordingsMap::iterator i = findDir(dir, dirName); + if (i == dir->m_entries.end()) { + RecordingsItemPtr recPtr (new RecordingsItemDir(dirName, level, dir)); + dir->m_entries.insert(pair< string, RecordingsItemPtr > (dirName, recPtr)); + i = findDir(dir, dirName); + if (i != dir->m_entries.end()) { + // esyslog("DH: added dir: '%s'", dirName.c_str()); + } + else { + // esyslog("DH: panic: didn't found inserted dir: '%s'", dirName.c_str()); + } + } + dir = i->second; + // esyslog("DH: current dir: '%s'", dir->Name().c_str()); + level++; + } + else { + string recName(name.substr(index, name.length() - index)); + RecordingsItemPtr recPtr (new RecordingsItemRec(recMan->Md5Hash(recording), recName, recording, dir)); + dir->m_entries.insert(pair< string, RecordingsItemPtr > (recName, recPtr)); + // esyslog("DH: added rec: '%s'", recName.c_str()); + } + } while (pos != string::npos); + } + // esyslog("DH: ------ RecordingsTree::RecordingsTree() --------"); + } + + RecordingsTree::~RecordingsTree() + { + // esyslog("DH: ****** RecordingsTree::~RecordingsTree() ********"); + } + + RecordingsMap::iterator RecordingsTree::begin(const vector< string >& path) + { + if (path.empty()) { + return m_root->m_entries.begin(); + } + + RecordingsItemPtr recItem = m_root; + for (vector< string >::const_iterator i = path.begin(); i != path.end(); ++i) + { + pair< RecordingsMap::iterator, RecordingsMap::iterator> range = recItem->m_entries.equal_range(*i); + for (RecordingsMap::iterator iter = range.first; iter != range.second; ++iter) { + if (iter->second->IsDir()) { + recItem = iter->second; + break; + } + } + } + return recItem->m_entries.begin(); + } + + RecordingsMap::iterator RecordingsTree::end(const vector< string >&path) + { + if (path.empty()) { + return m_root->m_entries.end(); + } + + RecordingsItemPtr recItem = m_root; + for (vector< string >::const_iterator i = path.begin(); i != path.end(); ++i) + { + pair< RecordingsMap::iterator, RecordingsMap::iterator> range = recItem->m_entries.equal_range(*i); + for (RecordingsMap::iterator iter = range.first; iter != range.second; ++iter) { + if (iter->second->IsDir()) { + recItem = iter->second; + break; + } + } + } + return recItem->m_entries.end(); + } + + RecordingsMap::iterator RecordingsTree::findDir(RecordingsItemPtr& dir, const string& dirName) + { + pair< RecordingsMap::iterator, RecordingsMap::iterator > range = dir->m_entries.equal_range(dirName); + for (RecordingsMap::iterator i = range.first; i != range.second; ++i) { + if (i->second->IsDir()) { + return i; + } + } + return dir->m_entries.end(); + } + + + /** + * Implementation of class RecordingsTreePtr: + */ + RecordingsTreePtr::RecordingsTreePtr(RecordingsManagerPtr recManPtr, std::tr1::shared_ptr< RecordingsTree > recTree) : + shared_ptr<RecordingsTree>(recTree), + m_recManPtr(recManPtr) + { + } + + RecordingsTreePtr::~RecordingsTreePtr() + { + } + + + /** + * Implementation of class RecordingsList: + */ + RecordingsList::RecordingsList(RecordingsTreePtr recTree) : + m_pRecVec(new RecVecType()) + { + if (!m_pRecVec) { + return; + } + + stack< RecordingsItemPtr > treeStack; + treeStack.push(recTree->Root()); + + while (!treeStack.empty()) { + RecordingsItemPtr current = treeStack.top(); + treeStack.pop(); + for (RecordingsMap::const_iterator iter = current->begin(); iter != current->end(); ++iter) { + RecordingsItemPtr recItem = iter->second; + if (recItem->IsDir()) { + treeStack.push(recItem); + } + else { + m_pRecVec->push_back(recItem); + } + } + } + } + + RecordingsList::RecordingsList(shared_ptr< RecordingsList > recList, bool ascending) : + m_pRecVec(new RecVecType(recList->size())) + { + if (!m_pRecVec) { + return; + } + if (ascending) { + partial_sort_copy(recList->begin(), recList->end(), m_pRecVec->begin(), m_pRecVec->end(), Ascending()); + } + else { + partial_sort_copy(recList->begin(), recList->end(), m_pRecVec->begin(), m_pRecVec->end(), Descending()); + } + } + + RecordingsList::RecordingsList(shared_ptr< RecordingsList > recList, time_t begin, time_t end, bool ascending) : + m_pRecVec(new RecVecType()) + { + if (end > begin) { + return; + } + if (!m_pRecVec) { + return; + } + remove_copy_if(recList->begin(), recList->end(), m_pRecVec->end(), NotInRange(begin, end)); + + if (ascending) { + sort(m_pRecVec->begin(), m_pRecVec->end(), Ascending()); + } + else { + sort(m_pRecVec->begin(), m_pRecVec->end(), Descending()); + } + } + + RecordingsList::~RecordingsList() + { + if (m_pRecVec) { + delete m_pRecVec, m_pRecVec = 0; + } + } + + + RecordingsList::NotInRange::NotInRange(time_t begin, time_t end) : + m_begin(begin), + m_end(end) + { + } + + bool RecordingsList::NotInRange::operator()(RecordingsItemPtr const &x) const + { + return (x->StartTime() < m_begin) || (m_end >= x->StartTime()); + } + + + /** + * Implementation of class RecordingsList: + */ + RecordingsListPtr::RecordingsListPtr(RecordingsManagerPtr recManPtr, shared_ptr< RecordingsList > recList) : + shared_ptr< RecordingsList >(recList), + m_recManPtr(recManPtr) + { + } + + RecordingsListPtr::~RecordingsListPtr() + { + } + + + /** + * Implementation of function LiveRecordingsManager: + */ + RecordingsManagerPtr LiveRecordingsManager() + { + RecordingsManagerPtr r = RecordingsManager::m_recMan.lock(); + if (r) { + return r; + } + else { + RecordingsManagerPtr n(new RecordingsManager); + RecordingsManager::m_recMan = n; + return n; + } + } + +} // namespace vdrlive |