diff --git a/CHANGES.md b/CHANGES.md index 877076a..0a3a841 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -86,6 +86,8 @@ * Fix handling of card filters and sort states * Add view paused "Only" to Daily Schedule * Add return paused "Only" to API +* Change Add shows/Search results first aired dates to UI date format setting +* Change improve the search progress text [develop changelog] @@ -104,6 +106,12 @@ * Change check if average mini-series runtime is reasonable before displaying it * Fix use meta lang to filter TMDB images * Fix glide cursor left and right key collisions with ctrl + alt/meta with left and right +* Change add language field to TVMaze search results +* Change set TVMaze network to webchannel if no network is found +* Change add TVMaze runtime, type, schedule, status, official site +* Fix search results sorting +* Add language flag to TVmaze search result items +* Change reinstate "expand results" and display more prominently ### 0.24.16 (2021-08-28 16:05:00 UTC) diff --git a/gui/slick/interfaces/default/home_newShow.tmpl b/gui/slick/interfaces/default/home_newShow.tmpl index e5ac183..63ca425 100644 --- a/gui/slick/interfaces/default/home_newShow.tmpl +++ b/gui/slick/interfaces/default/home_newShow.tmpl @@ -42,7 +42,7 @@
' + item[result.Overview] + '
' : '') + 'Click for more' + '"' @@ -153,26 +161,23 @@ $(document).ready(function () { + ('' === srcState ? '' : ' ' + '[' + srcState + ']' + '') + '' + "\n"; - if (nBuffer < nBufferSize || item[result.isInDB]) { - resultStr += resultItem; - if (!1 === item[result.isInDB]) - nBuffer++; - } else { - resultStrBuffer += resultItem; - } + resultStr += resultItem; + if(item[result.isInDB]) + nBufferSize++; + if ((nBuffer < nBufferSize) || item[result.isInDB]) + nBuffer++; nAll++; }); } var selAttr = 'selected="selected" ', selClass = 'selected-text', classAttrSel = 'class="' + selClass + '" ', - useBuffer = nBufferSize < nAll, defSortby = /^az/.test(config.resultsSortby) || /^za/.test(config.resultsSortby) || /^newest/.test(config.resultsSortby) || /^oldest/.test(config.resultsSortby) ? '': classAttrSel + selAttr; - $('#search-results').html( + $('#search-results').addClass('collapsed').html( '' ); - - if (useBuffer) { - $('#search-results-buffer').html(resultStrBuffer); - $('#more-results').show(); - $('#more-results a').on('click', function(e, d) { - e.preventDefault(); - $('#more-results').hide(); - $('#search-results #count').text(nAll + ' search result' + (1 === nAll ? '' : 's') + '...'); - $('#search-results-buffer .results-item').appendTo('#holder'); - container$.isotope( 'reloadItems' ).isotope( - {sortBy: $('#results-sortby').find('option:not([value$="top"],[value$="group"]).selected-text').val()}); - myform.loadsection(0); - }); - $('#search-results #count').text((nBuffer + ' / ' + nAll) - + ' search result' + (1 === nBuffer ? '' : 's') + '...'); - } else { - $('#search-results #count').text((0 < nBuffer ? nBuffer + (useBuffer ? ' / ' + nAll : '') : 'No') - + ' search result' + (1 === nAll ? '' : 's') + '...'); + function displayCount(){ + $('#count').html((nAll > nBufferSize ? nBuffer + ' of ' + nAll : (0 < nAll ? nAll : 'No')) + + ' result' + (1 === nAll ? '' : 's') + '...'); } + displayCount(); + var defaultExpander = 'expand list' + $('#results-expander').html((nAll > nBufferSize ? ' ' : '')); + $('#more-results').show(); + $('#more-results a').on('click', function(e, d) { + e.preventDefault(); + var results$ = $('#search-results'), displayAction = ''; + if (results$.hasClass('collapsed')){ + displayAction = 'collapse list'; + results$.removeClass('collapsed'); + $('#count').html('All ' + nAll + ' result' + (1 === nAll ? '' : 's')); + } else { + displayAction = defaultExpander; + results$.addClass('collapsed'); + displayCount(); + } + $('#more-results').find('a').html(displayAction); + + container$.isotope('updateSortData'); + updateResults(); + myform.loadsection(0); + }); var container$ = $('#holder'), sortbySelect$ = $('#results-sortby'), @@ -218,34 +230,53 @@ $(document).ready(function () { return ($('#results-sortby').find('option[value$="notop"]').hasClass(selClass) ? (1000 > value ? value + 1000 : value) : (1000 > value ? value : value - 1000))}), + fx = {filterData: function(){ + var results$ = $('#search-results'); + if (results$.hasClass('collapsed')){ + var itemElem = this, number = getAttr(itemElem, 'sort-' + results$.find('option:not([value$="top"],[value$="group"]).' + selClass).val()); + number -= number >= 1000 ? 1000 : 0; + return (number < nBufferSize ) || !!getAttr(itemElem, 'indb'); + } + return !0; + }}, + getAttr = (function(itemElem, attr){ + var number = $(itemElem).attr('data-' + attr); + return ('undefined' !== typeof(number)) && parseInt(number, 10) || 0; + }), getData = (function(itemElem, sortby){ - var position = parseInt($(itemElem).attr('data-sort-' + sortby + - ($('#results-sortby').find('option[value$="ingroup"]').hasClass(selClass) ? '' : '-combined'))); - return (!$(itemElem).attr('data-indb')) ? position : reOrder(position); + var position = getAttr(itemElem, 'sort-' + sortby + + ($('#results-sortby').find('option[value$="ingroup"]').hasClass(selClass) ? '' : '-combined')); + return (!!getAttr(itemElem, 'indb') ? reOrder(position) : position); }); sortbySelect$.find('.' + selClass).each(function(){ $(this).html('> ' + $(this).html()); }); - container$.isotope({ - itemSelector: '.results-item', - sortBy: sortbySelect$.find('option:not([value$="top"],[value$="group"]).' + selClass).val(), - layoutMode: 'masonry', - getSortData: { - az: function(itemElem){ return getData(itemElem, 'az'); }, - za: function(itemElem){ return getData(itemElem, 'za'); }, - newest: function(itemElem){ return getData(itemElem, 'newest'); }, - oldest: function(itemElem){ return getData(itemElem, 'oldest'); }, - rel: function(itemElem){ return getData(itemElem, 'rel'); } - } - }).on('arrangeComplete', function(event, items){ - $(items).each(function(i, item){ - if (1 === i % 2){ - $(item.element).addClass('alt'); + function updateResults(){ + $('.results-item').removeClass('alt'); + container$.isotope({ + itemSelector: '.results-item', + sortBy: sortbySelect$.find('option:not([value$="top"],[value$="group"]).' + selClass).val(), + layoutMode: 'masonry', + filter: fx['filterData'], + getSortData: { + az: function(itemElem){ return getData(itemElem, 'az'); }, + za: function(itemElem){ return getData(itemElem, 'za'); }, + newest: function(itemElem){ return getData(itemElem, 'newest'); }, + oldest: function(itemElem){ return getData(itemElem, 'oldest'); }, + rel: function(itemElem){ return getData(itemElem, 'rel'); } } + }).on('arrangeComplete', function(event, items){ + $(items).each(function(i, item){ + if (1 === i % 2){ + $(item.element).addClass('alt'); + } + }); }); - }); + } + container$.isotope(); // must init first + updateResults(); sortbySelect$.on('change', function(){ var selectedSort = String($(this).val()), sortby = selectedSort, curSortby$, curSel$, newSel$; @@ -264,8 +295,8 @@ $(document).ready(function () { $('.results-item[data-indb="1"]').each(function(){ $(this).attr(sortby, reOrder(parseInt($(this).attr(sortby), 10))); }); - $('.results-item').removeClass('alt'); - container$.isotope('updateSortData').isotope({sortBy: sortby}); + container$.isotope('updateSortData'); + updateResults(); config.resultsSortby = sortby + ($(this).find('option[value$="notop"]').hasClass(selClass) ? ' notop' : '') + @@ -402,7 +433,7 @@ $(document).ready(function () { } updateAniGrouplist(showName); var sample_text = 'Adding show ' + cleanseText(showName, !0) + ''
- + (!showName.length ? 'into
' : '
into' + (!config.folder.length ? '' : ' user location'))
+ + (!showName.length ? 'into
' : '
into' + (!config.folder.length ? '' : ' user location'))
+ ' ';
// if we have a root dir selected, figure out the path
diff --git a/lib/api_tvmaze/tvmaze_api.py b/lib/api_tvmaze/tvmaze_api.py
index 31d0605..54ce7c9 100644
--- a/lib/api_tvmaze/tvmaze_api.py
+++ b/lib/api_tvmaze/tvmaze_api.py
@@ -11,8 +11,10 @@ import logging
import re
import requests
-from urllib3.util.retry import Retry
from requests.adapters import HTTPAdapter
+from urllib3.util.retry import Retry
+# noinspection PyProtectedMember
+from tornado._locale_data import LOCALE_NAMES
from _23 import filter_iter
from six import integer_types, iteritems, string_types
@@ -142,10 +144,18 @@ class TvMaze(TVInfoBase):
def _search_show(self, name=None, ids=None, **kwargs):
def _make_result_dict(s):
+ language = s.language.lower()
+ language_country_code = None
+ for cur_locale in iteritems(LOCALE_NAMES):
+ if language in cur_locale[1]['name_en'].lower():
+ language_country_code = cur_locale[0].split('_')[1].lower()
+ break
return {'seriesname': s.name, 'id': s.id, 'firstaired': s.premiered,
- 'network': s.network and s.network.name,
+ 'network': (s.network and s.network.name) or (s.web_channel and s.web_channel.name),
'genres': isinstance(s.genres, list) and ', '.join(g.lower() for g in s.genres) or s.genres,
- 'overview': s.summary,
+ 'overview': s.summary, 'language': s.language, 'language_country_code': language_country_code,
+ 'runtime': s.average_runtime or s.runtime,
+ 'type': s.type, 'schedule': s.schedule, 'status': s.status, 'official_site': s.official_site,
'aliases': [a.name for a in s.akas], 'image': s.image and s.image.get('original'),
'ids': TVInfoIDs(
tvdb=s.externals.get('thetvdb'), rage=s.externals.get('tvrage'), tvmaze=s.id,
diff --git a/sickbeard/webserve.py b/sickbeard/webserve.py
index 84fcc36..f3507cf 100644
--- a/sickbeard/webserve.py
+++ b/sickbeard/webserve.py
@@ -3958,8 +3958,10 @@ class AddShows(Home):
else:
results[cur_tvid][tv_src_id] = cur_result.copy()
results[cur_tvid][tv_src_id]['direct_id'] = \
- (cur_tvid in ids_to_search and tv_src_id == ids_to_search.get(cur_tvid)) or \
- (TVINFO_TVDB == cur_tvid and ids_to_search.get(TVINFO_TVDB_SLUG) == cur_result.get('slug'))
+ (cur_tvid in ids_to_search and ids_to_search.get(cur_tvid)
+ and tv_src_id == ids_to_search.get(cur_tvid)) or \
+ (TVINFO_TVDB == cur_tvid and cur_result.get('slug') and
+ ids_to_search.get(TVINFO_TVDB_SLUG) == cur_result.get('slug')) or False
if results[cur_tvid][tv_src_id]['direct_id'] or \
any(ids_to_search[si] == cur_result.get('ids', {})[si] for si in ids_to_search):
ids_search_used.update({k: v for k, v in iteritems(cur_result.get('ids', {}))
@@ -4003,14 +4005,18 @@ class AddShows(Home):
+ ('', '&lid=%s' % sickbeard.TVInfoAPI().config['langabbv_to_id'][lang])[TVINFO_TVDB == tvid],
int(show['id']),
show['seriesname'], helpers.xhtml_escape(show['seriesname']), show['firstaired'],
- show.get('network', '') or '', show.get('genres', '') or '',
+ (isinstance(show['firstaired'], string_types)
+ and SGDatetime.sbfdate(dateutil.parser.parse(show['firstaired'])) or ''),
+ show.get('network', '') or '', show.get('genres', '') or '', # 11 - 12
+ show.get('language', ''), show.get('language_country_code') or '', # 13 - 14
re.sub(r'([,.!][^,.!]*?)$', '...',
re.sub(r'([.!?])(?=\w)', r'\1 ',
- helpers.xhtml_escape((show.get('overview', '') or '')[:250:].strip()))),
- self._make_search_image_url(tvid, show), # 13
- (show['direct_id'] and 100)
- or self.get_uw_ratio(term, show['seriesname'], show.get('aliases', [])),
- None, None, None, None, None, None, None, None, None, # 15 - 23
+ helpers.xhtml_escape((show.get('overview', '') or '')[:250:].strip()))), # 15
+ self._make_search_image_url(tvid, show), # 16
+ 100 - ((show['direct_id'] and 100)
+ or self.get_uw_ratio(term, show['seriesname'], show.get('aliases') or [],
+ show.get('language_country_code') or '')),
+ None, None, None, None, None, None, None, None, None, # 18 - 26
show['direct_id'], show.get('rename_suggest')
] for show in itervalues(shows)] for tvid, shows in iteritems(results)])
@@ -4021,67 +4027,48 @@ class AddShows(Home):
return data if not final_sort else sorted(data, reverse=False, key=lambda _x: _x[sortby_index])
def sort_newest(data_result, is_last_sort, combine):
- return sort_date(data_result, is_last_sort, 16, as_combined=combine)
+ return sort_date(data_result, is_last_sort, 19, as_combined=combine)
def sort_oldest(data_result, is_last_sort, combine):
- return sort_date(data_result, is_last_sort, 18, False, combine)
+ return sort_date(data_result, is_last_sort, 21, False, combine)
def sort_date(data_result, is_last_sort, idx_sort, reverse=True, as_combined=False):
idx_aired = 9
- combined = final_order(
- idx_sort + 1,
- sorted(data_result, reverse=reverse, key=lambda x: (dateutil.parser.parse(
+ date_sorted = sorted(data_result, reverse=reverse, key=lambda x: (dateutil.parser.parse(
re.match(r'^(?:19|20)\d\d$', str(x[idx_aired])) and ('%s-12-31' % str(x[idx_aired]))
- or (x[idx_aired] and str(x[idx_aired])) or '1900'))), is_last_sort)
+ or (x[idx_aired] and str(x[idx_aired])) or '1900')))
+ combined = final_order(idx_sort + 1, date_sorted, is_last_sort)
idx_src = 2
- grouped = final_order(
- idx_sort,
- sorted(
- sorted(combined, reverse=reverse, key=lambda x: (dateutil.parser.parse(
- re.match(r'^(?:19|20)\d\d$', str(x[idx_aired])) and ('%s-12-31' % str(x[idx_aired]))
- or (x[idx_aired] and str(x[idx_aired])) or '1900'))),
- reverse=False, key=lambda x: x[idx_src]), is_last_sort)
+ grouped = final_order(idx_sort, sorted(date_sorted, key=lambda x: x[idx_src]), is_last_sort)
return (grouped, combined)[as_combined]
def sort_az(data_result, is_last_sort, combine):
- return sort_zaaz(data_result, is_last_sort, 20, as_combined=combine)
+ return sort_zaaz(data_result, is_last_sort, 23, as_combined=combine)
def sort_za(data_result, is_last_sort, combine):
- return sort_zaaz(data_result, is_last_sort, 22, True, combine)
+ return sort_zaaz(data_result, is_last_sort, 25, True, combine)
def sort_zaaz(data_result, is_last_sort, idx_sort, reverse=False, as_combined=False):
idx_title = 7
- combined = final_order(
- idx_sort + 1,
- sorted(
- data_result, reverse=reverse, key=lambda x: (
- (remove_article(x[idx_title].lower()), x[idx_title].lower())[sickbeard.SORT_ARTICLE])),
- is_last_sort)
+ zaaz_sorted = sorted(data_result, reverse=reverse, key=lambda x: (
+ (remove_article(x[idx_title].lower()), x[idx_title].lower())[sickbeard.SORT_ARTICLE]))
+ combined = final_order(idx_sort + 1, zaaz_sorted, is_last_sort)
idx_src = 2
- grouped = final_order(
- idx_sort,
- sorted(
- combined, reverse=reverse, key=lambda x: (
- x[idx_src],
- (remove_article(x[idx_title].lower()), x[idx_title].lower())[sickbeard.SORT_ARTICLE])),
- is_last_sort)
+ grouped = final_order(idx_sort, sorted(zaaz_sorted, key=lambda x: x[idx_src]), is_last_sort)
return (grouped, combined)[as_combined]
def sort_rel(data_result, is_last_sort, as_combined):
- idx_rel_sort, idx_rel = 14, 14
+ idx_rel_sort, idx_rel = 17, 17
idx_title = 7
- combined = final_order(
- idx_rel_sort + 1,
- sorted(data_result, reverse=True, key=lambda x: (x[idx_rel], x[idx_title])), is_last_sort)
-
idx_src = 2
- grouped = final_order( # grouped
- idx_rel_sort,
- sorted(combined, reverse=True, key=lambda x: (x[idx_src], x[idx_rel], x[idx_title])), is_last_sort)
+ rel_sorted = sorted(data_result, key=lambda x: (x[idx_rel], x[idx_title], x[idx_src]))
+ combined = final_order(idx_rel_sort + 1, rel_sorted, is_last_sort)
+
+ grouped = final_order(idx_rel_sort, sorted(rel_sorted, key=lambda x: (x[idx_src])), is_last_sort)
return (grouped, combined)[as_combined]
@@ -4117,16 +4104,31 @@ class AddShows(Home):
return img_url
@classmethod
- def get_uw_ratio(cls, search_term, showname, aliases):
+ def get_uw_ratio(cls, search_term, showname, aliases, lang=None):
search_term = decode_str(search_term, errors='replace')
showname = decode_str(showname, errors='replace')
s = fuzz.UWRatio(search_term, showname)
# check aliases and give them a little lower score
- for a in aliases:
- ns = fuzz.UWRatio(search_term, a) - 1
- if ns > s:
+ lower_alias = 0
+ for cur_alias in aliases or []:
+ ns = fuzz.UWRatio(search_term, cur_alias)
+ if (ns - 1) > s:
s = ns
- return s
+ lower_alias = 1
+
+ # if lang param is supplied, add scale in order to reorder elements 1) en:lang 2) other:lang 3) alias
+ # this spacer behaviour may improve the original logic, but currently isn't due to lang used as off switch
+ # scale = 3 will enable spacing for all use cases
+ scale = (1, 3)[None is not lang]
+
+ score_scale = (s * scale)
+ if score_scale:
+ score_scale -= lower_alias
+
+ # if lang param is supplied, and does not specify English, then lower final score
+ score_scale -= (1, 0)[None is lang or lang in ('gb',) or not score_scale]
+
+ return score_scale
def mass_add_table(self, root_dir=None, hash_dir=None, **kwargs):
@@ -4277,14 +4279,15 @@ class AddShows(Home):
t.provided_show_dir = show_dir
t.other_shows = other_shows
+ t.infosrc = sickbeard.TVInfoAPI().search_sources
search_tvid = None
if use_show_name and 1 == show_name.count(':'): # if colon is found once
search_tvid = filter_list(lambda x: bool(x),
[('%s:' % sickbeard.TVInfoAPI(_tvid).config['slug']) in show_name and _tvid
- for _tvid, _ in iteritems(sickbeard.TVInfoAPI().search_sources)])
+ for _tvid, _ in iteritems(t.infosrc)])
search_tvid = 1 == len(search_tvid) and search_tvid[0]
t.provided_tvid = search_tvid or int(tvid or sickbeard.TVINFO_DEFAULT)
- t.infosrc = sickbeard.TVInfoAPI().search_sources
+ t.infosrc_icons = [sickbeard.TVInfoAPI(cur_tvid).config.get('icon') for cur_tvid in t.infosrc]
t.meta_lang = sickbeard.ADD_SHOWS_METALANG
t.allowlist = []
t.blocklist = []