From 09741bbf8c01f286ba0d4eec244d91cfad3a7e2c Mon Sep 17 00:00:00 2001 From: JackDandy Date: Tue, 19 Apr 2016 23:28:44 +0100 Subject: [PATCH] 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. --- CHANGES.md | 6 + .../interfaces/default/config_general.tmpl | 41 +++- .../interfaces/default/home_browseShows.tmpl | 65 ++++-- gui/slick/js/config.js | 126 +++++++++++- sickbeard/__init__.py | 7 +- sickbeard/helpers.py | 8 +- sickbeard/webserve.py | 185 +++++++++++++++++- 7 files changed, 410 insertions(+), 28 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 5809513d..c234a9ed 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -55,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) diff --git a/gui/slick/interfaces/default/config_general.tmpl b/gui/slick/interfaces/default/config_general.tmpl index 06a6478c..0d92f6a4 100644 --- a/gui/slick/interfaces/default/config_general.tmpl +++ b/gui/slick/interfaces/default/config_general.tmpl @@ -153,9 +153,9 @@ @@ -266,8 +266,8 @@ #set $hidden = ' class="hidden"' that contains all shows (default) - two groups, the show list and anime - multiple custom1 named groups and a "Show List" + two groups, the show list and anime + multiple custom1 named groups and a "Show List" @@ -278,7 +278,7 @@ comma separated names -

group shows to the order of this custom list (add shows to groups with mass edit)

+

group shows to the order of this custom list (add shows to groups with mass edit)

@@ -303,6 +303,37 @@ +
+ + +
+
#end for - #if $kwargs and $kwargs.get('footnote', None): + #if $kwargs and $kwargs.get('footnote'):
$kwargs['footnote']
#end if #else -
+

- #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. diff --git a/gui/slick/js/config.js b/gui/slick/js/config.js index a7e6a942..5b01a099 100644 --- a/gui/slick/js/config.js +++ b/gui/slick/js/config.js @@ -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 ' + nameList() + ' ?

', + '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 = $('
%s watchlist at IMDb' % (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 %s watchlist at IMDb' % (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: IMDb' % 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):