mirror of
https://github.com/SickGear/SickGear.git
synced 2024-12-23 19:23:37 +00:00
32987134ba
Cleanup most init warnings. Cleanup some vars, pythonic instead of js. Some typos and python var/func names for Scheduler. Remove legacy handlers deprecated in 2020. Remove some legacy tagged stuff. Cleanup ConfigParser and 23.py Change cleanup vendored scandir. Remove redundant pkg_resources.py in favour of the vendor folder. Remove backports. Remove trakt checker. Change remove redundant WindowsSelectorEventLoopPolicy from webserveInit. Cleanup varnames and providers Various minor tidy ups to remove ide warnings.
1050 lines
37 KiB
Python
1050 lines
37 KiB
Python
#
|
|
# 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 <http://www.gnu.org/licenses/>.
|
|
#
|
|
# Created on Sep 20, 2012
|
|
# @author: Dermot Buckley <dermot@buckley.ie>
|
|
# @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 timestamp_near
|
|
|
|
# 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 = int(timestamp_near(datetime.datetime.now())) > last_refresh + max_refresh_age_secs
|
|
else:
|
|
refresh = True
|
|
|
|
if refresh or force:
|
|
logger.log(u'Looking up XEM scene mapping for show %s on %s' % (prodid, tvinfo.name), logger.DEBUG)
|
|
|
|
# mark refreshed
|
|
my_db.upsert('xem_refresh',
|
|
dict(last_refreshed=int(timestamp_near(datetime.datetime.now()))),
|
|
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(u'No XEM data for show %s on %s' % (prodid, 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.log(u'Empty lookup result - no XEM data for show %s on %s' % (prodid, tvinfo.name), logger.DEBUG)
|
|
except (BaseException, Exception) as e:
|
|
logger.log(u'Exception refreshing XEM data for show ' + str(prodid) + ' on ' + tvinfo.name + ': ' + ex(e),
|
|
logger.WARNING)
|
|
logger.log(traceback.format_exc(), logger.ERROR)
|
|
|
|
|
|
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.log(
|
|
u'Fixing any XEM scene mapping issues for show %s on %s' % (prodid, sickgear.TVInfoAPI(tvid).name),
|
|
logger.DEBUG)
|
|
|
|
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 = u'Set episode scene numbering to %sx%s for episode %sx%s of "%s"' \
|
|
% (scene_season, scene_episode, for_season, for_episode, 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 = u'Set absolute scene numbering to %s for episode %sx%s of "%s"' \
|
|
% (scene_absolute, for_season, for_episode, 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.log(action_log, logger.DEBUG)
|
|
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
|