/* * update.c: TVM2VDR plugin for the Video Disk Recorder * * See the README file for copyright information and how to reach the author. * */ #include "tvm2vdr.h" #include "update.h" //*************************************************************************** // //*************************************************************************** class Event : public cListObject { public: int eventId; string channelId; string source; string title; string compTitle; }; class Events : public cList { public: Event* findByComp(string pattern) { for (Event* e = First(); e; e = Next(e)) { if (e->compTitle == pattern) return e; } return 0; } int lv(string pattern, int maxDist, Event*& e) { int d; int dummy; for (e = First(); e; e = Next(e)) { if (::abs(pattern.length() - e->compTitle.length()) > maxDist) continue; if ((d = lvDistance(e->compTitle, pattern, na, dummy)) <= maxDist) return d; } e = 0; return -1; } }; //*************************************************************************** // Evaluate //*************************************************************************** int cUpdate::evaluateEpisodes() { int ec = 0, pp = 0, plv = 0; // statistics long lStart = time(0); // Events events; tell(1, "SERIES: Starting episode lookup ..."); // first read all events into list .. /* for (int f = epgDb->find(cEventFields::viAll); f; f = epgDb->fetch(cEventFields::viAll)) { Event* e = new Event(); events.Add(e); e->eventId = epgDb->getIntValue(cEventFields::fiEventId); e->channelId = epgDb->getStrValue(cEventFields::fiChannelId); e->source = epgDb->getStrValue(cEventFields::fiSource); e->title = epgDb->getStrValue(cEventFields::fiTitle); e->compTitle = epgDb->getStrValue(cEventFields::fiCompTitle); } epgDb->resetFetch(cEventFields::viAll); */ // loop over all episodes .. for (int found = episodeDb->find(cEpisodeFields::viAllDistinct); found; found = episodeDb->fetch(cEpisodeFields::viAllDistinct)) { // const int maxTitleDist = (((double)strlen(episodeCompName)) / 100.0 * 20.0); char* episodeCompName = strdup(episodeDb->getStrValue(cEpisodeFields::fiDistCompName)); ec++; epgDb->setValue(cEventFields::fiCompTitle, episodeCompName); // loop over all events matching this episodes 1:1 for (int evtFound = epgDb->find(cEventFields::viByCompTitle); evtFound; evtFound = epgDb->fetch(cEventFields::viByCompTitle)) { if (strlen(epgDb->getStrValue(cEventFields::fiCompShortText)) == 0) continue; if (!epgDb->find()) // perform select to load all fields { tell(0, "TVM2VDR: Fatal lookup of event '%ld/%s' failed", epgDb->getIntValue(cEventFields::fiEventId), epgDb->getStrValue(cEventFields::fiChannelId)); continue; } const char* evtCompShortText = epgDb->getStrValue(cEventFields::fiCompShortText); epgDb->setValue(cEventFields::fiEpisode, episodeCompName); // enable store() here only for debugging! // epgDb->store(); episodeDb->clear(); episodeDb->setValue(cEpisodeFields::fiCompName, episodeCompName); episodeDb->setValue(cEpisodeFields::fiCompPartName, evtCompShortText); // search episode part 1:1 if (episodeDb->find(cEpisodeFields::viByCompNames)) { pp++; // store reference epgDb->setValue(cEventFields::fiEpisodePart, evtCompShortText); epgDb->setValue(cEventFields::fiEpisodeLang, episodeDb->getStrValue(cEpisodeFields::fiLang)); epgDb->setValue(cEventFields::fiUpdFlg, "U"); epgDb->store(); } else // if not found try via lv { const int maxDist = (((double)strlen(evtCompShortText)) / 100.0 * 20.0); int d; int dMin = maxDist+1; int tmp; char* bestCompPart = 0; char* bestCompPartLang = 0; for (int eFound = episodeDb->find(cEpisodeFields::viByCompName); eFound; eFound = episodeDb->fetch(cEpisodeFields::viByCompName)) { if (::abs(strlen(evtCompShortText) - strlen(episodeDb->getStrValue(cEpisodeFields::fiCompPartName))) >= dMin) continue; if ((d = lvDistance(evtCompShortText, episodeDb->getStrValue(cEpisodeFields::fiCompPartName), 20, tmp)) < dMin) { free(bestCompPart); free(bestCompPartLang); bestCompPart = strdup(episodeDb->getStrValue(cEpisodeFields::fiCompPartName)); bestCompPartLang = strdup(episodeDb->getStrValue(cEpisodeFields::fiLang)); dMin = d; } } if (bestCompPart) { plv++; // store reference epgDb->setValue(cEventFields::fiEpisodePart, bestCompPart); epgDb->setValue(cEventFields::fiEpisodeLang, bestCompPartLang); epgDb->setValue(cEventFields::fiUpdFlg, "U"); epgDb->store(); free(bestCompPart); bestCompPart = 0; free(bestCompPartLang); bestCompPartLang = 0; } episodeDb->resetFetch(cEpisodeFields::viByCompName); } episodeDb->resetFetch(cEpisodeFields::viByCompNames); } epgDb->resetFetch(cEventFields::viByCompTitle); free(episodeCompName); } episodeDb->resetFetch(cEpisodeFields::viAllDistinct); tell(1, "SERIES: Lookup done for " "%d series, matched %d parts by compare and %d parts by lv in %ld seconds", ec, pp, plv, time(0)-lStart); return success; } //*************************************************************************** // Download Episodes and store to filesystem //*************************************************************************** int cUpdate::downloadEpisodes() { cSvdrpClient cl(TVM2VDRConfig.seriesUrl, TVM2VDRConfig.seriesPort); string fileName; string linkName; int isLink = 0; cEpisodeFiles files; int code; int abort = 0; char command[50]; tell(0, "TVM2VDR: Starting episode download ..."); int minutes = na; if (episodeDb->find(cEpisodeFields::viMaxUpdSp) && episodeDb->getIntValue(cEpisodeFields::fiMaxUpdSp) > 0) minutes = (time(0) - episodeDb->getIntValue(cEpisodeFields::fiMaxUpdSp)) / 60; episodeDb->resetFetch(cEpisodeFields::viMaxUpdSp); if (!minutes && !fullupdate) { tell(0, "TVM2VDR: Nothing to be done, all episodes are up-to-date"); return done; } // open tcp connection if (cl.open() != 0) { tell(0, "TVM2VDR: Open network connection failed, aborting transfer!"); return fail; } // select characterset if (!cl.send(withutf8 ? "CHARSET utf-8\n": "CHARSET iso-8859-1\n")) { tell(0, "TVM2VDR: Send '%s' failed, aborting transfer!", command); cl.close(); return fail; } // check for characterset confirmation cList csconf; if (cl.receive(&csconf) != 225) { tell(0, "SVDRPCL: did not receive charset confirmation. Closing..."); cl.abort(); return fail; } if (csconf.First() && csconf.First()->Text()) tell(0, "TVM2VDR: Got '%s'", csconf.First()->Text()); // identify myself sprintf(command, "HELLO %s v%s (%s)\n", PLUGIN_NAME_I18N, VERSION, VERSION_DATE); cl.send(command); cl.receive(); // build GET command for the files *command = 0; if (fullupdate || minutes == na) { tell(1, "TVM2VDR: Requesting all episodes due to '%s'", minutes != na ? "fullupdate" : "empty table"); sprintf(command, "GET all\n"); // truncate table! episodeDb->truncate(); } else if (minutes > 0) { minutes += 5; // request 5 minutes more to compensate time diffs to constabel.net tell(0, "TVM2VDR: Requesting episode changes of last %d minutes", minutes); sprintf(command, "TGET newer than %d minutes\n", minutes); } if (!cl.send(command)) { tell(0, "TVM2VDR: Send '%s' failed, aborting transfer!", command); cl.close(); return fail; } cList* result = new cList; while (!abort && (code = cl.receive(result)) != codeCommunicationEnd) { switch (code) { case codeFileInfo: { if (result->Count() < 2) { tell(2, "TVM2VDR: Protocol violation, aborting!"); abort = 1; } else { linkName = ""; fileName = result->Next(result->First())->Text(); isLink = fileName != "not a link"; if (!isLink) fileName = result->First()->Text(); else linkName = result->First()->Text(); } break; } case codeFileContent: { if (isLink) { files.Add(new cEpisodeFile(fileName, linkName)); } else { for (cLine* l = result->First(); l; l = result->Next(l)) { if (strcmp(l->Text(), "End of data") == 0) { result->Del(l); break; } tell(3, "TVM2VDR: Got line '%s'", l->Text()); } if (result->Count()) { // create episode file and adopt the result files.Add(new cEpisodeFile(fileName, "", result)); // create new result object since cEpisodeFile adopted the current result = new cList; } } break; } case codeTransferEnd: { abort = 1; break; } } result->Clear(); } tell(0, "TVM2VDR: Received %d episode files", files.Count()); files.storeToTable(episodeDb); if (TVM2VDRConfig.storeSeriesToFs) files.storeToFile(episodesdir); delete result; cl.close(); return 0; }