Merge pull request #681 from JackDandy/feature/AddIMDbLists

Add IMDb Watchlists to 'View' drop down on the 'Add from IMDb' page
This commit is contained in:
JackDandy 2016-05-01 20:28:48 +01:00
commit 5e449ac7bc
17 changed files with 638 additions and 143 deletions

View file

@ -14,6 +14,7 @@
* Update dateutil library 2.4.2 (083f666) to 2.4.2 (d4baf97) * Update dateutil library 2.4.2 (083f666) to 2.4.2 (d4baf97)
* Update Hachoir library 1.3.4 (r1383) to 1.3.4 (r1435) * Update Hachoir library 1.3.4 (r1383) to 1.3.4 (r1435)
* Update html5lib 0.999 to 0.99999999/1.0b9 (46dae3d) * Update html5lib 0.999 to 0.99999999/1.0b9 (46dae3d)
* Update IMDb 5.0 to 5.1dev20160106
* Update PNotify library 2.0.1 to 2.1.0 * Update PNotify library 2.0.1 to 2.1.0
* Update profilehooks 1.4 to 1.8.2.dev0 (ee3f1a8) * Update profilehooks 1.4 to 1.8.2.dev0 (ee3f1a8)
* Update Requests library 2.7.0 (5d6d1bc) to 2.9.1 (a1c9b84) * Update Requests library 2.7.0 (5d6d1bc) to 2.9.1 (a1c9b84)
@ -54,6 +55,12 @@
* Fix syntax error causing renamer to error out * Fix syntax error causing renamer to error out
* Change storing metadata nfo vars from int to strings to resolve lxml type exceptions that don't occur with etree * Change storing metadata nfo vars from int to strings to resolve lxml type exceptions that don't occur with etree
* Add visual indicator for upcoming or started shows on Add Browse Shows * Add visual indicator for upcoming or started shows on Add Browse Shows
* Add IMDb Watchlists to 'View' drop down on the 'Add from IMDb' page
* Add 5 decades of 'IMDb Popular' selections to 'View' drop down on 'Add from... Browse Shows'
* Add 'Other Services' to 'View' drop down on 'Add from... Browse Shows'
* Add enable, disable and delete public IMDb watchlists to Config/General/Interface with a default 'SickGear' list
* Change prevent duplicate show ids from presenting items on 'Add from... Browse Shows'
* Change add 'nocache' kwarg to helpers.getURL to facilitate non-cached requests
### 0.11.11 (2016-04-05 19:20:00 UTC) ### 0.11.11 (2016-04-05 19:20:00 UTC)

View file

@ -153,9 +153,9 @@
<label> <label>
<span class="component-title">Show root directories</span> <span class="component-title">Show root directories</span>
<span class="component-desc"> <span class="component-desc">
<p>where the files of shows are located</p> <p>where the files of shows are located</p>
#include $os.path.join($sickbeard.PROG_DIR, 'gui/slick/interfaces/default/inc_rootDirs.tmpl') #include $os.path.join($sickbeard.PROG_DIR, 'gui/slick/interfaces/default/inc_rootDirs.tmpl')
</span> </span>
</label> </label>
</div> </div>
@ -266,8 +266,8 @@
</select> </select>
#set $hidden = ' class="hidden"' #set $hidden = ' class="hidden"'
<span id="showlist_tagview_standard"#echo ('', $hidden)['standard' not in $sickbeard.SHOWLIST_TAGVIEW]#>that contains all shows (default)</span> <span id="showlist_tagview_standard"#echo ('', $hidden)['standard' not in $sickbeard.SHOWLIST_TAGVIEW]#>that contains all shows (default)</span>
<span id="showlist_tagview_anime"#echo ('', $hidden)['anime' not in $sickbeard.SHOWLIST_TAGVIEW]#>two groups, the show list and anime</span> <span id="showlist_tagview_anime"#echo ('', $hidden)['anime' not in $sickbeard.SHOWLIST_TAGVIEW]#>two groups, the show list and anime</span>
<span id="showlist_tagview_custom"#echo ('', $hidden)['custom' not in $sickbeard.SHOWLIST_TAGVIEW]#>multiple custom<sup>1</sup> named groups and a "Show List"</span> <span id="showlist_tagview_custom"#echo ('', $hidden)['custom' not in $sickbeard.SHOWLIST_TAGVIEW]#>multiple custom<sup>1</sup> named groups and a "Show List"</span>
</span> </span>
</label> </label>
</div> </div>
@ -278,7 +278,7 @@
<span class="component-desc"> <span class="component-desc">
<input type="text" name="show_tags" id="show_tags" value="$show_tags" class="form-control input-sm input300"> <input type="text" name="show_tags" id="show_tags" value="$show_tags" class="form-control input-sm input300">
<span>comma separated names</span> <span>comma separated names</span>
<p class="clear-left note">group shows to the order of this custom list (add shows to groups with <a href="/manage/">mass edit</a>)</p> <p class="clear-left note">group shows to the order of this custom list (add shows to groups with <a href="/manage/">mass edit</a>)</p>
</span> </span>
</label> </label>
</div> </div>
@ -303,6 +303,37 @@
</label> </label>
</div> </div>
<div class="field-pair">
<label for="imdb-accounts">
<span class="component-title">Public IMDb watchlists:</span>
#if not hasattr($sickbeard, 'IMDB_ACCOUNTS')#<span class="red-text">Restart SickGear to reveal new options here</span>#else#
<span class="component-desc">
<select id="imdb-accounts" class="pull-left form-control input-sm">
<option value="new" selected="selected">Add watchlist ...</option>
#for $i, $v in $enumerate($sickbeard.IMDB_ACCOUNTS)
#if not $i % 2
#set $id = $v
#else
<option value="$id">#echo '%s%s' % ($v, ('\'s', '')['your' == $v.replace('(Off) ', '').lower()])# list</option>
#end if
#end for
</select>
<input type="button" class="btn btn-inline" value="Delete" id="imdb-list-del" disabled="disabled" />
</span>
</label>
<label for="imdb-url">
<span class="component-title">&nbsp</span>
<span class="component-desc">
<input type="text" id="imdb-url" value="" class="form-control input-sm input250" />
<input type="button" class="btn btn-inline" value="Enable" id="imdb-list-onoff" />
<div class="clear-left">
<p><code>imdb.com/user/ur64552276</code> or <code>ur64552276</code> to <span id="view-list">view list</span><a class="hide" id="link-list" href="$sbRoot/home/addShows/watchlist_imdb?account=64552276">view list</a> at "Add from IMDb"
</p></div>
#end if
</span>
</label>
</div>
<div class="field-pair"> <div class="field-pair">
<label for="sort_article"> <label for="sort_article">
<span class="component-title">Sort with "The", "A", "An"</span> <span class="component-title">Sort with "The", "A", "An"</span>

View file

@ -6,7 +6,7 @@
#from sickbeard import sbdatetime #from sickbeard import sbdatetime
#from sickbeard.helpers import anon_url #from sickbeard.helpers import anon_url
## ##
#set global $title='Browse Shows' #set global $title='Browse %s Shows' % $browse_type
#set global $header='Browse Shows' #set global $header='Browse Shows'
#set global $sbPath='..' #set global $sbPath='..'
#set global $topmenu='home' #set global $topmenu='home'
@ -97,7 +97,7 @@
\$('#showfilter').on( 'change', function() { \$('#showfilter').on( 'change', function() {
var filterValue = this.value; var filterValue = this.value;
if (-1 == filterValue.indexOf('trakt')) { if (-1 == filterValue.indexOf('trakt') && -1 == filterValue.indexOf('imdb') && -1 == filterValue.indexOf('default')) {
\$('#container').isotope({ filter: filterValue }); \$('#container').isotope({ filter: filterValue });
} else { } else {
location = '$sbRoot/home/addShows/' + filterValue; location = '$sbRoot/home/addShows/' + filterValue;
@ -118,18 +118,33 @@
#end if #end if
<h1 style="margin-bottom:0" class="grey-text #echo '%s">%s' % $heading#</h1> <h1 style="margin-bottom:0" class="grey-text #echo '%s">%s' % $heading#</h1>
#if $all_shows or ($kwargs and $kwargs.get('show_header', None)) #set $mode = $kwargs and $kwargs.get('mode', '')
#if $all_shows or ($kwargs and $kwargs.get('show_header'))
<div class="pull-right" style="margin-top:-35px"> <div class="pull-right" style="margin-top:-35px">
<span>Show:</span> <span>View:</span>
<select id="showfilter" class="form-control form-control-inline input-sm"> <select id="showfilter" class="form-control form-control-inline input-sm">
#set $count_all_shows = len($all_shows) #set $count_all_shows = len($all_shows)
#set $count_inlibrary = $all_shows_inlibrary #set $count_inlibrary = $all_shows_inlibrary
<option value="*" selected="selected">All<%= ' (%d)' % count_all_shows %></option> <option value="*" selected="selected">All<%= ' (%d)' % count_all_shows %></option>
<option value=".notinlibrary">Not In Library<%= ' (%d)' % (count_all_shows - count_inlibrary) %></option> <option value=".notinlibrary">Not In Library<%= ' (%d)' % (count_all_shows - count_inlibrary) %></option>
<option value=".inlibrary">In Library<%= ' (%d)' % count_inlibrary %></option> <option value=".inlibrary">In Library<%= ' (%d)' % count_inlibrary %></option>
#if $browse_type in ('IMDb', 'Trakt', 'AniDB')
<optgroup label="Other Services">
#if 'IMDb' == $browse_type
<option value="trakt_default">Trakt</option>
#elif 'Trakt' == $browse_type
<option value="imdb_default">IMDb</option>
#elif 'AniDB' == $browse_type
<option value="imdb_default">IMDb</option>
<option value="trakt_default">Trakt</option>
#end if
#if $sickbeard.USE_ANIDB and $browse_type in ('IMDb', 'Trakt')
<option value="anime_default">AniDB</option>
#end if
</optgroup>
#end if
#set $selected = ' class="selected"'
#if 'Trakt' == $browse_type #if 'Trakt' == $browse_type
#set $mode = $kwargs and $kwargs.get('mode', None)
#set $selected = ' class="selected"'
<optgroup label="Trakt"> <optgroup label="Trakt">
<option value="trakt_anticipated"#echo ('', selected)['anticipated' == $mode]#>Anticipating</option> <option value="trakt_anticipated"#echo ('', selected)['anticipated' == $mode]#>Anticipating</option>
<option value="trakt_newseasons"#echo ('', selected)['newseasons' == $mode]#>New Seasons</option> <option value="trakt_newseasons"#echo ('', selected)['newseasons' == $mode]#>New Seasons</option>
@ -151,7 +166,7 @@
<optgroup label="Trakt recommended"> <optgroup label="Trakt recommended">
#for $account in $sickbeard.TRAKT_ACCOUNTS #for $account in $sickbeard.TRAKT_ACCOUNTS
#if $sickbeard.TRAKT_ACCOUNTS[$account].active and $sickbeard.TRAKT_ACCOUNTS[$account].name #if $sickbeard.TRAKT_ACCOUNTS[$account].active and $sickbeard.TRAKT_ACCOUNTS[$account].name
<option value="trakt_recommended?account=$account"#echo ('', selected)['recommended-%s' % $account == $mode]#>for $sickbeard.TRAKT_ACCOUNTS[$account].name</option> <option value="trakt_recommended?account=$account"#echo ('', selected)[('recommended-%s' % $account) == $mode]#>for $sickbeard.TRAKT_ACCOUNTS[$account].name</option>
#end if #end if
#end for #end for
#else #else
@ -159,6 +174,30 @@
<option value="trakt_recommended?action=add">Enable Trakt here</option> <option value="trakt_recommended?action=add">Enable Trakt here</option>
#end if #end if
</optgroup> </optgroup>
#elif 'IMDb' == $browse_type
#set $periods = $kwargs and $kwargs.get('periods')
#if $periods
<optgroup label="IMDb Popular">
#for $i, $p in enumerate($periods)
#set $period='%s,%s' % ($p[0], $p[1])
<option value="popular_imdb?period=$period"#echo ('', selected)[('popular-%s' % $period) == $mode]#>#echo '%s - %s' % (($p[1], 'Current')[not $i], $p[0])#</option>
#end for
</optgroup>
#end if
#if not hasattr($sickbeard, 'IMDB_ACCOUNTS')#<optgroup label="Restart SickGear to reveal"><option>new options after restart</option></optgroup>#else#
<optgroup label="IMDb Watchlists">
#if any($sickbeard.IMDB_ACCOUNTS)
#for $i, $v in $enumerate($sickbeard.IMDB_ACCOUNTS)
#if not $i % 2
#set $id = $v
#elif not $v.startswith('(Off) ')
<option value="watchlist_imdb?account=$id"#echo ('', selected)[('watchlist-%s' % $id) == $mode]#>#echo '%s%s' % ($v, ('\'s', '')['your' == $v.replace('(Off) ', '').lower()])# list</option>
#end if
#end for
#end if
<option value="watchlist_imdb?action=add"><< Add lists >></option>
</optgroup>
#end if
#end if #end if
</select> </select>
@ -179,7 +218,7 @@
</select> </select>
</div> </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', None): #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:both;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>
@ -190,7 +229,7 @@
#if $all_shows #if $all_shows
#for $this_show in $all_shows: #for $this_show in $all_shows:
#set $title_html = $this_show['title'].replace('"', '&quot;').replace("'", '&#39;') #set $title_html = $this_show['title'].replace('"', '&quot;').replace("'", '&#39;')
#if 'newseasons' == $kwargs.get('mode', '') #if 'newseasons' == $mode
#set $overview = '%s: %s' % ( #set $overview = '%s: %s' % (
('Season %s' % $this_show['episode_season'], 'Brand-new')[1 == $this_show['episode_season']], ('Season %s' % $this_show['episode_season'], 'Brand-new')[1 == $this_show['episode_season']],
($this_show['overview'], $this_show['episode_overview'])[any($this_show['episode_overview']) and 1 != $this_show['episode_season']]) ($this_show['overview'], $this_show['episode_overview'])[any($this_show['episode_overview']) and 1 != $this_show['episode_season']])
@ -204,7 +243,7 @@
<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#
<p style='margin:0 0 2px'>#echo re.sub(r'([,\.!][^,\.!]*?)$', '...', re.sub(r'([!\?\.])(?=\w)', r'\1 ', $overview))#</p> <p style='margin:0 0 2px'>#echo re.sub(r'([,\.!][^,\.!]*?)$', '...', re.sub(r'([!\?\.])(?=\w)', r'\1 ', $overview))#</p>
<p><span style='font-weight:bold;font-size:0.9em;color:#888'><em>#if $kwargs and 'newseasons' == $kwargs.get('mode', None)#Air#else#First air#end if##echo ('s', 'ed')[$this_show['when_past']]#: $this_show['premiered_str']</em></span></p> <p><span style='font-weight:bold;font-size:0.9em;color:#888'><em>#if $kwargs and 'newseasons' == $mode#Air#else#First air#end if##echo ('s', 'ed')[$this_show['when_past']]#: $this_show['premiered_str']</em></span></p>
<span style='float:right'>Click for more at <span class='boldest'>$browse_type</span></span>"> <span style='float:right'>Click for more at <span class='boldest'>$browse_type</span></span>">
#if 'poster' in $this_show['images']: #if 'poster' in $this_show['images']:
#set $image = $this_show['images']['poster']['thumb'] #set $image = $this_show['images']['poster']['thumb']
@ -243,15 +282,15 @@
</div> </div>
#end for #end for
</div> </div>
#if $kwargs and $kwargs.get('footnote', None): #if $kwargs and $kwargs.get('footnote'):
<div> <div>
$kwargs['footnote'] $kwargs['footnote']
</div> </div>
#end if #end if
#else #else
<div class="show-card" style="width:100%; margin-top:20px"> <div class="show-card" style="width:100%; margin-top:20px; padding:20px">
<p class="red-text"> <p class="red-text">
#if $kwargs and $kwargs.get('error_msg', None): #if $kwargs and $kwargs.get('error_msg'):
$kwargs['error_msg'] $kwargs['error_msg']
#else #else
$browse_type API did not return results, this can happen from time to time. $browse_type API did not return results, this can happen from time to time.

View file

@ -30,6 +30,126 @@ $(document).ready(function () {
} }
}); });
var idSelect = '#imdb-accounts', idDel = '#imdb-list-del', idInput = '#imdb-url', idOnOff = '#imdb-list-onoff',
sel = 'selected', opt = 'option', selOpt = [opt, sel].join(':'),
elDropDown = $(idSelect), elDel = $(idDel), elInput = $(idInput), elOnOff = $(idOnOff);
function accId() {return elDropDown.find(selOpt).val();}
function nameList() {return elDropDown.find(selOpt).text();}
function isAdd() {return 'new' === accId();}
function isOff() {return 0 == nameList().indexOf('(Off) ');}
function warnMessage(msg) { elInput.addClass('warning').prop('title', msg); }
function all(state) {$([idSelect, idDel, idInput, idOnOff].join()).prop('disabled', 'on' == state ? !1 : !0)}
function setOnOff() {elOnOff.val(isAdd() || isOff() ? 'Enable' : 'Disable');}
function setLink() {
var idView = '#view-list', idLink = '#link-list';
return $([idView, idLink].join()).removeClass() &&
((isAdd() || isOff()) && $(idLink).addClass('hide') || $(idView).addClass('hide')) &&
(!isOff() && $(idLink)
.attr('href', sbRoot + '/home/addShows/watchlist_imdb?account=' + accId())
.attr('title', 'View ' + nameList()));
}
function defaultControls() {
elDel.prop('disabled', isAdd());
elInput.removeClass('warning')
.val(!isAdd() && accId() || '')
.prop('title', isAdd() ? '' : 'Select Add. Use Delete or Disable')
.prop('readonly', !isAdd());
setOnOff();
setLink();
}
function populateSelect(jsonData) {
/** @namespace response.accounts */
var response = $.parseJSON(jsonData);
if ('Success' !== response.result) {
warnMessage(response.result);
return !1;
}
elDropDown.find(opt).slice(1).remove();
var i, l, accounts = response.accounts, options = elDropDown.get(0).options;
for (i = 0, l = accounts.length; i < l; i = i + 2) {
options[options.length] = new Option(accounts[i + 1] +
(0 == accounts[i + 1].replace('(Off) ', '').toLowerCase().indexOf('your') ? '' : '\'s') + ' list', accounts[i]);
if (0 <= $.trim(elInput.val()).indexOf(accounts[i])) {
elDropDown.find(opt).prop(sel, !1);
elDropDown.find('option[value="' + accounts[i] + '"]').prop(sel, sel);
elInput.val(accounts[i]);
elInput.prop('title', 'Select Add. Use Delete or Disable');
setOnOff();
}
}
return !0;
}
elDropDown.change(function() {
defaultControls();
});
elDel.on('click', function(e) {
all('off');
$.confirm({
'title' : 'Remove the "' + nameList().replace('\'s', '').replace(' list', '') + '" IMDb Watchlist',
'message' : 'Are you sure you want to remove <span class="footerhighlight">' + nameList() + '</span> ?<br /><br />',
'buttons' : {
'Yes' : {
'class' : 'green',
'action': function() {
all('off');
$.get(sbRoot + '/home/addShows/watchlist_imdb', {
'action': elDel.val().toLowerCase(),
'select': accId()})
.done(function(response) {
all('on'); setControls(!populateSelect(response), !1); setOnOff(); })
.fail(function() {
all('on'); setControls(!0, 'Invalid ID'); setOnOff(); });
}
},
'No' : {
'class' : 'red',
'action': function() { e.preventDefault(); all('on'); defaultControls();}
}
}
});
});
elOnOff.on('click', function(e) {
var strList = $.trim(elInput.val());
elInput.removeClass('warning');
if (!strList) {
warnMessage('Missing IMDb list Id or URL');
} else {
all('off');
var params = {'action': elOnOff.val().toLowerCase()};
if ('enable' == params.action)
params.input = strList;
else
params.select = accId();
$.get(sbRoot + '/home/addShows/watchlist_imdb', params)
.done(function(data) { setControls(!populateSelect(data), !1); })
.fail(function() { setControls(!0, 'Failed to load list'); });
}
});
function setControls(resetSelect, message) {
all('on');
if (resetSelect) {
if (message)
warnMessage(message);
var addList = '[value="new"]';
elDropDown.find(opt).not(addList).prop(sel, !1);
elDropDown.find(opt + addList).prop(sel, sel);
}
elDel.prop('disabled', isAdd());
elInput.prop('readonly', !isAdd());
setLink()
}
var ui_update_trim_zero = (function () { var ui_update_trim_zero = (function () {
var secs = ('00' + new Date().getSeconds().toString()).slice(-2), var secs = ('00' + new Date().getSeconds().toString()).slice(-2),
elSecs = $('#trim_info_seconds'), elSecs = $('#trim_info_seconds'),
@ -193,15 +313,15 @@ function fetch_branches() {
} }
function add_option_to_pulls(text) { function add_option_to_pulls(text) {
option = $('<option>'); var option = $('<option>');
option.attr('value', text[1]); option.attr('value', text[1]);
option.html(text[0]); option.html(text[0]);
option.appendTo('#pullRequestVersion'); option.appendTo('#pullRequestVersion');
} }
function add_option_to_branches(text) { function add_option_to_branches(text) {
option = $('<option>'); var option = $('<option>');
option.attr('value', text); option.attr('value', text);
option.html(text); option.html(text);
option.appendTo('#branchVersion'); option.appendTo('#branchVersion');
} }

View file

@ -6,7 +6,7 @@ a person from the IMDb database.
It can fetch data through different media (e.g.: the IMDb web pages, It can fetch data through different media (e.g.: the IMDb web pages,
a SQL database, etc.) a SQL database, etc.)
Copyright 2004-2014 Davide Alberani <da@erlug.linux.it> Copyright 2004-2015 Davide Alberani <da@erlug.linux.it>
This program is free software; you can redistribute it and/or modify This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@ -25,7 +25,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
__all__ = ['IMDb', 'IMDbError', 'Movie', 'Person', 'Character', 'Company', __all__ = ['IMDb', 'IMDbError', 'Movie', 'Person', 'Character', 'Company',
'available_access_systems'] 'available_access_systems']
__version__ = VERSION = '5.0' __version__ = VERSION = '5.1dev20160106'
# Import compatibility module (importing it is enough). # Import compatibility module (importing it is enough).
import _compat import _compat

View file

@ -29,7 +29,7 @@
[imdbpy] [imdbpy]
## Default. ## Default.
accessSystem = httpThin #accessSystem = http
## Optional (options common to every data access system): ## Optional (options common to every data access system):
# Activate adult searches (on, by default). # Activate adult searches (on, by default).
@ -69,7 +69,7 @@ accessSystem = httpThin
## Set the threshold for logging messages. ## Set the threshold for logging messages.
# Can be one of "debug", "info", "warning", "error", "critical" (default: # Can be one of "debug", "info", "warning", "error", "critical" (default:
# "warning"). # "warning").
loggingLevel = debug #loggingLevel = debug
## Path to a configuration file for the logging facility; ## Path to a configuration file for the logging facility;
# see: http://docs.python.org/library/logging.html#configuring-logging # see: http://docs.python.org/library/logging.html#configuring-logging

View file

@ -1,12 +1,13 @@
# Gettext message file for imdbpy # Gettext message file for imdbpy
# Translators: # Translators:
# Ioan, 2013 # Nils Welzk, 2013
# Raphael, 2014
msgid "" msgid ""
msgstr "" msgstr ""
"Project-Id-Version: IMDbPY\n" "Project-Id-Version: IMDbPY\n"
"POT-Creation-Date: 2010-03-18 14:35+0000\n" "POT-Creation-Date: 2010-03-18 14:35+0000\n"
"PO-Revision-Date: 2013-11-20 11:07+0000\n" "PO-Revision-Date: 2014-10-21 15:24+0000\n"
"Last-Translator: Ioan\n" "Last-Translator: Raphael\n"
"Language-Team: German (http://www.transifex.com/projects/p/imdbpy/language/de/)\n" "Language-Team: German (http://www.transifex.com/projects/p/imdbpy/language/de/)\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
@ -20,11 +21,11 @@ msgstr ""
# Default: Actor # Default: Actor
msgid "actor" msgid "actor"
msgstr "" msgstr "Schauspieler"
# Default: Actress # Default: Actress
msgid "actress" msgid "actress"
msgstr "" msgstr "Schauspielerin"
# Default: Adaption # Default: Adaption
msgid "adaption" msgid "adaption"
@ -32,7 +33,7 @@ msgstr ""
# Default: Additional information # Default: Additional information
msgid "additional-information" msgid "additional-information"
msgstr "" msgstr "zusätzliche Information"
# Default: Admissions # Default: Admissions
msgid "admissions" msgid "admissions"
@ -48,7 +49,7 @@ msgstr ""
# Default: Akas # Default: Akas
msgid "akas" msgid "akas"
msgstr "" msgstr "Pseudonüme"
# Default: Akas from release info # Default: Akas from release info
msgid "akas-from-release-info" msgid "akas-from-release-info"
@ -56,7 +57,7 @@ msgstr ""
# Default: All products # Default: All products
msgid "all-products" msgid "all-products"
msgstr "" msgstr "Alle Produkte"
# Default: Alternate language version of # Default: Alternate language version of
msgid "alternate-language-version-of" msgid "alternate-language-version-of"
@ -68,7 +69,7 @@ msgstr ""
# Default: Amazon reviews # Default: Amazon reviews
msgid "amazon-reviews" msgid "amazon-reviews"
msgstr "" msgstr "Amazon Rezensionen"
# Default: Analog left # Default: Analog left
msgid "analog-left" msgid "analog-left"
@ -100,7 +101,7 @@ msgstr ""
# Default: Art director # Default: Art director
msgid "art-director" msgid "art-director"
msgstr "" msgstr "Art Director"
# Default: Article # Default: Article
msgid "article" msgid "article"
@ -112,7 +113,7 @@ msgstr ""
# Default: Aspect ratio # Default: Aspect ratio
msgid "aspect-ratio" msgid "aspect-ratio"
msgstr "" msgstr "Seitenverhältnis"
# Default: Assigner # Default: Assigner
msgid "assigner" msgid "assigner"
@ -132,7 +133,7 @@ msgstr ""
# Default: Audio quality # Default: Audio quality
msgid "audio-quality" msgid "audio-quality"
msgstr "" msgstr "Audio Qualität"
# Default: Award # Default: Award
msgid "award" msgid "award"
@ -188,7 +189,7 @@ msgstr "Kosten"
# Default: Business # Default: Business
msgid "business" msgid "business"
msgstr "" msgstr "Geschäft"
# Default: By arrangement with # Default: By arrangement with
msgid "by-arrangement-with" msgid "by-arrangement-with"
@ -220,7 +221,7 @@ msgstr ""
# Default: Cast # Default: Cast
msgid "cast" msgid "cast"
msgstr "" msgstr "Besetzung"
# Default: Casting department # Default: Casting department
msgid "casting-department" msgid "casting-department"
@ -236,23 +237,23 @@ msgstr ""
# Default: Category # Default: Category
msgid "category" msgid "category"
msgstr "" msgstr "Kategorie"
# Default: Certificate # Default: Certificate
msgid "certificate" msgid "certificate"
msgstr "" msgstr "Zertifikat"
# Default: Certificates # Default: Certificates
msgid "certificates" msgid "certificates"
msgstr "" msgstr "Zertifikate"
# Default: Certification # Default: Certification
msgid "certification" msgid "certification"
msgstr "" msgstr "Bescheinigung"
# Default: Channel # Default: Channel
msgid "channel" msgid "channel"
msgstr "" msgstr "Kanal"
# Default: Character # Default: Character
msgid "character" msgid "character"
@ -372,7 +373,7 @@ msgstr ""
# Default: Description # Default: Description
msgid "description" msgid "description"
msgstr "" msgstr "Beschreibung"
# Default: Dialogue intellegibility # Default: Dialogue intellegibility
msgid "dialogue-intellegibility" msgid "dialogue-intellegibility"
@ -396,7 +397,7 @@ msgstr ""
# Default: Distributors # Default: Distributors
msgid "distributors" msgid "distributors"
msgstr "" msgstr "Händler"
# Default: Dvd # Default: Dvd
msgid "dvd" msgid "dvd"
@ -452,7 +453,7 @@ msgstr "Episoden"
# Default: Episodes rating # Default: Episodes rating
msgid "episodes-rating" msgid "episodes-rating"
msgstr "" msgstr "Episoden Bewertung"
# Default: Essays # Default: Essays
msgid "essays" msgid "essays"
@ -464,7 +465,7 @@ msgstr ""
# Default: Faqs # Default: Faqs
msgid "faqs" msgid "faqs"
msgstr "" msgstr "FAQs"
# Default: Feature # Default: Feature
msgid "feature" msgid "feature"
@ -488,19 +489,19 @@ msgstr ""
# Default: Filmography # Default: Filmography
msgid "filmography" msgid "filmography"
msgstr "" msgstr "Filmografie"
# Default: Followed by # Default: Followed by
msgid "followed-by" msgid "followed-by"
msgstr "" msgstr "gefolgt von"
# Default: Follows # Default: Follows
msgid "follows" msgid "follows"
msgstr "" msgstr "folgt"
# Default: For # Default: For
msgid "for" msgid "for"
msgstr "" msgstr "für"
# Default: Frequency response # Default: Frequency response
msgid "frequency-response" msgid "frequency-response"
@ -508,7 +509,7 @@ msgstr ""
# Default: From # Default: From
msgid "from" msgid "from"
msgstr "" msgstr "von"
# Default: Full article link # Default: Full article link
msgid "full-article-link" msgid "full-article-link"
@ -524,7 +525,7 @@ msgstr ""
# Default: Genres # Default: Genres
msgid "genres" msgid "genres"
msgstr "" msgstr "Genres"
# Default: Goofs # Default: Goofs
msgid "goofs" msgid "goofs"
@ -540,7 +541,7 @@ msgstr ""
# Default: Headshot # Default: Headshot
msgid "headshot" msgid "headshot"
msgstr "" msgstr "Portrait"
# Default: Height # Default: Height
msgid "height" msgid "height"
@ -556,15 +557,15 @@ msgstr ""
# Default: Interview # Default: Interview
msgid "interview" msgid "interview"
msgstr "" msgstr "Interview"
# Default: Interviews # Default: Interviews
msgid "interviews" msgid "interviews"
msgstr "" msgstr "Interviews"
# Default: Introduction # Default: Introduction
msgid "introduction" msgid "introduction"
msgstr "" msgstr "Vorstellung"
# Default: Item # Default: Item
msgid "item" msgid "item"
@ -596,7 +597,7 @@ msgstr "Sprachen"
# Default: Laserdisc # Default: Laserdisc
msgid "laserdisc" msgid "laserdisc"
msgstr "" msgstr "Laserdisc"
# Default: Laserdisc title # Default: Laserdisc title
msgid "laserdisc-title" msgid "laserdisc-title"
@ -624,7 +625,7 @@ msgstr "Literatur"
# Default: Locations # Default: Locations
msgid "locations" msgid "locations"
msgstr "" msgstr "Standorte"
# Default: Long imdb canonical name # Default: Long imdb canonical name
msgid "long-imdb-canonical-name" msgid "long-imdb-canonical-name"
@ -708,11 +709,11 @@ msgstr ""
# Default: Nick names # Default: Nick names
msgid "nick-names" msgid "nick-names"
msgstr "" msgstr "Spitznamen"
# Default: Notes # Default: Notes
msgid "notes" msgid "notes"
msgstr "" msgstr "Anmerkungen"
# Default: Novel # Default: Novel
msgid "novel" msgid "novel"
@ -720,7 +721,7 @@ msgstr ""
# Default: Number # Default: Number
msgid "number" msgid "number"
msgstr "" msgstr "Zahl"
# Default: Number of chapter stops # Default: Number of chapter stops
msgid "number-of-chapter-stops" msgid "number-of-chapter-stops"
@ -800,7 +801,7 @@ msgstr ""
# Default: Plot # Default: Plot
msgid "plot" msgid "plot"
msgstr "Inhalt" msgstr "Handlung"
# Default: Plot outline # Default: Plot outline
msgid "plot-outline" msgid "plot-outline"
@ -824,7 +825,7 @@ msgstr ""
# Default: Producer # Default: Producer
msgid "producer" msgid "producer"
msgstr "" msgstr "Produzent"
# Default: Production companies # Default: Production companies
msgid "production-companies" msgid "production-companies"
@ -864,15 +865,15 @@ msgstr ""
# Default: Quote # Default: Quote
msgid "quote" msgid "quote"
msgstr "" msgstr "Zitat"
# Default: Quotes # Default: Quotes
msgid "quotes" msgid "quotes"
msgstr "" msgstr "Zitate"
# Default: Rating # Default: Rating
msgid "rating" msgid "rating"
msgstr "" msgstr "Bewertung"
# Default: Recommendations # Default: Recommendations
msgid "recommendations" msgid "recommendations"
@ -896,11 +897,11 @@ msgstr ""
# Default: Release date # Default: Release date
msgid "release-date" msgid "release-date"
msgstr "" msgstr "Veröffentlichungsdatum"
# Default: Release dates # Default: Release dates
msgid "release-dates" msgid "release-dates"
msgstr "" msgstr "Veröffentlichungstermine"
# Default: Remade as # Default: Remade as
msgid "remade-as" msgid "remade-as"
@ -908,27 +909,27 @@ msgstr ""
# Default: Remake of # Default: Remake of
msgid "remake-of" msgid "remake-of"
msgstr "" msgstr "Remake von"
# Default: Rentals # Default: Rentals
msgid "rentals" msgid "rentals"
msgstr "" msgstr "Leigebühr"
# Default: Result # Default: Result
msgid "result" msgid "result"
msgstr "" msgstr "Ergebnis"
# Default: Review # Default: Review
msgid "review" msgid "review"
msgstr "" msgstr "Kritik"
# Default: Review author # Default: Review author
msgid "review-author" msgid "review-author"
msgstr "" msgstr "Kritik Autor"
# Default: Review kind # Default: Review kind
msgid "review-kind" msgid "review-kind"
msgstr "" msgstr "Kritik Art"
# Default: Runtime # Default: Runtime
msgid "runtime" msgid "runtime"
@ -1096,7 +1097,7 @@ msgstr ""
# Default: Soundtrack # Default: Soundtrack
msgid "soundtrack" msgid "soundtrack"
msgstr "" msgstr "Soundtrack"
# Default: Spaciality # Default: Spaciality
msgid "spaciality" msgid "spaciality"
@ -1116,43 +1117,43 @@ msgstr ""
# Default: Spin off # Default: Spin off
msgid "spin-off" msgid "spin-off"
msgstr "" msgstr "Nebenprodukt"
# Default: Spin off from # Default: Spin off from
msgid "spin-off-from" msgid "spin-off-from"
msgstr "" msgstr "Nebenprodukt von"
# Default: Spoofed in # Default: Spoofed in
msgid "spoofed-in" msgid "spoofed-in"
msgstr "" msgstr "Parodiert in"
# Default: Spoofs # Default: Spoofs
msgid "spoofs" msgid "spoofs"
msgstr "" msgstr "Parodie"
# Default: Spouse # Default: Spouse
msgid "spouse" msgid "spouse"
msgstr "" msgstr "Gattin"
# Default: Status of availablility # Default: Status of availablility
msgid "status-of-availablility" msgid "status-of-availablility"
msgstr "" msgstr "Verfügbarkeitsstatus"
# Default: Studio # Default: Studio
msgid "studio" msgid "studio"
msgstr "" msgstr "Studio"
# Default: Studios # Default: Studios
msgid "studios" msgid "studios"
msgstr "" msgstr "Studios"
# Default: Stunt performer # Default: Stunt performer
msgid "stunt-performer" msgid "stunt-performer"
msgstr "" msgstr "Stunt-Darsteller"
# Default: Stunts # Default: Stunts
msgid "stunts" msgid "stunts"
msgstr "" msgstr "Stunts"
# Default: Subtitles # Default: Subtitles
msgid "subtitles" msgid "subtitles"
@ -1160,19 +1161,19 @@ msgstr "Untertitel"
# Default: Supplement # Default: Supplement
msgid "supplement" msgid "supplement"
msgstr "" msgstr "Ergänzung"
# Default: Supplements # Default: Supplements
msgid "supplements" msgid "supplements"
msgstr "" msgstr "Ergänzungen"
# Default: Synopsis # Default: Synopsis
msgid "synopsis" msgid "synopsis"
msgstr "" msgstr "Zusammenfassung"
# Default: Taglines # Default: Taglines
msgid "taglines" msgid "taglines"
msgstr "" msgstr "Slogan"
# Default: Tech info # Default: Tech info
msgid "tech-info" msgid "tech-info"
@ -1188,7 +1189,7 @@ msgstr "Zeit"
# Default: Title # Default: Title
msgid "title" msgid "title"
msgstr "" msgstr "Titel"
# Default: Titles in this product # Default: Titles in this product
msgid "titles-in-this-product" msgid "titles-in-this-product"
@ -1200,11 +1201,11 @@ msgstr ""
# Default: Top 250 rank # Default: Top 250 rank
msgid "top-250-rank" msgid "top-250-rank"
msgstr "" msgstr "Top 250 platzierung"
# Default: Trade mark # Default: Trade mark
msgid "trade-mark" msgid "trade-mark"
msgstr "" msgstr "Warenzeichen"
# Default: Transportation department # Default: Transportation department
msgid "transportation-department" msgid "transportation-department"
@ -1212,7 +1213,7 @@ msgstr ""
# Default: Trivia # Default: Trivia
msgid "trivia" msgid "trivia"
msgstr "" msgstr "Nichtigkeiten"
# Default: Tv # Default: Tv
msgid "tv" msgid "tv"
@ -1220,7 +1221,7 @@ msgstr "TV"
# Default: Under license from # Default: Under license from
msgid "under-license-from" msgid "under-license-from"
msgstr "" msgstr "lizensiert von"
# Default: Unknown link # Default: Unknown link
msgid "unknown-link" msgid "unknown-link"
@ -1256,19 +1257,19 @@ msgstr ""
# Default: Video quality # Default: Video quality
msgid "video-quality" msgid "video-quality"
msgstr "" msgstr "Video Qualität"
# Default: Video standard # Default: Video standard
msgid "video-standard" msgid "video-standard"
msgstr "" msgstr "Video Standart"
# Default: Visual effects # Default: Visual effects
msgid "visual-effects" msgid "visual-effects"
msgstr "" msgstr "Visuelle Effekte"
# Default: Votes # Default: Votes
msgid "votes" msgid "votes"
msgstr "" msgstr "Stimmen"
# Default: Votes distribution # Default: Votes distribution
msgid "votes-distribution" msgid "votes-distribution"
@ -1284,11 +1285,11 @@ msgstr ""
# Default: With # Default: With
msgid "with" msgid "with"
msgstr "" msgstr "mit"
# Default: Writer # Default: Writer
msgid "writer" msgid "writer"
msgstr "Schreiber" msgstr "Autor"
# Default: Written by # Default: Written by
msgid "written-by" msgid "written-by"

View file

@ -1,13 +1,14 @@
# Gettext message file for imdbpy # Gettext message file for imdbpy
# Translators: # Translators:
# RainDropR <rajaa@hilltx.com>, 2013 # lukophron, 2014
# Stéphane Aulery, 2012 # Rajaa Gutknecht <rajaa@hilltx.com>, 2013
# lkppo, 2012
msgid "" msgid ""
msgstr "" msgstr ""
"Project-Id-Version: IMDbPY\n" "Project-Id-Version: IMDbPY\n"
"POT-Creation-Date: 2010-03-18 14:35+0000\n" "POT-Creation-Date: 2010-03-18 14:35+0000\n"
"PO-Revision-Date: 2013-11-20 11:07+0000\n" "PO-Revision-Date: 2014-10-08 02:52+0000\n"
"Last-Translator: RainDropR <rajaa@hilltx.com>\n" "Last-Translator: lukophron\n"
"Language-Team: French (http://www.transifex.com/projects/p/imdbpy/language/fr/)\n" "Language-Team: French (http://www.transifex.com/projects/p/imdbpy/language/fr/)\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
@ -33,11 +34,11 @@ msgstr "adaptation"
# Default: Additional information # Default: Additional information
msgid "additional-information" msgid "additional-information"
msgstr "" msgstr "information-additionnelle"
# Default: Admissions # Default: Admissions
msgid "admissions" msgid "admissions"
msgstr "" msgstr "admissions"
# Default: Agent address # Default: Agent address
msgid "agent-address" msgid "agent-address"

View file

@ -726,6 +726,10 @@ class IMDbHTTPAccessSystem(IMDbBase):
cont = self._retrieve(self.urls['person_main'] % personID + 'bio') cont = self._retrieve(self.urls['person_main'] % personID + 'bio')
return self.pProxy.bio_parser.parse(cont, getRefs=self._getRefs) return self.pProxy.bio_parser.parse(cont, getRefs=self._getRefs)
def get_person_resume(self, personID):
cont = self._retrieve(self.urls['person_main'] % personID + 'resume')
return self.pProxy.resume_parser.parse(cont, getRefs=self._getRefs)
def get_person_awards(self, personID): def get_person_awards(self, personID):
cont = self._retrieve(self.urls['person_main'] % personID + 'awards') cont = self._retrieve(self.urls['person_main'] % personID + 'awards')
return self.pProxy.person_awards_parser.parse(cont) return self.pProxy.person_awards_parser.parse(cont)

View file

@ -226,7 +226,7 @@ class DOMHTMLMovieParser(DOMParserBase):
Attribute(key="countries", Attribute(key="countries",
path="./h5[starts-with(text(), " \ path="./h5[starts-with(text(), " \
"'Countr')]/../div[@class='info-content']//text()", "'Countr')]/../div[@class='info-content']//text()",
postprocess=makeSplitter('|')), postprocess=makeSplitter('|')),
Attribute(key="language", Attribute(key="language",
path="./h5[starts-with(text(), " \ path="./h5[starts-with(text(), " \
"'Language')]/..//text()", "'Language')]/..//text()",
@ -234,7 +234,7 @@ class DOMHTMLMovieParser(DOMParserBase):
Attribute(key='color info', Attribute(key='color info',
path="./h5[starts-with(text(), " \ path="./h5[starts-with(text(), " \
"'Color')]/..//text()", "'Color')]/..//text()",
postprocess=makeSplitter('Color:')), postprocess=makeSplitter('|')),
Attribute(key='sound mix', Attribute(key='sound mix',
path="./h5[starts-with(text(), " \ path="./h5[starts-with(text(), " \
"'Sound Mix')]/..//text()", "'Sound Mix')]/..//text()",
@ -462,6 +462,8 @@ class DOMHTMLMovieParser(DOMParserBase):
del data['other akas'] del data['other akas']
if nakas: if nakas:
data['akas'] = nakas data['akas'] = nakas
if 'color info' in data:
data['color info'] = [x.replace('Color:', '', 1) for x in data['color info']]
if 'runtimes' in data: if 'runtimes' in data:
data['runtimes'] = [x.replace(' min', u'') data['runtimes'] = [x.replace(' min', u'')
for x in data['runtimes']] for x in data['runtimes']]
@ -1177,7 +1179,7 @@ class DOMHTMLCriticReviewsParser(DOMParserBase):
path="//div[@class='article']/div[@class='see-more']/a", path="//div[@class='article']/div[@class='see-more']/a",
attrs=Attribute(key='metacritic url', attrs=Attribute(key='metacritic url',
path="./@href")) ] path="./@href")) ]
class DOMHTMLOfficialsitesParser(DOMParserBase): class DOMHTMLOfficialsitesParser(DOMParserBase):
"""Parser for the "official sites", "external reviews", "newsgroup """Parser for the "official sites", "external reviews", "newsgroup
reviews", "miscellaneous links", "sound clips", "video clips" and reviews", "miscellaneous links", "sound clips", "video clips" and
@ -1534,7 +1536,7 @@ class DOMHTMLSeasonEpisodesParser(DOMParserBase):
'').strip() '').strip()
episode_title = episode.get('title', '').strip() episode_title = episode.get('title', '').strip()
episode_plot = episode.get('plot', '') episode_plot = episode.get('plot', '')
if not (episode_nr and episode_id and episode_title): if not (episode_nr is not None and episode_id and episode_title):
continue continue
ep_obj = Movie(movieID=episode_id, title=episode_title, ep_obj = Movie(movieID=episode_id, title=episode_title,
accessSystem=self._as, modFunct=self._modFunct) accessSystem=self._as, modFunct=self._modFunct)

View file

@ -204,7 +204,7 @@ class DOMHTMLBioParser(DOMParserBase):
_birth_attrs = [Attribute(key='birth date', _birth_attrs = [Attribute(key='birth date',
path={ path={
'day': "./a[starts-with(@href, " \ 'day': "./a[starts-with(@href, " \
"'/date/')]/text()", "'/search/name?birth_monthday=')]/text()",
'year': "./a[starts-with(@href, " \ 'year': "./a[starts-with(@href, " \
"'/search/name?birth_year=')]/text()" "'/search/name?birth_year=')]/text()"
}, },
@ -215,7 +215,7 @@ class DOMHTMLBioParser(DOMParserBase):
_death_attrs = [Attribute(key='death date', _death_attrs = [Attribute(key='death date',
path={ path={
'day': "./a[starts-with(@href, " \ 'day': "./a[starts-with(@href, " \
"'/date/')]/text()", "'/search/name?death_monthday=')]/text()",
'year': "./a[starts-with(@href, " \ 'year': "./a[starts-with(@href, " \
"'/search/name?death_date=')]/text()" "'/search/name?death_date=')]/text()"
}, },
@ -326,6 +326,107 @@ class DOMHTMLBioParser(DOMParserBase):
return data return data
class DOMHTMLResumeParser(DOMParserBase):
"""Parser for the "resume" page of a given person.
The page should be provided as a string, as taken from
the akas.imdb.com server. The final result will be a
dictionary, with a key for every relevant section.
Example:
resumeparser = DOMHTMLResumeParser()
result = resumeparser.parse(resume_html_string)
"""
_defGetRefs = True
extractors = [
Extractor(label='info',
group="//div[@class='section_box']",
group_key="./h3/text()",
group_key_normalize=lambda x: x.lower().replace(' ', '_'),
path="./ul[@class='resume_section_multi_list']//li",
attrs=Attribute(key=None,
multi=True,
path={
'title': ".//b//text()",
'desc': ".//text()",
},
postprocess=lambda x: (x.get('title'), x.get('desc').strip().replace('\n', ' ')))),
Extractor(label='other_info',
group="//div[@class='section_box']",
group_key="./h3/text()",
group_key_normalize=lambda x: x.lower().replace(' ', '_'),
path="./ul[@class='_imdbpy']//li",
attrs=Attribute(key=None,
multi=True,
path=".//text()",
postprocess=lambda x: x.strip().replace('\n', ' '))),
Extractor(label='credits',
group="//div[@class='section_box']",
group_key="./h3/text()",
group_key_normalize=lambda x: x.lower().replace(' ', '_'),
path="./table[@class='credits']//tr",
attrs=Attribute(key=None,
multi=True,
path={
'0':".//td[1]//text()",
'1':".//td[2]//text()",
'2':".//td[3]//text()",
},
postprocess=lambda x: [x.get('0'),x.get('1'),x.get('2')])),
Extractor(label='mini_info',
path="//div[@class='center']",
attrs=Attribute(key='mini_info',
path=".//text()",
postprocess=lambda x: x.strip().replace('\n', ' '))),
Extractor(label='name',
path="//div[@class='center']/h1[@id='preview_user_name']",
attrs=Attribute(key='name',
path=".//text()",
postprocess=lambda x: x.strip().replace('\n', ' '))),
Extractor(label='resume_bio',
path="//div[@id='resume_rendered_html']//p",
attrs=Attribute(key='resume_bio',
multi=True,
path=".//text()")),
]
preprocessors = [
(re.compile('(<ul>)', re.I), r'<ul class="_imdbpy">\1'),
]
def postprocess_data(self, data):
for key in data.keys():
if data[key] == '':
del data[key]
if key in ('mini_info', 'name', 'resume_bio'):
if key == 'resume_bio':
data[key] = "".join(data[key]).strip()
continue
if len(data[key][0]) == 3:
for item in data[key]:
item[:] = [x for x in item if not x == None]
continue
if len(data[key][0]) == 2:
new_key = {}
for item in data[key]:
if item[0] == None:
continue
if ':' in item[0]:
if item[1].replace(item[0], '')[1:].strip() == '':
continue
new_key[item[0].strip().replace(':', '')] = item[1].replace(item[0], '')[1:].strip()
else:
new_key[item[0]] = item[1]
data[key] = new_key
new_data = {}
new_data['resume'] = data
return new_data
class DOMHTMLOtherWorksParser(DOMParserBase): class DOMHTMLOtherWorksParser(DOMParserBase):
"""Parser for the "other works" and "agent" pages of a given person. """Parser for the "other works" and "agent" pages of a given person.
The page should be provided as a string, as taken from The page should be provided as a string, as taken from
@ -502,6 +603,7 @@ from movieParser import DOMHTMLNewsParser
_OBJECTS = { _OBJECTS = {
'maindetails_parser': ((DOMHTMLMaindetailsParser,), None), 'maindetails_parser': ((DOMHTMLMaindetailsParser,), None),
'bio_parser': ((DOMHTMLBioParser,), None), 'bio_parser': ((DOMHTMLBioParser,), None),
'resume_parser': ((DOMHTMLResumeParser,), None),
'otherworks_parser': ((DOMHTMLOtherWorksParser,), None), 'otherworks_parser': ((DOMHTMLOtherWorksParser,), None),
#'agent_parser': ((DOMHTMLOtherWorksParser,), {'kind': 'agent'}), #'agent_parser': ((DOMHTMLOtherWorksParser,), {'kind': 'agent'}),
'person_officialsites_parser': ((DOMHTMLOfficialsitesParser,), None), 'person_officialsites_parser': ((DOMHTMLOfficialsitesParser,), None),

View file

@ -7,7 +7,7 @@ E.g.:
http://akas.imdb.com/chart/top http://akas.imdb.com/chart/top
http://akas.imdb.com/chart/bottom http://akas.imdb.com/chart/bottom
Copyright 2009 Davide Alberani <da@erlug.linux.it> Copyright 2009-2015 Davide Alberani <da@erlug.linux.it>
This program is free software; you can redistribute it and/or modify This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@ -43,14 +43,15 @@ class DOMHTMLTop250Parser(DOMParserBase):
def _init(self): def _init(self):
self.extractors = [Extractor(label=self.label, self.extractors = [Extractor(label=self.label,
path="//div[@id='main']//table//tr", path="//div[@id='main']//div[1]//div//table//tbody//tr",
attrs=Attribute(key=None, attrs=Attribute(key=None,
multi=True, multi=True,
path={self.ranktext: "./td[1]//text()", path={self.ranktext: "./td[2]//text()",
'rating': "./td[2]//text()", 'rating': "./td[3]//strong//text()",
'title': "./td[3]//text()", 'title': "./td[2]//a//text()",
'movieID': "./td[3]//a/@href", 'year': "./td[2]//span//text()",
'votes': "./td[4]//text()" 'movieID': "./td[2]//a/@href",
'votes': "./td[3]//strong/@title"
}))] }))]
def postprocess_data(self, data): def postprocess_data(self, data):
@ -72,12 +73,16 @@ class DOMHTMLTop250Parser(DOMParserBase):
if theID in seenIDs: if theID in seenIDs:
continue continue
seenIDs.append(theID) seenIDs.append(theID)
minfo = analyze_title(d['title']) minfo = analyze_title(d['title']+" "+d['year'])
try: minfo[self.ranktext] = int(d[self.ranktext].replace('.', '')) try: minfo[self.ranktext] = int(d[self.ranktext].replace('.', ''))
except: pass except: pass
if 'votes' in d: if 'votes' in d:
try: minfo['votes'] = int(d['votes'].replace(',', '')) try:
except: pass votes = d['votes'].replace(' votes','')
votes = votes.split(' based on ')[1]
minfo['votes'] = int(votes.replace(',', ''))
except:
pass
if 'rating' in d: if 'rating' in d:
try: minfo['rating'] = float(d['rating']) try: minfo['rating'] = float(d['rating'])
except: pass except: pass

View file

@ -441,12 +441,6 @@ class DOMParserBase(object):
self._useModule = useModule self._useModule = useModule
nrMods = len(useModule) nrMods = len(useModule)
_gotError = False _gotError = False
# Force warnings.warn() to omit the source code line in the message
formatwarning_orig = warnings.formatwarning
warnings.formatwarning = lambda message, category, filename, lineno, line=None: \
formatwarning_orig(message, category, filename, lineno, line='')
for idx, mod in enumerate(useModule): for idx, mod in enumerate(useModule):
mod = mod.strip().lower() mod = mod.strip().lower()
try: try:

View file

@ -639,11 +639,14 @@ def analyze_company_name(name, stripNotes=False):
o_name = name o_name = name
name = name.strip() name = name.strip()
country = None country = None
if name.endswith(']'): if name.startswith('['):
idx = name.rfind('[') name = re.sub('[!@#$\(\)\[\]]', '', name)
if idx != -1: else:
country = name[idx:] if name.endswith(']'):
name = name[:idx].rstrip() idx = name.rfind('[')
if idx != -1:
country = name[idx:]
name = name[:idx].rstrip()
if not name: if not name:
raise IMDbParserError('invalid name: "%s"' % o_name) raise IMDbParserError('invalid name: "%s"' % o_name)
result = {'name': name} result = {'name': name}
@ -957,7 +960,7 @@ def _tag4TON(ton, addAccessSystem=False, _containerOnly=False):
crl = [crl] crl = [crl]
for cr in crl: for cr in crl:
crTag = cr.__class__.__name__.lower() crTag = cr.__class__.__name__.lower()
crValue = cr['long imdb name'] crValue = cr.get('long imdb name') or u''
crValue = _normalizeValue(crValue) crValue = _normalizeValue(crValue)
crID = cr.getID() crID = cr.getID()
if crID is not None: if crID is not None:

View file

@ -170,6 +170,9 @@ INDEXER_TIMEOUT = None
SCENE_DEFAULT = False SCENE_DEFAULT = False
ANIME_DEFAULT = False ANIME_DEFAULT = False
USE_IMDB_INFO = True USE_IMDB_INFO = True
IMDB_ACCOUNTS = []
IMDB_DEFAULT_LIST_ID = '64552276'
IMDB_DEFAULT_LIST_NAME = 'SickGear'
PROVIDER_ORDER = [] PROVIDER_ORDER = []
NAMING_MULTI_EP = False NAMING_MULTI_EP = False
@ -535,7 +538,7 @@ def initialize(consoleLogging=True):
AUTOPOSTPROCESSER_FREQUENCY, DEFAULT_AUTOPOSTPROCESSER_FREQUENCY, MIN_AUTOPOSTPROCESSER_FREQUENCY, \ AUTOPOSTPROCESSER_FREQUENCY, DEFAULT_AUTOPOSTPROCESSER_FREQUENCY, MIN_AUTOPOSTPROCESSER_FREQUENCY, \
ANIME_DEFAULT, NAMING_ANIME, USE_ANIDB, ANIDB_USERNAME, ANIDB_PASSWORD, ANIDB_USE_MYLIST, \ ANIME_DEFAULT, NAMING_ANIME, USE_ANIDB, ANIDB_USERNAME, ANIDB_PASSWORD, ANIDB_USE_MYLIST, \
SCENE_DEFAULT, BACKLOG_DAYS, SEARCH_UNAIRED, ANIME_TREAT_AS_HDTV, \ SCENE_DEFAULT, BACKLOG_DAYS, SEARCH_UNAIRED, ANIME_TREAT_AS_HDTV, \
COOKIE_SECRET, USE_IMDB_INFO, DISPLAY_BACKGROUND, DISPLAY_BACKGROUND_TRANSPARENT, DISPLAY_ALL_SEASONS, \ COOKIE_SECRET, USE_IMDB_INFO, IMDB_ACCOUNTS, DISPLAY_BACKGROUND, DISPLAY_BACKGROUND_TRANSPARENT, DISPLAY_ALL_SEASONS, \
SHOW_TAGS, DEFAULT_SHOW_TAG, SHOWLIST_TAGVIEW SHOW_TAGS, DEFAULT_SHOW_TAG, SHOWLIST_TAGVIEW
if __INITIALIZED__: if __INITIALIZED__:
@ -602,6 +605,7 @@ def initialize(consoleLogging=True):
GUI_NAME = check_setting_str(CFG, 'GUI', 'gui_name', 'slick') GUI_NAME = check_setting_str(CFG, 'GUI', 'gui_name', 'slick')
DEFAULT_HOME = check_setting_str(CFG, 'GUI', 'default_home', 'home') DEFAULT_HOME = check_setting_str(CFG, 'GUI', 'default_home', 'home')
USE_IMDB_INFO = bool(check_setting_int(CFG, 'GUI', 'use_imdb_info', 1)) USE_IMDB_INFO = bool(check_setting_int(CFG, 'GUI', 'use_imdb_info', 1))
IMDB_ACCOUNTS = CFG.get('GUI', []).get('imdb_accounts', [IMDB_DEFAULT_LIST_ID, IMDB_DEFAULT_LIST_NAME])
HOME_SEARCH_FOCUS = bool(check_setting_int(CFG, 'General', 'home_search_focus', HOME_SEARCH_FOCUS)) HOME_SEARCH_FOCUS = bool(check_setting_int(CFG, 'General', 'home_search_focus', HOME_SEARCH_FOCUS))
SORT_ARTICLE = bool(check_setting_int(CFG, 'General', 'sort_article', 0)) SORT_ARTICLE = bool(check_setting_int(CFG, 'General', 'sort_article', 0))
FUZZY_DATING = bool(check_setting_int(CFG, 'GUI', 'fuzzy_dating', 0)) FUZZY_DATING = bool(check_setting_int(CFG, 'GUI', 'fuzzy_dating', 0))
@ -1798,6 +1802,7 @@ def save_config():
new_config['GUI']['theme_name'] = THEME_NAME new_config['GUI']['theme_name'] = THEME_NAME
new_config['GUI']['default_home'] = DEFAULT_HOME new_config['GUI']['default_home'] = DEFAULT_HOME
new_config['GUI']['use_imdb_info'] = int(USE_IMDB_INFO) new_config['GUI']['use_imdb_info'] = int(USE_IMDB_INFO)
new_config['GUI']['imdb_accounts'] = IMDB_ACCOUNTS
new_config['GUI']['fuzzy_dating'] = int(FUZZY_DATING) new_config['GUI']['fuzzy_dating'] = int(FUZZY_DATING)
new_config['GUI']['trim_zero'] = int(TRIM_ZERO) new_config['GUI']['trim_zero'] = int(TRIM_ZERO)
new_config['GUI']['date_preset'] = DATE_PRESET new_config['GUI']['date_preset'] = DATE_PRESET

View file

@ -1120,8 +1120,12 @@ def getURL(url, post_data=None, params=None, headers=None, timeout=30, session=N
# request session # request session
if None is session: if None is session:
session = requests.session() session = requests.session()
cache_dir = sickbeard.CACHE_DIR or _getTempDir()
session = CacheControl(sess=session, cache=caches.FileCache(os.path.join(cache_dir, 'sessions'))) if not kwargs.get('nocache'):
cache_dir = sickbeard.CACHE_DIR or _getTempDir()
session = CacheControl(sess=session, cache=caches.FileCache(os.path.join(cache_dir, 'sessions')))
else:
del(kwargs['nocache'])
# request session headers # request session headers
req_headers = {'User-Agent': USER_AGENT, 'Accept-Encoding': 'gzip,deflate'} req_headers = {'User-Agent': USER_AGENT, 'Accept-Encoding': 'gzip,deflate'}

View file

@ -2526,6 +2526,168 @@ class NewHomeAddShows(Home):
return return
return self.new_show('|'.join(['', '', '', indexer_id or showName]), use_show_name=True, is_anime=True) return self.new_show('|'.join(['', '', '', indexer_id or showName]), use_show_name=True, is_anime=True)
@staticmethod
def watchlist_config(**kwargs):
if not isinstance(sickbeard.IMDB_ACCOUNTS, type([])):
sickbeard.IMDB_ACCOUNTS = list(sickbeard.IMDB_ACCOUNTS)
accounts = dict(map(None, *[iter(sickbeard.IMDB_ACCOUNTS)] * 2))
if 'enable' == kwargs.get('action'):
account_id = re.findall('\d{6,32}', kwargs.get('input', ''))
if not account_id:
return json.dumps({'result': 'Fail: Invalid IMDb ID'})
acc_id = account_id[0]
url = 'http://www.imdb.com/user/ur%s/watchlist' % acc_id + \
'/_ajax?sort=date_added,desc&mode=detail&page=1&title_type=tvSeries%2CtvEpisode&ref_=wl_vm_dtl'
html = helpers.getURL(url, nocache=True)
try:
list_name = re.findall('(?i)<h1[^>]+>(.*)\s+Watchlist</h1>', html)[0].replace('\'s', '')
accounts[acc_id] = list_name or 'noname'
except:
return json.dumps({'result': 'Fail: No list found with id: %s' % acc_id})
else:
acc_id = kwargs.get('select', '')
if acc_id not in accounts:
return json.dumps({'result': 'Fail: Unknown IMDb ID'})
if 'disable' == kwargs.get('action'):
accounts[acc_id] = '(Off) %s' % accounts[acc_id].replace('(Off) ', '')
else:
del accounts[acc_id]
gears = [[k, v] for k, v in accounts.iteritems() if 'sickgear' in v.lower()]
if gears:
del accounts[gears[0][0]]
yours = [[k, v] for k, v in accounts.iteritems() if 'your' == v.replace('(Off) ', '').lower()]
if yours:
del accounts[yours[0][0]]
sickbeard.IMDB_ACCOUNTS = [x for tup in sorted(list(accounts.items()), key=lambda t: t[1]) for x in tup]
if gears:
sickbeard.IMDB_ACCOUNTS.insert(0, gears[0][1])
sickbeard.IMDB_ACCOUNTS.insert(0, gears[0][0])
if yours:
sickbeard.IMDB_ACCOUNTS.insert(0, yours[0][1])
sickbeard.IMDB_ACCOUNTS.insert(0, yours[0][0])
sickbeard.save_config()
return json.dumps({'result': 'Success', 'accounts': sickbeard.IMDB_ACCOUNTS})
def watchlist_imdb(self, *args, **kwargs):
if 'add' == kwargs.get('action'):
return self.redirect('/config/general/#core-component-group2')
if kwargs.get('action') in ('delete', 'enable', 'disable'):
return self.watchlist_config(**kwargs)
browse_type = 'IMDb'
filtered = []
footnote = None
start_year, end_year = (datetime.date.today().year - 10, datetime.date.today().year + 1)
periods = [(start_year, end_year)] + [(x-10, x) for x in range(start_year, start_year-40, -10)]
accounts = dict(map(None, *[iter(sickbeard.IMDB_ACCOUNTS)]*2))
acc_id, list_name = (sickbeard.IMDB_DEFAULT_LIST_ID, sickbeard.IMDB_DEFAULT_LIST_NAME) if \
0 == sickbeard.helpers.tryInt(kwargs.get('account')) or \
kwargs.get('account') not in accounts.keys() or \
accounts.get(kwargs.get('account'), '').startswith('(Off) ') else \
(kwargs.get('account'), accounts.get(kwargs.get('account')))
list_name += ('\'s', '')['your' == list_name.replace('(Off) ', '').lower()]
url = 'http://www.imdb.com/user/ur%s/watchlist' % acc_id
url_data = '/_ajax?sort=date_added,desc&mode=detail&page=1&title_type=tvSeries%2CtvEpisode&ref_=wl_vm_dtl'
url_ui = '?mode=detail&page=1&sort=date_added,desc&title_type=tvSeries%2CtvEpisode&ref_=wl_ref_typ'
html = helpers.getURL(url + url_data)
if html:
img_size = re.compile(r'(?im)(V1[^XY]+([XY]))(\d+)([^\d]+)(\d+)([^\d]+)(\d+)([^\d]+)(\d+)([^\d]+)(\d+)(.*?)$')
imdb_id = re.compile(r'(?i).*(tt\d+).*')
with BS4Parser(html, features=['html5lib', 'permissive']) as soup:
show_list = soup.find('div', {'class': 'lister-list'})
shows = [] if not show_list else show_list.find_all('div', {'class': 'mode-detail'})
oldest, newest, oldest_dt, newest_dt = None, None, 9999999, 0
for show in shows:
try:
url_path = show.select('h3.lister-item-header a[href*=title]')[0]['href'].strip('/')
ids = dict(imdb=imdb_id.sub(r'\1', url_path))
first_aired = show.select('h3.lister-item-header span.lister-item-year')
year = None if not len(first_aired) else re.sub(r'.*(\d{4}).*', r'\1', first_aired[0].get_text())
dt_ordinal = 0
if year:
dt = dateutil.parser.parse('01-01-%s' % year)
dt_ordinal = dt.toordinal()
if dt_ordinal < oldest_dt:
oldest_dt = dt_ordinal
oldest = year
if dt_ordinal > newest_dt:
newest_dt = dt_ordinal
newest = year
genres = show.select('span.genre')
images = {}
img = show.select('div.lister-item-image img')
overview = '' if not show.find('p', '') else show.find('p', '').get_text().strip()
rating = show.find('meta', attrs={'itemprop': 'ratingValue'})
rating = None is not rating and rating.get('content') or ''
voting = show.find('meta', attrs={'itemprop': 'ratingCount'})
voting = None is not voting and voting.get('content') or ''
if len(img):
img_uri = img[0].get('loadlate')
match = img_size.search(img_uri)
if match and 'tv_series.gif' not in img_uri and 'nopicture' not in img_uri:
scale = lambda low1, high1: int((float(450) / high1) * low1)
high = int(max([match.group(9), match.group(11)]))
scaled = [scale(x, high) for x in [(int(match.group(n)), high)[high == int(match.group(n))] for n in 3, 5, 7, 9, 11]]
parts = [match.group(1), match.group(4), match.group(6), match.group(8), match.group(10), match.group(12)]
img_uri = img_uri.replace(match.group(), ''.join([str(y) for x in map(None, parts, scaled) for y in x if y is not None]))
path = ek.ek(os.path.abspath, ek.ek(os.path.join, sickbeard.CACHE_DIR, 'images', 'imdb'))
helpers.make_dirs(path)
file_name = ek.ek(os.path.basename, img_uri)
cached_name = ek.ek(os.path.join, path, file_name)
if not ek.ek(os.path.isfile, cached_name):
helpers.download_file(img_uri, cached_name)
images = dict(poster=dict(thumb='cache/images/imdb/%s' % file_name))
filtered.append(dict(
premiered=dt_ordinal,
premiered_str=year or 'No year',
when_past=dt_ordinal < datetime.datetime.now().toordinal(), # air time not poss. 16.11.2015
genres='No genre yet' if not len(genres) else genres[0].get_text().strip().lower().replace(' |', ','),
ids=ids,
images=images,
overview='No overview yet' if not len(overview) else self.encode_html(overview[:250:].strip()),
rating=0 if not len(rating) else int(helpers.tryFloat(rating) * 10),
title=show.select('h3.lister-item-header a[href*=title]')[0].get_text().strip(),
url_src_db='http://www.imdb.com/%s/' % url_path,
votes=0 if not len(voting) else helpers.tryInt(voting)))
tvshow = filter(lambda x: x.imdbid == ids['imdb'], sickbeard.showList)[0]
src = ((None, 'tvrage')[INDEXER_TVRAGE == tvshow.indexer], 'tvdb')[INDEXER_TVDB == tvshow.indexer]
if src:
filtered[-1]['ids'][src] = tvshow.indexerid
filtered[-1]['url_' + src] = '%s%s' % (sickbeard.indexerApi(tvshow.indexer).config['show_url'], tvshow.indexerid)
except (AttributeError, TypeError, KeyError, IndexError):
continue
kwargs.update(dict(oldest=oldest, newest=newest, start_year=start_year))
if len(filtered):
footnote = 'Note; Some images on this page may be cropped at source: <a target="_blank" href="%s">%s watchlist at IMDb</a>' % (helpers.anon_url(url + url_ui), list_name)
elif None is not show_list:
kwargs['show_header'] = True
kwargs['error_msg'] = 'No TV titles in the <a target="_blank" href="%s">%s watchlist at IMDb</a>' % (helpers.anon_url(url + url_ui), list_name)
kwargs.update(dict(footnote=footnote, mode='watchlist-%s' % acc_id, periods=periods))
return self.browse_shows(browse_type, '%s IMDb Watchlist' % list_name, filtered, **kwargs)
def popular_imdb(self, *args, **kwargs): def popular_imdb(self, *args, **kwargs):
browse_type = 'IMDb' browse_type = 'IMDb'
@ -2533,6 +2695,15 @@ class NewHomeAddShows(Home):
filtered = [] filtered = []
footnote = None footnote = None
start_year, end_year = (datetime.date.today().year - 10, datetime.date.today().year + 1) start_year, end_year = (datetime.date.today().year - 10, datetime.date.today().year + 1)
periods = [(start_year, end_year)] + [(x-10, x) for x in range(start_year, start_year-40, -10)]
start_year_in, end_year_in = [helpers.tryInt(x) for x in (('0,0', kwargs.get('period'))[
',' in kwargs.get('period', '')]).split(',')]
if 1900 < start_year_in < 2050 and 2050 > end_year_in > 1900:
start_year, end_year = (start_year_in, end_year_in)
mode = 'popular-%s,%s' % (start_year, end_year)
url = 'http://www.imdb.com/search/title?at=0&sort=moviemeter&title_type=tv_series&year=%s,%s' % (start_year, end_year) url = 'http://www.imdb.com/search/title?at=0&sort=moviemeter&title_type=tv_series&year=%s,%s' % (start_year, end_year)
html = helpers.getURL(url) html = helpers.getURL(url)
if html: if html:
@ -2598,7 +2769,8 @@ class NewHomeAddShows(Home):
rating=0 if not len(rating) else int(helpers.tryFloat(rating[0].get_text()) * 10), rating=0 if not len(rating) else int(helpers.tryFloat(rating[0].get_text()) * 10),
title=tr.select('td.title a')[0].get_text().strip(), title=tr.select('td.title a')[0].get_text().strip(),
url_src_db='http://www.imdb.com/%s/' % url_path, url_src_db='http://www.imdb.com/%s/' % url_path,
votes=0 if not len(voting) else vote_value.sub(r'\1\2', voting[0].get('title')))) votes=0 if not len(voting) else helpers.tryInt(
vote_value.sub(r'\1\2', voting[0].get('title')), 'TBA')))
tvshow = filter(lambda x: x.imdbid == ids['imdb'], sickbeard.showList)[0] tvshow = filter(lambda x: x.imdbid == ids['imdb'], sickbeard.showList)[0]
src = ((None, 'tvrage')[INDEXER_TVRAGE == tvshow.indexer], 'tvdb')[INDEXER_TVDB == tvshow.indexer] src = ((None, 'tvrage')[INDEXER_TVRAGE == tvshow.indexer], 'tvdb')[INDEXER_TVDB == tvshow.indexer]
@ -2608,7 +2780,7 @@ class NewHomeAddShows(Home):
except (AttributeError, TypeError, KeyError, IndexError): except (AttributeError, TypeError, KeyError, IndexError):
continue continue
kwargs.update(dict(oldest=oldest, newest=newest)) kwargs.update(dict(oldest=oldest, newest=newest, mode=mode, periods=periods))
if len(filtered): if len(filtered):
footnote = 'Note; Some images on this page may be cropped at source: <a target="_blank" href="%s">IMDb</a>' % helpers.anon_url(url) footnote = 'Note; Some images on this page may be cropped at source: <a target="_blank" href="%s">IMDb</a>' % helpers.anon_url(url)
@ -2803,11 +2975,12 @@ class NewHomeAddShows(Home):
t.submenu = self.HomeMenu() t.submenu = self.HomeMenu()
t.browse_type = browse_type t.browse_type = browse_type
t.browse_title = browse_title t.browse_title = browse_title
t.all_shows = shows t.all_shows = []
t.kwargs = kwargs t.kwargs = kwargs
dedupe = []
t.all_shows_inlibrary = 0 t.all_shows_inlibrary = 0
for item in t.all_shows: for item in shows:
item['show_id'] = '' item['show_id'] = ''
for index, tvdb in enumerate(['tvdb', 'tvrage']): for index, tvdb in enumerate(['tvdb', 'tvrage']):
try: try:
@ -2827,6 +3000,10 @@ class NewHomeAddShows(Home):
if not item['show_id'] and 'tt' in item['ids'].get('imdb', ''): if not item['show_id'] and 'tt' in item['ids'].get('imdb', ''):
item['show_id'] = item['ids']['imdb'] item['show_id'] = item['ids']['imdb']
if item['show_id'] not in dedupe:
dedupe.append(item['show_id'])
t.all_shows.append(item)
return t.respond() return t.respond()
def existing_shows(self, *args, **kwargs): def existing_shows(self, *args, **kwargs):