295 lines
11 KiB
Python
295 lines
11 KiB
Python
# coding=utf-8
|
|
import traceback
|
|
import types
|
|
import datetime
|
|
import subprocess
|
|
import os
|
|
import operator
|
|
|
|
from func import enable_channel_wrapper, route_wrapper, register_route_function
|
|
from subzero.lib.io import get_viable_encoding
|
|
from subzero.language import Language
|
|
from support.i18n import is_localized_string, _
|
|
from support.items import get_kind, get_item_thumb, get_item, get_item_kind_from_item, refresh_item
|
|
from support.helpers import get_video_display_title, pad_title, display_language, quote_args, is_stream_forced, \
|
|
get_title_for_video_metadata, mswindows
|
|
from support.history import get_history
|
|
from support.ignore import get_decision_list
|
|
from support.lib import get_intent
|
|
from support.config import config
|
|
from subzero.constants import ICON_SUB, ICON
|
|
from support.plex_media import get_part, get_plex_metadata
|
|
from support.scheduler import scheduler
|
|
from support.scanning import scan_videos
|
|
from support.storage import save_subtitles
|
|
|
|
from subliminal_patch.subtitle import ModifiedSubtitle
|
|
|
|
default_thumb = R(ICON_SUB)
|
|
main_icon = ICON if not config.is_development else "icon-dev.jpg"
|
|
|
|
# noinspection PyUnboundLocalVariable
|
|
route = route_wrapper
|
|
# noinspection PyUnboundLocalVariable
|
|
handler = enable_channel_wrapper(handler)
|
|
|
|
|
|
def add_incl_excl_options(oc, kind, callback_menu=None, title=None, rating_key=None, add_kind=True):
|
|
"""
|
|
|
|
:param oc: oc to add our options to
|
|
:param kind: movie, show, episode ... - gets translated to the ignore key (sections, series, items)
|
|
:param callback_menu: menu to inject
|
|
:param title:
|
|
:param rating_key:
|
|
:return:
|
|
"""
|
|
# try to translate kind to the ignore key
|
|
use_kind = kind
|
|
ref_list = get_decision_list()
|
|
if kind not in ref_list:
|
|
use_kind = ref_list.translate_key(kind)
|
|
if not use_kind or use_kind not in ref_list:
|
|
return
|
|
|
|
in_list = rating_key in ref_list[use_kind]
|
|
include = ref_list.store == "include"
|
|
|
|
if include:
|
|
t = u"Enable Sub-Zero for %(kind)s \"%(title)s\""
|
|
if in_list:
|
|
t = u"Disable Sub-Zero for %(kind)s \"%(title)s\""
|
|
else:
|
|
t = u"Ignore %(kind)s \"%(title)s\""
|
|
if in_list:
|
|
t = u"Un-ignore %(kind)s \"%(title)s\""
|
|
|
|
oc.add(DirectoryObject(
|
|
key=Callback(callback_menu, kind=use_kind, sure=False, todo="not_set", rating_key=str(rating_key), title=title),
|
|
title=_(t,
|
|
kind=ref_list.verbose(kind) if add_kind else "",
|
|
title=unicode(title))
|
|
)
|
|
)
|
|
|
|
|
|
def dig_tree(oc, items, menu_callback, menu_determination_callback=None, force_rating_key=None, fill_args=None,
|
|
pass_kwargs=None, thumb=default_thumb):
|
|
for kind, title, key, dig_deeper, item in items:
|
|
thumb = get_item_thumb(item) or thumb
|
|
|
|
add_kwargs = {}
|
|
if fill_args:
|
|
add_kwargs = dict((name, getattr(item, k)) for k, name in fill_args.iteritems() if item and hasattr(item, k))
|
|
if pass_kwargs:
|
|
add_kwargs.update(pass_kwargs)
|
|
|
|
# force details view for show/season
|
|
summary = " " if kind in ("show", "season") else None
|
|
|
|
oc.add(DirectoryObject(
|
|
key=Callback(menu_callback or menu_determination_callback(kind, item, pass_kwargs=pass_kwargs), title=title,
|
|
rating_key=force_rating_key or key, **add_kwargs),
|
|
title=pad_title(title) if kind in ("show", "season") else title, thumb=thumb, summary=summary
|
|
))
|
|
return oc
|
|
|
|
|
|
def set_refresh_menu_state(state_or_media, media_type="movies"):
|
|
"""
|
|
|
|
:param state_or_media: string, None, or Media argument from Agent.update()
|
|
:param media_type: movies or series
|
|
:return:
|
|
"""
|
|
if not state_or_media:
|
|
# store it in last state and remove the current
|
|
Dict["last_refresh_state"] = Dict["current_refresh_state"]
|
|
Dict["current_refresh_state"] = None
|
|
Dict.Save()
|
|
return
|
|
|
|
if isinstance(state_or_media, types.StringTypes) or is_localized_string(state_or_media):
|
|
Dict["current_refresh_state"] = unicode(state_or_media)
|
|
Dict.Save()
|
|
return
|
|
|
|
media = state_or_media
|
|
media_id = media.id
|
|
title = None
|
|
if media_type == "series":
|
|
for season in media.seasons:
|
|
for episode in media.seasons[season].episodes:
|
|
ep = media.seasons[season].episodes[episode]
|
|
media_id = ep.id
|
|
title = get_video_display_title(_("show"), ep.title, parent_title=media.title, season=int(season), episode=int(episode))
|
|
else:
|
|
title = get_video_display_title(_("movie"), media.title)
|
|
|
|
intent = get_intent()
|
|
force_refresh = intent.get("force", media_id)
|
|
|
|
t = u"Refreshing %(title)s"
|
|
if force_refresh:
|
|
t = u"Force-refreshing %(title)s"
|
|
|
|
Dict["current_refresh_state"] = unicode(_(t,
|
|
title=unicode(title)))
|
|
Dict.Save()
|
|
|
|
|
|
def get_item_task_data(task_name, rating_key, language):
|
|
task_data = scheduler.get_task_data(task_name)
|
|
search_results = task_data.get(rating_key, {}) if task_data else {}
|
|
return search_results.get(language)
|
|
|
|
|
|
def debounce(func):
|
|
"""
|
|
prevent func from being called twice with the same arguments
|
|
:param func:
|
|
:return:
|
|
"""
|
|
|
|
func.debounce = True
|
|
|
|
return func
|
|
|
|
|
|
def extract_embedded_sub(**kwargs):
|
|
rating_key = kwargs["rating_key"]
|
|
part_id = kwargs.pop("part_id")
|
|
stream_index = kwargs.pop("stream_index")
|
|
with_mods = kwargs.pop("with_mods", False)
|
|
language = Language.fromietf(kwargs.pop("language"))
|
|
refresh = kwargs.pop("refresh", True)
|
|
set_current = kwargs.pop("set_current", True)
|
|
|
|
plex_item = kwargs.pop("plex_item", get_item(rating_key))
|
|
item_type = get_item_kind_from_item(plex_item)
|
|
part = kwargs.pop("part", get_part(plex_item, part_id))
|
|
scanned_videos = kwargs.pop("scanned_videos", None)
|
|
extract_mode = kwargs.pop("extract_mode", "a")
|
|
|
|
any_successful = False
|
|
|
|
if part:
|
|
if not scanned_videos:
|
|
metadata = get_plex_metadata(rating_key, part_id, item_type, plex_item=plex_item)
|
|
scanned_videos = scan_videos([metadata], ignore_all=True, skip_hashing=True)
|
|
|
|
for stream in part.streams:
|
|
# subtitle stream
|
|
if str(stream.index) == stream_index:
|
|
is_forced = is_stream_forced(stream)
|
|
bn = os.path.basename(part.file)
|
|
|
|
set_refresh_menu_state(_(u"Extracting subtitle %(stream_index)s of %(filename)s",
|
|
stream_index=stream_index,
|
|
filename=bn))
|
|
Log.Info(u"Extracting stream %s (%s) of %s", stream_index, str(language), bn)
|
|
|
|
out_codec = stream.codec if stream.codec != "mov_text" else "srt"
|
|
|
|
args = [
|
|
config.plex_transcoder, "-i", part.file, "-map", "0:%s" % stream_index, "-f", out_codec, "-"
|
|
]
|
|
|
|
cmdline = quote_args(args)
|
|
Log.Debug(u"Calling: %s", cmdline)
|
|
if mswindows:
|
|
Log.Debug("MSWindows: Fixing encoding")
|
|
cmdline = cmdline.encode("mbcs")
|
|
|
|
output = None
|
|
try:
|
|
output = subprocess.check_output(cmdline, stderr=subprocess.PIPE, shell=True)
|
|
except:
|
|
Log.Error("Extraction failed: %s", traceback.format_exc())
|
|
|
|
if output:
|
|
subtitle = ModifiedSubtitle(language, mods=config.default_mods if with_mods else None)
|
|
subtitle.content = output
|
|
subtitle.provider_name = "embedded"
|
|
subtitle.id = "stream_%s" % stream_index
|
|
subtitle.score = 0
|
|
subtitle.set_encoding("utf-8")
|
|
|
|
# fixme: speedup video; only video.name is needed
|
|
video = scanned_videos.keys()[0]
|
|
save_successful = save_subtitles(scanned_videos, {video: [subtitle]}, mode="m",
|
|
set_current=set_current)
|
|
set_refresh_menu_state(None)
|
|
|
|
if save_successful and refresh:
|
|
refresh_item(rating_key)
|
|
|
|
# add item to history
|
|
item_title = get_title_for_video_metadata(video.plexapi_metadata,
|
|
add_section_title=False, add_episode_title=True)
|
|
|
|
history = get_history()
|
|
history.add(item_title, video.id, section_title=video.plexapi_metadata["section"],
|
|
thumb=video.plexapi_metadata["super_thumb"],
|
|
subtitle=subtitle, mode=extract_mode)
|
|
history.destroy()
|
|
|
|
any_successful = True
|
|
|
|
return any_successful
|
|
|
|
|
|
class SZObjectContainer(ObjectContainer):
|
|
def __init__(self, *args, **kwargs):
|
|
skip_pin_lock = kwargs.pop("skip_pin_lock", False)
|
|
|
|
super(SZObjectContainer, self).__init__(*args, **kwargs)
|
|
|
|
if (config.lock_menu or config.lock_advanced_menu) and not config.pin_correct and not skip_pin_lock:
|
|
config.locked = True
|
|
|
|
def add(self, *args, **kwargs):
|
|
# disable self.add if we're in lockdown
|
|
container = args[0]
|
|
current_menu_target = container.key.split("?")[0]
|
|
is_pin_menu = current_menu_target.endswith("/pin")
|
|
|
|
if config.locked and config.lock_menu and not is_pin_menu:
|
|
return
|
|
return super(SZObjectContainer, self).add(*args, **kwargs)
|
|
|
|
|
|
OriginalObjectContainer = ObjectContainer
|
|
ObjectContainer = SZObjectContainer
|
|
|
|
|
|
class SubFolderObjectContainer(ObjectContainer):
|
|
def __init__(self, *args, **kwargs):
|
|
super(SubFolderObjectContainer, self).__init__(*args, **kwargs)
|
|
from interface.menu import fatality
|
|
from support.helpers import pad_title, timestamp
|
|
self.add(DirectoryObject(
|
|
key=Callback(fatality, force_title=" ", randomize=timestamp()),
|
|
title=pad_title(_("<< Back to home")),
|
|
summary=_("Current state: %s; Last state: %s",
|
|
(Dict["current_refresh_state"] or _("Idle")) if "current_refresh_state" in Dict else _("Idle"),
|
|
(Dict["last_refresh_state"] or _("None")) if "last_refresh_state" in Dict else _("None")
|
|
)
|
|
))
|
|
|
|
|
|
ObjectClass = getattr(getattr(Redirect, "_object_class"), "__bases__")[0]
|
|
|
|
|
|
class ZipObject(ObjectClass):
|
|
def __init__(self, data):
|
|
ObjectClass.__init__(self, "")
|
|
self.zipdata = data
|
|
self.SetHeader("Content-Type", "application/zip")
|
|
|
|
def Content(self):
|
|
self.SetHeader("Content-Disposition",
|
|
'attachment; filename="' + datetime.datetime.now().strftime("Logs_%y%m%d_%H-%M-%S.zip")
|
|
+ '"')
|
|
return self.zipdata
|