diff --git a/app/eparser/ecommons.py b/app/eparser/ecommons.py index 026ae2ea..eadaaebc 100644 --- a/app/eparser/ecommons.py +++ b/app/eparser/ecommons.py @@ -65,6 +65,19 @@ class Flag(Enum): return value & 1 << 5 +class Pids(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 Inversion(Enum): Off = "0" On = "1" diff --git a/app/ui/main_app_window.py b/app/ui/main_app_window.py index 47f5cea4..100a60eb 100644 --- a/app/ui/main_app_window.py +++ b/app/ui/main_app_window.py @@ -907,7 +907,12 @@ class MainAppWindow: if is_only_one_item_selected(paths, self.__main_window): model_name = get_base_model(model).get_name() if model_name == self._FAV_LIST_NAME: + srv_type = model.get_value(model.get_iter(paths), 5) + if srv_type == BqServiceType.IPTV.name or srv_type == BqServiceType.MARKER.name: + self.on_rename(view) + return self.on_locate_in_services(view) + dialog = ServiceDetailsDialog(self.__main_window, self.__options, self.__services_view) dialog.show() diff --git a/app/ui/main_helper.py b/app/ui/main_helper.py index 99c8a705..379ade6e 100644 --- a/app/ui/main_helper.py +++ b/app/ui/main_helper.py @@ -234,7 +234,7 @@ def set_hide(channels, model, paths): if value == 0 and index is not None: del flags[index] else: - value = "f:{}".format(value) if value > 10 else "f:0{}".format(value) + value = "f:{:02d}".format(value) if index is not None: flags[index] = value else: diff --git a/app/ui/service_details_dialog.glade b/app/ui/service_details_dialog.glade index 53eb6739..4298b474 100644 --- a/app/ui/service_details_dialog.glade +++ b/app/ui/service_details_dialog.glade @@ -357,9 +357,9 @@ True True - 10 - 10 - gtk-edit + 8 + 8 + 2 @@ -392,6 +392,8 @@ True True + 19 + 19 4 @@ -446,6 +448,7 @@ True 4 5 + 8 @@ -455,9 +458,11 @@ True + False True 4 4 + 7 @@ -467,9 +472,11 @@ True + False True 4 4 + 6 @@ -479,9 +486,11 @@ True + False True 4 4 + 5 @@ -494,6 +503,7 @@ True 4 4 + 4 @@ -561,6 +571,7 @@ True 4 4 + 1 @@ -598,8 +609,8 @@ False 25 Delays (ms): - 10 - 10 + 11 + 11 0 @@ -633,8 +644,9 @@ True True - 5 - 5 + 6 + 6 + 10 @@ -647,6 +659,7 @@ True 3 6 + 11 @@ -681,6 +694,7 @@ True 4 4 + 3 @@ -693,6 +707,7 @@ True 4 4 + 2 @@ -790,7 +805,8 @@ True True False - 30 + 26 + 26 7 @@ -888,24 +904,6 @@ 0 - - - True - False - sat_pos_list_store - 0 - - - - 0 - - - - - 0 - 1 - - True @@ -923,6 +921,7 @@ True 10 10 + 1 @@ -946,6 +945,7 @@ True 10 10 + 2 @@ -1075,6 +1075,9 @@ True True + 15 + 15 + 7 @@ -1092,6 +1095,24 @@ 0 + + + True + False + sat_pos_list_store + 0 + + + + 0 + + + + + 0 + 1 + + False @@ -1123,8 +1144,9 @@ True True - 10 - 10 + 5 + 8 + 0 @@ -1146,8 +1168,9 @@ True True - 10 - 10 + 7 + 8 + 1 @@ -1290,8 +1313,9 @@ True False True - 5 - 10 + 7 + 7 + 8 @@ -1315,7 +1339,8 @@ False True 7 - 10 + 8 + 6 @@ -1338,8 +1363,9 @@ True False True - 5 - 10 + 7 + 8 + 7 diff --git a/app/ui/service_details_dialog.py b/app/ui/service_details_dialog.py index a565fc77..74a25eca 100644 --- a/app/ui/service_details_dialog.py +++ b/app/ui/service_details_dialog.py @@ -1,26 +1,13 @@ -from enum import Enum +import re from functools import lru_cache from app.commons import run_idle from app.eparser import Service, get_satellites -from app.eparser.ecommons import MODULATION, Inversion, ROLL_OFF, Pilot, Flag +from app.eparser.ecommons import MODULATION, Inversion, ROLL_OFF, Pilot, Flag, Pids from app.properties import Profile from app.ui.dialogs import show_dialog, DialogType -from . import Gtk, UI_RESOURCES_PATH -from .main_helper import is_only_one_item_selected - - -class Pids(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" +from app.ui.main_helper import get_base_model +from . import Gtk, Gdk, UI_RESOURCES_PATH @lru_cache(maxsize=1) @@ -29,10 +16,17 @@ def get_sat_positions(path): class ServiceDetailsDialog: + + _DIGIT_ENTRY_ELEMENTS = ("id_entry", "bitstream_entry", "pcm_entry", "video_pid_entry", "pcr_pid_entry", + "audio_pid_entry", "ac3_pid_entry", "ac3plus_pid_entry", "acc_pid_entry", "freq_entry", + "he_acc_pid_entry", "teletext_pid_entry", "transponder_id_entry", "network_id_entry", + "rate_entry", "pls_code_entry", "stream_id_entry", "flags_entry", "namespace_entry") + def __init__(self, transient, options, view): handlers = {"on_system_changed": self.on_system_changed, "on_save": self.on_save, - "on_create_new": self.on_create_new} + "on_create_new": self.on_create_new, + "on_digit_entry_changed": self.on_digit_entry_changed} builder = Gtk.Builder() builder.add_from_file(UI_RESOURCES_PATH + "service_details_dialog.glade") @@ -44,33 +38,47 @@ class ServiceDetailsDialog: self._satellites_xml_path = options.get(self._profile.value)["data_dir_path"] + "satellites.xml" self._services_view = view self._old_service = None + self._current_model = None + self._pattern = re.compile("\D") + # style + self._style_provider = Gtk.CssProvider() + self._style_provider.load_from_path(UI_RESOURCES_PATH + "style.css") + # initialize only digit elements + self._digit_elements = {k: builder.get_object(k) for k in self._DIGIT_ENTRY_ELEMENTS} + for elem in self._digit_elements.values(): + elem.get_style_context().add_provider_for_screen(Gdk.Screen.get_default(), self._style_provider, + Gtk.STYLE_PROVIDER_PRIORITY_USER) + self._id_entry = self._digit_elements.get("id_entry") + self._bitstream_entry = self._digit_elements.get("bitstream_entry") + self._pcm_entry = self._digit_elements.get("pcm_entry") + self._video_pid_entry = self._digit_elements.get("video_pid_entry") + self._pcr_pid_entry = self._digit_elements.get("pcr_pid_entry") + self._audio_pid_entry = self._digit_elements.get("audio_pid_entry") + self._ac3_pid_entry = self._digit_elements.get("ac3_pid_entry") + self._ac3plus_pid_entry = self._digit_elements.get("ac3plus_pid_entry") + self._acc_pid_entry = self._digit_elements.get("acc_pid_entry") + self._he_acc_pid_entry = self._digit_elements.get("he_acc_pid_entry") + self._teletext_pid_entry = self._digit_elements.get("teletext_pid_entry") + self._transponder_id_entry = self._digit_elements.get("transponder_id_entry") + self._network_id_entry = self._digit_elements.get("network_id_entry") + self._freq_entry = self._digit_elements.get("freq_entry") + self._rate_entry = self._digit_elements.get("rate_entry") + self._pls_code_entry = self._digit_elements.get("pls_code_entry") + self._stream_id_entry = self._digit_elements.get("stream_id_entry") + self._flags_entry = self._digit_elements.get("flags_entry") + self._namespace_entry = self._digit_elements.get("namespace_entry") # 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._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._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") - 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") @@ -79,12 +87,10 @@ class ServiceDetailsDialog: 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._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 @@ -92,6 +98,7 @@ class ServiceDetailsDialog: model, paths = self._services_view.get_selection().get_selected_rows() srv = Service(*model[paths][:]) self._old_service = srv + self._current_model = get_base_model(model) # Service self._name_entry.set_text(srv.service) self._package_entry.set_text(srv.package) @@ -112,20 +119,30 @@ class ServiceDetailsDialog: @run_idle def init_enigma2_service_data(self, srv): """ Service data initialisation """ - flags = srv.flags_cas.split(",") - - cas = list(filter(lambda x: x.startswith("C:"), flags)) - if cas: - self._cas_entry.set_text(",".join(cas)) - - flags = list(filter(lambda x: x.startswith("f:"), flags)) + flags = srv.flags_cas if flags: - value = int(flags[0][2:]) + flags = flags.split(",") + self.init_enigma2_flags(flags) + self.init_enigma2_pids(flags) + self.init_enigma2_cas(flags) + + self._reference_entry.set_text(srv.picon_id.replace("_", ":").rstrip(".png")) + + def init_enigma2_flags(self, flags): + f_flags = list(filter(lambda x: x.startswith("f:"), flags)) + if f_flags: + value = int(f_flags[0][2:]) self._keep_check_button.set_active(Flag.is_keep(value)) self._hide_check_button.set_active(Flag.is_hide(value)) self._use_pids_check_button.set_active(Flag.is_pids(value)) self._new_check_button.set_active(Flag.is_new(value)) + def init_enigma2_cas(self, flags): + cas = list(filter(lambda x: x.startswith("C:"), flags)) + if cas: + self._cas_entry.set_text(",".join(cas)) + + def init_enigma2_pids(self, flags): pids = list(filter(lambda x: x.startswith("c:"), flags)) if pids: for pid in pids: @@ -150,8 +167,6 @@ class ServiceDetailsDialog: elif pid.startswith(Pids.SUBTITLE.value): pass - self._reference_entry.set_text(srv.picon_id.replace("_", ":").rstrip(".png")) - @run_idle def init_enigma2_transponder_data(self, srv): """ Transponder data initialisation """ @@ -187,6 +202,10 @@ class ServiceDetailsDialog: def on_system_changed(self, box): for elem in self._DVB_S2_ELEMENTS: elem.set_sensitive(box.get_active()) + self._pls_code_entry.set_name("GtkEntry") + self._pls_code_entry.set_text("") + self._stream_id_entry.set_name("GtkEntry") + self._stream_id_entry.set_text("") def show(self): response = self._dialog.run() @@ -200,27 +219,33 @@ class ServiceDetailsDialog: if show_dialog(DialogType.QUESTION, self._dialog) == Gtk.ResponseType.CANCEL: return - srv = Service(flags_cas=self.get_flags(), - transponder_type="s", - coded=None, - service=self._name_entry.get_text(), - locked=self._old_service.locked, - hide=None, - package=self._package_entry.get_text(), - service_type=self._service_type_combo_box.get_active_id(), - picon=self._old_service.picon, - picon_id=self._old_service.picon_id, - ssid=self._id_entry.get_text(), - freq=self._freq_entry.get_text(), - rate=self._rate_entry.get_text(), - pol=self._pol_combo_box.get_active_id(), - fec=self._fec_combo_box.get_active_id(), - system=self._sys_combo_box.get_active_id(), - pos=self._pol_combo_box.get_active_id(), - data_id=self.get_data_id(), - fav_id=self.get_fav_id(), - transponder=self.get_transponder_data()) - show_dialog(DialogType.ERROR, transient=self._dialog, text="Not implemented yet!") + self.update_data_in_model(Service(flags_cas=self.get_flags(), + transponder_type="s", + coded=None, + service=self._name_entry.get_text(), + locked=self._old_service.locked, + hide=None, + package=self._package_entry.get_text(), + service_type=self._service_type_combo_box.get_active_id(), + picon=self._old_service.picon, + picon_id=self._old_service.picon_id, + ssid=self._id_entry.get_text(), + freq=self._freq_entry.get_text(), + rate=self._rate_entry.get_text(), + pol=self._pol_combo_box.get_active_id(), + fec=self._fec_combo_box.get_active_id(), + system=self._sys_combo_box.get_active_id(), + pos=self._sat_pos_combo_box.get_active_id(), + data_id=self.get_data_id(), + fav_id=self.get_fav_id(), + transponder=self.get_transponder_data())) + + def update_data_in_model(self, srv: Service): + fav_id = self._old_service.fav_id + for row in get_base_model(self._current_model): + if row[18] == fav_id: + self._current_model.set(self._current_model.get_iter(row.path), {i: v for i, v in enumerate(srv)}) + break def on_create_new(self, item): if show_dialog(DialogType.QUESTION, self._dialog) == Gtk.ResponseType.CANCEL: @@ -230,28 +255,69 @@ class ServiceDetailsDialog: def get_flags(self): if self._profile is Profile.ENIGMA_2: - pass + return self.get_enigma2_flags() elif self._profile is Profile.NEUTRINO_MP: - pass + return self._old_service.flags_cas + + def get_enigma2_flags(self): + flags = [] + # cas + cas = self._cas_entry.get_text() + if cas: + flags.append(cas) + # pids + video_pid = self._video_pid_entry.get_text() + if video_pid: + flags.append("{}{:04x}".format(Pids.VIDEO.value, int(video_pid))) + audio_pid = self._audio_pid_entry.get_text() + if audio_pid: + flags.append("{}{:04x}".format(Pids.AUDIO.value, int(audio_pid))) + teletext_pid = self._teletext_pid_entry.get_text() + if teletext_pid: + flags.append("{}{:04x}".format(Pids.TELETEXT.value, int(teletext_pid))) + pcr_pid = self._pcr_pid_entry.get_text() + if pcr_pid: + flags.append("{}{:04x}".format(Pids.PCR.value, int(pcr_pid))) + ac3_pid = self._ac3_pid_entry.get_text() + if ac3_pid: + flags.append("{}{:04x}".format(Pids.AC3.value, int(ac3_pid))) + bitstream_pid = self._bitstream_entry.get_text() + if bitstream_pid: + flags.append("{}{:04x}".format(Pids.BIT_STREAM_DELAY.value, int(bitstream_pid))) + pcm_pid = self._pcm_entry.get_text() + if pcm_pid: + flags.append("{}{:04x}".format(Pids.PCM_DELAY.value, int(pcm_pid))) + # flags + f_flags = Flag.KEEP.value if self._keep_check_button.get_active() else 0 + f_flags = f_flags + Flag.HIDE.value if self._hide_check_button.get_active() else f_flags + f_flags = f_flags + Flag.PIDS.value if self._use_pids_check_button.get_active() else f_flags + f_flags = f_flags + Flag.NEW.value if self._new_check_button.get_active() else f_flags + if f_flags: + flags.append("f:{:02x}".format(f_flags)) + + return ",".join(flags) def get_data_id(self): if self._profile is Profile.ENIGMA_2: - pass + return self._old_service.data_id elif self._profile is Profile.NEUTRINO_MP: - pass + return self._old_service.data_id def get_fav_id(self): if self._profile is Profile.ENIGMA_2: - pass + return self._old_service.fav_id elif self._profile is Profile.NEUTRINO_MP: - pass + return self._old_service.fav_id def get_transponder_data(self): if self._profile is Profile.ENIGMA_2: if self._sys_combo_box.get_active_id() == "DVB-S2": - pass + return self._old_service.transponder elif self._profile is Profile.NEUTRINO_MP: - pass + return self._old_service.transponder + + def on_digit_entry_changed(self, entry): + entry.set_name("digit-entry" if self._pattern.search(entry.get_text()) else "GtkEntry") if __name__ == "__main__":