diff options
Diffstat (limited to 'module')
22 files changed, 349 insertions, 343 deletions
diff --git a/module/Api.py b/module/Api.py index 84712af18..6d7ac75b6 100644 --- a/module/Api.py +++ b/module/Api.py @@ -766,18 +766,45 @@ class Api(Iface): # User Interaction ############################# - @permission(PERMS.INTERACTION) def isInteractionWaiting(self, mode): - pass + """ Check if task is waiting. + + :param mode: binary or'ed output type + :return: boolean + """ + return self.core.interactionManager.isTaskWaiting(mode) @permission(PERMS.INTERACTION) def getInteractionTask(self, mode): - pass + """Retrieve task for specific mode. + + :param mode: binary or'ed output type + :return: :class:`InteractionTask` + """ + task = self.core.interactionManager.getTask(mode) + return InteractionTask(-1) if not task else task + @permission(PERMS.INTERACTION) def setInteractionResult(self, iid, result): - pass + """Set Result for a interaction task. It will be immediately removed from task queue afterwards + + :param iid: interaction id + :param result: result as string + """ + task = self.core.interactionManager.getTaskByID(iid) + if task: + task.setResult(result) + + @permission(PERMS.INTERACTION) + def getNotifications(self): + """List of all available notifcations. They stay in queue for some time, client should\ + save which notifications it already has seen. + + :return: list of :class:`InteractionTask` + """ + return self.core.interactionManager.getNotifications() @permission(PERMS.INTERACTION) def getAddonHandler(self): diff --git a/module/interaction/InteractionManager.py b/module/interaction/InteractionManager.py index c547e1c97..0c125bdd4 100644 --- a/module/interaction/InteractionManager.py +++ b/module/interaction/InteractionManager.py @@ -15,10 +15,13 @@ @author: RaNaN """ -from traceback import print_exc from threading import Lock +from time import time -from module.utils import lock, bits_set +from new_collections import OrderedDict + +from module.utils import lock, bits_set, to_list +from module.Api import Input, Output from InteractionTask import InteractionTask @@ -28,69 +31,127 @@ class InteractionManager: Arbitary task with predefined output and input type can be set off. Asyncronous callbacks and default values keeps the ability to fallback if no user is present. """ + + # number of seconds a client is classified as active + CLIENT_THRESHOLD = 60 + def __init__(self, core): self.lock = Lock() self.core = core - self.tasks = [] #task store, for outgoing tasks only + self.tasks = OrderedDict() #task store, for outgoing tasks only + self.notifications = [] #list of notifications - self.last_clients = {} + self.last_clients = { + Output.Notification : 0, + Output.Captcha : 0, + Output.Query : 0, + } self.ids = 0 #only for internal purpose + def isClientConnected(self, mode=Output.All): + if mode == Output.All: + return max(self.last_clients.values()) + self.CLIENT_THRESHOLD <= time() + else: + self.last_clients.get(mode, 0) + self.CLIENT_THRESHOLD <= time() + + def updateClient(self, mode): + t = time() + for output in self.last_clients: + if bits_set(output, mode): + self.last_clients[output] = t + + @lock def work(self): - pass + # old notifications will be removed + for n in [x for x in self.notifications if x.timedOut()]: + self.notifications.remove(n) + + # store at most 100 notifications + del self.notifications[50:] + @lock - def newNotification(self): - pass + def createNotification(self, title, content, desc="", plugin=""): + """ Creates and queues a new Notification + + :param title: short title + :param content: text content + :param desc: short form of the notification + :param plugin: plugin name + :return: :class:`InteractionTask` + """ + task = InteractionTask(self.ids, Input.Text, [content], Output.Notification, "", title, desc, plugin) + self.ids += 1 + self.notifications.insert(0, task) + self.handleTask(task) + return task @lock - def newQueryTask(self): - pass + def newQueryTask(self, input, data, desc, default="", plugin=""): + task = InteractionTask(self.ids, input, to_list(data), Output.Query, default, _("Query"), desc, plugin) + self.ids += 1 + return task @lock - def newCaptchaTask(self, img, format, file, result_type): - task = InteractionTask(self.ids, img, format, file, result_type) + def newCaptchaTask(self, img, format, filename, plugin="", input=Input.Text): + #todo: title desc plugin + task = InteractionTask(self.ids, input, [img, format, filename],Output.Captcha, + "", _("Captcha request"), _("Please solve the captcha."), plugin) self.ids += 1 return task @lock def removeTask(self, task): - if task in self.tasks: - self.tasks.remove(task) + if task.iid in self.tasks: + del self.tasks[task.iid] @lock - def getTask(self): - for task in self.tasks: - return task + def getTask(self, mode=Output.All): + self.updateClient(mode) + + for task in self.tasks.itervalues(): + if mode == Output.All or bits_set(task.output, mode): + return task + + @lock + def getNotifications(self): + """retrieves notifications, old ones are only deleted after a while\ + client has to make sure itself to dont display it twice""" + for n in self.notifications: + n.setWaiting(self.CLIENT_THRESHOLD * 5, True) + #store notification for shorter period, lock the timeout + + return self.notifications + + def isTaskWaiting(self, mode=Output.All): + return self.getTask(mode) is not None @lock def getTaskByID(self, iid): - for task in self.tasks: - if task.id == iid: - return task + if iid in self.tasks: + task = self.tasks[iid] + del self.tasks[iid] + return task - def handleCaptcha(self, task): - cli = self.core.isClientConnected() + def handleTask(self, task): + cli = self.isClientConnected(task.output) - if cli: #client connected -> should solve the captcha - task.setWaiting(50) #wait 50 sec for response + if cli: #client connected -> should handle the task + task.setWaiting(self.CLIENT_THRESHOLD) # wait for response + + if task.output == Output.Notification: + task.setWaiting(60 * 60 * 30) # notifications are valid for 30h for plugin in self.core.addonManager.activePlugins(): try: - plugin.newCaptchaTask(task) + plugin.newInteractionTask(task) except: - if self.core.debug: - print_exc() - - if task.handler or cli: #the captcha was handled - self.tasks.append(task) - return True - - task.error = _("No Client connected for captcha decrypting") + self.core.print_exc() - return False + if task.output != Output.Notification: + self.tasks[task.iid] = task if __name__ == "__main__": diff --git a/module/interaction/InteractionTask.py b/module/interaction/InteractionTask.py index 7963a5c72..b372321b0 100644 --- a/module/interaction/InteractionTask.py +++ b/module/interaction/InteractionTask.py @@ -30,12 +30,14 @@ class InteractionTask(BaseInteractionTask): storage = None #: Timestamp when task expires wait_until = 0 - #: The received result as string representation + #: The received result result = None #: List of registered handles handler = None #: Error Message error = None + #: Timeout locked + locked = False def __init__(self, *args, **kwargs): BaseInteractionTask.__init__(self, *args, **kwargs) @@ -46,25 +48,28 @@ class InteractionTask(BaseInteractionTask): self.wait_until = 0 def convertResult(self, value): + #TODO: convert based on input/output return value def getResult(self): return self.result def setResult(self, value): - pass + self.result = self.convertResult(value) - def setWaiting(self, sec): - self.wait_until = max(time() + sec, self.wait_until) + def setWaiting(self, sec, lock=False): + if not self.locked: + self.wait_until = max(time() + sec, self.wait_until) + if lock: self.locked = True - def isWaiting(self, sec): + def isWaiting(self): if self.result or self.error or time() > self.waitUntil: return False return True def timedOut(self): - return time() > self.waitUntil + return time() > self.wait_until > 0 def correct(self): [x.taskCorrect(self) for x in self.handler] diff --git a/module/network/HTTPDownload.py b/module/network/HTTPDownload.py index 520a4e5f4..05ca44406 100644 --- a/module/network/HTTPDownload.py +++ b/module/network/HTTPDownload.py @@ -28,7 +28,7 @@ import pycurl from HTTPChunk import ChunkInfo, HTTPChunk from HTTPRequest import BadHeader -from module.plugins.Hoster import Abort +from module.plugins.Base import Abort from module.utils.fs import save_join, fs_encode # TODO: save content-disposition for resuming diff --git a/module/network/HTTPRequest.py b/module/network/HTTPRequest.py index 2f084efb5..774c0e64d 100644 --- a/module/network/HTTPRequest.py +++ b/module/network/HTTPRequest.py @@ -25,7 +25,7 @@ from httplib import responses from logging import getLogger from cStringIO import StringIO -from module.plugins.Hoster import Abort +from module.plugins.Base import Abort def myquote(url): return quote(url.encode('utf8') if isinstance(url, unicode) else url, safe="%/:=&?~#+!$,;'@()*[]") diff --git a/module/plugins/Base.py b/module/plugins/Base.py index b846bbd60..61fa211f4 100644 --- a/module/plugins/Base.py +++ b/module/plugins/Base.py @@ -18,8 +18,11 @@ """ import sys +from time import time, sleep +from random import randint + from module.utils import decode -from module.utils.fs import exists, makedirs, join +from module.utils.fs import exists, makedirs, join, remove # TODO # more attributes if needed @@ -32,6 +35,9 @@ class Fail(Exception): class Retry(Exception): """ raised when start again from beginning """ +class Abort(Exception): + """ raised when aborted """ + class Base(object): """ The Base plugin class with all shared methods and every possible attribute for plugin definition. @@ -83,6 +89,9 @@ class Base(object): #: :class:`InteractionManager` self.im = core.interactionManager + #: last interaction task + self.task = None + def logInfo(self, *args, **kwargs): """ Print args to log at specific level @@ -118,11 +127,7 @@ class Base(object): getattr(self.log, level)("%s: %s" % (self.__name__, sep.join(strings))) def setConfig(self, option, value): - """ Set config value for current plugin - - :param option: - :param value: - """ + """ Set config value for current plugin """ self.core.config.set(self.__name__, option, value) def getConf(self, option): @@ -130,11 +135,7 @@ class Base(object): return self.core.config.get(self.__name__, option) def getConfig(self, option): - """ Returns config value for current plugin - - :param option: - :return: - """ + """ Returns config value for current plugin """ return self.getConf(option) def setStorage(self, key, value): @@ -167,6 +168,14 @@ class Base(object): sys.stdout = sys._stdout embed() + def abort(self): + """ Check if plugin is in an abort state, is overwritten by subtypes""" + return False + + def checkAbort(self): + """ Will be overwriten to determine if control flow should be aborted """ + if self.abort: raise Abort() + def load(self, url, get={}, post={}, ref=True, cookies=True, just_header=False, decode=False): """Load content at url and returns it @@ -180,6 +189,7 @@ class Base(object): :return: Loaded content """ if not hasattr(self, "req"): raise Exception("Plugin type does not have Request attribute.") + self.checkAbort() res = self.req.load(url, get, post, ref, cookies, just_header, decode=decode) @@ -225,6 +235,92 @@ class Base(object): return res + def invalidTask(self): + if self.task: + self.task.invalid() + + def invalidCaptcha(self): + self.logDebug("Deprecated method .invalidCaptcha, use .invalidTask") + self.invalidTask() + + def correctTask(self): + if self.task: + self.task.correct() + + def correctCaptcha(self): + self.logDebug("Deprecated method .correctCaptcha, use .correctTask") + self.correctTask() + + def decryptCaptcha(self, url, get={}, post={}, cookies=False, forceUser=False, imgtype='jpg', + result_type='textual'): + """ Loads a captcha and decrypts it with ocr, plugin, user input + + :param url: url of captcha image + :param get: get part for request + :param post: post part for request + :param cookies: True if cookies should be enabled + :param forceUser: if True, ocr is not used + :param imgtype: Type of the Image + :param result_type: 'textual' if text is written on the captcha\ + or 'positional' for captcha where the user have to click\ + on a specific region on the captcha + + :return: result of decrypting + """ + + img = self.load(url, get=get, post=post, cookies=cookies) + + id = ("%.2f" % time())[-6:].replace(".", "") + temp_file = open(join("tmp", "tmpCaptcha_%s_%s.%s" % (self.__name__, id, imgtype)), "wb") + temp_file.write(img) + temp_file.close() + + name = "%sOCR" % self.__name__ + has_plugin = name in self.core.pluginManager.getPlugins("internal") + + if self.core.captcha: + OCR = self.core.pluginManager.loadClass("internal", name) + else: + OCR = None + + if OCR and not forceUser: + sleep(randint(3000, 5000) / 1000.0) + self.checkAbort() + + ocr = OCR() + result = ocr.get_captcha(temp_file.name) + else: + task = self.im.newCaptchaTask(img, imgtype, temp_file.name, result_type) + self.task = task + self.im.handleTask(task) + + while task.isWaiting(): + if self.abort(): + self.im.removeTask(task) + raise Abort() + sleep(1) + + #TODO + self.im.removeTask(task) + + if task.error and has_plugin: #ignore default error message since the user could use OCR + self.fail(_("Pil and tesseract not installed and no Client connected for captcha decrypting")) + elif task.error: + self.fail(task.error) + elif not task.result: + self.fail(_("No captcha result obtained in appropiate time by any of the plugins.")) + + result = task.result + self.log.debug("Received captcha result: %s" % str(result)) + + if not self.core.debug: + try: + remove(temp_file.name) + except: + pass + + return result + def fail(self, reason): """ fail and give reason """ raise Fail(reason)
\ No newline at end of file diff --git a/module/plugins/Hoster.py b/module/plugins/Hoster.py index 32c587aa5..b330743e6 100644 --- a/module/plugins/Hoster.py +++ b/module/plugins/Hoster.py @@ -17,10 +17,8 @@ @author: RaNaN, spoob, mkaay """ -from time import time, sleep -from random import randint - import os +from time import time if os.name != "nt": from module.utils.fs import chown @@ -35,9 +33,6 @@ from module.utils.fs import save_join, save_filename, fs_encode, fs_decode,\ # Import for Hoster Plugins chunks = _chunks -class Abort(Exception): - """ raised when aborted """ - class Reconnect(Exception): """ raised when reconnected """ @@ -170,6 +165,9 @@ class Hoster(Base): """the 'main' method of every plugin, you **have to** overwrite it""" raise NotImplementedError + def abort(self): + return self.pyfile.abort + def resetAccount(self): """ dont use account and retry download """ self.account = None @@ -208,7 +206,7 @@ class Hoster(Base): while self.pyfile.waitUntil > time(): self.thread.m.reconnecting.wait(2) - if self.pyfile.abort: raise Abort + self.checkAbort() if self.thread.m.reconnecting.isSet(): self.waiting = False self.wantReconnect = False @@ -243,88 +241,6 @@ class Hoster(Base): self.retries += 1 raise Retry(reason) - def invalidCaptcha(self): - if self.cTask: - self.cTask.invalid() - - def correctCaptcha(self): - if self.cTask: - self.cTask.correct() - - def decryptCaptcha(self, url, get={}, post={}, cookies=False, forceUser=False, imgtype='jpg', - result_type='textual'): - """ Loads a captcha and decrypts it with ocr, plugin, user input - - :param url: url of captcha image - :param get: get part for request - :param post: post part for request - :param cookies: True if cookies should be enabled - :param forceUser: if True, ocr is not used - :param imgtype: Type of the Image - :param result_type: 'textual' if text is written on the captcha\ - or 'positional' for captcha where the user have to click\ - on a specific region on the captcha - - :return: result of decrypting - """ - - img = self.load(url, get=get, post=post, cookies=cookies) - - id = ("%.2f" % time())[-6:].replace(".", "") - temp_file = open(join("tmp", "tmpCaptcha_%s_%s.%s" % (self.__name__, id, imgtype)), "wb") - temp_file.write(img) - temp_file.close() - - has_plugin = self.__name__ in self.core.pluginManager.getPlugins("captcha") - - if self.core.captcha: - Ocr = self.core.pluginManager.loadClass("captcha", self.__name__) - else: - Ocr = None - - if Ocr and not forceUser: - sleep(randint(3000, 5000) / 1000.0) - if self.pyfile.abort: raise Abort - - ocr = Ocr() - result = ocr.get_captcha(temp_file.name) - else: - captchaManager = self.core.captchaManager - task = captchaManager.newTask(img, imgtype, temp_file.name, result_type) - self.cTask = task - captchaManager.handleCaptcha(task) - - while task.isWaiting(): - if self.pyfile.abort: - captchaManager.removeTask(task) - raise Abort - sleep(1) - - captchaManager.removeTask(task) - - if task.error and has_plugin: #ignore default error message since the user could use OCR - self.fail(_("Pil and tesseract not installed and no Client connected for captcha decrypting")) - elif task.error: - self.fail(task.error) - elif not task.result: - self.fail(_("No captcha result obtained in appropiate time by any of the plugins.")) - - result = task.result - self.log.debug("Received captcha result: %s" % str(result)) - - if not self.core.debug: - try: - remove(temp_file.name) - except: - pass - - return result - - - def load(self, *args, **kwargs): - """ See 'Base' load method for more info """ - if self.pyfile.abort: raise Abort - return Base.load(self, *args, **kwargs) def download(self, url, get={}, post={}, ref=True, cookies=True, disposition=False): """Downloads the content at url to download folder @@ -338,8 +254,8 @@ class Hoster(Base): the filename will be changed if needed :return: The location where the file was saved """ - self.checkForSameFiles() + self.checkAbort() self.pyfile.setStatus("downloading") diff --git a/module/plugins/captcha/GigasizeCom.py b/module/plugins/captcha/GigasizeCom.py deleted file mode 100644 index d31742eb5..000000000 --- a/module/plugins/captcha/GigasizeCom.py +++ /dev/null @@ -1,19 +0,0 @@ -# -*- coding: utf-8 -*- -from captcha import OCR - -class GigasizeCom(OCR): - def __init__(self): - OCR.__init__(self) - - def get_captcha(self, image): - self.load_image(image) - self.threshold(2.8) - self.run_tesser(True, False, False, True) - return self.result_captcha - -if __name__ == '__main__': - ocr = GigasizeCom() - import urllib - urllib.urlretrieve('http://www.gigasize.com/randomImage.php', "gigasize_tmp.jpg") - - print ocr.get_captcha('gigasize_tmp.jpg') diff --git a/module/plugins/captcha/LinksaveIn.py b/module/plugins/captcha/LinksaveIn.py deleted file mode 100644 index 3ad7b265a..000000000 --- a/module/plugins/captcha/LinksaveIn.py +++ /dev/null @@ -1,147 +0,0 @@ -from captcha import OCR -import Image -from os import sep -from os.path import dirname -from os.path import abspath -from glob import glob - - -class LinksaveIn(OCR): - __name__ = "LinksaveIn" - def __init__(self): - OCR.__init__(self) - self.data_dir = dirname(abspath(__file__)) + sep + "LinksaveIn" + sep - - def load_image(self, image): - im = Image.open(image) - frame_nr = 0 - - lut = im.resize((256, 1)) - lut.putdata(range(256)) - lut = list(lut.convert("RGB").getdata()) - - new = Image.new("RGB", im.size) - npix = new.load() - while True: - try: - im.seek(frame_nr) - except EOFError: - break - frame = im.copy() - pix = frame.load() - for x in range(frame.size[0]): - for y in range(frame.size[1]): - if lut[pix[x, y]] != (0,0,0): - npix[x, y] = lut[pix[x, y]] - frame_nr += 1 - new.save(self.data_dir+"unblacked.png") - self.image = new.copy() - self.pixels = self.image.load() - self.result_captcha = '' - - def get_bg(self): - stat = {} - cstat = {} - img = self.image.convert("P") - for bgpath in glob(self.data_dir+"bg/*.gif"): - stat[bgpath] = 0 - bg = Image.open(bgpath) - - bglut = bg.resize((256, 1)) - bglut.putdata(range(256)) - bglut = list(bglut.convert("RGB").getdata()) - - lut = img.resize((256, 1)) - lut.putdata(range(256)) - lut = list(lut.convert("RGB").getdata()) - - bgpix = bg.load() - pix = img.load() - for x in range(bg.size[0]): - for y in range(bg.size[1]): - rgb_bg = bglut[bgpix[x, y]] - rgb_c = lut[pix[x, y]] - try: - cstat[rgb_c] += 1 - except: - cstat[rgb_c] = 1 - if rgb_bg == rgb_c: - stat[bgpath] += 1 - max_p = 0 - bg = "" - for bgpath, value in stat.items(): - if max_p < value: - bg = bgpath - max_p = value - return bg - - def substract_bg(self, bgpath): - bg = Image.open(bgpath) - img = self.image.convert("P") - - bglut = bg.resize((256, 1)) - bglut.putdata(range(256)) - bglut = list(bglut.convert("RGB").getdata()) - - lut = img.resize((256, 1)) - lut.putdata(range(256)) - lut = list(lut.convert("RGB").getdata()) - - bgpix = bg.load() - pix = img.load() - orgpix = self.image.load() - for x in range(bg.size[0]): - for y in range(bg.size[1]): - rgb_bg = bglut[bgpix[x, y]] - rgb_c = lut[pix[x, y]] - if rgb_c == rgb_bg: - orgpix[x, y] = (255,255,255) - - def eval_black_white(self): - new = Image.new("RGB", (140, 75)) - pix = new.load() - orgpix = self.image.load() - thresh = 4 - for x in range(new.size[0]): - for y in range(new.size[1]): - rgb = orgpix[x, y] - r, g, b = rgb - pix[x, y] = (255,255,255) - if r > max(b, g)+thresh: - pix[x, y] = (0,0,0) - if g < min(r, b): - pix[x, y] = (0,0,0) - if g > max(r, b)+thresh: - pix[x, y] = (0,0,0) - if b > max(r, g)+thresh: - pix[x, y] = (0,0,0) - self.image = new - self.pixels = self.image.load() - - def get_captcha(self, image): - self.load_image(image) - bg = self.get_bg() - self.substract_bg(bg) - self.eval_black_white() - self.to_greyscale() - self.image.save(self.data_dir+"cleaned_pass1.png") - self.clean(4) - self.clean(4) - self.image.save(self.data_dir+"cleaned_pass2.png") - letters = self.split_captcha_letters() - final = "" - for n, letter in enumerate(letters): - self.image = letter - self.image.save(ocr.data_dir+"letter%d.png" % n) - self.run_tesser(True, True, False, False) - final += self.result_captcha - - return final - -if __name__ == '__main__': - import urllib - ocr = LinksaveIn() - testurl = "http://linksave.in/captcha/cap.php?hsh=2229185&code=ZzHdhl3UffV3lXTH5U4b7nShXj%2Bwma1vyoNBcbc6lcc%3D" - urllib.urlretrieve(testurl, ocr.data_dir+"captcha.gif") - - print ocr.get_captcha(ocr.data_dir+'captcha.gif') diff --git a/module/plugins/captcha/MegauploadCom.py b/module/plugins/captcha/MegauploadCom.py deleted file mode 100644 index 469ee4094..000000000 --- a/module/plugins/captcha/MegauploadCom.py +++ /dev/null @@ -1,14 +0,0 @@ -from captcha import OCR - -class MegauploadCom(OCR): - __name__ = "MegauploadCom" - def __init__(self): - OCR.__init__(self) - - def get_captcha(self, image): - self.load_image(image) - self.run_tesser(True, True, False, True) - return self.result_captcha - -if __name__ == '__main__': - ocr = MegauploadCom() diff --git a/module/plugins/captcha/__init__.py b/module/plugins/captcha/__init__.py deleted file mode 100644 index e69de29bb..000000000 --- a/module/plugins/captcha/__init__.py +++ /dev/null diff --git a/module/plugins/captcha/NetloadIn.py b/module/plugins/internal/NetloadInOCR.py index 7f2e6a8d1..e50978701 100644 --- a/module/plugins/captcha/NetloadIn.py +++ b/module/plugins/internal/NetloadInOCR.py @@ -1,7 +1,10 @@ -from captcha import OCR +# -*- coding: utf-8 -*- + +from OCR import OCR + +class NetloadInOCR(OCR): + __version__ = 0.1 -class NetloadIn(OCR): - __name__ = "NetloadIn" def __init__(self): OCR.__init__(self) @@ -18,7 +21,7 @@ class NetloadIn(OCR): if __name__ == '__main__': import urllib - ocr = NetloadIn() + ocr = NetloadInOCR() urllib.urlretrieve("http://netload.in/share/includes/captcha.php", "captcha.png") print ocr.get_captcha('captcha.png') diff --git a/module/plugins/captcha/captcha.py b/module/plugins/internal/OCR.py index 4cbb736c1..9f8b7ef8c 100644 --- a/module/plugins/captcha/captcha.py +++ b/module/plugins/internal/OCR.py @@ -33,8 +33,7 @@ import JpegImagePlugin class OCR(object): - - __name__ = "OCR" + __version__ = 0.1 def __init__(self): self.logger = logging.getLogger("log") diff --git a/module/plugins/captcha/ShareonlineBiz.py b/module/plugins/internal/ShareonlineBizOCR.py index b07fb9b0f..c5c2e92e8 100644 --- a/module/plugins/captcha/ShareonlineBiz.py +++ b/module/plugins/internal/ShareonlineBizOCR.py @@ -17,10 +17,10 @@ # along with this program; if not, see <http://www.gnu.org/licenses/>. # ### -from captcha import OCR +from OCR import OCR -class ShareonlineBiz(OCR): - __name__ = "ShareonlineBiz" +class ShareonlineBizOCR(OCR): + __version__ = 0.1 def __init__(self): OCR.__init__(self) @@ -48,6 +48,6 @@ class ShareonlineBiz(OCR): if __name__ == '__main__': import urllib - ocr = ShareonlineBiz() + ocr = ShareonlineBizOCR() urllib.urlretrieve("http://www.share-online.biz/captcha.php", "captcha.jpeg") print ocr.get_captcha('captcha.jpeg') diff --git a/module/remote/socketbackend/ttypes.py b/module/remote/socketbackend/ttypes.py index 91430d720..1fd61ae72 100644 --- a/module/remote/socketbackend/ttypes.py +++ b/module/remote/socketbackend/ttypes.py @@ -367,6 +367,8 @@ class Iface: pass def getLog(self, offset): pass + def getNotifications(self): + pass def getPackageContent(self, pid): pass def getPackageInfo(self, pid): diff --git a/module/remote/thriftbackend/pyload.thrift b/module/remote/thriftbackend/pyload.thrift index bcf96324c..df82dae2d 100644 --- a/module/remote/thriftbackend/pyload.thrift +++ b/module/remote/thriftbackend/pyload.thrift @@ -415,8 +415,9 @@ service Pyload { // generate a download link, everybody can download the file until timeout reached string generateDownloadLink(1: FileID fid, 2: i16 timeout), - map<PluginName, list<AddonService>> getAddonHandler(), + list<InteractionTask> getNotifications(), + map<PluginName, list<AddonService>> getAddonHandler(), void callAddonHandler(1: PluginName plugin, 2: string func, 3: PackageID pid_or_fid), /////////////////////// diff --git a/module/remote/thriftbackend/thriftgen/pyload/Pyload-remote b/module/remote/thriftbackend/thriftgen/pyload/Pyload-remote index 6f0c09182..55f9a1823 100755 --- a/module/remote/thriftbackend/thriftgen/pyload/Pyload-remote +++ b/module/remote/thriftbackend/thriftgen/pyload/Pyload-remote @@ -91,6 +91,7 @@ if len(sys.argv) <= 1 or sys.argv[1] == '--help': print ' InteractionTask getInteractionTask(i16 mode)' print ' void setInteractionResult(InteractionID iid, ValueString result)' print ' string generateDownloadLink(FileID fid, i16 timeout)' + print ' getNotifications()' print ' getAddonHandler()' print ' void callAddonHandler(PluginName plugin, string func, PackageID pid_or_fid)' print ' getEvents(string uuid)' @@ -565,6 +566,12 @@ elif cmd == 'generateDownloadLink': sys.exit(1) pp.pprint(client.generateDownloadLink(eval(args[0]),eval(args[1]),)) +elif cmd == 'getNotifications': + if len(args) != 0: + print 'getNotifications requires 0 args' + sys.exit(1) + pp.pprint(client.getNotifications()) + elif cmd == 'getAddonHandler': if len(args) != 0: print 'getAddonHandler requires 0 args' diff --git a/module/remote/thriftbackend/thriftgen/pyload/Pyload.py b/module/remote/thriftbackend/thriftgen/pyload/Pyload.py index e58070a59..51c8621ba 100644 --- a/module/remote/thriftbackend/thriftgen/pyload/Pyload.py +++ b/module/remote/thriftbackend/thriftgen/pyload/Pyload.py @@ -450,6 +450,9 @@ class Iface(object): """ pass + def getNotifications(self, ): + pass + def getAddonHandler(self, ): pass @@ -2542,6 +2545,31 @@ class Client(Iface): return result.success raise TApplicationException(TApplicationException.MISSING_RESULT, "generateDownloadLink failed: unknown result"); + def getNotifications(self, ): + self.send_getNotifications() + return self.recv_getNotifications() + + def send_getNotifications(self, ): + self._oprot.writeMessageBegin('getNotifications', TMessageType.CALL, self._seqid) + args = getNotifications_args() + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_getNotifications(self, ): + (fname, mtype, rseqid) = self._iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(self._iprot) + self._iprot.readMessageEnd() + raise x + result = getNotifications_result() + result.read(self._iprot) + self._iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "getNotifications failed: unknown result"); + def getAddonHandler(self, ): self.send_getAddonHandler() return self.recv_getAddonHandler() @@ -3062,6 +3090,7 @@ class Processor(Iface, TProcessor): self._processMap["getInteractionTask"] = Processor.process_getInteractionTask self._processMap["setInteractionResult"] = Processor.process_setInteractionResult self._processMap["generateDownloadLink"] = Processor.process_generateDownloadLink + self._processMap["getNotifications"] = Processor.process_getNotifications self._processMap["getAddonHandler"] = Processor.process_getAddonHandler self._processMap["callAddonHandler"] = Processor.process_callAddonHandler self._processMap["getEvents"] = Processor.process_getEvents @@ -3868,6 +3897,17 @@ class Processor(Iface, TProcessor): oprot.writeMessageEnd() oprot.trans.flush() + def process_getNotifications(self, seqid, iprot, oprot): + args = getNotifications_args() + args.read(iprot) + iprot.readMessageEnd() + result = getNotifications_result() + result.success = self._handler.getNotifications() + oprot.writeMessageBegin("getNotifications", TMessageType.REPLY, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + def process_getAddonHandler(self, seqid, iprot, oprot): args = getAddonHandler_args() args.read(iprot) @@ -6350,6 +6390,33 @@ class generateDownloadLink_result(TBase): self.success = success +class getNotifications_args(TBase): + + __slots__ = [ + ] + + thrift_spec = ( + ) + + +class getNotifications_result(TBase): + """ + Attributes: + - success + """ + + __slots__ = [ + 'success', + ] + + thrift_spec = ( + (0, TType.LIST, 'success', (TType.STRUCT,(InteractionTask, InteractionTask.thrift_spec)), None, ), # 0 + ) + + def __init__(self, success=None,): + self.success = success + + class getAddonHandler_args(TBase): __slots__ = [ diff --git a/module/threads/DownloadThread.py b/module/threads/DownloadThread.py index 6239cddd8..8166191af 100644 --- a/module/threads/DownloadThread.py +++ b/module/threads/DownloadThread.py @@ -24,8 +24,8 @@ from traceback import print_exc from sys import exc_clear from pycurl import error -from module.plugins.Base import Fail, Retry -from module.plugins.Hoster import Abort, Reconnect, SkipDownload +from module.plugins.Base import Fail, Retry, Abort +from module.plugins.Hoster import Reconnect, SkipDownload from module.network.HTTPRequest import BadHeader from BaseThread import BaseThread diff --git a/module/utils/__init__.py b/module/utils/__init__.py index db43f330d..37e65c738 100644 --- a/module/utils/__init__.py +++ b/module/utils/__init__.py @@ -60,7 +60,7 @@ def compare_time(start, end): else: return False def to_list(value): - return value if type(value) == list else [value] + return value if type(value) == list else ([value] if value is not None else []) def formatSize(size): print "Deprecated formatSize, use format_size" diff --git a/module/web/json_app.py b/module/web/json_app.py index fcaa906e1..ed4f6bcfb 100644 --- a/module/web/json_app.py +++ b/module/web/json_app.py @@ -11,6 +11,7 @@ from webinterface import PYLOAD from utils import login_required, render_to_response, toDict +from module.Api import Output from module.utils import decode, format_size def get_sort_key(item): @@ -23,7 +24,7 @@ def get_sort_key(item): def status(): try: status = toDict(PYLOAD.statusServer()) - status['captcha'] = PYLOAD.isCaptchaWaiting() + status['captcha'] = PYLOAD.isInteractionWaiting(Output.Captcha) return status except: return HTTPError() @@ -34,7 +35,7 @@ def status(): @login_required('LIST') def links(): try: - links = [toDict(x) for x in PYLOAD.statusDownloads()] + links = [toDict(x) for x in PYLOAD.getProgressInfo()] ids = [] for link in links: ids.append(link['fid']) diff --git a/module/web/pyload_app.py b/module/web/pyload_app.py index 4edc6e0a5..4c448d2cd 100644 --- a/module/web/pyload_app.py +++ b/module/web/pyload_app.py @@ -36,6 +36,7 @@ from utils import render_to_response, parse_permissions, parse_userdata, \ from filters import relpath, unquotepath +from module.Api import Output from module.utils import format_size from module.utils.fs import save_join, fs_encode, fs_decode, listdir @@ -52,7 +53,7 @@ def pre_processor(): if user["is_authenticated"]: status = PYLOAD.statusServer() info = PYLOAD.getInfoByPlugin("UpdateManager") - captcha = PYLOAD.isCaptchaWaiting() + captcha = PYLOAD.isInteractionWaiting(Output.Captcha) # check if update check is available if info: |