diff --git a/app/connections.py b/app/connections.py index 715f592a..7f5d3df9 100644 --- a/app/connections.py +++ b/app/connections.py @@ -11,7 +11,7 @@ from urllib.parse import urlencode from urllib.request import urlopen, HTTPPasswordMgrWithDefaultRealm, HTTPBasicAuthHandler, build_opener, install_opener from app.commons import log -from app.properties import Profile +from app.settings import Profile _BQ_FILES_LIST = ("tv", "radio", # enigma 2 "myservices.xml", "bouquets.xml", "ubouquets.xml") # neutrino @@ -44,16 +44,16 @@ class TestException(Exception): pass -def download_data(*, properties, download_type=DownloadType.ALL, callback=print): - with FTP(host=properties["host"], user=properties["user"], passwd=properties["password"]) as ftp: +def download_data(*, settings, download_type=DownloadType.ALL, callback=print): + with FTP(host=settings.host, user=settings.user, passwd=settings.password) as ftp: ftp.encoding = "utf-8" callback("FTP OK.\n") - save_path = properties["data_dir_path"] + save_path = settings.data_dir_path os.makedirs(os.path.dirname(save_path), exist_ok=True) files = [] # bouquets if download_type is DownloadType.ALL or download_type is DownloadType.BOUQUETS: - ftp.cwd(properties["services_path"]) + ftp.cwd(settings.services_path) ftp.dir(files.append) file_list = _BQ_FILES_LIST + _DATA_FILES_LIST if download_type is DownloadType.ALL else _BQ_FILES_LIST for file in files: @@ -63,7 +63,7 @@ def download_data(*, properties, download_type=DownloadType.ALL, callback=print) download_file(ftp, name, save_path, callback) # satellites.xml and webtv if download_type in (DownloadType.ALL, DownloadType.SATELLITES, DownloadType.WEBTV): - ftp.cwd(properties["satellites_xml_path"]) + ftp.cwd(settings.satellites_xml_path) files.clear() ftp.dir(files.append) @@ -75,11 +75,11 @@ def download_data(*, properties, download_type=DownloadType.ALL, callback=print) download_file(ftp, _WEBTV_XML_FILE, save_path, callback) # epg.dat if download_type is DownloadType.EPG: - stb_path = properties["services_path"] - epg_options = properties.get("epg_options", None) + stb_path = settings.services_path + epg_options = settings.epg_options if epg_options: - stb_path = epg_options.get("epg_dat_stb_path", stb_path) - save_path = epg_options.get("epg_dat_path", save_path) + stb_path = epg_options.epg_dat_stb_path or stb_path + save_path = epg_options.epg_dat_path or save_path ftp.cwd(stb_path) ftp.dir(files.append) for file in files: @@ -91,16 +91,17 @@ def download_data(*, properties, download_type=DownloadType.ALL, callback=print) callback("\nDone.\n") -def upload_data(*, properties, download_type=DownloadType.ALL, remove_unused=False, profile=Profile.ENIGMA_2, +def upload_data(*, settings, download_type=DownloadType.ALL, remove_unused=False, callback=print, done_callback=None, use_http=False): - data_path = properties["data_dir_path"] - host = properties["host"] - base_url = "http://{}:{}/api/".format(host, properties.get("http_port", "80")) + profile = settings.profile + data_path = settings.data_dir_path + host = settings.host + base_url = "http://{}:{}/api/".format(host, settings.http_port) tn, ht = None, None # telnet, http try: if profile is Profile.ENIGMA_2 and use_http: - ht = http(properties.get("http_user", ""), properties.get("http_password", ""), base_url, callback) + ht = http(settings.http_user, settings.http_password, base_url, callback) next(ht) message = "" if download_type is DownloadType.BOUQUETS: @@ -120,18 +121,19 @@ def upload_data(*, properties, download_type=DownloadType.ALL, remove_unused=Fal time.sleep(2) else: # telnet - tn = telnet(host=host, user=properties.get("telnet_user", "root"), - password=properties.get("telnet_password", ""), - timeout=properties.get("telnet_timeout", 5)) + tn = telnet(host=host, + user=settings.telnet_user, + password=settings.telnet_password, + timeout=settings.telnet_timeout) next(tn) # terminate enigma or neutrino tn.send("init 4") - with FTP(host=host, user=properties["user"], passwd=properties["password"]) as ftp: + with FTP(host=host, user=settings.user, passwd=settings.password) as ftp: ftp.encoding = "utf-8" callback("FTP OK.\n") - sat_xml_path = properties["satellites_xml_path"] - services_path = properties["services_path"] + sat_xml_path = settings.satellites_xml_path + services_path = settings.services_path if download_type is DownloadType.SATELLITES: upload_xml(ftp, data_path, sat_xml_path, _SAT_XML_FILE, callback) @@ -153,7 +155,7 @@ def upload_data(*, properties, download_type=DownloadType.ALL, remove_unused=Fal upload_files(ftp, data_path, _DATA_FILES_LIST, callback) if download_type is DownloadType.PICONS: - upload_picons(ftp, properties.get("picons_dir_path"), properties.get("picons_path"), callback) + upload_picons(ftp, settings.picons_dir_path, settings.picons_path, callback) if tn and not use_http: # resume enigma or restart neutrino @@ -362,7 +364,7 @@ def telnet_test(host, port, user, password, timeout): time.sleep(timeout) yield tn.read_very_eager() tn.close() - yield "Done" + yield "Done!" if __name__ == "__main__": diff --git a/app/eparser/__init__.py b/app/eparser/__init__.py index 6062f1c6..fff031a5 100644 --- a/app/eparser/__init__.py +++ b/app/eparser/__init__.py @@ -1,5 +1,5 @@ from app.commons import run_task -from app.properties import Profile +from app.settings import Profile from .ecommons import Service, Satellite, Transponder, Bouquet, Bouquets, is_transponder_valid from .enigma.blacklist import get_blacklist, write_blacklist from .enigma.bouquets import get_bouquets as get_enigma_bouquets, write_bouquets as write_enigma_bouquets, to_bouquet_id diff --git a/app/eparser/iptv.py b/app/eparser/iptv.py index 2c67a53f..b47a8967 100644 --- a/app/eparser/iptv.py +++ b/app/eparser/iptv.py @@ -3,7 +3,7 @@ import re import urllib.request from enum import Enum -from app.properties import Profile +from app.settings import Profile from app.ui.uicommons import IPTV_ICON from .ecommons import BqServiceType, Service diff --git a/app/properties.py b/app/properties.py deleted file mode 100644 index 370a9a82..00000000 --- a/app/properties.py +++ /dev/null @@ -1,67 +0,0 @@ -import json -import os -from enum import Enum -from pathlib import Path - -CONFIG_PATH = str(Path.home()) + "/.config/demon-editor/" -CONFIG_FILE = CONFIG_PATH + "config.json" -DATA_PATH = os.path.expanduser("~") + "/data/" - - -class Profile(Enum): - """ Profiles for settings """ - ENIGMA_2 = "0" - NEUTRINO_MP = "1" - - -def get_config(): - os.makedirs(os.path.dirname(CONFIG_PATH), exist_ok=True) # create dir if not exist - os.makedirs(os.path.dirname(DATA_PATH), exist_ok=True) - - if not os.path.isfile(CONFIG_FILE) or os.stat(CONFIG_FILE).st_size == 0: - reset_config() - - with open(CONFIG_FILE, "r") as config_file: - return json.load(config_file) - - -def reset_config(): - with open(CONFIG_FILE, "w") as default_config_file: - json.dump(get_default_settings(), default_config_file) - - -def write_config(config): - assert isinstance(config, dict) - with open(CONFIG_FILE, "w") as config_file: - json.dump(config, config_file) - - -def get_default_settings(): - return { - Profile.ENIGMA_2.value: { - "host": "127.0.0.1", "port": "21", "user": "root", "password": "root", - "http_user": "root", "http_password": "", "http_port": "80", "http_timeout": 5, - "telnet_user": "root", "telnet_password": "", "telnet_port": "23", "telnet_timeout": 5, - "services_path": "/etc/enigma2/", "user_bouquet_path": "/etc/enigma2/", - "satellites_xml_path": "/etc/tuxbox/", "data_dir_path": DATA_PATH + "enigma2/", - "picons_path": "/usr/share/enigma2/picon", "picons_dir_path": DATA_PATH + "enigma2/picons/", - "backup_dir_path": DATA_PATH + "enigma2/backup/", - "backup_before_save": True, "backup_before_downloading": True, - "v5_support": False, "http_api_support": False, "enable_yt_dl": False, "enable_send_to": False, - "use_colors": True, "new_color": "rgb(255,230,204)", "extra_color": "rgb(179,230,204)", - "fav_click_mode": 0}, - Profile.NEUTRINO_MP.value: { - "host": "127.0.0.1", "port": "21", "user": "root", "password": "root", - "http_user": "", "http_password": "", "http_port": "80", "http_timeout": 2, - "telnet_user": "root", "telnet_password": "", "telnet_port": "23", "telnet_timeout": 1, - "services_path": "/var/tuxbox/config/zapit/", "user_bouquet_path": "/var/tuxbox/config/zapit/", - "satellites_xml_path": "/var/tuxbox/config/", "data_dir_path": DATA_PATH + "neutrino/", - "picons_path": "/usr/share/tuxbox/neutrino/icons/logo/", "picons_dir_path": DATA_PATH + "neutrino/picons/", - "backup_dir_path": DATA_PATH + "neutrino/backup/", - "backup_before_save": True, "backup_before_downloading": True, - "fav_click_mode": 0}, - "profile": Profile.ENIGMA_2.value} - - -if __name__ == "__main__": - pass diff --git a/app/settings.py b/app/settings.py new file mode 100644 index 00000000..a73cd704 --- /dev/null +++ b/app/settings.py @@ -0,0 +1,365 @@ +import json +import os +from pprint import pformat +from textwrap import dedent +from enum import Enum +from pathlib import Path + +CONFIG_PATH = str(Path.home()) + "/.config/demon-editor/" +CONFIG_FILE = CONFIG_PATH + "config.json" +DATA_PATH = "data/" + + +class Profile(Enum): + """ Profiles for settings """ + ENIGMA_2 = "0" + NEUTRINO_MP = "1" + + +class Settings: + __INSTANCE = None + + def __init__(self): + self._config = get_config() + self._current_profile = Profile(self._config.get("profile")) + self._current_profile_options = self._config.get(self._current_profile.value) + + def __str__(self): + return dedent(""" Current profile: {} + Current profile options: + {} + Full config: + {} + """).format(self._current_profile, + pformat(self._current_profile_options), + pformat(self._config)) + + @classmethod + def get_instance(cls): + if not cls.__INSTANCE: + cls.__INSTANCE = Settings() + return cls.__INSTANCE + + def save(self): + write_config(self._config) + + def reset(self, force_write=False): + def_settings = get_default_settings() + for p in Profile: + current = self._config.get(p.value) + default = def_settings.get(p.value) + for k in default: + current[k] = default.get(k) + + if force_write: + write_config(get_default_settings()) + + def add(self, name, value): + """ Adds extra options """ + self._config[name] = value + + def get(self, name): + """ Returns extra options """ + return self._config.get(name, None) + + def get_default(self, name): + """ Returns default value of the option """ + return get_default_settings().get(self._current_profile.value).get(name) + + @property + def presets(self): + raise NotImplementedError + + @presets.setter + def presets(self, name): + raise NotImplementedError + + @property + def profile(self): + return self._current_profile + + @profile.setter + def profile(self, prf): + self._current_profile = prf + self._config["profile"] = prf.value + self._current_profile_options = self._config.get(prf.value) + + @property + def host(self): + return self._current_profile_options.get("host", self.get_default("host")) + + @host.setter + def host(self, value): + self._current_profile_options["host"] = value + + @property + def port(self): + return self._current_profile_options.get("port", self.get_default("port")) + + @port.setter + def port(self, value): + self._current_profile_options["port"] = value + + @property + def user(self): + return self._current_profile_options.get("user", self.get_default("user")) + + @user.setter + def user(self, value): + self._current_profile_options["user"] = value + + @property + def password(self): + return self._current_profile_options.get("password", self.get_default("password")) + + @password.setter + def password(self, value): + self._current_profile_options["password"] = value + + @property + def http_user(self): + return self._current_profile_options.get("http_user", self.get_default("http_user")) + + @http_user.setter + def http_user(self, value): + self._current_profile_options["http_user"] = value + + @property + def http_password(self): + return self._current_profile_options.get("http_password", self.get_default("http_password")) + + @http_password.setter + def http_password(self, value): + self._current_profile_options["http_password"] = value + + @property + def http_port(self): + return self._current_profile_options.get("http_port", self.get_default("http_port")) + + @http_port.setter + def http_port(self, value): + self._current_profile_options["http_port"] = value + + @property + def http_timeout(self): + return self._current_profile_options.get("http_timeout", self.get_default("http_timeout")) + + @http_timeout.setter + def http_timeout(self, value): + self._current_profile_options["http_timeout"] = value + + @property + def telnet_user(self): + return self._current_profile_options.get("telnet_user", self.get_default("telnet_user")) + + @telnet_user.setter + def telnet_user(self, value): + self._current_profile_options["telnet_user"] = value + + @property + def telnet_password(self): + return self._current_profile_options.get("telnet_password", self.get_default("telnet_password")) + + @telnet_password.setter + def telnet_password(self, value): + self._current_profile_options["telnet_password"] = value + + @property + def telnet_port(self): + return self._current_profile_options.get("telnet_port", self.get_default("telnet_port")) + + @telnet_port.setter + def telnet_port(self, value): + self._current_profile_options["telnet_port"] = value + + @property + def telnet_timeout(self): + return self._current_profile_options.get("telnet_timeout", self.get_default("telnet_timeout")) + + @telnet_timeout.setter + def telnet_timeout(self, value): + self._current_profile_options["telnet_timeout"] = value + + @property + def services_path(self): + return self._current_profile_options.get("services_path", self.get_default("services_path")) + + @services_path.setter + def services_path(self, value): + self._current_profile_options["services_path"] = value + + @property + def user_bouquet_path(self): + return self._current_profile_options.get("user_bouquet_path", self.get_default("user_bouquet_path")) + + @user_bouquet_path.setter + def user_bouquet_path(self, value): + self._current_profile_options["user_bouquet_path"] = value + + @property + def satellites_xml_path(self): + return self._current_profile_options.get("satellites_xml_path", self.get_default("satellites_xml_path")) + + @satellites_xml_path.setter + def satellites_xml_path(self, value): + self._current_profile_options["satellites_xml_path"] = value + + @property + def data_dir_path(self): + return self._current_profile_options.get("data_dir_path", self.get_default("data_dir_path")) + + @data_dir_path.setter + def data_dir_path(self, value): + self._current_profile_options["data_dir_path"] = value + + @property + def picons_path(self): + return self._current_profile_options.get("picons_path", self.get_default("picons_path")) + + @picons_path.setter + def picons_path(self, value): + self._current_profile_options["picons_path"] = value + + @property + def picons_dir_path(self): + return self._current_profile_options.get("picons_dir_path", self.get_default("picons_dir_path")) + + @picons_dir_path.setter + def picons_dir_path(self, value): + self._current_profile_options["picons_dir_path"] = value + + @property + def backup_dir_path(self): + return self._current_profile_options.get("backup_dir_path", self.get_default("backup_dir_path")) + + @backup_dir_path.setter + def backup_dir_path(self, value): + self._current_profile_options["backup_dir_path"] = value + + @property + def backup_before_save(self): + return self._current_profile_options.get("backup_before_save", self.get_default("backup_before_save")) + + @backup_before_save.setter + def backup_before_save(self, value): + self._current_profile_options["backup_before_save"] = value + + @property + def backup_before_downloading(self): + return self._current_profile_options.get("backup_before_downloading", + self.get_default("backup_before_downloading")) + + @backup_before_downloading.setter + def backup_before_downloading(self, value): + self._current_profile_options["backup_before_downloading"] = value + + @property + def v5_support(self): + return self._current_profile_options.get("v5_support", self.get_default("v5_support")) + + @v5_support.setter + def v5_support(self, value): + self._current_profile_options["v5_support"] = value + + @property + def http_api_support(self): + return self._current_profile_options.get("http_api_support", self.get_default("http_api_support")) + + @http_api_support.setter + def http_api_support(self, value): + self._current_profile_options["http_api_support"] = value + + @property + def enable_yt_dl(self): + return self._current_profile_options.get("enable_yt_dl", self.get_default("enable_yt_dl")) + + @enable_yt_dl.setter + def enable_yt_dl(self, value): + self._current_profile_options["enable_yt_dl"] = value + + @property + def enable_send_to(self): + return self._current_profile_options.get("enable_send_to", self.get_default("enable_send_to")) + + @enable_send_to.setter + def enable_send_to(self, value): + self._current_profile_options["enable_send_to"] = value + + @property + def use_colors(self): + return self._current_profile_options.get("use_colors", self.get_default("use_colors")) + + @use_colors.setter + def use_colors(self, value): + self._current_profile_options["use_colors"] = value + + @property + def new_color(self): + return self._current_profile_options.get("new_color", self.get_default("new_color")) + + @new_color.setter + def new_color(self, value): + self._current_profile_options["new_color"] = value + + @property + def extra_color(self): + return self._current_profile_options.get("extra_color", self.get_default("extra_color")) + + @extra_color.setter + def extra_color(self, value): + self._current_profile_options["extra_color"] = value + + @property + def fav_click_mode(self): + return self._current_profile_options.get("fav_click_mode", self.get_default("fav_click_mode")) + + @fav_click_mode.setter + def fav_click_mode(self, value): + self._current_profile_options["fav_click_mode"] = value + + +def get_config(): + os.makedirs(os.path.dirname(CONFIG_PATH), exist_ok=True) # create dir if not exist + os.makedirs(os.path.dirname(DATA_PATH), exist_ok=True) + + if not os.path.isfile(CONFIG_FILE) or os.stat(CONFIG_FILE).st_size == 0: + write_config(get_default_settings()) + + with open(CONFIG_FILE, "r") as config_file: + return json.load(config_file) + + +def write_config(config): + with open(CONFIG_FILE, "w") as config_file: + json.dump(config, config_file, indent=" ") + + +def get_default_settings(): + return { + Profile.ENIGMA_2.value: { + "host": "127.0.0.1", "port": "21", "user": "root", "password": "root", + "http_user": "root", "http_password": "", "http_port": "80", "http_timeout": 5, + "telnet_user": "root", "telnet_password": "", "telnet_port": "23", "telnet_timeout": 5, + "services_path": "/etc/enigma2/", "user_bouquet_path": "/etc/enigma2/", + "satellites_xml_path": "/etc/tuxbox/", "data_dir_path": DATA_PATH + "enigma2/", + "picons_path": "/usr/share/enigma2/picon", "picons_dir_path": DATA_PATH + "enigma2/picons/", + "backup_dir_path": DATA_PATH + "enigma2/backup/", + "backup_before_save": True, "backup_before_downloading": True, + "v5_support": False, "http_api_support": False, "enable_yt_dl": False, "enable_send_to": False, + "use_colors": True, "new_color": "rgb(255,230,204)", "extra_color": "rgb(179,230,204)", + "fav_click_mode": 0}, + Profile.NEUTRINO_MP.value: { + "host": "127.0.0.1", "port": "21", "user": "root", "password": "root", + "http_user": "", "http_password": "", "http_port": "80", "http_timeout": 2, + "telnet_user": "root", "telnet_password": "", "telnet_port": "23", "telnet_timeout": 1, + "services_path": "/var/tuxbox/config/zapit/", "user_bouquet_path": "/var/tuxbox/config/zapit/", + "satellites_xml_path": "/var/tuxbox/config/", "data_dir_path": DATA_PATH + "neutrino/", + "picons_path": "/usr/share/tuxbox/neutrino/icons/logo/", "picons_dir_path": DATA_PATH + "neutrino/picons/", + "backup_dir_path": DATA_PATH + "neutrino/backup/", + "backup_before_save": True, "backup_before_downloading": True, + "fav_click_mode": 0}, + "profile": Profile.ENIGMA_2.value} + + +if __name__ == "__main__": + pass diff --git a/app/tools/media.py b/app/tools/media.py index b85d2d43..2df7c3ea 100644 --- a/app/tools/media.py +++ b/app/tools/media.py @@ -1,36 +1,37 @@ -import ctypes import sys - from app.commons import run_task, log -from app.tools import vlc -from app.tools.vlc import EventType class Player: - _VLC_INSTANCE = None + __VLC_INSTANCE = None - def __init__(self, rewind_callback=None, position_callback=None): - self._is_playing = False - self._player = self.get_vlc_instance() - ev_mgr = self._player.event_manager() + def __init__(self, rewind_callback, position_callback): + try: + from app.tools import vlc + from app.tools.vlc import EventType + except OSError as e: + log("{}: Load library error: {}".format(__class__.__name__, e)) + else: + self._is_playing = False + args = "--quiet {}".format("" if sys.platform == "darwin" else "--no-xlib") + self._player = vlc.Instance(args).media_player_new() + ev_mgr = self._player.event_manager() - if rewind_callback: - # TODO look other EventType options - ev_mgr.event_attach(EventType.MediaPlayerBuffering, - lambda e, p: rewind_callback(p.get_media().get_duration()), - self._player) - if position_callback: - ev_mgr.event_attach(EventType.MediaPlayerTimeChanged, - lambda e, p: position_callback(p.get_time()), - self._player) + if rewind_callback: + # TODO look other EventType options + ev_mgr.event_attach(EventType.MediaPlayerBuffering, + lambda et, p: rewind_callback(p.get_media().get_duration()), + self._player) + if position_callback: + ev_mgr.event_attach(EventType.MediaPlayerTimeChanged, + lambda et, p: position_callback(p.get_time()), + self._player) - @staticmethod - def get_vlc_instance(): - if Player._VLC_INSTANCE: - return Player._VLC_INSTANCE - args = "--quiet {}".format("" if sys.platform == "darwin" else "--no-xlib") - _VLC_INSTANCE = vlc.Instance(args).media_player_new() - return _VLC_INSTANCE + @classmethod + def get_instance(cls, rewind_callback=None, position_callback=None): + if not cls.__VLC_INSTANCE: + cls.__VLC_INSTANCE = Player(rewind_callback, position_callback) + return cls.__VLC_INSTANCE @run_task def play(self, mrl=None): @@ -68,6 +69,7 @@ class Player: https://github.com/oaubert/python-vlc/tree/master/examples """ try: + import ctypes g_dll = ctypes.CDLL("libgdk-3.0.dylib") except OSError as e: log("{}: Load library error: {}".format(__class__.__name__, e)) diff --git a/app/tools/picons.py b/app/tools/picons.py index 01ad980b..87f3d5a4 100644 --- a/app/tools/picons.py +++ b/app/tools/picons.py @@ -7,7 +7,7 @@ from collections import namedtuple from html.parser import HTMLParser from app.commons import run_task -from app.properties import Profile +from app.settings import Profile _ENIGMA2_PICON_KEY = "{:X}:{:X}:{}" _NEUTRINO_PICON_KEY = "{:x}{:04x}{:04x}.png" diff --git a/app/ui/backup.py b/app/ui/backup.py index 3e11aadd..7aca7613 100644 --- a/app/ui/backup.py +++ b/app/ui/backup.py @@ -7,7 +7,7 @@ from datetime import datetime from enum import Enum from app.commons import run_idle -from app.properties import Profile +from app.settings import Profile from app.ui.dialogs import show_dialog, DialogType from app.ui.main_helper import append_text_to_tview from .uicommons import Gtk, Gdk, UI_RESOURCES_PATH, KeyboardKey @@ -19,7 +19,7 @@ class RestoreType(Enum): class BackupDialog: - def __init__(self, transient, options, profile, callback): + def __init__(self, transient, settings, callback): handlers = {"on_restore_bouquets": self.on_restore_bouquets, "on_restore_all": self.on_restore_all, "on_remove": self.on_remove, @@ -35,10 +35,10 @@ class BackupDialog: builder.add_from_file(UI_RESOURCES_PATH + "backup_dialog.glade") builder.connect_signals(handlers) - self._options = options.get(profile.value) - self._data_path = self._options.get("data_dir_path", "") - self._backup_path = self._options.get("backup_dir_path", self._data_path + "backup/") - self._profile = profile + self._settings = settings + self._profile = settings.profile + self._data_path = self._settings.data_dir_path + self._backup_path = self._settings.backup_dir_path or self._data_path + "backup/" self._open_data_callback = callback self._dialog_window = builder.get_object("dialog_window") self._dialog_window.set_transient_for(transient) @@ -50,7 +50,7 @@ class BackupDialog: self._info_bar = builder.get_object("info_bar") self._message_label = builder.get_object("message_label") # Setting the last size of the dialog window if it was saved - window_size = self._options.get("backup_tool_window_size", None) + window_size = self._settings.get("backup_tool_window_size") if window_size: self._dialog_window.resize(*window_size) @@ -166,8 +166,8 @@ class BackupDialog: self._open_data_callback(self._data_path) def on_resize(self, window): - if self._options: - self._options["backup_tool_window_size"] = window.get_size() + if self._settings: + self._settings.add("backup_tool_window_size", window.get_size()) def on_key_release(self, view, event): """ Handling keystrokes """ diff --git a/app/ui/dialogs.py b/app/ui/dialogs.py index e224f929..596b89f6 100644 --- a/app/ui/dialogs.py +++ b/app/ui/dialogs.py @@ -67,12 +67,12 @@ class WaitDialog: self._dialog.destroy() -def show_dialog(dialog_type: DialogType, transient, text=None, options=None, action_type=None, file_filter=None): +def show_dialog(dialog_type: DialogType, transient, text=None, settings=None, action_type=None, file_filter=None): """ Shows dialogs by name """ if dialog_type in (DialogType.INFO, DialogType.ERROR): return get_message_dialog(transient, dialog_type, Gtk.ButtonsType.OK, text) - elif dialog_type is DialogType.CHOOSER and options: - return get_file_chooser_dialog(transient, text, options, action_type, file_filter) + elif dialog_type is DialogType.CHOOSER and settings: + return get_file_chooser_dialog(transient, text, settings, action_type, file_filter) elif dialog_type is DialogType.INPUT: return get_input_dialog(transient, text) elif dialog_type is DialogType.QUESTION: @@ -81,34 +81,30 @@ def show_dialog(dialog_type: DialogType, transient, text=None, options=None, act return get_about_dialog(transient) -def get_chooser_dialog(transient, options, pattern, name): +def get_chooser_dialog(transient, settings, 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, + settings=settings, action_type=Gtk.FileChooserAction.OPEN, file_filter=file_filter) -def get_file_chooser_dialog(transient, text, options, action_type, file_filter): - dialog = Gtk.FileChooserNative() - dialog.set_title(get_message(text) if text else "") - dialog.set_transient_for(transient) - dialog.set_action(action_type if action_type is not None else Gtk.FileChooserAction.SELECT_FOLDER) - dialog.set_create_folders(False) - dialog.set_modal(True) - +def get_file_chooser_dialog(transient, text, settings, action_type, file_filter): + dialog = Gtk.FileChooserDialog(get_message(text) if text else "", transient, + action_type if action_type is not None else Gtk.FileChooserAction.SELECT_FOLDER, + (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, Gtk.STOCK_OPEN, Gtk.ResponseType.OK), + use_header_bar=IS_GNOME_SESSION) if file_filter is not None: dialog.add_filter(file_filter) - path = options.get("data_dir_path") + path = settings.data_dir_path dialog.set_current_folder(path) response = dialog.run() - - if response == Gtk.ResponseType.ACCEPT: + if response == Gtk.ResponseType.OK: if dialog.get_filename(): path = dialog.get_filename() if action_type is not Gtk.FileChooserAction.OPEN: @@ -167,8 +163,7 @@ def get_dialog_from_xml(dialog_type, transient, use_header=0, title=""): def get_message(message): """ returns translated message """ - # return locale.dgettext(TEXT_DOMAIN, message) - return message + return locale.dgettext(TEXT_DOMAIN, message) @lru_cache(maxsize=5) diff --git a/app/ui/download_dialog.py b/app/ui/download_dialog.py index 801ee934..91d0d9c5 100644 --- a/app/ui/download_dialog.py +++ b/app/ui/download_dialog.py @@ -2,7 +2,7 @@ from gi.repository import GLib from app.commons import run_idle, run_task from app.connections import download_data, DownloadType, upload_data -from app.properties import Profile, get_config +from app.settings import Profile from app.ui.backup import backup_data, restore_data from app.ui.main_helper import append_text_to_tview from app.ui.settings_dialog import show_settings_dialog @@ -11,12 +11,11 @@ from .dialogs import show_dialog, DialogType, get_message class DownloadDialog: - def __init__(self, transient, properties, open_data_callback, update_settings_callback, profile=Profile.ENIGMA_2): - self._profile_properties = properties.get(profile.value) - self._properties = properties + def __init__(self, transient, settings, open_data_callback, update_settings_callback): + self._profile = settings.profile + self._settings = settings self._open_data_callback = open_data_callback self._update_settings_callback = update_settings_callback - self._profile = profile handlers = {"on_receive": self.on_receive, "on_send": self.on_send, @@ -53,14 +52,14 @@ class DownloadDialog: self._use_http_switch = builder.get_object("use_http_switch") self._http_radio_button = builder.get_object("http_radio_button") self._use_http_box = builder.get_object("use_http_box") - self.init_properties() + self.init_settings() def show(self): self._dialog_window.show() - def init_properties(self): - self._host_entry.set_text(self._profile_properties["host"]) - self._data_path_entry.set_text(self._profile_properties["data_dir_path"]) + def init_settings(self): + self._host_entry.set_text(self._settings.host) + self._data_path_entry.set_text(self._settings.data_dir_path) is_enigma = self._profile is Profile.ENIGMA_2 self._webtv_radio_button.set_visible(not is_enigma) self._http_radio_button.set_visible(is_enigma) @@ -93,33 +92,34 @@ class DownloadDialog: if button.get_active(): label = button.get_label() if label == "Telnet": - self._login_entry.set_text(self._profile_properties.get("telnet_user", "")) - self._password_entry.set_text(self._profile_properties.get("telnet_password", "")) - self._port_entry.set_text(self._profile_properties.get("telnet_port", "")) - self._timeout_entry.set_text(str(self._profile_properties.get("telnet_timeout", 0))) + self._login_entry.set_text(self._settings.telnet_user) + self._password_entry.set_text(self._settings.telnet_password) + self._port_entry.set_text(self._settings.telnet_port) + self._timeout_entry.set_text(str(self._settings.telnet_timeout)) elif label == "HTTP": - self._login_entry.set_text(self._profile_properties.get("http_user", "root")) - self._password_entry.set_text(self._profile_properties.get("http_password", "")) - self._port_entry.set_text(self._profile_properties.get("http_port", "")) - self._timeout_entry.set_text(str(self._profile_properties.get("http_timeout", 0))) + self._login_entry.set_text(self._settings.http_user) + self._password_entry.set_text(self._settings.http_password) + self._port_entry.set_text(self._settings.http_port) + self._timeout_entry.set_text(str(self._settings.http_timeout)) elif label == "FTP": - self._login_entry.set_text(self._profile_properties.get("user", "")) - self._password_entry.set_text(self._profile_properties.get("password", "")) - self._port_entry.set_text(self._profile_properties.get("port", "")) + self._login_entry.set_text(self._settings.user) + self._password_entry.set_text(self._settings.password) + self._port_entry.set_text(self._settings.port) self._timeout_entry.set_text("") self._current_property = label def on_preferences(self, item): - show_settings_dialog(self._dialog_window, self._properties) - self._profile = Profile(self._properties.get("profile", Profile.ENIGMA_2.value)) - self._profile_properties = get_config().get(self._profile.value) - self.init_properties() - self._update_settings_callback() + response = show_settings_dialog(self._dialog_window, self._settings) + if response != Gtk.ResponseType.CANCEL: + self._profile = self._settings.profile + self.init_settings() + gen = self._update_settings_callback() + GLib.idle_add(lambda: next(gen, False), priority=GLib.PRIORITY_LOW) - for button in self._settings_buttons_box.get_children(): - if button.get_active(): - self.on_settings_button(button) - break + for button in self._settings_buttons_box.get_children(): + if button.get_active(): + self.on_settings_button(button) + break def on_info_bar_close(self, bar=None, resp=None): self._info_bar.set_visible(False) @@ -129,21 +129,20 @@ class DownloadDialog: """ Download/upload data from/to receiver """ self._expander.set_expanded(True) self.clear_output() - backup, backup_src, data_path = self._profile_properties.get("backup_before_downloading", True), None, None + backup, backup_src, data_path = self._settings.backup_before_downloading, None, None try: if download: if backup and d_type is not DownloadType.SATELLITES: - data_path = self._profile_properties.get("data_dir_path", self._data_path_entry.get_text()) - backup_path = self._profile_properties.get("backup_dir_path", data_path + "backup/") + data_path = self._settings.data_dir_path or self._data_path_entry.get_text() + backup_path = self._settings.backup_dir_path or data_path + "backup/" backup_src = backup_data(data_path, backup_path, d_type is DownloadType.ALL) - download_data(properties=self._profile_properties, download_type=d_type, callback=self.append_output) + download_data(settings=self._settings, download_type=d_type, callback=self.append_output) else: self.show_info_message(get_message("Please, wait..."), Gtk.MessageType.INFO) - upload_data(properties=self._profile_properties, + upload_data(settings=self._settings, download_type=d_type, remove_unused=self._remove_unused_check_button.get_active(), - profile=self._profile, callback=self.append_output, done_callback=lambda: self.show_info_message(get_message("Done!"), Gtk.MessageType.INFO), use_http=self._use_http_switch.get_active()) diff --git a/app/ui/epg_dialog.py b/app/ui/epg_dialog.py index 7754cdf2..de6e49c3 100644 --- a/app/ui/epg_dialog.py +++ b/app/ui/epg_dialog.py @@ -25,7 +25,7 @@ class RefsSource(Enum): class EpgDialog: - def __init__(self, transient, options, services, bouquet, fav_model, bouquet_name): + def __init__(self, transient, settings, services, bouquet, fav_model, bouquet_name): handlers = {"on_close_dialog": self.on_close_dialog, "on_apply": self.on_apply, @@ -56,7 +56,7 @@ class EpgDialog: self._services = {} self._ex_services = services self._ex_fav_model = fav_model - self._options = options + self._settings = settings self._bouquet = bouquet self._bouquet_name = bouquet_name self._current_ref = [] @@ -106,7 +106,7 @@ class EpgDialog: self._update_on_start_switch = builder.get_object("update_on_start_switch") self._epg_dat_source_box = builder.get_object("epg_dat_source_box") # Setting the last size of the dialog window - window_size = self._options.get("epg_tool_window_size", None) + window_size = self._settings.get("epg_tool_window_size") if window_size: self._dialog.resize(*window_size) @@ -288,7 +288,7 @@ class EpgDialog: @run_idle def on_save_to_xml(self, item): - response = show_dialog(DialogType.CHOOSER, self._dialog, options=self._options) + response = show_dialog(DialogType.CHOOSER, self._dialog, settings=self._settings) if response in (Gtk.ResponseType.CANCEL, Gtk.ResponseType.DELETE_EVENT): return @@ -483,10 +483,10 @@ class EpgDialog: # ***************** Options *********************# def init_options(self): - epg_dat_path = self._options.get("data_dir_path", "") + "epg/" + epg_dat_path = self._settings.data_dir_path + "epg/" self._epg_dat_path_entry.set_text(epg_dat_path) default_epg_data_stb_path = "/etc/enigma2" - epg_options = self._options.get("epg_options", None) + epg_options = self._settings.get("epg_options") if epg_options: self._refs_source = RefsSource.XML if epg_options.get("xml_source", False) else RefsSource.SERVICES self._xml_radiobutton.set_active(self._refs_source is RefsSource.XML) @@ -514,11 +514,11 @@ class EpgDialog: "epg_dat_path": self._epg_dat_path_entry.get_text(), "epg_dat_stb_path": self._epg_dat_stb_path_entry.get_text(), "epg_data_update_on_start": self._update_on_start_switch.get_active()} - self._options["epg_options"] = epg_options + self._settings.add("epg_options", epg_options) def on_resize(self, window): - if self._options: - self._options["epg_tool_window_size"] = window.get_size() + if self._settings: + self._settings.add("epg_tool_window_size", window.get_size()) def on_names_source_changed(self, button): self._refs_source = RefsSource.XML if button.get_active() else RefsSource.SERVICES @@ -536,13 +536,13 @@ class EpgDialog: self._xml_chooser_button.set_sensitive(not state) def on_field_icon_press(self, entry, icon, event_button): - update_entry_data(entry, self._dialog, self._options) + update_entry_data(entry, self._dialog, self._settings) # ***************** Downloads *********************# def download_epg_from_stb(self): """ Download the epg.dat file via ftp from the receiver. """ - download_data(properties=self._options, download_type=DownloadType.EPG, callback=print) + download_data(settings=self._settings, download_type=DownloadType.EPG, callback=print) if __name__ == "__main__": diff --git a/app/ui/import_dialog.glade b/app/ui/import_dialog.glade index b35db491..fe01dbcb 100644 --- a/app/ui/import_dialog.glade +++ b/app/ui/import_dialog.glade @@ -89,6 +89,7 @@ Author: Dmitriy Yefremov True dialog center + True diff --git a/app/ui/imports.py b/app/ui/imports.py index a957247d..bcbf979c 100644 --- a/app/ui/imports.py +++ b/app/ui/imports.py @@ -6,17 +6,18 @@ from app.eparser import get_bouquets, get_services from app.eparser.ecommons import BqType, BqServiceType, Bouquet from app.eparser.enigma.bouquets import get_bouquet from app.eparser.neutrino.bouquets import parse_webtv, parse_bouquets as get_neutrino_bouquets -from app.properties import Profile +from app.settings import Profile from app.ui.dialogs import show_dialog, DialogType, get_chooser_dialog, get_message from app.ui.main_helper import on_popup_menu from .uicommons import Gtk, UI_RESOURCES_PATH, KeyboardKey, Column -def import_bouquet(transient, profile, model, path, options, services, appender): +def import_bouquet(transient, model, path, settings, services, appender): """ Import of single bouquet """ itr = model.get_iter(path) bq_type = BqType(model.get(itr, Column.BQ_TYPE)[0]) pattern, f_pattern = None, None + profile = settings.profile if profile is Profile.ENIGMA_2: pattern = ".{}".format(bq_type.value) @@ -29,7 +30,7 @@ def import_bouquet(transient, profile, model, path, options, services, appender) elif bq_type is BqType.WEBTV: f_pattern = "webtv.xml" - file_path = get_chooser_dialog(transient, options, f_pattern, "bouquet files") + file_path = get_chooser_dialog(transient, settings, f_pattern, "bouquet files") if file_path == Gtk.ResponseType.CANCEL: return @@ -56,7 +57,7 @@ def import_bouquet(transient, profile, model, path, options, services, appender) else: bqs = get_neutrino_bouquets(file_path, "", bq_type.value) file_path = "{}/".format(Path(file_path).parent) - ImportDialog(transient, file_path, profile, services.keys(), lambda b, s: appender(b), (bqs,)).show() + ImportDialog(transient, file_path, settings, services.keys(), lambda b, s: appender(b), (bqs,)).show() def get_enigma2_bouquet(path): @@ -68,7 +69,7 @@ def get_enigma2_bouquet(path): class ImportDialog: - def __init__(self, transient, path, profile, service_ids, appender, bouquets=None): + def __init__(self, transient, path, settings, service_ids, appender, bouquets=None): handlers = {"on_import": self.on_import, "on_cursor_changed": self.on_cursor_changed, "on_info_button_toggled": self.on_info_button_toggled, @@ -77,6 +78,7 @@ class ImportDialog: "on_select_all": self.on_select_all, "on_unselect_all": self.on_unselect_all, "on_popup_menu": on_popup_menu, + "on_resize": self.on_resize, "on_key_press": self.on_key_press} builder = Gtk.Builder() @@ -88,7 +90,8 @@ class ImportDialog: self._services = {} self._service_ids = service_ids self._append = appender - self._profile = profile + self._profile = settings.profile + self._settings = settings self._bouquets = bouquets self._dialog_window = builder.get_object("dialog_window") @@ -101,6 +104,9 @@ class ImportDialog: self._info_check_button = builder.get_object("info_check_button") self._info_bar = builder.get_object("info_bar") self._message_label = builder.get_object("message_label") + window_size = self._settings.get("import_dialog_window_size") + if window_size: + self._dialog_window.resize(*window_size) self.init_data(path) @@ -206,6 +212,10 @@ class ImportDialog: def update_selection(self, view, select): view.get_model().foreach(lambda mod, path, itr: mod.set_value(itr, 2, select)) + def on_resize(self, window): + if self._settings: + self._settings.add("import_dialog_window_size", window.get_size()) + def on_key_press(self, view, event): """ Handling keystrokes """ key_code = event.hardware_keycode diff --git a/app/ui/iptv.py b/app/ui/iptv.py index 9e02667f..be1fe941 100644 --- a/app/ui/iptv.py +++ b/app/ui/iptv.py @@ -10,7 +10,7 @@ from gi.repository import GLib from app.commons import run_idle, run_task, log from app.eparser.ecommons import BqServiceType, Service from app.eparser.iptv import NEUTRINO_FAV_ID_FORMAT, StreamType, ENIGMA2_FAV_ID_FORMAT, get_fav_id, MARKER_FORMAT -from app.properties import Profile +from app.settings import Profile from app.tools.yt import YouTube, PlayListParser from .dialogs import Action, show_dialog, DialogType, get_dialogs_string, get_message from .main_helper import get_base_model, get_iptv_url, on_popup_menu diff --git a/app/ui/main_app_window.py b/app/ui/main_app_window.py index 5ecd2a2e..41ab9aac 100644 --- a/app/ui/main_app_window.py +++ b/app/ui/main_app_window.py @@ -16,7 +16,7 @@ from app.eparser.ecommons import CAS, Flag, BouquetService from app.eparser.enigma.bouquets import BqServiceType from app.eparser.iptv import export_to_m3u from app.eparser.neutrino.bouquets import BqType -from app.properties import get_config, write_config, Profile +from app.settings import Profile, Settings from app.tools.media import Player from app.ui.epg_dialog import EpgDialog from app.ui.transmitter import LinksTransmitter @@ -26,7 +26,7 @@ from .download_dialog import DownloadDialog from .iptv import IptvDialog, SearchUnavailableDialog, IptvListConfigurationDialog, YtListImportDialog from .search import SearchProvider from .uicommons import Gtk, Gdk, UI_RESOURCES_PATH, LOCKED_ICON, HIDE_ICON, IPTV_ICON, MOVE_KEYS, KeyboardKey, Column, \ - EXTRA_COLOR, NEW_COLOR, FavClickMode + FavClickMode from .dialogs import show_dialog, DialogType, get_chooser_dialog, WaitDialog, get_message from .main_helper import insert_marker, move_items, rename, ViewTarget, set_flags, locate_in_services, \ scroll_to, get_base_model, update_picons_data, copy_picon_reference, assign_picon, remove_picon, \ @@ -151,9 +151,9 @@ class Application(Gtk.Application): "on_create_bouquet_for_current_type": self.on_create_bouquet_for_current_type, "on_create_bouquet_for_each_type": self.on_create_bouquet_for_each_type} - self._options = get_config() - self._profile = self._options.get("profile") - os.makedirs(os.path.dirname(self._options.get(self._profile).get("data_dir_path")), exist_ok=True) + self._settings = Settings.get_instance() + self._profile = self._settings.profile + os.makedirs(os.path.dirname(self._settings.data_dir_path), exist_ok=True) # Used for copy/paste. When adding the previous data will not be deleted. # Clearing only after the insertion! self._rows_buffer = [] @@ -184,12 +184,12 @@ class Application(Gtk.Application): builder = Gtk.Builder() builder.set_translation_domain("demon-editor") builder.add_from_file(UI_RESOURCES_PATH + "main_window.glade") - builder.connect_signals(self._handlers) + builder.connect_signals(handlers) self._main_window = builder.get_object("main_window") - self._main_window_size = self._options.get("window_size", None) + main_window_size = self._settings.get("window_size") # Setting the last size of the window if it was saved - if self._main_window_size: - self._main_window.resize(*self._main_window_size) + if main_window_size: + self._main_window.resize(*main_window_size) self._services_view = builder.get_object("services_tree_view") self._fav_view = builder.get_object("fav_tree_view") self._bouquets_view = builder.get_object("bouquets_tree_view") @@ -211,7 +211,7 @@ class Application(Gtk.Application): self._app_info_box.bind_property("visible", builder.get_object("toolbar_extra_item"), "visible", 4) # Status bar self._ip_label = builder.get_object("ip_label") - self._ip_label.set_text(self._options.get(self._profile).get("host")) + self._ip_label.set_text(self._settings.host) self._receiver_info_box = builder.get_object("receiver_info_box") self._receiver_info_label = builder.get_object("receiver_info_label") self._signal_box = builder.get_object("signal_box") @@ -332,7 +332,7 @@ class Application(Gtk.Application): def do_shutdown(self): """ Performs shutdown tasks """ - write_config(self._options) # storing current config + self._settings.save() # storing current config if self._player: self._player.release() Gtk.Application.do_shutdown(self) @@ -374,15 +374,12 @@ class Application(Gtk.Application): If update=False - first call on program start, else - after options changes! """ - profile = Profile(self._profile) - if profile is Profile.ENIGMA_2: - opts = self._options.get(self._profile) - self._use_colors = opts.get("use_colors", False) - if self._use_colors: + if self._profile is Profile.ENIGMA_2: + if self._settings.use_colors: new_rgb = Gdk.RGBA() extra_rgb = Gdk.RGBA() - new_rgb = new_rgb if new_rgb.parse(opts.get("new_color", NEW_COLOR)) else None - extra_rgb = extra_rgb if extra_rgb.parse(opts.get("extra_color", EXTRA_COLOR)) else None + new_rgb = new_rgb if new_rgb.parse(self._settings.new_color) else None + extra_rgb = extra_rgb if extra_rgb.parse(self._settings.extra_color) else None if update: gen = self.update_background_colors(new_rgb, extra_rgb) GLib.idle_add(lambda: next(gen, False), priority=GLib.PRIORITY_LOW) @@ -419,7 +416,7 @@ class Application(Gtk.Application): def on_resize(self, window): """ Stores new size properties for app window after resize """ - self._options["window_size"] = window.get_size() + self._settings.add("window_size", window.get_size()) @run_idle def on_about_app(self, action, value=None): @@ -608,7 +605,7 @@ class Application(Gtk.Application): # ***************** ####### *********************# def get_bouquet_file_name(self, bouquet): - bouquet_file_name = "{}userbouquet.{}.{}".format(self._options.get(self._profile).get("data_dir_path"), + bouquet_file_name = "{}userbouquet.{}.{}".format(self._settings.get(self._profile).get("data_dir_path"), *bouquet.split(":")) return bouquet_file_name @@ -836,19 +833,18 @@ class Application(Gtk.Application): @run_idle def on_satellite_editor_show(self, action, value): """ Shows satellites editor dialog """ - show_satellites_dialog(self._main_window, self._options.get(self._profile)) + show_satellites_dialog(self._main_window, self._settings) def on_download(self, action, value): DownloadDialog(transient=self._main_window, - properties=self._options, + settings=self._settings, open_data_callback=self.open_data, - update_settings_callback=self.update_options, - profile=Profile(self._profile)).show() + update_settings_callback=self.update_options).show() @run_task def on_download_data(self): try: - download_data(properties=self._options.get(self._profile), + download_data(settings=self._settings, download_type=DownloadType.ALL, callback=lambda x: print(x, end="")) except Exception as e: @@ -859,22 +855,20 @@ class Application(Gtk.Application): @run_task def on_upload_data(self, download_type): try: - profile = Profile(self._profile) - opts = self._options.get(self._profile) + profile = self._profile + opts = self._settings use_http = profile is Profile.ENIGMA_2 if profile is Profile.ENIGMA_2: - host, port = opts.get("host", "127.0.0.1"), opts.get("http_port") - user, password = opts.get("http_user", "root"), opts.get("http_password", "") + host, port, user, password = opts.host, opts.http_port, opts.http_user, opts.http_password try: test_http(host, port, user, password, skip_message=True) except TestException: use_http = False - upload_data(properties=opts, + upload_data(settings=opts, download_type=download_type, remove_unused=True, - profile=profile, callback=lambda x: print(x, end=""), use_http=use_http) except Exception as e: @@ -895,18 +889,17 @@ class Application(Gtk.Application): self._wait_dialog.show() yield True - profile = Profile(self._profile) - data_path = self._options.get(self._profile).get("data_dir_path") if data_path is None else data_path - + data_path = self._settings.data_dir_path if data_path is None else data_path yield from self.clear_current_data() try: + prf = self._profile black_list = get_blacklist(data_path) - bouquets = get_bouquets(data_path, Profile(self._profile)) + bouquets = get_bouquets(data_path, prf) yield True - services = get_services(data_path, profile, self.get_format_version() if profile is Profile.ENIGMA_2 else 0) + services = get_services(data_path, prf, self.get_format_version() if prf is Profile.ENIGMA_2 else 0) yield True - update_picons_data(self._options.get(self._profile).get("picons_dir_path"), self._picons) + update_picons_data(self._settings.picons_dir_path, self._picons) yield True except FileNotFoundError as e: msg = get_message("Please, download files from receiver or setup your path for read data!") @@ -1046,12 +1039,12 @@ class Application(Gtk.Application): GLib.idle_add(lambda: next(gen, False), priority=GLib.PRIORITY_LOW) def save_data(self): - profile = Profile(self._profile) - options = self._options.get(self._profile) - path = options.get("data_dir_path") - backup_path = options.get("backup_dir_path", path + "backup/") + self._save_header_button.set_sensitive(False) + profile = self._profile + path = self._settings.data_dir_path + backup_path = self._settings.backup_dir_path # Backup data or clearing data path - backup_data(path, backup_path) if options.get("backup_before_save", True) else clear_data_path(path) + backup_data(path, backup_path) if self._settings.backup_before_save else clear_data_path(path) yield True bouquets = [] @@ -1096,7 +1089,7 @@ class Application(Gtk.Application): if show_dialog(DialogType.QUESTION, self._main_window) == Gtk.ResponseType.CANCEL: return - gen = self.create_new_configuration(Profile(self._profile)) + gen = self.create_new_configuration(self._profile) GLib.idle_add(lambda: next(gen, False), priority=GLib.PRIORITY_LOW) def create_new_configuration(self, profile): @@ -1179,7 +1172,7 @@ class Application(Gtk.Application): self.show_error_dialog("Error. No bouquet is selected!") return - if Profile(self._profile) is Profile.NEUTRINO_MP and self._bq_selected.endswith(BqType.WEBTV.value): + if self._profile is Profile.NEUTRINO_MP and self._bq_selected.endswith(BqType.WEBTV.value): self.show_error_dialog("Operation not allowed in this context!") return @@ -1205,14 +1198,14 @@ class Application(Gtk.Application): v.get_selection().unselect_all() def on_preferences(self, action, value): - response = show_settings_dialog(self._main_window, self._options) + response = show_settings_dialog(self._main_window, self._settings) if response != Gtk.ResponseType.CANCEL: gen = self.update_options() GLib.idle_add(lambda: next(gen, False), priority=GLib.PRIORITY_LOW) def update_options(self): - profile = self._options.get("profile") - self._ip_label.set_text(self._options.get(profile).get("host")) + profile = self._settings.profile + self._ip_label.set_text(self._settings.host) if profile != self._profile: yield from self.show_app_info(True) self._profile = profile @@ -1309,7 +1302,6 @@ class Application(Gtk.Application): self.update_bouquet_list() def on_view_focus(self, view, focus_event=None): - profile = Profile(self._profile) model_name, model = get_model_data(view) not_empty = len(model) > 0 # if > 0 model has items is_service = model_name == self._SERVICE_LIST_NAME @@ -1321,7 +1313,7 @@ class Application(Gtk.Application): self._tool_elements[elem].set_sensitive(not_empty) if elem == "bouquets_paste_popup_item": self._tool_elements[elem].set_sensitive(not_empty and self._bouquets_buffer) - if profile is Profile.NEUTRINO_MP: + if self._profile is Profile.NEUTRINO_MP: for elem in self._LOCK_HIDE_ELEMENTS: self._tool_elements[elem].set_sensitive(not_empty) else: @@ -1337,17 +1329,17 @@ class Application(Gtk.Application): for elem in self._BOUQUET_ELEMENTS: self._tool_elements[elem].set_sensitive(False) for elem in self._LOCK_HIDE_ELEMENTS: - self._tool_elements[elem].set_sensitive(not_empty and profile is Profile.ENIGMA_2) + self._tool_elements[elem].set_sensitive(not_empty and self._profile is Profile.ENIGMA_2) for elem in self._FAV_IPTV_ELEMENTS: is_iptv = self._bq_selected and not is_service - if profile is Profile.NEUTRINO_MP: + if self._profile is Profile.NEUTRINO_MP: is_iptv = is_iptv and BqType(self._bq_selected.split(":")[1]) is BqType.WEBTV self._tool_elements[elem].set_sensitive(is_iptv) for elem in self._COMMONS_ELEMENTS: self._tool_elements[elem].set_sensitive(not_empty) - if profile is not Profile.ENIGMA_2: + if self._profile is not Profile.ENIGMA_2: for elem in self._FAV_ENIGMA_ELEMENTS: self._tool_elements[elem].set_sensitive(False) @@ -1358,11 +1350,9 @@ class Application(Gtk.Application): self.set_service_flags(Flag.LOCK) def set_service_flags(self, flag): - profile = Profile(self._profile) - - if profile is Profile.ENIGMA_2: + if self._profile is Profile.ENIGMA_2: set_flags(flag, self._services_view, self._fav_view, self._services, self._blacklist) - elif profile is Profile.NEUTRINO_MP and self._bq_selected: + elif self._profile is Profile.NEUTRINO_MP and self._bq_selected: model, paths = self._bouquets_view.get_selection().get_selected_rows() itr = model.get_iter(paths[0]) value = model.get_value(itr, 1 if flag is Flag.LOCK else 2) @@ -1425,15 +1415,14 @@ class Application(Gtk.Application): self._fav_view, self._services, self._bouquets.get(self._bq_selected, None), - Profile(self._profile), + self._profile, Action.ADD).show() if response != Gtk.ResponseType.CANCEL: self.update_fav_num_column(self._fav_model) @run_idle - def on_iptv_list_configuration(self, action, value=None): - profile = Profile(self._profile) - if profile is Profile.NEUTRINO_MP: + def on_iptv_list_configuration(self, action, value): + if self._profile is Profile.NEUTRINO_MP: self.show_error_dialog("Neutrino at the moment not supported!") return @@ -1446,7 +1435,8 @@ class Application(Gtk.Application): return bq = self._bouquets.get(self._bq_selected, []) - IptvListConfigurationDialog(self._main_window, self._services, iptv_rows, bq, self._fav_model, profile).show() + IptvListConfigurationDialog(self._main_window, self._services, iptv_rows, bq, + self._fav_model, self._profile).show() @run_idle def on_remove_all_unavailable(self, action, value=None): @@ -1462,8 +1452,7 @@ class Application(Gtk.Application): return fav_bqt = self._bouquets.get(self._bq_selected, None) - prf = Profile(self._profile) - response = SearchUnavailableDialog(self._main_window, self._fav_model, fav_bqt, iptv_rows, prf).show() + response = SearchUnavailableDialog(self._main_window, self._fav_model, fav_bqt, iptv_rows, self._profile).show() if response: next(self.remove_favs(response, self._fav_model), False) @@ -1471,7 +1460,7 @@ class Application(Gtk.Application): @run_idle def on_epg_list_configuration(self, action, value=None): - if Profile(self._profile) is not Profile.ENIGMA_2: + if self._profile is not Profile.ENIGMA_2: self.show_error_dialog("Only Enigma2 is supported!") return @@ -1480,8 +1469,7 @@ class Application(Gtk.Application): return bq = self._bouquets.get(self._bq_selected) - profile = self._options.get(self._profile) - EpgDialog(self._main_window, profile, self._services, bq, self._fav_model, self._current_bq_name).show() + EpgDialog(self._main_window, self._settings, self._services, bq, self._fav_model, self._current_bq_name).show() # ***************** Import ********************# @@ -1490,11 +1478,11 @@ class Application(Gtk.Application): if not self._bq_selected: return - YtListImportDialog(self._main_window, Profile(self._profile), self.append_imported_services).show() + YtListImportDialog(self._main_window, self._profile, self.append_imported_services).show() def on_import_m3u(self, action, value=None): """ Imports iptv from m3u files. """ - response = get_chooser_dialog(self._main_window, self._options.get(self._profile), "*.m3u", "m3u files") + response = get_chooser_dialog(self._main_window, self._settings, "*.m3u", "m3u files") if response == Gtk.ResponseType.CANCEL: return @@ -1502,7 +1490,7 @@ class Application(Gtk.Application): self.show_error_dialog("No m3u file is selected!") return - channels = parse_m3u(response, Profile(self._profile)) + channels = parse_m3u(response, self._profile) if channels and self._bq_selected: self.append_imported_services(channels) @@ -1527,31 +1515,29 @@ class Application(Gtk.Application): self.show_error_dialog("This list does not contains IPTV streams!") return - response = show_dialog(DialogType.CHOOSER, self._main_window, options=self._options.get(self._profile)) + response = show_dialog(DialogType.CHOOSER, self._main_window, settings=self._settings) if response in (Gtk.ResponseType.CANCEL, Gtk.ResponseType.DELETE_EVENT): return try: bq = Bouquet(self._current_bq_name, None, bq_services, None, None) - export_to_m3u(response, bq, Profile(self._profile)) + export_to_m3u(response, bq, self._profile) except Exception as e: self.show_error_dialog(str(e)) else: show_dialog(DialogType.INFO, self._main_window, "Done!") def on_import_bouquet(self, action, value=None): - profile = Profile(self._profile) model, paths = self._bouquets_view.get_selection().get_selected_rows() if not paths: self.show_error_dialog("No selected item!") return - opts = self._options.get(self._profile) - appender = self.append_bouquet if profile is Profile.ENIGMA_2 else self.append_bouquets - import_bouquet(self._main_window, profile, model, paths[0], opts, self._services, appender) + appender = self.append_bouquet if self._profile is Profile.ENIGMA_2 else self.append_bouquets + import_bouquet(self._main_window, model, paths[0], self._settings, self._services, appender) def on_import_bouquets(self, action, value=None): - response = show_dialog(DialogType.CHOOSER, self._main_window, options=self._options.get(self._profile)) + response = show_dialog(DialogType.CHOOSER, self._main_window, settings=self._settings) if response in (Gtk.ResponseType.CANCEL, Gtk.ResponseType.DELETE_EVENT): return @@ -1559,7 +1545,7 @@ class Application(Gtk.Application): gen = self.append_imported_data(b, s) GLib.idle_add(lambda: next(gen, False)) - ImportDialog(self._main_window, response, Profile(self._profile), self._services.keys(), append).show() + ImportDialog(self._main_window, response, self._settings, self._services.keys(), append).show() def append_imported_data(self, bouquets, services): try: @@ -1570,12 +1556,9 @@ class Application(Gtk.Application): # ***************** Backup ********************# - def on_backup_tool_show(self, action, value=None): + def on_backup_tool_show(self, item): """ Shows backup tool dialog """ - BackupDialog(self._main_window, - self._options, - Profile(self._profile), - self.open_data).show() + BackupDialog(self._main_window, self._settings, self.open_data).show() # ***************** Player *********************# @@ -1591,7 +1574,7 @@ class Application(Gtk.Application): self.show_error_dialog("Not allowed in this context!") return - url = get_iptv_url(row, Profile(self._profile)) + url = get_iptv_url(row, self._profile) self.update_player_buttons() if not url: return @@ -1600,8 +1583,8 @@ class Application(Gtk.Application): def play(self, url): if not self._player: try: - self._player = Player(rewind_callback=self.on_player_duration_changed, - position_callback=self.on_player_time_changed) + self._player = Player.get_instance(rewind_callback=self.on_player_duration_changed, + position_callback=self.on_player_time_changed) except (NameError, AttributeError): self.show_error_dialog("No VLC is found. Check that it is installed!") return @@ -1638,8 +1621,7 @@ class Application(Gtk.Application): def on_player_close(self, item=None): if self._player: - self._player.release() - self._player = None + self._player.stop() GLib.idle_add(self._player_box.set_visible, False, priority=GLib.PRIORITY_LOW) @lru_cache(maxsize=1) @@ -1670,6 +1652,7 @@ class Application(Gtk.Application): def on_player_drawing_area_draw(self, widget, cr): """ Used for black background drawing in the player drawing area. + Required for Gtk >= 3.20. More info: https://developer.gnome.org/gtk3/stable/ch32s10.html, https://developer.gnome.org/gtk3/stable/GtkStyleContext.html#gtk-render-background @@ -1706,14 +1689,12 @@ class Application(Gtk.Application): @run_task def init_http_api(self): - prp = self._options.get(self._profile) - profile = Profile(self._profile) - self._fav_click_mode = FavClickMode(prp.get("fav_click_mode", FavClickMode.DISABLED)) - http_api_enable = prp.get("http_api_support", False) - status = all((http_api_enable, profile is Profile.ENIGMA_2, not self._receiver_info_box.get_visible())) + self._fav_click_mode = FavClickMode(self._settings.fav_click_mode) + http_api_enable = self._settings.http_api_support + status = all((http_api_enable, self._profile is Profile.ENIGMA_2, not self._receiver_info_box.get_visible())) GLib.idle_add(self._http_status_image.set_visible, status) - if profile is Profile.NEUTRINO_MP or not http_api_enable: + if self._profile is Profile.NEUTRINO_MP or not http_api_enable: self.update_info_boxes_visible(False) if self._http_api: self._http_api.close() @@ -1722,12 +1703,12 @@ class Application(Gtk.Application): return if not self._http_api: - self._http_api = HttpAPI(prp.get("host", "127.0.0.1"), prp.get("http_port", "80"), - prp.get("http_user", ""), prp.get("http_password", "")) + self._http_api = HttpAPI(self._settings.host, self._settings.http_port, + self._settings.http_user, self._settings.http_password) GLib.timeout_add_seconds(3, self.update_info, priority=GLib.PRIORITY_LOW) - self.init_send_to(http_api_enable and prp.get("enable_send_to", False)) + self.init_send_to(http_api_enable and self._settings.enable_send_to) @run_idle def init_send_to(self, enable): @@ -1836,22 +1817,26 @@ class Application(Gtk.Application): """ Updates positions values for the filtering function """ self._sat_positions.clear() sat_positions = set() - terrestrial = False - cable = False - for srv in self._services.values(): - tr_type = srv.transponder_type - if tr_type == "s" and srv.pos: - sat_positions.add(float(srv.pos)) - elif tr_type == "t": - terrestrial = True - elif tr_type == "c": - cable = True + if self._profile is Profile.ENIGMA_2: + terrestrial = False + cable = False - if terrestrial: - self._sat_positions.append("T") - if cable: - self._sat_positions.append("C") + for srv in self._services.values(): + tr_type = srv.transponder_type + if tr_type == "s" and srv.pos: + sat_positions.add(float(srv.pos)) + elif tr_type == "t": + terrestrial = True + elif tr_type == "c": + cable = True + + if terrestrial: + self._sat_positions.append("T") + if cable: + self._sat_positions.append("C") + elif self._profile is Profile.NEUTRINO_MP: + list(map(lambda s: sat_positions.add(float(s.pos)), filter(lambda s: s.pos, self._services.values()))) self._sat_positions.extend(map(str, sorted(sat_positions))) if self._filter_bar.is_visible(): @@ -1929,12 +1914,12 @@ class Application(Gtk.Application): self._fav_view, self._services, self._bouquets.get(self._bq_selected, None), - Profile(self._profile), + self._profile, Action.EDIT).show() self.on_locate_in_services(view) dialog = ServiceDetailsDialog(self._main_window, - self._options, + self._settings, self._services_view, self._fav_view, self._services, @@ -1944,7 +1929,7 @@ class Application(Gtk.Application): def on_services_add_new(self, item): dialog = ServiceDetailsDialog(self._main_window, - self._options, + self._settings, self._services_view, self._fav_view, self._services, @@ -2047,18 +2032,17 @@ class Application(Gtk.Application): @run_idle def on_picons_loader_show(self, action, value): ids = {} - if Profile(self._profile) is Profile.ENIGMA_2: + if self._profile is Profile.ENIGMA_2: for r in self._services_model: data = r[Column.SRV_PICON_ID].split("_") ids["{}:{}:{}".format(data[3], data[5], data[6])] = r[Column.SRV_PICON_ID] - dialog = PiconsDialog(self._main_window, self._options, ids, self._sat_positions, Profile(self._profile)) - dialog.show() + PiconsDialog(self._main_window, self._settings, ids, self._sat_positions).show() self.update_picons() @run_task def update_picons(self): - update_picons_data(self._options.get(self._profile).get("picons_dir_path"), self._picons) + update_picons_data(self._settings.picons_dir_path, self._picons) append_picons(self._picons, self._services_model) def on_assign_picon(self, view): @@ -2067,14 +2051,14 @@ class Application(Gtk.Application): self._fav_view, self._main_window, self._picons, - self._options.get(self._profile), + self._settings, self._services) def on_remove_picon(self, view): remove_picon(self.get_target_view(view), self._services_view, self._fav_view, self._picons, - self._options.get(self._profile)) + self._settings) def on_reference_picon(self, view): """ Copying picon id to clipboard """ @@ -2084,7 +2068,7 @@ class Application(Gtk.Application): if show_dialog(DialogType.QUESTION, self._main_window) == Gtk.ResponseType.CANCEL: return - remove_all_unused_picons(self._options.get(self._profile), self._picons, self._services.values()) + remove_all_unused_picons(self._settings, self._picons, self._services.values()) def get_target_view(self, view): return ViewTarget.SERVICES if Gtk.Buildable.get_name(view) == "services_tree_view" else ViewTarget.FAV @@ -2111,20 +2095,18 @@ class Application(Gtk.Application): def create_bouquets(self, g_type): gen_bouquets(self._services_view, self._bouquets_view, self._main_window, g_type, self._TV_TYPES, - Profile(self._profile), self.append_bouquet) + self._profile, self.append_bouquet) # ***************** Profile label *********************# def update_profile_label(self): - profile = Profile(self._profile) - if profile is Profile.ENIGMA_2: - ver = self.get_format_version() - self._main_window.set_title("DemonEditor [{} Enigma2 v.{}]".format(get_message("Profile:"), ver)) - elif profile is Profile.NEUTRINO_MP: - self._main_window.set_title("DemonEditor [{} Neutrino-MP]".format(get_message("Profile:"))) + if self._profile is Profile.ENIGMA_2: + self._header_bar.set_subtitle("{} Enigma2 v.{}".format(get_message("Profile:"), self.get_format_version())) + elif self._profile is Profile.NEUTRINO_MP: + self._header_bar.set_subtitle("{} Neutrino-MP".format(get_message("Profile:"))) def get_format_version(self): - return 5 if self._options.get(self._profile).get("v5_support", False) else 4 + return 5 if self._settings.v5_support else 4 @run_idle def update_info_boxes_visible(self, visible): diff --git a/app/ui/main_helper.py b/app/ui/main_helper.py index e8972876..bc874db9 100644 --- a/app/ui/main_helper.py +++ b/app/ui/main_helper.py @@ -9,7 +9,7 @@ from app.commons import run_task from app.eparser import Service from app.eparser.ecommons import Flag, BouquetService, Bouquet, BqType from app.eparser.enigma.bouquets import BqServiceType, to_bouquet_id -from app.properties import Profile +from app.settings import Profile from .uicommons import ViewTarget, BqGenType, Gtk, Gdk, HIDE_ICON, LOCKED_ICON, KeyboardKey, Column from .dialogs import show_dialog, DialogType, get_chooser_dialog, WaitDialog @@ -360,13 +360,13 @@ def append_picons(picons, model): GLib.idle_add(lambda: next(app, False), priority=GLib.PRIORITY_LOW) -def assign_picon(target, srv_view, fav_view, transient, picons, options, services): +def assign_picon(target, srv_view, fav_view, transient, picons, settings, services): view = srv_view if target is ViewTarget.SERVICES else fav_view model, paths = view.get_selection().get_selected_rows() if not is_only_one_item_selected(paths, transient): return - response = get_chooser_dialog(transient, options, "*.png", "png files") + response = get_chooser_dialog(transient, settings, "*.png", "png files") if response == Gtk.ResponseType.CANCEL: return @@ -381,8 +381,10 @@ def assign_picon(target, srv_view, fav_view, transient, picons, options, service picon_id = services.get(fav_id)[Column.SRV_PICON_ID] if picon_id: - picon_file = options.get("picons_dir_path") + picon_id if os.path.isfile(response): + picons_path = settings.picons_dir_path + os.makedirs(os.path.dirname(picons_path), exist_ok=True) + picon_file = picons_path + picon_id shutil.copy(response, picon_file) picon = get_picon_pixbuf(picon_file) picons[picon_id] = picon @@ -400,7 +402,7 @@ def set_picon(fav_id, model, picon, fav_id_pos, picon_pos): break -def remove_picon(target, srv_view, fav_view, picons, options): +def remove_picon(target, srv_view, fav_view, picons, settings): view = srv_view if target is ViewTarget.SERVICES else fav_view model, paths = view.get_selection().get_selected_rows() model = get_base_model(model) @@ -431,7 +433,7 @@ def remove_picon(target, srv_view, fav_view, picons, options): fav_view.get_model().foreach(remove) if target is ViewTarget.SERVICES else get_base_model( srv_view.get_model()).foreach(remove) - remove_picons(options, picon_ids, picons) + remove_picons(settings, picon_ids, picons) def copy_picon_reference(target, view, services, clipboard, transient): @@ -455,15 +457,15 @@ def copy_picon_reference(target, view, services, clipboard, transient): show_dialog(DialogType.ERROR, transient, "No reference is present!") -def remove_all_unused_picons(options, picons, services): +def remove_all_unused_picons(settings, picons, services): ids = {s.picon_id for s in services} pcs = list(filter(lambda x: x not in ids, picons)) - remove_picons(options, pcs, picons) + remove_picons(settings, pcs, picons) -def remove_picons(options, picon_ids, picons): - pions_path = options.get("picons_dir_path") - backup_path = options.get("backup_dir_path") + "picons/" +def remove_picons(settings, picon_ids, picons): + pions_path = settings.picons_dir_path + backup_path = settings.backup_dir_path + "picons/" os.makedirs(os.path.dirname(backup_path), exist_ok=True) for p_id in picon_ids: picons[p_id] = None @@ -550,9 +552,9 @@ def get_bouquets_names(model): # ***************** Others *********************# -def update_entry_data(entry, dialog, options): +def update_entry_data(entry, dialog, settings): """ Updates value in text entry from chooser dialog """ - response = show_dialog(dialog_type=DialogType.CHOOSER, transient=dialog, options=options) + response = show_dialog(dialog_type=DialogType.CHOOSER, transient=dialog, settings=settings) if response not in (Gtk.ResponseType.CANCEL, Gtk.ResponseType.DELETE_EVENT): entry.set_text(response) return response diff --git a/app/ui/picons_downloader.py b/app/ui/picons_downloader.py index f89c31b8..0ef967f0 100644 --- a/app/ui/picons_downloader.py +++ b/app/ui/picons_downloader.py @@ -9,7 +9,7 @@ from gi.repository import GLib, GdkPixbuf from app.commons import run_idle, run_task from app.connections import upload_data, DownloadType from app.tools.picons import PiconsParser, parse_providers, Provider, convert_to -from app.properties import Profile +from app.settings import Profile from app.tools.satellites import SatellitesParser, SatelliteSource from .uicommons import Gtk, Gdk, UI_RESOURCES_PATH, TEXT_DOMAIN, TV_ICON from .dialogs import show_dialog, DialogType, get_message @@ -17,7 +17,7 @@ from .main_helper import update_entry_data, append_text_to_tview, scroll_to, on_ class PiconsDialog: - def __init__(self, transient, options, picon_ids, sat_positions, profile=Profile.ENIGMA_2): + def __init__(self, transient, settings, picon_ids, sat_positions): self._picon_ids = picon_ids self._sat_positions = sat_positions self._TMP_DIR = tempfile.gettempdir() + "/" @@ -85,16 +85,13 @@ class PiconsDialog: self._style_provider.load_from_path(UI_RESOURCES_PATH + "style.css") self._url_entry.get_style_context().add_provider_for_screen(Gdk.Screen.get_default(), self._style_provider, Gtk.STYLE_PROVIDER_PRIORITY_USER) - self._properties = options.get(profile.value) - self._profile = profile - self._ip_entry.set_text(self._properties.get("host", "")) - self._picons_entry.set_text(self._properties.get("picons_path", "")) - self._picons_path = self._properties.get("picons_dir_path", "") + self._settings = settings + self._profile = settings.profile + self._ip_entry.set_text(self._settings.host) + self._picons_entry.set_text(self._settings.picons_path) + self._picons_path = self._settings.picons_dir_path self._picons_dir_entry.set_text(self._picons_path) - 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.") @@ -280,9 +277,8 @@ class PiconsDialog: try: GLib.idle_add(self._expander.set_expanded, True) - upload_data(properties=self._properties, + upload_data(settings=self._settings, download_type=DownloadType.PICONS, - profile=self._profile, callback=self.append_output, done_callback=lambda: self.show_info_message(get_message("Done!"), Gtk.MessageType.INFO)) except OSError as e: @@ -298,7 +294,7 @@ class PiconsDialog: self._message_label.set_text(text) def on_picons_dir_open(self, entry, icon, event_button): - update_entry_data(entry, self._dialog, options={"data_dir_path": self._picons_path}) + update_entry_data(entry, self._dialog, settings=self._settings) @run_idle def on_selected_toggled(self, toggle, path): @@ -332,9 +328,6 @@ class PiconsDialog: self._convert_button.set_visible(tab_num) self._send_button.set_visible(not tab_num) - if self._enigma2_path_button.get_filename() is None: - self._enigma2_path_button.set_current_folder(self._enigma2_picons_path) - @run_idle def on_convert(self, item): if show_dialog(DialogType.QUESTION, self._dialog) == Gtk.ResponseType.CANCEL: diff --git a/app/ui/satellites_dialog.py b/app/ui/satellites_dialog.py index 8cc7c420..80fc0bc0 100644 --- a/app/ui/satellites_dialog.py +++ b/app/ui/satellites_dialog.py @@ -24,9 +24,9 @@ def show_satellites_dialog(transient, options): class SatellitesDialog: _aggr = [None for x in range(9)] # aggregate - def __init__(self, transient, options): - self._data_path = options.get("data_dir_path") + "satellites.xml" - self._options = options + def __init__(self, transient, settings): + self._data_path = settings.data_dir_path + "satellites.xml" + self._settings = settings handlers = {"on_open": self.on_open, "on_remove": self.on_remove, @@ -55,7 +55,7 @@ class SatellitesDialog: self._window.set_transient_for(transient) self._sat_view = builder.get_object("satellites_editor_tree_view") # Setting the last size of the dialog window if it was saved - window_size = self._options.get("sat_editor_window_size", None) + window_size = self._settings.get("sat_editor_window_size") if window_size: self._window.resize(*window_size) @@ -75,8 +75,8 @@ class SatellitesDialog: def on_resize(self, window): """ Stores new size properties for dialog window after resize """ - if self._options: - self._options["sat_editor_window_size"] = window.get_size() + if self._settings: + self._settings.add("sat_editor_window_size", window.get_size()) @run_idle def on_quit(self, *args): @@ -100,7 +100,7 @@ class SatellitesDialog: file_filter.set_name("satellites.xml") response = show_dialog(dialog_type=DialogType.CHOOSER, transient=self._window, - options=self._options, + settings=self._settings, action_type=action, file_filter=file_filter) return response diff --git a/app/ui/service_details_dialog.py b/app/ui/service_details_dialog.py index 342fb1c7..0d2cffa0 100644 --- a/app/ui/service_details_dialog.py +++ b/app/ui/service_details_dialog.py @@ -6,7 +6,7 @@ from app.eparser import Service 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, SERVICE_TYPE, T_MODULATION, C_MODULATION, TrType, \ SystemCable, T_SYSTEM, BANDWIDTH, TRANSMISSION_MODE, GUARD_INTERVAL, HIERARCHY, T_FEC -from app.properties import Profile +from app.settings import Profile from .uicommons import Gtk, Gdk, UI_RESOURCES_PATH, HIDE_ICON, TEXT_DOMAIN, CODED_ICON, Column, IS_GNOME_SESSION from .dialogs import show_dialog, DialogType, Action, get_dialogs_string from .main_helper import get_base_model @@ -34,7 +34,7 @@ class ServiceDetailsDialog: _DIGIT_ENTRY_NAME = "digit-entry" - def __init__(self, transient, options, srv_view, fav_view, services, bouquets, new_color, action=Action.EDIT): + def __init__(self, transient, settings, srv_view, fav_view, services, bouquets, new_color, action=Action.EDIT): handlers = {"on_system_changed": self.on_system_changed, "on_save": self.on_save, "on_create_new": self.on_create_new, @@ -52,10 +52,10 @@ class ServiceDetailsDialog: self._dialog = builder.get_object("service_details_dialog") self._dialog.set_transient_for(transient) - self._profile = Profile(options["profile"]) + self._profile = settings.profile self._tr_type = None - self._satellites_xml_path = options.get(self._profile.value)["data_dir_path"] + "satellites.xml" - self._picons_dir_path = options.get(self._profile.value)["picons_dir_path"] + self._satellites_xml_path = settings.data_dir_path + "satellites.xml" + self._picons_dir_path = settings.picons_dir_path self._services_view = srv_view self._fav_view = fav_view self._action = action diff --git a/app/ui/settings_dialog.py b/app/ui/settings_dialog.py index 6697e62d..a86d7eb1 100644 --- a/app/ui/settings_dialog.py +++ b/app/ui/settings_dialog.py @@ -2,8 +2,8 @@ from enum import Enum from app.commons import run_task, run_idle from app.connections import test_telnet, test_ftp, TestException, test_http -from app.properties import write_config, Profile, get_default_settings -from .uicommons import Gtk, Gdk, UI_RESOURCES_PATH, TEXT_DOMAIN, NEW_COLOR, EXTRA_COLOR, FavClickMode +from app.settings import Profile +from .uicommons import Gtk, Gdk, UI_RESOURCES_PATH, TEXT_DOMAIN, FavClickMode from .main_helper import update_entry_data @@ -19,7 +19,7 @@ class Property(Enum): class SettingsDialog: - def __init__(self, transient, options): + def __init__(self, transient, settings): handlers = {"on_field_icon_press": self.on_field_icon_press, "on_profile_changed": self.on_profile_changed, "on_reset": self.on_reset, @@ -89,11 +89,11 @@ class SettingsDialog: self._click_mode_zap_button.bind_property("sensitive", self._enable_send_to_switch, "sensitive") self._enable_send_to_switch.bind_property("sensitive", builder.get_object("enable_send_to_label"), "sensitive") self._extra_support_grid.bind_property("sensitive", builder.get_object("v5_support_grid"), "sensitive") - # Options - self._options = options - self._active_profile = options.get("profile") + # Settings + self._settings = settings + self._active_profile = settings.profile self.set_settings() - self.init_ui_elements(Profile(self._active_profile)) + self.init_ui_elements(self._active_profile) def init_ui_elements(self, profile): is_enigma_profile = profile is Profile.ENIGMA_2 @@ -115,106 +115,90 @@ class SettingsDialog: return response def on_field_icon_press(self, entry, icon, event_button): - update_entry_data(entry, self._dialog, self._options.get(self._options.get("profile"))) + update_entry_data(entry, self._dialog, self._settings) def on_profile_changed(self, item): profile = Profile.ENIGMA_2 if self._enigma_radio_button.get_active() else Profile.NEUTRINO_MP - self._active_profile = profile.value + self._active_profile = profile + self._settings.profile = profile self.set_settings() self.init_ui_elements(profile) - def set_profile(self, profile): - self._active_profile = profile.value - self.set_settings() - def on_reset(self, item): - def_settings = get_default_settings() - for key in def_settings: - current = self._options.get(key) - if type(current) is str: - continue - default = def_settings.get(key) - for k in default: - current[k] = default.get(k) + self._settings.reset() self.set_settings() def set_settings(self): - def_settings = get_default_settings().get(self._active_profile) - options = self._options.get(self._active_profile) + self._host_field.set_text(self._settings.host) + self._port_field.set_text(self._settings.port) + self._login_field.set_text(self._settings.user) + self._password_field.set_text(self._settings.password) + self._http_login_field.set_text(self._settings.http_user) + self._http_password_field.set_text(self._settings.http_password) + self._http_port_field.set_text(self._settings.http_port) + self._telnet_login_field.set_text(self._settings.telnet_user) + self._telnet_password_field.set_text(self._settings.telnet_password) + self._telnet_port_field.set_text(self._settings.telnet_port) + self._telnet_timeout_spin_button.set_value(self._settings.telnet_timeout) + self._services_field.set_text(self._settings.services_path) + self._user_bouquet_field.set_text(self._settings.user_bouquet_path) + self._satellites_xml_field.set_text(self._settings.satellites_xml_path) + self._picons_field.set_text(self._settings.picons_path) + self._data_dir_field.set_text(self._settings.data_dir_path) + self._picons_dir_field.set_text(self._settings.picons_dir_path) + self._backup_dir_field.set_text(self._settings.backup_dir_path) + self._before_save_switch.set_active(self._settings.backup_before_save) + self._before_downloading_switch.set_active(self._settings.backup_before_downloading) + self.set_fav_click_mode(self._settings.fav_click_mode) - self._host_field.set_text(options.get("host", def_settings["host"])) - self._port_field.set_text(options.get("port", def_settings["port"])) - self._login_field.set_text(options.get("user", def_settings["user"])) - self._password_field.set_text(options.get("password", def_settings["password"])) - self._http_login_field.set_text(options.get("http_user", def_settings["http_user"])) - self._http_password_field.set_text(options.get("http_password", def_settings["http_password"])) - self._http_port_field.set_text(options.get("http_port", def_settings["http_port"])) - self._telnet_login_field.set_text(options.get("telnet_user", def_settings["telnet_user"])) - self._telnet_password_field.set_text(options.get("telnet_password", def_settings["telnet_password"])) - self._telnet_port_field.set_text(options.get("telnet_port", def_settings["telnet_port"])) - self._telnet_timeout_spin_button.set_value(options.get("telnet_timeout", def_settings["telnet_timeout"])) - self._services_field.set_text(options.get("services_path", def_settings["services_path"])) - self._user_bouquet_field.set_text(options.get("user_bouquet_path", def_settings["user_bouquet_path"])) - self._satellites_xml_field.set_text(options.get("satellites_xml_path", def_settings["satellites_xml_path"])) - self._picons_field.set_text(options.get("picons_path", def_settings["picons_path"])) - self._data_dir_field.set_text(options.get("data_dir_path", def_settings["data_dir_path"])) - self._picons_dir_field.set_text(options.get("picons_dir_path", def_settings["picons_dir_path"])) - self._backup_dir_field.set_text(options.get("backup_dir_path", def_settings["backup_dir_path"])) - self._before_save_switch.set_active(options.get("backup_before_save", def_settings["backup_before_save"])) - self._before_downloading_switch.set_active(options.get("backup_before_downloading", - def_settings["backup_before_downloading"])) - self.set_fav_click_mode(options.get("fav_click_mode", def_settings["fav_click_mode"])) - - if Profile(self._active_profile) is Profile.ENIGMA_2: - self._support_ver5_switch.set_active(options.get("v5_support", False)) - self._support_http_api_switch.set_active(options.get("http_api_support", False)) - self._enable_y_dl_switch.set_active(options.get("enable_yt_dl", False)) - self._enable_send_to_switch.set_active(options.get("enable_send_to", False)) - self._set_color_switch.set_active(options.get("use_colors", False)) + if self._active_profile is Profile.ENIGMA_2: + self._support_ver5_switch.set_active(self._settings.v5_support) + self._support_http_api_switch.set_active(self._settings.http_api_support) + self._enable_y_dl_switch.set_active(self._settings.enable_yt_dl) + self._enable_send_to_switch.set_active(self._settings.enable_send_to) + self._set_color_switch.set_active(self._settings.use_colors) new_rgb = Gdk.RGBA() - new_rgb.parse(options.get("new_color", NEW_COLOR)) + new_rgb.parse(self._settings.new_color) extra_rgb = Gdk.RGBA() - extra_rgb.parse(options.get("extra_color", EXTRA_COLOR)) + extra_rgb.parse(self._settings.extra_color) self._new_color_button.set_rgba(new_rgb) self._extra_color_button.set_rgba(extra_rgb) def apply_settings(self, item=None): - profile = Profile.ENIGMA_2 if self._enigma_radio_button.get_active() else Profile.NEUTRINO_MP - self._active_profile = profile.value - self._options["profile"] = self._active_profile - options = self._options.get(self._active_profile) - options["host"] = self._host_field.get_text() - options["port"] = self._port_field.get_text() - options["user"] = self._login_field.get_text() - options["password"] = self._password_field.get_text() - options["http_user"] = self._http_login_field.get_text() - options["http_password"] = self._http_password_field.get_text() - options["http_port"] = self._http_port_field.get_text() - options["telnet_user"] = self._telnet_login_field.get_text() - options["telnet_password"] = self._telnet_password_field.get_text() - options["telnet_port"] = self._telnet_port_field.get_text() - options["telnet_timeout"] = int(self._telnet_timeout_spin_button.get_value()) - options["services_path"] = self._services_field.get_text() - options["user_bouquet_path"] = self._user_bouquet_field.get_text() - options["satellites_xml_path"] = self._satellites_xml_field.get_text() - options["picons_path"] = self._picons_field.get_text() - options["data_dir_path"] = self._data_dir_field.get_text() - options["picons_dir_path"] = self._picons_dir_field.get_text() - options["backup_dir_path"] = self._backup_dir_field.get_text() - options["backup_before_save"] = self._before_save_switch.get_active() - options["backup_before_downloading"] = self._before_downloading_switch.get_active() - options["fav_click_mode"] = self.get_fav_click_mode() + self._active_profile = Profile.ENIGMA_2 if self._enigma_radio_button.get_active() else Profile.NEUTRINO_MP + self._settings.profile = self._active_profile + self._settings.host = self._host_field.get_text() + self._settings.port = self._port_field.get_text() + self._settings.user = self._login_field.get_text() + self._settings.password = self._password_field.get_text() + self._settings.http_user = self._http_login_field.get_text() + self._settings.http_password = self._http_password_field.get_text() + self._settings.http_port = self._http_port_field.get_text() + self._settings.telnet_user = self._telnet_login_field.get_text() + self._settings.telnet_password = self._telnet_password_field.get_text() + self._settings.telnet_port = self._telnet_port_field.get_text() + self._settings.telnet_timeout = int(self._telnet_timeout_spin_button.get_value()) + self._settings.services_path = self._services_field.get_text() + self._settings.user_bouquet_path = self._user_bouquet_field.get_text() + self._settings.satellites_xml_path = self._satellites_xml_field.get_text() + self._settings.picons_path = self._picons_field.get_text() + self._settings.data_dir_path = self._data_dir_field.get_text() + self._settings.picons_dir_path = self._picons_dir_field.get_text() + self._settings.backup_dir_path = self._backup_dir_field.get_text() + self._settings.backup_before_save = self._before_save_switch.get_active() + self._settings.backup_before_downloading = self._before_downloading_switch.get_active() + self._settings.fav_click_mode = self.get_fav_click_mode() - if profile is Profile.ENIGMA_2: - options["use_colors"] = self._set_color_switch.get_active() - options["new_color"] = self._new_color_button.get_rgba().to_string() - options["extra_color"] = self._extra_color_button.get_rgba().to_string() - options["v5_support"] = self._support_ver5_switch.get_active() - options["http_api_support"] = self._support_http_api_switch.get_active() - options["enable_yt_dl"] = self._enable_y_dl_switch.get_active() - options["enable_send_to"] = self._enable_send_to_switch.get_active() + if self._active_profile is Profile.ENIGMA_2: + self._settings.use_colors = self._set_color_switch.get_active() + self._settings.new_color = self._new_color_button.get_rgba().to_string() + self._settings.extra_color = self._extra_color_button.get_rgba().to_string() + self._settings.v5_support = self._support_ver5_switch.get_active() + self._settings.http_api_support = self._support_http_api_switch.get_active() + self._settings.enable_yt_dl = self._enable_y_dl_switch.get_active() + self._settings.enable_send_to = self._enable_send_to_switch.get_active() - write_config(self._options) + self._settings.save() @run_task def on_connection_test(self, item): diff --git a/app/ui/transmitter.py b/app/ui/transmitter.py index be1d6d5e..e286edb6 100644 --- a/app/ui/transmitter.py +++ b/app/ui/transmitter.py @@ -2,6 +2,7 @@ from urllib.parse import urlparse from gi.repository import GLib from app.connections import HttpRequestType from app.tools.yt import YouTube +from app.ui.iptv import get_yt_icon from .uicommons import Gtk, Gdk, UI_RESOURCES_PATH, TEXT_DOMAIN @@ -70,7 +71,7 @@ class LinksTransmitter: yield True if yt_id: - self._url_entry.set_icon_from_stock(Gtk.EntryIconPosition.SECONDARY, Gtk.STOCK_INFO) + self._url_entry.set_icon_from_pixbuf(Gtk.EntryIconPosition.SECONDARY, get_yt_icon("youtube", 32)) links, title = YouTube.get_yt_link(yt_id) yield True if links: diff --git a/app/ui/uicommons.py b/app/ui/uicommons.py index 908478c3..39f8e4f6 100644 --- a/app/ui/uicommons.py +++ b/app/ui/uicommons.py @@ -27,10 +27,6 @@ TV_ICON = theme.load_icon("tv-symbolic", 16, 0) if theme.lookup_icon("tv-symboli IPTV_ICON = theme.load_icon("emblem-shared", 16, 0) if theme.lookup_icon("emblem-shared", 16, 0) else None EPG_ICON = theme.load_icon("gtk-index", 16, 0) if theme.lookup_icon("gtk-index", 16, 0) else None -# Colors -NEW_COLOR = "rgb(255,230,204)" # Color for new services in the main list -EXTRA_COLOR = "rgb(179,230,204)" # Color for services with a extra name for the bouquet - class KeyboardKey(Enum): """ The raw(hardware) codes of the keyboard keys. """ diff --git a/deb/usr/share/locale/es/LC_MESSAGES/demon-editor.mo b/deb/usr/share/locale/es/LC_MESSAGES/demon-editor.mo new file mode 100644 index 00000000..1be1ad1e Binary files /dev/null and b/deb/usr/share/locale/es/LC_MESSAGES/demon-editor.mo differ diff --git a/po/es/demon-editor.po b/po/es/demon-editor.po index 9b3a86cd..bf2adf36 100644 --- a/po/es/demon-editor.po +++ b/po/es/demon-editor.po @@ -57,16 +57,16 @@ msgid "Assign" msgstr "Assignar" msgid "Bouquet details" -msgstr "Detalles Ramo" +msgstr "Detalles bouquet" msgid "Bouquets" -msgstr "Ramos" +msgstr "Bouquets" msgid "Copy" msgstr "Copiar" msgid "Copy reference" -msgstr "Copia de Referencia" +msgstr "Copia de referencia" msgid "Download" msgstr "Descargar" @@ -78,7 +78,7 @@ msgid "Edit mаrker text" msgstr "Editar texto del mаrcador" msgid "FTP-transfer" -msgstr "Transferencia-FTP" +msgstr "Transferencia FTP" msgid "Global search" msgstr "Búsqueda Global" @@ -99,10 +99,10 @@ msgid "Import m3u file" msgstr "Importar fichero m3u" msgid "List configuration" -msgstr "Lista configuración" +msgstr "Listar configuración" msgid "Rename for this bouquet" -msgstr "Renombrar para este ramo" +msgstr "Renombrar para este bouquet" msgid "Set default name" msgstr "Establecer nombre predeterminado" @@ -114,7 +114,7 @@ msgid "Locate in services" msgstr "Buscar en servicios" msgid "Locked" -msgstr "Cerrado" +msgstr "Bloqueado" msgid "Move" msgstr "Mover" @@ -123,13 +123,13 @@ msgid "New" msgstr "Nuevo" msgid "New bouquet" -msgstr "Ramo nuevo" +msgstr "Bouquet nuevo" msgid "Create bouquet" -msgstr "Crear ramo" +msgstr "Crear bouquet" msgid "For current satellite" -msgstr "Para el Satélite actual" +msgstr "Para el satélite actual" msgid "For current package" msgstr "Para el paquete actual" @@ -138,7 +138,7 @@ msgid "For current type" msgstr "Para el tipo actual" msgid "For each satellite" -msgstr "Para cada Satélite" +msgstr "Para cada satélite" msgid "For each package" msgstr "Para cada paquete" @@ -150,25 +150,25 @@ msgid "Open" msgstr "Abrir" msgid "Parent lock On/Off Ctrl + L" -msgstr "Bloqueo parentesco Encender/Apagar Ctrl + L" +msgstr "Bloqueo parental Encender/Apagar Ctrl + L" msgid "Picons" msgstr "Picons" msgid "Picons downloader" -msgstr "Picons descargar" +msgstr "Descargar picons" msgid "Satellites downloader" -msgstr "Satélites descargar" +msgstr "Descargar satélites" msgid "Remove" -msgstr "Remover" +msgstr "Quitar" msgid "Remove all unavailable" -msgstr "Remover todo lo indisponible" +msgstr "Quitar todo lo indisponible" msgid "Satellites editor" -msgstr "Editor Satélites" +msgstr "Editor de satélites" msgid "Save" msgstr "Guardar" @@ -183,7 +183,7 @@ msgid "Services filter" msgstr "Filtro servicios" msgid "Settings" -msgstr "Configuraciones" +msgstr "Configuración" msgid "Up" msgstr "Arriba" @@ -198,7 +198,7 @@ msgid "All" msgstr "Todo" msgid "Are you sure?" -msgstr "Estás seguro?" +msgstr "¿Estás seguro?" msgid "Current data path:" msgstr "Ruta de datos actual:" @@ -207,13 +207,13 @@ msgid "Data:" msgstr "Datos:" msgid "Enigma2 channel and satellites list editor for GNU/Linux" -msgstr "Editor de Canales y Satélites Enigma2 para GNU/Linux" +msgstr "Editor de canales y satélites Enigma2 para GNU/Linux" msgid "Host:" msgstr "Anfitrión:" msgid "Loading data..." -msgstr "Cargar datos..." +msgstr "Cargando datos..." msgid "Receive" msgstr "Recibir" @@ -222,10 +222,10 @@ msgid "Receive files from receiver" msgstr "Recibir ficheros de su receptor" msgid "Receiver IP:" -msgstr "Receptor IP:" +msgstr "IP del receptor:" msgid "Remove unused bouquets" -msgstr "Remover ramos sin usar" +msgstr "Quitar bouquets sin usar" msgid "Reset profile" msgstr "Restablecer perfil" @@ -234,7 +234,7 @@ msgid "Satellites" msgstr "Satélites" msgid "Satellites.xml file:" -msgstr "Fichero Satellites.xml:" +msgstr "Fichero satellites.xml:" msgid "Selected" msgstr "Seleccionado" @@ -246,10 +246,10 @@ msgid "Send files to receiver" msgstr "Enviar ficheros al receptor" msgid "Services and Bouquets files:" -msgstr "Ficheros de Servicios y ramos:" +msgstr "Ficheros de servicios y bouquets:" msgid "User bouquet files:" -msgstr "Importar ficheros de ramos:" +msgstr "Importar ficheros de bouquets:" msgid "Extra:" msgstr "Extra:" @@ -266,16 +266,16 @@ msgstr "Todos los tipos" # Streams player msgid "Play" -msgstr "Play" +msgstr "Reproducir" msgid "Stop playback" msgstr "Detener la reproducción" msgid "Previous stream in the list" -msgstr "Secuencia anterior en la lista" +msgstr "Anterior flujo en la lista" msgid "Next stream in the list" -msgstr "Secuencia siguiente en la lista" +msgstr "Siguiente flujo en la lista" msgid "Toggle in fullscreen" msgstr "Cambiar a pantalla completa" @@ -285,7 +285,7 @@ msgstr "Cerrar" # Picons dialog msgid "Load providers" -msgstr "Cargar Proveedores" +msgstr "Cargar proveedores" msgid "Providers" msgstr "Proveedores" @@ -300,19 +300,19 @@ msgid "Resize:" msgstr "Redimensionar:" msgid "Current picons path:" -msgstr "Ruta actual Picons:" +msgstr "Ruta actual picons:" msgid "Receiver picons path:" msgstr "Ruta picons receptor:" msgid "Picons download tool" -msgstr "Picons herramiento de descarga" +msgstr "Herramienta de descarga de picons" msgid "Transfer to receiver" msgstr "Transferir al receptor" msgid "Downloader" -msgstr "Descargador" +msgstr "Programa de descarga" msgid "Converter" msgstr "Convertidor" @@ -324,31 +324,31 @@ msgid "Path to save:" msgstr "Ruta para guardar:" msgid "Path to Enigma2 picons:" -msgstr "Ruta a picons Enigma2:" +msgstr "Ruta a picons de Enigma2:" msgid "Specify the correct position value for the provider!" -msgstr "Especifique la posición correcta para el proveedor!" +msgstr "¡Especifique el valor correcto de la posición del proveedor!" msgid "Converter between name formats" msgstr "Conversor entre formatos de nombre" msgid "Receive picons for providers" -msgstr "Recibir picons para proovedor" +msgstr "Recibir picons de proveedores" msgid "Load satellite providers." -msgstr "Cargar proovedores Satélite." +msgstr "Cargar proveedores de satélite." msgid "" "To automatically set the identifiers for picons,\n" "first load the required services list into the main application window." msgstr "" -"Para configurar automáticamente los identificadores para picons, \n" -"primero cargue la lista de serviços requeridos en la ventana principal." +"Para configurar automáticamente los identificadores para picons,\n" +"cargue primero la lista de servicios requeridos en la ventana principal." # Satellites editor msgid "Satellites edit tool" -msgstr "Editor de Satélites" +msgstr "Editor de satélites" msgid "Add" msgstr "Añadir" @@ -360,10 +360,10 @@ msgid "Transponder" msgstr "Transpondedor" msgid "Satellite properties:" -msgstr "Propiedades del Satélite:" +msgstr "Propiedades del satélite:" msgid "Transponder properties:" -msgstr "Propiedades del Transpondedor:" +msgstr "Propiedades del transpondedor:" msgid "Name" msgstr "Nombre" @@ -373,30 +373,30 @@ msgstr "Posición" # Satellites update dialog msgid "Satellites update" -msgstr "Actualisar Satélite" +msgstr "Actualizar satélites" msgid "Remove selection" -msgstr "Remover selección" +msgstr "Quitar selección" # Service details dialog msgid "Service data:" msgstr "Datos servicio:" msgid "Transponder data:" -msgstr "Datos Transpondedor:" +msgstr "Datos transpondedor:" msgid "Service data" msgstr "Datos servicio" msgid "Transponder details" -msgstr "Detalles Transpondedor" +msgstr "Detalles transpondedor" msgid "" "Changes will be applied to all services of this transponder!\n" "Continue?" msgstr "" "Los cambios se aplicarán a todos los servicios de este transpondedor!\n" -"Continuar?" +"¿Continuar?" msgid "Reference" msgstr "Referencia" @@ -408,10 +408,10 @@ msgid "Flags:" msgstr "Flags:" msgid "Delays (ms):" -msgstr "Retraso (mc)" +msgstr "Retraso (ms)" msgid "Bitstream" -msgstr "Secuencia de Bits" +msgstr "Secuencia de bits" msgid "Description" msgstr "Descripción" @@ -420,10 +420,10 @@ msgid "Source:" msgstr "Fuente:" msgid "Cancel" -msgstr "Annular" +msgstr "Cancelar" msgid "Update" -msgstr "Actualisar" +msgstr "Actualizar" msgid "Filter" msgstr "Filtrar" @@ -433,7 +433,7 @@ msgstr "Buscar" # IPTV dialog msgid "Stream data" -msgstr "Datos de la Secuencia" +msgstr "Transmitir flujo" # IPTV list configuration dialog msgid "Starting values" @@ -443,7 +443,7 @@ msgid "Reset to default" msgstr "Restablecer a predeterminado" msgid "IPTV streams list configuration" -msgstr "Configurar lista de Secuencias IPTV" +msgstr "Configurar lista de flujos IPTV" # Settings dialog msgid "Preferences" @@ -456,7 +456,7 @@ msgid "Timeout between commands in seconds" msgstr "Tiempo de espera entre comandos en segundos" msgid "Timeout:" -msgstr "Time-out:" +msgstr "Tiempo de espera:" msgid "Login:" msgstr "Usuario:" @@ -471,68 +471,68 @@ msgid "Picons:" msgstr "Picons:" msgid "Port:" -msgstr "Puerta:" +msgstr "Puerto:" msgid "Data path:" msgstr "Ruta de datos:" msgid "Picons path:" -msgstr "Ruta de Picons:" +msgstr "Ruta de picons:" msgid "Network settings:" msgstr "Configuración de red:" msgid "STB file paths:" -msgstr "Ruta de ficherors STB:" +msgstr "Rutas de ficheros del receptor:" msgid "Local file paths:" -msgstr "Ruta de ficheros local:" +msgstr "Rutas de ficheros local:" # Dialogs messages msgid "Error. No bouquet is selected!" -msgstr "Error. Ningún ramo está seleccionado!" +msgstr "Error. ¡Ningún bouquet seleccionado!" msgid "This item is not allowed to be removed!" -msgstr "Este artículo no puede ser eliminado!" +msgstr "¡Este elemento no puede ser quitado!" msgid "This item is not allowed to edit!" -msgstr "Este artículo no puede ser editado!" +msgstr "¡Este elemento no puede ser editado!" msgid "Not allowed in this context!" -msgstr "No permitido en este contexto!" +msgstr "¡No permitido en este contexto!" msgid "Please, download files from receiver or setup your path for read data!" -msgstr "Por favor, descargue archivos desde el receptor o configure su ruta para leer los datos!" +msgstr "Por favor, descargue ficheros desde el receptor o configure la ruta para leer los datos!" msgid "Reading data error!" -msgstr "Error de lectura de datos!" +msgstr "¡Error de lectura de datos!" msgid "No m3u file is selected!" -msgstr "Ningún archivo m3u ha sido seleccionado!" +msgstr "¡No se ha seleccionado ningún fichero m3u!" msgid "Not implemented yet!" -msgstr "Aun no implementado!" +msgstr "¡Aún sin implementar!" msgid "The text of marker is empty, please try again!" -msgstr "El texto del marcador está vacío, inténtalo de nuevo!" +msgstr "¡El texto del marcador está vacío, inténtalo de nuevo!" msgid "Please, select only one item!" -msgstr "Por favor, seleccione solo un elemento!" +msgstr "¡Por favor, seleccione sólo un elemento!" msgid "No png file is selected!" -msgstr "Ningún fichero png seleccionado!" +msgstr "¡No se ha seleccionado ningún fichero png!" msgid "No reference is present!" -msgstr "Ninguna referencia presente!" +msgstr "¡Ninguna referencia presente!" msgid "No selected item!" -msgstr "Ningún elemento seleccionado!" +msgstr "¡Ningún elemento seleccionado!" msgid "The task is already running!" -msgstr "La tarea ya se está ejecutando!" +msgstr "¡La tarea ya se está ejecutando!" msgid "Done!" -msgstr "Hecho!" +msgstr "¡Hecho!" msgid "Please, wait..." msgstr "Por favor, espere..." @@ -541,50 +541,50 @@ msgid "Resizing..." msgstr "Redimensionando..." msgid "Select paths!" -msgstr "Seleccione rutas!" +msgstr "¡Seleccione rutas!" msgid "No satellite is selected!" -msgstr "Ningún Satélite seleccionado!" +msgstr "¡Ningún satélite seleccionado!" msgid "Please, select only one satellite!" -msgstr "Seleccione solo un Satélite!" +msgstr "¡Seleccione sólo un Satélite!" msgid "Please check your parameters and try again." -msgstr "Por favor revise sus parámetros y vuelva a intentar!" +msgstr "¡Por favor revise sus parámetros y vuelva a intentarlo!" msgid "No satellites.xml file is selected!" -msgstr "Ningún satellites.xml seleccionado!" +msgstr "¡Ningún satellites.xml seleccionado!" msgid "Error. Verify the data!" -msgstr "Error. Revise sus datos!" +msgstr "Error. ¡Revise los datos!" msgid "Operation not allowed in this context!" -msgstr "Operación no permitida en este contexto!" +msgstr "¡Operación no permitida en este contexto!" msgid "No VLC is found. Check that it is installed!" -msgstr "VLC no encontrado. Verifica si está instalado!" +msgstr "VLC no encontrado. ¡Verifique que está instalado!" # Search unavailable streams dialog msgid "Please wait, streams testing in progress..." -msgstr "Por favor espera una prueba de las secuencias..." +msgstr "Por favor espere, hay una prueba de flujo en progreso..." msgid "Found" msgstr "Encontrado" msgid "unavailable streams." -msgstr "Secuencias no presentes" +msgstr "Flujos no presentes." msgid "No changes required!" -msgstr "ningún cambio requerido!" +msgstr "¡Ningún cambio requerido!" msgid "This list does not contains IPTV streams!" -msgstr "La lista no contiene secuencias IPTV!" +msgstr "¡La lista no contiene flujos IPTV!" msgid "New empty configuration" msgstr "Nueva configuración vacía" msgid "No data to save!" -msgstr "No hay datos para guardar!" +msgstr "¡No hay datos que guardar!" msgid "Network" msgstr "Red" @@ -596,19 +596,19 @@ msgid "Program" msgstr "Programa" msgid "Backup:" -msgstr "Backup:" +msgstr "Copia de seguridad:" msgid "Backup" -msgstr "Backup" +msgstr "Copia de seguridad" msgid "Backups" -msgstr "Backups" +msgstr "Copias de seguridad" msgid "Backup path:" -msgstr "Ruta del backup:" +msgstr "Ruta de la copia de seguridad:" msgid "Restore bouquets" -msgstr "Restaurar ramos" +msgstr "Restaurar bouquets" msgid "Restore all" msgstr "Restaurar todo" @@ -620,19 +620,19 @@ msgid "Before downloading from the receiver" msgstr "Antes de recibir del receptor" msgid "Set background color for the services" -msgstr "Determinar color de fondo para servicios" +msgstr "Determinar color de fondo de los servicios" msgid "Marked as new:" msgstr "Marcado como nuevo:" msgid "With an extra name in the bouquet:" -msgstr "Con nombre adicional en ramo:" +msgstr "Con nombre adicional en bouquet:" msgid "Select" msgstr "Seleccione" msgid "About" -msgstr "Sobre" +msgstr "Acerca de" msgid "Exit" msgstr "Salir" @@ -645,19 +645,19 @@ msgid "Import" msgstr "Importar" msgid "Bouquet" -msgstr "Ramo" +msgstr "Bouquet" msgid "Bouquets and services" -msgstr "Ramos y servicios" +msgstr "Bouquets y servicios" msgid "The main list does not contain services for this bouquet!" -msgstr "La lista principal no contiene servicios para este ramo!" +msgstr "¡La lista principal no contiene servicios para este bouquet!" msgid "No bouquet file is selected!" -msgstr "Nigún fichero de ramo ha sido seleccionado!" +msgstr "¡No se ha seleccionado nigún fichero de bouquet!" msgid "Remove all unused" -msgstr "Quite todos los" +msgstr "Quitar todos sin usar" msgid "Test" msgstr "Prueba" @@ -672,7 +672,7 @@ msgid "Zap" msgstr "Zapear" msgid "Play stream" -msgstr "Reproducir secuencia" +msgstr "Reproducir flujo" msgid "Disabled" msgstr "Desactivado" @@ -684,16 +684,16 @@ msgid "Enable HTTP API (experimental)" msgstr "Habilitar API HTTP (experimental)" msgid "Switch(zap) the channel(Ctrl + Z)" -msgstr "Cambiar (ZAP) el canal (Ctrl + Z)" +msgstr "Poner el canal (Ctrl + Z)" msgid "Switch the channel and watch in the program(Ctrl + W)" -msgstr "Cambiar el canal y ver en el programa (Ctrl + W)" +msgstr "Poner el canal y ver en el programa (Ctrl + W)" msgid "Play IPTV or other stream in the program(Ctrl + P)" msgstr "Reproducir IPTV u otro flujo en el programa (Ctrl + P)" msgid "Export to m3u" -msgstr "Exportar hacia m3u" +msgstr "Exportar a m3u" msgid "EPG configuration" msgstr "Configuración EPG" @@ -705,61 +705,61 @@ msgid "EPG source" msgstr "Fuente EPG" msgid "Service names source:" -msgstr "Nombre de servicio fuente:" +msgstr "Origen nombres de servicio:" msgid "Main service list" msgstr "Lista principal de servicios:" msgid "XML file" -msgstr "Archivo XML" +msgstr "Fichero XML" msgid "Use web source" msgstr "Usar fuente web" msgid "Url to *.xml.gz file:" -msgstr "URL del archivo *.xml.gz:" +msgstr "URL del fichero *.xml.gz:" msgid "Enable filtering" -msgstr "Habilitar filtrar" +msgstr "Habilitar filtrado" msgid "Filter by presence in the epg.dat file." -msgstr "Filtrar por presencia del archivo epg.dat." +msgstr "Filtrar según presencia del fichero epg.dat." msgid "Paths to the epg.dat file:" -msgstr "Ruta al archivo epg.dat:" +msgstr "Ruta al fichero epg.dat:" msgid "Local path:" msgstr "Ruta local:" msgid "STB path:" -msgstr "Ruta STB:" +msgstr "Ruta receptor:" msgid "Update on start" -msgstr "Actualisar al iniciar" +msgstr "Actualizar al inicio" msgid "Auto configuration by service names." -msgstr "Auto configuración por nombres de servicios." +msgstr "Auto configuración según nombres de servicios." msgid "Save list to xml." msgstr "Guardar como XML." msgid "Download XML file error." -msgstr "Error bajando archivo XML." +msgstr "Error bajando fichero XML." msgid "Unsupported file type:" -msgstr "Archivo no supportado:" +msgstr "Fichero no soportado:" msgid "Unpacking data error." -msgstr "Error abriende datos." +msgstr "Error abriendo datos." msgid "XML parsing error:" -msgstr "Error analisando XML:" +msgstr "Error analizando XML:" msgid "Count of successfully configured services:" msgstr "Número de servicios configurados con éxito:" msgid "Current epg.dat file does not contains references for the services of this bouquet!" -msgstr "Archivo epg.dat actual no tiene referencias a servicios de este ramo!" +msgstr "¡El fichero epg.dat actual no tiene referencias a servicios de este bouquet!" msgid "Use HTTP" msgstr "Utilizar HTTP" @@ -771,10 +771,10 @@ msgid "Import YouTube playlist" msgstr "Importar lista de reproducción de YouTube" msgid "Found a link to the YouTube resource!\nTry to get a direct link to the video?" -msgstr "Encontré un enlace al recurso de YouTube!\nIntentar obtener un enlace directo al video?" +msgstr "¡Encontrado enlace al recurso de YouTube!\n¿Intentar obtener un enlace directo al vídeo?" msgid "Playlist import" msgstr "Importar lista de reproducción" msgid "Getting link error:" -msgstr "Recibido Link error:" \ No newline at end of file +msgstr "Error en el enlace:"