2014-05-23 12:37:22 +00:00
|
|
|
# This file is part of SickRage.
|
2014-05-05 07:39:20 +00:00
|
|
|
#
|
2014-05-23 12:37:22 +00:00
|
|
|
# SickRage is free software: you can redistribute it and/or modify
|
2014-05-05 07:39:20 +00:00
|
|
|
# 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.
|
|
|
|
#
|
2014-05-23 12:37:22 +00:00
|
|
|
# SickRage is distributed in the hope that it will be useful,
|
2014-05-05 07:39:20 +00:00
|
|
|
# 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
|
2014-05-23 12:37:22 +00:00
|
|
|
# along with SickRage. If not, see <http://www.gnu.org/licenses/>.
|
2014-05-07 07:50:49 +00:00
|
|
|
|
2014-05-19 13:08:16 +00:00
|
|
|
import re
|
2014-05-07 07:50:49 +00:00
|
|
|
import time
|
2014-05-17 13:55:58 +00:00
|
|
|
import datetime
|
|
|
|
import urllib
|
2014-05-19 13:08:16 +00:00
|
|
|
import urlparse
|
|
|
|
import sys
|
2014-05-17 13:55:58 +00:00
|
|
|
import generic
|
|
|
|
import sickbeard
|
|
|
|
|
2014-05-19 13:08:16 +00:00
|
|
|
from lib import requests
|
|
|
|
from lib.requests import exceptions
|
2014-05-17 13:55:58 +00:00
|
|
|
from sickbeard import classes
|
|
|
|
from sickbeard import logger, tvcache, exceptions
|
|
|
|
from sickbeard import helpers
|
2014-05-19 13:08:16 +00:00
|
|
|
from sickbeard import clients
|
2014-05-17 11:40:26 +00:00
|
|
|
from sickbeard.common import cpu_presets
|
|
|
|
from sickbeard.exceptions import ex, AuthException
|
2014-05-17 13:55:58 +00:00
|
|
|
try:
|
|
|
|
import json
|
|
|
|
except ImportError:
|
|
|
|
from lib import simplejson as json
|
|
|
|
|
|
|
|
|
|
|
|
class HDBitsProvider(generic.TorrentProvider):
|
|
|
|
def __init__(self):
|
|
|
|
|
|
|
|
generic.TorrentProvider.__init__(self, "HDBits")
|
|
|
|
|
2014-05-17 05:23:11 +00:00
|
|
|
self.supportsBacklog = True
|
|
|
|
|
|
|
|
self.enabled = False
|
|
|
|
self.username = None
|
2014-05-19 20:06:32 +00:00
|
|
|
self.passkey = None
|
2014-05-17 05:23:11 +00:00
|
|
|
self.ratio = None
|
|
|
|
|
2014-05-17 13:55:58 +00:00
|
|
|
self.cache = HDBitsCache(self)
|
|
|
|
|
2014-07-31 05:02:45 +00:00
|
|
|
self.url = 'https://hdbits.org'
|
|
|
|
self.search_url = 'https://hdbits.org/api/torrents'
|
|
|
|
self.rss_url = 'https://hdbits.org/api/torrents'
|
|
|
|
self.download_url = 'https://hdbits.org/download.php?'
|
2014-05-17 13:55:58 +00:00
|
|
|
|
|
|
|
def isEnabled(self):
|
2014-05-17 05:23:11 +00:00
|
|
|
return self.enabled
|
2014-05-17 13:55:58 +00:00
|
|
|
|
|
|
|
def _checkAuth(self):
|
|
|
|
|
2014-05-19 20:06:32 +00:00
|
|
|
if not self.username or not self.passkey:
|
2014-05-17 13:55:58 +00:00
|
|
|
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
|
|
|
|
|
2014-05-05 07:39:20 +00:00
|
|
|
def _get_season_search_strings(self, ep_obj):
|
2014-07-27 10:59:21 +00:00
|
|
|
season_search_string = [self._make_post_data_JSON(show=ep_obj.show, season=ep_obj)]
|
2014-05-17 13:55:58 +00:00
|
|
|
return season_search_string
|
|
|
|
|
2014-05-05 07:39:20 +00:00
|
|
|
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)]
|
2014-05-17 13:55:58 +00:00
|
|
|
return episode_search_string
|
|
|
|
|
|
|
|
def _get_title_and_url(self, item):
|
|
|
|
|
|
|
|
title = item['name']
|
|
|
|
if title:
|
2014-07-15 02:00:53 +00:00
|
|
|
title = u'' + title
|
2014-05-17 13:55:58 +00:00
|
|
|
title = title.replace(' ', '.')
|
|
|
|
|
2014-05-19 20:06:32 +00:00
|
|
|
url = self.download_url + urllib.urlencode({'id': item['id'], 'passkey': self.passkey})
|
2014-05-17 13:55:58 +00:00
|
|
|
|
|
|
|
return (title, url)
|
|
|
|
|
2014-07-21 05:47:13 +00:00
|
|
|
def _doSearch(self, search_params, search_mode='eponly', epcount=0, age=0):
|
2014-05-05 07:39:20 +00:00
|
|
|
results = []
|
|
|
|
|
2014-05-17 13:55:58 +00:00
|
|
|
self._checkAuth()
|
|
|
|
|
|
|
|
logger.log(u"Search url: " + self.search_url + " search_params: " + search_params, logger.DEBUG)
|
|
|
|
|
2014-07-27 10:59:21 +00:00
|
|
|
parsedJSON = self.getURL(self.search_url, post_data=search_params, json=True)
|
|
|
|
if not parsedJSON:
|
2014-05-17 13:55:58 +00:00
|
|
|
return []
|
|
|
|
|
2014-05-05 07:39:20 +00:00
|
|
|
if self._checkAuthFromData(parsedJSON):
|
2014-05-17 13:55:58 +00:00
|
|
|
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)
|
2014-07-15 02:00:53 +00:00
|
|
|
results.append(classes.Proper(title, url, result_date, self.show))
|
2014-05-17 13:55:58 +00:00
|
|
|
|
|
|
|
return results
|
|
|
|
|
2014-05-05 07:39:20 +00:00
|
|
|
def _make_post_data_JSON(self, show=None, episode=None, season=None, search_term=None):
|
2014-05-17 13:55:58 +00:00
|
|
|
|
|
|
|
post_data = {
|
2014-05-17 05:23:11 +00:00
|
|
|
'username': self.username,
|
2014-05-19 20:06:32 +00:00
|
|
|
'passkey': self.passkey,
|
2014-05-17 13:55:58 +00:00
|
|
|
'category': [2],
|
|
|
|
# TV Category
|
|
|
|
}
|
|
|
|
|
|
|
|
if episode:
|
2014-06-06 23:55:14 +00:00
|
|
|
if show.air_by_date:
|
|
|
|
post_data['tvdb'] = {
|
|
|
|
'id': show.indexerid,
|
|
|
|
'episode': str(episode.airdate).replace('-', '|')
|
|
|
|
}
|
|
|
|
elif show.sports:
|
|
|
|
post_data['tvdb'] = {
|
|
|
|
'id': show.indexerid,
|
|
|
|
'episode': episode.airdate.strftime('%b')
|
|
|
|
}
|
|
|
|
elif show.anime:
|
|
|
|
post_data['tvdb'] = {
|
|
|
|
'id': show.indexerid,
|
|
|
|
'episode': "%i" % int(episode.scene_absolute_number)
|
|
|
|
}
|
|
|
|
else:
|
|
|
|
post_data['tvdb'] = {
|
|
|
|
'id': show.indexerid,
|
|
|
|
'season': episode.scene_season,
|
|
|
|
'episode': episode.scene_episode
|
|
|
|
}
|
2014-05-17 13:55:58 +00:00
|
|
|
|
2014-05-14 08:01:36 +00:00
|
|
|
if season:
|
|
|
|
if show.air_by_date or show.sports:
|
|
|
|
post_data['tvdb'] = {
|
|
|
|
'id': show.indexerid,
|
|
|
|
'season': str(episode.airdate)[:7],
|
|
|
|
}
|
2014-06-06 23:55:14 +00:00
|
|
|
elif show.anime:
|
|
|
|
post_data['tvdb'] = {
|
|
|
|
'id': show.indexerid,
|
|
|
|
'season': "%d" % episode.scene_absolute_number,
|
|
|
|
}
|
2014-05-14 08:01:36 +00:00
|
|
|
else:
|
|
|
|
post_data['tvdb'] = {
|
|
|
|
'id': show.indexerid,
|
2014-07-27 10:59:21 +00:00
|
|
|
'season': episode.scene_season,
|
2014-05-14 08:01:36 +00:00
|
|
|
}
|
|
|
|
|
2014-05-17 13:55:58 +00:00
|
|
|
if search_term:
|
|
|
|
post_data['search'] = search_term
|
|
|
|
|
2014-05-08 22:28:28 +00:00
|
|
|
return json.dumps(post_data)
|
2014-05-17 13:55:58 +00:00
|
|
|
|
2014-05-08 22:28:28 +00:00
|
|
|
def seedRatio(self):
|
2014-05-17 05:23:11 +00:00
|
|
|
return self.ratio
|
2014-05-17 13:55:58 +00:00
|
|
|
|
|
|
|
|
|
|
|
class HDBitsCache(tvcache.TVCache):
|
|
|
|
def __init__(self, provider):
|
|
|
|
|
|
|
|
tvcache.TVCache.__init__(self, provider)
|
|
|
|
|
2014-05-05 07:39:20 +00:00
|
|
|
# only poll HDBits every 15 minutes max
|
2014-05-17 13:55:58 +00:00
|
|
|
self.minTime = 15
|
|
|
|
|
|
|
|
def updateCache(self):
|
|
|
|
|
2014-05-18 15:33:31 +00:00
|
|
|
# delete anything older then 7 days
|
|
|
|
self._clearCache()
|
|
|
|
|
2014-05-17 13:55:58 +00:00
|
|
|
if not self.shouldUpdate():
|
|
|
|
return
|
|
|
|
|
|
|
|
if self._checkAuth(None):
|
|
|
|
|
2014-07-27 10:59:21 +00:00
|
|
|
parsedJSON = self._getRSSData()
|
|
|
|
if not parsedJSON:
|
2014-05-17 13:55:58 +00:00
|
|
|
logger.log(u"Error trying to load " + self.provider.name + " JSON feed", logger.ERROR)
|
|
|
|
return []
|
|
|
|
|
2014-07-27 10:59:21 +00:00
|
|
|
# mark updated
|
|
|
|
self.setLastUpdate()
|
|
|
|
|
2014-05-17 13:55:58 +00:00
|
|
|
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 []
|
|
|
|
|
2014-07-15 02:00:53 +00:00
|
|
|
cl = []
|
2014-03-10 05:18:05 +00:00
|
|
|
for item in items:
|
|
|
|
ci = self._parseItem(item)
|
|
|
|
if ci is not None:
|
2014-07-15 02:00:53 +00:00
|
|
|
cl.append(ci)
|
2014-03-25 05:57:24 +00:00
|
|
|
|
2014-07-15 02:00:53 +00:00
|
|
|
if len(cl) > 0:
|
2014-06-22 01:35:57 +00:00
|
|
|
myDB = self._getDB()
|
2014-07-15 02:00:53 +00:00
|
|
|
myDB.mass_action(cl)
|
2014-03-10 05:18:05 +00:00
|
|
|
else:
|
2014-05-17 13:55:58 +00:00
|
|
|
raise exceptions.AuthException(
|
|
|
|
"Your authentication info for " + self.provider.name + " is incorrect, check your config")
|
|
|
|
else:
|
|
|
|
return []
|
|
|
|
|
|
|
|
def _getRSSData(self):
|
2014-07-27 10:59:21 +00:00
|
|
|
return self.provider.getURL(self.provider.rss_url, post_data=self.provider._make_post_data_JSON(), json=True)
|
2014-05-17 13:55:58 +00:00
|
|
|
|
|
|
|
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)
|
|
|
|
|
|
|
|
|
2014-05-06 21:51:06 +00:00
|
|
|
provider = HDBitsProvider()
|