diff options
author | horchi <vdr@jwendel.de> | 2017-03-05 14:51:57 +0100 |
---|---|---|
committer | horchi <vdr@jwendel.de> | 2017-03-05 14:51:57 +0100 |
commit | 5eacf5bf36ddbac082a9e40a2bcdfd0f04fd3f9f (patch) | |
tree | 392875cb707b94aaba9d8941113eae35efaf2ec2 /recording.c | |
download | vdr-plugin-epg2vdr-5eacf5bf36ddbac082a9e40a2bcdfd0f04fd3f9f.tar.gz vdr-plugin-epg2vdr-5eacf5bf36ddbac082a9e40a2bcdfd0f04fd3f9f.tar.bz2 |
Diffstat (limited to 'recording.c')
-rw-r--r-- | recording.c | 533 |
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; +} |