From c11c61e8cf0aa247f199840ec8f5d0bcacbb860f Mon Sep 17 00:00:00 2001 From: echel0n Date: Mon, 5 May 2014 00:39:20 -0700 Subject: [PATCH] Added backlog and proper searchers to HDBits --- sickbeard/providers/hdbits.py | 483 ++++++++++++++++------------------ 1 file changed, 231 insertions(+), 252 deletions(-) diff --git a/sickbeard/providers/hdbits.py b/sickbeard/providers/hdbits.py index d863e8f9..8ca0a0f4 100644 --- a/sickbeard/providers/hdbits.py +++ b/sickbeard/providers/hdbits.py @@ -1,260 +1,239 @@ -# This file is part of Sick Beard. -# -# Sick Beard 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. -# -# Sick Beard 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 Sick Beard. If not, see . +# This file is part of Sick Beard. +# +# Sick Beard 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. +# +# Sick Beard 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 Sick Beard. If not, see . + +import datetime +import urllib +import generic +import sickbeard + +from sickbeard import classes +from sickbeard import logger, tvcache, exceptions +from sickbeard import helpers +from sickbeard.common import Quality +from sickbeard.exceptions import ex, AuthException +from sickbeard.name_parser.parser import NameParser, InvalidNameException + +try: + import json +except ImportError: + from lib import simplejson as json + + +class HDBitsProvider(generic.TorrentProvider): + + def __init__(self): + + generic.TorrentProvider.__init__(self, "HDBits") + + self.supportsBacklog = True + + self.cache = HDBitsCache(self) + + self.url = 'https://hdbits.org' + self.search_url = 'https://hdbits.org/api/torrents' + self.rss_url = 'https://hdbits.org/api/torrents' + self.download_url = 'http://hdbits.org/download.php?' + + def isEnabled(self): + return sickbeard.HDBITS + + def _checkAuth(self): + + if not sickbeard.HDBITS_USERNAME or not sickbeard.HDBITS_PASSKEY: + raise AuthException("Your authentication credentials for " + self.name + " are missing, check your config.") + + return True + + def _checkAuthFromData(self, parsedJSON): + + if parsedJSON is None: + return self._checkAuth() + + if 'status' in parsedJSON and 'message' in parsedJSON: + if parsedJSON.get('status') == 5: + logger.log(u"Incorrect authentication credentials for " + self.name + " : " + parsedJSON['message'], logger.DEBUG) + raise AuthException("Your authentication credentials for " + self.name + " are incorrect, check your config.") + + return True + + def _get_season_search_strings(self, ep_obj): + season_search_string = [self._make_post_data_JSON(show=ep_obj.show, season=ep_obj.scene_season)] + return season_search_string + + def _get_episode_search_strings(self, ep_obj, add_string=''): + episode_search_string = [self._make_post_data_JSON(show=ep_obj.show, episode=ep_obj)] + return episode_search_string + + def _get_title_and_url(self, item): + + title = item['name'] + if title: + title = title.replace(' ', '.') + + url = self.download_url + urllib.urlencode({'id': item['id'], 'passkey': sickbeard.HDBITS_PASSKEY}) + + return (title, url) + + def _doSearch(self, search_params, show=None, age=None): + results = [] -import urllib -import generic -import sickbeard - -from sickbeard import logger, tvcache, exceptions -from sickbeard.common import Quality, Overview -from sickbeard.exceptions import ex, AuthException -from sickbeard.name_parser.parser import NameParser, InvalidNameException - -try: - import json -except ImportError: - from lib import simplejson as json - - -class HDBitsProvider(generic.TorrentProvider): - def __init__(self): - - generic.TorrentProvider.__init__(self, "HDBits") - - self.supportsBacklog = False - - self.cache = HDBitsCache(self) - - self.url = 'https://hdbits.org' - self.search_url = 'https://hdbits.org/api/torrents' - self.rss_url = 'https://hdbits.org/api/torrents' - self.download_url = 'http://hdbits.org/download.php?' - - def isEnabled(self): - return sickbeard.HDBITS - - def _checkAuth(self): - - if not sickbeard.HDBITS_USERNAME or not sickbeard.HDBITS_PASSKEY: - raise AuthException("Your authentication credentials for " + self.name + " are missing, check your config.") - - return True - - def _checkAuthFromData(self, parsedJSON): - - if parsedJSON is None: - return self._checkAuth() - - if 'status' in parsedJSON and 'message' in parsedJSON: - if parsedJSON.get('status') == 5: - logger.log(u"Incorrect authentication credentials for " + self.name + " : " + parsedJSON['message'], - logger.DEBUG) - raise AuthException( - "Your authentication credentials for " + self.name + " are incorrect, check your config.") - - return True - - def searchProviders(self, show, season, episode=None, manualSearch=False): - itemList = [] - results = {} - - logger.log(u"Searching for stuff we need from " + show.name + " season " + str(season)) - - # gather all episodes for season and then pick out the wanted episodes and compare to determin if we want whole season or just a few episodes - if episode is None: - seasonEps = self.show.getAllEpisodes(season) - wantedEps = [x for x in seasonEps if self.show.getOverview(x.status) in (Overview.WANTED, Overview.QUAL)] - else: - wantedEps = [show.getEpisode(season, episode)] - - for ep_obj in wantedEps: - season = ep_obj.scene_season - episode = ep_obj.scene_episode - - if manualSearch: - self.cache.updateCache() - - results = self.cache.searchCache(episode, manualSearch) - logger.log(u"Cache results: " + str(results), logger.DEBUG) - - # if we got some results then use them no matter what. - # OR - # return anyway unless we're doing a manual search - if results or not manualSearch: - return results - - itemList += self.getURL(self.search_url, post_data=self._make_post_data_JSON(show=show, season=season, episode=episode), json=True) - - - for parsedJSON in itemList: - if not parsedJSON: - logger.log(u"No data returned from " + self.search_url, logger.ERROR) - return [] - - if self._checkAuthFromData(parsedJSON): - results = [] - - if parsedJSON and 'data' in parsedJSON: - items = parsedJSON['data'] - else: - logger.log(u"Resulting JSON from " + self.name + " isn't correct, not parsing it", logger.ERROR) - items = [] - - for item in items: - - (title, url) = self._get_title_and_url(item) - - # parse the file name - try: - myParser = NameParser() - parse_result = myParser.parse(title) - except InvalidNameException: - logger.log(u"Unable to parse the filename " + title + " into a valid episode", logger.WARNING) - continue - - if episode.show.air_by_date or episode.sports: - if parse_result.air_date != episode.airdate: - logger.log(u"Episode " + title + " didn't air on " + str(episode.airdate) + ", skipping it", - logger.DEBUG) - continue - elif parse_result.season_number != episode.season or episode.episode not in parse_result.episode_numbers: - logger.log(u"Episode " + title + " isn't " + str(episode.season) + "x" + str( - episode.episode) + ", skipping it", logger.DEBUG) - continue - - quality = self.getQuality(item) - - if not episode.show.wantEpisode(episode.season, episode.episode, quality, manualSearch): - logger.log(u"Ignoring result " + title + " because we don't want an episode that is " + - Quality.qualityStrings[quality], logger.DEBUG) - continue - - logger.log(u"Found result " + title + " at " + url, logger.DEBUG) - - result = self.getResult([episode]) - result.url = url - result.name = title - result.quality = quality - - results.append(result) - - return results - - def _get_title_and_url(self, item): - - title = item['name'] - url = self.download_url + urllib.urlencode({'id': item['id'], 'passkey': sickbeard.HDBITS_PASSKEY}) - - return (title, url) - - def _make_post_data_JSON(self, show=None, episode=None, season=None): - - post_data = { - 'username': sickbeard.HDBITS_USERNAME, - 'passkey': sickbeard.HDBITS_PASSKEY, - 'category': [2], # TV Category - } - - if episode: - post_data['tvdb'] = { + self._checkAuth() + + logger.log(u"Search url: " + self.search_url + " search_params: " + search_params, logger.DEBUG) + + data = self.getURL(self.search_url, post_data=search_params) + + if not data: + logger.log(u"No data returned from " + self.search_url, logger.ERROR) + return [] + + parsedJSON = helpers.parse_json(data) + + if parsedJSON is None: + logger.log(u"Error trying to load " + self.name + " JSON data", logger.ERROR) + return [] + + if self._checkAuthFromData(parsedJSON): + if parsedJSON and 'data' in parsedJSON: + items = parsedJSON['data'] + else: + logger.log(u"Resulting JSON from " + self.name + " isn't correct, not parsing it", logger.ERROR) + items = [] + + for item in items: + results.append(item) + + return results + + def findPropers(self, search_date=None): + results = [] + + search_terms = [' proper ', ' repack '] + + for term in search_terms: + for item in self._doSearch(self._make_post_data_JSON(search_term=term)): + if item['utadded']: + try: + result_date = datetime.datetime.fromtimestamp(int(item['utadded'])) + except: + result_date = None + + if result_date: + if not search_date or result_date > search_date: + title, url = self._get_title_and_url(item) + results.append(classes.Proper(title, url, result_date)) + + return results + + def _make_post_data_JSON(self, show=None, episode=None, season=None, search_term=None): + + post_data = { + 'username': sickbeard.HDBITS_USERNAME, + 'passkey': sickbeard.HDBITS_PASSKEY, + 'category': [2], # TV Category + } + + if episode: + post_data['tvdb'] = { + 'id': show.tvdbid, + 'season': episode.scene_season, + 'episode': episode.scene_episode + } + + if season: + post_data['tvdb'] = { 'id': show.indexerid, - 'season': season, - 'episode': episode - } - - if season: - post_data['tvdb'] = { - 'id': show.indexerid, - 'season': season, - } - - return json.dumps(post_data) - - -class HDBitsCache(tvcache.TVCache): - def __init__(self, provider): - - tvcache.TVCache.__init__(self, provider) - - # only poll HDBits every 15 minutes max - self.minTime = 15 - - def updateCache(self): - - if not self.shouldUpdate(): - return - - if self._checkAuth(None): - - parsedJSON = self._getRSSData() - - # As long as we got something from the provider we count it as an update - if parsedJSON: - self.setLastUpdate() - else: - return [] - - logger.log(u"Clearing " + self.provider.name + " cache and updating with new information") - self._clearCache() - - if parsedJSON is None: - logger.log(u"Error trying to load " + self.provider.name + " JSON feed", logger.ERROR) - return [] - - if self._checkAuth(parsedJSON): - if parsedJSON and 'data' in parsedJSON: - items = parsedJSON['data'] - else: - logger.log(u"Resulting JSON from " + self.provider.name + " isn't correct, not parsing it", - logger.ERROR) - return [] - - cl = [] + 'season': season, + } + + if search_term: + post_data['search'] = search_term + + return json.dumps(post_data) + + +class HDBitsCache(tvcache.TVCache): + + def __init__(self, provider): + + tvcache.TVCache.__init__(self, provider) + + # only poll HDBits every 15 minutes max + self.minTime = 15 + + def updateCache(self): + + if not self.shouldUpdate(): + return + + if self._checkAuth(None): + + data = self._getRSSData() + + # As long as we got something from the provider we count it as an update + if data: + self.setLastUpdate() + else: + return [] + + logger.log(u"Clearing " + self.provider.name + " cache and updating with new information") + self._clearCache() + + parsedJSON = helpers.parse_json(data) + + if parsedJSON is None: + logger.log(u"Error trying to load " + self.provider.name + " JSON feed", logger.ERROR) + return [] + + if self._checkAuth(parsedJSON): + if parsedJSON and 'data' in parsedJSON: + items = parsedJSON['data'] + else: + logger.log(u"Resulting JSON from " + self.provider.name + " isn't correct, not parsing it", logger.ERROR) + return [] + + ql = [] for item in items: ci = self._parseItem(item) if ci is not None: - cl.append(ci) + ql.append(ci) - if len(cl) > 0: - myDB = self._getDB() - myDB.mass_action(cl) + myDB = self._getDB() + myDB.mass_action(ql) else: - raise exceptions.AuthException( - "Your authentication info for " + self.provider.name + " is incorrect, check your config") - - else: - return [] - - def _getRSSData(self): - return self.provider.getURL(self.provider.rss_url, post_data=self.provider._make_post_data_JSON(), json=True) - - def _parseItem(self, item): - - (title, url) = self.provider._get_title_and_url(item) - - if title and url: - logger.log(u"Adding item to results: " + title, logger.DEBUG) - return self._addCacheEntry(title, url) - else: - logger.log(u"The data returned from the " + self.provider.name + " is incomplete, this result is unusable", - logger.ERROR) - return None - - def _checkAuth(self, data): - return self.provider._checkAuthFromData(data) - - -provider = HDBitsProvider() + raise exceptions.AuthException("Your authentication info for " + self.provider.name + " is incorrect, check your config") + + else: + return [] + + def _getRSSData(self): + return self.provider.getURL(self.provider.rss_url, post_data=self.provider._make_post_data_JSON()) + + def _parseItem(self, item): + + (title, url) = self.provider._get_title_and_url(item) + + if title and url: + logger.log(u"Adding item to results: " + title, logger.DEBUG) + return self._addCacheEntry(title, url) + else: + logger.log(u"The data returned from the " + self.provider.name + " is incomplete, this result is unusable", logger.ERROR) + return None + + def _checkAuth(self, data): + return self.provider._checkAuthFromData(data) + +provider = HDBitsProvider() \ No newline at end of file