Compare commits
19
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fc2208f9e5 | ||
|
|
1a4eb366bb | ||
|
|
b89c64a2c2 | ||
|
|
68e8f6e753 | ||
|
|
f15cc4cb3c | ||
|
|
903273e3ef | ||
|
|
1c9b744d31 | ||
|
|
7c0fb29886 | ||
|
|
2505a7510c | ||
|
|
0a66db40a2 | ||
|
|
6c68893979 | ||
|
|
c512eab0b6 | ||
|
|
3cedd4bd0f | ||
|
|
0759c5e4c6 | ||
|
|
ad6cf4be79 | ||
|
|
23c3899fb2 | ||
|
|
1a6515a660 | ||
|
|
58815a7650 | ||
|
|
c15ec9fefc |
No files matched your search
@@ -24,7 +24,6 @@ import support
|
||||
import interface
|
||||
sys.modules["interface"] = interface
|
||||
|
||||
from subliminal.cli import MutexLock
|
||||
from subzero.constants import OS_PLEX_USERAGENT, PERSONAL_MEDIA_IDENTIFIER
|
||||
from interface.menu import *
|
||||
from support.plex_media import media_to_videos, get_media_item_ids, scan_videos
|
||||
|
||||
@@ -18,3 +18,6 @@ sys.modules["interface.refresh_item"] = refresh_item
|
||||
|
||||
import item_details
|
||||
sys.modules["interface.item_details"] = item_details
|
||||
|
||||
import sub_mod
|
||||
sys.modules["interface.modification"] = sub_mod
|
||||
@@ -148,24 +148,34 @@ def TriggerStorageMaintenance(randomize=None):
|
||||
|
||||
@route(PREFIX + '/get_logs_link')
|
||||
def GetLogsLink():
|
||||
if not config.plex_token:
|
||||
oc = ObjectContainer(title2="Download Logs", no_cache=True, no_history=True,
|
||||
header="Sorry, feature unavailable",
|
||||
message="Universal Plex token not available")
|
||||
return oc
|
||||
|
||||
# try getting the link base via the request in context, first, otherwise use the public ip
|
||||
req_headers = Core.sandbox.context.request.headers
|
||||
get_external_ip = True
|
||||
link_base = ""
|
||||
|
||||
if "Origin" in req_headers:
|
||||
link_base = req_headers["Origin"]
|
||||
Log.Debug("Using origin-based link_base")
|
||||
get_external_ip = False
|
||||
|
||||
elif "Referer" in req_headers:
|
||||
parsed = urlparse.urlparse(req_headers["Referer"])
|
||||
link_base = "%s://%s:%s" % (parsed.scheme, parsed.hostname, parsed.port)
|
||||
Log.Debug("Using referer-based link_base")
|
||||
get_external_ip = False
|
||||
|
||||
else:
|
||||
if get_external_ip or "plex.tv" in link_base:
|
||||
ip = Core.networking.http_request("http://www.plexapp.com/ip.php", cacheTime=7200).content.strip()
|
||||
link_base = "https://%s:32400" % ip
|
||||
Log.Debug("Using ip-based fallback link_base")
|
||||
|
||||
logs_link = "%s%s?X-Plex-Token=%s" % (link_base, PREFIX + '/logs', config.universal_plex_token)
|
||||
logs_link = "%s%s?X-Plex-Token=%s" % (link_base, PREFIX + '/logs', config.plex_token)
|
||||
oc = ObjectContainer(title2="Download Logs", no_cache=True, no_history=True,
|
||||
header="Copy this link and open this in your browser, please",
|
||||
message=logs_link)
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
# coding=utf-8
|
||||
import os
|
||||
import traceback
|
||||
|
||||
from babelfish import Language
|
||||
from sub_mod import SubtitleModificationsMenu
|
||||
from menu_helpers import debounce, SubFolderObjectContainer, default_thumb, add_ignore_options, get_item_task_data, \
|
||||
set_refresh_menu_state
|
||||
from subzero.modification import registry as mod_registry
|
||||
|
||||
from refresh_item import RefreshItem
|
||||
from subliminal_patch import PatchedSubtitle as Subtitle
|
||||
from subzero.constants import PREFIX
|
||||
from support.config import config
|
||||
from support.helpers import timestamp, cast_bool, df, get_language
|
||||
@@ -15,7 +13,7 @@ from support.items import get_item_kind_from_rating_key, get_item, get_current_s
|
||||
from support.lib import Plex
|
||||
from support.plex_media import get_plex_metadata, scan_videos
|
||||
from support.scheduler import scheduler
|
||||
from support.storage import get_subtitle_storage, save_subtitles
|
||||
from support.storage import get_subtitle_storage
|
||||
|
||||
|
||||
@route(PREFIX + '/item/{rating_key}/actions')
|
||||
@@ -139,69 +137,6 @@ def SubtitleOptionsMenu(**kwargs):
|
||||
return oc
|
||||
|
||||
|
||||
@route(PREFIX + '/item/sub_mods/{rating_key}/{part_id}', force=bool)
|
||||
@debounce
|
||||
def SubtitleModificationsMenu(**kwargs):
|
||||
rating_key = kwargs["rating_key"]
|
||||
part_id = kwargs["part_id"]
|
||||
language = kwargs["language"]
|
||||
current_sub, stored_subs, storage = get_current_sub(rating_key, part_id, language)
|
||||
kwargs.pop("randomize")
|
||||
|
||||
oc = SubFolderObjectContainer(title2=kwargs["title"], replace_parent=True)
|
||||
for identifier, mod in mod_registry.mods.iteritems():
|
||||
oc.add(DirectoryObject(
|
||||
key=Callback(SubtitleApplyMod, mod_identifier=identifier, randomize=timestamp(), **kwargs),
|
||||
title=mod.description
|
||||
))
|
||||
|
||||
oc.add(DirectoryObject(
|
||||
key=Callback(SubtitleApplyMod, mod_identifier=None, randomize=timestamp(), **kwargs),
|
||||
title="Restore original version",
|
||||
summary=u"Currently applied mods: %s" % (", ".join(current_sub.mods) if current_sub.mods else "none")
|
||||
))
|
||||
|
||||
return oc
|
||||
|
||||
|
||||
@route(PREFIX + '/item/sub_add_mod/{rating_key}/{part_id}/{mod_identifier}', force=bool)
|
||||
@debounce
|
||||
def SubtitleApplyMod(mod_identifier=None, **kwargs):
|
||||
if mod_identifier is not None and mod_identifier not in mod_registry.mods:
|
||||
raise NotImplementedError
|
||||
|
||||
rating_key = kwargs["rating_key"]
|
||||
part_id = kwargs["part_id"]
|
||||
lang_a2 = kwargs["language"]
|
||||
item_type = kwargs["item_type"]
|
||||
|
||||
language = Language.fromietf(lang_a2)
|
||||
|
||||
current_sub, stored_subs, storage = get_current_sub(rating_key, part_id, language)
|
||||
current_sub.add_mod(mod_identifier)
|
||||
|
||||
storage.save(stored_subs)
|
||||
metadata = get_plex_metadata(rating_key, part_id, item_type)
|
||||
scanned_parts = scan_videos([metadata], kind="series" if item_type == "episode" else "movie", ignore_all=True)
|
||||
video, plex_part = scanned_parts.items()[0]
|
||||
|
||||
subtitle = Subtitle(language, mods=current_sub.mods)
|
||||
subtitle.content = current_sub.content
|
||||
subtitle.plex_media_fps = plex_part.fps
|
||||
subtitle.page_link = "modify subtitles with: %s" % (", ".join(current_sub.mods) if current_sub.mods else "none")
|
||||
subtitle.language = language
|
||||
|
||||
try:
|
||||
save_subtitles(scanned_parts, {video: [subtitle]}, mode="m", bare_save=True)
|
||||
Log.Debug("Modified %s subtitle for: %s:%s with: %s", language.name, rating_key, part_id,
|
||||
", ".join(current_sub.mods) if current_sub.mods else "none")
|
||||
except:
|
||||
Log.Error("Something went wrong when modifying subtitle: %s", traceback.format_exc())
|
||||
|
||||
kwargs.pop("randomize")
|
||||
return SubtitleModificationsMenu(randomize=timestamp(), **kwargs)
|
||||
|
||||
|
||||
@route(PREFIX + '/item/search/{rating_key}/{part_id}', force=bool)
|
||||
@debounce
|
||||
def ListAvailableSubsForItemMenu(rating_key=None, part_id=None, title=None, item_title=None, filename=None,
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
# coding=utf-8
|
||||
import logging
|
||||
import os
|
||||
|
||||
import logger
|
||||
|
||||
from item_details import ItemDetailsMenu
|
||||
@@ -147,16 +149,6 @@ def RefreshMissing(randomize=None):
|
||||
@route(PREFIX + '/ValidatePrefs', enforce_route=True)
|
||||
def ValidatePrefs():
|
||||
Core.log.setLevel(logging.DEBUG)
|
||||
Log.Debug("Validate Prefs called.")
|
||||
|
||||
# SZ config debug
|
||||
Log.Debug("--- SZ Config-Debug ---")
|
||||
for attr in [
|
||||
"app_support_path", "data_path", "data_items_path", "plugin_log_path", "server_log_path", "enable_agent",
|
||||
"enable_channel", "permissions_ok", "missing_permissions", "fs_encoding"]:
|
||||
Log.Debug("config.%s: %s", attr, getattr(config, attr))
|
||||
|
||||
Log.Debug("-----------------------")
|
||||
|
||||
# cache the channel state
|
||||
update_dict = False
|
||||
@@ -191,6 +183,42 @@ def ValidatePrefs():
|
||||
Core.log.removeHandler(logger.console_handler)
|
||||
Log.Debug("Stop logging to console")
|
||||
|
||||
Log.Debug("Validate Prefs called.")
|
||||
|
||||
# SZ config debug
|
||||
Log.Debug("--- SZ Config-Debug ---")
|
||||
for attr in [
|
||||
"app_support_path", "data_path", "data_items_path", "enable_agent",
|
||||
"enable_channel", "permissions_ok", "missing_permissions", "fs_encoding"]:
|
||||
Log.Debug("config.%s: %s", attr, getattr(config, attr))
|
||||
|
||||
for attr in ["plugin_log_path", "server_log_path"]:
|
||||
value = getattr(config, attr)
|
||||
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)
|
||||
|
||||
# fixme: check existance of and os access of logs
|
||||
Log.Debug("Platform: %s", Core.runtime.platform)
|
||||
Log.Debug("OS: %s", Core.runtime.os)
|
||||
Log.Debug("----- Environment -----")
|
||||
for key, value in os.environ.iteritems():
|
||||
if key.startswith("PLEX"):
|
||||
if "TOKEN" in key:
|
||||
outval = "xxxxxxxxxxxxxxxxxxx"
|
||||
|
||||
else:
|
||||
outval = value
|
||||
Log.Debug("%s: %s", key, outval)
|
||||
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"]))
|
||||
|
||||
@@ -0,0 +1,231 @@
|
||||
# coding=utf-8
|
||||
|
||||
import traceback
|
||||
import types
|
||||
|
||||
from babelfish import Language
|
||||
|
||||
from menu_helpers import debounce, SubFolderObjectContainer
|
||||
from subliminal_patch import PatchedSubtitle as Subtitle
|
||||
from subzero.modification import registry as mod_registry, SubtitleModifications
|
||||
from subzero.constants import PREFIX
|
||||
from support.plex_media import get_plex_metadata, scan_videos
|
||||
from support.storage import save_subtitles
|
||||
from support.helpers import timestamp, pad_title
|
||||
from support.items import get_current_sub
|
||||
|
||||
|
||||
@route(PREFIX + '/item/sub_mods/{rating_key}/{part_id}', force=bool)
|
||||
@debounce
|
||||
def SubtitleModificationsMenu(**kwargs):
|
||||
rating_key = kwargs["rating_key"]
|
||||
part_id = kwargs["part_id"]
|
||||
language = kwargs["language"]
|
||||
current_sub, stored_subs, storage = get_current_sub(rating_key, part_id, language)
|
||||
kwargs.pop("randomize")
|
||||
|
||||
oc = SubFolderObjectContainer(title2=kwargs["title"], replace_parent=True)
|
||||
for identifier, mod in mod_registry.mods.iteritems():
|
||||
if mod.advanced:
|
||||
continue
|
||||
|
||||
if mod.exclusive and identifier in current_sub.mods:
|
||||
continue
|
||||
|
||||
oc.add(DirectoryObject(
|
||||
key=Callback(SubtitleSetMods, mods=identifier, mode="add", randomize=timestamp(), **kwargs),
|
||||
title=pad_title(mod.description), summary=mod.long_description or ""
|
||||
))
|
||||
|
||||
fps_mod = SubtitleModifications.get_mod_class("change_FPS")
|
||||
oc.add(DirectoryObject(
|
||||
key=Callback(SubtitleFPSModMenu, randomize=timestamp(), **kwargs),
|
||||
title=pad_title(fps_mod.description), summary=fps_mod.long_description or ""
|
||||
))
|
||||
|
||||
shift_mod = SubtitleModifications.get_mod_class("shift_offset")
|
||||
oc.add(DirectoryObject(
|
||||
key=Callback(SubtitleShiftModUnitMenu, randomize=timestamp(), **kwargs),
|
||||
title=pad_title(shift_mod.description), summary=shift_mod.long_description or ""
|
||||
))
|
||||
|
||||
if current_sub.mods:
|
||||
oc.add(DirectoryObject(
|
||||
key=Callback(SubtitleSetMods, mods=None, mode="remove_last", randomize=timestamp(), **kwargs),
|
||||
title=pad_title("Remove last applied mod (%s)" % current_sub.mods[-1]),
|
||||
summary=u"Currently applied mods: %s" % (", ".join(current_sub.mods) if current_sub.mods else "none")
|
||||
))
|
||||
oc.add(DirectoryObject(
|
||||
key=Callback(SubtitleListMods, randomize=timestamp(), **kwargs),
|
||||
title=pad_title("Manage applied mods"),
|
||||
summary=u"Currently applied mods: %s" % (", ".join(current_sub.mods))
|
||||
))
|
||||
|
||||
oc.add(DirectoryObject(
|
||||
key=Callback(SubtitleSetMods, mods=None, mode="clear", randomize=timestamp(), **kwargs),
|
||||
title=pad_title("Restore original version"),
|
||||
summary=u"Currently applied mods: %s" % (", ".join(current_sub.mods) if current_sub.mods else "none")
|
||||
))
|
||||
|
||||
return oc
|
||||
|
||||
|
||||
@route(PREFIX + '/item/sub_mod_fps/{rating_key}/{part_id}', force=bool)
|
||||
@debounce
|
||||
def SubtitleFPSModMenu(**kwargs):
|
||||
rating_key = kwargs["rating_key"]
|
||||
part_id = kwargs["part_id"]
|
||||
item_type = kwargs["item_type"]
|
||||
|
||||
oc = SubFolderObjectContainer(title2=kwargs["title"], replace_parent=True)
|
||||
|
||||
metadata = get_plex_metadata(rating_key, part_id, item_type)
|
||||
scanned_parts = scan_videos([metadata], kind="series" if item_type == "episode" else "movie", ignore_all=True)
|
||||
video, plex_part = scanned_parts.items()[0]
|
||||
|
||||
target_fps = plex_part.fps
|
||||
|
||||
kwargs.pop("randomize")
|
||||
|
||||
for fps in ["23.976", "24.000", "25.000", "29.970", "30.000", "50.000", "59.940", "60.000"]:
|
||||
if float(fps) == float(target_fps):
|
||||
continue
|
||||
|
||||
if float(fps) > float(target_fps):
|
||||
indicator = "subs constantly getting faster"
|
||||
else:
|
||||
indicator = "subs constantly getting slower"
|
||||
|
||||
mod_ident = SubtitleModifications.get_mod_signature("change_FPS", **{"from": fps, "to": target_fps})
|
||||
oc.add(DirectoryObject(
|
||||
key=Callback(SubtitleSetMods, mods=mod_ident, mode="add", randomize=timestamp(), **kwargs),
|
||||
title="%s fps -> %s fps (%s)" % (fps, target_fps, indicator)
|
||||
))
|
||||
|
||||
return oc
|
||||
|
||||
|
||||
POSSIBLE_UNITS = (("ms", "milliseconds"), ("s", "seconds"), ("m", "minutes"), ("h", "hours"))
|
||||
POSSIBLE_UNITS_D = dict(POSSIBLE_UNITS)
|
||||
|
||||
|
||||
@route(PREFIX + '/item/sub_mod_shift_unit/{rating_key}/{part_id}', force=bool)
|
||||
@debounce
|
||||
def SubtitleShiftModUnitMenu(**kwargs):
|
||||
oc = SubFolderObjectContainer(title2=kwargs["title"], replace_parent=True)
|
||||
|
||||
kwargs.pop("randomize")
|
||||
|
||||
for unit, title in POSSIBLE_UNITS:
|
||||
oc.add(DirectoryObject(
|
||||
key=Callback(SubtitleShiftModMenu, unit=unit, randomize=timestamp(), **kwargs),
|
||||
title="Adjust by %s" % title
|
||||
))
|
||||
|
||||
return oc
|
||||
|
||||
|
||||
@route(PREFIX + '/item/sub_mod_shift/{rating_key}/{part_id}/{unit}', force=bool)
|
||||
@debounce
|
||||
def SubtitleShiftModMenu(unit=None, **kwargs):
|
||||
if unit not in POSSIBLE_UNITS_D:
|
||||
raise NotImplementedError
|
||||
|
||||
oc = SubFolderObjectContainer(title2=kwargs["title"], replace_parent=True)
|
||||
|
||||
kwargs.pop("randomize")
|
||||
|
||||
rng = []
|
||||
if unit == "h":
|
||||
rng = range(-10, 11)
|
||||
elif unit in ("m", "s"):
|
||||
rng = range(-59, 60)
|
||||
elif unit == "ms":
|
||||
rng = range(-900, 1000, 100)
|
||||
|
||||
for i in rng:
|
||||
if i == 0:
|
||||
continue
|
||||
|
||||
mod_ident = SubtitleModifications.get_mod_signature("shift_offset", **{unit: i})
|
||||
oc.add(DirectoryObject(
|
||||
key=Callback(SubtitleSetMods, mods=mod_ident, mode="add", randomize=timestamp(), **kwargs),
|
||||
title="%s %s" % (("%s" if i < 0 else "+%s") % i, unit)
|
||||
))
|
||||
|
||||
return oc
|
||||
|
||||
|
||||
@route(PREFIX + '/item/sub_set_mods/{rating_key}/{part_id}/{mods}/{mode}', force=bool)
|
||||
@debounce
|
||||
def SubtitleSetMods(mods=None, mode=None, **kwargs):
|
||||
if not isinstance(mods, types.ListType) and mods:
|
||||
mods = [mods]
|
||||
|
||||
rating_key = kwargs["rating_key"]
|
||||
part_id = kwargs["part_id"]
|
||||
lang_a2 = kwargs["language"]
|
||||
item_type = kwargs["item_type"]
|
||||
|
||||
language = Language.fromietf(lang_a2)
|
||||
|
||||
current_sub, stored_subs, storage = get_current_sub(rating_key, part_id, language)
|
||||
if mode == "add":
|
||||
for mod in mods:
|
||||
identifier, args = SubtitleModifications.parse_identifier(mod)
|
||||
if identifier not in mod_registry.mods_available:
|
||||
raise NotImplementedError("Mod unknown or not registered")
|
||||
|
||||
current_sub.add_mod(mod)
|
||||
elif mode == "clear":
|
||||
current_sub.add_mod(None)
|
||||
elif mode == "remove":
|
||||
for mod in mods:
|
||||
current_sub.mods.remove(mod)
|
||||
|
||||
elif mode == "remove_last":
|
||||
if current_sub.mods:
|
||||
current_sub.mods.pop()
|
||||
else:
|
||||
raise NotImplementedError("Wrong mode given")
|
||||
storage.save(stored_subs)
|
||||
|
||||
metadata = get_plex_metadata(rating_key, part_id, item_type)
|
||||
scanned_parts = scan_videos([metadata], kind="series" if item_type == "episode" else "movie", ignore_all=True)
|
||||
video, plex_part = scanned_parts.items()[0]
|
||||
|
||||
subtitle = Subtitle(language, mods=current_sub.mods)
|
||||
subtitle.content = current_sub.content
|
||||
subtitle.plex_media_fps = plex_part.fps
|
||||
subtitle.page_link = "modify subtitles with: %s" % (", ".join(current_sub.mods) if current_sub.mods else "none")
|
||||
subtitle.language = language
|
||||
|
||||
try:
|
||||
save_subtitles(scanned_parts, {video: [subtitle]}, mode="m", bare_save=True)
|
||||
Log.Debug("Modified %s subtitle for: %s:%s with: %s", language.name, rating_key, part_id,
|
||||
", ".join(current_sub.mods) if current_sub.mods else "none")
|
||||
except:
|
||||
Log.Error("Something went wrong when modifying subtitle: %s", traceback.format_exc())
|
||||
|
||||
kwargs.pop("randomize")
|
||||
return SubtitleModificationsMenu(randomize=timestamp(), **kwargs)
|
||||
|
||||
|
||||
@route(PREFIX + '/item/sub_list_mods/{rating_key}/{part_id}', force=bool)
|
||||
@debounce
|
||||
def SubtitleListMods(**kwargs):
|
||||
rating_key = kwargs["rating_key"]
|
||||
part_id = kwargs["part_id"]
|
||||
language = kwargs["language"]
|
||||
current_sub, stored_subs, storage = get_current_sub(rating_key, part_id, language)
|
||||
|
||||
kwargs.pop("randomize")
|
||||
|
||||
oc = SubFolderObjectContainer(title2=kwargs["title"], replace_parent=True)
|
||||
for identifier in current_sub.mods:
|
||||
oc.add(DirectoryObject(
|
||||
key=Callback(SubtitleSetMods, mods=identifier, mode="remove", randomize=timestamp(), **kwargs),
|
||||
title="Remove: %s" % identifier
|
||||
))
|
||||
|
||||
return oc
|
||||
@@ -11,9 +11,9 @@ class PlexActivityManager(object):
|
||||
def start(self):
|
||||
activity_sources_enabled = None
|
||||
|
||||
if config.universal_plex_token:
|
||||
if config.plex_token:
|
||||
from plex import Plex
|
||||
Plex.configuration.defaults.authentication(config.universal_plex_token)
|
||||
Plex.configuration.defaults.authentication(config.plex_token)
|
||||
activity_sources_enabled = ["websocket"]
|
||||
Activity.on('websocket.playing', self.on_playing)
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ import datetime
|
||||
import subliminal
|
||||
import subliminal_patch
|
||||
from babelfish import Language
|
||||
from subliminal.cli import MutexLock
|
||||
from subzero.lib.io import FileIO, get_viable_encoding
|
||||
from subzero.constants import PLUGIN_NAME, PLUGIN_IDENTIFIER, MOVIE, SHOW
|
||||
from lib import Plex
|
||||
@@ -45,6 +46,7 @@ class Config(object):
|
||||
data_path = None
|
||||
data_items_path = None
|
||||
universal_plex_token = None
|
||||
plex_token = None
|
||||
is_development = False
|
||||
|
||||
enable_channel = True
|
||||
@@ -92,6 +94,7 @@ class Config(object):
|
||||
self.data_path = getattr(Data, "_core").storage.data_path
|
||||
self.data_items_path = os.path.join(self.data_path, "DataItems")
|
||||
self.universal_plex_token = self.get_universal_plex_token()
|
||||
self.plex_token = os.environ.get("PLEXTOKEN", self.universal_plex_token)
|
||||
|
||||
self.set_plugin_mode()
|
||||
self.set_plugin_lock()
|
||||
@@ -161,6 +164,8 @@ class Config(object):
|
||||
else:
|
||||
Log("Did NOT find Preferences file - please check logfile and hierarchy. Aborting!")
|
||||
|
||||
# fixme: windows
|
||||
|
||||
def set_plugin_mode(self):
|
||||
if Prefs["plugin_mode"] == "only agent":
|
||||
self.enable_channel = False
|
||||
|
||||
@@ -110,9 +110,9 @@ def str_pad(s, length, align='left', pad_char=' ', trim=False):
|
||||
raise ValueError("Unknown align type, expected either 'left' or 'right'")
|
||||
|
||||
|
||||
def pad_title(value):
|
||||
def pad_title(value, width=49):
|
||||
"""Pad a title to 30 characters to force the 'details' view."""
|
||||
return str_pad(value, 49, pad_char=' ')
|
||||
return str_pad(value, width, pad_char=' ')
|
||||
|
||||
|
||||
def get_plex_item_display_title(item, kind, parent=None, parent_title=None, section_title=None,
|
||||
|
||||
@@ -374,6 +374,7 @@ class FindBetterSubtitles(DownloadSubtitleMixin, SubtitleListingMixin, Task):
|
||||
video_id = stored_subs.video_id
|
||||
|
||||
if stored_subs.item_type == "episode":
|
||||
cutoff = self.series_cutoff
|
||||
min_score = min_score_series
|
||||
else:
|
||||
cutoff = self.movies_cutoff
|
||||
|
||||
@@ -399,7 +399,7 @@
|
||||
"id": "subtitles.remove_hi",
|
||||
"label": "Remove Hearing Impaired tags from downloaded subtitles",
|
||||
"type": "bool",
|
||||
"default": "true"
|
||||
"default": "false"
|
||||
},
|
||||
{
|
||||
"id": "subtitles.fix_ocr",
|
||||
|
||||
+2
-2
@@ -13,7 +13,7 @@
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>2.0.0.12</string>
|
||||
<string>2.0.0.13</string>
|
||||
<key>PlexFrameworkVersion</key>
|
||||
<string>2</string>
|
||||
<key>PlexPluginClass</key>
|
||||
@@ -32,7 +32,7 @@
|
||||
|
||||
<h1>Sub-Zero for Plex</h1><i>Subtitles done right</i>
|
||||
|
||||
Version 2.0.0.12 DEV
|
||||
Version 2.0.0.13 DEV
|
||||
|
||||
Originally based on @bramwalet's awesome <a href="https://github.com/bramwalet/Subliminal.bundle">Subliminal.bundle</a>
|
||||
|
||||
|
||||
@@ -369,7 +369,8 @@ class Chapter(object):
|
||||
if chapterdisplays:
|
||||
string = chapterdisplays[0].get('ChapString')
|
||||
language = chapterdisplays[0].get('ChapLanguage')
|
||||
return cls(start, hidden, enabled, end, string, language)
|
||||
return cls(start, hidden, enabled, end, string, language)
|
||||
return cls(start, hidden, enabled, end)
|
||||
|
||||
def __repr__(self):
|
||||
return '<%s [%s, enabled=%s]>' % (self.__class__.__name__, self.start, self.enabled)
|
||||
@@ -146,7 +146,7 @@ class PatchedSubtitle(Subtitle):
|
||||
encoding = self.guess_encoding()
|
||||
|
||||
submods = SubtitleModifications()
|
||||
submods.load(content=self.text, fps=self.plex_media_fps, language=self.language)
|
||||
submods.load(content=self.text, language=self.language)
|
||||
submods.modify(*self.mods)
|
||||
return submods.to_string("srt", encoding=encoding).encode(encoding=encoding)
|
||||
|
||||
|
||||
@@ -16,8 +16,9 @@ if debug:
|
||||
logging.basicConfig(level=logging.DEBUG)
|
||||
|
||||
submod = SubMod(debug=debug)
|
||||
submod.load(fn, language=Language.fromietf("en"))
|
||||
submod.modify("remove_HI", "OCR_fixes")
|
||||
submod.load(fn, language=Language.fromietf("eng"))
|
||||
#submod.modify("remove_HI", "OCR_fixes")
|
||||
#submod.modify("OCR_fixes")
|
||||
submod.modify("change_FPS(from=24,to=25)")
|
||||
|
||||
#print submod.f.to_string("srt")
|
||||
@@ -1,5 +1,5 @@
|
||||
# coding=utf-8
|
||||
|
||||
from registry import registry
|
||||
from mods import hearing_impaired, ocr_fixes
|
||||
from mods import hearing_impaired, ocr_fixes, fps, offset
|
||||
from main import SubtitleModifications, SubMod
|
||||
@@ -18,22 +18,21 @@ class SubtitleModifications(object):
|
||||
self.debug = debug
|
||||
self.initialized_mods = {}
|
||||
|
||||
def load(self, fn=None, content=None, fps=None, language=None):
|
||||
def load(self, fn=None, content=None, language=None):
|
||||
"""
|
||||
|
||||
:param language: babelfish.Language language of the subtitle
|
||||
:param fn: filename
|
||||
:param content: unicode
|
||||
:param fps:
|
||||
:return:
|
||||
"""
|
||||
self.language = language
|
||||
self.initialized_mods = {}
|
||||
try:
|
||||
if fn:
|
||||
self.f = pysubs2.load(fn, fps=fps)
|
||||
self.f = pysubs2.load(fn)
|
||||
elif content:
|
||||
self.f = pysubs2.SSAFile.from_string(content, fps=fps)
|
||||
self.f = pysubs2.SSAFile.from_string(content)
|
||||
except (IOError,
|
||||
UnicodeDecodeError,
|
||||
pysubs2.exceptions.UnknownFPSError,
|
||||
@@ -44,19 +43,58 @@ class SubtitleModifications(object):
|
||||
elif content:
|
||||
logger.exception("Couldn't load subtitle: %s", traceback.format_exc())
|
||||
|
||||
@classmethod
|
||||
def parse_identifier(cls, identifier):
|
||||
# simple identifier
|
||||
if identifier in registry.mods:
|
||||
return identifier, {}
|
||||
|
||||
# identifier with params; identifier(param=value)
|
||||
split_args = identifier[identifier.find("(")+1:-1].split(",")
|
||||
args = dict((key, value) for key, value in [sub.split("=") for sub in split_args])
|
||||
return identifier[:identifier.find("(")], args
|
||||
|
||||
@classmethod
|
||||
def get_mod_class(cls, identifier):
|
||||
identifier, args = cls.parse_identifier(identifier)
|
||||
return registry.mods[identifier]
|
||||
|
||||
@classmethod
|
||||
def get_mod_signature(cls, identifier, **kwargs):
|
||||
return cls.get_mod_class(identifier).get_signature(**kwargs)
|
||||
|
||||
def modify(self, *mods):
|
||||
new_f = []
|
||||
|
||||
for identifier in mods:
|
||||
parsed_mods = [SubtitleModifications.parse_identifier(mod) for mod in mods]
|
||||
line_mods = []
|
||||
non_line_mods = []
|
||||
|
||||
print parsed_mods
|
||||
for identifier, args in parsed_mods:
|
||||
if identifier not in registry.mods:
|
||||
raise NotImplementedError("Mod %s not loaded" % identifier)
|
||||
|
||||
for line in self.f:
|
||||
applied_mods = []
|
||||
for identifier in mods:
|
||||
if identifier in registry.mods:
|
||||
if identifier not in self.initialized_mods:
|
||||
self.initialized_mods[identifier] = registry.mods[identifier](self)
|
||||
mod_cls = registry.mods[identifier]
|
||||
if mod_cls.modifies_whole_file:
|
||||
non_line_mods.append((identifier, args))
|
||||
else:
|
||||
line_mods.append((identifier, args))
|
||||
|
||||
if identifier not in self.initialized_mods:
|
||||
self.initialized_mods[identifier] = mod_cls(self)
|
||||
|
||||
# apply file mods
|
||||
if non_line_mods:
|
||||
for identifier, args in non_line_mods:
|
||||
mod = self.initialized_mods[identifier]
|
||||
mod.modify(None, debug=self.debug, parent=self, **args)
|
||||
|
||||
# apply line mods
|
||||
if line_mods:
|
||||
for line in self.f:
|
||||
applied_mods = []
|
||||
for identifier, args in line_mods:
|
||||
mod = self.initialized_mods[identifier]
|
||||
|
||||
# don't bother reapplying exclusive mods multiple times
|
||||
@@ -66,7 +104,7 @@ class SubtitleModifications(object):
|
||||
if not mod.processors:
|
||||
continue
|
||||
|
||||
new_content = mod.modify(line.text, debug=self.debug, parent=self)
|
||||
new_content = mod.modify(line.text, debug=self.debug, parent=self, **args)
|
||||
if not new_content:
|
||||
if self.debug:
|
||||
logger.debug("%s: deleting %s", identifier, line)
|
||||
@@ -74,7 +112,7 @@ class SubtitleModifications(object):
|
||||
|
||||
line.text = new_content
|
||||
applied_mods.append(identifier)
|
||||
new_f.append(line)
|
||||
new_f.append(line)
|
||||
|
||||
self.f.events = new_f
|
||||
|
||||
|
||||
@@ -10,7 +10,10 @@ logger = logging.getLogger(__name__)
|
||||
class SubtitleModification(object):
|
||||
identifier = None
|
||||
description = None
|
||||
long_description = None
|
||||
exclusive = False
|
||||
advanced = False # has parameters
|
||||
modifies_whole_file = False # operates on the whole file, not individual entries
|
||||
pre_processors = []
|
||||
processors = []
|
||||
post_processors = []
|
||||
@@ -18,7 +21,7 @@ class SubtitleModification(object):
|
||||
def __init__(self, parent):
|
||||
return
|
||||
|
||||
def _process(self, content, processors, debug=False, parent=None):
|
||||
def _process(self, content, processors, debug=False, parent=None, **kwargs):
|
||||
if not content:
|
||||
return
|
||||
|
||||
@@ -43,22 +46,27 @@ class SubtitleModification(object):
|
||||
logger.debug("%s: %s -> %s", processor, old_content, new_content)
|
||||
return new_content
|
||||
|
||||
def pre_process(self, content, debug=False, parent=None):
|
||||
return self._process(content, self.pre_processors, debug=debug, parent=parent)
|
||||
def pre_process(self, content, debug=False, parent=None, **kwargs):
|
||||
return self._process(content, self.pre_processors, debug=debug, parent=parent, **kwargs)
|
||||
|
||||
def process(self, content, debug=False, parent=None):
|
||||
return self._process(content, self.processors, debug=debug, parent=parent)
|
||||
def process(self, content, debug=False, parent=None, **kwargs):
|
||||
return self._process(content, self.processors, debug=debug, parent=parent, **kwargs)
|
||||
|
||||
def post_process(self, content, debug=False, parent=None):
|
||||
return self._process(content, self.post_processors, debug=debug, parent=parent)
|
||||
def post_process(self, content, debug=False, parent=None, **kwargs):
|
||||
return self._process(content, self.post_processors, debug=debug, parent=parent, **kwargs)
|
||||
|
||||
def modify(self, content, debug=False, parent=None):
|
||||
def modify(self, content, debug=False, parent=None, **kwargs):
|
||||
new_content = content
|
||||
for method in ("pre_process", "process", "post_process"):
|
||||
new_content = getattr(self, method)(new_content, debug=debug, parent=parent)
|
||||
new_content = getattr(self, method)(new_content, debug=debug, parent=parent, **kwargs)
|
||||
|
||||
return new_content
|
||||
|
||||
@classmethod
|
||||
def get_signature(cls, **kwargs):
|
||||
string_args = ",".join(["%s=%s" % (key, value) for key, value in kwargs.iteritems()])
|
||||
return "%s(%s)" % (cls.identifier, string_args)
|
||||
|
||||
|
||||
class SubtitleTextModification(SubtitleModification):
|
||||
post_processors = [
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
# coding=utf-8
|
||||
|
||||
import logging
|
||||
|
||||
from subzero.modification.mods import SubtitleModification
|
||||
from subzero.modification import registry
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class ChangeFPS(SubtitleModification):
|
||||
identifier = "change_FPS"
|
||||
description = "Change the FPS of the subtitle"
|
||||
exclusive = True
|
||||
advanced = True
|
||||
modifies_whole_file = True
|
||||
|
||||
long_description = """\
|
||||
Re-syncs the subtitle to the framerate of the current media file.
|
||||
"""
|
||||
|
||||
def modify(self, content, debug=False, parent=None, **kwargs):
|
||||
fps_from = kwargs.get("from")
|
||||
fps_to = kwargs.get("to")
|
||||
parent.f.transform_framerate(float(fps_from), float(fps_to))
|
||||
|
||||
registry.register(ChangeFPS)
|
||||
@@ -11,6 +11,10 @@ class HearingImpaired(SubtitleTextModification):
|
||||
description = "Remove Hearing Impaired tags"
|
||||
exclusive = True
|
||||
|
||||
long_description = """\
|
||||
Removes tags, text and characters from subtitles that are meant for hearing impaired people
|
||||
"""
|
||||
|
||||
processors = [
|
||||
# brackets
|
||||
NReProcessor(re.compile(r'(?sux)[([].+[)\]]'), "", name="HI_brackets"),
|
||||
|
||||
@@ -16,6 +16,10 @@ class FixOCR(SubtitleTextModification):
|
||||
exclusive = True
|
||||
data_dict = None
|
||||
|
||||
long_description = """\
|
||||
Fix issues that happen when a subtitle gets converted from bitmap to text through OCR
|
||||
"""
|
||||
|
||||
def __init__(self, parent):
|
||||
super(FixOCR, self).__init__(parent)
|
||||
data_dict = OCR_fix_data.get(parent.language.alpha3t)
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
# coding=utf-8
|
||||
|
||||
import logging
|
||||
|
||||
from subzero.modification.mods import SubtitleModification
|
||||
from subzero.modification import registry
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class ShiftOffset(SubtitleModification):
|
||||
identifier = "shift_offset"
|
||||
description = "Change the timing of the subtitle"
|
||||
exclusive = True
|
||||
advanced = True
|
||||
modifies_whole_file = True
|
||||
|
||||
long_description = """\
|
||||
Adds or substracts a certain amount of time from the whole subtitle to match your media
|
||||
"""
|
||||
|
||||
def modify(self, content, debug=False, parent=None, **kwargs):
|
||||
parent.f.shift(h=int(kwargs.get("h", 0)), m=int(kwargs.get("m", 0)), s=int(kwargs.get("s", 0)),
|
||||
ms=int(kwargs.get("ms", 0)))
|
||||
|
||||
|
||||
registry.register(ShiftOffset)
|
||||
Reference in new issue
Block a user