Change only show unaired episodes on Manage/Backlog Overview and Manage/Episode Status Management where relevant.

Change display show page, can mark unaired items with a date to "Wanted" to trigger a manual active backlog search provided search setting "Unaired episodes" is enabled.
This commit is contained in:
JackDandy 2016-09-24 12:23:22 +01:00
parent 4a4afdaa01
commit 145301e275
8 changed files with 130 additions and 104 deletions

View file

@ -166,6 +166,9 @@
* Remove redundant config/general/"Allow incomplete show data" * Remove redundant config/general/"Allow incomplete show data"
* Fix status reset of a snatched, downloaded, or archived episode when its date is set to never (no date) on the info * Fix status reset of a snatched, downloaded, or archived episode when its date is set to never (no date) on the info
source and there is no media file source and there is no media file
* Change only show unaired episodes on Manage/Backlog Overview and Manage/Episode Status Management where relevant
* Change display show page, can mark unaired items with a date to "Wanted" to trigger a manual active backlog search
provided search setting "Unaired episodes" is enabled
[develop changelog] [develop changelog]
* Change send nzb data to NZBGet for Anizb instead of url * Change send nzb data to NZBGet for Anizb instead of url

View file

@ -483,7 +483,7 @@
<tr class="#echo ($Overview.overviewStrings[$epCats[$epStr]], 'airdate-never')[$never_aired]##echo ('', ' archived')[ARCHIVED == int($epResult['status'])]# season-$curSeason seasonstyle"> <tr class="#echo ($Overview.overviewStrings[$epCats[$epStr]], 'airdate-never')[$never_aired]##echo ('', ' archived')[ARCHIVED == int($epResult['status'])]# season-$curSeason seasonstyle">
<td class="col-checkbox"> <td class="col-checkbox">
#if $UNAIRED != int($epResult['status']) and not $never_aired #if ($UNAIRED != int($epResult['status']) or $sickbeard.SEARCH_UNAIRED) and not $never_aired
<input type="checkbox" class="epCheck" id="#echo $epStr#" name="#echo $epStr#"> <input type="checkbox" class="epCheck" id="#echo $epStr#" name="#echo $epStr#">
#end if #end if
</td> </td>

View file

@ -175,7 +175,7 @@
<span class="component-desc"> <span class="component-desc">
<input type="checkbox" name="dvdorder" id="dvdorder"#echo ('', $html_checked)[$show.dvdorder]#> <input type="checkbox" name="dvdorder" id="dvdorder"#echo ('', $html_checked)[$show.dvdorder]#>
<p>#echo ('enable to use DVD title and episode ordering', 'disable to use TV network title, number and aired order')[$show.dvdorder]#.&nbsp; <p>#echo ('enable to use DVD title and episode ordering', 'disable to use TV network title, number and aired order')[$show.dvdorder]#.&nbsp;
After changing this setting, a "force full update" is essential, and existing episodes must be manually renamed</p> After changing this setting, a "force full update" is essential, and existing episodes should be manually renamed or replaced with #echo ('DVD', 'network')[$show.dvdorder]# numbered releases</p>
</span> </span>
</label> </label>
</div> </div>

View file

@ -52,25 +52,29 @@
#set $totalQual += $showCounts[$curShow.indexerid][$Overview.QUAL] #set $totalQual += $showCounts[$curShow.indexerid][$Overview.QUAL]
#end for #end for
## ##
<div class="h2footer pull-right"> <div class="h2footer pull-right">
<span class="listing-key wanted">Wanted: <b>$totalWanted</b></span> <span class="listing-key wanted">Wanted: <b>$totalWanted</b></span>
<span class="listing-key qual">Low Quality: <b>$totalQual</b></span> <span class="listing-key qual">Low Quality: <b>$totalQual</b></span>
</div> </div>
<br/> <br/>
<div class="pull-left"> #if not $totalWanted
Jump to Show <h3>no shows require a <span class="grey-text">backlog search</span></h3>
#else
<div class="pull-left">
Jump to Show
<select id="pickShow" class="form-control form-control-inline input-sm"> <select id="pickShow" class="form-control form-control-inline input-sm">
#for $curShow in sorted($sickbeard.showList, key = operator.attrgetter('name')): #for $curShow in sorted($sickbeard.showList, key = operator.attrgetter('name')):
#if 0 != $showCounts[$curShow.indexerid][$Overview.QUAL] + $showCounts[$curShow.indexerid][$Overview.WANTED]: #if 0 != $showCounts[$curShow.indexerid][$Overview.QUAL] + $showCounts[$curShow.indexerid][$Overview.WANTED]:
<option value="$curShow.indexerid">$curShow.name</option> <option value="$curShow.indexerid">$curShow.name</option>
#end if #end if
#end for #end for
</select> </select>
</div> </div>
#end if
<table class="sickbeardTable" border="0"> <table class="sickbeardTable" border="0">
#for $curShow in sorted($sickbeard.showList, key = operator.attrgetter('name')): #for $curShow in sorted($sickbeard.showList, key = operator.attrgetter('name')):
## ##
#if 0 == $showCounts[$curShow.indexerid][$Overview.QUAL] + $showCounts[$curShow.indexerid][$Overview.WANTED]: #if 0 == $showCounts[$curShow.indexerid][$Overview.QUAL] + $showCounts[$curShow.indexerid][$Overview.WANTED]:
#continue #continue
@ -92,29 +96,29 @@ Jump to Show
</td> </td>
</tr> </tr>
<tr class="seasoncols"><th>Episode</th><th class="text-left">Name</th><th class="text-nowrap">Airdate</th></tr> <tr class="seasoncols"><th style="width:10%">Episode</th><th class="text-left">Name</th><th class="text-nowrap">Airdate</th></tr>
## ##
#for $curResult in $showSQLResults[$curShow.indexerid]: #for $curResult in $showSQLResults[$curShow.indexerid]:
#set $whichStr = $str($curResult['season']) + 'x' + $str($curResult['episode']) #set $whichStr = '%sx%s' % ($str($curResult['season']), $str($curResult['episode']))
#try: #try:
#set $overview = $showCats[$curShow.indexerid][$whichStr] #set $overview = $showCats[$curShow.indexerid][$whichStr]
#except Exception #except Exception
#continue #continue
#end try #end try
## ##
#if $overview not in ($Overview.QUAL, $Overview.WANTED): #if $overview in ($Overview.QUAL, $Overview.WANTED) or ($sickbeard.SEARCH_UNAIRED and $Overview.UNAIRED == $overview and 0 < $curResult['season'])
#continue #
#end if
<tr class="seasonstyle $Overview.overviewStrings[$showCats[$curShow.indexerid][$whichStr]]"> <tr class="seasonstyle $Overview.overviewStrings[$showCats[$curShow.indexerid][$whichStr]]">
<td>$whichStr</td> <td>$whichStr</td>
<td class="text-left">$curResult["name"]</td> <td class="text-left">$curResult['name']</td>
<td class="text-nowrap"><div class="${fuzzydate}">#if int($curResult['airdate']) == 1 then 'never' else $sbdatetime.sbdatetime.sbfdate($sbdatetime.sbdatetime.convert_to_setting($network_timezones.parse_date_time($curResult['airdate'],$curShow.airs,$curShow.network)))#</div></td> <td class="text-nowrap"><div class="${fuzzydate}">#if 1 == int($curResult['airdate']) then 'never' else $sbdatetime.sbdatetime.sbfdate($sbdatetime.sbdatetime.convert_to_setting($network_timezones.parse_date_time($curResult['airdate'], $curShow.airs,$curShow.network)))#</div></td>
</tr> </tr>
#
#end if
#end for
#end for #end for
#end for
</table> </table>
</div> </div>
#include $os.path.join($sickbeard.PROG_DIR, 'gui/slick/interfaces/default/inc_bottom.tmpl') #include $os.path.join($sickbeard.PROG_DIR, 'gui/slick/interfaces/default/inc_bottom.tmpl')

View file

@ -35,12 +35,10 @@ from tvdb_ui import BaseUI, ConsoleUI
from tvdb_exceptions import (tvdb_error, tvdb_shownotfound, from tvdb_exceptions import (tvdb_error, tvdb_shownotfound,
tvdb_seasonnotfound, tvdb_episodenotfound, tvdb_attributenotfound) tvdb_seasonnotfound, tvdb_episodenotfound, tvdb_attributenotfound)
from sickbeard import logger
def log():
return logging.getLogger('tvdb_api')
def retry(ExceptionToCheck, tries=4, delay=3, backoff=2, logger=None): def retry(ExceptionToCheck, tries=4, delay=3, backoff=2, logr=None):
"""Retry calling the decorated function using an exponential backoff. """Retry calling the decorated function using an exponential backoff.
http://www.saltycrane.com/blog/2009/11/trying-out-retry-decorator-python/ http://www.saltycrane.com/blog/2009/11/trying-out-retry-decorator-python/
@ -56,8 +54,8 @@ def retry(ExceptionToCheck, tries=4, delay=3, backoff=2, logger=None):
:param backoff: backoff multiplier e.g. value of 2 will double the delay :param backoff: backoff multiplier e.g. value of 2 will double the delay
each retry each retry
:type backoff: int :type backoff: int
:param logger: logger to use. If None, print :param logr: logger to use. If None, print
:type logger: logging.Logger instance :type logr: logging.Logger instance
""" """
def deco_retry(f): def deco_retry(f):
@ -69,9 +67,9 @@ def retry(ExceptionToCheck, tries=4, delay=3, backoff=2, logger=None):
try: try:
return f(*args, **kwargs) return f(*args, **kwargs)
except ExceptionToCheck, e: except ExceptionToCheck, e:
msg = '%s, Retrying in %d seconds...' % (str(e), mdelay) msg = 'TVDB_API :: %s, Retrying in %d seconds...' % (str(e), mdelay)
if logger: if logr:
logger.warning(msg) logger.log(msg, logger.WARNING)
else: else:
print msg print msg
time.sleep(mdelay) time.sleep(mdelay)
@ -518,6 +516,9 @@ class Tvdb:
self.config['url_seriesBanner'] = u'%(base_url)s/api/%(apikey)s/series/%%s/banners.xml' % self.config self.config['url_seriesBanner'] = u'%(base_url)s/api/%(apikey)s/series/%%s/banners.xml' % self.config
self.config['url_artworkPrefix'] = u'%(base_url)s/banners/%%s' % self.config self.config['url_artworkPrefix'] = u'%(base_url)s/banners/%%s' % self.config
def log(self, msg, log_level=logger.DEBUG):
logger.log('TVDB_API :: %s' % (msg.replace(self.config['apikey'], '<apikey>')), logLevel=log_level)
@staticmethod @staticmethod
def _get_temp_dir(): def _get_temp_dir():
"""Returns the [system temp dir]/tvdb_api-u501 (or """Returns the [system temp dir]/tvdb_api-u501 (or
@ -536,7 +537,7 @@ class Tvdb:
@retry(tvdb_error) @retry(tvdb_error)
def _load_url(self, url, params=None, language=None): def _load_url(self, url, params=None, language=None):
log().debug('Retrieving URL %s' % url) self.log('Retrieving URL %s' % url)
session = requests.session() session = requests.session()
@ -544,7 +545,7 @@ class Tvdb:
session = CacheControl(session, cache=caches.FileCache(self.config['cache_location'])) session = CacheControl(session, cache=caches.FileCache(self.config['cache_location']))
if self.config['proxy']: if self.config['proxy']:
log().debug('Using proxy for URL: %s' % url) self.log('Using proxy for URL: %s' % url)
session.proxies = {'http': self.config['proxy'], 'https': self.config['proxy']} session.proxies = {'http': self.config['proxy'], 'https': self.config['proxy']}
session.headers.update({'Accept-Encoding': 'gzip,deflate'}) session.headers.update({'Accept-Encoding': 'gzip,deflate'})
@ -576,7 +577,7 @@ class Tvdb:
if 'application/zip' in resp.headers.get('Content-Type', ''): if 'application/zip' in resp.headers.get('Content-Type', ''):
try: try:
# TODO: The zip contains actors.xml and banners.xml, which are currently ignored [GH-20] # TODO: The zip contains actors.xml and banners.xml, which are currently ignored [GH-20]
log().debug('We recived a zip file unpacking now ...') self.log('We received a zip file unpacking now ...')
zipdata = StringIO.StringIO() zipdata = StringIO.StringIO()
zipdata.write(resp.content) zipdata.write(resp.content)
myzipfile = zipfile.ZipFile(zipdata) myzipfile = zipfile.ZipFile(zipdata)
@ -642,7 +643,7 @@ class Tvdb:
and returns the result list and returns the result list
""" """
series = series.encode('utf-8') series = series.encode('utf-8')
log().debug('Searching for show %s' % series) self.log('Searching for show %s' % series)
self.config['params_get_series']['seriesname'] = series self.config['params_get_series']['seriesname'] = series
try: try:
@ -666,19 +667,19 @@ class Tvdb:
all_series = [all_series] all_series = [all_series]
if 0 == len(all_series): if 0 == len(all_series):
log().debug('Series result returned zero') self.log('Series result returned zero')
raise tvdb_shownotfound('Show-name search returned zero results (cannot find show on TVDB)') raise tvdb_shownotfound('Show-name search returned zero results (cannot find show on TVDB)')
if None is not self.config['custom_ui']: if None is not self.config['custom_ui']:
log().debug('Using custom UI %s' % (repr(self.config['custom_ui']))) self.log('Using custom UI %s' % (repr(self.config['custom_ui'])))
custom_ui = self.config['custom_ui'] custom_ui = self.config['custom_ui']
ui = custom_ui(config=self.config) ui = custom_ui(config=self.config)
else: else:
if not self.config['interactive']: if not self.config['interactive']:
log().debug('Auto-selecting first search result using BaseUI') self.log('Auto-selecting first search result using BaseUI')
ui = BaseUI(config=self.config) ui = BaseUI(config=self.config)
else: else:
log().debug('Interactively selecting show using ConsoleUI') self.log('Interactively selecting show using ConsoleUI')
ui = ConsoleUI(config=self.config) ui = ConsoleUI(config=self.config)
return ui.selectSeries(all_series) return ui.selectSeries(all_series)
@ -701,7 +702,7 @@ class Tvdb:
This interface will be improved in future versions. This interface will be improved in future versions.
""" """
log().debug('Getting season banners for %s' % sid) self.log('Getting season banners for %s' % sid)
banners_et = self._getetsrc(self.config['url_seriesBanner'] % sid) banners_et = self._getetsrc(self.config['url_seriesBanner'] % sid)
banners = {} banners = {}
@ -729,7 +730,7 @@ class Tvdb:
for k, v in banners[btype][btype2][bid].items(): for k, v in banners[btype][btype2][bid].items():
if k.endswith('path'): if k.endswith('path'):
new_key = '_%s' % k new_key = '_%s' % k
log().debug('Transforming %s to %s' % (k, new_key)) self.log('Transforming %s to %s' % (k, new_key))
new_url = self.config['url_artworkPrefix'] % v new_url = self.config['url_artworkPrefix'] % v
banners[btype][btype2][bid][new_key] = new_url banners[btype][btype2][bid][new_key] = new_url
except: except:
@ -761,7 +762,7 @@ class Tvdb:
Any key starting with an underscore has been processed (not the raw Any key starting with an underscore has been processed (not the raw
data from the XML) data from the XML)
""" """
log().debug('Getting actors for %s' % sid) self.log('Getting actors for %s' % sid)
actors_et = self._getetsrc(self.config['url_actorsInfo'] % sid) actors_et = self._getetsrc(self.config['url_actorsInfo'] % sid)
cur_actors = Actors() cur_actors = Actors()
@ -789,16 +790,16 @@ class Tvdb:
""" """
if None is self.config['language']: if None is self.config['language']:
log().debug('Config language is none, using show language') self.log('Config language is none, using show language')
if None is language: if None is language:
raise tvdb_error('config[\'language\'] was None, this should not happen') raise tvdb_error('config[\'language\'] was None, this should not happen')
get_show_in_language = language get_show_in_language = language
else: else:
log().debug('Configured language %s override show language of %s' % (self.config['language'], language)) self.log('Configured language %s override show language of %s' % (self.config['language'], language))
get_show_in_language = self.config['language'] get_show_in_language = self.config['language']
# Parse show information # Parse show information
log().debug('Getting all series data for %s' % sid) self.log('Getting all series data for %s' % sid)
url = (self.config['url_seriesInfo'] % (sid, language), self.config['url_epInfo%s' % ('', '_zip')[self.config['useZip']]] % (sid, language))[get_ep_info] url = (self.config['url_seriesInfo'] % (sid, language), self.config['url_epInfo%s' % ('', '_zip')[self.config['useZip']]] % (sid, language))[get_ep_info]
show_data = self._getetsrc(url, language=get_show_in_language) show_data = self._getetsrc(url, language=get_show_in_language)
@ -825,7 +826,7 @@ class Tvdb:
self._parse_actors(sid) self._parse_actors(sid)
# Parse episode data # Parse episode data
log().debug('Getting all episodes of %s' % sid) self.log('Getting all episodes of %s' % sid)
if 'Episode' not in show_data: if 'Episode' not in show_data:
return False return False
@ -834,10 +835,10 @@ class Tvdb:
if not isinstance(episodes, list): if not isinstance(episodes, list):
episodes = [episodes] episodes = [episodes]
dvd_order = {'dvd': [], 'network': []}
for cur_ep in episodes: for cur_ep in episodes:
if self.config['dvdorder']: if self.config['dvdorder']:
log().debug('Using DVD ordering.') use_dvd = cur_ep['DVD_season'] not in (None, '') and cur_ep['DVD_episodenumber'] not in (None, '')
use_dvd = None is not cur_ep['DVD_season'] and None is not cur_ep['DVD_episodenumber']
else: else:
use_dvd = False use_dvd = False
@ -847,14 +848,17 @@ class Tvdb:
elem_seasnum, elem_epno = cur_ep['SeasonNumber'], cur_ep['EpisodeNumber'] elem_seasnum, elem_epno = cur_ep['SeasonNumber'], cur_ep['EpisodeNumber']
if None is elem_seasnum or None is elem_epno: if None is elem_seasnum or None is elem_epno:
log().warning('An episode has incomplete season/episode number (season: %r, episode: %r)' % ( self.log('An episode has incomplete season/episode number (season: %r, episode: %r)' % (
elem_seasnum, elem_epno)) elem_seasnum, elem_epno), logger.WARNING)
continue # Skip to next episode continue # Skip to next episode
# float() is because https://github.com/dbr/tvnamer/issues/95 - should probably be fixed in TVDB data # float() is because https://github.com/dbr/tvnamer/issues/95 - should probably be fixed in TVDB data
seas_no = int(float(elem_seasnum)) seas_no = int(float(elem_seasnum))
ep_no = int(float(elem_epno)) ep_no = int(float(elem_epno))
if self.config['dvdorder']:
dvd_order[('network', 'dvd')[use_dvd]] += ['S%02dE%02d' % (seas_no, ep_no)]
for k, v in cur_ep.items(): for k, v in cur_ep.items():
k = k.lower() k = k.lower()
@ -866,6 +870,15 @@ class Tvdb:
self._set_item(sid, seas_no, ep_no, k, v) self._set_item(sid, seas_no, ep_no, k, v)
if self.config['dvdorder']:
num_dvd, num_network = [len(dvd_order[x]) for x in 'dvd', 'network']
num_all = num_dvd + num_network
if num_all:
self.log('Of %s episodes, %s use the DVD order, and %s use the network aired order' % (
num_all, num_dvd, num_network))
for ep_numbers in [', '.join(dvd_order['dvd'][i:i + 5]) for i in xrange(0, num_dvd, 5)]:
self.log('Using DVD order: %s' % ep_numbers)
return True return True
def _name_to_sid(self, name): def _name_to_sid(self, name):
@ -874,10 +887,10 @@ class Tvdb:
the correct SID. the correct SID.
""" """
if name in self.corrections: if name in self.corrections:
log().debug('Correcting %s to %s' % (name, self.corrections[name])) self.log('Correcting %s to %s' % (name, self.corrections[name]))
return self.corrections[name] return self.corrections[name]
else: else:
log().debug('Getting show %s' % name) self.log('Getting show %s' % name)
selected_series = self._get_series(name) selected_series = self._get_series(name)
if isinstance(selected_series, dict): if isinstance(selected_series, dict):
selected_series = [selected_series] selected_series = [selected_series]

View file

@ -743,7 +743,7 @@ class TorrentProvider(object, GenericProvider):
@property @property
def url(self): def url(self):
if None is self._url or (hasattr(self, 'url_tmpl') and not self.urls): if None is self._url or (hasattr(self, 'url_tmpl') and not self.urls):
self._url = self._valid_home() self._url = self._valid_home(False)
self._valid_url() self._valid_url()
return self._url return self._url
@ -873,7 +873,7 @@ class TorrentProvider(object, GenericProvider):
return data and re.search(r'(?sim)<input[^<]+name="password"', data) and \ return data and re.search(r'(?sim)<input[^<]+name="password"', data) and \
re.search(r'(?sim)<input[^<]+name="username"', data) re.search(r'(?sim)<input[^<]+name="username"', data)
def _valid_home(self): def _valid_home(self, attempt_fetch=True):
""" """
:return: signature verified home url else None if validation fail :return: signature verified home url else None if validation fail
""" """
@ -910,7 +910,7 @@ class TorrentProvider(object, GenericProvider):
logger.log('Failed to identify a "%s" page with %s %s (local network issue, site down, or ISP blocked) ' % logger.log('Failed to identify a "%s" page with %s %s (local network issue, site down, or ISP blocked) ' %
(self.name, len(url_list), ('URL', 'different URLs')[1 < len(url_list)]) + (self.name, len(url_list), ('URL', 'different URLs')[1 < len(url_list)]) +
'Suggest; 1) Disable "%s" 2) Use a proxy/VPN' % self.get_id(), (attempt_fetch and ('Suggest; 1) Disable "%s" 2) Use a proxy/VPN' % self.get_id()) or ''),
(logger.WARNING, logger.ERROR)[self.enabled]) (logger.WARNING, logger.ERROR)[self.enabled])
self.urls = {} self.urls = {}
sickbeard.PROVIDER_HOMES[self.get_id()] = ('site down', int(time.time()) + (5 * 60)) sickbeard.PROVIDER_HOMES[self.get_id()] = ('site down', int(time.time()) + (5 * 60))

View file

@ -1871,7 +1871,7 @@ class Home(MainHandler):
with epObj.lock: with epObj.lock:
# don't let them mess up UNAIRED episodes # don't let them mess up UNAIRED episodes
if epObj.status == UNAIRED: if epObj.status == UNAIRED and not sickbeard.SEARCH_UNAIRED:
logger.log(u'Refusing to change status of ' + curEp + ' because it is UNAIRED', logger.ERROR) logger.log(u'Refusing to change status of ' + curEp + ' because it is UNAIRED', logger.ERROR)
continue continue
@ -3392,13 +3392,15 @@ class Manage(MainHandler):
result = {} result = {}
for cur_result in cur_show_results: for cur_result in cur_show_results:
if not sickbeard.SEARCH_UNAIRED and 1000 > cur_result['airdate']:
continue
cur_season = int(cur_result['season']) cur_season = int(cur_result['season'])
cur_episode = int(cur_result['episode']) cur_episode = int(cur_result['episode'])
if cur_season not in result: if cur_season not in result:
result[cur_season] = {} result[cur_season] = {}
result[cur_season][cur_episode] = {'name': cur_result['name'], 'airdate_never': (True, False)[1000 < int(cur_result['airdate'])]} result[cur_season][cur_episode] = {'name': cur_result['name'], 'airdate_never': 1000 > int(cur_result['airdate'])}
return json.dumps(result) return json.dumps(result)
@ -3438,6 +3440,8 @@ class Manage(MainHandler):
show_names = {} show_names = {}
sorted_show_ids = [] sorted_show_ids = []
for cur_status_result in status_results: for cur_status_result in status_results:
if not sickbeard.SEARCH_UNAIRED and 1000 > cur_status_result['airdate']:
continue
cur_indexer_id = int(cur_status_result['indexer_id']) cur_indexer_id = int(cur_status_result['indexer_id'])
if cur_indexer_id not in ep_counts: if cur_indexer_id not in ep_counts:
ep_counts[cur_indexer_id] = 1 ep_counts[cur_indexer_id] = 1
@ -3640,6 +3644,8 @@ class Manage(MainHandler):
[curShow.indexerid]) [curShow.indexerid])
for curResult in sqlResults: for curResult in sqlResults:
if not sickbeard.SEARCH_UNAIRED and 1 == curResult['airdate']:
continue
curEpCat = curShow.getOverview(int(curResult['status'])) curEpCat = curShow.getOverview(int(curResult['status']))
if curEpCat: if curEpCat:
epCats[str(curResult['season']) + 'x' + str(curResult['episode'])] = curEpCat epCats[str(curResult['season']) + 'x' + str(curResult['episode'])] = curEpCat