diff --git a/Contents/Code/__init__.py b/Contents/Code/__init__.py index 0d4af3ff..e68983a5 100755 --- a/Contents/Code/__init__.py +++ b/Contents/Code/__init__.py @@ -23,16 +23,15 @@ import subliminal_patch import support import interface - sys.modules["interface"] = interface from subzero.constants import OS_PLEX_USERAGENT, PERSONAL_MEDIA_IDENTIFIER from subzero import intent from interface.menu import * from support import helpers -from support.subtitlehelpers import getSubtitlesFromMetadata, force_utf8 -from support.storage import storeSubtitleInfo -from support.config import config, IGNORE_FN +from support.subtitlehelpers import get_subtitles_from_metadata, force_utf8 +from support.storage import store_subtitle_info, whack_missing_parts +from support.config import config def Start(): @@ -55,7 +54,7 @@ def Start(): scheduler.run() -def initSubliminalPatches(): +def init_subliminal_patches(): # configure custom subtitle destination folders for scanning pre-existing subs dest_folder = config.subtitleDestinationFolder subliminal_patch.patch_video.CUSTOM_PATHS = [dest_folder] if dest_folder else [] @@ -63,7 +62,7 @@ def initSubliminalPatches(): subliminal_patch.patch_providers.addic7ed.USE_BOOST = bool(Prefs['provider.addic7ed.boost']) -def flattenToParts(media, kind="series"): +def flatten_media(media, kind="series"): """ iterates through media and returns the associated parts (videos) :param media: @@ -85,14 +84,14 @@ def flattenToParts(media, kind="series"): return parts -def parseMediaToParts(media, kind="series"): +def convert_media_to_parts(media, kind="series"): """ returns a list of parts to be used later on; ignores folders with an existing "subzero.ignore" file :param media: :param kind: :return: """ - parts = flattenToParts(media, kind=kind) + parts = flatten_media(media, kind=kind) if not Prefs["subtitles.ignore_fs"]: return parts @@ -120,7 +119,7 @@ def parseMediaToParts(media, kind="series"): return use_parts -def getFPS(streams): +def get_stream_fps(streams): for stream in streams: # video if stream.type == 1: @@ -128,19 +127,19 @@ def getFPS(streams): return "25.000" -def scanParts(parts, kind="series"): +def scan_parts(parts, kind="series"): """ receives a list of parts containing dictionaries returned by flattenToParts :param parts: :param kind: series or movies - :return: dictionary of scanned videos of subliminal.video.scan_video + :return: dictionary of subliminal.video.scan_video, key=subliminal scanned video, value=plex file part """ ret = {} for part in parts: force_refresh = intent.get("force", part["id"]) hints = helpers.get_item_hints(part["title"], kind, series=part["series"] if kind == "series" else None) - part["video"].fps = getFPS(part["video"].streams) - scanned_video = scanVideo(part["video"], ignore_all=force_refresh, hints=hints) + part["video"].fps = get_stream_fps(part["video"].streams) + scanned_video = scan_video(part["video"], ignore_all=force_refresh, hints=hints) if not scanned_video: continue @@ -149,7 +148,7 @@ def scanParts(parts, kind="series"): return ret -def getItemIDs(media, kind="series"): +def get_media_item_ids(media, kind="series"): ids = [] if kind == "movies": ids.append(media.id) @@ -161,7 +160,7 @@ def getItemIDs(media, kind="series"): return ids -def scanVideo(plex_video, ignore_all=False, hints=None): +def scan_video(plex_video, ignore_all=False, hints=None): embedded_subtitles = not ignore_all and Prefs['subtitles.scan.embedded'] external_subtitles = not ignore_all and Prefs['subtitles.scan.external'] @@ -178,7 +177,7 @@ def scanVideo(plex_video, ignore_all=False, hints=None): Log.Warn("File could not be guessed by subliminal") -def downloadBestSubtitles(video_part_map, min_score=0): +def download_best_subtitles(video_part_map, min_score=0): hearing_impaired = Prefs['subtitles.search.hearingImpaired'] languages = config.langList if not languages: @@ -188,7 +187,7 @@ def downloadBestSubtitles(video_part_map, min_score=0): for video, part in video_part_map.iteritems(): if not Prefs['subtitles.save.filesystem']: # scan for existing metadata subtitles - meta_subs = getSubtitlesFromMetadata(part) + meta_subs = get_subtitles_from_metadata(part) for language, subList in meta_subs.iteritems(): if subList: video.subtitle_languages.add(language) @@ -216,20 +215,20 @@ def downloadBestSubtitles(video_part_map, min_score=0): Log.Debug("All languages for all requested videos exist. Doing nothing.") -def saveSubtitles(videos, subtitles): +def save_subtitles(videos, subtitles): if Prefs['subtitles.save.filesystem']: Log.Debug("Using filesystem as subtitle storage") - saveSubtitlesToFile(subtitles) + save_subtitles_to_file(subtitles) storage = "filesystem" else: Log.Debug("Using metadata as subtitle storage") - saveSubtitlesToMetadata(videos, subtitles) + save_subtitles_to_metadata(videos, subtitles) storage = "metadata" - storeSubtitleInfo(videos, subtitles, storage) + store_subtitle_info(videos, subtitles, storage) -def saveSubtitlesToFile(subtitles): +def save_subtitles_to_file(subtitles): fld_custom = Prefs["subtitles.save.subFolder.Custom"].strip() if bool(Prefs["subtitles.save.subFolder.Custom"]) else None for video, video_subtitles in subtitles.items(): @@ -254,7 +253,7 @@ def saveSubtitlesToFile(subtitles): encode_with=force_utf8 if Prefs['subtitles.enforce_encoding'] else None) -def saveSubtitlesToMetadata(videos, subtitles): +def save_subtitles_to_metadata(videos, subtitles): for video, video_subtitles in subtitles.items(): mediaPart = videos[video] for subtitle in video_subtitles: @@ -262,12 +261,12 @@ def saveSubtitlesToMetadata(videos, subtitles): mediaPart.subtitles[Locale.Language.Match(subtitle.language.alpha2)][subtitle.page_link] = Proxy.Media(content, ext="srt") -def updateLocalMedia(metadata, media, media_type="movies"): +def update_local_media(metadata, media, media_type="movies"): # Look for subtitles if media_type == "movies": for item in media.items: for part in item.parts: - support.localmedia.findSubtitles(part) + support.localmedia.find_subtitles(part) return # Look for subtitles for each episode. @@ -280,7 +279,7 @@ def updateLocalMedia(metadata, media, media_type="movies"): # Look for subtitles. for part in i.parts: - support.localmedia.findSubtitles(part) + support.localmedia.find_subtitles(part) else: pass @@ -295,7 +294,7 @@ class SubZeroAgent(object): def __init__(self, *args, **kwargs): super(SubZeroAgent, self).__init__(*args, **kwargs) self.agent_type = "movies" if isinstance(self, Agent.Movies) else "series" - self.name = "Sub-Zero Subtitles (%s, %s)" % (self.agent_type_verbose, config.getVersion()) + self.name = "Sub-Zero Subtitles (%s, %s)" % (self.agent_type_verbose, config.get_version()) def search(self, results, media, lang): Log.Debug("Sub-Zero %s, %s search" % (config.version, self.agent_type)) @@ -312,17 +311,19 @@ class SubZeroAgent(object): item_ids = [] try: - initSubliminalPatches() - parts = parseMediaToParts(media, kind=self.agent_type) + init_subliminal_patches() + parts = convert_media_to_parts(media, kind=self.agent_type) use_score = Prefs[self.score_prefs_key] - scanned_parts = scanParts(parts, kind=self.agent_type) - subtitles = downloadBestSubtitles(scanned_parts, min_score=int(use_score)) - item_ids = getItemIDs(media, kind=self.agent_type) + scanned_parts = scan_parts(parts, kind=self.agent_type) + subtitles = download_best_subtitles(scanned_parts, min_score=int(use_score)) + item_ids = get_media_item_ids(media, kind=self.agent_type) + + whack_missing_parts(scanned_parts) if subtitles: - saveSubtitles(scanned_parts, subtitles) + save_subtitles(scanned_parts, subtitles) - updateLocalMedia(metadata, media, media_type=self.agent_type) + update_local_media(metadata, media, media_type=self.agent_type) finally: # update the menu state diff --git a/Contents/Code/logger.py b/Contents/Code/logger.py index 999cc892..b670f1fc 100644 --- a/Contents/Code/logger.py +++ b/Contents/Code/logger.py @@ -1,8 +1,8 @@ import logging -def registerLoggingHander(dependencies, level="ERROR"): - plexHandler = PlexLoggerHandler() +def register_logging_handler(dependencies, level="ERROR"): + plex_handler = PlexLoggerHandler() for dependency in dependencies: Log.Debug("Registering LoggerHandler for dependency: %s" % dependency) log = logging.getLogger(dependency) @@ -13,7 +13,7 @@ def registerLoggingHander(dependencies, level="ERROR"): log.removeHandler(handler) log.setLevel(level) - log.addHandler(plexHandler) + log.addHandler(plex_handler) class PlexLoggerHandler(logging.StreamHandler): diff --git a/Contents/Code/support/config.py b/Contents/Code/support/config.py index 184c4ea6..9ed5da6d 100644 --- a/Contents/Code/support/config.py +++ b/Contents/Code/support/config.py @@ -3,10 +3,10 @@ import os import re import inspect - from babelfish import Language from subzero.lib.io import FileIO from subzero.constants import PLUGIN_NAME +from auth import refresh_plex_token from lib import Plex from helpers import check_write_permissions @@ -30,10 +30,11 @@ def int_or_default(s, default): class Config(object): version = None - langList = None - subtitleDestinationFolder = None + full_version = None + lang_list = None + subtitle_destination_folder = None providers = None - providerSettings = None + provider_settings = None max_recent_items_per_library = 200 permissions_ok = False missing_permissions = None @@ -41,12 +42,12 @@ class Config(object): initialized = False def initialize(self): - self.version = self.getVersion() + self.version = self.get_version() self.full_version = u"%s %s" % (PLUGIN_NAME, self.version) - self.langList = self.getLangList() - self.subtitleDestinationFolder = self.getSubtitleDestinationFolder() - self.providers = self.getProviders() - self.providerSettings = self.getProviderSettings() + self.lang_list = self.get_lang_list() + self.subtitle_destination_folder = self.get_subtitle_destination_folder() + self.providers = self.get_providers() + self.provider_settings = self.get_provider_settings() self.max_recent_items_per_library = int_or_default(Prefs["scheduler.max_recent_items_per_library"], 200) self.initialized = True self.missing_permissions = [] @@ -79,7 +80,7 @@ class Config(object): return all_permissions_ok - def getVersion(self): + def get_version(self): curDir = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe()))) info_file_path = os.path.abspath(os.path.join(curDir, "..", "..", "Info.plist")) data = FileIO.read(info_file_path) @@ -87,13 +88,13 @@ class Config(object): if result: return result.group(1) - def getBlacklist(self, key): + def get_blacklist(self, key): return map(lambda id: id.strip(), (Prefs[key] or "").split(",")) # Prepare a list of languages we want subs for - def getLangList(self): + def get_lang_list(self): l = {Language.fromietf(Prefs["langPref1"])} - langCustom = Prefs["langPrefCustom"].strip() + lang_custom = Prefs["langPrefCustom"].strip() if Prefs['subtitles.only_one']: return l @@ -104,8 +105,8 @@ class Config(object): if Prefs["langPref3"] != "None": l.update({Language.fromietf(Prefs["langPref3"])}) - if len(langCustom) and langCustom != "None": - for lang in langCustom.split(u","): + if len(lang_custom) and lang_custom != "None": + for lang in lang_custom.split(u","): lang = lang.strip() try: real_lang = Language.fromietf(lang) @@ -118,14 +119,14 @@ class Config(object): return l - def getSubtitleDestinationFolder(self): + def get_subtitle_destination_folder(self): if not Prefs["subtitles.save.filesystem"]: return fld_custom = Prefs["subtitles.save.subFolder.Custom"].strip() if bool(Prefs["subtitles.save.subFolder.Custom"]) else None return fld_custom or (Prefs["subtitles.save.subFolder"] if Prefs["subtitles.save.subFolder"] != "current folder" else None) - def getProviders(self): + def get_providers(self): providers = {'opensubtitles': Prefs['provider.opensubtitles.enabled'], #'thesubdb': Prefs['provider.thesubdb.enabled'], 'podnapisi': Prefs['provider.podnapisi.enabled'], @@ -134,7 +135,7 @@ class Config(object): } return filter(lambda prov: providers[prov], providers) - def getProviderSettings(self): + def get_provider_settings(self): provider_settings = {'addic7ed': {'username': Prefs['provider.addic7ed.username'], 'password': Prefs['provider.addic7ed.password'], 'use_random_agents': Prefs['provider.addic7ed.use_random_agents'], diff --git a/Contents/Code/support/helpers.py b/Contents/Code/support/helpers.py index b39c2995..20c95ee4 100755 --- a/Contents/Code/support/helpers.py +++ b/Contents/Code/support/helpers.py @@ -20,7 +20,7 @@ RE_UNICODE_CONTROL = u'([\u0000-\u0008\u000b-\u000c\u000e-\u001f\ufffe-\uffff])' # A platform independent way to split paths which might come in with different separators. -def splitPath(str): +def split_path(str): if str.find('\\') != -1: return str.split('\\') else: @@ -40,7 +40,7 @@ def unicodize(s): return filename -def cleanFilename(filename): +def clean_filename(filename): # this will remove any whitespace and punctuation chars and replace them with spaces, strip and return as lowercase return string.translate(filename.encode('utf-8'), string.maketrans(string.punctuation + string.whitespace, ' ' * len(string.punctuation + string.whitespace))).strip().lower() diff --git a/Contents/Code/support/items.py b/Contents/Code/support/items.py index 75915a8f..8af21632 100644 --- a/Contents/Code/support/items.py +++ b/Contents/Code/support/items.py @@ -3,7 +3,6 @@ import logging import re import types - from ignore import ignore_list from helpers import is_recent, format_item, query_plex from subzero import intent diff --git a/Contents/Code/support/localmedia.py b/Contents/Code/support/localmedia.py index e030606f..bee63e60 100755 --- a/Contents/Code/support/localmedia.py +++ b/Contents/Code/support/localmedia.py @@ -8,7 +8,7 @@ import subtitlehelpers from subzero.lib.io import getViableEncoding -def findSubtitles(part): +def find_subtitles(part): lang_sub_map = {} part_filename = helpers.unicodize(part.file) part_basename = os.path.splitext(os.path.basename(part_filename))[0] @@ -90,7 +90,7 @@ def findSubtitles(part): if total_media_files > 1 and not filename_matches_part: continue - subtitle_helper = subtitlehelpers.SubtitleHelpers(file_path) + subtitle_helper = subtitlehelpers.subtitle_helpers(file_path) if subtitle_helper != None: local_lang_map = subtitle_helper.process_subtitles(part) for new_language, subtitles in local_lang_map.items(): @@ -104,7 +104,7 @@ def findSubtitles(part): # add known metadata subs to our sub list if not use_filesystem: - for language, sub_list in subtitlehelpers.getSubtitlesFromMetadata(part).iteritems(): + for language, sub_list in subtitlehelpers.get_subtitles_from_metadata(part).iteritems(): if sub_list: if language not in lang_sub_map: lang_sub_map[language] = [] diff --git a/Contents/Code/support/missing_subtitles.py b/Contents/Code/support/missing_subtitles.py index fb61c516..5bc1772d 100755 --- a/Contents/Code/support/missing_subtitles.py +++ b/Contents/Code/support/missing_subtitles.py @@ -6,7 +6,7 @@ from support.helpers import format_item from support.lib import Plex -def itemDiscoverMissing(rating_key, kind="show", added_at=None, section_title=None, internal=False, external=True, languages=()): +def item_discover_missing_subs(rating_key, kind="show", added_at=None, section_title=None, internal=False, external=True, languages=()): existing_subs = {"internal": [], "external": [], "count": 0} item_id = int(rating_key) @@ -48,11 +48,11 @@ def itemDiscoverMissing(rating_key, kind="show", added_at=None, section_title=No return added_at, item_id, item_title -def getAllMissing(items): +def items_get_all_missing_subs(items): missing = [] for added_at, kind, section_title, key in items: try: - state = itemDiscoverMissing( + state = item_discover_missing_subs( key, kind=kind, added_at=added_at, diff --git a/Contents/Code/support/storage.py b/Contents/Code/support/storage.py index 30e4b2dd..de1ae344 100644 --- a/Contents/Code/support/storage.py +++ b/Contents/Code/support/storage.py @@ -4,25 +4,61 @@ import datetime import pprint -def storeSubtitleInfo(videos, subtitles, storage_type): +def get_subtitle_info(rating_key): + return Dict["subs"].get(rating_key) + + +def whack_missing_parts(videos, existing_parts=None): + """ + cleans out our internal storage's video parts (parts may get updated/deleted/whatever) + :param existing_parts: optional list of part ids known + :param videos: videos to check for + :return: + """ + # shortcut + + if not existing_parts: + existing_parts = [] + for part in videos.viewvalues(): + existing_parts.append(part.id) + + whacked_parts = False + for video in videos.keys(): + if video.id not in Dict["subs"]: + continue + + for part_id in Dict["subs"][video.id].keys(): + if part_id not in existing_parts: + del Dict["subs"][video.id][part_id] + Log.Info("Whacking part %s in internal storage of video %s", part_id, video.id) + whacked_parts = True + + if whacked_parts: + Dict.Save() + + +def store_subtitle_info(videos, subtitles, storage_type): """ stores information about downloaded subtitles in plex's Dict() """ - if not "subs" in Dict: + if "subs" not in Dict: Dict["subs"] = {} storage = Dict["subs"] + existing_parts = [] for video, video_subtitles in subtitles.items(): part = videos[video] - if not video.id in storage: + if video.id not in storage: storage[video.id] = {} video_dict = storage[video.id] - if not part.id in video_dict: + if part.id not in video_dict: video_dict[part.id] = {} + existing_parts.append(part.id) + part_dict = video_dict[part.id] for subtitle in video_subtitles: lang = Locale.Language.Match(subtitle.language.alpha2) @@ -32,11 +68,14 @@ def storeSubtitleInfo(videos, subtitles, storage_type): sub_key = (subtitle.provider_name, subtitle.id) lang_dict[sub_key] = dict(score=subtitle.score, link=subtitle.page_link, storage=storage_type, hash=Hash.MD5(subtitle.content), date_added=datetime.datetime.now()) + lang_dict["current"] = sub_key + if existing_parts: + whack_missing_parts(videos, existing_parts=existing_parts) Dict.Save() -def resetStorage(key): +def reset_storage(key): """ resets the Dict[key] storage, thanks to https://docs.google.com/document/d/1hhLjV1pI-TA5y91TiJq64BdgKwdLnFt4hWgeOqpz1NA/edit# We can't use the nice Plex interface for this, as it calls get multiple times before set @@ -48,6 +87,6 @@ def resetStorage(key): Dict.Save() -def logStorage(key): +def log_storage(key): if key in Dict: Log.Debug(pprint.pformat(Dict[key])) diff --git a/Contents/Code/support/subtitlehelpers.py b/Contents/Code/support/subtitlehelpers.py index eae19f4b..d0067779 100644 --- a/Contents/Code/support/subtitlehelpers.py +++ b/Contents/Code/support/subtitlehelpers.py @@ -12,7 +12,7 @@ class SubtitleHelper(object): self.filename = filename -def SubtitleHelpers(filename): +def subtitle_helpers(filename): filename = helpers.unicodize(filename) for cls in [VobSubSubtitleHelper, DefaultSubtitleHelper]: if cls.is_helper_for(filename): @@ -137,7 +137,7 @@ class DefaultSubtitleHelper(SubtitleHelper): return lang_sub_map -def getSubtitlesFromMetadata(part): +def get_subtitles_from_metadata(part): subs = {} for language in part.subtitles: subs[language] = [] diff --git a/Contents/Code/support/tasks.py b/Contents/Code/support/tasks.py index 6d91b25d..bd1a6614 100755 --- a/Contents/Code/support/tasks.py +++ b/Contents/Code/support/tasks.py @@ -3,9 +3,9 @@ import datetime import time -from missing_subtitles import getAllMissing, refresh_item +from missing_subtitles import items_get_all_missing_subs, refresh_item from background import scheduler -from support.items import getRecentItems +from support.items import get_recent_items class Task(object): @@ -82,8 +82,8 @@ class SearchAllRecentlyAddedMissing(Task): def prepare(self): self.items_done = [] - recent_items = getRecentItems() - missing = getAllMissing(recent_items) + recent_items = get_recent_items() + missing = items_get_all_missing_subs(recent_items) ids = set([id for added_at, id, title in missing]) self.items_searching = missing self.items_searching_ids = ids