378 lines
15 KiB
Python
378 lines
15 KiB
Python
# coding=utf-8
|
|
import locale
|
|
import logging
|
|
import os
|
|
import platform
|
|
import traceback
|
|
|
|
import logger
|
|
import copy
|
|
|
|
from requests import HTTPError
|
|
from item_details import ItemDetailsMenu
|
|
from refresh_item import RefreshItem
|
|
from menu_helpers import add_incl_excl_options, dig_tree, set_refresh_menu_state, \
|
|
default_thumb, debounce, ObjectContainer, SubFolderObjectContainer, route
|
|
from main import fatality, InclExclMenu
|
|
from advanced import DispatchRestart
|
|
from subzero.constants import ART, PREFIX, DEPENDENCY_MODULE_NAMES
|
|
from support.extract import season_extract_embedded
|
|
from support.scheduler import scheduler
|
|
from support.config import config
|
|
from support.helpers import timestamp, df, display_language
|
|
from support.ignore import get_decision_list
|
|
from support.items import get_all_items, get_items_info, get_item_kind_from_rating_key, get_item, get_item_title
|
|
from support.i18n import _
|
|
|
|
# init GUI
|
|
ObjectContainer.art = R(ART)
|
|
ObjectContainer.no_cache = True
|
|
|
|
# default thumb for DirectoryObjects
|
|
DirectoryObject.thumb = default_thumb
|
|
Plugin.AddViewGroup("full_details", viewMode="InfoList", mediaType="items", type="list", summary=2)
|
|
|
|
|
|
@route(PREFIX + '/section/firstLetter/key', deeper=bool)
|
|
def FirstLetterMetadataMenu(rating_key, key, title=None, base_title=None, display_items=False, previous_item_type=None,
|
|
previous_rating_key=None):
|
|
"""
|
|
displays the contents of a section filtered by the first letter
|
|
:param rating_key: actually is the section's key
|
|
:param key: the firstLetter wanted
|
|
:param title: the first letter, or #
|
|
:param deeper:
|
|
:return:
|
|
"""
|
|
title = base_title + " > " + unicode(title)
|
|
oc = SubFolderObjectContainer(title2=title, no_cache=True, no_history=True)
|
|
|
|
items = get_all_items(key="first_character", value=[rating_key, key], base="library/sections", flat=False)
|
|
kind, deeper = get_items_info(items)
|
|
dig_tree(oc, items, MetadataMenu,
|
|
pass_kwargs={"base_title": title, "display_items": deeper, "previous_item_type": kind,
|
|
"previous_rating_key": rating_key})
|
|
return oc
|
|
|
|
|
|
@route(PREFIX + '/section/contents', display_items=bool)
|
|
def MetadataMenu(rating_key, title=None, base_title=None, display_items=False, previous_item_type=None,
|
|
previous_rating_key=None, message=None, header=None, randomize=None):
|
|
"""
|
|
displays the contents of a section based on whether it has a deeper tree or not (movies->movie (item) list; series->series list)
|
|
:param rating_key:
|
|
:param title:
|
|
:param base_title:
|
|
:param display_items:
|
|
:param previous_item_type:
|
|
:param previous_rating_key:
|
|
:return:
|
|
"""
|
|
title = unicode(title)
|
|
item_title = title
|
|
title = base_title + " > " + title
|
|
oc = SubFolderObjectContainer(title2=title, no_cache=True, no_history=True, header=header, message=message,
|
|
view_group="full_details")
|
|
|
|
current_kind = get_item_kind_from_rating_key(rating_key)
|
|
|
|
if display_items:
|
|
timeout = 30
|
|
show = None
|
|
|
|
# add back to series for season
|
|
if current_kind == "season":
|
|
timeout = 720
|
|
|
|
show = get_item(previous_rating_key)
|
|
oc.add(DirectoryObject(
|
|
key=Callback(MetadataMenu, rating_key=show.rating_key, title=show.title, base_title=show.section.title,
|
|
previous_item_type="section", display_items=True, randomize=timestamp()),
|
|
title=_(u"< Back to %s", show.title),
|
|
thumb=show.thumb or default_thumb
|
|
))
|
|
elif current_kind == "series":
|
|
# it shouldn't take more than 6 minutes to scan all of a series' files and determine the force refresh
|
|
timeout = 3600
|
|
|
|
items = get_all_items(key="children", value=rating_key, base="library/metadata")
|
|
kind, deeper = get_items_info(items)
|
|
dig_tree(oc, items, MetadataMenu,
|
|
pass_kwargs={"base_title": title, "display_items": deeper, "previous_item_type": kind,
|
|
"previous_rating_key": rating_key})
|
|
|
|
# we don't know exactly where we are here, only add ignore option to series
|
|
if current_kind in ("series", "season"):
|
|
item = get_item(rating_key)
|
|
sub_title = get_item_title(item)
|
|
add_incl_excl_options(oc, current_kind, title=sub_title, rating_key=rating_key, callback_menu=InclExclMenu)
|
|
|
|
# mass-extract embedded
|
|
if current_kind == "season" and config.plex_transcoder:
|
|
for lang in config.lang_list:
|
|
oc.add(DirectoryObject(
|
|
key=Callback(SeasonExtractEmbedded, rating_key=rating_key, language=lang,
|
|
base_title=show.section.title, display_items=display_items, item_title=item_title,
|
|
title=title,
|
|
previous_item_type=previous_item_type, with_mods=True,
|
|
previous_rating_key=previous_rating_key, randomize=timestamp()),
|
|
title=_(u"Extract missing %(language)s embedded subtitles", language=display_language(lang)),
|
|
summary=_("Extracts the not yet extracted embedded subtitles of all episodes for the current "
|
|
"season with all configured default modifications")
|
|
))
|
|
oc.add(DirectoryObject(
|
|
key=Callback(SeasonExtractEmbedded, rating_key=rating_key, language=lang,
|
|
base_title=show.section.title, display_items=display_items, item_title=item_title,
|
|
title=title, force=True,
|
|
previous_item_type=previous_item_type, with_mods=True,
|
|
previous_rating_key=previous_rating_key, randomize=timestamp()),
|
|
title=_(u"Extract and activate %(language)s embedded subtitles", language=display_language(lang)),
|
|
summary=_("Extracts embedded subtitles of all episodes for the current season "
|
|
"with all configured default modifications")
|
|
))
|
|
|
|
# add refresh
|
|
oc.add(DirectoryObject(
|
|
key=Callback(RefreshItem, rating_key=rating_key, item_title=title, refresh_kind=current_kind,
|
|
previous_rating_key=previous_rating_key, timeout=timeout * 1000, randomize=timestamp()),
|
|
title=_(u"Refresh: %s", item_title),
|
|
summary=_("Refreshes %(the_movie_series_season_episode)s, possibly searching for missing and picking up "
|
|
"new subtitles on disk", the_movie_series_season_episode=_(u"the %s" % current_kind))
|
|
))
|
|
oc.add(DirectoryObject(
|
|
key=Callback(RefreshItem, rating_key=rating_key, item_title=title, force=True,
|
|
refresh_kind=current_kind, previous_rating_key=previous_rating_key, timeout=timeout * 1000,
|
|
randomize=timestamp()),
|
|
title=_(u"Auto-Find subtitles: %s", item_title),
|
|
summary=_("Issues a forced refresh, ignoring known subtitles and searching for new ones")
|
|
))
|
|
else:
|
|
return ItemDetailsMenu(rating_key=rating_key, title=title, item_title=item_title)
|
|
|
|
return oc
|
|
|
|
|
|
@route(PREFIX + '/season/extract_embedded/{rating_key}/{language}')
|
|
def SeasonExtractEmbedded(**kwargs):
|
|
rating_key = kwargs.get("rating_key")
|
|
requested_language = kwargs.pop("language")
|
|
with_mods = kwargs.pop("with_mods")
|
|
item_title = kwargs.pop("item_title")
|
|
title = kwargs.pop("title")
|
|
force = kwargs.pop("force", False)
|
|
|
|
Thread.Create(season_extract_embedded, **{"rating_key": rating_key, "requested_language": requested_language,
|
|
"with_mods": with_mods, "force": force})
|
|
|
|
kwargs["header"] = _("Success")
|
|
kwargs["message"] = _(u"Extracting of embedded subtitles for %s triggered", title)
|
|
|
|
kwargs.pop("randomize")
|
|
return MetadataMenu(randomize=timestamp(), title=item_title, **kwargs)
|
|
|
|
|
|
@route(PREFIX + '/ignore_list')
|
|
def IgnoreListMenu():
|
|
ref_list = get_decision_list()
|
|
include = ref_list.store == "include"
|
|
list_title = _("Include list" if include else "Ignore list")
|
|
oc = SubFolderObjectContainer(title2=list_title, replace_parent=True)
|
|
for key in ref_list.key_order:
|
|
values = ref_list[key]
|
|
for value in values:
|
|
add_incl_excl_options(oc, key, title=ref_list.get_title(key, value), rating_key=value,
|
|
callback_menu=InclExclMenu)
|
|
return oc
|
|
|
|
|
|
@route(PREFIX + '/history')
|
|
def HistoryMenu():
|
|
from support.history import get_history
|
|
history = get_history()
|
|
oc = SubFolderObjectContainer(title2=_("History"), replace_parent=True)
|
|
|
|
for item in history.items[:100]:
|
|
possible_language = item.language
|
|
language_display = item.lang_name if not possible_language else display_language(possible_language)
|
|
|
|
oc.add(DirectoryObject(
|
|
key=Callback(ItemDetailsMenu, title=item.title, item_title=item.item_title,
|
|
rating_key=item.rating_key),
|
|
title=u"%s (%s)" % (item.item_title, _(item.mode_verbose)),
|
|
summary=_(u"%s in %s (%s, score: %s), %s", language_display, item.section_title,
|
|
_(item.provider_name), item.score, df(item.time)),
|
|
thumb=item.thumb or default_thumb
|
|
))
|
|
|
|
history.destroy()
|
|
|
|
return oc
|
|
|
|
|
|
@route(PREFIX + '/missing/refresh')
|
|
@debounce
|
|
def RefreshMissing(randomize=None):
|
|
scheduler.dispatch_task("SearchAllRecentlyAddedMissing")
|
|
header = "Refresh of recently added items with missing subtitles triggered"
|
|
return fatality(header=header, replace_parent=True)
|
|
|
|
|
|
def replace_item(obj, key, replace_value):
|
|
for k, v in obj.items():
|
|
if isinstance(v, dict):
|
|
obj[k] = replace_item(v, key, replace_value)
|
|
if key in obj:
|
|
obj[key] = replace_value
|
|
return obj
|
|
|
|
|
|
def check_connections():
|
|
# debug drone
|
|
Log.Debug("Checking connections ...")
|
|
log_buffer = []
|
|
try:
|
|
from subliminal_patch.refiners.drone import SonarrClient, RadarrClient
|
|
log_buffer.append(["----- Connections -----"])
|
|
for key, cls in [("sonarr", SonarrClient), ("radarr", RadarrClient)]:
|
|
if key in config.refiner_settings:
|
|
cname = key.capitalize()
|
|
try:
|
|
status = cls(**config.refiner_settings[key]).status(timeout=5)
|
|
except HTTPError, e:
|
|
if e.response.status_code == 401:
|
|
log_buffer.append(("%s: NOT WORKING - BAD API KEY", cname))
|
|
else:
|
|
log_buffer.append(("%s: NOT WORKING - %s", cname, traceback.format_exc()))
|
|
except:
|
|
log_buffer.append(("%s: NOT WORKING - %s", cname, traceback.format_exc()))
|
|
else:
|
|
if status and status["version"]:
|
|
log_buffer.append(("%s: OK - %s", cname, status["version"]))
|
|
else:
|
|
log_buffer.append(("%s: NOT WORKING - %s", cname))
|
|
except:
|
|
log_buffer.append(("Something went really wrong when evaluating Sonarr/Radarr: %s", traceback.format_exc()))
|
|
finally:
|
|
Core.log.setLevel(logging.DEBUG)
|
|
for entry in log_buffer:
|
|
Log.Debug(*entry)
|
|
|
|
Core.log.setLevel(logging.getLevelName(Prefs["log_level"]))
|
|
|
|
|
|
@route(PREFIX + '/ValidatePrefs', enforce_route=True)
|
|
def ValidatePrefs():
|
|
Core.log.setLevel(logging.DEBUG)
|
|
|
|
if Prefs["log_console"]:
|
|
Core.log.addHandler(logger.console_handler)
|
|
Log.Debug("Logging to console from now on")
|
|
else:
|
|
Core.log.removeHandler(logger.console_handler)
|
|
Log.Debug("Stop logging to console")
|
|
|
|
# cache the channel state
|
|
update_dict = False
|
|
restart = False
|
|
|
|
# reset pin
|
|
Dict["pin_correct_time"] = None
|
|
|
|
config.initialize()
|
|
if "channel_enabled" not in Dict:
|
|
update_dict = True
|
|
|
|
elif Dict["channel_enabled"] != config.enable_channel:
|
|
Log.Debug("Interface features %s, restarting plugin", "enabled" if config.enable_channel else "disabled")
|
|
update_dict = True
|
|
restart = True
|
|
|
|
if "plugin_pin_mode2" not in Dict:
|
|
update_dict = True
|
|
|
|
elif Dict["plugin_pin_mode2"] != Prefs["plugin_pin_mode2"]:
|
|
update_dict = True
|
|
restart = True
|
|
|
|
if update_dict:
|
|
Dict["channel_enabled"] = config.enable_channel
|
|
Dict["plugin_pin_mode2"] = Prefs["plugin_pin_mode2"]
|
|
Dict.Save()
|
|
|
|
if restart:
|
|
scheduler.stop()
|
|
DispatchRestart()
|
|
return
|
|
|
|
scheduler.setup_tasks()
|
|
scheduler.clear_task_data("MissingSubtitles")
|
|
set_refresh_menu_state(None)
|
|
|
|
Log.Debug("Validate Prefs called.")
|
|
|
|
# SZ config debug
|
|
Log.Debug("--- SZ Config-Debug ---")
|
|
for attr in [
|
|
"version", "app_support_path", "data_path", "data_items_path", "enable_agent",
|
|
"enable_channel", "permissions_ok", "missing_permissions", "fs_encoding",
|
|
"subtitle_destination_folder", "include", "include_exclude_paths", "include_exclude_sz_files",
|
|
"new_style_cache", "dbm_supported", "lang_list", "providers", "normal_subs", "forced_only", "forced_also",
|
|
"plex_transcoder", "refiner_settings", "unrar", "adv_cfg_path", "use_custom_dns",
|
|
"has_anticaptcha", "anticaptcha_cls", "mediainfo_bin"]:
|
|
|
|
value = getattr(config, attr)
|
|
if isinstance(value, dict):
|
|
d = replace_item(copy.deepcopy(value), "api_key", "xxxxxxxxxxxxxxxxxxxxxxxxx")
|
|
Log.Debug("config.%s: %s", attr, d)
|
|
continue
|
|
|
|
if attr in ("api_key",):
|
|
value = "xxxxxxxxxxxxxxxxxxxxxxxxx"
|
|
|
|
Log.Debug("config.%s: %s", attr, value)
|
|
|
|
for attr in ["plugin_log_path", "server_log_path"]:
|
|
value = getattr(config, attr)
|
|
|
|
if value:
|
|
access = os.access(value, os.R_OK)
|
|
if Core.runtime.os == "Windows":
|
|
try:
|
|
f = open(value, "r")
|
|
f.read(1)
|
|
f.close()
|
|
except:
|
|
access = False
|
|
|
|
Log.Debug("config.%s: %s (accessible: %s)", attr, value, access)
|
|
|
|
for attr in [
|
|
"subtitles.save.filesystem", ]:
|
|
Log.Debug("Pref.%s: %s", attr, Prefs[attr])
|
|
|
|
if "sonarr" in config.refiner_settings or "radarr" in config.refiner_settings:
|
|
Thread.Create(check_connections)
|
|
|
|
# fixme: check existance of and os access of logs
|
|
Log.Debug("----- Environment -----")
|
|
Log.Debug("Platform: %s", Core.runtime.platform)
|
|
Log.Debug("OS: %s", Core.runtime.os)
|
|
Log.Debug("Python: %s", platform.python_version())
|
|
for key, value in os.environ.iteritems():
|
|
if key.startswith("PLEX") or key.startswith("SZ_"):
|
|
if "TOKEN" in key:
|
|
outval = "xxxxxxxxxxxxxxxxxxx"
|
|
|
|
else:
|
|
outval = value
|
|
Log.Debug("%s: %s", key, outval)
|
|
Log.Debug("Locale: %s", locale.getdefaultlocale())
|
|
Log.Debug("-----------------------")
|
|
|
|
Log.Debug("Setting log-level to %s", Prefs["log_level"])
|
|
logger.register_logging_handler(DEPENDENCY_MODULE_NAMES, level=Prefs["log_level"])
|
|
Core.log.setLevel(logging.getLevelName(Prefs["log_level"]))
|
|
os.environ['U1pfT01EQl9LRVk'] = '789CF30DAC2C8B0AF433F5C9AD34290A712DF30D7135F12D0FB3E502006FDE081E'
|
|
|
|
return
|