Browse Source

Merge branch 'feature/AddTVmazeCards' into develop

tags/release_0.25.1
JackDandy 4 years ago
parent
commit
d7363ce78d
  1. 3
      CHANGES.md
  2. 3
      gui/slick/css/style.css
  3. 85
      gui/slick/interfaces/default/home_browseShows.tmpl
  4. 4
      gui/slick/interfaces/default/inc_top.tmpl
  5. 2
      lib/imdb_api/imdb_api.py
  6. 2
      lib/libtrakt/indexerapiinterface.py
  7. 2
      lib/tmdb_api/tmdb_api.py
  8. 26
      lib/tvinfo_base/base.py
  9. 215
      lib/tvmaze_api/tvmaze_api.py
  10. 9
      sickbeard/__init__.py
  11. 2
      sickbeard/indexers/indexer_config.py
  12. 12
      sickbeard/tv.py
  13. 159
      sickbeard/webserve.py

3
CHANGES.md

@ -79,6 +79,9 @@
* Add fetch extra data fallback from TMDB for persons * Add fetch extra data fallback from TMDB for persons
* Change fanart icon * Change fanart icon
* Add provider TorrentDB * Add provider TorrentDB
* Add menu Shows/"TVmaze Cards"
* Add show name/networks card user input filter
* Change only auto refresh card view if a recoverable error occurs
[develop changelog] [develop changelog]

3
gui/slick/css/style.css

@ -707,7 +707,8 @@ inc_top.tmpl
} }
.sgicon-tvmaze:before{ .sgicon-tvmaze:before{
content:"\e89a" content:"\e89a";
margin-right:14px
} }
.sgicon-emby:before{ .sgicon-emby:before{

85
gui/slick/interfaces/default/home_browseShows.tmpl

@ -8,6 +8,8 @@
<% def sg_str(varname, default=''): return getattr(sickbeard, varname, default) %>#slurp# <% def sg_str(varname, default=''): return getattr(sickbeard, varname, default) %>#slurp#
## ##
#set $mode = $kwargs and $kwargs.get('mode', '') #set $mode = $kwargs and $kwargs.get('mode', '')
#set $use_network = $kwargs.get('use_networks', False)
#set $use_returning = 'returning' == mode
#set $use_votes = $kwargs and $kwargs.get('use_votes', True) #set $use_votes = $kwargs and $kwargs.get('use_votes', True)
#set $use_ratings = $kwargs and $kwargs.get('use_ratings', True) #set $use_ratings = $kwargs and $kwargs.get('use_ratings', True)
## ##
@ -20,7 +22,11 @@
## ##
#import os.path #import os.path
#include $os.path.join($sg_str('PROG_DIR'), 'gui/slick/interfaces/default/inc_top.tmpl') #include $os.path.join($sg_str('PROG_DIR'), 'gui/slick/interfaces/default/inc_top.tmpl')
<script>
var config = {
homeSearchFocus: #echo ['!1','!0'][$sg_var('HOME_SEARCH_FOCUS', True)]#,
};
</script>
<script type="text/javascript" src="$sg_root/js/plotTooltip.js?v=$sbPID"></script> <script type="text/javascript" src="$sg_root/js/plotTooltip.js?v=$sbPID"></script>
<script type="text/javascript" charset="utf-8"> <script type="text/javascript" charset="utf-8">
@ -78,6 +84,13 @@ $(document).ready(function(){
#end if #end if
return name.toLowerCase(); return name.toLowerCase();
}, },
#if $use_returning
#raw
returning: function( itemElem ) {
return $( itemElem ).attr('data-returning') || '';
},
#end raw
#end if
#if $use_ratings #if $use_ratings
rating: '[data-rating] parseFloat', rating: '[data-rating] parseFloat',
#end if #end if
@ -98,6 +111,9 @@ $(document).ready(function(){
#end raw #end raw
case 'order': case 'order':
case 'premiered': case 'premiered':
#if $use_returning
case 'returning':
#end if
#if $use_votes #if $use_votes
case 'votes': case 'votes':
#end if #end if
@ -222,7 +238,8 @@ $(document).ready(function(){
$('#showfilter').on('change', function(){ $('#showfilter').on('change', function(){
var filterValue = this.value; var filterValue = this.value;
if (-1 == filterValue.indexOf('trakt') && -1 == filterValue.indexOf('imdb') && -1 == filterValue.indexOf('mc_') if (-1 == filterValue.indexOf('trakt') && -1 == filterValue.indexOf('imdb') && -1 == filterValue.indexOf('mc_')
&& -1 == filterValue.indexOf('tvc_') && -1 == filterValue.indexOf('ne_') && -1 == filterValue.indexOf('_ne') && -1 == filterValue.indexOf('tvc_') && -1 == filterValue.indexOf('tvm_')
&& -1 == filterValue.indexOf('ne_') && -1 == filterValue.indexOf('_ne')
&& -1 == filterValue.indexOf('default')) { && -1 == filterValue.indexOf('default')) {
var el$ = $('#container') var el$ = $('#container')
el$.on('layoutComplete', llUpdate); el$.on('layoutComplete', llUpdate);
@ -241,6 +258,34 @@ $(document).ready(function(){
}); });
$('.service, .browse-image').each(addQTip); $('.service, .browse-image').each(addQTip);
if (config.homeSearchFocus) {
$('#search_show_name').focus();
}
llUpdate = (function(){
$.ll.handleScroll();
});
$('#search_show_name').on('input', function() {
var obj = $('#container');
obj.one('layoutComplete', llUpdate);
obj.isotope({
filter: function () {
var reSearch = RegExp($('#search_show_name').val(), 'i');
return reSearch.test($(this).attr('data-name'))#end raw##slurp#
#if $use_network#
|| reSearch.test(\$(this).attr('data-network'))#end if#;
#raw
}
});
});
$('.resetshows').click(function() {
var input = $('#search_show_name');
if ('' !== input.val()){
input.val('').trigger('input').change();
if (config.homeSearchFocus)
input.focus();
}
});
}); });
#end raw #end raw
@ -279,8 +324,14 @@ $(document).ready(function(){
</optgroup> </optgroup>
<optgroup label="Sort by"> <optgroup label="Sort by">
<option value="by_name"#if 'by_name' in $saved_showsort_sortby and not $reset_showsort_sortby#$selected>>&nbsp;#else#>#end if#Name</option> <option value="by_name"#if 'by_name' in $saved_showsort_sortby and not $reset_showsort_sortby#$selected>>&nbsp;#else#>#end if#Name</option>
# omit for TVMaze as Original == First Aired
#if 'TVmaze' not in $browse_type
<option value="by_order"#if 'by_order' in $saved_showsort_sortby or $reset_showsort_sortby#$selected>>&nbsp;#else#>#end if#Original</option> <option value="by_order"#if 'by_order' in $saved_showsort_sortby or $reset_showsort_sortby#$selected>>&nbsp;#else#>#end if#Original</option>
#end if
<option value="by_premiered"#if 'by_premiered' in $saved_showsort_sortby and not $reset_showsort_sortby#$selected>>&nbsp;#else#>#end if#First aired</option> <option value="by_premiered"#if 'by_premiered' in $saved_showsort_sortby and not $reset_showsort_sortby#$selected>>&nbsp;#else#>#end if#First aired</option>
#if $use_returning
<option value="by_returning"#if 'by_returning' in $saved_showsort_sortby and not $reset_showsort_sortby#$selected>>&nbsp;#else#>#end if#Returning</option>
#end if
#if $use_votes #if $use_votes
<option value="by_votes"#if 'by_votes' in $saved_showsort_sortby#$selected>>&nbsp;#else#>#end if#Votes</option> <option value="by_votes"#if 'by_votes' in $saved_showsort_sortby#$selected>>&nbsp;#else#>#end if#Votes</option>
#end if #end if
@ -381,6 +432,11 @@ $(document).ready(function(){
#end for #end for
<option value="tvc_latest"#echo ('', selected)['latest' == $mode]#>Latest additions</option> <option value="tvc_latest"#echo ('', selected)['latest' == $mode]#>Latest additions</option>
</optgroup> </optgroup>
#elif 'TVmaze' == $browse_type
<optgroup label="TVmaze">
<option value="tvm_premieres"#echo ('', selected)['premieres' == $mode]#>Premieres</option>
<option value="tvm_returning"#echo ('', selected)['returning' == $mode]#>Returning</option>
</optgroup>
#elif 'Nextepisode' == $browse_type #elif 'Nextepisode' == $browse_type
<optgroup label="Nextepisode"> <optgroup label="Nextepisode">
<option value="ne_newpop"#echo ('', selected)['newpop' == $mode]#>Popular premiered</option> <option value="ne_newpop"#echo ('', selected)['newpop' == $mode]#>Popular premiered</option>
@ -393,9 +449,13 @@ $(document).ready(function(){
</select> </select>
#end if #end if
</div> </div>
<div class="pull-right" style="clear:right">
<input id="search_show_name" class="search form-control form-control-inline input-sm input200" type="search" placeholder="Filter Show Name#if $use_network#/Network#end if#">
&nbsp;<button type="button" class="resetshows btn btn-inline">Reset Filter</button>
</div>
<h4 style="float:left;margin:0 0 0 2px">$browse_title</h4> <h4 style="float:left;margin:0 0 0 2px">$browse_title</h4>
#if $kwargs and $kwargs.get('oldest') #if $kwargs and $kwargs.get('oldest')
<div class="grey-text" style="clear:both;margin-left:2px;font-size:0.85em"> <div class="grey-text" style="clear:left;margin-left:2px;font-size:0.85em">
First aired from $kwargs['oldest'] until $kwargs['newest'] First aired from $kwargs['oldest'] until $kwargs['newest']
</div> </div>
#end if #end if
@ -421,11 +481,14 @@ $(document).ready(function(){
#set $hide = ('', '%shide ' % ('', 'to-')['.hide' in $saved_showsort_view])[bool($this_show.get('hide'))] #set $hide = ('', '%shide ' % ('', 'to-')['.hide' in $saved_showsort_view])[bool($this_show.get('hide'))]
#set $data_rating = $try_float($this_show['rating']) #set $data_rating = $try_float($this_show['rating'])
<div class="show-card ${hide}${known}inlibrary" data-name="#echo re.sub(r'([\'\"])', r'', $this_show['title'])#" data_id="$show_id"#if $use_ratings# data-rating="$data_rating"#end if##if $use_votes# data-votes="$this_show['votes']"#end if# data-premiered="$this_show['premiered']" data-order="$this_show['order']"> <div class="show-card ${hide}${known}inlibrary" data-name="#echo re.sub(r'([\'\"])', r'', $this_show['title'])#" data_id="$show_id"#if $use_ratings# data-rating="$data_rating"#end if##if $use_votes# data-votes="$this_show['votes']"#end if# data-premiered="$this_show['premiered']"#if $use_returning# data-returning="$this_show['returning']"#end if# data-order="$this_show['order']"#if $use_network# data-network="$this_show['network']"#end if#>
<div class="show-card-inner"> <div class="show-card-inner">
<div class="browse-image"> <div class="browse-image">
<a class="browse-image" href="<%= anon_url(this_show['url_src_db']) %>" target="_blank" <a class="browse-image" href="<%= anon_url(this_show['url_src_db']) %>" target="_blank"
title="<span style='color: rgb(66, 139, 202)'>$re.sub(r'(?m)\s+\((?:19|20)\d\d\)\s*$', '', $title_html)</span>#if $this_show['genres']#<br /><div style='font-weight:bold'>(<em>$this_show['genres']</em>)</div>#end if# title="<span style='color: rgb(66, 139, 202)'>$re.sub(r'(?m)\s+\((?:19|20)\d\d\)\s*$', '', $title_html)</span>
#if $this_show['genres']#<br><div style='font-weight:bold'>(<em>$this_show['genres']</em>)</div>#end if#
#if $kwargs and $use_returning#<span style='font-weight:bold;font-size:0.9em;color:#888'><em>Season $this_show['episode_season'] returns $this_show['returning_str']</em></span>#end if#
#if $this_show.get('country') or $this_show.get('language') #if $this_show.get('country') or $this_show.get('language')
<p style='line-height:15px;margin-bottom:2px'> <p style='line-height:15px;margin-bottom:2px'>
#if $this_show.get('country') #if $this_show.get('country')
@ -496,18 +559,18 @@ $(document).ready(function(){
$kwargs['error_msg'] $kwargs['error_msg']
#else #else
$browse_type did not return results, this can happen from time to time. $browse_type did not return results, this can happen from time to time.
<br /><br />This view should auto refresh every 10 mins. <br><br>This view should auto refresh every 10 mins.
#end if
</p>
</div>
</div>
#end if
<script type="text/javascript" charset="utf-8"> <script type="text/javascript" charset="utf-8">
<!-- <!--
window.setInterval('location.reload(true)', 600000); // Refresh every 10 minutes window.setInterval('location.reload(true)', 600000); // Refresh every 10 minutes
//--> //-->
</script> </script>
#end if
</p>
</div>
</div>
#end if
<script type="text/javascript" src="$sg_root/js/lazyload/lazyload.min.js?v=$sbPID"></script> <script type="text/javascript" src="$sg_root/js/lazyload/lazyload.min.js?v=$sbPID"></script>
<script type="text/javascript" src="$sg_root/js/inc_bottom.js?v=$sbPID"></script> <script type="text/javascript" src="$sg_root/js/inc_bottom.js?v=$sbPID"></script>

4
gui/slick/interfaces/default/inc_top.tmpl

@ -188,6 +188,10 @@
#set $tvc_mode = $tvc_modes.get($sg_var('TVC_MRU'), 'new shows') #set $tvc_mode = $tvc_modes.get($sg_var('TVC_MRU'), 'new shows')
<li><a href="$sbRoot/add-shows/tvc-default/" tabindex="$tab#set $tab += 1#"><i class="sgicon-tvc"></i>TV Calendar Cards <li><a href="$sbRoot/add-shows/tvc-default/" tabindex="$tab#set $tab += 1#"><i class="sgicon-tvc"></i>TV Calendar Cards
<div class="menu-item-desc opacity60">$tvc_mode...</div></a></li> <div class="menu-item-desc opacity60">$tvc_mode...</div></a></li>
#set $tvm_modes = dict(tvm_premieres='new shows', tvm_returning='returning')
#set $tvm_mode = $tvm_modes.get($sg_var('TVM_MRU'), 'new shows')
<li><a href="$sbRoot/add-shows/tvm-default/" tabindex="$tab#set $tab += 1#"><i class="sgicon-tvmaze"></i>TVmaze Cards
<div class="menu-item-desc opacity60">$tvm_mode...</div></a></li>
#set $ne_modes = dict(ne_newpop='new popular', ne_newtop='new top rated', ne_upcoming='upcoming', ne_trending='trending') #set $ne_modes = dict(ne_newpop='new popular', ne_newtop='new top rated', ne_upcoming='upcoming', ne_trending='trending')
#set $ne_mode = $ne_modes.get($sg_var('NE_MRU'), 'new popular') #set $ne_mode = $ne_modes.get($sg_var('NE_MRU'), 'new popular')
<li><a href="$sbRoot/add-shows/ne-default/" tabindex="$tab#set $tab += 1#"><i class="sgicon-ne"></i>Next Episode Cards <li><a href="$sbRoot/add-shows/ne-default/" tabindex="$tab#set $tab += 1#"><i class="sgicon-ne"></i>Next Episode Cards

2
lib/imdb_api/imdb_api.py

@ -80,7 +80,7 @@ class IMDbIndexer(TVInfoBase):
is_none, shows = self._get_cache_entry(cache_id_key) is_none, shows = self._get_cache_entry(cache_id_key)
if not self.config.get('cache_search') or (None is shows and not is_none): if not self.config.get('cache_search') or (None is shows and not is_none):
try: try:
show = imdbpie.Imdb().get_title_auxiliary('tt%07d' % p) show = imdbpie.Imdb().get_title_auxiliary('tt%08d' % p)
except (BaseException, Exception): except (BaseException, Exception):
continue continue
self._set_cache_entry(cache_id_key, show, expire=self.search_cache_expire) self._set_cache_entry(cache_id_key, show, expire=self.search_cache_expire)

2
lib/libtrakt/indexerapiinterface.py

@ -185,7 +185,7 @@ class TraktIndexer(TVInfoBase):
if TraktSearchTypes.trakt_slug == search_type: if TraktSearchTypes.trakt_slug == search_type:
url = '/shows/%s?extended=full' % series url = '/shows/%s?extended=full' % series
elif TraktSearchTypes.text != search_type: elif TraktSearchTypes.text != search_type:
url = '/search/%s/%s?type=%s&extended=full&limit=100' % (search_type, (series, 'tt%07d' % series)[ url = '/search/%s/%s?type=%s&extended=full&limit=100' % (search_type, (series, 'tt%08d' % series)[
TraktSearchTypes.imdb_id == search_type and not str(series).startswith('tt')], TraktSearchTypes.imdb_id == search_type and not str(series).startswith('tt')],
','.join(self.config['result_types'])) ','.join(self.config['result_types']))
else: else:

2
lib/tmdb_api/tmdb_api.py

@ -210,7 +210,7 @@ class TmdbIndexer(TVInfoBase):
is_none, shows = self._get_cache_entry(cache_id_key) is_none, shows = self._get_cache_entry(cache_id_key)
if not self.config.get('cache_search') or (None is shows and not is_none): if not self.config.get('cache_search') or (None is shows and not is_none):
try: try:
show = tmdbsimple.Find(id=(p, 'tt%07d' % p)[t == TVINFO_IMDB]).info( show = tmdbsimple.Find(id=(p, 'tt%08d' % p)[t == TVINFO_IMDB]).info(
external_source=id_map[t]) external_source=id_map[t])
if show.get('tv_results') and 1 == len(show['tv_results']): if show.get('tv_results') and 1 == len(show['tv_results']):
show = tmdbsimple.TV(id=show['tv_results'][0]['id']).info( show = tmdbsimple.TV(id=show['tv_results'][0]['id']).info(

26
lib/tvinfo_base/base.py

@ -126,6 +126,9 @@ class TVInfoIDs(object):
return {TVINFO_TVDB: self.tvdb, TVINFO_TMDB: self.tmdb, TVINFO_TVMAZE: self.tvmaze, return {TVINFO_TVDB: self.tvdb, TVINFO_TMDB: self.tmdb, TVINFO_TVMAZE: self.tvmaze,
TVINFO_IMDB: self.imdb, TVINFO_TRAKT: self.trakt, TVINFO_TVRAGE: self.rage}.get(key) TVINFO_IMDB: self.imdb, TVINFO_TRAKT: self.trakt, TVINFO_TVRAGE: self.rage}.get(key)
def get(self, key):
return self.__getitem__(key)
def __iter__(self): def __iter__(self):
for s, v in [(TVINFO_TVDB, self.tvdb), (TVINFO_TMDB, self.tmdb), (TVINFO_TVMAZE, self.tvmaze), for s, v in [(TVINFO_TVDB, self.tvdb), (TVINFO_TMDB, self.tmdb), (TVINFO_TVMAZE, self.tvmaze),
(TVINFO_IMDB, self.imdb), (TVINFO_TRAKT, self.trakt), (TVINFO_TVRAGE, self.rage)]: (TVINFO_IMDB, self.imdb), (TVINFO_TRAKT, self.trakt), (TVINFO_TVRAGE, self.rage)]:
@ -502,14 +505,16 @@ class TVInfoEpisode(dict):
self.thumbadded = None # type: Optional[AnyStr] self.thumbadded = None # type: Optional[AnyStr]
self.rating = None # type: Union[integer_types, float] self.rating = None # type: Union[integer_types, float]
self.siteratingcount = None # type: integer_types self.siteratingcount = None # type: integer_types
self.show = None # type: Optional[TVInfoShow]
def __str__(self): def __str__(self):
show_name = self.show and self.show.seriesname and '<Show %s> - ' % self.show.seriesname
seasno, epno = int(getattr(self, 'seasonnumber', 0)), int(getattr(self, 'episodenumber', 0)) seasno, epno = int(getattr(self, 'seasonnumber', 0)), int(getattr(self, 'episodenumber', 0))
epname = getattr(self, 'episodename', '') epname = getattr(self, 'episodename', '')
if None is not epname: if None is not epname:
return '<Episode %02dx%02d - %r>' % (seasno, epno, epname) return '%s<Episode %02dx%02d - %r>' % (show_name, seasno, epno, epname)
else: else:
return '<Episode %02dx%02d>' % (seasno, epno) return '%s<Episode %02dx%02d>' % (show_name, seasno, epno)
def __getattr__(self, key): def __getattr__(self, key):
if key in self: if key in self:
@ -554,6 +559,7 @@ class TVInfoEpisode(dict):
if cur_value.find(text_type(term).lower()) > -1: if cur_value.find(text_type(term).lower()) > -1:
return self return self
__unicode__ = __str__
__repr__ = __str__ __repr__ = __str__
__nonzero__ = __bool__ __nonzero__ = __bool__
@ -1146,8 +1152,22 @@ class TVInfoBase(object):
""" """
return [] return []
def discover(self, result_count=100, **kwargs): def discover(self, result_count=100, get_extra_images=False, **kwargs):
# type: (...) -> List[TVInfoEpisode]
return []
def get_premieres(self, result_count=100, **kwargs):
# type: (...) -> List[TVInfoEpisode]
"""
get all premiering shows
"""
return []
def get_returning(self, result_count=100, get_extra_images=False, **kwargs):
# type: (...) -> List[TVInfoShow] # type: (...) -> List[TVInfoShow]
"""
get all returning shows
"""
return [] return []
def __getitem__(self, item): def __getitem__(self, item):

215
lib/tvmaze_api/tvmaze_api.py

@ -6,18 +6,21 @@ __author__ = 'Prinz23'
__version__ = '1.0' __version__ = '1.0'
__api_version__ = '1.0.0' __api_version__ = '1.0.0'
import logging
import datetime import datetime
import logging
import re
import requests import requests
from requests.packages.urllib3.util.retry import Retry from urllib3.util.retry import Retry
from requests.adapters import HTTPAdapter from requests.adapters import HTTPAdapter
from six import iteritems from six import integer_types, iteritems
from sg_helpers import get_url, try_int from sg_helpers import get_url, try_int
from lib.dateutil.parser import parser from lib.dateutil.parser import parser
# noinspection PyProtectedMember
from lib.dateutil.tz.tz import _datetime_to_timestamp from lib.dateutil.tz.tz import _datetime_to_timestamp
from lib.exceptions_helper import ConnectionSkipException, ex from lib.exceptions_helper import ConnectionSkipException, ex
from .tvmaze_exceptions import * # from .tvmaze_exceptions import *
from lib.tvinfo_base import TVInfoBase, TVInfoImage, TVInfoImageSize, TVInfoImageType, Character, Crew, \ from lib.tvinfo_base import TVInfoBase, TVInfoImage, TVInfoImageSize, TVInfoImageType, Character, Crew, \
crew_type_names, Person, RoleTypes, TVInfoShow, TVInfoEpisode, TVInfoIDs, TVInfoSeason, PersonGenders, \ crew_type_names, Person, RoleTypes, TVInfoShow, TVInfoEpisode, TVInfoIDs, TVInfoSeason, PersonGenders, \
TVINFO_TVMAZE, TVINFO_TVDB, TVINFO_IMDB TVINFO_TVMAZE, TVINFO_TVDB, TVINFO_IMDB
@ -25,8 +28,8 @@ from lib.pytvmaze import tvmaze
# noinspection PyUnreachableCode # noinspection PyUnreachableCode
if False: if False:
from typing import Any, AnyStr, Dict, List, Optional, Union from typing import Any, AnyStr, Dict, List, Optional
from six import integer_types from lib.pytvmaze.tvmaze import Episode as TVMazeEpisode, Show as TVMazeShow
log = logging.getLogger('tvmaze.api') log = logging.getLogger('tvmaze.api')
log.addHandler(logging.NullHandler()) log.addHandler(logging.NullHandler())
@ -38,8 +41,10 @@ def tvmaze_endpoint_standard_get(url):
retries = Retry(total=5, retries = Retry(total=5,
backoff_factor=0.1, backoff_factor=0.1,
status_forcelist=[429]) status_forcelist=[429])
# noinspection HttpUrlsUsage
s.mount('http://', HTTPAdapter(max_retries=retries)) s.mount('http://', HTTPAdapter(max_retries=retries))
s.mount('https://', HTTPAdapter(max_retries=retries)) s.mount('https://', HTTPAdapter(max_retries=retries))
# noinspection PyProtectedMember
return get_url(url, json=True, session=s, hooks={'response': tvmaze._record_hook}, raise_skip_exception=True) return get_url(url, json=True, session=s, hooks={'response': tvmaze._record_hook}, raise_skip_exception=True)
@ -139,10 +144,10 @@ class TvMaze(TVInfoBase):
return {'seriesname': s.name, 'id': s.id, 'firstaired': s.premiered, return {'seriesname': s.name, 'id': s.id, 'firstaired': s.premiered,
'network': s.network and s.network.name, 'network': s.network and s.network.name,
'genres': s.genres, 'overview': s.summary, 'genres': s.genres, 'overview': s.summary,
'aliases': [a.name for a in s.akas], 'image': s.image and s.image.get('original'), 'aliases': [a.name for a in s.akas], 'image': s.image and s.image.get('original'),
'ids': TVInfoIDs(tvdb=s.externals.get('thetvdb'), rage=s.externals.get('tvrage'), tvmaze=s.id, 'ids': TVInfoIDs(
imdb=s.externals.get('imdb') and try_int(s.externals.get('imdb').replace('tt', ''), tvdb=s.externals.get('thetvdb'), rage=s.externals.get('tvrage'), tvmaze=s.id,
None))} imdb=s.externals.get('imdb') and try_int(s.externals.get('imdb').replace('tt', ''), None))}
results = [] results = []
if ids: if ids:
for t, p in iteritems(ids): for t, p in iteritems(ids):
@ -161,7 +166,7 @@ class TvMaze(TVInfoBase):
elif t == TVINFO_IMDB: elif t == TVINFO_IMDB:
if not self.config.get('cache_search') or (None is shows and not is_none): if not self.config.get('cache_search') or (None is shows and not is_none):
try: try:
show = tvmaze.lookup_imdb((p, 'tt%07d' % p)[not str(p).startswith('tt')]) show = tvmaze.lookup_imdb((p, 'tt%08d' % p)[not str(p).startswith('tt')])
self._set_cache_entry(cache_id_key, show, expire=self.search_cache_expire) self._set_cache_entry(cache_id_key, show, expire=self.search_cache_expire)
except (BaseException, Exception): except (BaseException, Exception):
continue continue
@ -201,11 +206,11 @@ class TvMaze(TVInfoBase):
return results return results
def _set_episode(self, sid, ep_obj): def _set_episode(self, sid, ep_obj):
for _k, _s in [('seasonnumber', 'season_number'), ('episodenumber', 'episode_number'), for _k, _s in (
('episodename', 'title'), ('overview', 'summary'), ('firstaired', 'airdate'), ('seasonnumber', 'season_number'), ('episodenumber', 'episode_number'),
('airtime', 'airtime'), ('runtime', 'runtime'), ('episodename', 'title'), ('overview', 'summary'), ('firstaired', 'airdate'),
('seriesid', 'maze_id'), ('id', 'maze_id'), ('is_special', 'special'), ('airtime', 'airtime'), ('runtime', 'runtime'),
('filename', 'image')]: ('seriesid', 'maze_id'), ('id', 'maze_id'), ('is_special', 'special'), ('filename', 'image')):
if 'filename' == _k: if 'filename' == _k:
image = getattr(ep_obj, _s, {}) or {} image = getattr(ep_obj, _s, {}) or {}
image = image.get('original') or image.get('medium') image = image.get('original') or image.get('medium')
@ -221,7 +226,8 @@ class TvMaze(TVInfoBase):
except (BaseException, Exception): except (BaseException, Exception):
pass pass
def _set_network(self, show_obj, network, is_stream): @staticmethod
def _set_network(show_obj, network, is_stream):
show_obj['network'] = network.name show_obj['network'] = network.name
show_obj['network_timezone'] = network.timezone show_obj['network_timezone'] = network.timezone
show_obj['network_country'] = network.country show_obj['network_country'] = network.country
@ -229,17 +235,21 @@ class TvMaze(TVInfoBase):
show_obj['network_id'] = network.maze_id show_obj['network_id'] = network.maze_id
show_obj['network_is_stream'] = is_stream show_obj['network_is_stream'] = is_stream
def _get_show_data(self, sid, language, get_ep_info=False, banners=False, posters=False, seasons=False, def _get_tvm_show(self, show_id, get_ep_info):
seasonwides=False, fanart=False, actors=False, **kwargs):
log.debug('Getting all series data for %s' % sid)
try: try:
self.show_not_found = False self.show_not_found = False
show_data = tvm_obj.get_show(maze_id=sid, embed='cast%s' % ('', ',episodeswithspecials')[get_ep_info]) return tvm_obj.get_show(maze_id=show_id, embed='cast%s' % ('', ',episodeswithspecials')[get_ep_info])
except tvmaze.ShowNotFound: except tvmaze.ShowNotFound:
self.show_not_found = True self.show_not_found = True
return False except (BaseException, Exception):
except (BaseException, Exception) as e: log.debug('Error getting data for tvmaze show id: %s' % show_id)
log.debug('Error getting data for tvmaze show id: %s' % sid)
def _get_show_data(self, sid, language, get_ep_info=False, banners=False, posters=False, seasons=False,
seasonwides=False, fanart=False, actors=False, **kwargs):
log.debug('Getting all series data for %s' % sid)
show_data = self._get_tvm_show(sid, get_ep_info)
if not show_data:
return False return False
show_obj = self.shows[sid].__dict__ show_obj = self.shows[sid].__dict__
@ -330,19 +340,20 @@ class TvMaze(TVInfoBase):
except (BaseException, Exception): except (BaseException, Exception):
print('error') print('error')
pass pass
existing_person.p_id, existing_person.name, existing_person.image, existing_person.gender, \ (existing_person.p_id, existing_person.name, existing_person.image, existing_person.gender,
existing_person.birthdate, existing_person.deathdate, existing_person.country, \ existing_person.birthdate, existing_person.deathdate, existing_person.country,
existing_person.country_code, existing_person.country_timezone, existing_person.thumb_url, \ existing_person.country_code, existing_person.country_timezone, existing_person.thumb_url,
existing_person.url, existing_person.ids = \ existing_person.url, existing_person.ids) = \
ch.person.id, ch.person.name, ch.person.image and ch.person.image.get('original'), \ (ch.person.id, ch.person.name,
PersonGenders.named.get(ch.person.gender and ch.person.gender.lower(), ch.person.image and ch.person.image.get('original'),
PersonGenders.unknown),\ PersonGenders.named.get(
person.birthdate, person.deathdate,\ ch.person.gender and ch.person.gender.lower(), PersonGenders.unknown),
ch.person.country and ch.person.country.get('name'),\ person.birthdate, person.deathdate,
ch.person.country and ch.person.country.get('code'),\ ch.person.country and ch.person.country.get('name'),
ch.person.country and ch.person.country.get('timezone'),\ ch.person.country and ch.person.country.get('code'),
ch.person.image and ch.person.image.get('medium'),\ ch.person.country and ch.person.country.get('timezone'),
ch.person.url, {TVINFO_TVMAZE: ch.person.id} ch.person.image and ch.person.image.get('medium'),
ch.person.url, {TVINFO_TVMAZE: ch.person.id})
else: else:
existing_character.person.append(person) existing_character.person.append(person)
else: else:
@ -396,7 +407,7 @@ class TvMaze(TVInfoBase):
show_obj['ids'] = TVInfoIDs(tvdb=show_data.externals.get('thetvdb'), show_obj['ids'] = TVInfoIDs(tvdb=show_data.externals.get('thetvdb'),
rage=show_data.externals.get('tvrage'), rage=show_data.externals.get('tvrage'),
imdb=show_data.externals.get('imdb') and imdb=show_data.externals.get('imdb') and
try_int(show_data.externals.get('imdb').replace('tt', ''), None)) try_int(show_data.externals.get('imdb').replace('tt', ''), None))
if show_data.network: if show_data.network:
self._set_network(show_obj, show_data.network, False) self._set_network(show_obj, show_data.network, False)
@ -406,15 +417,8 @@ class TvMaze(TVInfoBase):
if get_ep_info and not getattr(self.shows.get(sid), 'ep_loaded', False): if get_ep_info and not getattr(self.shows.get(sid), 'ep_loaded', False):
log.debug('Getting all episodes of %s' % sid) log.debug('Getting all episodes of %s' % sid)
if None is show_data: if None is show_data:
try: show_data = self._get_tvm_show(sid, get_ep_info)
self.show_not_found = False if not show_data:
show_data = tvm_obj.get_show(maze_id=sid, embed='cast%s' % ('', ',episodeswithspecials')[
get_ep_info])
except tvmaze.ShowNotFound:
self.show_not_found = True
return False
except (BaseException, Exception) as e:
log.debug('Error getting data for tvmaze show id: %s' % sid)
return False return False
if show_data.episodes: if show_data.episodes:
@ -543,3 +547,120 @@ class TvMaze(TVInfoBase):
p = None p = None
if p: if p:
return self._convert_person(p) return self._convert_person(p)
def get_premieres(self, result_count=100, get_extra_images=False, **kwargs):
# type: (...) -> List[TVInfoEpisode]
return self._filtered_schedule(lambda e: all([1 == e.season_number, 1 == e.episode_number]),
get_images=get_extra_images)
def get_returning(self, result_count=100, get_extra_images=False, **kwargs):
# type: (...) -> List[TVInfoEpisode]
return self._filtered_schedule(lambda e: all([1 != e.season_number, 1 == e.episode_number]),
get_images=get_extra_images)
def _make_episode(self, episode_data, show_data=None, get_images=False):
# type: (TVMazeEpisode, TVMazeShow, bool) -> TVInfoEpisode
"""
make out of TVMazeEpisode object and optionally TVMazeShow a TVInfoEpisode
"""
ti_show = TVInfoShow()
ti_show.seriesname = show_data.name
ti_show.id = show_data.maze_id
ti_show.seriesid = ti_show.id
ti_show.language = show_data.language
ti_show.overview = show_data.summary
ti_show.firstaired = show_data.premiered
ti_show.runtime = show_data.average_runtime or show_data.runtime
ti_show.vote_average = show_data.rating and show_data.rating.get('average')
ti_show.popularity = show_data.weight
ti_show.genre_list = show_data.genres or []
ti_show.genre = ', '.join(ti_show.genre_list)
ti_show.official_site = show_data.official_site
ti_show.status = show_data.status
ti_show.show_type = show_data.type
ti_show.lastupdated = show_data.updated
ti_show.poster = show_data.image and show_data.image.get('original')
ti_show.aliases = [a.name for a in show_data.akas]
if 'days' in show_data.schedule:
ti_show.airs_dayofweek = ', '.join(show_data.schedule['days'])
network = show_data.network or show_data.web_channel
if network:
ti_show.network_is_stream = None is not show_data.web_channel
ti_show.network = network.name
ti_show.network_id = network.maze_id
ti_show.network_country = network.country
ti_show.network_country_code = network.code
ti_show.network_timezone = network.timezone
if get_images and show_data.images:
b_set, f_set, p_set = False, False, False
for cur_img in show_data.images:
img_type = img_type_map.get(cur_img.type, TVInfoImageType.other)
img_width, img_height = cur_img.resolutions['original'].get('width'), \
cur_img.resolutions['original'].get('height')
img_ar = img_width and img_height and float(img_width) / float(img_height)
img_ar_type = self._which_type(img_width, img_ar)
if TVInfoImageType.poster == img_type and img_ar and img_ar_type != img_type and \
ti_show.poster == cur_img.resolutions.get('original')['url']:
p_set = False
ti_show.poster = None
ti_show.poster_thumb = None
img_type = (TVInfoImageType.other, img_type)[
not img_ar or img_ar_type == img_type or
img_type not in (TVInfoImageType.banner, TVInfoImageType.poster, TVInfoImageType.fanart)]
img_src = {}
for cur_res, cur_img_url in iteritems(cur_img.resolutions):
img_size = img_size_map.get(cur_res)
if img_size:
img_src[img_size] = cur_img_url.get('url')
ti_show.images.setdefault(img_type, []).append(
TVInfoImage(
image_type=img_type, sizes=img_src, img_id=cur_img.id, main_image=cur_img.main,
type_str=cur_img.type, width=img_width, height=img_height, aspect_ratio=img_ar))
if not p_set and TVInfoImageType.poster == img_type:
p_set = True
ti_show.poster = cur_img.resolutions.get('original')['url']
ti_show.poster_thumb = cur_img.resolutions.get('original')['url']
elif not b_set and 'banner' == cur_img.type and TVInfoImageType.banner == img_type:
b_set = True
ti_show.banner = cur_img.resolutions.get('original')['url']
ti_show.banner_thumb = cur_img.resolutions.get('medium')['url']
elif not f_set and 'background' == cur_img.type and TVInfoImageType.fanart == img_type:
f_set = True
ti_show.fanart = cur_img.resolutions.get('original')['url']
ti_show.ids = TVInfoIDs(
tvdb=show_data.externals.get('thetvdb'), rage=show_data.externals.get('tvrage'), tvmaze=show_data.id,
imdb=show_data.externals.get('imdb') and try_int(show_data.externals.get('imdb').replace('tt', ''), None))
ti_show.imdb_id = show_data.externals.get('imdb')
if isinstance(ti_show.imdb_id, integer_types):
ti_show.imdb_id = 'tt%08d' % ti_show.imdb_id
ti_episode = TVInfoEpisode()
ti_episode.id = episode_data.maze_id
ti_episode.seasonnumber = episode_data.season_number
ti_episode.episodenumber = episode_data.episode_number
ti_episode.episodename = episode_data.title
ti_episode.airtime = episode_data.airtime
ti_episode.firstaired = episode_data.airdate
if episode_data.airstamp:
try:
at = _datetime_to_timestamp(tz_p.parse(episode_data.airstamp))
ti_episode.timestamp = at
except (BaseException, Exception):
pass
ti_episode.filename = episode_data.image and (episode_data.image.get('original') or
episode_data.image.get('medium'))
ti_episode.is_special = episode_data.is_special()
ti_episode.overview = episode_data.summary
ti_episode.runtime = episode_data.runtime
ti_episode.show = ti_show
return ti_episode
def _filtered_schedule(self, condition, get_images=False):
try:
result = sorted([
e for e in tvmaze.get_full_schedule()
if condition(e) and (None is e.show.language or re.search('(?i)eng|jap', e.show.language))],
key=lambda x: x.show.premiered or x.airstamp)
return [self._make_episode(r, r.show, get_images) for r in result]
except(BaseException, Exception):
return []

9
sickbeard/__init__.py

@ -618,6 +618,7 @@ else:
MC_MRU = '' MC_MRU = ''
TVC_MRU = '' TVC_MRU = ''
TVM_MRU = ''
NE_MRU = '' NE_MRU = ''
COOKIE_SECRET = b64encodestring(uuid.uuid4().bytes + uuid.uuid4().bytes) COOKIE_SECRET = b64encodestring(uuid.uuid4().bytes + uuid.uuid4().bytes)
@ -763,7 +764,7 @@ def init_stage_1(console_logging):
global USE_TRAKT, TRAKT_CONNECTED_ACCOUNT, TRAKT_ACCOUNTS, TRAKT_MRU, TRAKT_VERIFY, \ global USE_TRAKT, TRAKT_CONNECTED_ACCOUNT, TRAKT_ACCOUNTS, TRAKT_MRU, TRAKT_VERIFY, \
TRAKT_USE_WATCHLIST, TRAKT_REMOVE_WATCHLIST, TRAKT_TIMEOUT, TRAKT_METHOD_ADD, TRAKT_START_PAUSED, \ TRAKT_USE_WATCHLIST, TRAKT_REMOVE_WATCHLIST, TRAKT_TIMEOUT, TRAKT_METHOD_ADD, TRAKT_START_PAUSED, \
TRAKT_SYNC, TRAKT_DEFAULT_INDEXER, TRAKT_REMOVE_SERIESLIST, TRAKT_UPDATE_COLLECTION, \ TRAKT_SYNC, TRAKT_DEFAULT_INDEXER, TRAKT_REMOVE_SERIESLIST, TRAKT_UPDATE_COLLECTION, \
MC_MRU, TVC_MRU, NE_MRU, \ MC_MRU, TVC_MRU, TVM_MRU, NE_MRU, \
USE_SLACK, SLACK_NOTIFY_ONSNATCH, SLACK_NOTIFY_ONDOWNLOAD, SLACK_NOTIFY_ONSUBTITLEDOWNLOAD, \ USE_SLACK, SLACK_NOTIFY_ONSNATCH, SLACK_NOTIFY_ONDOWNLOAD, SLACK_NOTIFY_ONSUBTITLEDOWNLOAD, \
SLACK_CHANNEL, SLACK_AS_AUTHED, SLACK_BOT_NAME, SLACK_ICON_URL, SLACK_ACCESS_TOKEN, \ SLACK_CHANNEL, SLACK_AS_AUTHED, SLACK_BOT_NAME, SLACK_ICON_URL, SLACK_ACCESS_TOKEN, \
USE_DISCORD, DISCORD_NOTIFY_ONSNATCH, DISCORD_NOTIFY_ONDOWNLOAD, \ USE_DISCORD, DISCORD_NOTIFY_ONSNATCH, DISCORD_NOTIFY_ONDOWNLOAD, \
@ -1207,6 +1208,7 @@ def init_stage_1(console_logging):
MC_MRU = check_setting_str(CFG, 'Metacritic', 'mc_mru', '') MC_MRU = check_setting_str(CFG, 'Metacritic', 'mc_mru', '')
TVC_MRU = check_setting_str(CFG, 'TVCalendar', 'tvc_mru', '') TVC_MRU = check_setting_str(CFG, 'TVCalendar', 'tvc_mru', '')
TVM_MRU = check_setting_str(CFG, 'TVmaze', 'tvm_mru', '')
NE_MRU = check_setting_str(CFG, 'NextEpisode', 'ne_mru', '') NE_MRU = check_setting_str(CFG, 'NextEpisode', 'ne_mru', '')
USE_PYTIVO = bool(check_setting_int(CFG, 'pyTivo', 'use_pytivo', 0)) USE_PYTIVO = bool(check_setting_int(CFG, 'pyTivo', 'use_pytivo', 0))
@ -1692,7 +1694,7 @@ def init_stage_2():
run_delay=datetime.timedelta(minutes=5), run_delay=datetime.timedelta(minutes=5),
threadName='PLEXWATCHEDSTATE') threadName='PLEXWATCHEDSTATE')
MEMCACHE['history_tab_limit'] = 10 MEMCACHE['history_tab_limit'] = 11
MEMCACHE['history_tab'] = History.menu_tab(MEMCACHE['history_tab_limit']) MEMCACHE['history_tab'] = History.menu_tab(MEMCACHE['history_tab_limit'])
try: try:
@ -2212,6 +2214,9 @@ def save_config():
('TVCalendar', [ ('TVCalendar', [
('mru', TVC_MRU) ('mru', TVC_MRU)
]), ]),
('TVmaze', [
('mru', TVM_MRU)
]),
('NextEpisode', [ ('NextEpisode', [
('mru', NE_MRU) ('mru', NE_MRU)
]), ]),

2
sickbeard/indexers/indexer_config.py

@ -176,7 +176,7 @@ tvinfo_config[src].update(dict(
src = TVINFO_IMDB src = TVINFO_IMDB
tvinfo_config[src].update(dict( tvinfo_config[src].update(dict(
base_url=tvinfo_config[src]['main_url'], base_url=tvinfo_config[src]['main_url'],
show_url='%stitle/tt%%07d' % tvinfo_config[src]['main_url'], show_url='%stitle/tt%%08d' % tvinfo_config[src]['main_url'],
finder='%sfind?q=%s&s=tt&ttype=tv&ref_=fn_tv' % (tvinfo_config[src]['main_url'], '%s'), finder='%sfind?q=%s&s=tt&ttype=tv&ref_=fn_tv' % (tvinfo_config[src]['main_url'], '%s'),
)) ))

12
sickbeard/tv.py

@ -2757,7 +2757,7 @@ class TVShow(TVShowBase):
old_imdb = self.imdbid old_imdb = self.imdbid
if show_info.ids.imdb: if show_info.ids.imdb:
self.imdbid = 'tt%07d' % show_info.ids.imdb self.imdbid = 'tt%08d' % show_info.ids.imdb
else: else:
self.imdbid = self.dict_prevent_nonetype(show_info, 'imdb_id') self.imdbid = self.dict_prevent_nonetype(show_info, 'imdb_id')
if old_imdb != self.imdbid: if old_imdb != self.imdbid:
@ -2977,7 +2977,7 @@ class TVShow(TVShowBase):
if not self._imdbid and 0 >= self.ids.get(indexermapper.TVINFO_IMDB, {'id': 0}).get('id', 0): if not self._imdbid and 0 >= self.ids.get(indexermapper.TVINFO_IMDB, {'id': 0}).get('id', 0):
return return
imdb_info = {'imdb_id': self._imdbid or 'tt%07d' % self.ids[indexermapper.TVINFO_IMDB]['id'], imdb_info = {'imdb_id': self._imdbid or 'tt%08d' % self.ids[indexermapper.TVINFO_IMDB]['id'],
'title': '', 'title': '',
'year': '', 'year': '',
'akas': '', 'akas': '',
@ -2995,7 +2995,7 @@ class TVShow(TVShowBase):
imdb_id = None imdb_id = None
imdb_certificates = None imdb_certificates = None
try: try:
imdb_id = str(self._imdbid or 'tt%07d' % self.ids[indexermapper.TVINFO_IMDB]['id']) imdb_id = str(self._imdbid or 'tt%08d' % self.ids[indexermapper.TVINFO_IMDB]['id'])
redirect_check = self.check_imdb_redirect(imdb_id) redirect_check = self.check_imdb_redirect(imdb_id)
if redirect_check: if redirect_check:
self._imdbid = redirect_check self._imdbid = redirect_check
@ -3016,15 +3016,15 @@ class TVShow(TVShowBase):
}) })
imdb_certificates = i.get_title_certificates(imdb_id=imdb_id) imdb_certificates = i.get_title_certificates(imdb_id=imdb_id)
except LookupError as e: except LookupError as e:
if 'Title was an episode' in ex(e) and imdb_id == 'tt%07d' % self.ids[indexermapper.TVINFO_IMDB]['id']: if 'Title was an episode' in ex(e) and imdb_id == 'tt%08d' % self.ids[indexermapper.TVINFO_IMDB]['id']:
self.ids[indexermapper.TVINFO_IMDB]['id'] = 0 self.ids[indexermapper.TVINFO_IMDB]['id'] = 0
self.ids[indexermapper.TVINFO_IMDB]['status'] = MapStatus.NOT_FOUND self.ids[indexermapper.TVINFO_IMDB]['status'] = MapStatus.NOT_FOUND
if datetime.date.today() != self.ids[indexermapper.TVINFO_IMDB]['date']: if datetime.date.today() != self.ids[indexermapper.TVINFO_IMDB]['date']:
indexermapper.map_indexers_to_show(self, force=True) indexermapper.map_indexers_to_show(self, force=True)
if not retry and imdb_id != 'tt%07d' % self.ids[indexermapper.TVINFO_IMDB]['id']: if not retry and imdb_id != 'tt%08d' % self.ids[indexermapper.TVINFO_IMDB]['id']:
# add retry arg to prevent endless loops # add retry arg to prevent endless loops
logger.log('imdbid: %s not found. retrying with newly found id: %s' % logger.log('imdbid: %s not found. retrying with newly found id: %s' %
(imdb_id, 'tt%07d' % self.ids[indexermapper.TVINFO_IMDB]['id']), logger.DEBUG) (imdb_id, 'tt%08d' % self.ids[indexermapper.TVINFO_IMDB]['id']), logger.DEBUG)
self._get_imdb_info(retry=True) self._get_imdb_info(retry=True)
return return
logger.log('imdbid: %s not found. Error: %s' % (imdb_id, ex(e)), logger.WARNING) logger.log('imdbid: %s not found. Error: %s' % (imdb_id, ex(e)), logger.WARNING)

159
sickbeard/webserve.py

@ -5168,6 +5168,159 @@ class AddShows(Home):
return self.new_show('|'.join(['', '', '', show_name]), use_show_name=True) return self.new_show('|'.join(['', '', '', show_name]), use_show_name=True)
def tvm_default(self):
return self.redirect('/add-shows/%s' % ('tvm_premieres', sickbeard.TVM_MRU)[any(sickbeard.TVM_MRU)])
def tvm_premieres(self, **kwargs):
return self.browse_tvm(
'New Shows at TVmaze', mode='premieres', **kwargs)
def tvm_returning(self, **kwargs):
return self.browse_tvm(
'Returning Shows at TVmaze', mode='returning', **kwargs)
def browse_tvm(self, browse_title, **kwargs):
browse_type = 'TVmaze'
footnote = None
filtered = []
def card_cache(mem_key):
# noinspection PyProtectedMember
from lib.dateutil.tz.tz import _datetime_to_timestamp
now = int(_datetime_to_timestamp(datetime.datetime.now()))
if (sickbeard.MEMCACHE.get(mem_key, {}).get('data')
and (now < sickbeard.MEMCACHE.get(mem_key, {}).get('expire', 0))):
return sickbeard.MEMCACHE.get(mem_key).get('data')
tvinfo_config = sickbeard.TVInfoAPI(TVINFO_TVMAZE).api_params.copy()
t = sickbeard.TVInfoAPI(TVINFO_TVMAZE).setup(**tvinfo_config)
if 'prem' in mem_key:
data = t.get_premieres()
else:
data = t.get_returning()
sickbeard.MEMCACHE[mem_key] = dict(expire=(30*60) + now, data=data)
return data
if 'New' in browse_title:
episodes = card_cache('tvmaze_premiere')
else:
episodes = card_cache('tvmaze_returning')
oldest, newest, oldest_dt, newest_dt, use_networks = None, None, 9999999, 0, False
dedupe = []
parseinfo = dateutil.parser.parserinfo(dayfirst=False, yearfirst=True)
base_url = sickbeard.TVInfoAPI(TVINFO_TVMAZE).config['show_url']
for cur_episode_info in episodes:
if cur_episode_info.show.id in dedupe:
continue
dedupe += [cur_episode_info.show.id]
try:
if cur_episode_info.airtime:
airtime = dateutil.parser.parse(cur_episode_info.airtime).time()
else:
airtime = cur_episode_info.timestamp \
and SGDatetime.from_timestamp(cur_episode_info.timestamp).time()
if (0, 0) == (airtime.hour, airtime.minute):
airtime = dateutil.parser.parse('23:59').time()
dt = datetime.datetime.combine(
dateutil.parser.parse(
(cur_episode_info.show.firstaired or cur_episode_info.firstaired), parseinfo).date(), airtime)
dt_ordinal = dt.toordinal()
now_ordinal = datetime.datetime.now().toordinal()
when_past = dt_ordinal < now_ordinal
dt_string = SGDatetime.sbfdate(dt)
if dt_ordinal < oldest_dt:
oldest_dt = dt_ordinal
oldest = dt_string
if dt_ordinal > newest_dt:
newest_dt = dt_ordinal
newest = dt_string
returning = returning_str = None
if 'Return' in browse_title:
returning = '9'
returning_str = 'TBC'
if cur_episode_info.firstaired:
returning = cur_episode_info.firstaired
dt_returning = datetime.datetime.combine(
dateutil.parser.parse(returning, parseinfo).date(), airtime)
when_past = dt_returning.toordinal() < now_ordinal
returning_str = SGDatetime.sbfdate(dt_returning)
try:
img_uri = cur_episode_info.show.poster
images = dict(poster=dict(thumb='imagecache?path=browse/thumb/tvmaze&source=%s' % img_uri))
sickbeard.CACHE_IMAGE_URL_LIST.add_url(img_uri)
except(BaseException, Exception):
images = {}
ids = dict(tvmaze=cur_episode_info.id)
imdb_id = cur_episode_info.show.imdb_id
if imdb_id:
ids['imdb'] = imdb_id
tvdb_id = cur_episode_info.show.ids.get(TVINFO_TVDB)
if tvdb_id:
ids['tvdb'] = tvdb_id
network_name = cur_episode_info.show.network
cc = 'US'
if network_name:
use_networks = True
cc = cur_episode_info.show.network_country_code or cc
language = ((cur_episode_info.show.language and 'jap' in cur_episode_info.show.language.lower())
and 'jp' or 'en')
filtered.append(dict(
ids=ids,
premiered=dt_ordinal,
premiered_str=dt_string,
returning=returning,
returning_str=returning_str,
when_past=when_past,
episode_number=cur_episode_info.episodenumber,
episode_season=cur_episode_info.seasonnumber,
episode_overview='' if not cur_episode_info.overview else cur_episode_info.overview.strip(),
genres=(', '.join(['%s' % v for v in cur_episode_info.show.genre_list])
or cur_episode_info.show.show_type or ''),
images=images,
overview=('No overview yet' if not cur_episode_info.show.overview
else helpers.xhtml_escape(cur_episode_info.show.overview.strip()[:250:])
.strip('*').strip()),
title=cur_episode_info.show.seriesname,
language=language,
language_img=sickbeard.MEMCACHE_FLAG_IMAGES.get(language, False),
country=cc,
country_img=sickbeard.MEMCACHE_FLAG_IMAGES.get(cc.lower(), False),
network=network_name,
rating=cur_episode_info.show.rating or cur_episode_info.show.popularity or 0,
url_src_db=base_url % cur_episode_info.show.id,
))
except (BaseException, Exception):
pass
kwargs.update(dict(oldest=oldest, newest=newest))
kwargs.update(dict(footnote=footnote, use_votes=False, use_networks=use_networks))
mode = kwargs.get('mode', '')
if mode:
func = 'tvm_%s' % mode
if callable(getattr(self, func, None)):
sickbeard.TVM_MRU = func
sickbeard.save_config()
return self.browse_shows(browse_type, browse_title, filtered, **kwargs)
# noinspection PyUnusedLocal
def info_tvmaze(self, ids, show_name):
if not filter_list(lambda tvid_prodid: helpers.find_show_by_id(tvid_prodid), ids.split(' ')):
return self.new_show('|'.join(['', '', '', ' '.join([ids, show_name])]), use_show_name=True)
def tvc_default(self): def tvc_default(self):
return self.redirect('/add-shows/%s' % ('tvc_newshows', sickbeard.TVC_MRU)[any(sickbeard.TVC_MRU)]) return self.redirect('/add-shows/%s' % ('tvc_newshows', sickbeard.TVC_MRU)[any(sickbeard.TVC_MRU)])
@ -5525,7 +5678,7 @@ class AddShows(Home):
@staticmethod @staticmethod
def browse_mru(browse_type, **kwargs): def browse_mru(browse_type, **kwargs):
save_config = False save_config = False
if browse_type in ('AniDB', 'IMDb', 'Metacritic', 'Trakt', 'TVCalendar', 'Nextepisode'): if browse_type in ('AniDB', 'IMDb', 'Metacritic', 'Trakt', 'TVCalendar', 'TVmaze', 'Nextepisode'):
save_config = True save_config = True
sickbeard.BROWSELIST_MRU[browse_type] = dict( sickbeard.BROWSELIST_MRU[browse_type] = dict(
showfilter=kwargs.get('showfilter', ''), showsort=kwargs.get('showsort', '')) showfilter=kwargs.get('showfilter', ''), showsort=kwargs.get('showsort', ''))
@ -5547,7 +5700,8 @@ class AddShows(Home):
showsort = t.saved_showsort.split(',') showsort = t.saved_showsort.split(',')
t.saved_showsort_sortby = 3 == len(showsort) and showsort[2] or 'by_order' t.saved_showsort_sortby = 3 == len(showsort) and showsort[2] or 'by_order'
t.reset_showsort_sortby = ('votes' in t.saved_showsort_sortby and not kwargs.get('use_votes', True) t.reset_showsort_sortby = ('votes' in t.saved_showsort_sortby and not kwargs.get('use_votes', True)
or 'rating' in t.saved_showsort_sortby and not kwargs.get('use_ratings', True)) or 'rating' in t.saved_showsort_sortby and not kwargs.get('use_ratings', True)
or 'returning' in t.saved_showsort_sortby and 'Returning' not in browse_title)
t.is_showsort_desc = ('desc' == (2 <= len(showsort) and showsort[1] or 'asc')) and not t.reset_showsort_sortby t.is_showsort_desc = ('desc' == (2 <= len(showsort) and showsort[1] or 'asc')) and not t.reset_showsort_sortby
t.saved_showsort_view = 1 <= len(showsort) and showsort[0] or '*' t.saved_showsort_view = 1 <= len(showsort) and showsort[0] or '*'
t.all_shows = [] t.all_shows = []
@ -5635,6 +5789,7 @@ class AddShows(Home):
('order', lambda _x: _x['order']), ('order', lambda _x: _x['order']),
('name', lambda _x: _title(_x['title'])), ('name', lambda _x: _title(_x['title'])),
('premiered', lambda _x: (_x['premiered'], _title(_x['title']))), ('premiered', lambda _x: (_x['premiered'], _title(_x['title']))),
('returning', lambda _x: (_x['returning'], _title(_x['title']))),
('votes', lambda _x: (helpers.try_int(_x['votes']), _title(_x['title']))), ('votes', lambda _x: (helpers.try_int(_x['votes']), _title(_x['title']))),
('rating', lambda _x: (helpers.try_float(_x['rating']), _title(_x['title']))), ('rating', lambda _x: (helpers.try_float(_x['rating']), _title(_x['title']))),
('rating_votes', lambda _x: (helpers.try_float(_x['rating']), helpers.try_int(_x['votes']), ('rating_votes', lambda _x: (helpers.try_float(_x['rating']), helpers.try_int(_x['votes']),

Loading…
Cancel
Save