From 052dd3efbe75fea1de80dddc7005eb312d6ee380 Mon Sep 17 00:00:00 2001 From: DYefremov Date: Mon, 8 Feb 2021 22:23:28 +0300 Subject: [PATCH] improved built-in player [added windowed mode] --- app/tools/media.py | 36 ++++++------ app/ui/main_app_window.py | 117 ++++++++++++++++++++++++++++---------- 2 files changed, 108 insertions(+), 45 deletions(-) diff --git a/app/tools/media.py b/app/tools/media.py index a43877f2..2bed2789 100644 --- a/app/tools/media.py +++ b/app/tools/media.py @@ -10,10 +10,10 @@ from app.settings import PlayStreamsMode class Player: + """ Simple wrapper for VLC media player. """ __VLC_INSTANCE = None - __PLAY_STREAMS_MODE = PlayStreamsMode.BUILT_IN - def __init__(self, rewind_callback, position_callback, error_callback, playing_callback): + def __init__(self, mode, rewind_cb, position_cb, error_cb, playing_cb): try: from app.tools import vlc from app.tools.vlc import EventType @@ -21,39 +21,39 @@ class Player: log("{}: Load library error: {}".format(__class__.__name__, e)) raise ImportError else: + self._mode = mode self._is_playing = False args = "--quiet {}".format("" if sys.platform == "darwin" else "--no-xlib") self._player = vlc.Instance(args).media_player_new() ev_mgr = self._player.event_manager() - if rewind_callback: + if rewind_cb: # TODO look other EventType options ev_mgr.event_attach(EventType.MediaPlayerBuffering, - lambda et, p: rewind_callback(p.get_media().get_duration()), + lambda et, p: rewind_cb(p.get_media().get_duration()), self._player) - if position_callback: + if position_cb: ev_mgr.event_attach(EventType.MediaPlayerTimeChanged, - lambda et, p: position_callback(p.get_time()), + lambda et, p: position_cb(p.get_time()), self._player) - if error_callback: + if error_cb: ev_mgr.event_attach(EventType.MediaPlayerEncounteredError, - lambda et, p: error_callback(), + lambda et, p: error_cb(), self._player) - if playing_callback: + if playing_cb: ev_mgr.event_attach(EventType.MediaPlayerPlaying, - lambda et, p: playing_callback(), + lambda et, p: playing_cb(), self._player) @classmethod - def get_instance(cls, rewind_callback=None, position_callback=None, error_callback=None, playing_callback=None): + def get_instance(cls, mode, rewind_cb=None, position_cb=None, error_cb=None, playing_cb=None): if not cls.__VLC_INSTANCE: - cls.__VLC_INSTANCE = Player(rewind_callback, position_callback, error_callback, playing_callback) + cls.__VLC_INSTANCE = Player(mode, rewind_cb, position_cb, error_cb, playing_cb) return cls.__VLC_INSTANCE - @staticmethod - def get_play_mode(): - return Player.__PLAY_STREAMS_MODE + def get_play_mode(self): + return self._mode @run_task def play(self, mrl=None): @@ -80,6 +80,7 @@ class Player: self._is_playing = False self._player.stop() self._player.release() + self.__VLC_INSTANCE = None def set_xwindow(self, xid): self._player.set_xwindow(xid) @@ -115,7 +116,10 @@ class Player: class HttpPlayer: - """ Simple wrapper for VLC media player to interact over http. """ + """ Simple wrapper for VLC media player to interact over http. + + NOT used anymore! May be deleted in the future! + """ __VLC_INSTANCE = None __PLAY_STREAMS_MODE = PlayStreamsMode.VLC diff --git a/app/ui/main_app_window.py b/app/ui/main_app_window.py index 6e28395e..36c8f987 100644 --- a/app/ui/main_app_window.py +++ b/app/ui/main_app_window.py @@ -18,7 +18,7 @@ from app.eparser.enigma.bouquets import BqServiceType from app.eparser.iptv import export_to_m3u from app.eparser.neutrino.bouquets import BqType from app.settings import SettingsType, Settings, SettingsException, PlayStreamsMode, SettingsReadException -from app.tools.media import Player, Recorder, HttpPlayer +from app.tools.media import Player, Recorder from app.ui.epg_dialog import EpgDialog from app.ui.transmitter import LinksTransmitter from .backup import BackupDialog, backup_data, clear_data_path @@ -198,6 +198,7 @@ class Application(Gtk.Application): self._player = None self._full_screen = False self._current_mrl = None + self._playback_window = None # Record self._recorder = None # http api @@ -1867,6 +1868,7 @@ class Application(Gtk.Application): def on_bouquets_selection(self, model, path, column): self.reset_view_sort_indication(self._fav_view) + self._alt_revealer.set_visible(False) self._current_bq_name = model[path][0] if len(path) > 1 else None self._bq_name_label.set_text(self._current_bq_name if self._current_bq_name else "") @@ -2441,30 +2443,34 @@ class Application(Gtk.Application): self.save_stream_to_m3u(url) return + if self._player and self._player.get_play_mode() is not mode: + self.show_error_dialog("Play mode has been changed!\nRestart the program to apply the settings.") + self.set_playback_elms_active() + return + if mode is PlayStreamsMode.VLC: try: - if self._player and self._player.get_play_mode() is not mode: - self._player.release() - self._player = None if not self._player: - self._player = HttpPlayer.get_instance(self._settings) - except ImportError: - self.show_error_dialog("No VLC is found. Check that it is installed!") - else: - self._player.play(url) + self._current_mrl = url + self.show_playback_window() + elif self._playback_window: + title = self.get_playback_title() + GLib.idle_add(self._playback_window.set_title, title) + GLib.idle_add(self._player.play, url, priority=GLib.PRIORITY_LOW) + GLib.idle_add(self._playback_window.show) + else: + self.show_error_dialog("Init player error!") finally: self.set_playback_elms_active() else: - if not self._player_box.get_visible(): - self.set_player_area_size(self._player_box) + if not self._player: self._current_mrl = url - self._player_box.set_visible(True) + else: + if not self._player_box.get_visible(): + self.set_player_area_size(self._player_box) - if self._player and self._player.get_play_mode() is PlayStreamsMode.BUILT_IN: GLib.idle_add(self._player.play, url, priority=GLib.PRIORITY_LOW) - elif self._player: - self.show_error_dialog("Play mode has been changed!\nRestart the program to apply the settings.") - self.set_playback_elms_active() + self._player_box.set_visible(True) def on_player_stop(self, item=None): if self._player: @@ -2497,12 +2503,17 @@ class Application(Gtk.Application): self._player_prev_button.set_sensitive(current_index != 0) self._player_next_button.set_sensitive(len(self._fav_model) != current_index + 1) - def on_player_close(self, item=None): + def on_player_close(self, window=None, event=None): if self._player: self._player.stop() self.set_playback_elms_active() - GLib.idle_add(self._player_box.set_visible, False, priority=GLib.PRIORITY_LOW) + if self._playback_window: + self._settings.add("playback_window_size", self._playback_window.get_size()) + self._playback_window.hide() + else: + GLib.idle_add(self._player_box.set_visible, False, priority=GLib.PRIORITY_LOW) + return True @lru_cache(maxsize=1) def on_player_duration_changed(self, duration): @@ -2532,14 +2543,13 @@ class Application(Gtk.Application): return "{}{:02d}:{:02d}".format(str(h) + ":" if h else "", m, s) def on_drawing_area_realize(self, widget): - self.set_player_area_size(widget) - if not self._player: try: - self._player = Player.get_instance(rewind_callback=self.on_player_duration_changed, - position_callback=self.on_player_time_changed, - error_callback=self.on_player_error, - playing_callback=self.set_playback_elms_active) + self._player = Player.get_instance(mode=self._settings.play_streams_mode, + rewind_cb=self.on_player_duration_changed, + position_cb=self.on_player_time_changed, + error_cb=self.on_player_error, + playing_cb=self.set_playback_elms_active) except (ImportError, NameError, AttributeError): self.show_error_dialog("No VLC is found. Check that it is installed!") return True @@ -2551,7 +2561,10 @@ class Application(Gtk.Application): self._player.play(self._current_mrl) finally: self.set_playback_elms_active() + if self._settings.play_streams_mode is PlayStreamsMode.BUILT_IN: + self.set_player_area_size(widget) + @run_idle def set_player_area_size(self, widget): w, h = self._main_window.get_size() widget.set_size_request(w * 0.6, -1) @@ -2579,8 +2592,12 @@ class Application(Gtk.Application): def on_full_screen(self, item=None): self._full_screen = not self._full_screen - self.update_state_on_full_screen(not self._full_screen) - self._main_window.fullscreen() if self._full_screen else self._main_window.unfullscreen() + if self._settings.play_streams_mode is PlayStreamsMode.BUILT_IN: + self.update_state_on_full_screen(not self._full_screen) + self._main_window.fullscreen() if self._full_screen else self._main_window.unfullscreen() + elif self._playback_window: + self._player_tool_bar.set_visible(not self._full_screen) + self._playback_window.fullscreen() if self._full_screen else self._playback_window.unfullscreen() @run_idle def update_state_on_full_screen(self, visible): @@ -2588,6 +2605,40 @@ class Application(Gtk.Application): self._player_tool_bar.set_visible(visible) self._status_bar_box.set_visible(visible and not self._app_info_box.get_visible()) + @run_idle + def show_playback_window(self): + width, height = 480, 240 + size = self._settings.get("playback_window_size") + if size: + width, height = size + + self._playback_window = Gtk.Window(title=self.get_playback_title(), + window_position=Gtk.WindowPosition.CENTER, + gravity=Gdk.Gravity.CENTER, + icon_name="demon-editor") + self._playback_window.resize(width, height) + self._playback_window.connect("delete-event", self.on_player_close) + + if self._settings.is_darwin: + self._player_drawing_area.reparent(self._playback_window) + else: + self._player_prev_button.set_visible(False) + self._player_next_button.set_visible(False) + box = Gtk.HBox(visible=True, orientation="vertical") + self._player_drawing_area.reparent(box) + self._player_box.remove(self._player_tool_bar) + box.pack_end(self._player_tool_bar, False, False, 0) + self._playback_window.add(box) + + self._playback_window.set_application(self) + self._playback_window.show() + + def get_playback_title(self): + path, column = self._fav_view.get_cursor() + if path: + return "DemonEditor [{}]".format(self._fav_model[path][:][Column.FAV_SERVICE]) + return "DemonEditor [Playback]" + # ************************* Record ***************************** # def on_record(self, button): @@ -2675,7 +2726,7 @@ class Application(Gtk.Application): def on_watch(self, item=None): """ Switch to the channel and watch in the player """ - if self._app_info_box.get_visible() and self._settings.play_streams_mode is PlayStreamsMode.BUILT_IN: + if not self._app_info_box.get_visible() and self._settings.play_streams_mode is PlayStreamsMode.BUILT_IN: self.set_player_area_size(self._player_box) self._player_box.set_visible(True) GLib.idle_add(self._app_info_box.set_visible, False) @@ -2731,6 +2782,11 @@ class Application(Gtk.Application): if self._player and self._player.is_playing(): self._player.stop() + # IPTV type checking + row = self._fav_model[path][:] + if row[Column.FAV_TYPE] == BqServiceType.IPTV.name and callback: + callback = self.play(get_iptv_url(row, self._s_type)) + def zap(rq): if rq and rq.get("e2state", False): GLib.idle_add(scroll_to, path, self._fav_view) @@ -2752,8 +2808,11 @@ class Application(Gtk.Application): return srv = self._services.get(fav_id, None) - if srv and srv.transponder or srv_type == BqServiceType.IPTV.name: - return srv.picon_id.rstrip(".png").replace("_", ":") + if srv: + if srv_type == BqServiceType.IPTV.name: + return srv.fav_id.strip() + elif srv.picon_id: + return srv.picon_id.rstrip(".png").replace("_", ":") def update_info(self): """ Updating current info over HTTP API """