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 Hachoir library 1.3.4 (r1383) to 1.3.4 (r1435)
* 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 profilehooks 1.4 to 1.8.2.dev0 (ee3f1a8)
* Update Requests library 2.7.0 (5d6d1bc) to 2.9.1 (a1c9b84)
@ -54,6 +55,12 @@
* 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
* 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)

View file

@ -153,9 +153,9 @@
<label>
<span class="component-title">Show root directories</span>
<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')
</span>
</span>
</label>
</div>
@ -266,8 +266,8 @@
</select>
#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_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_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>
</label>
</div>
@ -278,7 +278,7 @@
<span class="component-desc">
<input type="text" name="show_tags" id="show_tags" value="$show_tags" class="form-control input-sm input300">
<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>
</label>
</div>
@ -303,6 +303,37 @@
</label>
</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">
<label for="sort_article">
<span class="component-title">Sort with "The", "A", "An"</span>

View file

@ -6,7 +6,7 @@
#from sickbeard import sbdatetime
#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 $sbPath='..'
#set global $topmenu='home'
@ -97,7 +97,7 @@
\$('#showfilter').on( 'change', function() {
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 });
} else {
location = '$sbRoot/home/addShows/' + filterValue;
@ -118,18 +118,33 @@
#end if
<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">
<span>Show:</span>
<span>View:</span>
<select id="showfilter" class="form-control form-control-inline input-sm">
#set $count_all_shows = len($all_shows)
#set $count_inlibrary = $all_shows_inlibrary
<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=".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
#set $mode = $kwargs and $kwargs.get('mode', None)
#set $selected = ' class="selected"'
<optgroup label="Trakt">
<option value="trakt_anticipated"#echo ('', selected)['anticipated' == $mode]#>Anticipating</option>
<option value="trakt_newseasons"#echo ('', selected)['newseasons' == $mode]#>New Seasons</option>
@ -151,7 +166,7 @@
<optgroup label="Trakt recommended">
#for $account in $sickbeard.TRAKT_ACCOUNTS
#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 for
#else
@ -159,6 +174,30 @@
<option value="trakt_recommended?action=add">Enable Trakt here</option>
#end if
</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
</select>
@ -179,7 +218,7 @@
</select>
</div>
<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">
First aired from $kwargs['oldest'] until $kwargs['newest']
</div>
@ -190,7 +229,7 @@
#if $all_shows
#for $this_show in $all_shows:
#set $title_html = $this_show['title'].replace('"', '&quot;').replace("'", '&#39;')
#if 'newseasons' == $kwargs.get('mode', '')
#if 'newseasons' == $mode
#set $overview = '%s: %s' % (
('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']])
@ -204,7 +243,7 @@
<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#
<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>">
#if 'poster' in $this_show['images']:
#set $image = $this_show['images']['poster']['thumb']
@ -243,15 +282,15 @@
</div>
#end for
</div>
#if $kwargs and $kwargs.get('footnote', None):
#if $kwargs and $kwargs.get('footnote'):
<div>
$kwargs['footnote']
</div>
#end if
#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">
#if $kwargs and $kwargs.get('error_msg', None):
#if $kwargs and $kwargs.get('error_msg'):
$kwargs['error_msg']
#else
$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 secs = ('00' + new Date().getSeconds().toString()).slice(-2),
elSecs = $('#trim_info_seconds'),
@ -193,15 +313,15 @@ function fetch_branches() {
}
function add_option_to_pulls(text) {
option = $('<option>');
var option = $('<option>');
option.attr('value', text[1]);
option.html(text[0]);
option.appendTo('#pullRequestVersion');
}
function add_option_to_branches(text) {
option = $('<option>');
var option = $('<option>');
option.attr('value', text);
option.html(text);
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,
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
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',
'available_access_systems']
__version__ = VERSION = '5.0'
__version__ = VERSION = '5.1dev20160106'
# Import compatibility module (importing it is enough).
import _compat

View file

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

View file

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

View file

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

View file

@ -726,6 +726,10 @@ class IMDbHTTPAccessSystem(IMDbBase):
cont = self._retrieve(self.urls['person_main'] % personID + 'bio')
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):
cont = self._retrieve(self.urls['person_main'] % personID + 'awards')
return self.pProxy.person_awards_parser.parse(cont)

View file

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

View file

@ -204,7 +204,7 @@ class DOMHTMLBioParser(DOMParserBase):
_birth_attrs = [Attribute(key='birth date',
path={
'day': "./a[starts-with(@href, " \
"'/date/')]/text()",
"'/search/name?birth_monthday=')]/text()",
'year': "./a[starts-with(@href, " \
"'/search/name?birth_year=')]/text()"
},
@ -215,7 +215,7 @@ class DOMHTMLBioParser(DOMParserBase):
_death_attrs = [Attribute(key='death date',
path={
'day': "./a[starts-with(@href, " \
"'/date/')]/text()",
"'/search/name?death_monthday=')]/text()",
'year': "./a[starts-with(@href, " \
"'/search/name?death_date=')]/text()"
},
@ -326,6 +326,107 @@ class DOMHTMLBioParser(DOMParserBase):
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):
"""Parser for the "other works" and "agent" pages of a given person.
The page should be provided as a string, as taken from
@ -502,6 +603,7 @@ from movieParser import DOMHTMLNewsParser
_OBJECTS = {
'maindetails_parser': ((DOMHTMLMaindetailsParser,), None),
'bio_parser': ((DOMHTMLBioParser,), None),
'resume_parser': ((DOMHTMLResumeParser,), None),
'otherworks_parser': ((DOMHTMLOtherWorksParser,), None),
#'agent_parser': ((DOMHTMLOtherWorksParser,), {'kind': 'agent'}),
'person_officialsites_parser': ((DOMHTMLOfficialsitesParser,), None),

View file

@ -7,7 +7,7 @@ E.g.:
http://akas.imdb.com/chart/top
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
it under the terms of the GNU General Public License as published by
@ -43,14 +43,15 @@ class DOMHTMLTop250Parser(DOMParserBase):
def _init(self):
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,
multi=True,
path={self.ranktext: "./td[1]//text()",
'rating': "./td[2]//text()",
'title': "./td[3]//text()",
'movieID': "./td[3]//a/@href",
'votes': "./td[4]//text()"
path={self.ranktext: "./td[2]//text()",
'rating': "./td[3]//strong//text()",
'title': "./td[2]//a//text()",
'year': "./td[2]//span//text()",
'movieID': "./td[2]//a/@href",
'votes': "./td[3]//strong/@title"
}))]
def postprocess_data(self, data):
@ -72,12 +73,16 @@ class DOMHTMLTop250Parser(DOMParserBase):
if theID in seenIDs:
continue
seenIDs.append(theID)
minfo = analyze_title(d['title'])
minfo = analyze_title(d['title']+" "+d['year'])
try: minfo[self.ranktext] = int(d[self.ranktext].replace('.', ''))
except: pass
if 'votes' in d:
try: minfo['votes'] = int(d['votes'].replace(',', ''))
except: pass
try:
votes = d['votes'].replace(' votes','')
votes = votes.split(' based on ')[1]
minfo['votes'] = int(votes.replace(',', ''))
except:
pass
if 'rating' in d:
try: minfo['rating'] = float(d['rating'])
except: pass

View file

@ -441,12 +441,6 @@ class DOMParserBase(object):
self._useModule = useModule
nrMods = len(useModule)
_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):
mod = mod.strip().lower()
try:

View file

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

View file

@ -170,6 +170,9 @@ INDEXER_TIMEOUT = None
SCENE_DEFAULT = False
ANIME_DEFAULT = False
USE_IMDB_INFO = True
IMDB_ACCOUNTS = []
IMDB_DEFAULT_LIST_ID = '64552276'
IMDB_DEFAULT_LIST_NAME = 'SickGear'
PROVIDER_ORDER = []
NAMING_MULTI_EP = False
@ -535,7 +538,7 @@ def initialize(consoleLogging=True):
AUTOPOSTPROCESSER_FREQUENCY, DEFAULT_AUTOPOSTPROCESSER_FREQUENCY, MIN_AUTOPOSTPROCESSER_FREQUENCY, \
ANIME_DEFAULT, NAMING_ANIME, USE_ANIDB, ANIDB_USERNAME, ANIDB_PASSWORD, ANIDB_USE_MYLIST, \
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
if __INITIALIZED__:
@ -602,6 +605,7 @@ def initialize(consoleLogging=True):
GUI_NAME = check_setting_str(CFG, 'GUI', 'gui_name', 'slick')
DEFAULT_HOME = check_setting_str(CFG, 'GUI', 'default_home', 'home')
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))
SORT_ARTICLE = bool(check_setting_int(CFG, 'General', 'sort_article', 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']['default_home'] = DEFAULT_HOME
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']['trim_zero'] = int(TRIM_ZERO)
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
if None is 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
req_headers = {'User-Agent': USER_AGENT, 'Accept-Encoding': 'gzip,deflate'}

View file

@ -2526,6 +2526,168 @@ class NewHomeAddShows(Home):
return
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):
browse_type = 'IMDb'
@ -2533,6 +2695,15 @@ class NewHomeAddShows(Home):
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)]
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)
html = helpers.getURL(url)
if html:
@ -2598,7 +2769,8 @@ class NewHomeAddShows(Home):
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(),
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]
src = ((None, 'tvrage')[INDEXER_TVRAGE == tvshow.indexer], 'tvdb')[INDEXER_TVDB == tvshow.indexer]
@ -2608,7 +2780,7 @@ class NewHomeAddShows(Home):
except (AttributeError, TypeError, KeyError, IndexError):
continue
kwargs.update(dict(oldest=oldest, newest=newest))
kwargs.update(dict(oldest=oldest, newest=newest, mode=mode, periods=periods))
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)
@ -2803,11 +2975,12 @@ class NewHomeAddShows(Home):
t.submenu = self.HomeMenu()
t.browse_type = browse_type
t.browse_title = browse_title
t.all_shows = shows
t.all_shows = []
t.kwargs = kwargs
dedupe = []
t.all_shows_inlibrary = 0
for item in t.all_shows:
for item in shows:
item['show_id'] = ''
for index, tvdb in enumerate(['tvdb', 'tvrage']):
try:
@ -2827,6 +3000,10 @@ class NewHomeAddShows(Home):
if not item['show_id'] and 'tt' in item['ids'].get('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()
def existing_shows(self, *args, **kwargs):