diff --git a/app/connections.py b/app/connections.py index bdce6cac..ea9cff51 100644 --- a/app/connections.py +++ b/app/connections.py @@ -12,7 +12,7 @@ from urllib.parse import urlencode from urllib.request import urlopen, HTTPPasswordMgrWithDefaultRealm, HTTPBasicAuthHandler, build_opener, install_opener from app.commons import log -from app.settings import Profile +from app.settings import SettingsType _BQ_FILES_LIST = ("tv", "radio", # enigma 2 "myservices.xml", "bouquets.xml", "ubouquets.xml") # neutrino @@ -49,7 +49,7 @@ 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 = settings.data_dir_path + save_path = settings.data_local_path os.makedirs(os.path.dirname(save_path), exist_ok=True) files = [] # bouquets @@ -94,14 +94,14 @@ def download_data(*, settings, download_type=DownloadType.ALL, callback=print): def upload_data(*, settings, download_type=DownloadType.ALL, remove_unused=False, callback=print, done_callback=None, use_http=False): - profile = settings.profile - data_path = settings.data_dir_path + s_type = settings.setting_type + data_path = settings.data_local_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: + if s_type is SettingsType.ENIGMA_2 and use_http: ht = http(settings.http_user, settings.http_password, base_url, callback) next(ht) message = "" @@ -139,7 +139,7 @@ def upload_data(*, settings, download_type=DownloadType.ALL, remove_unused=False if download_type is DownloadType.SATELLITES: upload_xml(ftp, data_path, sat_xml_path, _SAT_XML_FILE, callback) - if profile is Profile.NEUTRINO_MP and download_type is DownloadType.WEBTV: + if s_type is SettingsType.NEUTRINO_MP and download_type is DownloadType.WEBTV: upload_xml(ftp, data_path, sat_xml_path, _WEBTV_XML_FILE, callback) if download_type is DownloadType.BOUQUETS: @@ -148,7 +148,7 @@ def upload_data(*, settings, download_type=DownloadType.ALL, remove_unused=False if download_type is DownloadType.ALL: upload_xml(ftp, data_path, sat_xml_path, _SAT_XML_FILE, callback) - if profile is Profile.NEUTRINO_MP: + if s_type is SettingsType.NEUTRINO_MP: upload_xml(ftp, data_path, sat_xml_path, _WEBTV_XML_FILE, callback) ftp.cwd(services_path) @@ -156,11 +156,11 @@ def upload_data(*, settings, download_type=DownloadType.ALL, remove_unused=False upload_files(ftp, data_path, _DATA_FILES_LIST, callback) if download_type is DownloadType.PICONS: - upload_picons(ftp, settings.picons_dir_path, settings.picons_path, callback) + upload_picons(ftp, settings.picons_local_path, settings.picons_path, callback) if tn and not use_http: # resume enigma or restart neutrino - tn.send("init 3" if profile is Profile.ENIGMA_2 else "init 6") + tn.send("init 3" if s_type is SettingsType.ENIGMA_2 else "init 6") elif ht and use_http: if download_type is DownloadType.BOUQUETS: ht.send(base_url + "/servicelistreload?mode=2") diff --git a/app/eparser/__init__.py b/app/eparser/__init__.py index fff031a5..d514d26b 100644 --- a/app/eparser/__init__.py +++ b/app/eparser/__init__.py @@ -1,5 +1,5 @@ from app.commons import run_task -from app.settings import Profile +from app.settings import SettingsType 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 @@ -10,33 +10,33 @@ from .neutrino.services import get_services as get_neutrino_services, write_serv from .satxml import get_satellites, write_satellites -def get_services(data_path, profile, format_version): - if profile is Profile.ENIGMA_2: +def get_services(data_path, s_type, format_version): + if s_type is SettingsType.ENIGMA_2: return get_enigma_services(data_path, format_version) - elif profile is Profile.NEUTRINO_MP: + elif s_type is SettingsType.NEUTRINO_MP: return get_neutrino_services(data_path) @run_task -def write_services(path, channels, profile, format_version): - if profile is Profile.ENIGMA_2: +def write_services(path, channels, s_type, format_version): + if s_type is SettingsType.ENIGMA_2: write_enigma_services(path, channels, format_version) - elif profile is Profile.NEUTRINO_MP: + elif s_type is SettingsType.NEUTRINO_MP: write_neutrino_services(path, channels) -def get_bouquets(path, profile): - if profile is Profile.ENIGMA_2: +def get_bouquets(path, s_type): + if s_type is SettingsType.ENIGMA_2: return get_enigma_bouquets(path) - elif profile is Profile.NEUTRINO_MP: + elif s_type is SettingsType.NEUTRINO_MP: return get_neutrino_bouquets(path) @run_task -def write_bouquets(path, bouquets, profile): - if profile is Profile.ENIGMA_2: +def write_bouquets(path, bouquets, s_type): + if s_type is SettingsType.ENIGMA_2: write_enigma_bouquets(path, bouquets) - elif profile is Profile.NEUTRINO_MP: + elif s_type is SettingsType.NEUTRINO_MP: write_neutrino_bouquets(path, bouquets) diff --git a/app/eparser/iptv.py b/app/eparser/iptv.py index b47a8967..55451b5e 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.settings import Profile +from app.settings import SettingsType from app.ui.uicommons import IPTV_ICON from .ecommons import BqServiceType, Service @@ -20,18 +20,18 @@ class StreamType(Enum): NONE_REC_2 = "5002" -def parse_m3u(path, profile): +def parse_m3u(path, s_type): with open(path) as file: aggr = [None] * 10 services = [] groups = set() counter = 0 name = None - + for line in file.readlines(): if line.startswith("#EXTINF"): name = line[1 + line.index(","):].strip() - elif line.startswith("#EXTGRP") and profile is Profile.ENIGMA_2: + elif line.startswith("#EXTGRP") and s_type is SettingsType.ENIGMA_2: grp_name = line.strip("#EXTGRP:").strip() if grp_name not in groups: groups.add(grp_name) @@ -41,7 +41,7 @@ def parse_m3u(path, profile): services.append(mr) elif not line.startswith("#"): url = line.strip() - fav_id = get_fav_id(url, name, profile) + fav_id = get_fav_id(url, name, s_type) if name and url: srv = Service(None, None, IPTV_ICON, name, *aggr[0:3], BqServiceType.IPTV.name, *aggr, fav_id, None) services.append(srv) @@ -49,8 +49,8 @@ def parse_m3u(path, profile): return services -def export_to_m3u(path, bouquet, profile): - pattern = re.compile(".*:(http.*):.*") if profile is Profile.ENIGMA_2 else re.compile("(http.*?)::::.*") +def export_to_m3u(path, bouquet, s_type): + pattern = re.compile(".*:(http.*):.*") if s_type is SettingsType.ENIGMA_2 else re.compile("(http.*?)::::.*") lines = ["#EXTM3U\n"] current_grp = None @@ -72,13 +72,13 @@ def export_to_m3u(path, bouquet, profile): file.writelines(lines) -def get_fav_id(url, service_name, profile): +def get_fav_id(url, service_name, s_type): """ Returns fav id depending on the profile. """ - if profile is Profile.ENIGMA_2: + if s_type is SettingsType.ENIGMA_2: url = urllib.request.quote(url) stream_type = StreamType.NONE_TS.value return ENIGMA2_FAV_ID_FORMAT.format(stream_type, 1, 0, 0, 0, 0, url, service_name, service_name, None) - elif profile is Profile.NEUTRINO_MP: + elif s_type is SettingsType.NEUTRINO_MP: return NEUTRINO_FAV_ID_FORMAT.format(url, "", 0, None, None, None, None, "", "", 1) diff --git a/app/settings.py b/app/settings.py index a73cd704..9fa3633a 100644 --- a/app/settings.py +++ b/app/settings.py @@ -2,7 +2,7 @@ import json import os from pprint import pformat from textwrap import dedent -from enum import Enum +from enum import Enum, IntEnum from pathlib import Path CONFIG_PATH = str(Path.home()) + "/.config/demon-editor/" @@ -10,19 +10,85 @@ CONFIG_FILE = CONFIG_PATH + "config.json" DATA_PATH = "data/" -class Profile(Enum): +class Defaults(Enum): + """ Default program settings """ + DEFAULT_PROFILE = "default" + BACKUP_BEFORE_DOWNLOADING = True + BACKUP_BEFORE_SAVE = 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 + + +def get_default_settings(): + return { + "version": 1, + "default_profile": Defaults.DEFAULT_PROFILE.value, + "profiles": {"default": SettingsType.ENIGMA_2.get_default_settings()}, + "v5_support": Defaults.V5_SUPPORT.value, + "http_api_support": Defaults.HTTP_API_SUPPORT.value, + "enable_yt_dl": Defaults.ENABLE_YT_DL.value, + "enable_send_to": Defaults.ENABLE_SEND_TO.value, + "use_colors": Defaults.USE_COLORS.value, + "new_color": Defaults.NEW_COLOR.value, + "extra_color": Defaults.EXTRA_COLOR.value, + "fav_click_mode": Defaults.FAV_CLICK_MODE.value + } + + +class SettingsType(IntEnum): """ Profiles for settings """ - ENIGMA_2 = "0" - NEUTRINO_MP = "1" + ENIGMA_2 = 0 + NEUTRINO_MP = 1 + + def get_default_settings(self): + """ Returns default settings for current type """ + if self is self.ENIGMA_2: + return {"setting_type": self, + "host": "127.0.0.1", "port": "21", "user": "root", "password": "root", "timeout": 5, + "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_local_path": DATA_PATH + "enigma2/", + "picons_path": "/usr/share/enigma2/picon", + "picons_local_path": DATA_PATH + "enigma2/picons/", + "backup_local_path": DATA_PATH + "enigma2/backup/"} + elif self is self.NEUTRINO_MP: + return {"setting_type": self, + "host": "127.0.0.1", "port": "21", "user": "root", "password": "root", "timeout": 5, + "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_local_path": DATA_PATH + "neutrino/", + "picons_path": "/usr/share/tuxbox/neutrino/icons/logo/", + "picons_local_path": DATA_PATH + "neutrino/picons/", + "backup_local_path": DATA_PATH + "neutrino/backup/"} + + +class SettingsException(Exception): + pass class Settings: __INSTANCE = None + __VERSION = 1 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) + settings = get_settings() + + if self.__VERSION > settings.get("version", 0): + write_settings(get_default_settings()) + raise SettingsException("Outdated version of the settings format!") + + self._settings = settings + self._current_profile = self._settings.get("default_profile", "default") + self._profiles = self._settings.get("profiles", {"default": SettingsType.ENIGMA_2.get_default_settings()}) + self._cp_settings = self._profiles.get(self._current_profile) # Current profile settings def __str__(self): return dedent(""" Current profile: {} @@ -31,8 +97,8 @@ class Settings: Full config: {} """).format(self._current_profile, - pformat(self._current_profile_options), - pformat(self._config)) + pformat(self._cp_settings), + pformat(self._settings)) @classmethod def get_instance(cls): @@ -41,325 +107,296 @@ class Settings: return cls.__INSTANCE def save(self): - write_config(self._config) + write_settings(self._settings) 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) + for k, v in self.setting_type.get_default_settings().items(): + self._cp_settings[k] = v if force_write: - write_config(get_default_settings()) + self.save() + + def get_default(self, p_name): + """ Returns default value for current settings type """ + return self.setting_type.get_default_settings().get(p_name) def add(self, name, value): """ Adds extra options """ - self._config[name] = value + self._settings[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) + """ Returns extra options or None """ + return self._settings.get(name, None) @property - def presets(self): - raise NotImplementedError + def profiles(self): + return self._profiles - @presets.setter - def presets(self, name): - raise NotImplementedError + @profiles.setter + def profiles(self, ps): + self._profiles = ps + self._settings["profiles"] = self._profiles @property - def profile(self): - return self._current_profile + def setting_type(self): + return SettingsType(self._cp_settings.get("setting_type", SettingsType.ENIGMA_2.value)) - @profile.setter - def profile(self, prf): - self._current_profile = prf - self._config["profile"] = prf.value - self._current_profile_options = self._config.get(prf.value) + @setting_type.setter + def setting_type(self, s_type): + self._cp_settings["setting_type"] = s_type + for k, v in s_type.get_default_settings().items(): + self._cp_settings[k] = v @property def host(self): - return self._current_profile_options.get("host", self.get_default("host")) + return self._cp_settings.get("host", self.get_default("host")) @host.setter def host(self, value): - self._current_profile_options["host"] = value + self._cp_settings["host"] = value @property def port(self): - return self._current_profile_options.get("port", self.get_default("port")) + return self._cp_settings.get("port", self.get_default("port")) @port.setter def port(self, value): - self._current_profile_options["port"] = value + self._cp_settings["port"] = value @property def user(self): - return self._current_profile_options.get("user", self.get_default("user")) + return self._cp_settings.get("user", self.get_default("user")) @user.setter def user(self, value): - self._current_profile_options["user"] = value + self._cp_settings["user"] = value @property def password(self): - return self._current_profile_options.get("password", self.get_default("password")) + return self._cp_settings.get("password", self.get_default("password")) @password.setter def password(self, value): - self._current_profile_options["password"] = value + self._cp_settings["password"] = value @property def http_user(self): - return self._current_profile_options.get("http_user", self.get_default("http_user")) + return self._cp_settings.get("http_user", self.get_default("http_user")) @http_user.setter def http_user(self, value): - self._current_profile_options["http_user"] = value + self._cp_settings["http_user"] = value @property def http_password(self): - return self._current_profile_options.get("http_password", self.get_default("http_password")) + return self._cp_settings.get("http_password", self.get_default("http_password")) @http_password.setter def http_password(self, value): - self._current_profile_options["http_password"] = value + self._cp_settings["http_password"] = value @property def http_port(self): - return self._current_profile_options.get("http_port", self.get_default("http_port")) + return self._cp_settings.get("http_port", self.get_default("http_port")) @http_port.setter def http_port(self, value): - self._current_profile_options["http_port"] = value + self._cp_settings["http_port"] = value @property def http_timeout(self): - return self._current_profile_options.get("http_timeout", self.get_default("http_timeout")) + return self._cp_settings.get("http_timeout", self.get_default("http_timeout")) @http_timeout.setter def http_timeout(self, value): - self._current_profile_options["http_timeout"] = value + self._cp_settings["http_timeout"] = value @property def telnet_user(self): - return self._current_profile_options.get("telnet_user", self.get_default("telnet_user")) + return self._cp_settings.get("telnet_user", self.get_default("telnet_user")) @telnet_user.setter def telnet_user(self, value): - self._current_profile_options["telnet_user"] = value + self._cp_settings["telnet_user"] = value @property def telnet_password(self): - return self._current_profile_options.get("telnet_password", self.get_default("telnet_password")) + return self._cp_settings.get("telnet_password", self.get_default("telnet_password")) @telnet_password.setter def telnet_password(self, value): - self._current_profile_options["telnet_password"] = value + self._cp_settings["telnet_password"] = value @property def telnet_port(self): - return self._current_profile_options.get("telnet_port", self.get_default("telnet_port")) + return self._cp_settings.get("telnet_port", self.get_default("telnet_port")) @telnet_port.setter def telnet_port(self, value): - self._current_profile_options["telnet_port"] = value + self._cp_settings["telnet_port"] = value @property def telnet_timeout(self): - return self._current_profile_options.get("telnet_timeout", self.get_default("telnet_timeout")) + return self._cp_settings.get("telnet_timeout", self.get_default("telnet_timeout")) @telnet_timeout.setter def telnet_timeout(self, value): - self._current_profile_options["telnet_timeout"] = value + self._cp_settings["telnet_timeout"] = value @property def services_path(self): - return self._current_profile_options.get("services_path", self.get_default("services_path")) + return self._cp_settings.get("services_path", self.get_default("services_path")) @services_path.setter def services_path(self, value): - self._current_profile_options["services_path"] = value + self._cp_settings["services_path"] = value @property def user_bouquet_path(self): - return self._current_profile_options.get("user_bouquet_path", self.get_default("user_bouquet_path")) + return self._cp_settings.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 + self._cp_settings["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")) + return self._cp_settings.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 + self._cp_settings["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")) + def data_local_path(self): + return self._cp_settings.get("data_local_path", self.get_default("data_local_path")) - @data_dir_path.setter - def data_dir_path(self, value): - self._current_profile_options["data_dir_path"] = value + @data_local_path.setter + def data_local_path(self, value): + self._cp_settings["data_local_path"] = value @property def picons_path(self): - return self._current_profile_options.get("picons_path", self.get_default("picons_path")) + return self._cp_settings.get("picons_path", self.get_default("picons_path")) @picons_path.setter def picons_path(self, value): - self._current_profile_options["picons_path"] = value + self._cp_settings["picons_path"] = value @property - def picons_dir_path(self): - return self._current_profile_options.get("picons_dir_path", self.get_default("picons_dir_path")) + def picons_local_path(self): + return self._cp_settings.get("picons_local_path", self.get_default("picons_local_path")) - @picons_dir_path.setter - def picons_dir_path(self, value): - self._current_profile_options["picons_dir_path"] = value + @picons_local_path.setter + def picons_local_path(self, value): + self._cp_settings["picons_local_path"] = value @property - def backup_dir_path(self): - return self._current_profile_options.get("backup_dir_path", self.get_default("backup_dir_path")) + def backup_local_path(self): + return self._cp_settings.get("backup_local_path", self.get_default("backup_local_path")) - @backup_dir_path.setter - def backup_dir_path(self, value): - self._current_profile_options["backup_dir_path"] = value + @backup_local_path.setter + def backup_local_path(self, value): + self._cp_settings["backup_local_path"] = value + + # ***** Program settings ***** @property def backup_before_save(self): - return self._current_profile_options.get("backup_before_save", self.get_default("backup_before_save")) + return self._settings.get("backup_before_save", Defaults.BACKUP_BEFORE_SAVE.value) @backup_before_save.setter def backup_before_save(self, value): - self._current_profile_options["backup_before_save"] = value + self._settings["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")) + return self._settings.get("backup_before_downloading", Defaults.BACKUP_BEFORE_DOWNLOADING.value) @backup_before_downloading.setter def backup_before_downloading(self, value): - self._current_profile_options["backup_before_downloading"] = value + self._settings["backup_before_downloading"] = value @property def v5_support(self): - return self._current_profile_options.get("v5_support", self.get_default("v5_support")) + return self._settings.get("v5_support", Defaults.V5_SUPPORT.value) @v5_support.setter def v5_support(self, value): - self._current_profile_options["v5_support"] = value + self._settings["v5_support"] = value @property def http_api_support(self): - return self._current_profile_options.get("http_api_support", self.get_default("http_api_support")) + return self._settings.get("http_api_support", Defaults.HTTP_API_SUPPORT.value) @http_api_support.setter def http_api_support(self, value): - self._current_profile_options["http_api_support"] = value + self._settings["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")) + return self._settings.get("enable_yt_dl", Defaults.ENABLE_YT_DL.value) @enable_yt_dl.setter def enable_yt_dl(self, value): - self._current_profile_options["enable_yt_dl"] = value + self._settings["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")) + return self._settings.get("enable_send_to", Defaults.ENABLE_SEND_TO.value) @enable_send_to.setter def enable_send_to(self, value): - self._current_profile_options["enable_send_to"] = value + self._settings["enable_send_to"] = value @property def use_colors(self): - return self._current_profile_options.get("use_colors", self.get_default("use_colors")) + return self._settings.get("use_colors", Defaults.USE_COLORS.value) @use_colors.setter def use_colors(self, value): - self._current_profile_options["use_colors"] = value + self._settings["use_colors"] = value @property def new_color(self): - return self._current_profile_options.get("new_color", self.get_default("new_color")) + return self._settings.get("new_color", Defaults.NEW_COLOR.value) @new_color.setter def new_color(self, value): - self._current_profile_options["new_color"] = value + self._settings["new_color"] = value @property def extra_color(self): - return self._current_profile_options.get("extra_color", self.get_default("extra_color")) + return self._settings.get("extra_color", Defaults.EXTRA_COLOR.value) @extra_color.setter def extra_color(self, value): - self._current_profile_options["extra_color"] = value + self._settings["extra_color"] = value @property def fav_click_mode(self): - return self._current_profile_options.get("fav_click_mode", self.get_default("fav_click_mode")) + return self._settings.get("fav_click_mode", Defaults.FAV_CLICK_MODE.value) @fav_click_mode.setter def fav_click_mode(self, value): - self._current_profile_options["fav_click_mode"] = value + self._settings["fav_click_mode"] = value -def get_config(): +def get_settings(): 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()) + write_settings(get_default_settings()) with open(CONFIG_FILE, "r") as config_file: return json.load(config_file) -def write_config(config): +def write_settings(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/picons.py b/app/tools/picons.py index 87f3d5a4..9e4419bf 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.settings import Profile +from app.settings import SettingsType _ENIGMA2_PICON_KEY = "{:X}:{:X}:{}" _NEUTRINO_PICON_KEY = "{:x}{:04x}{:04x}.png" @@ -79,7 +79,7 @@ class PiconsParser(HTMLParser): pass @staticmethod - def parse(open_path, picons_path, tmp_path, provider, picon_ids, profile=Profile.ENIGMA_2): + def parse(open_path, picons_path, tmp_path, provider, picon_ids, s_type=SettingsType.ENIGMA_2): with open(open_path, encoding="utf-8", errors="replace") as f: on_id, pos, ssid, single = provider.on_id, provider.pos, provider.ssid, provider.single neg_pos = pos.endswith("W") @@ -100,7 +100,7 @@ class PiconsParser(HTMLParser): namespace = "{:X}{:X}".format(int(pos), int(freq)) else: namespace = "{:X}0000".format(int(pos)) - name = PiconsParser.format(ssid if single else p.ssid, on_id, namespace, picon_ids, profile) + name = PiconsParser.format(ssid if single else p.ssid, on_id, namespace, picon_ids, s_type) p_name = picons_path + (name if name else os.path.basename(p.ref)) shutil.copyfile(tmp_path + "www.lyngsat.com/" + p.ref.lstrip("."), p_name) except (TypeError, ValueError) as e: @@ -109,10 +109,10 @@ class PiconsParser(HTMLParser): print(msg) @staticmethod - def format(ssid, on_id, namespace, picon_ids, profile: Profile): - if profile is Profile.ENIGMA_2: + def format(ssid, on_id, namespace, picon_ids, s_type): + if s_type is SettingsType.ENIGMA_2: return picon_ids.get(_ENIGMA2_PICON_KEY.format(int(ssid), int(on_id), namespace), None) - elif profile is Profile.NEUTRINO_MP: + elif s_type is SettingsType.NEUTRINO_MP: tr_id = int(ssid[:-2] if len(ssid) < 4 else ssid[:2]) return _NEUTRINO_PICON_KEY.format(tr_id, int(on_id), int(ssid)) else: @@ -249,12 +249,12 @@ def parse_providers(open_path): @run_task -def convert_to(src_path, dest_path, profile, callback, done_callback): +def convert_to(src_path, dest_path, s_type, callback, done_callback): """ Converts names format of picons. Copies resulting files from src to dest and writes state to callback. """ - pattern = "/*_0_0_0.png" if profile is Profile.ENIGMA_2 else "/*.png" + pattern = "/*_0_0_0.png" if s_type is SettingsType.ENIGMA_2 else "/*.png" for file in glob.glob(src_path + pattern): base_name = os.path.basename(file) pic_data = base_name.rstrip(".png").split("_") diff --git a/app/ui/backup.py b/app/ui/backup.py index 7aca7613..048ff058 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.settings import Profile +from app.settings import SettingsType 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 @@ -36,9 +36,9 @@ class BackupDialog: builder.connect_signals(handlers) 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._s_type = settings.setting_type + self._data_path = self._settings.data_local_path + self._backup_path = self._settings.backup_local_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) @@ -152,7 +152,7 @@ class BackupDialog: shutil.unpack_archive(full_file_name, self._data_path) elif restore_type is RestoreType.BOUQUETS: tmp_dir = tempfile.gettempdir() + "/" + file_name - cond = (".tv", ".radio") if self._profile is Profile.ENIGMA_2 else "bouquets.xml" + cond = (".tv", ".radio") if self._s_type is SettingsType.ENIGMA_2 else "bouquets.xml" shutil.unpack_archive(full_file_name, tmp_dir) for file in filter(lambda f: f.endswith(cond), os.listdir(self._data_path)): os.remove(os.path.join(self._data_path, file)) diff --git a/app/ui/dialogs.py b/app/ui/dialogs.py index 596b89f6..8601ca8a 100644 --- a/app/ui/dialogs.py +++ b/app/ui/dialogs.py @@ -101,7 +101,7 @@ def get_file_chooser_dialog(transient, text, settings, action_type, file_filter) if file_filter is not None: dialog.add_filter(file_filter) - path = settings.data_dir_path + path = settings.data_local_path dialog.set_current_folder(path) response = dialog.run() if response == Gtk.ResponseType.OK: diff --git a/app/ui/download_dialog.py b/app/ui/download_dialog.py index 91d0d9c5..76fb06d6 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.settings import Profile +from app.settings import SettingsType 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 @@ -12,7 +12,7 @@ from .dialogs import show_dialog, DialogType, get_message class DownloadDialog: def __init__(self, transient, settings, open_data_callback, update_settings_callback): - self._profile = settings.profile + self._s_type = settings.setting_type self._settings = settings self._open_data_callback = open_data_callback self._update_settings_callback = update_settings_callback @@ -59,8 +59,8 @@ class DownloadDialog: 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._data_path_entry.set_text(self._settings.data_local_path) + is_enigma = self._s_type is SettingsType.ENIGMA_2 self._webtv_radio_button.set_visible(not is_enigma) self._http_radio_button.set_visible(is_enigma) self._use_http_box.set_visible(is_enigma) @@ -111,7 +111,7 @@ class DownloadDialog: def on_preferences(self, item): response = show_settings_dialog(self._dialog_window, self._settings) if response != Gtk.ResponseType.CANCEL: - self._profile = self._settings.profile + self._s_type = self._settings.setting_type self.init_settings() gen = self._update_settings_callback() GLib.idle_add(lambda: next(gen, False), priority=GLib.PRIORITY_LOW) @@ -134,8 +134,8 @@ class DownloadDialog: try: if download: if backup and d_type is not DownloadType.SATELLITES: - 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/" + data_path = self._settings.data_local_path or self._data_path_entry.get_text() + backup_path = self._settings.backup_local_path or data_path + "backup/" backup_src = backup_data(data_path, backup_path, d_type is DownloadType.ALL) download_data(settings=self._settings, download_type=d_type, callback=self.append_output) else: diff --git a/app/ui/epg_dialog.py b/app/ui/epg_dialog.py index de6e49c3..151bea9f 100644 --- a/app/ui/epg_dialog.py +++ b/app/ui/epg_dialog.py @@ -483,7 +483,7 @@ class EpgDialog: # ***************** Options *********************# def init_options(self): - epg_dat_path = self._settings.data_dir_path + "epg/" + epg_dat_path = self._settings.data_local_path + "epg/" self._epg_dat_path_entry.set_text(epg_dat_path) default_epg_data_stb_path = "/etc/enigma2" epg_options = self._settings.get("epg_options") diff --git a/app/ui/imports.py b/app/ui/imports.py index bcbf979c..8c203759 100644 --- a/app/ui/imports.py +++ b/app/ui/imports.py @@ -6,7 +6,7 @@ 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.settings import Profile +from app.settings import SettingsType 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 @@ -17,12 +17,12 @@ def import_bouquet(transient, model, path, settings, services, appender): itr = model.get_iter(path) bq_type = BqType(model.get(itr, Column.BQ_TYPE)[0]) pattern, f_pattern = None, None - profile = settings.profile + profile = settings.setting_type - if profile is Profile.ENIGMA_2: + if profile is SettingsType.ENIGMA_2: pattern = ".{}".format(bq_type.value) f_pattern = "userbouquet.*{}".format(pattern) - elif profile is Profile.NEUTRINO_MP: + elif profile is SettingsType.NEUTRINO_MP: pattern = "webtv.xml" if bq_type is BqType.WEBTV else "bouquets.xml" f_pattern = "bouquets.xml" if bq_type is BqType.TV: @@ -38,7 +38,7 @@ def import_bouquet(transient, model, path, settings, services, appender): show_dialog(DialogType.ERROR, transient, text="No bouquet file is selected!") return - if profile is Profile.ENIGMA_2: + if profile is SettingsType.ENIGMA_2: bq = get_enigma2_bouquet(file_path) imported = list(filter(lambda x: x.data in services or x.type is BqServiceType.IPTV, bq.services)) @@ -51,7 +51,7 @@ def import_bouquet(transient, model, path, settings, services, appender): else: p_itr = model.iter_parent(itr) appender(bq, p_itr) if p_itr else appender(bq, itr) - elif profile is Profile.NEUTRINO_MP: + elif profile is SettingsType.NEUTRINO_MP: if bq_type is BqType.WEBTV: bqs = parse_webtv(file_path, "WEBTV", bq_type.value) else: @@ -90,7 +90,7 @@ class ImportDialog: self._services = {} self._service_ids = service_ids self._append = appender - self._profile = settings.profile + self._profile = settings.setting_type self._settings = settings self._bouquets = bouquets @@ -125,7 +125,7 @@ class ImportDialog: self._main_model.append((bq.name, bq.type, True)) self._bq_services[(bq.name, bq.type)] = bq.services # Note! Getting default format ver. 4 - services = get_services(path, self._profile, 4 if self._profile is Profile.ENIGMA_2 else 0) + services = get_services(path, self._profile, 4 if self._profile is SettingsType.ENIGMA_2 else 0) for srv in services: self._services[srv.fav_id] = srv except FileNotFoundError as e: diff --git a/app/ui/iptv.py b/app/ui/iptv.py index 7693daeb..1c6705e2 100644 --- a/app/ui/iptv.py +++ b/app/ui/iptv.py @@ -13,7 +13,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.settings import Profile +from app.settings import SettingsType 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 @@ -61,7 +61,7 @@ def get_yt_icon(icon_name, size=24): class IptvDialog: - def __init__(self, transient, view, services, bouquet, profile=Profile.ENIGMA_2, action=Action.ADD): + def __init__(self, transient, view, services, bouquet, profile=SettingsType.ENIGMA_2, action=Action.ADD): handlers = {"on_response": self.on_response, "on_entry_changed": self.on_entry_changed, "on_url_changed": self.on_url_changed, @@ -109,7 +109,7 @@ class IptvDialog: for el in self._digit_elems: el.get_style_context().add_provider_for_screen(Gdk.Screen.get_default(), self._style_provider, Gtk.STYLE_PROVIDER_PRIORITY_USER) - if profile is Profile.NEUTRINO_MP: + if profile is SettingsType.NEUTRINO_MP: builder.get_object("iptv_dialog_ts_data_frame").set_visible(False) builder.get_object("iptv_type_label").set_visible(False) builder.get_object("reference_entry").set_visible(False) @@ -122,7 +122,7 @@ class IptvDialog: if self._action is Action.ADD: self._save_button.set_visible(False) self._add_button.set_visible(True) - if self._profile is Profile.ENIGMA_2: + if self._profile is SettingsType.ENIGMA_2: self._update_reference_entry() self._stream_type_combobox.set_active(1) elif self._action is Action.EDIT: @@ -147,13 +147,13 @@ class IptvDialog: if show_dialog(DialogType.QUESTION, self._dialog) == Gtk.ResponseType.CANCEL: return - self.save_enigma2_data() if self._profile is Profile.ENIGMA_2 else self.save_neutrino_data() + self.save_enigma2_data() if self._profile is SettingsType.ENIGMA_2 else self.save_neutrino_data() self._dialog.destroy() def init_data(self, srv): name, fav_id = srv[2], srv[7] self._name_entry.set_text(name) - self.init_enigma2_data(fav_id) if self._profile is Profile.ENIGMA_2 else self.init_neutrino_data(fav_id) + self.init_enigma2_data(fav_id) if self._profile is SettingsType.ENIGMA_2 else self.init_neutrino_data(fav_id) def init_enigma2_data(self, fav_id): data, sep, desc = fav_id.partition("#DESCRIPTION") @@ -190,7 +190,7 @@ class IptvDialog: self._description_entry.set_text(data[1]) def _update_reference_entry(self): - if self._profile is Profile.ENIGMA_2: + if self._profile is SettingsType.ENIGMA_2: self._reference_entry.set_text(_ENIGMA2_REFERENCE.format(self.get_type(), self._srv_type_entry.get_text(), int(self._sid_entry.get_text()), @@ -505,7 +505,7 @@ class IptvListConfigurationDialog: show_dialog(DialogType.ERROR, self._dialog, "Error. Verify the data!") return - if self._profile is Profile.ENIGMA_2: + if self._profile is SettingsType.ENIGMA_2: reset = self._reset_to_default_switch.get_active() type_default = self._type_check_button.get_active() tid_default = self._tid_check_button.get_active() diff --git a/app/ui/main_app_window.py b/app/ui/main_app_window.py index 79b7c91e..e1c3897b 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.settings import Profile, Settings +from app.settings import SettingsType, Settings, SettingsException from app.tools.media import Player from app.ui.epg_dialog import EpgDialog from app.ui.transmitter import LinksTransmitter @@ -158,8 +158,8 @@ class Application(Gtk.Application): "on_create_bouquet_for_each_type": self.on_create_bouquet_for_each_type} self._settings = Settings.get_instance() - self._profile = self._settings.profile - os.makedirs(os.path.dirname(self._settings.data_dir_path), exist_ok=True) + self._s_type = self._settings.setting_type + os.makedirs(os.path.dirname(self._settings.data_local_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 = [] @@ -188,7 +188,6 @@ class Application(Gtk.Application): self._EXTRA_COLOR = None # Color for services with a extra name for the bouquet builder = Gtk.Builder() - builder.set_translation_domain("demon-editor") builder.add_from_file(UI_RESOURCES_PATH + "main_window.glade") builder.connect_signals(handlers) self._main_window = builder.get_object("main_window") @@ -215,8 +214,8 @@ class Application(Gtk.Application): self._app_info_box.bind_property("visible", builder.get_object("right_header_box"), "sensitive", 4) self._app_info_box.bind_property("visible", builder.get_object("left_header_box"), "sensitive", 4) # Status bar - self._ip_label = builder.get_object("ip_label") - self._ip_label.set_text(self._settings.host) + self._profile_combo_box = builder.get_object("profile_combo_box") + self._profile_combo_box.set_tooltip_text(self._profile_combo_box.get_tooltip_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") @@ -343,7 +342,7 @@ class Application(Gtk.Application): If update=False - first call on program start, else - after options changes! """ - if self._profile is Profile.ENIGMA_2: + if self._s_type is SettingsType.ENIGMA_2: if self._settings.use_colors: new_rgb = Gdk.RGBA() extra_rgb = Gdk.RGBA() @@ -574,7 +573,7 @@ class Application(Gtk.Application): # ***************** ####### *********************# def get_bouquet_file_name(self, bouquet): - bouquet_file_name = "{}userbouquet.{}.{}".format(self._settings.get(self._profile).get("data_dir_path"), + bouquet_file_name = "{}userbouquet.{}.{}".format(self._settings.get(self._s_type).get("data_dir_path"), *bouquet.split(":")) return bouquet_file_name @@ -824,11 +823,11 @@ class Application(Gtk.Application): @run_task def on_upload_data(self, download_type): try: - profile = self._profile + profile = self._s_type opts = self._settings - use_http = profile is Profile.ENIGMA_2 + use_http = profile is SettingsType.ENIGMA_2 - if profile is Profile.ENIGMA_2: + if profile is SettingsType.ENIGMA_2: 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) @@ -858,17 +857,17 @@ class Application(Gtk.Application): self._wait_dialog.show() yield True - data_path = self._settings.data_dir_path if data_path is None else data_path + data_path = self._settings.data_local_path if data_path is None else data_path yield from self.clear_current_data() try: - prf = self._profile + prf = self._s_type black_list = get_blacklist(data_path) bouquets = get_bouquets(data_path, prf) yield True - services = get_services(data_path, prf, self.get_format_version() if prf is Profile.ENIGMA_2 else 0) + services = get_services(data_path, prf, self.get_format_version() if prf is SettingsType.ENIGMA_2 else 0) yield True - update_picons_data(self._settings.picons_dir_path, self._picons) + update_picons_data(self._settings.picons_local_path, self._picons) yield True except FileNotFoundError as e: msg = get_message("Please, download files from receiver or setup your path for read data!") @@ -1009,9 +1008,9 @@ class Application(Gtk.Application): def save_data(self): self._save_header_button.set_sensitive(False) - profile = self._profile - path = self._settings.data_dir_path - backup_path = self._settings.backup_dir_path + profile = self._s_type + path = self._settings.data_local_path + backup_path = self._settings.backup_local_path # Backup data or clearing data path backup_data(path, backup_path) if self._settings.backup_before_save else clear_data_path(path) yield True @@ -1031,7 +1030,7 @@ class Application(Gtk.Application): favs = self._bouquets[bq_id] ex_s = self._extra_bouquets.get(bq_id) bq_s = list(filter(None, [self._services.get(f_id, None) for f_id in favs])) - if profile is Profile.ENIGMA_2: + if profile is SettingsType.ENIGMA_2: bq_s = list(map(lambda s: s._replace(service=ex_s.get(s.fav_id, None) if ex_s else None), bq_s)) bq = Bouquet(bq_name, bq_type, bq_s, locked, hidden) bqs.append(bq) @@ -1045,10 +1044,10 @@ class Application(Gtk.Application): # Getting services services_model = get_base_model(self._services_view.get_model()) services = [Service(*row[: Column.SRV_TOOLTIP]) for row in services_model] - write_services(path, services, profile, self.get_format_version() if profile is Profile.ENIGMA_2 else 0) + write_services(path, services, profile, self.get_format_version() if profile is SettingsType.ENIGMA_2 else 0) yield True # removing bouquet files - if profile is Profile.ENIGMA_2: + if profile is SettingsType.ENIGMA_2: # blacklist write_blacklist(path, self._blacklist) @@ -1060,7 +1059,7 @@ class Application(Gtk.Application): if show_dialog(DialogType.QUESTION, self._main_window) == Gtk.ResponseType.CANCEL: return - gen = self.create_new_configuration(self._profile) + gen = self.create_new_configuration(self._s_type) GLib.idle_add(lambda: next(gen, False), priority=GLib.PRIORITY_LOW) def create_new_configuration(self, profile): @@ -1070,12 +1069,12 @@ class Application(Gtk.Application): c_gen = self.clear_current_data() yield from c_gen - if profile is Profile.ENIGMA_2: + if profile is SettingsType.ENIGMA_2: parent = self._bouquets_model.append(None, ["Favourites (TV)", None, None, BqType.TV.value]) self.append_bouquet(Bouquet("Favourites (TV)", BqType.TV.value, [], None, None), parent) parent = self._bouquets_model.append(None, ["Favourites (Radio)", None, None, BqType.RADIO.value]) self.append_bouquet(Bouquet("Favourites (Radio)", BqType.RADIO.value, [], None, None), parent) - elif profile is Profile.NEUTRINO_MP: + elif profile is SettingsType.NEUTRINO_MP: self._bouquets_model.append(None, ["Providers", None, None, BqType.BOUQUET.value]) self._bouquets_model.append(None, ["FAV", None, None, BqType.TV.value]) self._bouquets_model.append(None, ["WEBTV", None, None, BqType.WEBTV.value]) @@ -1143,7 +1142,7 @@ class Application(Gtk.Application): self.show_error_dialog("Error. No bouquet is selected!") return - if self._profile is Profile.NEUTRINO_MP and self._bq_selected.endswith(BqType.WEBTV.value): + if self._s_type is SettingsType.NEUTRINO_MP and self._bq_selected.endswith(BqType.WEBTV.value): self.show_error_dialog("Operation not allowed in this context!") return @@ -1175,11 +1174,11 @@ class Application(Gtk.Application): GLib.idle_add(lambda: next(gen, False), priority=GLib.PRIORITY_LOW) def update_options(self): - profile = self._settings.profile - self._ip_label.set_text(self._settings.host) - if profile != self._profile: + profile = self._settings.setting_type + + if profile != self._s_type: yield from self.show_app_info(True) - self._profile = profile + self._s_type = profile c_gen = self.clear_current_data() yield from c_gen self.update_profile_label() @@ -1284,7 +1283,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 self._profile is Profile.NEUTRINO_MP: + if self._s_type is SettingsType.NEUTRINO_MP: for elem in self._LOCK_HIDE_ELEMENTS: self._tool_elements[elem].set_sensitive(not_empty) else: @@ -1300,17 +1299,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 self._profile is Profile.ENIGMA_2) + self._tool_elements[elem].set_sensitive(not_empty and self._s_type is SettingsType.ENIGMA_2) for elem in self._FAV_IPTV_ELEMENTS: is_iptv = self._bq_selected and not is_service - if self._profile is Profile.NEUTRINO_MP: + if self._s_type is SettingsType.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 self._profile is not Profile.ENIGMA_2: + if self._s_type is not SettingsType.ENIGMA_2: for elem in self._FAV_ENIGMA_ELEMENTS: self._tool_elements[elem].set_sensitive(False) @@ -1321,9 +1320,9 @@ class Application(Gtk.Application): self.set_service_flags(Flag.LOCK) def set_service_flags(self, flag): - if self._profile is Profile.ENIGMA_2: + if self._s_type is SettingsType.ENIGMA_2: set_flags(flag, self._services_view, self._fav_view, self._services, self._blacklist) - elif self._profile is Profile.NEUTRINO_MP and self._bq_selected: + elif self._s_type is SettingsType.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) @@ -1386,14 +1385,14 @@ class Application(Gtk.Application): self._fav_view, self._services, self._bouquets.get(self._bq_selected, None), - self._profile, + self._s_type, Action.ADD).show() if response != Gtk.ResponseType.CANCEL: self.update_fav_num_column(self._fav_model) @run_idle def on_iptv_list_configuration(self, item): - if self._profile is Profile.NEUTRINO_MP: + if self._s_type is SettingsType.NEUTRINO_MP: self.show_error_dialog("Neutrino at the moment not supported!") return @@ -1407,7 +1406,7 @@ class Application(Gtk.Application): bq = self._bouquets.get(self._bq_selected, []) IptvListConfigurationDialog(self._main_window, self._services, iptv_rows, bq, - self._fav_model, self._profile).show() + self._fav_model, self._s_type).show() @run_idle def on_remove_all_unavailable(self, item): @@ -1423,7 +1422,7 @@ class Application(Gtk.Application): return fav_bqt = self._bouquets.get(self._bq_selected, None) - response = SearchUnavailableDialog(self._main_window, self._fav_model, fav_bqt, iptv_rows, self._profile).show() + response = SearchUnavailableDialog(self._main_window, self._fav_model, fav_bqt, iptv_rows, self._s_type).show() if response: next(self.remove_favs(response, self._fav_model), False) @@ -1431,7 +1430,7 @@ class Application(Gtk.Application): @run_idle def on_epg_list_configuration(self, item): - if self._profile is not Profile.ENIGMA_2: + if self._s_type is not SettingsType.ENIGMA_2: self.show_error_dialog("Only Enigma2 is supported!") return @@ -1449,7 +1448,7 @@ class Application(Gtk.Application): if not self._bq_selected: return - YtListImportDialog(self._main_window, self._profile, self.append_imported_services).show() + YtListImportDialog(self._main_window, self._s_type, self.append_imported_services).show() def on_import_m3u(self, item): """ Imports iptv from m3u files. """ @@ -1461,7 +1460,7 @@ class Application(Gtk.Application): self.show_error_dialog("No m3u file is selected!") return - channels = parse_m3u(response, self._profile) + channels = parse_m3u(response, self._s_type) if channels and self._bq_selected: self.append_imported_services(channels) @@ -1492,7 +1491,7 @@ class Application(Gtk.Application): try: bq = Bouquet(self._current_bq_name, None, bq_services, None, None) - export_to_m3u(response, bq, self._profile) + export_to_m3u(response, bq, self._s_type) except Exception as e: self.show_error_dialog(str(e)) else: @@ -1504,7 +1503,7 @@ class Application(Gtk.Application): self.show_error_dialog("No selected item!") return - appender = self.append_bouquet if self._profile is Profile.ENIGMA_2 else self.append_bouquets + appender = self.append_bouquet if self._s_type is SettingsType.ENIGMA_2 else self.append_bouquets import_bouquet(self._main_window, model, paths[0], self._settings, self._services, appender) def on_import_bouquets(self, item): @@ -1545,7 +1544,7 @@ class Application(Gtk.Application): self.show_error_dialog("Not allowed in this context!") return - url = get_iptv_url(row, self._profile) + url = get_iptv_url(row, self._s_type) self.update_player_buttons() if not url: return @@ -1659,10 +1658,11 @@ class Application(Gtk.Application): def init_http_api(self): 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())) + status = all( + (http_api_enable, self._s_type is SettingsType.ENIGMA_2, not self._receiver_info_box.get_visible())) GLib.idle_add(self._http_status_image.set_visible, status) - if self._profile is Profile.NEUTRINO_MP or not http_api_enable: + if self._s_type is SettingsType.NEUTRINO_MP or not http_api_enable: self.update_info_boxes_visible(False) if self._http_api: self._http_api.close() @@ -1786,7 +1786,7 @@ class Application(Gtk.Application): self._sat_positions.clear() sat_positions = set() - if self._profile is Profile.ENIGMA_2: + if self._s_type is SettingsType.ENIGMA_2: terrestrial = False cable = False @@ -1803,7 +1803,7 @@ class Application(Gtk.Application): self._sat_positions.append("T") if cable: self._sat_positions.append("C") - elif self._profile is Profile.NEUTRINO_MP: + elif self._s_type is SettingsType.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))) @@ -1881,7 +1881,7 @@ class Application(Gtk.Application): self._fav_view, self._services, self._bouquets.get(self._bq_selected, None), - self._profile, + self._s_type, Action.EDIT).show() self.on_locate_in_services(view) @@ -1999,7 +1999,7 @@ class Application(Gtk.Application): @run_idle def on_picons_loader_show(self, item): ids = {} - if self._profile is Profile.ENIGMA_2: + if self._s_type is SettingsType.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] @@ -2009,7 +2009,7 @@ class Application(Gtk.Application): @run_task def update_picons(self): - update_picons_data(self._settings.picons_dir_path, self._picons) + update_picons_data(self._settings.picons_local_path, self._picons) append_picons(self._picons, self._services_model) def on_assign_picon(self, view): @@ -2062,15 +2062,20 @@ 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, - self._profile, self.append_bouquet) + self._s_type, self.append_bouquet) # ***************** Profile label *********************# def update_profile_label(self): - 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:"))) + label, sep, ip = self._profile_combo_box.get_tooltip_text().partition(":") + profile_name = self._profile_combo_box.get_active_text() + self._profile_combo_box.set_tooltip_text("{}: {}".format(label, self._settings.host)) + msg = get_message("Profile:") + + if self._s_type is SettingsType.ENIGMA_2: + self._header_bar.set_subtitle("{} {} [Enigma2 v.{}]".format(msg, profile_name, self.get_format_version())) + elif self._s_type is SettingsType.NEUTRINO_MP: + self._header_bar.set_subtitle("{} {} [Neutrino-MP]".format(msg, profile_name)) def get_format_version(self): return 5 if self._settings.v5_support else 4 @@ -2086,8 +2091,14 @@ class Application(Gtk.Application): def start_app(): - app = Application() - app.run(sys.argv) + try: + Settings.get_instance() + except SettingsException as e: + msg = "{} \n{}".format(e, "All setting were reset. Restart the program!") + show_dialog(DialogType.INFO, transient=Gtk.Dialog(), text=msg) + else: + app = Application() + app.run(sys.argv) if __name__ == "__main__": diff --git a/app/ui/main_helper.py b/app/ui/main_helper.py index bc874db9..39226f8d 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.settings import Profile +from app.settings import SettingsType from .uicommons import ViewTarget, BqGenType, Gtk, Gdk, HIDE_ICON, LOCKED_ICON, KeyboardKey, Column from .dialogs import show_dialog, DialogType, get_chooser_dialog, WaitDialog @@ -382,7 +382,7 @@ def assign_picon(target, srv_view, fav_view, transient, picons, settings, servic if picon_id: if os.path.isfile(response): - picons_path = settings.picons_dir_path + picons_path = settings.picons_local_path os.makedirs(os.path.dirname(picons_path), exist_ok=True) picon_file = picons_path + picon_id shutil.copy(response, picon_file) @@ -464,8 +464,8 @@ def remove_all_unused_picons(settings, picons, services): def remove_picons(settings, picon_ids, picons): - pions_path = settings.picons_dir_path - backup_path = settings.backup_dir_path + "picons/" + pions_path = settings.picons_local_path + backup_path = settings.backup_local_path + "picons/" os.makedirs(os.path.dirname(backup_path), exist_ok=True) for p_id in picon_ids: picons[p_id] = None @@ -492,7 +492,7 @@ def get_picon_pixbuf(path): # ***************** Bouquets *********************# -def gen_bouquets(view, bq_view, transient, gen_type, tv_types, profile, callback): +def gen_bouquets(view, bq_view, transient, gen_type, tv_types, s_type, callback): """ Auto-generate and append list of bouquets """ fav_id_index = Column.SRV_FAV_ID index = Column.SRV_TYPE @@ -502,7 +502,7 @@ def gen_bouquets(view, bq_view, transient, gen_type, tv_types, profile, callback index = Column.SRV_POS model, paths = view.get_selection().get_selected_rows() - bq_type = BqType.BOUQUET.value if profile is Profile.NEUTRINO_MP else BqType.TV.value + bq_type = BqType.BOUQUET.value if s_type is SettingsType.NEUTRINO_MP else BqType.TV.value if gen_type in (BqGenType.SAT, BqGenType.PACKAGE, BqGenType.TYPE): if not is_only_one_item_selected(paths, transient): return @@ -511,17 +511,17 @@ def gen_bouquets(view, bq_view, transient, gen_type, tv_types, profile, callback bq_type = BqType.RADIO.value append_bouquets(bq_type, bq_view, callback, fav_id_index, index, model, [service.package if gen_type is BqGenType.PACKAGE else - service.pos if gen_type is BqGenType.SAT else service.service_type], profile) + service.pos if gen_type is BqGenType.SAT else service.service_type], s_type) else: wait_dialog = WaitDialog(transient) wait_dialog.show() append_bouquets(bq_type, bq_view, callback, fav_id_index, index, model, - {row[index] for row in model}, profile, wait_dialog) + {row[index] for row in model}, s_type, wait_dialog) @run_task -def append_bouquets(bq_type, bq_view, callback, fav_id_index, index, model, names, profile, wait_dialog=None): - bq_index = 0 if profile is Profile.ENIGMA_2 else 1 +def append_bouquets(bq_type, bq_view, callback, fav_id_index, index, model, names, s_type, wait_dialog=None): + bq_index = 0 if s_type is SettingsType.ENIGMA_2 else 1 bq_view.expand_row(Gtk.TreePath(bq_index), 0) bqs_model = bq_view.get_model() bouquets_names = get_bouquets_names(bqs_model) @@ -583,14 +583,14 @@ def append_text_to_tview(char, view): view.scroll_to_mark(insert, 0.0, True, 0.0, 1.0) -def get_iptv_url(row, profile): +def get_iptv_url(row, s_type): """ Returns url from iptv type row """ - data = row[Column.FAV_ID].split(":" if profile is Profile.ENIGMA_2 else "::") - if profile is Profile.ENIGMA_2: + data = row[Column.FAV_ID].split(":" if s_type is SettingsType.ENIGMA_2 else "::") + if s_type is SettingsType.ENIGMA_2: data = list(filter(lambda x: "http" in x, data)) if data: url = data[0] - return urllib.request.unquote(url) if profile is Profile.ENIGMA_2 else url + return urllib.request.unquote(url) if s_type is SettingsType.ENIGMA_2 else url def on_popup_menu(menu, event): diff --git a/app/ui/main_window.glade b/app/ui/main_window.glade index 6daac2a0..b9eb54a4 100644 --- a/app/ui/main_window.glade +++ b/app/ui/main_window.glade @@ -26,7 +26,7 @@ THE SOFTWARE. Author: Dmitriy Yefremov --> - + @@ -961,6 +961,7 @@ Author: Dmitriy Yefremov 640 + 480 False center accessories-text-editor @@ -2798,7 +2799,7 @@ Author: Dmitriy Yefremov - 30 + 35 False @@ -2842,62 +2843,56 @@ Author: Dmitriy Yefremov - - True + False + False + Current IP: center - 2 - - - True - False - gtk-connect - - - False - True - 0 - - - - - True + center + 1 + 1 + 0 + False + True + + default + + + False + True center - Current IP: - - - + 1 + 1 + False + False + 9 + default + gtk-connect - - False - True - 1 - - - - - True - False - 127.0.0.1 - - - - - - False - True - 2 - - True + False True - 5 3 + + + False + No connection to the receiver + 10 + 10 + network-offline + + + False + True + end + 2 + + False @@ -2986,21 +2981,6 @@ Author: Dmitriy Yefremov 2 - - - False - No connection to the receiver - 10 - 10 - network-offline - - - False - True - end - 3 - - diff --git a/app/ui/picons_downloader.py b/app/ui/picons_downloader.py index 0ef967f0..6a343d61 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.settings import Profile +from app.settings import SettingsType 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 @@ -86,13 +86,13 @@ class PiconsDialog: self._url_entry.get_style_context().add_provider_for_screen(Gdk.Screen.get_default(), self._style_provider, Gtk.STYLE_PROVIDER_PRIORITY_USER) self._settings = settings - self._profile = settings.profile + self._s_type = settings.setting_type 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_path = self._settings.picons_local_path self._picons_dir_entry.set_text(self._picons_path) - if not len(self._picon_ids) and self._profile is Profile.ENIGMA_2: + if not len(self._picon_ids) and self._s_type is SettingsType.ENIGMA_2: message = get_message("To automatically set the identifiers for picons,\n" "first load the required services list into the main application window.") self.show_info_message(message, Gtk.MessageType.WARNING) @@ -342,7 +342,7 @@ class PiconsDialog: self._expander.set_expanded(True) convert_to(src_path=picons_path, dest_path=save_path, - profile=Profile.ENIGMA_2, + s_type=SettingsType.ENIGMA_2, callback=self.append_output, done_callback=lambda: self.show_info_message(get_message("Done!"), Gtk.MessageType.INFO)) @@ -362,10 +362,10 @@ class PiconsDialog: show_dialog(dialog_type, self._dialog, message) def get_picons_format(self): - picon_format = Profile.ENIGMA_2 + picon_format = SettingsType.ENIGMA_2 if self._neutrino_mp_radio_button.get_active(): - picon_format = Profile.NEUTRINO_MP + picon_format = SettingsType.NEUTRINO_MP return picon_format diff --git a/app/ui/satellites_dialog.py b/app/ui/satellites_dialog.py index 80fc0bc0..5da0656e 100644 --- a/app/ui/satellites_dialog.py +++ b/app/ui/satellites_dialog.py @@ -25,7 +25,7 @@ class SatellitesDialog: _aggr = [None for x in range(9)] # aggregate def __init__(self, transient, settings): - self._data_path = settings.data_dir_path + "satellites.xml" + self._data_path = settings.data_local_path + "satellites.xml" self._settings = settings handlers = {"on_open": self.on_open, diff --git a/app/ui/service_details_dialog.py b/app/ui/service_details_dialog.py index 0d2cffa0..3b2a8df5 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.settings import Profile +from app.settings import SettingsType 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 @@ -52,10 +52,10 @@ class ServiceDetailsDialog: self._dialog = builder.get_object("service_details_dialog") self._dialog.set_transient_for(transient) - self._profile = settings.profile + self._s_type = settings.setting_type self._tr_type = None - self._satellites_xml_path = settings.data_dir_path + "satellites.xml" - self._picons_dir_path = settings.picons_dir_path + self._satellites_xml_path = settings.data_local_path + "satellites.xml" + self._picons_dir_path = settings.picons_local_path self._services_view = srv_view self._fav_view = fav_view self._action = action @@ -197,7 +197,7 @@ class ServiceDetailsDialog: self._package_entry.set_text(srv.package) self._sid_entry.set_text(str(int(srv.ssid, 16))) # Transponder - if self._profile is Profile.ENIGMA_2: + if self._s_type is SettingsType.ENIGMA_2: self._tr_type = TrType(srv.transponder_type) self._freq_entry.set_text(srv.freq) self._rate_entry.set_text(srv.rate) @@ -211,10 +211,10 @@ class ServiceDetailsDialog: else: self.set_sat_positions(srv.pos) - if self._profile is Profile.ENIGMA_2: + if self._s_type is SettingsType.ENIGMA_2: self.init_enigma2_service_data(srv) self.init_enigma2_transponder_data(srv) - elif self._profile is Profile.NEUTRINO_MP: + elif self._s_type is SettingsType.NEUTRINO_MP: self.init_neutrino_data(srv) self.init_neutrino_ui_elements() @@ -484,9 +484,9 @@ class ServiceDetailsDialog: transponder=transponder) def get_flags(self): - if self._profile is Profile.ENIGMA_2: + if self._s_type is SettingsType.ENIGMA_2: return self.get_enigma2_flags() - elif self._profile is Profile.NEUTRINO_MP: + elif self._s_type is SettingsType.NEUTRINO_MP: return self._old_service.flags_cas def get_enigma2_flags(self): @@ -532,12 +532,12 @@ class ServiceDetailsDialog: net_id, tr_id = int(self._network_id_entry.get_text()), int(self._transponder_id_entry.get_text()) service_type = self._srv_type_entry.get_text() - if self._profile is Profile.ENIGMA_2: + if self._s_type is SettingsType.ENIGMA_2: namespace = int(self._namespace_entry.get_text()) data_id = self._ENIGMA2_DATA_ID.format(ssid, namespace, tr_id, net_id, service_type, 0) fav_id = self._ENIGMA2_FAV_ID.format(ssid, tr_id, net_id, namespace) return fav_id, data_id - elif self._profile is Profile.NEUTRINO_MP: + elif self._s_type is SettingsType.NEUTRINO_MP: fav_id = self._NEUTRINO_FAV_ID.format(tr_id, net_id, ssid) return fav_id, self._old_service.data_id @@ -548,7 +548,7 @@ class ServiceDetailsDialog: fec = self._fec_combo_box.get_active_id() system = self._sys_combo_box.get_active_id() - if self._tr_type is TrType.Satellite or self._profile is Profile.NEUTRINO_MP: + if self._tr_type is TrType.Satellite or self._s_type is SettingsType.NEUTRINO_MP: freq = self._freq_entry.get_text() rate = self._rate_entry.get_text() pol = self._pol_combo_box.get_active_id() @@ -571,7 +571,7 @@ class ServiceDetailsDialog: inv = get_value_by_name(Inversion, self._invertion_combo_box.get_active_id()) srv_sys = "0" # !!! - if self._profile is Profile.ENIGMA_2: + if self._s_type is SettingsType.ENIGMA_2: dvb_s_tr = self._ENIGMA2_TRANSPONDER_DATA.format("s", freq, rate, pol, fec, sat_pos, inv, srv_sys) if sys == "DVB-S": return dvb_s_tr @@ -585,7 +585,7 @@ class ServiceDetailsDialog: st_id = self._stream_id_entry.get_text() pls = ":{}:{}:{}".format(st_id, pls_code, pls_mode) if pls_mode and pls_code and st_id else "" return "{}:{}:{}:{}:{}{}".format(dvb_s_tr, flag, mod, roll_off, pilot, pls) - elif self._profile is Profile.NEUTRINO_MP: + elif self._s_type is SettingsType.NEUTRINO_MP: on_id, tr_id = int(self._network_id_entry.get_text()), int(self._transponder_id_entry.get_text()) mod = self.get_value_from_combobox_id(self._mod_combo_box, MODULATION) if sys == "DVB-S2" else None srv_sys = None @@ -682,7 +682,7 @@ class ServiceDetailsDialog: return True def update_reference(self, entry, event=None): - if not self.is_data_correct() or (event is None and self._profile is Profile.NEUTRINO_MP): + if not self.is_data_correct() or (event is None and self._s_type is SettingsType.NEUTRINO_MP): return self.update_reference_entry() @@ -691,7 +691,7 @@ class ServiceDetailsDialog: ssid = int(self._sid_entry.get_text()) tid = int(self._transponder_id_entry.get_text()) nid = int(self._network_id_entry.get_text()) - if self._profile is Profile.ENIGMA_2: + if self._s_type is SettingsType.ENIGMA_2: on_id = int(self._namespace_entry.get_text()) ref = "1:0:{:X}:{:X}:{:X}:{:X}:{:X}:0:0:0".format(srv_type, ssid, tid, nid, on_id) self._reference_entry.set_text(ref) diff --git a/app/ui/settings_dialog.glade b/app/ui/settings_dialog.glade index 9f2f65e1..8c949c1a 100644 --- a/app/ui/settings_dialog.glade +++ b/app/ui/settings_dialog.glade @@ -26,13 +26,23 @@ THE SOFTWARE. Author: Dmitriy Yefremov --> - + + + + + + + + + + + 1 10 @@ -47,7 +57,6 @@ Author: Dmitriy Yefremov 1 False - False True center-on-parent True @@ -59,6 +68,8 @@ Author: Dmitriy Yefremov True False + Options + Settings type: 2 True @@ -98,92 +109,6 @@ Author: Dmitriy Yefremov 2 - - - True - False - 10 - 10 - 5 - 5 - vertical - 5 - - - True - False - Options - - - - - - False - True - 0 - - - - - True - False - 10 - 10 - - - True - False - 2 - Profile: - - - False - True - 0 - - - - - Enigma2 - True - True - False - center - True - neutrino_radio_button - - - - False - True - 1 - - - - - Neutrino-MP - True - True - False - center - True - enigma_radio_button - - - False - True - 4 - - - - - False - True - 1 - - - - True @@ -254,74 +179,241 @@ Author: Dmitriy Yefremov False 5 - + True - False - 6 - 5 - vertical + True + True - - True + False + 5 + 5 0.019999999552965164 in - + + 155 True False - 5 - 5 + Set as default vertical 2 - + True - False - 5 - 2 - 2 - True + True + 1 + 1 + in - + True True - 127.0.0.1 - network-transmit-receive-symbolic + profile_lists_tore + False + 0 + + + + + + autosize + 70 + Profile + True + True + 0 + + + True + end + + + + 0 + + + + + + + fixed + Default + + + 5 + + + 1 + + + + + + + + + True + True + 0 + + + + + True + False + + + True + False + False + Add + Add + True + gtk-add + - 0 - 1 + False + True - + True False - Host: + Edit + Edit + True + gtk-edit + - 0 - 0 + False + True + + + + + True + False + Remove + Remove + True + gtk-remove + + + + False + True + + + + + True + False + + + + + + True + False + + + + + True + False + end + Set default + True + emblem-default + + + + False + True False True - 0 + 1 + + + + + True + False + Profile: + + + + + True + False + + + + + True + False + vertical + + + True + False + 5 + 5 + 5 + 0.019999999552965164 + in - + True False + 5 + 5 + 5 + vertical + 2 - + True False - center - settings_stack + 5 + 2 + 2 + True + + + True + False + center + 5 + Host: + + + 0 + 0 + + + + + True + True + 127.0.0.1 + network-transmit-receive-symbolic + + + 0 + 1 + + False @@ -330,59 +422,741 @@ Author: Dmitriy Yefremov - + True False - + + True + False + center + settings_stack + + + False + True + 0 + + + + + True + False + + + + + + True + True + 1 + + + + + True + False + + + False + True + 2 + + + + + Test + 110 + True + True + True + Test connection + test_button_image + True + + + + False + True + end + 3 + + + + + True + False + + + + + + True + True + 4 + - True + False True 1 - - True - False - - - False - True - 2 - - - - - Test - 110 - True - True - True - Test connection - test_button_image - True - - - - False - True - end - 3 - - - - + True False - + + True + False + 2 + 5 + 2 + 2 + + + True + False + Login: + + + 0 + 0 + + + + + True + True + True + root + avatar-default-symbolic + False + + + 0 + 1 + + + + + True + False + Password: + + + 1 + 0 + + + + + True + True + True + False + + root + emblem-readonly + False + password + + + 1 + 1 + + + + + True + True + 6 + 6 + 21 + network-workgroup-symbolic + + + 2 + 1 + + + + + + + + ftp + FTP + + + + + True + False + 2 + 5 + 2 + 2 + + + True + False + Login: + + + 0 + 0 + + + + + True + False + Password: + + + 1 + 0 + + + + + True + True + True + avatar-default-symbolic + + + 0 + 1 + + + + + True + False + Port: + + + 2 + 0 + + + + + True + True + 5 + 6 + 6 + 80 + network-workgroup-symbolic + + + 2 + 1 + + + + + True + True + True + False + + root + emblem-readonly + False + password + + + 1 + 1 + + + + + http + HTTP + 1 + + + + + True + False + 2 + 5 + 2 + 2 + + + True + False + Port: + + + 2 + 0 + + + + + True + True + 6 + 6 + 23 + network-workgroup-symbolic + + + 2 + 1 + + + + + True + False + Login: + + + 0 + 0 + + + + + True + True + True + 12 + 15 + avatar-default-symbolic + + + 0 + 1 + + + + + True + False + Password: + + + 1 + 0 + + + + + True + True + True + 12 + 15 + emblem-readonly + + + 1 + 1 + + + + + True + False + Timeout: + + + 3 + 0 + + + + + True + True + Timeout between commands in seconds + 2 + 6 + 6 + 1 + alarm-symbolic + number + telnet_timeout_adjustment + True + 1 + + + 3 + 1 + + + + + telnet + Telnet + 2 + - True + False + True + 1 + + + + + + + True + False + Network settings: + + + + + True + True + 0 + + + + + True + False + 5 + 5 + 0.019999999552965164 + in + + + True + False + 5 + 5 + 5 + 2 + True + + + True + False + Services and Bouquets files: + 0 + + + 0 + 0 + + + + + True + True + /etc/enigma2/ + gtk-edit + + + 0 + 1 + + + + + False + User bouquet files: + 2.2351741291171123e-10 + + + 0 + 2 + + + + + True + /etc/enigma2/ + gtk-edit + + + 0 + 3 + + + + + True + False + Satellites.xml file: + 0.019999999552965164 + + + 0 + 4 + + + + + True + True + /etc/tuxbox/ + gtk-edit + + + 0 + 5 + + + + + True + False + Picons: + 2.2351741291171123e-10 + + + 0 + 6 + + + + + True + True + /usr/share/enigma2/picon + gtk-edit + + + 0 + 7 + + + + + + + True + False + STB file paths: + + + + + True + True + 1 + + + + + True + False + 5 + 5 + 5 + 0.019999999552965164 + in + + + True + False + 5 + 5 + 5 + 2 + True + + + True + False + Picons path: + 0.019999999552965164 + + + 0 + 2 + + + + + True + True + /data/picons + gtk-edit + folder-open + False + Select + Select + + + + 0 + 3 + + + + + True + False + Data path: + 0 + 0.019999999552965164 + + + 0 + 0 + + + + + True + True + /data + gtk-edit + folder-open + False + Select + Select + + + + 0 + 1 + + + + + True + False + Backup path: + 0.019999999552965164 + + + 0 + 4 + + + + + True + True + /data/backup + gtk-edit + folder-open + False + Select + Select + + + + 0 + 5 + + + + + + + True + False + Local file paths: + + + + + True + True + 2 + + + + + True + False + + + + + profiles + Network + + + + + True + False + vertical + + + True + False + 5 + 5 + 5 + 5 + 0.019999999552965164 + in + + + True + False + 5 + 5 + 5 + 5 + + + True + False + Settings type: + + + False + True + 0 + + + + + True + False + center + center + 5 + + + Enigma2 + True + True + False + True + neutrino_radio_button + + + + False + True + 1 + + + + + Neutrino-MP + True + True + False + True + enigma_radio_button + + + False True 4 @@ -391,609 +1165,22 @@ Author: Dmitriy Yefremov False True - 1 - - - - - True - False - - - True - False - 2 - 5 - 2 - 2 - - - True - False - Login: - - - 0 - 0 - - - - - True - True - True - root - avatar-default-symbolic - False - - - 0 - 1 - - - - - True - False - Password: - - - 1 - 0 - - - - - True - True - True - False - - root - emblem-readonly - False - password - - - 1 - 1 - - - - - True - False - Port: - - - 2 - 0 - - - - - True - True - 6 - 6 - 21 - network-workgroup-symbolic - - - 2 - 1 - - - - - ftp - FTP - - - - - True - False - 2 - 5 - 2 - 2 - - - True - False - Login: - - - 0 - 0 - - - - - True - False - Password: - - - 1 - 0 - - - - - True - True - True - avatar-default-symbolic - - - 0 - 1 - - - - - True - False - Port: - - - 2 - 0 - - - - - True - True - 5 - 6 - 6 - 80 - network-workgroup-symbolic - - - 2 - 1 - - - - - True - True - True - False - - root - emblem-readonly - False - password - - - 1 - 1 - - - - - http - HTTP - 1 - - - - - True - False - 2 - 5 - 2 - 2 - - - True - False - Port: - - - 2 - 0 - - - - - True - True - 6 - 6 - 23 - network-workgroup-symbolic - - - 2 - 1 - - - - - True - False - Login: - - - 0 - 0 - - - - - True - True - True - 12 - 15 - avatar-default-symbolic - - - 0 - 1 - - - - - True - False - Password: - - - 1 - 0 - - - - - True - True - True - 12 - 15 - emblem-readonly - - - 1 - 1 - - - - - True - False - Timeout: - - - 3 - 0 - - - - - True - True - Timeout between commands in seconds - 2 - 6 - 6 - 1 - alarm-symbolic - number - telnet_timeout_adjustment - True - 1 - - - 3 - 1 - - - - - telnet - Telnet - 2 - - - - - False - True - 1 + end + 2 - - - True - False - Network settings: - + + - True + False True 0 - - - network - Network - - - - - True - False - 5 - 5 - vertical - - - True - False - 0.019999999552965164 - in - - - True - False - 5 - 5 - 5 - 2 - True - - - True - False - Services and Bouquets files: - 0 - - - 0 - 0 - - - - - True - True - /etc/enigma2/ - gtk-edit - - - 0 - 1 - - - - - False - User bouquet files: - 2.2351741291171123e-10 - - - 0 - 2 - - - - - True - /etc/enigma2/ - gtk-edit - - - 0 - 3 - - - - - True - False - Satellites.xml file: - 0.019999999552965164 - - - 0 - 4 - - - - - True - True - /etc/tuxbox/ - gtk-edit - - - 0 - 5 - - - - - True - False - Picons: - 2.2351741291171123e-10 - - - 0 - 6 - - - - - True - True - /usr/share/enigma2/picon - gtk-edit - - - 0 - 7 - - - - - - - True - False - STB file paths: - - - - - True - True - 0 - - - - - True - False - 5 - 0.019999999552965164 - in - - - True - False - 5 - 5 - 5 - 2 - True - - - True - False - Picons path: - 0.019999999552965164 - - - 0 - 2 - - - - - True - True - /data/picons - gtk-edit - folder-open - False - Select - - - - 0 - 3 - - - - - True - False - Data path: - 0 - 0.019999999552965164 - - - 0 - 0 - - - - - True - True - /data - gtk-edit - folder-open - False - Select - Select - - - - 0 - 1 - - - - - True - False - Backup path: - 0.019999999552965164 - - - 0 - 4 - - - - - True - True - /data/backup - gtk-edit - folder-open - False - Select - - - - 0 - 5 - - - - - - - True - False - Local file paths: - - - - - True - True - 1 - - - - - paths - Paths - 1 - - - - - True - False - vertical True @@ -1074,9 +1261,11 @@ Author: Dmitriy Yefremov + 250 True True True + end True @@ -1098,9 +1287,11 @@ Author: Dmitriy Yefremov + 250 True True True + end True @@ -1124,7 +1315,7 @@ Author: Dmitriy Yefremov False True - 0 + 1 @@ -1206,14 +1397,14 @@ Author: Dmitriy Yefremov False True - 1 + 2 program Program - 2 + 1 @@ -1525,7 +1716,7 @@ Author: Dmitriy Yefremov extra Extra - 3 + 2 @@ -1537,7 +1728,7 @@ Author: Dmitriy Yefremov - False + True True 0 diff --git a/app/ui/settings_dialog.py b/app/ui/settings_dialog.py index a86d7eb1..2376d8b9 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.settings import Profile -from .uicommons import Gtk, Gdk, UI_RESOURCES_PATH, TEXT_DOMAIN, FavClickMode +from app.settings import SettingsType +from .uicommons import Gtk, Gdk, UI_RESOURCES_PATH, FavClickMode, DEFAULT_ICON from .main_helper import update_entry_data @@ -21,7 +21,7 @@ class SettingsDialog: def __init__(self, transient, settings): handlers = {"on_field_icon_press": self.on_field_icon_press, - "on_profile_changed": self.on_profile_changed, + "on_settings_type_changed": self.on_settings_type_changed, "on_reset": self.on_reset, "apply_settings": self.apply_settings, "on_connection_test": self.on_connection_test, @@ -29,10 +29,16 @@ class SettingsDialog: "on_set_color_switch_state": self.on_set_color_switch_state, "on_http_mode_switch_state": self.on_http_mode_switch_state, "on_yt_dl_switch_state": self.on_yt_dl_switch_state, - "on_send_to_switch_state": self.on_send_to_switch_state} + "on_send_to_switch_state": self.on_send_to_switch_state, + "on_profile_add": self.on_profile_add, + "on_profile_edit": self.on_profile_edit, + "on_profile_remove": self.on_profile_remove, + "on_profile_deleted": self.on_profile_deleted, + "on_profile_inserted": self.on_profile_inserted, + "on_profile_edited": self.on_profile_edited, + "on_profile_set_default": self.on_profile_set_default} builder = Gtk.Builder() - builder.set_translation_domain(TEXT_DOMAIN) builder.add_from_file(UI_RESOURCES_PATH + "settings_dialog.glade") builder.connect_signals(handlers) @@ -89,23 +95,38 @@ 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") + # Profiles + self._profile_view = builder.get_object("profile_tree_view") + self._profile_remove_button = builder.get_object("profile_remove_button") + self._profile_view.get_model().append(("default", DEFAULT_ICON)) # Settings self._settings = settings - self._active_profile = settings.profile + self._profiles = settings.profiles + self._s_type = settings.setting_type self.set_settings() - self.init_ui_elements(self._active_profile) + self.init_ui_elements(self._s_type) - def init_ui_elements(self, profile): - is_enigma_profile = profile is Profile.ENIGMA_2 - self._neutrino_radio_button.set_active(profile is Profile.NEUTRINO_MP) + @run_idle + def init_ui_elements(self, s_type): + is_enigma_profile = s_type is SettingsType.ENIGMA_2 + self._neutrino_radio_button.set_active(s_type is SettingsType.NEUTRINO_MP) + self.update_header_bar() self._settings_stack.get_child_by_name(Property.HTTP.value).set_visible(is_enigma_profile) self._program_frame.set_sensitive(is_enigma_profile) self._extra_support_grid.set_sensitive(is_enigma_profile) http_active = self._support_http_api_switch.get_active() self._click_mode_zap_button.set_sensitive(is_enigma_profile and http_active) + self._profile_remove_button.set_sensitive(len(self._profile_view.get_model()) > 1) self.on_info_bar_close() if is_enigma_profile else self.show_info_message( "The Neutrino has only experimental support. Not all features are supported!", Gtk.MessageType.WARNING) + def update_header_bar(self): + label, sep, st = self._header_bar.get_subtitle().partition(":") + if self._s_type is SettingsType.ENIGMA_2: + self._header_bar.set_subtitle("{}: {}".format(label, self._enigma_radio_button.get_label())) + elif self._s_type is SettingsType.NEUTRINO_MP: + self._header_bar.set_subtitle("{}: {}".format(label, self._neutrino_radio_button.get_label())) + def show(self): response = self._dialog.run() if response == Gtk.ResponseType.OK: @@ -117,10 +138,10 @@ class SettingsDialog: def on_field_icon_press(self, entry, icon, event_button): 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 - self._settings.profile = profile + def on_settings_type_changed(self, item): + profile = SettingsType.ENIGMA_2 if self._enigma_radio_button.get_active() else SettingsType.NEUTRINO_MP + self._s_type = profile + self._settings.setting_type = profile self.set_settings() self.init_ui_elements(profile) @@ -144,14 +165,14 @@ class SettingsDialog: 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._data_dir_field.set_text(self._settings.data_local_path) + self._picons_dir_field.set_text(self._settings.picons_local_path) + self._backup_dir_field.set_text(self._settings.backup_local_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) - if self._active_profile is Profile.ENIGMA_2: + if self._s_type is SettingsType.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) @@ -165,8 +186,8 @@ class SettingsDialog: self._extra_color_button.set_rgba(extra_rgb) def apply_settings(self, item=None): - self._active_profile = Profile.ENIGMA_2 if self._enigma_radio_button.get_active() else Profile.NEUTRINO_MP - self._settings.profile = self._active_profile + self._s_type = SettingsType.ENIGMA_2 if self._enigma_radio_button.get_active() else SettingsType.NEUTRINO_MP + self._settings.setting_type = self._s_type self._settings.host = self._host_field.get_text() self._settings.port = self._port_field.get_text() self._settings.user = self._login_field.get_text() @@ -182,14 +203,14 @@ class SettingsDialog: 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.data_local_path = self._data_dir_field.get_text() + self._settings.picons_local_path = self._picons_dir_field.get_text() + self._settings.backup_local_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 self._active_profile is Profile.ENIGMA_2: + if self._s_type is SettingsType.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() @@ -272,6 +293,42 @@ class SettingsDialog: def on_send_to_switch_state(self, switch, state): self.show_info_message("Not implemented yet!", Gtk.MessageType.WARNING) + def on_profile_add(self, item): + model = self._profile_view.get_model() + count = 0 + name = "profile" + while name in self._profiles: + count += 1 + name = "profile{}".format(count) + self._profiles[name] = {"host": self._host_field.get_text()} + itr = model.append((name, None)) + + def on_profile_edit(self, item): + self.show_info_message("Not implemented yet!", Gtk.MessageType.WARNING) + + def on_profile_remove(self, item): + model, paths = self._profile_view.get_selection().get_selected_rows() + if paths: + row = model[paths] + self._profiles.pop(row[0], None) + del model[paths] + + def on_profile_deleted(self, model, paths): + self._profile_remove_button.set_sensitive(len(model) > 1) + + def on_profile_edited(self, render, path, new_text): + p_name = render.get_property("text") + p_data = self._profiles.pop(p_name, None) + row = self._profile_view.get_model()[path] + row[0] = new_text + self._profiles[new_text] = p_data + + def on_profile_set_default(self, item): + self.show_info_message("Not implemented yet!", Gtk.MessageType.WARNING) + + def on_profile_inserted(self, model, path, itr): + self._profile_remove_button.set_sensitive(len(model) > 1) + @run_idle def set_fav_click_mode(self, mode): mode = FavClickMode(mode) diff --git a/app/ui/uicommons.py b/app/ui/uicommons.py index a222d354..20b28205 100644 --- a/app/ui/uicommons.py +++ b/app/ui/uicommons.py @@ -28,6 +28,7 @@ HIDE_ICON = theme.load_icon("go-jump", 16, 0) if theme.lookup_icon("go-jump", 16 TV_ICON = theme.load_icon("tv-symbolic", 16, 0) if theme.lookup_icon("tv-symbolic", 16, 0) else _IMAGE_MISSING IPTV_ICON = theme.load_icon("emblem-shared", 16, 0) if theme.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 +DEFAULT_ICON = theme.load_icon("emblem-default", 16, 0) if theme.lookup_icon("emblem-default", 16, 0) else None class KeyboardKey(Enum):