summaryrefslogtreecommitdiffstats
path: root/module
diff options
context:
space:
mode:
Diffstat (limited to 'module')
-rw-r--r--module/plugins/accounts/Keep2ShareCc.py73
-rw-r--r--module/plugins/accounts/MegaRapidoNet.py57
-rw-r--r--module/plugins/captcha/OCR.py319
-rw-r--r--module/plugins/crypter/DailymotionComFolder.py106
-rw-r--r--module/plugins/crypter/YoutubeComFolder.py148
-rw-r--r--module/plugins/hooks/AntiVirus.py95
-rw-r--r--module/plugins/hooks/MegaRapidoNet.py81
-rw-r--r--module/plugins/hoster/MegaRapidoNet.py54
-rw-r--r--module/plugins/internal/CaptchaService.py526
9 files changed, 1459 insertions, 0 deletions
diff --git a/module/plugins/accounts/Keep2ShareCc.py b/module/plugins/accounts/Keep2ShareCc.py
new file mode 100644
index 000000000..d2ba1d237
--- /dev/null
+++ b/module/plugins/accounts/Keep2ShareCc.py
@@ -0,0 +1,73 @@
+# -*- coding: utf-8 -*-
+
+import re
+import time
+
+from module.plugins.Account import Account
+
+
+class Keep2ShareCc(Account):
+ __name__ = "Keep2ShareCc"
+ __type__ = "account"
+ __version__ = "0.05"
+
+ __description__ = """Keep2Share.cc account plugin"""
+ __license__ = "GPLv3"
+ __authors__ = [("aeronaut", "aeronaut@pianoguy.de"),
+ ("Walter Purcaro", "vuolter@gmail.com")]
+
+
+ VALID_UNTIL_PATTERN = r'Premium expires:\s*<b>(.+?)<'
+ TRAFFIC_LEFT_PATTERN = r'Available traffic \(today\):\s*<b><a href="/user/statistic.html">(.+?)<'
+
+ LOGIN_FAIL_PATTERN = r'Please fix the following input errors'
+
+
+ def loadAccountInfo(self, user, req):
+ validuntil = None
+ trafficleft = -1
+ premium = False
+
+ html = req.load("http://keep2share.cc/site/profile.html", decode=True)
+
+ m = re.search(self.VALID_UNTIL_PATTERN, html)
+ if m:
+ expiredate = m.group(1).strip()
+ self.logDebug("Expire date: " + expiredate)
+
+ if expiredate == "LifeTime":
+ premium = True
+ validuntil = -1
+ else:
+ try:
+ validuntil = time.mktime(time.strptime(expiredate, "%Y.%m.%d"))
+
+ except Exception, e:
+ self.logError(e)
+
+ else:
+ premium = True if validuntil > time.mktime(time.gmtime()) else False
+
+ m = re.search(self.TRAFFIC_LEFT_PATTERN, html)
+ if m:
+ try:
+ trafficleft = self.parseTraffic(m.group(1))
+
+ except Exception, e:
+ self.logError(e)
+
+ return {'validuntil': validuntil, 'trafficleft': trafficleft, 'premium': premium}
+
+
+ def login(self, user, data, req):
+ req.cj.setCookie("keep2share.cc", "lang", "en")
+
+ html = req.load("http://keep2share.cc/login.html",
+ post={'LoginForm[username]' : user,
+ 'LoginForm[password]' : data['password'],
+ 'LoginForm[rememberMe]': 1,
+ 'yt0' : ""},
+ decode=True)
+
+ if re.search(self.LOGIN_FAIL_PATTERN, html):
+ self.wrongPassword()
diff --git a/module/plugins/accounts/MegaRapidoNet.py b/module/plugins/accounts/MegaRapidoNet.py
new file mode 100644
index 000000000..80e066244
--- /dev/null
+++ b/module/plugins/accounts/MegaRapidoNet.py
@@ -0,0 +1,57 @@
+# -*- coding: utf-8 -*-
+
+import re
+import time
+
+from module.plugins.Account import Account
+
+
+class MegaRapidoNet(Account):
+ __name__ = "MegaRapidoNet"
+ __type__ = "account"
+ __version__ = "0.02"
+
+ __description__ = """MegaRapido.net account plugin"""
+ __license__ = "GPLv3"
+ __authors__ = [("Kagenoshin", "kagenoshin@gmx.ch")]
+
+
+ VALID_UNTIL_PATTERN = r'<\s*?div[^>]*?class\s*?=\s*?[\'"]premium_index[\'"][^>]*>[^<]*?<[^>]*?b[^>]*>\s*?TEMPO\s*?PREMIUM[^<]*<[^>]*?/b[^>]*>\s*?(\d*)[^\d]*?DIAS[^\d]*?(\d*)[^\d]*?HORAS[^\d]*?(\d*)[^\d]*?MINUTOS[^\d]*?(\d*)[^\d]*?SEGUNDOS'
+ USER_ID_PATTERN = r'<\s*?div[^>]*?class\s*?=\s*?["\']checkbox_compartilhar["\'][^>]*>[^<]*<\s*?input[^>]*?name\s*?=\s*?["\']usar["\'][^>]*>[^<]*<\s*?input[^>]*?name\s*?=\s*?["\']user["\'][^>]*?value\s*?=\s*?["\'](.*?)\s*?["\']'
+
+
+ def loadAccountInfo(self, user, req):
+ validuntil = None
+ trafficleft = None
+ premium = False
+
+ html = req.load("http://megarapido.net/gerador", decode=True)
+
+ validuntil = re.search(self.VALID_UNTIL_PATTERN, html)
+ if validuntil:
+ #hier weitermachen!!! (müssen umbedingt die zeit richtig machen damit! (sollte aber möglich))
+ validuntil = time.time() + int(validuntil.group(1)) * 24 * 3600 + int(validuntil.group(2)) * 3600 + int(validuntil.group(3)) * 60 + int(validuntil.group(4))
+ trafficleft = -1
+ premium = True
+
+ return {'validuntil' : validuntil,
+ 'trafficleft': trafficleft,
+ 'premium' : premium}
+
+
+ def login(self, user, data, req):
+ req.load("http://megarapido.net/login")
+ req.load("http://megarapido.net/painel_user/ajax/logar.php",
+ post={'login': user, 'senha': data['password']},
+ decode=True)
+
+ html = req.load("http://megarapido.net/gerador")
+
+ if "sair" not in html.lower():
+ self.wrongPassword()
+ else:
+ m = re.search(self.USER_ID_PATTERN, html)
+ if m:
+ data['uid'] = m.group(1)
+ else:
+ self.fail("Couldn't find the user ID")
diff --git a/module/plugins/captcha/OCR.py b/module/plugins/captcha/OCR.py
new file mode 100644
index 000000000..1874ba07d
--- /dev/null
+++ b/module/plugins/captcha/OCR.py
@@ -0,0 +1,319 @@
+# -*- coding: utf-8 -*-
+
+from __future__ import with_statement
+
+try:
+ from PIL import Image, GifImagePlugin, JpegImagePlugin, PngImagePlugin, TiffImagePlugin
+
+except ImportError:
+ import Image, GifImagePlugin, JpegImagePlugin, PngImagePlugin, TiffImagePlugin
+
+import logging
+import os
+import subprocess
+#import tempfile
+
+from module.utils import save_join
+
+
+class OCR(object):
+ __name__ = "OCR"
+ __type__ = "ocr"
+ __version__ = "0.11"
+
+ __description__ = """OCR base plugin"""
+ __license__ = "GPLv3"
+ __authors__ = [("pyLoad Team", "admin@pyload.org")]
+
+
+ def __init__(self):
+ self.logger = logging.getLogger("log")
+
+
+ def load_image(self, image):
+ self.image = Image.open(image)
+ self.pixels = self.image.load()
+ self.result_captcha = ''
+
+
+ def unload(self):
+ """delete all tmp images"""
+ pass
+
+
+ def threshold(self, value):
+ self.image = self.image.point(lambda a: a * value + 10)
+
+
+ def run(self, command):
+ """Run a command"""
+
+ popen = subprocess.Popen(command, bufsize = -1, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ popen.wait()
+ output = popen.stdout.read() +" | "+ popen.stderr.read()
+ popen.stdout.close()
+ popen.stderr.close()
+ self.logger.debug("Tesseract ReturnCode %s Output: %s" % (popen.returncode, output))
+
+
+ def run_tesser(self, subset=False, digits=True, lowercase=True, uppercase=True):
+ #tmpTif = tempfile.NamedTemporaryFile(suffix=".tif")
+ try:
+ tmpTif = open(save_join("tmp", "tmpTif_%s.tif" % self.__name__), "wb")
+ tmpTif.close()
+
+ #tmpTxt = tempfile.NamedTemporaryFile(suffix=".txt")
+ tmpTxt = open(save_join("tmp", "tmpTxt_%s.txt" % self.__name__), "wb")
+ tmpTxt.close()
+
+ except IOError, e:
+ self.logError(e)
+ return
+
+ self.logger.debug("save tiff")
+ self.image.save(tmpTif.name, 'TIFF')
+
+ if os.name == "nt":
+ tessparams = [os.path.join(pypath, "tesseract", "tesseract.exe")]
+ else:
+ tessparams = ["tesseract"]
+
+ tessparams.extend( [os.path.abspath(tmpTif.name), os.path.abspath(tmpTxt.name).replace(".txt", "")] )
+
+ if subset and (digits or lowercase or uppercase):
+ #tmpSub = tempfile.NamedTemporaryFile(suffix=".subset")
+ with open(save_join("tmp", "tmpSub_%s.subset" % self.__name__), "wb") as tmpSub:
+ tmpSub.write("tessedit_char_whitelist ")
+
+ if digits:
+ tmpSub.write("0123456789")
+ if lowercase:
+ tmpSub.write("abcdefghijklmnopqrstuvwxyz")
+ if uppercase:
+ tmpSub.write("ABCDEFGHIJKLMNOPQRSTUVWXYZ")
+
+ tmpSub.write("\n")
+ tessparams.append("nobatch")
+ tessparams.append(os.path.abspath(tmpSub.name))
+
+ self.logger.debug("run tesseract")
+ self.run(tessparams)
+ self.logger.debug("read txt")
+
+ try:
+ with open(tmpTxt.name, 'r') as f:
+ self.result_captcha = f.read().replace("\n", "")
+ except Exception:
+ self.result_captcha = ""
+
+ self.logger.debug(self.result_captcha)
+ try:
+ os.remove(tmpTif.name)
+ os.remove(tmpTxt.name)
+ if subset and (digits or lowercase or uppercase):
+ os.remove(tmpSub.name)
+ except Exception:
+ pass
+
+
+ def get_captcha(self, name):
+ raise NotImplementedError
+
+
+ def to_greyscale(self):
+ if self.image.mode != 'L':
+ self.image = self.image.convert('L')
+
+ self.pixels = self.image.load()
+
+
+ def eval_black_white(self, limit):
+ self.pixels = self.image.load()
+ w, h = self.image.size
+ for x in xrange(w):
+ for y in xrange(h):
+ if self.pixels[x, y] > limit:
+ self.pixels[x, y] = 255
+ else:
+ self.pixels[x, y] = 0
+
+
+ def clean(self, allowed):
+ pixels = self.pixels
+
+ w, h = self.image.size
+
+ for x in xrange(w):
+ for y in xrange(h):
+ if pixels[x, y] == 255:
+ continue
+ # No point in processing white pixels since we only want to remove black pixel
+ count = 0
+
+ try:
+ if pixels[x-1, y-1] != 255:
+ count += 1
+ if pixels[x-1, y] != 255:
+ count += 1
+ if pixels[x-1, y + 1] != 255:
+ count += 1
+ if pixels[x, y + 1] != 255:
+ count += 1
+ if pixels[x + 1, y + 1] != 255:
+ count += 1
+ if pixels[x + 1, y] != 255:
+ count += 1
+ if pixels[x + 1, y-1] != 255:
+ count += 1
+ if pixels[x, y-1] != 255:
+ count += 1
+ except Exception:
+ pass
+
+ # not enough neighbors are dark pixels so mark this pixel
+ # to be changed to white
+ if count < allowed:
+ pixels[x, y] = 1
+
+ # second pass: this time set all 1's to 255 (white)
+ for x in xrange(w):
+ for y in xrange(h):
+ if pixels[x, y] == 1:
+ pixels[x, y] = 255
+
+ self.pixels = pixels
+
+
+ def derotate_by_average(self):
+ """rotate by checking each angle and guess most suitable"""
+
+ w, h = self.image.size
+ pixels = self.pixels
+
+ for x in xrange(w):
+ for y in xrange(h):
+ if pixels[x, y] == 0:
+ pixels[x, y] = 155
+
+ highest = {}
+ counts = {}
+
+ for angle in xrange(-45, 45):
+
+ tmpimage = self.image.rotate(angle)
+
+ pixels = tmpimage.load()
+
+ w, h = self.image.size
+
+ for x in xrange(w):
+ for y in xrange(h):
+ if pixels[x, y] == 0:
+ pixels[x, y] = 255
+
+
+ count = {}
+
+ for x in xrange(w):
+ count[x] = 0
+ for y in xrange(h):
+ if pixels[x, y] == 155:
+ count[x] += 1
+
+ sum = 0
+ cnt = 0
+
+ for x in count.values():
+ if x != 0:
+ sum += x
+ cnt += 1
+
+ avg = sum / cnt
+ counts[angle] = cnt
+ highest[angle] = 0
+ for x in count.values():
+ if x > highest[angle]:
+ highest[angle] = x
+
+ highest[angle] = highest[angle] - avg
+
+ hkey = 0
+ hvalue = 0
+
+ for key, value in highest.iteritems():
+ if value > hvalue:
+ hkey = key
+ hvalue = value
+
+ self.image = self.image.rotate(hkey)
+ pixels = self.image.load()
+
+ for x in xrange(w):
+ for y in xrange(h):
+ if pixels[x, y] == 0:
+ pixels[x, y] = 255
+
+ if pixels[x, y] == 155:
+ pixels[x, y] = 0
+
+ self.pixels = pixels
+
+
+ def split_captcha_letters(self):
+ captcha = self.image
+ started = False
+ letters = []
+ width, height = captcha.size
+ bottomY, topY = 0, height
+ pixels = captcha.load()
+
+ for x in xrange(width):
+ black_pixel_in_col = False
+ for y in xrange(height):
+ if pixels[x, y] != 255:
+ if not started:
+ started = True
+ firstX = x
+ lastX = x
+
+ if y > bottomY:
+ bottomY = y
+ if y < topY:
+ topY = y
+ if x > lastX:
+ lastX = x
+
+ black_pixel_in_col = True
+
+ if black_pixel_in_col is False and started is True:
+ rect = (firstX, topY, lastX, bottomY)
+ new_captcha = captcha.crop(rect)
+
+ w, h = new_captcha.size
+ if w > 5 and h > 5:
+ letters.append(new_captcha)
+
+ started = False
+ bottomY, topY = 0, height
+
+ return letters
+
+
+ def correct(self, values, var=None):
+ if var:
+ result = var
+ else:
+ result = self.result_captcha
+
+ for key, item in values.iteritems():
+
+ if key.__class__ == str:
+ result = result.replace(key, item)
+ else:
+ for expr in key:
+ result = result.replace(expr, item)
+
+ if var:
+ return result
+ else:
+ self.result_captcha = result
diff --git a/module/plugins/crypter/DailymotionComFolder.py b/module/plugins/crypter/DailymotionComFolder.py
new file mode 100644
index 000000000..5c078000a
--- /dev/null
+++ b/module/plugins/crypter/DailymotionComFolder.py
@@ -0,0 +1,106 @@
+# -*- coding: utf-8 -*-
+
+import re
+
+from urlparse import urljoin
+
+from module.common.json_layer import json_loads
+from module.plugins.Crypter import Crypter
+from module.utils import save_join
+
+
+class DailymotionComFolder(Crypter):
+ __name__ = "DailymotionComFolder"
+ __type__ = "crypter"
+ __version__ = "0.01"
+
+ __pattern__ = r'https?://(?:www\.)?dailymotion\.com/((playlists/)?(?P<TYPE>playlist|user)/)?(?P<ID>[\w^_]+)(?(TYPE)|#)'
+ __config__ = [("use_subfolder" , "bool", "Save package to subfolder" , True),
+ ("subfolder_per_pack", "bool", "Create a subfolder for each package", True)]
+
+ __description__ = """Dailymotion.com channel & playlist decrypter"""
+ __license__ = "GPLv3"
+ __authors__ = [("Walter Purcaro", "vuolter@gmail.com")]
+
+
+ def api_response(self, ref, req=None):
+ url = urljoin("https://api.dailymotion.com/", ref)
+ html = self.load(url, get=req)
+ return json_loads(html)
+
+
+ def getPlaylistInfo(self, id):
+ ref = "playlist/" + id
+ req = {"fields": "name,owner.screenname"}
+ playlist = self.api_response(ref, req)
+
+ if "error" in playlist:
+ return
+
+ name = playlist['name']
+ owner = playlist['owner.screenname']
+ return name, owner
+
+
+ def _getPlaylists(self, user_id, page=1):
+ ref = "user/%s/playlists" % user_id
+ req = {"fields": "id", "page": page, "limit": 100}
+ user = self.api_response(ref, req)
+
+ if "error" in user:
+ return
+
+ for playlist in user['list']:
+ yield playlist['id']
+
+ if user['has_more']:
+ for item in self._getPlaylists(user_id, page + 1):
+ yield item
+
+
+ def getPlaylists(self, user_id):
+ return [(id,) + self.getPlaylistInfo(id) for id in self._getPlaylists(user_id)]
+
+
+ def _getVideos(self, id, page=1):
+ ref = "playlist/%s/videos" % id
+ req = {"fields": "url", "page": page, "limit": 100}
+ playlist = self.api_response(ref, req)
+
+ if "error" in playlist:
+ return
+
+ for video in playlist['list']:
+ yield video['url']
+
+ if playlist['has_more']:
+ for item in self._getVideos(id, page + 1):
+ yield item
+
+
+ def getVideos(self, playlist_id):
+ return list(self._getVideos(playlist_id))[::-1]
+
+
+ def decrypt(self, pyfile):
+ m = re.match(self.__pattern__, pyfile.url)
+ m_id = m.group('ID')
+ m_type = m.group('TYPE')
+
+ if m_type == "playlist":
+ self.logDebug("Url recognized as Playlist")
+ p_info = self.getPlaylistInfo(m_id)
+ playlists = [(m_id,) + p_info] if p_info else None
+ else:
+ self.logDebug("Url recognized as Channel")
+ playlists = self.getPlaylists(m_id)
+ self.logDebug("%s playlist\s found on channel \"%s\"" % (len(playlists), m_id))
+
+ if not playlists:
+ self.fail(_("No playlist available"))
+
+ for p_id, p_name, p_owner in playlists:
+ p_videos = self.getVideos(p_id)
+ p_folder = save_join(self.config['general']['download_folder'], p_owner, p_name)
+ self.logDebug("%s video\s found on playlist \"%s\"" % (len(p_videos), p_name))
+ self.packages.append((p_name, p_videos, p_folder)) #: folder is NOT recognized by pyload 0.4.9!
diff --git a/module/plugins/crypter/YoutubeComFolder.py b/module/plugins/crypter/YoutubeComFolder.py
new file mode 100644
index 000000000..d7ca494fa
--- /dev/null
+++ b/module/plugins/crypter/YoutubeComFolder.py
@@ -0,0 +1,148 @@
+# -*- coding: utf-8 -*-
+
+import re
+
+from urlparse import urljoin
+
+from module.common.json_layer import json_loads
+from module.plugins.Crypter import Crypter
+from module.utils import save_join
+
+
+class YoutubeComFolder(Crypter):
+ __name__ = "YoutubeComFolder"
+ __type__ = "crypter"
+ __version__ = "1.01"
+
+ __pattern__ = r'https?://(?:www\.|m\.)?youtube\.com/(?P<TYPE>user|playlist|view_play_list)(/|.*?[?&](?:list|p)=)(?P<ID>[\w-]+)'
+ __config__ = [("use_subfolder" , "bool", "Save package to subfolder" , True ),
+ ("subfolder_per_pack", "bool", "Create a subfolder for each package", True ),
+ ("likes" , "bool", "Grab user (channel) liked videos" , False),
+ ("favorites" , "bool", "Grab user (channel) favorite videos", False),
+ ("uploads" , "bool", "Grab channel unplaylisted videos" , True )]
+
+ __description__ = """Youtube.com channel & playlist decrypter plugin"""
+ __license__ = "GPLv3"
+ __authors__ = [("Walter Purcaro", "vuolter@gmail.com")]
+
+
+ API_KEY = "AIzaSyCKnWLNlkX-L4oD1aEzqqhRw1zczeD6_k0"
+
+
+ def api_response(self, ref, req):
+ req.update({"key": self.API_KEY})
+ url = urljoin("https://www.googleapis.com/youtube/v3/", ref)
+ html = self.load(url, get=req)
+ return json_loads(html)
+
+
+ def getChannel(self, user):
+ channels = self.api_response("channels", {"part": "id,snippet,contentDetails", "forUsername": user, "maxResults": "50"})
+ if channels['items']:
+ channel = channels['items'][0]
+ return {"id": channel['id'],
+ "title": channel['snippet']['title'],
+ "relatedPlaylists": channel['contentDetails']['relatedPlaylists'],
+ "user": user} # One lone channel for user?
+
+
+ def getPlaylist(self, p_id):
+ playlists = self.api_response("playlists", {"part": "snippet", "id": p_id})
+ if playlists['items']:
+ playlist = playlists['items'][0]
+ return {"id": p_id,
+ "title": playlist['snippet']['title'],
+ "channelId": playlist['snippet']['channelId'],
+ "channelTitle": playlist['snippet']['channelTitle']}
+
+
+ def _getPlaylists(self, id, token=None):
+ req = {"part": "id", "maxResults": "50", "channelId": id}
+ if token:
+ req.update({"pageToken": token})
+
+ playlists = self.api_response("playlists", req)
+
+ for playlist in playlists['items']:
+ yield playlist['id']
+
+ if "nextPageToken" in playlists:
+ for item in self._getPlaylists(id, playlists['nextPageToken']):
+ yield item
+
+
+ def getPlaylists(self, ch_id):
+ return map(self.getPlaylist, self._getPlaylists(ch_id))
+
+
+ def _getVideosId(self, id, token=None):
+ req = {"part": "contentDetails", "maxResults": "50", "playlistId": id}
+ if token:
+ req.update({"pageToken": token})
+
+ playlist = self.api_response("playlistItems", req)
+
+ for item in playlist['items']:
+ yield item['contentDetails']['videoId']
+
+ if "nextPageToken" in playlist:
+ for item in self._getVideosId(id, playlist['nextPageToken']):
+ yield item
+
+
+ def getVideosId(self, p_id):
+ return list(self._getVideosId(p_id))
+
+
+ def decrypt(self, pyfile):
+ m = re.match(self.__pattern__, pyfile.url)
+ m_id = m.group('ID')
+ m_type = m.group('TYPE')
+
+ if m_type == "user":
+ self.logDebug("Url recognized as Channel")
+ user = m_id
+ channel = self.getChannel(user)
+
+ if channel:
+ playlists = self.getPlaylists(channel['id'])
+ self.logDebug("%s playlist\s found on channel \"%s\"" % (len(playlists), channel['title']))
+
+ relatedplaylist = {p_name: self.getPlaylist(p_id) for p_name, p_id in channel['relatedPlaylists'].iteritems()}
+ self.logDebug("Channel's related playlists found = %s" % relatedplaylist.keys())
+
+ relatedplaylist['uploads']['title'] = "Unplaylisted videos"
+ relatedplaylist['uploads']['checkDups'] = True #: checkDups flag
+
+ for p_name, p_data in relatedplaylist.iteritems():
+ if self.getConfig(p_name):
+ p_data['title'] += " of " + user
+ playlists.append(p_data)
+ else:
+ playlists = []
+ else:
+ self.logDebug("Url recognized as Playlist")
+ playlists = [self.getPlaylist(m_id)]
+
+ if not playlists:
+ self.fail(_("No playlist available"))
+
+ addedvideos = []
+ urlize = lambda x: "https://www.youtube.com/watch?v=" + x
+ for p in playlists:
+ p_name = p['title']
+ p_videos = self.getVideosId(p['id'])
+ p_folder = save_join(self.config['general']['download_folder'], p['channelTitle'], p_name)
+ self.logDebug("%s video\s found on playlist \"%s\"" % (len(p_videos), p_name))
+
+ if not p_videos:
+ continue
+ elif "checkDups" in p:
+ p_urls = [urlize(v_id) for v_id in p_videos if v_id not in addedvideos]
+ self.logDebug("%s video\s available on playlist \"%s\" after duplicates cleanup" % (len(p_urls), p_name))
+ else:
+ p_urls = map(urlize, p_videos)
+
+ self.packages.append((p_name, p_urls, p_folder)) #: folder is NOT recognized by pyload 0.4.9!
+
+ addedvideos.extend(p_videos)
diff --git a/module/plugins/hooks/AntiVirus.py b/module/plugins/hooks/AntiVirus.py
new file mode 100644
index 000000000..cc3c5c754
--- /dev/null
+++ b/module/plugins/hooks/AntiVirus.py
@@ -0,0 +1,95 @@
+# -*- coding: utf-8 -*-
+
+import os
+import shutil
+import subprocess
+
+from module.plugins.Hook import Hook, Expose, threaded
+from module.utils import fs_encode, save_join
+
+
+class AntiVirus(Hook):
+ __name__ = "AntiVirus"
+ __type__ = "hook"
+ __version__ = "0.05"
+
+ #@TODO: add trash option (use Send2Trash lib)
+ __config__ = [("action" , "Antivirus default;Delete;Quarantine", "Manage infected files" , "Antivirus default"),
+ ("quardir" , "folder" , "Quarantine folder" , "" ),
+ ("scanfailed", "bool" , "Scan incompleted files (failed downloads)", False ),
+ ("cmdfile" , "file" , "Antivirus executable" , "" ),
+ ("cmdargs" , "str" , "Scan options" , "" ),
+ ("ignore-err", "bool" , "Ignore scan errors" , False )]
+
+ __description__ = """Scan downloaded files with antivirus program"""
+ __license__ = "GPLv3"
+ __authors__ = [("Walter Purcaro", "vuolter@gmail.com")]
+
+
+ interval = 0 #@TODO: Remove in 0.4.10
+
+
+ def setup(self):
+ self.info = {} #@TODO: Remove in 0.4.10
+
+
+ @Expose
+ @threaded
+ def scan(self, pyfile, thread):
+ file = fs_encode(pyfile.plugin.lastDownload)
+ filename = os.path.basename(pyfile.plugin.lastDownload)
+ cmdfile = fs_encode(self.getConfig('cmdfile'))
+ cmdargs = fs_encode(self.getConfig('cmdargs').strip())
+
+ if not os.path.isfile(file) or not os.path.isfile(cmdfile):
+ return
+
+ thread.addActive(pyfile)
+ pyfile.setCustomStatus(_("virus scanning"))
+
+ try:
+ p = subprocess.Popen([cmdfile, cmdargs, file], bufsize=-1, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+
+ out, err = map(str.strip, p.communicate())
+
+ if out:
+ self.logInfo(filename, out)
+
+ if err:
+ self.logWarning(filename, err)
+ if not self.getConfig('ignore-err'):
+ self.logDebug("Delete/Quarantine task is aborted")
+ return
+
+ if p.returncode:
+ pyfile.error = _("infected file")
+ action = self.getConfig('action')
+ try:
+ if action == "Delete":
+ os.remove(file)
+
+ elif action == "Quarantine":
+ pyfile.setCustomStatus(_("file moving"))
+ pyfile.setProgress(0)
+ shutil.move(file, self.getConfig('quardir'))
+
+ except (IOError, shutil.Error), e:
+ self.logError(filename, action + " action failed!", e)
+
+ elif not out and not err:
+ self.logDebug(filename, "No infected file found")
+
+ finally:
+ pyfile.setProgress(100)
+ thread.finishFile(pyfile)
+
+
+ def downloadFinished(self, pyfile):
+ return self.scan(pyfile)
+
+
+ def downloadFailed(self, pyfile):
+ #: Check if pyfile is still "failed",
+ # maybe might has been restarted in meantime
+ if pyfile.status == 8 and self.getConfig('scanfailed'):
+ return self.scan(pyfile)
diff --git a/module/plugins/hooks/MegaRapidoNet.py b/module/plugins/hooks/MegaRapidoNet.py
new file mode 100644
index 000000000..fb5e77994
--- /dev/null
+++ b/module/plugins/hooks/MegaRapidoNet.py
@@ -0,0 +1,81 @@
+# -*- coding: utf-8 -*-
+
+import re
+
+from module.plugins.internal.MultiHook import MultiHook
+
+
+class MegaRapidoNet(MultiHook):
+ __name__ = "MegaRapidoNet"
+ __type__ = "hook"
+ __version__ = "0.02"
+
+ __config__ = [("pluginmode" , "all;listed;unlisted", "Use for plugins" , "all"),
+ ("pluginlist" , "str" , "Plugin list (comma separated)", "" ),
+ ("reload" , "bool" , "Reload plugin list" , True ),
+ ("reloadinterval", "int" , "Reload interval in hours" , 12 )]
+
+ __description__ = """MegaRapido.net hook plugin"""
+ __license__ = "GPLv3"
+ __authors__ = [("Kagenoshin", "kagenoshin@gmx.ch")]
+
+
+ def getHosters(self):
+ hosters = {'1fichier' : [],#leave it there are so many possible addresses?
+ '1st-files' : ['1st-files.com'],
+ '2shared' : ['2shared.com'],
+ '4shared' : ['4shared.com', '4shared-china.com'],
+ 'asfile' : ['http://asfile.com/'],
+ 'bitshare' : ['bitshare.com'],
+ 'brupload' : ['brupload.net'],
+ 'crocko' : ['crocko.com','easy-share.com'],
+ 'dailymotion' : ['dailymotion.com'],
+ 'depfile' : ['depfile.com'],
+ 'depositfiles': ['depositfiles.com', 'dfiles.eu'],
+ 'dizzcloud' : ['dizzcloud.com'],
+ 'dl.dropbox' : [],
+ 'extabit' : ['extabit.com'],
+ 'extmatrix' : ['extmatrix.com'],
+ 'facebook' : [],
+ 'file4go' : ['file4go.com'],
+ 'filecloud' : ['filecloud.io','ifile.it','mihd.net'],
+ 'filefactory' : ['filefactory.com'],
+ 'fileom' : ['fileom.com'],
+ 'fileparadox' : ['fileparadox.in'],
+ 'filepost' : ['filepost.com', 'fp.io'],
+ 'filerio' : ['filerio.in','filerio.com','filekeen.com'],
+ 'filesflash' : ['filesflash.com'],
+ 'firedrive' : ['firedrive.com', 'putlocker.com'],
+ 'flashx' : [],
+ 'freakshare' : ['freakshare.net', 'freakshare.com'],
+ 'gigasize' : ['gigasize.com'],
+ 'hipfile' : ['hipfile.com'],
+ 'junocloud' : ['junocloud.me'],
+ 'letitbit' : ['letitbit.net','shareflare.net'],
+ 'mediafire' : ['mediafire.com'],
+ 'mega' : ['mega.co.nz'],
+ 'megashares' : ['megashares.com'],
+ 'metacafe' : ['metacafe.com'],
+ 'netload' : ['netload.in'],
+ 'oboom' : ['oboom.com'],
+ 'rapidgator' : ['rapidgator.net'],
+ 'rapidshare' : ['rapidshare.com'],
+ 'rarefile' : ['rarefile.net'],
+ 'ryushare' : ['ryushare.com'],
+ 'sendspace' : ['sendspace.com'],
+ 'turbobit' : ['turbobit.net', 'unextfiles.com'],
+ 'uploadable' : ['uploadable.ch'],
+ 'uploadbaz' : ['uploadbaz.com'],
+ 'uploaded' : ['uploaded.to', 'uploaded.net', 'ul.to'],
+ 'uploadhero' : ['uploadhero.com'],
+ 'uploading' : ['uploading.com'],
+ 'uptobox' : ['uptobox.com'],
+ 'xvideos' : ['xvideos.com'],
+ 'youtube' : ['youtube.com']}
+
+ hoster_list = []
+
+ for item in hosters.itervalues():
+ hoster_list.extend(item)
+
+ return hoster_list
diff --git a/module/plugins/hoster/MegaRapidoNet.py b/module/plugins/hoster/MegaRapidoNet.py
new file mode 100644
index 000000000..b38374646
--- /dev/null
+++ b/module/plugins/hoster/MegaRapidoNet.py
@@ -0,0 +1,54 @@
+# -*- coding: utf-8 -*-
+
+from random import randint
+
+from module.plugins.internal.MultiHoster import MultiHoster
+
+
+def random_with_N_digits(n):
+ rand = "0."
+ not_zero = 0
+ for i in range(1,n+1):
+ r = randint(0,9)
+ if(r > 0):
+ not_zero += 1
+ rand += str(r)
+
+ if not_zero > 0:
+ return rand
+ else:
+ return random_with_N_digits(n)
+
+
+class MegaRapidoNet(MultiHoster):
+ __name__ = "MegaRapidoNet"
+ __type__ = "hoster"
+ __version__ = "0.02"
+
+ __pattern__ = r'http://(?:www\.)?\w+\.megarapido\.net/\?file=\w+'
+ __config__ = [("use_premium", "bool", "Use premium account if available", True)]
+
+ __description__ = """MegaRapido.net multi-hoster plugin"""
+ __license__ = "GPLv3"
+ __authors__ = [("Kagenoshin", "kagenoshin@gmx.ch")]
+
+
+ LINK_PREMIUM_PATTERN = r'<\s*?a[^>]*?title\s*?=\s*?["\'][^"\']*?download["\'][^>]*?href=["\']([^"\']*)'
+
+ ERROR_PATTERN = r'<\s*?div[^>]*?class\s*?=\s*?["\']?alert-message error[^>]*>([^<]*)'
+
+
+ def handlePremium(self, pyfile):
+ self.html = self.load("http://megarapido.net/gerar.php",
+ post={'rand' :random_with_N_digits(16),
+ 'urllist' : pyfile.url,
+ 'links' : pyfile.url,
+ 'exibir' : "normal",
+ 'usar' : "premium",
+ 'user' : self.account.getAccountInfo(self.user).get('sid', None),
+ 'autoreset': ""})
+
+ if "desloga e loga novamente para gerar seus links" in self.html.lower():
+ self.error("You have logged in at another place")
+
+ return super(MegaRapidoNet, self).handlePremium(pyfile)
diff --git a/module/plugins/internal/CaptchaService.py b/module/plugins/internal/CaptchaService.py
new file mode 100644
index 000000000..fcb8f0095
--- /dev/null
+++ b/module/plugins/internal/CaptchaService.py
@@ -0,0 +1,526 @@
+# -*- coding: utf-8 -*-
+
+import re
+import time
+
+from base64 import b64encode
+from random import random, randint
+from urlparse import urljoin, urlparse
+
+from module.common.json_layer import json_loads
+from module.plugins.Plugin import Base
+
+
+#@TODO: Extend (new) Plugin class; remove all `html` args
+class CaptchaService(Base):
+ __name__ = "CaptchaService"
+ __type__ = "captcha"
+ __version__ = "0.25"
+
+ __description__ = """Base captcha service plugin"""
+ __license__ = "GPLv3"
+ __authors__ = [("pyLoad Team", "admin@pyload.org")]
+
+
+ key = None #: last key detected
+
+
+ def __init__(self, plugin):
+ self.plugin = plugin
+ super(CaptchaService, self).__init__(plugin.core)
+
+
+ def detect_key(self, html=None):
+ raise NotImplementedError
+
+
+ def challenge(self, key=None, html=None):
+ raise NotImplementedError
+
+
+ def result(self, server, challenge):
+ raise NotImplementedError
+
+
+class ReCaptcha(CaptchaService):
+ __name__ = "ReCaptcha"
+ __type__ = "captcha"
+ __version__ = "0.14"
+
+ __description__ = """ReCaptcha captcha service plugin"""
+ __license__ = "GPLv3"
+ __authors__ = [("pyLoad Team", "admin@pyload.org"),
+ ("Walter Purcaro", "vuolter@gmail.com"),
+ ("zapp-brannigan", "fuerst.reinje@web.de")]
+
+
+ KEY_V2_PATTERN = r'(?:data-sitekey=["\']|["\']sitekey["\']:\s*["\'])([\w-]+)'
+ KEY_V1_PATTERN = r'(?:recaptcha(?:/api|\.net)/(?:challenge|noscript)\?k=|Recaptcha\.create\s*\(\s*["\'])([\w-]+)'
+
+
+ def detect_key(self, html=None):
+ if not html:
+ if hasattr(self.plugin, "html") and self.plugin.html:
+ html = self.plugin.html
+ else:
+ errmsg = _("ReCaptcha html not found")
+ self.plugin.fail(errmsg)
+ raise TypeError(errmsg)
+
+ m = re.search(self.KEY_V2_PATTERN, html) or re.search(self.KEY_V1_PATTERN, html)
+ if m:
+ self.key = m.group(1).strip()
+ self.logDebug("Key: %s" % self.key)
+ return self.key
+ else:
+ self.logDebug("Key not found")
+ return None
+
+
+ def challenge(self, key=None, html=None, version=None):
+ if not key:
+ if self.detect_key(html):
+ key = self.key
+ else:
+ errmsg = _("ReCaptcha key not found")
+ self.plugin.fail(errmsg)
+ raise TypeError(errmsg)
+
+ if version in (1, 2):
+ return getattr(self, "_challenge_v%s" % version)(key)
+
+ elif not html and hasattr(self.plugin, "html") and self.plugin.html:
+ version = 2 if re.search(self.KEY_V2_PATTERN, self.plugin.html) else 1
+ return self.challenge(key, self.plugin.html, version)
+
+ else:
+ errmsg = _("ReCaptcha html not found")
+ self.plugin.fail(errmsg)
+ raise TypeError(errmsg)
+
+
+ def _challenge_v1(self, key):
+ html = self.plugin.req.load("http://www.google.com/recaptcha/api/challenge",
+ get={'k': key})
+ try:
+ challenge = re.search("challenge : '(.+?)',", html).group(1)
+ server = re.search("server : '(.+?)',", html).group(1)
+
+ except AttributeError:
+ errmsg = _("ReCaptcha challenge pattern not found")
+ self.plugin.fail(errmsg)
+ raise AttributeError(errmsg)
+
+ self.logDebug("Challenge: %s" % challenge)
+
+ return self.result(server, challenge), challenge
+
+
+ def result(self, server, challenge):
+ result = self.plugin.decryptCaptcha("%simage" % server,
+ get={'c': challenge},
+ cookies=True,
+ forceUser=True,
+ imgtype="jpg")
+
+ self.logDebug("Result: %s" % result)
+
+ return result
+
+
+ def _collectApiInfo(self):
+ html = self.plugin.req.load("http://www.google.com/recaptcha/api.js")
+ a = re.search(r'po.src = \'(.*?)\';', html).group(1)
+ vers = a.split("/")[5]
+
+ self.logDebug("API version: %s" %vers)
+
+ language = a.split("__")[1].split(".")[0]
+
+ self.logDebug("API language: %s" % language)
+
+ html = self.plugin.req.load("https://apis.google.com/js/api.js")
+ b = re.search(r'"h":"(.*?)","', html).group(1)
+ jsh = b.decode('unicode-escape')
+
+ self.logDebug("API jsh-string: %s" % jsh)
+
+ return vers, language, jsh
+
+
+ def _prepareTimeAndRpc(self):
+ self.plugin.req.load("http://www.google.com/recaptcha/api2/demo")
+
+ millis = int(round(time.time() * 1000))
+
+ self.logDebug("Time: %s" % millis)
+
+ rand = randint(1, 99999999)
+ a = "0.%s" % str(rand * 2147483647)
+ rpc = int(100000000 * float(a))
+
+ self.logDebug("Rpc-token: %s" % rpc)
+
+ return millis, rpc
+
+
+ def _challenge_v2(self, key, parent=None):
+ if parent is None:
+ try:
+ parent = urljoin("http://", urlparse(self.plugin.pyfile.url).netloc)
+
+ except Exception:
+ parent = ""
+
+ botguardstring = "!A"
+ vers, language, jsh = self._collectApiInfo()
+ millis, rpc = self._prepareTimeAndRpc()
+
+ html = self.plugin.req.load("https://www.google.com/recaptcha/api2/anchor",
+ get={'k' : key,
+ 'hl' : language,
+ 'v' : vers,
+ 'usegapi' : "1",
+ 'jsh' : "%s#id=IO_%s" % (jsh, millis),
+ 'parent' : parent,
+ 'pfname' : "",
+ 'rpctoken': rpc})
+
+ token1 = re.search(r'id="recaptcha-token" value="(.*?)">', html)
+ self.logDebug("Token #1: %s" % token1.group(1))
+
+ html = self.plugin.req.load("https://www.google.com/recaptcha/api2/frame",
+ get={'c' : token1.group(1),
+ 'hl' : language,
+ 'v' : vers,
+ 'bg' : botguardstring,
+ 'k' : key,
+ 'usegapi': "1",
+ 'jsh' : jsh}).decode('unicode-escape')
+
+ token2 = re.search(r'"finput","(.*?)",', html)
+ self.logDebug("Token #2: %s" % token2.group(1))
+
+ token3 = re.search(r'."asconf".\s,".*?".\s,"(.*?)".', html)
+ self.logDebug("Token #3: %s" % token3.group(1))
+
+ html = self.plugin.req.load("https://www.google.com/recaptcha/api2/reload",
+ post={'k' : key,
+ 'c' : token2.group(1),
+ 'reason': "fi",
+ 'fbg' : token3.group(1)})
+
+ token4 = re.search(r'"rresp","(.*?)",', html)
+ self.logDebug("Token #4: %s" % token4.group(1))
+
+ millis_captcha_loading = int(round(time.time() * 1000))
+ captcha_response = self.plugin.decryptCaptcha("https://www.google.com/recaptcha/api2/payload",
+ get={'c':token4.group(1), 'k':key},
+ cookies=True,
+ forceUser=True)
+ response = b64encode('{"response":"%s"}' % captcha_response)
+
+ self.logDebug("Result: %s" % response)
+
+ timeToSolve = int(round(time.time() * 1000)) - millis_captcha_loading
+ timeToSolveMore = timeToSolve + int(float("0." + str(randint(1, 99999999))) * 500)
+
+ html = self.plugin.req.load("https://www.google.com/recaptcha/api2/userverify",
+ post={'k' : key,
+ 'c' : token4.group(1),
+ 'response': response,
+ 't' : timeToSolve,
+ 'ct' : timeToSolveMore,
+ 'bg' : botguardstring})
+
+ token5 = re.search(r'"uvresp","(.*?)",', html)
+ self.logDebug("Token #5: %s" % token5.group(1))
+
+ result = token5.group(1)
+
+ return result, None
+
+
+class AdsCaptcha(CaptchaService):
+ __name__ = "AdsCaptcha"
+ __type__ = "captcha"
+ __version__ = "0.08"
+
+ __description__ = """AdsCaptcha captcha service plugin"""
+ __license__ = "GPLv3"
+ __authors__ = [("pyLoad Team", "admin@pyload.org")]
+
+
+ CAPTCHAID_PATTERN = r'api\.adscaptcha\.com/Get\.aspx\?[^"\']*CaptchaId=(\d+)'
+ PUBLICKEY_PATTERN = r'api\.adscaptcha\.com/Get\.aspx\?[^"\']*PublicKey=([\w-]+)'
+
+
+ def detect_key(self, html=None):
+ if not html:
+ if hasattr(self.plugin, "html") and self.plugin.html:
+ html = self.plugin.html
+ else:
+ errmsg = _("AdsCaptcha html not found")
+ self.plugin.fail(errmsg)
+ raise TypeError(errmsg)
+
+ m = re.search(self.PUBLICKEY_PATTERN, html)
+ n = re.search(self.CAPTCHAID_PATTERN, html)
+ if m and n:
+ self.key = (m.group(1).strip(), n.group(1).strip()) #: key is the tuple(PublicKey, CaptchaId)
+ self.logDebug("Key|id: %s | %s" % self.key)
+ return self.key
+ else:
+ self.logDebug("Key or id not found")
+ return None
+
+
+ def challenge(self, key=None, html=None):
+ if not key:
+ if self.detect_key(html):
+ key = self.key
+ else:
+ errmsg = _("AdsCaptcha key not found")
+ self.plugin.fail(errmsg)
+ raise TypeError(errmsg)
+
+ PublicKey, CaptchaId = key
+
+ html = self.plugin.req.load("http://api.adscaptcha.com/Get.aspx",
+ get={'CaptchaId': CaptchaId,
+ 'PublicKey': PublicKey})
+ try:
+ challenge = re.search("challenge: '(.+?)',", html).group(1)
+ server = re.search("server: '(.+?)',", html).group(1)
+
+ except AttributeError:
+ errmsg = _("AdsCaptcha challenge pattern not found")
+ self.plugin.fail(errmsg)
+ raise AttributeError(errmsg)
+
+ self.logDebug("Challenge: %s" % challenge)
+
+ return self.result(server, challenge), challenge
+
+
+ def result(self, server, challenge):
+ result = self.plugin.decryptCaptcha("%sChallenge.aspx" % server,
+ get={'cid': challenge, 'dummy': random()},
+ cookies=True,
+ imgtype="jpg")
+
+ self.logDebug("Result: %s" % result)
+
+ return result
+
+
+class SolveMedia(CaptchaService):
+ __name__ = "SolveMedia"
+ __type__ = "captcha"
+ __version__ = "0.12"
+
+ __description__ = """SolveMedia captcha service plugin"""
+ __license__ = "GPLv3"
+ __authors__ = [("pyLoad Team", "admin@pyload.org")]
+
+
+ KEY_PATTERN = r'api\.solvemedia\.com/papi/challenge\.(?:no)?script\?k=(.+?)["\']'
+
+
+ def detect_key(self, html=None):
+ if not html:
+ if hasattr(self.plugin, "html") and self.plugin.html:
+ html = self.plugin.html
+ else:
+ errmsg = _("SolveMedia html not found")
+ self.plugin.fail(errmsg)
+ raise TypeError(errmsg)
+
+ m = re.search(self.KEY_PATTERN, html)
+ if m:
+ self.key = m.group(1).strip()
+ self.logDebug("Key: %s" % self.key)
+ return self.key
+ else:
+ self.logDebug("Key not found")
+ return None
+
+
+ def challenge(self, key=None, html=None):
+ if not key:
+ if self.detect_key(html):
+ key = self.key
+ else:
+ errmsg = _("SolveMedia key not found")
+ self.plugin.fail(errmsg)
+ raise TypeError(errmsg)
+
+ html = self.plugin.req.load("http://api.solvemedia.com/papi/challenge.noscript",
+ get={'k': key})
+ try:
+ challenge = re.search(r'<input type=hidden name="adcopy_challenge" id="adcopy_challenge" value="([^"]+)">',
+ html).group(1)
+ server = "http://api.solvemedia.com/papi/media"
+
+ except AttributeError:
+ errmsg = _("SolveMedia challenge pattern not found")
+ self.plugin.fail(errmsg)
+ raise AttributeError(errmsg)
+
+ self.logDebug("Challenge: %s" % challenge)
+
+ result = self.result(server, challenge)
+
+ try:
+ magic = re.search(r'name="magic" value="(.+?)"', html).group(1)
+
+ except AttributeError:
+ self.logDebug("Magic code not found")
+
+ else:
+ if not self._verify(key, magic, result, challenge):
+ self.logDebug("Captcha code was invalid")
+
+ return result, challenge
+
+
+ def _verify(self, key, magic, result, challenge, ref=None): #@TODO: Clean up
+ if ref is None:
+ try:
+ ref = self.plugin.pyfile.url
+
+ except Exception:
+ ref = ""
+
+ html = self.plugin.req.load("http://api.solvemedia.com/papi/verify.noscript",
+ post={'adcopy_response' : result,
+ 'k' : key,
+ 'l' : "en",
+ 't' : "img",
+ 's' : "standard",
+ 'magic' : magic,
+ 'adcopy_challenge' : challenge,
+ 'ref' : ref})
+ try:
+ html = self.plugin.req.load(re.search(r'URL=(.+?)">', html).group(1))
+ gibberish = re.search(r'id=gibberish>(.+?)</textarea>', html).group(1)
+
+ except Exception:
+ return False
+
+ else:
+ return True
+
+
+ def result(self, server, challenge):
+ result = self.plugin.decryptCaptcha(server,
+ get={'c': challenge},
+ cookies=True,
+ imgtype="gif")
+
+ self.logDebug("Result: %s" % result)
+
+ return result
+
+
+class AdYouLike(CaptchaService):
+ __name__ = "AdYouLike"
+ __type__ = "captcha"
+ __version__ = "0.05"
+
+ __description__ = """AdYouLike captcha service plugin"""
+ __license__ = "GPLv3"
+ __authors__ = [("Walter Purcaro", "vuolter@gmail.com")]
+
+
+ AYL_PATTERN = r'Adyoulike\.create\s*\((.+?)\)'
+ CALLBACK_PATTERN = r'(Adyoulike\.g\._jsonp_\d+)'
+
+
+ def detect_key(self, html=None):
+ if not html:
+ if hasattr(self.plugin, "html") and self.plugin.html:
+ html = self.plugin.html
+ else:
+ errmsg = _("AdYouLike html not found")
+ self.plugin.fail(errmsg)
+ raise TypeError(errmsg)
+
+ m = re.search(self.AYL_PATTERN, html)
+ n = re.search(self.CALLBACK_PATTERN, html)
+ if m and n:
+ self.key = (m.group(1).strip(), n.group(1).strip())
+ self.logDebug("Ayl|callback: %s | %s" % self.key)
+ return self.key #: key is the tuple(ayl, callback)
+ else:
+ self.logDebug("Ayl or callback not found")
+ return None
+
+
+ def challenge(self, key=None, html=None):
+ if not key:
+ if self.detect_key(html):
+ key = self.key
+ else:
+ errmsg = _("AdYouLike key not found")
+ self.plugin.fail(errmsg)
+ raise TypeError(errmsg)
+
+ ayl, callback = key
+
+ # {"adyoulike":{"key":"P~zQ~O0zV0WTiAzC-iw0navWQpCLoYEP"},
+ # "all":{"element_id":"ayl_private_cap_92300","lang":"fr","env":"prod"}}
+ ayl = json_loads(ayl)
+
+ html = self.plugin.req.load("http://api-ayl.appspot.com/challenge",
+ get={'key' : ayl['adyoulike']['key'],
+ 'env' : ayl['all']['env'],
+ 'callback': callback})
+ try:
+ challenge = json_loads(re.search(callback + r'\s*\((.+?)\)', html).group(1))
+
+ except AttributeError:
+ errmsg = _("AdYouLike challenge pattern not found")
+ self.plugin.fail(errmsg)
+ raise AttributeError(errmsg)
+
+ self.logDebug("Challenge: %s" % challenge)
+
+ return self.result(ayl, challenge), challenge
+
+
+ def result(self, server, challenge):
+ # Adyoulike.g._jsonp_5579316662423138
+ # ({"translations":{"fr":{"instructions_visual":"Recopiez « Soonnight » ci-dessous :"}},
+ # "site_under":true,"clickable":true,"pixels":{"VIDEO_050":[],"DISPLAY":[],"VIDEO_000":[],"VIDEO_100":[],
+ # "VIDEO_025":[],"VIDEO_075":[]},"medium_type":"image/adyoulike",
+ # "iframes":{"big":"<iframe src=\"http://www.soonnight.com/campagn.html\" scrolling=\"no\"
+ # height=\"250\" width=\"300\" frameborder=\"0\"></iframe>"},"shares":{},"id":256,
+ # "token":"e6QuI4aRSnbIZJg02IsV6cp4JQ9~MjA1","formats":{"small":{"y":300,"x":0,"w":300,"h":60},
+ # "big":{"y":0,"x":0,"w":300,"h":250},"hover":{"y":440,"x":0,"w":300,"h":60}},
+ # "tid":"SqwuAdxT1EZoi4B5q0T63LN2AkiCJBg5"})
+
+ if isinstance(server, basestring):
+ server = json_loads(server)
+
+ if isinstance(challenge, basestring):
+ challenge = json_loads(challenge)
+
+ try:
+ instructions_visual = challenge['translations'][server['all']['lang']]['instructions_visual']
+ result = re.search(u'«(.+?)»', instructions_visual).group(1).strip()
+
+ except AttributeError:
+ errmsg = _("AdYouLike result not found")
+ self.plugin.fail(errmsg)
+ raise AttributeError(errmsg)
+
+ result = {'_ayl_captcha_engine' : "adyoulike",
+ '_ayl_env' : server['all']['env'],
+ '_ayl_tid' : challenge['tid'],
+ '_ayl_token_challenge': challenge['token'],
+ '_ayl_response' : response}
+
+ self.logDebug("Result: %s" % result)
+
+ return result