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
- 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"