diff --git a/gui/slick/interfaces/default/displayShow.tmpl b/gui/slick/interfaces/default/displayShow.tmpl index 2dd966c1..0c2d7af2 100644 --- a/gui/slick/interfaces/default/displayShow.tmpl +++ b/gui/slick/interfaces/default/displayShow.tmpl @@ -189,6 +189,7 @@ Sports: \"Y" Anime: \"Y" DVD Order: \"Y" + Scene Numbering: \"Y" #if $anyQualities + $bestQualities Archive First Match: \"Y" #end if @@ -260,19 +261,37 @@ #continue #end if + #set $scene = False + #set $scene_anime = False + #if not $show.air_by_date and not $show.is_sports and not $show.is_anime and $show.is_scene: + #set $scene = True + #elif not $show.air_by_date and not $show.is_sports and $show.is_anime and $show.is_scene: + #set $scene_anime = True + #end if + + #set ($dfltSeas, $dfltEpis, $dfltAbsolute) = (0, 0, 0) + #if (epResult["season"], epResult["episode"]) in $xem_numbering: - #set ($dfltSeas, $dfltEpis, $dfltAbsolute) = $xem_numbering[(epResult["season"], epResult["episode"])] - #elif $xem_numbering and (epResult["season"], epResult["episode"]) not in $xem_numbering: - #set ($dfltSeas, $dfltEpis, $dfltAbsolute) = (0,0,0) - #else: - #set ($dfltSeas, $dfltEpis, $dfltAbsolute) = (epResult["season"], epResult["episode"], epResult["absolute_number"]) + #set ($dfltSeas, $dfltEpis) = $xem_numbering[(epResult["season"], epResult["episode"])] + #end if + + #if epResult["absolute_number"] in $xem_absolute_numbering: + #set $dfltAbsolute = $xem_absolute_numbering[epResult["absolute_number"]] + #end if + + #if epResult["absolute_number"] in $scene_absolute_numbering: + #set $scAbsolute = $scene_absolute_numbering[epResult["absolute_number"]] + #set $dfltAbsNumbering = False + #else + #set $scAbsolute = $dfltAbsolute + #set $dfltAbsNumbering = True #end if #if (epResult["season"], epResult["episode"]) in $scene_numbering: - #set ($scSeas, $scEpis, $scAbsolute) = $scene_numbering[(epResult["season"], epResult["episode"])] + #set ($scSeas, $scEpis) = $scene_numbering[(epResult["season"], epResult["episode"])] #set $dfltEpNumbering = False #else - #set ($scSeas, $scEpis, $scAbsolute) = ($dfltSeas, $dfltEpis, $dfltAbsolute) + #set ($scSeas, $scEpis) = ($dfltSeas, $dfltEpis) #set $dfltEpNumbering = True #end if @@ -283,7 +302,7 @@

#if int($epResult["season"]) == 0 then "Specials" else "Season "+str($epResult["season"])#

- NFOTBNEpisodeScene #Absolute #NameAirdateFilename#if $sickbeard.USE_SUBTITLES and $show.subtitles then "Subtitles" else ""#StatusSearch + NFOTBNEpisode#if $scene then "Scene #" else ""# #if $scene_anime then "Scene Absolute #" else ""#NameAirdateFilename#if $sickbeard.USE_SUBTITLES and $show.subtitles then "Subtitles" else ""#StatusSearch #set $curSeason = int($epResult["season"]) #end if @@ -298,8 +317,9 @@ \"Y" \"Y" $epResult["episode"] + + #if $scene: - #if not $show.air_by_date and not $show.is_sports " @@ -311,27 +331,23 @@ #end if style="padding: 0; text-align: center; max-width: 60px;" /> - #else - - - #end if + #elif $scene_anime: - #if not $show.air_by_date and not $show.is_sports " + class="sceneAbsolute" data-for-absolute="$epResult["absolute_number"]" + id="sceneAbsolute_$show.indexerid<%="_"+str(epResult["absolute_number"])%>" title="Change the value here if scene absolute numbering differs from the indexer absolute numbering" - #if $dfltEpNumbering: + #if $dfltAbsNumbering: value="" #else value="<%=str(scAbsolute)%>" #end if style="padding: 0; text-align: center; max-width: 60px;" /> - #else - - - #end if + #end if + #if $epResult["description"] != "" and $epResult["description"] != None: " /> diff --git a/gui/slick/interfaces/default/editShow.tmpl b/gui/slick/interfaces/default/editShow.tmpl index c9c90fd9..dec91c72 100644 --- a/gui/slick/interfaces/default/editShow.tmpl +++ b/gui/slick/interfaces/default/editShow.tmpl @@ -114,6 +114,10 @@ This DOES NOT allow SickRage to download non-english TV episodes!
Paused:

Subtitles:

+Scene Numbering: +
+(check this if you wish to search by scene numbering, uncheck to search by indexer numbering) +

Air by date:
(check this if the show is released as Show.03.02.2010 rather than Show.S02E03) diff --git a/gui/slick/js/displayShow.js b/gui/slick/js/displayShow.js index 3b9cee95..d486e31b 100644 --- a/gui/slick/js/displayShow.js +++ b/gui/slick/js/displayShow.js @@ -170,7 +170,7 @@ $(document).ready(function(){ if (sceneSeason === '') sceneSeason = null; if (sceneEpisode === '') sceneEpisode = null; - $.getJSON(sbRoot + '/home/setEpisodeSceneNumbering', + $.getJSON(sbRoot + '/home/setSceneNumbering', { 'show': showId, 'indexer': indexer, @@ -200,7 +200,43 @@ $(document).ready(function(){ } ); } - + + function setAbsoluteSceneNumbering(forAbsolute, sceneAbsolute) { + var sbRoot = $('#sbRoot').val(); + var showId = $('#showID').val(); + var indexer = $('#indexer').val(); + + if (sceneAbsolute === '') sceneAbsolute = null; + + $.getJSON(sbRoot + '/home/setSceneNumbering', + { + 'show': showId, + 'indexer': indexer, + 'forAbsolute': forAbsolute, + 'sceneAbsolute': sceneAbsolute + }, + function(data) { + // Set the values we get back + if (data.sceneAbsolute === null) + { + $('#sceneAbsolute_' + showId +'_' + forAbsolute).val(''); + } + else + { + $('#sceneAbsolute_' + showId +'_' + forAbsolute).val(data.sceneAbsolute); + } + if (!data.success) + { + if (data.errorMessage) { + alert(data.errorMessage); + } else { + alert('Update failed.'); + } + } + } + ); + } + $('.sceneSeasonXEpisode').change(function() { // Strip non-numeric characters $(this).val($(this).val().replace(/[^0-9xX]*/g,'')); @@ -219,4 +255,20 @@ $(document).ready(function(){ } setEpisodeSceneNumbering(forSeason, forEpisode, sceneSeason, sceneEpisode); }); + + $('.sceneAbsolute').change(function() { + // Strip non-numeric characters + $(this).val($(this).val().replace(/[^0-9xX]*/g,'')); + var forAbsolute = $(this).attr('data-for-absolute'); + var showId = $('#showID').val(); + var indexer = $('#indexer').val(); + + var m = $(this).val().match(/^(\d{1,3})$/i); + var sceneAbsolute = null; + if (m) + { + sceneAbsolute = m[1]; + } + setAbsoluteSceneNumbering(forAbsolute, sceneAbsolute); + }); }); diff --git a/lib/subliminal/__init__.py b/lib/subliminal/__init__.py index 77297f5a..cb4904a5 100644 --- a/lib/subliminal/__init__.py +++ b/lib/subliminal/__init__.py @@ -30,5 +30,5 @@ except ImportError: __all__ = ['SERVICES', 'LANGUAGE_INDEX', 'SERVICE_INDEX', 'SERVICE_CONFIDENCE', - 'MATCHING_CONFIDENCE', 'list_subtitles', 'download_subtitles', 'Pool'] + 'MATCHING_CONFIDENCE', 'list_subtitles', 'download_subtitles', 'Pool', 'language'] logging.getLogger("subliminal").addHandler(NullHandler()) diff --git a/sickbeard/databases/mainDB.py b/sickbeard/databases/mainDB.py index 120651c1..dc5b3d8d 100644 --- a/sickbeard/databases/mainDB.py +++ b/sickbeard/databases/mainDB.py @@ -27,7 +27,7 @@ from sickbeard import encodingKludge as ek from sickbeard.name_parser.parser import NameParser, InvalidNameException MIN_DB_VERSION = 9 # oldest db version we support migrating from -MAX_DB_VERSION = 37 +MAX_DB_VERSION = 38 class MainSanityCheck(db.DBSanityCheck): def check(self): @@ -873,4 +873,17 @@ class AddXemRefresh(AddSceneAbsoluteNumbering): self.connection.action( "CREATE TABLE xem_refresh (indexer TEXT, indexer_id INTEGER PRIMARY KEY, last_refreshed INTEGER)") - self.incDBVersion() \ No newline at end of file + self.incDBVersion() + +class AddSceneToTvShows(AddXemRefresh): + def test(self): + return self.checkDBVersion() >= 38 + + def execute(self): + backupDatabase(38) + + logger.log(u"Adding column scene to tv_shows") + self.addColumn("tv_shows", "scene", "NUMERIC", "0") + + self.incDBVersion() + diff --git a/sickbeard/name_parser/parser.py b/sickbeard/name_parser/parser.py index 098231ad..8c15f8f6 100644 --- a/sickbeard/name_parser/parser.py +++ b/sickbeard/name_parser/parser.py @@ -345,6 +345,7 @@ class NameParser(object): return final_result + class ParseResult(object): def __init__(self, original_name, @@ -449,35 +450,29 @@ class ParseResult(object): return to_return.encode('utf-8') def convert(self): - if not self.show: return self # need show object - if self.air_by_date or self.sports: return self # scene numbering does not apply to air-by-date + if not self.show: + return self # can't convert with out a show object - # check if show is anime - if self.show.is_anime and not (len(self.episode_numbers) or self.season_number) and not len(self.ab_episode_numbers): - return self # can't work without a season - elif not self.show._is_anime and not (len(self.episode_numbers) or self.season_number): + if self.air_by_date or self.sports: # scene numbering does not apply to air-by-date or sports shows return self new_episode_numbers = [] new_season_numbers = [] new_absolute_numbers = [] - if len(self.ab_episode_numbers) and not len(self.episode_numbers): - for epAbNo in self.ab_episode_numbers: - (s, e, a) = scene_numbering.get_absolute_numbering(self.show.indexerid, self.show.indexer, epAbNo) + if self.show.is_anime and len(self.ab_episode_numbers): + for epAbsNo in self.ab_episode_numbers: + a = scene_numbering.get_indexer_absolute_numbering(self.show.indexerid, self.show.indexer, epAbsNo) - if (s or e or a): - new_episode_numbers.append(e) - new_season_numbers.append(s) - new_absolute_numbers.append(a) - else: + new_absolute_numbers.append(a) + + if self.season_number and len(self.episode_numbers): for epNo in self.episode_numbers: - (s, e, a) = scene_numbering.get_indexer_numbering(self.show.indexerid, self.show.indexer, - self.season_number, - epNo, None) + (s, e) = scene_numbering.get_indexer_numbering(self.show.indexerid, self.show.indexer, + self.season_number, + epNo) new_episode_numbers.append(e) new_season_numbers.append(s) - new_absolute_numbers.append(a) # need to do a quick sanity check here. It's possible that we now have episodes # from more than one season (by tvdb numbering), and this is just too much @@ -494,13 +489,16 @@ class ParseResult(object): new_episode_numbers = list(set(new_episode_numbers)) new_episode_numbers.sort() - # dedupe absolute numbers + # maybe even duplicate absolute numbers so why not do them as well new_absolute_numbers = list(set(new_absolute_numbers)) new_absolute_numbers.sort() - self.ab_episode_numbers = new_absolute_numbers - self.episode_numbers = new_episode_numbers - self.season_number = new_season_numbers[0] + if len(new_absolute_numbers): + self.ab_episode_numbers = new_absolute_numbers + + if len(new_season_numbers) and len(new_episode_numbers): + self.episode_numbers = new_episode_numbers + self.season_number = new_season_numbers[0] return self diff --git a/sickbeard/scene_numbering.py b/sickbeard/scene_numbering.py index 3f92034a..46bc6325 100644 --- a/sickbeard/scene_numbering.py +++ b/sickbeard/scene_numbering.py @@ -42,7 +42,7 @@ from lib import requests MAX_XEM_AGE_SECS = 86400 # 1 day -def get_scene_numbering(indexer_id, indexer, season, episode, absolute_number=None, fallback_to_xem=True): +def get_scene_numbering(indexer_id, indexer, season, episode, fallback_to_xem=True): """ Returns a tuple, (season, episode), with the scene numbering (if there is one), otherwise returns the xem numbering (if fallback_to_xem is set), otherwise @@ -56,27 +56,29 @@ def get_scene_numbering(indexer_id, indexer, season, episode, absolute_number=No @return: (int, int) a tuple with (season, episode) """ if indexer_id is None or season is None or episode is None: - return (season, episode, absolute_number) + return (season, episode) - indexer_id = int(indexer_id) - indexer = int(indexer) + showObj = sickbeard.helpers.findCertainShow(sickbeard.showList, int(indexer_id)) + if not showObj.is_scene: + return (season, episode) - result = find_scene_numbering(indexer_id, indexer, season, episode, absolute_number) + result = find_scene_numbering(int(indexer_id), int(indexer), season, episode) if result: return result else: if fallback_to_xem: - xem_result = find_xem_numbering(indexer_id, indexer, season, episode, absolute_number) + xem_result = find_xem_numbering(int(indexer_id), int(indexer), season, episode) if xem_result: return xem_result - return (season, episode, absolute_number) + return (season, episode) -def find_scene_numbering(indexer_id, indexer, season, episode, absolute_number=None): + +def find_scene_numbering(indexer_id, indexer, season, episode): """ Same as get_scene_numbering(), but returns None if scene numbering is not set """ if indexer_id is None or season is None or episode is None: - return (season, episode, absolute_number) + return (season, episode) indexer_id = int(indexer_id) indexer = int(indexer) @@ -84,13 +86,66 @@ def find_scene_numbering(indexer_id, indexer, season, episode, absolute_number=N myDB = db.DBConnection() rows = myDB.select( - "SELECT scene_season, scene_episode, scene_absolute_number FROM scene_numbering WHERE indexer = ? and indexer_id = ? and season = ? and episode = ?", + "SELECT scene_season, scene_episode FROM scene_numbering WHERE indexer = ? and indexer_id = ? and season = ? and episode = ? and (scene_season or scene_episode) != 0", [indexer, indexer_id, season, episode]) if rows: - return (int(rows[0]["scene_season"]), int(rows[0]["scene_episode"]), int(rows[0]["scene_absolute_number"])) + return (int(rows[0]["scene_season"]), int(rows[0]["scene_episode"])) -def get_indexer_numbering(indexer_id, indexer, sceneSeason, sceneEpisode, sceneAbsoluteNumber=None, fallback_to_xem=True): +def get_scene_absolute_numbering(indexer_id, indexer, absolute_number, fallback_to_xem=True): + """ + Returns a tuple, (season, episode), with the scene numbering (if there is one), + otherwise returns the xem numbering (if fallback_to_xem is set), otherwise + returns the TVDB and TVRAGE numbering. + (so the return values will always be set) + + @param indexer_id: int + @param season: int + @param episode: int + @param fallback_to_xem: bool If set (the default), check xem for matches if there is no local scene numbering + @return: (int, int) a tuple with (season, episode) + """ + if indexer_id is None or absolute_number is None: + return absolute_number + + indexer_id = int(indexer_id) + indexer = int(indexer) + + showObj = sickbeard.helpers.findCertainShow(sickbeard.showList, indexer_id) + if not showObj.is_scene: + return absolute_number + + result = find_scene_absolute_numbering(indexer_id, indexer, absolute_number) + if result: + return result + else: + if fallback_to_xem: + xem_result = find_xem_absolute_numbering(indexer_id, indexer, absolute_number) + if xem_result: + return xem_result + return absolute_number + + +def find_scene_absolute_numbering(indexer_id, indexer, absolute_number): + """ + Same as get_scene_numbering(), but returns None if scene numbering is not set + """ + if indexer_id is None or absolute_number is None: + return absolute_number + + indexer_id = int(indexer_id) + indexer = int(indexer) + + myDB = db.DBConnection() + + rows = myDB.select( + "SELECT scene_absolute_number FROM scene_numbering WHERE indexer = ? and indexer_id = ? and absolute_number = ? and scene_absolute_number != 0", + [indexer, indexer_id, absolute_number]) + if rows: + return int(rows[0]["scene_absolute_number"]) + + +def get_indexer_numbering(indexer_id, indexer, sceneSeason, sceneEpisode, fallback_to_xem=True): """ Returns a tuple, (season, episode) with the TVDB and TVRAGE numbering for (sceneSeason, sceneEpisode) (this works like the reverse of get_scene_numbering) @@ -104,22 +159,23 @@ def get_indexer_numbering(indexer_id, indexer, sceneSeason, sceneEpisode, sceneA myDB = db.DBConnection() rows = myDB.select( - "SELECT season, episode, absolute_number FROM scene_numbering WHERE indexer = ? and indexer_id = ? and scene_season = ? and scene_episode = ?", + "SELECT season, episode FROM scene_numbering WHERE indexer = ? and indexer_id = ? and scene_season = ? and scene_episode = ?", [indexer, indexer_id, sceneSeason, sceneEpisode]) if rows: - return (int(rows[0]["season"]), int(rows[0]["episode"]), int(rows[0]["absolute_number"])) + return (int(rows[0]["season"]), int(rows[0]["episode"])) else: if fallback_to_xem: - return get_indexer_numbering_for_xem(indexer_id, indexer, sceneSeason, sceneEpisode, sceneAbsoluteNumber) - return (sceneSeason, sceneEpisode, sceneAbsoluteNumber) + return get_indexer_numbering_for_xem(indexer_id, indexer, sceneSeason, sceneEpisode) + return (sceneSeason, sceneEpisode) -def get_absolute_numbering(indexer_id, indexer, sceneAbsoluteNumber, fallback_to_xem=True): + +def get_indexer_absolute_numbering(indexer_id, indexer, sceneAbsoluteNumber, fallback_to_xem=True): """ Returns a tuple, (season, episode, absolute_number) with the TVDB and TVRAGE numbering for (sceneAbsoluteNumber) (this works like the reverse of get_absolute_numbering) """ if indexer_id is None or sceneAbsoluteNumber is None: - return (None, None, None) + return sceneAbsoluteNumber indexer_id = int(indexer_id) indexer = int(indexer) @@ -127,14 +183,163 @@ def get_absolute_numbering(indexer_id, indexer, sceneAbsoluteNumber, fallback_to myDB = db.DBConnection() rows = myDB.select( - "SELECT season, episode, absolute_number FROM scene_numbering WHERE indexer = ? and indexer_id = ? and scene_absolute_number = ?", + "SELECT absolute_number FROM scene_numbering WHERE indexer = ? and indexer_id = ? and scene_absolute_number = ?", [indexer, indexer_id, sceneAbsoluteNumber]) if rows: - return (int(rows[0]["season"]), int(rows[0]["episode"]), int(rows[0]["absolute_number"])) + return int(rows[0]["absolute_number"]) else: if fallback_to_xem: - return get_absolute_numbering_for_xem(indexer_id, indexer, sceneAbsoluteNumber) - return (None, None, None) + return get_indexer_absolute_numbering_for_xem(indexer_id, indexer, sceneAbsoluteNumber) + return sceneAbsoluteNumber + + +def set_scene_numbering(indexer_id, indexer, season=None, episode=None, absolute_number=None, sceneSeason=None, sceneEpisode=None, sceneAbsolute=None): + """ + Set scene numbering for a season/episode. + To clear the scene numbering, leave both sceneSeason and sceneEpisode as None. + + """ + if indexer_id is None: + return + + indexer_id = int(indexer_id) + indexer = int(indexer) + + myDB = db.DBConnection() + + if season and episode and sceneSeason and sceneEpisode: + myDB.action( + "INSERT OR IGNORE INTO scene_numbering (indexer, indexer_id, season, episode) VALUES (?,?,?,?)", + [indexer, indexer_id, season, episode]) + + myDB.action( + "UPDATE scene_numbering SET scene_season = ?, scene_episode = ? WHERE indexer = ? and indexer_id = ? and season = ? and episode = ?", + [sceneSeason, sceneEpisode, indexer, indexer_id, season, episode]) + elif absolute_number and sceneAbsolute: + myDB.action( + "INSERT OR IGNORE INTO scene_numbering (indexer, indexer_id, absolute_number) VALUES (?,?,?)", + [indexer, indexer_id, absolute_number]) + + myDB.action( + "UPDATE scene_numbering SET scene_absolute_number = ? WHERE indexer = ? and indexer_id = ? and absolute_number = ?", + [sceneAbsolute, indexer, indexer_id, absolute_number]) + + +def find_xem_numbering(indexer_id, indexer, season, episode): + """ + Returns the scene numbering, as retrieved from xem. + Refreshes/Loads as needed. + + @param indexer_id: int + @param season: int + @param episode: int + @return: (int, int) a tuple of scene_season, scene_episode, or None if there is no special mapping. + """ + if indexer_id is None or season is None or episode is None: + return (season, episode) + + indexer_id = int(indexer_id) + indexer = int(indexer) + + if xem_refresh_needed(indexer_id, indexer): + xem_refresh(indexer_id, indexer) + + myDB = db.DBConnection() + + rows = myDB.select( + "SELECT scene_season, scene_episode FROM tv_episodes WHERE indexer = ? and showid = ? and season = ? and episode = ? and (scene_season or scene_episode) != 0", + [indexer, indexer_id, season, episode]) + + if rows: + return (int(rows[0]["scene_season"]), int(rows[0]["scene_episode"])) + + +def find_xem_absolute_numbering(indexer_id, indexer, absolute_number): + """ + Returns the scene numbering, as retrieved from xem. + Refreshes/Loads as needed. + + @param indexer_id: int + @param season: int + @param episode: int + @return: (int, int) a tuple of scene_season, scene_episode, or None if there is no special mapping. + """ + if indexer_id is None or absolute_number is None: + return absolute_number + + indexer_id = int(indexer_id) + indexer = int(indexer) + + if xem_refresh_needed(indexer_id, indexer): + xem_refresh(indexer_id, indexer) + + myDB = db.DBConnection() + + rows = myDB.select( + "SELECT scene_absolute_number FROM tv_episodes WHERE indexer = ? and showid = ? and absolute_number = ? and scene_absolute_number != 0", + [indexer, indexer_id, absolute_number]) + + if rows: + return int(rows[0]["scene_absolute_number"]) + + +def get_indexer_numbering_for_xem(indexer_id, indexer, sceneSeason, sceneEpisode): + """ + Reverse of find_xem_numbering: lookup a tvdb season and episode using scene numbering + + @param indexer_id: int + @param sceneSeason: int + @param sceneEpisode: int + @return: (int, int) a tuple of (season, episode) + """ + if indexer_id is None or sceneSeason is None or sceneEpisode is None: + return (sceneSeason, sceneEpisode) + + indexer_id = int(indexer_id) + indexer = int(indexer) + + if xem_refresh_needed(indexer_id, indexer): + xem_refresh(indexer_id, indexer) + + myDB = db.DBConnection() + + rows = myDB.select( + "SELECT season, episode FROM tv_episodes WHERE indexer = ? and showid = ? and scene_season = ? and scene_episode = ?", + [indexer, indexer_id, sceneSeason, sceneEpisode]) + if rows: + return (int(rows[0]["season"]), int(rows[0]["episode"])) + + return (sceneSeason, sceneEpisode) + + +def get_indexer_absolute_numbering_for_xem(indexer_id, indexer, sceneAbsoluteNumber): + """ + Reverse of find_xem_numbering: lookup a tvdb season and episode using scene numbering + + @param indexer_id: int + @param sceneSeason: int + @param sceneEpisode: int + @return: (int, int) a tuple of (season, episode) + """ + if indexer_id is None or sceneAbsoluteNumber is None: + return sceneAbsoluteNumber + + indexer_id = int(indexer_id) + indexer = int(indexer) + + if xem_refresh_needed(indexer_id, indexer): + xem_refresh(indexer_id, indexer) + + myDB = db.DBConnection() + + rows = myDB.select( + "SELECT absolute_number FROM tv_episodes WHERE indexer = ? and showid = ? and scene_absolute_number = ?", + [indexer, indexer_id, sceneAbsoluteNumber]) + if rows: + return int(rows[0]["absolute_number"]) + + return sceneAbsoluteNumber + def get_scene_numbering_for_show(indexer_id, indexer): """ @@ -151,7 +356,7 @@ def get_scene_numbering_for_show(indexer_id, indexer): myDB = db.DBConnection() rows = myDB.select( - 'SELECT season, episode, absolute_number, scene_season, scene_episode, scene_absolute_number FROM scene_numbering WHERE indexer = ? and indexer_id = ? ORDER BY season, episode', + 'SELECT season, episode, scene_season, scene_episode FROM scene_numbering WHERE indexer = ? and indexer_id = ? and (scene_season or scene_episode) != 0 ORDER BY season, episode', [indexer, indexer_id]) result = {} @@ -160,55 +365,20 @@ def get_scene_numbering_for_show(indexer_id, indexer): episode = int(row['episode']) scene_season = int(row['scene_season']) scene_episode = int(row['scene_episode']) - scene_absolute_number = int(row['scene_absolute_number']) - result[(season, episode)] = (scene_season, scene_episode, scene_absolute_number) + result[(season, episode)] = (scene_season, scene_episode) return result -def set_scene_numbering(indexer_id, indexer, season, episode, absolute_number, sceneSeason=None, sceneEpisode=None, - sceneAbsoluteNumber=None): +def get_xem_numbering_for_show(indexer_id, indexer): """ - Set scene numbering for a season/episode. - To clear the scene numbering, leave both sceneSeason and sceneEpisode as None. - + Returns a dict of (season, episode) : (sceneSeason, sceneEpisode) mappings + for an entire show. Both the keys and values of the dict are tuples. + Will be empty if there are no scene numbers set in xem """ - if indexer_id is None or season is None or episode is None: - return - - indexer_id = int(indexer_id) - indexer = int(indexer) - - myDB = db.DBConnection() - - # sanity - # if sceneSeason == None: sceneSeason = season - #if sceneEpisode == None: sceneEpisode = episode - - # delete any existing record first - myDB.action('DELETE FROM scene_numbering where indexer = ? and indexer_id = ? and season = ? and episode = ?', - [indexer, indexer_id, season, episode]) - - # now, if the new numbering is not the default, we save a new record - if sceneSeason is not None and sceneEpisode is not None: - myDB.action( - "INSERT INTO scene_numbering (indexer, indexer_id, season, episode, absolute_number, scene_season, scene_episode, scene_absolute_number) VALUES (?,?,?,?,?,?,?,?)", - [indexer, indexer_id, season, episode, absolute_number, sceneSeason, sceneEpisode, sceneAbsoluteNumber]) - - -def find_xem_numbering(indexer_id, indexer, season, episode, absolute_number): - """ - Returns the scene numbering, as retrieved from xem. - Refreshes/Loads as needed. - - @param indexer_id: int - @param season: int - @param episode: int - @return: (int, int) a tuple of scene_season, scene_episode, or None if there is no special mapping. - """ - if indexer_id is None or season is None or episode is None: - return (season, episode, absolute_number) + if indexer_id is None: + return {} indexer_id = int(indexer_id) indexer = int(indexer) @@ -219,23 +389,57 @@ def find_xem_numbering(indexer_id, indexer, season, episode, absolute_number): myDB = db.DBConnection() rows = myDB.select( - "SELECT scene_season, scene_episode, scene_absolute_number FROM tv_episodes WHERE indexer = ? and showid = ? and season = ? and episode = ?", - [indexer, indexer_id, season, episode]) + 'SELECT season, episode, scene_season, scene_episode FROM tv_episodes WHERE indexer = ? and showid = ? and (scene_season or scene_episode) != 0 ORDER BY season, episode', + [indexer, indexer_id]) - if rows: - return (int(rows[0]["scene_season"]), int(rows[0]["scene_episode"]), int(rows[0]["scene_absolute_number"])) + result = {} + for row in rows: + season = int(row['season']) + episode = int(row['episode']) + scene_season = int(row['scene_season']) + scene_episode = int(row['scene_episode']) -def get_indexer_numbering_for_xem(indexer_id, indexer, sceneSeason, sceneEpisode, sceneAbsoluteNumber): + result[(season, episode)] = (scene_season, scene_episode) + + return result + + +def get_scene_absolute_numbering_for_show(indexer_id, indexer): """ - Reverse of find_xem_numbering: lookup a tvdb season and episode using scene numbering - - @param indexer_id: int - @param sceneSeason: int - @param sceneEpisode: int - @return: (int, int) a tuple of (season, episode) + Returns a dict of (season, episode) : (sceneSeason, sceneEpisode) mappings + for an entire show. Both the keys and values of the dict are tuples. + Will be empty if there are no scene numbers set """ - if indexer_id is None or sceneSeason is None or sceneEpisode is None: - return None + if indexer_id is None: + return {} + + indexer_id = int(indexer_id) + indexer = int(indexer) + + myDB = db.DBConnection() + + rows = myDB.select( + 'SELECT absolute_number, scene_absolute_number FROM scene_numbering WHERE indexer = ? and indexer_id = ? and scene_absolute_number != 0 ORDER BY absolute_number', + [indexer, indexer_id]) + + result = {} + for row in rows: + absolute_number = int(row['absolute_number']) + scene_absolute_number = int(row['scene_absolute_number']) + + result[absolute_number] = scene_absolute_number + + return result + + +def get_xem_absolute_numbering_for_show(indexer_id, indexer): + """ + Returns a dict of (season, episode) : (sceneSeason, sceneEpisode) mappings + for an entire show. Both the keys and values of the dict are tuples. + Will be empty if there are no scene numbers set in xem + """ + if indexer_id is None: + return {} indexer_id = int(indexer_id) indexer = int(indexer) @@ -245,41 +449,19 @@ def get_indexer_numbering_for_xem(indexer_id, indexer, sceneSeason, sceneEpisode myDB = db.DBConnection() + result = {} rows = myDB.select( - "SELECT season, episode, absolute_number FROM tv_episodes WHERE indexer = ? and showid = ? and scene_season = ? and scene_episode = ?", - [indexer, indexer_id, sceneSeason, sceneEpisode]) - if rows: - return (int(rows[0]["season"]), int(rows[0]["episode"]), int(rows[0]["absolute_number"])) + 'SELECT absolute_number, scene_absolute_number FROM tv_episodes WHERE indexer = ? and showid = ? and scene_absolute_number != 0 ORDER BY absolute_number', + [indexer, indexer_id]) - return (sceneSeason, sceneEpisode, sceneAbsoluteNumber) + for row in rows: + absolute_number = int(row['absolute_number']) + scene_absolute_number = int(row['scene_absolute_number']) -def get_absolute_numbering_for_xem(indexer_id, indexer, sceneAbsoluteNumber): - """ - Reverse of find_xem_numbering: lookup a tvdb season and episode using scene numbering + result[absolute_number] = scene_absolute_number - @param indexer_id: int - @param sceneSeason: int - @param sceneEpisode: int - @return: (int, int) a tuple of (season, episode) - """ - if indexer_id is None or sceneAbsoluteNumber is None: - return None + return result - indexer_id = int(indexer_id) - indexer = int(indexer) - - if xem_refresh_needed(indexer_id, indexer): - xem_refresh(indexer_id, indexer) - - myDB = db.DBConnection() - - rows = myDB.select( - "SELECT season, episode, absolute_number FROM tv_episodes WHERE indexer = ? and showid = ? and scene_absolute_number = ?", - [indexer, indexer_id, sceneAbsoluteNumber]) - if rows: - return (int(rows[0]["season"]), int(rows[0]["episode"]), int(rows[0]["absolute_number"])) - - return (None, None, None) def xem_refresh_needed(indexer_id, indexer): """ @@ -296,7 +478,7 @@ def xem_refresh_needed(indexer_id, indexer): myDB = db.DBConnection() rows = myDB.select("SELECT last_refreshed FROM xem_refresh WHERE indexer = ? and indexer_id = ?", - [indexer, indexer_id]) + [indexer, indexer_id]) if rows: return time.time() > (int(rows[0]['last_refreshed']) + MAX_XEM_AGE_SECS) else: @@ -376,41 +558,9 @@ def xem_refresh(indexer_id, indexer): if ql: myDB.mass_action(ql) - # fix xem scene numbering issues - #fix_xem_numbering(indexer_id, indexer) + # fix xem scene numbering issues + # fix_xem_numbering(indexer_id, indexer) -def get_xem_numbering_for_show(indexer_id, indexer): - """ - Returns a dict of (season, episode) : (sceneSeason, sceneEpisode) mappings - for an entire show. Both the keys and values of the dict are tuples. - Will be empty if there are no scene numbers set in xem - """ - if indexer_id is None: - return {} - - indexer_id = int(indexer_id) - indexer = int(indexer) - - if xem_refresh_needed(indexer_id, indexer): - xem_refresh(indexer_id, indexer) - - myDB = db.DBConnection() - - rows = myDB.select( - 'SELECT season, episode, absolute_number, scene_season, scene_episode, scene_absolute_number FROM tv_episodes WHERE indexer = ? and showid = ? ORDER BY season, episode', - [indexer, indexer_id]) - - result = {} - for row in rows: - season = int(row['season'] or 0) - episode = int(row['episode'] or 0) - scene_season = int(row['scene_season'] or 0) - scene_episode = int(row['scene_episode'] or 0) - scene_absolute_number = int(row['scene_absolute_number'] or 0) - - result[(season, episode)] = (scene_season, scene_episode, scene_absolute_number) - - return result def fix_xem_numbering(indexer_id, indexer): """ @@ -425,7 +575,7 @@ def fix_xem_numbering(indexer_id, indexer): indexer = int(indexer) # query = [{ - # "name": self.show.name, + # "name": self.show.name, # "seasons": [{ # "episodes": [{ # "episode_number": None, diff --git a/sickbeard/show_queue.py b/sickbeard/show_queue.py index 897f1fdc..68706f29 100644 --- a/sickbeard/show_queue.py +++ b/sickbeard/show_queue.py @@ -296,6 +296,8 @@ class QueueItemAdd(ShowQueueItem): self.show.sports = 1 if self.show.genre and "animation" in self.show.genre.lower(): self.show.anime = 1 + if sickbeard.scene_numbering.get_xem_numbering_for_show(self.show.indexerid, self.show.indexer): + self.show.scene = 1 except sickbeard.indexer_exception, e: logger.log( @@ -395,12 +397,15 @@ class QueueItemAdd(ShowQueueItem): class QueueItemRefresh(ShowQueueItem): - def __init__(self, show=None): + def __init__(self, show=None, force=False): ShowQueueItem.__init__(self, ShowQueueActions.REFRESH, show) # do refreshes first because they're quick self.priority = generic_queue.QueuePriorities.HIGH + # force refresh certain items + self.force = force + def execute(self): ShowQueueItem.execute(self) @@ -408,6 +413,8 @@ class QueueItemRefresh(ShowQueueItem): self.show.refreshDir() self.show.writeMetadata() + if self.force: + self.show.updateMetadata() self.show.populateCache() self.inProgress = False @@ -421,9 +428,6 @@ class QueueItemRename(ShowQueueItem): ShowQueueItem.execute(self) - # Load XEM data to DB for show - sickbeard.scene_numbering.xem_refresh(self.show.indexerid, self.show.indexer) - logger.log(u"Performing rename on " + self.show.name) try: @@ -483,7 +487,8 @@ class QueueItemUpdate(ShowQueueItem): logger.log(u"Beginning update of " + self.show.name) # Load XEM data to DB for show - sickbeard.scene_numbering.xem_refresh(self.show.indexerid, self.show.indexer) + if sickbeard.scene_numbering.xem_refresh_needed(self.show.indexerid, self.show.indexer) or self.force: + sickbeard.scene_numbering.xem_refresh(self.show.indexerid, self.show.indexer) logger.log(u"Retrieving show info from " + sickbeard.indexerApi(self.show.indexer).name + "", logger.DEBUG) try: @@ -550,7 +555,7 @@ class QueueItemUpdate(ShowQueueItem): except exceptions.EpisodeDeletedException: pass - sickbeard.showQueueScheduler.action.refreshShow(self.show, True) + sickbeard.showQueueScheduler.action.refreshShow(self.show, self.force) class QueueItemForceUpdate(QueueItemUpdate): def __init__(self, show=None): diff --git a/sickbeard/tv.py b/sickbeard/tv.py index 9ce902cb..c972021d 100644 --- a/sickbeard/tv.py +++ b/sickbeard/tv.py @@ -83,6 +83,7 @@ class TVShow(object): self.lang = lang self.last_update_indexer = 1 self.anime = 0 + self.scene = 0 self.rls_ignore_words = "" self.rls_require_words = "" @@ -114,6 +115,14 @@ class TVShow(object): is_sports = property(_is_sports) + def _is_scene(self): + if (self.scene > 0): + return True + else: + return False + + is_scene = property(_is_scene) + def _getLocation(self): # no dir check needed if missing show dirs are created during post-processing if sickbeard.CREATE_MISSING_SHOW_DIRS: @@ -186,7 +195,7 @@ class TVShow(object): return ep_list - def getEpisode(self, season, episode, file=None, noCreate=False, absolute_number=None): + def getEpisode(self, season=None, episode=None, file=None, noCreate=False, absolute_number=None): # Load XEM data to DB for show if sickbeard.scene_numbering.xem_refresh_needed(self.indexerid, self.indexer): @@ -198,7 +207,7 @@ class TVShow(object): ep = None # if we get an anime get the real season and episode - if self.anime and absolute_number != None and season == None and episode == None: + if self.is_anime and not self.is_scene and absolute_number != None and season == None and episode == None: myDB = db.DBConnection() sql = "SELECT * FROM tv_episodes WHERE showid = ? and absolute_number = ? and season != 0" sqlResults = myDB.select(sql, [self.indexerid, absolute_number]) @@ -236,6 +245,15 @@ class TVShow(object): epObj = self.episodes[season][episode] + # get scene absolute numbering + epObj.scene_absolute_number = sickbeard.scene_numbering.get_scene_absolute_numbering(self.indexerid, + self.indexer, + epObj.absolute_number) + + # get scene season and episode numbering + (epObj.scene_season, epObj.scene_episode) = sickbeard.scene_numbering.get_scene_numbering(self.indexerid, + self.indexer, + season, episode) return epObj def should_update(self, update_date=datetime.date.today()): @@ -738,10 +756,18 @@ class TVShow(object): if not self.air_by_date: self.air_by_date = 0 + self.anime = sqlResults[0]["anime"] + if self.anime == None: + self.anime = 0 + self.sports = sqlResults[0]["sports"] if not self.sports: self.sports = 0 + self.scene = sqlResults[0]["scene"] + if not self.scene: + self.scene = 0 + self.subtitles = sqlResults[0]["subtitles"] if self.subtitles: self.subtitles = 1 @@ -765,10 +791,6 @@ class TVShow(object): if not self.lang: self.lang = sqlResults[0]["lang"] - self.anime = sqlResults[0]["anime"] - if self.anime == None: - self.anime = 0 - self.last_update_indexer = sqlResults[0]["last_update_indexer"] self.rls_ignore_words = sqlResults[0]["rls_ignore_words"] @@ -905,7 +927,6 @@ class TVShow(object): logger.log(str(self.indexerid) + u": Obtained info from IMDb ->" + str(self.imdb_info), logger.DEBUG) def nextEpisode(self): - logger.log(str(self.indexerid) + ": Finding the episode which airs next", logger.DEBUG) myDB = db.DBConnection() @@ -1085,13 +1106,14 @@ class TVShow(object): "flatten_folders": self.flatten_folders, "paused": self.paused, "air_by_date": self.air_by_date, + "anime": self.anime, + "scene": self.scene, "sports": self.sports, "subtitles": self.subtitles, "dvdorder": self.dvdorder, "archive_firstmatch": self.archive_firstmatch, "startyear": self.startyear, "lang": self.lang, - "anime": self.anime, "imdb_id": self.imdbid, "last_update_indexer": self.last_update_indexer, "rls_ignore_words": self.rls_ignore_words, @@ -1124,6 +1146,8 @@ class TVShow(object): toReturn += "classification: " + self.classification + "\n" toReturn += "runtime: " + str(self.runtime) + "\n" toReturn += "quality: " + str(self.quality) + "\n" + toReturn += "scene: " + str(self.is_scene) + "\n" + toReturn += "sports: " + str(self.is_sports) + "\n" toReturn += "anime: " + str(self.is_anime) + "\n" return toReturn diff --git a/sickbeard/webserve.py b/sickbeard/webserve.py index ac3abf22..8a9efe9d 100644 --- a/sickbeard/webserve.py +++ b/sickbeard/webserve.py @@ -26,7 +26,6 @@ import re import threading import datetime import random -import logging from Cheetah.Template import Template import cherrypy @@ -46,8 +45,6 @@ from sickbeard import image_cache from sickbeard import naming from sickbeard import scene_exceptions from sickbeard import subtitles -from sickbeard import failed_history -from sickbeard import failedProcessor from sickbeard import network_timezones from sickbeard.providers import newznab, rsstorrent @@ -58,12 +55,13 @@ from sickbeard.exceptions import ex from sickbeard.webapi import Api from sickbeard.scene_exceptions import get_scene_exceptions from sickbeard.scene_numbering import get_scene_numbering, set_scene_numbering, get_scene_numbering_for_show, \ - get_xem_numbering_for_show + get_xem_numbering_for_show, get_scene_absolute_numbering_for_show, get_xem_absolute_numbering_for_show, \ + get_scene_absolute_numbering from sickbeard.blackandwhitelist import BlackAndWhiteList from lib.dateutil import tz -from lib.unrar2 import RarFile, RarInfo +from lib.unrar2 import RarFile from lib import subliminal @@ -163,16 +161,24 @@ def _genericMessage(subject, message): return _munge(t) -def _getEpisode(show, season, episode): - if show is None or season is None or episode is None: - return "Invalid parameters" +def _getEpisode(show, season=None, episode=None, absolute=None): + if show is None: + return "Invalid show parameters" showObj = sickbeard.helpers.findCertainShow(sickbeard.showList, int(show)) if showObj is None: return "Show not in show list" - epObj = showObj.getEpisode(int(season), int(episode)) + if showObj.is_anime and not absolute is None: + return "Invalid absolute number parameters" + elif season is None or episode is None: + return "Invalid season or episode number parameters" + + if showObj.is_anime: + epObj = showObj.getEpisode(absolute_number=int(absolute)) + else: + epObj = showObj.getEpisode(int(season), int(episode)) if epObj is None: return "Episode couldn't be retrieved" @@ -485,7 +491,7 @@ class Manage: to_download[cur_indexer_id] = [str(x["season"]) + 'x' + str(x["episode"]) for x in all_eps_results] for epResult in to_download[cur_indexer_id]: - season, episode = epResult.split('x'); + season, episode = epResult.split('x') show = sickbeard.helpers.findCertainShow(sickbeard.showList, int(cur_indexer_id)) subtitles = show.getEpisode(int(season), int(episode)).downloadSubtitles() @@ -866,7 +872,7 @@ class History: myDB = db.DBConnection() - # sqlResults = myDB.select("SELECT h.*, show_name, name FROM history h, tv_shows s, tv_episodes e WHERE h.showid=s.indexer_id AND h.showid=e.showid AND h.season=e.season AND h.episode=e.episode ORDER BY date DESC LIMIT "+str(numPerPage*(p-1))+", "+str(numPerPage)) + #sqlResults = myDB.select("SELECT h.*, show_name, name FROM history h, tv_shows s, tv_episodes e WHERE h.showid=s.indexer_id AND h.showid=e.showid AND h.season=e.season AND h.episode=e.episode ORDER BY date DESC LIMIT "+str(numPerPage*(p-1))+", "+str(numPerPage)) if limit == "0": sqlResults = myDB.select( "SELECT h.*, show_name FROM history h, tv_shows s WHERE h.showid=s.indexer_id ORDER BY date DESC") @@ -3058,6 +3064,8 @@ class Home: t.all_scene_exceptions = get_scene_exceptions(indexerid) t.scene_numbering = get_scene_numbering_for_show(indexerid, indexer) t.xem_numbering = get_xem_numbering_for_show(indexerid, indexer) + t.scene_absolute_numbering = get_scene_absolute_numbering_for_show(indexerid, indexer) + t.xem_absolute_numbering = get_xem_absolute_numbering_for_show(indexerid, indexer) return _munge(t) @@ -3085,7 +3093,8 @@ class Home: def editShow(self, show=None, location=None, anyQualities=[], bestQualities=[], exceptions_list=[], flatten_folders=None, paused=None, directCall=False, air_by_date=None, sports=None, dvdorder=None, indexerLang=None, subtitles=None, archive_firstmatch=None, rls_ignore_words=None, - rls_require_words=None, anime=None, blackWords=None, whiteWords=None, blacklist=None, whitelist=None): + rls_require_words=None, anime=None, blackWords=None, whiteWords=None, blacklist=None, whitelist=None, + scene=None): if show is None: errString = "Invalid show ID: " + str(show) @@ -3146,6 +3155,7 @@ class Home: archive_firstmatch = config.checkbox_to_value(archive_firstmatch) paused = config.checkbox_to_value(paused) air_by_date = config.checkbox_to_value(air_by_date) + scene = config.checkbox_to_value(scene) sports = config.checkbox_to_value(sports) anime = config.checkbox_to_value(anime) subtitles = config.checkbox_to_value(subtitles) @@ -3243,6 +3253,7 @@ class Home: # if this routine was called via the mass edit, do not change the options that are not passed if not directCall: showObj.air_by_date = air_by_date + showObj.scene = scene showObj.sports = sports showObj.anime = anime showObj.subtitles = subtitles @@ -3663,7 +3674,7 @@ class Home: # try do download subtitles for that episode previous_subtitles = ep_obj.subtitles try: - subtitles = ep_obj.downloadSubtitles() + ep_obj.subtitles = ep_obj.downloadSubtitles() except: return json.dumps({'result': 'failure'}) @@ -3679,23 +3690,49 @@ class Home: return json.dumps({'result': status, 'subtitles': ','.join([x for x in ep_obj.subtitles])}) @cherrypy.expose - def setEpisodeSceneNumbering(self, show, indexer, forSeason, forEpisode, sceneSeason=None, sceneEpisode=None): + def setSceneNumbering(self, show, indexer, forSeason=None, forEpisode=None, forAbsolute=None, sceneSeason=None, sceneEpisode=None, sceneAbsolute=None): # sanitize: + if forSeason in ['null', '']: forSeason = None + if forEpisode in ['null', '']: forEpisode = None + if forAbsolute in ['null', '']: forAbsolute = None if sceneSeason in ['null', '']: sceneSeason = None if sceneEpisode in ['null', '']: sceneEpisode = None + if sceneAbsolute in ['null', '']: sceneAbsolute = None - result = { - 'success': True, - 'forSeason': forSeason, - 'forEpisode': forEpisode, - } + showObj = sickbeard.helpers.findCertainShow(sickbeard.showList, int(show)) + + if showObj.is_anime: + result = { + 'success': True, + 'forAbsolute': forAbsolute, + } + else: + result = { + 'success': True, + 'forSeason': forSeason, + 'forEpisode': forEpisode, + } # retrieve the episode object and fail if we can't get one - ep_obj = _getEpisode(show, forSeason, forEpisode) + if showObj.is_anime: + ep_obj = _getEpisode(show, absolute=forAbsolute) + else: + ep_obj = _getEpisode(show, forSeason, forEpisode) + if isinstance(ep_obj, str): result['success'] = False result['errorMessage'] = ep_obj + elif showObj.is_anime: + logger.log(u"setAbsoluteSceneNumbering for %s from %s to %s" % + (show, forAbsolute, sceneAbsolute), logger.DEBUG) + + show = int(show) + indexer = int(indexer) + forAbsolute = int(forAbsolute) + if sceneAbsolute is not None: sceneAbsolute = int(sceneAbsolute) + + set_scene_numbering(show, indexer, absolute_number=forAbsolute, sceneAbsolute=sceneAbsolute) else: logger.log(u"setEpisodeSceneNumbering for %s from %sx%s to %sx%s" % (show, forSeason, forEpisode, sceneSeason, sceneEpisode), logger.DEBUG) @@ -3707,13 +3744,20 @@ class Home: if sceneSeason is not None: sceneSeason = int(sceneSeason) if sceneEpisode is not None: sceneEpisode = int(sceneEpisode) - set_scene_numbering(show, indexer, forSeason, forEpisode, sceneSeason, sceneEpisode) + set_scene_numbering(show, indexer, season=forSeason, episode=forEpisode, sceneSeason=sceneSeason, sceneEpisode=sceneEpisode) - sn = get_scene_numbering(show, indexer, forSeason, forEpisode) - if sn: - (result['sceneSeason'], result['sceneEpisode']) = sn + if showObj.is_anime: + sn = get_scene_absolute_numbering(show, indexer, forAbsolute) + if sn: + result['sceneAbsolute'] = sn + else: + result['sceneAbsolute'] = None else: - (result['sceneSeason'], result['sceneEpisode']) = (None, None) + sn = get_scene_numbering(show, indexer, forSeason, forEpisode) + if sn: + (result['sceneSeason'], result['sceneEpisode']) = sn + else: + (result['sceneSeason'], result['sceneEpisode']) = (None, None) return json.dumps(result)