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, "-"
                ]
                output = None
                try:
                    output = subprocess.check_output(quote_args(args),
                                                     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
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
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
Exemple #4
0
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
Exemple #5
0
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
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