summaryrefslogtreecommitdiffstats
path: root/module
diff options
context:
space:
mode:
Diffstat (limited to 'module')
-rw-r--r--module/Api.py35
-rw-r--r--module/interaction/InteractionManager.py127
-rw-r--r--module/interaction/InteractionTask.py17
-rw-r--r--module/network/HTTPDownload.py2
-rw-r--r--module/network/HTTPRequest.py2
-rw-r--r--module/plugins/Base.py118
-rw-r--r--module/plugins/Hoster.py96
-rw-r--r--module/plugins/captcha/GigasizeCom.py19
-rw-r--r--module/plugins/captcha/LinksaveIn.py147
-rw-r--r--module/plugins/captcha/MegauploadCom.py14
-rw-r--r--module/plugins/captcha/__init__.py0
-rw-r--r--module/plugins/internal/NetloadInOCR.py (renamed from module/plugins/captcha/NetloadIn.py)11
-rw-r--r--module/plugins/internal/OCR.py (renamed from module/plugins/captcha/captcha.py)3
-rw-r--r--module/plugins/internal/ShareonlineBizOCR.py (renamed from module/plugins/captcha/ShareonlineBiz.py)8
-rw-r--r--module/remote/socketbackend/ttypes.py2
-rw-r--r--module/remote/thriftbackend/pyload.thrift3
-rwxr-xr-xmodule/remote/thriftbackend/thriftgen/pyload/Pyload-remote7
-rw-r--r--module/remote/thriftbackend/thriftgen/pyload/Pyload.py67
-rw-r--r--module/threads/DownloadThread.py4
-rw-r--r--module/utils/__init__.py2
-rw-r--r--module/web/json_app.py5
-rw-r--r--module/web/pyload_app.py3
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: