diff options
author | Antti Ajanki <antti.ajanki@iki.fi> | 2011-07-06 17:04:33 +0300 |
---|---|---|
committer | Antti Ajanki <antti.ajanki@iki.fi> | 2011-07-06 17:04:33 +0300 |
commit | bd88072b5df85c392917e4bce11a40c0058c4657 (patch) | |
tree | c822a94e501a1f6fecf6d0742b772771f2b34441 | |
parent | 195fcc2ea284489240d58a72859f0963ad82e27a (diff) | |
download | vdr-plugin-webvideo-bd88072b5df85c392917e4bce11a40c0058c4657.tar.gz vdr-plugin-webvideo-bd88072b5df85c392917e4bce11a40c0058c4657.tar.bz2 |
Properly implement timeout callback (bug #619), bump SONAME because of API changes
-rw-r--r-- | debian/control | 6 | ||||
-rw-r--r-- | debian/libwebvi0.install | 1 | ||||
-rw-r--r-- | debian/libwebvi1.docs (renamed from debian/libwebvi0.docs) | 0 | ||||
-rw-r--r-- | debian/libwebvi1.install | 1 | ||||
-rw-r--r-- | src/libwebvi/Makefile | 4 | ||||
-rw-r--r-- | src/libwebvi/libwebvi.c | 53 | ||||
-rw-r--r-- | src/libwebvi/libwebvi.h | 38 | ||||
-rw-r--r-- | src/libwebvi/webvi/api.py | 66 | ||||
-rw-r--r-- | src/libwebvi/webvi/asyncurl.py | 34 | ||||
-rw-r--r-- | src/libwebvi/webvi/constants.py | 7 | ||||
-rw-r--r-- | src/libwebvi/webvi/request.py | 6 | ||||
-rw-r--r-- | src/vdr-plugin/download.c | 60 | ||||
-rw-r--r-- | src/vdr-plugin/download.h | 4 | ||||
-rw-r--r-- | src/webvicli/webvicli/client.py | 34 |
14 files changed, 240 insertions, 74 deletions
diff --git a/debian/control b/debian/control index f24d954..728b215 100644 --- a/debian/control +++ b/debian/control @@ -20,7 +20,7 @@ Description: Web video downloader library - Python module This is the Python module. XB-Python-Version: ${python:Versions} -Package: libwebvi0 +Package: libwebvi1 Architecture: any Section: libs Depends: ${shlibs:Depends}, ${misc:Depends}, python, python-webvi @@ -34,7 +34,7 @@ XB-Python-Version: ${python:Versions} Package: libwebvi-dev Architecture: any Section: libdevel -Depends: ${misc:Depends}, libc-dev +Depends: ${misc:Depends}, libc-dev, python-dev Description: Web video downloader library - development files This package provides a library for downloading video and audio streams from media sharing websites, such as YouTube or Google Video. @@ -44,7 +44,7 @@ Description: Web video downloader library - development files Package: vdr-plugin-webvideo Architecture: any Section: video -Depends: ${shlibs:Depends}, ${misc:Depends}, ${vdr:Depends}, libwebvi0 +Depends: ${shlibs:Depends}, ${misc:Depends}, ${vdr:Depends}, libwebvi1 Suggests: vdr-plugin-mplayer | vdr-plugin-xineliboutput Description: VDR plugin for downloading videos from the Web This plugin for the Linux Video Disc Recorder (VDR) provides ability diff --git a/debian/libwebvi0.install b/debian/libwebvi0.install deleted file mode 100644 index 6c9de3d..0000000 --- a/debian/libwebvi0.install +++ /dev/null @@ -1 +0,0 @@ -src/libwebvi/libwebvi.so.0* usr/lib/ diff --git a/debian/libwebvi0.docs b/debian/libwebvi1.docs index 1333ed7..1333ed7 100644 --- a/debian/libwebvi0.docs +++ b/debian/libwebvi1.docs diff --git a/debian/libwebvi1.install b/debian/libwebvi1.install new file mode 100644 index 0000000..ef3ed62 --- /dev/null +++ b/debian/libwebvi1.install @@ -0,0 +1 @@ +src/libwebvi/libwebvi.so.1* usr/lib/ diff --git a/src/libwebvi/Makefile b/src/libwebvi/Makefile index d5a2ff1..47d5826 100644 --- a/src/libwebvi/Makefile +++ b/src/libwebvi/Makefile @@ -2,8 +2,8 @@ PREFIX ?= /usr/local SYSLIBDIR = $(PREFIX)/lib LIBNAME=libwebvi.so -LIBSONAME=$(LIBNAME).0 -LIBMINOR=$(LIBSONAME).4 +LIBSONAME=$(LIBNAME).1 +LIBMINOR=$(LIBSONAME).0 VERSION:=$(shell grep VERSION webvi/version.py | cut -d \' -f 2) PYLIB:=$(shell python pythonlibname.py) diff --git a/src/libwebvi/libwebvi.c b/src/libwebvi/libwebvi.c index d8a7b9c..0f1c129 100644 --- a/src/libwebvi/libwebvi.c +++ b/src/libwebvi/libwebvi.c @@ -231,9 +231,11 @@ WebviCtx webvi_initialize_context(void) { /* These are used to wrap C-callbacks into Python callables. Keep in sync with libwebvi.h. */ - if (PyRun_SimpleString("from ctypes import CFUNCTYPE, c_int, c_size_t, c_char_p, c_void_p\n" - "WriteCallback = CFUNCTYPE(c_size_t, c_char_p, c_size_t, c_void_p)\n" - "ReadCallback = CFUNCTYPE(c_size_t, c_char_p, c_size_t, c_void_p)\n") != 0) { + if (PyRun_SimpleString( + "from ctypes import CFUNCTYPE, c_int, c_long, c_size_t, c_char_p, c_void_p\n" + "WriteCallback = CFUNCTYPE(c_size_t, c_char_p, c_size_t, c_void_p)\n" + "ReadCallback = CFUNCTYPE(c_size_t, c_char_p, c_size_t, c_void_p)\n" + "TimeoutCallback = CFUNCTYPE(c_long, c_void_p)\n") != 0) { debug("callback definitions failed\n"); goto err; } @@ -322,18 +324,51 @@ const char* webvi_strerror(WebviCtx ctx, WebviResult res) { return c->last_error; } -WebviResult webvi_set_config(WebviCtx ctx, WebviConfig conf, const char *value) { +WebviResult webvi_set_config(WebviCtx ctx, WebviConfig conf, ...) { + va_list argptr; WebviResult res; per_interpreter_data *c = (per_interpreter_data *)ctx; PyEval_AcquireThread(c->interp); - PyObject *args = Py_BuildValue("(is)", conf, value); - PyObject *v = call_python(c->webvi_module, "set_config", args); - Py_DECREF(args); + PyObject *m = PyImport_AddModule("__main__"); + if (!m) { + handle_pyerr(); + PyEval_ReleaseThread(c->interp); + return WEBVIERR_UNKNOWN_ERROR; + } - res = pyint_as_webviresult(v); - Py_XDECREF(v); + va_start(argptr, conf); + + PyObject *args = NULL; + if (conf == WEBVI_CONFIG_TIMEOUT_CALLBACK) { + PyObject *maindict = PyModule_GetDict(m); + PyObject *prototype = PyDict_GetItemString(maindict, "TimeoutCallback"); + PyObject *cb_addr = Py_BuildValue("(l)", va_arg(argptr, long)); + PyObject *cb_as_pyobject = PyObject_CallObject(prototype, cb_addr); + args = Py_BuildValue("(iO)", conf, cb_as_pyobject); + Py_DECREF(cb_as_pyobject); + Py_DECREF(cb_addr); + + } else if (conf == WEBVI_CONFIG_TIMEOUT_DATA) { + args = Py_BuildValue("(il)", conf, va_arg(argptr, long)); + + } else { + args = Py_BuildValue("(is)", conf, va_arg(argptr, char *)); + } + + if (args) { + PyObject *v = call_python(c->webvi_module, "set_config", args); + Py_DECREF(args); + + res = pyint_as_webviresult(v); + Py_XDECREF(v); + } else { + handle_pyerr(); + res = WEBVIERR_UNKNOWN_ERROR; + } + + va_end(argptr); PyEval_ReleaseThread(c->interp); diff --git a/src/libwebvi/libwebvi.h b/src/libwebvi/libwebvi.h index 9253eea..e80ce43 100644 --- a/src/libwebvi/libwebvi.h +++ b/src/libwebvi/libwebvi.h @@ -26,6 +26,7 @@ typedef int WebviHandle; typedef ssize_t (*webvi_callback)(const char *, size_t, void *); +typedef void (*webvi_timeout_callback)(long, void *); typedef enum { WEBVIMSG_DONE @@ -65,8 +66,10 @@ typedef enum { WEBVIINFO_STREAM_TITLE } WebviInfo; +#define WEBVI_SELECT_TIMEOUT -1 + typedef enum { - WEBVI_SELECT_TIMEOUT = 0, + WEBVI_SELECT_CHECK = 0, WEBVI_SELECT_READ = 1, WEBVI_SELECT_WRITE = 2, WEBVI_SELECT_EXCEPTION = 4 @@ -74,7 +77,9 @@ typedef enum { typedef enum { WEBVI_CONFIG_TEMPLATE_PATH, - WEBVI_CONFIG_DEBUG + WEBVI_CONFIG_DEBUG, + WEBVI_CONFIG_TIMEOUT_CALLBACK, + WEBVI_CONFIG_TIMEOUT_DATA } WebviConfig; typedef long WebviCtx; @@ -133,12 +138,24 @@ const char* webvi_strerror(WebviCtx ctx, WebviResult err); * Set a new value for a global configuration option conf. * * Possible values and their meanings: - * TEMPLATE_PATH Set the base directory for the XSLT templates - * DEBUG If value is not "0", print debug output to stdin * - * The string pointed by value is copied to the library. + * WEBVI_CONFIG_TEMPLATE_PATH + * Set the base directory for the XSLT templates (char *) + * + * WEBVI_CONFIG_DEBUG + * If value is not "0", print debug output to stdin (char *) + * + * WEBVI_CONFIG_TIMEOUT_CALLBACK + * Set timeout callback function (webvi_timeout_callback) + * + * WEBVI_CONFIG_TIMEOUT_DATA + * Set user data which will passed as second argument of the timeout + * callback (void *) + * + * The strings (char * arguments) are copied to the library (the user + * can free their original copy). */ -WebviResult webvi_set_config(WebviCtx ctx, WebviConfig conf, const char *value); +WebviResult webvi_set_config(WebviCtx ctx, WebviConfig conf, ...); /* * Creates a new download request. @@ -301,14 +318,13 @@ WebviResult webvi_fdset(WebviCtx ctx, fd_set *readfd, fd_set *writefd, fd_set *e * WEBVI_SELECT_READ, WEBVI_SELECT_WRITE, WEBVI_SELECT_EXCEPTION to * indicate that activefd has been signalled to be ready for reading, * writing or being in exception state, respectively. ev_bitmask can - * also set to WEBVI_SELECT_TIMEOUT which means that the state is + * also set to WEBVI_SELECT_CHECK which means that the state is * checked internally. On return, running_handles will contain the * number of still active file descriptors. * - * This function should be called with activefd set to 0 and - * ev_bitmask to WEBVI_SELECT_TIMEOUT periodically (every few seconds) - * even if no file descriptors have become ready to allow for timeout - * handling and other internal tasks. + * If a timeout occurs before any file descriptor becomes ready, this + * function should be called with sockfd set to WEBVI_SELECT_TIMEOUT + * and ev_bitmask set to WEBVI_SELECT_CHECK. */ WebviResult webvi_perform(WebviCtx ctx, int sockfd, int ev_bitmask, long *running_handles); diff --git a/src/libwebvi/webvi/api.py b/src/libwebvi/webvi/api.py index f7f4ad1..6dfa31e 100644 --- a/src/libwebvi/webvi/api.py +++ b/src/libwebvi/webvi/api.py @@ -23,29 +23,45 @@ Example workflow: handle = new_request(ref, WebviRequestType.MENU) -2) Setup a callback function: +2) Setup a timeout callback: -setopt(handle, WebviOpt.WRITEFUNC, my_callback) +def timeout_func(t, ignored): + global alarm + now = current_time_in_milliseconds() + alarm = now + t -3) Start the network transfer: +set_config(WebviConfig.TIMEOUT_CALLBACK, timeout_func) + +3) Setup a data callback: + +set_opt(handle, WebviOpt.WRITEFUNC, write_func) + +4) Start the network transfer: start_handle(handle) -4) Get active file descriptors, wait for activity on them, and let -webvi process the file descriptor. +5) Get active file descriptors, wait for activity using the timeout +value supplied by timeout callback, and let webvi process the file +descriptor. import select ... readfd, writefd, excfd = fdset()[1:4] -readfd, writefd, excfd = select.select(readfd, writefd, excfd, 5.0) -for fd in readfd: - perform(fd, WebviSelectBitmask.READ) -for fd in writefd: - perform(fd, WebviSelectBitmask.WRITE) - -5) Iterate 4) until pop_message returns handle, which indicates that +timeout = alarm - current_time_in_milliseconds() +if timeout < 0: + timeout = 10.0 +readfd, writefd, excfd = select.select(readfd, writefd, excfd, timeout) +if readfd == writefd == excfd == []: # timeout + perform(WebviSelect.TIMEOUT, WebviSelectBitmask.CHECK) +else: + for fd in readfd: + perform(fd, WebviSelectBitmask.READ) + for fd in writefd: + perform(fd, WebviSelectBitmask.WRITE) + +6) Iterate 5) until pop_message returns handle, which indicates that the request has been completed. finished, status, errmsg, remaining = pop_message() @@ -105,6 +121,12 @@ def set_config(conf, value): else: request.DEBUG = True return WebviErr.OK + elif conf == WebviConfig.TIMEOUT_CALLBACK: + request.set_timeout_callback(value) + return WebviErr.OK + elif conf == WebviConfig.TIMEOUT_DATA: + request.timeout_data = value + return WebviErr.OK else: return WebviErr.INVALID_PARAMETER @@ -242,7 +264,7 @@ def fdset(): """Get the list of file descriptors that are currently in use by the library. - Returrns a tuple, where the first item is a constants.WebviErr + Returns a tuple, where the first item is a constants.WebviErr value indicating the success of the call, the next three values are lists of descriptors that should be monitored for reading, writing, and exceptional conditions, respectively. The last item @@ -272,24 +294,24 @@ def perform(fd, ev_bitmask): select() or similar system call. ev_bitmask specifies what kind of activity has been detected using values of constants.WebviSelectBitmask. If ev_bitmask is - constants.WebviSelectBitmask.TIMEOUT the type of activity is check - by the function. + constants.WebviSelectBitmask.CHECK the type of activity is + checked by the function. - This function should be called every few seconds with fd=-1, - ev_bitmask=constants.WebviSelectBitmask.TIMEOUT even if no - activity has been signalled on the file descriptors to ensure - correct handling of timeouts and other internal processing. + If a timeout occurs before any file descriptor becomes ready, this + function should be called with fd set to + constants.WebviSelect.TIMEOUT and ev_bitmask set to + constants.WebviSelectBitmask.CHECK. """ if fd < 0: - asyncurl.poll() + asyncurl.poll_timeout() else: disp = socket_map.get(fd) if disp is not None: if ev_bitmask & WebviSelectBitmask.READ != 0 or \ - (ev_bitmask == 0 and disp.readable()): + (ev_bitmask == WebviSelectBitmask.CHECK and disp.readable()): disp.handle_read_event() if ev_bitmask & WebviSelectBitmask.WRITE != 0 or \ - (ev_bitmask == 0 and disp.writable()): + (ev_bitmask == WebviSelectBitmask.CHECK and disp.writable()): disp.handle_write_event() return (WebviErr.OK, len(socket_map)) diff --git a/src/libwebvi/webvi/asyncurl.py b/src/libwebvi/webvi/asyncurl.py index 585f090..6b17893 100644 --- a/src/libwebvi/webvi/asyncurl.py +++ b/src/libwebvi/webvi/asyncurl.py @@ -40,7 +40,8 @@ def poll(timeout=0.0, map=None, mdisp=None): if mdisp is None: mdisp = multi_dispatcher if map: - timeout = min(timeout, mdisp.timeout/1000.0) + if mdisp.timeout != -1: + timeout = min(timeout, mdisp.timeout/1000.0) r = []; w = []; e = [] for fd, obj in map.items(): @@ -53,7 +54,8 @@ def poll(timeout=0.0, map=None, mdisp=None): if is_r or is_w: e.append(fd) if [] == r == w == e: - time.sleep(timeout) + if timeout > 0: + time.sleep(timeout) else: try: r, w, e = select.select(r, w, e, timeout) @@ -85,6 +87,11 @@ def poll(timeout=0.0, map=None, mdisp=None): continue asyncore._exception(obj) +def poll_timeout(mdisp=None): + if mdisp is None: + mdisp = multi_dispatcher + mdisp.socket_action(SOCKET_TIMEOUT, 0) + def loop(timeout=30.0, use_poll=False, map=None, count=None, mdisp=None): if map is None: map = asyncore.socket_map @@ -118,13 +125,15 @@ class curl_multi_dispatcher: else: self._map = socket_map self.dispatchers = {} - self.timeout = 1000 + self.timeout = -1 + # The lambda is to avoid "not callable" error from pylint + self.timeout_callback = lambda x, y: None self._sockets_removed = False self._curlm = pycurl.CurlMulti() - self._curlm.setopt(pycurl.M_SOCKETFUNCTION, self.socket_callback) - self._curlm.setopt(pycurl.M_TIMERFUNCTION, self.timeout_callback) + self._curlm.setopt(pycurl.M_SOCKETFUNCTION, self._socket_callback) + self._curlm.setopt(pycurl.M_TIMERFUNCTION, self._update_timeout) - def socket_callback(self, action, socket, user_data, socket_data): + def _socket_callback(self, action, socket, user_data, socket_data): # print 'socket callback: %d, %s' % \ # (socket, {pycurl.POLL_NONE: "NONE", # pycurl.POLL_IN: "IN", @@ -155,8 +164,10 @@ class curl_multi_dispatcher: obj.set_readable(True) obj.set_writable(True) - def timeout_callback(self, msec): + def _update_timeout(self, msec): self.timeout = msec + if self.timeout_callback: + self.timeout_callback(self, msec) def attach(self, curldisp): """Starts a transfer on curl handle by attaching it to this @@ -168,9 +179,9 @@ class curl_multi_dispatcher: # the curl object is already on this multi-stack pass - while self._curlm.socket_all()[0] == pycurl.E_CALL_MULTI_PERFORM: - pass - + # _curlm.timeout() seems to be needed, although curl + # documentation doesn't mention it. + self._update_timeout(self._curlm.timeout()) self.check_completed(True) def detach(self, curldisp): @@ -181,9 +192,6 @@ class curl_multi_dispatcher: # libcurl does not send POLL_REMOVE when a handle is aborted for socket, curlobj in self._map.items(): if curlobj == curldisp: - - print 'handle stopped but socket in map' - del self._map[socket] break diff --git a/src/libwebvi/webvi/constants.py b/src/libwebvi/webvi/constants.py index 2d29700..16e1f92 100644 --- a/src/libwebvi/webvi/constants.py +++ b/src/libwebvi/webvi/constants.py @@ -41,11 +41,16 @@ class WebviInfo: STREAM_TITLE = 3 class WebviSelectBitmask: - TIMEOUT = 0 + CHECK = 0 READ = 1 WRITE = 2 EXCEPTION = 4 +class WebviSelect: + TIMEOUT = -1 + class WebviConfig: TEMPLATE_PATH = 0 DEBUG = 1 + TIMEOUT_CALLBACK = 2 + TIMEOUT_DATA = 3 diff --git a/src/libwebvi/webvi/request.py b/src/libwebvi/webvi/request.py index 87dcf87..cab15f7 100644 --- a/src/libwebvi/webvi/request.py +++ b/src/libwebvi/webvi/request.py @@ -24,12 +24,14 @@ import download import sys import utils import json2xml +import asyncurl from constants import WebviRequestType DEBUG = False DEFAULT_TEMPLATE_PATH = '/usr/local/share/webvi/templates' template_path = DEFAULT_TEMPLATE_PATH +timeout_data = None def debug(msg): if DEBUG: @@ -49,6 +51,10 @@ def set_template_path(path): debug("set_template_path " + template_path) +def set_timeout_callback(callback): + c_api_callback = lambda mdisp, timeout: callback(timeout, timeout_data) + asyncurl.multi_dispatcher.timeout_callback = c_api_callback + def parse_reference(reference): """Parses URLs of the following form: diff --git a/src/vdr-plugin/download.c b/src/vdr-plugin/download.c index ffdfa54..4b7a971 100644 --- a/src/vdr-plugin/download.c +++ b/src/vdr-plugin/download.c @@ -14,6 +14,19 @@ #include "download.h" #include "common.h" +static void diff_timeval(struct timeval *a, struct timeval *b, + struct timeval *result) { + long usec_diff = a->tv_usec - b->tv_usec; + result->tv_sec = a->tv_sec - b->tv_sec; + + while (usec_diff < 0) { + usec_diff += 1000000; + result->tv_sec -= 1; + } + + result->tv_usec = usec_diff; +} + // --- cWebviThread -------------------------------------------------------- cWebviThread::cWebviThread() { @@ -25,8 +38,13 @@ cWebviThread::cWebviThread() { newreqwrite = pipefd[1]; //fcntl(newreqread, F_SETFL, O_NONBLOCK); //fcntl(newreqwrite, F_SETFL, O_NONBLOCK); + timerActive = false; webvi = webvi_initialize_context(); + if (webvi != 0) { + webvi_set_config(webvi, WEBVI_CONFIG_TIMEOUT_DATA, this); + webvi_set_config(webvi, WEBVI_CONFIG_TIMEOUT_CALLBACK, UpdateTimeout); + } } cWebviThread::~cWebviThread() { @@ -47,6 +65,24 @@ cWebviThread::~cWebviThread() { } } +void cWebviThread::UpdateTimeout(long timeout, void *instance) { + cWebviThread *self = (cWebviThread *)instance; + if (!self) + return; + + if (timeout < 0) { + self->timerActive = false; + } else { + struct timeval now; + long alrm; + gettimeofday(&now, NULL); + alrm = timeout + now.tv_usec/1000; + self->timer.tv_sec = now.tv_sec + alrm/1000; + self->timer.tv_usec = (alrm % 1000) * 1000; + self->timerActive = true; + } +} + cWebviThread &cWebviThread::Instance() { static cWebviThread instance; @@ -128,8 +164,8 @@ void cWebviThread::Stop() { void cWebviThread::Action(void) { fd_set readfds, writefds, excfds; - int maxfd; - struct timeval timeout; + int maxfd, s; + struct timeval timeout, now; long running_handles; bool check_done = false; bool has_request_files = false; @@ -161,11 +197,20 @@ void cWebviThread::Action(void) { } requestMutex.Unlock(); - timeout.tv_sec = 5; - timeout.tv_usec = 0; + if (!timerActive) { + timeout.tv_sec = 60; + timeout.tv_usec = 0; + } else { + gettimeofday(&now, NULL); + diff_timeval(&timer, &now, &timeout); + if (timeout.tv_sec < 0 || timeout.tv_usec < 0) { + timeout.tv_sec = 0; + timeout.tv_usec = 0; + } + } - int s = TEMP_FAILURE_RETRY(select(maxfd+1, &readfds, &writefds, NULL, - &timeout)); + s = TEMP_FAILURE_RETRY(select(maxfd+1, &readfds, &writefds, NULL, + &timeout)); if (s == -1) { // select error LOG_ERROR_STR("select() error in webvideo downloader thread:"); @@ -173,7 +218,8 @@ void cWebviThread::Action(void) { } else if (s == 0) { // timeout - webvi_perform(webvi, 0, WEBVI_SELECT_TIMEOUT, &running_handles); + timerActive = false; + webvi_perform(webvi, WEBVI_SELECT_TIMEOUT, WEBVI_SELECT_CHECK, &running_handles); check_done = true; } else { diff --git a/src/vdr-plugin/download.h b/src/vdr-plugin/download.h index 5f29150..14a5c66 100644 --- a/src/vdr-plugin/download.h +++ b/src/vdr-plugin/download.h @@ -9,6 +9,7 @@ #ifndef __WEBVIDEO_DOWNLOAD_H #define __WEBVIDEO_DOWNLOAD_H +#include <sys/time.h> #include <vdr/thread.h> #include <libwebvi.h> #include "request.h" @@ -23,6 +24,8 @@ private: cRequestVector newRequestList; cRequestVector finishedRequestList; int newreqread, newreqwrite; + bool timerActive; + struct timeval timer; void MoveToFinishedList(cMenuRequest *req); void ActivateNewRequest(); @@ -30,6 +33,7 @@ private: protected: void Action(void); + static void UpdateTimeout(long timeout, void *data); public: cWebviThread(); diff --git a/src/webvicli/webvicli/client.py b/src/webvicli/webvicli/client.py index e0a8baa..0c86c0d 100644 --- a/src/webvicli/webvicli/client.py +++ b/src/webvicli/webvicli/client.py @@ -26,6 +26,7 @@ import os.path import subprocess import time import re +import datetime import urllib import libxml2 import webvi.api @@ -33,7 +34,7 @@ import webvi.utils from optparse import OptionParser from ConfigParser import RawConfigParser from urlparse import urlparse -from webvi.constants import WebviRequestType, WebviOpt, WebviInfo, WebviSelectBitmask, WebviConfig +from webvi.constants import WebviRequestType, WebviOpt, WebviInfo, WebviSelectBitmask, WebviConfig, WebviSelect from . import menu VERSION = '0.4.2' @@ -188,6 +189,15 @@ class WVClient: self.quality_limits = {'download': downloadlimits, 'stream': streamlimits} self.vfatfilenames = vfatfilenames + self.alarm = None + webvi.api.set_config(WebviConfig.TIMEOUT_CALLBACK, self.update_timeout) + + def update_timeout(self, timeout_ms, data): + if timeout_ms < 0: + self.alarm = None + else: + now = datetime.datetime.now() + self.alarm = now + datetime.timedelta(milliseconds=timeout_ms) def parse_page(self, page): if page is None: @@ -226,7 +236,7 @@ class WVClient: node = node.next doc.freeDoc() return menupage - + def parse_link(self, node): label = '' ref = None @@ -319,10 +329,24 @@ class WVClient: return (501, 'Unexpected handle (got %d, expected %d)' % (finished, handle)) elif status != -1 or errmsg: return (status, errmsg) - else: - return (502, 'empty fdset but handle is not ready!') - readyread, readywrite, readyexc = select.select(readfds, writefds, excfds, 30.0) + if self.alarm is None: + timeout = 10.0 + else: + delta = self.alarm - datetime.datetime.now() + if delta < datetime.timedelta(0): + timeout = 10.0 + self.alarm = None + else: + timeout = delta.microseconds/1000000.0 + delta.seconds + + readyread, readywrite, readyexc = \ + select.select(readfds, writefds, excfds, timeout) + + if readyread == readywrite == readyexc == []: + webvi.api.perform(WebviSelect.TIMEOUT, WebviSelectBitmask.CHECK) + self.alarm = None + continue for fd in readyread: webvi.api.perform(fd, WebviSelectBitmask.READ) |