From 0fea7f3368ab6ca3f056c26151d30e978a2f2eee Mon Sep 17 00:00:00 2001 From: Dieter Hametner Date: Sun, 21 Oct 2007 15:56:00 +0000 Subject: - renamed recordings.h/cpp to recman.h/cpp. Preparations for recordings updates. --- Makefile | 4 +- doc/ChangeLog | 7 + epg_events.cpp | 2 +- pages/Makefile | 2 +- pages/epginfo.ecpp | 2 +- pages/ibox.ecpp | 2 +- pages/recordings.ecpp | 16 +- recman.cpp | 464 ++++++++++++++++++++++++++++++++++++++++++++++++++ recman.h | 313 ++++++++++++++++++++++++++++++++++ recordings.cpp | 259 ---------------------------- recordings.h | 153 ----------------- tasks.cpp | 8 +- 12 files changed, 803 insertions(+), 429 deletions(-) create mode 100644 recman.cpp create mode 100644 recman.h delete mode 100644 recordings.cpp delete mode 100644 recordings.h diff --git a/Makefile b/Makefile index fe35907..72f0b2d 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ # # Makefile for a Video Disk Recorder plugin # -# $Id: Makefile,v 1.51 2007/10/21 14:26:09 winni Exp $ +# $Id: Makefile,v 1.52 2007/10/21 15:56:00 tadi Exp $ # The official name of this plugin. # This name will be used in the '-P...' option of VDR to load the plugin. @@ -62,7 +62,7 @@ SUBDIRS = httpd pages css javascript ### The object files (add further files here): PLUGINOBJS = $(PLUGIN).o thread.o tntconfig.o setup.o i18n.o timers.o \ - tools.o recordings.o tasks.o status.o epg_events.o epgsearch.o \ + tools.o recman.o tasks.o status.o epg_events.o epgsearch.o \ grab.o md5.o filecache.o livefeatures.o preload.o WEBLIBS = pages/libpages.a css/libcss.a javascript/libjavascript.a diff --git a/doc/ChangeLog b/doc/ChangeLog index 0cc3c58..68cb085 100644 --- a/doc/ChangeLog +++ b/doc/ChangeLog @@ -1,3 +1,10 @@ +2007-10-21 Dieter Hametner + + - Renamed recordings.h/cpp files to recman.h/cpp. Adapted + files that included them. + - recman.h have extended functionality for recordings. It is not used + yet. + 2007-10-17 Dieter Hametner * css/styles.css diff --git a/epg_events.cpp b/epg_events.cpp index 83d067d..2302827 100644 --- a/epg_events.cpp +++ b/epg_events.cpp @@ -2,7 +2,7 @@ #include #include "tools.h" -#include "recordings.h" +#include "recman.h" #include "epg_events.h" #include "setup.h" diff --git a/pages/Makefile b/pages/Makefile index 09dfd63..9945f1f 100644 --- a/pages/Makefile +++ b/pages/Makefile @@ -43,7 +43,7 @@ all: libpages.a MAKEDEP = $(CXX) -MM -MG DEPFILE = .dependencies -$(DEPFILE): Makefile $(OBJS:%.o=%.cpp) +$(DEPFILE): Makefile $(OBJS:%.o=%.cpp) $(OBJS:%.o=%.ecpp) @$(MAKEDEP) $(DEFINES) $(INCLUDES) $(OBJS:%.o=%.cpp) > $@ -include $(DEPFILE) diff --git a/pages/epginfo.ecpp b/pages/epginfo.ecpp index c14d89d..4f49f0f 100644 --- a/pages/epginfo.ecpp +++ b/pages/epginfo.ecpp @@ -7,7 +7,7 @@ #include "setup.h" #include "tools.h" #include "epg_events.h" -#include "recordings.h" +#include "recman.h" namespace vdrlive { class SchedulesLock diff --git a/pages/ibox.ecpp b/pages/ibox.ecpp index 0118c07..147dcfa 100644 --- a/pages/ibox.ecpp +++ b/pages/ibox.ecpp @@ -10,7 +10,7 @@ #include "setup.h" #include "tools.h" #include "epg_events.h" -#include "recordings.h" +#include "recman.h" using namespace vdrlive; using namespace std; diff --git a/pages/recordings.ecpp b/pages/recordings.ecpp index dabb301..f0d6553 100644 --- a/pages/recordings.ecpp +++ b/pages/recordings.ecpp @@ -6,7 +6,7 @@ #include "tools.h" #include "epg_events.h" -#include "recordings.h" +#include "recman.h" #include "setup.h" using namespace vdrlive; @@ -19,7 +19,7 @@ using namespace std; bool logged_in(false); <%request scope="page"> -RecordingsTree recordingsTree(LiveRecordingsManager()); + RecordingsTreePtr recordingsTree(LiveRecordingsManager()->GetRecordingsTree()); <%include>page_init.eh <%cpp> @@ -62,11 +62,11 @@ RecordingsTree recordingsTree(LiveRecordingsManager()); int level = 0; <%cpp> -RecordingsTree::Map::iterator iter; -RecordingsTree::Map::iterator end = recordingsTree.end(path); +RecordingsMap::iterator iter; +RecordingsMap::iterator end = recordingsTree->end(path); -for (iter = recordingsTree.begin(path); iter != end; ++iter) { - RecordingsTree::RecordingsItemPtr recItem = iter->second; +for (iter = recordingsTree->begin(path); iter != end; ++iter) { + RecordingsItemPtr recItem = iter->second; string folderimg("folder_closed.png"); string collapseimg("plus.png"); if (recItem->IsDir()) { @@ -88,8 +88,8 @@ for (iter = recordingsTree.begin(path); iter != end; ++iter) { % } %} <%cpp> -for (iter = recordingsTree.begin(path); iter != end; ++iter) { - RecordingsTree::RecordingsItemPtr recItem = iter->second; +for (iter = recordingsTree->begin(path); iter != end; ++iter) { + RecordingsItemPtr recItem = iter->second; if (!recItem->IsDir()) { string day(FormatDateTime("%a,", recItem->StartTime())); string dayLen(lexical_cast(day.length() - 1) + ".25em;"); 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 +#include +#include +#include +#include +#include + +#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(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 diff --git a/recman.h b/recman.h new file mode 100644 index 0000000..0f8b8c5 --- /dev/null +++ b/recman.h @@ -0,0 +1,313 @@ +#ifndef VDR_LIVE_RECORDINGS_H +#define VDR_LIVE_RECORDINGS_H + +#include +#include +#include +#include +#include "stdext.h" + +namespace vdrlive { + + // Forward declations from epg_events.h + class EpgInfo; + typedef std::tr1::shared_ptr EpgInfoPtr; + + /** + * Some forward declarations + */ + class RecordingsManager; + class RecordingsTree; + class RecordingsTreePtr; + class RecordingsList; + class RecordingsListPtr; + class RecordingsItem; + + typedef std::tr1::shared_ptr< RecordingsManager > RecordingsManagerPtr; + typedef std::tr1::shared_ptr< RecordingsItem > RecordingsItemPtr; + // typedef std::tr1::weak_ptr< RecordingsItem > RecordingsItemWeakPtr; + typedef std::multimap< std::string, RecordingsItemPtr > RecordingsMap; + + + /** + * Class for managing recordings inside the live plugin. It + * provides some convenience methods and provides automatic + * locking during requests on the recordings or during the + * traversion of the recordings tree or lists, which can only be + * obtained through methods of the RecordingsManager. + */ + class RecordingsManager + { + friend RecordingsManagerPtr LiveRecordingsManager(); + + public: + /** + * Returns a shared pointer to a fully populated + * recordings tree. + */ + RecordingsTreePtr GetRecordingsTree() const; + + /** + * Return a shared pointer to a populated recordings + * list. The list is optionally sorted ascending or + * descending by date and may be constrained by a date + * range. + */ + RecordingsListPtr GetRecordingsList(bool ascending = true) const; + RecordingsListPtr GetRecordingsList(time_t begin, time_t end, bool ascending = true) const; + + /** + * generates a Md5 hash from a cRecording entry. It can be used + * to reidentify a recording. + */ + std::string Md5Hash(cRecording const * recording) const; + + /** + * fetches a cRecording from VDR's Recordings collection. Returns + * NULL if recording was not found + */ + cRecording const* GetByMd5Hash(std::string const & hash) const; + + /** + * Determine wether the recording has been archived on + * removable media (e.g. DVD-ROM) + */ + static bool IsArchived(cRecording const * recording); + + /** + * Provide an identification of the removable media + * (e.g. DVD-ROM Number or Name) where the recording has + * been archived. + */ + static std::string const GetArchiveId(cRecording const * recording); + + static std::string const GetArchiveDescr(cRecording const * recording); + + private: + RecordingsManager(); + + static RecordingsManagerPtr EnsureValidData(); + + static std::tr1::weak_ptr< RecordingsManager > m_recMan; + static std::tr1::shared_ptr< RecordingsTree > m_recTree; + static std::tr1::shared_ptr< RecordingsList > m_recList; + static int m_recordingsState; + + cThreadLock m_recordingsLock; + }; + + + /** + * Base class for entries in recordings tree and recordings list. + * All opeations possible on recordings are performed against + * pointers to instances of recordings items. The C++ polymorphy + * delegates them to the 'right' class. + */ + class RecordingsItem + { + friend class RecordingsTree; + + protected: + RecordingsItem(const std::string& name, RecordingsItemPtr parent); + + public: + virtual ~RecordingsItem(); + + virtual time_t StartTime() const = 0; + virtual bool IsDir() const = 0; + virtual const std::string& Name() const { return m_name; } + virtual const std::string Id() const = 0; + + virtual const cRecording* Recording() const { return 0; } + virtual const cRecordingInfo* RecInfo() const { return 0; } + + RecordingsMap::const_iterator begin() const { return m_entries.begin(); } + RecordingsMap::const_iterator end() const { return m_entries.end(); } + + private: + std::string m_name; + RecordingsMap m_entries; + RecordingsItemPtr m_parent; + }; + + + /** + * A recordings item that resembles a directory with other + * subdirectories and/or real recordings. + */ + class RecordingsItemDir : public RecordingsItem + { + public: + RecordingsItemDir(const std::string& name, int level, RecordingsItemPtr parent); + + virtual ~RecordingsItemDir(); + + virtual time_t StartTime() const { return 0; } + virtual bool IsDir() const { return true; } + virtual std::string const Id() const { return ""; } + + private: + int m_level; + }; + + + /** + * A recordings item that represents a real recording. This is + * the leaf item in the recordings tree or one of the items in + * the recordings list. + */ + class RecordingsItemRec : public RecordingsItem + { + public: + RecordingsItemRec(const std::string& id, const std::string& name, const cRecording* recording, RecordingsItemPtr parent); + + virtual ~RecordingsItemRec(); + + virtual time_t StartTime() const; + virtual bool IsDir() const { return false; } + virtual const std::string Id() const { return m_id; } + + virtual const cRecording* Recording() const { return m_recording; } + virtual const cRecordingInfo* RecInfo() const { return m_recording->Info(); } + + private: + const cRecording *m_recording; + std::string m_id; + }; + + + /** + * The recordings tree contains all recordings in a file system + * tree like fashion. + */ + class RecordingsTree + { + friend class RecordingsManager; + + private: + RecordingsTree(RecordingsManagerPtr recManPtr); + + public: + virtual ~RecordingsTree(); + + RecordingsItemPtr const & Root() const { return m_root; } + + RecordingsMap::iterator begin(const std::vector< std::string >& path); + RecordingsMap::iterator end(const std::vector< std::string >&path); + + int MaxLevel() const { return m_maxLevel; } + + private: + int m_maxLevel; + RecordingsItemPtr m_root; + + RecordingsMap::iterator findDir(RecordingsItemPtr& dir, const std::string& dirname); + }; + + + /** + * A smart pointer to a recordings tree. As long as an instance of this + * exists the recordings are locked in the vdr. + */ + class RecordingsTreePtr : public std::tr1::shared_ptr< RecordingsTree > + { + friend class RecordingsManager; + + private: + RecordingsTreePtr(RecordingsManagerPtr recManPtr, std::tr1::shared_ptr< RecordingsTree > recTree); + + public: + virtual ~RecordingsTreePtr(); + + private: + RecordingsManagerPtr m_recManPtr; + }; + + + /** + * The recordings list contains all real recordings in a list + * sorted by a given sorting predicate function. The directory + * entries are not part of this list. The path towards the root + * can be obtained via the 'parent' members of the recordings + * items. + */ + class RecordingsList + { + friend class RecordingsManager; + + private: + RecordingsList(RecordingsTreePtr recTree); + RecordingsList(std::tr1::shared_ptr< RecordingsList > recList, bool ascending); + RecordingsList(std::tr1::shared_ptr< RecordingsList > recList, time_t begin, time_t end, bool ascending); + + public: + typedef std::vector< RecordingsItemPtr > RecVecType; + + virtual ~RecordingsList(); + + RecVecType::const_iterator begin() const { return m_pRecVec->begin(); } + RecVecType::const_iterator end() const { return m_pRecVec->end(); } + + RecVecType::size_type size() const { return m_pRecVec->size(); } + + private: + class Ascending + { + public: + bool operator()(RecordingsItemPtr const &x, RecordingsItemPtr const &y) const { return x->StartTime() < y->StartTime(); } + }; + + class Descending + { + public: + bool operator()(RecordingsItemPtr const &x, RecordingsItemPtr const &y) const { return y->StartTime() < x->StartTime(); } + }; + + class NotInRange + { + public: + NotInRange(time_t begin, time_t end); + + bool operator()(RecordingsItemPtr const &x) const; + + private: + time_t m_begin; + time_t m_end; + }; + + private: + RecVecType *m_pRecVec; + }; + + + /** + * A smart pointer to a recordings list. As long as an instance of this + * exists the recordings are locked in the vdr. + */ + class RecordingsListPtr : public std::tr1::shared_ptr< RecordingsList > + { + friend class RecordingsManager; + + private: + RecordingsListPtr(RecordingsManagerPtr recManPtr, std::tr1::shared_ptr< RecordingsList > recList); + + public: + virtual ~RecordingsListPtr(); + + private: + RecordingsManagerPtr m_recManPtr; + }; + + + /** + * return singleton instance of RecordingsManager as a shared Pointer. + * This ensures that after last use of the RecordingsManager it is + * deleted. After deletion of the original RecordingsManager a repeated + * call to this function creates a new RecordingsManager which is again + * kept alive as long references to it exist. + */ + RecordingsManagerPtr LiveRecordingsManager(); + +} // namespace vdrlive + +#endif // VDR_LIVE_RECORDINGS_H diff --git a/recordings.cpp b/recordings.cpp deleted file mode 100644 index 5d5070a..0000000 --- a/recordings.cpp +++ /dev/null @@ -1,259 +0,0 @@ -#include -#include -#include -#include -#include -#include "tools.h" -#include "epg_events.h" -#include "recordings.h" -#include "stdext.h" - - -using namespace std::tr1; -using namespace std; - -namespace vdrlive { - - RecordingsManager::RecordingsManager() : - m_recordingsLock(&Recordings) - { - } - - string RecordingsManager::Md5Hash(const cRecording* recording) const - { - return "recording_" + MD5Hash(recording->FileName()); -/* unsigned char md5[MD5_DIGEST_LENGTH]; - const char* fileName = recording->FileName(); - MD5(reinterpret_cast(fileName), strlen(fileName), md5); - - ostringstream hashStr; - hashStr << "MD5" << hex; - for (size_t i = 0; i < MD5_DIGEST_LENGTH; i++) - hashStr << (0 + md5[i]); - return hashStr.str(); -*/ - } - - const cRecording* RecordingsManager::GetByMd5Hash(const string& 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(const cRecording* 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)); - } - - const std::string RecordingsManager::GetArchiveId(const cRecording* 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 ""; - } - - const string RecordingsManager::GetArchiveDescr(const cRecording* recording) - { - string archived; - if (IsArchived(recording)) { - archived += " ["; - archived += tr("On archive DVD No."); - archived += ": "; - archived += GetArchiveId(recording); - archived += "]"; - } - return archived; - } - - - RecordingsTree::RecordingsTree(RecordingsManagerPtr recMan) : - m_maxLevel(0), - m_root(new RecordingsItemDir()), - m_recManPtr(recMan) - { - // 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; - Map::iterator i = findDir(dir, dirName); - if (i == dir->m_entries.end()) { - RecordingsItemPtr recPtr (new RecordingsItemDir(dirName, level)); - 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(m_recManPtr->Md5Hash(recording), recName, recording)); - 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() ********"); - } - - RecordingsTree::Map::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< Map::iterator, Map::iterator> range = recItem->m_entries.equal_range(*i); - for (Map::iterator iter = range.first; iter != range.second; ++iter) { - if (iter->second->IsDir()) { - recItem = iter->second; - break; - } - } - } - return recItem->m_entries.begin(); - } - - RecordingsTree::Map::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< Map::iterator, Map::iterator> range = recItem->m_entries.equal_range(*i); - for (Map::iterator iter = range.first; iter != range.second; ++iter) { - if (iter->second->IsDir()) { - recItem = iter->second; - break; - } - } - } - return recItem->m_entries.end(); - } - - RecordingsTree::Map::iterator RecordingsTree::findDir(RecordingsItemPtr& dir, const string& dirName) - { - pair< Map::iterator, Map::iterator > range = dir->m_entries.equal_range(dirName); - for (Map::iterator i = range.first; i != range.second; ++i) { - if (i->second->IsDir()) { - return i; - } - } - return dir->m_entries.end(); - } - - RecordingsTree::RecordingsItem::RecordingsItem(const string& name) : - m_name(name), - m_entries() - { - } - - RecordingsTree::RecordingsItem::~RecordingsItem() - { - } - - RecordingsTree::RecordingsItemDir::RecordingsItemDir() : - RecordingsItem(""), - m_level(0) - { - } - - RecordingsTree::RecordingsItemDir::~RecordingsItemDir() - { - } - - RecordingsTree::RecordingsItemDir::RecordingsItemDir(const string& name, int level) : - RecordingsItem(name), - m_level(level) - { - } - - RecordingsTree::RecordingsItemRec::RecordingsItemRec(const string& id, const string& name, const cRecording* recording) : - RecordingsItem(name), - m_recording(recording), - m_id(id) - { - } - - RecordingsTree::RecordingsItemRec::~RecordingsItemRec() - { - } - - time_t RecordingsTree::RecordingsItemRec::StartTime() const - { - return m_recording->start; - } - - - - RecordingsManagerPtr LiveRecordingsManager() - { - static weak_ptr livingRecMan; - - RecordingsManagerPtr r = livingRecMan.lock(); - if (r) { - return r; - } - else { - RecordingsManagerPtr n(new RecordingsManager); - livingRecMan = n; - return n; - } - } - -} // namespace vdrlive diff --git a/recordings.h b/recordings.h deleted file mode 100644 index 81b971f..0000000 --- a/recordings.h +++ /dev/null @@ -1,153 +0,0 @@ -#ifndef VDR_LIVE_RECORDINGS_H -#define VDR_LIVE_RECORDINGS_H - -#include -#include -#include -#include -#include "stdext.h" - -namespace vdrlive { - - // Forward declations from epg_events.h - class EpgInfo; - typedef std::tr1::shared_ptr EpgInfoPtr; - - class RecordingsManager; - typedef std::tr1::shared_ptr RecordingsManagerPtr; - - class RecordingsManager - { - friend RecordingsManagerPtr LiveRecordingsManager(); - - public: - /** - * generates a Md5 hash from a cRecording entry. It can be used - * to reidentify a recording. - */ - std::string Md5Hash(const cRecording* recording) const; - - /** - * fetches a cRecording from VDR's Recordings collection. Returns - * NULL if recording was not found - */ - const cRecording* GetByMd5Hash(const std::string& hash) const; - - /** - * Determine wether the recording has been archived on - * removable media (e.g. DVD-ROM) - */ - static bool IsArchived(const cRecording* recording); - - /** - * Provide an identification of the removable media - * (e.g. DVD-ROM Number or Name) where the recording has - * been archived. - */ - static const std::string GetArchiveId(const cRecording* recording); - - static const std::string GetArchiveDescr(const cRecording* recording); - - private: - RecordingsManager(); - - cThreadLock m_recordingsLock; - }; - - - class RecordingsTree - { - public: - - class RecordingsItem; - - typedef std::tr1::shared_ptr< RecordingsItem > RecordingsItemPtr; - typedef std::multimap< std::string, RecordingsItemPtr > Map; - - class RecordingsItem - { - friend class RecordingsTree; - - public: - virtual ~RecordingsItem(); - - virtual time_t StartTime() const = 0; - virtual bool IsDir() const = 0; - virtual const std::string& Name() const { return m_name; } - virtual const std::string Id() const = 0; - - virtual const cRecording* Recording() const { return 0; } - virtual const cRecordingInfo* RecInfo() const { return 0; } - - protected: - RecordingsItem(const std::string& name); - - private: - std::string m_name; - Map m_entries; - }; - - class RecordingsItemDir : public RecordingsItem - { - public: - RecordingsItemDir(); - RecordingsItemDir(const std::string& name, int level); - - virtual ~RecordingsItemDir(); - - virtual time_t StartTime() const { return 0; } - virtual bool IsDir() const { return true; } - virtual const std::string Id() const { std::string e; return e; } - - private: - int m_level; - }; - - class RecordingsItemRec : public RecordingsItem - { - public: - RecordingsItemRec(const std::string& id, const std::string& name, const cRecording* recording); - - virtual ~RecordingsItemRec(); - - virtual time_t StartTime() const; - virtual bool IsDir() const { return false; } - virtual const std::string Id() const { return m_id; } - - virtual const cRecording* Recording() const { return m_recording; } - virtual const cRecordingInfo* RecInfo() const { return m_recording->Info(); } - - private: - const cRecording *m_recording; - std::string m_id; - }; - - RecordingsTree(RecordingsManagerPtr recManPtr); - - virtual ~RecordingsTree(); - - Map::iterator begin(const std::vector< std::string >& path); - Map::iterator end(const std::vector< std::string >&path); - - int MaxLevel() const { return m_maxLevel; } - - private: - int m_maxLevel; - RecordingsItemPtr m_root; - RecordingsManagerPtr m_recManPtr; - - Map::iterator findDir(RecordingsItemPtr& dir, const std::string& dirname); - }; - - /** - * return singleton instance of RecordingsManager as a shared Pointer. - * This ensures that after last use of the RecordingsManager it is - * deleted. After deletion of the original RecordingsManager a repeated - * call to this function creates a new RecordingsManager which is again - * kept alive as long references to it exist. - */ - RecordingsManagerPtr LiveRecordingsManager(); - -} // namespace vdrlive - -#endif // VDR_LIVE_RECORDINGS_H diff --git a/tasks.cpp b/tasks.cpp index 92b893e..d7acaf3 100644 --- a/tasks.cpp +++ b/tasks.cpp @@ -3,12 +3,14 @@ #include #include #include -#include "exception.h" -#include "recordings.h" + #include "stdext.h" -#include "tasks.h" +#include "exception.h" +#include "recman.h" #include "tools.h" +#include "tasks.h" + namespace vdrlive { using namespace std; -- cgit v1.2.3