summaryrefslogtreecommitdiff
path: root/recman.cpp
diff options
context:
space:
mode:
authorDieter Hametner <dh (plus) vdr (at) gekrumbel (dot) de>2007-10-21 15:56:00 +0000
committerDieter Hametner <dh (plus) vdr (at) gekrumbel (dot) de>2007-10-21 15:56:00 +0000
commit0fea7f3368ab6ca3f056c26151d30e978a2f2eee (patch)
tree8fe0c6eb5c3cd46ecf68a1cd52cc224b0349ed2d /recman.cpp
parent8ac9758dd9dc502c64c06c3339b6a60a7d54e39c (diff)
downloadvdr-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.cpp464
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