From 3fe78e0292ded71a11751216c06bda94b29ee56d Mon Sep 17 00:00:00 2001 From: DYefremov Date: Wed, 1 Sep 2021 00:05:23 +0300 Subject: [PATCH] added epg page --- app/ui/app_menu.ui | 4 + app/ui/control.glade | 380 +++++++++++++--------- app/ui/control.py | 723 +++-------------------------------------- app/ui/main.glade | 62 +++- app/ui/main.py | 27 +- app/ui/timer_row.glade | 141 -------- app/ui/uicommons.py | 3 + 7 files changed, 368 insertions(+), 972 deletions(-) delete mode 100644 app/ui/timer_row.glade diff --git a/app/ui/app_menu.ui b/app/ui/app_menu.ui index a2f3d536..7ef2da71 100644 --- a/app/ui/app_menu.ui +++ b/app/ui/app_menu.ui @@ -97,6 +97,10 @@ Picons app.show_picons + + EPG + app.show_epg + Timers app.show_timers diff --git a/app/ui/control.glade b/app/ui/control.glade index 4177eb90..14ed206c 100644 --- a/app/ui/control.glade +++ b/app/ui/control.glade @@ -1,5 +1,5 @@ - - - True - False - alarm-symbolic - - - True - False - vertical - 5 - - - True - True - in - - - True - False - - - True - False - multiple - False - - - - - - - - True - True - 0 - - - - - True - False - 5 - - - True - True - Filter - gtk-spell-check - tools-check-spelling - False - False - Filter - - - - True - True - 0 - - - - - True - True - True - Add timer - app.on_timer_add_from_event - add_timer_image - True - - - False - True - 1 - - - - - False - False - 1 - - - - - True - False - gtk-go-back - 23 1 1 - - True - False - gtk-go-down - 23 1 10 - + + + + + + + + + + + + + True False - gtk-go-forward + 2 + 0.49000000953674316 + in + + + True + False + 5 + 5 + 7 + 5 + vertical + + + True + False + 15 + 15 + 5 + 5 + 5 + + + True + True + True + Add timer + app.on_timer_add_from_event + True + + + True + False + alarm-symbolic + + + + + False + True + 0 + + + + + + + + False + False + 0 + + + + + True + True + in + + + True + True + True + epg_model + both + 2 + + + + + + 25 + Title + 0.49000000953674316 + 0 + + + 5 + + + 0 + + + + + + + 25 + Time + 0.49000000953674316 + 1 + + + 10 + + + 1 + + + + + + + Description + True + 0.49000000953674316 + + + end + + + 2 + + + + + + + + + True + True + 1 + + + + + + + True + False + EPG + + 59 @@ -1007,18 +1058,6 @@ Author: Dmitriy Yefremov - - True - False - 16 - view-refresh - 1 - - - True - False - user-trash - True False @@ -1091,8 +1130,14 @@ Author: Dmitriy Yefremov True Remove app.on_recording_remove - remove_recording_image True + + + True + False + user-trash + + False @@ -1109,26 +1154,6 @@ Author: Dmitriy Yefremov - - True - False - 16 - window-new - 1 - - - True - False - 16 - application-exit - - - True - False - 16 - system-log-out - 1 - True False @@ -1216,23 +1241,11 @@ Author: Dmitriy Yefremov - - True - False - gtk-go-up - 100 1 10 - - True - False - 16 - document-revert - 1 - True False @@ -1256,7 +1269,15 @@ Author: Dmitriy Yefremov True Standby app.on_standby - standby_image + + + True + False + 16 + system-log-out + 1 + + False @@ -1272,7 +1293,15 @@ Author: Dmitriy Yefremov True Wake Up app.on_wake_up - wake_up_image + + + True + False + 16 + document-revert + 1 + + False @@ -1281,14 +1310,22 @@ Author: Dmitriy Yefremov - + 70 True True True Reboot app.on_reboot - reboot_image + + + True + False + 16 + view-refresh + 1 + + False @@ -1304,7 +1341,15 @@ Author: Dmitriy Yefremov True Restart GUI app.on_restart_gui - restart_gui_image + + + True + False + 16 + window-new + 1 + + False @@ -1320,7 +1365,14 @@ Author: Dmitriy Yefremov True Shutdown app.on_shutdown - shutdown_image + + + True + False + 16 + application-exit + + False @@ -1554,7 +1606,13 @@ Author: Dmitriy Yefremov True True app.on_up - up_image + + + True + False + gtk-go-up + + 1 @@ -1567,7 +1625,13 @@ Author: Dmitriy Yefremov True True app.on_down - down_image + + + True + False + gtk-go-down + + 1 @@ -1580,8 +1644,14 @@ Author: Dmitriy Yefremov True True app.on_right - forward_image True + + + True + False + gtk-go-forward + + 2 @@ -1594,7 +1664,13 @@ Author: Dmitriy Yefremov True True app.on_left - back_image + + + True + False + gtk-go-back + + 0 diff --git a/app/ui/control.py b/app/ui/control.py index 3954540a..4f71240e 100644 --- a/app/ui/control.py +++ b/app/ui/control.py @@ -29,208 +29,73 @@ """ Receiver control module via HTTP API. """ import os from datetime import datetime -from enum import Enum from urllib.parse import quote from gi.repository import GLib -from .dialogs import show_dialog, DialogType, get_message, get_builder -from .uicommons import Gtk, Gdk, UI_RESOURCES_PATH, Column +from .dialogs import get_builder +from .uicommons import Gtk, Gdk, UI_RESOURCES_PATH from ..commons import run_task, run_with_delay, log, run_idle -from ..connections import HttpAPI, UtfFTP -from ..eparser.ecommons import BqServiceType +from ..connections import HttpAPI + + +class EpgBox(Gtk.Box): + def __init__(self, app, http_api, *args, **kwargs): + super().__init__(*args, **kwargs) + + self._http_api = http_api + self._app = app + self._app.connect("fav-changed", self.on_service_changed) + + handlers = {"on_epg_press": self.on_epg_press} + + builder = get_builder(UI_RESOURCES_PATH + "control.glade", handlers, objects=("epg_frame", "epg_model")) + self._view = builder.get_object("epg_view") + self.add(builder.get_object("epg_frame")) + self.show() + + def on_epg_press(self, list_box, event): + if event.get_event_type() == Gdk.EventType.DOUBLE_BUTTON_PRESS and len(list_box) > 0: + row = list_box.get_selected_row() + if row: + self.set_timer_from_event_data(row.event_data) + + def on_service_changed(self, app, ref): + self._app._wait_dialog.show() + self._http_api.send(HttpAPI.Request.EPG, quote(ref), self.update_epg_data) + + @run_idle + def update_epg_data(self, epg): + model = self._view.get_model() + model.clear() + list(map(model.append, (self.get_event_row(e) for e in epg.get("event_list", [])))) + self._app._wait_dialog.hide() + + def get_event_row(self, event): + title = event.get("e2eventtitle", "") + desc = event.get("e2eventdescription", "") + + start = int(event.get("e2eventstart", "0")) + start_time = datetime.fromtimestamp(start) + end_time = datetime.fromtimestamp(start + int(event.get("e2eventduration", "0"))) + time = "{} - {}".format(start_time.strftime("%A, %H:%M"), end_time.strftime("%H:%M")) + + return title, time, desc, event class ControlBox(Gtk.HBox): - _TIME_STR = "%Y-%m-%d %H:%M" - - class Tool(Enum): - """ The currently displayed tool. """ - REMOTE = "control" - EPG = "epg" - TIMERS = "timers" - TIMER = "timer" - RECORDINGS = "recordings" - - class EpgRow(Gtk.ListBoxRow): - def __init__(self, event: dict, **properties): - super().__init__(**properties) - - self._event_data = event - h_box = Gtk.HBox() - h_box.set_orientation(Gtk.Orientation.VERTICAL) - - self._title = event.get("e2eventtitle", "") - title_label = Gtk.Label(self._title) - - self._desc = event.get("e2eventdescription", "") - description = Gtk.Label() - description.set_markup("{}".format(self._desc)) - description.set_line_wrap(True) - description.set_max_width_chars(25) - - start = int(event.get("e2eventstart", "0")) - start_time = datetime.fromtimestamp(start) - end_time = datetime.fromtimestamp(start + int(event.get("e2eventduration", "0"))) - time_label = Gtk.Label() - time_label.set_margin_top(5) - self._time_header = "{} - {}".format(start_time.strftime("%A, %H:%M"), end_time.strftime("%H:%M")) - time_label.set_markup("{}".format(self._time_header)) - - h_box.add(time_label) - h_box.add(title_label) - h_box.add(description) - sep = Gtk.Separator() - sep.set_margin_top(5) - h_box.add(sep) - h_box.set_spacing(5) - - self.add(h_box) - self.show_all() - - @property - def event_data(self): - return self._event_data or {} - - @property - def title(self): - return self._title or "" - - @property - def desc(self): - return self._desc or "" - - @property - def time_header(self): - return self._time_header or "" - - class TimerRow(Gtk.ListBoxRow): - - _UI_PATH = UI_RESOURCES_PATH + "timer_row.glade" - - def __init__(self, timer, **properties): - super().__init__(**properties) - - self._timer = timer - - builder = get_builder(self._UI_PATH, None, use_str=True) - row_box = builder.get_object("timer_row_box") - name_label = builder.get_object("timer_name_label") - description_label = builder.get_object("timer_description_label") - service_name_label = builder.get_object("timer_service_name_label") - time_label = builder.get_object("timer_time_label") - - name_label.set_text(timer.get("e2name", "") or "") - description_label.set_text(timer.get("e2description", "") or "") - service_name_label.set_text(timer.get("e2servicename", "") or "") - # Time - start_time = datetime.fromtimestamp(int(timer.get("e2timebegin", "0"))) - end_time = datetime.fromtimestamp(int(timer.get("e2timeend", "0"))) - time_label.set_text("{} - {}".format(start_time.strftime("%A, %H:%M"), end_time.strftime("%H:%M"))) - - self.add(row_box) - self.show() - - @property - def timer(self): - return self._timer - - class TimerAction(Enum): - ADD = 0 - EVENT = 1 - CHANGE = 2 - - class RecordingsRow(Gtk.ListBoxRow): - def __init__(self, movie: dict, **properties): - super().__init__(**properties) - - self._movie = movie - h_box = Gtk.HBox() - h_box.set_orientation(Gtk.Orientation.VERTICAL) - - self._service = movie.get("e2servicename") - service_label = Gtk.Label() - service_label.set_markup("{}".format(self._service)) - - self._title = movie.get("e2title", "") - title_label = Gtk.Label(self._title) - - self._desc = movie.get("e2description", "") - description = Gtk.Label() - description.set_markup("{}".format(self._desc)) - description.set_line_wrap(True) - description.set_max_width_chars(25) - - start_time = datetime.fromtimestamp(int(movie.get("e2time", "0"))) - start_time_label = Gtk.Label() - start_time_label.set_margin_top(5) - start_time_label.set_markup("{}".format(start_time.strftime("%A, %H:%M"))) - - time_label = Gtk.Label() - time_label.set_margin_top(5) - time_label.set_markup("{}".format(movie.get("e2length", "0"))) - - info_box = Gtk.HBox() - info_box.set_orientation(Gtk.Orientation.HORIZONTAL) - info_box.set_spacing(10) - info_box.pack_start(start_time_label, False, True, 5) - info_box.pack_end(time_label, False, True, 5) - - h_box.add(service_label) - h_box.add(title_label) - h_box.add(description) - h_box.add(info_box) - sep = Gtk.Separator() - sep.set_margin_top(5) - h_box.add(sep) - h_box.set_spacing(5) - - self.set_tooltip_text(movie.get("e2filename", "")) - self.add(h_box) - self.show_all() - - @property - def movie(self): - return self._movie - - @property - def service(self): - return self._service or "" - - @property - def title(self): - return self._title or "" - - @property - def desc(self): - return self._desc or "" - - @property - def file(self): - return self._movie.get("e2filename", "") def __init__(self, app, http_api, settings, *args, **kwargs): super().__init__(*args, **kwargs) self._http_api = http_api self._settings = settings - self._update_epg = False self._app = app - self._last_tool = self.Tool.REMOTE - self._timer_action = self.TimerAction.ADD - self._current_timer = {} - handlers = {"on_visible_tool": self.on_visible_tool, - "on_volume_changed": self.on_volume_changed, - "on_epg_press": self.on_epg_press, - "on_epg_filter_changed": self.on_epg_filter_changed, - "on_timers_press": self.on_timers_press, - "on_timers_drag_data_received": self.on_timers_drag_data_received, - "on_recordings_press": self.on_recordings_press, - "on_recording_filter_changed": self.on_recording_filter_changed, - "on_recordings_dir_changed": self.on_recordings_dir_changed} + handlers = {"on_volume_changed": self.on_volume_changed} - builder = get_builder(UI_RESOURCES_PATH + "control.glade", handlers) + builder = get_builder(UI_RESOURCES_PATH + "control.glade", handlers, + objects=("control_box", "volume_adjustment")) self.add(builder.get_object("control_box")) self._stack = builder.get_object("stack") @@ -242,55 +107,7 @@ class ControlBox(Gtk.HBox): self._ber_value_label = builder.get_object("ber_value_label") self._agc_value_label = builder.get_object("agc_value_label") self._volume_button = builder.get_object("volume_button") - self._epg_list_box = builder.get_object("epg_list_box") - self._epg_list_box.set_filter_func(self.epg_filter_function) - self._epg_filter_entry = builder.get_object("epg_filter_entry") - self._timers_list_box = builder.get_object("timers_list_box") - # Timers - self._timer_remove_button = builder.get_object("timer_remove_button") - self._timer_remove_button.bind_property("visible", builder.get_object("timer_edit_button"), "visible") - # Timer - self._timer_name_entry = builder.get_object("timer_name_entry") - self._timer_desc_entry = builder.get_object("timer_desc_entry") - self._timer_service_entry = builder.get_object("timer_service_entry") - self._timer_service_ref_entry = builder.get_object("timer_service_ref_entry") - self._timer_event_id_entry = builder.get_object("timer_event_id_entry") - self._timer_begins_entry = builder.get_object("timer_begins_entry") - self._timer_ends_entry = builder.get_object("timer_ends_entry") - self._timer_begins_calendar = builder.get_object("timer_begins_calendar") - self._timer_begins_hr_button = builder.get_object("timer_begins_hr_button") - self._timer_begins_min_button = builder.get_object("timer_begins_min_button") - self._timer_ends_calendar = builder.get_object("timer_ends_calendar") - self._timer_ends_hr_button = builder.get_object("timer_ends_hr_button") - self._timer_ends_min_button = builder.get_object("timer_ends_min_button") - self._timer_enabled_switch = builder.get_object("timer_enabled_switch") - self._timer_action_combo_box = builder.get_object("timer_action_combo_box") - self._timer_after_combo_box = builder.get_object("timer_after_combo_box") - self._timer_mo_check_button = builder.get_object("timer_mo_check_button") - self._timer_tu_check_button = builder.get_object("timer_tu_check_button") - self._timer_we_check_button = builder.get_object("timer_we_check_button") - self._timer_th_check_button = builder.get_object("timer_th_check_button") - self._timer_fr_check_button = builder.get_object("timer_fr_check_button") - self._timer_sa_check_button = builder.get_object("timer_sa_check_button") - self._timer_su_check_button = builder.get_object("timer_su_check_button") - self._timer_location_switch = builder.get_object("timer_location_switch") - self._timer_location_entry = builder.get_object("timer_location_entry") - self._timer_location_switch.bind_property("active", self._timer_location_entry, "sensitive") - # Disable DnD for timer entries. - self._timer_name_entry.drag_dest_unset() - self._timer_desc_entry.drag_dest_unset() - self._timer_service_entry.drag_dest_unset() - # DnD initialization for the timer list. - self._timers_list_box.drag_dest_set(Gtk.DestDefaults.ALL, [], Gdk.DragAction.DEFAULT | Gdk.DragAction.COPY) - self._timers_list_box.drag_dest_add_text_targets() - # Recordings. - self._recordings_list_box = builder.get_object("recordings_list_box") - self._recordings_list_box.set_filter_func(self.recording_filter_function) - self._recordings_filter_entry = builder.get_object("recordings_filter_entry") - self._recordings_dir_box = builder.get_object("recordings_dir_box") - self.init_actions(app) - self.connect("hide", self.on_hide) self.show() def init_actions(self, app): @@ -316,37 +133,6 @@ class ControlBox(Gtk.HBox): app.set_action("on_screenshot_all", self.on_screenshot_all) app.set_action("on_screenshot_video", self.on_screenshot_video) app.set_action("on_screenshot_osd", self.on_screenshot_osd) - # Timers - app.set_action("on_timer_add", self.on_timer_add) - app.set_action("on_timer_add_from_event", self.on_timer_add_from_event) - app.set_action("on_timer_remove", self.on_timer_remove) - app.set_action("on_timer_edit", self.on_timer_edit) - app.set_action("on_timer_save", self.on_timer_save) - app.set_action("on_timer_cancel", self.on_timer_cancel) - app.set_action("on_timer_begins_set", self.on_timer_begins_set) - app.set_action("on_timer_ends_set", self.on_timer_ends_set) - # Recordings - app.set_action("on_recording_remove", self.on_recording_remove) - - @property - def update_epg(self): - return self._update_epg - - def on_visible_tool(self, stack, param): - tool = self.Tool(stack.get_visible_child_name()) - self._update_epg = tool is self.Tool.EPG - - if tool is self.Tool.TIMERS: - self.update_timer_list() - - if tool is self.Tool.RECORDINGS: - self.update_recordings_list() - - if tool is not self.Tool.TIMER: - self._last_tool = tool - - def on_hide(self, item): - self._update_epg = False # ***************** Remote controller ********************* # @@ -443,412 +229,3 @@ class ControlBox(Gtk.HBox): self._snr_value_label.set_text(sig.get("e2snrdb", "0 dB").strip()) self._ber_value_label.set_text(str(sig.get("e2ber", None) or "0").strip()) self._agc_value_label.set_text(sig.get("e2acg", "0 %").strip()) - - # ************************ EPG **************************** # - - def on_service_changed(self, ref): - self._app._wait_dialog.show() - self._http_api.send(HttpAPI.Request.EPG, quote(ref), self.update_epg_data) - - @run_idle - def update_epg_data(self, epg): - list(map(self._epg_list_box.remove, (r for r in self._epg_list_box))) - list(map(lambda e: self._epg_list_box.add(self.EpgRow(e)), epg.get("event_list", []))) - self._app._wait_dialog.hide() - - def on_epg_press(self, list_box, event): - if event.get_event_type() == Gdk.EventType.DOUBLE_BUTTON_PRESS and len(list_box) > 0: - row = list_box.get_selected_row() - if row: - self.set_timer_from_event_data(row.event_data) - - def on_epg_filter_changed(self, entry): - self._epg_list_box.invalidate_filter() - - def epg_filter_function(self, row): - txt = self._epg_filter_entry.get_text().upper() - return any((not txt, txt in row.time_header.upper(), txt in row.title.upper(), txt in row.desc.upper())) - - def on_timer_add_from_event(self, action, value=None): - rows = self._epg_list_box.get_selected_rows() - if not rows: - self._app.show_error_message("No selected item!") - return - - refs = [] - for row in rows: - event = row.event_data - ref = "timeraddbyeventid?sRef={}&eventid={}&justplay=0".format(event.get("e2eventservicereference", ""), - event.get("e2eventid", "")) - refs.append(ref) - - gen = self.write_timers_list(refs) - GLib.idle_add(lambda: next(gen, False)) - - def write_timers_list(self, refs): - self._app._wait_dialog.show() - tasks = list(refs) - for ref in refs: - self._http_api.send(HttpAPI.Request.TIMER, ref, lambda x: tasks.pop()) - yield True - - while tasks: - yield True - - self._stack.set_visible_child_name(self.Tool.TIMERS.value) - - # *********************** Timers *************************** # - - def on_timers_press(self, list_box, event): - if event.get_event_type() == Gdk.EventType.DOUBLE_BUTTON_PRESS and len(list_box) > 0: - self.on_timer_edit() - - def update_timer_list(self): - self._app._wait_dialog.show() - self._http_api.send(HttpAPI.Request.TIMER_LIST, "", self.update_timers_data) - - @run_idle - def update_timers_data(self, timers): - list(map(self._timers_list_box.remove, (r for r in self._timers_list_box))) - list(map(lambda t: self._timers_list_box.add(self.TimerRow(t)), timers.get("timer_list", []))) - self._timer_remove_button.set_visible(len(self._timers_list_box)) - self._app._wait_dialog.hide() - - def on_timer_add(self, action=None, value=None): - self._timer_action = self.TimerAction.ADD - date = datetime.now() - self.set_begins_date(date) - self.set_ends_date(date) - self._timer_event_id_entry.set_text("") - self._timer_location_switch.set_active(False) - self.set_repetition_flags(0) - self._stack.set_visible_child_name(self.Tool.TIMER.value) - - def on_timer_remove(self, action, value=None): - rows = self._timers_list_box.get_selected_rows() - if not rows or show_dialog(DialogType.QUESTION, self._app._main_window) != Gtk.ResponseType.OK: - return - - refs = {} - for row in rows: - timer = row.timer - ref = "timerdelete?sRef={}&begin={}&end={}".format(quote(timer.get("e2servicereference", "")), - timer.get("e2timebegin", ""), - timer.get("e2timeend", "")) - refs[ref] = row - - self._app._wait_dialog.show("Deleting data...") - gen = self.remove_timers(refs) - GLib.idle_add(lambda: next(gen, False)) - - def remove_timers(self, refs): - tasks = list(refs) - removed = set() - for ref in refs: - yield from self.remove_timer(ref, removed, tasks) - - while tasks: - yield True - - list(map(self._timers_list_box.remove, (refs[ref] for ref in refs if ref in removed))) - self._app._wait_dialog.hide() - self._timer_remove_button.set_visible(len(self._timers_list_box)) - yield True - - def remove_timer(self, ref, removed, tasks=None): - def callback(resp): - if resp.get("e2state", "") == "True": - log(resp.get("e2statetext", "")) - removed.add(ref) - else: - log(resp.get("e2statetext", None) or "Timer deletion error.") - if tasks: - tasks.pop() - - self._http_api.send(HttpAPI.Request.TIMER, ref, callback) - yield True - - def on_timer_edit(self, action=None, value=None): - row = self._timers_list_box.get_selected_row() - if row: - self._timer_action = self.TimerAction.CHANGE - - timer = row.timer - self._current_timer = timer - self._timer_name_entry.set_text(timer.get("e2name", "")) - self._timer_desc_entry.set_text(timer.get("e2description", "") or "") - self._timer_service_entry.set_text(timer.get("e2servicename", "") or "") - self._timer_service_ref_entry.set_text(timer.get("e2servicereference", "")) - self._timer_event_id_entry.set_text(timer.get("e2eit", "")) - self._timer_enabled_switch.set_active((timer.get("e2disabled", "0") == "0")) - self._timer_action_combo_box.set_active_id(timer.get("e2justplay", "0")) - self._timer_after_combo_box.set_active_id(timer.get("e2afterevent", "0")) - self.set_time_data(int(timer.get("e2timebegin", "0")), int(timer.get("e2timeend", "0"))) - location = timer.get("e2location", "") - self._timer_location_entry.set_text("" if location == "None" else location) - # Days - self.set_repetition_flags(int(timer.get("e2repeated", "0"))) - self._stack.set_visible_child_name(self.Tool.TIMER.value) - - def on_timer_save(self, action, value=None): - args = [] - t_data = self.get_timer_data() - s_ref = quote(t_data.get("sRef", "")) - - if self._timer_action is self.TimerAction.EVENT: - args.append("timeraddbyeventid?sRef={}".format(s_ref)) - args.append("eventid={}".format(t_data.get("eit", "0"))) - args.append("justplay={}".format(t_data.get("justplay", ""))) - args.append("tags={}".format("")) - else: - if self._timer_action is self.TimerAction.ADD: - args.append("timeradd?sRef={}".format(s_ref)) - args.append("deleteOldOnSave={}".format(0)) - elif self._timer_action is self.TimerAction.CHANGE: - args.append("timerchange?sRef={}".format(s_ref)) - args.append("channelOld={}".format(s_ref)) - args.append("beginOld={}".format(self._current_timer.get("e2timebegin", "0"))) - args.append("endOld={}".format(self._current_timer.get("e2timeend", "0"))) - args.append("deleteOldOnSave={}".format(1)) - - args.append("begin={}".format(t_data.get("begin", ""))) - args.append("end={}".format(t_data.get("end", ""))) - args.append("name={}".format(quote(t_data.get("name", "")))) - args.append("description={}".format(quote(t_data.get("description", "")))) - args.append("tags={}".format("")) - args.append("eit={}".format("0")) - args.append("disabled={}".format(t_data.get("disabled", "1"))) - args.append("justplay={}".format(t_data.get("justplay", "1"))) - args.append("afterevent={}".format(t_data.get("afterevent", "0"))) - args.append("repeated={}".format(self.get_repetition_flags())) - - if self._timer_location_switch.get_active(): - args.append("dirname={}".format(self._timer_location_entry.get_text())) - - self._http_api.send(HttpAPI.Request.TIMER, "&".join(args), self.timer_add_edit_callback) - - @run_idle - def timer_add_edit_callback(self, resp): - if "error_code" in resp: - msg = "Error getting timer status.\n{}".format(resp.get("error_code")) - self._app.show_error_message(msg) - log(msg) - return - - state = resp.get("e2state", None) - if state == "False": - msg = resp.get("e2statetext", "") - self._app.show_error_message(msg) - log(msg) - if state == "True": - log(resp.get("e2statetext", "")) - self._stack.set_visible_child_name(self._last_tool.value) - else: - log("Error getting timer status. No response!") - - def on_timer_cancel(self, action, value=None): - self._stack.set_visible_child_name(self._last_tool.value) - - def on_timer_begins_set(self, action, value=None): - self.set_begins_date(self.get_begins_date()) - - def on_timer_ends_set(self, action, value=None): - self.set_ends_date(self.get_ends_date()) - - def get_begins_date(self): - date = self._timer_begins_calendar.get_date() - return datetime(year=date.year, month=date.month + 1, day=date.day, - hour=int(self._timer_begins_hr_button.get_value()), - minute=int(self._timer_begins_min_button.get_value())) - - def set_begins_date(self, date): - hour = date.hour - minute = date.minute - self._timer_begins_hr_button.set_value(hour) - self._timer_begins_min_button.set_value(minute) - self._timer_begins_calendar.select_day(date.day) - self._timer_begins_calendar.select_month(date.month - 1, date.year) - self._timer_begins_entry.set_text("{}-{}-{} {}:{:02d}".format(date.year, date.month, date.day, hour, minute)) - - def get_ends_date(self): - date = self._timer_ends_calendar.get_date() - return datetime(year=date.year, month=date.month + 1, day=date.day, - hour=int(self._timer_ends_hr_button.get_value()), - minute=int(self._timer_ends_min_button.get_value())) - - def set_ends_date(self, date): - hour = date.hour - minute = date.minute - self._timer_ends_hr_button.set_value(hour) - self._timer_ends_min_button.set_value(minute) - self._timer_ends_calendar.select_day(date.day) - self._timer_ends_calendar.select_month(date.month - 1, date.year) - self._timer_ends_entry.set_text("{}-{}-{} {}:{:02d}".format(date.year, date.month, date.day, hour, minute)) - - def set_timer_from_event_data(self, timer): - self._stack.set_visible_child_name(self.Tool.TIMER.value) - self._timer_action = self.TimerAction.EVENT - self._timer_name_entry.set_text(timer.get("e2eventtitle", "")) - self._timer_desc_entry.set_text(timer.get("e2eventdescription", "")) - self._timer_service_entry.set_text(timer.get("e2eventservicename", "")) - self._timer_service_ref_entry.set_text(timer.get("e2eventservicereference", "")) - self._timer_event_id_entry.set_text(timer.get("e2eventid", "")) - self._timer_action_combo_box.set_active_id("1") - self._timer_after_combo_box.set_active_id("3") - start_time = int(timer.get("e2eventstart", "0")) - self.set_time_data(start_time, start_time + int(timer.get("e2eventduration", "0"))) - - def set_time_data(self, start_time, end_time): - """ Sets values for time widgets. """ - ev_time_start = datetime.fromtimestamp(start_time) or datetime.now() - ev_time_end = datetime.fromtimestamp(end_time) or datetime.now() - self._timer_begins_entry.set_text(ev_time_start.strftime(self._TIME_STR)) - self._timer_ends_entry.set_text(ev_time_end.strftime(self._TIME_STR)) - self._timer_begins_calendar.select_day(ev_time_start.day) - self._timer_begins_calendar.select_month(ev_time_start.month - 1, ev_time_start.year) - self._timer_ends_calendar.select_day(ev_time_end.day) - self._timer_ends_calendar.select_month(ev_time_end.month - 1, ev_time_end.year) - self._timer_begins_hr_button.set_value(ev_time_start.hour) - self._timer_begins_min_button.set_value(ev_time_start.minute) - self._timer_ends_hr_button.set_value(ev_time_end.hour) - self._timer_ends_min_button.set_value(ev_time_end.minute) - - def get_timer_data(self): - """ Returns timer data as a dict. """ - return {"sRef": self._timer_service_ref_entry.get_text(), - "begin": int(datetime.strptime(self._timer_begins_entry.get_text(), self._TIME_STR).timestamp()), - "end": int(datetime.strptime(self._timer_ends_entry.get_text(), self._TIME_STR).timestamp()), - "name": self._timer_name_entry.get_text(), - "description": self._timer_desc_entry.get_text(), - "dirname": "", - "eit": self._timer_event_id_entry.get_text(), - "disabled": int(not self._timer_enabled_switch.get_active()), - "justplay": self._timer_action_combo_box.get_active_id(), - "afterevent": self._timer_after_combo_box.get_active_id(), - "repeated": self.get_repetition_flags()} - - def get_repetition_flags(self): - """ Returns flags for repetition. """ - day_flags = 0 - for i, box in enumerate((self._timer_mo_check_button, - self._timer_tu_check_button, - self._timer_we_check_button, - self._timer_th_check_button, - self._timer_fr_check_button, - self._timer_sa_check_button, - self._timer_su_check_button)): - - if box.get_active(): - day_flags = day_flags | (1 << i) - - return day_flags - - def set_repetition_flags(self, flags): - for i, box in enumerate((self._timer_mo_check_button, - self._timer_tu_check_button, - self._timer_we_check_button, - self._timer_th_check_button, - self._timer_fr_check_button, - self._timer_sa_check_button, - self._timer_su_check_button)): - box.set_active(flags & 1 == 1) - flags = flags >> 1 - - # ***************** Drag-and-drop ********************* # - - def on_timers_drag_data_received(self, box, context, x, y, data, info, time): - txt = data.get_text() - if txt: - itr_str, sep, source = txt.partition(self._app.DRAG_SEP) - if not source: - return - - itrs = itr_str.split(",") - if len(itrs) > 1: - self._app.show_error_message("Please, select only one item!") - return - - fav_id = None - if source == self._app.FAV_MODEL_NAME: - model = self._app.fav_view.get_model() - fav_id = model.get_value(model.get_iter_from_string(itrs[0]), Column.FAV_ID) - elif source == self._app.SERVICE_MODEL_NAME: - model = self._app.services_view.get_model() - fav_id = model.get_value(model.get_iter_from_string(itrs[0]), Column.SRV_FAV_ID) - - service = self._app.current_services.get(fav_id, None) - if service: - if service.service_type == BqServiceType.ALT.name: - msg = "Alternative service.\n\n {}".format(get_message("Not implemented yet!")) - show_dialog(DialogType.ERROR, transient=self._app._main_window, text=msg) - context.finish(False, False, time) - return - - self._timer_name_entry.set_text(service.service) - self._timer_service_entry.set_text(service.service) - self._timer_service_ref_entry.set_text(service.picon_id.rstrip(".png").replace("_", ":")) - self.on_timer_add() - context.finish(True, False, time) - - # *********************** Recordings *************************** # - - def on_recordings_press(self, list_box, event): - if event.get_event_type() == Gdk.EventType.DOUBLE_BUTTON_PRESS and len(list_box) > 0: - row = list_box.get_selected_row() - if row: - self._http_api.send(HttpAPI.Request.STREAM_TS, - row.movie.get("e2filename", ""), - self.on_play_recording) - - def on_recording_filter_changed(self, entry): - self._recordings_list_box.invalidate_filter() - - def recording_filter_function(self, row): - txt = self._recordings_filter_entry.get_text().upper() - return any((not txt, txt in row.service.upper(), txt in row.title.upper(), txt in row.desc.upper())) - - def on_recording_remove(self, action, value=None): - """ Removes recordings via FTP. """ - if show_dialog(DialogType.QUESTION, self._app._main_window) != Gtk.ResponseType.OK: - return - - rows = self._recordings_list_box.get_selected_rows() - if rows: - settings = self._app._settings - - with UtfFTP(host=settings.host, user=settings.user, passwd=settings.password) as ftp: - ftp.encoding = "utf-8" - for r in rows: - resp = ftp.delete_file(r.file) - if resp.startswith("2"): - GLib.idle_add(self._recordings_list_box.remove, r) - else: - show_dialog(DialogType.ERROR, transient=self._app._main_window, text=resp) - break - - def on_recordings_dir_changed(self, box: Gtk.ComboBoxText): - self._http_api.send(HttpAPI.Request.RECORDINGS, quote(box.get_active_id()), self.update_recordings_data) - - def update_recordings_list(self): - if not len(self._recordings_dir_box.get_model()): - self._http_api.send(HttpAPI.Request.REC_CURRENT, "", self.update_current_rec_dir) - - def update_current_rec_dir(self, current): - cur = current.get("e2location", None) - if cur: - self._recordings_dir_box.append(cur, cur) - self._http_api.send(HttpAPI.Request.REC_DIRS, "", self.update_rec_dirs) - - def update_rec_dirs(self, dirs): - for d in dirs.get("rec_dirs", []): - self._recordings_dir_box.append(d, d) - - @run_idle - def update_recordings_data(self, recordings): - list(map(self._recordings_list_box.remove, (r for r in self._recordings_list_box))) - list(map(lambda r: self._recordings_list_box.add(self.RecordingsRow(r)), recordings.get("recordings", []))) - - def on_play_recording(self, m3u): - url = self._app.get_url_from_m3u(m3u) - if url: - self._app.play(url) diff --git a/app/ui/main.glade b/app/ui/main.glade index d44c4f51..03cf0bab 100644 --- a/app/ui/main.glade +++ b/app/ui/main.glade @@ -2380,6 +2380,64 @@ Author: Dmitriy Yefremov 4 + + + True + False + 5 + 5 + 5 + 5 + vertical + + + + + + + epg + EPG + 5 + + + + + True + False + 5 + 5 + 5 + 5 + vertical + + + + + + timers + Timers + 6 + + + + + True + False + 5 + 5 + 5 + 5 + vertical + + + + + + recordings + Recordings + 7 + + True @@ -2397,7 +2455,7 @@ Author: Dmitriy Yefremov ftp FTP - 5 + 8 @@ -2416,7 +2474,7 @@ Author: Dmitriy Yefremov control Control - 6 + 9 diff --git a/app/ui/main.py b/app/ui/main.py index c1f1c928..d2fd52fb 100644 --- a/app/ui/main.py +++ b/app/ui/main.py @@ -48,7 +48,7 @@ from app.eparser.neutrino.bouquets import BqType from app.settings import (SettingsType, Settings, SettingsException, PlayStreamsMode, SettingsReadException, IS_DARWIN) from app.tools.media import Player, Recorder -from app.ui.control import ControlBox +from app.ui.control import ControlBox, EpgBox from app.ui.epg_dialog import EpgDialog from app.ui.ftp import FtpClientBox from app.ui.transmitter import LinksTransmitter @@ -199,6 +199,7 @@ class Application(Gtk.Application): "on_add_alternatives": self.on_add_alternatives, "on_satellites_realize": self.on_satellites_realize, "on_picons_realize": self.on_picons_realize, + "on_epg_realize": self.on_epg_realize, "on_control_realize": self.on_control_realize, "on_ftp_realize": self.on_ftp_realize, "on_visible_page": self.on_visible_page} @@ -232,6 +233,7 @@ class Application(Gtk.Application): self._links_transmitter = None self._satellite_tool = None self._picon_manager = None + self._epg_box = None self._control_box = None self._ftp_client = None # Player @@ -255,6 +257,8 @@ class Application(Gtk.Application): # Signals. GObject.signal_new("profile-changed", self, GObject.SIGNAL_RUN_LAST, GObject.TYPE_PYOBJECT, (GObject.TYPE_PYOBJECT,)) + GObject.signal_new("fav-changed", self, GObject.SIGNAL_RUN_LAST, + GObject.TYPE_PYOBJECT, (GObject.TYPE_PYOBJECT,)) builder = get_builder(UI_RESOURCES_PATH + "main.glade", handlers) self._main_window = builder.get_object("main_window") @@ -365,6 +369,12 @@ class Application(Gtk.Application): self._stack_satellite_box.set_visible(self._settings.get("show_satellites", True)) self._stack_picon_box = builder.get_object("picon_box") self._stack_picon_box.set_visible(self._settings.get("show_picons", True)) + self._stack_epg_box = builder.get_object("epg_box") + self._stack_epg_box.set_visible(self._settings.get("show_epg", True)) + self._stack_timers_box = builder.get_object("timers_box") + self._stack_timers_box.set_visible(self._settings.get("show_timers", True)) + self._stack_recordings_box = builder.get_object("recordings_box") + self._stack_timers_box.set_visible(self._settings.get("show_recordings", True)) self._stack_ftp_box = builder.get_object("ftp_box") self._stack_ftp_box.set_visible(self._settings.get("show_ftp", True)) self._stack_control_box = builder.get_object("control_box") @@ -468,8 +478,12 @@ class Application(Gtk.Application): sa.connect("change-state", lambda a, v: self._stack_satellite_box.set_visible(v)) sa = self.set_state_action("show_picons", self.on_page_show, self._settings.get("show_picons", True)) sa.connect("change-state", lambda a, v: self._stack_picon_box.set_visible(v)) + sa = self.set_state_action("show_epg", self.on_page_show, self._settings.get("show_epg", True)) + sa.connect("change-state", lambda a, v: self._stack_epg_box.set_visible(v)) sa = self.set_state_action("show_timers", self.on_page_show, self._settings.get("show_timers", True)) + sa.connect("change-state", lambda a, v: self._stack_timers_box.set_visible(v)) sa = self.set_state_action("show_recordings", self.on_page_show, self._settings.get("show_recordings", True)) + sa.connect("change-state", lambda a, v: self._stack_recordings_box.set_visible(v)) sa = self.set_state_action("show_ftp", self.on_page_show, self._settings.get("show_ftp", True)) sa.connect("change-state", lambda a, v: self._stack_ftp_box.set_visible(v)) sa = self.set_state_action("show_control", self.on_page_show, self._settings.get("show_control", True)) @@ -716,6 +730,10 @@ class Application(Gtk.Application): self._picon_manager = PiconManager(self, self._settings, ids, self._sat_positions) box.pack_start(self._picon_manager, True, True, 0) + def on_epg_realize(self, box): + self._epg_box = EpgBox(self, self._http_api) + box.pack_start(self._epg_box, True, True, 0) + def on_ftp_realize(self, box): self._ftp_client = FtpClientBox(self, self._settings) box.pack_start(self._ftp_client, True, True, 0) @@ -726,7 +744,7 @@ class Application(Gtk.Application): def on_visible_page(self, stack, param): self._page = Page(stack.get_visible_child_name()) - self._fav_paned.set_visible(self._page in (Page.SERVICES, Page.PICONS, Page.PLAYBACK)) + self._fav_paned.set_visible(self._page in (Page.SERVICES, Page.PICONS, Page.PLAYBACK, Page.EPG)) self._save_tool_button.set_visible(self._page in (Page.SERVICES, Page.SATELLITE)) def on_page_show(self, action, value): @@ -1952,11 +1970,12 @@ class Application(Gtk.Application): else: self._alt_revealer.set_visible(False) - if self._control_box and self._control_box.update_epg: + if self._page is Page.EPG: ref = self.get_service_ref(path) if not ref: return - self._control_box.on_service_changed(ref) + + self.emit("fav-changed", ref) def on_services_selection(self, model, path, column): self.update_service_bar(model, path) diff --git a/app/ui/timer_row.glade b/app/ui/timer_row.glade deleted file mode 100644 index aea8825c..00000000 --- a/app/ui/timer_row.glade +++ /dev/null @@ -1,141 +0,0 @@ - - - - - - True - False - 2 - 2 - vertical - 5 - - - True - False - 5 - - - True - False - end - - - - - - False - True - 0 - - - - - True - False - - - False - True - end - 1 - - - - - False - True - 0 - - - - - True - False - 5 - - - True - False - end - - - - - - False - True - 0 - - - - - True - False - - - False - True - end - 1 - - - - - False - True - 1 - - - - - True - False - 5 - - - True - False - end - - - False - True - 0 - - - - - True - False - - - - - - False - True - end - 1 - - - - - False - True - 2 - - - - - True - False - - - False - True - 3 - - - - diff --git a/app/ui/uicommons.py b/app/ui/uicommons.py index c88d038b..7b99e9b3 100644 --- a/app/ui/uicommons.py +++ b/app/ui/uicommons.py @@ -126,6 +126,9 @@ class Page(Enum): SATELLITE = "satellite" PICONS = "picons" PLAYBACK = "playback" + EPG = "epg" + TIMERS = "timers" + RECORDINGS = "recordings" FTP = "ftp" CONTROL = "control"