# # This file is part of SickGear. # # SickGear is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # SickGear is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with SickGear. If not, see . # # Created on Sep 20, 2012 # @author: Dermot Buckley # @copyright: Dermot Buckley # import datetime import traceback from sqlite3 import Row from exceptions_helper import ex import sickgear from . import db, logger from .helpers import try_int from .scene_exceptions import xem_ids_list from .sgdatetime import SGDatetime # noinspection PyUnreachableCode if False: from typing import Dict, List, Optional, Tuple, Union from six import integer_types def get_scene_numbering(tvid, prodid, season, episode, fallback_to_xem=True, show_obj=None, **kwargs): """ 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 numbering. (so the return values will always be set) kwargs['scene_result']: type: Optional[List[Row]] passed through kwargs['show_result']: type: Optional[List[Row]] passed through :param tvid: tvid :type tvid: int :param prodid: prodid :type prodid: int or long :param season: :type season: int :param episode: :type episode: int :param fallback_to_xem: If set (the default), check xem for matches if there is no local scene numbering :type fallback_to_xem: bool :param show_obj: :type show_obj: :return: (int, int) a tuple with (season, episode) :rtype: Tuple[int, int] """ if None is prodid or None is season or None is episode: return season, episode tvid, prodid = int(tvid), int(prodid) if None is show_obj: show_obj = sickgear.helpers.find_show_by_id({tvid: prodid}) if show_obj and not show_obj.is_scene: return season, episode result = find_scene_numbering(tvid, prodid, season, episode, scene_result=kwargs.get('scene_result')) if result: return result else: if fallback_to_xem: xem_result = find_xem_numbering(tvid, prodid, season, episode, show_result=kwargs.get('show_result')) if xem_result: return xem_result return season, episode def find_scene_numbering(tvid, prodid, season, episode, scene_result=None): """ Same as get_scene_numbering(), but returns None if scene numbering is not set :param tvid: tvid :type tvid: int :param prodid: prodid :type prodid: int or long :param season: season number :type season: int :param episode: episode number :type episode: int :param scene_result: :type scene_result: :return: :rtype: Tuple[int, int] or None """ if None is prodid or None is season or None is episode: return tvid, prodid = int(tvid), int(prodid) sql_result = None if None is not scene_result: for cur_row in scene_result: if cur_row['season'] == season and cur_row['episode'] == episode: if cur_row['scene_season'] or cur_row['scene_episode']: sql_result = [cur_row] break else: my_db = db.DBConnection() sql_result = my_db.select( """ SELECT scene_season, scene_episode FROM scene_numbering WHERE indexer = ? AND indexer_id = ? AND season = ? AND episode = ? AND (scene_season OR scene_episode) != 0 """, [tvid, prodid, season, episode]) if sql_result: s_s, s_e = try_int(sql_result[0]['scene_season'], None), try_int(sql_result[0]['scene_episode'], None) if None is not s_s and None is not s_e: return s_s, s_e def get_scene_absolute_numbering(tvid, prodid, absolute_number, season, episode, fallback_to_xem=True, show_obj=None, **kwargs): """ 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 numbering. (so the return values will always be set) kwargs['scene_result']: type: Optional[List[Row]] passed through kwargs['show_result']: type: Optional[List[Row]] passed through :param tvid: tvid :type tvid: int :param prodid: prodid :type prodid: int or long :param absolute_number: absolute number :type absolute_number: int :param season: season number :type season: int :param episode: episode number :type episode: int :param fallback_to_xem: fallback_to_xem: bool If set (the default), check xem for matches if there is no local scene numbering :type fallback_to_xem: bool :param show_obj: :type show_obj: :return: (int, int) a tuple with (season, episode) :rtype: Tuple[int, int] or None """ has_sxe = None is not season and None is not episode if None is prodid or (None is absolute_number and not has_sxe): return absolute_number tvid, prodid = int(tvid), int(prodid) if None is show_obj: show_obj = sickgear.helpers.find_show_by_id({tvid: prodid}) if show_obj and not show_obj.is_scene and not has_sxe: return absolute_number result = find_scene_absolute_numbering(tvid, prodid, absolute_number, season, episode, scene_result=kwargs.get('scene_result')) if result: return result if fallback_to_xem: xem_result = find_xem_absolute_numbering(tvid, prodid, absolute_number, season, episode, show_result=kwargs.get('show_result')) if xem_result: return xem_result return absolute_number def find_scene_absolute_numbering(tvid, prodid, absolute_number, season=None, episode=None, scene_result=None): """ Same as get_scene_numbering(), but returns None if scene numbering is not set :param tvid: :type tvid: int :param prodid: :type prodid: int or long :param absolute_number: :type absolute_number: int :param season: :type season: int :param episode: :type episode: int :param scene_result: :type scene_result: :return: :rtype: None or int """ has_sxe = None is not season and None is not episode if None is prodid or (None is absolute_number and not has_sxe): return tvid, prodid = int(tvid), int(prodid) sql_result = None if None is not scene_result: for cur_row in scene_result: if cur_row['season'] == season and cur_row['episode'] == episode: if cur_row['scene_absolute_number']: sql_result = [cur_row] break else: my_db = db.DBConnection() sql_vars, cond = (([absolute_number], 'absolute_number = ?'), ([season, episode], 'season = ? AND episode = ?'))[has_sxe] sql_result = my_db.select( """ SELECT scene_absolute_number FROM scene_numbering WHERE indexer = ? AND indexer_id = ? AND %s AND scene_absolute_number != 0 """ % cond, [tvid, prodid] + sql_vars) if sql_result: return try_int(sql_result[0]['scene_absolute_number'], None) def get_indexer_numbering(tvid, prodid, scene_season, scene_episode, fallback_to_xem=True, return_multiple=False): # type: (int, integer_types, int, int, bool, bool) -> Union[Tuple[Optional[int], Optional[int]], List[Tuple[Optional[int], Optional[int]]]] """ :param tvid: tvid :type tvid: int :param prodid: prodid :type prodid: int or long :param scene_season: scene season :type scene_season: int :param scene_episode: scene episode :type scene_episode: int :param fallback_to_xem: :type fallback_to_xem: bool :param return_multiple: :return: a tuple, (season, episode) with the TVDB numbering for (sceneSeason, sceneEpisode) (this works like the reverse of get_scene_numbering) :rtype: Tuple[int, int] """ if None is prodid or None is scene_season or None is scene_episode: return scene_season, scene_episode tvid, prodid = int(tvid), int(prodid) my_db = db.DBConnection() sql_result = my_db.select( """ SELECT season, episode FROM scene_numbering WHERE indexer = ? AND indexer_id = ? AND scene_season = ? AND scene_episode = ? """, [tvid, prodid, scene_season, scene_episode]) if sql_result: if return_multiple and 1 < len(sql_result): return [(try_int(_s['season'], None), try_int(_s['episode'], None)) for _s in sql_result] ss, se = try_int(sql_result[0]['season'], None), try_int(sql_result[0]['episode'], None) if None is not ss and None is not se: return ss, se if fallback_to_xem: return get_indexer_numbering_for_xem(tvid, prodid, scene_season, scene_episode, return_multiple=return_multiple) return scene_season, scene_episode def get_indexer_absolute_numbering(tvid, prodid, scene_absolute_number, fallback_to_xem=True, scene_season=None): """ Returns a tuple, (season, episode, absolute_number) with the TVDB numbering for (sceneAbsoluteNumber) (this works like the reverse of get_absolute_numbering) :param tvid: :type tvid: int :param prodid: :type prodid: int or long :param scene_absolute_number: :type scene_absolute_number: int :param fallback_to_xem: :type fallback_to_xem: bool :param scene_season: :type scene_season: int :return: :rtype: int """ if None is prodid or None is scene_absolute_number: return scene_absolute_number tvid, prodid = int(tvid), int(prodid) my_db = db.DBConnection() sql = """ SELECT absolute_number FROM scene_numbering WHERE indexer = ? AND indexer_id = ? AND scene_absolute_number = ? """ params = [tvid, prodid, scene_absolute_number] if None is not scene_season: sql += ' AND scene_season = ?' params += [scene_season] for cur_row in (my_db.select(sql, params) or []): an = try_int(cur_row['absolute_number'], None) if None is not an: return an if fallback_to_xem: return get_indexer_absolute_numbering_for_xem(tvid, prodid, scene_absolute_number, scene_season) return scene_absolute_number def set_scene_numbering(tvid=None, prodid=None, season=None, episode=None, absolute_number=None, scene_season=None, scene_episode=None, scene_absolute=None, anime=False): """ Set scene numbering for a season/episode. To clear the scene numbering, leave both sceneSeason and sceneEpisode as None. :param tvid: :type tvid: int :param prodid: :type prodid: int :param season: :type season: int :param episode: :type episode: int :param absolute_number: :type absolute_number: int :param scene_season: :type scene_season: int :param scene_episode: :type scene_episode: int :param scene_absolute: :type scene_absolute: int :param anime: :type anime: bool """ if None is tvid or None is prodid: return my_db = db.DBConnection() if None is not season and None is not episode: my_db.action( """ INSERT OR IGNORE INTO scene_numbering (indexer, indexer_id, season, episode) VALUES (?,?,?,?) """, [tvid, prodid, season, episode]) # sxe replaced abs_num as key, migrate data with only abs _, _, ep_absolute_number = _get_sea(tvid, prodid, season, episode) sql_result = my_db.select( """ SELECT scene_season, scene_episode, scene_absolute_number FROM scene_numbering WHERE indexer = ? AND indexer_id = ? AND season IS NULL AND episode IS NULL AND absolute_number = ? """, [tvid, prodid, ep_absolute_number]) if not len(sql_result): update, values = (('scene_absolute_number = ?', [scene_absolute]), ('scene_season = ?, scene_episode = ?', [scene_season, scene_episode]))[not anime] else: for cur_row in sql_result: scene_season = scene_season or cur_row['scene_season'] scene_episode = scene_episode or cur_row['scene_episode'] scene_absolute = scene_absolute or cur_row['scene_absolute_number'] update, values = ('scene_season = ?, scene_episode = ?, scene_absolute_number = ?', [scene_season, scene_episode, scene_absolute]) my_db.action( """ UPDATE scene_numbering SET %s WHERE indexer = ? AND indexer_id = ? AND season = ? AND episode = ? """ % update, values + [tvid, prodid, season, episode]) my_db.action( """ DELETE FROM scene_numbering WHERE indexer = ? AND indexer_id = ? AND ((absolute_number = ? OR (season = ? AND episode = ?)) AND scene_season IS NULL AND scene_episode IS NULL AND scene_absolute_number IS NULL) """, [tvid, prodid, ep_absolute_number, season, episode]) elif absolute_number: my_db.action( """ INSERT OR IGNORE INTO scene_numbering (indexer, indexer_id, absolute_number) VALUES (?,?,?) """, [tvid, prodid, absolute_number]) my_db.action( """ UPDATE scene_numbering SET scene_absolute_number = ? WHERE indexer = ? AND indexer_id = ? AND absolute_number = ? """, [scene_absolute, tvid, prodid, absolute_number]) def find_xem_numbering(tvid, prodid, season, episode, show_result=None): """ Returns the scene numbering, as retrieved from xem. Refreshes/Loads as needed. :param tvid: :type tvid: int :param prodid: :type prodid: int :param season: :type season: int :param episode: :type episode: int :param show_result: :type show_result: :return: :rtype: (int, int) a tuple of scene_season, scene_episode, or None if there is no special mapping. """ if None is prodid or None is season or None is episode: return season, episode tvid, prodid = int(tvid), int(prodid) xem_refresh(tvid, prodid) sql_result = None if None is not show_result: if isinstance(show_result, Row) and (season, episode) == (show_result['season'], show_result['episode']) \ and (show_result['scene_season'] or show_result['scene_episode']): sql_result = [show_result] else: my_db = db.DBConnection() sql_result = my_db.select( """ SELECT scene_season, scene_episode FROM tv_episodes WHERE indexer = ? AND showid = ? AND season = ? AND episode = ? AND (scene_season OR scene_episode) != 0 """, [tvid, prodid, season, episode]) if sql_result: s_s, s_e = try_int(sql_result[0]['scene_season'], None), try_int(sql_result[0]['scene_episode'], None) if None is not s_s and None is not s_e: return s_s, s_e def find_xem_absolute_numbering(tvid, prodid, absolute_number, season, episode, show_result=None): """ Returns the scene numbering, as retrieved from xem. Refreshes/Loads as needed. :param tvid: :type tvid: int :param prodid: :type prodid: int :param absolute_number: :type absolute_number: int :param season: :type season: int :param episode: :type episode: int :param show_result: :type show_result: :return: :rtype: int """ if None is prodid or None is absolute_number: return absolute_number tvid, prodid = int(tvid), int(prodid) xem_refresh(tvid, prodid) sql_result = None if None is not show_result: if isinstance(show_result, Row) and (season, episode) == (show_result['season'], show_result['episode']) \ and show_result['scene_absolute_number']: sql_result = [show_result] else: my_db = db.DBConnection() sql_result = my_db.select( """ SELECT scene_absolute_number FROM tv_episodes WHERE indexer = ? AND showid = ? AND season = ? AND episode = ? AND scene_absolute_number != 0 """, [tvid, prodid, season, episode]) if sql_result: return try_int(sql_result[0]['scene_absolute_number'], None) def get_indexer_numbering_for_xem(tvid, prodid, scene_season, scene_episode, return_multiple=False): # type: (int, int, int, int, bool) -> Union[Tuple[Optional[int], Optional[int]], List[Tuple[Optional[int], Optional[int]]]] """ Reverse of find_xem_numbering: lookup a tvdb season and episode using scene numbering :param tvid: :type tvid: int :param prodid: :type prodid: int :param scene_season: :type scene_season: int :param scene_episode: :type scene_episode: int :param return_multiple: :return: :rtype: (int, int) a tuple of (season, episode) """ if None is prodid or None is scene_season or None is scene_episode: return scene_season, scene_episode tvid, prodid = int(tvid), int(prodid) xem_refresh(tvid, prodid) my_db = db.DBConnection() sql_result = my_db.select( """ SELECT season, episode FROM tv_episodes WHERE indexer = ? AND showid = ? AND scene_season = ? AND scene_episode = ? """, [tvid, prodid, scene_season, scene_episode]) if return_multiple and 1 < len(sql_result or []): return [(try_int(_s['season'], None), try_int(_s['episode'], None)) for _s in sql_result] for cur_row in (sql_result or []): ss, se = try_int(cur_row['season'], None), try_int(cur_row['episode'], None) if None is not ss and None is not se: return ss, se break return scene_season, scene_episode def get_indexer_absolute_numbering_for_xem(tvid, prodid, scene_absolute_number, scene_season=None): """ Reverse of find_xem_numbering: lookup a tvdb season and episode using scene numbering :param tvid: :type tvid: int :param prodid: :type prodid: int :param scene_absolute_number: :type scene_absolute_number: int :param scene_season: :type scene_season: int :return: :rtype: int """ if None is prodid or None is scene_absolute_number: return scene_absolute_number tvid, prodid = int(tvid), int(prodid) xem_refresh(tvid, prodid) my_db = db.DBConnection() sql = """ SELECT absolute_number FROM tv_episodes WHERE indexer = ? AND showid = ? AND scene_absolute_number = ? """ params = [tvid, prodid, scene_absolute_number] if None is not scene_season: sql += ' AND scene_season = ?' params += [scene_season] for cur_row in (my_db.select(sql, params) or []): an = try_int(cur_row['absolute_number'], None) if None is not an: return an break return scene_absolute_number def get_scene_numbering_for_show(tvid, prodid): """ Returns a dict of (season, episode) : (scene_season, scene_episode) mappings for an entire show. Both the keys and values of the dict are tuples. Will be empty if no scene numbers are set :param tvid: tvid :type tvid: int :param prodid: prodid :type prodid: ing or long :return: :rtype: Dict """ return _get_numbering_for_show('scene_numbering', tvid, prodid) def has_xem_scene_mapping(tvid, prodid): """ Test if a scene mapping exists for a show at XEM :param tvid: :type tvid: int :param prodid: :type prodid: int :return: True if scene mapping exists, False if not :rtype: Bool """ return bool(get_xem_numbering_for_show(tvid, prodid)) def get_xem_numbering_for_show(tvid, prodid): """ Returns a dict of (season, episode) : (scene_season, scene_episode) mappings for an entire show. Both the keys and values of the dict are tuples. Will be empty if no scene numbers are set in xem :param tvid: tvid :type tvid: int :param prodid: prodid :type prodid: int or long :return: :rtype: Dict """ return _get_numbering_for_show('tv_episodes', tvid, prodid) def _get_numbering_for_show(tbl, tvid, prodid): """ :param tbl: table :type tbl: AnyStr :param tvid: tvid :type tvid: int :param prodid: prodid :type prodid: int or long :return: :rtype: Dict """ result = {} if None is not prodid: if 'tv_episodes' == tbl: xem_refresh(tvid, prodid) my_db = db.DBConnection() # noinspection SqlResolve sql_result = my_db.select( """ SELECT season, episode, scene_season, scene_episode FROM %s WHERE indexer = ? AND %s = ? AND (scene_season OR scene_episode) != 0 ORDER BY season, episode """ % (tbl, ('indexer_id', 'showid')['tv_episodes' == tbl]), [int(tvid), int(prodid)]) for cur_row in sql_result: season, episode = try_int(cur_row['season'], None), try_int(cur_row['episode'], None) if None is not season and None is not episode: scene_season, scene_episode = try_int(cur_row['scene_season'], None), \ try_int(cur_row['scene_episode'], None) if None is not scene_season and None is not scene_episode: result[(season, episode)] = (scene_season, scene_episode) return result def get_scene_absolute_numbering_for_show(tvid, prodid): """ Returns a dict of (season, episode) : scene_absolute_number mappings for an entire show. Will be empty if no scene numbers are set :param tvid: tvid :type tvid: int :param prodid: prodid :type prodid: int or long :return: :rtype: Dict """ return _get_absolute_numbering_for_show('scene_numbering', tvid, prodid) def get_xem_absolute_numbering_for_show(tvid, prodid): """ Returns a dict of (season, episode) : scene_absolute_number mappings for an entire show. Will be empty if no scene numbers are set in xem :param tvid: tvid :type tvid: int :param prodid: prodid :type prodid: int or long :return: :rtype: Dict """ return _get_absolute_numbering_for_show('tv_episodes', tvid, prodid) def _get_absolute_numbering_for_show(tbl, tvid, prodid): """ :param tbl: table name :type tbl: AnyStr :param tvid: tvid :type tvid: int :param prodid: prodid :type prodid: int or long :return: :rtype: Dict """ result = {} if None is not prodid: if 'tv_episodes' == tbl: xem_refresh(tvid, prodid) my_db = db.DBConnection() # noinspection SqlResolve sql_result = my_db.select( """ SELECT season, episode, absolute_number, scene_absolute_number FROM %s WHERE indexer = ? AND %s = ? AND scene_absolute_number != 0 ORDER BY season, episode """ % (tbl, ('indexer_id', 'showid')['tv_episodes' == tbl]), [int(tvid), int(prodid)]) for cur_row in sql_result: season, episode, abs_num = list(map(lambda x: try_int(cur_row[x], None), ('season', 'episode', 'absolute_number'))) if None is season and None is episode and None is not abs_num: season, episode, _ = _get_sea(tvid, prodid, absolute_number=abs_num) if None is not season and None is not episode: scene_absolute_number = try_int(cur_row['scene_absolute_number'], None) if None is not scene_absolute_number: result[(season, episode)] = scene_absolute_number return result def _get_sea(tvid, prodid, season=None, episode=None, absolute_number=None): """ :param tvid: tvid :type tvid: int :param prodid: prodid :type prodid: int or long :param season: season number :type season: int or None :param episode: episode number :type episode: int or None :param absolute_number: absolute number :type absolute_number: int or None :return: :rtype: Tuple[int, int, int] """ show_obj = sickgear.helpers.find_show_by_id({tvid: prodid}, no_mapped_ids=True) if show_obj: ep_obj = None if None is not absolute_number: ep_obj = show_obj.get_episode(absolute_number=absolute_number) elif None is not season and None is not episode: ep_obj = show_obj.get_episode(season, episode) if None is not ep_obj: season, episode, absolute_number = ep_obj.season, ep_obj.episode, ep_obj.absolute_number return season, episode, absolute_number def xem_refresh(tvid, prodid, force=False): """ Refresh data from xem for a tv show :param tvid: :type tvid: int :param prodid: :type prodid: int :param force: :type force: bool """ if None is prodid: return tvid, prodid = int(tvid), int(prodid) tvinfo = sickgear.TVInfoAPI(tvid) if 'xem_origin' not in tvinfo.config or prodid not in xem_ids_list.get(tvid, []): return xem_origin = tvinfo.config['xem_origin'] # XEM API URL # noinspection HttpUrlsUsage url = 'http://thexem.info/map/all?id=%s&origin=%s&destination=scene' % (prodid, xem_origin) max_refresh_age_secs = 86400 # 1 day my_db = db.DBConnection() sql_result = my_db.select( """ SELECT last_refreshed FROM xem_refresh WHERE indexer = ? AND indexer_id = ? """, [tvid, prodid]) if sql_result: last_refresh = int(sql_result[0]['last_refreshed']) refresh = SGDatetime.timestamp_near() > last_refresh + max_refresh_age_secs else: refresh = True if refresh or force: logger.debug(f'Looking up XEM scene mapping for show {prodid} on {tvinfo.name}') # mark refreshed my_db.upsert('xem_refresh', dict(last_refreshed=SGDatetime.timestamp_near()), dict(indexer=tvid, indexer_id=prodid)) try: parsed_json = sickgear.helpers.get_url(url, parse_json=True, timeout=90) if not parsed_json or '' == parsed_json: logger.log(f'No XEM data for show {prodid} on {tvinfo.name}', logger.MESSAGE) return if 'success' in parsed_json['result']: cl = list(map(lambda entry: [ """ UPDATE tv_episodes SET scene_season = ?, scene_episode = ?, scene_absolute_number = ? WHERE indexer = ? AND showid = ? AND season = ? AND episode = ? """, [entry.get('scene%s' % ('', '_2')['scene_2' in entry]).get(v) for v in ('season', 'episode', 'absolute')] + [tvid, prodid] + [entry.get(xem_origin).get(v) for v in ('season', 'episode')] ], filter(lambda x: 'scene' in x, parsed_json['data']))) if 0 < len(cl): my_db = db.DBConnection() my_db.mass_action(cl) else: logger.debug(f'Empty lookup result - no XEM data for show {prodid} on {tvinfo.name}') except (BaseException, Exception) as e: logger.warning(f'Exception refreshing XEM data for show {str(prodid)} on {tvinfo.name}: {ex(e)}') logger.error(traceback.format_exc()) def fix_xem_numbering(tvid, prodid): """ :param tvid: tvid :type tvid: int :param prodid: prodid :type prodid: int or long """ if None is prodid: return {} tvid, prodid = int(tvid), int(prodid) my_db = db.DBConnection() sql_result = my_db.select( """ SELECT season, episode, absolute_number, scene_season, scene_episode, scene_absolute_number FROM tv_episodes WHERE indexer = ? AND showid = ? """, [tvid, prodid]) last_absolute_number = None last_scene_season = None last_scene_episode = None last_scene_absolute_number = None update_absolute_number = False update_scene_season = False update_scene_episode = False update_scene_absolute_number = False logger.debug(f'Fixing any XEM scene mapping issues for show {prodid} on {sickgear.TVInfoAPI(tvid).name}') cl = [] for cur_row in sql_result: season = int(cur_row['season']) episode = int(cur_row['episode']) if not int(cur_row['scene_season']) and last_scene_season: scene_season = last_scene_season + 1 update_scene_season = True else: scene_season = int(cur_row['scene_season']) if last_scene_season and scene_season < last_scene_season: scene_season = last_scene_season + 1 update_scene_season = True if not int(cur_row['scene_episode']) and last_scene_episode: scene_episode = last_scene_episode + 1 update_scene_episode = True else: scene_episode = int(cur_row['scene_episode']) if last_scene_episode and scene_episode < last_scene_episode: scene_episode = last_scene_episode + 1 update_scene_episode = True # check for unset values and correct them if not int(cur_row['absolute_number']) and last_absolute_number: absolute_number = last_absolute_number + 1 update_absolute_number = True else: absolute_number = int(cur_row['absolute_number']) if last_absolute_number and absolute_number < last_absolute_number: absolute_number = last_absolute_number + 1 update_absolute_number = True if not int(cur_row['scene_absolute_number']) and last_scene_absolute_number: scene_absolute_number = last_scene_absolute_number + 1 update_scene_absolute_number = True else: scene_absolute_number = int(cur_row['scene_absolute_number']) if last_scene_absolute_number and scene_absolute_number < last_scene_absolute_number: scene_absolute_number = last_scene_absolute_number + 1 update_scene_absolute_number = True # store values for lookup on next iteration last_absolute_number = absolute_number last_scene_season = scene_season last_scene_episode = scene_episode last_scene_absolute_number = scene_absolute_number if update_absolute_number: cl.append([ """ UPDATE tv_episodes SET absolute_number = ? WHERE indexer = ? AND showid = ? AND season = ? AND episode = ? """, [absolute_number, tvid, prodid, season, episode] ]) update_absolute_number = False if update_scene_season: cl.append([ """ UPDATE tv_episodes SET scene_season = ? WHERE indexer = ? AND showid = ? AND season = ? AND episode = ? """, [scene_season, tvid, prodid, season, episode] ]) update_scene_season = False if update_scene_episode: cl.append([ """ UPDATE tv_episodes SET scene_episode = ? WHERE indexer = ? AND showid = ? AND season = ? AND episode = ? """, [scene_episode, tvid, prodid, season, episode] ]) update_scene_episode = False if update_scene_absolute_number: cl.append([ """ UPDATE tv_episodes SET scene_absolute_number = ? WHERE indexer = ? AND showid = ? AND season = ? AND episode = ? """, [scene_absolute_number, tvid, prodid, season, episode] ]) update_scene_absolute_number = False if 0 < len(cl): my_db = db.DBConnection() my_db.mass_action(cl) def set_scene_numbering_helper(tvid, prodid, for_season=None, for_episode=None, for_absolute=None, scene_season=None, scene_episode=None, scene_absolute=None): """ :param tvid: tvid :type tvid: int :param prodid: prodid :type prodid: int or long :param for_season: :type for_season: int or None :param for_episode: :type for_episode: int or None :param for_absolute: :type for_absolute: int or None :param scene_season: :type scene_season: int or None :param scene_episode: :type scene_episode: int or None :param scene_absolute: :type scene_absolute: int or None :return: :rtype: Dict[AnyStr, int] """ # sanitize: tvid = None if tvid in [None, 'null', ''] else int(tvid) prodid = None if prodid in [None, 'null', ''] else int(prodid) show_obj = sickgear.helpers.find_show_by_id({tvid: prodid}, no_mapped_ids=True) if not show_obj: return {'success': False} for_season = None if for_season in [None, 'null', ''] else int(for_season) for_episode = None if for_episode in [None, 'null', ''] else int(for_episode) ep_args = {'show': prodid, 'season': for_season, 'episode': for_episode} scene_args = {'tvid': tvid, 'prodid': prodid, 'season': for_season, 'episode': for_episode} if not show_obj.is_anime: scene_season = None if scene_season in [None, 'null', ''] else int(scene_season) scene_episode = None if scene_episode in [None, 'null', ''] else int(scene_episode) action_log = f'Set episode scene numbering to {scene_season}x{scene_episode}' \ f' for episode {for_season}x{for_episode} of "{show_obj.unique_name}"' scene_args.update({'scene_season': scene_season, 'scene_episode': scene_episode}) result = {'forSeason': for_season, 'forEpisode': for_episode, 'sceneSeason': None, 'sceneEpisode': None} else: for_absolute = None if for_absolute in [None, 'null', ''] else int(for_absolute) scene_absolute = None if scene_absolute in [None, 'null', ''] else int(scene_absolute) action_log = f'Set absolute scene numbering to {scene_absolute}' \ f' for episode {for_season}x{for_episode} of "{show_obj.unique_name}"' ep_args.update({'absolute': for_absolute}) scene_args.update({'absolute_number': for_absolute, 'scene_absolute': scene_absolute, 'anime': True}) result = {'forAbsolute': for_absolute, 'sceneAbsolute': None} if ep_args.get('absolute'): ep_obj = show_obj.get_episode(absolute_number=int(ep_args['absolute'])) elif None is not ep_args['season'] and None is not ep_args['episode']: ep_obj = show_obj.get_episode(int(ep_args['season']), int(ep_args['episode'])) else: ep_obj = 'Invalid parameters' result['success'] = None is not ep_obj and not isinstance(ep_obj, str) if result['success']: logger.debug(action_log) set_scene_numbering(**scene_args) show_obj.flush_episodes() if not show_obj.is_anime: if (None is scene_season and None is scene_episode) or (0 == scene_season and 0 == scene_episode): # when clearing the field, do not return existing values of sxe, otherwise this may be confusing # with the case where manually setting sxe to the actual sxe is done to prevent a data overwrite. # So now the only instance an actual sxe is in the field is if user enters it, else 0x0 is presented. return result elif None is scene_absolute or 0 == scene_absolute: return result else: result['errorMessage'] = "Episode couldn't be retrieved, invalid parameters" if not show_obj.is_anime: scene_numbering = get_scene_numbering(tvid, prodid, for_season, for_episode, show_obj=show_obj) if scene_numbering: (result['sceneSeason'], result['sceneEpisode']) = scene_numbering else: scene_numbering = get_scene_absolute_numbering(tvid, prodid, for_absolute, for_season, for_episode, show_obj=show_obj) if scene_numbering: result['sceneAbsolute'] = scene_numbering return result