diff --git a/lib/pynma/__init__.py b/lib/pynma/__init__.py index f90424eb70e977fa6f417a29fc944a6fba32e65d..a75b42855b632e050325c9d3517891cb094d086f 100644 --- a/lib/pynma/__init__.py +++ b/lib/pynma/__init__.py @@ -1,4 +1,4 @@ #!/usr/bin/python -from pynma import PyNMA +from .pynma import PyNMA diff --git a/lib/pynma/pynma.py b/lib/pynma/pynma.py index fc7d8de2eeb4cf90ec5e6ceae4fb60c6925fd3ac..2fc55560af9624dc4a7526aec27b6e5bbebef03a 100644 --- a/lib/pynma/pynma.py +++ b/lib/pynma/pynma.py @@ -1,12 +1,20 @@ #!/usr/bin/python from xml.dom.minidom import parseString -from httplib import HTTPSConnection -from urllib import urlencode -__version__ = "0.1" +try: + from http.client import HTTPSConnection +except ImportError: + from httplib import HTTPSConnection -API_SERVER = 'nma.usk.bz' +try: + from urllib.parse import urlencode +except ImportError: + from urllib import urlencode + +__version__ = "1.0" + +API_SERVER = 'www.notifymyandroid.com' ADD_PATH = '/publicapi/notify' USER_AGENT="PyNMA/v%s"%__version__ @@ -18,14 +26,14 @@ def uniq_preserve(seq): # Dave Kirby def uniq(seq): # Not order preserving - return {}.fromkeys(seq).keys() + return list({}.fromkeys(seq).keys()) class PyNMA(object): """PyNMA(apikey=[], developerkey=None) - takes 2 optional arguments: - - (opt) apykey: might me a string containing 1 key or an array of keys - - (opt) developerkey: where you can store your developer key - """ +takes 2 optional arguments: + - (opt) apykey: might me a string containing 1 key or an array of keys + - (opt) developerkey: where you can store your developer key +""" def __init__(self, apikey=[], developerkey=None): self._developerkey = None @@ -60,19 +68,20 @@ class PyNMA(object): if type(developerkey) == str and len(developerkey) == 48: self._developerkey = developerkey - def push(self, application="", event="", description="", url="", priority=0, batch_mode=False): + def push(self, application="", event="", description="", url="", contenttype=None, priority=0, batch_mode=False, html=False): """Pushes a message on the registered API keys. - takes 5 arguments: - - (req) application: application name [256] - - (req) event: event name [1000] - - (req) description: description [10000] - - (opt) url: url [512] - - (opt) priority: from -2 (lowest) to 2 (highest) (def:0) - - (opt) batch_mode: call API 5 by 5 (def:False) - - Warning: using batch_mode will return error only if all API keys are bad - cf: http://nma.usk.bz/api.php - """ +takes 5 arguments: + - (req) application: application name [256] + - (req) event: event name [1000] + - (req) description: description [10000] + - (opt) url: url [512] + - (opt) contenttype: Content Type (act: None (plain text) or text/html) + - (opt) priority: from -2 (lowest) to 2 (highest) (def:0) + - (opt) batch_mode: push to all keys at once (def:False) + - (opt) html: shortcut for contenttype=text/html +Warning: using batch_mode will return error only if all API keys are bad + cf: http://nma.usk.bz/api.php +""" datas = { 'application': application[:256].encode('utf8'), 'event': event[:1024].encode('utf8'), @@ -82,7 +91,10 @@ class PyNMA(object): if url: datas['url'] = url[:512] - + + if contenttype == "text/html" or html == True: # Currently only accepted content type + datas['content-type'] = "text/html" + if self._developerkey: datas['developerkey'] = self._developerkey @@ -94,10 +106,9 @@ class PyNMA(object): res = self.callapi('POST', ADD_PATH, datas) results[key] = res else: - for i in range(0, len(self._apikey), 5): - datas['apikey'] = ",".join(self._apikey[i:i+5]) - res = self.callapi('POST', ADD_PATH, datas) - results[datas['apikey']] = res + datas['apikey'] = ",".join(self._apikey) + res = self.callapi('POST', ADD_PATH, datas) + results[datas['apikey']] = res return results def callapi(self, method, path, args): @@ -110,7 +121,7 @@ class PyNMA(object): try: res = self._parse_reponse(resp.read()) - except Exception, e: + except Exception as e: res = {'type': "pynmaerror", 'code': 600, 'message': str(e) @@ -124,12 +135,12 @@ class PyNMA(object): for elem in root.childNodes: if elem.nodeType == elem.TEXT_NODE: continue if elem.tagName == 'success': - res = dict(elem.attributes.items()) + res = dict(list(elem.attributes.items())) res['message'] = "" res['type'] = elem.tagName return res if elem.tagName == 'error': - res = dict(elem.attributes.items()) + res = dict(list(elem.attributes.items())) res['message'] = elem.firstChild.nodeValue res['type'] = elem.tagName return res diff --git a/sickbeard/providers/libertalia.py b/sickbeard/providers/libertalia.py index 3f6f14b513f2ac75de0de466430b679c8080fcf2..308c5dddf8db186dbc8488be00328d564e1a10df 100644 --- a/sickbeard/providers/libertalia.py +++ b/sickbeard/providers/libertalia.py @@ -57,15 +57,15 @@ class LIBERTALIAProvider(generic.TorrentProvider): if audio_lang == "en" and french==None: results.append( urllib.urlencode( { 'name': searchString - } ) + "*VO*&cat%5B%5D="+cat+"&[PARAMSTR]=" + searchString +" VO" ) + } ) + "*VO*&cat%5B%5D="+cat+"&[PARAMSTR]=" + searchString ) elif audio_lang == "en" and french==None: results.append( urllib.urlencode( { 'name': searchString - } ) + "*VO*&cat%5B%5D="+cat+"&[PARAMSTR]=" + searchString +" VO" ) + } ) + "*VO*&cat%5B%5D="+cat+"&[PARAMSTR]=" + searchString ) elif audio_lang == "fr" or french: results.append( urllib.urlencode( { 'name': searchString - } ) + "*FRENCH*&cat%5B%5D="+cat+"&[PARAMSTR]=" + searchString +" FRENCH") + } ) + "*FRENCH*&cat%5B%5D="+cat+"&[PARAMSTR]=" + searchString ) else: results.append( urllib.urlencode( { 'name': searchString diff --git a/sickbeard/providers/xthor.py b/sickbeard/providers/xthor.py index b5fde78e4df524db333e62b8909773ddb165a83d..e7bded7bdd84fb4020c42123d5e70045586f41eb 100644 --- a/sickbeard/providers/xthor.py +++ b/sickbeard/providers/xthor.py @@ -31,178 +31,176 @@ import re class XTHORProvider(generic.TorrentProvider): - def __init__(self): - - generic.TorrentProvider.__init__(self, "XTHOR") - - self.supportsBacklog = True - - self.cj = cookielib.CookieJar() - self.opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(self.cj)) - - self.url = "https://xthor.bz" - - self.login_done = False - self.failed_login_logged = False - self.successful_login_logged = False - - def isEnabled(self): - return sickbeard.XTHOR - - def getSearchParams(self, searchString, audio_lang, french=None, fullSeason=False): - results = [] - if audio_lang == "en" and french==None: - results.append( urllib.urlencode( { - 'keywords': searchString , - } ) + "&cid=43,69&[PARAMSTR]=" + searchString ) - elif audio_lang == "fr" or french: - results.append( urllib.urlencode( { - 'keywords': searchString - } ) + "&cid=42,41&[PARAMSTR]=" + searchString) - else: - results.append( urllib.urlencode( { - 'keywords': searchString - } ) + "&cid=42,43,41,69&[PARAMSTR]=" + searchString) - #Désactivé car on ne peut pas savoir la langue - #if fullSeason: - # results.append( urllib.urlencode( { - # 'keywords': searchString - # } ) + "&cid=70&[PARAMSTR]=" + searchString) - return results - - def _get_season_search_strings(self, show, season): - - showNam = show_name_helpers.allPossibleShowNames(show) - showNames = list(set(showNam)) - results = [] - for showName in showNames: - results.extend( self.getSearchParams(showName + " saison%d" % season, show.audio_lang, fullSeason=True)) - results.extend( self.getSearchParams(showName + " season%d" % season, show.audio_lang, fullSeason=True)) - results.extend( self.getSearchParams(showName + " saison %d" % season, show.audio_lang, fullSeason=True)) - results.extend( self.getSearchParams(showName + " season %d" % season, show.audio_lang, fullSeason=True)) - results.extend( self.getSearchParams(showName + " saison%02d" % season, show.audio_lang, fullSeason=True)) - results.extend( self.getSearchParams(showName + " season%02d" % season, show.audio_lang, fullSeason=True)) - results.extend( self.getSearchParams(showName + " saison %02d" % season, show.audio_lang, fullSeason=True)) - results.extend( self.getSearchParams(showName + " season %02d" % season, show.audio_lang, fullSeason=True)) - results.extend( self.getSearchParams(showName + ".S%02d." % season, show.audio_lang, fullSeason=True)) - return results - - def _get_episode_search_strings(self, ep_obj, french=None): - - showNam = show_name_helpers.allPossibleShowNames(ep_obj.show) - showNames = list(set(showNam)) - results = [] - for showName in showNames: - results.extend( self.getSearchParams( "%s S%02dE%02d" % ( showName, ep_obj.scene_season, ep_obj.scene_episode), ep_obj.show.audio_lang, french )) - results.extend( self.getSearchParams( "%s %dx%02d" % ( showName, ep_obj.scene_season, ep_obj.scene_episode ), ep_obj.show.audio_lang, french )) - return results - - def _get_title_and_url(self, item): - return (item.title, item.url) - - def getQuality(self, item): - return item.getQuality() - - def _doLogin(self, login, password): - - listeUserAgents = [ 'Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_5; fr-fr) AppleWebKit/525.18 (KHTML, like Gecko) Version/3.1.2 Safari/525.20.1', - 'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/14.0.835.186 Safari/535.1', - 'Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US) AppleWebKit/525.13 (KHTML, like Gecko) Chrome/0.2.149.27 Safari/525.13', - 'Mozilla/5.0 (X11; U; Linux x86_64; en-us) AppleWebKit/528.5+ (KHTML, like Gecko, Safari/528.5+) midori', - 'Mozilla/5.0 (Windows NT 6.0) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/13.0.782.107 Safari/535.1', - 'Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en-us) AppleWebKit/312.1 (KHTML, like Gecko) Safari/312', - 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.12 Safari/535.11', - 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.8 (KHTML, like Gecko) Chrome/17.0.940.0 Safari/535.8' ] - - self.opener.addheaders = [('User-agent', random.choice(listeUserAgents))] - - data = urllib.urlencode({'action':'login','loginbox_membername': login, 'loginbox_password' : password, 'loginbox_remember' : 'true'}) - - r = self.opener.open(self.url + '/ajax/login.php',data) - - for index, cookie in enumerate(self.cj): - if (cookie.name == "tsue_member"): self.login_done = True - - if not self.login_done and not self.failed_login_logged: - logger.log(u"Unable to login to XTHOR. Please check username and password.", logger.WARNING) - self.failed_login_logged = True - - if self.login_done and not self.successful_login_logged: - logger.log(u"Login to XTHOR successful", logger.MESSAGE) - self.successful_login_logged = True - - def _doSearch(self, searchString, show=None, season=None, french=None): - - - if not self.login_done: - self._doLogin( sickbeard.XTHOR_USERNAME, sickbeard.XTHOR_PASSWORD ) - - results = [] - - searchUrl = self.url + '?p=torrents&pid=10&search_type=name&' + searchString.replace('!','') - - logger.log(u"Search string: " + searchUrl, logger.DEBUG) - - r = self.opener.open( searchUrl ) - - soup = BeautifulSoup( r) - - resultsTable = soup.find("table", { "id" : "torrents_table_classic" }) - if resultsTable: - - rows = resultsTable.findAll("tr") - - for row in rows: - - link = row.find("a",href=re.compile("action=details")) - - if link: - title = link.text - recherched=searchUrl.split("&[PARAMSTR]=")[1] - recherched=recherched.replace(" ","(.*)") - logger.log(u"XTHOR TITLE : " + title, logger.DEBUG) - logger.log(u"XTHOR CHECK MATCH : " + recherched, logger.DEBUG) - if re.match(recherched,title , re.IGNORECASE): - downloadURL = row.find("a",href=re.compile("action=download"))['href'] - logger.log(u"XTHOR DOWNLOAD URL : " + downloadURL, logger.DEBUG) - else: - continue - quality = Quality.nameQuality( title ) - if quality==Quality.UNKNOWN and title: - if '720p' not in title.lower() and '1080p' not in title.lower(): - quality=Quality.SDTV - if show and french==None: - results.append( XTHORSearchResult( self.opener, title, downloadURL, quality, str(show.audio_lang) ) ) - elif show and french: - results.append( XTHORSearchResult( self.opener, title, downloadURL, quality, 'fr' ) ) - else: - results.append( XTHORSearchResult( self.opener, title, downloadURL, quality ) ) - - return results - - def getResult(self, episodes): - """ - Returns a result of the correct type for this provider - """ - result = classes.TorrentDataSearchResult(episodes) - result.provider = self - - return result - + def __init__(self): + + generic.TorrentProvider.__init__(self, "XTHOR") + + self.supportsBacklog = True + + + self.cj = cookielib.CookieJar() + self.opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(self.cj)) + + self.url = "https://xthor.bz" + + self.login_done = False + self.failed_login_logged = False + self.successful_login_logged = False + + def isEnabled(self): + return sickbeard.XTHOR + + def getSearchParams(self, searchString, audio_lang, french=None, fullSeason=False): + results = [] + + if audio_lang == "en" and french==None: + results.append("c16=1&c17=1&searchin=title&search=" + searchString ) + elif audio_lang == "fr" or french: + results.append("c14=1&c15=1&searchin=title&search=" + searchString) + else: + results.append("c14=1&c15=1&c16=1&c17=1&searchin=title&search=" + searchString) + #Désactivé car on ne peut pas savoir la langue + #if fullSeason: + # results.append( urllib.urlencode( { + # 'keywords': searchString + # } ) + "&cid=70&[PARAMSTR]=" + searchString) + return results + + def _get_season_search_strings(self, show, season): + + showNam = show_name_helpers.allPossibleShowNames(show) + showNames = list(set(showNam)) + results = [] + for showName in showNames: + results.extend( self.getSearchParams(showName + " saison%d" % season, show.audio_lang, fullSeason=True)) + results.extend( self.getSearchParams(showName + " season%d" % season, show.audio_lang, fullSeason=True)) + results.extend( self.getSearchParams(showName + " saison %d" % season, show.audio_lang, fullSeason=True)) + results.extend( self.getSearchParams(showName + " season %d" % season, show.audio_lang, fullSeason=True)) + results.extend( self.getSearchParams(showName + " saison%02d" % season, show.audio_lang, fullSeason=True)) + results.extend( self.getSearchParams(showName + " season%02d" % season, show.audio_lang, fullSeason=True)) + results.extend( self.getSearchParams(showName + " saison %02d" % season, show.audio_lang, fullSeason=True)) + results.extend( self.getSearchParams(showName + " season %02d" % season, show.audio_lang, fullSeason=True)) + results.extend( self.getSearchParams(showName + ".S%02d." % season, show.audio_lang, fullSeason=True)) + return results + + def _get_episode_search_strings(self, ep_obj, french=None): + + showNam = show_name_helpers.allPossibleShowNames(ep_obj.show) + showNames = list(set(showNam)) + results = [] + for showName in showNames: + results.extend( self.getSearchParams( "%s S%02dE%02d" % ( showName, ep_obj.scene_season, ep_obj.scene_episode), ep_obj.show.audio_lang, french )) + results.extend( self.getSearchParams( "%s %dx%02d" % ( showName, ep_obj.scene_season, ep_obj.scene_episode ), ep_obj.show.audio_lang, french )) + return results + + def _get_title_and_url(self, item): + return (item.title, item.url) + + def getQuality(self, item): + return item.getQuality() + + def _doLogin(self, login, password): + + listeUserAgents = [ 'Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_5; fr-fr) AppleWebKit/525.18 (KHTML, like Gecko) Version/3.1.2 Safari/525.20.1', + 'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/14.0.835.186 Safari/535.1', + 'Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US) AppleWebKit/525.13 (KHTML, like Gecko) Chrome/0.2.149.27 Safari/525.13', + 'Mozilla/5.0 (X11; U; Linux x86_64; en-us) AppleWebKit/528.5+ (KHTML, like Gecko, Safari/528.5+) midori', + 'Mozilla/5.0 (Windows NT 6.0) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/13.0.782.107 Safari/535.1', + 'Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en-us) AppleWebKit/312.1 (KHTML, like Gecko) Safari/312', + 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.12 Safari/535.11', + 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.8 (KHTML, like Gecko) Chrome/17.0.940.0 Safari/535.8' ] + + self.opener.addheaders = [('User-agent', random.choice(listeUserAgents))] + + data = urllib.urlencode({'action':'takelogin.php','username': login, 'password' : password, 'submitme' : 'X'}) + + r = self.opener.open(self.url + '/takelogin.php',data) + + for index, cookie in enumerate(self.cj): + if (cookie.name == "uid"): self.login_done = True + + if not self.login_done and not self.failed_login_logged: + logger.log(u"Unable to login to XTHOR. Please check username and password.", logger.WARNING) + self.failed_login_logged = True + + if self.login_done and not self.successful_login_logged: + logger.log(u"Login to XTHOR successful", logger.MESSAGE) + self.successful_login_logged = True + + def _doSearch(self, searchString, show=None, season=None, french=None): + + + if not self.login_done: + self._doLogin( sickbeard.XTHOR_USERNAME, sickbeard.XTHOR_PASSWORD ) + + results = [] + + searchUrl = self.url + '/browse.php?' + searchString.replace('!','') + + logger.log(u"Search string: " + searchUrl, logger.DEBUG) + + r = self.opener.open( searchUrl ) + + soup = BeautifulSoup( r) + + resultsTable = soup.find("table", { "class" : "table2" }) + if resultsTable: + + rows = resultsTable.findAll("tr") + + for row in rows: + + link = row.find("a",href=re.compile("details.php")) + + if link: + title = link.text + recherched=searchUrl.split("&search=")[1] + recherched=recherched.replace(" ","(.*)") + recherched= recherched + "(.*)" + logger.log(u"XTHOR TITLE : " + title, logger.DEBUG) + logger.log(u"XTHOR CHECK MATCH : " + recherched, logger.DEBUG) + if re.match(recherched,title , re.IGNORECASE): + downloadURL = row.find("a",href=re.compile("download.php"))['href'] + downloadURL = self.url + '/'+ downloadURL + logger.log(u"XTHOR DOWNLOAD URL : " + downloadURL, logger.DEBUG) + else: + continue + quality = Quality.nameQuality( title ) + if quality==Quality.UNKNOWN and title: + if '720p' not in title.lower() and '1080p' not in title.lower(): + quality=Quality.SDTV + if show and french==None: + results.append( XTHORSearchResult( self.opener, title, downloadURL, quality, str(show.audio_lang) ) ) + elif show and french: + results.append( XTHORSearchResult( self.opener, title, downloadURL, quality, 'fr' ) ) + else: + results.append( XTHORSearchResult( self.opener, title, downloadURL, quality ) ) + + return results + + def getResult(self, episodes): + """ + Returns a result of the correct type for this provider + """ + result = classes.TorrentDataSearchResult(episodes) + result.provider = self + + return result + class XTHORSearchResult: - - def __init__(self, opener, title, url, quality, audio_langs=None): - self.opener = opener - self.title = title - self.url = url - self.quality = quality - self.audio_langs=audio_langs - - def getNZB(self): - logger.log(u"XTHOR GETNZB URL : " + self.url, logger.DEBUG) - return self.opener.open( self.url , 'wb').read() - - def getQuality(self): - return self.quality + + def __init__(self, opener, title, url, quality, audio_langs=None): + self.opener = opener + self.title = title + self.url = url + self.quality = quality + self.audio_langs=audio_langs + + def getNZB(self): + logger.log(u"XTHOR GETNZB URL : " + self.url, logger.DEBUG) + return self.opener.open( self.url , 'wb').read() + + def getQuality(self): + return self.quality provider = XTHORProvider()