#include #include #include #include #include #include #include #include #include #include "setup.h" #include "thread.h" #define EPGSYNC_SLEEPMS 30 bool IsType(const cChannel* Channel, eChannelTypes Type) { if (!Channel) return false; if (Type == ctAll) return true; cString source = cSource::ToString(Channel->Source()); char c = ((const char*) source)[0]; switch (c) { case 'C': // analogtv-, pvrinput-, pvrusb2-plugin if (Channel->Ca() >= 0xa0 && Channel->Ca() <= 0xa2) return Type == ctAnalog; else return Type == ctDVB_C; case 'I': return Type == ctIptv; case 'S': return Type == ctDVB_S; case 'T': return Type == ctDVB_T; case 'V': return Type == ctAnalog; default: return false; } } cChannel *GetChannelByName(const char* Name, const cChannel *IgnoreChannel = NULL, eChannelTypes Type = ctAll) { for (cChannel *channel = Channels.First(); channel; channel = Channels.Next(channel)) { if (strcasecmp(Name, channel->Name()) == 0 || strcasecmp(Name, channel->ShortName()) == 0) { if (IsType(channel, Type) && channel != IgnoreChannel) return channel; } } return NULL; } void cEpgSyncThread::Action() { SetPriority(15); plugin = cPluginManager::GetPlugin("svdrpservice"); if (!plugin) { esyslog("EpgSync: Plugin svdrpservice not available"); return; } svdrp.handle = -1; for (int i = 0; svdrp.handle < 0 && i < EpgSyncSetup.connectAttempts && Running(); i++) { if (i > 0) cCondWait::SleepMs((1 << i) * 1000); svdrp.serverIp = EpgSyncSetup.serverIp; svdrp.serverPort = EpgSyncSetup.serverPort; svdrp.shared = EpgSyncSetup.channelByChannel; plugin->Service("SvdrpConnection-v1.0", &svdrp); } if (svdrp.handle < 0) { Skins.QueueMessage(mtError, tr("EpgSync: Unable to connect to Server")); return; } FILE *f = tmpfile(); if (!f) { esyslog("EpgSync: Unable to open temporary file"); plugin->Service("SvdrpConnection-v1.0", &svdrp); return; } // Get now and next if (EpgSyncSetup.nowNext && CmdLSTE(f, "now") && CmdLSTE(f, "next")) { AddSchedule(f); } if (EpgSyncSetup.channelByChannel) { // Get channel by channel if (EpgSyncSetup.redirectChannels == rcmId) { // Direct import, no mapping: // loop through local channels, get channels by ID cSchedulesLock *lock = NULL; for (cChannel *channel = Channels.First(); channel && Running(); channel = Channels.Next(channel)) { if (!lock) lock = new cSchedulesLock(); if (cSchedules::Schedules(*lock)->GetSchedule(channel)) { DELETENULL(lock); if (CmdLSTE(f, *channel->GetChannelID().ToString())) { AddSchedule(f); } cCondWait::SleepMs(EPGSYNC_SLEEPMS); } } DELETENULL(lock); } else { // Map channels by name: // loop through remote channel list, get channels by number SvdrpCommand_v1_0 cmd; cmd.command = "LSTC\r\n"; cmd.handle = svdrp.handle; plugin->Service("SvdrpCommand-v1.0", &cmd); if (cmd.responseCode == 250) { for (cLine *line = cmd.reply.First(); line && Running(); line = cmd.reply.Next(line)) { const char* s = line->Text(); const char* p = strchr(s, ' '); if (p && p > s) { if (CmdLSTE(f, cString::sprintf("%.*s", (int)(p - s), s))) { AddSchedule(f); } cCondWait::SleepMs(EPGSYNC_SLEEPMS); } else { esyslog("EpgSync: LSTC returned channel without number: %s", line->Text()); } } } else { cLine *line = cmd.reply.First(); esyslog("EpgSync: LSTC error %hu %s", cmd.responseCode, line ? line->Text() : ""); } } } else { // Get complete epg if (CmdLSTE(f)) AddSchedule(f); } fclose(f); plugin->Service("SvdrpConnection-v1.0", &svdrp); cSchedules::Cleanup(true); } bool cEpgSyncThread::CmdLSTE(FILE *f, const char *Arg) { SvdrpCommand_v1_0 cmd; cmd.command = cString::sprintf("LSTE %s\r\n", Arg ? Arg : ""); cmd.handle = svdrp.handle; if (!Running()) return false; plugin->Service("SvdrpCommand-v1.0", &cmd); cLine *line = cmd.reply.First(); if (cmd.responseCode == 550) { // "Channel not defined" or "No schedule found" return false; } else if (cmd.responseCode != 215 || !line) { esyslog("EpgSync: LSTE error %hu %s", cmd.responseCode, line ? line->Text() : ""); return false; } const cChannel* targetChannel = NULL; while (cmd.reply.Next(line)) { const char* s = line->Text(); if (*s == 'C') { // new channel begins targetChannel = NULL; const char* p = skipspace(s + 1); cChannel *c = Channels.GetByChannelID(tChannelID::FromString(p)); bool cOk = IsType(c, (eChannelTypes) EpgSyncSetup.channelTypes); if (cOk && EpgSyncSetup.redirectChannels != rcmNameId) { // got acceptable channel by ID targetChannel = c; } else if (EpgSyncSetup.redirectChannels != rcmId) { // get channel by name p = strchr(p, ' '); if (p) { // got channel name p = skipspace(p); targetChannel = GetChannelByName(p, c, (eChannelTypes) EpgSyncSetup.channelTypes); } // fallback to channel with original ID if (!targetChannel && cOk) targetChannel = c; } if (targetChannel) { // generate channel header if (fputs("C ", f) < 0 || fputs(targetChannel->GetChannelID().ToString(), f) < 0 || fputs(" ", f) < 0 || fputs(targetChannel->Name(), f) < 0 || fputs("\n", f) < 0) { LOG_ERROR; return false; } } } else if (targetChannel) { // copy channel data if (fputs(s, f) < 0 || fputs("\n", f) < 0) { LOG_ERROR; return false; } } line = cmd.reply.Next(line); } return Running(); } void cEpgSyncThread::AddSchedule(FILE *f) { rewind(f); if (!cSchedules::Read(f)) esyslog("EpgSync: Error parsing EPG data"); rewind(f); if (ftruncate(fileno(f), 0) < 0) { LOG_ERROR; } } cEpgSyncThread::cEpgSyncThread(): cThread("epgsync") { plugin = NULL; } cEpgSyncThread::~cEpgSyncThread() { Cancel(5); }