diff options
Diffstat (limited to 'src/vdr-plugin/request.c')
-rw-r--r-- | src/vdr-plugin/request.c | 286 |
1 files changed, 219 insertions, 67 deletions
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) { } |