diff options
author | Antti Ajanki <antti.ajanki@iki.fi> | 2013-11-07 20:22:49 +0200 |
---|---|---|
committer | Antti Ajanki <antti.ajanki@iki.fi> | 2013-11-07 20:22:49 +0200 |
commit | 8cc97018ad564998a42d02dfbdaa98a36101e350 (patch) | |
tree | d4b5b9b253df70cc760e0967b657baf69cd2d5c0 | |
parent | 451f691d367a46f365101f39cc093ce2feaacd13 (diff) | |
download | vdr-plugin-webvideo-8cc97018ad564998a42d02dfbdaa98a36101e350.tar.gz vdr-plugin-webvideo-8cc97018ad564998a42d02dfbdaa98a36101e350.tar.bz2 |
Initial update of the VDR plugin
-rw-r--r-- | src/vdr-plugin/Makefile | 3 | ||||
-rw-r--r-- | src/vdr-plugin/download.c | 138 | ||||
-rw-r--r-- | src/vdr-plugin/download.h | 1 | ||||
-rw-r--r-- | src/vdr-plugin/filedownloader.c | 147 | ||||
-rw-r--r-- | src/vdr-plugin/filedownloader.h | 85 | ||||
-rw-r--r-- | src/vdr-plugin/menu.c | 897 | ||||
-rw-r--r-- | src/vdr-plugin/menu.h | 38 | ||||
-rw-r--r-- | src/vdr-plugin/request.c | 286 | ||||
-rw-r--r-- | src/vdr-plugin/request.h | 92 | ||||
-rw-r--r-- | src/vdr-plugin/timer.c | 1 | ||||
-rw-r--r-- | src/vdr-plugin/timer.h | 1 | ||||
-rw-r--r-- | src/vdr-plugin/webvideo.c | 18 |
12 files changed, 1350 insertions, 357 deletions
diff --git a/src/vdr-plugin/Makefile b/src/vdr-plugin/Makefile index 0fcaffe..a0102a4 100644 --- a/src/vdr-plugin/Makefile +++ b/src/vdr-plugin/Makefile @@ -22,12 +22,13 @@ LOCDIR = $(call PKGCFG,locdir) PLGCFG = $(call PKGCFG,plgcfg) # TMPDIR ?= /tmp +LIBWEBVIDIR ?= $(shell find ../.. -name libwebvi.so -printf "%h\n" | head -1) ### The compiler options: override CFLAGS += $(call PKGCFG,cflags) $(shell xml2-config --cflags) override CXXFLAGS += $(call PKGCFG,cxxflags) $(shell xml2-config --cflags) -override LDFLAGS += $(shell xml2-config --libs) -L../libwebvi -lwebvi +override LDFLAGS += $(shell xml2-config --libs) -L$(LIBWEBVIDIR) -lwebvi export CFLAGS export CXXFLAGS diff --git a/src/vdr-plugin/download.c b/src/vdr-plugin/download.c index 4b7a971..908575b 100644 --- a/src/vdr-plugin/download.c +++ b/src/vdr-plugin/download.c @@ -10,6 +10,7 @@ #include <sys/select.h> #include <unistd.h> #include <fcntl.h> +#include <stdexcept> #include <vdr/tools.h> #include "download.h" #include "common.h" @@ -27,9 +28,18 @@ static void diff_timeval(struct timeval *a, struct timeval *b, result->tv_usec = usec_diff; } +static void merge_fdsets(fd_set *outputfds, fd_set *inputfds, int maxfd) { + for (int fd=0; fd<maxfd; fd++) { + if (FD_ISSET(fd, inputfds)) { + FD_SET(fd, outputfds); + } + } +} + // --- cWebviThread -------------------------------------------------------- -cWebviThread::cWebviThread() { +cWebviThread::cWebviThread() +{ int pipefd[2]; if (pipe(pipefd) == -1) @@ -45,6 +55,8 @@ cWebviThread::cWebviThread() { webvi_set_config(webvi, WEBVI_CONFIG_TIMEOUT_DATA, this); webvi_set_config(webvi, WEBVI_CONFIG_TIMEOUT_CALLBACK, UpdateTimeout); } + + // FIXME: downloadManager timeout callbacks } cWebviThread::~cWebviThread() { @@ -140,6 +152,10 @@ void cWebviThread::StopFinishedRequests() { WebviMsg *donemsg; cMenuRequest *req; + downloadManager.CheckForFinished(); + // FIXME: MoveToFinishedList + + do { donemsg = webvi_get_message(webvi, &msg_remaining); @@ -164,11 +180,14 @@ void cWebviThread::Stop() { void cWebviThread::Action(void) { fd_set readfds, writefds, excfds; + fd_set webvi_readfds, webvi_writefds, webvi_excfds; + fd_set curl_readfds, curl_writefds, curl_excfds; int maxfd, s; struct timeval timeout, now; - long running_handles; - bool check_done = false; + bool check_for_finished_requests = false; bool has_request_files = false; + long running_webvi_handles; + long running_curl_handles = 0; if (webvi == 0) { error("Failed to get libwebvi context"); @@ -179,7 +198,33 @@ void cWebviThread::Action(void) { FD_ZERO(&readfds); FD_ZERO(&writefds); FD_ZERO(&excfds); - webvi_fdset(webvi, &readfds, &writefds, &excfds, &maxfd); + FD_ZERO(&webvi_readfds); + FD_ZERO(&webvi_writefds); + FD_ZERO(&webvi_excfds); + FD_ZERO(&curl_readfds); + FD_ZERO(&curl_writefds); + FD_ZERO(&curl_excfds); + maxfd = -1; + + int tmpmaxfd; + webvi_fdset(webvi, &webvi_readfds, &webvi_writefds, &webvi_excfds, &tmpmaxfd); + if (tmpmaxfd != -1) { + merge_fdsets(&readfds, &webvi_readfds, tmpmaxfd); + merge_fdsets(&writefds, &webvi_writefds, tmpmaxfd); + merge_fdsets(&excfds, &webvi_excfds, tmpmaxfd); + if (tmpmaxfd > maxfd) + maxfd = tmpmaxfd; + } + + downloadManager.FDSet(&curl_readfds, &curl_writefds, &curl_excfds, &tmpmaxfd); + if (tmpmaxfd != -1) { + merge_fdsets(&readfds, &curl_readfds, tmpmaxfd); + merge_fdsets(&writefds, &curl_writefds, tmpmaxfd); + merge_fdsets(&excfds, &curl_excfds, tmpmaxfd); + if (tmpmaxfd > maxfd) + maxfd = tmpmaxfd; + } + FD_SET(newreqread, &readfds); if (newreqread > maxfd) maxfd = newreqread; @@ -187,7 +232,7 @@ void cWebviThread::Action(void) { has_request_files = false; requestMutex.Lock(); for (int i=0; i<activeRequestList.Size(); i++) { - int fd = activeRequestList[i]->File(); + int fd = activeRequestList[i]->ReadFile(); if (fd != -1) { FD_SET(fd, &readfds); if (fd > maxfd) @@ -219,61 +264,80 @@ void cWebviThread::Action(void) { } else if (s == 0) { // timeout timerActive = false; - webvi_perform(webvi, WEBVI_SELECT_TIMEOUT, WEBVI_SELECT_CHECK, &running_handles); - check_done = true; + webvi_perform(webvi, WEBVI_SELECT_TIMEOUT, WEBVI_SELECT_CHECK, &running_webvi_handles); + check_for_finished_requests = true; + + // FIXME: curl timeout } else { - for (int fd=0; fd<=maxfd; fd++) { + int num_processed_fds = 0; + for (int fd=0; (fd<=maxfd) && (num_processed_fds<=s); fd++) { if (FD_ISSET(fd, &readfds)) { - if (fd == newreqread) { + if (FD_ISSET(fd, &webvi_readfds)) { + webvi_perform(webvi, fd, WEBVI_SELECT_READ, &running_webvi_handles); + num_processed_fds += 1; + } else if (FD_ISSET(fd, &curl_readfds)) { + downloadManager.HandleSocket(fd, WEBVI_SELECT_READ, &running_curl_handles); + num_processed_fds += 1; + } else if (fd == newreqread) { char tmpbuf[8]; int n = read(fd, tmpbuf, 8); if (n > 0 && memchr(tmpbuf, 'S', n)) Cancel(-1); ActivateNewRequest(); - } else { - cMenuRequest *match = NULL; - - if (has_request_files) { - requestMutex.Lock(); - for (int i=0; i<activeRequestList.Size(); i++) { - if (fd == activeRequestList[i]->File()) { - match = activeRequestList[i]; - break; - } - } - requestMutex.Unlock(); - - // call Read() after releasing the mutex - if (match) { - match->Read(); - if (match->IsFinished()) - MoveToFinishedList(match); + num_processed_fds += 1; + } else if (has_request_files) { + requestMutex.Lock(); + cMenuRequest *readRequest = NULL; + for (int i=0; i<activeRequestList.Size(); i++) { + if (fd == activeRequestList[i]->ReadFile()) { + readRequest = activeRequestList[i]; + break; } } - - if (!match) { - webvi_perform(webvi, fd, WEBVI_SELECT_READ, &running_handles); - check_done = true; + requestMutex.Unlock(); + + // call Read() after releasing the mutex + if (readRequest) { + num_processed_fds += 1; + readRequest->Read(); + if (readRequest->IsFinished()) + MoveToFinishedList(readRequest); } } } - if (FD_ISSET(fd, &writefds)) - webvi_perform(webvi, fd, WEBVI_SELECT_WRITE, &running_handles); - if (FD_ISSET(fd, &excfds)) - webvi_perform(webvi, fd, WEBVI_SELECT_EXCEPTION, &running_handles); + if (FD_ISSET(fd, &writefds)) { + if (FD_ISSET(fd, &webvi_writefds)) { + webvi_perform(webvi, fd, WEBVI_SELECT_WRITE, &running_webvi_handles); + num_processed_fds += 1; + } else if (FD_ISSET(fd, &curl_writefds)) { + downloadManager.HandleSocket(fd, WEBVI_SELECT_WRITE, &running_curl_handles); + num_processed_fds += 1; + } + } + if (FD_ISSET(fd, &excfds)) { + if (FD_ISSET(fd, &webvi_excfds)) { + webvi_perform(webvi, fd, WEBVI_SELECT_EXCEPTION, &running_webvi_handles); + num_processed_fds += 1; + } else if (FD_ISSET(fd, &curl_excfds)) { + downloadManager.HandleSocket(fd, WEBVI_SELECT_EXCEPTION, &running_curl_handles); + num_processed_fds += 1; + } + } } + + check_for_finished_requests = (num_processed_fds > 0); } - if (check_done) { + if (check_for_finished_requests) { StopFinishedRequests(); - check_done = false; } } } void cWebviThread::AddRequest(cMenuRequest *req) { requestMutex.Lock(); + req->SetDownloader(&downloadManager); newRequestList.Append(req); requestMutex.Unlock(); diff --git a/src/vdr-plugin/download.h b/src/vdr-plugin/download.h index 14a5c66..f10a4b9 100644 --- a/src/vdr-plugin/download.h +++ b/src/vdr-plugin/download.h @@ -19,6 +19,7 @@ class cWebviThread : public cThread { private: WebviCtx webvi; + cCurlMultiManager downloadManager; cMutex requestMutex; cRequestVector activeRequestList; cRequestVector newRequestList; diff --git a/src/vdr-plugin/filedownloader.c b/src/vdr-plugin/filedownloader.c new file mode 100644 index 0000000..5ae8ee1 --- /dev/null +++ b/src/vdr-plugin/filedownloader.c @@ -0,0 +1,147 @@ +/* + * filedownloader.c: Web video plugin for the Video Disk Recorder + * + * See the README file for copyright information and how to reach the author. + * + * $Id$ + */ + +#include <exception> + +// --- cCurlDownloadTask --------------------------------------------------- + +cCurlDownloadTask::cCurlDownloadTask(cCurlMultiManager *_manager, const cString& url) + : manager(_manager), curl(NULL), writeCallback(NULL), writeData(NULL), + finishCallback(NULL), finishData(NULL) +{ + if (!manager) + throw new invalid_argument("manager can not be null"); + + curl = curl_easy_init(); + if (!curl) + throw new runtime_error("failed to initialize curl"); + + curl_easy_setopt(curl, CURLOPT_URL, url); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, ReadWrapper); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, this); + curl_multi_add_handle(manager->CurlMultiHandle(), curl); +} + +cCurlDownloadTask::~cCurlDownloadTask() { + manager->RemoveTask(this); + curl_multi_remove_handle(manager->CurlMultiHandle(), curl); + curl_easy_cleanup(curl); +} + +size_t cCurlDownloadTask::ReadWrapper(char *ptr, size_t size, size_t nmemb, void *userdata) { + if (self->writeCallback) { + cCurlDownloadTask *self = (cCurlDownloadTask *)userdata; + return self->ExecuteReadCallback(ptr, size*nmemb); + } else { + // ignore data + return size*nmemb; + } +} + +size_t cCurlDownloadTask::ExecuteReadCallback(void *buf, size_t count) { + return (size_t)writeCallback(buf, count, writeData); +} + +void cCurlDownloadTask::SetReadCallback(ssize_t (*cb)(void *buf, size_t count, void *data), void *data) { + writeCallback = cb; + writeData = data; +} + +void cCurlDownloadTask::SetFinishedCallback(void (*cb)(void *data), void *data) { + finishCallback = cb; + finishData = data; +} + +void cCurlDownloadTask::MarkFinished(CURLcode result) { + + // FIXME: CURLcode + + if (finishCallback) { + finishCallback(finishData); + } + + curl_multi_remove_handle(manager->CurlMultiHandle(), curl); +} + +// --- cCurlMultiManager --------------------------------------------------- + +cCurlMultiManager::cCurlMultiManager() { + curlmulti = curl_multi_init(); + if (!curlmulti) + throw new runtime_error("curl initialization failed"); +} + +cCurlMultiManager::~cCurlMultiManager() { + std::list<cCurlDownloadTask *>::iterator it; + for (it=activeTasks.begin(); it!=activeTasks.end(); ++it) { + delete (*it); + } + + curl_multi_cleanup(curlmulti); +} + +iFileDownloadTask *cCurlMultiManager::CreateDownloadTask(const cString& url) { + cCurlDownloadTask *task = new cCurlDownloadTask(url); + activeTasks.push_back(task); + return task; +} + +void cCurlMultiManager::FDSet(fd_set *readfds, fd_set *writefds, + fd_set *excfds, int *maxfd) +{ + curl_multi_fdset(curlmulti, readfds, writefds, excfds, maxfd); +} + +void cCurlMultiManager::HandleSocket(int fd, int ev_bitmask, + long *running_handles) +{ + int curl_bitmask = 0; + if ((ev_bitmask & WEBVI_SELECT_READ) != 0) + curl_bitmask |= CURL_CSELECT_IN; + if ((ev_bitmask & WEBVI_SELECT_WRITE) != 0) + curl_bitmask |= CURL_CSELECT_OUT; + if ((ev_bitmask & WEBVI_SELECT_EXCEPTION) != 0) + curl_bitmask |= CURL_CSELECT_ERR; + + curl_multi_socket_action(curlmulti, fd, curl_bitmask, running_handles); +} + +void cCurlMultiManager::CheckForFinished() { + int remaining = 0; + CURLMsg *msg; + + while ((msg = curl_multi_info_read(curlmulti, &remaining))) { + if (msg->msg == CURLMSG_DONE) { + cCurlDownloadTask *task = FindByHandle(msg->easy_handle); + if (task) { + task->MarkFinished(msg->data.result); + } + } + } +} + +cCurlDownloadTask *cCurlMultiManager::FindByHandle(CURL *handle) { + std::list<cCurlDownloadTask *>::iterator it; + for (it=activeTasks.begin(); it!=activeTasks.end(); ++it) { + if ((*it)->Handle() == handle) + return *it; + } + + return NULL; +} + +struct EqualsComparator { + cCurlDownloadTask *mark; + bool operator() (cCurlDownloadTask *value) { return (value == mark); } +}; + +void cCurlMultiManager::RemoveTask(cCurlDownloadTask *task) { + EqualsComparator cmp; + cmp.mark = task; + activeTasks.remove_if(cmp()); +} diff --git a/src/vdr-plugin/filedownloader.h b/src/vdr-plugin/filedownloader.h new file mode 100644 index 0000000..5a65498 --- /dev/null +++ b/src/vdr-plugin/filedownloader.h @@ -0,0 +1,85 @@ +/* + * filedownloader.h: Web video plugin for the Video Disk Recorder + * + * See the README file for copyright information and how to reach the author. + * + * $Id$ + */ + +#include <sys/select.h> +#include <curl/curl.h> +#include <list> + +#ifndef __WEBVIDEO_FILEDOWNLOADER_H +#define __WEBVIDEO_FILEDOWNLOADER_H + +// --- iFileDownloadTask --------------------------------------------------- + +class iFileDownloadTask { +public: + virtual ~iFileDownloadTask() {}; + virtual void SetReadCallback(ssize_t (*cb)(void *buf, size_t count, void *data), void *data) = 0; + virtual void SetFinishedCallback(void (*cb)(void *data), void *data) = 0; +}; + +// --- iAsyncFileDownloaderManager ----------------------------------------- + +class iAsyncFileDownloaderManager { +public: + virtual ~iAsyncFileDownloaderManager() {}; + virtual iFileDownloadTask *CreateDownloadTask(const cString& url) = 0; + virtual void FDSet(fd_set *readfds, fd_set *writefds, fd_set *excfds, int *maxfd) = 0; + virtual void HandleSocket(int fd, int ev_bitmask, long *running_handles) = 0; + virtual void CheckForFinished() = 0; +}; + +// --- cCurlDownloadTask --------------------------------------------------- + +class cCurlMultiManager; + +class cCurlDownloadTask : public iFileDownloadTask { +private: + cCurlMultiManager *manager; + CURL *curl; + ssize_t (*writeCallback)(void *buf, size_t count, void *data); + void *writeData; + void (*finishCallback)(void *data); + void *finishData; + + static size_t ReadWrapper(char *ptr, size_t size, size_t nmemb, void *userdata); + size_t ExecuteReadCallback(void *buf, size_t count); + +public: + cCurlDownloadTask(cCurlMultiManager *manager, const cString& url); + virtual ~cCurlDownloadTask(); + + CURL *Handle() { return curl; } + void MarkFinished(CURLcode result); + virtual void SetReadCallback(ssize_t (*cb)(void *buf, size_t count, void *data), void *data) = 0; + virtual void SetFinishedCallback(void (*cb)(void *data), void *data) = 0; +}; + +// --- cCurlMultiManager --------------------------------------------------- + +class cCurlMultiManager : public iAsyncFileDownloaderManager { +private: + CURLM *curlmulti; + std::list<cCurlDownloadTask *> activeTasks; + + cCurlDownloadTask *FindByHandle(CURL *handle); + +public: + cCurlMultiManager(); + virtual ~cCurlMultiManager(); + + CURLM *CurlMultiHandle() { return curlmulti; } + + virtual iFileDownloadTask *CreateDownloadTask(const cString& url); + virtual void FDSet(fd_set *readfds, fd_set *writefds, fd_set *excfds, int *maxfd); + virtual void HandleSocket(int fd, int ev_bitmask, long *running_handles); + virtual void CheckForFinished(); + + void RemoveTask(cCurlDownloadTask *task); +}; + +#endif diff --git a/src/vdr-plugin/menu.c b/src/vdr-plugin/menu.c index d03c61d..a2bc8d0 100644 --- a/src/vdr-plugin/menu.c +++ b/src/vdr-plugin/menu.c @@ -8,6 +8,7 @@ #include <stdlib.h> #include <time.h> +#include <assert.h> #include <vdr/skins.h> #include <vdr/tools.h> #include <vdr/i18n.h> @@ -27,6 +28,567 @@ cCharSetConv csc = cCharSetConv("UTF-8", cCharSetConv::SystemCharacterTable()); struct MenuPointers menuPointers; +typedef enum { + INPUT_TYPE_TEXT, + INPUT_TYPE_RADIO, + INPUT_TYPE_SUBMIT, +} InputItemType; + +// --- cURITemplate ---------------------------------------------- + +class cURITemplate { +private: + static const char *GetValue(const char *key, const cStringList& keys, + const cStringList& values); +public: + static cString Substitute(const cString& uritemplate, const cStringList& keys, + const cStringList& values); +}; + +cString cURITemplate::Substitute(const cString& uritemplate, + const cStringList& keys, + const cStringList& values) +{ + if ((const char *)uritemplate == NULL) + return ""; + + cVector<char> substituted(2*strlen(uritemplate)); + const char *currentKey = NULL; + const char *c = uritemplate; + while (*c) { + if (currentKey) { + if (*c == '}') { + char *key = strndup(currentKey+1, c - (currentKey+1)); + const char *value = cURITemplate::GetValue(key, keys, values); + if (value) { + for (const char *v = value; *v; v++) { + if (*v == ' ') { + substituted.Append('+'); + } else { + substituted.Append(*v); + } + } + } else { + for (const char *v = currentKey; v <= c; v++) { + substituted.Append(*v); + } + } + currentKey = NULL; + free(key); + } + } else if (*c == '{') { + currentKey = c; + } else { + substituted.Append(*c); + } + + c++; + } + + if (currentKey) { + for (const char *v=currentKey; *v; v++) { + substituted.Append(*v); + } + } + + return cString(strdup(&substituted[0]), true); +} + +const char *cURITemplate::GetValue(const char *key, + const cStringList& keys, + const cStringList& values) +{ + int i = keys.Find(key); + if ((i >= 0) && (i < values.Size())) { + return values[i]; + } else { + return NULL; + } +} + +// --- cEditControl ---------------------------------------------- + +class cEditControl { +public: + virtual const char *Key() = 0; + virtual const char *Value() = 0; +}; + +/* // --- cFormControlWrapper --------------------------------------- */ + +/* class cFormControlWrapper { */ +/* public: */ +/* virtual const char *Key() = 0; */ +/* virtual const char *Value() = 0; */ +/* virtual cOsdItem *CreateOSDItem() = 0; */ +/* }; */ + +/* // --- cTextFieldWrapper ----------------------------------------- */ + +/* class cTextFieldWrapper : public cFormControlWrapper { */ +/* private: */ +/* char *value; */ +/* int valueLen; */ +/* char *key; */ +/* char *name; */ +/* char *allowed; */ +/* public: */ +/* cTextFieldWrapper(const char *Name, const char *Key, int MaxLength, const char *Allowed = NULL); */ +/* ~cTextFieldWrapper(); */ +/* virtual const char *Key() { return key; } */ +/* virtual const char *Value(); */ +/* virtual cOsdItem *CreateOSDItem(); */ +/* }; */ + +/* cTextFieldWrapper::cTextFieldWrapper(const char *Name, const char *Key, */ +/* int MaxLength, const char *Allowed = NULL) */ +/* { */ +/* valueLen = MaxLength; */ +/* value = malloc(valueLen + 1); */ +/* *value = '\0'; */ +/* key = strdup(Key ? Key : ""); */ +/* name = strdup(Name ? Name : ""); */ +/* allowed = Allowed ? strdup(Allowed) : NULL; */ +/* } */ + +/* cTextFieldWrapper::~cTextFieldWrapper() { */ +/* free(value); */ +/* free(key); */ +/* free(name); */ +/* if (allowed) */ +/* free(allowed); */ +/* } */ + +/* cMenuEditStrItem *cTextFieldWrapper::CreateOSDItem() { */ +/* return new cMenuEditStrItem(name, value, valueLen, allowed); */ +/* } */ + +/* const char *cTextFieldWrapper::Value() { */ +/* return value; */ +/* } */ + +/* // --- cSelectionWrapper ----------------------------------------- */ + +/* class cSelectionWrapper : public cFormControlWrapper { */ +/* private: */ +/* char *key; */ +/* char **labels; */ +/* int numLabels; */ +/* cStringList values; */ +/* int selectedIndex; */ +/* char *name; */ +/* public: */ +/* cSelectionWrapper(const char *Name, const char *Key, */ +/* const cStringList& Labels, const cStringList& Values); */ +/* ~cSelectionWrapper(); */ +/* virtual const char *Key() { return key; } */ +/* virtual const char *Value(); */ +/* virtual cOsdItem *CreateOSDItem(); */ +/* }; */ + +/* cSelectionWrapper::cSelectionWrapper(const char *Name, const char *Key, */ +/* const cStringList& LabelStrings, */ +/* const cStringList& ValueStrings) */ +/* { */ +/* name = strdup(Name ? Name : ""); */ +/* key = strdup(Key ? Key : ""); */ +/* selectedIndex = 0; */ +/* numLabels = LabelStrings.Size(), */ +/* labels = malloc(numLabels*sizeof(char *)); */ +/* for (int i=0; i<numLabels; i++) { */ +/* labels[i] = strdup(LabelStrings[i]); */ +/* } */ +/* for (int i=0; i<ValueStrings.Size(); i++) { */ +/* values.Append(ValueStrings[i]); */ +/* } */ +/* } */ + +/* cSelectionWrapper::~cSelectionWrapper() { */ +/* for (int i=0; i<numValues; i++) { */ +/* free(labels[i]); */ +/* } */ +/* free(labels); */ +/* free(key); */ +/* free(name); */ +/* } */ + +/* cOsdItem *cSelectionWrapper::CreateOSDItem() { */ +/* return new cMenuEditStraItem(name, &selectedIndex, numLabels, labels); */ +/* } */ + +/* const char *cSelectionWrapper::Value() { */ +/* if (selectedIndex < values.Size()) */ +/* return values[selectedIndex]; */ +/* else */ +/* return ""; */ +/* } */ + +/* // --- cMenuTextField -------------------------------------------- */ + +/* class cMenuTextField : public cMenuEditStrItem, public cEditControl { */ +/* private: */ +/* //char *textBuffer; */ +/* char *keyName; */ +/* public: */ +/* cMenuTextField(const char *Name, const char *Key, int MaxLength, const char *Allowed = NULL); */ +/* ~cMenuTextField(); */ + +/* const char *Key(); */ +/* const char *Value(); */ +/* }; */ + +/* cMenuTextField::cMenuTextField(const char *Name, const char *Key, int MaxLength, const char *Allowed) */ +/* : cMenuEditStrItem(Name, new uint[MaxLength], MaxLength, Allowed) */ +/* { */ +/* keyName = strdup(Key); */ +/* } */ + +/* cMenuTextField::~cMenuTextField() { */ +/* free(keyName); */ + +/* // FIXME: delete value */ +/* } */ + +/* const char *cMenuTextField::Key() { */ +/* return keyName; */ +/* } */ + +/* const char *cMenuTextField::Value() { */ +/* // FIXME */ +/* } */ + +// --- cOsdSubmitButton ------------------------------------------ + +class cOsdSubmitButton : public cOsdItem, public cMenuLink { +private: + cVector<cEditControl *> editControls; + cString substituted; + char *uriTemplate; + +public: + cOsdSubmitButton(const char *Text, eOSState State = osUnknown, bool Selectable = true); + + void SetURITemplate(const char *newTemplate); + void ClearEditControls(); + void AttachEditControl(cEditControl *control); + + const char *GetURL(); + bool HasStream(); +}; + +cOsdSubmitButton::cOsdSubmitButton(const char *Text, eOSState State, bool Selectable) +: cOsdItem(Text, State, Selectable), uriTemplate(NULL) +{ +} + +void cOsdSubmitButton::SetURITemplate(const char *newTemplate) { + if (uriTemplate) + free(uriTemplate); + + if (newTemplate) + uriTemplate = strdup(newTemplate); + else + uriTemplate = NULL; +} + +void cOsdSubmitButton::ClearEditControls() { + editControls.Clear(); +} + +void cOsdSubmitButton::AttachEditControl(cEditControl *control) { + editControls.Append(control); +} + +const char *cOsdSubmitButton::GetURL() { + if (uriTemplate) { + cStringList keys; + cStringList values; + for (int i=0; i<editControls.Size(); i++) { + keys.Append(strdup(editControls[i]->Key())); + values.Append(strdup(editControls[i]->Value())); + } + substituted = cURITemplate::Substitute(uriTemplate, keys, values); + } else { + substituted = ""; + } + + return substituted; +} + +bool cOsdSubmitButton::HasStream() { + return false; +} + +// --- cFormItem ------------------------------------------------- + +class cFormItem : public cListObject { +private: + InputItemType type; + char *name; + char *mainLabel; +protected: + cFormItem(InputItemType _type, const char *_name, const char *_mainLabel); +public: + virtual ~cFormItem(); + InputItemType GetType(); + const char *GetName(); + virtual const char *GetLabel(); + virtual void AppendValue(const char *value, const char *label); + virtual cOsdItem *CreateOsdItem() = 0; +}; + +cFormItem::cFormItem(InputItemType _type, const char *_name, const char *_mainLabel) { + type = _type; + name = strdup(_name ? _name : ""); + mainLabel = strdup(_mainLabel ? _mainLabel : ""); +} + +cFormItem::~cFormItem() { + if (name) + free(name); + if (mainLabel) + free(mainLabel); +} + +InputItemType cFormItem::GetType() { + return type; +} + +const char *cFormItem::GetName() { + return name; +} + +const char *cFormItem::GetLabel() { + return mainLabel; +} + +void cFormItem::AppendValue(const char *value, const char *label) { + // default implementation does nothing +} + +// --- cFormItemText --------------------------------------------- + +class cFormItemText : public cFormItem, public cEditControl { +private: + char *value; + int valueLen; + //char *key; // name? FIXME + char *allowed; +public: + cFormItemText(const char *_name, const char *_mainLabel, int MaxLength, const char *Allowed = NULL); + ~cFormItemText(); + const char *Key(); + const char *Value(); + cOsdItem *CreateOsdItem(); +}; + +cFormItemText::cFormItemText(const char *_name, const char *_mainLabel, int MaxLength, const char *Allowed) +: cFormItem(INPUT_TYPE_TEXT, _name, _mainLabel) +{ + valueLen = MaxLength; + value = (char *)malloc(valueLen + 1); + *value = '\0'; + //key = strdup(Key ? Key : ""); + allowed = Allowed ? strdup(Allowed) : NULL; +} + +cFormItemText::~cFormItemText() { + free(value); + //free(key); + if (allowed) + free(allowed); +} + +const char *cFormItemText::Key() { + return GetName(); +} + +const char *cFormItemText::Value() { + return value; +} + +cOsdItem *cFormItemText::CreateOsdItem() { + return new cMenuEditStrItem(GetName(), value, valueLen, allowed); +} + +// --- cFormItemRadio -------------------------------------------- + +class cFormItemRadio : public cFormItem, public cEditControl { +private: + cStringList values; + cStringList labels; + + //char *key; // name? FIXME + char **labelsArray; + int labelsArraySize; + int selectedIndex; + +public: + cFormItemRadio(const char *_name, const char *_mainLabel); + ~cFormItemRadio(); + const char *Key(); + const char *Value(); + void AppendValue(const char *value, const char *label); + cOsdItem *CreateOsdItem(); +}; + +cFormItemRadio::cFormItemRadio(const char *_name, const char *_mainLabel) +: cFormItem(INPUT_TYPE_RADIO, _name, _mainLabel) +{ + labelsArray = NULL; + labelsArraySize = 0; + selectedIndex = 0; +} + +cFormItemRadio::~cFormItemRadio() { + if (labelsArray) { + for (int i=0; i<labelsArraySize; i++) { + free(labelsArray[i]); + } + free(labelsArray); + } +} + +void cFormItemRadio::AppendValue(const char *value, const char *label) { + values.Append(strdup(value ? value : "")); + labels.Append(strdup(label ? label : "")); +} + +const char *cFormItemRadio::Key() { + return GetName(); +} + +const char *cFormItemRadio::Value() { + if (selectedIndex < values.Size()) + return values[selectedIndex]; + else + return ""; +} + +cOsdItem *cFormItemRadio::CreateOsdItem() { + if (!labelsArray) { + labelsArray = (char **)malloc(labels.Size()*sizeof(char *)); + for (int i=0; i<labels.Size(); i++) { + labelsArray[i] = strdup(labels[i]); + } + } + + return new cMenuEditStraItem(GetName(), &selectedIndex, + labelsArraySize, labelsArray); +} + +// --- cFormItemSubmit ------------------------------------------- + +class cFormItemSubmit : public cFormItem { +private: + char *value; +public: + cFormItemSubmit(const char *_name, const char *_mainLabel); + ~cFormItemSubmit(); + const char *GetLabel(); + void AppendValue(const char *value, const char *label); + cOsdItem *CreateOsdItem(); +}; + +cFormItemSubmit::cFormItemSubmit(const char *_name, const char *_mainLabel) +: cFormItem(INPUT_TYPE_SUBMIT, _name, _mainLabel) +{ + value = strdup(""); +} + +cFormItemSubmit::~cFormItemSubmit() { + if (value) + free(value); +} + +const char *cFormItemSubmit::GetLabel() { + return value; +} + +void cFormItemSubmit::AppendValue(const char *_value, const char *_label) { + if (value) + free(value); + value = strdup(_value ? _value : ""); +} + +cOsdItem *cFormItemSubmit::CreateOsdItem() { + return new cOsdSubmitButton(csc.Convert(GetLabel())); +} + +// --- cFormItemList --------------------------------------------- + +cFormItem *cFormItemList::FindByName(const char *name) { + cFormItem *item = inputItems.First(); + while (item) { + if (strcmp(item->GetName(), name) == 0) { + return item; + } + item = inputItems.Next(item); + } + + return NULL; +} + +void cFormItemList::AddInputItem(const char *name, const char *type, + const char *mainLabel, const char *value, + const char *valueLabel) +{ + if (!name || !type) + return; + + cFormItem *item = FindByName(name); + if (item) { + item->AppendValue(value, valueLabel); + } else { + cFormItem *item = FormItemFactory(type, name, mainLabel); + item->AppendValue(value, valueLabel); + inputItems.Add(item); + } +} + +cFormItem *cFormItemList::FormItemFactory(const char *type, + const char *name, + const char *mainLabel) { + if (strcmp(type, "radio") == 0) { + return new cFormItemRadio(name, mainLabel); + } else if (strcmp(type, "submit") == 0) { + return new cFormItemSubmit(name, mainLabel); + } else { + if (strcmp(type, "text") != 0) + warning("Unexpected <input> type %s", type); + return new cFormItemText(name, mainLabel, 255); + } +} + +void cFormItemList::CreateAndAppendOsdItems(cList<cOsdItem> *destination, + const char *uriTemplate) { + cVector<cEditControl *> editControls; + cVector<cOsdSubmitButton *> submitButtons; + cFormItem *inputItem; + for (inputItem=inputItems.First(); inputItem; inputItems.Next(inputItem)) { + cOsdItem *osdItem = inputItem->CreateOsdItem(); + if (inputItem->GetType() == INPUT_TYPE_SUBMIT) { + assert(dynamic_cast<cOsdSubmitButton *>(osdItem)); + submitButtons.Append(static_cast<cOsdSubmitButton *>(osdItem)); + } else { + cEditControl *edit = dynamic_cast<cEditControl *>(inputItem); + assert(edit); + editControls.Append(edit); + } + + destination->Add(osdItem); + } + + for (int i=0; i<submitButtons.Size(); i++) { + cOsdSubmitButton *submitButton = submitButtons[i]; + submitButton->SetURITemplate(uriTemplate); + submitButton->ClearEditControls(); + for (int j=0; j<editControls.Size(); j++) { + submitButton->AttachEditControl(editControls[j]); + } + } +} + // --- cXMLMenu -------------------------------------------------- cXMLMenu::cXMLMenu(const char *Title, int c0, int c1, int c2, @@ -52,7 +614,7 @@ bool cXMLMenu::Deserialize(const char *xml) { while (node) { if (node->type == XML_ELEMENT_NODE) { - if (!CreateItemFromTag(doc, node)) { + if (!ParseRootChild(doc, node)) { warning("Failed to parse menu tag: %s", (char *)node->name); } } @@ -73,14 +635,11 @@ int cXMLMenu::Load(const char *xmlstr) { // --- cNavigationMenu ----------------------------------------------------- -cNavigationMenu::cNavigationMenu(cHistory *History, +cNavigationMenu::cNavigationMenu(cHistory *_history, cProgressVector& dlsummaries) - : cXMLMenu("", 25), summaries(dlsummaries) + : cXMLMenu("", 25), summaries(dlsummaries), + title(NULL), reference(NULL), shortcutMode(0), history(_history) { - title = NULL; - reference = NULL; - shortcutMode = 0; - history = History; UpdateHelp(); } @@ -91,79 +650,73 @@ cNavigationMenu::~cNavigationMenu() { free(reference); } -bool cNavigationMenu::CreateItemFromTag(xmlDocPtr doc, xmlNodePtr node) { - if (!xmlStrcmp(node->name, BAD_CAST "link")) { - NewLinkItem(doc, node); - return true; - } else if (!xmlStrcmp(node->name, BAD_CAST "textfield")) { - NewTextField(doc, node); - return true; - } else if (!xmlStrcmp(node->name, BAD_CAST "itemlist")) { - NewItemList(doc, node); - return true; - } else if (!xmlStrcmp(node->name, BAD_CAST "textarea")) { - NewTextArea(doc, node); - return true; - } else if (!xmlStrcmp(node->name, BAD_CAST "button")) { - NewButton(doc, node); - return true; +bool cNavigationMenu::ParseRootChild(xmlDocPtr doc, xmlNodePtr node) { + if (!xmlStrcmp(node->name, BAD_CAST "ul")) { + ParseUL(doc, node); + } else if (!xmlStrcmp(node->name, BAD_CAST "form")) { + ParseForm(doc, node); } else if (!xmlStrcmp(node->name, BAD_CAST "title")) { NewTitle(doc, node); - return true; + } else { + return false; } - return false; + return true; } -void cNavigationMenu::AddLinkItem(cOsdItem *item, - cLinkBase *ref, - cLinkBase *streamref) { - Add(item); +void cNavigationMenu::ParseUL(xmlDocPtr doc, xmlNodePtr node) { + xmlNodePtr child = node->children; + while (child) { + if (xmlStrEqual(child->name, BAD_CAST "il")) { + CreateLinkElement(doc, child); + } + child = child->next; + } +} - if (ref) - links.Append(ref); - else - links.Append(NULL); +void cNavigationMenu::CreateLinkElement(xmlDocPtr doc, xmlNodePtr node) { + xmlNodePtr child = node->children; - if (streamref) - streams.Append(streamref); - else - streams.Append(NULL); -} + while (child) { + if (xmlStrEqual(child->name, BAD_CAST "a")) { + xmlChar *href = xmlGetProp(child, BAD_CAST "href"); + if (href) { + xmlChar *title = xmlNodeListGetString(doc, child->xmlChildrenNode, 1); + if (!title) { + title = xmlCharStrdup("???"); + } + xmlChar *cls = xmlGetProp(child, BAD_CAST "class"); + bool isStream = !cls || !xmlStrEqual(cls, BAD_CAST "webvi"); -void cNavigationMenu::NewLinkItem(xmlDocPtr doc, xmlNodePtr node) { - // label, ref and object tags - xmlChar *itemtitle = NULL, *ref = NULL, *streamref = NULL; + CreateAndAddOSDLink((char *)title, (char *)href, isStream); - node = node->xmlChildrenNode; - while (node) { - if (!xmlStrcmp(node->name, BAD_CAST "label")) { - if (itemtitle) - xmlFree(itemtitle); - itemtitle = xmlNodeListGetString(doc, node->xmlChildrenNode, 1); - } else if (!xmlStrcmp(node->name, BAD_CAST "ref")) { - if (ref) - xmlFree(ref); - ref = xmlNodeListGetString(doc, node->xmlChildrenNode, 1); - } else if (!xmlStrcmp(node->name, BAD_CAST "stream")) { - if (streamref) - xmlFree(streamref); - streamref = xmlNodeListGetString(doc, node->xmlChildrenNode, 1); + if (cls) + xmlFree(cls); + xmlFree(title); + xmlFree(href); + } + + break; } - node = node->next; + + child = child->next; } - if (!itemtitle) - itemtitle = xmlCharStrdup("???"); +} - const char *titleconv = csc.Convert((char *)itemtitle); +void cNavigationMenu::CreateAndAddOSDLink(const char *title, const char *href, + bool isStream) { + char *strippedTitle = compactspace(strdup(title)); + const char *titleconv = csc.Convert(strippedTitle); + free(strippedTitle); + strippedTitle = NULL; cOsdItem *item = new cOsdItem(titleconv); cSimpleLink *objlinkdata = NULL; cSimpleLink *linkdata = NULL; - if (ref) - linkdata = new cSimpleLink((char *)ref); - if (streamref) { - // media object - objlinkdata = new cSimpleLink((char *)streamref); + if (href) + linkdata = new cSimpleLink(href); + if (isStream) { + // stream link + objlinkdata = new cSimpleLink(href); } else { // navigation link char *bracketed = (char *)malloc((strlen(titleconv)+3)*sizeof(char)); @@ -176,167 +729,85 @@ void cNavigationMenu::NewLinkItem(xmlDocPtr doc, xmlNodePtr node) { } } AddLinkItem(item, linkdata, objlinkdata); - - xmlFree(itemtitle); - if (ref) - xmlFree(ref); - if (streamref) - xmlFree(streamref); -} - -void cNavigationMenu::NewTextField(xmlDocPtr doc, xmlNodePtr node) { - // name attribute - xmlChar *name = xmlGetProp(node, BAD_CAST "name"); - cHistoryObject *curhistpage = history->Current(); - - // label tag - xmlChar *text = NULL; - node = node->xmlChildrenNode; - while (node) { - if (!xmlStrcmp(node->name, BAD_CAST "label")) { - if (text) - xmlFree(text); - text = xmlNodeListGetString(doc, node->xmlChildrenNode, 1); - } - node = node->next; - } - if (!text) - text = xmlCharStrdup("???"); - - cTextFieldData *data = curhistpage->GetTextFieldData((char *)name); - cMenuEditStrItem *item = new cMenuEditStrItem(csc.Convert((char *)text), - data->GetValue(), - data->GetLength()); - AddLinkItem(item, NULL, NULL); - - free(text); - if (name) - xmlFree(name); } -void cNavigationMenu::NewItemList(xmlDocPtr doc, xmlNodePtr node) { - // name attribute - xmlChar *name = xmlGetProp(node, BAD_CAST "name"); - cHistoryObject *curhistpage = history->Current(); - - // label and item tags - xmlChar *text = NULL; - cStringList items; - cStringList itemvalues; - node = node->xmlChildrenNode; - while (node) { - if (!xmlStrcmp(node->name, BAD_CAST "label")) { - if (text) - xmlFree(text); - text = xmlNodeListGetString(doc, node->xmlChildrenNode, 1); - } else if (!xmlStrcmp(node->name, BAD_CAST "item")) { - xmlChar *str = xmlNodeListGetString(doc, node->xmlChildrenNode, 1); - if (!str) - str = xmlCharStrdup("???"); - xmlChar *strvalue = xmlGetProp(node, BAD_CAST "value"); - if (!strvalue) - strvalue = xmlCharStrdup(""); - - items.Append(strdup((char *)str)); - itemvalues.Append(strdup((char *)strvalue)); - - xmlFree(str); - xmlFree(strvalue); - } - node = node->next; - } - if (!text) - text = xmlCharStrdup("???"); - - cItemListData *data = curhistpage->GetItemListData((const char *)name, - items, - itemvalues); +void cNavigationMenu::AddLinkItem(cOsdItem *item, + cLinkBase *ref, + cLinkBase *streamref) { + Add(item); - cMenuEditStraItem *item = new cMenuEditStraItem(csc.Convert((char *)text), - data->GetValuePtr(), - data->GetNumStrings(), - data->GetStrings()); - AddLinkItem(item, NULL, NULL); + if (ref) + links.Append(ref); + else + links.Append(NULL); - xmlFree(text); - if (name) - xmlFree(name); + if (streamref) + streams.Append(streamref); + else + streams.Append(NULL); } -void cNavigationMenu::NewTextArea(xmlDocPtr doc, xmlNodePtr node) { - // label tag - xmlChar *itemtitle = NULL; - node = node->xmlChildrenNode; - while (node) { - if (!xmlStrcmp(node->name, BAD_CAST "label")) { - if (itemtitle) - xmlFree(itemtitle); - itemtitle = xmlNodeListGetString(doc, node->xmlChildrenNode, 1); +void cNavigationMenu::ParseForm(xmlDocPtr doc, xmlNodePtr node) { + xmlChar *urltemplate = xmlGetProp(node, BAD_CAST "action"); + xmlNodePtr child = node->children; + while (child) { + if (xmlStrEqual(child->name, BAD_CAST "li")) { + ParseFormItem(formItems, doc, child); } - node = node->next; + child = child->next; } - if (!itemtitle) - return; - const cFont *font = cFont::GetFont(fontOsd); - cTextWrapper tw(csc.Convert((char *)itemtitle), font, cOsd::OsdWidth()); - for (int i=0; i < tw.Lines(); i++) { - AddLinkItem(new cOsdItem(tw.GetLine(i), osUnknown, false), NULL, NULL); - } + formItems.CreateAndAppendOsdItems(this, (const char *)urltemplate); - xmlFree(itemtitle); + xmlFree(urltemplate); } -void cNavigationMenu::NewButton(xmlDocPtr doc, xmlNodePtr node) { - // label and submission tags - xmlChar *itemtitle = NULL, *submission = NULL; - cHistoryObject *curhistpage = history->Current(); - xmlChar *encoding = NULL; - - node = node->xmlChildrenNode; - while (node) { - if (!xmlStrcmp(node->name, BAD_CAST "label")) { - if (itemtitle) - xmlFree(itemtitle); - itemtitle = xmlNodeListGetString(doc, node->xmlChildrenNode, 1); - } else if (!xmlStrcmp(node->name, BAD_CAST "submission")) { - if (submission) - xmlFree(submission); - submission = xmlNodeListGetString(doc, node->xmlChildrenNode, 1); - - xmlChar *enc = xmlGetProp(node, BAD_CAST "encoding"); - if (enc) { - if (encoding) - xmlFree(encoding); - encoding = enc; +void cNavigationMenu::ParseFormItem(cFormItemList& formItems, xmlDocPtr doc, + xmlNodePtr node) { + xmlChar *mainLabel = xmlNodeListGetString(doc, node->xmlChildrenNode, 1); + if (!mainLabel) + mainLabel = xmlCharStrdup("???"); + xmlNodePtr child = node->children; + while (child) { + if (xmlStrEqual(child->name, BAD_CAST "input")) { + xmlChar *type = xmlGetProp(child, BAD_CAST "type"); + xmlChar *name = xmlGetProp(child, BAD_CAST "name"); + xmlChar *value = xmlGetProp(child, BAD_CAST "value"); + formItems.AddInputItem((const char *)name, (const char *)type, + (const char*)mainLabel, (const char*)value, NULL); + if (value) + xmlFree(value); + if (name) + xmlFree(name); + if (type) + xmlFree(type); + + } else if (xmlStrEqual(child->name, BAD_CAST "label")) { + xmlChar *itemLabel = xmlNodeListGetString(doc, child->xmlChildrenNode, 1); + xmlNodePtr inputNode = child->children; + while (inputNode) { + if (xmlStrEqual(inputNode->name, BAD_CAST "input")) { + xmlChar *type = xmlGetProp(inputNode, BAD_CAST "type"); + xmlChar *name = xmlGetProp(inputNode, BAD_CAST "name"); + xmlChar *value = xmlGetProp(inputNode, BAD_CAST "value"); + formItems.AddInputItem((const char*)name, (const char*)type, + (const char *)mainLabel, (const char*)value, + (const char*)itemLabel); + if (value) + xmlFree(value); + if (name) + xmlFree(name); + if (type) + xmlFree(type); + } + inputNode = inputNode->next; } + if (itemLabel) + xmlFree(itemLabel); } - node = node->next; + child = child->next; } - if (!itemtitle) - itemtitle = xmlCharStrdup("???"); - - cSubmissionButtonData *data = \ - new cSubmissionButtonData((char *)submission, curhistpage, - (char *)encoding); - const char *titleconv = csc.Convert((char *)itemtitle); // do not free - char *newtitle = (char *)malloc((strlen(titleconv)+3)*sizeof(char)); - if (newtitle) { - newtitle[0] = '\0'; - strcat(newtitle, "["); - strcat(newtitle, titleconv); - strcat(newtitle, "]"); - - cOsdItem *item = new cOsdItem(newtitle); - AddLinkItem(item, data, NULL); - free(newtitle); - } - - xmlFree(itemtitle); - if (submission) - xmlFree(submission); - if (encoding) - xmlFree(encoding); + xmlFree(mainLabel); } void cNavigationMenu::NewTitle(xmlDocPtr doc, xmlNodePtr node) { @@ -347,6 +818,7 @@ void cNavigationMenu::NewTitle(xmlDocPtr doc, xmlNodePtr node) { if (title) free(title); title = strdup(conv); + title = compactspace(title); xmlFree(newtitle); } } @@ -465,8 +937,7 @@ eOSState cNavigationMenu::Select(cLinkBase *link, eLinkType type) if (type == LT_MEDIA) { cDownloadProgress *progress = summaries.NewDownload(); cFileDownloadRequest *req = \ - new cFileDownloadRequest(history->Current()->GetID(), ref, - progress); + new cFileDownloadRequest(history->Current()->GetID(), ref, progress); cWebviThread::Instance().AddRequest(req); Skins.Message(mtInfo, tr("Downloading in the background")); @@ -477,7 +948,7 @@ eOSState cNavigationMenu::Select(cLinkBase *link, eLinkType type) return osEnd; } else { cWebviThread::Instance().AddRequest(new cMenuRequest(history->Current()->GetID(), - ref)); + REQT_MENU, ref)); Skins.Message(mtStatus, tr("Retrieving...")); } diff --git a/src/vdr-plugin/menu.h b/src/vdr-plugin/menu.h index b1e67df..7bfa763 100644 --- a/src/vdr-plugin/menu.h +++ b/src/vdr-plugin/menu.h @@ -19,12 +19,26 @@ extern cCharSetConv csc; +// --- cFormItemList --------------------------------------------- + +class cFormItem; +class cFormItemList { +private: + cList<cFormItem> inputItems; + cFormItem *FindByName(const char *name); + cFormItem *FormItemFactory(const char *type, const char *name, const char *mainLabel); +public: + void AddInputItem(const char *name, const char *type, const char *mainLabel, + const char *value, const char *valueLabel); + void CreateAndAppendOsdItems(cList<cOsdItem> *destination, const char *uriTemplate); +}; + // --- cXMLMenu -------------------------------------------------- class cXMLMenu : public cOsdMenu { protected: virtual bool Deserialize(const char *xml); - virtual bool CreateItemFromTag(xmlDocPtr doc, xmlNodePtr node) = 0; + virtual bool ParseRootChild(xmlDocPtr doc, xmlNodePtr node) = 0; public: cXMLMenu(const char *Title, int c0 = 0, int c1 = 0, int c2 = 0, int c3 = 0, int c4 = 0); @@ -46,7 +60,9 @@ private: cVector<cLinkBase *> links; // streams[i] is the media stream link of the i:th item cVector<cLinkBase *> streams; + cFormItemList formItems; cProgressVector& summaries; + iAsyncFileDownloaderManager *dlmanager; char *title; char *reference; int shortcutMode; @@ -54,13 +70,13 @@ private: protected: cHistory *history; - virtual bool CreateItemFromTag(xmlDocPtr doc, xmlNodePtr node); + virtual bool ParseRootChild(xmlDocPtr doc, xmlNodePtr node); + void ParseForm(xmlDocPtr doc, xmlNodePtr node); + void ParseFormItem(cFormItemList& formItems, xmlDocPtr doc, xmlNodePtr node); + void ParseUL(xmlDocPtr doc, xmlNodePtr node); + void CreateLinkElement(xmlDocPtr doc, xmlNodePtr node); + void CreateAndAddOSDLink(const char *title, const char *href, bool isStream); void AddLinkItem(cOsdItem *item, cLinkBase *ref, cLinkBase *streamref); - void NewLinkItem(xmlDocPtr doc, xmlNodePtr node); - void NewTextField(xmlDocPtr doc, xmlNodePtr node); - void NewItemList(xmlDocPtr doc, xmlNodePtr node); - void NewTextArea(xmlDocPtr doc, xmlNodePtr node); - void NewButton(xmlDocPtr doc, xmlNodePtr node); void NewTitle(xmlDocPtr doc, xmlNodePtr node); void UpdateHelp(); @@ -78,6 +94,14 @@ public: void Populate(const cHistoryObject *page, const char *statusmsg=NULL); }; +// --- cMenuLink ------------------------------------------------- + +class cMenuLink { +public: + virtual const char *GetURL() = 0; + virtual bool HasStream() = 0; +}; + // --- cStatusScreen ------------------------------------------------------- class cStatusScreen : public cOsdMenu { diff --git a/src/vdr-plugin/request.c b/src/vdr-plugin/request.c index 0d1abf1..211f67e 100644 --- a/src/vdr-plugin/request.c +++ b/src/vdr-plugin/request.c @@ -13,6 +13,9 @@ #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> +#include <assert.h> +#include <libxml/tree.h> +#include <libxml/parser.h> #include <vdr/tools.h> #include <vdr/i18n.h> #include "request.h" @@ -101,20 +104,41 @@ cDownloadProgress *cProgressVector::NewDownload() { return progress; } +// --- cRequest ------------------------------------------------------------ + +cRequest::cRequest(int _ID, eRequestType _type, const char *_href) +: reqID(_ID), type(_type), href(strdup(_href)), aborted(false), finished(false) +{ +} + +cRequest::~cRequest() { + free(href); +} + +void cRequest::Abort() { + if (aborted || finished) + return; + + aborted = true; +}; + +void cRequest::RequestDone(int errorcode, cString pharse) { + debug("RequestDone %d %s", errorcode, (const char *)pharse); + finished = true; +} + // --- cMenuRequest -------------------------------------------------------- -cMenuRequest::cMenuRequest(int ID, const char *wvtreference) -: reqID(ID), aborted(false), finished(false), status(0), webvi(-1), - handle(-1), timer(NULL) +cMenuRequest::cMenuRequest(int ID, eRequestType type, const char *wvtreference) +: cRequest(ID, type, wvtreference), status(0), webvi(-1), handle(-1), timer(NULL) { - wvtref = strdup(wvtreference); } cMenuRequest::~cMenuRequest() { if (handle != -1) { - if (!finished) + if (!IsFinished()) Abort(); - webvi_delete_handle(webvi, handle); + webvi_delete_request(webvi, handle); } // do not delete timer @@ -145,36 +169,36 @@ char *cMenuRequest::ExtractSiteName(const char *ref) { } void cMenuRequest::AppendQualityParamsToRef() { - if (!wvtref) + if (!href) return; - char *site = ExtractSiteName(wvtref); + char *site = ExtractSiteName(href); if (site) { const char *min = webvideoConfig->GetMinQuality(site, GetType()); const char *max = webvideoConfig->GetMaxQuality(site, GetType()); free(site); if (min && !max) { - cString newref = cString::sprintf("%s&minquality=%s", wvtref, min); - free(wvtref); - wvtref = strdup((const char *)newref); + cString newref = cString::sprintf("%s&minquality=%s", href, min); + free(href); + href = strdup((const char *)newref); } else if (!min && max) { - cString newref = cString::sprintf("%s&maxquality=%s", wvtref, max); - free(wvtref); - wvtref = strdup((const char *)newref); + cString newref = cString::sprintf("%s&maxquality=%s", href, max); + free(href); + href = strdup((const char *)newref); } else if (min && max) { - cString newref = cString::sprintf("%s&minquality=%s&maxquality=%s", wvtref, min, max); - free(wvtref); - wvtref = strdup((const char *)newref); + cString newref = cString::sprintf("%s&minquality=%s&maxquality=%s", href, min, max); + free(href); + href = strdup((const char *)newref); } } } WebviHandle cMenuRequest::PrepareHandle() { if (handle == -1) { - handle = webvi_new_request(webvi, wvtref, WEBVIREQ_MENU); + handle = webvi_new_request(webvi, href); if (handle != -1) { webvi_set_opt(webvi, handle, WEBVIOPT_WRITEFUNC, WriteCallback); @@ -189,7 +213,8 @@ bool cMenuRequest::Start(WebviCtx webvictx) { debug("starting request %d", reqID); webvi = webvictx; - if ((PrepareHandle() != -1) && (webvi_start_handle(webvi, handle) == WEBVIERR_OK)) { + if ((PrepareHandle() != -1) && + (webvi_start_request(webvi, handle) == WEBVIERR_OK)) { finished = false; return true; } else @@ -197,26 +222,24 @@ bool cMenuRequest::Start(WebviCtx webvictx) { } void cMenuRequest::RequestDone(int errorcode, cString pharse) { - debug("RequestDone %d %s", errorcode, (const char *)pharse); - - finished = true; + cRequest::RequestDone(errorcode, pharse); status = errorcode; statusPharse = pharse; } void cMenuRequest::Abort() { - if (aborted || finished || handle == -1) + if (IsAborted() || IsFinished() || handle == -1) return; aborted = true; - webvi_stop_handle(webvi, handle); + webvi_stop_request(webvi, handle); }; -bool cMenuRequest::Success() { +bool cMenuRequest::Success() const { return status == 0; } -cString cMenuRequest::GetStatusPharse() { +cString cMenuRequest::GetStatusPharse() const { return statusPharse; } @@ -233,14 +256,15 @@ cString cMenuRequest::GetResponse() { cFileDownloadRequest::cFileDownloadRequest(int ID, const char *streamref, cDownloadProgress *progress) -: cMenuRequest(ID, streamref), title(NULL), bytesDownloaded(0), - contentLength(-1), destfile(NULL), destfilename(NULL), - progressUpdater(progress), state(STATE_WEBVI) +: cMenuRequest(ID, REQT_FILE, streamref), title(NULL), bytesDownloaded(0), + contentLength(-1), streamSocket(1), destfile(NULL), destfilename(NULL), + progressUpdater(progress), state(STATE_GET_STREAM_URL), + downloadManager(NULL), streamDownloader(NULL) { if (progressUpdater) progressUpdater->AssociateWith(this); - AppendQualityParamsToRef(); + //AppendQualityParamsToRef(); } cFileDownloadRequest::~cFileDownloadRequest() { @@ -248,16 +272,26 @@ cFileDownloadRequest::~cFileDownloadRequest() { destfile->Close(); delete destfile; } - if (destfilename) + if (destfilename) { free(destfilename); - if (title) + } + if (streamDownloader) { + delete streamDownloader; + streamDownloader = NULL; + } + if (title) { free(title); + } // do not delete progressUpdater } +void cFileDownloadRequest::SetDownloader(iAsyncFileDownloaderManager *dlmanager) { + downloadManager = dlmanager; +} + WebviHandle cFileDownloadRequest::PrepareHandle() { if (handle == -1) { - handle = webvi_new_request(webvi, wvtref, WEBVIREQ_FILE); + handle = webvi_new_request(webvi, href); if (handle != -1) { webvi_set_opt(webvi, handle, WEBVIOPT_WRITEFUNC, WriteCallback); @@ -268,19 +302,6 @@ WebviHandle cFileDownloadRequest::PrepareHandle() { return handle; } -ssize_t cFileDownloadRequest::WriteData(const char *ptr, size_t len) { - if (!destfile) { - if (!OpenDestFile()) - return -1; - } - - bytesDownloaded += len; - if (progressUpdater) - progressUpdater->Progress(bytesDownloaded); - - return destfile->Write(ptr, len); -} - bool cFileDownloadRequest::OpenDestFile() { char *contentType; char *url; @@ -394,17 +415,23 @@ char *cFileDownloadRequest::GetExtension(const char *contentType, const char *ur } void cFileDownloadRequest::RequestDone(int errorcode, cString pharse) { - if (state == STATE_WEBVI) { - if (destfile) - destfile->Close(); + if (errorcode == REQERR_OK) { + if (state == STATE_GET_STREAM_URL) { + parseStreamMetadataFromXml(inBuffer.Get(), inBuffer.Length(), streamUrl, streamTitle); + + StartStreamDownload(); + + } else if (state == STATE_STREAM_DOWNLOAD) { + if (destfile) + destfile->Close(); - if (errorcode == 0) StartPostProcessing(); - else - state = STATE_FINISHED; - } else if (state == STATE_POSTPROCESS) { - postProcessPipe.Close(); + } else if (state == STATE_POSTPROCESS) { + postProcessPipe.Close(); + state = STATE_FINISHED; + } + } else { state = STATE_FINISHED; } @@ -416,12 +443,67 @@ void cFileDownloadRequest::RequestDone(int errorcode, cString pharse) { } void cFileDownloadRequest::Abort() { - if (state == STATE_POSTPROCESS) + if (state == STATE_STREAM_DOWNLOAD) { + if (streamDownloader) { + delete streamDownloader; + streamDownloader = NULL; + } + } else if (state == STATE_POSTPROCESS) { postProcessPipe.Close(); + } cMenuRequest::Abort(); } +void cFileDownloadRequest::StartStreamDownload() { + state = STATE_STREAM_DOWNLOAD; + + if (IsRTMPStream(streamUrl)) { + RequestDone(REQERR_INTERNAL, "FIXME: downloading RTMP stream"); + } + + assert(!streamDownloader); + if (downloadManager) { + streamDownloader = downloadManager->CreateDownloadTask(streamUrl); + streamDownloader->SetReadCallback(StreamReadWrapper, this); + streamDownloader->SetFinishedCallback(StreamFinishedWrapper, this); + } else { + RequestDone(REQERR_INTERNAL, "No downloadManager"); + } +} + +bool cFileDownloadRequest::IsRTMPStream(const char *url) { + return (strncmp(url, "rtmp://", 7) == 0) || + (strncmp(url, "rtmpe://", 8) == 0) || + (strncmp(url, "rtmpt://", 8) == 0) || + (strncmp(url, "rtmps://", 8) == 0) || + (strncmp(url, "rtmpte://", 9) == 0) || + (strncmp(url, "rtmpts://", 9) == 0); +} + +ssize_t cFileDownloadRequest::StreamReadWrapper(void *buf, size_t len, void *data) { + cFileDownloadRequest *self = (cFileDownloadRequest *)data; + return self->WriteToDestFile(buf, len); +} + +ssize_t cFileDownloadRequest::WriteToDestFile(void *buf, size_t len) { + if (!destfile) { + if (!OpenDestFile()) + return -1; + } + + bytesDownloaded += len; + if (progressUpdater) + progressUpdater->Progress(bytesDownloaded); + + return destfile->Write(buf, len); +} + +void cFileDownloadRequest::StreamFinishedWrapper(void *data) { + cFileDownloadRequest *self = (cFileDownloadRequest *)data; + self->RequestDone(REQERR_OK, ""); +} + void cFileDownloadRequest::StartPostProcessing() { state = STATE_POSTPROCESS; @@ -448,13 +530,19 @@ void cFileDownloadRequest::StartPostProcessing() { fcntl(fileno(postProcessPipe), F_SETFL, flags); } -int cFileDownloadRequest::File() { - FILE *f = postProcessPipe; +int cFileDownloadRequest::ReadFile() { + if (state == STATE_STREAM_DOWNLOAD) { + return streamSocket; + } else if (state == STATE_POSTPROCESS) { + FILE *f = postProcessPipe; + + if (f) + return fileno(f); + else + return -1; + } - if (f) - return fileno(f); - else - return -1; + return -1; } bool cFileDownloadRequest::Read() { @@ -476,9 +564,9 @@ bool cFileDownloadRequest::Read() { info("post-processing of %s finished", destfilename); if (IsAborted()) - RequestDone(-2, "Aborted"); + RequestDone(REQERR_ABORT, "Aborted"); else - RequestDone(0, ""); + RequestDone(REQERR_OK, ""); return true; } else { @@ -495,13 +583,13 @@ bool cFileDownloadRequest::Read() { // --- cStreamUrlRequest --------------------------------------------------- cStreamUrlRequest::cStreamUrlRequest(int ID, const char *ref) -: cMenuRequest(ID, ref) { - AppendQualityParamsToRef(); +: cMenuRequest(ID, REQT_STREAM, ref) { + //AppendQualityParamsToRef(); } WebviHandle cStreamUrlRequest::PrepareHandle() { if (handle == -1) { - handle = webvi_new_request(webvi, wvtref, WEBVIREQ_STREAMURL); + handle = webvi_new_request(webvi, href); if (handle != -1) { webvi_set_opt(webvi, handle, WEBVIOPT_WRITEFUNC, WriteCallback); @@ -512,10 +600,74 @@ WebviHandle cStreamUrlRequest::PrepareHandle() { return handle; } +void cStreamUrlRequest::RequestDone(int errorcode, cString pharse) { + if (errorcode == 0) { + parseStreamMetadataFromXml(inBuffer.Get(), inBuffer.Length(), streamUrl, streamTitle); + } + cMenuRequest::RequestDone(errorcode, pharse); +} + +void parseStreamMetadataFromXml(const char *xml, size_t length, cString& outUrl, cString& outTitle) { + outUrl = ""; + outTitle = ""; + + xmlDocPtr doc = xmlReadMemory(xml, length, "menu.xml", NULL, 0); + if (doc == NULL) { + return; + } + + xmlNodePtr root = xmlDocGetRootElement(doc); + if (root && xmlStrEqual(root->name, BAD_CAST "wvmenu")) { + xmlNodePtr node = root->children; + while (node) { + if (xmlStrEqual(node->name, BAD_CAST "ul")) { + xmlNodePtr linode = node->children; + while (linode) { + xmlNodePtr anode = linode->children; + while (anode) { + if (xmlStrEqual(anode->name, BAD_CAST "a")) { + xmlChar *xmlTitle = xmlNodeGetContent(anode); + if (xmlTitle) { + outTitle = cString((const char*)xmlTitle); + xmlFree(xmlTitle); + } + + xmlChar *xmlHref = xmlGetNoNsProp(anode, BAD_CAST "href"); + if (xmlHref) { + outUrl = cString((const char *)xmlHref); + xmlFree(xmlHref); + } + + xmlFreeDoc(doc); + return; + } + + anode = anode->next; + } + + linode = linode->next; + } + } + + node = node->next; + } + } + + xmlFreeDoc(doc); +} + +cString cStreamUrlRequest::getStreamUrl() { + return streamUrl; +} + +cString cStreamUrlRequest::getStreamTitle() { + return streamTitle; +} + // --- cTimerRequest ------------------------------------------------------- cTimerRequest::cTimerRequest(int ID, const char *ref) -: cMenuRequest(ID, ref) +: cMenuRequest(ID, REQT_TIMER, ref) { } diff --git a/src/vdr-plugin/request.h b/src/vdr-plugin/request.h index 4a37741..0ef3be6 100644 --- a/src/vdr-plugin/request.h +++ b/src/vdr-plugin/request.h @@ -13,12 +13,19 @@ #include <vdr/thread.h> #include <libwebvi.h> #include "buffer.h" +#include "filedownloader.h" enum eRequestType { REQT_NONE, REQT_MENU, REQT_FILE, REQT_STREAM, REQT_TIMER }; +#define REQERR_OK 0 +#define REQERR_INTERNAL -1 +#define REQERR_ABORT -2 + class cFileDownloadRequest; class cWebviTimer; +void parseStreamMetadataFromXml(const char *xml, size_t length, cString& outUrl, cString& outTitle); + // --- cDownloadProgress --------------------------------------------------- class cDownloadProgress { @@ -56,20 +63,45 @@ public: cDownloadProgress *NewDownload(); }; -// --- cMenuRequest ---------------------------------------------------- +// --- cRequest ------------------------------------------------------------ -class cMenuRequest { +class cRequest { private: int reqID; + eRequestType type; + +protected: + char *href; bool aborted; bool finished; + +public: + cRequest(int ID, eRequestType type, const char *href); + virtual ~cRequest(); + + int GetID() const { return reqID; } + virtual eRequestType GetType() const { return type; } + const char *GetReference() const { return href; } + + virtual void RequestDone(int errorcode, cString pharse); + bool IsFinished() const { return finished; } + virtual void Abort(); + bool IsAborted() const { return aborted; } + + virtual int ReadFile() { return -1; } + virtual bool Read() { return true; } +}; + +// --- cMenuRequest -------------------------------------------------------- + +class cMenuRequest : public cRequest { +private: int status; cString statusPharse; protected: WebviCtx webvi; WebviHandle handle; - char *wvtref; cMemoryBuffer inBuffer; cWebviTimer *timer; @@ -81,83 +113,95 @@ protected: void AppendQualityParamsToRef(); public: - cMenuRequest(int ID, const char *wvtreference); + cMenuRequest(int ID, eRequestType type, const char *wvtreference); virtual ~cMenuRequest(); - int GetID() { return reqID; } - WebviHandle GetHandle() { return handle; } - const char *GetReference() { return wvtref; } + WebviHandle GetHandle() const { return handle; } bool Start(WebviCtx webvictx); - virtual void RequestDone(int errorcode, cString pharse); - bool IsFinished() { return finished; } virtual void Abort(); - bool IsAborted() { return aborted; } + virtual void RequestDone(int errorcode, cString pharse); // Return true if the lastest status code indicates success. - bool Success(); + bool Success() const; // Return the status code - int GetStatusCode() { return status; } + int GetStatusCode() const { return status; } // Return the response pharse - cString GetStatusPharse(); - - virtual eRequestType GetType() { return REQT_MENU; } + cString GetStatusPharse() const; + virtual void SetDownloader(iAsyncFileDownloaderManager *dlmanager) {}; // Return the content of the response message virtual cString GetResponse(); void SetTimer(cWebviTimer *t) { timer = t; } cWebviTimer *GetTimer() { return timer; } - - virtual int File() { return -1; } - virtual bool Read() { return true; } }; // --- cFileDownloadRequest ------------------------------------------------ class cFileDownloadRequest : public cMenuRequest { private: - enum eDownloadState { STATE_WEBVI, STATE_POSTPROCESS, STATE_FINISHED }; + enum eDownloadState { STATE_GET_STREAM_URL, STATE_STREAM_DOWNLOAD, + STATE_POSTPROCESS, STATE_FINISHED }; char *title; long bytesDownloaded; long contentLength; + int streamSocket; cUnbufferedFile *destfile; char *destfilename; cDownloadProgress *progressUpdater; cPipe postProcessPipe; eDownloadState state; + cString streamUrl; + cString streamTitle; + iAsyncFileDownloaderManager *downloadManager; + iFileDownloadTask *streamDownloader; + + static ssize_t StreamReadWrapper(void *buf, size_t count, void *data); + static void StreamFinishedWrapper(void *data); protected: virtual WebviHandle PrepareHandle(); - virtual ssize_t WriteData(const char *ptr, size_t len); bool OpenDestFile(); char *GetExtension(const char *contentType, const char *url); + void StartStreamDownload(); void StartPostProcessing(); + ssize_t WriteToDestFile(void *buf, size_t len); + + static bool IsRTMPStream(const char *url); public: cFileDownloadRequest(int ID, const char *streamref, cDownloadProgress *progress); virtual ~cFileDownloadRequest(); - eRequestType GetType() { return REQT_FILE; } + void SetDownloader(iAsyncFileDownloaderManager *dlmanager); + void RequestDone(int errorcode, cString pharse); void Abort(); - int File(); + virtual int ReadFile(); bool Read(); }; // --- cStreamUrlRequest --------------------------------------------------- class cStreamUrlRequest : public cMenuRequest { +private: + cString streamUrl; + cString streamTitle; + protected: virtual WebviHandle PrepareHandle(); public: cStreamUrlRequest(int ID, const char *ref); - eRequestType GetType() { return REQT_STREAM; } + virtual void RequestDone(int errorcode, cString pharse); + + cString getStreamUrl(); + cString getStreamTitle(); }; // --- cTimerRequest ------------------------------------------------------- @@ -165,8 +209,6 @@ public: class cTimerRequest : public cMenuRequest { public: cTimerRequest(int ID, const char *ref); - - eRequestType GetType() { return REQT_TIMER; } }; // --- cRequestVector ------------------------------------------------------ diff --git a/src/vdr-plugin/timer.c b/src/vdr-plugin/timer.c index 6346763..e6ae88b 100644 --- a/src/vdr-plugin/timer.c +++ b/src/vdr-plugin/timer.c @@ -14,6 +14,7 @@ #include "common.h" #include "download.h" #include "config.h" +#include "filedownloader.h" // --- cWebviTimer ----------------------------------------------- diff --git a/src/vdr-plugin/timer.h b/src/vdr-plugin/timer.h index 369fbce..3db0d8c 100644 --- a/src/vdr-plugin/timer.h +++ b/src/vdr-plugin/timer.h @@ -21,6 +21,7 @@ #define MAX_TIMER_HISTORY_SIZE 2000 class cWebviTimerManager; +class iAsyncFileDownloaderManager; // --- cWebviTimer ----------------------------------------------- diff --git a/src/vdr-plugin/webvideo.c b/src/vdr-plugin/webvideo.c index 4f3cf68..444a346 100644 --- a/src/vdr-plugin/webvideo.c +++ b/src/vdr-plugin/webvideo.c @@ -23,6 +23,7 @@ #include "player.h" #include "common.h" #include "timer.h" +#include "filedownloader.h" const char *VERSION = "0.5.0"; static const char *DESCRIPTION = trNOOP("Download video files from the web"); @@ -86,7 +87,7 @@ cPluginWebvideo::cPluginWebvideo(void) cPluginWebvideo::~cPluginWebvideo() { // Clean up after yourself! - webvi_cleanup(0); + webvi_cleanup(); } const char *cPluginWebvideo::CommandLineHelp(void) @@ -234,6 +235,7 @@ void cPluginWebvideo::HandleFinishedRequests(void) bool forceStatusUpdate = false; cMenuRequest *req; cFileDownloadRequest *dlreq; + cStreamUrlRequest *streamreq; cString streamurl; cWebviTimer *timer; cString timermsg; @@ -269,11 +271,10 @@ void cPluginWebvideo::HandleFinishedRequests(void) break; case REQT_STREAM: - streamurl = req->GetResponse(); + streamreq = dynamic_cast<cStreamUrlRequest *>(req); + streamurl = streamreq->getStreamUrl(); if (streamurl[0] == '\0') Skins.Message(mtError, tr("Streaming failed: no URL")); - else if (strncmp(streamurl, "wvt://", 6) == 0) - Skins.Message(mtError, tr("Streaming not supported, try downloading")); else if (!StartStreaming(streamurl)) Skins.Message(mtError, tr("Failed to launch media player")); break; @@ -367,18 +368,19 @@ cString cPluginWebvideo::Active(void) cOsdObject *cPluginWebvideo::MainMenuAction(void) { // Perform the action when selected from the main VDR menu. - const char *mainMenuReference = "wvt:///?srcurl=mainmenu"; + const char *mainMenuReference = "wvt://mainmenu"; const char *placeholderMenu = "<wvmenu><title>Webvideo</title></wvmenu>"; const char *statusmsg = NULL; struct timespec ts; ts.tv_sec = 0; ts.tv_nsec = 100*1000*1000; // 100 ms - menuPointers.navigationMenu = new cNavigationMenu(&history, summaries); + menuPointers.navigationMenu = \ + new cNavigationMenu(&history, summaries); cHistoryObject *hist = history.Home(); if (!hist) { - cWebviThread::Instance().AddRequest(new cMenuRequest(0, mainMenuReference)); + cWebviThread::Instance().AddRequest(new cMenuRequest(0, REQT_MENU, mainMenuReference)); cHistoryObject *placeholder = new cHistoryObject(placeholderMenu, mainMenuReference, 0); history.TruncateAndAdd(placeholder); @@ -462,6 +464,8 @@ cString cPluginWebvideo::CreateWvtRef(const char *url) { cString domain = parseDomain(url); if (strcmp(domain, "") == 0) return ""; + + // FIXME! char *encoded = URLencode(url); cString res = cString::sprintf("wvt:///%s/videopage.xsl?srcurl=%s", |