/* * timer.c: Web video plugin for the Video Disk Recorder * * See the README file for copyright information and how to reach the author. * * $Id$ */ #include #include #include #include "timer.h" #include "request.h" #include "common.h" #include "download.h" #include "config.h" #include "filedownloader.h" // --- cWebviTimer ----------------------------------------------- cWebviTimer::cWebviTimer(int ID, const char *title, const char *ref, cWebviTimerManager *manager, time_t last, int interval, bool success, const char *errmsg) : id(ID), title(title ? strdup(title) : strdup("???")), reference(ref ? strdup(ref) : NULL), lastUpdate(last), interval(interval), running(false), lastSucceeded(success), lastError(errmsg ? strdup(errmsg) : NULL), parent(manager) { } cWebviTimer::~cWebviTimer() { if(title) free(title); if (reference) free(reference); if (lastError) free(lastError); } void cWebviTimer::SetTitle(const char *newTitle) { if (title) free(title); title = newTitle ? strdup(newTitle) : strdup("???"); parent->SetModified(); } void cWebviTimer::SetInterval(int interval) { if (interval < MIN_TIMER_INTERVAL) this->interval = MIN_TIMER_INTERVAL; else this->interval = interval; parent->SetModified(); } int cWebviTimer::GetInterval() const { return interval; } time_t cWebviTimer::NextUpdate() const { int delta = interval; // Retry again soon if the last try failed if (!lastSucceeded && delta > RETRY_TIMER_INTERVAL) delta = RETRY_TIMER_INTERVAL; return lastUpdate + delta; } void cWebviTimer::Execute() { if (running) { debug("previous instance of this timer is still running"); return; } info("Executing timer \"%s\"", title); running = true; cTimerRequest *req = new cTimerRequest(id, reference); req->SetTimer(this); cWebviThread::Instance().AddRequest(req); lastUpdate = time(NULL); SetError(NULL); parent->SetModified(); activeStreams.Clear(); } void cWebviTimer::SetError(const char *errmsg) { bool oldSuccess = lastSucceeded; if (lastError) free(lastError); lastError = NULL; if (errmsg) { lastSucceeded = false; lastError = strdup(errmsg); } else { lastSucceeded = true; } if (oldSuccess != lastSucceeded) parent->SetModified(); } const char *cWebviTimer::LastError() const { return lastError ? lastError : ""; } void cWebviTimer::DownloadStreams(const char *menuxml, cProgressVector& summaries) { if (!menuxml) { SetError("xml == NULL"); return; } xmlDocPtr doc = xmlParseMemory(menuxml, strlen(menuxml)); if (!doc) { xmlErrorPtr xmlerr = xmlGetLastError(); if (xmlerr) error("libxml error: %s", xmlerr->message); SetError(xmlerr->message); return; } xmlNodePtr node = xmlDocGetRootElement(doc); if (node) node = node->xmlChildrenNode; while (node) { if (!xmlStrcmp(node->name, BAD_CAST "link")) { xmlNodePtr node2 = node->children; while(node2) { if (!xmlStrcmp(node2->name, BAD_CAST "stream")) { xmlChar *streamref = xmlNodeListGetString(doc, node2->xmlChildrenNode, 1); const char *ref = (const char *)streamref; if (parent->AlreadyDownloaded(ref)) { debug("timer: %s has already been downloaded", ref); } else if (*ref) { info("timer: downloading %s", ref); activeStreams.Append(strdup(ref)); cFileDownloadRequest *req = \ new cFileDownloadRequest(REQ_ID_TIMER, ref, summaries.NewDownload()); req->SetTimer(this); cWebviThread::Instance().AddRequest(req); } xmlFree(streamref); } node2 = node2->next; } } node = node->next; } xmlFreeDoc(doc); if (activeStreams.Size() == 0) { running = false; } } void cWebviTimer::CheckFailed(const char *errmsg) { SetError(errmsg); running = false; } void cWebviTimer::RequestFinished(const char *ref, const char *errmsg) { if (errmsg && !lastError) SetError(errmsg); if (ref) { if (!errmsg && parent) parent->MarkDownloaded(ref); int i = activeStreams.Find(ref); if (i != -1) { free(activeStreams[i]); activeStreams.Remove(i); } } if (activeStreams.Size() == 0) { info("timer \"%s\" done", title); running = false; } else { debug("timer %s is still downloading %d streams", reference, activeStreams.Size()); } } // --- cWebviTimerManager ---------------------------------------- cWebviTimerManager::cWebviTimerManager() : nextID(1), modified(false), disableSaving(false), convertTemplatePaths(false) { } cWebviTimerManager &cWebviTimerManager::Instance() { static cWebviTimerManager instance; return instance; } void cWebviTimerManager::LoadTimers(FILE *f) { cReadLine rl; long lastRefresh; int interval; int success; char *ref; const char *ver; const char *title; const char *errmsg; int n, i; ver = rl.Read(f); if (strcmp(ver, "# WVTIMER1") != 0 && strncmp(ver, "# WVTIMER1/", 11) != 0) { error("Can't load timers. Unknown format: %s", ver); disableSaving = true; return; } convertTemplatePaths = (strcmp(ver, "# WVTIMER1") == 0); i = 1; while (true) { n = fscanf(f, "%ld %d %d %ms", &lastRefresh, &interval, &success, &ref); if (n != 4) { if (n != EOF) { error("Error while reading webvi timers file"); } else if (ferror(f)) { LOG_ERROR_STR("webvi timers file"); } break; } if (convertTemplatePaths) { char *newref = UpgradedTemplatePath(ref); if (newref) { free(ref); ref = newref; } } title = rl.Read(f); title = title ? skipspace(title) : "???"; errmsg = success ? NULL : ""; info("timer %d: title %s", i++, title); debug(" ref %s, lastRefresh %ld, interval %d", ref, lastRefresh, interval); timers.Add(new cWebviTimer(nextID++, title, ref, this, (time_t)lastRefresh, interval, success, errmsg)); free(ref); } } void cWebviTimerManager::LoadHistory(FILE *f) { cReadLine rl; char *line; while ((line = rl.Read(f))) refHistory.Append(strdup(line)); debug("loaded history: len = %d", refHistory.Size()); } void cWebviTimerManager::SaveTimers(FILE *f, const char *version) { // Format: space separated field in this order: // lastUpdate interval lastSucceeded reference title fprintf(f, "# WVTIMER1/%s\n", version); cWebviTimer *t = timers.First(); while (t) { if (fprintf(f, "%ld %d %d %s %s\n", t->LastUpdate(), t->GetInterval(), t->Success(), t->GetReference(), t->GetTitle()) < 0) { error("Failed to save timer data!"); } t = timers.Next(t); } } void cWebviTimerManager::SaveHistory(FILE *f) { int size = refHistory.Size(); int first; if (size <= MAX_TIMER_HISTORY_SIZE) first = 0; else first = size - MAX_TIMER_HISTORY_SIZE; for (int i=first; iNextUpdate() < now) { debug("%d. %s: launching now", timer->GetID(), timer->GetTitle()); timer->Execute(); } else { #ifdef DEBUG time_t next = timer->NextUpdate(); strftime(timestr, 25, "%x %X", localtime(&next)); debug("%d. %s: next update at %s", timer->GetID(), timer->GetTitle(), timestr); #endif } timer = timers.Next(timer); } } cWebviTimer *cWebviTimerManager::GetByID(int id) const { cWebviTimer *timer = timers.First(); while (timer) { if (timer->GetID() == id) return timer; timer = timers.Next(timer); } return NULL; } cWebviTimer *cWebviTimerManager::Create(const char *title, const char *ref, bool getExisting) { cWebviTimer *t; if (!ref) return NULL; if (getExisting) { t = timers.First(); while (t) { if (strcmp(t->GetReference(), ref) == 0) { return t; } t = timers.Next(t); } } t = new cWebviTimer(nextID++, title, ref, this); timers.Add(t); modified = true; return t; } void cWebviTimerManager::Remove(cWebviTimer *timer) { timers.Del(timer); modified = true; } void cWebviTimerManager::MarkDownloaded(const char *ref) { if (!ref) return; if (refHistory.Find(ref) == -1) { refHistory.Append(strdup(ref)); modified = true; } } bool cWebviTimerManager::AlreadyDownloaded(const char *ref) { return refHistory.Find(ref) != -1; }