def ItemDetailsMenu(rating_key, title=None, base_title=None, item_title=None, randomize=None, header=None): """ displays the item details menu of an item that doesn't contain any deeper tree, such as a movie or an episode :param rating_key: :param title: :param base_title: :param item_title: :param randomize: :return: """ from interface.main import IgnoreMenu title = unicode(base_title) + " > " + unicode(title) if base_title else unicode(title) item = plex_item = get_item(rating_key) current_kind = get_item_kind_from_rating_key(rating_key) timeout = 30 oc = SubFolderObjectContainer(title2=title, replace_parent=True, header=header) if not item: oc.add(DirectoryObject( key=Callback(ItemDetailsMenu, rating_key=rating_key, title=title, base_title=base_title, item_title=item_title, randomize=timestamp()), title=u"Item not found: %s!" % item_title, summary="Plex didn't return any information about the item, please refresh it and come back later", thumb=default_thumb )) return oc # add back to season for episode if current_kind == "episode": from interface.menu import MetadataMenu show = get_item(item.show.rating_key) season = get_item(item.season.rating_key) oc.add(DirectoryObject( key=Callback(MetadataMenu, rating_key=season.rating_key, title=season.title, base_title=show.title, previous_item_type="show", previous_rating_key=show.rating_key, display_items=True, randomize=timestamp()), title=u"< Back to %s" % season.title, summary="Back to %s > %s" % (show.title, season.title), thumb=season.thumb or default_thumb )) oc.add(DirectoryObject( key=Callback(RefreshItem, rating_key=rating_key, item_title=item_title, randomize=timestamp(), timeout=timeout * 1000), title=u"Refresh: %s" % item_title, summary="Refreshes the %s, possibly searching for missing and picking up new subtitles on disk" % current_kind, thumb=item.thumb or default_thumb )) oc.add(DirectoryObject( key=Callback(RefreshItem, rating_key=rating_key, item_title=item_title, force=True, randomize=timestamp(), timeout=timeout * 1000), title=u"Force-find subtitles: %s" % item_title, summary="Issues a forced refresh, ignoring known subtitles and searching for new ones", thumb=item.thumb or default_thumb )) # get stored subtitle info for item id subtitle_storage = get_subtitle_storage() stored_subs = subtitle_storage.load_or_new(item) # look for subtitles for all available media parts and all of their languages has_multiple_parts = len(plex_item.media) > 1 part_index = 0 for media in plex_item.media: for part in media.parts: filename = os.path.basename(part.file) if not os.path.exists(part.file): continue part_id = str(part.id) part_index += 1 part_index_addon = "" part_summary_addon = "" if has_multiple_parts: part_index_addon = u"File %s: " % part_index part_summary_addon = "%s " % filename # iterate through all configured languages for lang in config.lang_list: # get corresponding stored subtitle data for that media part (physical media item), for language current_sub = stored_subs.get_any(part_id, lang) current_sub_id = None current_sub_provider_name = None summary = u"%sNo current subtitle in storage" % part_summary_addon current_score = None if current_sub: current_sub_id = current_sub.id current_sub_provider_name = current_sub.provider_name current_score = current_sub.score summary = u"%sCurrent subtitle: %s (added: %s, %s), Language: %s, Score: %i, Storage: %s" % \ (part_summary_addon, current_sub.provider_name, df(current_sub.date_added), current_sub.mode_verbose, display_language(lang), current_sub.score, current_sub.storage_type) oc.add(DirectoryObject( key=Callback(SubtitleOptionsMenu, rating_key=rating_key, part_id=part_id, title=title, item_title=item_title, language=lang, language_name=display_language(lang), current_id=current_sub_id, item_type=plex_item.type, filename=filename, current_data=summary, randomize=timestamp(), current_provider=current_sub_provider_name, current_score=current_score), title=u"%sManage %s subtitle" % (part_index_addon, display_language(lang)), summary=summary )) else: oc.add(DirectoryObject( key=Callback(ListAvailableSubsForItemMenu, rating_key=rating_key, part_id=part_id, title=title, item_title=item_title, language=lang, language_name=display_language(lang), current_id=current_sub_id, item_type=plex_item.type, filename=filename, current_data=summary, randomize=timestamp(), current_provider=current_sub_provider_name, current_score=current_score), title=u"%sList %s subtitles" % (part_index_addon, display_language(lang)), summary=summary )) if config.plex_transcoder: # embedded subtitles embedded_count = 0 embedded_langs = [] for stream in part.streams: # subtitle stream if stream.stream_type == 3 and not stream.stream_key and stream.codec in TEXT_SUBTITLE_EXTS: lang = get_language_from_stream(stream.language_code) if not lang and config.treat_und_as_first: lang = list(config.lang_list)[0] if lang: embedded_langs.append(lang) embedded_count += 1 if embedded_count: oc.add(DirectoryObject( key=Callback(ListEmbeddedSubsForItemMenu, rating_key=rating_key, part_id=part_id, title=title, item_type=plex_item.type, item_title=item_title, base_title=base_title, randomize=timestamp()), title=u"%sEmbedded subtitles (%s)" % (part_index_addon, ", ".join(display_language(l) for l in set(embedded_langs))), summary=u"Extract and activate embedded subtitle streams" )) ignore_title = item_title if current_kind == "episode": ignore_title = get_item_title(item) add_ignore_options(oc, "videos", title=ignore_title, rating_key=rating_key, callback_menu=IgnoreMenu) subtitle_storage.destroy() return oc
def ItemDetailsMenu(rating_key, title=None, base_title=None, item_title=None, randomize=None, header=None, message=None): """ displays the item details menu of an item that doesn't contain any deeper tree, such as a movie or an episode :param rating_key: :param title: :param base_title: :param item_title: :param randomize: :return: """ from interface.main import InclExclMenu title = unicode(base_title) + " > " + unicode(title) if base_title else unicode(title) item = plex_item = get_item(rating_key) current_kind = get_item_kind_from_rating_key(rating_key) timeout = 30 oc = SubFolderObjectContainer( title2=title, replace_parent=True, header=header, message=message) if not item: oc.add(DirectoryObject( key=Callback( ItemDetailsMenu, rating_key=rating_key, title=title, base_title=base_title, item_title=item_title, randomize=timestamp()), title=_(u"Item not found: %s!", item_title), summary=_("Plex didn't return any information about the item, please refresh it and come back later"), thumb=default_thumb )) return oc # add back to season for episode if current_kind == "episode": from interface.menu import MetadataMenu show = get_item(item.show.rating_key) season = get_item(item.season.rating_key) oc.add(DirectoryObject( key=Callback( MetadataMenu, rating_key=season.rating_key, title=season.title, base_title=show.title, previous_item_type="show", previous_rating_key=show.rating_key, display_items=True, randomize=timestamp()), title=_(u"< Back to %s", season.title), summary=_("Back to %s > %s", show.title, season.title), thumb=season.thumb or default_thumb )) oc.add(DirectoryObject( key=Callback( RefreshItem, rating_key=rating_key, item_title=item_title, randomize=timestamp(), timeout=timeout * 1000), 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)), thumb=item.thumb or default_thumb )) oc.add(DirectoryObject( key=Callback(RefreshItem, rating_key=rating_key, item_title=item_title, force=True, randomize=timestamp(), timeout=timeout * 1000), title=_(u"Force-find subtitles: %(item_title)s", item_title=item_title), summary=_("Issues a forced refresh, ignoring known subtitles and searching for new ones"), thumb=item.thumb or default_thumb )) # get stored subtitle info for item id subtitle_storage = get_subtitle_storage() stored_subs = subtitle_storage.load_or_new(item) # look for subtitles for all available media parts and all of their languages has_multiple_parts = len(plex_item.media) > 1 part_index = 0 for media in plex_item.media: for part in media.parts: filename = os.path.basename(part.file) if not os.path.exists(part.file): continue part_id = str(part.id) part_index += 1 part_index_addon = u"" part_summary_addon = u"" if has_multiple_parts: part_index_addon = _(u"File %(file_part_index)s: ", file_part_index=part_index) part_summary_addon = u"%s " % filename # iterate through all configured languages for lang in config.lang_list: # get corresponding stored subtitle data for that media part (physical media item), for language current_sub = stored_subs.get_any(part_id, lang) current_sub_id = None current_sub_provider_name = None summary = _(u"%(part_summary)sNo current subtitle in storage", part_summary=part_summary_addon) current_score = None if current_sub: current_sub_id = current_sub.id current_sub_provider_name = current_sub.provider_name current_score = current_sub.score summary = _(u"%(part_summary)sCurrent subtitle: %(provider_name)s (added: %(date_added)s, " u"%(mode)s), Language: %(language)s, Score: %(score)i, Storage: %(storage_type)s", part_summary=part_summary_addon, provider_name=_(current_sub.provider_name), date_added=df(current_sub.date_added), mode=_(current_sub.mode_verbose), language=display_language(lang), score=current_sub.score, storage_type=current_sub.storage_type) oc.add(DirectoryObject( key=Callback(SubtitleOptionsMenu, rating_key=rating_key, part_id=part_id, title=title, item_title=item_title, language=lang, language_name=display_language(lang), current_id=current_sub_id, item_type=plex_item.type, filename=filename, current_data=summary, randomize=timestamp(), current_provider=current_sub_provider_name, current_score=current_score), title=_(u"%(part_summary)sManage %(language)s subtitle", part_summary=part_index_addon, language=display_language(lang)), summary=summary )) else: oc.add(DirectoryObject( key=Callback(ListAvailableSubsForItemMenu, rating_key=rating_key, part_id=part_id, title=title, item_title=item_title, language=lang, language_name=display_language(lang), current_id=current_sub_id, item_type=plex_item.type, filename=filename, current_data=summary, randomize=timestamp(), current_provider=current_sub_provider_name, current_score=current_score), title=_(u"%(part_summary)sList %(language)s subtitles", part_summary=part_index_addon, language=display_language(lang)), summary=summary )) if config.plex_transcoder: # embedded subtitles embedded_count = 0 embedded_langs = [] for stream in part.streams: # subtitle stream if stream.stream_type == 3 and not stream.stream_key and stream.codec in TEXT_SUBTITLE_EXTS: lang = get_language_from_stream(stream.language_code) is_forced = is_stream_forced(stream) if not lang and config.treat_und_as_first: lang = list(config.lang_list)[0] if lang: lang = Language.rebuild(lang, forced=is_forced) embedded_langs.append(lang) embedded_count += 1 if embedded_count: oc.add(DirectoryObject( key=Callback(ListEmbeddedSubsForItemMenu, rating_key=rating_key, part_id=part_id, title=title, item_type=plex_item.type, item_title=item_title, base_title=base_title, randomize=timestamp()), title=_(u"%(part_summary)sEmbedded subtitles (%(languages)s)", part_summary=part_index_addon, languages=", ".join(display_language(l) for l in list(OrderedDict.fromkeys(embedded_langs)))), summary=_(u"Extract embedded subtitle streams") )) ignore_title = item_title if current_kind == "episode": ignore_title = get_item_title(item) add_incl_excl_options(oc, "videos", title=ignore_title, rating_key=rating_key, callback_menu=InclExclMenu) subtitle_storage.destroy() return oc
def item_discover_missing_subs(rating_key, kind="show", added_at=None, section_title=None, internal=False, external=True, languages=()): item_id = int(rating_key) item = get_item(rating_key) if kind == "show": item_title = get_plex_item_display_title(item, kind, parent=item.season, section_title=section_title, parent_title=item.show.title) else: item_title = get_plex_item_display_title(item, kind, section_title=section_title) subtitle_storage = get_subtitle_storage() stored_subs = subtitle_storage.load(rating_key) subtitle_storage.destroy() subtitle_target_dir, tdir_is_absolute = config.subtitle_sub_dir missing = set() languages_set = set([Language.rebuild(l) for l in languages]) for media in item.media: existing_subs = {"internal": [], "external": [], "own_external": [], "count": 0} for part in media.parts: update_stream_info(part) # did we already download an external subtitle before? if subtitle_target_dir and stored_subs: for language in languages_set: if has_external_subtitle(part.id, stored_subs, language): # check the existence of the actual subtitle file # get media filename without extension part_basename = os.path.splitext(os.path.basename(part.file))[0] # compute target directory for subtitle # fixme: move to central location if tdir_is_absolute: possible_subtitle_path_base = subtitle_target_dir else: possible_subtitle_path_base = os.path.join(os.path.dirname(part.file), subtitle_target_dir) possible_subtitle_path_base = os.path.realpath(possible_subtitle_path_base) # folder actually exists? if not os.path.isdir(possible_subtitle_path_base): continue found_any = False for ext in config.subtitle_formats: if cast_bool(Prefs['subtitles.only_one']): possible_subtitle_path = os.path.join(possible_subtitle_path_base, u"%s.%s" % (part_basename, ext)) else: possible_subtitle_path = os.path.join(possible_subtitle_path_base, u"%s.%s.%s" % (part_basename, language, ext)) # check for subtitle existence if os.path.isfile(possible_subtitle_path): found_any = True Log.Debug(u"Found: %s", possible_subtitle_path) break if found_any: existing_subs["own_external"].append(language) existing_subs["count"] = existing_subs["count"] + 1 for stream in part.streams: if stream.stream_type == 3: is_forced = is_stream_forced(stream) if stream.index: key = "internal" else: key = "external" if not config.exotic_ext and stream.codec.lower() not in TEXT_SUBTITLE_EXTS: continue # treat unknown language as lang1? if not stream.language_code and config.treat_und_as_first: lang = Language.rebuild(list(config.lang_list)[0]) # we can't parse empty language codes elif not stream.language_code or not stream.codec: continue else: # parse with internal language parser first try: lang = get_language_from_stream(stream.language_code) if not lang: if config.treat_und_as_first: lang = Language.rebuild(list(config.lang_list)[0]) else: continue except (ValueError, LanguageReverseError): continue if lang: # Log.Debug("Found babelfish language: %r", lang) lang.forced = is_forced existing_subs[key].append(lang) existing_subs["count"] = existing_subs["count"] + 1 missing_from_part = set([Language.rebuild(l) for l in languages]) if existing_subs["count"]: # fixme: this is actually somewhat broken with IETF, as Plex doesn't store the country portion # (pt instead of pt-BR) inside the database. So it might actually download pt-BR if there's a local pt-BR # subtitle but not our own. existing_flat = set((existing_subs["internal"] if internal else []) + (existing_subs["external"] if external else []) + existing_subs["own_external"]) check_languages = set([Language.rebuild(l) for l in languages]) alpha3_map = {} if config.ietf_as_alpha3: for language in existing_flat: if language.country: alpha3_map[language.alpha3] = language.country language.country = None for language in check_languages: if language.country: alpha3_map[language.alpha3] = language.country language.country = None # compare sets of strings, not sets of different Language instances check_languages_str = set(str(l) for l in check_languages) existing_flat_str = set(str(l) for l in existing_flat) if check_languages_str.issubset(existing_flat_str) or \ (len(existing_flat) >= 1 and Prefs['subtitles.only_one']): # all subs found #Log.Info(u"All subtitles exist for '%s'", item_title) continue missing_from_part = set(Language.fromietf(l) for l in check_languages_str - existing_flat_str) if config.ietf_as_alpha3: for language in missing_from_part: language.country = alpha3_map.get(language.alpha3, None) if missing_from_part: Log.Info(u"Subs still missing for '%s' (%s: %s): %s", item_title, rating_key, media.id, missing_from_part) missing.update(missing_from_part) if missing: # deduplicate missing = set(Language.fromietf(la) for la in set(str(l) for l in missing)) return added_at, item_id, item_title, item, missing
def item_discover_missing_subs(rating_key, kind="show", added_at=None, section_title=None, internal=False, external=True, languages=()): item_id = int(rating_key) item = get_item(rating_key) if kind == "show": item_title = get_plex_item_display_title(item, kind, parent=item.season, section_title=section_title, parent_title=item.show.title) else: item_title = get_plex_item_display_title(item, kind, section_title=section_title) subtitle_storage = get_subtitle_storage() stored_subs = subtitle_storage.load(rating_key) subtitle_storage.destroy() subtitle_target_dir, tdir_is_absolute = config.subtitle_sub_dir missing = set() languages_set = set([Language.rebuild(l) for l in languages]) for media in item.media: existing_subs = {"internal": [], "external": [], "own_external": [], "count": 0} for part in media.parts: # did we already download an external subtitle before? if subtitle_target_dir and stored_subs: for language in languages_set: if has_external_subtitle(part.id, stored_subs, language): # check the existence of the actual subtitle file # get media filename without extension part_basename = os.path.splitext(os.path.basename(part.file))[0] # compute target directory for subtitle # fixme: move to central location if tdir_is_absolute: possible_subtitle_path_base = subtitle_target_dir else: possible_subtitle_path_base = os.path.join(os.path.dirname(part.file), subtitle_target_dir) possible_subtitle_path_base = os.path.realpath(possible_subtitle_path_base) # folder actually exists? if not os.path.isdir(possible_subtitle_path_base): continue found_any = False for ext in config.subtitle_formats: if cast_bool(Prefs['subtitles.only_one']): possible_subtitle_path = os.path.join(possible_subtitle_path_base, u"%s.%s" % (part_basename, ext)) else: possible_subtitle_path = os.path.join(possible_subtitle_path_base, u"%s.%s.%s" % (part_basename, language, ext)) # check for subtitle existence if os.path.isfile(possible_subtitle_path): found_any = True Log.Debug(u"Found: %s", possible_subtitle_path) break if found_any: existing_subs["own_external"].append(language) existing_subs["count"] = existing_subs["count"] + 1 for stream in part.streams: if stream.stream_type == 3: is_forced = is_stream_forced(stream) if stream.index: key = "internal" else: key = "external" if not config.exotic_ext and stream.codec.lower() not in TEXT_SUBTITLE_EXTS: continue # treat unknown language as lang1? if not stream.language_code and config.treat_und_as_first: lang = Language.rebuild(list(config.lang_list)[0]) # we can't parse empty language codes elif not stream.language_code or not stream.codec: continue else: # parse with internal language parser first try: lang = get_language_from_stream(stream.language_code) if not lang: if config.treat_und_as_first: lang = Language.rebuild(list(config.lang_list)[0]) else: continue except (ValueError, LanguageReverseError): continue if lang: # Log.Debug("Found babelfish language: %r", lang) lang.forced = is_forced existing_subs[key].append(lang) existing_subs["count"] = existing_subs["count"] + 1 missing_from_part = set([Language.rebuild(l) for l in languages]) if existing_subs["count"]: # fixme: this is actually somewhat broken with IETF, as Plex doesn't store the country portion # (pt instead of pt-BR) inside the database. So it might actually download pt-BR if there's a local pt-BR # subtitle but not our own. existing_flat = set((existing_subs["internal"] if internal else []) + (existing_subs["external"] if external else []) + existing_subs["own_external"]) check_languages = set([Language.rebuild(l) for l in languages]) alpha3_map = {} if config.ietf_as_alpha3: for language in existing_flat: if language.country: alpha3_map[language.alpha3] = language.country language.country = None for language in check_languages: if language.country: alpha3_map[language.alpha3] = language.country language.country = None # compare sets of strings, not sets of different Language instances check_languages_str = set(str(l) for l in check_languages) existing_flat_str = set(str(l) for l in existing_flat) if check_languages_str.issubset(existing_flat_str) or \ (len(existing_flat) >= 1 and Prefs['subtitles.only_one']): # all subs found #Log.Info(u"All subtitles exist for '%s'", item_title) continue missing_from_part = set(Language.fromietf(l) for l in check_languages_str - existing_flat_str) if config.ietf_as_alpha3: for language in missing_from_part: language.country = alpha3_map.get(language.alpha3, None) if missing_from_part: Log.Info(u"Subs still missing for '%s' (%s: %s): %s", item_title, rating_key, media.id, missing_from_part) missing.update(missing_from_part) if missing: # deduplicate missing = set(Language.fromietf(la) for la in set(str(l) for l in missing)) return added_at, item_id, item_title, item, missing