diff --git a/app/tools/epg.py b/app/tools/epg.py new file mode 100644 index 00000000..9415feef --- /dev/null +++ b/app/tools/epg.py @@ -0,0 +1,83 @@ +""" Module for working with epg.dat file """ + +import struct +from xml.dom.minidom import parse, Node + +from app.eparser.ecommons import BouquetService, BqServiceType + + +class EPG: + + @staticmethod + def get_epg_refs(path): + """ The read algorithm was taken from the eEPGCache::load() function from this source: + https://github.com/OpenPLi/enigma2/blob/44d9b92f5260c7de1b3b3a1b9a9cbe0f70ca4bf0/lib/dvb/epgcache.cpp#L1300 + """ + refs = [] + + with open(path, mode="rb") as f: + crc = struct.unpack(" 0: + [f.read(4) for n in range(n_crc)] + + refs.append(service_id) + + return refs + + +class ChannelsParser: + + @staticmethod + def get_refs_from_xml(path): + services = [] + dom = parse(path) + + description = "".join(n.data + "\n" for n in dom.childNodes if n.nodeType == Node.COMMENT_NODE) + services.append(BouquetService(name=description, type=BqServiceType.MARKER, data=None, num=-1)) + + for elem in dom.getElementsByTagName("channels"): + c_count = 0 + comment_count = 0 + current_data = "" + + if elem.hasChildNodes(): + for n in elem.childNodes: + if n.nodeType == Node.COMMENT_NODE: + c_count += 1 + comment_count += 1 + txt = n.data.strip() + if comment_count: + services.append(BouquetService(name=txt, type=BqServiceType.MARKER, data=None, num=c_count)) + comment_count -= 1 + else: + services.append(BouquetService(name=txt, + type=BqServiceType.DEFAULT, + data="{}:{}:{}:{}".format(*current_data.split(":")[3:7]), + num=c_count)) + if n.hasChildNodes(): + for s_node in n.childNodes: + if s_node.nodeType == Node.TEXT_NODE: + comment_count -= 1 + current_data = s_node.data + return services + + +if __name__ == "__main__": + pass diff --git a/app/ui/epg_dialog.glade b/app/ui/epg_dialog.glade new file mode 100644 index 00000000..da4d2a12 --- /dev/null +++ b/app/ui/epg_dialog.glade @@ -0,0 +1,535 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + False + center-on-parent + 480 + 240 + center + + + True + False + EPG Tool + 2 + True + + + True + False + 2 + + + True + True + True + Apply + + + True + False + gtk-apply + + + + + False + True + 0 + + + + + True + False + 5 + 5 + + + False + True + 1 + + + + + lamedb + True + True + False + Channel names source + True + True + radiobutton1 + + + False + True + 2 + + + + + xml + True + True + False + Channel names source + True + True + radiobutton2 + + + False + True + 3 + + + + + True + False + Select xml file + + + + False + True + 4 + + + + + + + True + True + True + Save to xml + + + True + False + gtk-save-as + + + + + end + 1 + + + + + + + True + False + vertical + + + True + False + True + + + True + True + tools-check-spelling + False + False + + + + + False + True + 0 + + + + + True + True + True + + + True + False + vertical + + + True + False + 2 + 2 + Source + + + + + + False + True + 0 + + + + + True + True + in + + + True + True + services_list_store + + + + + + Service + + + + 0 + + + + + + 1 + + + + + + + + + True + True + 2 + + + + + True + True + + + + + True + False + vertical + + + True + False + 2 + 2 + Bouquet details + + + + + + False + True + 0 + + + + + True + True + in + + + True + True + bouquet_list_store + 2 + True + both + True + + + multiple + + + + + True + 25 + Num + + + 0.20000000298023224 + 5 + 5 + + + 10 + 0 + + + + + + + True + 50 + Service + True + + + 2 + + + 10 + 8 + + + + + 2 + + + 10 + 1 + + + + + end + 25 + + + 10 + 2 + + + + + 2 + + + 10 + 3 + + + + + 2 + + + 10 + 4 + + + + + + + True + 25 + Type + True + + + 0.50999999046325684 + + + 10 + 5 + + + + + + + 25 + Pos + True + + + 0.50999999046325684 + + + 10 + 6 + + + + + + + False + fav_id + + + + 7 + + + + + + + False + extra + + + + 9 + + + + + + + + + + + + True + True + 1 + + + + + True + True + + + + + True + True + 2 + + + + + False + True + + + + False + 6 + end + + + False + False + 0 + + + + + False + 16 + + + True + False + start + label + + + True + True + 0 + + + + + False + False + 0 + + + + + False + True + 3 + + + + + + diff --git a/app/ui/epg_dialog.py b/app/ui/epg_dialog.py new file mode 100644 index 00000000..d081ae58 --- /dev/null +++ b/app/ui/epg_dialog.py @@ -0,0 +1,67 @@ +from app.commons import run_idle +from app.tools.epg import EPG +from .main_helper import on_popup_menu +from .uicommons import Gtk, UI_RESOURCES_PATH, TEXT_DOMAIN + + +class EpgDialog: + + def __init__(self, transient, options, services, fav_model): + + handlers = {"on_info_bar_close": self.on_info_bar_close, + "on_popup_menu": on_popup_menu} + + self._services = services + self._ex_fav_model = fav_model + self._options = options + + builder = Gtk.Builder() + builder.set_translation_domain(TEXT_DOMAIN) + builder.add_from_file(UI_RESOURCES_PATH + "epg_dialog.glade") + builder.connect_signals(handlers) + + self._dialog = builder.get_object("epg_dialog_window") + self._dialog.set_transient_for(transient) + self._bouquet_model = builder.get_object("bouquet_list_store") + self._services_model = builder.get_object("services_list_store") + self._info_bar = builder.get_object("info_bar") + self._message_label = builder.get_object("info_bar_message_label") + + self.init_data() + + @run_idle + def init_data(self): + for r in self._ex_fav_model: + self._bouquet_model.append(r[:]) + + try: + refs = EPG.get_epg_refs(self._options.get("data_dir_path", "") + "epg.dat") + + # for source lamedb + srvs = {k[:k.rfind(":")]: v for k, v in self._services.items()} + list(map(self._services_model.append, + map(lambda s: (s.service, s.fav_id), + filter(None, [srvs.get(ref) for ref in refs])))) + + except (FileNotFoundError, ValueError) as e: + self.show_info_message("Read epg.dat error: {}".format(e), Gtk.MessageType.ERROR) + else: + if len(self._services_model) == 0: + msg = "Current epg.dat file does not contains references for the services of this bouquet!" + self.show_info_message(msg, Gtk.MessageType.ERROR) + + def show(self): + self._dialog.show() + + def on_info_bar_close(self, bar=None, resp=None): + self._info_bar.set_visible(False) + + @run_idle + def show_info_message(self, text, message_type): + self._info_bar.set_visible(True) + self._info_bar.set_message_type(message_type) + self._message_label.set_text(text) + + +if __name__ == "__main__": + pass diff --git a/app/ui/main_app_window.py b/app/ui/main_app_window.py index e827ee90..af1ceb14 100644 --- a/app/ui/main_app_window.py +++ b/app/ui/main_app_window.py @@ -17,6 +17,7 @@ from app.eparser.iptv import export_to_m3u from app.eparser.neutrino.bouquets import BqType from app.properties import get_config, write_config, Profile from app.tools.media import Player +from app.ui.epg_dialog import EpgDialog from .backup import BackupDialog, backup_data, clear_data_path from .imports import ImportDialog, import_bouquet from .download_dialog import DownloadDialog @@ -133,6 +134,7 @@ class Application(Gtk.Application): "on_search_up": self.on_search_up, "on_search": self.on_search, "on_iptv": self.on_iptv, + "on_epg_list_configuration": self.on_epg_list_configuration, "on_iptv_list_configuration": self.on_iptv_list_configuration, "on_play_stream": self.on_play_stream, "on_player_play": self.on_player_play, @@ -1342,6 +1344,14 @@ class Application(Gtk.Application): if response: next(self.remove_favs(response, self._fav_model), False) + # ****************** EPG **********************# + + def on_epg_list_configuration(self, item): + if Profile(self._profile) is not Profile.ENIGMA_2: + self.show_error_dialog("Only Enigma2 is supported!") + return + EpgDialog(self._main_window, self._options.get(self._profile), self._services, self._fav_model).show() + # ***************** Import ********************# def on_import_m3u(self, item): diff --git a/app/ui/main_window.glade b/app/ui/main_window.glade index fae5df0d..9d01306e 100644 --- a/app/ui/main_window.glade +++ b/app/ui/main_window.glade @@ -82,6 +82,11 @@ Author: Dmitriy Yefremov False gtk-revert-to-saved + + True + False + gtk-index + True False @@ -578,7 +583,7 @@ Author: Dmitriy Yefremov - + True False @@ -666,7 +671,7 @@ Author: Dmitriy Yefremov - + True False @@ -736,6 +741,22 @@ Author: Dmitriy Yefremov False + + + EPG configuration + True + False + epg_configuration_image + False + + + + + + True + False + + List configuration @@ -747,7 +768,7 @@ Author: Dmitriy Yefremov - + True False @@ -767,7 +788,7 @@ Author: Dmitriy Yefremov - + True False @@ -805,7 +826,7 @@ Author: Dmitriy Yefremov - + True False @@ -821,7 +842,7 @@ Author: Dmitriy Yefremov - + True False @@ -841,7 +862,7 @@ Author: Dmitriy Yefremov - + True False