summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAntti Ajanki <antti.ajanki@iki.fi>2013-11-07 20:22:49 +0200
committerAntti Ajanki <antti.ajanki@iki.fi>2013-11-07 20:22:49 +0200
commit8cc97018ad564998a42d02dfbdaa98a36101e350 (patch)
treed4b5b9b253df70cc760e0967b657baf69cd2d5c0
parent451f691d367a46f365101f39cc093ce2feaacd13 (diff)
downloadvdr-plugin-webvideo-8cc97018ad564998a42d02dfbdaa98a36101e350.tar.gz
vdr-plugin-webvideo-8cc97018ad564998a42d02dfbdaa98a36101e350.tar.bz2
Initial update of the VDR plugin
-rw-r--r--src/vdr-plugin/Makefile3
-rw-r--r--src/vdr-plugin/download.c138
-rw-r--r--src/vdr-plugin/download.h1
-rw-r--r--src/vdr-plugin/filedownloader.c147
-rw-r--r--src/vdr-plugin/filedownloader.h85
-rw-r--r--src/vdr-plugin/menu.c897
-rw-r--r--src/vdr-plugin/menu.h38
-rw-r--r--src/vdr-plugin/request.c286
-rw-r--r--src/vdr-plugin/request.h92
-rw-r--r--src/vdr-plugin/timer.c1
-rw-r--r--src/vdr-plugin/timer.h1
-rw-r--r--src/vdr-plugin/webvideo.c18
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",