Beispiel #1
0
def BlacklistAllPartsSubtitleMenu(**kwargs):
    rating_key = kwargs.get("rating_key")
    language = kwargs.get("language")
    if language:
        language = Language.fromietf(language)

    item = get_item(rating_key)

    if not item:
        return

    item_title = get_item_title(item)

    subtitle_storage = get_subtitle_storage()
    stored_subs = subtitle_storage.load_or_new(item)
    for part_id, languages in stored_subs.parts.iteritems():
        sub_dict = languages
        if language:
            key = str(language)
            if key not in sub_dict:
                continue

            sub_dict = {key: sub_dict[key]}

        for language, subs in sub_dict.iteritems():
            if "current" in subs:
                stored_subs.blacklist(part_id, language, subs["current"])
                Log.Info("Added %s to blacklist", subs["current"])

    subtitle_storage.save(stored_subs)
    subtitle_storage.destroy()

    return RefreshItem(rating_key=rating_key, item_title=item_title, force=True, randomize=timestamp(), timeout=30000)
Beispiel #2
0
def season_extract_embedded(rating_key, requested_language, with_mods=False, force=False):
    # get stored subtitle info for item id
    subtitle_storage = get_subtitle_storage()

    try:
        for data in get_all_items(key="children", value=rating_key, base="library/metadata"):
            item = get_item(data[MI_KEY])
            if item:
                stored_subs = subtitle_storage.load_or_new(item)
                for part in get_all_parts(item):
                    embedded_subs = stored_subs.get_by_provider(part.id, requested_language, "embedded")
                    current = stored_subs.get_any(part.id, requested_language)
                    if not embedded_subs or force:
                        stream_data = get_embedded_subtitle_streams(part, requested_language=requested_language,
                                                                    get_forced=config.forced_only)
                        if stream_data:
                            stream = stream_data[0]["stream"]

                            set_current = not current or force
                            refresh = not current

                            extract_embedded_sub(rating_key=item.rating_key, part_id=part.id,
                                                 stream_index=str(stream.index), set_current=set_current,
                                                 refresh=refresh, language=requested_language, with_mods=with_mods)
    finally:
        subtitle_storage.destroy()
Beispiel #3
0
def season_extract_embedded(rating_key, requested_language, with_mods=False, force=False):
    # get stored subtitle info for item id
    subtitle_storage = get_subtitle_storage()

    try:
        for data in get_all_items(key="children", value=rating_key, base="library/metadata"):
            item = get_item(data[MI_KEY])
            if item:
                stored_subs = subtitle_storage.load_or_new(item)
                for part in get_all_parts(item):
                    embedded_subs = stored_subs.get_by_provider(part.id, requested_language, "embedded")
                    current = stored_subs.get_any(part.id, requested_language)
                    if not embedded_subs or force:
                        stream_data = get_embedded_subtitle_streams(part, requested_language=requested_language)
                        if stream_data:
                            stream = stream_data[0]["stream"]

                            set_current = not current or force
                            refresh = not current

                            extract_embedded_sub(rating_key=item.rating_key, part_id=part.id,
                                                 stream_index=str(stream.index), set_current=set_current,
                                                 refresh=refresh, language=requested_language, with_mods=with_mods,
                                                 extract_mode="m")
    finally:
        subtitle_storage.destroy()
def SelectStoredSubForItemMenu(**kwargs):
    rating_key = kwargs["rating_key"]
    part_id = kwargs["part_id"]
    language = Language.fromietf(kwargs["language"])
    item_type = kwargs["item_type"]
    sub_key = tuple(kwargs.pop("sub_key").split("__"))

    plex_item = get_item(rating_key)
    storage = get_subtitle_storage()
    stored_subs = storage.load(plex_item.rating_key)

    subtitles = stored_subs.get_all(part_id, language)
    subtitle = subtitles[sub_key]

    subtitles["current"] = sub_key

    save_stored_sub(subtitle, rating_key, part_id, language, item_type, plex_item=plex_item, storage=storage,
                    stored_subs=stored_subs)

    storage.destroy()

    kwargs.pop("randomize")

    kwargs["header"] = 'Success'
    kwargs["message"] = 'Subtitle saved to disk'

    return SubtitleOptionsMenu(randomize=timestamp(), **kwargs)
def agent_extract_embedded(videos):
    try:
        subtitle_storage = get_subtitle_storage()

        for video in videos:
            item = video["item"]
            stored_subs = subtitle_storage.load_or_new(item)

            for part in get_all_parts(item):
                for requested_language in config.lang_list:
                    embedded_subs = stored_subs.get_by_provider(part.id, requested_language, "embedded")
                    current = stored_subs.get_any(part.id, requested_language)
                    if not embedded_subs:
                        stream_data = get_embedded_subtitle_streams(part, requested_language=requested_language,
                                                                    get_forced=config.forced_only)

                        if stream_data:
                            stream = stream_data[0]["stream"]

                            extract_embedded_sub(rating_key=item.rating_key, part_id=part.id,
                                                 stream_index=str(stream.index),
                                                 language=str(requested_language), with_mods=True, refresh=False,
                                                 set_current=not current)
                    else:
                        Log.Debug("Skipping embedded subtitle extraction for %s, already got %r from %s",
                                  item.rating_key, requested_language, embedded_subs[0].id)
    except:
        Log.Error("Something went wrong when auto-extracting subtitles, continuing: %s", traceback.format_exc())
def BlacklistAllPartsSubtitleMenu(**kwargs):
    rating_key = kwargs.get("rating_key")
    language = kwargs.get("language")
    if language:
        language = Language.fromietf(language)

    item = get_item(rating_key)

    if not item:
        return

    item_title = get_item_title(item)

    subtitle_storage = get_subtitle_storage()
    stored_subs = subtitle_storage.load_or_new(item)
    for part_id, languages in stored_subs.parts.iteritems():
        sub_dict = languages
        if language:
            key = str(language)
            if key not in sub_dict:
                continue

            sub_dict = {key: sub_dict[key]}

        for language, subs in sub_dict.iteritems():
            if "current" in subs:
                stored_subs.blacklist(part_id, language, subs["current"])
                Log.Info("Added %s to blacklist", subs["current"])

    subtitle_storage.save(stored_subs)
    subtitle_storage.destroy()

    return RefreshItem(rating_key=rating_key, item_title=item_title, force=True, randomize=timestamp(), timeout=30000)
Beispiel #7
0
def SelectStoredSubForItemMenu(**kwargs):
    rating_key = kwargs["rating_key"]
    part_id = kwargs["part_id"]
    language = Language.fromietf(kwargs["language"])
    item_type = kwargs["item_type"]
    sub_key = tuple(kwargs.pop("sub_key").split("__"))

    plex_item = get_item(rating_key)
    storage = get_subtitle_storage()
    stored_subs = storage.load(plex_item.rating_key)

    subtitles = stored_subs.get_all(part_id, language)
    subtitle = subtitles[sub_key]

    save_stored_sub(subtitle, rating_key, part_id, language, item_type, plex_item=plex_item, storage=storage,
                    stored_subs=stored_subs)

    stored_subs.set_current(part_id, language, sub_key)
    storage.save(stored_subs)
    storage.destroy()

    kwa = {
        "header": _("Success"),
        "message": _("Subtitle saved to disk"),
        "title": kwargs["title"],
        "item_title": kwargs["item_title"],
        "base_title": kwargs.get("base_title")
    }

    # fixme: return to SubtitleOptionsMenu properly? (needs recomputation of current_data

    return ItemDetailsMenu(rating_key, randomize=timestamp(), **kwa)
Beispiel #8
0
def get_current_sub(rating_key, part_id, language, plex_item=None):
    from support.storage import get_subtitle_storage

    item = plex_item or get_item(rating_key)
    subtitle_storage = get_subtitle_storage()
    stored_subs = subtitle_storage.load_or_new(item)
    current_sub = stored_subs.get_any(part_id, language)
    return current_sub, stored_subs, subtitle_storage
Beispiel #9
0
def get_current_sub(rating_key, part_id, language, plex_item=None):
    from support.storage import get_subtitle_storage

    item = plex_item or get_item(rating_key)
    subtitle_storage = get_subtitle_storage()
    stored_subs = subtitle_storage.load_or_new(item)
    current_sub = stored_subs.get_any(part_id, language)
    return current_sub, stored_subs, subtitle_storage
Beispiel #10
0
def agent_extract_embedded(video_part_map):
    try:
        subtitle_storage = get_subtitle_storage()

        to_extract = []
        item_count = 0

        for scanned_video, part_info in video_part_map.iteritems():
            plexapi_item = scanned_video.plexapi_metadata["item"]
            stored_subs = subtitle_storage.load_or_new(plexapi_item)

            for plexapi_part in get_all_parts(plexapi_item):
                item_count = item_count + 1
                for requested_language in config.lang_list:
                    embedded_subs = stored_subs.get_by_provider(
                        plexapi_part.id, requested_language, "embedded")
                    current = stored_subs.get_any(plexapi_part.id,
                                                  requested_language)
                    if not embedded_subs:
                        stream_data = get_embedded_subtitle_streams(
                            plexapi_part,
                            requested_language=requested_language,
                            get_forced=config.forced_only)

                        if stream_data:
                            stream = stream_data[0]["stream"]

                            to_extract.append(
                                ({
                                    scanned_video: part_info
                                }, plexapi_part, str(stream.index),
                                 str(requested_language), not current))

                            if not cast_bool(
                                    Prefs["subtitles.search_after_autoextract"]
                            ):
                                scanned_video.subtitle_languages.update(
                                    {requested_language})
                    else:
                        Log.Debug(
                            "Skipping embedded subtitle extraction for %s, already got %r from %s",
                            plexapi_item.rating_key, requested_language,
                            embedded_subs[0].id)
        if to_extract:
            Log.Info(
                "Triggering extraction of %d embedded subtitles of %d items",
                len(to_extract), item_count)
            Thread.Create(
                multi_extract_embedded,
                stream_list=to_extract,
                refresh=True,
                with_mods=True,
                single_thread=not config.advanced.auto_extract_multithread)
    except:
        Log.Error(
            "Something went wrong when auto-extracting subtitles, continuing: %s",
            traceback.format_exc())
Beispiel #11
0
def apply_default_mods(reapply_current=False, scandir_generic=False):
    storage = get_subtitle_storage()
    subs_applied = 0

    try:
        for fn in storage.get_all_files(scandir_generic=scandir_generic):
            data = storage.load(None, filename=fn)
            if data:
                video_id = data.video_id
                item_type = get_item_kind_from_rating_key(video_id)
                if not item_type:
                    continue

                for part_id, part in data.parts.iteritems():
                    for lang, subs in part.iteritems():
                        current_sub = subs.get("current")
                        if not current_sub:
                            continue
                        sub = subs[current_sub]

                        if not sub.content:
                            continue

                        current_mods = sub.mods or []
                        if not reapply_current:
                            add_mods = list(
                                set(config.default_mods).difference(
                                    set(current_mods)))
                            if not add_mods:
                                continue
                        else:
                            if not current_mods:
                                continue
                            add_mods = []

                        try:
                            set_mods_for_part(video_id,
                                              part_id,
                                              Language.fromietf(lang),
                                              item_type,
                                              add_mods,
                                              mode="add")
                        except:
                            Log.Error("Couldn't set mods for %s:%s: %s",
                                      video_id, part_id,
                                      traceback.format_exc())
                            continue

                        subs_applied += 1
    except OSError:
        return apply_default_mods(reapply_current=reapply_current,
                                  scandir_generic=True)
    storage.destroy()
    Log.Debug("Applied mods to %i items" % subs_applied)
Beispiel #12
0
def save_stored_sub(stored_subtitle, rating_key, part_id, language, item_type, plex_item=None, storage=None,
                    stored_subs=None):
    from support.plex_media import get_plex_metadata
    from support.scanning import scan_videos
    from support.storage import save_subtitles, get_subtitle_storage

    plex_item = plex_item or get_item(rating_key)
    storage = storage or get_subtitle_storage()

    cleanup = not storage

    stored_subs = stored_subs or storage.load(plex_item.rating_key)

    if not all([plex_item, stored_subs]):
        return

    try:
        metadata = get_plex_metadata(rating_key, part_id, item_type, plex_item=plex_item)
    except PartUnknownException:
        return

    scanned_parts = scan_videos([metadata], ignore_all=True, skip_hashing=True)
    video, plex_part = scanned_parts.items()[0]

    subtitle = ModifiedSubtitle(language, mods=stored_subtitle.mods)
    subtitle.content = stored_subtitle.content
    if stored_subtitle.encoding:
        # thanks plex
        setattr(subtitle, "_guessed_encoding", stored_subtitle.encoding)

        if stored_subtitle.encoding != "utf-8":
            subtitle.normalize()
            stored_subtitle.content = subtitle.content
            stored_subtitle.encoding = "utf-8"
            storage.save(stored_subs)

    subtitle.plex_media_fps = plex_part.fps
    subtitle.page_link = stored_subtitle.id
    subtitle.language = language
    subtitle.id = stored_subtitle.id

    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(stored_subtitle.mods) if stored_subtitle.mods else "none")
    except:
        Log.Error("Something went wrong when modifying subtitle: %s", traceback.format_exc())

    if subtitle.storage_path:
        stored_subtitle.last_mod = datetime.datetime.fromtimestamp(os.path.getmtime(subtitle.storage_path))
        storage.save(stored_subs)

    if cleanup:
        storage.destroy()
Beispiel #13
0
def agent_extract_embedded(video_part_map):
    try:
        subtitle_storage = get_subtitle_storage()

        to_extract = []
        item_count = 0

        for scanned_video, part_info in video_part_map.iteritems():
            plexapi_item = scanned_video.plexapi_metadata["item"]
            stored_subs = subtitle_storage.load_or_new(plexapi_item)
            valid_langs_in_media = audio_streams_match_languages(scanned_video, config.get_lang_list(ordered=True))

            if not config.lang_list.difference(valid_langs_in_media):
                Log.Debug("Skipping embedded subtitle extraction for %s, audio streams are in correct language(s)",
                          plexapi_item.rating_key)
                continue

            for plexapi_part in get_all_parts(plexapi_item):
                item_count = item_count + 1
                used_one_unknown_stream = False
                for requested_language in config.lang_list:
                    embedded_subs = stored_subs.get_by_provider(plexapi_part.id, requested_language, "embedded")
                    current = stored_subs.get_any(plexapi_part.id, requested_language) or \
                        requested_language in scanned_video.external_subtitle_languages

                    if not embedded_subs:
                        stream_data = get_embedded_subtitle_streams(plexapi_part, requested_language=requested_language,
                                                                    skip_unknown=used_one_unknown_stream)

                        if stream_data:
                            stream = stream_data[0]["stream"]
                            if stream_data[0]["is_unknown"]:
                                used_one_unknown_stream = True

                            to_extract.append(({scanned_video: part_info}, plexapi_part, str(stream.index),
                                               str(requested_language), not current))

                            if not cast_bool(Prefs["subtitles.search_after_autoextract"]):
                                scanned_video.subtitle_languages.update({requested_language})
                    else:
                        Log.Debug("Skipping embedded subtitle extraction for %s, already got %r from %s",
                                  plexapi_item.rating_key, requested_language, embedded_subs[0].id)
        if to_extract:
            Log.Info("Triggering extraction of %d embedded subtitles of %d items", len(to_extract), item_count)
            Thread.Create(multi_extract_embedded, stream_list=to_extract, refresh=True, with_mods=True,
                          single_thread=not config.advanced.auto_extract_multithread)
    except:
        Log.Error("Something went wrong when auto-extracting subtitles, continuing: %s", traceback.format_exc())
Beispiel #14
0
def get_blacklist_from_part_map(video_part_map, languages):
    from support.storage import get_subtitle_storage
    subtitle_storage = get_subtitle_storage()
    blacklist = []
    for video, part in video_part_map.iteritems():
        stored_subs = subtitle_storage.load_or_new(video.plexapi_metadata["item"])
        for language in languages:
            current_bl, subs = stored_subs.get_blacklist(part.id, language)
            if not current_bl:
                continue

            blacklist = blacklist + [(str(a), str(b)) for a, b in current_bl.keys()]

    subtitle_storage.destroy()

    return blacklist
def get_blacklist_from_part_map(video_part_map, languages):
    from support.storage import get_subtitle_storage
    subtitle_storage = get_subtitle_storage()
    blacklist = []
    for video, part in video_part_map.iteritems():
        stored_subs = subtitle_storage.load_or_new(video.plexapi_metadata["item"])
        for language in languages:
            current_bl, subs = stored_subs.get_blacklist(part.id, language)
            if not current_bl:
                continue

            blacklist = blacklist + [(str(a), str(b)) for a, b in current_bl.keys()]

    subtitle_storage.destroy()

    return blacklist
Beispiel #16
0
def apply_default_mods(reapply_current=False, scandir_generic=False):
    storage = get_subtitle_storage()
    subs_applied = 0

    try:
        for fn in storage.get_all_files(scandir_generic=scandir_generic):
            data = storage.load(None, filename=fn)
            if data:
                video_id = data.video_id
                item_type = get_item_kind_from_rating_key(video_id)
                if not item_type:
                    continue

                for part_id, part in data.parts.iteritems():
                    for lang, subs in part.iteritems():
                        current_sub = subs.get("current")
                        if not current_sub:
                            continue
                        sub = subs[current_sub]

                        if not sub.content:
                            continue

                        current_mods = sub.mods or []
                        if not reapply_current:
                            add_mods = list(set(config.default_mods).difference(set(current_mods)))
                            if not add_mods:
                                continue
                        else:
                            if not current_mods:
                                continue
                            add_mods = []

                        try:
                            set_mods_for_part(video_id, part_id, Language.fromietf(lang), item_type, add_mods, mode="add")
                        except:
                            Log.Error("Couldn't set mods for %s:%s: %s", video_id, part_id, traceback.format_exc())
                            continue

                        subs_applied += 1
    except OSError:
        return apply_default_mods(reapply_current=reapply_current, scandir_generic=True)
    storage.destroy()
    Log.Debug("Applied mods to %i items" % subs_applied)
Beispiel #17
0
def SelectStoredSubForItemMenu(**kwargs):
    rating_key = kwargs["rating_key"]
    part_id = kwargs["part_id"]
    language = Language.fromietf(kwargs["language"])
    item_type = kwargs["item_type"]
    sub_key = tuple(kwargs.pop("sub_key").split("__"))

    plex_item = get_item(rating_key)
    storage = get_subtitle_storage()
    stored_subs = storage.load(plex_item.rating_key)

    subtitles = stored_subs.get_all(part_id, language)
    subtitle = subtitles[sub_key]

    save_stored_sub(subtitle,
                    rating_key,
                    part_id,
                    language,
                    item_type,
                    plex_item=plex_item,
                    storage=storage,
                    stored_subs=stored_subs)

    stored_subs.set_current(part_id, language, sub_key)
    storage.save(stored_subs)
    storage.destroy()

    kwa = {
        "header": _("Success"),
        "message": _("Subtitle saved to disk"),
        "title": kwargs["title"],
        "item_title": kwargs["item_title"],
        "base_title": kwargs.get("base_title")
    }

    # fixme: return to SubtitleOptionsMenu properly? (needs recomputation of current_data

    return ItemDetailsMenu(rating_key, randomize=timestamp(), **kwa)
Beispiel #18
0
def save_stored_sub(stored_subtitle, rating_key, part_id, language, item_type, plex_item=None, storage=None,
                    stored_subs=None):
    """
    in order for this to work, if the calling supplies stored_subs and storage, it has to trigger its saving and
    destruction explicitly
    :param stored_subtitle:
    :param rating_key:
    :param part_id:
    :param language:
    :param item_type:
    :param plex_item:
    :param storage:
    :param stored_subs:
    :return:
    """
    from support.plex_media import get_plex_metadata
    from support.scanning import scan_videos
    from support.storage import save_subtitles, get_subtitle_storage

    plex_item = plex_item or get_item(rating_key)

    stored_subs_was_provided = True
    if not stored_subs or not storage:
        storage = get_subtitle_storage()
        stored_subs = storage.load(plex_item.rating_key)
        stored_subs_was_provided = False

    if not all([plex_item, stored_subs]):
        return

    try:
        metadata = get_plex_metadata(rating_key, part_id, item_type, plex_item=plex_item)
    except PartUnknownException:
        return

    scanned_parts = scan_videos([metadata], ignore_all=True, skip_hashing=True)
    video, plex_part = scanned_parts.items()[0]

    subtitle = ModifiedSubtitle(language, mods=stored_subtitle.mods)
    subtitle.content = stored_subtitle.content
    if stored_subtitle.encoding:
        # thanks plex
        setattr(subtitle, "_guessed_encoding", stored_subtitle.encoding)

        if stored_subtitle.encoding != "utf-8":
            subtitle.normalize()
            stored_subtitle.content = subtitle.content
            stored_subtitle.encoding = "utf-8"
            storage.save(stored_subs)

    subtitle.plex_media_fps = plex_part.fps
    subtitle.page_link = stored_subtitle.id
    subtitle.language = language
    subtitle.id = stored_subtitle.id

    try:
        save_subtitles(scanned_parts, {video: [subtitle]}, mode="m", bare_save=True)
        stored_subtitle.mods = subtitle.mods
        Log.Debug("Modified %s subtitle for: %s:%s with: %s", language.name, rating_key, part_id,
                  ", ".join(subtitle.mods) if subtitle.mods else "none")
    except:
        Log.Error("Something went wrong when modifying subtitle: %s", traceback.format_exc())

    if subtitle.storage_path:
        stored_subtitle.last_mod = datetime.datetime.fromtimestamp(os.path.getmtime(subtitle.storage_path))

    if not stored_subs_was_provided:
        storage.save(stored_subs)
        storage.destroy()
Beispiel #19
0
def scan_video(pms_video_info,
               ignore_all=False,
               hints=None,
               rating_key=None,
               providers=None,
               skip_hashing=False):
    """
    returnes a subliminal/guessit-refined parsed video
    :param pms_video_info:
    :param ignore_all:
    :param hints:
    :param rating_key:
    :return:
    """
    embedded_subtitles = not ignore_all and Prefs['subtitles.scan.embedded']
    external_subtitles = not ignore_all and Prefs['subtitles.scan.external']

    plex_part = pms_video_info["plex_part"]

    if ignore_all:
        Log.Debug("Force refresh intended.")

    Log.Debug(
        "Scanning video: %s, external_subtitles=%s, embedded_subtitles=%s" %
        (plex_part.file, external_subtitles, embedded_subtitles))

    known_embedded = []
    parts = []
    for media in list(Plex["library"].metadata(rating_key))[0].media:
        parts += media.parts

    plexpy_part = None
    for part in parts:
        if int(part.id) == int(plex_part.id):
            plexpy_part = part

    # embedded subtitles
    # fixme: skip the whole scanning process if known_embedded == wanted languages?
    if plexpy_part:
        if embedded_subtitles:
            for stream in plexpy_part.streams:
                # subtitle stream
                if stream.stream_type == 3:
                    is_forced = helpers.is_stream_forced(stream)

                    if (config.forced_only and is_forced) or \
                            (not config.forced_only and not is_forced):

                        # embedded subtitle
                        # fixme: tap into external subtitles here instead of scanning for ourselves later?
                        if stream.codec and getattr(stream, "index", None):
                            if config.exotic_ext or stream.codec.lower(
                            ) in config.text_based_formats:
                                lang = None
                                try:
                                    lang = language_from_stream(
                                        stream.language_code)
                                except LanguageError:
                                    Log.Debug(
                                        "Couldn't detect embedded subtitle stream language: %s",
                                        stream.language_code)

                                # treat unknown language as lang1?
                                if not lang and config.treat_und_as_first:
                                    lang = list(config.lang_list)[0]

                                if lang:
                                    known_embedded.append(lang.alpha3)
    else:
        Log.Warn("Part %s missing of %s, not able to scan internal streams",
                 plex_part.id, rating_key)

    Log.Debug("Known embedded: %r", known_embedded)

    subtitle_storage = get_subtitle_storage()
    stored_subs = subtitle_storage.load(rating_key)
    subtitle_storage.destroy()

    try:
        # get basic video info scan (filename)
        video = parse_video(plex_part.file,
                            hints,
                            skip_hashing=config.low_impact_mode
                            or skip_hashing,
                            providers=providers)

        if not ignore_all:
            set_existing_languages(video,
                                   pms_video_info,
                                   external_subtitles=external_subtitles,
                                   embedded_subtitles=embedded_subtitles,
                                   known_embedded=known_embedded,
                                   forced_only=config.forced_only,
                                   stored_subs=stored_subs,
                                   languages=config.lang_list,
                                   only_one=config.only_one)

        # add video fps info
        video.fps = plex_part.fps
        return video

    except ValueError:
        Log.Warn("File could not be guessed: %s: %s", plex_part.file,
                 traceback.format_exc())
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
Beispiel #21
0
def ItemDetailsMenu(rating_key,
                    title=None,
                    base_title=None,
                    item_title=None,
                    randomize=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 = get_item(rating_key)
    current_kind = get_item_kind_from_rating_key(rating_key)

    timeout = 30

    oc = SubFolderObjectContainer(title2=title, replace_parent=True)

    # 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)

    # get the plex item
    plex_item = get_item(rating_key)

    # 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

            # 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

                part_index_addon = ""
                part_summary_addon = ""
                if has_multiple_parts:
                    part_index_addon = u"File %s: " % part_index
                    part_summary_addon = "%s " % filename

                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, 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=lang.name,
                            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"%sActions for %s subtitle" %
                                        (part_index_addon, lang.name),
                                        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=lang.name,
                            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, lang.name),
                                        summary=summary))

    add_ignore_options(oc,
                       "videos",
                       title=item_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):
    """
    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
Beispiel #23
0
def prepare_video(pms_video_info,
                  ignore_all=False,
                  hints=None,
                  rating_key=None,
                  providers=None,
                  skip_hashing=False):
    """
    returnes a subliminal/guessit-refined parsed video
    :param pms_video_info:
    :param ignore_all:
    :param hints:
    :param rating_key:
    :return:
    """
    embedded_subtitles = not ignore_all and Prefs['subtitles.scan.embedded']
    external_subtitles = not ignore_all and Prefs['subtitles.scan.external']

    plex_part = pms_video_info["plex_part"]

    if ignore_all:
        Log.Debug("Force refresh intended.")

    Log.Debug(
        "Detecting streams: %s, account_for_external_subtitles=%s, account_for_embedded_subtitles=%s"
        % (plex_part.file, external_subtitles, embedded_subtitles))

    known_embedded = []
    parts = []
    for media in list(Plex["library"].metadata(rating_key))[0].media:
        parts += media.parts

    plexpy_part = None
    for part in parts:
        if int(part.id) == int(plex_part.id):
            plexpy_part = part

    # embedded subtitles
    # fixme: skip the whole scanning process if known_embedded == wanted languages?
    audio_languages = []
    if plexpy_part:
        update_stream_info(plexpy_part)
        for stream in plexpy_part.streams:
            if stream.stream_type == 2:
                lang = None
                try:
                    lang = language_from_stream(stream.language_code)
                except LanguageError:
                    Log.Info(
                        "Couldn't detect embedded audio stream language: %s",
                        stream.language_code)

                # treat unknown language as lang1?
                if not lang and config.treat_und_as_first:
                    lang = Language.rebuild(list(config.lang_list)[0])
                    Log.Info("Assuming language %s for audio stream: %s", lang,
                             getattr(stream, "index", None))

                audio_languages.append(lang)

            # subtitle stream
            elif stream.stream_type == 3 and embedded_subtitles:
                is_forced = is_stream_forced(stream)

                if ((config.forced_only or config.forced_also)
                        and is_forced) or not is_forced:
                    # embedded subtitle
                    # fixme: tap into external subtitles here instead of scanning for ourselves later?
                    if stream.codec and getattr(stream, "index", None):
                        if config.exotic_ext or stream.codec.lower(
                        ) in config.text_based_formats:
                            lang = None
                            try:
                                lang = language_from_stream(
                                    stream.language_code)
                            except LanguageError:
                                Log.Info(
                                    "Couldn't detect embedded subtitle stream language: %s",
                                    stream.language_code)

                            # treat unknown language as lang1?
                            if not lang and config.treat_und_as_first:
                                lang = Language.rebuild(
                                    list(config.lang_list)[0])
                                Log.Info(
                                    "Assuming language %s for subtitle stream: %s",
                                    lang, getattr(stream, "index", None))

                            if lang:
                                if is_forced:
                                    lang.forced = True
                                known_embedded.append(lang)
    else:
        Log.Warn("Part %s missing of %s, not able to scan internal streams",
                 plex_part.id, rating_key)

    # metadata subtitles
    known_metadata_subs = set()
    meta_subs = get_subtitles_from_metadata(plex_part)
    for language, subList in meta_subs.iteritems():
        try:
            lang = Language.fromietf(Locale.Language.Match(language))
        except LanguageError:
            if config.treat_und_as_first:
                lang = Language.rebuild(list(config.lang_list)[0])
            else:
                continue

        if subList:
            for key in subList:
                if key.startswith("subzero_md_forced"):
                    lang = Language.rebuild(lang, forced=True)

                known_metadata_subs.add(lang)
                Log.Debug("Found metadata subtitle %r:%s for %s", lang, key,
                          plex_part.file)

    Log.Debug("Known metadata subtitles: %r", known_metadata_subs)
    Log.Debug("Known embedded subtitles: %r", known_embedded)

    subtitle_storage = get_subtitle_storage()
    stored_subs = subtitle_storage.load(rating_key)
    subtitle_storage.destroy()

    try:
        # get basic video info scan (filename)
        video = parse_video(plex_part.file,
                            hints,
                            skip_hashing=config.low_impact_mode
                            or skip_hashing,
                            providers=providers)

        # set stream languages
        if audio_languages:
            video.audio_languages = audio_languages
            Log.Info("Found audio streams: %s" %
                     ", ".join([str(l) for l in audio_languages]))

        if not ignore_all:
            set_existing_languages(
                video,
                pms_video_info,
                external_subtitles=external_subtitles,
                embedded_subtitles=embedded_subtitles,
                known_embedded=known_embedded,
                stored_subs=stored_subs,
                languages=config.lang_list,
                only_one=config.only_one,
                known_metadata_subs=known_metadata_subs,
                match_strictness=config.ext_match_strictness)

        # add video fps info
        video.fps = plex_part.fps
        return video

    except ValueError:
        Log.Warn("File could not be guessed: %s: %s", plex_part.file,
                 traceback.format_exc())
Beispiel #24
0
def agent_extract_embedded(video_part_map):
    try:
        subtitle_storage = get_subtitle_storage()

        to_extract = []
        item_count = 0

        for scanned_video, part_info in video_part_map.iteritems():
            plexapi_item = scanned_video.plexapi_metadata["item"]
            stored_subs = subtitle_storage.load_or_new(plexapi_item)
            valid_langs_in_media = audio_streams_match_languages(
                scanned_video, config.get_lang_list(ordered=True))

            if not config.lang_list.difference(valid_langs_in_media):
                Log.Debug(
                    "Skipping embedded subtitle extraction for %s, audio streams are in correct language(s)",
                    plexapi_item.rating_key)
                continue

            for plexapi_part in get_all_parts(plexapi_item):
                item_count = item_count + 1
                used_one_unknown_stream = False
                used_one_known_stream = False
                for requested_language in config.lang_list:
                    skip_unknown = used_one_unknown_stream or used_one_known_stream
                    embedded_subs = stored_subs.get_by_provider(
                        plexapi_part.id, requested_language, "embedded")
                    current = stored_subs.get_any(plexapi_part.id, requested_language) or \
                        requested_language in scanned_video.external_subtitle_languages

                    if not embedded_subs:
                        stream_data = get_embedded_subtitle_streams(
                            plexapi_part,
                            requested_language=requested_language,
                            skip_unknown=skip_unknown)

                        if stream_data and stream_data[0]["language"]:
                            stream = stream_data[0]["stream"]
                            if stream_data[0]["is_unknown"]:
                                used_one_unknown_stream = True
                            else:
                                used_one_known_stream = True

                            to_extract.append(
                                ({
                                    scanned_video: part_info
                                }, plexapi_part, str(stream.index),
                                 str(requested_language), not current))

                            if not cast_bool(
                                    Prefs["subtitles.search_after_autoextract"]
                            ):
                                scanned_video.subtitle_languages.update(
                                    {requested_language})
                    else:
                        Log.Debug(
                            "Skipping embedded subtitle extraction for %s, already got %r from %s",
                            plexapi_item.rating_key, requested_language,
                            embedded_subs[0].id)
        if to_extract:
            Log.Info(
                "Triggering extraction of %d embedded subtitles of %d items",
                len(to_extract), item_count)
            Thread.Create(
                multi_extract_embedded,
                stream_list=to_extract,
                refresh=True,
                with_mods=True,
                single_thread=not config.advanced.auto_extract_multithread)
    except:
        Log.Error(
            "Something went wrong when auto-extracting subtitles, continuing: %s",
            traceback.format_exc())
Beispiel #25
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:
            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
Beispiel #26
0
def scan_video(pms_video_info,
               ignore_all=False,
               hints=None,
               rating_key=None,
               no_refining=False):
    """
    returnes a subliminal/guessit-refined parsed video
    :param pms_video_info:
    :param ignore_all:
    :param hints:
    :param rating_key:
    :return:
    """
    embedded_subtitles = not ignore_all and Prefs['subtitles.scan.embedded']
    external_subtitles = not ignore_all and Prefs['subtitles.scan.external']

    plex_part = pms_video_info["plex_part"]

    if ignore_all:
        Log.Debug("Force refresh intended.")

    Log.Debug(
        "Scanning video: %s, external_subtitles=%s, embedded_subtitles=%s" %
        (plex_part.file, external_subtitles, embedded_subtitles))

    known_embedded = []
    parts = []
    for media in list(Plex["library"].metadata(rating_key))[0].media:
        parts += media.parts

    plexpy_part = None
    for part in parts:
        if int(part.id) == int(plex_part.id):
            plexpy_part = part

    # embedded subtitles
    if plexpy_part:
        for stream in plexpy_part.streams:
            # subtitle stream
            if stream.stream_type == 3:
                if (config.forced_only and getattr(stream, "forced")) or \
                        (not config.forced_only and not getattr(stream, "forced")):

                    # embedded subtitle
                    # fixme: tap into external subtitles here instead of scanning for ourselves later?
                    if not stream.stream_key and stream.codec:
                        if config.exotic_ext or stream.codec.lower(
                        ) in TEXT_SUBTITLE_EXTS:
                            lang_code = stream.language_code

                            # treat unknown language as lang1?
                            if not lang_code and config.treat_und_as_first:
                                lang_code = list(config.lang_list)[0].alpha3
                            known_embedded.append(lang_code)
    else:
        Log.Warn("Part %s missing of %s, not able to scan internal streams",
                 plex_part.id, rating_key)

    subtitle_storage = get_subtitle_storage()
    stored_subs = subtitle_storage.load(rating_key)
    subtitle_storage.destroy()

    try:
        # get basic video info scan (filename)
        video = parse_video(plex_part.file,
                            pms_video_info,
                            hints,
                            external_subtitles=external_subtitles,
                            embedded_subtitles=embedded_subtitles,
                            known_embedded=known_embedded,
                            forced_only=config.forced_only,
                            no_refining=no_refining,
                            ignore_all=ignore_all,
                            stored_subs=stored_subs)

        # add video fps info
        video.fps = plex_part.fps
        return video

    except ValueError:
        Log.Warn("File could not be guessed by subliminal: %s" %
                 plex_part.file)
Beispiel #27
0
def prepare_video(pms_video_info, ignore_all=False, hints=None, rating_key=None, providers=None, skip_hashing=False):
    """
    returnes a subliminal/guessit-refined parsed video
    :param pms_video_info:
    :param ignore_all:
    :param hints:
    :param rating_key:
    :return:
    """
    embedded_subtitles = not ignore_all and Prefs['subtitles.scan.embedded']
    external_subtitles = not ignore_all and Prefs['subtitles.scan.external']

    plex_part = pms_video_info["plex_part"]

    if ignore_all:
        Log.Debug("Force refresh intended.")

    Log.Debug("Detecting streams: %s, external_subtitles=%s, embedded_subtitles=%s" % (
        plex_part.file, external_subtitles, embedded_subtitles))

    known_embedded = []
    parts = []
    for media in list(Plex["library"].metadata(rating_key))[0].media:
        parts += media.parts

    plexpy_part = None
    for part in parts:
        if int(part.id) == int(plex_part.id):
            plexpy_part = part

    # embedded subtitles
    # fixme: skip the whole scanning process if known_embedded == wanted languages?
    audio_languages = []
    if plexpy_part:
        for stream in plexpy_part.streams:
            if stream.stream_type == 2:
                lang = None
                try:
                    lang = language_from_stream(stream.language_code)
                except LanguageError:
                    Log.Debug("Couldn't detect embedded audio stream language: %s", stream.language_code)

                # treat unknown language as lang1?
                if not lang and config.treat_und_as_first:
                    lang = Language.rebuild(list(config.lang_list)[0])

                audio_languages.append(lang)

            # subtitle stream
            elif stream.stream_type == 3 and embedded_subtitles:
                is_forced = helpers.is_stream_forced(stream)

                if ((config.forced_only or config.forced_also) and is_forced) or not is_forced:
                    # embedded subtitle
                    # fixme: tap into external subtitles here instead of scanning for ourselves later?
                    if stream.codec and getattr(stream, "index", None):
                        if config.exotic_ext or stream.codec.lower() in config.text_based_formats:
                            lang = None
                            try:
                                lang = language_from_stream(stream.language_code)
                            except LanguageError:
                                Log.Debug("Couldn't detect embedded subtitle stream language: %s", stream.language_code)

                            # treat unknown language as lang1?
                            if not lang and config.treat_und_as_first:
                                lang = Language.rebuild(list(config.lang_list)[0])

                            if lang:
                                if is_forced:
                                    lang.forced = True
                                known_embedded.append(lang)
    else:
        Log.Warn("Part %s missing of %s, not able to scan internal streams", plex_part.id, rating_key)

    # metadata subtitles
    known_metadata_subs = set()
    meta_subs = get_subtitles_from_metadata(plex_part)
    for language, subList in meta_subs.iteritems():
        try:
            lang = Language.fromietf(Locale.Language.Match(language))
        except LanguageError:
            if config.treat_und_as_first:
                lang = Language.rebuild(list(config.lang_list)[0])
            else:
                continue

        if subList:
            for key in subList:
                if key.startswith("subzero_md_forced"):
                    lang = Language.rebuild(lang, forced=True)

                known_metadata_subs.add(lang)
                Log.Debug("Found metadata subtitle %r:%s for %s", lang, key, plex_part.file)

    Log.Debug("Known metadata subtitles: %r", known_metadata_subs)
    Log.Debug("Known embedded subtitles: %r", known_embedded)

    subtitle_storage = get_subtitle_storage()
    stored_subs = subtitle_storage.load(rating_key)
    subtitle_storage.destroy()

    try:
        # get basic video info scan (filename)
        video = parse_video(plex_part.file, hints, skip_hashing=config.low_impact_mode or skip_hashing,
                            providers=providers)

        # set stream languages
        if audio_languages:
            video.audio_languages = audio_languages
            Log.Info("Found audio streams: %s" % ", ".join([str(l) for l in audio_languages]))

        if not ignore_all:
            set_existing_languages(video, pms_video_info, external_subtitles=external_subtitles,
                                   embedded_subtitles=embedded_subtitles, known_embedded=known_embedded,
                                   stored_subs=stored_subs, languages=config.lang_list,
                                   only_one=config.only_one, known_metadata_subs=known_metadata_subs)

        # add video fps info
        video.fps = plex_part.fps
        return video

    except ValueError:
        Log.Warn("File could not be guessed: %s: %s", plex_part.file, traceback.format_exc())
Beispiel #28
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
Beispiel #29
0
def migrate():
    """
    some Dict/Data migrations here, no need for a more in-depth migration path for now
    :return:
    """

    # migrate subtitle history from Dict to Data
    if "history" in Dict and Dict["history"].get("history_items"):
        Log.Debug("Running migration for history data")
        from support.history import get_history
        history = get_history()

        for item in reversed(Dict["history"]["history_items"]):
            history.add(item.item_title,
                        item.rating_key,
                        item.section_title,
                        subtitle=item.subtitle,
                        mode=item.mode,
                        time=item.time)

        del Dict["history"]
        Dict.Save()

    # migrate subtitle storage from Dict to Data
    if "subs" in Dict:
        from support.storage import get_subtitle_storage
        from subzero.subtitle_storage import StoredSubtitle
        from support.plex_media import get_item

        subtitle_storage = get_subtitle_storage()

        for video_id, parts in Dict["subs"].iteritems():
            try:
                item = get_item(video_id)
            except:
                continue

            if not item:
                continue

            stored_subs = subtitle_storage.load_or_new(item)
            stored_subs.version = 1

            Log.Debug(u"Migrating %s" % video_id)

            stored_any = False
            for part_id, lang_dict in parts.iteritems():
                part_id = str(part_id)
                Log.Debug(u"Migrating %s, %s" % (video_id, part_id))

                for lang, subs in lang_dict.iteritems():
                    lang = str(lang)
                    if "current" in subs:
                        current_key = subs["current"]
                        provider_name, subtitle_id = current_key
                        sub = subs.get(current_key)
                        if sub and sub.get("title") and sub.get(
                                "mode"
                        ):  # ditch legacy data without sufficient info
                            stored_subs.title = sub["title"]
                            new_sub = StoredSubtitle(
                                sub["score"],
                                sub["storage"],
                                sub["hash"],
                                provider_name,
                                subtitle_id,
                                date_added=sub["date_added"],
                                mode=sub["mode"])

                            if part_id not in stored_subs.parts:
                                stored_subs.parts[part_id] = {}

                            if lang not in stored_subs.parts[part_id]:
                                stored_subs.parts[part_id][lang] = {}

                            Log.Debug(u"Migrating %s, %s, %s" %
                                      (video_id, part_id, current_key))

                            stored_subs.parts[part_id][lang][
                                current_key] = new_sub
                            stored_subs.parts[part_id][lang][
                                "current"] = current_key
                            stored_any = True

            if stored_any:
                subtitle_storage.save(stored_subs)

        subtitle_storage.destroy()
        del Dict["subs"]
        Dict.Save()
Beispiel #30
0
def ItemDetailsMenu(rating_key,
                    title=None,
                    base_title=None,
                    item_title=None,
                    randomize=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:
    """
    title = unicode(base_title) + " > " + unicode(
        title) if base_title else unicode(title)
    item = get_item(rating_key)
    current_kind = get_item_kind_from_rating_key(rating_key)

    timeout = 30

    oc = SubFolderObjectContainer(title2=title, replace_parent=True)
    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"Auto-search: %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)

    # get the plex item
    plex_item = list(Plex["library"].metadata(rating_key))[0]

    # get current media info for that item
    media = plex_item.media

    # look for subtitles for all available media parts and all of their languages
    for part in media.parts:
        filename = os.path.basename(part.file)
        part_id = str(part.id)

        # iterate through all configured languages
        for lang in config.lang_list:
            lang_a2 = lang.alpha2
            # ietf lang?
            if cast_bool(Prefs["subtitles.language.ietf"]) and "-" in lang_a2:
                lang_a2 = lang_a2.split("-")[0]

            # get corresponding stored subtitle data for that media part (physical media item), for language
            current_sub = stored_subs.get_any(part_id, lang_a2)
            current_sub_id = None
            current_sub_provider_name = None

            summary = u"No current subtitle in storage"
            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"Current subtitle: %s (added: %s, %s), Language: %s, Score: %i, Storage: %s" % \
                          (current_sub.provider_name, df(current_sub.date_added), current_sub.mode_verbose, lang,
                           current_sub.score, current_sub.storage_type)

            oc.add(
                DirectoryObject(key=Callback(
                    ListAvailableSubsForItemMenu,
                    rating_key=rating_key,
                    part_id=part_id,
                    title=title,
                    item_title=item_title,
                    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"List %s subtitles" % lang.name,
                                summary=summary))

    add_ignore_options(oc,
                       "videos",
                       title=item_title,
                       rating_key=rating_key,
                       callback_menu=IgnoreMenu)

    return oc