mirror of
https://github.com/SickGear/SickGear.git
synced 2024-11-25 14:25:05 +00:00
fce8878fa9
Add top rated, popular, trending today, trending this week, to TMDB cards. Change Shows/Add show... Trakt and IMDb card descriptions to be dynamic. Add network data to trakt cards. Add fallback for show cast when no suitable items are found in tvdb_api. Change refactor for trakt lib update. Change sanitise api response dates for cards (an invalid trakt date caused a UI glitch). Fix issue where trakt can return no tvdb id causing a card to not display. Change view-show TVDb genre links to use anon link setting. Change remove displayShow.tmpl code for '|' sep as it is a fixed list string at data source. Change add TVINFO_FANSITE placeholder icon. Change refactor api sg.`CMD_SickGearShowsBrowseTrakt`. --- Refactor date handling to make it's usage consistent from indexerapi Split when_past out to to started_past and return_past for UI. Fix trakt returning dates. Change bring properly into use, the proper new season returning date. Change remove PY2 unused import statements. --- Fix issue with Trakt cards where an incorrect cache image obscures the default card placeholder image. Simplify airtime for cards. Remove nonsense TMDB api overview text. Rearrange cards order on top menu. --- Remove unused vars and simplify regex use cases. Fix tv cards issue with Trakt and Tvmaze where show_info does not carry a first episode_info. Instantiate with TVInfoEpisode() instead of None, so that expected properties exist without a need for Nonetype tests. Change simplify `overview` and `airtime` use after lib output type is made trustable. Change cards view to not allow items with no seriesname.
1807 lines
77 KiB
Python
1807 lines
77 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/>.
|
|
|
|
import datetime
|
|
import os
|
|
import traceback
|
|
|
|
from lib.dateutil.parser import parser
|
|
from lib.tvinfo_base.exceptions import *
|
|
|
|
import exceptions_helper
|
|
from exceptions_helper import ex
|
|
|
|
import sickgear
|
|
from . import logger, ui, db, generic_queue, name_cache
|
|
from .anime import AniGroupList
|
|
from .common import SKIPPED, WANTED, UNAIRED, Quality, statusStrings
|
|
from .helpers import should_delete_episode, find_show_by_id
|
|
from .indexermapper import clean_show_name, map_indexers_to_show
|
|
from .indexers.indexer_config import TVINFO_TVDB, TVINFO_TVRAGE
|
|
from .name_parser.parser import NameParser
|
|
from .tv import TVidProdid, TVShow, TVSWITCH_DUPLICATE_SHOW, TVSWITCH_EP_DELETED, TVSWITCH_ID_CONFLICT, \
|
|
TVSWITCH_NO_NEW_ID, TVSWITCH_NORMAL, TVSWITCH_NOT_FOUND_ERROR, TVSWITCH_SAME_ID, TVSWITCH_SOURCE_NOT_FOUND_ERROR, \
|
|
TVSWITCH_VERIFY_ERROR
|
|
|
|
from six import integer_types, iteritems, itervalues
|
|
from sg_helpers import try_int
|
|
|
|
# noinspection PyUnreachableCode
|
|
if False:
|
|
from typing import Any, AnyStr, Dict, List, Optional, Tuple, Union
|
|
from lib.tvinfo_base import TVInfoShow
|
|
from .tv import TVEpisode
|
|
|
|
# Define special priority of tv source switch tasks, higher than anything else except newly added shows
|
|
SWITCH_PRIO = generic_queue.QueuePriorities.HIGH + 5
|
|
|
|
DAILY_SHOW_UPDATE_FINISHED_EVENT = 1
|
|
|
|
|
|
def bool_none(val):
|
|
# type: (Optional[int]) -> Optional[bool]
|
|
if None is val:
|
|
return None
|
|
return bool(val)
|
|
|
|
|
|
class ShowQueue(generic_queue.GenericQueue):
|
|
def __init__(self):
|
|
generic_queue.GenericQueue.__init__(self, cache_db_tables=['show_queue'], main_db_tables=['tv_src_switch'])
|
|
self.queue_name = 'SHOWQUEUE'
|
|
self.daily_update_running = False
|
|
if not db.DBConnection().has_flag('kodi_nfo_uid'):
|
|
self.add_event(DAILY_SHOW_UPDATE_FINISHED_EVENT, sickgear.metadata.kodi.set_nfo_uid_updated)
|
|
|
|
def check_events(self):
|
|
if self.daily_update_running and \
|
|
not (self.is_show_update_running() or sickgear.update_show_scheduler.is_running_job):
|
|
self.execute_events(DAILY_SHOW_UPDATE_FINISHED_EVENT)
|
|
self.daily_update_running = False
|
|
|
|
def load_queue(self):
|
|
try:
|
|
my_db = db.DBConnection('cache.db')
|
|
sql_result = my_db.select('SELECT * FROM show_queue')
|
|
for cur_row in sql_result:
|
|
show_obj = None
|
|
if ShowQueueActions.ADD != cur_row['action_id']:
|
|
try:
|
|
show_obj = find_show_by_id({cur_row['tvid']: cur_row['prodid']})
|
|
except (BaseException, Exception):
|
|
continue
|
|
if not show_obj:
|
|
continue
|
|
|
|
if cur_row['action_id'] in (ShowQueueActions.UPDATE, ShowQueueActions.FORCEUPDATE,
|
|
ShowQueueActions.WEBFORCEUPDATE):
|
|
self.update_show(add_to_db=False, force=bool(cur_row['force']),
|
|
pausestatus_after=bool_none(cur_row['pausestatus_after']),
|
|
scheduled_update=bool(cur_row['scheduled_update']),
|
|
show_obj=show_obj, skip_refresh=bool(cur_row['skip_refresh']),
|
|
uid=cur_row['uid'],
|
|
web=ShowQueueActions.WEBFORCEUPDATE == cur_row['action_id'])
|
|
|
|
elif ShowQueueActions.REFRESH == cur_row['action_id']:
|
|
self.refresh_show(add_to_db=False, force=bool(cur_row['force']),
|
|
force_image_cache=bool(cur_row['force_image_cache']),
|
|
priority=cur_row['priority'],
|
|
scheduled_update=bool(cur_row['scheduled_update']),
|
|
show_obj=show_obj,
|
|
uid=cur_row['uid'])
|
|
|
|
elif ShowQueueActions.RENAME == cur_row['action_id']:
|
|
self.rename_show_episodes(add_to_db=False, show_obj=show_obj, uid=cur_row['uid'])
|
|
|
|
elif ShowQueueActions.SUBTITLE == cur_row['action_id']:
|
|
self.download_subtitles(add_to_db=False, show_obj=show_obj, uid=cur_row['uid'])
|
|
|
|
elif ShowQueueActions.ADD == cur_row['action_id']:
|
|
self.add_show(
|
|
tvid=cur_row['tvid'], prodid=cur_row['prodid'], show_dir=cur_row['show_dir'],
|
|
quality=cur_row['quality'], upgrade_once=bool(cur_row['upgrade_once']),
|
|
wanted_begin=cur_row['wanted_begin'], wanted_latest=cur_row['wanted_latest'],
|
|
tag=cur_row['tag'],
|
|
paused=bool_none(cur_row['paused']), prune=cur_row['prune'],
|
|
default_status=cur_row['default_status'], scene=bool_none(cur_row['scene']),
|
|
subtitles=cur_row['subtitles'],
|
|
flatten_folders=bool_none(cur_row['flatten_folders']), anime=bool_none(cur_row['anime']),
|
|
blocklist=cur_row['blocklist'], allowlist=cur_row['allowlist'],
|
|
show_name=cur_row['show_name'], new_show=bool(cur_row['new_show']),
|
|
lang=cur_row['lang'], uid=cur_row['uid'], add_to_db=False)
|
|
|
|
except (BaseException, Exception) as e:
|
|
logger.error('Exception loading queue %s: %s' % (self.__class__.__name__, ex(e)))
|
|
|
|
def save_item(self, item):
|
|
# type: (ShowQueueItem) -> None
|
|
if ShowQueueActions.SWITCH == item.action_id:
|
|
my_db = db.DBConnection()
|
|
my_db.action(
|
|
"""
|
|
REPLACE INTO tv_src_switch (old_indexer, old_indexer_id, new_indexer, new_indexer_id,
|
|
action_id, status, uid, mark_wanted, set_pause, force_id)
|
|
VALUES (?,?,?,?,?,?,?,?,?,?)
|
|
""", [item.show_obj.tvid, item.show_obj.prodid, item.new_tvid, item.new_prodid,
|
|
ShowQueueActions.SWITCH, TVSWITCH_NORMAL, item.uid, int(item.mark_wanted), int(item.set_pause),
|
|
int(item.force_id)])
|
|
else:
|
|
generic_queue.GenericQueue.save_item(self, item)
|
|
|
|
def _clear_sql(self):
|
|
# noinspection SqlWithoutWhere
|
|
return [['DELETE FROM show_queue']]
|
|
|
|
def _get_item_sql(self, item):
|
|
# type: (ShowQueueItem) -> List[List]
|
|
if isinstance(item, QueueItemUpdate):
|
|
if item.switch:
|
|
return []
|
|
pause_status_after = item.kwargs.get('pausestatus_after')
|
|
if None is not pause_status_after:
|
|
pause_status_after = int(pause_status_after)
|
|
return [[
|
|
"""
|
|
INSERT OR IGNORE INTO show_queue (tvid, prodid, priority, force,
|
|
scheduled_update, skip_refresh, pausestatus_after,
|
|
action_id, uid) VALUES (?,?,?,?,?,?,?,?,?)
|
|
""", [item.show_obj.tvid, item.show_obj.prodid, item.priority, int(item.force),
|
|
int(item.scheduled_update), int(item.kwargs.get('skip_refresh', False)), pause_status_after,
|
|
item.action_id, item.uid]
|
|
]]
|
|
elif isinstance(item, QueueItemRefresh):
|
|
if item.switch:
|
|
return []
|
|
pause_status_after = item.kwargs.get('pausestatus_after')
|
|
if None is not pause_status_after:
|
|
pause_status_after = int(pause_status_after)
|
|
return [[
|
|
"""
|
|
INSERT OR IGNORE INTO show_queue (tvid, prodid, priority, force,
|
|
scheduled_update, force_image_cache, pausestatus_after,
|
|
action_id, uid) VALUES (?,?,?,?,?,?,?,?,?)
|
|
""", [item.show_obj.tvid, item.show_obj.prodid, item.priority, int(item.force),
|
|
int(item.scheduled_update), int(item.force_image_cache), pause_status_after,
|
|
item.action_id, item.uid]
|
|
]]
|
|
elif isinstance(item, QueueItemRename):
|
|
return [[
|
|
"""
|
|
INSERT OR IGNORE INTO show_queue (tvid, prodid, priority, scheduled_update, action_id, uid)
|
|
VALUES (?,?,?,?,?,?)
|
|
""", [item.show_obj.tvid, item.show_obj.prodid, item.priority, int(item.scheduled_update),
|
|
item.action_id, item.uid]
|
|
]]
|
|
elif isinstance(item, QueueItemSubtitle):
|
|
return [[
|
|
"""
|
|
INSERT OR IGNORE INTO show_queue (tvid, prodid, priority, scheduled_update, action_id, uid)
|
|
VALUES (?,?,?,?,?,?)
|
|
""", [item.show_obj.tvid, item.show_obj.prodid, item.priority, int(item.scheduled_update),
|
|
item.action_id, item.uid]
|
|
]]
|
|
elif isinstance(item, QueueItemAdd):
|
|
return [[
|
|
"""
|
|
INSERT OR IGNORE INTO show_queue (tvid, prodid, priority, scheduled_update,
|
|
show_dir, default_status, quality, flatten_folders, lang,
|
|
subtitles, anime, scene, paused,
|
|
blocklist, allowlist, wanted_begin, wanted_latest,
|
|
prune, tag, new_show, show_name, upgrade_once,
|
|
action_id, uid) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)
|
|
""", [item.tvid, item.prodid, item.priority, int(item.scheduled_update),
|
|
item.showDir, item.default_status, item.quality, try_int(item.flatten_folders, None), item.lang,
|
|
item.subtitles, try_int(item.anime, None), try_int(item.scene, None), try_int(item.paused, None),
|
|
item.blocklist, item.allowlist, item.default_wanted_begin, item.default_wanted_latest,
|
|
item.prune, item.tag, int(item.new_show), item.show_name, int(item.upgrade_once),
|
|
item.action_id, item.uid]
|
|
]]
|
|
return []
|
|
|
|
def delete_item(self, item, finished_run=False):
|
|
# type: (ShowQueueItem, bool) -> None
|
|
if isinstance(item, QueueItemSwitchSource):
|
|
try:
|
|
my_db = db.DBConnection()
|
|
if finished_run:
|
|
my_db.action('DELETE FROM tv_src_switch WHERE uid = ? AND status = ?', [item.uid, TVSWITCH_NORMAL])
|
|
else:
|
|
my_db.action('DELETE FROM tv_src_switch WHERE uid = ?', [item.uid])
|
|
except (BaseException, Exception) as e:
|
|
logger.error('Exception deleting item %s from db: %s' % (item, ex(e)))
|
|
else:
|
|
generic_queue.GenericQueue.delete_item(self, item)
|
|
|
|
def _delete_item_from_db_sql(self, item):
|
|
# type: (ShowQueueItem) -> List[List]
|
|
return [
|
|
['DELETE FROM show_queue WHERE uid = ?', [item.uid]]
|
|
]
|
|
|
|
def _clear_queue(self, action_types=None, excluded_types=None):
|
|
# type: (integer_types, List[integer_types]) -> None
|
|
generic_queue.GenericQueue._clear_queue(self, action_types=action_types)
|
|
|
|
def remove_from_queue(self, to_remove=None, force=False):
|
|
# type: (List[integer_types], bool) -> None
|
|
generic_queue.GenericQueue._remove_from_queue(self, to_remove=to_remove, force=force)
|
|
|
|
def _is_in_queue(self, show_obj, actions):
|
|
# type: (TVShow, Tuple[integer_types, ...]) -> bool
|
|
"""
|
|
|
|
:param show_obj: show object
|
|
:param actions:
|
|
:return:
|
|
"""
|
|
with self.lock:
|
|
return any(1 for x in self.queue if x.action_id in actions and show_obj == x.show_obj)
|
|
|
|
def _is_being_somethinged(self, show_obj, actions):
|
|
# type: (TVShow, Tuple[integer_types, ...]) -> bool
|
|
"""
|
|
|
|
:param show_obj: show object
|
|
:type show_obj: sickgear.tv.TVShow
|
|
:param actions:
|
|
:return:
|
|
:rtype: bool
|
|
"""
|
|
with self.lock:
|
|
return None is not self.currentItem \
|
|
and show_obj == self.currentItem.show_obj \
|
|
and self.currentItem.action_id in actions
|
|
|
|
def is_in_update_queue(self, show_obj):
|
|
# type: (TVShow) -> bool
|
|
"""
|
|
|
|
:param show_obj: show object
|
|
:type show_obj: sickgear.tv.TVShow
|
|
:return:
|
|
:rtype: bool
|
|
"""
|
|
return self._is_in_queue(show_obj, (ShowQueueActions.UPDATE, ShowQueueActions.FORCEUPDATE,
|
|
ShowQueueActions.WEBFORCEUPDATE))
|
|
|
|
def is_in_refresh_queue(self, show_obj):
|
|
# type: (TVShow) -> bool
|
|
"""
|
|
|
|
:param show_obj: show object
|
|
:type show_obj: sickgear.tv.TVShow
|
|
:return:
|
|
:rtype: bool
|
|
"""
|
|
return self._is_in_queue(show_obj, (ShowQueueActions.REFRESH,))
|
|
|
|
def is_in_rename_queue(self, show_obj):
|
|
# type: (TVShow) -> bool
|
|
"""
|
|
|
|
:param show_obj: show object
|
|
:type show_obj: sickgear.tv.TVShow
|
|
:return:
|
|
:rtype: bool
|
|
"""
|
|
return self._is_in_queue(show_obj, (ShowQueueActions.RENAME,))
|
|
|
|
def is_in_subtitle_queue(self, show_obj):
|
|
# type: (TVShow) -> bool
|
|
"""
|
|
|
|
:param show_obj: show object
|
|
:type show_obj: sickgear.tv.TVShow
|
|
:return:
|
|
:rtype: bool
|
|
"""
|
|
return self._is_in_queue(show_obj, (ShowQueueActions.SUBTITLE,))
|
|
|
|
def is_being_added(self, show_obj):
|
|
# type: (TVShow) -> bool
|
|
"""
|
|
|
|
:param show_obj: show object
|
|
:type show_obj: sickgear.tv.TVShow
|
|
:return:
|
|
:rtype: bool
|
|
"""
|
|
return self._is_being_somethinged(show_obj, (ShowQueueActions.ADD,))
|
|
|
|
def is_being_updated(self, show_obj):
|
|
# type: (TVShow) -> bool
|
|
"""
|
|
|
|
:param show_obj: show object
|
|
:type show_obj: sickgear.tv.TVShow
|
|
:return:
|
|
:rtype: bool
|
|
"""
|
|
return self._is_being_somethinged(show_obj, (ShowQueueActions.UPDATE, ShowQueueActions.FORCEUPDATE,
|
|
ShowQueueActions.WEBFORCEUPDATE))
|
|
|
|
def is_being_refreshed(self, show_obj):
|
|
# type: (TVShow) -> bool
|
|
"""
|
|
|
|
:param show_obj: show object
|
|
:type show_obj: sickgear.tv.TVShow
|
|
:return:
|
|
:rtype: bool
|
|
"""
|
|
return self._is_being_somethinged(show_obj, (ShowQueueActions.REFRESH,))
|
|
|
|
def is_being_renamed(self, show_obj):
|
|
# type: (TVShow) -> bool
|
|
"""
|
|
|
|
:param show_obj: show object
|
|
:type show_obj: sickgear.tv.TVShow
|
|
:return:
|
|
:rtype: bool
|
|
"""
|
|
return self._is_being_somethinged(show_obj, (ShowQueueActions.RENAME,))
|
|
|
|
def is_being_subtitled(self, show_obj):
|
|
# type: (TVShow) -> bool
|
|
"""
|
|
|
|
:param show_obj: show object
|
|
:type show_obj: sickgear.tv.TVShow
|
|
:return:
|
|
:rtype: bool
|
|
"""
|
|
return self._is_being_somethinged(show_obj, (ShowQueueActions.SUBTITLE,))
|
|
|
|
def is_show_update_running(self):
|
|
"""
|
|
|
|
:return:
|
|
:rtype: bool
|
|
"""
|
|
with self.lock:
|
|
return any(1 for x in self.queue + [self.currentItem]
|
|
if isinstance(x, ShowQueueItem) and x.scheduled_update)
|
|
|
|
def is_show_being_switched(self, show_obj):
|
|
# type: (TVShow) -> bool
|
|
"""
|
|
check if show is being switched currently
|
|
|
|
:param show_obj: show object
|
|
"""
|
|
return self._is_being_somethinged(show_obj, (ShowQueueActions.SWITCH,))
|
|
|
|
def is_show_switch_queued(self, show_obj):
|
|
# type: (TVShow) -> bool
|
|
"""
|
|
check if show is in switch queue
|
|
|
|
:param show_obj: show object
|
|
"""
|
|
return self._is_in_queue(show_obj, (ShowQueueActions.SWITCH,))
|
|
|
|
def is_switch_running(self):
|
|
# type: (...) -> bool
|
|
with self.lock:
|
|
return any(1 for x in self.queue + [self.currentItem] if isinstance(x, QueueItemSwitchSource))
|
|
|
|
def _get_loading_showlist(self):
|
|
"""
|
|
|
|
:return:
|
|
:rtype: List
|
|
"""
|
|
with self.lock:
|
|
return [x for x in self.queue + [self.currentItem] if None is not x and x.is_loading]
|
|
|
|
def queue_length(self):
|
|
# type: (...) -> Dict[AnyStr, List[AnyStr, Dict]]
|
|
"""
|
|
|
|
:return:
|
|
"""
|
|
length = {'add': [], 'update': [], 'forceupdate': [], 'forceupdateweb': [], 'refresh': [], 'rename': [],
|
|
'subtitle': [], 'switch': []}
|
|
with self.lock:
|
|
for cur_item in [self.currentItem] + self.queue: # type: ShowQueueItem
|
|
if not cur_item:
|
|
continue
|
|
result_item = {'name': cur_item.show_name, 'scheduled_update': cur_item.scheduled_update,
|
|
'uid': cur_item.uid}
|
|
if isinstance(cur_item, QueueItemAdd):
|
|
length['add'].append(result_item)
|
|
else:
|
|
result_item.update({
|
|
'tvid': cur_item.show_obj.tvid,
|
|
'prodid': cur_item.show_obj.prodid, 'tvid_prodid': cur_item.show_obj.tvid_prodid,
|
|
# legacy keys for api responses
|
|
'indexer': cur_item.show_obj.tvid, 'indexerid': cur_item.show_obj.prodid})
|
|
if isinstance(cur_item, QueueItemUpdate):
|
|
update_type = 'Normal'
|
|
if isinstance(cur_item, QueueItemForceUpdate):
|
|
update_type = 'Forced'
|
|
elif isinstance(cur_item, QueueItemForceUpdateWeb):
|
|
update_type = 'Forced Web'
|
|
result_item.update({'update_type': update_type})
|
|
length['update'].append(result_item)
|
|
elif isinstance(cur_item, QueueItemRefresh):
|
|
length['refresh'].append(result_item)
|
|
elif isinstance(cur_item, QueueItemRename):
|
|
length['rename'].append(result_item)
|
|
elif isinstance(cur_item, QueueItemSubtitle):
|
|
length['subtitle'].append(result_item)
|
|
elif isinstance(cur_item, QueueItemSwitchSource):
|
|
result_item.update({'new_tvid': cur_item.new_tvid, 'new_prodid': cur_item.new_prodid,
|
|
'progress': cur_item.progress})
|
|
length['switch'].append(result_item)
|
|
return length
|
|
|
|
loading_showlist = property(_get_loading_showlist)
|
|
|
|
def update_show(self,
|
|
show_obj, # type: TVShow
|
|
force=False, # type: bool
|
|
web=False, # type: bool
|
|
scheduled_update=False, # type: bool
|
|
priority=generic_queue.QueuePriorities.NORMAL, # type: integer_types
|
|
uid=None, # type: integer_types
|
|
add_to_db=True, # type: bool
|
|
**kwargs # type: Any
|
|
): # type: (...) -> Union[QueueItemUpdate, QueueItemForceUpdate, QueueItemForceUpdateWeb]
|
|
"""
|
|
|
|
:param show_obj: show object
|
|
:type show_obj: sickgear.tv.TVShow
|
|
:param force:
|
|
:type force: bool
|
|
:param web:
|
|
:type web: bool
|
|
:param scheduled_update:
|
|
:type scheduled_update: bool
|
|
:param priority:
|
|
:type priority: int
|
|
:param uid:
|
|
:param add_to_db:
|
|
:param kwargs:
|
|
:return:
|
|
:rtype: QueueItemUpdate or QueueItemForceUpdateWeb or QueueItemForceUpdate
|
|
"""
|
|
with self.lock:
|
|
if self.is_being_added(show_obj):
|
|
raise exceptions_helper.CantUpdateException(
|
|
'Show is still being added, wait until it is finished before you update.')
|
|
|
|
if self.is_being_updated(show_obj):
|
|
raise exceptions_helper.CantUpdateException(
|
|
'This show is already being updated, can\'t update again until it\'s done.')
|
|
|
|
if self.is_in_update_queue(show_obj):
|
|
raise exceptions_helper.CantUpdateException(
|
|
'This show is already being updated, can\'t update again until it\'s done.')
|
|
|
|
if self.is_show_being_switched(show_obj):
|
|
raise exceptions_helper.CantUpdateException('Show is in progress of being switched')
|
|
if self.is_show_switch_queued(show_obj):
|
|
raise exceptions_helper.CantUpdateException('Show is already queued to be switched')
|
|
|
|
if not force:
|
|
queue_item_obj = QueueItemUpdate(
|
|
show_obj, scheduled_update=scheduled_update, uid=uid, **kwargs)
|
|
elif web:
|
|
queue_item_obj = QueueItemForceUpdateWeb(
|
|
show_obj, scheduled_update=scheduled_update, priority=priority, uid=uid, **kwargs)
|
|
else:
|
|
queue_item_obj = QueueItemForceUpdate(
|
|
show_obj, scheduled_update=scheduled_update, uid=uid, **kwargs)
|
|
|
|
self.add_item(queue_item_obj, add_to_db=add_to_db)
|
|
|
|
return queue_item_obj
|
|
|
|
def refresh_show(self, show_obj, force=False, scheduled_update=False, after_update=False,
|
|
priority=generic_queue.QueuePriorities.HIGH, force_image_cache=False, uid=None, add_to_db=True,
|
|
**kwargs):
|
|
# type: (TVShow, bool, bool, bool, integer_types, bool, integer_types, bool, Any) -> Optional[QueueItemRefresh]
|
|
"""
|
|
|
|
:param show_obj: show object
|
|
:type show_obj: sickgear.tv.TVShow
|
|
:param force:
|
|
:type force: bool
|
|
:param scheduled_update:
|
|
:type scheduled_update: bool
|
|
:param after_update:
|
|
:type after_update:
|
|
:param priority:
|
|
:type priority: int
|
|
:param force_image_cache:
|
|
:type force_image_cache: bool
|
|
:param uid:
|
|
:param add_to_db:
|
|
:param kwargs:
|
|
:return:
|
|
:rtype: QueueItemRefresh
|
|
"""
|
|
with self.lock:
|
|
if (self.is_being_refreshed(show_obj) or self.is_in_refresh_queue(show_obj)) and not force:
|
|
raise exceptions_helper.CantRefreshException('This show is being refreshed, not refreshing again.')
|
|
|
|
if ((not after_update and self.is_being_updated(show_obj))
|
|
or self.is_in_update_queue(show_obj)) and not force:
|
|
logger.debug('Skipping this refresh as there is already an update queued or'
|
|
' in progress and a refresh is done at the end of an update anyway.')
|
|
return
|
|
|
|
if self.is_show_being_switched(show_obj):
|
|
raise exceptions_helper.CantRefreshException('Show is in progress of being switched')
|
|
if self.is_show_switch_queued(show_obj):
|
|
raise exceptions_helper.CantRefreshException('Show is already queued to be switched')
|
|
|
|
queue_item_obj = QueueItemRefresh(show_obj, force=force, scheduled_update=scheduled_update,
|
|
priority=priority, force_image_cache=force_image_cache, uid=uid, **kwargs)
|
|
|
|
self.add_item(queue_item_obj, add_to_db=add_to_db)
|
|
|
|
return queue_item_obj
|
|
|
|
def rename_show_episodes(self, show_obj, uid=None, add_to_db=True):
|
|
# type: (TVShow, integer_types, bool) -> QueueItemRename
|
|
"""
|
|
|
|
:param show_obj: show object
|
|
:type show_obj: sickgear.tv.TVShow
|
|
:param uid:
|
|
:param add_to_db:
|
|
:return:
|
|
:rtype: QueueItemRename
|
|
"""
|
|
queue_item_obj = QueueItemRename(show_obj, uid=uid)
|
|
|
|
self.add_item(queue_item_obj, add_to_db=add_to_db)
|
|
|
|
return queue_item_obj
|
|
|
|
def switch_show(self,
|
|
show_obj, # type: TVShow
|
|
new_tvid, # type: integer_types
|
|
new_prodid, # type: integer_types
|
|
force_id=False, # type: bool
|
|
uid=None, # type: AnyStr
|
|
set_pause=False, # type: bool
|
|
mark_wanted=False, # type: bool
|
|
resume=False, # type: bool
|
|
old_tvid=None, # type: integer_types
|
|
old_prodid=None, # type: integer_types
|
|
add_to_db=True # type: bool
|
|
): # type: (...) -> QueueItemSwitchSource
|
|
"""
|
|
:param show_obj:
|
|
:param new_tvid:
|
|
:param new_prodid:
|
|
:param force_id:
|
|
:param uid:
|
|
:param set_pause:
|
|
:param mark_wanted:
|
|
:param resume:
|
|
:param old_tvid:
|
|
:param old_prodid:
|
|
:param add_to_db:
|
|
"""
|
|
with self.lock:
|
|
if self.is_show_being_switched(show_obj):
|
|
raise exceptions_helper.CantSwitchException('Show is in progress of being switched')
|
|
if self.is_show_switch_queued(show_obj):
|
|
raise exceptions_helper.CantSwitchException('Show is already queued to be switched')
|
|
|
|
item = QueueItemSwitchSource(show_obj=show_obj, new_tvid=new_tvid, new_prodid=new_prodid, force_id=force_id,
|
|
uid=uid, set_pause=set_pause, mark_wanted=mark_wanted, resume=resume,
|
|
old_tvid=old_tvid, old_prodid=old_prodid)
|
|
self.add_item(item, add_to_db=add_to_db)
|
|
return item
|
|
|
|
def download_subtitles(self, show_obj, uid=None, add_to_db=True):
|
|
# type: (TVShow, integer_types, bool) -> QueueItemSubtitle
|
|
"""
|
|
|
|
:param show_obj: show object
|
|
:type show_obj: sickgear.tv.TVShow
|
|
:param uid:
|
|
:param add_to_db:
|
|
:return:
|
|
:rtype: QueueItemSubtitle
|
|
"""
|
|
if sickgear.USE_SUBTITLES:
|
|
queue_item_obj = QueueItemSubtitle(show_obj, uid=uid)
|
|
|
|
self.add_item(queue_item_obj, add_to_db=add_to_db)
|
|
|
|
return queue_item_obj
|
|
|
|
def abort_show(self, show_obj):
|
|
# type: (TVShow) -> None
|
|
if show_obj:
|
|
with self.lock:
|
|
for c in ((self.currentItem and [self.currentItem]) or []) + self.queue:
|
|
if show_obj == getattr(c, 'show_obj', None):
|
|
try:
|
|
self.remove_from_queue([c.uid])
|
|
except (BaseException, Exception):
|
|
pass
|
|
try:
|
|
c.stop.set()
|
|
except (BaseException, Exception):
|
|
pass
|
|
|
|
def add_show(self, tvid, prodid, show_dir,
|
|
quality=None, upgrade_once=False, wanted_begin=None, wanted_latest=None, tag=None,
|
|
paused=None, prune=None, default_status=None, scene=None, subtitles=None,
|
|
flatten_folders=None, anime=None, blocklist=None, allowlist=None,
|
|
show_name=None, new_show=False, lang='en', uid=None, add_to_db=True):
|
|
"""
|
|
|
|
:param tvid: tvid
|
|
:type tvid: int
|
|
:param prodid: prodid
|
|
:type prodid: int or long
|
|
:param show_dir: show dir
|
|
:type show_dir: AnyStr
|
|
:param quality:
|
|
:type quality: None or int
|
|
:param upgrade_once:
|
|
:type upgrade_once: bool or None
|
|
:param wanted_begin:
|
|
:type wanted_begin: int or None
|
|
:param wanted_latest:
|
|
:type wanted_latest: int or None
|
|
:param tag:
|
|
:type tag: AnyStr or None
|
|
:param paused:
|
|
:type paused: None or int
|
|
:param prune:
|
|
:type prune: int or None
|
|
:param default_status:
|
|
:type default_status: int or None
|
|
:param scene:
|
|
:type scene: int or None
|
|
:param subtitles:
|
|
:type subtitles: int or None
|
|
:param flatten_folders:
|
|
:type flatten_folders: int or None
|
|
:param anime:
|
|
:type anime: int or None
|
|
:param blocklist:
|
|
:type blocklist: AnyStr or None
|
|
:param allowlist:
|
|
:type allowlist: AnyStr or None
|
|
:param show_name:
|
|
:type show_name: AnyStr or None
|
|
:param new_show:
|
|
:param lang:
|
|
:type lang: AnyStr
|
|
:param uid:
|
|
:param add_to_db:
|
|
:return:
|
|
:rtype: QueueItemAdd
|
|
"""
|
|
queue_item_obj = QueueItemAdd(tvid, prodid, show_dir, default_status, quality, flatten_folders, lang,
|
|
subtitles, anime, scene, paused, blocklist, allowlist,
|
|
wanted_begin, wanted_latest, prune, tag,
|
|
new_show=new_show, show_name=show_name, upgrade_once=upgrade_once, uid=uid)
|
|
|
|
self.add_item(queue_item_obj, add_to_db=add_to_db)
|
|
|
|
return queue_item_obj
|
|
|
|
|
|
class ShowQueueActions(object):
|
|
REFRESH = 1
|
|
ADD = 2
|
|
UPDATE = 3
|
|
FORCEUPDATE = 4
|
|
WEBFORCEUPDATE = 7
|
|
RENAME = 5
|
|
SUBTITLE = 6
|
|
SWITCH = 10
|
|
|
|
names = {REFRESH: 'Refresh',
|
|
ADD: 'Add',
|
|
UPDATE: 'Update',
|
|
FORCEUPDATE: 'Force Update',
|
|
WEBFORCEUPDATE: 'Force Web Update',
|
|
RENAME: 'Rename',
|
|
SUBTITLE: 'Subtitle',
|
|
SWITCH: 'Switch Source'}
|
|
|
|
|
|
class ShowQueueItem(generic_queue.QueueItem):
|
|
"""
|
|
Represents an item in the queue waiting to be executed
|
|
|
|
Can be either:
|
|
- show being added (may or may not be associated with a show object)
|
|
- show being refreshed
|
|
- show being updated
|
|
- show being force updated
|
|
- show being subtitled
|
|
"""
|
|
|
|
def __init__(self, action_id, show_obj, scheduled_update=False, uid=None):
|
|
# type: (integer_types, TVShow, bool, integer_types) -> None
|
|
"""
|
|
|
|
:param action_id:
|
|
:type action_id:
|
|
:param show_obj: show object
|
|
:type show_obj: sickgear.tv.TVShow or None
|
|
:param scheduled_update:
|
|
:type scheduled_update: bool
|
|
:param uid:
|
|
"""
|
|
generic_queue.QueueItem.__init__(self, ShowQueueActions.names[action_id], action_id, uid=uid)
|
|
self.show_obj = show_obj # type: sickgear.tv.TVShow
|
|
self.scheduled_update = scheduled_update # type: bool
|
|
|
|
def is_in_queue(self):
|
|
"""
|
|
:rtype: bool
|
|
"""
|
|
return self in sickgear.show_queue_scheduler.action.queue + [
|
|
sickgear.show_queue_scheduler.action.currentItem]
|
|
|
|
def _get_name(self):
|
|
"""
|
|
:rtype: AnyStr
|
|
"""
|
|
if self.show_obj:
|
|
return self.show_obj.name
|
|
return ''
|
|
|
|
def _is_loading(self):
|
|
return False
|
|
|
|
def __str__(self):
|
|
return '<%s (%s)>' % (self.__class__.__name__, (self.show_obj and self.show_obj.name))
|
|
|
|
def __repr__(self):
|
|
return self.__str__()
|
|
|
|
show_name = property(_get_name)
|
|
|
|
is_loading = property(_is_loading)
|
|
|
|
|
|
class QueueItemAdd(ShowQueueItem):
|
|
def __init__(self, tvid, prodid, show_dir, default_status, quality, flatten_folders, lang, subtitles, anime,
|
|
scene, paused, blocklist, allowlist, default_wanted_begin, default_wanted_latest, prune, tag,
|
|
scheduled_update=False, new_show=False, show_name=None, upgrade_once=False, uid=None):
|
|
"""
|
|
|
|
:param tvid: tvid
|
|
:type tvid: int
|
|
:param prodid: prodid
|
|
:type prodid: int or long
|
|
:param show_dir:
|
|
:type show_dir: AnyStr
|
|
:param default_status:
|
|
:type default_status:
|
|
:param quality:
|
|
:type quality: int
|
|
:param flatten_folders:
|
|
:type flatten_folders:
|
|
:param lang:
|
|
:type lang:
|
|
:param subtitles:
|
|
:type subtitles:
|
|
:param anime:
|
|
:type anime:
|
|
:param scene:
|
|
:type scene:
|
|
:param paused:
|
|
:type paused:
|
|
:param blocklist:
|
|
:type blocklist:
|
|
:param allowlist:
|
|
:type allowlist:
|
|
:param default_wanted_begin:
|
|
:type default_wanted_begin:
|
|
:param default_wanted_latest:
|
|
:type default_wanted_latest:
|
|
:param prune:
|
|
:type prune:
|
|
:param tag:
|
|
:type tag:
|
|
:param scheduled_update:
|
|
:type scheduled_update:
|
|
:param new_show:
|
|
:type new_show:
|
|
:param show_name:
|
|
:type show_name: AnyStr or None
|
|
:param upgrade_once:
|
|
:type upgrade_once:
|
|
:param uid:
|
|
"""
|
|
self.tvid = tvid # type: int
|
|
self.prodid = prodid # type: int
|
|
self.allowlist = allowlist
|
|
self.anime = anime
|
|
self.blocklist = blocklist
|
|
self.default_status = default_status
|
|
self.default_wanted_begin = (0, default_wanted_begin)[isinstance(default_wanted_begin, integer_types)]
|
|
self.default_wanted_latest = (0, default_wanted_latest)[isinstance(default_wanted_latest, integer_types)]
|
|
self.flatten_folders = flatten_folders
|
|
self.lang = lang
|
|
self.new_show = new_show
|
|
self.paused = paused
|
|
self.prune = prune
|
|
self.quality = quality # type: int
|
|
self.scene = scene
|
|
self.show_obj = None
|
|
self.showDir = show_dir # type: AnyStr
|
|
self.showname = show_name # type: AnyStr or None
|
|
self.subtitles = subtitles
|
|
self.tag = tag
|
|
self.upgrade_once = upgrade_once
|
|
|
|
# this will initialize self.show_obj to None
|
|
ShowQueueItem.__init__(self, ShowQueueActions.ADD, self.show_obj, scheduled_update, uid=uid)
|
|
|
|
self.priority = generic_queue.QueuePriorities.VERYHIGH
|
|
|
|
def _get_name(self):
|
|
"""
|
|
:return: the show name if there is a show object created, if not returns
|
|
the dir that the show is being added to.
|
|
:rtype: AnyStr
|
|
"""
|
|
if None is not self.showname:
|
|
return self.showname
|
|
if None is self.show_obj:
|
|
return self.showDir
|
|
return self.show_obj.name
|
|
|
|
show_name = property(_get_name)
|
|
|
|
def _is_loading(self):
|
|
"""
|
|
:return: True if we've gotten far enough to have a show object, or False
|
|
if we still only know the folder name.
|
|
:rtype: bool
|
|
"""
|
|
return None is self.show_obj
|
|
|
|
is_loading = property(_is_loading)
|
|
|
|
# if they gave a number to start or number to end as wanted, then change those eps to it
|
|
def _get_wanted(self, db_obj, wanted_max, latest):
|
|
# type (...) -> int
|
|
|
|
latest_season = 0
|
|
actual = 0
|
|
upgradable = 0
|
|
process_sql = True
|
|
wanted_updates = []
|
|
if latest:
|
|
# find season number with latest aired episode
|
|
latest_result = db_obj.select(
|
|
"""
|
|
SELECT MAX(season) AS latest_season
|
|
FROM tv_episodes
|
|
WHERE season > 0 AND indexer = ? AND showid = ? AND status != ?
|
|
""", [self.show_obj.tvid, self.show_obj.prodid, UNAIRED])
|
|
if latest_result and None is not latest_result[0]['latest_season']:
|
|
latest_season = int(latest_result[0]['latest_season'])
|
|
else:
|
|
process_sql = False
|
|
|
|
if process_sql:
|
|
if -1 == wanted_max:
|
|
compare_op = '' # equal, we only want the specific season
|
|
else:
|
|
compare_op = ('>', '<')[latest] # less/more then equal season
|
|
base_sql = """
|
|
SELECT indexerid, season, episode, status
|
|
FROM tv_episodes
|
|
WHERE indexer = ? AND showid = ? AND season != 0 AND season %s= ? AND status != ?
|
|
ORDER BY season%s, episode%s%s
|
|
""" % (compare_op, ('', ' DESC')[latest], ('', ' DESC')[latest], (' LIMIT ?', '')[-1 == wanted_max])
|
|
|
|
selected_res = db_obj.select(base_sql, [self.show_obj.tvid, self.show_obj.prodid,
|
|
(1, latest_season)[latest], UNAIRED] +
|
|
([wanted_max], [])[-1 == wanted_max])
|
|
selected_ids = []
|
|
for sr in selected_res:
|
|
if sr['status'] in [SKIPPED]:
|
|
selected_ids.append(sr['indexerid'])
|
|
wanted_updates.append({'season': sr['season'], 'episode': sr['episode'],
|
|
'status': sr['status']})
|
|
elif sr['status'] not in [WANTED]:
|
|
cur_status, cur_quality = Quality.split_composite_status(int(sr['status']))
|
|
if sickgear.WANTEDLIST_CACHE.get_wantedlist(
|
|
self.quality, self.upgrade_once, cur_quality, cur_status,
|
|
unaired=(sickgear.SEARCH_UNAIRED and not sickgear.UNAIRED_RECENT_SEARCH_ONLY)):
|
|
upgradable += 1
|
|
|
|
if selected_ids:
|
|
update = 'UPDATE [tv_episodes] SET status = ? WHERE indexer = ? AND indexerid IN (%s)' % \
|
|
','.join(['?'] * len(selected_ids))
|
|
db_obj.action(update, [WANTED, self.show_obj.tvid] + selected_ids)
|
|
|
|
# noinspection SqlResolve
|
|
sql_result = db_obj.select('SELECT changes() as last FROM [tv_episodes]')
|
|
|
|
for cur_row in sql_result:
|
|
actual = cur_row['last']
|
|
break
|
|
|
|
action_log = 'didn\'t find any episodes that need to be set wanted'
|
|
if actual:
|
|
action_log = ('updated %s %s episodes > %s' % (
|
|
(((('%s of %s' % (actual, wanted_max)),
|
|
('%s of max %s limited' % (actual, wanted_max)))[10 == wanted_max]),
|
|
('max %s available' % actual))[-1 == wanted_max],
|
|
('first season', 'latest')[latest],
|
|
', '.join([
|
|
('S%02dE%02d=%s' % (a['season'], a['episode'], statusStrings[a['status']]))
|
|
for a in wanted_updates
|
|
])
|
|
))
|
|
logger.log('Get wanted ' + action_log)
|
|
return actual + upgradable
|
|
|
|
def run(self):
|
|
|
|
ShowQueueItem.run(self)
|
|
|
|
logger.log('Starting to add show %s' % self.showDir)
|
|
# make sure the TV info source IDs are valid
|
|
try:
|
|
|
|
tvinfo_config = sickgear.TVInfoAPI(self.tvid).api_params.copy()
|
|
kw = {}
|
|
if self.lang:
|
|
tvinfo_config['language'] = self.lang
|
|
kw = {'language': self.lang}
|
|
|
|
logger.log(f'{sickgear.TVInfoAPI(self.tvid).name}: {repr(tvinfo_config)}')
|
|
|
|
t = sickgear.TVInfoAPI(self.tvid).setup(**tvinfo_config)
|
|
s = t.get_show(self.prodid, load_episodes=False, **kw)
|
|
|
|
if getattr(t, 'show_not_found', False):
|
|
logger.error(f'Show {self.show_name} was not found on {sickgear.TVInfoAPI(self.tvid).name},'
|
|
f' maybe show was deleted')
|
|
self._finish_early()
|
|
return
|
|
|
|
# this usually only happens if they have an NFO in their show dir
|
|
# which gave us a TV info source ID that has no proper english version of the show
|
|
if None is getattr(s, 'seriesname', None):
|
|
logger.error(f'Show in {self.showDir} has no name on {sickgear.TVInfoAPI(self.tvid).name},'
|
|
f' probably the wrong language used to search with.')
|
|
ui.notifications.error('Unable to add show',
|
|
'Show in %s has no name on %s, probably the wrong language.'
|
|
' Delete .nfo and add manually in the correct language.' %
|
|
(self.showDir, sickgear.TVInfoAPI(self.tvid).name))
|
|
self._finish_early()
|
|
return
|
|
except (BaseException, Exception):
|
|
logger.error('Unable to find show ID:%s on TV info: %s' % (self.prodid, sickgear.TVInfoAPI(self.tvid).name))
|
|
ui.notifications.error('Unable to add show',
|
|
'Unable to look up the show in %s on %s using ID %s, not using the NFO.'
|
|
' Delete .nfo and try adding manually again.' %
|
|
(self.showDir, sickgear.TVInfoAPI(self.tvid).name, self.prodid))
|
|
self._finish_early()
|
|
return
|
|
|
|
try:
|
|
show_exists = find_show_by_id({self.tvid: self.prodid}, no_exceptions=True)
|
|
new_show_obj = show_exists or TVShow(self.tvid, self.prodid, self.lang)
|
|
result = new_show_obj.load_from_tvinfo()
|
|
|
|
self.show_obj = new_show_obj
|
|
|
|
# set up initial values
|
|
self.show_obj.location = self.showDir
|
|
self.show_obj.quality = self.quality if self.quality else sickgear.QUALITY_DEFAULT
|
|
self.show_obj.upgrade_once = self.upgrade_once
|
|
self.show_obj.tag = self.tag if None is not self.tag else 'Show List'
|
|
self.show_obj.paused = self.paused if None is not self.paused else sickgear.PAUSE_DEFAULT
|
|
self.show_obj.prune = self.prune if None is not self.prune else 0
|
|
self.show_obj.scene = self.scene if None is not self.scene else sickgear.SCENE_DEFAULT
|
|
self.show_obj.subtitles = self.subtitles if None is not self.subtitles else sickgear.SUBTITLES_DEFAULT
|
|
self.show_obj.flatten_folders = self.flatten_folders if None is not self.flatten_folders \
|
|
else sickgear.FLATTEN_FOLDERS_DEFAULT
|
|
self.show_obj.anime = self.anime if None is not self.anime else sickgear.ANIME_DEFAULT
|
|
|
|
if self.show_obj.anime:
|
|
self.show_obj.release_groups = AniGroupList(self.show_obj.tvid,
|
|
self.show_obj.prodid,
|
|
self.show_obj.tvid_prodid)
|
|
if self.allowlist:
|
|
self.show_obj.release_groups.set_allow_keywords(self.allowlist)
|
|
if self.blocklist:
|
|
self.show_obj.release_groups.set_block_keywords(self.blocklist)
|
|
|
|
# be smartish about this
|
|
if self.show_obj.genre and 'talk show' in self.show_obj.genre.lower():
|
|
self.show_obj.air_by_date = 1
|
|
if self.show_obj.genre and 'documentary' in self.show_obj.genre.lower():
|
|
self.show_obj.air_by_date = 0
|
|
if self.show_obj.classification and 'sports' in self.show_obj.classification.lower():
|
|
self.show_obj.sports = 1
|
|
|
|
except BaseTVinfoException as e:
|
|
logger.error(f'Unable to add show due to an error with {sickgear.TVInfoAPI(self.tvid).name}: {ex(e)}')
|
|
if self.show_obj:
|
|
ui.notifications.error('Unable to add %s due to an error with %s'
|
|
% (self.show_obj.unique_name, sickgear.TVInfoAPI(self.tvid).name))
|
|
else:
|
|
ui.notifications.error(
|
|
'Unable to add show due to an error with %s' % sickgear.TVInfoAPI(self.tvid).name)
|
|
self._finish_early()
|
|
return
|
|
|
|
except exceptions_helper.MultipleShowObjectsException:
|
|
logger.error('The show in %s is already in your show list, skipping' % self.showDir)
|
|
ui.notifications.error('Show skipped', 'The show in %s is already in your show list' % self.showDir)
|
|
self._finish_early()
|
|
return
|
|
|
|
except (BaseException, Exception) as e:
|
|
logger.error('Error trying to add show: %s' % ex(e))
|
|
logger.error(traceback.format_exc())
|
|
self._finish_early()
|
|
raise
|
|
|
|
self.show_obj.load_imdb_info()
|
|
|
|
try:
|
|
self.show_obj.save_to_db()
|
|
except (BaseException, Exception) as e:
|
|
logger.error('Error saving the show to the database: %s' % ex(e))
|
|
logger.error(traceback.format_exc())
|
|
self._finish_early()
|
|
raise
|
|
|
|
if not show_exists:
|
|
# add it to the show list if not already in it
|
|
sickgear.showList.append(self.show_obj)
|
|
sickgear.showDict[self.show_obj.sid_int] = self.show_obj
|
|
sickgear.webserve.Home.make_showlist_unique_names()
|
|
sickgear.MEMCACHE['history_tab'] = sickgear.webserve.History.menu_tab(
|
|
sickgear.MEMCACHE['history_tab_limit'])
|
|
|
|
try:
|
|
self.show_obj.load_episodes_from_tvinfo(tvinfo_data=(None, result)[
|
|
self.show_obj.prodid == getattr(result, 'id', None)])
|
|
except (BaseException, Exception) as e:
|
|
logger.error(f'Error with {sickgear.TVInfoAPI(self.show_obj.tvid).name},'
|
|
f' not creating episode list: {ex(e)}')
|
|
logger.error(traceback.format_exc())
|
|
|
|
try:
|
|
self.show_obj.load_episodes_from_dir()
|
|
except (BaseException, Exception) as e:
|
|
logger.error('Error searching directory for episodes: %s' % ex(e))
|
|
logger.error(traceback.format_exc())
|
|
|
|
# if they gave a custom status then change all the eps to it
|
|
my_db = db.DBConnection()
|
|
if self.default_status != SKIPPED:
|
|
logger.log('Setting all episodes to the specified default status: %s'
|
|
% sickgear.common.statusStrings[self.default_status])
|
|
my_db.action(
|
|
"""
|
|
UPDATE tv_episodes
|
|
SET status = ?
|
|
WHERE status = ? AND indexer = ? AND showid = ? AND season != 0
|
|
""", [self.default_status, SKIPPED, self.show_obj.tvid, self.show_obj.prodid])
|
|
|
|
items_wanted = self._get_wanted(my_db, self.default_wanted_begin, latest=False)
|
|
items_wanted += self._get_wanted(my_db, self.default_wanted_latest, latest=True)
|
|
|
|
self.show_obj.write_metadata()
|
|
self.show_obj.update_metadata()
|
|
self.show_obj.populate_cache()
|
|
|
|
self.show_obj.flush_episodes()
|
|
|
|
# load ids
|
|
_ = self.show_obj.ids
|
|
|
|
# if sickgear.USE_TRAKT:
|
|
# # if there are specific episodes that need to be added by trakt
|
|
# sickgear.trakt_checker_scheduler.action.manageNewShow(self.show_obj)
|
|
#
|
|
# # add show to trakt.tv library
|
|
# if sickgear.TRAKT_SYNC:
|
|
# sickgear.trakt_checker_scheduler.action.addShowToTraktLibrary(self.show_obj)
|
|
|
|
# Load XEM data to DB for show
|
|
sickgear.scene_numbering.xem_refresh(self.show_obj.tvid, self.show_obj.prodid, force=True)
|
|
if self.show_obj.scene:
|
|
# enable/disable scene flag based on if show has an explicit _scene_ mapping at XEM
|
|
self.show_obj.scene = sickgear.scene_numbering.has_xem_scene_mapping(
|
|
self.show_obj.tvid, self.show_obj.prodid)
|
|
# if "scene" numbering is disabled during add show, output availability to log
|
|
if None is not self.scene and not self.show_obj.scene and \
|
|
self.show_obj.prodid in sickgear.scene_exceptions.MEMCACHE['release_map_xem'][self.show_obj.tvid]:
|
|
logger.log('No scene number mappings found at TheXEM. Therefore, episode scene numbering disabled, '
|
|
'edit show and enable it to manually add custom numbers for search and media processing.')
|
|
try:
|
|
self.show_obj.save_to_db()
|
|
except (BaseException, Exception) as e:
|
|
logger.error('Error saving the show to the database: %s' % ex(e))
|
|
logger.error(traceback.format_exc())
|
|
self._finish_early()
|
|
raise
|
|
|
|
# update internal name cache
|
|
name_cache.build_name_cache(self.show_obj)
|
|
|
|
self.show_obj.load_episodes_from_db()
|
|
|
|
map_indexers_to_show(self.show_obj, recheck=True)
|
|
|
|
if self.show_obj.tvid in (TVINFO_TVRAGE, TVINFO_TVDB):
|
|
# noinspection SqlResolve
|
|
oh = my_db.select('SELECT resource FROM history WHERE indexer = 0 AND showid = ?', [self.show_obj.prodid])
|
|
if oh:
|
|
found = False
|
|
for o in oh:
|
|
np = NameParser(file_name=True, indexer_lookup=False)
|
|
try:
|
|
pr = np.parse(o['resource'])
|
|
except (BaseException, Exception):
|
|
continue
|
|
if pr.show_obj.tvid == self.show_obj.tvid and pr.show_obj.prodid == self.show_obj.prodid:
|
|
found = True
|
|
break
|
|
if found:
|
|
my_db.action('UPDATE history SET indexer = ? WHERE indexer = 0 AND showid = ?',
|
|
[self.show_obj.tvid, self.show_obj.prodid])
|
|
|
|
msg = ' the specified show into ' + self.showDir
|
|
# if started with WANTED eps then run the backlog
|
|
if WANTED == self.default_status or items_wanted:
|
|
logger.log('Launching backlog for this show since episodes are WANTED')
|
|
sickgear.search_backlog_scheduler.action.search_backlog([self.show_obj], prevent_same=True)
|
|
ui.notifications.message('Show added/search', 'Adding and searching for episodes of' + msg)
|
|
else:
|
|
ui.notifications.message('Show added', 'Adding' + msg)
|
|
|
|
self.finish()
|
|
|
|
def _finish_early(self):
|
|
if None is not self.show_obj:
|
|
self.show_obj.delete_show()
|
|
|
|
if self.new_show:
|
|
# if adding a new show, delete the empty folder that was already created
|
|
try:
|
|
os.rmdir(self.showDir)
|
|
except (BaseException, Exception):
|
|
pass
|
|
|
|
self.finish()
|
|
|
|
def __str__(self):
|
|
return '<%s (%s)>' % (self.__class__.__name__, '%s:%s%s' %
|
|
(self.tvid, self.prodid, ('', ' - %s ' % self.show_name)[None is not self.show_name]))
|
|
|
|
def __repr__(self):
|
|
return self.__str__()
|
|
|
|
|
|
class QueueItemRefresh(ShowQueueItem):
|
|
def __init__(self, show_obj=None, force=False, scheduled_update=False, priority=generic_queue.QueuePriorities.HIGH,
|
|
force_image_cache=False, uid=None, switch=False, **kwargs):
|
|
# type: (TVShow, bool, bool, integer_types, bool, integer_types, bool, Any) -> None
|
|
"""
|
|
|
|
:param show_obj: show object
|
|
:type show_obj: sickgear.tv.TVShow
|
|
:param force:
|
|
:type force: bool
|
|
:param scheduled_update:
|
|
:type scheduled_update: bool
|
|
:param priority:
|
|
:type priority: int
|
|
:param force_image_cache:
|
|
:type force_image_cache: bool
|
|
:param uid:
|
|
:param switch: switching show
|
|
:param kwargs:
|
|
"""
|
|
ShowQueueItem.__init__(self, ShowQueueActions.REFRESH, show_obj, scheduled_update, uid=uid)
|
|
|
|
# do refreshes first because they're quick
|
|
self.priority = priority # type: int
|
|
|
|
# force refresh certain items
|
|
self.force = force # type: bool
|
|
|
|
self.force_image_cache = force_image_cache # type: bool
|
|
|
|
self.switch = switch # type: bool
|
|
|
|
self.kwargs = kwargs
|
|
|
|
def run(self):
|
|
ShowQueueItem.run(self)
|
|
|
|
logger.log('Performing refresh on %s' % self.show_obj.unique_name)
|
|
|
|
self.show_obj.refresh_dir()
|
|
self.show_obj.write_metadata(force=self.force)
|
|
# if self.force:
|
|
# self.show_obj.update_metadata()
|
|
self.show_obj.populate_cache(self.force_image_cache)
|
|
|
|
# Load XEM data to DB for show
|
|
if self.show_obj.prodid in sickgear.scene_exceptions.MEMCACHE['release_map_xem'][self.show_obj.tvid]:
|
|
sickgear.scene_numbering.xem_refresh(self.show_obj.tvid, self.show_obj.prodid)
|
|
|
|
if 'pausestatus_after' in self.kwargs and None is not self.kwargs['pausestatus_after']:
|
|
self.show_obj.paused = self.kwargs['pausestatus_after']
|
|
self.show_obj.save_to_db()
|
|
self.inProgress = False
|
|
|
|
|
|
class QueueItemRename(ShowQueueItem):
|
|
def __init__(self, show_obj=None, scheduled_update=False, uid=None):
|
|
# type: (TVShow, bool, integer_types) -> None
|
|
"""
|
|
|
|
:param show_obj: show object
|
|
:type show_obj: sickgear.tv.TVShow
|
|
:param scheduled_update:
|
|
:type scheduled_update: bool
|
|
:param uid:
|
|
"""
|
|
ShowQueueItem.__init__(self, ShowQueueActions.RENAME, show_obj, scheduled_update, uid=uid)
|
|
|
|
def run(self):
|
|
|
|
ShowQueueItem.run(self)
|
|
|
|
logger.log('Performing rename on %s' % self.show_obj.unique_name)
|
|
|
|
try:
|
|
_ = self.show_obj.location
|
|
except exceptions_helper.ShowDirNotFoundException:
|
|
logger.warning(f'Can\'t perform rename on {self.show_obj.unique_name} when the show directory is missing.')
|
|
return
|
|
|
|
ep_obj_rename_list = []
|
|
|
|
ep_obj_list = self.show_obj.get_all_episodes(has_location=True)
|
|
for cur_ep_obj in ep_obj_list:
|
|
# Only want to rename if we have a location
|
|
if cur_ep_obj.location:
|
|
if cur_ep_obj.related_ep_obj:
|
|
# do we have one of multi-episodes in the rename list already
|
|
have_already = False
|
|
for cur_related_ep_obj in cur_ep_obj.related_ep_obj + [cur_ep_obj]:
|
|
if cur_related_ep_obj in ep_obj_rename_list:
|
|
have_already = True
|
|
break
|
|
if not have_already:
|
|
ep_obj_rename_list.append(cur_ep_obj)
|
|
|
|
else:
|
|
ep_obj_rename_list.append(cur_ep_obj)
|
|
|
|
for cur_ep_obj in ep_obj_rename_list:
|
|
cur_ep_obj.rename()
|
|
|
|
self.inProgress = False
|
|
|
|
|
|
class QueueItemSubtitle(ShowQueueItem):
|
|
def __init__(self, show_obj=None, scheduled_update=False, uid=None):
|
|
# type: (TVShow, bool, integer_types) -> None
|
|
"""
|
|
|
|
:param show_obj: show object
|
|
:type show_obj: sickgear.tv.TVShow
|
|
:param scheduled_update:
|
|
:type scheduled_update: bool
|
|
:param uid:
|
|
"""
|
|
ShowQueueItem.__init__(self, ShowQueueActions.SUBTITLE, show_obj, scheduled_update, uid=uid)
|
|
|
|
def run(self):
|
|
ShowQueueItem.run(self)
|
|
if not sickgear.USE_SUBTITLES:
|
|
self.finish()
|
|
return
|
|
|
|
logger.log('Downloading subtitles for %s' % self.show_obj.unique_name)
|
|
|
|
self.show_obj.download_subtitles()
|
|
|
|
self.inProgress = False
|
|
|
|
|
|
class QueueItemUpdate(ShowQueueItem):
|
|
def __init__(self, show_obj=None, scheduled_update=False, uid=None, switch=False, tvinfo_data=None, old_tvid=None,
|
|
old_prodid=None, **kwargs):
|
|
# type: (TVShow, bool, AnyStr, bool, Optional[TVInfoShow], int, integer_types, Any) -> None
|
|
"""
|
|
|
|
:param show_obj: show object
|
|
:type show_obj: sickgear.tv.TVShow
|
|
:param scheduled_update:
|
|
:type scheduled_update: bool
|
|
:param skip_refresh: skip queuing refresh task at the end
|
|
:param switch: switching show
|
|
:param tvinfo_data: tvinfo data for the show
|
|
:param old_tvid: old tvid when switching
|
|
:param old_prodid: old prodid when switching
|
|
:param kwargs:
|
|
:param uid:
|
|
"""
|
|
ShowQueueItem.__init__(self, ShowQueueActions.UPDATE, show_obj, scheduled_update, uid=uid)
|
|
self.force = False # type: bool
|
|
self.force_web = False # type: bool
|
|
self.skip_refresh = kwargs.get('skip_refresh', False) # type: bool
|
|
self.switch = switch # type: bool
|
|
self.tvinfo_data = tvinfo_data # type: Optional[TVInfoShow]
|
|
self.old_tvid = old_tvid # type: Optional[int]
|
|
self.old_prodid = old_prodid # type: Optional[integer_types]
|
|
self.kwargs = kwargs
|
|
|
|
def run(self):
|
|
|
|
ShowQueueItem.run(self)
|
|
last_update = datetime.date.fromordinal(self.show_obj.last_update_indexer)
|
|
old_name = self.show_obj.name
|
|
|
|
if not sickgear.TVInfoAPI(self.show_obj.tvid).config['active']:
|
|
logger.log('TV info source %s is marked inactive, aborting update for show %s and continue with refresh.'
|
|
% (sickgear.TVInfoAPI(self.show_obj.tvid).config['name'], self.show_obj.name))
|
|
sickgear.show_queue_scheduler.action.refresh_show(self.show_obj, self.force, self.scheduled_update,
|
|
after_update=True)
|
|
return
|
|
|
|
logger.log('Beginning update of %s' % self.show_obj.unique_name)
|
|
|
|
logger.debug('Retrieving show info from %s' % sickgear.TVInfoAPI(self.show_obj.tvid).name)
|
|
try:
|
|
result = self.show_obj.load_from_tvinfo(cache=not self.force, tvinfo_data=self.tvinfo_data,
|
|
scheduled_update=self.scheduled_update, switch=self.switch)
|
|
if result in (None, False):
|
|
return
|
|
elif not self.show_obj.prodid == getattr(self.tvinfo_data, 'id', None):
|
|
self.tvinfo_data = result
|
|
except BaseTVinfoAttributenotfound as e:
|
|
logger.error(f'Data retrieved from {sickgear.TVInfoAPI(self.show_obj.tvid).name} was incomplete,'
|
|
f' aborting: {ex(e)}')
|
|
return
|
|
except BaseTVinfoError as e:
|
|
logger.warning('Unable to contact %s, aborting: %s' % (sickgear.TVInfoAPI(self.show_obj.tvid).name, ex(e)))
|
|
return
|
|
|
|
if self.force_web:
|
|
self.show_obj.load_imdb_info()
|
|
|
|
try:
|
|
self.show_obj.save_to_db()
|
|
except (BaseException, Exception) as e:
|
|
logger.error('Error saving the show to the database: %s' % ex(e))
|
|
logger.error(traceback.format_exc())
|
|
|
|
# get episode list from DB
|
|
logger.debug('Loading all episodes from the database')
|
|
db_ep_obj_list = self.show_obj.load_episodes_from_db(update=True)
|
|
|
|
# get episode list from TVDB
|
|
logger.debug('Loading all episodes from %s' % sickgear.TVInfoAPI(self.show_obj.tvid).name)
|
|
try:
|
|
tvinfo_ep_list = self.show_obj.load_episodes_from_tvinfo(cache=not self.force, update=True,
|
|
tvinfo_data=self.tvinfo_data, switch=self.switch,
|
|
old_tvid=self.old_tvid, old_prodid=self.old_prodid)
|
|
except BaseTVinfoException as e:
|
|
logger.error(f'Unable to get info from {sickgear.TVInfoAPI(self.show_obj.tvid).name},'
|
|
f' the show info will not be refreshed: {ex(e)}')
|
|
tvinfo_ep_list = None
|
|
|
|
if None is tvinfo_ep_list:
|
|
logger.error('No data returned from %s, unable to update episodes for show: %s' %
|
|
(sickgear.TVInfoAPI(self.show_obj.tvid).name, self.show_obj.unique_name))
|
|
elif not tvinfo_ep_list or 0 == len(tvinfo_ep_list):
|
|
logger.warning('No episodes returned from %s for show: %s' %
|
|
(sickgear.TVInfoAPI(self.show_obj.tvid).name, self.show_obj.unique_name))
|
|
else:
|
|
# for each ep we found on TVDB delete it from the DB list
|
|
for cur_season in tvinfo_ep_list:
|
|
for cur_episode in tvinfo_ep_list[cur_season]:
|
|
logger.debug('Removing %sx%s from the DB list' % (cur_season, cur_episode))
|
|
if cur_season in db_ep_obj_list and cur_episode in db_ep_obj_list[cur_season]:
|
|
del db_ep_obj_list[cur_season][cur_episode]
|
|
|
|
cl = []
|
|
# for the remaining episodes in the DB list just delete them from the DB
|
|
for cur_season in db_ep_obj_list:
|
|
for cur_episode in db_ep_obj_list[cur_season]:
|
|
ep_obj = self.show_obj.get_episode(cur_season, cur_episode) # type: Optional[TVEpisode]
|
|
status = sickgear.common.Quality.split_composite_status(ep_obj.status)[0]
|
|
if self.switch or should_delete_episode(status):
|
|
if self.switch:
|
|
cl.append(self.show_obj.switch_ep_change_sql(
|
|
self.old_tvid, self.old_prodid, cur_season, cur_episode, TVSWITCH_EP_DELETED))
|
|
logger.log(f'Permanently deleting episode {cur_season}x{cur_episode} from the database')
|
|
try:
|
|
cl.extend(ep_obj.delete_episode(return_sql=True))
|
|
except exceptions_helper.EpisodeDeletedException:
|
|
pass
|
|
else:
|
|
logger.log(f'Not deleting episode {cur_season}x{cur_episode} from the database'
|
|
f' because status is: {statusStrings[status]}')
|
|
|
|
if cl:
|
|
my_db = db.DBConnection()
|
|
my_db.mass_action(cl)
|
|
|
|
# update indexer mapper once a month (using the day of the first ep as random date)
|
|
update_over_month = (datetime.date.today() - last_update).days > 31
|
|
try:
|
|
if (self.scheduled_update or update_over_month) and tvinfo_ep_list.get(1, {}).get(1, False):
|
|
first_ep_airdate = self.show_obj.first_aired_regular_episode.airdate
|
|
day = (first_ep_airdate.day, 28)[28 < first_ep_airdate.day]
|
|
if datetime.date.today().day == day or update_over_month or \
|
|
-8 < (datetime.date.today() - first_ep_airdate).days < 31:
|
|
map_indexers_to_show(self.show_obj, force=True)
|
|
except (BaseException, Exception):
|
|
pass
|
|
|
|
if self.priority != generic_queue.QueuePriorities.NORMAL:
|
|
self.kwargs['priority'] = self.priority
|
|
if self.kwargs.get('switch_src', False) or old_name != self.show_obj.name:
|
|
sickgear.webserve.Home.make_showlist_unique_names()
|
|
sickgear.MEMCACHE['history_tab'] = sickgear.webserve.History.menu_tab(
|
|
sickgear.MEMCACHE['history_tab_limit'])
|
|
if not getattr(self, 'skip_refresh', False):
|
|
sickgear.show_queue_scheduler.action.refresh_show(self.show_obj, self.force, self.scheduled_update,
|
|
after_update=True, force_image_cache=self.force_web,
|
|
**self.kwargs)
|
|
|
|
def finish(self):
|
|
self.tvinfo_data = None
|
|
super(QueueItemUpdate, self).finish()
|
|
|
|
|
|
class QueueItemForceUpdate(QueueItemUpdate):
|
|
def __init__(self, show_obj=None, scheduled_update=False, **kwargs):
|
|
# type: (TVShow, bool, Any) -> None
|
|
"""
|
|
|
|
:param show_obj: show object
|
|
:type show_obj: sickgear.tv.TVShow
|
|
:param scheduled_update:
|
|
:type scheduled_update: bool
|
|
:param kwargs:
|
|
:param uid:
|
|
"""
|
|
QueueItemUpdate.__init__(self, show_obj, scheduled_update, **kwargs)
|
|
self.action_id = ShowQueueActions.FORCEUPDATE
|
|
self.force = True # type: bool
|
|
self.force_web = False # type: bool
|
|
self.kwargs = kwargs
|
|
|
|
|
|
class QueueItemForceUpdateWeb(QueueItemUpdate):
|
|
def __init__(self, show_obj=None, scheduled_update=False, priority=generic_queue.QueuePriorities.NORMAL, **kwargs):
|
|
# type: (TVShow, bool, integer_types, Any) -> None
|
|
"""
|
|
|
|
:param show_obj: show object
|
|
:type show_obj: sickgear.tv.TVShow
|
|
:param scheduled_update:
|
|
:type scheduled_update: bool
|
|
:param priority:
|
|
:type priority: int
|
|
:param kwargs:
|
|
:param uid:
|
|
"""
|
|
QueueItemUpdate.__init__(self, show_obj, scheduled_update, **kwargs)
|
|
self.action_id = ShowQueueActions.WEBFORCEUPDATE
|
|
self.force = True # type: bool
|
|
self.force_web = True # type: bool
|
|
self.priority = priority # type: int
|
|
self.kwargs = kwargs
|
|
|
|
|
|
class QueueItemSwitchSource(ShowQueueItem):
|
|
def __init__(self,
|
|
show_obj, # type: TVShow
|
|
new_tvid, # type: integer_types
|
|
new_prodid, # type: integer_types
|
|
force_id=False, # type: bool
|
|
uid=None, # type: integer_types
|
|
set_pause=False, # type: bool
|
|
mark_wanted=False, # type: bool
|
|
resume=False, # type: bool
|
|
old_tvid=None, # type: integer_types
|
|
old_prodid=None, # type: integer_types
|
|
**kwargs):
|
|
"""
|
|
|
|
:param show_obj: TV Show object
|
|
:param new_tvid: new tvid
|
|
:param new_prodid: new prodid
|
|
:param force_id: skip verification and forcibly use new id
|
|
:param uid: uid
|
|
:param set_pause: set pause
|
|
:param mark_wanted: mark wanted after switch
|
|
:param resume: resume unfinished switch (id already switched)
|
|
:param old_tvid: old tvid if resume set
|
|
:param old_prodid: old prodid if resume set
|
|
:param kwargs:
|
|
"""
|
|
ShowQueueItem.__init__(self, ShowQueueActions.SWITCH, show_obj, uid=uid)
|
|
self.new_tvid = new_tvid # type: int
|
|
self.new_prodid = new_prodid # type: integer_types
|
|
self.old_tvid = old_tvid or show_obj.tvid # type: int
|
|
self.old_prodid = old_prodid or show_obj.prodid # type: integer_types
|
|
self.force_id = force_id # type: bool
|
|
self.priority = SWITCH_PRIO # type: int
|
|
self.progress = 'Not Started' # type: AnyStr
|
|
self.set_pause = set_pause # type: bool
|
|
self.mark_wanted = mark_wanted # type: bool
|
|
self.resume = resume # type: bool
|
|
self.kwargs = kwargs # type: Dict
|
|
|
|
def _set_switch_tbl_status(self, status=TVSWITCH_NORMAL):
|
|
# type: (integer_types) -> None
|
|
"""
|
|
sets status in table or deletes the entry if status: TVSWITCH_NORMAL
|
|
:param status:
|
|
"""
|
|
my_db = db.DBConnection()
|
|
if 0 == status:
|
|
my_db.action('DELETE FROM tv_src_switch WHERE uid = ?',
|
|
[self.uid])
|
|
else:
|
|
my_db.action('UPDATE tv_src_switch SET status = ? WHERE uid = ?',
|
|
[status, self.uid])
|
|
|
|
def _set_switch_id(self, new_id):
|
|
# type: (integer_types) -> None
|
|
"""
|
|
set the new prodid of the show in db
|
|
:param new_id:
|
|
"""
|
|
my_db = db.DBConnection()
|
|
my_db.action('UPDATE tv_src_switch SET new_indexer_id = ? WHERE uid = ?',
|
|
[new_id, self.uid])
|
|
|
|
def _check_same_id(self, new_prodid):
|
|
# type: (integer_types) -> bool
|
|
if new_prodid and self.old_tvid == self.new_tvid and new_prodid == self.old_prodid:
|
|
if self.show_obj:
|
|
which_show = self.show_obj.unique_name
|
|
else:
|
|
which_show = '%s:%s' % (self.old_tvid, self.old_prodid)
|
|
self._set_switch_tbl_status(TVSWITCH_SAME_ID)
|
|
logger.error('Unchanged ids given, nothing to do for %s' % which_show)
|
|
return True
|
|
return False
|
|
|
|
def run(self):
|
|
ShowQueueItem.run(self)
|
|
td = None
|
|
if self.resume:
|
|
logger.log('Resume switching show: %s' % self.show_obj.unique_name)
|
|
self.progress = 'Resume switching show'
|
|
with self.show_obj.lock:
|
|
pausestatus_after = None
|
|
if not self.set_pause:
|
|
self.show_obj.paused = False
|
|
if not self.mark_wanted:
|
|
self.show_obj.paused = True
|
|
pausestatus_after = False
|
|
elif not self.show_obj.paused:
|
|
self.show_obj.paused = True
|
|
else:
|
|
logger.log('Start switching show: %s' % self.show_obj.unique_name)
|
|
# verify show before switching
|
|
self.progress = 'Verifying validity of new id'
|
|
|
|
new_prodid = (self.new_prodid or self.show_obj.ids.get(self.new_tvid, {}).get('id'),
|
|
self.new_prodid)[self.force_id and self.new_prodid not in (None, 0)]
|
|
if self._check_same_id(new_prodid):
|
|
return
|
|
if not new_prodid:
|
|
if not self.force_id:
|
|
map_indexers_to_show(self.show_obj, recheck=True)
|
|
if self.show_obj.ids.get(self.new_tvid, {}).get('id') not in (None, 0):
|
|
new_prodid = self.show_obj.ids.get(self.new_tvid)['id']
|
|
|
|
if not new_prodid:
|
|
if self.show_obj:
|
|
which_show = self.show_obj.unique_name
|
|
else:
|
|
which_show = '%s:%s' % (self.old_tvid, self.old_prodid)
|
|
ui.notifications.message('TV info source switch: %s' % which_show,
|
|
'Error: could not find a id for show on new tv info source')
|
|
logger.warning('Error: could not find a id for show on new tv info source: %s' % which_show)
|
|
self._set_switch_tbl_status(TVSWITCH_NO_NEW_ID)
|
|
return
|
|
|
|
if self._check_same_id(new_prodid):
|
|
return
|
|
|
|
try:
|
|
m_show_obj = find_show_by_id({self.new_tvid: new_prodid}, no_mapped_ids=False, check_multishow=True)
|
|
except exceptions_helper.MultipleShowObjectsException:
|
|
msg = 'Duplicate shows in DB'
|
|
if self.show_obj:
|
|
which_show = self.show_obj.unique_name
|
|
else:
|
|
which_show = '%s:%s' % (self.old_tvid, self.old_prodid)
|
|
logger.warning('Duplicate shows in DB for show: %s' % which_show)
|
|
ui.notifications.message('TV info source switch: %s' % which_show, 'Error: %s' % msg)
|
|
|
|
self._set_switch_tbl_status(TVSWITCH_DUPLICATE_SHOW)
|
|
return
|
|
if not self.show_obj or (m_show_obj and self.show_obj is not m_show_obj):
|
|
msg = 'Unable to find the specified show'
|
|
if self.show_obj:
|
|
which_show = self.show_obj.unique_name
|
|
else:
|
|
which_show = '%s:%s' % (self.old_tvid, self.old_prodid)
|
|
ui.notifications.message('TV info source switch: %s' % which_show, 'Error: %s' % msg)
|
|
|
|
self._set_switch_tbl_status(TVSWITCH_SOURCE_NOT_FOUND_ERROR)
|
|
logger.warning('Unable to find the specified show: %s' % which_show)
|
|
return
|
|
|
|
tvinfo_config = sickgear.TVInfoAPI(self.new_tvid).api_params.copy()
|
|
tvinfo_config['cache'] = False
|
|
tvinfo_config['language'] = self.show_obj._lang
|
|
tvinfo_config['dvdorder'] = 0 != self.show_obj._dvdorder
|
|
t = sickgear.TVInfoAPI(self.new_tvid).setup(**tvinfo_config)
|
|
try:
|
|
td = t.get_show(show_id=new_prodid, actors=True, language=self.show_obj._lang)
|
|
except (BaseException, Exception):
|
|
td = None
|
|
if not self.force_id:
|
|
map_indexers_to_show(self.show_obj, recheck=True)
|
|
if new_prodid != self.show_obj.ids.get(self.new_tvid, {}).get('id') is not None:
|
|
new_prodid = self.show_obj.ids.get(self.new_tvid, {}).get('id')
|
|
try:
|
|
td = t.get_show(show_id=new_prodid, actors=True, language=self.show_obj._lang)
|
|
except (BaseException, Exception):
|
|
td = None
|
|
logger.warning(f'Failed to get new tv show id ({new_prodid})'
|
|
f' from source {sickgear.TVInfoAPI(self.new_tvid).name}')
|
|
if None is td:
|
|
self._set_switch_tbl_status(TVSWITCH_NOT_FOUND_ERROR)
|
|
msg = 'Show not found on new tv source'
|
|
if self.show_obj:
|
|
which_show = self.show_obj.unique_name
|
|
else:
|
|
which_show = '%s:%s' % (self.old_tvid, self.old_prodid)
|
|
ui.notifications.message('TV info source switch: %s' % which_show, 'Error: %s' % msg)
|
|
logger.warning('show: %s not found on new tv source' % self.show_obj.tvid_prodid)
|
|
return
|
|
|
|
try:
|
|
new_show_startyear = parser().parse(td[1][1].firstaired).year
|
|
if 1900 > new_show_startyear:
|
|
new_show_startyear = None
|
|
except (BaseException, Exception):
|
|
new_show_startyear = None
|
|
if not new_show_startyear:
|
|
try:
|
|
new_show_startyear = parser().parse(td.firstaired).year
|
|
except (BaseException, Exception):
|
|
new_show_startyear = None
|
|
|
|
try:
|
|
first_ep = self.show_obj.first_aired_regular_episode
|
|
existing_show_startyear = first_ep and first_ep.airdate and first_ep.airdate.year
|
|
if existing_show_startyear and 1900 > existing_show_startyear:
|
|
existing_show_startyear = None
|
|
except (BaseException, Exception):
|
|
existing_show_startyear = None
|
|
if not existing_show_startyear:
|
|
existing_show_startyear = self.show_obj.startyear
|
|
|
|
if not self.force_id \
|
|
and not ((clean_show_name(td.seriesname.lower()) == clean_show_name(self.show_obj.name.lower()) or
|
|
any(1 for s, v in td.ids if v and v == self.show_obj.ids.get(s, {}).get('id')))
|
|
and (str(existing_show_startyear) == str(new_show_startyear)
|
|
or abs(try_int(existing_show_startyear, 10) - try_int(new_show_startyear, 1)) <= 1)):
|
|
self._set_switch_tbl_status(TVSWITCH_VERIFY_ERROR)
|
|
logger.warning('Failed to verify new ids for show %s' % self.show_obj.unique_name)
|
|
msg = 'Failed to verify the show on new source'
|
|
if self.show_obj:
|
|
which_show = self.show_obj.unique_name
|
|
else:
|
|
which_show = '%s:%s' % (self.old_tvid, self.old_prodid)
|
|
ui.notifications.message('TV info source switch: %s' % which_show, 'Error: %s' % msg)
|
|
return
|
|
# switch show to new id
|
|
with self.show_obj.lock:
|
|
try:
|
|
new_show_obj = find_show_by_id({self.new_tvid: new_prodid})
|
|
except (BaseException, Exception):
|
|
new_show_obj = None
|
|
if new_show_obj:
|
|
self._set_switch_tbl_status(TVSWITCH_ID_CONFLICT)
|
|
msg = 'Show %s new id conflicts with existing show: %s' % \
|
|
('[%s (%s)]' % (self.show_obj.unique_name, self.show_obj.tvid_prodid),
|
|
'[%s (%s)]' % (new_show_obj.unique_name, new_show_obj.tvid_prodid))
|
|
logger.warning(msg)
|
|
return
|
|
self.progress = 'Switching to new source'
|
|
self._set_switch_id(new_prodid)
|
|
self.show_obj.remove_character_images()
|
|
self.show_obj._tvid = self.new_tvid
|
|
self.show_obj._prodid = new_prodid
|
|
new_tvid_prodid = TVidProdid({self.new_tvid: new_prodid})()
|
|
old_tvid_prodid = TVidProdid({self.old_tvid: self.old_prodid})()
|
|
for tp in (old_tvid_prodid, new_tvid_prodid):
|
|
try:
|
|
if tp in sickgear.switched_shows:
|
|
sickgear.switched_shows.pop(tp)
|
|
except (BaseException, Exception):
|
|
pass
|
|
try:
|
|
if tp in itervalues(sickgear.switched_shows):
|
|
sickgear.switched_shows = {k: v for k, v in iteritems(sickgear.switched_shows) if tp != v}
|
|
except (BaseException, Exception):
|
|
pass
|
|
sickgear.switched_shows[TVidProdid({self.old_tvid: self.old_prodid})()] = \
|
|
TVidProdid({self.new_tvid: new_prodid})()
|
|
pausestatus_after = None
|
|
if not self.set_pause:
|
|
self.show_obj.paused = False
|
|
if not self.mark_wanted:
|
|
self.show_obj.paused = True
|
|
pausestatus_after = False
|
|
elif not self.show_obj.paused:
|
|
self.show_obj.paused = True
|
|
self.show_obj.switch_infosrc(self.old_tvid, self.old_prodid, update_show=False,
|
|
pausestatus_after=pausestatus_after)
|
|
|
|
# we directly update and refresh the show without queue as part of the switch
|
|
self.progress = 'Updating from new source'
|
|
update_show = QueueItemUpdate(show_obj=self.show_obj, skip_refresh=True, pausestatus_after=pausestatus_after,
|
|
switch=True, tvinfo_data=td, old_tvid=self.old_tvid, old_prodid=self.old_prodid,
|
|
switch_src=True)
|
|
update_show.run()
|
|
self.progress = 'Refreshing from disk'
|
|
refresh_show = QueueItemRefresh(show_obj=self.show_obj, force_image_cache=True,
|
|
pausestatus_after=pausestatus_after, switch=True, force=True)
|
|
refresh_show.run()
|
|
self.progress = 'Finished Switch'
|
|
# now remove from switch tbl
|
|
self._set_switch_tbl_status()
|
|
logger.log('Finished switching show: %s' % self.show_obj.unique_name)
|
|
ui.notifications.message('TV info source switch: %s' % self.show_obj.unique_name, 'Finished switching show')
|
|
|
|
def __str__(self):
|
|
return '<Show Switch Queue Item: %s (%s to %s)>' % \
|
|
(self.show_obj.unique_name, sickgear.TVInfoAPI(self.old_tvid).name,
|
|
(sickgear.TVInfoAPI(self.new_tvid).name, self.new_prodid)[self.old_tvid == self.new_tvid])
|
|
|
|
def __repr__(self):
|
|
return self.__str__()
|