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
+
+
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 @@
+
+
+
+
+
+
+
+
+
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 "Ошибка. Не выбран букет!"