Example #1
0
    def __init__(self, **kwargs):
        self.client = AsyncioClient(**kwargs)
        self.subtitle_downloads = {}

        asyncio.async(self.client_event_handler())
Example #2
0
class VideoPlayer:
    def __init__(self, **kwargs):
        self.client = AsyncioClient(**kwargs)
        self.subtitle_downloads = {}

        asyncio.async(self.client_event_handler())

    @property
    def audio(self):
        try:
            return self.client.audio
        except ValueError:
            return 0

    def sub_language(self):
        for track in self.client.subtitles():
            if track.get("selected", 0):
                return track.get("lang", "unk")
        return "unk"

    def audio_language(self):
        for track in self.client.audio_tracks():
            if track.get("selected", 0):
                return track.get("lang", "unk")
        return "unk"

    @property
    def sub(self):
        try:
            return self.client.sub
        except ValueError:
            return 0

    @property
    def length(self):
        try:
            return self.client.length
        except ValueError:
            return 0

    @property
    def path(self):
        return self.client.path

    @property
    def paused(self):
        return self.client.pause

    @paused.setter
    def paused(self, v):
        self.client.pause = v

    @property
    def percent_pos(self):
        try:
            return self.client.percent_pos
        except ValueError:
            return 0.0

    @property
    def volume(self):
        try:
            return self.client.volume
        except ValueError:
            return None

    @volume.setter
    def volume(self, amount):
        try:
            self.client.volume = float(amount)
        except ValueError:
            pass

    @property
    def has_chapters(self):
        try:
            return bool(self.client.chapters)
        except ValueError:
            return False

    @property
    def length(self):
        try:
            return self.client.length
        except ValueError:
            return 0.0

    @property
    def time_pos(self):
        try:
            return self.client.time_pos
        except ValueError:
            return 0.0

    def get_progress(self):
        time_pos, length = self.time_pos, self.length

        if length <= 0.0:
            return ""

        progress = str(timedelta(seconds=time_pos)).split(".")[0]
        length = str(timedelta(seconds=length)).split(".")[0]
        return "{} / {}".format(progress, length)

    def play(self, media_file, append=False):
        self.client.loadfile(media_file, add_mode=LoadFile.Append if append else LoadFile.Replace)

    def stop(self):
        self.client.stop()

    def seek_backward(self, seek_size):
        try:
            if self.has_chapters:
                self.client.chapter -= 1
            else:
                self.client.seek(-seek_size)
        except ValueError:
            pass

    def seek_forward(self, seek_size):
        try:
            if self.has_chapters:
                self.client.chapter += 1
            else:
                self.client.seek(seek_size)
        except ValueError:
            pass

    def load_srt_subtitle(self, path, language):
        self.client.sub_add(path, title=language, lang=language)

    @asyncio.coroutine
    def set_subtitle(self, sid):
        if isinstance(sid, str) and sid.startswith("download_"):
            offset = len("download_")
            lang = sid[offset:]
            yield from events.broadcast("download-subtitle", path=self.client.path, language=lang)
            return

        log.info("Setting subtitle to {}", sid)
        self.client.sub = sid

    def set_audio(self, aid):
        log.info("Setting subtitle to {}", aid)
        self.client.audio = aid

    def add_available_srt_subtitles(self):
        path = pathlib.Path(self.client.path)
        glob = "{}*.srt".format(path.stem)
        glob = re.sub(r"\[", "[[]", glob)
        glob = re.sub(r"(?<!\[)\]", "[]]", glob)

        for subtitle in path.parent.glob(glob):
            subtitle = pathlib.Path(subtitle)
            language = get_language(subtitle)

            if language is not None:
                log.debug("Adding {} as {}", subtitle, language)
                self.load_srt_subtitle(str(subtitle), language)
            else:
                log.debug("Couldn't figure out a language for {}", subtitle)

    @asyncio.coroutine
    def client_event_handler(self):
        while True:
            event = yield from self.client.event_queue.get()

            log.debug("mpv event received {}", event_name(event))

            if event == libmpv.MPV_EVENT_FILE_LOADED:
                self.subtitle_downloads.clear()
                self.add_available_srt_subtitles()

                media = get_movie_or_tv_show(self.client.path)
                now_playing = media.title
                log.info("Now playing {}", now_playing)
                yield from asyncio.gather(
                    events.info(now_playing), broadcast_player_property("now_playing", now_playing)
                )
            elif event == libmpv.MPV_EVENT_TRACKS_CHANGED:
                yield from self.update_track_info()
            elif event == libmpv.MPV_EVENT_PLAYBACK_RESTART:
                yield from self.broadcast_volume()

    @asyncio.coroutine
    def update_track_info(self):
        yield from asyncio.gather(
            self.broadcast_available_subtitles(),
            self.broadcast_available_audio(),
            self.broadcast_subtitle(),
            broadcast_player_property("selected_audio", str(self.audio)),
            events.broadcast("list-subtitles", path=self.client.path),
        )

        if (
            self.sub != 0
            and self.audio_language() != "unk"
            and self.audio_language() == self.sub_language()
            and not int(Config.get("player", "subtitles for matching audio"))
        ):
            log.info("Disabling subtitle as it's the same as the language")
            self.client.sub = 0

    @asyncio.coroutine
    def broadcast_now_playing(self):
        if self.client.path is None:
            now_playing = None
        else:
            media = get_movie_or_tv_show(self.client.path)
            now_playing = media.title

        yield from broadcast_player_property("now_playing", now_playing)

    @asyncio.coroutine
    def broadcast_volume(self):
        volume = self.volume
        yield from broadcast_player_property("volume", volume)

    @asyncio.coroutine
    def broadcast_subtitle(self):
        yield from broadcast_player_property("selected_subtitle", str(self.sub))

    @asyncio.coroutine
    def broadcast_available_audio(self):
        audio_streams = [
            dict(value=str(track["id"]), display=isocodes.nicename(track.get("lang", "unk")))
            for track in self.client.audio_tracks()
        ]

        if len(audio_streams) <= 1:
            audio_streams = None
        else:
            audio_streams = sorted(audio_streams, key=itemgetter("display"))

        yield from broadcast_player_property("available_audio", audio_streams)

    @asyncio.coroutine
    def broadcast_available_subtitles(self):
        subtitles = [
            dict(value=str(track["id"]), display=isocodes.nicename(track.get("lang", "unk")))
            for track in self.client.subtitles()
        ]

        if self.subtitle_downloads:
            subtitles.extend(
                [
                    dict(value="download_{}".format(slang), display=nicename)
                    for (slang, nicename) in self.subtitle_downloads.items()
                ]
            )

        if not subtitles:
            subtitles = None
        else:
            subtitles = sorted(subtitles, key=itemgetter("display"))

        yield from broadcast_player_property("available_subtitles", subtitles)

    @asyncio.coroutine
    def broadcast_all_properties(self):
        yield from asyncio.gather(
            self.broadcast_now_playing(),
            self.broadcast_available_subtitles(),
            self.broadcast_available_audio(),
            self.broadcast_subtitle(),
            broadcast_player_property("selected_audio", str(self.audio)),
            self.broadcast_volume(),
        )