summaryrefslogtreecommitdiff
path: root/recording.c
diff options
context:
space:
mode:
authorhorchi <vdr@jwendel.de>2017-03-05 14:51:57 +0100
committerhorchi <vdr@jwendel.de>2017-03-05 14:51:57 +0100
commit5eacf5bf36ddbac082a9e40a2bcdfd0f04fd3f9f (patch)
tree392875cb707b94aaba9d8941113eae35efaf2ec2 /recording.c
downloadvdr-plugin-epg2vdr-5eacf5bf36ddbac082a9e40a2bcdfd0f04fd3f9f.tar.gz
vdr-plugin-epg2vdr-5eacf5bf36ddbac082a9e40a2bcdfd0f04fd3f9f.tar.bz2
commit of actual revision1.1.441.1.42
Diffstat (limited to 'recording.c')
-rw-r--r--recording.c533
1 files changed, 533 insertions, 0 deletions
diff --git a/recording.c b/recording.c
new file mode 100644
index 0000000..c3d5ac4
--- /dev/null
+++ b/recording.c
@@ -0,0 +1,533 @@
+/*
+ * recording.c: EPG2VDR plugin for the Video Disk Recorder
+ *
+ * See the README file for copyright information and how to reach the author.
+ *
+ */
+
+#include <set>
+
+#include <vdr/videodir.h>
+
+#include "lib/common.h"
+#include "update.h"
+
+//***************************************************************************
+// Is Protected
+//***************************************************************************
+
+int isProtected(const char* path)
+{
+ char* dir;
+ int fsk = no;
+ char* file = 0;
+ char* p = 0;
+
+ const char* videoDir = cVideoDirectory::Name();
+
+ if (strncmp(path, videoDir, strlen(videoDir)) != 0)
+ {
+ tell(0, "Fatal: Unexpected video dir in '%s'", path);
+ return no;
+ }
+
+ dir = strdup(path + strlen(videoDir));
+
+ do
+ {
+ if (p) *p = 0;
+
+ asprintf(&file, "%s/%s/protection.fsk", videoDir, dir);
+ fsk = fileExists(file);
+ free(file);
+
+ } while (!fsk && (p = strrchr(dir, '/')));
+
+ tell(3, "'%s' is %sprotected", path, fsk ? "" : "not ");
+ free(dir);
+
+ return fsk;
+}
+
+//***************************************************************************
+// Cleanup Deleted Recordings from Table
+//***************************************************************************
+
+int cUpdate::cleanupDeletedRecordings(int force)
+{
+ int delCnt = 0;
+ md5Buf md5path;
+ std::set<std::string> recMd5List;
+
+ // --------------------------
+ // get recordings lock
+
+#if defined (APIVERSNUM) && (APIVERSNUM >= 20301)
+ LOCK_RECORDINGS_READ;
+ const cRecordings* recordings = Recordings;
+#else
+ const cRecordings* recordings = &Recordings;
+#endif
+
+ if (recordings->Count() == lastRecordingCount && !force)
+ return done;
+
+ tell(0, "Cleanup deleted recordings at database%s", force ? " (forced)" : "");
+
+ // create set of existing recordings (to improve speed)
+
+ for (const cRecording* rec = recordings->First(); rec; rec = recordings->Next(rec))
+ {
+ int pathOffset = 0;
+
+ if (strncmp(rec->FileName(), videoBasePath, strlen(videoBasePath)) == 0)
+ {
+ pathOffset = strlen(videoBasePath);
+
+ if (*(rec->FileName()+pathOffset) == '/')
+ pathOffset++;
+ }
+
+ createMd5(rec->FileName()+pathOffset, md5path);
+
+ tell(5, "DEBUG: Recording: '%s' [%s]", rec->Title(), rec->FileName());
+ recMd5List.insert(md5path);
+ }
+
+ connection->startTransaction();
+
+ recordingListDb->clear();
+ recordingListDb->setValue("VDRUUID", Epg2VdrConfig.uuid);
+
+ for (int f = selectRecordings->find(); f && dbConnected(); f = selectRecordings->fetch())
+ {
+ // bin 'ich' zuständig?
+
+ if (!recordingListDb->hasValue("VDRUUID", Epg2VdrConfig.uuid))
+ {
+ if (!recordingListDb->hasValue("OWNER", "") || !Epg2VdrConfig.useCommonRecFolder)
+ continue;
+
+ // sonderlocke, das mounted ggf. nicht jeder überall
+
+ if (recordingListDb->getIntValue("FSK"))
+ continue;
+ }
+
+ if (recMd5List.find(recordingListDb->getStrValue("MD5PATH")) == recMd5List.end())
+ {
+ // not found, mark as deleted
+
+ delCnt++;
+ recordingListDb->setValue("STATE", "D");
+ recordingListDb->update();
+ }
+ }
+
+ selectRecordings->freeResult();
+
+ connection->commit();
+ lastRecordingCount = recordings->Count();
+
+ tell(0, "Info: Marked %d recordings as deleted", delCnt);
+
+ return success;
+}
+
+//***************************************************************************
+// Update Pending Recording Info Files
+//***************************************************************************
+
+int cUpdate::updatePendingRecordingInfoFiles(const cRecordings* recordings)
+{
+ cEventDetails evd;
+ int count = 0;
+
+ if (pendingNewRecordings.empty())
+ return done;
+
+ tell(1, "Updating recording info in info.epg2vdr");
+
+ // iterate over pending recordings
+
+ while (!pendingNewRecordings.empty())
+ {
+ std::string path = pendingNewRecordings.front();
+ const cRecording* rec = ((cRecordings*)recordings)->GetByName(path.c_str());
+
+ pendingNewRecordings.pop();
+
+ if (!rec || !rec->Info() || !rec->Info()->GetEvent())
+ {
+ tell(0, "Warning: Recording '%s' not found or missin recording info,"
+ " can't create info.epg2vdr", path.c_str());
+ continue;
+ }
+
+ useeventsDb->clear();
+ useeventsDb->setValue("USEID", (int)rec->Info()->GetEvent()->EventID());
+
+ if (selectEventById->find())
+ {
+ evd.loadFromFs(path.c_str());
+ evd.updateByRow(useeventsDb->getRow());
+
+ if (evd.getChanges())
+ {
+ count++;
+ evd.storeToFs(path.c_str());
+
+ tell(1, "Updated/Created recording info for %d in info.epg2vdr with %d changes",
+ rec->Info()->GetEvent()->EventID(), evd.getChanges());
+ }
+ }
+ else
+ tell(0, "Warning: Event %d not found in table", rec->Info()->GetEvent()->EventID());
+
+ selectEventById->freeResult();
+ }
+
+ tell(1, "Updated %d pending info.epg2vdr files", count);
+
+ return done;
+}
+
+//***************************************************************************
+// Store All Recording Info Files (only used by manual trigger)
+//***************************************************************************
+
+int cUpdate::storeAllRecordingInfoFiles()
+{
+ cEventDetails evd;
+ int count = 0;
+
+ tell(1, "Store info.epg2vdr for all recordings");
+
+ connection->startTransaction();
+ recordingListDb->clear();
+ recordingListDb->setValue("VDRUUID", Epg2VdrConfig.uuid);
+
+ for (int f = selectRecordings->find(); f && dbConnected(); f = selectRecordings->fetch())
+ {
+ char* path;
+
+ // bin 'ich' zuständig?
+
+ if (!recordingListDb->hasValue("VDRUUID", Epg2VdrConfig.uuid))
+ {
+ // nicht zuständig wenn OWNER nicht leer oder ich nicht am NAS Verbund teilnehme
+
+ if (!recordingListDb->hasValue("OWNER", "") || !Epg2VdrConfig.useCommonRecFolder)
+ continue;
+ }
+
+ asprintf(&path, "%s/%s", videoBasePath, recordingListDb->getStrValue("PATH"));
+
+ evd.loadFromFs(path);
+ evd.updateByRow(recordingListDb->getRow());
+
+ if (evd.getChanges())
+ {
+ count++;
+ evd.storeToFs(path);
+
+ tell(2, "Store recording info for %ld in info.epg2vdr with %d changes",
+ recordingListDb->getIntValue("EVENTID"), evd.getChanges());
+ }
+
+ time_t sp = time(0);
+ recordingListDb->setValue("LASTIFOUPD", sp);
+ recordingListDb->update(sp);
+
+ free(path);
+ }
+
+ connection->commit();
+ selectRecordings->freeResult();
+ storeAllRecordingInfoFilesTrigger = no;
+
+ tell(1, "Stored %d info.epg2vdr files", count);
+
+ return done;
+}
+
+//***************************************************************************
+// Update Recording Info Files
+//***************************************************************************
+
+int cUpdate::updateRecordingInfoFiles()
+{
+ cEventDetails evd;
+ int count = 0;
+
+ tell(1, "Update info.epg2vdr recordings");
+
+ connection->startTransaction();
+ recordingListDb->clear();
+
+ for (int f = selectRecForInfoUpdate->find(); f && dbConnected(); f = selectRecForInfoUpdate->fetch())
+ {
+ char* path;
+
+ asprintf(&path, "%s/%s", videoBasePath, recordingListDb->getStrValue("PATH"));
+
+ // only update if this VDR can access the recording folder ...
+
+ if (folderExists(path))
+ {
+ evd.loadFromFs(path);
+ evd.updateByRow(recordingListDb->getRow());
+
+ if (evd.getChanges())
+ {
+ count++;
+ evd.storeToFs(path);
+
+ tell(1, "Update recording info for %ld in info.epg2vdr with %d changes",
+ recordingListDb->getIntValue("EVENTID"), evd.getChanges());
+ }
+
+ time_t sp = time(0);
+ recordingListDb->setValue("LASTIFOUPD", sp);
+ recordingListDb->update(sp); // force same stamp!
+ }
+ else
+ {
+ tell(3, "Info: Folder '%s' not found, suppose it belong to another vdr", path);
+ }
+
+ free(path);
+ }
+
+ connection->commit();
+ selectRecForInfoUpdate->freeResult();
+ tell(1, "Updated %d info.epg2vdr files", count);
+
+ return done;
+}
+
+//***************************************************************************
+// Update Recording Table
+//***************************************************************************
+
+int cUpdate::updateRecordingTable(int fullReload)
+{
+ int count = 0, insCnt = 0, updCnt = 0, dirCnt = 0;
+
+ // first cleanup, at least to adjust count in 'lastRecordingCount'
+
+ if (fullReload)
+ recordingListDb->deleteWhere("vdruuid = '%s'", Epg2VdrConfig.uuid);
+ else
+ cleanupDeletedRecordings(yes);
+
+ // get channel and recordings lock
+
+#if defined (APIVERSNUM) && (APIVERSNUM >= 20301)
+ cChannelsLock channelsLock(false);
+ const cChannels* channels = channelsLock.Channels();
+#else
+ cChannels* channels = &Channels;
+#endif
+
+#if defined (APIVERSNUM) && (APIVERSNUM >= 20301)
+ cRecordingsLock recordingsLock(false);
+ const cRecordings* recordings = recordingsLock.Recordings();
+#else
+ const cRecordings* recordings = &Recordings;
+#endif
+
+ // update
+
+ tell(0, "Updating recording list table");
+
+ connection->startTransaction();
+
+ recordingDirDb->deleteWhere("vdruuid = '%s'", Epg2VdrConfig.uuid);
+
+ // ----------------
+ // update ...
+
+ for (const cRecording* rec = recordings->First(); rec; rec = recordings->Next(rec))
+ {
+ int insert;
+ int fsk;
+ md5Buf md5path;
+ const char* subTitle = "";
+ int eventId = 0;
+ std::string channelId = "";
+ const char* description = "";
+ const char* title = rec->Name();
+ const cRecordingInfo* recInfo = rec->Info();
+ int pathOffset = 0;
+ const cChannel* channel = 0;
+ int baseChanges = 0;
+
+ // check if directory is registered
+
+ if (rec->HierarchyLevels() > 0)
+ dirCnt += updateRecordingDirectory(rec);
+
+ // check if recording is registered
+
+ if (strncmp(rec->FileName(), videoBasePath, strlen(videoBasePath)) == 0)
+ {
+ pathOffset = strlen(videoBasePath);
+
+ if (*(rec->FileName()+pathOffset) == '/')
+ pathOffset++;
+ }
+
+ createMd5(rec->FileName()+pathOffset, md5path);
+
+ if (recInfo)
+ {
+ channelId = *(recInfo->ChannelID().ToString());
+ subTitle = recInfo->ShortText() ? recInfo->ShortText() : "";
+ description = recInfo->Description() ? recInfo->Description() : "";
+ channel = channels->GetByChannelID(recInfo->ChannelID());
+
+ if (recInfo->Title()) title = recInfo->Title();
+ if (recInfo->GetEvent()) eventId = recInfo->GetEvent()->EventID();
+ }
+
+ fsk = isProtected(rec->FileName());
+
+ recordingListDb->clear();
+
+ recordingListDb->setValue("MD5PATH", md5path);
+ recordingListDb->setValue("STARTTIME", rec->Start());
+ recordingListDb->setValue("OWNER", Epg2VdrConfig.useCommonRecFolder ? "" : Epg2VdrConfig.uuid);
+
+ insert = !recordingListDb->find();
+ recordingListDb->clearChanged();
+
+ // base data ..
+
+ recordingListDb->setValue("STATE", rec->IsInUse() == ruTimer ? "R" : "F");
+ recordingListDb->setValue("INUSE", rec->IsInUse());
+ recordingListDb->setValue("PATH", rec->FileName()+pathOffset);
+ recordingListDb->setValue("NAME", rec->BaseName());
+ recordingListDb->setValue("FOLDER", rec->Folder());
+ recordingListDb->setValue("LONGDESCRIPTION", description);
+ recordingListDb->setValue("DURATION", rec->LengthInSeconds() > 0 ? rec->LengthInSeconds() : 0);
+ recordingListDb->setValue("EVENTID", eventId);
+ recordingListDb->setValue("CHANNELID", channelId.c_str());
+ recordingListDb->setValue("FSK", fsk);
+
+ if (channel)
+ recordingListDb->setValue("CHANNELNAME", channel->Name());
+
+ // scraping relevand data ..
+
+ baseChanges = recordingListDb->getChanges();
+
+ recordingListDb->setValue("TITLE", title);
+ recordingListDb->setValue("SHORTTEXT", subTitle);
+
+ // load event details
+
+ cEventDetails evd;
+ evd.loadFromFs(rec->FileName());
+ evd.updateToRow(recordingListDb->getRow());
+
+ // any scrap relevand data changed?
+
+ if (recordingListDb->getChanges() != baseChanges)
+ {
+ int isSeries = recordingListDb->hasValue("CATEGORY", "Serie");
+ int changed = no;
+
+ if (isSeries)
+ {
+ if (recordingListDb->getValue("SCRSERIESID")->isEmpty() ||
+ !recordingListDb->hasValue("SCRSERIESEPISODE", recordingListDb->getIntValue("SCRINFOEPISODEID")) ||
+ !recordingListDb->hasValue("SCRSERIESID", recordingListDb->getIntValue("SCRINFOSERIESID")))
+ {
+ changed = yes;
+ }
+ }
+ else
+ {
+ if (recordingListDb->getValue("SCRMOVIEID")->isEmpty() ||
+ !recordingListDb->hasValue("SCRMOVIEID", recordingListDb->getIntValue("SCRINFOMOVIEID")))
+ {
+ changed = yes;
+ }
+ }
+
+ if (changed)
+ {
+ recordingListDb->setValue("SCRNEW", yes); // force scrap
+ recordingListDb->setValue("SCRSP", time(0)); // force load from vdr
+ }
+ }
+
+ // don't toggle uuid if already set!
+
+ if (recordingListDb->getValue("VDRUUID")->isNull())
+ recordingListDb->setValue("VDRUUID", Epg2VdrConfig.uuid);
+
+ if (insert || recordingListDb->getChanges())
+ {
+ insert ? insCnt++ : updCnt++;
+ tell(2, "Info: '%s' recording '%s / %s' due to %d changes [%s]",
+ insert ? "Insert" : "Update", title, subTitle,
+ recordingListDb->getChanges(), recordingListDb->getChangedFields().c_str());
+ recordingListDb->store();
+ }
+
+ count++;
+
+ recordingListDb->reset();
+ }
+
+ connection->commit();
+
+ tell(0, "Info: Found %d recordings; %d inserted; %d updated "
+ "and %d directories", count, insCnt, updCnt, dirCnt);
+
+ // create info files for new recordings
+
+ updatePendingRecordingInfoFiles(recordings);
+
+ return success;
+}
+
+//***************************************************************************
+// Update Recording Directory
+//***************************************************************************
+
+int cUpdate::updateRecordingDirectory(const cRecording* recording)
+{
+ int cnt = 0;
+ char* dir = strdup(recording->Name());
+ char* pos = strrchr(dir, '~');
+
+ if (pos)
+ {
+ *pos = 0;
+
+ for (int level = 0; level < recording->HierarchyLevels(); level++)
+ {
+ recordingDirDb->clear();
+ recordingDirDb->setValue("VDRUUID", Epg2VdrConfig.uuid);
+ recordingDirDb->setValue("DIRECTORY", dir);
+
+ if (!recordingDirDb->find())
+ {
+ cnt++;
+ recordingDirDb->store();
+ }
+
+ recordingDirDb->reset();
+
+ char* pos = strrchr(dir, '~');
+ if (pos) *pos=0;
+ }
+ }
+
+ free(dir);
+
+ return cnt;
+}