From 90418f0e287e7daf283d26b0cb3289643133c025 Mon Sep 17 00:00:00 2001 From: Dmitriy Yefremov Date: Sun, 11 Feb 2018 16:47:20 +0300 Subject: [PATCH 01/15] webtv parse for neutrino --- app/eparser/neutrino/bouquets.py | 99 +++++++++++++++++++++++++++++--- 1 file changed, 90 insertions(+), 9 deletions(-) diff --git a/app/eparser/neutrino/bouquets.py b/app/eparser/neutrino/bouquets.py index a6ff972c..ebfb2c4a 100644 --- a/app/eparser/neutrino/bouquets.py +++ b/app/eparser/neutrino/bouquets.py @@ -1,5 +1,4 @@ import os -from contextlib import suppress from enum import Enum from xml.dom.minidom import parse, Document @@ -8,16 +7,21 @@ from ..ecommons import Bouquets, Bouquet, BouquetService, BqServiceType, PROVIDE _FILE = "bouquets.xml" _U_FILE = "ubouquets.xml" +_W_FILE = "webtv.xml" + +_COMMENT = " File was created in DemonEditor. Enjoy watching! " class BqType(Enum): BOUQUET = "bouquet" TV = "tv" + WEBTV = "webtv" def get_bouquets(path): return (parse_bouquets(path + _FILE, "Providers", BqType.BOUQUET.value), - parse_bouquets(path + _U_FILE, "FAV", BqType.TV.value)) + parse_bouquets(path + _U_FILE, "FAV", BqType.TV.value), + parse_webtv(path + _W_FILE, "WEBTV", BqType.WEBTV.value)) def parse_bouquets(file, name, bq_type): @@ -61,22 +65,61 @@ def parse_bouquets(file, name, bq_type): return bouquets -def write_bouquets(path, bouquets): - if len(bouquets) < 2: - for f in path + _FILE, path + _U_FILE: - with suppress(FileNotFoundError): - os.remove(f) +def parse_webtv(path, name, bq_type): + bouquets = Bouquets(name=name, type=bq_type, bouquets=[]) + if not os.path.exists(path): + return bouquets + dom = parse(path) + services = [] + for elem in dom.getElementsByTagName("webtv"): + if elem.hasAttributes(): + title = elem.attributes["title"].value + url = elem.attributes["url"].value + description = elem.attributes["description"].value + urlkey = elem.attributes.get("urlkey", None) + urlkey = urlkey.value if urlkey else urlkey + account = elem.attributes.get("account", None) + account = account.value if account else account + usrname = elem.attributes.get("usrname", None) + usrname = usrname.value if usrname else usrname + psw = elem.attributes.get("psw", None) + psw = psw.value if psw else psw + s_type = elem.attributes.get("type", None) + s_type = s_type.value if s_type else s_type + iconsrc = elem.attributes.get("iconsrc", None) + iconsrc = iconsrc.value if iconsrc else iconsrc + iconsrc_b = elem.attributes.get("iconsrc_b", None) + iconsrc_b = iconsrc_b.value if iconsrc_b else iconsrc_b + group = elem.attributes.get("group", None) + group = group.value if group else group + fav_id = "{}::{}::{}::{}::{}::{}::{}::{}::{}::{}".format(url, description, urlkey, account, usrname, + psw, s_type, iconsrc, iconsrc_b, group) + srv = BouquetService(name=title, + type=BqServiceType.IPTV, + data=fav_id, + num=0) + services.append(srv) + bouquet = Bouquet(name="default", type=bq_type, services=services, locked=None, hidden=None) + bouquets[2].append(bouquet) + + return bouquets + + +def write_bouquets(path, bouquets): for bq in bouquets: bq_type = BqType(bq.type) - write_bouquet(path + (_FILE if bq_type is BqType.BOUQUET else _U_FILE), bq) + if bq_type is BqType.WEBTV: + write_webtv(path + _W_FILE, bq) + else: + write_bouquet(path + (_FILE if bq_type is BqType.BOUQUET else _U_FILE), bq) def write_bouquet(file, bouquet): doc = Document() root = doc.createElement("zapit") doc.appendChild(root) - comment = doc.createComment(" File was created in DemonEditor. Enjoy watching! ") + comment = doc.createComment(_COMMENT) doc.appendChild(comment) for bq in bouquet.bouquets: @@ -102,5 +145,43 @@ def write_bouquet(file, bouquet): doc.writexml(open(file, "w"), addindent=" ", newl="\n", encoding="UTF-8") +def write_webtv(file, bouquet): + doc = Document() + root = doc.createElement("webtvs") + doc.appendChild(root) + comment = doc.createComment(_COMMENT) + doc.appendChild(comment) + + for bq in bouquet.bouquets: + for srv in bq.services: + url, description, urlkey, account, usrname, psw, s_type, iconsrc, iconsrc_b, group = srv.fav_id.split("::") + srv_elem = doc.createElement("webtv") + srv_elem.setAttribute("title", srv.service) + srv_elem.setAttribute("url", url) + + if description != "None": + srv_elem.setAttribute("description", description) + if urlkey != "None": + srv_elem.setAttribute("urlkey", urlkey) + if account != "None": + srv_elem.setAttribute("account", account) + if usrname != "None": + srv_elem.setAttribute("usrname", usrname) + if psw != "None": + srv_elem.setAttribute("psw", psw) + if s_type != "None": + srv_elem.setAttribute("type", s_type) + if iconsrc != "None": + srv_elem.setAttribute("iconsrc", iconsrc) + if iconsrc_b != "None": + srv_elem.setAttribute("iconsrc_b", iconsrc_b) + if group != "None": + srv_elem.setAttribute("group", group) + + root.appendChild(srv_elem) + + doc.writexml(open(file, "w"), addindent=" ", newl="\n", encoding="UTF-8") + + if __name__ == "__main__": pass From ffce103eae53820cd0c40bf57614a27c736e02cf Mon Sep 17 00:00:00 2001 From: DYefremov Date: Sun, 11 Feb 2018 23:14:22 +0300 Subject: [PATCH 02/15] m3u gui elements for neutrino --- app/ui/main_app_window.py | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/app/ui/main_app_window.py b/app/ui/main_app_window.py index 9e121750..5c2e5d2f 100644 --- a/app/ui/main_app_window.py +++ b/app/ui/main_app_window.py @@ -9,6 +9,7 @@ from app.eparser import get_blacklist, write_blacklist, parse_m3u from app.eparser import get_services, get_bouquets, write_bouquets, write_services, Bouquets, Bouquet, Service from app.eparser.ecommons import CAS, FLAG from app.eparser.enigma.bouquets import BqServiceType +from app.eparser.neutrino.bouquets import BqType from app.properties import get_config, write_config, Profile from . import Gtk, Gdk, UI_RESOURCES_PATH, LOCKED_ICON, HIDE_ICON from .dialogs import show_dialog, DialogType, get_chooser_dialog @@ -39,8 +40,9 @@ class MainAppWindow: "fav_import_m3u_popup_item", "fav_insert_marker_popup_item", "fav_edit_popup_item", "fav_locate_popup_item", "fav_picon_popup_item") - _FAV_ONLY_ELEMENTS = ("import_m3u_tool_button", "fav_import_m3u_popup_item", "fav_insert_marker_popup_item", - "fav_edit_marker_popup_item") + _FAV_ENIGMA_ELEMENTS = ("fav_insert_marker_popup_item", "fav_edit_marker_popup_item") + + _FAV_M3U_ELEMENTS = ("import_m3u_tool_button", "fav_import_m3u_popup_item") _LOCK_HIDE_ELEMENTS = ("locked_tool_button", "hide_tool_button") @@ -714,7 +716,6 @@ class MainAppWindow: open_data=self.open_data, profile=Profile(self.__profile)) - @run_idle def on_view_focus(self, view, focus_event): profile = Profile(self.__profile) model = get_base_model(view.get_model()) @@ -731,12 +732,21 @@ class MainAppWindow: self.__tool_elements[elem].set_sensitive(not_empty) else: is_service = model_name == self._SERVICE_LIST_NAME + bq_selected = False + if model_name == self._FAV_LIST_NAME: + bq_selected = self.is_bouquet_selected() + if profile is Profile.NEUTRINO_MP and bq_selected: + name, bq_type = bq_selected.split(":") + bq_selected = BqType(bq_type) is BqType.WEBTV + for elem in self._FAV_ELEMENTS: if elem in ("paste_tool_button", "paste_menu_item", "fav_paste_popup_item"): self.__tool_elements[elem].set_sensitive(not is_service and self.__rows_buffer) - elif elem in self._FAV_ONLY_ELEMENTS: + elif elem in self._FAV_ENIGMA_ELEMENTS: if profile is Profile.ENIGMA_2: - self.__tool_elements[elem].set_sensitive(self.is_bouquet_selected() and not is_service) + self.__tool_elements[elem].set_sensitive(bq_selected and not is_service) + elif elem in self._FAV_M3U_ELEMENTS: + self.__tool_elements[elem].set_sensitive(bq_selected and not is_service) else: self.__tool_elements[elem].set_sensitive(not_empty and not is_service) for elem in self._SERVICE_ELEMENTS: From ab7f560b4f41db2e8d88aca2d50c566e9d3476cc Mon Sep 17 00:00:00 2001 From: Dmitriy Yefremov Date: Mon, 12 Feb 2018 13:34:00 +0300 Subject: [PATCH 03/15] webtv download/upload --- app/eparser/iptv.py | 15 ++++++++--- app/eparser/neutrino/bouquets.py | 8 +++--- app/ftp.py | 45 +++++++++++++++++++++++--------- app/ui/dialogs.glade | 16 +++++++++++- app/ui/download_dialog.py | 11 +++++--- app/ui/main_app_window.py | 2 +- 6 files changed, 74 insertions(+), 23 deletions(-) diff --git a/app/eparser/iptv.py b/app/eparser/iptv.py index c40ed203..342dbbd7 100644 --- a/app/eparser/iptv.py +++ b/app/eparser/iptv.py @@ -1,20 +1,29 @@ +""" Module for m3u import """ +from app.properties import Profile from .ecommons import BqServiceType, Service +# url, description, urlkey, account, usrname, psw, s_type, iconsrc, iconsrc_b, group +NEUTRINO_FAV_ID_FORMAT = "{}::{}::{}::{}::{}::{}::{}::{}::{}::{}" +ENIGMA2_FAV_ID_FORMAT = " 1:0:1:0:0:0:0:0:0:0:{}:{}\n#DESCRIPTION: {}\n" -def parse_m3u(path): + +def parse_m3u(path, profile): with open(path) as file: aggr = [None] * 10 channels = [] count = 0 name = None + fav_id = None for line in file.readlines(): if line.startswith("#EXTINF"): name = line[1 + line.index(","):].strip() count += 1 elif count == 1: count = 0 - fav_id = " 1:0:1:0:0:0:0:0:0:0:{}:{}\n#DESCRIPTION: {}\n".format( - line.strip().replace(":", "%3a"), name, name, None) + if profile is Profile.ENIGMA_2: + fav_id = ENIGMA2_FAV_ID_FORMAT.format(line.strip().replace(":", "%3a"), name, name, None) + elif profile is Profile.NEUTRINO_MP: + fav_id = NEUTRINO_FAV_ID_FORMAT.format(line.strip(), "", 0, None, None, None, None, "", "", 1) srv = Service(*aggr[0:3], name, *aggr[0:3], BqServiceType.IPTV.name, *aggr, fav_id, None) channels.append(srv) diff --git a/app/eparser/neutrino/bouquets.py b/app/eparser/neutrino/bouquets.py index ebfb2c4a..2af40aab 100644 --- a/app/eparser/neutrino/bouquets.py +++ b/app/eparser/neutrino/bouquets.py @@ -2,6 +2,7 @@ import os from enum import Enum from xml.dom.minidom import parse, Document +from app.eparser.iptv import NEUTRINO_FAV_ID_FORMAT from app.ui import LOCKED_ICON, HIDE_ICON from ..ecommons import Bouquets, Bouquet, BouquetService, BqServiceType, PROVIDER @@ -76,7 +77,8 @@ def parse_webtv(path, name, bq_type): if elem.hasAttributes(): title = elem.attributes["title"].value url = elem.attributes["url"].value - description = elem.attributes["description"].value + description = elem.attributes.get("description") + description = description.value if description else description urlkey = elem.attributes.get("urlkey", None) urlkey = urlkey.value if urlkey else urlkey account = elem.attributes.get("account", None) @@ -93,8 +95,8 @@ def parse_webtv(path, name, bq_type): iconsrc_b = iconsrc_b.value if iconsrc_b else iconsrc_b group = elem.attributes.get("group", None) group = group.value if group else group - fav_id = "{}::{}::{}::{}::{}::{}::{}::{}::{}::{}".format(url, description, urlkey, account, usrname, - psw, s_type, iconsrc, iconsrc_b, group) + fav_id = NEUTRINO_FAV_ID_FORMAT.format(url, description, urlkey, account, usrname, psw, s_type, iconsrc, + iconsrc_b, group) srv = BouquetService(name=title, type=BqServiceType.IPTV, data=fav_id, diff --git a/app/ftp.py b/app/ftp.py index 7ddb2839..76697f0e 100644 --- a/app/ftp.py +++ b/app/ftp.py @@ -11,12 +11,16 @@ from app.properties import Profile __DATA_FILES_LIST = ("tv", "radio", "lamedb", "blacklist", "whitelist", # enigma 2 "services.xml", "myservices.xml", "bouquets.xml", "ubouquets.xml") # neutrino +_SATELLITES_XML_FILE = "satellites.xml" +_WEBTV_XML_FILE = "webtv.xml" + class DownloadDataType(Enum): ALL = 0 BOUQUETS = 1 SATELLITES = 2 PICONS = 3 + WEBTV = 4 def download_data(*, properties, download_type=DownloadDataType.ALL, callback=None): @@ -35,20 +39,21 @@ def download_data(*, properties, download_type=DownloadDataType.ALL, callback=No name = str(file).strip() if name.endswith(__DATA_FILES_LIST): name = name.split()[-1] - with open(save_path + name, "wb") as f: - ftp.retrbinary("RETR " + name, f.write) - # satellites.xml section - if download_type is DownloadDataType.ALL or download_type is DownloadDataType.SATELLITES: + download_file(ftp, name, save_path) + # satellites.xml and webtv section + if download_type in (DownloadDataType.ALL, DownloadDataType.SATELLITES, DownloadDataType.WEBTV): ftp.cwd(properties["satellites_xml_path"]) files.clear() ftp.dir(files.append) for file in files: name = str(file).strip() - xml_file = "satellites.xml" - if name.endswith(xml_file): - with open(save_path + xml_file, 'wb') as f: - ftp.retrbinary("RETR " + xml_file, f.write) + if download_type in (DownloadDataType.ALL, DownloadDataType.SATELLITES): + if name.endswith(_SATELLITES_XML_FILE): + download_file(ftp, _SATELLITES_XML_FILE, save_path) + elif download_type in (DownloadDataType.ALL, DownloadDataType.WEBTV): + if name.endswith(_WEBTV_XML_FILE): + download_file(ftp, _WEBTV_XML_FILE, save_path) if callback is not None: callback() @@ -71,9 +76,20 @@ def upload_data(*, properties, download_type=DownloadDataType.ALL, remove_unused if download_type is DownloadDataType.ALL or download_type is DownloadDataType.SATELLITES: ftp.cwd(properties["satellites_xml_path"]) - file_name = "satellites.xml" - send = send_file(file_name, data_path, ftp) - if download_type == DownloadDataType.SATELLITES: + send = send_file(_SATELLITES_XML_FILE, data_path, ftp) + if download_type is DownloadDataType.SATELLITES: + tn.send("init 3" if profile is Profile.ENIGMA_2 else "init 6") + if callback is not None: + callback() + return send + + if profile is Profile.NEUTRINO_MP and download_type in (DownloadDataType.ALL, DownloadDataType.WEBTV): + ftp.cwd(properties["satellites_xml_path"]) + send = send_file(_WEBTV_XML_FILE, data_path, ftp) + if download_type is DownloadDataType.WEBTV: + tn.send("init 6") + if callback is not None: + callback() return send if download_type is DownloadDataType.ALL or download_type is DownloadDataType.BOUQUETS: @@ -88,7 +104,7 @@ def upload_data(*, properties, download_type=DownloadDataType.ALL, remove_unused ftp.delete(name) for file_name in os.listdir(data_path): - if file_name == "satellites.xml": + if file_name == _SATELLITES_XML_FILE or file_name == _WEBTV_XML_FILE: continue if file_name.endswith(__DATA_FILES_LIST): send_file(file_name, data_path, ftp) @@ -123,6 +139,11 @@ def upload_data(*, properties, download_type=DownloadDataType.ALL, remove_unused callback() +def download_file(ftp, name, save_path): + with open(save_path + name, "wb") as f: + ftp.retrbinary("RETR " + name, f.write) + + def send_file(file_name, path, ftp): """ Opens the file in binary mode and transfers into receiver """ with open(path + file_name, "rb") as f: diff --git a/app/ui/dialogs.glade b/app/ui/dialogs.glade index c0349d35..47c09607 100644 --- a/app/ui/dialogs.glade +++ b/app/ui/dialogs.glade @@ -220,7 +220,21 @@ dmitry.v.yefremov@gmail.com - + + WebTV + True + False + 0 + 0.52999997138977051 + True + True + all_radio_button + + + False + True + 4 + diff --git a/app/ui/download_dialog.py b/app/ui/download_dialog.py index 6cce92e1..8d934db9 100644 --- a/app/ui/download_dialog.py +++ b/app/ui/download_dialog.py @@ -35,16 +35,19 @@ class DownloadDialog: self._all_radio_button = builder.get_object("all_radio_button") self._bouquets_radio_button = builder.get_object("bouquets_radio_button") self._satellites_radio_button = builder.get_object("satellites_radio_button") + self._webtv_radio_button = builder.get_object("webtv_radio_button") + if profile is Profile.NEUTRINO_MP: + self._webtv_radio_button.set_visible(True) # self._dialog.get_content_area().set_border_width(0) @run_idle def on_receive(self, item): - self.download(True, d_type=self.get_download_type()) + self.download(True, self.get_download_type()) @run_idle def on_send(self, item): if show_dialog(DialogType.QUESTION, self._dialog) != Gtk.ResponseType.CANCEL: - self.download(d_type=self.get_download_type()) + self.download(False, self.get_download_type()) def get_download_type(self): download_type = DownloadDataType.ALL @@ -52,6 +55,8 @@ class DownloadDialog: download_type = DownloadDataType.BOUQUETS elif self._satellites_radio_button.get_active(): download_type = DownloadDataType.SATELLITES + elif self._webtv_radio_button.get_active(): + download_type = DownloadDataType.WEBTV return download_type def run(self): @@ -65,7 +70,7 @@ class DownloadDialog: @run_idle @run_task - def download(self, download=False, d_type=DownloadDataType.ALL): + def download(self, download, d_type): """ Download/upload data from/to receiver """ try: if download: diff --git a/app/ui/main_app_window.py b/app/ui/main_app_window.py index 5c2e5d2f..780ea65a 100644 --- a/app/ui/main_app_window.py +++ b/app/ui/main_app_window.py @@ -821,7 +821,7 @@ class MainAppWindow: show_dialog(DialogType.ERROR, self.__main_window, text="No m3u file is selected!") return - channels = parse_m3u(response) + channels = parse_m3u(response, Profile(self.__profile)) bq_selected = self.is_bouquet_selected() if channels and bq_selected: bq_services = self.__bouquets.get(bq_selected) From 588df32b2fd28c9e3d91945cc2e8d41134fb7657 Mon Sep 17 00:00:00 2001 From: Dmitriy Yefremov Date: Mon, 12 Feb 2018 14:27:21 +0300 Subject: [PATCH 04/15] added IPTV icon --- app/eparser/iptv.py | 3 ++- app/ui/__init__.py | 1 + app/ui/main_app_window.py | 5 +++-- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/app/eparser/iptv.py b/app/eparser/iptv.py index 342dbbd7..45e1cdce 100644 --- a/app/eparser/iptv.py +++ b/app/eparser/iptv.py @@ -1,5 +1,6 @@ """ Module for m3u import """ from app.properties import Profile +from app.ui import IPTV_ICON from .ecommons import BqServiceType, Service # url, description, urlkey, account, usrname, psw, s_type, iconsrc, iconsrc_b, group @@ -24,7 +25,7 @@ def parse_m3u(path, profile): fav_id = ENIGMA2_FAV_ID_FORMAT.format(line.strip().replace(":", "%3a"), name, name, None) elif profile is Profile.NEUTRINO_MP: fav_id = NEUTRINO_FAV_ID_FORMAT.format(line.strip(), "", 0, None, None, None, None, "", "", 1) - srv = Service(*aggr[0:3], name, *aggr[0:3], BqServiceType.IPTV.name, *aggr, fav_id, None) + srv = Service(None, None, IPTV_ICON, name, *aggr[0:3], BqServiceType.IPTV.name, *aggr, fav_id, None) channels.append(srv) return channels diff --git a/app/ui/__init__.py b/app/ui/__init__.py index cdd99b27..811757c8 100644 --- a/app/ui/__init__.py +++ b/app/ui/__init__.py @@ -15,6 +15,7 @@ LOCKED_ICON = theme.load_icon("system-lock-screen", 16, 0) if theme.lookup_icon( "system-lock-screen", 16, 0) else _IMAGE_MISSING HIDE_ICON = theme.load_icon("go-jump", 16, 0) if theme.lookup_icon("go-jump", 16, 0) else _IMAGE_MISSING TV_ICON = theme.load_icon("tv-symbolic", 16, 0) if theme.lookup_icon("tv-symbolic", 16, 0) else _IMAGE_MISSING +IPTV_ICON = theme.load_icon("emblem-shared", 16, 0) if theme.load_icon("emblem-shared", 16, 0) else None if __name__ == "__main__": pass diff --git a/app/ui/main_app_window.py b/app/ui/main_app_window.py index 780ea65a..5f834471 100644 --- a/app/ui/main_app_window.py +++ b/app/ui/main_app_window.py @@ -11,7 +11,7 @@ from app.eparser.ecommons import CAS, FLAG from app.eparser.enigma.bouquets import BqServiceType from app.eparser.neutrino.bouquets import BqType from app.properties import get_config, write_config, Profile -from . import Gtk, Gdk, UI_RESOURCES_PATH, LOCKED_ICON, HIDE_ICON +from . import Gtk, Gdk, UI_RESOURCES_PATH, LOCKED_ICON, HIDE_ICON, IPTV_ICON from .dialogs import show_dialog, DialogType, get_chooser_dialog from .download_dialog import show_download_dialog from .main_helper import edit_marker, insert_marker, move_items, edit, ViewTarget, set_flags, locate_in_services, \ @@ -526,7 +526,8 @@ class MainAppWindow: # IPTV and MARKER services s_type = srv.type if s_type is BqServiceType.MARKER or s_type is BqServiceType.IPTV: - srv = Service(*agr[0:3], srv.name, *agr[0:3], s_type.name, *agr, srv.num, fav_id, None) + icon = IPTV_ICON if s_type is BqServiceType.IPTV else None + srv = Service(*agr[0:2], icon, srv.name, *agr[0:3], s_type.name, *agr, srv.num, fav_id, None) self.__services[fav_id] = srv services.append(fav_id) self.__bouquets["{}:{}".format(name, bt_type)] = services From ca06400071db27a492f1cfb30f0d00a7d0b24e11 Mon Sep 17 00:00:00 2001 From: Dmitriy Yefremov Date: Tue, 13 Feb 2018 17:21:25 +0300 Subject: [PATCH 05/15] little ui changes --- app/ui/dialogs.glade | 2 +- app/ui/main_window.glade | 25 ++++++++----------------- 2 files changed, 9 insertions(+), 18 deletions(-) diff --git a/app/ui/dialogs.glade b/app/ui/dialogs.glade index 47c09607..3a9a532f 100644 --- a/app/ui/dialogs.glade +++ b/app/ui/dialogs.glade @@ -9,7 +9,7 @@ system-help normal DemonEditor - 0.2.3 Pre-alpha + 0.2.4 Pre-alpha 2018 Dmitriy Yefremov dmitry.v.yefremov@gmail.com diff --git a/app/ui/main_window.glade b/app/ui/main_window.glade index 74c50561..2e9f801d 100644 --- a/app/ui/main_window.glade +++ b/app/ui/main_window.glade @@ -1158,35 +1158,29 @@ True False - True False - 6 + 5 + True end - - - - - - False False - 0 + 1 False - 16 + 2 True - + True False @@ -1216,7 +1210,7 @@ - + True False @@ -1233,12 +1227,9 @@ False False - 0 + 1 - - - False @@ -2232,7 +2223,7 @@ True False - Ver. 0.2.3 Pre-alpha + Ver. 0.2.4 Pre-alpha 0.94999998807907104 From 51605ae6802fe76174f4adf4d749721911c515a9 Mon Sep 17 00:00:00 2001 From: Dmitriy Yefremov Date: Wed, 14 Feb 2018 00:00:52 +0300 Subject: [PATCH 06/15] service details dialog skeleton --- app/ui/main_app_window.py | 8 +- app/ui/main_window.glade | 21 + app/ui/service_details_dialog.glade | 1136 +++++++++++++++++++++++++++ app/ui/service_details_dialog.py | 23 + build-deb.sh | 2 +- deb/DEBIAN/control | 2 +- 6 files changed, 1189 insertions(+), 3 deletions(-) create mode 100644 app/ui/service_details_dialog.glade create mode 100644 app/ui/service_details_dialog.py diff --git a/app/ui/main_app_window.py b/app/ui/main_app_window.py index 5f834471..1ef0f0ff 100644 --- a/app/ui/main_app_window.py +++ b/app/ui/main_app_window.py @@ -19,6 +19,7 @@ from .main_helper import edit_marker, insert_marker, move_items, edit, ViewTarge from .picons_dialog import PiconsDialog from .satellites_dialog import show_satellites_dialog from .settings_dialog import show_settings_dialog +from .service_details_dialog import ServiceDetailsDialog class MainAppWindow: @@ -101,7 +102,8 @@ class MainAppWindow: "on_reference_picon": self.on_reference_picon, "on_filter_toggled": self.on_filter_toggled, "on_search_toggled": self.on_search_toggled, - "on_search": self.on_search} + "on_search": self.on_search, + "on_services_data_edit": self.on_services_data_edit} self.__options = get_config() self.__profile = self.__options.get("profile") @@ -887,6 +889,10 @@ class MainAppWindow: self.__services, self.__bouquets) + def on_services_data_edit(self, item): + dialog = ServiceDetailsDialog(self.__main_window) + dialog.show() + @run_idle def update_picons(self): update_picons(self.__options.get(self.__profile).get("picons_dir_path"), self.__picons, self.__services_model) diff --git a/app/ui/main_window.glade b/app/ui/main_window.glade index 2e9f801d..fbb278d6 100644 --- a/app/ui/main_window.glade +++ b/app/ui/main_window.glade @@ -117,6 +117,11 @@ False gtk-copy + + True + False + gtk-properties + True False @@ -367,6 +372,22 @@ + + + True + False + + + + + Edit data/new service + True + False + image17 + False + + + True diff --git a/app/ui/service_details_dialog.glade b/app/ui/service_details_dialog.glade new file mode 100644 index 00000000..7e95f433 --- /dev/null +++ b/app/ui/service_details_dialog.glade @@ -0,0 +1,1136 @@ + + + + + + False + False + True + True + document-properties-symbolic + dialog + + + False + vertical + 2 + + + False + end + + + gtk-cancel + True + True + True + True + + + True + True + 0 + + + + + gtk-apply + True + True + True + True + + + True + True + 1 + + + + + False + False + 0 + + + + + True + False + + + False + True + 1 + + + + + True + False + + + True + False + vertical + 2 + + + True + False + Service data: + 0.0099999997764825821 + + + False + True + 0 + + + + + True + False + 2 + 2 + + + True + False + Name: + 0 + + + 0 + 0 + + + + + True + False + Package: + 0 + + + 0 + 1 + + + + + True + False + Type: + 0 + + + 0 + 2 + + + + + True + True + + + 1 + 0 + + + + + True + True + + + 1 + 1 + + + + + True + True + + + 1 + 2 + + + + + True + True + + + 3 + 2 + + + + + True + True + + + 3 + 1 + + + + + True + False + Extra: + 1 + + + 2 + 2 + + + + + True + False + Reference: + 1 + + + 2 + 1 + + + + + True + True + + + 3 + 0 + + + + + True + False + ID: + 1 + + + 2 + 0 + + + + + True + True + 1 + + + + + True + False + + + False + True + 2 + + + + + True + False + vertical + 2 + + + True + True + 4 + + + 8 + 1 + + + + + True + True + 4 + + + 7 + 1 + + + + + True + True + 4 + + + 6 + 1 + + + + + True + True + 4 + + + 5 + 1 + + + + + True + True + 4 + + + 4 + 1 + + + + + True + True + 4 + + + 3 + 1 + + + + + True + True + 4 + + + 2 + 1 + + + + + True + False + Teletext + + + 8 + 0 + + + + + True + False + HE-ACC + + + 7 + 0 + + + + + True + False + ACC + + + 6 + 0 + + + + + True + False + AC3+ + + + 5 + 0 + + + + + True + False + AC3 + + + 4 + 0 + + + + + True + False + MPEG + + + 3 + 0 + + + + + True + False + PCR + + + 2 + 0 + + + + + True + True + 4 + + + 1 + 1 + + + + + True + False + Video + + + 1 + 0 + + + + + True + False + PID's + + + 0 + 1 + + + + + + + + False + False + 3 + + + + + True + False + + + False + True + 4 + + + + + True + False + 2 + + + True + True + 4 + + + 2 + 1 + + + + + True + False + PCM: + + + 2 + 0 + + + + + True + True + 5 + + + 1 + 1 + + + + + True + False + Bitstream: + + + 1 + 0 + + + + + True + False + Delays (ms): + 0 + + + 0 + 1 + + + + + + + + False + True + 5 + + + + + True + False + + + False + True + 6 + + + + + True + False + 2 + + + True + False + label + + + 1 + 1 + + + + + True + False + label + + + 1 + 2 + + + + + True + False + label + + + 1 + 3 + + + + + True + False + label + + + 1 + 4 + + + + + New + True + True + False + 0 + True + + + 0 + 4 + + + + + Use PID's + True + True + False + 0 + True + + + 0 + 3 + + + + + Hide + True + True + False + 0 + True + + + 0 + 2 + + + + + Keep + True + True + False + 0 + True + + + 0 + 1 + + + + + True + False + Flags: + 0 + + + 0 + 0 + + + + + + + + False + True + 7 + + + + + False + True + 0 + + + + + True + False + vertical + + + False + False + 10 + 1 + + + + + True + False + vertical + + + True + False + Transponder data: + 0.0099999997764825821 + + + False + True + 0 + + + + + True + False + 2 + 2 + True + + + True + False + Sat. position: + 0 + + + 0 + 0 + + + + + True + False + Transponder ID: + 0 + + + 0 + 1 + + + + + True + False + Network ID: + 0 + + + 0 + 2 + + + + + True + True + + + 1 + 0 + + + + + True + True + + + 1 + 1 + + + + + True + True + + + 1 + 2 + + + + + True + True + + + 1 + 3 + + + + + True + False + Frequency: + 0 + + + 0 + 3 + + + + + True + False + Symbolrate: + 0 + + + 0 + 4 + + + + + True + True + + + 1 + 4 + + + + + True + False + System: + 0 + + + 0 + 7 + + + + + True + False + Polarisation: + 0 + + + 0 + 5 + + + + + True + False + FEC: + 0 + + + 0 + 6 + + + + + True + False + + + 1 + 5 + + + + + True + False + + + 1 + 6 + + + + + True + False + + + 1 + 7 + + + + + True + False + Flags: + 1 + + + 2 + 7 + + + + + True + True + + + 3 + 7 + + + + + True + False + Stream ID: + 1 + + + 2 + 6 + + + + + True + True + + + 3 + 6 + + + + + True + False + PLS mode: + 1 + + + 2 + 5 + + + + + True + True + + + 3 + 5 + + + + + True + False + PLS code: + 1 + + + 2 + 4 + + + + + True + False + Pilot: + 1 + + + 2 + 3 + + + + + True + False + + + 3 + 3 + + + + + True + False + Rolloff: + 1 + + + 2 + 2 + + + + + True + False + + + 3 + 2 + + + + + True + False + Invertion: + 1 + + + 2 + 1 + + + + + True + False + + + 3 + 1 + + + + + True + False + Modulation: + 0 + + + 2 + 0 + + + + + True + False + + + 3 + 0 + + + + + True + True + + + 3 + 4 + + + + + False + False + 1 + + + + + True + False + 2 + + + True + False + Namespace: + 1 + + + 0 + 0 + + + + + True + True + + + 1 + 0 + + + + + False + True + end + 2 + + + + + False + True + 2 + + + + + False + True + 3 + + + + + True + False + + + False + True + 5 + + + + + + diff --git a/app/ui/service_details_dialog.py b/app/ui/service_details_dialog.py new file mode 100644 index 00000000..2b488bb0 --- /dev/null +++ b/app/ui/service_details_dialog.py @@ -0,0 +1,23 @@ +from . import Gtk, UI_RESOURCES_PATH + + +class ServiceDetailsDialog: + def __init__(self, transient): + builder = Gtk.Builder() + builder.add_from_file(UI_RESOURCES_PATH + "service_details_dialog.glade") + + self._dialog = builder.get_object("service_details_dialog") + self._dialog.set_transient_for(transient) + + def show(self): + response = self._dialog.run() + if response == Gtk.ResponseType.OK: + pass + self._dialog.destroy() + + return response + + +if __name__ == "__main__": + dialog = ServiceDetailsDialog() + dialog.show() diff --git a/build-deb.sh b/build-deb.sh index 81231bd8..91d22c22 100755 --- a/build-deb.sh +++ b/build-deb.sh @@ -1,5 +1,5 @@ #!/bin/env bash -VER="0.2.3_Pre-alpha" +VER="0.2.4_Pre-alpha" B_PATH="dist/DemonEditor" DEB_PATH="$B_PATH/usr/share/demoneditor" diff --git a/deb/DEBIAN/control b/deb/DEBIAN/control index 625cdc74..9af45df8 100644 --- a/deb/DEBIAN/control +++ b/deb/DEBIAN/control @@ -1,5 +1,5 @@ Package: DemonEditor -Version: 0.2.3-Pre-alpha +Version: 0.2.4-Pre-alpha Section: utils Priority: optional Architecture: all From 99a9f081fa7bda55b0c4a2c0a8c7d6ce66d2186c Mon Sep 17 00:00:00 2001 From: DYefremov Date: Wed, 14 Feb 2018 19:06:08 +0300 Subject: [PATCH 07/15] service details dialog ui changes --- app/ui/service_details_dialog.glade | 568 ++++++++++++++-------------- 1 file changed, 285 insertions(+), 283 deletions(-) diff --git a/app/ui/service_details_dialog.glade b/app/ui/service_details_dialog.glade index 7e95f433..215b0691 100644 --- a/app/ui/service_details_dialog.glade +++ b/app/ui/service_details_dialog.glade @@ -4,6 +4,7 @@ False + Service details False True True @@ -61,7 +62,7 @@ False True - 1 + 0 @@ -75,7 +76,7 @@ vertical 2 - + True False Service data: @@ -118,19 +119,7 @@ - - True - False - Type: - 0 - - - 0 - 2 - - - - + True True @@ -140,7 +129,7 @@ - + True True @@ -150,63 +139,11 @@ - - True - True - - - 1 - 2 - - - - - True - True - - - 3 - 2 - - - - - True - True - - - 3 - 1 - - - - - True - False - Extra: - 1 - - - 2 - 2 - - - - - True - False - Reference: - 1 - - - 2 - 1 - - - - + True True + 10 + 10 3 @@ -225,9 +162,126 @@ 0 + + + True + False + Type: + 1 + + + 2 + 1 + + + + + True + True + 10 + 10 + + + 3 + 1 + + + + + True + False + 2 + + + True + False + Bitstream: + + + 0 + 0 + + + + + True + True + 5 + 5 + + + 0 + 1 + + + + + True + False + PCM: + + + 1 + 0 + + + + + True + True + 3 + 6 + + + 1 + 1 + + + + + 3 + 2 + + + + + True + False + Delays (ms): + 10 + 10 + 0 + + + 2 + 2 + + + + + True + True + + + 1 + 2 + + + + + True + False + Extra: + 0 + + + 0 + 2 + + - True + False True 1 @@ -250,10 +304,11 @@ vertical 2 - + True True 4 + 5 8 @@ -261,10 +316,11 @@ - + True True 4 + 4 7 @@ -272,10 +328,11 @@ - + True True 4 + 4 6 @@ -283,10 +340,11 @@ - + True True 4 + 4 5 @@ -294,10 +352,11 @@ - + True True 4 + 4 4 @@ -305,10 +364,11 @@ - + True True 4 + 4 3 @@ -316,10 +376,11 @@ - + True True 4 + 4 2 @@ -404,10 +465,11 @@ - + True True 4 + 4 1 @@ -429,7 +491,10 @@ True False - PID's + start + PID's: + 6 + 6 0 @@ -446,192 +511,13 @@ 3 - - - True - False - - - False - True - 4 - - - - - True - False - 2 - - - True - True - 4 - - - 2 - 1 - - - - - True - False - PCM: - - - 2 - 0 - - - - - True - True - 5 - - - 1 - 1 - - - - - True - False - Bitstream: - - - 1 - 0 - - - - - True - False - Delays (ms): - 0 - - - 0 - 1 - - - - - - - - False - True - 5 - - - - - True - False - - - False - True - 6 - - True False 2 - - True - False - label - - - 1 - 1 - - - - - True - False - label - - - 1 - 2 - - - - - True - False - label - - - 1 - 3 - - - - - True - False - label - - - 1 - 4 - - - - - New - True - True - False - 0 - True - - - 0 - 4 - - - - - Use PID's - True - True - False - 0 - True - - - 0 - 3 - - - - - Hide - True - True - False - 0 - True - - - 0 - 2 - - - - + Keep True True @@ -645,7 +531,7 @@ - + True False Flags: @@ -656,6 +542,54 @@ 0 + + + Hide + True + True + False + 0 + True + + + 1 + 1 + + + + + Use PID's + True + True + False + 0 + True + + + 2 + 1 + + + + + New + True + True + False + 0 + True + + + 3 + 1 + + + + + + + + @@ -663,7 +597,56 @@ False True - 7 + 4 + + + + + True + False + + + False + True + 5 + + + + + True + False + 2 + + + True + False + Reference: + 1 + + + False + True + 0 + + + + + True + True + 30 + + + False + False + 1 + + + + + False + True + end + 8 @@ -692,7 +675,7 @@ False vertical - + True False Transponder data: @@ -748,9 +731,11 @@ - + True True + 10 + 10 1 @@ -758,9 +743,11 @@ - + True True + 10 + 10 1 @@ -768,9 +755,11 @@ - + True True + 10 + 10 1 @@ -778,9 +767,11 @@ - + True True + 10 + 10 1 @@ -812,9 +803,11 @@ - + True True + 10 + 10 1 @@ -858,7 +851,7 @@ - + True False @@ -868,7 +861,7 @@ - + True False @@ -878,7 +871,7 @@ - + True False @@ -900,9 +893,11 @@ - + True True + 10 + 10 3 @@ -922,9 +917,11 @@ - + True True + 10 + 10 3 @@ -944,9 +941,11 @@ - + True True + 10 + 10 3 @@ -978,7 +977,7 @@ - + True False @@ -1000,7 +999,7 @@ - + True False @@ -1022,7 +1021,7 @@ - + True False @@ -1044,7 +1043,7 @@ - + True False @@ -1054,9 +1053,11 @@ - + True True + 10 + 10 3 @@ -1074,6 +1075,7 @@ True False + 5 2 @@ -1088,7 +1090,7 @@ - + True True @@ -1116,7 +1118,7 @@ False True - 3 + 2 @@ -1127,7 +1129,7 @@ False True - 5 + 4 From e8f30b667d7a50a841da0202eb13421f8405ec81 Mon Sep 17 00:00:00 2001 From: Dmitriy Yefremov Date: Thu, 15 Feb 2018 15:16:34 +0300 Subject: [PATCH 08/15] service dialog elements update --- README.md | 21 +-- app/ui/main_app_window.py | 3 +- app/ui/service_details_dialog.glade | 273 +++++++++++++++++++++++++--- app/ui/service_details_dialog.py | 72 +++++++- 4 files changed, 328 insertions(+), 41 deletions(-) diff --git a/README.md b/README.md index 9a1d8b5d..5b3985b4 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,10 @@ # DemonEditor -Enigma2 channel and satellites list editor for GNU/Linux. +##Enigma2 channel and satellites list editor for GNU/Linux. Experimental support of Neutrino-MP or others on the same basis (BPanther, etc). Focused on the convenience of working in lists from the keyboard. The mouse is also fully supported (Drag and Drop etc) -Keyboard shortcuts: +###Keyboard shortcuts: Ctrl + X, C, V, Up, Down, PageUp, PageDown, S, T, E, L, H, Space; Insert, Delete, F2. Insert - copies the selected channels from the main list to the bouquet or inserts (creates) a new bouquet. Ctrl + X - only in bouquet list. Ctrl + C - only in services list. @@ -15,18 +15,17 @@ Ctrl + L - parental lock. Ctrl + H - hide/skip. Left/Right - remove selection. -Multiple selections in lists only with Space key (as in file managers)! - -Extra: -Ability to import IPTV into bouquet from m3u files(Enigma2 only)! +###Extra: +Multiple selections in lists only with Space key (as in file managers)! +Ability to import IPTV into bouquet (Neutrino WEBTV) from m3u files! Tool for downloading picons from lyngsat.com. +###Minimum requirements: +Python >= 3.5.2 and GTK+ 3 with PyGObject bindings. +####Note. +To create a simple debian package, you can use the build-deb.sh Tests only in image based on OpenPLi or last BPanther(neutrino) images with GM 990 Spark Reloaded receiver in my preferred linux distro (Last Linux Mint 18.* - MATE 64-bit)! +#####Terrestrial and cable channels at the moment are not supported! -Minimum requirements: Python >= 3.5.2 and GTK+ 3 with PyGObject bindings. - -Terrestrial and cable channels at the moment are not supported! - -Note. To create a simple debian package, you can use the build-deb.sh diff --git a/app/ui/main_app_window.py b/app/ui/main_app_window.py index 1ef0f0ff..addf7a60 100644 --- a/app/ui/main_app_window.py +++ b/app/ui/main_app_window.py @@ -889,8 +889,9 @@ class MainAppWindow: self.__services, self.__bouquets) + @run_idle def on_services_data_edit(self, item): - dialog = ServiceDetailsDialog(self.__main_window) + dialog = ServiceDetailsDialog(self.__main_window, Profile(self.__profile), self.__services_view) dialog.show() @run_idle diff --git a/app/ui/service_details_dialog.glade b/app/ui/service_details_dialog.glade index 215b0691..e5fca4c8 100644 --- a/app/ui/service_details_dialog.glade +++ b/app/ui/service_details_dialog.glade @@ -2,6 +2,142 @@ + + + + + + + + Auto + + + 1/2 + + + 2/3 + + + 3/4 + + + 5/6 + + + 7/8 + + + 8/9 + + + 3/5 + + + 4/5 + + + 9/10 + + + + + + + + + + + + + + + + + Auto + + + QPSK + + + 8PSK + + + 16APSK + + + 32APSK + + + + + + + + + + + + + + + + + Root + + + Gold + + + Combo + + + + + + + + + + + H + + + V + + + R + + + L + + + + + + + + + + + + + + + + + + + + + + + DVB-S + + + DVB-S2 + + + False Service details @@ -154,7 +290,7 @@ True False - ID: + SSID: 1 @@ -251,6 +387,7 @@ 10 10 0 + 0.75 2 @@ -328,7 +465,7 @@ - + True True 4 @@ -340,7 +477,7 @@ - + True True 4 @@ -585,13 +722,34 @@ - + + True + False + + + 1 + 0 + - + + True + False + + + 2 + 0 + - + + True + False + + + 3 + 0 + @@ -730,18 +888,6 @@ 2 - - - True - True - 10 - 10 - - - 1 - 0 - - True @@ -854,6 +1000,13 @@ True False + pol_list_store + + + + 0 + + 1 @@ -864,6 +1017,13 @@ True False + fec_list_store + + + + 0 + + 1 @@ -874,6 +1034,13 @@ True False + sys_list_store + + + + 0 + + 1 @@ -896,7 +1063,7 @@ True True - 10 + 5 10 @@ -920,7 +1087,7 @@ True True - 10 + 5 10 @@ -932,7 +1099,7 @@ True False - PLS mode: + PLS code: 1 @@ -941,10 +1108,10 @@ - + True True - 10 + 5 10 @@ -956,7 +1123,7 @@ True False - PLS code: + PLS mode: 1 @@ -980,6 +1147,13 @@ True False + pilot_list_store + + + + 0 + + 3 @@ -1002,6 +1176,13 @@ True False + rolloff_list_store + + + + 0 + + 3 @@ -1024,6 +1205,13 @@ True False + invertion_list_store + + + + 0 + + 3 @@ -1046,6 +1234,13 @@ True False + mod_list_store + + + + 0 + + 3 @@ -1053,11 +1248,33 @@ - + True - True - 10 - 10 + False + sat_pos_list_store + + + + 0 + + + + + 1 + 0 + + + + + True + False + pls_mode_list_store + + + + 0 + + 3 diff --git a/app/ui/service_details_dialog.py b/app/ui/service_details_dialog.py index 2b488bb0..e54ed063 100644 --- a/app/ui/service_details_dialog.py +++ b/app/ui/service_details_dialog.py @@ -1,13 +1,83 @@ +from app.commons import run_idle +from app.eparser import Service +from app.ui.main_helper import get_base_model, is_only_one_item_selected from . import Gtk, UI_RESOURCES_PATH class ServiceDetailsDialog: - def __init__(self, transient): + def __init__(self, transient, options, view): builder = Gtk.Builder() builder.add_from_file(UI_RESOURCES_PATH + "service_details_dialog.glade") self._dialog = builder.get_object("service_details_dialog") self._dialog.set_transient_for(transient) + self._options = options + self._services_view = view + # Service elements + self._name_entry = builder.get_object("name_entry") + self._package_entry = builder.get_object("package_entry") + self._id_entry = builder.get_object("id_entry") + self._type_entry = builder.get_object("type_entry") + self._extra_entry = builder.get_object("extra_entry") + self._bitstream_entry = builder.get_object("bitstream_entry") + self._pcm_entry = builder.get_object("pcm_entry") + self._reference_entry = builder.get_object("reference_entry") + self._video_pid_entry = builder.get_object("video_pid_entry") + self._pcr_pid_entry = builder.get_object("pcr_pid_entry") + self._mpeg_pid_entry = builder.get_object("mpeg_pid_entry") + self._ac3_pid_entry = builder.get_object("ac3_pid_entry") + self._ac3plus_pid_entry = builder.get_object("ac3plus_pid_entry") + self._acc_pid_entry = builder.get_object("acc_pid_entry") + self._he_acc_pid_entry = builder.get_object("he_acc_pid_entry") + self._teletext_pid_entry = builder.get_object("teletext_pid_entry") + self._keep_check_button = builder.get_object("keep_check_button") + self._hide_check_button = builder.get_object("hide_check_button") + self._use_pids_check_button = builder.get_object("use_pids_check_button") + self._new_check_button = builder.get_object("new_check_button") + # Transponder elements + self._sat_pos_combo_box = builder.get_object("sat_pos_combo_box") + self._transponder_id_entry = builder.get_object("transponder_id_entry") + self._network_id_entry = builder.get_object("network_id_entry") + self._freq_entry = builder.get_object("freq_entry") + self._rate_entry = builder.get_object("rate_entry") + self._pol_combo_box = builder.get_object("pol_combo_box") + self._fec_combo_box = builder.get_object("fec_combo_box") + self._sys_combo_box = builder.get_object("sys_combo_box") + self._mod_combo_box = builder.get_object("mod_combo_box") + self._invertion_combo_box = builder.get_object("invertion_combo_box") + self._rolloff_combo_box = builder.get_object("rolloff_combo_box") + self._pilot_combo_box = builder.get_object("pilot_combo_box") + self._pls_mode_combo_box = builder.get_object("pls_mode_combo_box") + self._pls_code_entry = builder.get_object("pls_code_entry") + self._stream_id_entry = builder.get_object("stream_id_entry") + self._flags_entry = builder.get_object("flags_entry") + self._namespace_entry = builder.get_object("namespace_entry") + self.update_data_elements() + + @run_idle + def update_data_elements(self): + model, paths = self._services_view.get_selection().get_selected_rows() + model = get_base_model(model) + if is_only_one_item_selected(paths, self._dialog): + srv = Service(*model[paths][:]) + self._name_entry.set_text(srv.service) + self._package_entry.set_text(srv.package) + self._type_entry.set_text("") + self._id_entry.set_text(str(int(srv.ssid, 16))) + self._reference_entry.set_text(srv.picon_id.replace("_", ":").rstrip(".png")) + # transponder + self._freq_entry.set_text(srv.freq) + self._rate_entry.set_text(srv.rate) + self.select_active_text(self._pol_combo_box, srv.pol) + self.select_active_text(self._fec_combo_box, srv.fec) + self.select_active_text(self._sys_combo_box, srv.system) + + def select_active_text(self, box: Gtk.ComboBox, text): + model = box.get_model() + for index, row in enumerate(model): + if row[0] == text: + box.set_active(index) + break def show(self): response = self._dialog.run() From 0040ecee325a101b5646cd0115d5653b7349ee23 Mon Sep 17 00:00:00 2001 From: Dmitriy Yefremov Date: Thu, 15 Feb 2018 15:21:08 +0300 Subject: [PATCH 09/15] Update README.md --- README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 5b3985b4..710390bd 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,10 @@ # DemonEditor -##Enigma2 channel and satellites list editor for GNU/Linux. +## Enigma2 channel and satellites list editor for GNU/Linux. Experimental support of Neutrino-MP or others on the same basis (BPanther, etc). Focused on the convenience of working in lists from the keyboard. The mouse is also fully supported (Drag and Drop etc) -###Keyboard shortcuts: +### Keyboard shortcuts: Ctrl + X, C, V, Up, Down, PageUp, PageDown, S, T, E, L, H, Space; Insert, Delete, F2. Insert - copies the selected channels from the main list to the bouquet or inserts (creates) a new bouquet. Ctrl + X - only in bouquet list. Ctrl + C - only in services list. @@ -15,17 +15,17 @@ Ctrl + L - parental lock. Ctrl + H - hide/skip. Left/Right - remove selection. -###Extra: +### Extra: Multiple selections in lists only with Space key (as in file managers)! Ability to import IPTV into bouquet (Neutrino WEBTV) from m3u files! Tool for downloading picons from lyngsat.com. -###Minimum requirements: +### Minimum requirements: Python >= 3.5.2 and GTK+ 3 with PyGObject bindings. -####Note. +#### Note. To create a simple debian package, you can use the build-deb.sh Tests only in image based on OpenPLi or last BPanther(neutrino) images with GM 990 Spark Reloaded receiver in my preferred linux distro (Last Linux Mint 18.* - MATE 64-bit)! -#####Terrestrial and cable channels at the moment are not supported! +##### Terrestrial and cable channels at the moment are not supported! From 5dc20232ef3ecbe48e00f19624ba966dff5ac11d Mon Sep 17 00:00:00 2001 From: Dmitriy Yefremov Date: Thu, 15 Feb 2018 15:22:09 +0300 Subject: [PATCH 10/15] Update README.md --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 710390bd..c0ca408d 100644 --- a/README.md +++ b/README.md @@ -26,6 +26,7 @@ To create a simple debian package, you can use the build-deb.sh Tests only in image based on OpenPLi or last BPanther(neutrino) images with GM 990 Spark Reloaded receiver in my preferred linux distro (Last Linux Mint 18.* - MATE 64-bit)! -##### Terrestrial and cable channels at the moment are not supported! + +#### Terrestrial and cable channels at the moment are not supported! From 12983bb1a6bae1ac0510bb7980c3c8307f5c2949 Mon Sep 17 00:00:00 2001 From: Dmitriy Yefremov Date: Thu, 15 Feb 2018 18:05:13 +0300 Subject: [PATCH 11/15] new variant of service details dialog --- app/ui/service_details_dialog.glade | 1755 +++++++++++++-------------- 1 file changed, 814 insertions(+), 941 deletions(-) diff --git a/app/ui/service_details_dialog.glade b/app/ui/service_details_dialog.glade index e5fca4c8..ce5ce52a 100644 --- a/app/ui/service_details_dialog.glade +++ b/app/ui/service_details_dialog.glade @@ -152,7 +152,7 @@ vertical 2 - + False end @@ -191,9 +191,14 @@ - + True False + Service data: + 0.0099999997764825821 + False @@ -202,609 +207,106 @@ - + True False + vertical + 2 - + True False - vertical - 2 + 2 - + True False - Service data: - 0.0099999997764825821 + Name - False - True - 0 + 0 + 0 - + True - False - 2 - 2 - - - True - False - Name: - 0 - - - 0 - 0 - - - - - True - False - Package: - 0 - - - 0 - 1 - - - - - True - True - - - 1 - 0 - - - - - True - True - - - 1 - 1 - - - - - True - True - 10 - 10 - - - 3 - 0 - - - - - True - False - SSID: - 1 - - - 2 - 0 - - - - - True - False - Type: - 1 - - - 2 - 1 - - - - - True - True - 10 - 10 - - - 3 - 1 - - - - - True - False - 2 - - - True - False - Bitstream: - - - 0 - 0 - - - - - True - True - 5 - 5 - - - 0 - 1 - - - - - True - False - PCM: - - - 1 - 0 - - - - - True - True - 3 - 6 - - - 1 - 1 - - - - - 3 - 2 - - - - - True - False - Delays (ms): - 10 - 10 - 0 - 0.75 - - - 2 - 2 - - - - - True - True - - - 1 - 2 - - - - - True - False - Extra: - 0 - - - 0 - 2 - - + True + gtk-edit - False - True - 1 + 0 + 1 - + True False + Package - False - True - 2 + 1 + 0 - + True - False - vertical - 2 - - - True - True - 4 - 5 - - - 8 - 1 - - - - - True - True - 4 - 4 - - - 7 - 1 - - - - - True - True - 4 - 4 - - - 6 - 1 - - - - - True - True - 4 - 4 - - - 5 - 1 - - - - - True - True - 4 - 4 - - - 4 - 1 - - - - - True - True - 4 - 4 - - - 3 - 1 - - - - - True - True - 4 - 4 - - - 2 - 1 - - - - - True - False - Teletext - - - 8 - 0 - - - - - True - False - HE-ACC - - - 7 - 0 - - - - - True - False - ACC - - - 6 - 0 - - - - - True - False - AC3+ - - - 5 - 0 - - - - - True - False - AC3 - - - 4 - 0 - - - - - True - False - MPEG - - - 3 - 0 - - - - - True - False - PCR - - - 2 - 0 - - - - - True - True - 4 - 4 - - - 1 - 1 - - - - - True - False - Video - - - 1 - 0 - - - - - True - False - start - PID's: - 6 - 6 - - - 0 - 1 - - - - - + True + gtk-edit - False - False - 3 + 1 + 1 - + True False - 2 - - - Keep - True - True - False - 0 - True - - - 0 - 1 - - - - - True - False - Flags: - 0 - - - 0 - 0 - - - - - Hide - True - True - False - 0 - True - - - 1 - 1 - - - - - Use PID's - True - True - False - 0 - True - - - 2 - 1 - - - - - New - True - True - False - 0 - True - - - 3 - 1 - - - - - True - False - - - 1 - 0 - - - - - True - False - - - 2 - 0 - - - - - True - False - - - 3 - 0 - - + Ssid - False - True - 4 + 2 + 0 - + True - False + True + 10 + 10 + gtk-edit - False - True - 5 + 2 + 1 - + True False - 2 - - - True - False - Reference: - 1 - - - False - True - 0 - - - - - True - True - 30 - - - False - False - 1 - - + Type - False - True - end - 8 + 3 + 0 + + + + + True + True + 10 + 10 + gtk-edit + + + 3 + 1 @@ -815,248 +317,707 @@ - + True False - vertical False - False - 10 + True 1 - + True False - vertical + 2 + 2 - + True False - Transponder data: - 0.0099999997764825821 + Reference - False - True - 0 + 0 + 0 - + + True + True + 30 + + + 0 + 1 + + + + + True + False + Extra + + + 1 + 0 + + + + + True + True + + + 1 + 1 + + + + + False + True + 2 + + + + + True + False + vertical + 2 + + + True + True + 4 + 5 + + + 8 + 1 + + + + + True + True + 4 + 4 + + + 7 + 1 + + + + + True + True + 4 + 4 + + + 6 + 1 + + + + + True + True + 4 + 4 + + + 5 + 1 + + + + + True + True + 4 + 4 + + + 4 + 1 + + + + + True + True + 4 + 4 + + + 3 + 1 + + + + + True + True + 4 + 4 + + + 2 + 1 + + + + + True + False + Teletext + + + 8 + 0 + + + + + True + False + HE-ACC + + + 7 + 0 + + + + + True + False + ACC + + + 6 + 0 + + + + + True + False + AC3+ + + + 5 + 0 + + + + + True + False + AC3 + + + 4 + 0 + + + + + True + False + MPEG + + + 3 + 0 + + + + + True + False + PCR + + + 2 + 0 + + + + + True + True + 4 + 4 + + + 1 + 1 + + + + + True + False + Video + + + 1 + 0 + + + + + True + False + start + PID's: + 6 + 6 + + + 0 + 1 + + + + + True + False + 25 + Delays (ms): + 10 + 10 + 0 + + + 9 + 1 + + + + + True + False + Bitstream + + + 10 + 0 + + + + + True + False + PCM + + + 11 + 0 + + + + + True + True + 5 + 5 + + + 10 + 1 + + + + + True + True + 3 + 6 + + + 11 + 1 + + + + + + + + + + + False + False + 3 + + + + + True + False + 2 + + + True + False + Flags: + 0 + + + 0 + 0 + + + + + Keep + True + True + False + 0 + True + + + 1 + 0 + + + + + Hide + True + True + False + 0 + True + + + 2 + 0 + + + + + Use PID's + True + True + False + 0 + True + + + 3 + 0 + + + + + New + True + True + False + 0 + True + + + 4 + 0 + + + + + False + True + 4 + + + + + False + True + 1 + + + + + True + False + + + False + True + 2 + + + + + True + False + vertical + 2 + + + True + False + Transponder data: + 0.0099999997764825821 + + + + False + True + 0 + + + + + True + False + 2 + + + True + False + Sat. pos. + + + 0 + 0 + + + + + True + False + sat_pos_list_store + + + + 0 + + + + + 0 + 1 + + + + + True + False + Freq. + + + 1 + 0 + + + + + True + True + 10 + 10 + + + 1 + 1 + + + + + True + False + Rate + + + 2 + 0 + + + + + True + True + 10 + 10 + + + 2 + 1 + + + + + True + False + Pol. + + + 3 + 0 + + + + + True + False + pol_list_store + + + + 0 + + + + + 3 + 1 + + + + + True + False + FEC: + + + 4 + 0 + + + + + True + False + fec_list_store + + + + 0 + + + + + 4 + 1 + + + + + True + False + Sys. + + + 5 + 0 + + + + + True + False + sys_list_store + + + + 0 + + + + + 5 + 1 + + + + + True + False + Mod. + + + 6 + 0 + + + + + True + False + mod_list_store + + + + 0 + + + + + 6 + 1 + + + + + True + True + + + 7 + 1 + + + + + True + False + Namespace + + + 7 + 0 + + + + + False + True + 1 + + + + + True + True + + True False - 2 2 - True - - - True - False - Sat. position: - 0 - - - 0 - 0 - - - - - True - False - Transponder ID: - 0 - - - 0 - 1 - - - - - True - False - Network ID: - 0 - - - 0 - 2 - - - - - True - True - 10 - 10 - - - 1 - 1 - - - - - True - True - 10 - 10 - - - 1 - 2 - - - - - True - True - 10 - 10 - - - 1 - 3 - - - - - True - False - Frequency: - 0 - - - 0 - 3 - - - - - True - False - Symbolrate: - 0 - - - 0 - 4 - - - - - True - True - 10 - 10 - - - 1 - 4 - - - - - True - False - System: - 0 - - - 0 - 7 - - - - - True - False - Polarisation: - 0 - - - 0 - 5 - - - - - True - False - FEC: - 0 - - - 0 - 6 - - - - - True - False - pol_list_store - - - - 0 - - - - - 1 - 5 - - - - - True - False - fec_list_store - - - - 0 - - - - - 1 - 6 - - - - - True - False - sys_list_store - - - - 0 - - - - - 1 - 7 - - True False - Flags: - 1 + Flags - 2 - 7 + 6 + 0 @@ -1067,80 +1028,20 @@ 10 - 3 - 7 - - - - - True - False - Stream ID: - 1 - - - 2 - 6 - - - - - True - True - 5 - 10 - - - 3 - 6 - - - - - True - False - PLS code: - 1 - - - 2 - 5 - - - - - True - True - 5 - 10 - - - 3 - 5 - - - - - True - False - PLS mode: - 1 - - - 2 - 4 + 6 + 1 True False - Pilot: + Pilot 1 - 2 - 3 + 5 + 0 @@ -1156,20 +1057,32 @@ - 3 - 3 + 5 + 1 True False - Rolloff: + R.off 1 - 2 - 2 + 4 + 0 + + + + + True + False + Inv. + 1 + + + 3 + 0 @@ -1185,19 +1098,7 @@ - 3 - 2 - - - - - True - False - Invertion: - 1 - - - 2 + 4 1 @@ -1219,87 +1120,10 @@ - + True False - Modulation: - 0 - - - 2 - 0 - - - - - True - False - mod_list_store - - - - 0 - - - - - 3 - 0 - - - - - True - False - sat_pos_list_store - - - - 0 - - - - - 1 - 0 - - - - - True - False - pls_mode_list_store - - - - 0 - - - - - 3 - 4 - - - - - False - False - 1 - - - - - True - False - 5 - 2 - - - True - False - Namespace: - 1 + Tr. ID 0 @@ -1307,22 +1131,71 @@ - + True True + 10 + 10 + + + 0 + 1 + + + + + True + False + Net. ID 1 0 + + + True + True + 10 + 10 + + + 1 + 1 + + + + + True + False + Stream ID + + + 2 + 0 + + + + + True + True + 5 + 10 + + + 2 + 1 + + + + + + + True + False + Extra: - - False - True - end - 2 - @@ -1335,7 +1208,7 @@ False True - 2 + 3 @@ -1346,7 +1219,7 @@ False True - 4 + 5 From 547046bddbaed0ee82b524af23b71f681e0af5ea Mon Sep 17 00:00:00 2001 From: DYefremov Date: Fri, 16 Feb 2018 01:56:28 +0300 Subject: [PATCH 12/15] service details dialog data filling --- app/ui/service_details_dialog.glade | 119 +++++++++++++++------------- app/ui/service_details_dialog.py | 76 +++++++++++++++--- 2 files changed, 127 insertions(+), 68 deletions(-) diff --git a/app/ui/service_details_dialog.glade b/app/ui/service_details_dialog.glade index ce5ce52a..f5220cc9 100644 --- a/app/ui/service_details_dialog.glade +++ b/app/ui/service_details_dialog.glade @@ -309,6 +309,27 @@ 1 + + + True + False + CA ID's + + + 4 + 0 + + + + + True + True + + + 4 + 1 + + False @@ -327,62 +348,6 @@ 1 - - - True - False - 2 - 2 - - - True - False - Reference - - - 0 - 0 - - - - - True - True - 30 - - - 0 - 1 - - - - - True - False - Extra - - - 1 - 0 - - - - - True - True - - - 1 - 1 - - - - - False - True - 2 - - True @@ -734,6 +699,45 @@ 0 + + + True + True + 30 + + + 7 + 0 + + + + + True + False + Reference: + + + 6 + 0 + + + + + True + False + + + True + False + + + + + + 5 + 0 + + False @@ -1224,5 +1228,8 @@ + + cancel_button + diff --git a/app/ui/service_details_dialog.py b/app/ui/service_details_dialog.py index e54ed063..a692dd0f 100644 --- a/app/ui/service_details_dialog.py +++ b/app/ui/service_details_dialog.py @@ -1,9 +1,24 @@ +from enum import Enum + from app.commons import run_idle from app.eparser import Service from app.ui.main_helper import get_base_model, is_only_one_item_selected from . import Gtk, UI_RESOURCES_PATH +class Pisd(Enum): + VIDEO = "c:00" + AUDIO = "c:01" + TELETEXT = "c:02" + PCR = "c:03" + AC3 = "c:04" + VIDEO_TYPE = "c:05" + AUDIO_CHANNEL = "c:06" + BIT_STREAM_DELAY = "c:07" # in ms + PCM_DELAY = "c:08" # in ms + SUBTITLE = "c:09" + + class ServiceDetailsDialog: def __init__(self, transient, options, view): builder = Gtk.Builder() @@ -18,7 +33,7 @@ class ServiceDetailsDialog: self._package_entry = builder.get_object("package_entry") self._id_entry = builder.get_object("id_entry") self._type_entry = builder.get_object("type_entry") - self._extra_entry = builder.get_object("extra_entry") + self._cas_entry = builder.get_object("cas_entry") self._bitstream_entry = builder.get_object("bitstream_entry") self._pcm_entry = builder.get_object("pcm_entry") self._reference_entry = builder.get_object("reference_entry") @@ -60,17 +75,54 @@ class ServiceDetailsDialog: model = get_base_model(model) if is_only_one_item_selected(paths, self._dialog): srv = Service(*model[paths][:]) - self._name_entry.set_text(srv.service) - self._package_entry.set_text(srv.package) - self._type_entry.set_text("") - self._id_entry.set_text(str(int(srv.ssid, 16))) - self._reference_entry.set_text(srv.picon_id.replace("_", ":").rstrip(".png")) - # transponder - self._freq_entry.set_text(srv.freq) - self._rate_entry.set_text(srv.rate) - self.select_active_text(self._pol_combo_box, srv.pol) - self.select_active_text(self._fec_combo_box, srv.fec) - self.select_active_text(self._sys_combo_box, srv.system) + self.init_service_data(srv) + self.init_transponder_data(srv) + + def init_service_data(self, srv): + """ Service data initialisation """ + self._name_entry.set_text(srv.service) + self._package_entry.set_text(srv.package) + self._type_entry.set_text(srv.service_type) + self._id_entry.set_text(str(int(srv.ssid, 16))) + flags = srv.flags_cas.split(",") + cas = list(filter(lambda x: x.startswith("C:"), flags)) + if cas: + self._cas_entry.set_text(",".join(cas)) + + pids = list(filter(lambda x: x.startswith("c:"), flags)) + if pids: + for pid in pids: + if pid.startswith(Pisd.VIDEO.value): + self._video_pid_entry.set_text(pid.strip(Pisd.VIDEO.value)) + elif pid.startswith(Pisd.AUDIO.value): + pass + elif pid.startswith(Pisd.TELETEXT.value): + self._teletext_pid_entry.set_text(pid.strip(Pisd.TELETEXT.value)) + elif pid.startswith(Pisd.PCR.value): + self._pcr_pid_entry.set_text(pid.strip(Pisd.PCR.value)) + elif pid.startswith(Pisd.AC3.value): + self._ac3_pid_entry.set_text(pid.strip(Pisd.AC3.value)) + elif pid.startswith(Pisd.VIDEO_TYPE.value): + # self._type_entry.set_text(pid.strip(Pisd.VIDEO_TYPE.value)) + pass + elif pid.startswith(Pisd.AUDIO_CHANNEL.value): + pass + elif pid.startswith(Pisd.BIT_STREAM_DELAY.value): + self._bitstream_entry.set_text(pid.strip(Pisd.BIT_STREAM_DELAY.value)) + elif pid.startswith(Pisd.PCM_DELAY.value): + self._pcm_entry.set_text(pid.strip(Pisd.PCM_DELAY.value)) + elif pid.startswith(Pisd.SUBTITLE.value): + pass + + self._reference_entry.set_text(srv.picon_id.replace("_", ":").rstrip(".png")) + + def init_transponder_data(self, srv): + """ Transponder data initialisation """ + self._freq_entry.set_text(srv.freq) + self._rate_entry.set_text(srv.rate) + self.select_active_text(self._pol_combo_box, srv.pol) + self.select_active_text(self._fec_combo_box, srv.fec) + self.select_active_text(self._sys_combo_box, srv.system) def select_active_text(self, box: Gtk.ComboBox, text): model = box.get_model() From 8c433680a9efae63b1f04a69bfd737a78cc1a8eb Mon Sep 17 00:00:00 2001 From: DYefremov Date: Fri, 16 Feb 2018 01:58:43 +0300 Subject: [PATCH 13/15] little refactoring ) --- app/ui/service_details_dialog.py | 34 ++++++++++++++++---------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/app/ui/service_details_dialog.py b/app/ui/service_details_dialog.py index a692dd0f..b0ff3d3b 100644 --- a/app/ui/service_details_dialog.py +++ b/app/ui/service_details_dialog.py @@ -6,7 +6,7 @@ from app.ui.main_helper import get_base_model, is_only_one_item_selected from . import Gtk, UI_RESOURCES_PATH -class Pisd(Enum): +class Pids(Enum): VIDEO = "c:00" AUDIO = "c:01" TELETEXT = "c:02" @@ -92,26 +92,26 @@ class ServiceDetailsDialog: pids = list(filter(lambda x: x.startswith("c:"), flags)) if pids: for pid in pids: - if pid.startswith(Pisd.VIDEO.value): - self._video_pid_entry.set_text(pid.strip(Pisd.VIDEO.value)) - elif pid.startswith(Pisd.AUDIO.value): + if pid.startswith(Pids.VIDEO.value): + self._video_pid_entry.set_text(pid.strip(Pids.VIDEO.value)) + elif pid.startswith(Pids.AUDIO.value): pass - elif pid.startswith(Pisd.TELETEXT.value): - self._teletext_pid_entry.set_text(pid.strip(Pisd.TELETEXT.value)) - elif pid.startswith(Pisd.PCR.value): - self._pcr_pid_entry.set_text(pid.strip(Pisd.PCR.value)) - elif pid.startswith(Pisd.AC3.value): - self._ac3_pid_entry.set_text(pid.strip(Pisd.AC3.value)) - elif pid.startswith(Pisd.VIDEO_TYPE.value): + elif pid.startswith(Pids.TELETEXT.value): + self._teletext_pid_entry.set_text(pid.strip(Pids.TELETEXT.value)) + elif pid.startswith(Pids.PCR.value): + self._pcr_pid_entry.set_text(pid.strip(Pids.PCR.value)) + elif pid.startswith(Pids.AC3.value): + self._ac3_pid_entry.set_text(pid.strip(Pids.AC3.value)) + elif pid.startswith(Pids.VIDEO_TYPE.value): # self._type_entry.set_text(pid.strip(Pisd.VIDEO_TYPE.value)) pass - elif pid.startswith(Pisd.AUDIO_CHANNEL.value): + elif pid.startswith(Pids.AUDIO_CHANNEL.value): pass - elif pid.startswith(Pisd.BIT_STREAM_DELAY.value): - self._bitstream_entry.set_text(pid.strip(Pisd.BIT_STREAM_DELAY.value)) - elif pid.startswith(Pisd.PCM_DELAY.value): - self._pcm_entry.set_text(pid.strip(Pisd.PCM_DELAY.value)) - elif pid.startswith(Pisd.SUBTITLE.value): + elif pid.startswith(Pids.BIT_STREAM_DELAY.value): + self._bitstream_entry.set_text(pid.strip(Pids.BIT_STREAM_DELAY.value)) + elif pid.startswith(Pids.PCM_DELAY.value): + self._pcm_entry.set_text(pid.strip(Pids.PCM_DELAY.value)) + elif pid.startswith(Pids.SUBTITLE.value): pass self._reference_entry.set_text(srv.picon_id.replace("_", ":").rstrip(".png")) From e26d08ca8ef826e71ac303b073a773eb4fc22f05 Mon Sep 17 00:00:00 2001 From: DYefremov Date: Fri, 16 Feb 2018 07:41:19 +0300 Subject: [PATCH 14/15] filling pids in service dialog --- app/ui/service_details_dialog.glade | 145 +++++++++++++++++----------- app/ui/service_details_dialog.py | 21 ++-- 2 files changed, 96 insertions(+), 70 deletions(-) diff --git a/app/ui/service_details_dialog.glade b/app/ui/service_details_dialog.glade index f5220cc9..671a9a45 100644 --- a/app/ui/service_details_dialog.glade +++ b/app/ui/service_details_dialog.glade @@ -124,6 +124,29 @@ + + + + + + + + TV + + + TV (HD) + + + TV (UHD) + + + Radio + + + Data + + + @@ -296,19 +319,6 @@ 0 - - - True - True - 10 - 10 - gtk-edit - - - 3 - 1 - - True @@ -330,6 +340,23 @@ 1 + + + True + False + srv_type_liststore + + + + 0 + + + + + 3 + 1 + + False @@ -414,30 +441,6 @@ 1 - - - True - True - 4 - 4 - - - 3 - 1 - - - - - True - True - 4 - 4 - - - 2 - 1 - - True @@ -493,28 +496,6 @@ 0 - - - True - False - MPEG - - - 3 - 0 - - - - - True - False - PCR - - - 2 - 0 - - True @@ -613,6 +594,52 @@ 1 + + + True + False + PCR + + + 3 + 0 + + + + + True + False + Audio + + + 2 + 0 + + + + + True + True + 4 + 4 + + + 3 + 1 + + + + + True + True + 4 + 4 + + + 2 + 1 + + diff --git a/app/ui/service_details_dialog.py b/app/ui/service_details_dialog.py index b0ff3d3b..e7bd3d4c 100644 --- a/app/ui/service_details_dialog.py +++ b/app/ui/service_details_dialog.py @@ -32,14 +32,14 @@ class ServiceDetailsDialog: self._name_entry = builder.get_object("name_entry") self._package_entry = builder.get_object("package_entry") self._id_entry = builder.get_object("id_entry") - self._type_entry = builder.get_object("type_entry") + self._service_type_combo_box = builder.get_object("service_type_combo_box") self._cas_entry = builder.get_object("cas_entry") self._bitstream_entry = builder.get_object("bitstream_entry") self._pcm_entry = builder.get_object("pcm_entry") self._reference_entry = builder.get_object("reference_entry") self._video_pid_entry = builder.get_object("video_pid_entry") self._pcr_pid_entry = builder.get_object("pcr_pid_entry") - self._mpeg_pid_entry = builder.get_object("mpeg_pid_entry") + self._audio_pid_entry = builder.get_object("audio_pid_entry") self._ac3_pid_entry = builder.get_object("ac3_pid_entry") self._ac3plus_pid_entry = builder.get_object("ac3plus_pid_entry") self._acc_pid_entry = builder.get_object("acc_pid_entry") @@ -72,7 +72,6 @@ class ServiceDetailsDialog: @run_idle def update_data_elements(self): model, paths = self._services_view.get_selection().get_selected_rows() - model = get_base_model(model) if is_only_one_item_selected(paths, self._dialog): srv = Service(*model[paths][:]) self.init_service_data(srv) @@ -82,7 +81,7 @@ class ServiceDetailsDialog: """ Service data initialisation """ self._name_entry.set_text(srv.service) self._package_entry.set_text(srv.package) - self._type_entry.set_text(srv.service_type) + self.select_active_text(self._service_type_combo_box, srv.service_type) self._id_entry.set_text(str(int(srv.ssid, 16))) flags = srv.flags_cas.split(",") cas = list(filter(lambda x: x.startswith("C:"), flags)) @@ -93,24 +92,24 @@ class ServiceDetailsDialog: if pids: for pid in pids: if pid.startswith(Pids.VIDEO.value): - self._video_pid_entry.set_text(pid.strip(Pids.VIDEO.value)) + self._video_pid_entry.set_text(str(int(pid.lstrip(Pids.VIDEO.value), 16))) elif pid.startswith(Pids.AUDIO.value): - pass + self._audio_pid_entry.set_text(str(int(pid.lstrip(Pids.AUDIO.value), 16))) elif pid.startswith(Pids.TELETEXT.value): - self._teletext_pid_entry.set_text(pid.strip(Pids.TELETEXT.value)) + self._teletext_pid_entry.set_text(str(int(pid.lstrip(Pids.TELETEXT.value), 16))) elif pid.startswith(Pids.PCR.value): - self._pcr_pid_entry.set_text(pid.strip(Pids.PCR.value)) + self._pcr_pid_entry.set_text(str(int(pid.lstrip(Pids.PCR.value), 16))) elif pid.startswith(Pids.AC3.value): - self._ac3_pid_entry.set_text(pid.strip(Pids.AC3.value)) + self._ac3_pid_entry.set_text(str(int(pid.lstrip(Pids.AC3.value), 16))) elif pid.startswith(Pids.VIDEO_TYPE.value): # self._type_entry.set_text(pid.strip(Pisd.VIDEO_TYPE.value)) pass elif pid.startswith(Pids.AUDIO_CHANNEL.value): pass elif pid.startswith(Pids.BIT_STREAM_DELAY.value): - self._bitstream_entry.set_text(pid.strip(Pids.BIT_STREAM_DELAY.value)) + self._bitstream_entry.set_text(str(int(pid.lstrip(Pids.BIT_STREAM_DELAY.value), 16))) elif pid.startswith(Pids.PCM_DELAY.value): - self._pcm_entry.set_text(pid.strip(Pids.PCM_DELAY.value)) + self._pcm_entry.set_text(str(int(pid.lstrip(Pids.PCM_DELAY.value), 16))) elif pid.startswith(Pids.SUBTITLE.value): pass From 074fc960e556c201be89545eee213e73e8d5a17e Mon Sep 17 00:00:00 2001 From: Dmitriy Yefremov Date: Sat, 17 Feb 2018 16:23:41 +0300 Subject: [PATCH 15/15] skeleton implementation of service details show --- README.md | 7 +- app/eparser/ecommons.py | 16 +- app/eparser/enigma/lamedb.py | 6 +- app/ui/main_app_window.py | 23 +- app/ui/main_helper.py | 14 +- app/ui/main_window.glade | 4 +- app/ui/service_details_dialog.glade | 323 ++++++++++++++++++---------- app/ui/service_details_dialog.py | 94 +++++--- 8 files changed, 325 insertions(+), 162 deletions(-) diff --git a/README.md b/README.md index c0ca408d..632309e0 100644 --- a/README.md +++ b/README.md @@ -9,15 +9,16 @@ Ctrl + X, C, V, Up, Down, PageUp, PageDown, S, T, E, L, H, Space; Insert, Delete Insert - copies the selected channels from the main list to the bouquet or inserts (creates) a new bouquet. Ctrl + X - only in bouquet list. Ctrl + C - only in services list. Clipboard is "rubber". There is an accumulation before the insertion! -Ctrl + E, F2 - edit/rename. +Ctrl + E, F2 - edit. +Ctrl + R - rename. Ctrl + S, T, E in Satellites edit tool for create and edit satellite or transponder. Ctrl + L - parental lock. Ctrl + H - hide/skip. Left/Right - remove selection. ### Extra: -Multiple selections in lists only with Space key (as in file managers)! -Ability to import IPTV into bouquet (Neutrino WEBTV) from m3u files! +Multiple selections in lists only with Space key (as in file managers). +Ability to import IPTV into bouquet (Neutrino WEBTV) from m3u files. Tool for downloading picons from lyngsat.com. ### Minimum requirements: Python >= 3.5.2 and GTK+ 3 with PyGObject bindings. diff --git a/app/eparser/ecommons.py b/app/eparser/ecommons.py index a593d947..0755c1c5 100644 --- a/app/eparser/ecommons.py +++ b/app/eparser/ecommons.py @@ -34,7 +34,7 @@ class Type(Enum): Cable = "c" -class FLAG(Enum): +class Flag(Enum): """ Service flags """ KEEP = 1 # Do not automatically update the services parameters. HIDE = 2 @@ -47,6 +47,20 @@ class FLAG(Enum): return 2, 3, 6, 7, 10, 42, 43, 46, 47 +class Inversion(Enum): + Off = "0" + On = "1" + Auto = "2" + + +class Pilot(Enum): + Off = "0" + On = "1" + Auto = "2" + + +ROLL_OFF = {"0": "35%", "1": "25%", "2": "20%", "3": "Auto"} + POLARIZATION = {"0": "H", "1": "V", "2": "L", "3": "R"} PLS_MODE = {"0": "Root", "1": "Gold", "2": "Combo"} diff --git a/app/eparser/enigma/lamedb.py b/app/eparser/enigma/lamedb.py index 2ff88f0c..a4054c2c 100644 --- a/app/eparser/enigma/lamedb.py +++ b/app/eparser/enigma/lamedb.py @@ -6,7 +6,7 @@ from app.commons import log from app.ui import CODED_ICON, LOCKED_ICON, HIDE_ICON from .blacklist import get_blacklist -from ..ecommons import Service, POLARIZATION, SYSTEM, FEC, SERVICE_TYPE, FLAG +from ..ecommons import Service, POLARIZATION, SYSTEM, FEC, SERVICE_TYPE, Flag _HEADER = "eDVB services /4/" _SEP = ":" # separator @@ -101,7 +101,7 @@ def parse_services(services, transponders, path): all_flags = ch[2].split(",") coded = CODED_ICON if list(filter(lambda x: x.startswith("C:"), all_flags)) else None flags = list(filter(lambda x: x.startswith("f:"), all_flags)) - hide = HIDE_ICON if flags and int(flags[0][2:]) in FLAG.hide_values() else None + hide = HIDE_ICON if flags and int(flags[0][2:]) in Flag.hide_values() else None locked = LOCKED_ICON if fav_id in blacklist else None package = list(filter(lambda x: x.startswith("p:"), all_flags)) @@ -128,7 +128,7 @@ def parse_services(services, transponders, path): rate=tr[1], pol=POLARIZATION[tr[2]], fec=FEC[tr[3]], - system=SYSTEM[tr[6]], + system="DVB-S2" if len(tr) > 7 else "DVB-S", pos="{}.{}".format(tr[4][:-1], tr[4][-1:]), data_id=ch[0], fav_id=fav_id, diff --git a/app/ui/main_app_window.py b/app/ui/main_app_window.py index addf7a60..c71947f5 100644 --- a/app/ui/main_app_window.py +++ b/app/ui/main_app_window.py @@ -7,7 +7,7 @@ import shutil from app.commons import run_idle, log from app.eparser import get_blacklist, write_blacklist, parse_m3u from app.eparser import get_services, get_bouquets, write_bouquets, write_services, Bouquets, Bouquet, Service -from app.eparser.ecommons import CAS, FLAG +from app.eparser.ecommons import CAS, Flag from app.eparser.enigma.bouquets import BqServiceType from app.eparser.neutrino.bouquets import BqType from app.properties import get_config, write_config, Profile @@ -708,8 +708,15 @@ class MainAppWindow: self.on_locked(None) elif ctrl and key == Gdk.KEY_h or key == Gdk.KEY_H: self.on_hide(None) - elif ctrl and key == Gdk.KEY_E or key == Gdk.KEY_e or key == Gdk.KEY_F2: + elif ctrl and key == Gdk.KEY_R or key == Gdk.KEY_r: self.on_edit(view) + elif ctrl and key == Gdk.KEY_E or key == Gdk.KEY_e or key == Gdk.KEY_F2: + if model_name == self._BOUQUETS_LIST_NAME: + self.on_edit(view) + return + elif model_name == self._FAV_LIST_NAME: + self.on_locate_in_services(view) + self.on_services_data_edit(view) elif key == Gdk.KEY_Left or key == Gdk.KEY_Right: view.do_unselect_all(view) @@ -763,10 +770,10 @@ class MainAppWindow: self.__tool_elements[elem].set_sensitive(not_empty) def on_hide(self, item): - self.set_service_flags(FLAG.HIDE) + self.set_service_flags(Flag.HIDE) def on_locked(self, item): - self.set_service_flags(FLAG.LOCK) + self.set_service_flags(Flag.LOCK) def set_service_flags(self, flag): profile = Profile(self.__profile) @@ -779,9 +786,9 @@ class MainAppWindow: elif profile is Profile.NEUTRINO_MP: if bq_selected: model, path = self.__bouquets_view.get_selection().get_selected() - value = model.get_value(path, 1 if flag is FLAG.LOCK else 2) - value = None if value else LOCKED_ICON if flag is FLAG.LOCK else HIDE_ICON - model.set_value(path, 1 if flag is FLAG.LOCK else 2, value) + value = model.get_value(path, 1 if flag is Flag.LOCK else 2) + value = None if value else LOCKED_ICON if flag is Flag.LOCK else HIDE_ICON + model.set_value(path, 1 if flag is Flag.LOCK else 2, value) @run_idle def on_model_changed(self, model, path, itr=None): @@ -891,7 +898,7 @@ class MainAppWindow: @run_idle def on_services_data_edit(self, item): - dialog = ServiceDetailsDialog(self.__main_window, Profile(self.__profile), self.__services_view) + dialog = ServiceDetailsDialog(self.__main_window, self.__options, self.__services_view) dialog.show() @run_idle diff --git a/app/ui/main_helper.py b/app/ui/main_helper.py index a7cf51a2..c4dd2546 100644 --- a/app/ui/main_helper.py +++ b/app/ui/main_helper.py @@ -7,7 +7,7 @@ import shutil from gi.repository import GdkPixbuf from app.eparser import Service -from app.eparser.ecommons import FLAG +from app.eparser.ecommons import Flag from app.eparser.enigma.bouquets import BqServiceType, to_bouquet_id from . import Gtk, Gdk, HIDE_ICON, LOCKED_ICON from .dialogs import show_dialog, DialogType, get_chooser_dialog @@ -166,7 +166,7 @@ def set_flags(flag, services_view, fav_view, channels, blacklist): model = get_base_model(model) - if flag is FLAG.HIDE: + if flag is Flag.HIDE: if target is ViewTarget.SERVICES: set_hide(channels, model, paths) else: @@ -174,7 +174,7 @@ def set_flags(flag, services_view, fav_view, channels, blacklist): srv_model = get_base_model(services_view.get_model()) srv_paths = [row.path for row in srv_model if row[18] in fav_ids] set_hide(channels, srv_model, srv_paths) - elif flag is FLAG.LOCK: + elif flag is Flag.LOCK: set_lock(blacklist, channels, model, paths, target, services_model=get_base_model(services_view.get_model())) return True @@ -223,13 +223,13 @@ def set_hide(channels, model, paths): value = int(flag[2:]) if flag else 0 if not hide: - if value in FLAG.hide_values(): + if value in Flag.hide_values(): continue # skip if already hidden - value += FLAG.HIDE.value + value += Flag.HIDE.value else: - if value not in FLAG.hide_values(): + if value not in Flag.hide_values(): continue # skip if already allowed to show - value -= FLAG.HIDE.value + value -= Flag.HIDE.value if value == 0 and index is not None: del flags[index] diff --git a/app/ui/main_window.glade b/app/ui/main_window.glade index fbb278d6..4ee4efb4 100644 --- a/app/ui/main_window.glade +++ b/app/ui/main_window.glade @@ -380,12 +380,12 @@ - Edit data/new service + Show details/edit True False image17 False - + diff --git a/app/ui/service_details_dialog.glade b/app/ui/service_details_dialog.glade index 671a9a45..6822f3eb 100644 --- a/app/ui/service_details_dialog.glade +++ b/app/ui/service_details_dialog.glade @@ -45,6 +45,17 @@ + + + Off + + + On + + + Auto + + @@ -74,6 +85,17 @@ + + + Off + + + On + + + Auto + + @@ -117,6 +139,20 @@ + + + 35% + + + 25% + + + 20% + + + Auto + + @@ -730,6 +766,7 @@ True True + False 30 @@ -963,6 +1000,7 @@ True False sys_list_store + @@ -989,7 +1027,9 @@ True + False False + 0.98999999999999999 mod_list_store @@ -1040,116 +1080,6 @@ True False 2 - - - True - False - Flags - - - 6 - 0 - - - - - True - True - 5 - 10 - - - 6 - 1 - - - - - True - False - Pilot - 1 - - - 5 - 0 - - - - - True - False - pilot_list_store - - - - 0 - - - - - 5 - 1 - - - - - True - False - R.off - 1 - - - 4 - 0 - - - - - True - False - Inv. - 1 - - - 3 - 0 - - - - - True - False - rolloff_list_store - - - - 0 - - - - - 4 - 1 - - - - - True - False - invertion_list_store - - - - 0 - - - - - 3 - 1 - - True @@ -1197,10 +1127,10 @@ - + True False - Stream ID + Inversion 2 @@ -1208,14 +1138,179 @@ - + True + False + Rolloff + 0.02 + + + 3 + 0 + + + + + True + False + Pilot + + + 4 + 0 + + + + + True + False + invertion_list_store + + + + 0 + + + + + 2 + 1 + + + + + True + False + False + rolloff_list_store + + + + 0 + + + + + 3 + 1 + + + + + True + False + False + pilot_list_store + + + + 0 + + + + + 4 + 1 + + + + + True + False + PLS mode + + + 5 + 0 + + + + + True + False + False + pls_mode_list_store + + + + 0 + + + + + 5 + 1 + + + + + True + False + Flags + + + 8 + 0 + + + + + True + False True 5 10 - 2 + 8 + 1 + + + + + True + False + PLS code + + + 6 + 0 + + + + + True + False + True + 7 + 10 + + + 6 + 1 + + + + + True + False + Stream ID + + + 7 + 0 + + + + + True + False + True + 5 + 10 + + + 7 1 diff --git a/app/ui/service_details_dialog.py b/app/ui/service_details_dialog.py index e7bd3d4c..766c0679 100644 --- a/app/ui/service_details_dialog.py +++ b/app/ui/service_details_dialog.py @@ -1,9 +1,12 @@ from enum import Enum +from functools import lru_cache from app.commons import run_idle -from app.eparser import Service -from app.ui.main_helper import get_base_model, is_only_one_item_selected +from app.eparser import Service, get_satellites +from app.eparser.ecommons import MODULATION, Inversion, ROLL_OFF, Pilot +from app.properties import Profile from . import Gtk, UI_RESOURCES_PATH +from .main_helper import is_only_one_item_selected class Pids(Enum): @@ -19,14 +22,23 @@ class Pids(Enum): SUBTITLE = "c:09" +@lru_cache(maxsize=1) +def get_sat_positions(path): + return ["{:.1f}".format(float(x.position) / 10) for x in get_satellites(path)] + + class ServiceDetailsDialog: def __init__(self, transient, options, view): + handlers = {"on_system_changed": self.on_system_changed} + builder = Gtk.Builder() builder.add_from_file(UI_RESOURCES_PATH + "service_details_dialog.glade") + builder.connect_signals(handlers) self._dialog = builder.get_object("service_details_dialog") self._dialog.set_transient_for(transient) - self._options = options + self._profile = Profile(options["profile"]) + self._satellites_xml_path = options.get(self._profile.value)["data_dir_path"] + "satellites.xml" self._services_view = view # Service elements self._name_entry = builder.get_object("name_entry") @@ -67,6 +79,8 @@ class ServiceDetailsDialog: self._stream_id_entry = builder.get_object("stream_id_entry") self._flags_entry = builder.get_object("flags_entry") self._namespace_entry = builder.get_object("namespace_entry") + self._DVB_S2_ELEMENTS = (self._mod_combo_box, self._rolloff_combo_box, self._pilot_combo_box, + self._pls_mode_combo_box, self._pls_code_entry, self._stream_id_entry) self.update_data_elements() @run_idle @@ -74,16 +88,28 @@ class ServiceDetailsDialog: model, paths = self._services_view.get_selection().get_selected_rows() if is_only_one_item_selected(paths, self._dialog): srv = Service(*model[paths][:]) - self.init_service_data(srv) - self.init_transponder_data(srv) + # Service + self._name_entry.set_text(srv.service) + self._package_entry.set_text(srv.package) + self.select_active_text(self._service_type_combo_box, srv.service_type) + self._id_entry.set_text(str(int(srv.ssid, 16))) + # Transponder + self._freq_entry.set_text(srv.freq) + self._rate_entry.set_text(srv.rate) + self.select_active_text(self._pol_combo_box, srv.pol) + self.select_active_text(self._fec_combo_box, srv.fec) + self.select_active_text(self._sys_combo_box, srv.system) + self.set_sat_positions(srv.pos) - def init_service_data(self, srv): + if self._profile is Profile.ENIGMA_2: + self.init_enigma2_service_data(srv) + self.init_enigma2_transponder_data(srv) + + @run_idle + def init_enigma2_service_data(self, srv): """ Service data initialisation """ - self._name_entry.set_text(srv.service) - self._package_entry.set_text(srv.package) - self.select_active_text(self._service_type_combo_box, srv.service_type) - self._id_entry.set_text(str(int(srv.ssid, 16))) flags = srv.flags_cas.split(",") + cas = list(filter(lambda x: x.startswith("C:"), flags)) if cas: self._cas_entry.set_text(",".join(cas)) @@ -92,36 +118,44 @@ class ServiceDetailsDialog: if pids: for pid in pids: if pid.startswith(Pids.VIDEO.value): - self._video_pid_entry.set_text(str(int(pid.lstrip(Pids.VIDEO.value), 16))) + self._video_pid_entry.set_text(str(int(pid[4:], 16))) elif pid.startswith(Pids.AUDIO.value): - self._audio_pid_entry.set_text(str(int(pid.lstrip(Pids.AUDIO.value), 16))) + self._audio_pid_entry.set_text(str(int(pid[4:], 16))) elif pid.startswith(Pids.TELETEXT.value): - self._teletext_pid_entry.set_text(str(int(pid.lstrip(Pids.TELETEXT.value), 16))) + self._teletext_pid_entry.set_text(str(int(pid[4:], 16))) elif pid.startswith(Pids.PCR.value): - self._pcr_pid_entry.set_text(str(int(pid.lstrip(Pids.PCR.value), 16))) + self._pcr_pid_entry.set_text(str(int(pid[4:], 16))) elif pid.startswith(Pids.AC3.value): - self._ac3_pid_entry.set_text(str(int(pid.lstrip(Pids.AC3.value), 16))) + self._ac3_pid_entry.set_text(str(int(pid[4:], 16))) elif pid.startswith(Pids.VIDEO_TYPE.value): - # self._type_entry.set_text(pid.strip(Pisd.VIDEO_TYPE.value)) pass elif pid.startswith(Pids.AUDIO_CHANNEL.value): pass elif pid.startswith(Pids.BIT_STREAM_DELAY.value): - self._bitstream_entry.set_text(str(int(pid.lstrip(Pids.BIT_STREAM_DELAY.value), 16))) + self._bitstream_entry.set_text(str(int(pid[4:], 16))) elif pid.startswith(Pids.PCM_DELAY.value): - self._pcm_entry.set_text(str(int(pid.lstrip(Pids.PCM_DELAY.value), 16))) + self._pcm_entry.set_text(str(int(pid[4:], 16))) elif pid.startswith(Pids.SUBTITLE.value): pass self._reference_entry.set_text(srv.picon_id.replace("_", ":").rstrip(".png")) - def init_transponder_data(self, srv): + @run_idle + def init_enigma2_transponder_data(self, srv): """ Transponder data initialisation """ - self._freq_entry.set_text(srv.freq) - self._rate_entry.set_text(srv.rate) - self.select_active_text(self._pol_combo_box, srv.pol) - self.select_active_text(self._fec_combo_box, srv.fec) - self.select_active_text(self._sys_combo_box, srv.system) + data = srv.data_id.split(":") + tr_data = srv.transponder.split(":") + + if srv.system == "DVB-S2": + self.select_active_text(self._mod_combo_box, MODULATION.get(tr_data[8])) + self.select_active_text(self._rolloff_combo_box, ROLL_OFF.get(tr_data[9])) + self.select_active_text(self._pilot_combo_box, Pilot(tr_data[10]).name) + + self._namespace_entry.set_text(str(int(data[1], 16))) + self._transponder_id_entry.set_text(str(int(data[2], 16))) + self._network_id_entry.set_text(str(int(data[3], 16))) + self.select_active_text(self._invertion_combo_box, Inversion(tr_data[5]).name) + self._flags_entry.set_text(tr_data[6]) def select_active_text(self, box: Gtk.ComboBox, text): model = box.get_model() @@ -130,6 +164,18 @@ class ServiceDetailsDialog: box.set_active(index) break + @run_idle + def set_sat_positions(self, sat_pos): + model = self._sat_pos_combo_box.get_model() + positions = get_sat_positions(self._satellites_xml_path) + for pos in positions: + model.append((pos,)) + self.select_active_text(self._sat_pos_combo_box, sat_pos) + + def on_system_changed(self, box): + for elem in self._DVB_S2_ELEMENTS: + elem.set_sensitive(box.get_active()) + def show(self): response = self._dialog.run() if response == Gtk.ResponseType.OK: