Merge branch 'development' into experimental-mac

# Conflicts:
#	app/properties.py
#	app/tools/media.py
#	app/ui/dialogs.py
#	app/ui/main_app_window.py
#	app/ui/transmitter.py
#	deb/usr/share/locale/es/LC_MESSAGES/demon-editor.mo
This commit is contained in:
DYefremov
2019-12-16 10:36:19 +03:00
24 changed files with 844 additions and 579 deletions

View File

@@ -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__":

View File

@@ -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

View File

@@ -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

View File

@@ -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

365
app/settings.py Normal file
View File

@@ -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

View File

@@ -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))

View File

@@ -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"

View File

@@ -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 """

View File

@@ -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)

View File

@@ -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())

View File

@@ -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__":

View File

@@ -89,6 +89,7 @@ Author: Dmitriy Yefremov
<property name="destroy_with_parent">True</property>
<property name="type_hint">dialog</property>
<property name="gravity">center</property>
<signal name="check-resize" handler="on_resize" swapped="no"/>
<child type="titlebar">
<object class="GtkHeaderBar" id="header_bar">
<property name="visible">True</property>

View File

@@ -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

View File

@@ -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

View File

@@ -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):

View File

@@ -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

View File

@@ -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:

View File

@@ -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

View File

@@ -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

View File

@@ -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):

View File

@@ -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:

View File

@@ -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. """

Binary file not shown.

View File

@@ -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:"
msgstr "Error en el enlace:"