diff --git a/app/eparser/iptv.py b/app/eparser/iptv.py index 45e1cdce..f8257d84 100644 --- a/app/eparser/iptv.py +++ b/app/eparser/iptv.py @@ -1,11 +1,18 @@ -""" Module for m3u import """ +""" Module for IPTV and streams support """ +from enum import Enum + 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 NEUTRINO_FAV_ID_FORMAT = "{}::{}::{}::{}::{}::{}::{}::{}::{}::{}" -ENIGMA2_FAV_ID_FORMAT = " 1:0:1:0:0:0:0:0:0:0:{}:{}\n#DESCRIPTION: {}\n" +ENIGMA2_FAV_ID_FORMAT = " {}:0:{}:{}:{}:{}:{}:0:0:0:{}:{}\n#DESCRIPTION: {}\n" + + +class StreamType(Enum): + DVB_TS = "1" + NONE_TS = "4097" def parse_m3u(path, profile): @@ -22,7 +29,8 @@ def parse_m3u(path, profile): elif count == 1: count = 0 if profile is Profile.ENIGMA_2: - fav_id = ENIGMA2_FAV_ID_FORMAT.format(line.strip().replace(":", "%3a"), name, name, None) + fav_id = ENIGMA2_FAV_ID_FORMAT.format(StreamType.DVB_TS.value, 1, 0, 0, 0, 0, + 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(None, None, IPTV_ICON, name, *aggr[0:3], BqServiceType.IPTV.name, *aggr, fav_id, None) diff --git a/app/eparser/satxml.py b/app/eparser/satxml.py index 27a8faed..8cc7145d 100644 --- a/app/eparser/satxml.py +++ b/app/eparser/satxml.py @@ -2,8 +2,11 @@ For more info see __COMMENT """ +from functools import lru_cache from xml.dom.minidom import parse, Document +import os + from .ecommons import POLARIZATION, FEC, SYSTEM, MODULATION, PLS_MODE, Transponder, Satellite, get_key_by_value __COMMENT = (" File was created in DemonEditor\n\n" @@ -29,7 +32,7 @@ __COMMENT = (" File was created in DemonEditor\n\n" def get_satellites(path): - return parse_satellites(path) + return parse_satellites(path, os.path.getsize(path)) def write_satellites(satellites, data_path): @@ -98,7 +101,8 @@ def parse_sat(elem): parse_transponders(elem)) -def parse_satellites(path): +@lru_cache(maxsize=1) +def parse_satellites(path, file_size): """ Parsing satellites from xml""" dom = parse(path) satellites = [] diff --git a/app/picons/picons.py b/app/picons/picons.py index 62210654..342a017f 100644 --- a/app/picons/picons.py +++ b/app/picons/picons.py @@ -4,6 +4,8 @@ import shutil from collections import namedtuple from html.parser import HTMLParser +import re + from app.commons import log, run_task from app.properties import Profile @@ -103,6 +105,8 @@ class PiconsParser(HTMLParser): class ProviderParser(HTMLParser): """ Parser for satellite html page. (https://www.lyngsat.com/*sat-name*.html) """ + _POSITION_PATTERN = re.compile("at\s\d+\..*(?:E|W)']") + def __init__(self, entities=False, separator=' '): HTMLParser.__init__(self) @@ -117,7 +121,6 @@ class ProviderParser(HTMLParser): self._current_cell = [] self.rows = [] self._ids = set() - self._counter = 0 self._positon = None def handle_starttag(self, tag, attrs): @@ -150,11 +153,10 @@ class ProviderParser(HTMLParser): elif tag == 'tr': row = self._current_row # Satellite position - self._counter = self._counter + 1 - if self._counter == 12: - pos = str(row) - pos = pos[pos.rfind("at") + 2:] - self._positon = "".join(c for c in pos if c.isalnum() or c == ".") + if not self._positon: + pos = re.findall(self._POSITION_PATTERN, str(row)) + if pos: + self._positon = "".join(c for c in str(pos) if c.isdigit() or c in ".EW") if len(row) == 12: on_id, sep, tid = str(row[-2]).partition("-") @@ -170,7 +172,6 @@ class ProviderParser(HTMLParser): def reset(self): super().reset() - self._counter = 0 def parse_providers(open_path): diff --git a/app/ui/dialogs.glade b/app/ui/dialogs.glade index 36ecde7b..604b09a5 100644 --- a/app/ui/dialogs.glade +++ b/app/ui/dialogs.glade @@ -613,6 +613,480 @@ dmitry.v.yefremov@gmail.com + + + + + + + + DVB/TS + + + non-TS + + + + + 480 + False + Stream data + False + True + True + dialog + True + True + + + False + vertical + 2 + + + False + end + + + gtk-cancel + True + True + True + True + True + + + True + True + 0 + + + + + gtk-add + True + True + True + True + + + + True + True + 1 + + + + + gtk-save + True + True + True + True + True + + + + True + True + 2 + + + + + False + False + 0 + + + + + True + False + vertical + 2 + + + True + False + Service data: + 0.0099999997764825821 + + + + False + True + 0 + + + + + True + False + 2 + 2 + + + True + False + Name + 7 + 7 + + + 0 + 0 + + + + + -1 + True + True + True + gtk-edit + + + 0 + 1 + + + + + True + False + Description + + + 1 + 0 + + + + + True + True + True + gtk-edit + + + 1 + 1 + + + + + True + False + Type + + + 2 + 0 + + + + + True + False + stream_type_liststore + 0 + 0 + + + + + 0 + + + + + 2 + 1 + + + + + True + False + Reference + + + 3 + 0 + + + + + True + True + True + False + + + 3 + 1 + + + + + True + True + 1 + + + + + True + False + True + + + True + False + Url: + 7 + 7 + 0.0099999997764825821 + + + + 0 + 0 + + + + + True + True + gtk-edit + + + 0 + 1 + + + + + False + True + 2 + + + + + True + False + vertical + 2 + + + True + False + + + False + True + 1 + 0 + + + + + True + False + DVB/TS data: + 0.0099999997764825821 + + + + False + True + 1 + + + + + True + False + 2 + 2 + True + + + True + False + Type + + + 0 + 0 + + + + + True + False + SID + + + 1 + 0 + + + + + True + False + Tr. ID + + + 2 + 0 + + + + + True + False + Net. ID + + + 3 + 0 + + + + + True + False + Namespace + + + 4 + 0 + + + + + True + True + 5 + 5 + 1 + gtk-edit + + + + 0 + 1 + + + + + True + True + 5 + 5 + 0 + gtk-edit + + + + 1 + 1 + + + + + True + True + 5 + 5 + 0 + gtk-edit + + + + 2 + 1 + + + + + True + True + 5 + 5 + 0 + gtk-edit + + + + 3 + 1 + + + + + True + True + 5 + 5 + 0 + gtk-edit + + + + 4 + 1 + + + + + False + True + 2 + + + + + False + True + 3 + + + + + True + False + + + False + True + 2 + 7 + + + + + False + True + 1 + + + + + + iptv_dialog_cancel_button + + 1 11 diff --git a/app/ui/dialogs.py b/app/ui/dialogs.py index 99383f2f..3eee74f5 100644 --- a/app/ui/dialogs.py +++ b/app/ui/dialogs.py @@ -6,6 +6,11 @@ from app.commons import run_idle from . import Gtk, UI_RESOURCES_PATH, TEXT_DOMAIN +class Action(Enum): + EDIT = 0 + ADD = 1 + + class DialogType(Enum): INPUT = "input_dialog" CHOOSER = "path_chooser_dialog" @@ -74,9 +79,10 @@ def show_dialog(dialog_type: DialogType, transient, text=None, options=None, act def get_dialog_from_xml(dialog_type, transient): builder = Gtk.Builder() builder.set_translation_domain(TEXT_DOMAIN) - builder.add_from_file(UI_RESOURCES_PATH + "dialogs.glade") + builder.add_objects_from_file(UI_RESOURCES_PATH + "dialogs.glade", (dialog_type.value,)) dialog = builder.get_object(dialog_type.value) dialog.set_transient_for(transient) + return builder, dialog @@ -84,6 +90,7 @@ def get_chooser_dialog(transient, options, pattern, name): file_filter = Gtk.FileFilter() file_filter.add_pattern(pattern) file_filter.set_name(name) + return show_dialog(dialog_type=DialogType.CHOOSER, transient=transient, options=options, diff --git a/app/ui/iptv.py b/app/ui/iptv.py new file mode 100644 index 00000000..3e9fafa1 --- /dev/null +++ b/app/ui/iptv.py @@ -0,0 +1,167 @@ +import re + +from app.eparser.ecommons import BqServiceType, Service +from app.eparser.iptv import NEUTRINO_FAV_ID_FORMAT, StreamType, ENIGMA2_FAV_ID_FORMAT +from app.properties import Profile +from . import Gtk, Gdk, TEXT_DOMAIN, UI_RESOURCES_PATH, IPTV_ICON +from .dialogs import Action, show_dialog, DialogType +from .main_helper import get_base_model + + +class IptvDialog: + def __init__(self, transient, view, services, bouquet, profile=Profile.ENIGMA_2, action=Action.ADD): + handlers = {"on_entry_changed": self.on_entry_changed, + "on_save": self.on_save, + "on_stream_type_changed": self.on_stream_type_changed} + + builder = Gtk.Builder() + builder.set_translation_domain(TEXT_DOMAIN) + builder.add_objects_from_file(UI_RESOURCES_PATH + "dialogs.glade", ("iptv_dialog", "stream_type_liststore")) + builder.connect_signals(handlers) + + self._dialog = builder.get_object("iptv_dialog") + self._dialog.set_transient_for(transient) + self._name_entry = builder.get_object("name_entry") + self._description_entry = builder.get_object("description_entry") + self._url_entry = builder.get_object("url_entry") + self._reference_entry = builder.get_object("reference_entry") + self._srv_type_entry = builder.get_object("srv_type_entry") + self._sid_entry = builder.get_object("sid_entry") + self._tr_id_entry = builder.get_object("tr_id_entry") + self._net_id_entry = builder.get_object("net_id_entry") + self._namespace_entry = builder.get_object("namespace_entry") + self._stream_type_combobox = builder.get_object("stream_type_combobox") + self._add_button = builder.get_object("iptv_dialog_add_button") + self._save_button = builder.get_object("iptv_dialog_save_button") + self._stream_type_combobox = builder.get_object("stream_type_combobox") + self._action = action + self._profile = profile + self._bouquet = bouquet + self._services = services + self._model, self._paths = view.get_selection().get_selected_rows() + + self._PATTERN = re.compile("(?:^[\s]*$|\D)") + # style + self._style_provider = Gtk.CssProvider() + self._style_provider.load_from_path(UI_RESOURCES_PATH + "style.css") + for el in (self._srv_type_entry, self._sid_entry, self._tr_id_entry, self._net_id_entry, self._namespace_entry): + el.get_style_context().add_provider_for_screen(Gdk.Screen.get_default(), self._style_provider, + Gtk.STYLE_PROVIDER_PRIORITY_USER) + if profile is Profile.NEUTRINO_MP: + builder.get_object("iptv_data_box").set_visible(False) + builder.get_object("iptv_type_label").set_visible(False) + builder.get_object("reference_entry").set_visible(False) + builder.get_object("iptv_reference_label").set_visible(False) + self._stream_type_combobox.set_visible(False) + else: + self._description_entry.set_visible(False) + builder.get_object("iptv_description_label").set_visible(False) + + if self._action is Action.ADD: + self._save_button.set_visible(False) + self._add_button.set_visible(True) + if self._profile is Profile.ENIGMA_2: + self._update_reference_entry() + elif self._action is Action.EDIT: + self._current_srv = get_base_model(self._model)[self._paths][:] + self.init_data(self._current_srv) + + def show(self): + self._dialog.run() + self._dialog.destroy() + + def on_save(self, item): + if show_dialog(DialogType.QUESTION, self._dialog) == Gtk.ResponseType.CANCEL: + return + + self.save_enigma2_data() if self._profile is Profile.ENIGMA_2 else self.save_neutrino_data() + self._dialog.destroy() + + def init_data(self, srv): + name, fav_id = srv[2], srv[7] + self._name_entry.set_text(name) + self.init_enigma2_data(fav_id) if self._profile is Profile.ENIGMA_2 else self.init_neutrino_data(fav_id) + + def init_enigma2_data(self, fav_id): + data, sep, desc = fav_id.partition("#DESCRIPTION:") + self._description_entry.set_text(desc.strip()) + data = data.split(":") + if len(data) < 12: + return + self._stream_type_combobox.set_active(0 if StreamType(data[0].strip()) is StreamType.DVB_TS else 1) + self._srv_type_entry.set_text(data[2]) + self._sid_entry.set_text(data[3]) + self._tr_id_entry.set_text(data[4]) + self._net_id_entry.set_text(data[5]) + self._namespace_entry.set_text(data[6]) + self._url_entry.set_text(data[10].replace("%3a", ":")) + self._update_reference_entry() + + def init_neutrino_data(self, fav_id): + data = fav_id.split("::") + self._url_entry.set_text(data[0]) + self._description_entry.set_text(data[1]) + + def _update_reference_entry(self): + if self._profile is Profile.ENIGMA_2: + self._reference_entry.set_text("{}:0:{}:{}:{}:{}:{}:0:0:0".format(self.get_type(), + self._srv_type_entry.get_text(), + self._sid_entry.get_text(), + self._tr_id_entry.get_text(), + self._net_id_entry.get_text(), + self._namespace_entry.get_text())) + + def get_type(self): + return 1 if self._stream_type_combobox.get_active() == 0 else 4097 + + def on_entry_changed(self, entry): + if self._PATTERN.search(entry.get_text()): + entry.set_name("digit-entry") + else: + entry.set_name("GtkEntry") + self._update_reference_entry() + + def on_stream_type_changed(self, item): + self._update_reference_entry() + + def save_enigma2_data(self): + name = self._name_entry.get_text().strip() + fav_id = ENIGMA2_FAV_ID_FORMAT.format(self.get_type(), + self._srv_type_entry.get_text(), + self._sid_entry.get_text(), + self._tr_id_entry.get_text(), + self._net_id_entry.get_text(), + self._namespace_entry.get_text(), + self._url_entry.get_text().replace(":", "%3a"), + name, name) + self.update_bouquet_data(name, fav_id) + + def save_neutrino_data(self): + if self._action is Action.EDIT: + id_data = self._current_srv[7].split("::") + else: + id_data = ["", "", "0", None, None, None, None, "", "", "1"] + id_data[0] = self._url_entry.get_text() + id_data[1] = self._description_entry.get_text() + self.update_bouquet_data(self._name_entry.get_text(), NEUTRINO_FAV_ID_FORMAT.format(*id_data)) + self._dialog.destroy() + + def update_bouquet_data(self, name, fav_id): + if self._action is Action.EDIT: + old_srv = self._services.pop(self._current_srv[7]) + self._services[fav_id] = old_srv._replace(service=name, fav_id=fav_id) + self._bouquet[self._paths[0][0]] = fav_id + self._model.set(self._model.get_iter(self._paths), {2: name, 7: fav_id}) + else: + aggr = [None] * 10 + s_type = BqServiceType.IPTV.name + srv = (None, None, name, None, None, s_type, None, fav_id, None) + itr = self._model.insert_after(self._model.get_iter(self._paths[0]), + srv) if self._paths else self._model.insert(0, srv) + self._model.set_value(itr, 1, IPTV_ICON) + self._bouquet.insert(self._model.get_path(itr)[0], fav_id) + self._services[fav_id] = Service(None, None, IPTV_ICON, name, *aggr[0:3], s_type, *aggr, fav_id, None) + + +if __name__ == "__main__": + pass diff --git a/app/ui/lang/ru/LC_MESSAGES/demon-editor.mo b/app/ui/lang/ru/LC_MESSAGES/demon-editor.mo index 9fc25598..b3248e89 100644 Binary files a/app/ui/lang/ru/LC_MESSAGES/demon-editor.mo and b/app/ui/lang/ru/LC_MESSAGES/demon-editor.mo differ diff --git a/app/ui/main_app_window.py b/app/ui/main_app_window.py index dd94a2d9..e38f9f69 100644 --- a/app/ui/main_app_window.py +++ b/app/ui/main_app_window.py @@ -11,6 +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 .iptv import IptvDialog from .search import SearchProvider from . import Gtk, Gdk, UI_RESOURCES_PATH, LOCKED_ICON, HIDE_ICON, IPTV_ICON from .dialogs import show_dialog, DialogType, get_chooser_dialog, WaitDialog, get_message @@ -21,7 +22,7 @@ from .main_helper import edit_marker, insert_marker, move_items, rename, ViewTar 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 +from .service_details_dialog import ServiceDetailsDialog, Action class MainAppWindow: @@ -46,11 +47,11 @@ class MainAppWindow: _FAV_ELEMENTS = ("cut_tool_button", "paste_tool_button", "cut_menu_item", "paste_menu_item", "fav_cut_popup_item", "fav_paste_popup_item", "import_m3u_tool_button", "fav_import_m3u_popup_item", "fav_insert_marker_popup_item", "fav_edit_popup_item", - "fav_locate_popup_item", "fav_picon_popup_item") + "fav_locate_popup_item", "fav_picon_popup_item", "fav_add_iptv_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") + _FAV_M3U_ELEMENTS = ("import_m3u_tool_button", "fav_import_m3u_popup_item", "fav_add_iptv_popup_item") _LOCK_HIDE_ELEMENTS = ("locked_tool_button", "hide_tool_button") @@ -63,7 +64,8 @@ class MainAppWindow: "bouquets_remove_popup_item", "fav_remove_popup_item", "hide_tool_button", "import_m3u_tool_button", "fav_import_m3u_popup_item", "fav_insert_marker_popup_item", "fav_edit_marker_popup_item", "fav_edit_popup_item", "fav_locate_popup_item", - "services_copy_popup_item", "services_picon_popup_item", "fav_picon_popup_item") + "services_copy_popup_item", "services_picon_popup_item", "fav_picon_popup_item", + "services_add_new_popup_item", "fav_add_iptv_popup_item") def __init__(self): handlers = {"on_close_main_window": self.on_quit, @@ -112,7 +114,9 @@ class MainAppWindow: "on_search_down": self.on_search_down, "on_search_up": self.on_search_up, "on_search": self.on_search, - "on_service_edit": self.on_service_edit} + "on_service_edit": self.on_service_edit, + "on_services_add_new": self.on_services_add_new, + "on_iptv": self.on_iptv} self.__options = get_config() self.__profile = self.__options.get("profile") @@ -171,7 +175,9 @@ class MainAppWindow: # Search self.__search_info_bar = builder.get_object("search_info_bar") self.__search_provider = SearchProvider(self.__services_view, self.__fav_view, self.__bouquets_view, - self.__services, self.__bouquets) + self.__services, self.__bouquets, + builder.get_object("search_down_button"), + builder.get_object("search_up_button")) self.__main_window.show() def init_drag_and_drop(self): @@ -785,6 +791,8 @@ class MainAppWindow: for elem in self._COMMONS_ELEMENTS: self.__tool_elements[elem].set_sensitive(not_empty) + self.__tool_elements["services_add_new_popup_item"].set_sensitive(len(self.__bouquets_model)) + def on_hide(self, item): self.set_service_flags(Flag.HIDE) @@ -857,6 +865,16 @@ class MainAppWindow: bq_services.append(ch.fav_id) self.update_bouquet_channels(self.__fav_model, None, bq_selected) + def on_iptv(self, item): + response = IptvDialog(self.__main_window, + self.__fav_view, + self.__services, + self.__bouquets.get(self.is_bouquet_selected(), None), + Profile(self.__profile), + Action.ADD).show() + if response != Gtk.ResponseType.CANCEL: + self.update_fav_num_column(self.__fav_model) + def on_insert_marker(self, view): """ Inserts marker into bouquet services list. """ insert_marker(view, self.__bouquets, self.is_bouquet_selected(), self.__services, self.__main_window) @@ -920,9 +938,15 @@ class MainAppWindow: 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 + if srv_type == BqServiceType.MARKER.name: + return self.on_rename(view) + elif srv_type == BqServiceType.IPTV.name: + return IptvDialog(self.__main_window, + self.__fav_view, + self.__services, + self.__bouquets.get(self.is_bouquet_selected(), None), + Profile(self.__profile), + Action.EDIT).show() self.on_locate_in_services(view) dialog = ServiceDetailsDialog(self.__main_window, @@ -932,6 +956,15 @@ class MainAppWindow: self.__bouquets) dialog.show() + def on_services_add_new(self, item): + dialog = ServiceDetailsDialog(self.__main_window, + self.__options, + self.__services_view, + self.__services, + self.__bouquets, + action=Action.ADD) + 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_helper.py b/app/ui/main_helper.py index ae9fa068..4cf8a99a 100644 --- a/app/ui/main_helper.py +++ b/app/ui/main_helper.py @@ -59,7 +59,7 @@ def edit_marker(view, bouquets, selected_bouquet, channels, parent_window): old_ch = channels.pop(fav_id, None) new_fav_id = "{}::{}\n#DESCRIPTION {}\n".format(fav_id.split("::")[0], response, response) model.set(itr, {2: response, 7: new_fav_id}) - channels[new_fav_id] = Service(*old_ch[0:3], response, *old_ch[4:17], old_ch.data_id, new_fav_id, None) + channels[new_fav_id] = old_ch._replace(service=response, fav_id=new_fav_id) bq_services.pop(index) bq_services.insert(index, new_fav_id) @@ -143,7 +143,7 @@ def rename(view, parent_window, target, fav_view=None, service_view=None, channe old_ch = channels.get(f_id, None) if old_ch: - channels[f_id] = Service(*old_ch[0:3], channel_name, *old_ch[4:]) + channels[f_id] = old_ch._replace(service=channel_name) # ***************** Flags *******************# @@ -196,7 +196,7 @@ def set_lock(blacklist, channels, model, paths, target, services_model): continue blacklist.discard(bq_id) if locked else blacklist.add(bq_id) model.set_value(itr, col_num, None if locked else LOCKED_ICON) - channels[fav_id] = Service(*channel[:4], None if locked else LOCKED_ICON, *channel[5:]) + channels[fav_id] = channel._replace(locked=None if locked else LOCKED_ICON) ids.append(fav_id) if target is ViewTarget.FAV and ids: @@ -244,7 +244,7 @@ def set_hide(channels, model, paths): fav_id = model.get_value(itr, 18) channel = channels.get(fav_id, None) if channel: - channels[fav_id] = Service(*channel[:5], None if hide else HIDE_ICON, *channel[6:]) + channels[fav_id] = channel._replace(hide=None if hide else HIDE_ICON) def has_locked_hide(model, paths, col_num): diff --git a/app/ui/main_window.glade b/app/ui/main_window.glade index a9f6e865..fea0e33f 100644 --- a/app/ui/main_window.glade +++ b/app/ui/main_window.glade @@ -117,6 +117,11 @@ False gtk-copy + + True + False + network-transmit-receive + True False @@ -238,6 +243,17 @@ + + + Add IPTV or stream + True + False + False + image17 + False + + + True @@ -355,6 +371,12 @@ + + + True + False + + gtk-edit @@ -367,6 +389,15 @@ + + + gtk-new + False + True + True + + + True @@ -1215,6 +1246,7 @@ True + False True True @@ -1235,6 +1267,7 @@ True + False True True diff --git a/app/ui/picons_dialog.glade b/app/ui/picons_dialog.glade index 866ece19..ea187745 100644 --- a/app/ui/picons_dialog.glade +++ b/app/ui/picons_dialog.glade @@ -768,22 +768,16 @@ True False + 2 True False - 6 end - - - - - - False @@ -794,25 +788,22 @@ False - 16 - - - True False - Info + Info + center + True + word-char + 2 False - True + False 1 - - - False @@ -820,9 +811,6 @@ 0 - - - False diff --git a/app/ui/picons_dialog.py b/app/ui/picons_dialog.py index 277215ad..a30afa37 100644 --- a/app/ui/picons_dialog.py +++ b/app/ui/picons_dialog.py @@ -79,6 +79,10 @@ class PiconsDialog: self._enigma2_picons_path = self._picons_path if profile is Profile.NEUTRINO_MP: self._enigma2_picons_path = options.get(Profile.ENIGMA_2.value).get("picons_dir_path", "") + if not len(self._picon_ids) and self._profile is Profile.ENIGMA_2: + message = get_message("To automatically set the identifiers for picons,\n" + "first load the required services list into the main application window.") + self.show_info_message(message, Gtk.MessageType.WARNING) def show(self): self._dialog.run() @@ -93,12 +97,13 @@ class PiconsDialog: stderr=subprocess.PIPE, universal_newlines=True) GLib.io_add_watch(self._current_process.stderr, GLib.IO_IN, self.write_to_buffer) - self.append_providers(url) - - @run_task - def append_providers(self, url): model = self._providers_tree_view.get_model() model.clear() + self.update_receive_button_state() + self.append_providers(url, model) + + @run_task + def append_providers(self, url, model): self._current_process.wait() providers = parse_providers(self._TMP_DIR + url[url.find("w"):]) if providers: @@ -123,7 +128,14 @@ class PiconsDialog: self._terminate = False self._expander.set_expanded(True) - for prv in self.get_selected_providers(): + providers = self.get_selected_providers() + for prv in providers: + if not prv[2] and prv[2][:-2].isdigit(): + self.show_info_message( + get_message("Specify the correct position value for the provider!"), Gtk.MessageType.ERROR) + return + + for prv in providers: if self._terminate: break self.process_provider(Provider(*prv)) @@ -142,7 +154,7 @@ class PiconsDialog: PiconsParser.parse(path, self._picons_path, self._TMP_DIR, prv.on_id, pos, self._picon_ids, self.get_picons_format()) self.resize(self._picons_path) - self.show_info_message("Done", Gtk.MessageType.INFO) + self.show_info_message(get_message("Done!"), Gtk.MessageType.INFO) def write_to_buffer(self, fd, condition): if condition == GLib.IO_IN: diff --git a/app/ui/search.py b/app/ui/search.py index 7e005f1c..5d1d106d 100644 --- a/app/ui/search.py +++ b/app/ui/search.py @@ -1,9 +1,8 @@ """ This is helper module for search features """ -from app.ui.main_helper import get_base_model class SearchProvider: - def __init__(self, srv_view, fav_view, bqs_view, services, bouquets): + def __init__(self, srv_view, fav_view, bqs_view, services, bouquets, down_button, up_button): self._paths = [] self._current_index = -1 self._max_indexes = 0 @@ -12,12 +11,14 @@ class SearchProvider: self._bqs_view = bqs_view self._services = services self._bouquets = bouquets + self._up_button = up_button + self._down_button = down_button def search(self, text, ): self._current_index = -1 self._paths.clear() for view in self._srv_view, self._fav_view: - model = get_base_model(view.get_model()) + model = view.get_model() selection = view.get_selection() selection.unselect_all() if not text: @@ -37,6 +38,7 @@ class SearchProvider: def scroll_to(self, index): view, path = self._paths[index] view.scroll_to_cell(path, None) + self.update_navigation_buttons() def on_search_down(self): if self._current_index < self._max_indexes: @@ -48,6 +50,10 @@ class SearchProvider: self._current_index -= 1 self.scroll_to(self._current_index) + def update_navigation_buttons(self): + self._up_button.set_sensitive(self._current_index > 0) + self._down_button.set_sensitive(self._current_index < self._max_indexes) + if __name__ == "__main__": pass diff --git a/app/ui/service_details_dialog.glade b/app/ui/service_details_dialog.glade index b87b1c3f..93cfbe62 100644 --- a/app/ui/service_details_dialog.glade +++ b/app/ui/service_details_dialog.glade @@ -154,6 +154,12 @@ + + -180 + 180 + 0.10000000000000001 + 10 + @@ -261,9 +267,8 @@ - + gtk-new - True True True Create and save as new service @@ -373,7 +378,7 @@ 10 10 gtk-edit - + 2 @@ -391,30 +396,6 @@ 0 - - - True - False - CA ID's - - - 4 - 0 - - - - - True - True - 15 - 22 - gtk-edit - - - 4 - 1 - - True @@ -448,6 +429,31 @@ 1 + + + True + False + Reference + + + 4 + 0 + + + + + True + True + True + False + 8 + 10 + + + 4 + 1 + + False @@ -456,7 +462,7 @@ - + True False @@ -638,10 +644,11 @@ True False 25 + True Delays (ms): 10 10 - 0 + 1 9 @@ -758,7 +765,7 @@ - + True False @@ -842,16 +849,17 @@ - + True False 2 - + True True - False - 24 + 15 + 26 + gtk-edit 1 @@ -859,10 +867,10 @@ - + True False - Reference: + CAID's: 0 @@ -917,8 +925,9 @@ True False + 5 Transponder data: - 0.0099999997764825821 + 0 False @@ -972,7 +981,7 @@ True False - Pos + Position 0 @@ -998,7 +1007,7 @@ 12 12 gtk-edit - + 1 @@ -1024,7 +1033,7 @@ 12 12 gtk-edit - + 2 @@ -1091,77 +1100,15 @@ 1 - - - True - False - System - - - 5 - 0 - - - - - True - False - False - sys_list_store - 0 - - - - - 0 - - - - - 5 - 1 - - - - - True - False - Mod - - - 6 - 0 - - - - - True - False - False - 0.98999999999999999 - mod_list_store - 0 - - - - 0 - - - - - 6 - 1 - - True False True - 17 - 17 + 12 + 12 gtk-edit - + 7 @@ -1180,24 +1127,72 @@ - + True False - False - sat_pos_list_store - 0 - - - - 0 - - + True + number + sat_pos_adjustment + 1 + True 0 1 + + + True + False + TID + + + 5 + 0 + + + + + True + False + True + 8 + 10 + gtk-edit + + + + 5 + 1 + + + + + True + False + NID + + + 6 + 0 + + + + + True + False + True + 8 + 10 + gtk-edit + + + + 6 + 1 + + False @@ -1214,58 +1209,6 @@ True False 2 - - - True - False - Tr. ID - - - 0 - 0 - - - - - True - False - True - 8 - 10 - gtk-edit - - - - 0 - 1 - - - - - True - False - Net. ID - - - 1 - 0 - - - - - True - False - True - 8 - 10 - gtk-edit - - - - 1 - 1 - - True @@ -1403,8 +1346,8 @@ True False True - 5 - 5 + 8 + 10 gtk-edit @@ -1465,6 +1408,69 @@ 1 + + + True + False + System + + + 0 + 0 + + + + + True + False + False + True + sys_list_store + 0 + + + + + 0 + + + + + 0 + 1 + + + + + True + False + Mod + + + 1 + 0 + + + + + True + False + False + True + mod_list_store + 0 + + + + 0 + + + + + 1 + 1 + + diff --git a/app/ui/service_details_dialog.py b/app/ui/service_details_dialog.py index 9d660599..62a7bb08 100644 --- a/app/ui/service_details_dialog.py +++ b/app/ui/service_details_dialog.py @@ -1,62 +1,79 @@ 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, Pids, POLARIZATION, \ get_key_by_value, get_value_by_name, FEC_DEFAULT, PLS_MODE from app.properties import Profile -from app.ui.dialogs import show_dialog, DialogType -from app.ui.main_helper import get_base_model from . import Gtk, Gdk, UI_RESOURCES_PATH, HIDE_ICON, TEXT_DOMAIN +from .dialogs import show_dialog, DialogType, Action +from .main_helper import get_base_model class ServiceDetailsDialog: - _DATA_ID = "{:04x}:{:08x}:{:04x}:{:04x}:{}:{}" + _ENIGMA2_DATA_ID = "{:04x}:{:08x}:{:04x}:{:04x}:{}:{}" - _FAV_ID = "{:X}:{:X}:{:X}:{:X}" + _ENIGMA2_FAV_ID = "{:X}:{:X}:{:X}:{:X}" - _TRANSPONDER_DATA = "{} {}:{}:{}:{}:{}:{}:{}" + _ENIGMA2_TRANSPONDER_DATA = "{} {}:{}:{}:{}:{}:{}:{}" - _DIGIT_ENTRY_ELEMENTS = ("sid_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", "tr_flag_entry", "namespace_entry", - "srv_type_entry") + _NEUTRINO_FAV_ID = "{:x}:{:x}:{:x}" - def __init__(self, transient, options, view, services, bouquets): + _NEUTRINO_TRANSPONDER_DATA = "{:04x}:{:04x}:{}:{}:{}:{}:{}:{}:{}" + + _DIGIT_ENTRY_ELEMENTS = ("bitstream_entry", "pcm_entry", "video_pid_entry", "pcr_pid_entry", "srv_type_entry", + "ac3_pid_entry", "ac3plus_pid_entry", "acc_pid_entry", "he_acc_pid_entry", + "teletext_pid_entry", "pls_code_entry", "stream_id_entry", "tr_flag_entry", + "audio_pid_entry") + _NOT_EMPTY_DIGIT_ELEMENTS = ("sid_entry", "freq_entry", "rate_entry", "transponder_id_entry", "network_id_entry", + "namespace_entry") + + _DIGIT_ENTRY_NAME = "digit-entry" + + def __init__(self, transient, options, view, services, bouquets, action=Action.EDIT): handlers = {"on_system_changed": self.on_system_changed, "on_save": self.on_save, "on_create_new": self.on_create_new, "on_digit_entry_changed": self.on_digit_entry_changed, - "on_tr_edit_toggled": self.on_tr_edit_toggled} + "on_tr_edit_toggled": self.on_tr_edit_toggled, + "on_non_empty_entry_changed": self.on_non_empty_entry_changed} builder = Gtk.Builder() builder.set_translation_domain(TEXT_DOMAIN) builder.add_from_file(UI_RESOURCES_PATH + "service_details_dialog.glade") builder.connect_signals(handlers) + self._builder = builder self._dialog = builder.get_object("service_details_dialog") self._dialog.set_transient_for(transient) self._profile = Profile(options["profile"]) self._satellites_xml_path = options.get(self._profile.value)["data_dir_path"] + "satellites.xml" self._services_view = view + self._action = action self._old_service = None self._services = services self._bouquets = bouquets self._transponder_services_iters = None self._current_model = None self._current_itr = None - self._pattern = re.compile("\D") + self._DIGIT_PATTERN = re.compile("\D") + self._NON_EMPTY_PATTERN = re.compile("(?:^[\s]*$|\D)") + self._apply_button = builder.get_object("apply_button") + self._create_button = builder.get_object("create_button") # style self._style_provider = Gtk.CssProvider() self._style_provider.load_from_path(UI_RESOURCES_PATH + "style.css") - # initialize only digit elements + # initialization 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._sid_entry = self._digit_elements.get("sid_entry") + # initialization of non empty elements + self._non_empty_elements = {k: builder.get_object(k) for k in self._NOT_EMPTY_DIGIT_ELEMENTS} + for elem in self._non_empty_elements.values(): + elem.get_style_context().add_provider_for_screen(Gdk.Screen.get_default(), self._style_provider, + Gtk.STYLE_PROVIDER_PRIORITY_USER) + self._sid_entry = self._non_empty_elements.get("sid_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") @@ -67,14 +84,14 @@ class ServiceDetailsDialog: 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._transponder_id_entry = self._non_empty_elements.get("transponder_id_entry") + self._network_id_entry = self._non_empty_elements.get("network_id_entry") + self._freq_entry = self._non_empty_elements.get("freq_entry") + self._rate_entry = self._non_empty_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._tr_flag_entry = self._digit_elements.get("tr_flag_entry") - self._namespace_entry = self._digit_elements.get("namespace_entry") + self._namespace_entry = self._non_empty_elements.get("namespace_entry") # Service elements self._name_entry = builder.get_object("name_entry") self._package_entry = builder.get_object("package_entry") @@ -88,7 +105,7 @@ class ServiceDetailsDialog: self._new_check_button = builder.get_object("new_check_button") self._pids_grid = builder.get_object("pids_grid") # Transponder elements - self._sat_pos_combo_box = builder.get_object("sat_pos_combo_box") + self._sat_pos_button = builder.get_object("sat_pos_button") 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") @@ -98,15 +115,19 @@ class ServiceDetailsDialog: self._pilot_combo_box = builder.get_object("pilot_combo_box") self._pls_mode_combo_box = builder.get_object("pls_mode_combo_box") self._tr_edit_switch = builder.get_object("tr_edit_switch") + self._tr_extra_expander = builder.get_object("tr_extra_expander") 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._TRANSPONDER_ELEMENTS = (self._sat_pos_combo_box, self._pol_combo_box, self._invertion_combo_box, + self._TRANSPONDER_ELEMENTS = (self._sat_pos_button, self._pol_combo_box, self._invertion_combo_box, self._sys_combo_box, self._freq_entry, self._transponder_id_entry, self._network_id_entry, self._namespace_entry, self._fec_combo_box, self._rate_entry) - self.update_data_elements() + if self._action is Action.EDIT: + self.update_data_elements() + elif self._action is Action.ADD: + self.init_default_data_elements() def show(self): response = self._dialog.run() @@ -117,6 +138,22 @@ class ServiceDetailsDialog: return response @run_idle + def init_default_data_elements(self): + self._apply_button.set_visible(False) + self._create_button.set_visible(True) + self._tr_edit_switch.set_sensitive(False) + self.on_tr_edit_toggled(self._tr_edit_switch.set_active(True), True) + for elem in self._non_empty_elements.values(): + elem.set_text(" ") + elem.set_text("") + self._new_check_button.set_active(True) + self._tr_extra_expander.activate() + self._service_type_combo_box.set_active(0) + self._pol_combo_box.set_active(0) + self._fec_combo_box.set_active(0) + self._sys_combo_box.set_active(0) + self._invertion_combo_box.set_active(2) + def update_data_elements(self): model, paths = self._services_view.get_selection().get_selected_rows() itr = model.get_iter(paths) @@ -145,7 +182,7 @@ class ServiceDetailsDialog: self.init_enigma2_transponder_data(srv) elif self._profile is Profile.NEUTRINO_MP: self.init_neutrino_data(srv) - self.init_enigma_ui_elements() + self.init_neutrino_ui_elements() # ***************** Init Enigma2 data *********************# @@ -230,27 +267,21 @@ class ServiceDetailsDialog: self._reference_entry.set_text(srv.picon_id.rstrip(".png")) self._transponder_id_entry.set_text(str(int(tr_data[0], 16))) self._network_id_entry.set_text(str(int(tr_data[1], 16))) + self.select_active_text(self._invertion_combo_box, Inversion(tr_data[3]).name) - def init_enigma_ui_elements(self): - self._pids_grid.set_sensitive(False) - self._cas_entry.set_sensitive(False) - self._keep_check_button.set_sensitive(False) - self._hide_check_button.set_sensitive(False) - self._use_pids_check_button.set_sensitive(False) - self._new_check_button.set_sensitive(False) + def init_neutrino_ui_elements(self): + self._builder.get_object("flags_box").set_visible(False) + self._builder.get_object("pids_grid").set_visible(False) + self._builder.get_object("tr_grid").remove_column(7) + self._builder.get_object("tr_extra_expander").set_visible(False) + self._builder.get_object("srv_separator").set_visible(False) # ***************** Init Sat positions *********************# - @run_idle def set_sat_positions(self, sat_pos): """ Sat positions initialisation """ - model = self._sat_pos_combo_box.get_model() - positions = self.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) + self._sat_pos_button.set_value(float(sat_pos)) - @lru_cache(maxsize=1) def get_sat_positions(self, path): try: return ["{:.1f}".format(float(x.position) / 10) for x in get_satellites(path)] @@ -272,56 +303,31 @@ class ServiceDetailsDialog: # ***************** Save data *********************# def on_save(self, item): + self.save_data() + + def on_create_new(self, item): + self.save_data() + + def save_data(self): + if not self.is_data_correct(): + show_dialog(DialogType.ERROR, self._dialog, "Error. Verify the data!") + return + if show_dialog(DialogType.QUESTION, self._dialog) == Gtk.ResponseType.CANCEL: return + self.on_edit() if self._action is Action.EDIT else self.on_new() + self._dialog.destroy() + + def on_edit(self): fav_id, data_id = self.get_srv_data() # transponder transponder = self._old_service.transponder - 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() - if self._tr_edit_switch.get_active(): transponder = self.get_transponder_data() if self._transponder_services_iters: - for itr in self._transponder_services_iters: - srv = self._current_model[itr][:] - srv[-9] = freq - srv[-8] = rate - srv[-7] = pol - srv[-6] = fec - srv[-5] = system - srv[-4] = pos - srv[-1] = transponder - srv = Service(*srv) - self._services[srv.fav_id] = srv - self._current_model.set(itr, {i: v for i, v in enumerate(srv)}) - - service = Service(flags_cas=self.get_flags(), - transponder_type="s", - coded=self._old_service.coded, - service=self._name_entry.get_text(), - locked=self._old_service.locked, - hide=HIDE_ICON if self._hide_check_button.get_active() else 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="{:x}".format(int(self._sid_entry.get_text())), - freq=freq, - rate=rate, - pol=pol, - fec=fec, - system=system, - pos=pos, - data_id=data_id, - fav_id=fav_id, - transponder=transponder) - + self.update_transponder_services(transponder) + service = self.get_service(fav_id, data_id, transponder) old_fav_id = self._old_service.fav_id if old_fav_id != fav_id: self._services.pop(old_fav_id, None) @@ -332,17 +338,38 @@ class ServiceDetailsDialog: indexes.append(i) for i in indexes: bq[i] = fav_id - self._services[fav_id] = service self._current_model.set(self._current_itr, {i: v for i, v in enumerate(service)}) self._old_service = service - def on_create_new(self, item): - if show_dialog(DialogType.QUESTION, self._dialog) == Gtk.ResponseType.CANCEL: - return - + def on_new(self): + service = self.get_service(*self.get_srv_data(), self.get_transponder_data()) + print(service) show_dialog(DialogType.ERROR, transient=self._dialog, text="Not implemented yet!") + def get_service(self, fav_id, data_id, transponder): + freq, rate, pol, fec, system, pos = self.get_transponder_values() + return Service(flags_cas=self.get_flags(), + transponder_type="s", + coded=self._old_service.coded, + service=self._name_entry.get_text(), + locked=self._old_service.locked, + hide=HIDE_ICON if self._hide_check_button.get_active() else 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="{:x}".format(int(self._sid_entry.get_text())), + freq=freq, + rate=rate, + pol=pol, + fec=fec, + system=system, + pos=pos, + data_id=data_id, + fav_id=fav_id, + transponder=transponder) + def get_flags(self): if self._profile is Profile.ENIGMA_2: return self.get_enigma2_flags() @@ -389,23 +416,26 @@ class ServiceDetailsDialog: def get_srv_data(self): ssid = int(self._sid_entry.get_text()) - namespace = int(self._namespace_entry.get_text()) - transponder_id = int(self._transponder_id_entry.get_text()) - network_id = int(self._network_id_entry.get_text()) + net_id, tr_id = int(self._network_id_entry.get_text()), int(self._transponder_id_entry.get_text()) service_type = self._srv_type_entry.get_text() if self._profile is Profile.ENIGMA_2: - data_id = self._DATA_ID.format(ssid, namespace, transponder_id, network_id, service_type, 0) - fav_id = self._FAV_ID.format(ssid, transponder_id, network_id, namespace) + namespace = int(self._namespace_entry.get_text()) + data_id = self._ENIGMA2_DATA_ID.format(ssid, namespace, tr_id, net_id, service_type, 0) + fav_id = self._ENIGMA2_FAV_ID.format(ssid, tr_id, net_id, namespace) return fav_id, data_id elif self._profile is Profile.NEUTRINO_MP: - return self._old_service.fav_id, self._old_service.data_id + fav_id = self._NEUTRINO_FAV_ID.format(tr_id, net_id, ssid) + return fav_id, self._old_service.data_id - def get_fav_id(self): - if self._profile is Profile.ENIGMA_2: - return self._old_service.fav_id - elif self._profile is Profile.NEUTRINO_MP: - return self._old_service.fav_id + def get_transponder_values(self): + 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 = str(round(self._sat_pos_button.get_value(), 1)) + return freq, rate, pol, fec, system, pos def get_transponder_data(self): sys = self._sys_combo_box.get_active_id() @@ -413,12 +443,12 @@ class ServiceDetailsDialog: rate = self._rate_entry.get_text() pol = self.get_value_from_combobox_id(self._pol_combo_box, POLARIZATION) fec = self.get_value_from_combobox_id(self._fec_combo_box, FEC_DEFAULT) - sat_pos = self._sat_pos_combo_box.get_active_id().replace(".", "") + sat_pos = str(round(self._sat_pos_button.get_value(), 1)).replace(".", "") inv = get_value_by_name(Inversion, self._invertion_combo_box.get_active_id()) srv_sys = "0" # !!! if self._profile is Profile.ENIGMA_2: - dvb_s_tr = self._TRANSPONDER_DATA.format("s", freq, rate, pol, fec, sat_pos, inv, srv_sys) + dvb_s_tr = self._ENIGMA2_TRANSPONDER_DATA.format("s", freq, rate, pol, fec, sat_pos, inv, srv_sys) if sys == "DVB-S": return dvb_s_tr if sys == "DVB-S2": @@ -432,7 +462,19 @@ class ServiceDetailsDialog: pls = ":{}:{}:{}".format(st_id, pls_code, pls_mode) if pls_mode and pls_code and st_id else "" return "{}:{}:{}:{}:{}{}".format(dvb_s_tr, flag, mod, roll_off, pilot, pls) elif self._profile is Profile.NEUTRINO_MP: - return self._old_service.transponder + on_id, tr_id = int(self._network_id_entry.get_text()), int(self._transponder_id_entry.get_text()) + mod = self.get_value_from_combobox_id(self._mod_combo_box, MODULATION) if sys == "DVB-S2" else None + srv_sys = None + return self._NEUTRINO_TRANSPONDER_DATA.format(tr_id, on_id, freq, inv, rate, fec, pol, mod, srv_sys) + + def update_transponder_services(self, transponder): + for itr in self._transponder_services_iters: + srv = self._current_model[itr][:] + srv[-9], srv[-8], srv[-7], srv[-6], srv[-5], srv[-4] = self.get_transponder_values() + srv[-1] = transponder + srv = Service(*srv) + self._services[srv.fav_id] = self._services.pop(srv.fav_id)._replace(transponder=transponder) + self._current_model.set(itr, {i: v for i, v in enumerate(srv)}) # ***************** Others *********************# @@ -444,7 +486,10 @@ class ServiceDetailsDialog: break def on_digit_entry_changed(self, entry): - entry.set_name("digit-entry" if self._pattern.search(entry.get_text()) else "GtkEntry") + entry.set_name(self._DIGIT_ENTRY_NAME if self._DIGIT_PATTERN.search(entry.get_text()) else "GtkEntry") + + def on_non_empty_entry_changed(self, entry): + entry.set_name(self._DIGIT_ENTRY_NAME if self._NON_EMPTY_PATTERN.search(entry.get_text()) else "GtkEntry") def get_value_from_combobox_id(self, box: Gtk.ComboBox, dc: dict): cb_id = box.get_active_id() @@ -453,7 +498,7 @@ class ServiceDetailsDialog: @run_idle def on_tr_edit_toggled(self, switch: Gtk.Switch, active): - if active: + if active and self._action is Action.EDIT: self._transponder_services_iters = [] response = TransponderServicesDialog(self._dialog, self._current_model, @@ -469,6 +514,15 @@ class ServiceDetailsDialog: for elem in self._TRANSPONDER_ELEMENTS: elem.set_sensitive(active) + def is_data_correct(self): + for elem in self._digit_elements.values(): + if elem.get_name() == self._DIGIT_ENTRY_NAME: + return False + for elem in self._non_empty_elements.values(): + if elem.get_name() == self._DIGIT_ENTRY_NAME: + return False + return True + class TransponderServicesDialog: def __init__(self, transient, model, transponder, tr_iters): diff --git a/build-deb.sh b/build-deb.sh index a342c222..f5b95f72 100755 --- a/build-deb.sh +++ b/build-deb.sh @@ -5,7 +5,7 @@ DEB_PATH="$B_PATH/usr/share/demoneditor" mkdir -p $B_PATH cp -TRv deb $B_PATH -cp -Rv app $DEB_PATH +rsync --exclude=app/ui/lang -arv app $DEB_PATH cp -Rv start.py $DEB_PATH cd dist diff --git a/deb/usr/share/locale/ru/LC_MESSAGES/demon-editor.mo b/deb/usr/share/locale/ru/LC_MESSAGES/demon-editor.mo index 9fc25598..b3248e89 100644 Binary files a/deb/usr/share/locale/ru/LC_MESSAGES/demon-editor.mo and b/deb/usr/share/locale/ru/LC_MESSAGES/demon-editor.mo differ diff --git a/po/ru/demon-editor.mo b/po/ru/demon-editor.mo index 9fc25598..b3248e89 100644 Binary files a/po/ru/demon-editor.mo and b/po/ru/demon-editor.mo differ diff --git a/po/ru/demon-editor.po b/po/ru/demon-editor.po index a4c689fe..0ed9a789 100644 --- a/po/ru/demon-editor.po +++ b/po/ru/demon-editor.po @@ -43,294 +43,222 @@ msgstr "№" msgid "Current IP:" msgstr "Текущий IP:" -#: main_window.glade:261 msgid "Assign" msgstr "Привязать" -#: main_window.glade:1826 msgid "Bouquet details" msgstr "Сервисы букета" -#: main_window.glade:2021 msgid "Bouquets" msgstr "Букеты" -#: main_window.glade:916 msgid "Copy" msgstr "Копировать" -#: main_window.glade:287 main_window.glade:416 msgid "Copy reference" msgstr "Копировать ссылку" -#: main_window.glade:1777 msgid "Data" msgstr "" -#: main_window.glade:744 msgid "Download" msgstr "Загрузить" -#: main_window.glade:1014 msgid "Edit" msgstr "Изменить" -#: main_window.glade:1015 msgid "Edit " msgstr "Изменить" -#: main_window.glade:213 msgid "Edit mаrker text" msgstr "Изменить текст маркера" -#: main_window.glade:543 main_window.glade:743 msgid "FTP-transfer" msgstr "Передача установок по FTP" -#: main_window.glade:808 msgid "Global search" msgstr "Глобальный поиск" -#: main_window.glade:973 msgid "Hide" msgstr "Пропустить" -#: main_window.glade:972 msgid "Hide/Skip On/Off Ctrl + H" msgstr "Скрыть/Пропустить Вкл/Выкл Ctrl + H" -#: main_window.glade:1113 msgid "IPTV" msgstr "" -#: main_window.glade:231 msgid "Import m3u" msgstr "Импортировать m3u" -#: main_window.glade:1111 msgid "Import m3u file" msgstr "Импортировать файл m3u" -#: main_window.glade:201 msgid "Insert marker" msgstr "Вставить маркер" -#: main_window.glade:184 msgid "Locate in services" msgstr "Найти в списке сервисов" -#: main_window.glade:957 msgid "Locked" msgstr "Заблокирован" -#: main_window.glade:834 msgid "Move" msgstr "Переместить" -#: main_window.glade:999 msgid "New" msgstr "Новый" -#: main_window.glade:998 msgid "New bouquet" msgstr "Новый букет" -#: main_window.glade:718 main_window.glade:719 msgid "Open" msgstr "Открыть" -#: main_window.glade:956 msgid "Parent lock On/Off Ctrl + L" msgstr "Родительский замок Вкл/Выкл Ctrl + L" -#: main_window.glade:931 msgid "Paste" msgstr "" -#: main_window.glade:1095 msgid "Picons" msgstr "Пиконы" -#: main_window.glade:650 main_window.glade:1096 msgid "Picons loader" msgstr "Загрузчик пиконов" -#: main_window.glade:1055 msgid "Preferences" msgstr "Настройки" -#: main_window.glade:2195 msgid "Profile:" msgstr "Профиль:" -#: main_window.glade:1751 msgid "Radio" msgstr "" -#: main_window.glade:271 main_window.glade:1030 msgid "Remove" msgstr "Удалить" -#: main_window.glade:640 main_window.glade:1079 main_window.glade:1080 msgid "Satellites editor" msgstr "Редактор спутников" -#: main_window.glade:768 main_window.glade:769 msgid "Save" msgstr "Сохранить" -#: main_window.glade:809 msgid "Search" msgstr "Поиск" -#: main_window.glade:1269 msgid "Services" msgstr "Сервисы" -#: main_window.glade:793 main_window.glade:794 msgid "Services filter" msgstr "Фильтр сервисов" -#: main_window.glade:1054 msgid "Settings" msgstr "Настройки" -#: main_window.glade:1725 msgid "TV" msgstr "" -#: main_window.glade:859 main_window.glade:860 msgid "Up" msgstr "Переместить вверх" msgid "Down" msgstr "Переместить вниз" -#: dialogs.glade:1101 msgid "Active profile:" msgstr "Активный профиль:" -#: dialogs.glade:175 msgid "All" msgstr "Все" -#: dialogs.glade:595 msgid "Are you sure?" msgstr "Вы уверены?" -#: dialogs.glade:127 msgid "Current data path:" msgstr "Текущий путь к данным:" -#: dialogs.glade:1192 msgid "Data dir:" msgstr "Путь к данным:" -#: dialogs.glade:164 msgid "Data:" msgstr "Данные:" -#: dialogs.glade:16 msgid "Enigma2 channel and satellites list editor for GNU/Linux" msgstr "Редактор списка каналов и спутников Enigma2\n для GNU/Linux" -#: dialogs.glade:814 msgid "FTP" msgstr "" -#: dialogs.glade:712 msgid "Host:" msgstr "Адрес ресивера:" -#: dialogs.glade:1328 msgid "Loading data..." msgstr "Загрузка данных..." -#: dialogs.glade:735 dialogs.glade:863 msgid "Login:" msgstr "Логин:" -#: dialogs.glade:625 msgid "Options" msgstr "Настройки" -#: dialogs.glade:746 dialogs.glade:874 msgid "Password:" msgstr "Пароль:" -#: dialogs.glade:1235 msgid "Picons dir:" msgstr "Директория пиконов:" -#: dialogs.glade:1050 msgid "Picons:" msgstr "Пиконы:" -#: dialogs.glade:769 dialogs.glade:830 msgid "Port:" msgstr "Порт:" -#: dialogs.glade:94 dialogs.glade:256 msgid "Receive" msgstr "Получить" -#: dialogs.glade:254 msgid "Receive files from receiver" msgstr "Получить файлы из ресивера" -#: dialogs.glade:100 msgid "Receiver IP:" msgstr "IP адрес ресивера:" -#: dialogs.glade:297 msgid "Remove unused bouquets" msgstr "Удалить не испрльзуемые букеты" -#: dialogs.glade:1148 msgid "Reset profile" msgstr "Сброс профиля" -#: dialogs.glade:208 msgid "Satellites" msgstr "Спутники" -#: dialogs.glade:1026 msgid "Satellites.xml file:" msgstr "Файл satellites.xml:" -#: dialogs.glade:1215 dialogs.glade:1216 msgid "Select" msgstr "" -#: dialogs.glade:271 msgid "Send" msgstr "Отправить" -#: dialogs.glade:269 msgid "Send files to receiver" -msgstr "Отправить вайлы в ресивер" +msgstr "Отправить файлы в ресивер" -#: dialogs.glade:978 msgid "Services and Bouquets files:" msgstr "Файлы сервисов и букетов:" -#: dialogs.glade:932 msgid "Telnet" msgstr "" -#: dialogs.glade:908 msgid "Timeout between commands in seconds" msgstr "Пауза между коммандами в сек." -#: dialogs.glade:897 msgid "Timeout:" msgstr "Тайм-аут:" -#: dialogs.glade:1002 msgid "User bouquet files:" msgstr "Файлы букетов:" -#: dialogs.glade:224 msgid "WebTV" msgstr "" @@ -377,6 +305,21 @@ msgstr "Путь для сохранения:" msgid "Path to Enigma2 picons:" msgstr "Путь к пиконам формата Enigma2:" +msgid "Specify the correct position value for the provider!" +msgstr "Укажите правильное значение позиции для провайдера!" + +msgid "Converter between name formats" +msgstr "Конвертер формата имен" + +msgid "Receive picons for providers" +msgstr "Получение пиконов для провайдеров" + +msgid "Load satellite providers." +msgstr "Загрузка провайдеров" + +msgid "To automatically set the identifiers for picons,\nfirst load the required services list into the main application window." +msgstr "Для автоматического именования пиконов,\nзагрузите список необходимых сервисов в главное окно программы." + # Satellites editor msgid "Satellites edit tool" msgstr "Редактор спутников" @@ -418,6 +361,24 @@ msgstr "Данные транспондера" msgid "Changes will be applied to all services of this transponder!\nContinue?" msgstr "Изменения будут применены ко всем сервисам данного транспондера!\nПродолжить?" +msgid "Reference" +msgstr "Ссылка" + +msgid "Namespace" +msgstr "Пр. имен" + +msgid "Flags:" +msgstr "Флаги:" + +msgid "Delays (ms):" +msgstr "Задержки (mc)" + +msgid "Bitstream" +msgstr "Поток" + +msgid "Description" +msgstr "Описание" + # Dialogs messages msgid "Error. No bouquet is selected!" msgstr "Ошибка. Не выбран букет!"