From ee2965973982cc2e095fb4a3ca644c78a840d8ca Mon Sep 17 00:00:00 2001 From: DYefremov Date: Sat, 7 Mar 2020 18:33:51 +0300 Subject: [PATCH] added record of current service --- app/tools/media.py | 60 ++++++++++++++++++++++++++++++++++++- app/ui/main_app_window.py | 63 +++++++++++++++++++++++++++++++++++---- app/ui/main_window.glade | 30 ++++++++++++++++--- 3 files changed, 143 insertions(+), 10 deletions(-) diff --git a/app/tools/media.py b/app/tools/media.py index 9ef94dca..4ba89ec1 100644 --- a/app/tools/media.py +++ b/app/tools/media.py @@ -1,6 +1,8 @@ +import os import sys +from datetime import datetime -from app.commons import run_task, log +from app.commons import run_task, log, _DATE_FORMAT class Player: @@ -103,5 +105,61 @@ class Player: self._player.set_fullscreen(full) +class Recorder: + __VLC_REC_INSTANCE = None + + _CMD = "sout=#std{{access=file,mux=ts,dst={}{}_{}.ts}}" + + def __init__(self): + try: + from app.tools import vlc + from app.tools.vlc import EventType + except OSError as e: + log("{}: Load library error: {}".format(__class__.__name__, e)) + raise ImportError + else: + self._is_record = False + args = "--quiet {}".format("" if sys.platform == "darwin" else "--no-xlib") + self._recorder = vlc.Instance(args).media_player_new() + + @classmethod + def get_instance(cls): + if not cls.__VLC_REC_INSTANCE: + cls.__VLC_REC_INSTANCE = Recorder() + return cls.__VLC_REC_INSTANCE + + @run_task + def record(self, url, path, name): + if self._recorder: + self._recorder.stop() + + os.makedirs(os.path.dirname(path), exist_ok=True) + d_now = datetime.now().strftime(_DATE_FORMAT) + media = self._recorder.get_instance().media_new(url, self._CMD.format(path, name, d_now)) + media.get_mrl() + + self._recorder.set_media(media) + self._is_record = True + self._recorder.play() + log("Record started {}".format(d_now)) + + @run_task + def stop(self): + self._recorder.stop() + self._is_record = False + log("Recording stopped.") + + def is_record(self): + return self._is_record + + @run_task + def release(self): + if self._recorder: + self._recorder.stop() + self._recorder.release() + self._is_record = False + log("Recording stopped. Releasing...") + + if __name__ == "__main__": pass diff --git a/app/ui/main_app_window.py b/app/ui/main_app_window.py index 6dbd6345..16d0d49b 100644 --- a/app/ui/main_app_window.py +++ b/app/ui/main_app_window.py @@ -17,7 +17,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 -from app.tools.media import Player +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 @@ -69,6 +69,7 @@ class Application(Gtk.Application): super().__init__(flags=Gio.ApplicationFlags.HANDLES_COMMAND_LINE, **kwargs) # Adding command line options self.add_main_option("log", ord("l"), GLib.OptionFlags.NONE, GLib.OptionArg.NONE, "", None) + self.add_main_option("record", ord("r"), GLib.OptionFlags.NONE, GLib.OptionArg.NONE, "", None) self._handlers = {"on_close_app": self.on_close_app, "on_resize": self.on_resize, @@ -172,6 +173,8 @@ class Application(Gtk.Application): self._player = None self._full_screen = False self._drawing_area_xid = None + # Record + self._recorder = None # http api self._http_api = None self._fav_click_mode = None @@ -226,6 +229,9 @@ class Application(Gtk.Application): self._tv_count_label = builder.get_object("tv_count_label") self._radio_count_label = builder.get_object("radio_count_label") self._data_count_label = builder.get_object("data_count_label") + self._app_info_box.bind_property("visible", self._save_header_button, "visible", 4) + self._signal_level_bar.bind_property("visible", builder.get_object("play_current_service_button"), "visible") + self._signal_level_bar.bind_property("visible", builder.get_object("record_button"), "visible") self._receiver_info_box.bind_property("visible", self._http_status_image, "visible", 4) self._receiver_info_box.bind_property("visible", self._signal_box, "visible") # Force ctrl press event for view. Multiple selections in lists only with Space key(as in file managers)!!! @@ -264,7 +270,8 @@ class Application(Gtk.Application): self._player_box.bind_property("visible", self._profile_combo_box, "sensitive", 4) self._fav_view.bind_property("sensitive", self._player_prev_button, "sensitive") self._fav_view.bind_property("sensitive", self._player_next_button, "sensitive") - self._signal_level_bar.bind_property("visible", builder.get_object("play_current_service_button"), "visible") + # Record + self._record_image = builder.get_object("record_button_image") # Enabling events for the drawing area self._player_drawing_area.set_events(Gdk.ModifierType.BUTTON1_MASK) self._player_frame = builder.get_object("player_frame") @@ -395,9 +402,14 @@ class Application(Gtk.Application): """ Processing command line parameters. """ options = command_line.get_options_dict() options = options.end().unpack() + if "log" in options: init_logger() + if "record" in options: + log("Starting record of current stream...") + log("Not implemented yet!") + self.activate() return 0 @@ -474,9 +486,15 @@ class Application(Gtk.Application): """ Function for force ctrl press event for view """ event.state |= MOD_MASK - @run_idle def on_close_app(self, *args): - self.quit() + if self._recorder: + if self._recorder.is_record(): + msg = "{}\n\n\t{}".format(get_message("Recording in progress!"), get_message("Are you sure?")) + if show_dialog(DialogType.QUESTION, self._main_window, msg) == Gtk.ResponseType.CANCEL: + return True + self._recorder.release() + + GLib.idle_add(self.quit) def on_resize(self, window): """ Stores new size properties for app window after resize """ @@ -925,7 +943,7 @@ class Application(Gtk.Application): """ Shows satellites editor dialog """ show_satellites_dialog(self._main_window, self._settings) - def on_download(self, action, value): + def on_download(self, action=None, value=None): DownloadDialog(transient=self._main_window, settings=self._settings, open_data_callback=self.open_data, @@ -1843,6 +1861,41 @@ 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()) + # ************************* Record ***************************** # + + def on_record(self, button): + if show_dialog(DialogType.QUESTION, self._main_window) == Gtk.ResponseType.CANCEL: + return True + + if not self._recorder: + try: + self._recorder = Recorder.get_instance() + except (ImportError, NameError, AttributeError): + self.show_error_dialog("No VLC is found. Check that it is installed!") + return + + is_record = self._recorder.is_record() + + if is_record: + self._recorder.stop() + else: + self._http_api.send(HttpRequestType.STREAM_CURRENT, None, self.record) + + def record(self, m3u): + if m3u: + url = [s for s in m3u.split("\n") if not s.startswith("#")] + if url: + self._recorder.record(url[0], self._settings.records_path, self._service_name_label.get_text()) + GLib.timeout_add_seconds(1, self.update_record_button, priority=GLib.PRIORITY_LOW) + + def update_record_button(self): + is_rec = self._recorder.is_record() + if not is_rec: + self._record_image.set_visible(True) + else: + self._record_image.set_visible(not self._record_image.get_visible()) + return is_rec + # ************************ HTTP API **************************** # def init_http_api(self): diff --git a/app/ui/main_window.glade b/app/ui/main_window.glade index 18e55c88..0651ddee 100644 --- a/app/ui/main_window.glade +++ b/app/ui/main_window.glade @@ -1343,6 +1343,7 @@ Author: Dmitriy Yefremov + 320 250 True True @@ -2525,7 +2526,28 @@ Author: Dmitriy Yefremov False True - 0 + 1 + + + + + status-bar-button + False + True + Record + + + + True + False + gtk-media-record + + + + + False + True + 2 @@ -2551,7 +2573,7 @@ Author: Dmitriy Yefremov False True - 1 + 3 @@ -2570,7 +2592,7 @@ Author: Dmitriy Yefremov True True - 2 + 4 @@ -2584,7 +2606,7 @@ Author: Dmitriy Yefremov False False - 4 + 5