#include #include #include #include "tvm2vdr.h" #include "update.h" #define PLUGIN_NAME_I18N "tvm2vdr" // g++ -ggdb -DLOCDIR=\"./locale\" -I /usr/include/libxml2/ tvmtest.cc db.c ~/VDR/i18n.c /home/wendel/VDR/tools.c /home/wendel/VDR/thread.c -lsqlite3 -lrt -ljpeg -lpthread -o tvmt int loglevel = 4; cEvent::cEvent(tEventID EventID) { schedule = NULL; eventID = EventID; tableID = 0xFF; version = 0xFF; title = NULL; shortText = NULL; description = NULL; components = NULL; memset(contents, 0, sizeof(contents)); parentalRating = 0; startTime = 0; duration = 0; vps = 0; } cEvent::~cEvent() { } int cEvent::Compare(const cListObject &ListObject) const { return 0; } void cEvent::SetTitle(const char* t) { title = strcpyrealloc(title, t); } void cEvent::SetShortText(const char *ShortText) { shortText = strcpyrealloc(shortText, ShortText); } void cEvent::SetDescription(const char *Description) { description = strcpyrealloc(description, Description); } void cEvent::SetStartTime(time_t StartTime) { } void cEvent::SetDuration(int Duration) { } // --- tChannelID ------------------------------------------------------------ const tChannelID tChannelID::InvalidID; tChannelID tChannelID::FromString(const char *s) { // char *sourcebuf = NULL; // int nid; // int tid; // int sid; // int rid = 0; // int fields = sscanf(s, "%a[^-]-%d-%d-%d-%d", &sourcebuf, &nid, &tid, &sid, &rid); // if (fields == 4 || fields == 5) { // int source = cSource::FromString(sourcebuf); // free(sourcebuf); // if (source >= 0) // return tChannelID(source, nid, tid, sid, rid); // } return tChannelID::InvalidID; } // cString tChannelID::ToString(void) const // { // char buffer[256]; // snprintf(buffer, sizeof(buffer), rid ? "%s-%d-%d-%d-%d" : "%s-%d-%d-%d", *cSource::ToString(source), nid, tid, sid, rid); // return buffer; // } tChannelID &tChannelID::ClrPolarization(void) { while (tid > 100000) tid -= 100000; return *this; } //*************************************************************************** // Parse XML Event //*************************************************************************** int parseEvent(cDbRow* event, xmlNode* node) { const char* name; char* content; for (xmlNodePtr n = node->xmlChildrenNode; n; n = n->next) { if (n->type != XML_ELEMENT_NODE) continue; name = (const char*)n->name; content = (char*)xmlNodeGetContent(n); printf("\t%s = '%s'\n", name, content); if (cDbService::FieldDef* f = cEventFields::toField(name)) { if (f->format == cDbService::ffAscii) event->setValue(f->index, content); else event->setValue(f->index, atoi(content)); } else printf("Ignoring unexpected element <%s>\n", name); xmlFree(content); } return success; } //*************************************************************************** // //*************************************************************************** CurlMemoryStruct data; void unscramble() { unsigned int hunkSize = 1000; char buffer[hunkSize]; int pos = 0; struct Hunktab { int offset; int len; }; static Hunktab hunktab[] = { {0x006a, 6}, {0x0064, 6}, {0x0070, 88}, {0x0320, 17}, {0x0011, 83}, {0x012c,100}, {0x0000,17}, {0x0331,183}, {0x0258,200}, {0x0190,200}, {0x00c8,100} }; if (data.size < hunkSize) return ; for (int i = 0; i < sizeof(hunktab)/sizeof(hunktab[0]); i++) { memcpy(&buffer[pos], &data.memory[hunktab[i].offset], hunktab[i].len); pos += hunktab[i].len; } memcpy(data.memory, buffer, hunkSize); } int unscrambleFile() { int size = 0; FILE* f = fopen("20120605_594.xml.tvm", "r"); if (!f) { printf("cant open file \n"); return 1; } data.memory = (char*)malloc(1000); size = fread(data.memory, 1, 1000, f); data.size = size; printf("read %d bytes\n", size); fclose(f); unscramble(); f = fopen("20120605_594.xml.gz", "w"); if (!f) { printf("cant open file for writing\n"); return 1; } fwrite(data.memory, 1, data.size, f); fclose(f); return 0; } //*************************************************************************** // //*************************************************************************** int evaluateEpisodes(Table* episodeDb, Table* epgDb) { char* stmtStr; sqlite3_stmt* lvStatement = 0; int ec = 0, pp = 0, plv = 0; // statistics long lStart = time(0); printf("SERIES: Starting episode lookup ...\n"); // loop over all episodes for (int found = episodeDb->find(cEpisodeFields::viAllDistinct); found; found = episodeDb->fetch(cEpisodeFields::viAllDistinct)) { char* episodeCompName = strdup(episodeDb->getStrValue(cEpisodeFields::fiDistCompName)); const int maxTitleDist = (((double)strlen(episodeCompName)) / 100.0 * 20.0); asprintf(&stmtStr, "comptitle = '%s' or lv(comptitle, '%s') < %d", episodeCompName, episodeCompName, maxTitleDist); ec++; // loop over all events matching this episodes 1:1 or by LV epgDb->prepareWhere(stmtStr, lvStatement); for (int evtFound = epgDb->findWhere(lvStatement); evtFound; evtFound = epgDb->fetch(lvStatement)) { if (strlen(epgDb->getStrValue(cEventFields::fiCompShortText)) == 0) continue; if (!epgDb->find()) // perform select to load all fields dsyslog("TVM2VDR: Fatal lookup of event '%s/%s' failed", epgDb->getStrValue(cEventFields::fiEventId), epgDb->getStrValue(cEventFields::fiChannelId)); const char* evtCompShortText = epgDb->getStrValue(cEventFields::fiCompShortText); epgDb->setValue(cEventFields::fiEpisode, episodeCompName); // 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->store(); } else // if not found try via lv { const int maxDist = (((double)strlen(evtCompShortText)) / 100.0D * 20.0D); 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->store(); free(bestCompPart); bestCompPart = 0; free(bestCompPartLang); bestCompPartLang = 0; } episodeDb->resetFetch(cEpisodeFields::viByCompName); } episodeDb->resetFetch(cEpisodeFields::viByCompNames); } epgDb->resetFetch(lvStatement); // epgDb->resetFetch(cEventFields::viByCompTitle); free(stmtStr); free(episodeCompName); } episodeDb->resetFetch(cEpisodeFields::viAllDistinct); printf("SERIES: Lookup done for " "%d series, matched %d parts by compare and %d parts by lv in %ld seconds\n", ec, pp, plv, time(0)-lStart); return success; } //*************************************************************************** // Download Episodes and store to filesystem //*************************************************************************** int downloadEpisodes(Table* episodeDb) { cSvdrpClient cl("eplists.constabel.net", 2006 /*port*/); string fileName; string linkName; int isLink = 0; cEpisodeFiles files; int code; int abort = 0; char command[50]; int withutf8 = yes; if (cl.open() != 0) { printf("TVM2VDR: Open network connection failed, aborting transfer!\n"); return fail; } printf("TVM2VDR: Starting episode download ...\n"); // select characterset printf("TVM2VDR: request charset ...\n"); 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); printf("TVM2VDR: send %s", command); cl.send(command); cl.receive(); // GET sprintf(command, "GET all\n"); // sprintf(command, "TGET newer than 4 days\n"); printf("TVM2VDR: send %s", command); if (!cl.send(command)) { printf("TVM2VDR: Send '%s' failed, aborting transfer!\n", command); cl.close(); return fail; } cList* result = new cList; while (!abort && (code = cl.receive(result)) != codeCommunicationEnd) { switch (code) { case codeFileInfo: { if (result->Count() < 2) { if (loglevel > 1) printf("TVM2VDR: Protocol violation, aborting!\n"); 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; } } 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(); } printf("TVM2VDR: Received %d episode files\n", files.Count()); files.storeToFile("./epl/"); files.storeToTable(episodeDb); delete result; cl.close(); return 0; } //*************************************************************************** // Update Tvm Map Table //*************************************************************************** int updateTvmMap(cChannelMap* chanmap, const char* dbpath) { Table* mapDb; int count = 0; mapDb = new Table("tvmmap", cTvmMapFields::fields); if (mapDb->open() != success) { tell(0, "TVM2VDR: Could not access sqlite database %s for table %s", dbpath, mapDb->TableName()); return fail; } mapDb->truncate(); tell(0, "TVM2VDR: Update TVM map"); for (cChanMap::iterator iter = chanmap->chanmap.begin(); iter != chanmap->chanmap.end(); iter++) { int tvmid = iter->first; tell(0, "TVM2VDR: Update TVM map for tvmid %d", tvmid); for (int index = 0; index < chanmap->GetChanCount(tvmid); index++) { mapDb->clear(); mapDb->setValue(cTvmMapFields::fiTvmId, tvmid); mapDb->setValue(cTvmMapFields::fiChannelName, chanmap->GetChanStr(tvmid, index)); mapDb->setValue(cTvmMapFields::fiSource, tvmid ? "tvm" : "vdr"); mapDb->store(); tell(0, "TVM2VDR: insert '%s' to tvmid %d in TVM map", chanmap->GetChanStr(tvmid, index), tvmid); count++; } } tell(0, "TVM2VDR: Update TVM map done with %d entries", count); mapDb->close(); delete mapDb; return success; } //*************************************************************************** // Main //*************************************************************************** int main(int argc, char** argv) { int res; if (argc < 1) { printf("Usage: %s \n", argv[0]); return 1; } Table::setFileName("./tvm.db"); Table::setConfPath("./configs/tvm2vdr"); Table::setEncoding("UTF-8"); // cChannelMap* chanmap = new cChannelMap("./configs/tvm2vdr/tvm2vdr_channelmap.conf"); // updateTvmMap(chanmap, "./tvm.db"); // delete chanmap; // return 0; // // --------------------------- // // check memory map // map evtMemList; // map* p; // p = &evtMemList; // string evtKey = "id111"; // evtMemList[evtKey].md5 = "xxx"; // evtMemList[evtKey].version = 12; // evtMemList[evtKey].tableid = 13; // evtKey = "id222"; // (*p)[evtKey].md5 = "yyy"; // (*p)[evtKey].version = 111; // (*p)[evtKey].tableid = 333; // evtKey = "id111"; // if (p->find(evtKey) != evtMemList.end()) // { // printf("(%s) md5 - '%s'; version - (%d); tid - (%d) \n", evtKey.c_str(), // (*p)[evtKey].md5.c_str(), (*p)[evtKey].version, (*p)[evtKey].tableid); // } // return 0; // unscrambleFile(); // // --------------------------- // // check channelmap parser // cChannelMap* chanmap; // chanmap = new cChannelMap("./configs/tvm2vdr/tvm2vdr_channelmap.conf"); // for (cChanMap::iterator iter = chanmap->chanmap.begin(); iter != chanmap->chanmap.end(); iter++) // { // int tvmid = iter->first; // printf(" -------------------- \n"); // for (int index = 0; index < chanmap->GetChanCount(tvmid); index++) // printf("%03d - '%s'\n", tvmid, chanmap->GetChanStr(tvmid, index)); // } // return 0; // ------------------------------------------------ // Constabel Test Table* episodesDb = new Table("episodes", cEpisodeFields::fields, cEpisodeFields::views); if (episodesDb->open() != success) { printf("Opening database 'episodes' failed\n"); delete episodesDb; return 1; } downloadEpisodes(episodesDb); // evaluateEpisodes(episodesDb, evtDb); delete episodesDb; // ------------------------------------------------ // Test Episodes Table ... printf("\n----------------------------------\n"); Table* epiDb = new Table("episodes", cEpisodeFields::fields, cEpisodeFields::views); if (epiDb->open() != success) { printf("Opening database 'episodes' failed\n"); delete epiDb; return 1; } int days = na; epiDb->setValue(cEpisodeFields::fiEpisodeName, "test serie"); epiDb->setValue(cEpisodeFields::fiPartName, "part1"); epiDb->setValue(cEpisodeFields::fiLang, "de"); epiDb->store(); if (epiDb->find(cEpisodeFields::viMaxUpdSp)) { printf("found - max updsp is (%ld)\n", epiDb->getIntValue(cEpisodeFields::fiMaxUpdSp)); if (epiDb->getIntValue(cEpisodeFields::fiMaxUpdSp)) days = (time(0) - epiDb->getIntValue(cEpisodeFields::fiMaxUpdSp)) / 60 / 60 / 24; } epiDb->resetFetch(cEpisodeFields::viMaxUpdSp); printf("episodes have to get %d days\n", days); delete epiDb; // ------------------------------------------------ // Test EPG Table ... printf("\n----------------------------------\n"); Table* db = new Table("events", cEventFields::fields, cEventFields::views); if (db->open() != success) { printf("Opening database 'events' failed\n"); delete db; return 1; } // cDbRow* eventRow = new cDbRow(cEventFields::fields); db->setValue(cEventFields::fiEventId, 4711); db->setValue(cEventFields::fiSource, "tvm"); db->setValue(cEventFields::fiTitle, "Test Title"); db->setValue(cEventFields::fiCompTitle, "TESTTITLE"); db->setValue(cEventFields::fiChannelId, "11"); db->setValue(cEventFields::fiCategory, "cat 11"); db->store(); db->clear(); db->setValue(cEventFields::fiEventId, 4712); db->setValue(cEventFields::fiSource, "tvm"); db->setValue(cEventFields::fiTitle, "Test Title 12"); db->setValue(cEventFields::fiCompTitle, "TESTTITLE"); db->setValue(cEventFields::fiChannelId, "11"); db->setValue(cEventFields::fiFileRef, "test-1234"); db->setValue(cEventFields::fiCategory, "cat 12"); db->store(); db->clear(); db->setValue(cEventFields::fiEventId, 4713); db->setValue(cEventFields::fiSource, "tvm"); db->setValue(cEventFields::fiTitle, "Test Title what the fuck ..."); db->setValue(cEventFields::fiChannelId, "11"); db->setValue(cEventFields::fiFileRef, "test-11111"); db->setValue(cEventFields::fiCategory, "cat 13"); db->setValue(cEventFields::fiCompTitle, "TESTTITLEWHATTHEFUCK"); db->store(); // fetch via view db->clear(); db->setValue(cEventFields::fiChannelId, "11"); db->setValue(cEventFields::fiSource, "tvm"); for (res = db->find(cEventFields::viVdr); res; res = db->fetch(cEventFields::viVdr)) { if (!db->find()) printf("FATAL!\n"); int id = db->getIntValue(cEventFields::fiEventId); printf(" -> found event '%s'(%d) [%s] '%s'\n", db->getStrValue(cEventFields::fiTitle), id, db->getStrValue(cEventFields::fiCompTitle), db->getStrValue(cEventFields::fiCategory) ); } db->resetFetch(cEventFields::viVdr); printf("\n"); db->setValue(cEventFields::fiCompTitle, "TESTTITLE"); for (int res = db->find(cEventFields::viByCompTitle); res; res = db->fetch(cEventFields::viByCompTitle)) { if (!db->find()) printf("FATAL!\n"); printf("found (%ld) '%s' by comptitle [%s]\n", db->getIntValue(cEventFields::fiEventId), db->getStrValue(cEventFields::fiTitle), db->getStrValue(cEventFields::fiCompTitle)); } db->resetFetch(cEventFields::viByCompTitle); printf("\n"); for (res = db->find(cEventFields::viVdr); res; res = db->fetch(cEventFields::viVdr)) { if (!db->find()) printf("FATAL!\n"); int id = db->getIntValue(cEventFields::fiEventId); printf(" -> found event '%s'(%d) [%s] '%s'\n", db->getStrValue(cEventFields::fiTitle), id, db->getStrValue(cEventFields::fiCompTitle), db->getStrValue(cEventFields::fiCategory) ); } db->resetFetch(cEventFields::viVdr); delete db; return 0; printf("----------------------------------\n"); // delete db; // ------------------------------------------------ // Test Image Table ... printf("\n----------------------------------\n"); Table* imgDb = new Table("images", cImageFields::fields, cImageFields::views); if (imgDb->open() != success) { printf("Opening database 'images' failed\n"); delete imgDb; return 1; } imgDb->setValue(cImageFields::fiEventId, 4711); imgDb->setValue(cImageFields::fiLfn, 0); imgDb->setValue(cImageFields::fiName, "ok 1"); imgDb->store(); imgDb->setValue(cImageFields::fiEventId, 4711); imgDb->setValue(cImageFields::fiLfn, 1); imgDb->setValue(cImageFields::fiName, "fail"); imgDb->store(); imgDb->setValue(cImageFields::fiEventId, 4713); imgDb->setValue(cImageFields::fiLfn, 0); imgDb->setValue(cImageFields::fiName, "ok 2"); imgDb->store(); // fetch via view imgDb->clear(); imgDb->setValue(cImageFields::fiLfn, 1); for (res = imgDb->find(cImageFields::viAllLessLfn); res; res = imgDb->fetch(cImageFields::viAllLessLfn)) { int id = imgDb->getIntValue(cImageFields::fiEventId); printf(" -> found image (%d) via view, name is '%s', lfn is (%ld)\n", id, imgDb->getStrValue(cImageFields::fiName), imgDb->getIntValue(cImageFields::fiLfn)); } imgDb->resetFetch(cImageFields::viAllLessLfn); printf("----------------------------------\n"); delete imgDb; // ------------------------------------------------ return 0; // ------------------------------------------------ // Test File Table ... Table* fileDb = new Table("tvmfiles", cTvmFields::fields); fileDb->open(); cDbRow tvmFile(cTvmFields::fields); tvmFile.setValue(cTvmFields::fiName, "test"); if (fileDb->find(&tvmFile)) printf("found with tag %s\n", fileDb->getStrValue(cTvmFields::fiTag)); else printf("NOT found\n"); fileDb->setValue(cTvmFields::fiTag, "1234"); fileDb->setValue(cTvmFields::fiFileRef, "test-1234"); fileDb->store(); delete fileDb; // ------------------------------------------------ return 0; // ------------------------------------------------ // Test XML parsing xmlDocPtr tvmXml = 0; tvmXml = xmlParseFile("test.xml"); if (!tvmXml) { printf("Failed to parse test.xml\n"); return 1; } xmlNodePtr xmlRoot = xmlDocGetRootElement(tvmXml); for (xmlNodePtr node = xmlRoot->xmlChildrenNode; node; node = node->next) { cDbRow* evt = 0; char* prop = 0; tEventID id; // skip all unexpected elements if (node->type != XML_ELEMENT_NODE || strcmp((char*)node->name, "event") != 0) continue; // get/check id if (!(prop = (char*)xmlGetProp(node, (xmlChar*)"id")) || !*prop || !(id = atoi(prop))) { xmlFree(prop); printf("Missing event id, ignoring!\n"); continue; } xmlFree(prop); // create event evt = new cDbRow(cEventFields::fields); evt->setValue(cEventFields::fiEventId, id); evt->setValue(cEventFields::fiSource, "tvm"); printf("event %ld\n", evt->getIntValue(cEventFields::fiEventId)); if (parseEvent(evt, node) != success) delete evt; } xmlFreeDoc(tvmXml); return 0; }