XMLTV display support for EPG tab

This commit is contained in:
DYefremov
2023-12-29 14:09:13 +03:00
parent bb847cd94c
commit 32043b7df0
3 changed files with 147 additions and 28 deletions

View File

@@ -36,6 +36,7 @@ import shutil
import urllib.request
from datetime import datetime
from enum import Enum
from itertools import chain
from urllib.error import HTTPError, URLError
from urllib.parse import quote
@@ -141,8 +142,9 @@ class FavEpgCache(EpgCache):
GLib.timeout_add_seconds(self._settings.epg_update_interval, self.update_epg_data, priority=GLib.PRIORITY_LOW)
def reset(self) -> None:
self._is_run = False
GLib.timeout_add_seconds(self._settings.epg_update_interval, self.init)
if self._is_run:
self._is_run = False
GLib.timeout_add_seconds(self._settings.epg_update_interval, self.init)
def update_epg_data(self):
if self._src is EpgSource.HTTP:
@@ -166,7 +168,7 @@ class FavEpgCache(EpgCache):
services = self._app.current_services
names = {services[s].service for s in self._app.current_bouquets.get(self._current_bq, [])}
for name, events in self._reader.get_current_events(names).items():
ev = max(events, key=lambda x: x.start, default=None)
ev = min(events, key=lambda x: x.start, default=None)
if ev:
self.events[name] = ev
@@ -177,6 +179,61 @@ class FavEpgCache(EpgCache):
return [EpgEvent()]
class TabEpgCache(EpgCache):
def __init__(self, app):
super().__init__(app)
self.init()
def init(self):
self._is_run = True
url = self._settings.epg_xml_source
gz_file = f"{self._settings.profile_data_path}epg{os.sep}epg.gz"
self._reader = XmlTvReader(gz_file, url)
if os.path.isfile(gz_file):
# Difference calculation between the current time and file modification.
dif = datetime.now() - datetime.fromtimestamp(os.path.getmtime(gz_file))
# We will update daily. -> Temporarily!!!
if dif.days > 0 and not self._canceled:
task = BGTaskWidget(self._app, "Downloading EPG...", self._reader.download, self.process_data, )
self._app.emit("add-background-task", task)
else:
self.process_data()
else:
if not self._canceled:
task = BGTaskWidget(self._app, "Downloading EPG...", self._reader.download, self.process_data, )
self._app.emit("add-background-task", task)
def process_data(self):
self._app.wait_dialog.show()
def process():
self._reader.parse()
self.update_epg_data()
self._app.wait_dialog.hide()
t = BGTaskWidget(self._app, "Processing XMLTV data...", process, )
self._app.emit("add-background-task", t)
def reset(self) -> None:
pass
def update_epg_data(self) -> bool:
services = self._app.current_services
names = {services[s].service for s in self._app.current_bouquets.get(self._current_bq, [])}
for name, events in self._reader.get_current_events(names).items():
self.events[name] = events
return self._is_run
def get_current_event(self, service_name) -> EpgEvent:
pass
def get_current_events(self, service_name) -> list[EpgEvent]:
return self.events.get(service_name, [])
class EpgSettingsPopover(Gtk.Popover):
def __init__(self, app, **kwarg):
@@ -288,7 +345,10 @@ class EpgTool(Gtk.Box):
def __init__(self, app, **kwargs):
super().__init__(**kwargs)
self._current_bq = None
self._epg_cache = None
self._src = EpgSource.HTTP
self._current_bq = app.current_bouquet
self._app = app
self._app.connect("fav-changed", self.on_service_changed)
self._app.connect("profile-changed", self.on_profile_changed)
@@ -300,7 +360,8 @@ class EpgTool(Gtk.Box):
"on_epg_filter_changed": self.on_epg_filter_changed,
"on_epg_filter_toggled": self.on_epg_filter_toggled,
"on_view_query_tooltip": self.on_view_query_tooltip,
"on_multi_epg_toggled": self.on_multi_epg_toggled}
"on_multi_epg_toggled": self.on_multi_epg_toggled,
"on_xmltv_toggled": self.on_xmltv_toggled}
builder = get_builder(f"{UI_RESOURCES_PATH}epg{SEP}tab.glade", handlers)
@@ -334,7 +395,7 @@ class EpgTool(Gtk.Box):
p_count = len(paths)
if p_count == 1:
dialog = TimerTool.TimerDialog(self._app.app_window, TimerTool.TimerAction.EVENT, model[paths][-1])
dialog = TimerTool.TimerDialog(self._app.app_window, TimerTool.TimerAction.EVENT, model[paths][-1] or {})
response = dialog.run()
if response == Gtk.ResponseType.OK:
gen = self.write_timers_list([dialog.get_request()])
@@ -373,7 +434,10 @@ class EpgTool(Gtk.Box):
self.on_timer_add()
def on_service_changed(self, app, srv):
if app.page is Page.EPG:
if app.page is not Page.EPG:
return
if self._src is EpgSource.HTTP:
ref = app.get_service_ref_data(srv)
if not ref:
return
@@ -384,21 +448,36 @@ class EpgTool(Gtk.Box):
scroll_to(path, self._view) if path else None
else:
self._app.wait_dialog.show()
self._app.send_http_request(HttpAPI.Request.EPG, quote(ref), self.update_epg_data)
self._app.send_http_request(HttpAPI.Request.EPG, quote(ref), self.update_http_epg_data)
else:
if self._multi_epg_button.get_active():
path = next((r.path for r in self._model if r[-1].get("e2eventservicename", None) == srv.service), None)
scroll_to(path, self._view) if path else None
else:
self.update_xmltv_epg_data([srv.service])
def on_profile_changed(self, app, prf):
self.update_epg_data()
self.update_http_epg_data()
@run_idle
def update_epg_data(self, epg=None):
self._event_count_label.set_text("0")
self._model.clear()
def update_http_epg_data(self, epg=None):
self.clear()
if epg:
list(map(self._model.append, (self.get_event(e) for e in epg.get("event_list", [])
if e.get("e2eventid", "").isdigit())))
self._event_count_label.set_text(str(len(self._model)))
self._app.wait_dialog.hide()
def update_xmltv_epg_data(self, names):
self.clear()
events = chain.from_iterable(self._epg_cache.get_current_events(n) for n in names)
[self._model.append(e) for e in events]
self._event_count_label.set_text(str(len(self._model)))
def clear(self):
self._model.clear()
self._event_count_label.set_text("0")
@staticmethod
def get_event(event, show_day=True):
s_name = event.get("e2eventservicename", "")
@@ -450,8 +529,13 @@ class EpgTool(Gtk.Box):
path, pos = dst
model = view.get_model()
data = model[path][-1]
if not data:
return False
desc = data.get("e2eventdescription", "") or ""
ext_desc = data.get("e2eventdescriptionextended", "") or ""
if not any((desc, ext_desc)):
return False
tooltip.set_text(ext_desc if ext_desc else desc)
view.set_tooltip_row(tooltip, path)
@@ -459,12 +543,19 @@ class EpgTool(Gtk.Box):
return True
def on_multi_epg_toggled(self, button):
self._model.clear()
self._event_count_label.set_text("0")
self.clear()
if button.get_active():
self.get_multi_epg()
def on_xmltv_toggled(self, button):
if button.get_active():
self._src = EpgSource.XML
if self._epg_cache is None:
self._epg_cache = TabEpgCache(self._app)
else:
self._src = EpgSource.HTTP
def on_bouquet_changed(self, app, bq):
self._current_bq = bq
if app.page is Page.EPG and self._multi_epg_button.get_active():
@@ -474,14 +565,18 @@ class EpgTool(Gtk.Box):
if not self._current_bq:
return
self._app.wait_dialog.show()
bq = self._app.current_bouquet_files.get(self._current_bq, None)
api = self._app.http_api
if self._src is EpgSource.HTTP:
self._app.wait_dialog.show()
bq = self._app.current_bouquet_files.get(self._current_bq, None)
api = self._app.http_api
if bq and api:
tm = datetime.now().timestamp()
req = quote(f'FROM BOUQUET "userbouquet.{bq}.{self._current_bq.split(":")[-1]}"&time={tm}')
api.send(HttpAPI.Request.EPG_MULTI, f'1:7:1:0:0:0:0:0:0:0:{req}', self.update_epg_data, timeout=15)
if bq and api:
tm = datetime.now().timestamp()
req = quote(f'FROM BOUQUET "userbouquet.{bq}.{self._current_bq.split(":")[-1]}"&time={tm}')
api.send(HttpAPI.Request.EPG_MULTI, f'1:7:1:0:0:0:0:0:0:0:{req}', self.update_http_epg_data, timeout=15)
else:
srvs = self._app.current_services
self.update_xmltv_epg_data(srvs[s].service for s in self._app.current_bouquets.get(self._current_bq, []))
class EpgDialog:

View File

@@ -110,11 +110,13 @@ Author: Dmitriy Yefremov
<child>
<object class="GtkRadioButton" id="src_xmltv_button">
<property name="label" translatable="yes">XML TV</property>
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="receives-default">False</property>
<property name="active">True</property>
<property name="draw-indicator">False</property>
<property name="group">src_receiver_button</property>
<signal name="toggled" handler="on_xmltv_toggled" swapped="no"/>
</object>
<packing>
<property name="expand">True</property>
@@ -196,6 +198,27 @@ Author: Dmitriy Yefremov
<property name="position">2</property>
</packing>
</child>
<child>
<object class="GtkMenuButton" id="epg_options_button">
<property name="visible" bind-source="src_xmltv_button" bind-property="active">False</property>
<property name="can-focus">True</property>
<property name="focus-on-click">False</property>
<property name="receives-default">True</property>
<child>
<object class="GtkImage" id="epg_options_button_image">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="icon-name">applications-system-symbolic</property>
</object>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="pack-type">end</property>
<property name="position">4</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
@@ -354,6 +377,7 @@ Author: Dmitriy Yefremov
<child>
<object class="GtkCellRendererText" id="epg_title_renderer">
<property name="xpad">5</property>
<property name="ellipsize">end</property>
</object>
<attributes>
<attribute name="text">1</attribute>

View File

@@ -236,15 +236,15 @@ class TimerTool(Gtk.Box):
TimerTool.set_repetition_flags(int(self._timer_data.get("e2repeated", "0")), self._days_buttons)
def set_timer_from_event_data(self):
self._timer_name_entry.set_text(self._timer_data.get("e2eventtitle", ""))
self._timer_desc_entry.set_text(self._timer_data.get("e2eventdescription", ""))
self._timer_service_entry.set_text(self._timer_data.get("e2eventservicename", ""))
self._timer_service_ref_entry.set_text(self._timer_data.get("e2eventservicereference", ""))
self._timer_event_id_entry.set_text(self._timer_data.get("e2eventid", ""))
self._timer_name_entry.set_text(self._timer_data.get("e2eventtitle", None) or "")
self._timer_desc_entry.set_text(self._timer_data.get("e2eventdescription", None) or "")
self._timer_service_entry.set_text(self._timer_data.get("e2eventservicename", None) or "")
self._timer_service_ref_entry.set_text(self._timer_data.get("e2eventservicereference", None) or "")
self._timer_event_id_entry.set_text(self._timer_data.get("e2eventid", None) or "")
self._timer_action_combo_box.set_active_id("1")
self._timer_after_combo_box.set_active_id("3")
start_time = int(self._timer_data.get("e2eventstart", "0"))
self.set_time_data(start_time, start_time + int(self._timer_data.get("e2eventduration", "0")))
start_time = int(self._timer_data.get("e2eventstart", "0") or "0")
self.set_time_data(start_time, start_time + int(self._timer_data.get("e2eventduration", "0") or "0"))
def set_time_data(self, start_time, end_time):
""" Sets values for time widgets. """