From 1790b3be7f226a91fbf13c63580dfebf64c8768d Mon Sep 17 00:00:00 2001 From: Adam Date: Fri, 12 Sep 2014 01:26:17 +0800 Subject: [PATCH] Add TokyoToshokanProvider support --- sickbeard/__init__.py | 2 +- sickbeard/providers/__init__.py | 3 +- sickbeard/providers/generic.py | 6 +- sickbeard/providers/tokyotoshokan.py | 174 +++++++++++++++++++++++++++ 4 files changed, 182 insertions(+), 3 deletions(-) create mode 100644 sickbeard/providers/tokyotoshokan.py diff --git a/sickbeard/__init__.py b/sickbeard/__init__.py index de9c05cc..682ae1ab 100755 --- a/sickbeard/__init__.py +++ b/sickbeard/__init__.py @@ -32,7 +32,7 @@ from sickbeard import providers, metadata, config, webserveInit from sickbeard.providers.generic import GenericProvider from providers import ezrss, tvtorrents, btn, newznab, womble, thepiratebay, torrentleech, kat, iptorrents, \ omgwtfnzbs, scc, hdtorrents, torrentday, hdbits, nextgen, speedcd, nyaatorrents, fanzub, torrentbytes, animezb, \ - freshontv, bitsoup, t411 + freshontv, bitsoup, t411, tokyotoshokan from sickbeard.config import CheckSection, check_setting_int, check_setting_str, check_setting_float, ConfigMigrator, \ naming_ep_type from sickbeard import searchBacklog, showUpdater, versionChecker, properFinder, autoPostProcesser, \ diff --git a/sickbeard/providers/__init__.py b/sickbeard/providers/__init__.py index c1800ca1..3158f9ca 100755 --- a/sickbeard/providers/__init__.py +++ b/sickbeard/providers/__init__.py @@ -37,7 +37,8 @@ __all__ = ['ezrss', 'animezb', 'freshontv', 'bitsoup', - 't411' + 't411', + 'tokyotoshokan', ] import sickbeard diff --git a/sickbeard/providers/generic.py b/sickbeard/providers/generic.py index 2b23d711..18c9c324 100644 --- a/sickbeard/providers/generic.py +++ b/sickbeard/providers/generic.py @@ -36,7 +36,7 @@ from sickbeard.common import Quality from sickbeard import clients from hachoir_parser import createParser - +from base64 import b16encode, b32decode class GenericProvider: NZB = "nzb" @@ -140,6 +140,10 @@ class GenericProvider: if self.providerType == GenericProvider.TORRENT: try: torrent_hash = re.findall('urn:btih:([\w]{32,40})', result.url)[0].upper() + + if len(torrent_hash) == 32: + torrent_hash = b16encode(b32decode(torrent_hash)).lower() + if not torrent_hash: logger.log("Unable to extract torrent hash from link: " + ex(result.url), logger.ERROR) return False diff --git a/sickbeard/providers/tokyotoshokan.py b/sickbeard/providers/tokyotoshokan.py new file mode 100644 index 00000000..2fee7432 --- /dev/null +++ b/sickbeard/providers/tokyotoshokan.py @@ -0,0 +1,174 @@ +# Author: Mr_Orange +# URL: http://code.google.com/p/sickbeard/ +# +# This file is part of SickRage. +# +# SickRage is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# SickRage is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with SickRage. If not, see . + +import urllib +import re +import traceback + +import sickbeard +import generic + +from sickbeard import show_name_helpers +from sickbeard import logger +from sickbeard.common import Quality +from sickbeard import tvcache +from sickbeard import show_name_helpers, helpers +from sickbeard.bs4_parser import BS4Parser + + +class TokyoToshokanProvider(generic.TorrentProvider): + def __init__(self): + + generic.TorrentProvider.__init__(self, "TokyoToshokan") + + self.supportsBacklog = True + self.supportsAbsoluteNumbering = True + self.anime_only = True + self.enabled = False + self.ratio = None + + self.cache = TokyoToshokanCache(self) + + self.url = 'http://tokyotosho.info/' + + def isEnabled(self): + return self.enabled + + def imageName(self): + return 'nyaatorrents.png' + + def _get_title_and_url(self, item): + + title, url = item + + if title: + title = u'' + title + title = title.replace(' ', '.') + + if url: + url = url.replace('&', '&') + + return (title, url) + + def seedRatio(self): + return self.ratio + + def getQuality(self, item, anime=False): + quality = Quality.sceneQuality(item[0], anime) + return quality + + def findSearchResults(self, show, episodes, search_mode, manualSearch=False): + return generic.TorrentProvider.findSearchResults(self, show, episodes, search_mode, manualSearch) + + def _get_season_search_strings(self, ep_obj): + return [x.replace('.', ' ') for x in show_name_helpers.makeSceneSeasonSearchString(self.show, ep_obj)] + + def _get_episode_search_strings(self, ep_obj, add_string=''): + return [x.replace('.', ' ') for x in show_name_helpers.makeSceneSearchString(self.show, ep_obj)] + + def _doSearch(self, search_string, search_mode='eponly', epcount=0, age=0): + if self.show and not self.show.is_anime: + logger.log(u"" + str(self.show.name) + " is not an anime skiping " + str(self.name)) + return [] + + params = { + "terms": search_string.encode('utf-8'), + "type": 1, # get anime types + } + + searchURL = self.url + 'search.php?' + urllib.urlencode(params) + + data = self.getURL(searchURL) + + logger.log(u"Search string: " + searchURL, logger.DEBUG) + + if not data: + return [] + + results = [] + try: + with BS4Parser(data, features=["html5lib", "permissive"]) as soup: + torrent_table = soup.find('table', attrs={'class': 'listing'}) + torrent_rows = torrent_table.find_all('tr') if torrent_table else [] + if torrent_rows[0].find('td', attrs={'class': 'centertext'}): + a = 1 + else: + a = 0 + + for top, bottom in zip(torrent_rows[a::2], torrent_rows[a::2]): + title = top.find('td', attrs={'class': 'desc-top'}).text + url = top.find('td', attrs={'class': 'desc-top'}).find('a')['href'] + + if not title or not url: + continue + + item = title.lstrip(), url + results.append(item) + + except Exception, e: + logger.log(u"Failed to parsing " + self.name + " Traceback: " + traceback.format_exc(), logger.ERROR) + + + return results + + +class TokyoToshokanCache(tvcache.TVCache): + def __init__(self, provider): + tvcache.TVCache.__init__(self, provider) + + # only poll NyaaTorrents every 15 minutes max + self.minTime = 15 + + def _get_title_and_url(self, item): + """ + Retrieves the title and URL data from the item XML node + + item: An elementtree.ElementTree element representing the tag of the RSS feed + + Returns: A tuple containing two strings representing title and URL respectively + """ + + title = item.title if item.title else None + if title: + title = u'' + title + title = title.replace(' ', '.') + + url = item.link if item.link else None + if url: + url = url.replace('&', '&') + + return (title, url) + + def _getRSSData(self): + params = { + "filter": '1', + } + + url = self.provider.url + 'rss.php?' + urllib.urlencode(params) + + logger.log(u"TokyoToshokan cache update URL: " + url, logger.DEBUG) + + data = self.getRSSFeed(url) + + if data and 'entries' in data: + return data.entries + else: + return [] + + +provider = TokyoToshokanProvider()