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):