Skip to content

Commit

Permalink
Merge pull request #2143 from pymedusa/btnfix
Browse files Browse the repository at this point in the history
Fix BTN when air by date but show is indexed with SxxExx
  • Loading branch information
labrys authored Feb 19, 2017
2 parents e0731af + a6ca43b commit fc9bac8
Showing 1 changed file with 79 additions and 95 deletions.
174 changes: 79 additions & 95 deletions medusa/providers/torrent/json/btn.py
Original file line number Diff line number Diff line change
@@ -1,21 +1,7 @@
# coding=utf-8
# Author: Daniel Heimans
#
# This file is part of Medusa.
#
# Medusa 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.
#
# Medusa 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 Medusa. If not, see <https://www.gnu.org/licenses/>.

"""Provider code for BTN."""

from __future__ import unicode_literals

import socket
Expand All @@ -36,14 +22,12 @@
from six import itervalues


class BTNProvider(TorrentProvider):
"""BTN Torrent provider.
# API docs:
# https://web.archive.org/web/20160316073644/http://btnapps.net/docs.php
# https://web.archive.org/web/20160425205926/http://btnapps.net/apigen/class-btnapi.html

API docs:
https://web.archive.org/web/20160316073644/http://btnapps.net/docs.php
and
https://web.archive.org/web/20160425205926/http://btnapps.net/apigen/class-btnapi.html
"""
class BTNProvider(TorrentProvider):
"""BTN Torrent provider."""

def __init__(self):
"""Initialize the class."""
Expand All @@ -55,7 +39,7 @@ def __init__(self):
# URLs
self.url = 'https://broadcasthe.net'
self.urls = {
'base_url': 'https://api.btnapps.net',
'base_url': 'https://api.broadcasthe.net',
}

# Proper Strings
Expand All @@ -71,11 +55,11 @@ def __init__(self):
# Cache
self.cache = tv.Cache(self, min_time=10) # Only poll BTN every 15 minutes max

def search(self, search_strings, age=0, ep_obj=None): # pylint:disable=too-many-locals
def search(self, search_strings, age=0, ep_obj=None):
"""
Search a provider and parse the results.
:param search_strings: A dict with mode (key) and the search value (value)
:param search_strings: A dict with {mode: search value}
:param age: Not used
:param ep_obj: Not used
:returns: A list of search results (structure)
Expand All @@ -93,19 +77,21 @@ def search(self, search_strings, age=0, ep_obj=None): # pylint:disable=too-many
logger.log('Search mode: {0}'.format(mode), logger.DEBUG)

if mode != 'RSS':
if mode == 'Season':
search_params = self._season_search_params(ep_obj)
else:
search_params = self._episode_search_params(ep_obj)
logger.log('Search string: {search}'.format
(search=search_params), logger.DEBUG)

response = self._api_call(self.api_key, search_params)
if not response or response.get('results') == '0':
logger.log('No data returned from provider', logger.DEBUG)
continue
searches = self._search_params(ep_obj, mode)
else:
searches = [search_params]

for search_params in searches:
if mode != 'RSS':
logger.log('Search string: {search}'.format
(search=search_params), logger.DEBUG)

response = self._api_call(search_params)
if not response or response.get('results') == '0':
logger.log('No data returned from provider', logger.DEBUG)
continue

results += self.parse(response.get('torrents', {}), mode)
results += self.parse(response.get('torrents', {}), mode)

return results

Expand Down Expand Up @@ -147,8 +133,13 @@ def parse(self, data, mode):
'leechers': leechers,
'pubdate': None,
}
logger.log('Found result: {0} with {1} seeders and {2} leechers'.format
(title, seeders, leechers), logger.DEBUG)
logger.log(
'Found result: {title} with {x} seeders'
' and {y} leechers'.format(
title=title, x=seeders, y=leechers
),
logger.DEBUG
)

items.append(item)

Expand Down Expand Up @@ -189,22 +180,37 @@ def _process_title_and_url(parsed_json):
url = parsed_json.get('DownloadURL').replace('\\/', '/')
return title, url

def _season_search_params(self, ep_obj):
def _search_params(self, ep_obj, mode, season_numbering=None):

if not ep_obj:
return []

searches = []
season = 'Season' if mode == 'Season' else ''

search_params = []
current_params = {'category': 'Season'}
air_by_date = ep_obj.show.air_by_date
sports = ep_obj.show.sports

# Search for entire seasons: no need to do special things for air by date or sports shows
if ep_obj.show.air_by_date or ep_obj.show.sports:
# Search for the year of the air by date show
current_params['name'] = str(ep_obj.airdate).split('-')[0]
if not season_numbering and (air_by_date or sports):
date_fmt = '%Y' if season else '%Y.%m.%d'
search_name = ep_obj.airdate.strftime(date_fmt)
else:
current_params['name'] = 'Season {0}'.format(ep_obj.season)
search_name = '{type} {number}'.format(
type=season,
number=ep_obj.season if season else episode_num(
ep_obj.season, ep_obj.episode
),
).strip()

params = {
'category': season or 'Episode',
'name': search_name,
}

# Search
if ep_obj.show.indexer == 1:
current_params['tvdb'] = self._get_tvdb_id()
search_params.append(current_params)
params['tvdb'] = self._get_tvdb_id()
searches.append(params)
else:
name_exceptions = scene_exceptions.get_scene_exceptions(
ep_obj.show.indexerid,
Expand All @@ -213,67 +219,45 @@ def _season_search_params(self, ep_obj):
name_exceptions.add(ep_obj.show.name)
for name in name_exceptions:
# Search by name if we don't have tvdb id
current_params['series'] = name
search_params.append(current_params)

return search_params

def _episode_search_params(self, ep_obj):
params['series'] = name
searches.append(params)

if not ep_obj:
return [{}]

to_return = []
search_params = {'category': 'Episode'}

# Episode
if ep_obj.show.air_by_date or ep_obj.show.sports:
date_str = str(ep_obj.airdate)
# BTN uses dots in dates, we just search for the date since that
# combined with the series identifier should result in just one episode
search_params['name'] = date_str.replace('-', '.')
else:
# Do a general name search for the episode, formatted like SXXEYY
search_params['name'] = '{ep}'.format(ep=episode_num(ep_obj.season, ep_obj.episode))

# Search
if ep_obj.show.indexer == 1:
search_params['tvdb'] = self._get_tvdb_id()
to_return.append(search_params)
else:
# Add new query string for every exception
name_exceptions = scene_exceptions.get_scene_exceptions(
ep_obj.show.indexerid,
ep_obj.show.indexer
# extend air by date searches to include season numbering
if air_by_date and not season_numbering:
searches.extend(
self._search_params(ep_obj, mode, season_numbering=True)
)
name_exceptions.add(ep_obj.show.name)
for cur_exception in name_exceptions:
# Search by name if we don't have tvdb id
search_params['series'] = cur_exception
to_return.append(search_params)

return to_return
return searches

def _api_call(self, apikey, params=None, results_per_page=300, offset=0):
def _api_call(self, params=None, results_per_page=300, offset=0):
"""Call provider API."""
parsed_json = {}

try:
server = jsonrpclib.Server(self.urls['base_url'])
parsed_json = server.getTorrents(apikey, params or {}, int(results_per_page), int(offset))
parsed_json = server.getTorrents(
self.api_key,
params or {},
int(results_per_page),
int(offset)
)
time.sleep(cpu_presets[app.CPU_PRESET])
except jsonrpclib.jsonrpc.ProtocolError as error:
if error.message[1] == 'Invalid API Key':
logger.log('Incorrect authentication credentials.', logger.WARNING)
logger.log('Incorrect authentication credentials.',
logger.WARNING)
elif error.message[1] == 'Call Limit Exceeded':
logger.log('You have exceeded the limit of 150 calls per hour.', logger.WARNING)
logger.log('You have exceeded the limit of'
' 150 calls per hour.', logger.WARNING)
else:
logger.log('JSON-RPC protocol error while accessing provider. Error: {msg!r}'.format
(msg=error.message[1]), logger.ERROR)
logger.log('JSON-RPC protocol error while accessing provider.'
' Error: {msg!r}'.format(msg=error.message[1]),
logger.ERROR)

except (socket.error, socket.timeout, ValueError) as error:
logger.log('Error while accessing provider. Error: {msg}'.format
(msg=error), logger.WARNING)
logger.log('Error while accessing provider.'
' Error: {msg}'.format(msg=error), logger.WARNING)
return parsed_json


Expand Down

0 comments on commit fc9bac8

Please sign in to comment.