mirror of
https://github.com/DYefremov/DemonEditor.git
synced 2026-01-19 14:03:17 +01:00
Merge branch 'development' into experimental-mac
# Conflicts: # DemonEditor.desktop # README.md # app/connections.py # app/ui/main_app_window.py # app/ui/main_window.glade # app/ui/uicommons.py # build-deb.sh # deb/DEBIAN/control # deb/DEBIAN/copyright # deb/usr/bin/demon-editor # deb/usr/share/applications/DemonEditor.desktop # start.py
This commit is contained in:
@@ -1,18 +1,20 @@
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import socket
|
||||
import time
|
||||
import urllib
|
||||
import xml.etree.ElementTree as ETree
|
||||
from enum import Enum
|
||||
from ftplib import FTP, error_perm
|
||||
from http.client import RemoteDisconnected
|
||||
from telnetlib import Telnet
|
||||
from urllib.error import HTTPError, URLError
|
||||
from urllib.parse import urlencode
|
||||
from urllib.request import urlopen, HTTPPasswordMgrWithDefaultRealm, HTTPBasicAuthHandler, build_opener, install_opener
|
||||
from urllib.request import urlopen, HTTPPasswordMgrWithDefaultRealm, HTTPBasicAuthHandler, \
|
||||
build_opener, install_opener, Request
|
||||
|
||||
from app.commons import log
|
||||
from app.settings import Profile
|
||||
from app.commons import log, run_task
|
||||
from app.settings import SettingsType
|
||||
|
||||
_BQ_FILES_LIST = ("tv", "radio", # enigma 2
|
||||
"myservices.xml", "bouquets.xml", "ubouquets.xml") # neutrino
|
||||
@@ -35,21 +37,28 @@ class DownloadType(Enum):
|
||||
class HttpRequestType(Enum):
|
||||
ZAP = "zap?sRef="
|
||||
INFO = "about"
|
||||
SIGNAL = "tunersignal"
|
||||
STREAM = "streamcurrentm3u"
|
||||
STATUS = "statusinfo"
|
||||
SIGNAL = "signal"
|
||||
STREAM = "stream.m3u?ref="
|
||||
STREAM_CURRENT = "streamcurrent.m3u"
|
||||
CURRENT = "getcurrent"
|
||||
PLAY = "mediaplayerplay?file=4097:0:1:0:0:0:0:0:0:0:"
|
||||
TEST = None
|
||||
TOKEN = "session"
|
||||
|
||||
|
||||
class TestException(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class HttpApiException(Exception):
|
||||
pass
|
||||
|
||||
|
||||
def download_data(*, settings, download_type=DownloadType.ALL, callback=print):
|
||||
with FTP(host=settings.host, user=settings.user, passwd=settings.password) as ftp:
|
||||
ftp.encoding = "utf-8"
|
||||
callback("FTP OK.\n")
|
||||
save_path = settings.data_dir_path
|
||||
save_path = settings.data_local_path
|
||||
os.makedirs(os.path.dirname(save_path), exist_ok=True)
|
||||
files = []
|
||||
# bouquets
|
||||
@@ -94,15 +103,16 @@ def download_data(*, settings, download_type=DownloadType.ALL, callback=print):
|
||||
|
||||
def upload_data(*, settings, download_type=DownloadType.ALL, remove_unused=False,
|
||||
callback=print, done_callback=None, use_http=False):
|
||||
profile = settings.profile
|
||||
data_path = settings.data_dir_path
|
||||
s_type = settings.setting_type
|
||||
data_path = settings.data_local_path
|
||||
host = settings.host
|
||||
base_url = "http://{}:{}/api/".format(host, settings.http_port)
|
||||
base_url = "http{}://{}:{}".format("s" if settings.http_use_ssl else "", host, settings.http_port)
|
||||
url = "{}/web/".format(base_url)
|
||||
tn, ht = None, None # telnet, http
|
||||
|
||||
try:
|
||||
if profile is Profile.ENIGMA_2 and use_http:
|
||||
ht = http(settings.http_user, settings.http_password, base_url, callback)
|
||||
if s_type is SettingsType.ENIGMA_2 and use_http:
|
||||
ht = http(settings.http_user, settings.http_password, base_url, callback, settings.http_use_ssl)
|
||||
next(ht)
|
||||
message = ""
|
||||
if download_type is DownloadType.BOUQUETS:
|
||||
@@ -113,12 +123,11 @@ def upload_data(*, settings, download_type=DownloadType.ALL, remove_unused=False
|
||||
message = "Satellites.xml file will be updated!"
|
||||
|
||||
params = urlencode({"text": message, "type": 2, "timeout": 5})
|
||||
url = base_url + "message?{}".format(params)
|
||||
ht.send(url)
|
||||
ht.send((url + "message?{}".format(params), "Sending info message... "))
|
||||
|
||||
if download_type is DownloadType.ALL:
|
||||
time.sleep(5)
|
||||
ht.send(base_url + "/powerstate?newstate=0")
|
||||
ht.send((url + "powerstate?newstate=0", "Toggle Standby "))
|
||||
time.sleep(2)
|
||||
else:
|
||||
# telnet
|
||||
@@ -139,7 +148,7 @@ def upload_data(*, settings, download_type=DownloadType.ALL, remove_unused=False
|
||||
if download_type is DownloadType.SATELLITES:
|
||||
upload_xml(ftp, data_path, sat_xml_path, _SAT_XML_FILE, callback)
|
||||
|
||||
if profile is Profile.NEUTRINO_MP and download_type is DownloadType.WEBTV:
|
||||
if s_type is SettingsType.NEUTRINO_MP and download_type is DownloadType.WEBTV:
|
||||
upload_xml(ftp, data_path, sat_xml_path, _WEBTV_XML_FILE, callback)
|
||||
|
||||
if download_type is DownloadType.BOUQUETS:
|
||||
@@ -148,7 +157,7 @@ def upload_data(*, settings, download_type=DownloadType.ALL, remove_unused=False
|
||||
|
||||
if download_type is DownloadType.ALL:
|
||||
upload_xml(ftp, data_path, sat_xml_path, _SAT_XML_FILE, callback)
|
||||
if profile is Profile.NEUTRINO_MP:
|
||||
if s_type is SettingsType.NEUTRINO_MP:
|
||||
upload_xml(ftp, data_path, sat_xml_path, _WEBTV_XML_FILE, callback)
|
||||
|
||||
ftp.cwd(services_path)
|
||||
@@ -156,17 +165,17 @@ def upload_data(*, settings, download_type=DownloadType.ALL, remove_unused=False
|
||||
upload_files(ftp, data_path, _DATA_FILES_LIST, callback)
|
||||
|
||||
if download_type is DownloadType.PICONS:
|
||||
upload_picons(ftp, settings.picons_dir_path, settings.picons_path, callback)
|
||||
upload_picons(ftp, settings.picons_local_path, settings.picons_path, callback)
|
||||
|
||||
if tn and not use_http:
|
||||
# resume enigma or restart neutrino
|
||||
tn.send("init 3" if profile is Profile.ENIGMA_2 else "init 6")
|
||||
tn.send("init 3" if s_type is SettingsType.ENIGMA_2 else "init 6")
|
||||
elif ht and use_http:
|
||||
if download_type is DownloadType.BOUQUETS:
|
||||
ht.send(base_url + "/servicelistreload?mode=2")
|
||||
ht.send((url + "servicelistreload?mode=2", "Reloading Userbouquets."))
|
||||
elif download_type is DownloadType.ALL:
|
||||
ht.send(base_url + "/servicelistreload?mode=0")
|
||||
ht.send(base_url + "/powerstate?newstate=4")
|
||||
ht.send((url + "servicelistreload?mode=0", "Reloading lamedb and Userbouquets."))
|
||||
ht.send((url + "powerstate?newstate=4", "Wakeup from Standby."))
|
||||
|
||||
if done_callback is not None:
|
||||
done_callback()
|
||||
@@ -238,14 +247,14 @@ def send_file(file_name, path, ftp, callback):
|
||||
callback("Uploading file: {}. Status: {}\n".format(file_name, str(ftp.storbinary("STOR " + file_name, f))))
|
||||
|
||||
|
||||
def http(user, password, url, callback):
|
||||
init_auth(user, password, url)
|
||||
def http(user, password, url, callback, use_ssl=False):
|
||||
init_auth(user, password, url, use_ssl)
|
||||
data = get_post_data(url, password, url)
|
||||
|
||||
while True:
|
||||
url = yield
|
||||
with urlopen(url, timeout=5) as f:
|
||||
msg = json.loads(f.read().decode("utf-8")).get("message", None)
|
||||
if msg:
|
||||
callback("HTTP: {}\n".format(msg))
|
||||
url, message = yield
|
||||
resp = get_response(HttpRequestType.TEST, url, data).get("e2statetext", None)
|
||||
callback("HTTP: {} {}\n".format(message, "Successful." if resp and message else ""))
|
||||
|
||||
|
||||
def telnet(host, port=23, user="", password="", timeout=5):
|
||||
@@ -276,38 +285,102 @@ def telnet(host, port=23, user="", password="", timeout=5):
|
||||
# ***************** HTTP API *******************#
|
||||
|
||||
class HttpAPI:
|
||||
__MAX_WORKERS = 4
|
||||
|
||||
def __init__(self, host, port, user, password):
|
||||
self._base_url = "http://{}:{}/api/".format(host, port)
|
||||
init_auth(user, password, self._base_url)
|
||||
def __init__(self, settings):
|
||||
self._settings = settings
|
||||
self._shutdown = False
|
||||
self._session_id = 0
|
||||
self._base_url = None
|
||||
self._data = None
|
||||
self.init()
|
||||
|
||||
from concurrent.futures import ThreadPoolExecutor as PoolExecutor
|
||||
self._executor = PoolExecutor(max_workers=2)
|
||||
self._executor = PoolExecutor(max_workers=self.__MAX_WORKERS)
|
||||
|
||||
def send(self, req_type, ref, callback=print):
|
||||
if self._shutdown:
|
||||
return
|
||||
|
||||
url = self._base_url + req_type.value
|
||||
|
||||
if req_type is HttpRequestType.ZAP:
|
||||
if req_type is HttpRequestType.ZAP or req_type is HttpRequestType.STREAM:
|
||||
url += urllib.parse.quote(ref)
|
||||
elif req_type is HttpRequestType.PLAY:
|
||||
url += urllib.parse.quote(ref).replace("%3A", "%253A")
|
||||
|
||||
future = self._executor.submit(get_json, req_type, url)
|
||||
future = self._executor.submit(get_response, req_type, url, self._data)
|
||||
future.add_done_callback(lambda f: callback(f.result()))
|
||||
|
||||
@run_task
|
||||
def init(self):
|
||||
user, password = self._settings.http_user, self._settings.http_password
|
||||
use_ssl = self._settings.http_use_ssl
|
||||
url = "http{}://{}:{}".format("s" if use_ssl else "", self._settings.host, self._settings.http_port)
|
||||
self._base_url = "{}/web/".format(url)
|
||||
init_auth(user, password, url, use_ssl)
|
||||
url = "{}/web/{}".format(url, HttpRequestType.TOKEN.value)
|
||||
s_id = get_session_id(user, password, url)
|
||||
if s_id != "0":
|
||||
self._data = urllib.parse.urlencode({"user": user, "password": password, "sessionid": s_id}).encode("utf-8")
|
||||
|
||||
@run_task
|
||||
def close(self):
|
||||
self._executor.shutdown(False)
|
||||
self._shutdown = True
|
||||
self._executor.shutdown()
|
||||
|
||||
|
||||
def get_json(req_type, url):
|
||||
def get_response(req_type, url, data=None):
|
||||
try:
|
||||
with urlopen(url, timeout=10) as f:
|
||||
if req_type is HttpRequestType.STREAM:
|
||||
with urlopen(Request(url, data=data), timeout=10) as f:
|
||||
if req_type is HttpRequestType.STREAM or req_type is HttpRequestType.STREAM_CURRENT:
|
||||
return f.read().decode("utf-8")
|
||||
elif req_type is HttpRequestType.CURRENT:
|
||||
for el in ETree.fromstring(f.read().decode("utf-8")).iter("e2event"):
|
||||
return {el.tag: el.text for el in el.iter()} # return first[current] event from the list
|
||||
else:
|
||||
return json.loads(f.read().decode("utf-8"))
|
||||
except (URLError, HTTPError):
|
||||
pass
|
||||
return {el.tag: el.text for el in ETree.fromstring(f.read().decode("utf-8")).iter()}
|
||||
except HTTPError as e:
|
||||
if req_type is HttpRequestType.TEST:
|
||||
raise e
|
||||
return {"error_code": e.code}
|
||||
except (URLError, RemoteDisconnected, ConnectionResetError) as e:
|
||||
if req_type is HttpRequestType.TEST:
|
||||
raise e
|
||||
except ETree.ParseError as e:
|
||||
log("Parsing response error: {}".format(e))
|
||||
|
||||
return {"error_code": -1}
|
||||
|
||||
|
||||
def init_auth(user, password, url, use_ssl=False):
|
||||
""" Init authentication """
|
||||
pass_mgr = HTTPPasswordMgrWithDefaultRealm()
|
||||
pass_mgr.add_password(None, url, user, password)
|
||||
auth_handler = HTTPBasicAuthHandler(pass_mgr)
|
||||
|
||||
if use_ssl:
|
||||
import ssl
|
||||
from urllib.request import HTTPSHandler
|
||||
|
||||
opener = build_opener(auth_handler, HTTPSHandler(context=ssl._create_unverified_context()))
|
||||
else:
|
||||
opener = build_opener(auth_handler)
|
||||
|
||||
install_opener(opener)
|
||||
|
||||
|
||||
def get_session_id(user, password, url):
|
||||
data = urllib.parse.urlencode(dict(user=user, password=password)).encode("utf-8")
|
||||
return get_response(HttpRequestType.TOKEN, url, data=data).get("e2sessionid", "0")
|
||||
|
||||
|
||||
def get_post_data(base_url, password, user):
|
||||
s_id = get_session_id(user, password, "{}/web/{}".format(base_url, HttpRequestType.TOKEN.value))
|
||||
data = None
|
||||
if s_id != "0":
|
||||
data = urllib.parse.urlencode({"user": user, "password": password, "sessionid": s_id}).encode("utf-8")
|
||||
return data
|
||||
|
||||
|
||||
# ***************** Connections testing *******************#
|
||||
@@ -320,36 +393,30 @@ def test_ftp(host, port, user, password, timeout=5):
|
||||
raise TestException(e)
|
||||
|
||||
|
||||
def test_http(host, port, user, password, timeout=5, skip_message=False):
|
||||
try:
|
||||
params = urlencode({"text": "Connection test", "type": 2, "timeout": timeout})
|
||||
params = "statusinfo" if skip_message else "message?{}".format(params)
|
||||
url = "http://{}:{}/api/{}".format(host, port, params)
|
||||
# authentication
|
||||
init_auth(user, password, url)
|
||||
def test_http(host, port, user, password, timeout=5, use_ssl=False, skip_message=False):
|
||||
params = urlencode({"text": "Connection test", "type": 2, "timeout": timeout})
|
||||
params = "statusinfo" if skip_message else "message?{}".format(params)
|
||||
base_url = "http{}://{}:{}".format("s" if use_ssl else "", host, port)
|
||||
# authentication
|
||||
init_auth(user, password, base_url, use_ssl)
|
||||
data = get_post_data(base_url, password, user)
|
||||
|
||||
with urlopen(url, timeout=5) as f:
|
||||
return json.loads(f.read().decode("utf-8")).get("message", "")
|
||||
try:
|
||||
return get_response(HttpRequestType.TEST, "{}/web/{}".format(base_url, params), data).get("e2statetext", "")
|
||||
except (RemoteDisconnected, URLError, HTTPError) as e:
|
||||
raise TestException(e)
|
||||
|
||||
|
||||
def init_auth(user, password, url):
|
||||
""" Init authentication """
|
||||
pass_mgr = HTTPPasswordMgrWithDefaultRealm()
|
||||
pass_mgr.add_password(None, url, user, password)
|
||||
auth_handler = HTTPBasicAuthHandler(pass_mgr)
|
||||
opener = build_opener(auth_handler)
|
||||
install_opener(opener)
|
||||
|
||||
|
||||
def test_telnet(host, port, user, password, timeout=5):
|
||||
try:
|
||||
gen = telnet_test(host, port, user, password, timeout)
|
||||
res = next(gen)
|
||||
print(res)
|
||||
res = next(gen)
|
||||
return res
|
||||
msg = str(res, encoding="utf8").strip()
|
||||
log(msg)
|
||||
next(gen)
|
||||
if re.search("password", msg, re.IGNORECASE):
|
||||
raise TestException(msg)
|
||||
return msg
|
||||
except (socket.timeout, OSError) as e:
|
||||
raise TestException(e)
|
||||
|
||||
@@ -358,14 +425,14 @@ def telnet_test(host, port, user, password, timeout):
|
||||
tn = Telnet(host=host, port=port, timeout=timeout)
|
||||
time.sleep(1)
|
||||
tn.read_until(b"login: ", timeout=2)
|
||||
tn.write(user.encode("utf-8") + b"\n")
|
||||
tn.write(user.encode("utf-8") + b"\r")
|
||||
time.sleep(timeout)
|
||||
tn.read_until(b"Password: ", timeout=2)
|
||||
tn.write(password.encode("utf-8") + b"\n")
|
||||
tn.write(password.encode("utf-8") + b"\r")
|
||||
time.sleep(timeout)
|
||||
yield tn.read_very_eager()
|
||||
tn.close()
|
||||
yield "Done!"
|
||||
yield
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
from app.commons import run_task
|
||||
from app.settings import Profile
|
||||
from app.settings import SettingsType
|
||||
from .ecommons import Service, Satellite, Transponder, Bouquet, Bouquets, is_transponder_valid
|
||||
from .enigma.blacklist import get_blacklist, write_blacklist
|
||||
from .enigma.bouquets import get_bouquets as get_enigma_bouquets, write_bouquets as write_enigma_bouquets, to_bouquet_id
|
||||
@@ -10,33 +10,33 @@ from .neutrino.services import get_services as get_neutrino_services, write_serv
|
||||
from .satxml import get_satellites, write_satellites
|
||||
|
||||
|
||||
def get_services(data_path, profile, format_version):
|
||||
if profile is Profile.ENIGMA_2:
|
||||
def get_services(data_path, s_type, format_version):
|
||||
if s_type is SettingsType.ENIGMA_2:
|
||||
return get_enigma_services(data_path, format_version)
|
||||
elif profile is Profile.NEUTRINO_MP:
|
||||
elif s_type is SettingsType.NEUTRINO_MP:
|
||||
return get_neutrino_services(data_path)
|
||||
|
||||
|
||||
@run_task
|
||||
def write_services(path, channels, profile, format_version):
|
||||
if profile is Profile.ENIGMA_2:
|
||||
def write_services(path, channels, s_type, format_version):
|
||||
if s_type is SettingsType.ENIGMA_2:
|
||||
write_enigma_services(path, channels, format_version)
|
||||
elif profile is Profile.NEUTRINO_MP:
|
||||
elif s_type is SettingsType.NEUTRINO_MP:
|
||||
write_neutrino_services(path, channels)
|
||||
|
||||
|
||||
def get_bouquets(path, profile):
|
||||
if profile is Profile.ENIGMA_2:
|
||||
def get_bouquets(path, s_type):
|
||||
if s_type is SettingsType.ENIGMA_2:
|
||||
return get_enigma_bouquets(path)
|
||||
elif profile is Profile.NEUTRINO_MP:
|
||||
elif s_type is SettingsType.NEUTRINO_MP:
|
||||
return get_neutrino_bouquets(path)
|
||||
|
||||
|
||||
@run_task
|
||||
def write_bouquets(path, bouquets, profile):
|
||||
if profile is Profile.ENIGMA_2:
|
||||
def write_bouquets(path, bouquets, s_type):
|
||||
if s_type is SettingsType.ENIGMA_2:
|
||||
write_enigma_bouquets(path, bouquets)
|
||||
elif profile is Profile.NEUTRINO_MP:
|
||||
elif s_type is SettingsType.NEUTRINO_MP:
|
||||
write_neutrino_bouquets(path, bouquets)
|
||||
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ import re
|
||||
import urllib.request
|
||||
from enum import Enum
|
||||
|
||||
from app.settings import Profile
|
||||
from app.settings import SettingsType
|
||||
from app.ui.uicommons import IPTV_ICON
|
||||
from .ecommons import BqServiceType, Service
|
||||
|
||||
@@ -20,18 +20,18 @@ class StreamType(Enum):
|
||||
NONE_REC_2 = "5002"
|
||||
|
||||
|
||||
def parse_m3u(path, profile):
|
||||
def parse_m3u(path, s_type):
|
||||
with open(path) as file:
|
||||
aggr = [None] * 10
|
||||
services = []
|
||||
groups = set()
|
||||
counter = 0
|
||||
name = None
|
||||
|
||||
|
||||
for line in file.readlines():
|
||||
if line.startswith("#EXTINF"):
|
||||
name = line[1 + line.index(","):].strip()
|
||||
elif line.startswith("#EXTGRP") and profile is Profile.ENIGMA_2:
|
||||
elif line.startswith("#EXTGRP") and s_type is SettingsType.ENIGMA_2:
|
||||
grp_name = line.strip("#EXTGRP:").strip()
|
||||
if grp_name not in groups:
|
||||
groups.add(grp_name)
|
||||
@@ -41,7 +41,7 @@ def parse_m3u(path, profile):
|
||||
services.append(mr)
|
||||
elif not line.startswith("#"):
|
||||
url = line.strip()
|
||||
fav_id = get_fav_id(url, name, profile)
|
||||
fav_id = get_fav_id(url, name, s_type)
|
||||
if name and url:
|
||||
srv = Service(None, None, IPTV_ICON, name, *aggr[0:3], BqServiceType.IPTV.name, *aggr, fav_id, None)
|
||||
services.append(srv)
|
||||
@@ -49,8 +49,8 @@ def parse_m3u(path, profile):
|
||||
return services
|
||||
|
||||
|
||||
def export_to_m3u(path, bouquet, profile):
|
||||
pattern = re.compile(".*:(http.*):.*") if profile is Profile.ENIGMA_2 else re.compile("(http.*?)::::.*")
|
||||
def export_to_m3u(path, bouquet, s_type):
|
||||
pattern = re.compile(".*:(http.*):.*") if s_type is SettingsType.ENIGMA_2 else re.compile("(http.*?)::::.*")
|
||||
lines = ["#EXTM3U\n"]
|
||||
current_grp = None
|
||||
|
||||
@@ -72,13 +72,13 @@ def export_to_m3u(path, bouquet, profile):
|
||||
file.writelines(lines)
|
||||
|
||||
|
||||
def get_fav_id(url, service_name, profile):
|
||||
def get_fav_id(url, service_name, s_type):
|
||||
""" Returns fav id depending on the profile. """
|
||||
if profile is Profile.ENIGMA_2:
|
||||
if s_type is SettingsType.ENIGMA_2:
|
||||
url = urllib.request.quote(url)
|
||||
stream_type = StreamType.NONE_TS.value
|
||||
return ENIGMA2_FAV_ID_FORMAT.format(stream_type, 1, 0, 0, 0, 0, url, service_name, service_name, None)
|
||||
elif profile is Profile.NEUTRINO_MP:
|
||||
elif s_type is SettingsType.NEUTRINO_MP:
|
||||
return NEUTRINO_FAV_ID_FORMAT.format(url, "", 0, None, None, None, None, "", "", 1)
|
||||
|
||||
|
||||
|
||||
372
app/settings.py
372
app/settings.py
@@ -1,9 +1,11 @@
|
||||
import copy
|
||||
import json
|
||||
import locale
|
||||
import os
|
||||
from enum import Enum, IntEnum
|
||||
from pathlib import Path
|
||||
from pprint import pformat
|
||||
from textwrap import dedent
|
||||
from enum import Enum
|
||||
from pathlib import Path
|
||||
|
||||
HOME_PATH = str(Path.home())
|
||||
CONFIG_PATH = HOME_PATH + "/.config/demon-editor/"
|
||||
@@ -11,19 +13,94 @@ CONFIG_FILE = CONFIG_PATH + "config.json"
|
||||
DATA_PATH = HOME_PATH + "/data/"
|
||||
|
||||
|
||||
class Profile(Enum):
|
||||
class Defaults(Enum):
|
||||
""" Default program settings """
|
||||
DEFAULT_PROFILE = "default"
|
||||
BACKUP_BEFORE_DOWNLOADING = True
|
||||
BACKUP_BEFORE_SAVE = True
|
||||
V5_SUPPORT = False
|
||||
HTTP_API_SUPPORT = False
|
||||
ENABLE_YT_DL = False
|
||||
ENABLE_SEND_TO = False
|
||||
USE_COLORS = True
|
||||
NEW_COLOR = "rgb(255,230,204)"
|
||||
EXTRA_COLOR = "rgb(179,230,204)"
|
||||
FAV_CLICK_MODE = 0
|
||||
|
||||
|
||||
def get_default_settings(profile_name="default"):
|
||||
def_settings = SettingsType.ENIGMA_2.get_default_settings()
|
||||
set_local_paths(def_settings, profile_name)
|
||||
|
||||
return {
|
||||
"version": 1,
|
||||
"default_profile": Defaults.DEFAULT_PROFILE.value,
|
||||
"profiles": {profile_name: def_settings},
|
||||
"v5_support": Defaults.V5_SUPPORT.value,
|
||||
"http_api_support": Defaults.HTTP_API_SUPPORT.value,
|
||||
"enable_yt_dl": Defaults.ENABLE_YT_DL.value,
|
||||
"enable_send_to": Defaults.ENABLE_SEND_TO.value,
|
||||
"use_colors": Defaults.USE_COLORS.value,
|
||||
"new_color": Defaults.NEW_COLOR.value,
|
||||
"extra_color": Defaults.EXTRA_COLOR.value,
|
||||
"fav_click_mode": Defaults.FAV_CLICK_MODE.value
|
||||
}
|
||||
|
||||
|
||||
def set_local_paths(settings, profile_name):
|
||||
settings["data_local_path"] = "{}{}/".format(settings["data_local_path"], profile_name)
|
||||
settings["picons_local_path"] = "{}{}/".format(settings["picons_local_path"], profile_name)
|
||||
settings["backup_local_path"] = "{}{}/".format(settings["backup_local_path"], profile_name)
|
||||
|
||||
|
||||
class SettingsType(IntEnum):
|
||||
""" Profiles for settings """
|
||||
ENIGMA_2 = "0"
|
||||
NEUTRINO_MP = "1"
|
||||
ENIGMA_2 = 0
|
||||
NEUTRINO_MP = 1
|
||||
|
||||
def get_default_settings(self):
|
||||
""" Returns default settings for current type """
|
||||
if self is self.ENIGMA_2:
|
||||
return {"setting_type": self.value,
|
||||
"host": "127.0.0.1", "port": "21", "user": "root", "password": "root", "timeout": 5,
|
||||
"http_user": "root", "http_password": "", "http_port": "80",
|
||||
"http_timeout": 5, "http_use_ssl": False,
|
||||
"telnet_user": "root", "telnet_password": "", "telnet_port": "23", "telnet_timeout": 5,
|
||||
"services_path": "/etc/enigma2/", "user_bouquet_path": "/etc/enigma2/",
|
||||
"satellites_xml_path": "/etc/tuxbox/", "data_local_path": DATA_PATH + "enigma2/",
|
||||
"picons_path": "/usr/share/enigma2/picon/",
|
||||
"picons_local_path": DATA_PATH + "enigma2/picons/",
|
||||
"backup_local_path": DATA_PATH + "enigma2/backup/"}
|
||||
elif self is self.NEUTRINO_MP:
|
||||
return {"setting_type": self,
|
||||
"host": "127.0.0.1", "port": "21", "user": "root", "password": "root", "timeout": 5,
|
||||
"http_user": "", "http_password": "", "http_port": "80", "http_timeout": 2, "http_use_ssl": False,
|
||||
"telnet_user": "root", "telnet_password": "", "telnet_port": "23", "telnet_timeout": 1,
|
||||
"services_path": "/var/tuxbox/config/zapit/", "user_bouquet_path": "/var/tuxbox/config/zapit/",
|
||||
"satellites_xml_path": "/var/tuxbox/config/", "data_local_path": DATA_PATH + "neutrino/",
|
||||
"picons_path": "/usr/share/tuxbox/neutrino/icons/logo/",
|
||||
"picons_local_path": DATA_PATH + "neutrino/picons/",
|
||||
"backup_local_path": DATA_PATH + "neutrino/backup/"}
|
||||
|
||||
|
||||
class SettingsException(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class Settings:
|
||||
__INSTANCE = None
|
||||
__VERSION = 1
|
||||
|
||||
def __init__(self):
|
||||
self._config = get_config()
|
||||
self._current_profile = Profile(self._config.get("profile"))
|
||||
self._current_profile_options = self._config.get(self._current_profile.value)
|
||||
def __init__(self, ext_settings=None):
|
||||
settings = ext_settings or get_settings()
|
||||
|
||||
if self.__VERSION > settings.get("version", 0):
|
||||
raise SettingsException("Outdated version of the settings format!")
|
||||
|
||||
self._settings = settings
|
||||
self._current_profile = self._settings.get("default_profile", "default")
|
||||
self._profiles = self._settings.get("profiles", {"default": SettingsType.ENIGMA_2.get_default_settings()})
|
||||
self._cp_settings = self._profiles.get(self._current_profile) # Current profile settings
|
||||
|
||||
def __str__(self):
|
||||
return dedent(""" Current profile: {}
|
||||
@@ -32,8 +109,8 @@ class Settings:
|
||||
Full config:
|
||||
{}
|
||||
""").format(self._current_profile,
|
||||
pformat(self._current_profile_options),
|
||||
pformat(self._config))
|
||||
pformat(self._cp_settings),
|
||||
pformat(self._settings))
|
||||
|
||||
@classmethod
|
||||
def get_instance(cls):
|
||||
@@ -42,325 +119,350 @@ class Settings:
|
||||
return cls.__INSTANCE
|
||||
|
||||
def save(self):
|
||||
write_config(self._config)
|
||||
write_settings(self._settings)
|
||||
|
||||
def reset(self, force_write=False):
|
||||
def_settings = get_default_settings()
|
||||
for p in Profile:
|
||||
current = self._config.get(p.value)
|
||||
default = def_settings.get(p.value)
|
||||
for k in default:
|
||||
current[k] = default.get(k)
|
||||
for k, v in self.setting_type.get_default_settings().items():
|
||||
self._cp_settings[k] = v
|
||||
set_local_paths(self._cp_settings, self._current_profile)
|
||||
|
||||
if force_write:
|
||||
write_config(get_default_settings())
|
||||
self.save()
|
||||
|
||||
@staticmethod
|
||||
def reset_to_default():
|
||||
write_settings(get_default_settings())
|
||||
|
||||
def get_default(self, p_name):
|
||||
""" Returns default value for current settings type """
|
||||
return self.setting_type.get_default_settings().get(p_name)
|
||||
|
||||
def add(self, name, value):
|
||||
""" Adds extra options """
|
||||
self._config[name] = value
|
||||
self._settings[name] = value
|
||||
|
||||
def get(self, name):
|
||||
""" Returns extra options """
|
||||
return self._config.get(name, None)
|
||||
|
||||
def get_default(self, name):
|
||||
""" Returns default value of the option """
|
||||
return get_default_settings().get(self._current_profile.value).get(name)
|
||||
""" Returns extra options or None """
|
||||
return self._settings.get(name, None)
|
||||
|
||||
@property
|
||||
def presets(self):
|
||||
raise NotImplementedError
|
||||
def settings(self):
|
||||
""" Returns copy of the current settings! """
|
||||
return copy.deepcopy(self._settings)
|
||||
|
||||
@presets.setter
|
||||
def presets(self, name):
|
||||
raise NotImplementedError
|
||||
@settings.setter
|
||||
def settings(self, value):
|
||||
""" Sets copy of the settings! """
|
||||
self._settings = copy.deepcopy(value)
|
||||
|
||||
@property
|
||||
def profile(self):
|
||||
def current_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)
|
||||
@current_profile.setter
|
||||
def current_profile(self, value):
|
||||
self._current_profile = value
|
||||
self._cp_settings = self._profiles.get(self._current_profile)
|
||||
|
||||
@property
|
||||
def default_profile(self):
|
||||
return self._settings.get("default_profile", "default")
|
||||
|
||||
@default_profile.setter
|
||||
def default_profile(self, value):
|
||||
self._settings["default_profile"] = value
|
||||
|
||||
@property
|
||||
def profiles(self):
|
||||
return self._profiles
|
||||
|
||||
@profiles.setter
|
||||
def profiles(self, ps):
|
||||
self._profiles = ps
|
||||
self._settings["profiles"] = self._profiles
|
||||
|
||||
@property
|
||||
def setting_type(self):
|
||||
return SettingsType(self._cp_settings.get("setting_type", SettingsType.ENIGMA_2.value))
|
||||
|
||||
@setting_type.setter
|
||||
def setting_type(self, s_type):
|
||||
self._cp_settings["setting_type"] = s_type.value
|
||||
|
||||
@property
|
||||
def language(self):
|
||||
return self._settings.get("language", locale.getlocale()[0] or "en_US")
|
||||
|
||||
@language.setter
|
||||
def language(self, value):
|
||||
self._settings["language"] = value
|
||||
|
||||
@property
|
||||
def load_last_config(self):
|
||||
return self._settings.get("load_last_config", False)
|
||||
|
||||
@load_last_config.setter
|
||||
def load_last_config(self, value):
|
||||
self._settings["load_last_config"] = value
|
||||
|
||||
@property
|
||||
def host(self):
|
||||
return self._current_profile_options.get("host", self.get_default("host"))
|
||||
return self._cp_settings.get("host", self.get_default("host"))
|
||||
|
||||
@host.setter
|
||||
def host(self, value):
|
||||
self._current_profile_options["host"] = value
|
||||
self._cp_settings["host"] = value
|
||||
|
||||
@property
|
||||
def port(self):
|
||||
return self._current_profile_options.get("port", self.get_default("port"))
|
||||
return self._cp_settings.get("port", self.get_default("port"))
|
||||
|
||||
@port.setter
|
||||
def port(self, value):
|
||||
self._current_profile_options["port"] = value
|
||||
self._cp_settings["port"] = value
|
||||
|
||||
@property
|
||||
def user(self):
|
||||
return self._current_profile_options.get("user", self.get_default("user"))
|
||||
return self._cp_settings.get("user", self.get_default("user"))
|
||||
|
||||
@user.setter
|
||||
def user(self, value):
|
||||
self._current_profile_options["user"] = value
|
||||
self._cp_settings["user"] = value
|
||||
|
||||
@property
|
||||
def password(self):
|
||||
return self._current_profile_options.get("password", self.get_default("password"))
|
||||
return self._cp_settings.get("password", self.get_default("password"))
|
||||
|
||||
@password.setter
|
||||
def password(self, value):
|
||||
self._current_profile_options["password"] = value
|
||||
self._cp_settings["password"] = value
|
||||
|
||||
@property
|
||||
def http_user(self):
|
||||
return self._current_profile_options.get("http_user", self.get_default("http_user"))
|
||||
return self._cp_settings.get("http_user", self.get_default("http_user"))
|
||||
|
||||
@http_user.setter
|
||||
def http_user(self, value):
|
||||
self._current_profile_options["http_user"] = value
|
||||
self._cp_settings["http_user"] = value
|
||||
|
||||
@property
|
||||
def http_password(self):
|
||||
return self._current_profile_options.get("http_password", self.get_default("http_password"))
|
||||
return self._cp_settings.get("http_password", self.get_default("http_password"))
|
||||
|
||||
@http_password.setter
|
||||
def http_password(self, value):
|
||||
self._current_profile_options["http_password"] = value
|
||||
self._cp_settings["http_password"] = value
|
||||
|
||||
@property
|
||||
def http_port(self):
|
||||
return self._current_profile_options.get("http_port", self.get_default("http_port"))
|
||||
return self._cp_settings.get("http_port", self.get_default("http_port"))
|
||||
|
||||
@http_port.setter
|
||||
def http_port(self, value):
|
||||
self._current_profile_options["http_port"] = value
|
||||
self._cp_settings["http_port"] = value
|
||||
|
||||
@property
|
||||
def http_timeout(self):
|
||||
return self._current_profile_options.get("http_timeout", self.get_default("http_timeout"))
|
||||
return self._cp_settings.get("http_timeout", self.get_default("http_timeout"))
|
||||
|
||||
@http_timeout.setter
|
||||
def http_timeout(self, value):
|
||||
self._current_profile_options["http_timeout"] = value
|
||||
self._cp_settings["http_timeout"] = value
|
||||
|
||||
@property
|
||||
def http_use_ssl(self):
|
||||
return self._cp_settings.get("http_use_ssl", self.get_default("http_use_ssl"))
|
||||
|
||||
@http_use_ssl.setter
|
||||
def http_use_ssl(self, value):
|
||||
self._cp_settings["http_use_ssl"] = value
|
||||
|
||||
@property
|
||||
def telnet_user(self):
|
||||
return self._current_profile_options.get("telnet_user", self.get_default("telnet_user"))
|
||||
return self._cp_settings.get("telnet_user", self.get_default("telnet_user"))
|
||||
|
||||
@telnet_user.setter
|
||||
def telnet_user(self, value):
|
||||
self._current_profile_options["telnet_user"] = value
|
||||
self._cp_settings["telnet_user"] = value
|
||||
|
||||
@property
|
||||
def telnet_password(self):
|
||||
return self._current_profile_options.get("telnet_password", self.get_default("telnet_password"))
|
||||
return self._cp_settings.get("telnet_password", self.get_default("telnet_password"))
|
||||
|
||||
@telnet_password.setter
|
||||
def telnet_password(self, value):
|
||||
self._current_profile_options["telnet_password"] = value
|
||||
self._cp_settings["telnet_password"] = value
|
||||
|
||||
@property
|
||||
def telnet_port(self):
|
||||
return self._current_profile_options.get("telnet_port", self.get_default("telnet_port"))
|
||||
return self._cp_settings.get("telnet_port", self.get_default("telnet_port"))
|
||||
|
||||
@telnet_port.setter
|
||||
def telnet_port(self, value):
|
||||
self._current_profile_options["telnet_port"] = value
|
||||
self._cp_settings["telnet_port"] = value
|
||||
|
||||
@property
|
||||
def telnet_timeout(self):
|
||||
return self._current_profile_options.get("telnet_timeout", self.get_default("telnet_timeout"))
|
||||
return self._cp_settings.get("telnet_timeout", self.get_default("telnet_timeout"))
|
||||
|
||||
@telnet_timeout.setter
|
||||
def telnet_timeout(self, value):
|
||||
self._current_profile_options["telnet_timeout"] = value
|
||||
self._cp_settings["telnet_timeout"] = value
|
||||
|
||||
@property
|
||||
def services_path(self):
|
||||
return self._current_profile_options.get("services_path", self.get_default("services_path"))
|
||||
return self._cp_settings.get("services_path", self.get_default("services_path"))
|
||||
|
||||
@services_path.setter
|
||||
def services_path(self, value):
|
||||
self._current_profile_options["services_path"] = value
|
||||
self._cp_settings["services_path"] = value
|
||||
|
||||
@property
|
||||
def user_bouquet_path(self):
|
||||
return self._current_profile_options.get("user_bouquet_path", self.get_default("user_bouquet_path"))
|
||||
return self._cp_settings.get("user_bouquet_path", self.get_default("user_bouquet_path"))
|
||||
|
||||
@user_bouquet_path.setter
|
||||
def user_bouquet_path(self, value):
|
||||
self._current_profile_options["user_bouquet_path"] = value
|
||||
self._cp_settings["user_bouquet_path"] = value
|
||||
|
||||
@property
|
||||
def satellites_xml_path(self):
|
||||
return self._current_profile_options.get("satellites_xml_path", self.get_default("satellites_xml_path"))
|
||||
return self._cp_settings.get("satellites_xml_path", self.get_default("satellites_xml_path"))
|
||||
|
||||
@satellites_xml_path.setter
|
||||
def satellites_xml_path(self, value):
|
||||
self._current_profile_options["satellites_xml_path"] = value
|
||||
self._cp_settings["satellites_xml_path"] = value
|
||||
|
||||
@property
|
||||
def data_dir_path(self):
|
||||
return self._current_profile_options.get("data_dir_path", self.get_default("data_dir_path"))
|
||||
def data_local_path(self):
|
||||
return self._cp_settings.get("data_local_path", self.get_default("data_local_path"))
|
||||
|
||||
@data_dir_path.setter
|
||||
def data_dir_path(self, value):
|
||||
self._current_profile_options["data_dir_path"] = value
|
||||
@data_local_path.setter
|
||||
def data_local_path(self, value):
|
||||
self._cp_settings["data_local_path"] = value
|
||||
|
||||
@property
|
||||
def picons_path(self):
|
||||
return self._current_profile_options.get("picons_path", self.get_default("picons_path"))
|
||||
return self._cp_settings.get("picons_path", self.get_default("picons_path"))
|
||||
|
||||
@picons_path.setter
|
||||
def picons_path(self, value):
|
||||
self._current_profile_options["picons_path"] = value
|
||||
self._cp_settings["picons_path"] = value
|
||||
|
||||
@property
|
||||
def picons_dir_path(self):
|
||||
return self._current_profile_options.get("picons_dir_path", self.get_default("picons_dir_path"))
|
||||
def picons_local_path(self):
|
||||
return self._cp_settings.get("picons_local_path", self.get_default("picons_local_path"))
|
||||
|
||||
@picons_dir_path.setter
|
||||
def picons_dir_path(self, value):
|
||||
self._current_profile_options["picons_dir_path"] = value
|
||||
@picons_local_path.setter
|
||||
def picons_local_path(self, value):
|
||||
self._cp_settings["picons_local_path"] = value
|
||||
|
||||
@property
|
||||
def backup_dir_path(self):
|
||||
return self._current_profile_options.get("backup_dir_path", self.get_default("backup_dir_path"))
|
||||
def backup_local_path(self):
|
||||
return self._cp_settings.get("backup_local_path", self.get_default("backup_local_path"))
|
||||
|
||||
@backup_dir_path.setter
|
||||
def backup_dir_path(self, value):
|
||||
self._current_profile_options["backup_dir_path"] = value
|
||||
@backup_local_path.setter
|
||||
def backup_local_path(self, value):
|
||||
self._cp_settings["backup_local_path"] = value
|
||||
|
||||
# ***** Program settings *****
|
||||
|
||||
@property
|
||||
def backup_before_save(self):
|
||||
return self._current_profile_options.get("backup_before_save", self.get_default("backup_before_save"))
|
||||
return self._settings.get("backup_before_save", Defaults.BACKUP_BEFORE_SAVE.value)
|
||||
|
||||
@backup_before_save.setter
|
||||
def backup_before_save(self, value):
|
||||
self._current_profile_options["backup_before_save"] = value
|
||||
self._settings["backup_before_save"] = value
|
||||
|
||||
@property
|
||||
def backup_before_downloading(self):
|
||||
return self._current_profile_options.get("backup_before_downloading",
|
||||
self.get_default("backup_before_downloading"))
|
||||
return self._settings.get("backup_before_downloading", Defaults.BACKUP_BEFORE_DOWNLOADING.value)
|
||||
|
||||
@backup_before_downloading.setter
|
||||
def backup_before_downloading(self, value):
|
||||
self._current_profile_options["backup_before_downloading"] = value
|
||||
self._settings["backup_before_downloading"] = value
|
||||
|
||||
@property
|
||||
def v5_support(self):
|
||||
return self._current_profile_options.get("v5_support", self.get_default("v5_support"))
|
||||
return self._settings.get("v5_support", Defaults.V5_SUPPORT.value)
|
||||
|
||||
@v5_support.setter
|
||||
def v5_support(self, value):
|
||||
self._current_profile_options["v5_support"] = value
|
||||
self._settings["v5_support"] = value
|
||||
|
||||
@property
|
||||
def http_api_support(self):
|
||||
return self._current_profile_options.get("http_api_support", self.get_default("http_api_support"))
|
||||
return self._settings.get("http_api_support", Defaults.HTTP_API_SUPPORT.value)
|
||||
|
||||
@http_api_support.setter
|
||||
def http_api_support(self, value):
|
||||
self._current_profile_options["http_api_support"] = value
|
||||
self._settings["http_api_support"] = value
|
||||
|
||||
@property
|
||||
def enable_yt_dl(self):
|
||||
return self._current_profile_options.get("enable_yt_dl", self.get_default("enable_yt_dl"))
|
||||
return self._settings.get("enable_yt_dl", Defaults.ENABLE_YT_DL.value)
|
||||
|
||||
@enable_yt_dl.setter
|
||||
def enable_yt_dl(self, value):
|
||||
self._current_profile_options["enable_yt_dl"] = value
|
||||
self._settings["enable_yt_dl"] = value
|
||||
|
||||
@property
|
||||
def enable_send_to(self):
|
||||
return self._current_profile_options.get("enable_send_to", self.get_default("enable_send_to"))
|
||||
return self._settings.get("enable_send_to", Defaults.ENABLE_SEND_TO.value)
|
||||
|
||||
@enable_send_to.setter
|
||||
def enable_send_to(self, value):
|
||||
self._current_profile_options["enable_send_to"] = value
|
||||
self._settings["enable_send_to"] = value
|
||||
|
||||
@property
|
||||
def use_colors(self):
|
||||
return self._current_profile_options.get("use_colors", self.get_default("use_colors"))
|
||||
return self._settings.get("use_colors", Defaults.USE_COLORS.value)
|
||||
|
||||
@use_colors.setter
|
||||
def use_colors(self, value):
|
||||
self._current_profile_options["use_colors"] = value
|
||||
self._settings["use_colors"] = value
|
||||
|
||||
@property
|
||||
def new_color(self):
|
||||
return self._current_profile_options.get("new_color", self.get_default("new_color"))
|
||||
return self._settings.get("new_color", Defaults.NEW_COLOR.value)
|
||||
|
||||
@new_color.setter
|
||||
def new_color(self, value):
|
||||
self._current_profile_options["new_color"] = value
|
||||
self._settings["new_color"] = value
|
||||
|
||||
@property
|
||||
def extra_color(self):
|
||||
return self._current_profile_options.get("extra_color", self.get_default("extra_color"))
|
||||
return self._settings.get("extra_color", Defaults.EXTRA_COLOR.value)
|
||||
|
||||
@extra_color.setter
|
||||
def extra_color(self, value):
|
||||
self._current_profile_options["extra_color"] = value
|
||||
self._settings["extra_color"] = value
|
||||
|
||||
@property
|
||||
def fav_click_mode(self):
|
||||
return self._current_profile_options.get("fav_click_mode", self.get_default("fav_click_mode"))
|
||||
return self._settings.get("fav_click_mode", Defaults.FAV_CLICK_MODE.value)
|
||||
|
||||
@fav_click_mode.setter
|
||||
def fav_click_mode(self, value):
|
||||
self._current_profile_options["fav_click_mode"] = value
|
||||
self._settings["fav_click_mode"] = value
|
||||
|
||||
|
||||
def get_config():
|
||||
def get_settings():
|
||||
os.makedirs(os.path.dirname(CONFIG_PATH), exist_ok=True) # create dir if not exist
|
||||
os.makedirs(os.path.dirname(DATA_PATH), exist_ok=True)
|
||||
|
||||
if not os.path.isfile(CONFIG_FILE) or os.stat(CONFIG_FILE).st_size == 0:
|
||||
write_config(get_default_settings())
|
||||
write_settings(get_default_settings())
|
||||
|
||||
with open(CONFIG_FILE, "r") as config_file:
|
||||
return json.load(config_file)
|
||||
|
||||
|
||||
def write_config(config):
|
||||
def write_settings(config):
|
||||
with open(CONFIG_FILE, "w") as config_file:
|
||||
json.dump(config, config_file, indent=" ")
|
||||
|
||||
|
||||
def get_default_settings():
|
||||
return {
|
||||
Profile.ENIGMA_2.value: {
|
||||
"host": "127.0.0.1", "port": "21", "user": "root", "password": "root",
|
||||
"http_user": "root", "http_password": "", "http_port": "80", "http_timeout": 5,
|
||||
"telnet_user": "root", "telnet_password": "", "telnet_port": "23", "telnet_timeout": 5,
|
||||
"services_path": "/etc/enigma2/", "user_bouquet_path": "/etc/enigma2/",
|
||||
"satellites_xml_path": "/etc/tuxbox/", "data_dir_path": DATA_PATH + "enigma2/",
|
||||
"picons_path": "/usr/share/enigma2/picon", "picons_dir_path": DATA_PATH + "enigma2/picons/",
|
||||
"backup_dir_path": DATA_PATH + "enigma2/backup/",
|
||||
"backup_before_save": True, "backup_before_downloading": True,
|
||||
"v5_support": False, "http_api_support": False, "enable_yt_dl": False, "enable_send_to": False,
|
||||
"use_colors": True, "new_color": "rgb(255,230,204)", "extra_color": "rgb(179,230,204)",
|
||||
"fav_click_mode": 0},
|
||||
Profile.NEUTRINO_MP.value: {
|
||||
"host": "127.0.0.1", "port": "21", "user": "root", "password": "root",
|
||||
"http_user": "", "http_password": "", "http_port": "80", "http_timeout": 2,
|
||||
"telnet_user": "root", "telnet_password": "", "telnet_port": "23", "telnet_timeout": 1,
|
||||
"services_path": "/var/tuxbox/config/zapit/", "user_bouquet_path": "/var/tuxbox/config/zapit/",
|
||||
"satellites_xml_path": "/var/tuxbox/config/", "data_dir_path": DATA_PATH + "neutrino/",
|
||||
"picons_path": "/usr/share/tuxbox/neutrino/icons/logo/", "picons_dir_path": DATA_PATH + "neutrino/picons/",
|
||||
"backup_dir_path": DATA_PATH + "neutrino/backup/",
|
||||
"backup_before_save": True, "backup_before_downloading": True,
|
||||
"fav_click_mode": 0},
|
||||
"profile": Profile.ENIGMA_2.value}
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
pass
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import sys
|
||||
|
||||
from app.commons import run_task, log
|
||||
|
||||
|
||||
class Player:
|
||||
__VLC_INSTANCE = None
|
||||
|
||||
def __init__(self, rewind_callback, position_callback):
|
||||
def __init__(self, rewind_callback, position_callback, error_callback, playing_callback):
|
||||
try:
|
||||
from app.tools import vlc
|
||||
from app.tools.vlc import EventType
|
||||
@@ -28,10 +29,19 @@ class Player:
|
||||
lambda et, p: position_callback(p.get_time()),
|
||||
self._player)
|
||||
|
||||
if error_callback:
|
||||
ev_mgr.event_attach(EventType.MediaPlayerEncounteredError,
|
||||
lambda et, p: error_callback(),
|
||||
self._player)
|
||||
if playing_callback:
|
||||
ev_mgr.event_attach(EventType.MediaPlayerPlaying,
|
||||
lambda et, p: playing_callback(),
|
||||
self._player)
|
||||
|
||||
@classmethod
|
||||
def get_instance(cls, rewind_callback=None, position_callback=None):
|
||||
def get_instance(cls, rewind_callback=None, position_callback=None, error_callback=None, playing_callback=None):
|
||||
if not cls.__VLC_INSTANCE:
|
||||
cls.__VLC_INSTANCE = Player(rewind_callback, position_callback)
|
||||
cls.__VLC_INSTANCE = Player(rewind_callback, position_callback, error_callback, playing_callback)
|
||||
return cls.__VLC_INSTANCE
|
||||
|
||||
@run_task
|
||||
|
||||
@@ -7,7 +7,7 @@ from collections import namedtuple
|
||||
from html.parser import HTMLParser
|
||||
|
||||
from app.commons import run_task
|
||||
from app.settings import Profile
|
||||
from app.settings import SettingsType
|
||||
|
||||
_ENIGMA2_PICON_KEY = "{:X}:{:X}:{}"
|
||||
_NEUTRINO_PICON_KEY = "{:x}{:04x}{:04x}.png"
|
||||
@@ -79,7 +79,7 @@ class PiconsParser(HTMLParser):
|
||||
pass
|
||||
|
||||
@staticmethod
|
||||
def parse(open_path, picons_path, tmp_path, provider, picon_ids, profile=Profile.ENIGMA_2):
|
||||
def parse(open_path, picons_path, tmp_path, provider, picon_ids, s_type=SettingsType.ENIGMA_2):
|
||||
with open(open_path, encoding="utf-8", errors="replace") as f:
|
||||
on_id, pos, ssid, single = provider.on_id, provider.pos, provider.ssid, provider.single
|
||||
neg_pos = pos.endswith("W")
|
||||
@@ -100,7 +100,7 @@ class PiconsParser(HTMLParser):
|
||||
namespace = "{:X}{:X}".format(int(pos), int(freq))
|
||||
else:
|
||||
namespace = "{:X}0000".format(int(pos))
|
||||
name = PiconsParser.format(ssid if single else p.ssid, on_id, namespace, picon_ids, profile)
|
||||
name = PiconsParser.format(ssid if single else p.ssid, on_id, namespace, picon_ids, s_type)
|
||||
p_name = picons_path + (name if name else os.path.basename(p.ref))
|
||||
shutil.copyfile(tmp_path + "www.lyngsat.com/" + p.ref.lstrip("."), p_name)
|
||||
except (TypeError, ValueError) as e:
|
||||
@@ -109,10 +109,10 @@ class PiconsParser(HTMLParser):
|
||||
print(msg)
|
||||
|
||||
@staticmethod
|
||||
def format(ssid, on_id, namespace, picon_ids, profile: Profile):
|
||||
if profile is Profile.ENIGMA_2:
|
||||
def format(ssid, on_id, namespace, picon_ids, s_type):
|
||||
if s_type is SettingsType.ENIGMA_2:
|
||||
return picon_ids.get(_ENIGMA2_PICON_KEY.format(int(ssid), int(on_id), namespace), None)
|
||||
elif profile is Profile.NEUTRINO_MP:
|
||||
elif s_type is SettingsType.NEUTRINO_MP:
|
||||
tr_id = int(ssid[:-2] if len(ssid) < 4 else ssid[:2])
|
||||
return _NEUTRINO_PICON_KEY.format(tr_id, int(on_id), int(ssid))
|
||||
else:
|
||||
@@ -249,12 +249,12 @@ def parse_providers(open_path):
|
||||
|
||||
|
||||
@run_task
|
||||
def convert_to(src_path, dest_path, profile, callback, done_callback):
|
||||
def convert_to(src_path, dest_path, s_type, callback, done_callback):
|
||||
""" Converts names format of picons.
|
||||
|
||||
Copies resulting files from src to dest and writes state to callback.
|
||||
"""
|
||||
pattern = "/*_0_0_0.png" if profile is Profile.ENIGMA_2 else "/*.png"
|
||||
pattern = "/*_0_0_0.png" if s_type is SettingsType.ENIGMA_2 else "/*.png"
|
||||
for file in glob.glob(src_path + pattern):
|
||||
base_name = os.path.basename(file)
|
||||
pic_data = base_name.rstrip(".png").split("_")
|
||||
|
||||
@@ -7,7 +7,7 @@ from datetime import datetime
|
||||
from enum import Enum
|
||||
|
||||
from app.commons import run_idle
|
||||
from app.settings import Profile
|
||||
from app.settings import SettingsType
|
||||
from app.ui.dialogs import show_dialog, DialogType
|
||||
from app.ui.main_helper import append_text_to_tview
|
||||
from .uicommons import Gtk, Gdk, UI_RESOURCES_PATH, KeyboardKey
|
||||
@@ -36,9 +36,9 @@ class BackupDialog:
|
||||
builder.connect_signals(handlers)
|
||||
|
||||
self._settings = settings
|
||||
self._profile = settings.profile
|
||||
self._data_path = self._settings.data_dir_path
|
||||
self._backup_path = self._settings.backup_dir_path or self._data_path + "backup/"
|
||||
self._s_type = settings.setting_type
|
||||
self._data_path = self._settings.data_local_path
|
||||
self._backup_path = self._settings.backup_local_path or self._data_path + "backup/"
|
||||
self._open_data_callback = callback
|
||||
self._dialog_window = builder.get_object("dialog_window")
|
||||
self._dialog_window.set_transient_for(transient)
|
||||
@@ -152,7 +152,7 @@ class BackupDialog:
|
||||
shutil.unpack_archive(full_file_name, self._data_path)
|
||||
elif restore_type is RestoreType.BOUQUETS:
|
||||
tmp_dir = tempfile.gettempdir() + "/" + file_name
|
||||
cond = (".tv", ".radio") if self._profile is Profile.ENIGMA_2 else "bouquets.xml"
|
||||
cond = (".tv", ".radio") if self._s_type is SettingsType.ENIGMA_2 else "bouquets.xml"
|
||||
shutil.unpack_archive(full_file_name, tmp_dir)
|
||||
for file in filter(lambda f: f.endswith(cond), os.listdir(self._data_path)):
|
||||
os.remove(os.path.join(self._data_path, file))
|
||||
@@ -192,6 +192,7 @@ def backup_data(path, backup_path, move=True):
|
||||
"""
|
||||
backup_path = "{}{}/".format(backup_path, datetime.now().strftime("%Y-%m-%d_%H-%M-%S"))
|
||||
os.makedirs(os.path.dirname(backup_path), exist_ok=True)
|
||||
os.makedirs(os.path.dirname(path), exist_ok=True)
|
||||
# backup files in data dir(skipping dirs and satellites.xml)
|
||||
for file in filter(lambda f: f != "satellites.xml" and os.path.isfile(os.path.join(path, f)), os.listdir(path)):
|
||||
src, dst = os.path.join(path, file), backup_path + file
|
||||
|
||||
@@ -41,7 +41,7 @@ Author: Dmitriy Yefremov
|
||||
<property name="type_hint">normal</property>
|
||||
<property name="program_name">DemonEditor</property>
|
||||
<property name="version">0.4.7 Pre-alpha</property>
|
||||
<property name="copyright">2018-2019 Dmitriy Yefremov
|
||||
<property name="copyright">2018-2020 Dmitriy Yefremov
|
||||
</property>
|
||||
<property name="comments" translatable="yes">Enigma2 channel and satellites list editor for MacOS.
|
||||
(Experimental)</property>
|
||||
@@ -51,7 +51,8 @@ Author: Dmitriy Yefremov
|
||||
<property name="authors">Dmitriy Yefremov
|
||||
</property>
|
||||
<property name="translator_credits" translatable="yes">translator-credits</property>
|
||||
<property name="logo_icon_name">accessories-text-editor</property>
|
||||
<property name="artists">Program logo: <a href="http://ihad.tv"> mfgeg</a></property>
|
||||
<property name="logo_icon_name">demon-editor</property>
|
||||
<property name="wrap_license">True</property>
|
||||
<property name="license_type">mit-x11</property>
|
||||
<child>
|
||||
|
||||
@@ -104,7 +104,7 @@ def get_file_chooser_dialog(transient, text, settings, action_type, file_filter)
|
||||
if file_filter is not None:
|
||||
dialog.add_filter(file_filter)
|
||||
|
||||
path = settings.data_dir_path
|
||||
path = settings.data_local_path
|
||||
dialog.set_current_folder(path)
|
||||
response = dialog.run()
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ THE SOFTWARE.
|
||||
Author: Dmitriy Yefremov
|
||||
|
||||
-->
|
||||
<interface>
|
||||
<interface domain="demon-editor">
|
||||
<requires lib="gtk+" version="3.16"/>
|
||||
<!-- interface-license-type mit -->
|
||||
<!-- interface-name DemonEditor -->
|
||||
@@ -47,6 +47,7 @@ Author: Dmitriy Yefremov
|
||||
<object class="GtkHeaderBar" id="header_bar">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="title" translatable="yes">FTP-transfer</property>
|
||||
<property name="spacing">5</property>
|
||||
<property name="show_close_button">True</property>
|
||||
<child>
|
||||
@@ -56,7 +57,6 @@ Author: Dmitriy Yefremov
|
||||
<property name="spacing">2</property>
|
||||
<child>
|
||||
<object class="GtkButton" id="receive_button">
|
||||
<property name="width_request">48</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
@@ -78,7 +78,6 @@ Author: Dmitriy Yefremov
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="send_button">
|
||||
<property name="width_request">48</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
@@ -100,120 +99,13 @@ Author: Dmitriy Yefremov
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child type="title">
|
||||
<object class="GtkBox" id="header_box">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_top">5</property>
|
||||
<property name="margin_bottom">2</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<property name="spacing">5</property>
|
||||
<child>
|
||||
<object class="GtkLabel" id="header_label">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">FTP-transfer</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkBox" id="header_data_box">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_bottom">5</property>
|
||||
<property name="spacing">5</property>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label10">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="xalign">0</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkRadioButton" id="all_radio_button">
|
||||
<property name="label" translatable="yes">All</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">False</property>
|
||||
<property name="draw_indicator">True</property>
|
||||
<property name="group">satellites_radio_button</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkRadioButton" id="bouquets_radio_button">
|
||||
<property name="label" translatable="yes">Bouquets</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">False</property>
|
||||
<property name="draw_indicator">True</property>
|
||||
<property name="group">satellites_radio_button</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkRadioButton" id="satellites_radio_button">
|
||||
<property name="label" translatable="yes">Satellites</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">False</property>
|
||||
<property name="draw_indicator">True</property>
|
||||
<property name="group">all_radio_button</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">3</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkRadioButton" id="webtv_radio_button">
|
||||
<property name="label" translatable="yes">WebTV</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">False</property>
|
||||
<property name="draw_indicator">True</property>
|
||||
<property name="group">all_radio_button</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">4</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="options_button">
|
||||
<property name="width_request">48</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="tooltip_text" translatable="yes">Options</property>
|
||||
<signal name="clicked" handler="on_preferences" swapped="no"/>
|
||||
<signal name="clicked" handler="on_settings" swapped="no"/>
|
||||
<child>
|
||||
<object class="GtkImage">
|
||||
<property name="visible">True</property>
|
||||
@@ -238,13 +130,156 @@ Author: Dmitriy Yefremov
|
||||
<property name="margin_bottom">1</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<property name="spacing">2</property>
|
||||
<child>
|
||||
<object class="GtkBox" id="selection_data_box">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="halign">center</property>
|
||||
<property name="margin_top">10</property>
|
||||
<property name="margin_bottom">5</property>
|
||||
<property name="spacing">5</property>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label10">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="xalign">0</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkRadioButton" id="all_radio_button">
|
||||
<property name="label" translatable="yes">All</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">False</property>
|
||||
<property name="draw_indicator">True</property>
|
||||
<property name="group">satellites_radio_button</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkRadioButton" id="bouquets_radio_button">
|
||||
<property name="label" translatable="yes">Bouquets</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">False</property>
|
||||
<property name="draw_indicator">True</property>
|
||||
<property name="group">satellites_radio_button</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkRadioButton" id="satellites_radio_button">
|
||||
<property name="label" translatable="yes">Satellites</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">False</property>
|
||||
<property name="draw_indicator">True</property>
|
||||
<property name="group">all_radio_button</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">3</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkRadioButton" id="webtv_radio_button">
|
||||
<property name="label" translatable="yes">WebTV</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">False</property>
|
||||
<property name="draw_indicator">True</property>
|
||||
<property name="group">all_radio_button</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">4</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkBox">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="halign">center</property>
|
||||
<property name="margin_top">5</property>
|
||||
<property name="margin_bottom">5</property>
|
||||
<property name="spacing">5</property>
|
||||
<child>
|
||||
<object class="GtkLabel">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">Profile:</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkComboBoxText" id="profile_combo_box">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="focus_on_click">False</property>
|
||||
<property name="halign">center</property>
|
||||
<property name="margin_top">1</property>
|
||||
<property name="margin_bottom">1</property>
|
||||
<property name="active">0</property>
|
||||
<property name="has_frame">False</property>
|
||||
<property name="has_entry">True</property>
|
||||
<signal name="changed" handler="on_profile_changed" swapped="no"/>
|
||||
<child internal-child="entry">
|
||||
<object class="GtkEntry">
|
||||
<property name="can_focus">True</property>
|
||||
<property name="has_tooltip">True</property>
|
||||
<property name="halign">center</property>
|
||||
<property name="margin_top">1</property>
|
||||
<property name="margin_bottom">1</property>
|
||||
<property name="editable">False</property>
|
||||
<property name="has_frame">False</property>
|
||||
<property name="max_width_chars">9</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkFrame" id="main_settings_box_frame">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_left">5</property>
|
||||
<property name="margin_right">5</property>
|
||||
<property name="margin_top">5</property>
|
||||
<property name="label_xalign">0.019999999552965164</property>
|
||||
<property name="shadow_type">in</property>
|
||||
<child>
|
||||
@@ -253,6 +288,7 @@ Author: Dmitriy Yefremov
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_left">5</property>
|
||||
<property name="margin_right">5</property>
|
||||
<property name="margin_top">5</property>
|
||||
<property name="margin_bottom">5</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
@@ -319,7 +355,7 @@ Author: Dmitriy Yefremov
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
@@ -398,7 +434,7 @@ Author: Dmitriy Yefremov
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
@@ -590,7 +626,7 @@ Author: Dmitriy Yefremov
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
<property name="position">3</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
@@ -629,7 +665,7 @@ Author: Dmitriy Yefremov
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">3</property>
|
||||
<property name="position">4</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
@@ -685,9 +721,12 @@ Author: Dmitriy Yefremov
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">4</property>
|
||||
<property name="position">5</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
|
||||
@@ -1,18 +1,20 @@
|
||||
import os
|
||||
|
||||
from gi.repository import GLib
|
||||
|
||||
from app.commons import run_idle, run_task
|
||||
from app.connections import download_data, DownloadType, upload_data
|
||||
from app.settings import Profile
|
||||
from app.settings import SettingsType
|
||||
from app.ui.backup import backup_data, restore_data
|
||||
from app.ui.main_helper import append_text_to_tview
|
||||
from app.ui.settings_dialog import show_settings_dialog
|
||||
from .uicommons import Gtk, UI_RESOURCES_PATH, TEXT_DOMAIN
|
||||
from .dialogs import show_dialog, DialogType, get_message
|
||||
from .uicommons import Gtk, UI_RESOURCES_PATH
|
||||
|
||||
|
||||
class DownloadDialog:
|
||||
def __init__(self, transient, settings, open_data_callback, update_settings_callback):
|
||||
self._profile = settings.profile
|
||||
self._s_type = settings.setting_type
|
||||
self._settings = settings
|
||||
self._open_data_callback = open_data_callback
|
||||
self._update_settings_callback = update_settings_callback
|
||||
@@ -20,11 +22,11 @@ class DownloadDialog:
|
||||
handlers = {"on_receive": self.on_receive,
|
||||
"on_send": self.on_send,
|
||||
"on_settings_button": self.on_settings_button,
|
||||
"on_preferences": self.on_preferences,
|
||||
"on_settings": self.on_settings,
|
||||
"on_profile_changed": self.on_profile_changed,
|
||||
"on_info_bar_close": self.on_info_bar_close}
|
||||
|
||||
builder = Gtk.Builder()
|
||||
builder.set_translation_domain(TEXT_DOMAIN)
|
||||
builder.add_from_file(UI_RESOURCES_PATH + "download_dialog.glade")
|
||||
builder.connect_signals(handlers)
|
||||
|
||||
@@ -35,7 +37,6 @@ class DownloadDialog:
|
||||
self._message_label = builder.get_object("info_bar_message_label")
|
||||
self._text_view = builder.get_object("text_view")
|
||||
self._expander = builder.get_object("expander")
|
||||
|
||||
self._host_entry = builder.get_object("host_entry")
|
||||
self._data_path_entry = builder.get_object("data_path_entry")
|
||||
self._remove_unused_check_button = builder.get_object("remove_unused_check_button")
|
||||
@@ -52,20 +53,33 @@ 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._profile_combo_box = builder.get_object("profile_combo_box")
|
||||
|
||||
self.init_settings()
|
||||
|
||||
def show(self):
|
||||
self._dialog_window.show()
|
||||
|
||||
def init_settings(self):
|
||||
self.update_profiles()
|
||||
self.init_ui_settings()
|
||||
|
||||
@run_idle
|
||||
def init_ui_settings(self):
|
||||
self._host_entry.set_text(self._settings.host)
|
||||
self._data_path_entry.set_text(self._settings.data_dir_path)
|
||||
is_enigma = self._profile is Profile.ENIGMA_2
|
||||
self._data_path_entry.set_text(self._settings.data_local_path)
|
||||
is_enigma = self._s_type is SettingsType.ENIGMA_2
|
||||
self._webtv_radio_button.set_visible(not is_enigma)
|
||||
self._http_radio_button.set_visible(is_enigma)
|
||||
self._use_http_box.set_visible(is_enigma)
|
||||
self._use_http_switch.set_active(is_enigma)
|
||||
|
||||
def update_profiles(self):
|
||||
self._profile_combo_box.remove_all()
|
||||
for p in self._settings.profiles:
|
||||
self._profile_combo_box.append(p, p)
|
||||
self._profile_combo_box.set_active_id(self._settings.current_profile)
|
||||
|
||||
@run_idle
|
||||
def on_receive(self, item):
|
||||
self.download(True, self.get_download_type())
|
||||
@@ -108,11 +122,11 @@ class DownloadDialog:
|
||||
self._timeout_entry.set_text("")
|
||||
self._current_property = label
|
||||
|
||||
def on_preferences(self, item):
|
||||
def on_settings(self, item):
|
||||
response = show_settings_dialog(self._dialog_window, self._settings)
|
||||
if response != Gtk.ResponseType.CANCEL:
|
||||
self._profile = self._settings.profile
|
||||
self.init_settings()
|
||||
self._s_type = self._settings.setting_type
|
||||
self.update_profiles()
|
||||
gen = self._update_settings_callback()
|
||||
GLib.idle_add(lambda: next(gen, False), priority=GLib.PRIORITY_LOW)
|
||||
|
||||
@@ -121,6 +135,13 @@ class DownloadDialog:
|
||||
self.on_settings_button(button)
|
||||
break
|
||||
|
||||
def on_profile_changed(self, box):
|
||||
active = box.get_active_text()
|
||||
if active in self._settings.profiles:
|
||||
self._settings.current_profile = active
|
||||
self._profile_combo_box.set_active_id(active)
|
||||
self.init_ui_settings()
|
||||
|
||||
def on_info_bar_close(self, bar=None, resp=None):
|
||||
self._info_bar.set_visible(False)
|
||||
|
||||
@@ -134,9 +155,11 @@ class DownloadDialog:
|
||||
try:
|
||||
if download:
|
||||
if backup and d_type is not DownloadType.SATELLITES:
|
||||
data_path = self._settings.data_dir_path or self._data_path_entry.get_text()
|
||||
backup_path = self._settings.backup_dir_path or data_path + "backup/"
|
||||
data_path = self._settings.data_local_path or self._data_path_entry.get_text()
|
||||
os.makedirs(os.path.dirname(data_path), exist_ok=True)
|
||||
backup_path = self._settings.backup_local_path or data_path + "backup/"
|
||||
backup_src = backup_data(data_path, backup_path, d_type is DownloadType.ALL)
|
||||
|
||||
download_data(settings=self._settings, download_type=d_type, callback=self.append_output)
|
||||
else:
|
||||
self.show_info_message(get_message("Please, wait..."), Gtk.MessageType.INFO)
|
||||
|
||||
@@ -483,7 +483,7 @@ class EpgDialog:
|
||||
# ***************** Options *********************#
|
||||
|
||||
def init_options(self):
|
||||
epg_dat_path = self._settings.data_dir_path + "epg/"
|
||||
epg_dat_path = self._settings.data_local_path + "epg/"
|
||||
self._epg_dat_path_entry.set_text(epg_dat_path)
|
||||
default_epg_data_stb_path = "/etc/enigma2"
|
||||
epg_options = self._settings.get("epg_options")
|
||||
|
||||
BIN
app/ui/icons/hicolor/96x96/apps/demon-editor.png
Normal file
BIN
app/ui/icons/hicolor/96x96/apps/demon-editor.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.2 KiB |
634
app/ui/icons/hicolor/scalable/apps/demon-editor.svg
Normal file
634
app/ui/icons/hicolor/scalable/apps/demon-editor.svg
Normal file
@@ -0,0 +1,634 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
id="svg1541"
|
||||
version="1.1"
|
||||
viewBox="0 0 16.743339 16.72816"
|
||||
height="63.224556"
|
||||
width="63.281971"
|
||||
sodipodi:docname="demon-editor.svg"
|
||||
inkscape:version="0.92.4 (unknown)">
|
||||
<sodipodi:namedview
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="1366"
|
||||
inkscape:window-height="716"
|
||||
id="namedview127"
|
||||
showgrid="true"
|
||||
fit-margin-left="0"
|
||||
inkscape:zoom="6.1714295"
|
||||
inkscape:cx="40.088627"
|
||||
inkscape:cy="31.742631"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg1541"
|
||||
fit-margin-top="0"
|
||||
fit-margin-right="0"
|
||||
fit-margin-bottom="0">
|
||||
<inkscape:grid
|
||||
type="xygrid"
|
||||
id="grid128"
|
||||
originx="-10.604603"
|
||||
originy="-1.1727724" />
|
||||
</sodipodi:namedview>
|
||||
<title
|
||||
id="title1088">DeamonEditor Icons</title>
|
||||
<defs
|
||||
id="defs1535">
|
||||
<linearGradient
|
||||
id="linearGradient2198">
|
||||
<stop
|
||||
id="stop2194"
|
||||
style="stop-color:#ffb320;stop-opacity:1"
|
||||
offset="0" />
|
||||
<stop
|
||||
id="stop2196"
|
||||
style="stop-color:#e7ff00;stop-opacity:1"
|
||||
offset="1" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient2192">
|
||||
<stop
|
||||
offset="0"
|
||||
style="stop-color:#ffb320;stop-opacity:1"
|
||||
id="stop2188" />
|
||||
<stop
|
||||
offset="1"
|
||||
style="stop-color:#b3c54c;stop-opacity:1"
|
||||
id="stop2190" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient3700-8">
|
||||
<stop
|
||||
offset="0"
|
||||
style="stop-color:#2e4f84;stop-opacity:1"
|
||||
id="stop2183" />
|
||||
<stop
|
||||
offset="1"
|
||||
style="stop-color:#4c77c5;stop-opacity:1"
|
||||
id="stop2185" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
x1="0"
|
||||
y1="0"
|
||||
x2="1"
|
||||
y2="0"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(13.526835,20.525875,20.525875,-13.526835,19.18986,1021.0543)"
|
||||
spreadMethod="pad"
|
||||
id="linearGradient1844">
|
||||
<stop
|
||||
style="stop-opacity:1;stop-color:#29282b"
|
||||
offset="0"
|
||||
id="stop1826" />
|
||||
<stop
|
||||
id="stop1828"
|
||||
offset="0.13293758"
|
||||
style="stop-color:#b5bdcf;stop-opacity:1" />
|
||||
<stop
|
||||
style="stop-opacity:1;stop-color:#92979f"
|
||||
offset="0.21261224"
|
||||
id="stop1832" />
|
||||
<stop
|
||||
style="stop-opacity:1;stop-color:#737881"
|
||||
offset="0.29780689"
|
||||
id="stop1834" />
|
||||
<stop
|
||||
style="stop-opacity:1;stop-color:#70757e"
|
||||
offset="0.29780689"
|
||||
id="stop1836" />
|
||||
<stop
|
||||
style="stop-opacity:1;stop-color:#e4e6e8"
|
||||
offset="0.45395693"
|
||||
id="stop1838" />
|
||||
<stop
|
||||
style="stop-opacity:1;stop-color:#696c76"
|
||||
offset="0.71871042"
|
||||
id="stop1840" />
|
||||
<stop
|
||||
style="stop-opacity:1;stop-color:#29282b"
|
||||
offset="1"
|
||||
id="stop1842" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient1754"
|
||||
spreadMethod="pad"
|
||||
gradientTransform="matrix(13.526835,20.525875,20.525875,-13.526835,19.18986,1021.0543)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
y2="0"
|
||||
x2="1"
|
||||
y1="0"
|
||||
x1="0">
|
||||
<stop
|
||||
id="stop1736"
|
||||
offset="0"
|
||||
style="stop-opacity:1;stop-color:#29282b" />
|
||||
<stop
|
||||
style="stop-color:#b5bdcf;stop-opacity:1"
|
||||
offset="0.02582242"
|
||||
id="stop1738" />
|
||||
<stop
|
||||
id="stop1740"
|
||||
offset="0.14669065"
|
||||
style="stop-opacity:1;stop-color:#868c95" />
|
||||
<stop
|
||||
id="stop1742"
|
||||
offset="0.21261224"
|
||||
style="stop-opacity:1;stop-color:#92979f" />
|
||||
<stop
|
||||
id="stop1744"
|
||||
offset="0.29780689"
|
||||
style="stop-opacity:1;stop-color:#737881" />
|
||||
<stop
|
||||
id="stop1746"
|
||||
offset="0.29780689"
|
||||
style="stop-opacity:1;stop-color:#70757e" />
|
||||
<stop
|
||||
id="stop1748"
|
||||
offset="0.45395693"
|
||||
style="stop-opacity:1;stop-color:#e4e6e8" />
|
||||
<stop
|
||||
id="stop1750"
|
||||
offset="0.71871042"
|
||||
style="stop-opacity:1;stop-color:#ffffff" />
|
||||
<stop
|
||||
id="stop1752"
|
||||
offset="1"
|
||||
style="stop-opacity:1;stop-color:#1d191a" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
x1="0"
|
||||
y1="0"
|
||||
x2="1"
|
||||
y2="0"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(13.526835,20.525875,20.525875,-13.526835,19.18986,1021.0543)"
|
||||
spreadMethod="pad"
|
||||
id="linearGradient1606">
|
||||
<stop
|
||||
style="stop-opacity:1;stop-color:#29282b"
|
||||
offset="0"
|
||||
id="stop1590" />
|
||||
<stop
|
||||
id="stop1608"
|
||||
offset="0.03065561"
|
||||
style="stop-color:#b5bdcf;stop-opacity:1" />
|
||||
<stop
|
||||
style="stop-opacity:1;stop-color:#868c95"
|
||||
offset="0.1125849"
|
||||
id="stop1592" />
|
||||
<stop
|
||||
style="stop-opacity:1;stop-color:#92979f"
|
||||
offset="0.13955873"
|
||||
id="stop1594" />
|
||||
<stop
|
||||
style="stop-opacity:1;stop-color:#737881"
|
||||
offset="0.1915853"
|
||||
id="stop1596" />
|
||||
<stop
|
||||
style="stop-opacity:1;stop-color:#70757e"
|
||||
offset="0.25400829"
|
||||
id="stop1598" />
|
||||
<stop
|
||||
style="stop-opacity:1;stop-color:#e4e6e8"
|
||||
offset="0.45395693"
|
||||
id="stop1600" />
|
||||
<stop
|
||||
style="stop-opacity:1;stop-color:#ffffff"
|
||||
offset="0.71871042"
|
||||
id="stop1602" />
|
||||
<stop
|
||||
style="stop-opacity:1;stop-color:#1d191a"
|
||||
offset="1"
|
||||
id="stop1604" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient886-0">
|
||||
<stop
|
||||
style="stop-color:#535353;stop-opacity:1"
|
||||
offset="0"
|
||||
id="stop888" />
|
||||
<stop
|
||||
style="stop-color:#f0f0f0;stop-opacity:1"
|
||||
offset="1"
|
||||
id="stop890" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient1473">
|
||||
<stop
|
||||
id="stop1469"
|
||||
offset="0"
|
||||
style="stop-color:#ffffff;stop-opacity:0" />
|
||||
<stop
|
||||
id="stop1471"
|
||||
offset="1"
|
||||
style="stop-color:#ffffff;stop-opacity:0.96088022" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient2447-35">
|
||||
<stop
|
||||
id="stop2449"
|
||||
offset="0"
|
||||
style="stop-color:#00c62e;stop-opacity:1" />
|
||||
<stop
|
||||
id="stop2451"
|
||||
offset="1"
|
||||
style="stop-color:#136100;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient3795-1">
|
||||
<stop
|
||||
id="stop3797-1"
|
||||
offset="0"
|
||||
style="stop-color:#803400;stop-opacity:1" />
|
||||
<stop
|
||||
id="stop3799-3"
|
||||
offset="1"
|
||||
style="stop-color:#c87137;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient2447-3">
|
||||
<stop
|
||||
style="stop-color:#c60300;stop-opacity:1"
|
||||
offset="0"
|
||||
id="stop2443" />
|
||||
<stop
|
||||
style="stop-color:#c40e00;stop-opacity:1"
|
||||
offset="1"
|
||||
id="stop2445" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient2458">
|
||||
<stop
|
||||
id="stop2454"
|
||||
offset="0"
|
||||
style="stop-color:#c60300;stop-opacity:1" />
|
||||
<stop
|
||||
id="stop2456"
|
||||
offset="1"
|
||||
style="stop-color:#ee6000;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient3519">
|
||||
<stop
|
||||
id="stop3521"
|
||||
offset="0"
|
||||
style="stop-color:#1d2120;stop-opacity:1" />
|
||||
<stop
|
||||
id="stop3523"
|
||||
offset="1"
|
||||
style="stop-color:#545d5d;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient1446">
|
||||
<stop
|
||||
id="stop1442"
|
||||
offset="0"
|
||||
style="stop-color:#ffffff;stop-opacity:1;" />
|
||||
<stop
|
||||
id="stop1444"
|
||||
offset="1"
|
||||
style="stop-color:#ffffff;stop-opacity:0;" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient1547"
|
||||
spreadMethod="pad"
|
||||
gradientTransform="matrix(13.526835,20.525875,20.525875,-13.526835,19.18986,1021.0543)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
y2="0"
|
||||
x2="1"
|
||||
y1="0"
|
||||
x1="0">
|
||||
<stop
|
||||
id="stop1529"
|
||||
offset="0"
|
||||
style="stop-opacity:1;stop-color:#ffffff" />
|
||||
<stop
|
||||
id="stop1531"
|
||||
offset="0.0263736"
|
||||
style="stop-opacity:1;stop-color:#29282b" />
|
||||
<stop
|
||||
id="stop1533"
|
||||
offset="0.263736"
|
||||
style="stop-opacity:1;stop-color:#868c95" />
|
||||
<stop
|
||||
id="stop1535"
|
||||
offset="0.395604"
|
||||
style="stop-opacity:1;stop-color:#92979f" />
|
||||
<stop
|
||||
id="stop1537"
|
||||
offset="0.39560401"
|
||||
style="stop-opacity:1;stop-color:#737881" />
|
||||
<stop
|
||||
id="stop1539"
|
||||
offset="0.42333773"
|
||||
style="stop-opacity:1;stop-color:#70757e" />
|
||||
<stop
|
||||
id="stop1541"
|
||||
offset="0.56268591"
|
||||
style="stop-opacity:1;stop-color:#e4e6e8" />
|
||||
<stop
|
||||
id="stop1543"
|
||||
offset="0.62400264"
|
||||
style="stop-opacity:1;stop-color:#ffffff" />
|
||||
<stop
|
||||
id="stop1545"
|
||||
offset="1"
|
||||
style="stop-opacity:1;stop-color:#1d191a" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient1527"
|
||||
spreadMethod="pad"
|
||||
gradientTransform="matrix(13.526835,20.525875,20.525875,-13.526835,19.18986,1021.0543)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
y2="0"
|
||||
x2="1"
|
||||
y1="0"
|
||||
x1="0">
|
||||
<stop
|
||||
id="stop1509"
|
||||
offset="0"
|
||||
style="stop-opacity:1;stop-color:#ffffff" />
|
||||
<stop
|
||||
id="stop1511"
|
||||
offset="0.0527472"
|
||||
style="stop-opacity:1;stop-color:#29282b" />
|
||||
<stop
|
||||
id="stop1513"
|
||||
offset="0.142147"
|
||||
style="stop-opacity:1;stop-color:#868c95" />
|
||||
<stop
|
||||
id="stop1515"
|
||||
offset="0.19700864"
|
||||
style="stop-opacity:1;stop-color:#92979f" />
|
||||
<stop
|
||||
id="stop1517"
|
||||
offset="0.25031137"
|
||||
style="stop-opacity:1;stop-color:#737881" />
|
||||
<stop
|
||||
id="stop1519"
|
||||
offset="0.3710371"
|
||||
style="stop-opacity:1;stop-color:#70757e" />
|
||||
<stop
|
||||
id="stop1521"
|
||||
offset="0.53961843"
|
||||
style="stop-opacity:1;stop-color:#e4e6e8" />
|
||||
<stop
|
||||
id="stop1523"
|
||||
offset="0.76283824"
|
||||
style="stop-opacity:1;stop-color:#ffffff" />
|
||||
<stop
|
||||
id="stop1525"
|
||||
offset="1"
|
||||
style="stop-opacity:1;stop-color:#1d191a" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
gradientTransform="matrix(0.26458333,0,0,0.26458333,-5.8254634,-78.732754)"
|
||||
y2="1179.7145"
|
||||
x2="66.791626"
|
||||
y1="1188.7661"
|
||||
x1="99.044022"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="linearGradient1485"
|
||||
xlink:href="#linearGradient1473" />
|
||||
<linearGradient
|
||||
gradientTransform="matrix(0.26458333,0,0,0.26458333,-2.2733744,-70.526334)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
y2="1155.8046"
|
||||
x2="67.311417"
|
||||
y1="1161.6112"
|
||||
x1="77.19442"
|
||||
id="linearGradient1448"
|
||||
xlink:href="#linearGradient1446" />
|
||||
<linearGradient
|
||||
gradientTransform="matrix(0.26458333,0,0,0.26458333,-18.198747,-79.859424)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
y2="1186.8096"
|
||||
x2="146.16808"
|
||||
y1="1186.8096"
|
||||
x1="131.86871"
|
||||
id="linearGradient2180"
|
||||
xlink:href="#linearGradient886-0" />
|
||||
<linearGradient
|
||||
gradientTransform="matrix(0.20863982,0,0,0.20863982,-0.63935937,-13.031239)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
y2="1184.73"
|
||||
x2="101.19952"
|
||||
y1="1184.73"
|
||||
x1="83.066002"
|
||||
id="linearGradient2160"
|
||||
xlink:href="#linearGradient886-0" />
|
||||
<linearGradient
|
||||
gradientTransform="matrix(0.26458333,0,0,0.26458333,-6.2370714,-102.12414)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
y2="1267.1335"
|
||||
x2="117.99127"
|
||||
y1="1267.1335"
|
||||
x1="75.853806"
|
||||
id="linearGradient2131"
|
||||
xlink:href="#linearGradient886-0" />
|
||||
<linearGradient
|
||||
id="linearGradient3700-8-3">
|
||||
<stop
|
||||
id="stop3702-1"
|
||||
style="stop-color:#2e4f84;stop-opacity:1"
|
||||
offset="0" />
|
||||
<stop
|
||||
id="stop3704-8"
|
||||
style="stop-color:#4c77c5;stop-opacity:1"
|
||||
offset="1" />
|
||||
</linearGradient>
|
||||
<defs
|
||||
id="defs4922">
|
||||
<filter
|
||||
style="color-interpolation-filters:sRGB"
|
||||
id="Adobe_OpacityMaskFilter"
|
||||
filterUnits="userSpaceOnUse"
|
||||
x="3.7850001"
|
||||
y="4.6750002"
|
||||
width="5.8829999"
|
||||
height="73.013">
|
||||
<feColorMatrix
|
||||
type="matrix"
|
||||
values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 1 0"
|
||||
id="feColorMatrix4925" />
|
||||
</filter>
|
||||
</defs>
|
||||
<mask
|
||||
maskUnits="userSpaceOnUse"
|
||||
x="3.785"
|
||||
y="4.675"
|
||||
width="5.883"
|
||||
height="73.013"
|
||||
id="SVGID_2_">
|
||||
<g
|
||||
style="filter:url(#Adobe_OpacityMaskFilter)"
|
||||
id="g4928">
|
||||
<linearGradient
|
||||
id="SVGID_3_"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="3.7852001"
|
||||
y1="41.181198"
|
||||
x2="9.6680002"
|
||||
y2="41.181198">
|
||||
<stop
|
||||
offset="0"
|
||||
style="stop-color:#FFFFFF"
|
||||
id="stop4931" />
|
||||
<stop
|
||||
offset="0.0029"
|
||||
style="stop-color:#FAFBFB"
|
||||
id="stop4933" />
|
||||
<stop
|
||||
offset="0.0756"
|
||||
style="stop-color:#BBBDBF"
|
||||
id="stop4935" />
|
||||
<stop
|
||||
offset="0.1438"
|
||||
style="stop-color:#898B8E"
|
||||
id="stop4937" />
|
||||
<stop
|
||||
offset="0.2053"
|
||||
style="stop-color:#646567"
|
||||
id="stop4939" />
|
||||
<stop
|
||||
offset="0.259"
|
||||
style="stop-color:#444446"
|
||||
id="stop4941" />
|
||||
<stop
|
||||
offset="0.3028"
|
||||
style="stop-color:#1D1C1D"
|
||||
id="stop4943" />
|
||||
<stop
|
||||
offset="0.3313"
|
||||
style="stop-color:#000000"
|
||||
id="stop4945" />
|
||||
</linearGradient>
|
||||
<rect
|
||||
style="fill:url(#SVGID_3_)"
|
||||
x="3.7850001"
|
||||
y="4.6750002"
|
||||
width="5.8829999"
|
||||
height="73.013"
|
||||
id="rect4947" />
|
||||
</g>
|
||||
</mask>
|
||||
<filter
|
||||
style="color-interpolation-filters:sRGB"
|
||||
id="filter1268"
|
||||
filterUnits="userSpaceOnUse"
|
||||
x="3.7850001"
|
||||
y="4.6750002"
|
||||
width="5.8829999"
|
||||
height="73.013">
|
||||
<feColorMatrix
|
||||
type="matrix"
|
||||
values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 1 0"
|
||||
id="feColorMatrix1266" />
|
||||
</filter>
|
||||
<linearGradient
|
||||
gradientUnits="userSpaceOnUse"
|
||||
y2="203.22046"
|
||||
x2="23.551136"
|
||||
y1="203.22046"
|
||||
x1="13.487289"
|
||||
id="linearGradient1160"
|
||||
xlink:href="#linearGradient3519" />
|
||||
<clipPath
|
||||
id="clipPath1890"
|
||||
clipPathUnits="userSpaceOnUse">
|
||||
<circle
|
||||
style="opacity:1;fill:#2d2d2d;fill-opacity:1;stroke:#434242;stroke-width:0.0575568;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="circle1892"
|
||||
cx="78.548424"
|
||||
cy="-31.019459"
|
||||
r="6.4721422"
|
||||
transform="scale(1,-1)" />
|
||||
</clipPath>
|
||||
<linearGradient
|
||||
gradientTransform="translate(-0.24389927,14.877856)"
|
||||
y2="27.314217"
|
||||
x2="84.864914"
|
||||
y1="27.314217"
|
||||
x1="72.164909"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="linearGradient2134"
|
||||
xlink:href="#linearGradient3700-8-3" />
|
||||
</defs>
|
||||
<metadata
|
||||
id="metadata1538">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title>DeamonEditor Icons</dc:title>
|
||||
<dc:publisher>
|
||||
<cc:Agent>
|
||||
<dc:title>mfgeg</dc:title>
|
||||
</cc:Agent>
|
||||
</dc:publisher>
|
||||
<dc:date>7.1.2020</dc:date>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
transform="matrix(1.1690805,0,0,1.1690805,-14.929261,-167.45253)"
|
||||
id="layer1">
|
||||
<g
|
||||
transform="translate(0.86526724,-82.691658)"
|
||||
id="g1351">
|
||||
<path
|
||||
d="m 13.715358,227.65981 h 2.97616 v 12.57332 h -2.97616 z m 5.79826,-1.73376 -0.327076,0.42227 c -0.179879,0.23226 -0.586303,0.67932 -0.903223,0.99412 -0.480677,0.47726 -0.640897,0.57235 -0.967777,0.57235 -0.223557,0 -0.380895,-0.0584 -0.624024,-0.25067 v 3.18307 c 0.172884,-0.0214 0.359647,-0.0333 0.575071,-0.0352 0.68403,-0.006 0.91815,0.0416 1.436333,0.29581 1.14586,0.56274 1.762492,1.5589 1.768251,2.8566 0.0094,2.10836 -1.870896,3.55161 -3.779655,3.16529 v 3.10345 c 4.345352,0.0758 4.093104,-2.37537 6.573781,-2.25837 1.486139,0.0748 1.333421,0.16555 1.796225,-1.064 l 0.259834,-0.6913 -0.803704,-0.66493 c -0.960855,-0.79478 -1.303983,-1.29079 -1.205013,-1.74132 0.06631,-0.30189 1.20818,-1.50093 1.779011,-1.86778 0.240181,-0.1543 0.244255,-0.17878 0.09575,-0.59661 -0.08522,-0.23979 -0.259522,-0.65746 -0.386789,-0.92744 l -0.23132,-0.49115 h -1.412666 c -1.868582,0 -1.802386,0.068 -1.805368,-1.82472 l -0.0021,-1.43582 -0.917745,-0.37171 z"
|
||||
style="opacity:1;fill:#000000;fill-opacity:0.5372549;stroke:none;stroke-width:0.18460207;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.5012225"
|
||||
id="path2133"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
id="path2106"
|
||||
style="opacity:1;fill:url(#linearGradient2131);fill-opacity:1;stroke:none;stroke-width:0.17733108;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.5012225"
|
||||
d="m 13.832581,227.93117 h 2.858936 v 12.07807 h -2.858936 z m 5.569881,-1.6655 -0.314194,0.40566 c -0.172793,0.22309 -0.563209,0.65257 -0.867647,0.95496 -0.46174,0.45847 -0.615654,0.5498 -0.929658,0.5498 -0.214752,0 -0.365893,-0.056 -0.599446,-0.24079 v 3.05768 c 0.166077,-0.0206 0.345483,-0.0319 0.55242,-0.0338 0.657089,-0.006 0.881987,0.0399 1.379764,0.28416 1.100724,0.54057 1.693073,1.49747 1.698606,2.74408 0.009,2.02535 -1.797211,3.41174 -3.63079,3.04061 v 2.98122 c 4.174205,0.0728 3.931888,-2.28179 6.314859,-2.1694 1.427604,0.0718 1.2809,0.15902 1.725477,-1.02213 l 0.249597,-0.66408 -0.772046,-0.63871 c -0.923009,-0.76345 -1.252622,-1.23996 -1.157552,-1.67277 0.0637,-0.29001 1.160592,-1.44177 1.708939,-1.79419 0.230721,-0.14825 0.234635,-0.17174 0.09198,-0.57312 -0.08187,-0.23032 -0.249296,-0.63156 -0.371555,-0.89088 l -0.222207,-0.4718 h -1.357022 c -1.794983,0 -1.731396,0.0653 -1.734262,-1.75284 l -0.0021,-1.3793 -0.881603,-0.35705 z"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
id="path2151"
|
||||
d="m 17.266983,230.95755 c -0.215637,0.003 -0.402408,0.014 -0.575465,0.0354 v 6.28854 c 1.910653,0.38671 3.792716,-1.05815 3.783336,-3.16865 -0.0058,-1.29897 -0.623064,-2.29597 -1.770059,-2.85927 -0.518699,-0.25451 -0.753103,-0.30234 -1.437812,-0.29607 z m 0.482341,1.17002 c 0.433747,-0.004 0.582202,0.0265 0.910784,0.18761 0.726595,0.35682 1.117604,0.98848 1.121256,1.81134 0.0059,1.33694 -1.186343,2.25211 -2.396695,2.00715 v -3.98359 c 0.109628,-0.0135 0.228054,-0.0214 0.364655,-0.0225 z"
|
||||
style="opacity:1;fill:url(#linearGradient2160);fill-opacity:1;stroke:none;stroke-width:0.18478528;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.5012225"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
style="opacity:1;fill:none;fill-opacity:1;stroke:url(#linearGradient2180);stroke-width:0.18478528;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.5012225"
|
||||
d="m 17.266981,230.95753 c -0.215636,0.003 -0.402408,0.014 -0.575464,0.0354 v 6.28853 c 1.910652,0.38672 3.792715,-1.05815 3.783336,-3.16865 -0.0057,-1.29897 -0.623065,-2.29597 -1.77006,-2.85927 -0.518697,-0.2545 -0.753103,-0.30234 -1.437812,-0.29607 z m 0.482343,1.17001 c 0.433747,-0.004 0.5822,0.0265 0.910783,0.18762 0.726594,0.35681 1.117605,0.98848 1.121257,1.81133 0.006,1.33694 -1.186344,2.25211 -2.396697,2.00716 v -3.98359 c 0.109628,-0.0135 0.228055,-0.0214 0.364657,-0.0225 z"
|
||||
id="path2170"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
id="path1422"
|
||||
d="m 17.266983,230.95755 c -0.215637,0.003 -0.402408,0.014 -0.575464,0.0354 v 6.28854 c 1.910652,0.38671 3.792715,-1.05815 3.783335,-3.16865 -0.0057,-1.29897 -0.623064,-2.29597 -1.770059,-2.85927 -0.518699,-0.25451 -0.753103,-0.30234 -1.437812,-0.29607 z m 0.482342,1.17002 c 0.433748,-0.004 0.582201,0.0265 0.910784,0.18761 0.726594,0.35682 1.117605,0.98848 1.121257,1.81134 0.006,1.33694 -1.186344,2.25211 -2.396697,2.00715 v -3.98359 c 0.109628,-0.0135 0.228054,-0.0214 0.364656,-0.0225 z"
|
||||
style="opacity:1;fill:none;fill-opacity:1;stroke:url(#linearGradient1448);stroke-width:0.1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
id="path1454"
|
||||
d="m 19.402462,226.26149 -0.314194,0.40566 c -0.172793,0.22309 -0.563209,0.65259 -0.867644,0.95499 -0.461741,0.45847 -0.615657,0.54983 -0.929661,0.54983 -0.214752,0 -0.365893,-0.056 -0.599446,-0.2408 v -0.004 h -2.858741 v 12.0783 h 2.858741 c 4.174205,0.0728 3.931888,-2.28177 6.314861,-2.16937 1.427604,0.0718 1.280898,0.15899 1.725475,-1.02217 l 0.249597,-0.66402 -0.772046,-0.63873 c -0.923009,-0.76346 -1.252622,-1.23997 -1.157552,-1.67278 0.0637,-0.29001 1.160595,-1.44176 1.708939,-1.79419 0.230721,-0.14825 0.234635,-0.17171 0.09198,-0.57309 -0.08187,-0.23032 -0.249296,-0.63158 -0.371555,-0.8909 l -0.222207,-0.47181 h -1.357025 c -1.794983,0 -1.731393,0.0653 -1.734259,-1.75286 l -0.0021,-1.37925 -0.8816,-0.35708 z"
|
||||
style="opacity:1;fill:none;fill-opacity:1;stroke:url(#linearGradient1485);stroke-width:0.1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
inkscape:connector-curvature="0" />
|
||||
</g>
|
||||
<g
|
||||
transform="translate(63.49728,-55.136805)"
|
||||
id="g1174" />
|
||||
<g
|
||||
transform="translate(50.48327,11.624037)"
|
||||
id="g1812" />
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 22 KiB |
@@ -6,7 +6,7 @@ from app.eparser import get_bouquets, get_services
|
||||
from app.eparser.ecommons import BqType, BqServiceType, Bouquet
|
||||
from app.eparser.enigma.bouquets import get_bouquet
|
||||
from app.eparser.neutrino.bouquets import parse_webtv, parse_bouquets as get_neutrino_bouquets
|
||||
from app.settings import Profile
|
||||
from app.settings import SettingsType
|
||||
from app.ui.dialogs import show_dialog, DialogType, get_chooser_dialog, get_message
|
||||
from app.ui.main_helper import on_popup_menu
|
||||
from .uicommons import Gtk, UI_RESOURCES_PATH, KeyboardKey, Column
|
||||
@@ -17,12 +17,12 @@ def import_bouquet(transient, model, path, settings, services, appender):
|
||||
itr = model.get_iter(path)
|
||||
bq_type = BqType(model.get(itr, Column.BQ_TYPE)[0])
|
||||
pattern, f_pattern = None, None
|
||||
profile = settings.profile
|
||||
profile = settings.setting_type
|
||||
|
||||
if profile is Profile.ENIGMA_2:
|
||||
if profile is SettingsType.ENIGMA_2:
|
||||
pattern = ".{}".format(bq_type.value)
|
||||
f_pattern = "userbouquet.*{}".format(pattern)
|
||||
elif profile is Profile.NEUTRINO_MP:
|
||||
elif profile is SettingsType.NEUTRINO_MP:
|
||||
pattern = "webtv.xml" if bq_type is BqType.WEBTV else "bouquets.xml"
|
||||
f_pattern = "bouquets.xml"
|
||||
if bq_type is BqType.TV:
|
||||
@@ -38,7 +38,7 @@ def import_bouquet(transient, model, path, settings, services, appender):
|
||||
show_dialog(DialogType.ERROR, transient, text="No bouquet file is selected!")
|
||||
return
|
||||
|
||||
if profile is Profile.ENIGMA_2:
|
||||
if profile is SettingsType.ENIGMA_2:
|
||||
bq = get_enigma2_bouquet(file_path)
|
||||
imported = list(filter(lambda x: x.data in services or x.type is BqServiceType.IPTV, bq.services))
|
||||
|
||||
@@ -51,7 +51,7 @@ def import_bouquet(transient, model, path, settings, services, appender):
|
||||
else:
|
||||
p_itr = model.iter_parent(itr)
|
||||
appender(bq, p_itr) if p_itr else appender(bq, itr)
|
||||
elif profile is Profile.NEUTRINO_MP:
|
||||
elif profile is SettingsType.NEUTRINO_MP:
|
||||
if bq_type is BqType.WEBTV:
|
||||
bqs = parse_webtv(file_path, "WEBTV", bq_type.value)
|
||||
else:
|
||||
@@ -90,7 +90,7 @@ class ImportDialog:
|
||||
self._services = {}
|
||||
self._service_ids = service_ids
|
||||
self._append = appender
|
||||
self._profile = settings.profile
|
||||
self._profile = settings.setting_type
|
||||
self._settings = settings
|
||||
self._bouquets = bouquets
|
||||
|
||||
@@ -125,7 +125,7 @@ class ImportDialog:
|
||||
self._main_model.append((bq.name, bq.type, True))
|
||||
self._bq_services[(bq.name, bq.type)] = bq.services
|
||||
# Note! Getting default format ver. 4
|
||||
services = get_services(path, self._profile, 4 if self._profile is Profile.ENIGMA_2 else 0)
|
||||
services = get_services(path, self._profile, 4 if self._profile is SettingsType.ENIGMA_2 else 0)
|
||||
for srv in services:
|
||||
self._services[srv.fav_id] = srv
|
||||
except FileNotFoundError as e:
|
||||
|
||||
@@ -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.settings import Profile
|
||||
from app.settings import SettingsType
|
||||
from app.tools.yt import YouTube, PlayListParser
|
||||
from .dialogs import Action, show_dialog, DialogType, get_dialogs_string, get_message
|
||||
from .main_helper import get_base_model, get_iptv_url, on_popup_menu
|
||||
@@ -42,7 +42,7 @@ def get_stream_type(box):
|
||||
|
||||
class IptvDialog:
|
||||
|
||||
def __init__(self, transient, view, services, bouquet, profile=Profile.ENIGMA_2, action=Action.ADD):
|
||||
def __init__(self, transient, view, services, bouquet, profile=SettingsType.ENIGMA_2, action=Action.ADD):
|
||||
handlers = {"on_response": self.on_response,
|
||||
"on_entry_changed": self.on_entry_changed,
|
||||
"on_url_changed": self.on_url_changed,
|
||||
@@ -90,7 +90,7 @@ class IptvDialog:
|
||||
for el in self._digit_elems:
|
||||
el.get_style_context().add_provider_for_screen(Gdk.Screen.get_default(), self._style_provider,
|
||||
Gtk.STYLE_PROVIDER_PRIORITY_USER)
|
||||
if profile is Profile.NEUTRINO_MP:
|
||||
if profile is SettingsType.NEUTRINO_MP:
|
||||
builder.get_object("iptv_dialog_ts_data_frame").set_visible(False)
|
||||
builder.get_object("iptv_type_label").set_visible(False)
|
||||
builder.get_object("reference_entry").set_visible(False)
|
||||
@@ -103,7 +103,7 @@ class IptvDialog:
|
||||
if self._action is Action.ADD:
|
||||
self._save_button.set_visible(False)
|
||||
self._add_button.set_visible(True)
|
||||
if self._profile is Profile.ENIGMA_2:
|
||||
if self._profile is SettingsType.ENIGMA_2:
|
||||
self._update_reference_entry()
|
||||
self._stream_type_combobox.set_active(1)
|
||||
elif self._action is Action.EDIT:
|
||||
@@ -128,13 +128,13 @@ class IptvDialog:
|
||||
if show_dialog(DialogType.QUESTION, self._dialog) == Gtk.ResponseType.CANCEL:
|
||||
return
|
||||
|
||||
self.save_enigma2_data() if self._profile is Profile.ENIGMA_2 else self.save_neutrino_data()
|
||||
self.save_enigma2_data() if self._profile is SettingsType.ENIGMA_2 else self.save_neutrino_data()
|
||||
self._dialog.destroy()
|
||||
|
||||
def init_data(self, srv):
|
||||
name, fav_id = srv[2], srv[7]
|
||||
self._name_entry.set_text(name)
|
||||
self.init_enigma2_data(fav_id) if self._profile is Profile.ENIGMA_2 else self.init_neutrino_data(fav_id)
|
||||
self.init_enigma2_data(fav_id) if self._profile is SettingsType.ENIGMA_2 else self.init_neutrino_data(fav_id)
|
||||
|
||||
def init_enigma2_data(self, fav_id):
|
||||
data, sep, desc = fav_id.partition("#DESCRIPTION")
|
||||
@@ -171,7 +171,7 @@ class IptvDialog:
|
||||
self._description_entry.set_text(data[1])
|
||||
|
||||
def _update_reference_entry(self):
|
||||
if self._profile is Profile.ENIGMA_2:
|
||||
if self._profile is SettingsType.ENIGMA_2:
|
||||
self._reference_entry.set_text(_ENIGMA2_REFERENCE.format(self.get_type(),
|
||||
self._srv_type_entry.get_text(),
|
||||
int(self._sid_entry.get_text()),
|
||||
@@ -486,7 +486,7 @@ class IptvListConfigurationDialog:
|
||||
show_dialog(DialogType.ERROR, self._dialog, "Error. Verify the data!")
|
||||
return
|
||||
|
||||
if self._profile is Profile.ENIGMA_2:
|
||||
if self._profile is SettingsType.ENIGMA_2:
|
||||
reset = self._reset_to_default_switch.get_active()
|
||||
type_default = self._type_check_button.get_active()
|
||||
tid_default = self._tid_check_button.get_active()
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import os
|
||||
import sys
|
||||
|
||||
from contextlib import suppress
|
||||
from datetime import datetime
|
||||
from functools import lru_cache
|
||||
from itertools import chain
|
||||
|
||||
@@ -9,33 +9,33 @@ from gi.repository import GLib, Gio
|
||||
|
||||
from app.commons import run_idle, log, run_task, run_with_delay, init_logger
|
||||
from app.connections import HttpAPI, HttpRequestType, download_data, DownloadType, upload_data, test_http, \
|
||||
TestException
|
||||
TestException, HttpApiException
|
||||
from app.eparser import get_blacklist, write_blacklist, parse_m3u
|
||||
from app.eparser import get_services, get_bouquets, write_bouquets, write_services, Bouquets, Bouquet, Service
|
||||
from app.eparser.ecommons import CAS, Flag, BouquetService
|
||||
from app.eparser.enigma.bouquets import BqServiceType
|
||||
from app.eparser.iptv import export_to_m3u
|
||||
from app.eparser.neutrino.bouquets import BqType
|
||||
from app.settings import Profile, Settings
|
||||
from app.settings import SettingsType, Settings, SettingsException
|
||||
from app.tools.media import Player
|
||||
from app.ui.epg_dialog import EpgDialog
|
||||
from app.ui.transmitter import LinksTransmitter
|
||||
from .backup import BackupDialog, backup_data, clear_data_path
|
||||
from .imports import ImportDialog, import_bouquet
|
||||
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, \
|
||||
FavClickMode
|
||||
from .dialogs import show_dialog, DialogType, get_chooser_dialog, WaitDialog, get_message
|
||||
from .download_dialog import DownloadDialog
|
||||
from .imports import ImportDialog, import_bouquet
|
||||
from .iptv import IptvDialog, SearchUnavailableDialog, IptvListConfigurationDialog, YtListImportDialog
|
||||
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, \
|
||||
is_only_one_item_selected, gen_bouquets, BqGenType, get_iptv_url, append_picons, get_selection, get_model_data, \
|
||||
remove_all_unused_picons
|
||||
from .picons_downloader import PiconsDialog
|
||||
from .satellites_dialog import show_satellites_dialog
|
||||
from .settings_dialog import show_settings_dialog
|
||||
from .search import SearchProvider
|
||||
from .service_details_dialog import ServiceDetailsDialog, Action
|
||||
from .settings_dialog import show_settings_dialog
|
||||
from .uicommons import Gtk, Gdk, UI_RESOURCES_PATH, LOCKED_ICON, HIDE_ICON, IPTV_ICON, MOVE_KEYS, KeyboardKey, Column, \
|
||||
FavClickMode
|
||||
|
||||
|
||||
class Application(Gtk.Application):
|
||||
@@ -152,8 +152,8 @@ class Application(Gtk.Application):
|
||||
"on_create_bouquet_for_each_type": self.on_create_bouquet_for_each_type}
|
||||
|
||||
self._settings = Settings.get_instance()
|
||||
self._profile = self._settings.profile
|
||||
os.makedirs(os.path.dirname(self._settings.data_dir_path), exist_ok=True)
|
||||
self._s_type = self._settings.setting_type
|
||||
os.makedirs(os.path.dirname(self._settings.data_local_path), exist_ok=True)
|
||||
# Used for copy/paste. When adding the previous data will not be deleted.
|
||||
# Clearing only after the insertion!
|
||||
self._rows_buffer = []
|
||||
@@ -182,7 +182,6 @@ class Application(Gtk.Application):
|
||||
self._EXTRA_COLOR = None # Color for services with a extra name for the bouquet
|
||||
|
||||
builder = Gtk.Builder()
|
||||
builder.set_translation_domain("demon-editor")
|
||||
builder.add_from_file(UI_RESOURCES_PATH + "main_window.glade")
|
||||
builder.connect_signals(self._handlers)
|
||||
self._main_window = builder.get_object("main_window")
|
||||
@@ -210,8 +209,7 @@ class Application(Gtk.Application):
|
||||
self._app_info_box.bind_property("visible", builder.get_object("main_paned"), "visible", 4)
|
||||
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._settings.host)
|
||||
self._profile_combo_box = builder.get_object("profile_combo_box")
|
||||
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")
|
||||
@@ -226,6 +224,7 @@ class Application(Gtk.Application):
|
||||
self._radio_count_label = builder.get_object("radio_count_label")
|
||||
self._data_count_label = builder.get_object("data_count_label")
|
||||
self._receiver_info_box.bind_property("visible", self._http_status_image, "visible", 4)
|
||||
self._receiver_info_box.bind_property("visible", builder.get_object("signal_box"), "visible")
|
||||
# Force ctrl press event for view. Multiple selections in lists only with Space key(as in file managers)!!!
|
||||
self._services_view.connect("key-press-event", self.force_ctrl)
|
||||
self._fav_view.connect("key-press-event", self.force_ctrl)
|
||||
@@ -323,7 +322,15 @@ class Application(Gtk.Application):
|
||||
self.update_profile_label()
|
||||
self.init_drag_and_drop()
|
||||
self.init_colors()
|
||||
self.init_http_api()
|
||||
if self._settings.load_last_config:
|
||||
config = self._settings.get("last_config") or {}
|
||||
self.init_profiles(config.get("last_profile", None))
|
||||
last_bouquet = config.get("last_bouquet", None)
|
||||
self.open_data(callback=lambda: self.open_bouquet(last_bouquet))
|
||||
else:
|
||||
self.init_profiles()
|
||||
gen = self.init_http_api()
|
||||
GLib.idle_add(lambda: next(gen, False), priority=GLib.PRIORITY_LOW)
|
||||
|
||||
def do_activate(self):
|
||||
self._main_window.set_application(self)
|
||||
@@ -332,9 +339,17 @@ class Application(Gtk.Application):
|
||||
|
||||
def do_shutdown(self):
|
||||
""" Performs shutdown tasks """
|
||||
self._settings.save() # storing current config
|
||||
if self._settings.load_last_config:
|
||||
self._settings.add("last_config", {"last_profile": self._settings.current_profile,
|
||||
"last_bouquet": self._current_bq_name})
|
||||
self._settings.save() # storing current settings
|
||||
|
||||
if self._player:
|
||||
self._player.release()
|
||||
|
||||
if self._http_api:
|
||||
self._http_api.close()
|
||||
|
||||
Gtk.Application.do_shutdown(self)
|
||||
|
||||
def do_command_line(self, command_line):
|
||||
@@ -347,6 +362,12 @@ class Application(Gtk.Application):
|
||||
self.activate()
|
||||
return 0
|
||||
|
||||
def init_profiles(self, profile=None):
|
||||
self.update_profiles()
|
||||
self._profile_combo_box.set_active_id(profile if profile else self._settings.default_profile)
|
||||
if profile:
|
||||
self.set_profile(profile)
|
||||
|
||||
def init_drag_and_drop(self):
|
||||
""" Enable drag-and-drop """
|
||||
target = []
|
||||
@@ -374,7 +395,7 @@ class Application(Gtk.Application):
|
||||
|
||||
If update=False - first call on program start, else - after options changes!
|
||||
"""
|
||||
if self._profile is Profile.ENIGMA_2:
|
||||
if self._s_type is SettingsType.ENIGMA_2:
|
||||
if self._settings.use_colors:
|
||||
new_rgb = Gdk.RGBA()
|
||||
extra_rgb = Gdk.RGBA()
|
||||
@@ -410,8 +431,6 @@ class Application(Gtk.Application):
|
||||
|
||||
@run_idle
|
||||
def on_close_app(self, *args):
|
||||
if self._http_api:
|
||||
self._http_api.close()
|
||||
self.quit()
|
||||
|
||||
def on_resize(self, window):
|
||||
@@ -605,7 +624,7 @@ class Application(Gtk.Application):
|
||||
# ***************** ####### *********************#
|
||||
|
||||
def get_bouquet_file_name(self, bouquet):
|
||||
bouquet_file_name = "{}userbouquet.{}.{}".format(self._settings.get(self._profile).get("data_dir_path"),
|
||||
bouquet_file_name = "{}userbouquet.{}.{}".format(self._settings.get(self._s_type).get("data_dir_path"),
|
||||
*bouquet.split(":"))
|
||||
return bouquet_file_name
|
||||
|
||||
@@ -839,7 +858,7 @@ class Application(Gtk.Application):
|
||||
DownloadDialog(transient=self._main_window,
|
||||
settings=self._settings,
|
||||
open_data_callback=self.open_data,
|
||||
update_settings_callback=self.update_options).show()
|
||||
update_settings_callback=self.update_settings).show()
|
||||
|
||||
@run_task
|
||||
def on_download_data(self):
|
||||
@@ -855,15 +874,15 @@ class Application(Gtk.Application):
|
||||
@run_task
|
||||
def on_upload_data(self, download_type):
|
||||
try:
|
||||
profile = self._profile
|
||||
profile = self._s_type
|
||||
opts = self._settings
|
||||
use_http = profile is Profile.ENIGMA_2
|
||||
use_http = profile is SettingsType.ENIGMA_2
|
||||
|
||||
if profile is Profile.ENIGMA_2:
|
||||
if profile is SettingsType.ENIGMA_2:
|
||||
host, port, user, password = opts.host, opts.http_port, opts.http_user, opts.http_password
|
||||
try:
|
||||
test_http(host, port, user, password, skip_message=True)
|
||||
except TestException:
|
||||
test_http(host, port, user, password, use_ssl=opts.http_use_ssl, skip_message=True)
|
||||
except (TestException, HttpApiException):
|
||||
use_http = False
|
||||
|
||||
upload_data(settings=opts,
|
||||
@@ -880,26 +899,27 @@ class Application(Gtk.Application):
|
||||
return
|
||||
self.open_data(response)
|
||||
|
||||
def open_data(self, data_path=None):
|
||||
def open_data(self, data_path=None, callback=None):
|
||||
""" Opening data and fill views. """
|
||||
gen = self.update_data(data_path)
|
||||
gen = self.update_data(data_path, callback)
|
||||
GLib.idle_add(lambda: next(gen, False), priority=GLib.PRIORITY_DEFAULT_IDLE)
|
||||
|
||||
def update_data(self, data_path):
|
||||
def update_data(self, data_path, callback=None):
|
||||
self._profile_combo_box.set_sensitive(False)
|
||||
self._wait_dialog.show()
|
||||
yield True
|
||||
|
||||
data_path = self._settings.data_dir_path if data_path is None else data_path
|
||||
data_path = self._settings.data_local_path if data_path is None else data_path
|
||||
yield from self.clear_current_data()
|
||||
|
||||
try:
|
||||
prf = self._profile
|
||||
prf = self._s_type
|
||||
black_list = get_blacklist(data_path)
|
||||
bouquets = get_bouquets(data_path, prf)
|
||||
yield True
|
||||
services = get_services(data_path, prf, self.get_format_version() if prf is Profile.ENIGMA_2 else 0)
|
||||
services = get_services(data_path, prf, self.get_format_version() if prf is SettingsType.ENIGMA_2 else 0)
|
||||
yield True
|
||||
update_picons_data(self._settings.picons_dir_path, self._picons)
|
||||
update_picons_data(self._settings.picons_local_path, self._picons)
|
||||
yield True
|
||||
except FileNotFoundError as e:
|
||||
msg = get_message("Please, download files from receiver or setup your path for read data!")
|
||||
@@ -917,6 +937,9 @@ class Application(Gtk.Application):
|
||||
yield from self.append_data(bouquets, services)
|
||||
finally:
|
||||
self._wait_dialog.hide()
|
||||
self._profile_combo_box.set_sensitive(True)
|
||||
if callback:
|
||||
callback()
|
||||
yield True
|
||||
|
||||
def append_data(self, bouquets, services):
|
||||
@@ -981,6 +1004,17 @@ class Application(Gtk.Application):
|
||||
if extra_services:
|
||||
self._extra_bouquets[bq_id] = extra_services
|
||||
|
||||
@run_idle
|
||||
def open_bouquet(self, name):
|
||||
""" Find and open bouquet by name """
|
||||
for r in self._bouquets_model:
|
||||
for i in r.iterchildren():
|
||||
if i[Column.BQ_NAME] == name:
|
||||
self._bouquets_view.expand_row(self._bouquets_model.get_path(r.iter), Column.BQ_NAME)
|
||||
self._bouquets_view.set_cursor(i.path)
|
||||
self._bouquets_view.row_activated(i.path, self._bouquets_view.get_column(Column.BQ_NAME))
|
||||
break
|
||||
|
||||
def append_services(self, services):
|
||||
for srv in services:
|
||||
# adding channels to dict with fav_id as keys
|
||||
@@ -1019,6 +1053,7 @@ class Application(Gtk.Application):
|
||||
self._blacklist.clear()
|
||||
self._services.clear()
|
||||
self._rows_buffer.clear()
|
||||
self._picons.clear()
|
||||
self._bouquets.clear()
|
||||
self._extra_bouquets.clear()
|
||||
self._current_bq_name = None
|
||||
@@ -1039,9 +1074,9 @@ class Application(Gtk.Application):
|
||||
GLib.idle_add(lambda: next(gen, False), priority=GLib.PRIORITY_LOW)
|
||||
|
||||
def save_data(self):
|
||||
profile = self._profile
|
||||
path = self._settings.data_dir_path
|
||||
backup_path = self._settings.backup_dir_path
|
||||
profile = self._s_type
|
||||
path = self._settings.data_local_path
|
||||
backup_path = self._settings.backup_local_path
|
||||
# Backup data or clearing data path
|
||||
backup_data(path, backup_path) if self._settings.backup_before_save else clear_data_path(path)
|
||||
yield True
|
||||
@@ -1061,7 +1096,7 @@ class Application(Gtk.Application):
|
||||
favs = self._bouquets[bq_id]
|
||||
ex_s = self._extra_bouquets.get(bq_id)
|
||||
bq_s = list(filter(None, [self._services.get(f_id, None) for f_id in favs]))
|
||||
if profile is Profile.ENIGMA_2:
|
||||
if profile is SettingsType.ENIGMA_2:
|
||||
bq_s = list(map(lambda s: s._replace(service=ex_s.get(s.fav_id, None) if ex_s else None), bq_s))
|
||||
bq = Bouquet(bq_name, bq_type, bq_s, locked, hidden)
|
||||
bqs.append(bq)
|
||||
@@ -1075,10 +1110,10 @@ class Application(Gtk.Application):
|
||||
# Getting services
|
||||
services_model = get_base_model(self._services_view.get_model())
|
||||
services = [Service(*row[: Column.SRV_TOOLTIP]) for row in services_model]
|
||||
write_services(path, services, profile, self.get_format_version() if profile is Profile.ENIGMA_2 else 0)
|
||||
write_services(path, services, profile, self.get_format_version() if profile is SettingsType.ENIGMA_2 else 0)
|
||||
yield True
|
||||
# removing bouquet files
|
||||
if profile is Profile.ENIGMA_2:
|
||||
if profile is SettingsType.ENIGMA_2:
|
||||
# blacklist
|
||||
write_blacklist(path, self._blacklist)
|
||||
yield True
|
||||
@@ -1088,7 +1123,7 @@ class Application(Gtk.Application):
|
||||
if show_dialog(DialogType.QUESTION, self._main_window) == Gtk.ResponseType.CANCEL:
|
||||
return
|
||||
|
||||
gen = self.create_new_configuration(self._profile)
|
||||
gen = self.create_new_configuration(self._s_type)
|
||||
GLib.idle_add(lambda: next(gen, False), priority=GLib.PRIORITY_LOW)
|
||||
|
||||
def create_new_configuration(self, profile):
|
||||
@@ -1098,12 +1133,12 @@ class Application(Gtk.Application):
|
||||
c_gen = self.clear_current_data()
|
||||
yield from c_gen
|
||||
|
||||
if profile is Profile.ENIGMA_2:
|
||||
if profile is SettingsType.ENIGMA_2:
|
||||
parent = self._bouquets_model.append(None, ["Favourites (TV)", None, None, BqType.TV.value])
|
||||
self.append_bouquet(Bouquet("Favourites (TV)", BqType.TV.value, [], None, None), parent)
|
||||
parent = self._bouquets_model.append(None, ["Favourites (Radio)", None, None, BqType.RADIO.value])
|
||||
self.append_bouquet(Bouquet("Favourites (Radio)", BqType.RADIO.value, [], None, None), parent)
|
||||
elif profile is Profile.NEUTRINO_MP:
|
||||
elif profile is SettingsType.NEUTRINO_MP:
|
||||
self._bouquets_model.append(None, ["Providers", None, None, BqType.BOUQUET.value])
|
||||
self._bouquets_model.append(None, ["FAV", None, None, BqType.TV.value])
|
||||
self._bouquets_model.append(None, ["WEBTV", None, None, BqType.WEBTV.value])
|
||||
@@ -1171,7 +1206,7 @@ class Application(Gtk.Application):
|
||||
self.show_error_dialog("Error. No bouquet is selected!")
|
||||
return
|
||||
|
||||
if self._profile is Profile.NEUTRINO_MP and self._bq_selected.endswith(BqType.WEBTV.value):
|
||||
if self._s_type is SettingsType.NEUTRINO_MP and self._bq_selected.endswith(BqType.WEBTV.value):
|
||||
self.show_error_dialog("Operation not allowed in this context!")
|
||||
return
|
||||
|
||||
@@ -1196,25 +1231,49 @@ class Application(Gtk.Application):
|
||||
for v in [view, *args]:
|
||||
v.get_selection().unselect_all()
|
||||
|
||||
def on_preferences(self, action, value):
|
||||
def on_settings(self, action, value):
|
||||
response = show_settings_dialog(self._main_window, self._settings)
|
||||
if response != Gtk.ResponseType.CANCEL:
|
||||
gen = self.update_options()
|
||||
gen = self.update_settings()
|
||||
GLib.idle_add(lambda: next(gen, False), priority=GLib.PRIORITY_LOW)
|
||||
|
||||
def update_options(self):
|
||||
profile = self._settings.profile
|
||||
self._ip_label.set_text(self._settings.host)
|
||||
if profile != self._profile:
|
||||
def update_settings(self):
|
||||
s_type = self._settings.setting_type
|
||||
|
||||
if s_type != self._s_type:
|
||||
yield from self.show_app_info(True)
|
||||
self._profile = profile
|
||||
self._s_type = s_type
|
||||
c_gen = self.clear_current_data()
|
||||
yield from c_gen
|
||||
self.update_profile_label()
|
||||
|
||||
self.init_colors(True)
|
||||
self.init_profiles()
|
||||
yield True
|
||||
self.init_http_api()
|
||||
yield True
|
||||
gen = self.init_http_api()
|
||||
yield from gen
|
||||
|
||||
def on_profile_changed(self, entry):
|
||||
if self._app_info_box.get_visible():
|
||||
self.update_profile_label()
|
||||
return
|
||||
|
||||
active = self._profile_combo_box.get_active_text()
|
||||
if active in self._settings.profiles:
|
||||
self.set_profile(active)
|
||||
gen = self.init_http_api()
|
||||
GLib.idle_add(lambda: next(gen, False), priority=GLib.PRIORITY_LOW)
|
||||
self.open_data()
|
||||
|
||||
def set_profile(self, active):
|
||||
self._settings.current_profile = active
|
||||
self._s_type = self._settings.setting_type
|
||||
self._profile_combo_box.set_tooltip_text(self._profile_combo_box.get_tooltip_text() + self._settings.host)
|
||||
self.update_profile_label()
|
||||
|
||||
def update_profiles(self):
|
||||
self._profile_combo_box.remove_all()
|
||||
for p in self._settings.profiles:
|
||||
self._profile_combo_box.append(p, p)
|
||||
|
||||
def on_tree_view_key_press(self, view, event):
|
||||
""" Handling keystrokes on press """
|
||||
@@ -1312,7 +1371,7 @@ class Application(Gtk.Application):
|
||||
self._tool_elements[elem].set_sensitive(not_empty)
|
||||
if elem == "bouquets_paste_popup_item":
|
||||
self._tool_elements[elem].set_sensitive(not_empty and self._bouquets_buffer)
|
||||
if self._profile is Profile.NEUTRINO_MP:
|
||||
if self._s_type is SettingsType.NEUTRINO_MP:
|
||||
for elem in self._LOCK_HIDE_ELEMENTS:
|
||||
self._tool_elements[elem].set_sensitive(not_empty)
|
||||
else:
|
||||
@@ -1328,17 +1387,17 @@ class Application(Gtk.Application):
|
||||
for elem in self._BOUQUET_ELEMENTS:
|
||||
self._tool_elements[elem].set_sensitive(False)
|
||||
for elem in self._LOCK_HIDE_ELEMENTS:
|
||||
self._tool_elements[elem].set_sensitive(not_empty and self._profile is Profile.ENIGMA_2)
|
||||
self._tool_elements[elem].set_sensitive(not_empty and self._s_type is SettingsType.ENIGMA_2)
|
||||
|
||||
for elem in self._FAV_IPTV_ELEMENTS:
|
||||
is_iptv = self._bq_selected and not is_service
|
||||
if self._profile is Profile.NEUTRINO_MP:
|
||||
if self._s_type is SettingsType.NEUTRINO_MP:
|
||||
is_iptv = is_iptv and BqType(self._bq_selected.split(":")[1]) is BqType.WEBTV
|
||||
self._tool_elements[elem].set_sensitive(is_iptv)
|
||||
for elem in self._COMMONS_ELEMENTS:
|
||||
self._tool_elements[elem].set_sensitive(not_empty)
|
||||
|
||||
if self._profile is not Profile.ENIGMA_2:
|
||||
if self._s_type is not SettingsType.ENIGMA_2:
|
||||
for elem in self._FAV_ENIGMA_ELEMENTS:
|
||||
self._tool_elements[elem].set_sensitive(False)
|
||||
|
||||
@@ -1349,9 +1408,9 @@ class Application(Gtk.Application):
|
||||
self.set_service_flags(Flag.LOCK)
|
||||
|
||||
def set_service_flags(self, flag):
|
||||
if self._profile is Profile.ENIGMA_2:
|
||||
if self._s_type is SettingsType.ENIGMA_2:
|
||||
set_flags(flag, self._services_view, self._fav_view, self._services, self._blacklist)
|
||||
elif self._profile is Profile.NEUTRINO_MP and self._bq_selected:
|
||||
elif self._s_type is SettingsType.NEUTRINO_MP and self._bq_selected:
|
||||
model, paths = self._bouquets_view.get_selection().get_selected_rows()
|
||||
itr = model.get_iter(paths[0])
|
||||
value = model.get_value(itr, 1 if flag is Flag.LOCK else 2)
|
||||
@@ -1400,10 +1459,12 @@ class Application(Gtk.Application):
|
||||
return
|
||||
elif self._fav_click_mode is FavClickMode.STREAM:
|
||||
self.on_play_stream()
|
||||
elif self._fav_click_mode is FavClickMode.PLAY:
|
||||
elif self._fav_click_mode is FavClickMode.ZAP_PLAY:
|
||||
self.on_zap(self.on_watch)
|
||||
elif self._fav_click_mode is FavClickMode.ZAP:
|
||||
self.on_zap()
|
||||
elif self._fav_click_mode is FavClickMode.PLAY:
|
||||
self.on_stream()
|
||||
else:
|
||||
return self.on_view_popup_menu(menu, event)
|
||||
|
||||
@@ -1414,14 +1475,14 @@ class Application(Gtk.Application):
|
||||
self._fav_view,
|
||||
self._services,
|
||||
self._bouquets.get(self._bq_selected, None),
|
||||
self._profile,
|
||||
self._s_type,
|
||||
Action.ADD).show()
|
||||
if response != Gtk.ResponseType.CANCEL:
|
||||
self.update_fav_num_column(self._fav_model)
|
||||
|
||||
@run_idle
|
||||
def on_iptv_list_configuration(self, action, value):
|
||||
if self._profile is Profile.NEUTRINO_MP:
|
||||
if self._s_type is SettingsType.NEUTRINO_MP:
|
||||
self.show_error_dialog("Neutrino at the moment not supported!")
|
||||
return
|
||||
|
||||
@@ -1435,7 +1496,7 @@ class Application(Gtk.Application):
|
||||
|
||||
bq = self._bouquets.get(self._bq_selected, [])
|
||||
IptvListConfigurationDialog(self._main_window, self._services, iptv_rows, bq,
|
||||
self._fav_model, self._profile).show()
|
||||
self._fav_model, self._s_type).show()
|
||||
|
||||
@run_idle
|
||||
def on_remove_all_unavailable(self, action, value=None):
|
||||
@@ -1451,7 +1512,7 @@ class Application(Gtk.Application):
|
||||
return
|
||||
|
||||
fav_bqt = self._bouquets.get(self._bq_selected, None)
|
||||
response = SearchUnavailableDialog(self._main_window, self._fav_model, fav_bqt, iptv_rows, self._profile).show()
|
||||
response = SearchUnavailableDialog(self._main_window, self._fav_model, fav_bqt, iptv_rows, self._s_type).show()
|
||||
if response:
|
||||
next(self.remove_favs(response, self._fav_model), False)
|
||||
|
||||
@@ -1459,7 +1520,7 @@ class Application(Gtk.Application):
|
||||
|
||||
@run_idle
|
||||
def on_epg_list_configuration(self, action, value=None):
|
||||
if self._profile is not Profile.ENIGMA_2:
|
||||
if self._s_type is not SettingsType.ENIGMA_2:
|
||||
self.show_error_dialog("Only Enigma2 is supported!")
|
||||
return
|
||||
|
||||
@@ -1477,7 +1538,7 @@ class Application(Gtk.Application):
|
||||
if not self._bq_selected:
|
||||
return
|
||||
|
||||
YtListImportDialog(self._main_window, self._profile, self.append_imported_services).show()
|
||||
YtListImportDialog(self._main_window, self._s_type, self.append_imported_services).show()
|
||||
|
||||
def on_import_m3u(self, action, value=None):
|
||||
""" Imports iptv from m3u files. """
|
||||
@@ -1489,7 +1550,7 @@ class Application(Gtk.Application):
|
||||
self.show_error_dialog("No m3u file is selected!")
|
||||
return
|
||||
|
||||
channels = parse_m3u(response, self._profile)
|
||||
channels = parse_m3u(response, self._s_type)
|
||||
|
||||
if channels and self._bq_selected:
|
||||
self.append_imported_services(channels)
|
||||
@@ -1520,7 +1581,7 @@ class Application(Gtk.Application):
|
||||
|
||||
try:
|
||||
bq = Bouquet(self._current_bq_name, None, bq_services, None, None)
|
||||
export_to_m3u(response, bq, self._profile)
|
||||
export_to_m3u(response, bq, self._s_type)
|
||||
except Exception as e:
|
||||
self.show_error_dialog(str(e))
|
||||
else:
|
||||
@@ -1532,7 +1593,7 @@ class Application(Gtk.Application):
|
||||
self.show_error_dialog("No selected item!")
|
||||
return
|
||||
|
||||
appender = self.append_bouquet if self._profile is Profile.ENIGMA_2 else self.append_bouquets
|
||||
appender = self.append_bouquet if self._s_type is SettingsType.ENIGMA_2 else self.append_bouquets
|
||||
import_bouquet(self._main_window, model, paths[0], self._settings, self._services, appender)
|
||||
|
||||
def on_import_bouquets(self, action, value=None):
|
||||
@@ -1573,7 +1634,7 @@ class Application(Gtk.Application):
|
||||
self.show_error_dialog("Not allowed in this context!")
|
||||
return
|
||||
|
||||
url = get_iptv_url(row, self._profile)
|
||||
url = get_iptv_url(row, self._s_type)
|
||||
self.update_player_buttons()
|
||||
if not url:
|
||||
return
|
||||
@@ -1583,7 +1644,9 @@ class Application(Gtk.Application):
|
||||
if not self._player:
|
||||
try:
|
||||
self._player = Player.get_instance(rewind_callback=self.on_player_duration_changed,
|
||||
position_callback=self.on_player_time_changed)
|
||||
position_callback=self.on_player_time_changed,
|
||||
error_callback=self.on_player_error,
|
||||
playing_callback=self.set_playback_elms_active)
|
||||
except (ImportError, NameError, AttributeError):
|
||||
self.show_error_dialog("No VLC is found. Check that it is installed!")
|
||||
return
|
||||
@@ -1594,6 +1657,7 @@ class Application(Gtk.Application):
|
||||
self._player_box.set_size_request(w * 0.6, -1)
|
||||
|
||||
self._player_box.set_visible(True)
|
||||
self._fav_view.set_sensitive(False)
|
||||
GLib.idle_add(self._player.play, url, priority=GLib.PRIORITY_LOW)
|
||||
|
||||
def on_player_stop(self, item=None):
|
||||
@@ -1602,10 +1666,19 @@ class Application(Gtk.Application):
|
||||
|
||||
def on_player_previous(self, item):
|
||||
if self._fav_view.do_move_cursor(self._fav_view, Gtk.MovementStep.DISPLAY_LINES, -1):
|
||||
self.on_play_stream()
|
||||
self.set_player_action()
|
||||
|
||||
def on_player_next(self, item):
|
||||
if self._fav_view.do_move_cursor(self._fav_view, Gtk.MovementStep.DISPLAY_LINES, 1):
|
||||
self.set_player_action()
|
||||
|
||||
@run_with_delay(1)
|
||||
def set_player_action(self):
|
||||
if self._fav_click_mode is FavClickMode.PLAY:
|
||||
self.on_stream()
|
||||
elif self._fav_click_mode is FavClickMode.ZAP_PLAY:
|
||||
self.on_zap(self.on_watch)
|
||||
elif self._fav_click_mode is FavClickMode.STREAM:
|
||||
self.on_play_stream()
|
||||
|
||||
def on_player_rewind(self, scale, scroll_type, value):
|
||||
@@ -1621,21 +1694,32 @@ class Application(Gtk.Application):
|
||||
def on_player_close(self, item=None):
|
||||
if self._player:
|
||||
self._player.stop()
|
||||
|
||||
self.set_playback_elms_active()
|
||||
GLib.idle_add(self._player_box.set_visible, False, priority=GLib.PRIORITY_LOW)
|
||||
|
||||
@lru_cache(maxsize=1)
|
||||
def on_player_duration_changed(self, duration):
|
||||
self._player_scale.set_value(0)
|
||||
self._player_scale.get_adjustment().set_upper(duration)
|
||||
GLib.idle_add(self._player_rewind_box.set_visible, duration > 0)
|
||||
GLib.idle_add(self._player_current_time_label.set_text, "0")
|
||||
GLib.idle_add(self._player_full_time_label.set_text, self.get_time_str(duration))
|
||||
GLib.idle_add(self._player_rewind_box.set_visible, duration > 0, priority=GLib.PRIORITY_LOW)
|
||||
GLib.idle_add(self._player_current_time_label.set_text, "0", priority=GLib.PRIORITY_LOW)
|
||||
GLib.idle_add(self._player_full_time_label.set_text, self.get_time_str(duration), priority=GLib.PRIORITY_LOW)
|
||||
|
||||
def on_player_time_changed(self, t):
|
||||
if not self._full_screen and self._player_rewind_box.get_visible():
|
||||
GLib.idle_add(self._player_current_time_label.set_text, self.get_time_str(t),
|
||||
priority=GLib.PRIORITY_LOW)
|
||||
|
||||
def on_player_error(self):
|
||||
self.set_playback_elms_active()
|
||||
self.show_error_dialog("Can't Playback!")
|
||||
|
||||
@run_idle
|
||||
def set_playback_elms_active(self):
|
||||
self._fav_view.set_sensitive(True)
|
||||
self._fav_view.do_grab_focus(self._fav_view)
|
||||
|
||||
def get_time_str(self, duration):
|
||||
""" returns a string representation of time from duration in milliseconds """
|
||||
m, s = divmod(duration // 1000, 60)
|
||||
@@ -1684,30 +1768,35 @@ class Application(Gtk.Application):
|
||||
if not state & Gdk.WindowState.ICONIFIED and self._links_transmitter:
|
||||
self._links_transmitter.hide()
|
||||
|
||||
# ************************ HTTP API ****************************#
|
||||
# ************************ HTTP API **************************** #
|
||||
|
||||
@run_task
|
||||
def init_http_api(self):
|
||||
self._fav_click_mode = FavClickMode(self._settings.fav_click_mode)
|
||||
http_api_enable = self._settings.http_api_support
|
||||
status = all((http_api_enable, self._profile is Profile.ENIGMA_2, not self._receiver_info_box.get_visible()))
|
||||
GLib.idle_add(self._http_status_image.set_visible, status)
|
||||
st = all((http_api_enable, self._s_type is SettingsType.ENIGMA_2, not self._receiver_info_box.get_visible()))
|
||||
GLib.idle_add(self._http_status_image.set_visible, st)
|
||||
|
||||
if self._profile is Profile.NEUTRINO_MP or not http_api_enable:
|
||||
self.update_info_boxes_visible(False)
|
||||
if self._s_type is SettingsType.NEUTRINO_MP or not http_api_enable:
|
||||
GLib.idle_add(self._receiver_info_box.set_visible, False)
|
||||
if self._http_api:
|
||||
self._http_api.close()
|
||||
yield True
|
||||
self._http_api = None
|
||||
self.init_send_to(False)
|
||||
return
|
||||
|
||||
if not self._http_api:
|
||||
self._http_api = HttpAPI(self._settings.host, self._settings.http_port,
|
||||
self._settings.http_user, self._settings.http_password)
|
||||
current_profile = self._profile_combo_box.get_active_text()
|
||||
if current_profile in self._settings.profiles:
|
||||
self._settings.current_profile = current_profile
|
||||
|
||||
if not self._http_api:
|
||||
self._http_api = HttpAPI(self._settings)
|
||||
GLib.timeout_add_seconds(3, self.update_info, priority=GLib.PRIORITY_LOW)
|
||||
else:
|
||||
self._http_api.init()
|
||||
|
||||
self.init_send_to(http_api_enable and self._settings.enable_send_to)
|
||||
yield True
|
||||
|
||||
@run_idle
|
||||
def init_send_to(self, enable):
|
||||
@@ -1716,9 +1805,23 @@ class Application(Gtk.Application):
|
||||
elif self._links_transmitter:
|
||||
self._links_transmitter.show(enable)
|
||||
|
||||
def on_stream(self, item=None):
|
||||
path, column = self._fav_view.get_cursor()
|
||||
if not path or not self._http_api:
|
||||
return
|
||||
|
||||
ref = self.get_service_ref(path)
|
||||
if not ref:
|
||||
return
|
||||
|
||||
if self._player and self._player.is_playing():
|
||||
self._player.stop()
|
||||
|
||||
self._http_api.send(HttpRequestType.STREAM, ref, self.watch)
|
||||
|
||||
def on_watch(self, item=None):
|
||||
""" Switch to the channel and watch in the player """
|
||||
self._http_api.send(HttpRequestType.STREAM, None, self.watch)
|
||||
self._http_api.send(HttpRequestType.STREAM_CURRENT, None, self.watch)
|
||||
|
||||
def watch(self, m3u):
|
||||
if m3u:
|
||||
@@ -1733,67 +1836,94 @@ class Application(Gtk.Application):
|
||||
if not path or not self._http_api:
|
||||
return
|
||||
|
||||
ref = self.get_service_ref(path)
|
||||
if not ref:
|
||||
return
|
||||
|
||||
if self._player and self._player.is_playing():
|
||||
self._player.stop()
|
||||
|
||||
def zap(rq):
|
||||
if rq and rq.get("e2state", False):
|
||||
GLib.idle_add(scroll_to, path, self._fav_view)
|
||||
if callback:
|
||||
callback()
|
||||
|
||||
self._http_api.send(HttpRequestType.ZAP, ref, zap)
|
||||
|
||||
def get_service_ref(self, path):
|
||||
row = self._fav_model[path][:]
|
||||
srv = self._services.get(row[Column.FAV_ID], None)
|
||||
srv_type, fav_id = row[Column.FAV_TYPE], row[Column.FAV_ID]
|
||||
|
||||
if srv_type == BqServiceType.IPTV.name or srv_type == BqServiceType.MARKER.name:
|
||||
self.show_error_dialog("Not allowed in this context!")
|
||||
return
|
||||
|
||||
srv = self._services.get(fav_id, None)
|
||||
if srv and srv.transponder:
|
||||
ref = srv.picon_id.rstrip(".png").replace("_", ":")
|
||||
|
||||
def zap(rq):
|
||||
if rq and rq.get("result", False):
|
||||
GLib.idle_add(scroll_to, path, self._fav_view)
|
||||
if callback is not None:
|
||||
callback()
|
||||
|
||||
self._http_api.send(HttpRequestType.ZAP, ref, zap)
|
||||
return srv.picon_id.rstrip(".png").replace("_", ":")
|
||||
|
||||
def update_info(self):
|
||||
""" Updating current info over HTTP API """
|
||||
if not self._http_api:
|
||||
if not self._http_api or self._s_type is SettingsType.NEUTRINO_MP:
|
||||
GLib.idle_add(self._http_status_image.set_visible, False)
|
||||
GLib.idle_add(self._receiver_info_box.set_visible, False)
|
||||
return False
|
||||
|
||||
self._http_api.send(HttpRequestType.INFO, None, self.update_receiver_info)
|
||||
self._http_api.send(HttpRequestType.INFO, None, self.update_service_info)
|
||||
return True
|
||||
|
||||
def update_receiver_info(self, info):
|
||||
res_info = info.get("info", None) if info else None
|
||||
if res_info:
|
||||
image = res_info.get("friendlyimagedistro", "")
|
||||
image_ver = res_info.get("imagever", "")
|
||||
brand = res_info.get("brand", "")
|
||||
model = res_info.get("model", "")
|
||||
info_text = "{} {} Image: {} {}".format(brand, model, image, image_ver)
|
||||
GLib.idle_add(self._receiver_info_label.set_text, info_text)
|
||||
GLib.idle_add(self._receiver_info_box.set_visible, bool(res_info))
|
||||
error_code = info.get("error_code", 0) if info else 0
|
||||
GLib.idle_add(self._receiver_info_box.set_visible, error_code == 0, priority=GLib.PRIORITY_LOW)
|
||||
|
||||
def update_service_info(self, info):
|
||||
service_info = info.get("service", None) if info else None
|
||||
if service_info:
|
||||
GLib.idle_add(self._service_name_label.set_text, service_info.get("name", ""))
|
||||
if service_info.get("onid", None) and self._http_api:
|
||||
self._http_api.send(HttpRequestType.SIGNAL, None, self.update_signal)
|
||||
self._http_api.send(HttpRequestType.STATUS, None, self.update_status)
|
||||
GLib.idle_add(self._signal_box.set_visible, bool(service_info))
|
||||
if error_code < 0:
|
||||
return
|
||||
elif error_code == 412:
|
||||
self._http_api.init()
|
||||
return
|
||||
|
||||
res_info = info.get("e2about", None) if info else None
|
||||
if res_info:
|
||||
image = info.get("e2distroversion", "")
|
||||
image_ver = info.get("e2imageversion", "")
|
||||
model = info.get("e2model", "")
|
||||
info_text = "{} Image: {} {}".format(model, image, image_ver)
|
||||
GLib.idle_add(self._receiver_info_label.set_text, info_text, priority=GLib.PRIORITY_LOW)
|
||||
service_name = info.get("e2servicename", None) or ""
|
||||
GLib.idle_add(self._service_name_label.set_text, service_name, priority=GLib.PRIORITY_LOW)
|
||||
if service_name:
|
||||
self.update_service_info()
|
||||
|
||||
def update_service_info(self):
|
||||
if self._http_api:
|
||||
self._http_api.send(HttpRequestType.SIGNAL, None, self.update_signal)
|
||||
self._http_api.send(HttpRequestType.CURRENT, None, self.update_status)
|
||||
|
||||
def update_signal(self, sig):
|
||||
self.set_signal(sig.get("snr", 0) if sig else 0)
|
||||
self.set_signal(sig.get("e2snr", "0 %") if sig else "0 %")
|
||||
|
||||
@lru_cache(maxsize=2)
|
||||
def set_signal(self, val):
|
||||
self._signal_level_bar.set_value(val if isinstance(val, int) else 0)
|
||||
self._signal_level_bar.set_visible(val)
|
||||
val = val.strip().rstrip("%") or 0
|
||||
with suppress(ValueError):
|
||||
self._signal_level_bar.set_value(int(val))
|
||||
GLib.idle_add(self._signal_level_bar.set_visible, val != "N/A")
|
||||
|
||||
def update_status(self, status):
|
||||
if status:
|
||||
dsc = "{} {} - {}".format(status.get("currservice_name", ""),
|
||||
status.get("currservice_begin", ""),
|
||||
status.get("currservice_end", ""))
|
||||
@run_idle
|
||||
def update_status(self, evn):
|
||||
if evn:
|
||||
s_duration = evn.get("e2eventstart", 0)
|
||||
self._service_epg_label.set_visible(bool(s_duration))
|
||||
if not s_duration:
|
||||
return
|
||||
s_duration = int(s_duration)
|
||||
s_time = datetime.fromtimestamp(s_duration)
|
||||
end_time = datetime.fromtimestamp(s_duration + int(evn.get("e2eventduration", "0") or "0"))
|
||||
title = evn.get("e2eventtitle", "")
|
||||
dsc = "{} {}:{} - {}:{}".format(title, s_time.hour, s_time.minute, end_time.hour, end_time.minute)
|
||||
self._service_epg_label.set_text(dsc)
|
||||
self._service_epg_label.set_tooltip_text(status.get("currservice_description", ""))
|
||||
self._service_epg_label.set_tooltip_text(evn.get("e2eventdescription", ""))
|
||||
|
||||
# ***************** Filter and search *********************#
|
||||
|
||||
@@ -1817,7 +1947,7 @@ class Application(Gtk.Application):
|
||||
self._sat_positions.clear()
|
||||
sat_positions = set()
|
||||
|
||||
if self._profile is Profile.ENIGMA_2:
|
||||
if self._s_type is SettingsType.ENIGMA_2:
|
||||
terrestrial = False
|
||||
cable = False
|
||||
|
||||
@@ -1834,7 +1964,7 @@ class Application(Gtk.Application):
|
||||
self._sat_positions.append("T")
|
||||
if cable:
|
||||
self._sat_positions.append("C")
|
||||
elif self._profile is Profile.NEUTRINO_MP:
|
||||
elif self._s_type is SettingsType.NEUTRINO_MP:
|
||||
list(map(lambda s: sat_positions.add(float(s.pos)), filter(lambda s: s.pos, self._services.values())))
|
||||
|
||||
self._sat_positions.extend(map(str, sorted(sat_positions)))
|
||||
@@ -1913,7 +2043,7 @@ class Application(Gtk.Application):
|
||||
self._fav_view,
|
||||
self._services,
|
||||
self._bouquets.get(self._bq_selected, None),
|
||||
self._profile,
|
||||
self._s_type,
|
||||
Action.EDIT).show()
|
||||
self.on_locate_in_services(view)
|
||||
|
||||
@@ -2031,7 +2161,7 @@ class Application(Gtk.Application):
|
||||
@run_idle
|
||||
def on_picons_loader_show(self, action, value):
|
||||
ids = {}
|
||||
if self._profile is Profile.ENIGMA_2:
|
||||
if self._s_type is SettingsType.ENIGMA_2:
|
||||
for r in self._services_model:
|
||||
data = r[Column.SRV_PICON_ID].split("_")
|
||||
ids["{}:{}:{}".format(data[3], data[5], data[6])] = r[Column.SRV_PICON_ID]
|
||||
@@ -2041,7 +2171,7 @@ class Application(Gtk.Application):
|
||||
|
||||
@run_task
|
||||
def update_picons(self):
|
||||
update_picons_data(self._settings.picons_dir_path, self._picons)
|
||||
update_picons_data(self._settings.picons_local_path, self._picons)
|
||||
append_picons(self._picons, self._services_model)
|
||||
|
||||
def on_assign_picon(self, view):
|
||||
@@ -2094,33 +2224,40 @@ class Application(Gtk.Application):
|
||||
|
||||
def create_bouquets(self, g_type):
|
||||
gen_bouquets(self._services_view, self._bouquets_view, self._main_window, g_type, self._TV_TYPES,
|
||||
self._profile, self.append_bouquet)
|
||||
self._s_type, self.append_bouquet)
|
||||
|
||||
# ***************** Profile label *********************#
|
||||
|
||||
@run_idle
|
||||
def update_profile_label(self):
|
||||
if self._profile is Profile.ENIGMA_2:
|
||||
ver = self.get_format_version()
|
||||
self._main_window.set_title("DemonEditor [{} Enigma2 v.{}]".format(get_message("Profile:"), ver))
|
||||
elif self._profile is Profile.NEUTRINO_MP:
|
||||
self._main_window.set_title("DemonEditor [{} Neutrino-MP]".format(get_message("Profile:")))
|
||||
label, sep, ip = self._profile_combo_box.get_tooltip_text().partition(":")
|
||||
profile_name = self._profile_combo_box.get_active_text()
|
||||
self._profile_combo_box.set_tooltip_text("{}: {}".format(label, self._settings.host))
|
||||
msg = get_message("Profile:")
|
||||
|
||||
if self._s_type is SettingsType.ENIGMA_2:
|
||||
self._main_window.set_title.set_subtitle("DemonEditor [{} {} Enigma2 v.{}]".format(msg, profile_name, self.get_format_version()))
|
||||
elif self._s_type is SettingsType.NEUTRINO_MP:
|
||||
self._main_window.set_title("DemonEditor [{} {} Neutrino-MP]".format(msg, profile_name))
|
||||
|
||||
def get_format_version(self):
|
||||
return 5 if self._settings.v5_support else 4
|
||||
|
||||
@run_idle
|
||||
def update_info_boxes_visible(self, visible):
|
||||
self._signal_box.set_visible(visible)
|
||||
self._receiver_info_box.set_visible(visible)
|
||||
|
||||
@run_idle
|
||||
def show_error_dialog(self, message):
|
||||
show_dialog(DialogType.ERROR, self._main_window, message)
|
||||
|
||||
|
||||
def start_app():
|
||||
app = Application()
|
||||
app.run(sys.argv)
|
||||
try:
|
||||
Settings.get_instance()
|
||||
except SettingsException as e:
|
||||
msg = "{} \n{}".format(e, "All setting were reset. Restart the program!")
|
||||
show_dialog(DialogType.INFO, transient=Gtk.Dialog(), text=msg)
|
||||
Settings.reset_to_default()
|
||||
else:
|
||||
app = Application()
|
||||
app.run(sys.argv)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -9,9 +9,9 @@ from app.commons import run_task
|
||||
from app.eparser import Service
|
||||
from app.eparser.ecommons import Flag, BouquetService, Bouquet, BqType
|
||||
from app.eparser.enigma.bouquets import BqServiceType, to_bouquet_id
|
||||
from app.settings import Profile
|
||||
from .uicommons import ViewTarget, BqGenType, Gtk, Gdk, HIDE_ICON, LOCKED_ICON, KeyboardKey, Column
|
||||
from app.settings import SettingsType
|
||||
from .dialogs import show_dialog, DialogType, get_chooser_dialog, WaitDialog
|
||||
from .uicommons import ViewTarget, BqGenType, Gtk, Gdk, HIDE_ICON, LOCKED_ICON, KeyboardKey, Column
|
||||
|
||||
|
||||
# ***************** Markers *******************#
|
||||
@@ -347,7 +347,9 @@ def update_picons_data(path, picons):
|
||||
return
|
||||
|
||||
for file in os.listdir(path):
|
||||
picons[file] = get_picon_pixbuf(path + file)
|
||||
pf = get_picon_pixbuf(path + file)
|
||||
if pf:
|
||||
picons[file] = pf
|
||||
|
||||
|
||||
def append_picons(picons, model):
|
||||
@@ -382,7 +384,7 @@ def assign_picon(target, srv_view, fav_view, transient, picons, settings, servic
|
||||
|
||||
if picon_id:
|
||||
if os.path.isfile(response):
|
||||
picons_path = settings.picons_dir_path
|
||||
picons_path = settings.picons_local_path
|
||||
os.makedirs(os.path.dirname(picons_path), exist_ok=True)
|
||||
picon_file = picons_path + picon_id
|
||||
shutil.copy(response, picon_file)
|
||||
@@ -464,8 +466,8 @@ def remove_all_unused_picons(settings, picons, services):
|
||||
|
||||
|
||||
def remove_picons(settings, picon_ids, picons):
|
||||
pions_path = settings.picons_dir_path
|
||||
backup_path = settings.backup_dir_path + "picons/"
|
||||
pions_path = settings.picons_local_path
|
||||
backup_path = settings.backup_local_path + "picons/"
|
||||
os.makedirs(os.path.dirname(backup_path), exist_ok=True)
|
||||
for p_id in picon_ids:
|
||||
picons[p_id] = None
|
||||
@@ -487,12 +489,15 @@ def is_only_one_item_selected(paths, transient):
|
||||
|
||||
|
||||
def get_picon_pixbuf(path):
|
||||
return GdkPixbuf.Pixbuf.new_from_file_at_scale(filename=path, width=32, height=32, preserve_aspect_ratio=True)
|
||||
try:
|
||||
return GdkPixbuf.Pixbuf.new_from_file_at_scale(filename=path, width=32, height=32, preserve_aspect_ratio=True)
|
||||
except GLib.GError as e:
|
||||
pass
|
||||
|
||||
|
||||
# ***************** Bouquets *********************#
|
||||
|
||||
def gen_bouquets(view, bq_view, transient, gen_type, tv_types, profile, callback):
|
||||
def gen_bouquets(view, bq_view, transient, gen_type, tv_types, s_type, callback):
|
||||
""" Auto-generate and append list of bouquets """
|
||||
fav_id_index = Column.SRV_FAV_ID
|
||||
index = Column.SRV_TYPE
|
||||
@@ -502,7 +507,7 @@ def gen_bouquets(view, bq_view, transient, gen_type, tv_types, profile, callback
|
||||
index = Column.SRV_POS
|
||||
|
||||
model, paths = view.get_selection().get_selected_rows()
|
||||
bq_type = BqType.BOUQUET.value if profile is Profile.NEUTRINO_MP else BqType.TV.value
|
||||
bq_type = BqType.BOUQUET.value if s_type is SettingsType.NEUTRINO_MP else BqType.TV.value
|
||||
if gen_type in (BqGenType.SAT, BqGenType.PACKAGE, BqGenType.TYPE):
|
||||
if not is_only_one_item_selected(paths, transient):
|
||||
return
|
||||
@@ -511,17 +516,17 @@ def gen_bouquets(view, bq_view, transient, gen_type, tv_types, profile, callback
|
||||
bq_type = BqType.RADIO.value
|
||||
append_bouquets(bq_type, bq_view, callback, fav_id_index, index, model,
|
||||
[service.package if gen_type is BqGenType.PACKAGE else
|
||||
service.pos if gen_type is BqGenType.SAT else service.service_type], profile)
|
||||
service.pos if gen_type is BqGenType.SAT else service.service_type], s_type)
|
||||
else:
|
||||
wait_dialog = WaitDialog(transient)
|
||||
wait_dialog.show()
|
||||
append_bouquets(bq_type, bq_view, callback, fav_id_index, index, model,
|
||||
{row[index] for row in model}, profile, wait_dialog)
|
||||
{row[index] for row in model}, s_type, wait_dialog)
|
||||
|
||||
|
||||
@run_task
|
||||
def append_bouquets(bq_type, bq_view, callback, fav_id_index, index, model, names, profile, wait_dialog=None):
|
||||
bq_index = 0 if profile is Profile.ENIGMA_2 else 1
|
||||
def append_bouquets(bq_type, bq_view, callback, fav_id_index, index, model, names, s_type, wait_dialog=None):
|
||||
bq_index = 0 if s_type is SettingsType.ENIGMA_2 else 1
|
||||
bq_view.expand_row(Gtk.TreePath(bq_index), 0)
|
||||
bqs_model = bq_view.get_model()
|
||||
bouquets_names = get_bouquets_names(bqs_model)
|
||||
@@ -583,14 +588,14 @@ def append_text_to_tview(char, view):
|
||||
view.scroll_to_mark(insert, 0.0, True, 0.0, 1.0)
|
||||
|
||||
|
||||
def get_iptv_url(row, profile):
|
||||
def get_iptv_url(row, s_type):
|
||||
""" Returns url from iptv type row """
|
||||
data = row[Column.FAV_ID].split(":" if profile is Profile.ENIGMA_2 else "::")
|
||||
if profile is Profile.ENIGMA_2:
|
||||
data = row[Column.FAV_ID].split(":" if s_type is SettingsType.ENIGMA_2 else "::")
|
||||
if s_type is SettingsType.ENIGMA_2:
|
||||
data = list(filter(lambda x: "http" in x, data))
|
||||
if data:
|
||||
url = data[0]
|
||||
return urllib.request.unquote(url) if profile is Profile.ENIGMA_2 else url
|
||||
return urllib.request.unquote(url) if s_type is SettingsType.ENIGMA_2 else url
|
||||
|
||||
|
||||
def on_popup_menu(menu, event):
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2018-2019 Dmitriy Yefremov
|
||||
Copyright (c) 2018-2020 Dmitriy Yefremov
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -26,7 +26,7 @@ THE SOFTWARE.
|
||||
Author: Dmitriy Yefremov
|
||||
|
||||
-->
|
||||
<interface>
|
||||
<interface domain="demon-editor">
|
||||
<requires lib="gtk+" version="3.16"/>
|
||||
<!-- interface-css-provider-path style.css -->
|
||||
<!-- interface-license-type mit -->
|
||||
@@ -674,9 +674,10 @@ Author: Dmitriy Yefremov
|
||||
</object>
|
||||
<object class="GtkApplicationWindow" id="main_window">
|
||||
<property name="width_request">640</property>
|
||||
<property name="height_request">480</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="window_position">center</property>
|
||||
<property name="icon_name">accessories-text-editor</property>
|
||||
<property name="icon_name">demon-editor</property>
|
||||
<property name="gravity">center</property>
|
||||
<property name="startup_id">DemonEditor</property>
|
||||
<signal name="check-resize" handler="on_resize" swapped="no"/>
|
||||
@@ -963,6 +964,9 @@ Author: Dmitriy Yefremov
|
||||
<object class="GtkScale" id="player_scale">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_left">5</property>
|
||||
<property name="margin_right">5</property>
|
||||
<property name="adjustment">player_scale_adjustment</property>
|
||||
<property name="restrict_to_fill_level">False</property>
|
||||
<property name="fill_level">0</property>
|
||||
@@ -2207,7 +2211,6 @@ Author: Dmitriy Yefremov
|
||||
<object class="GtkSeparator" id="bottom_separator">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_bottom">2</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
@@ -2240,7 +2243,7 @@ Author: Dmitriy Yefremov
|
||||
<object class="GtkImage" id="app_image">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="stock">gtk-edit</property>
|
||||
<property name="icon_name">demon-editor</property>
|
||||
<property name="icon_size">6</property>
|
||||
</object>
|
||||
<packing>
|
||||
@@ -2289,14 +2292,13 @@ Author: Dmitriy Yefremov
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkBox" id="status_bar_box">
|
||||
<property name="height_request">30</property>
|
||||
<property name="can_focus">False</property>
|
||||
<child>
|
||||
<object class="GtkBox" id="receiver_info_box">
|
||||
<property name="can_focus">False</property>
|
||||
<property name="halign">start</property>
|
||||
<property name="margin_left">10</property>
|
||||
<property name="spacing">2</property>
|
||||
<property name="spacing">5</property>
|
||||
<child>
|
||||
<object class="GtkImage" id="info_image">
|
||||
<property name="visible">True</property>
|
||||
@@ -2333,62 +2335,56 @@ Author: Dmitriy Yefremov
|
||||
</packing>
|
||||
</child>
|
||||
<child type="center">
|
||||
<object class="GtkBox" id="ip_status_box">
|
||||
<object class="GtkComboBoxText" id="profile_combo_box">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="focus_on_click">False</property>
|
||||
<property name="tooltip_text" translatable="yes">Current IP:</property>
|
||||
<property name="halign">center</property>
|
||||
<property name="spacing">2</property>
|
||||
<child>
|
||||
<object class="GtkImage" id="ip_status_image">
|
||||
<property name="visible">True</property>
|
||||
<property name="valign">center</property>
|
||||
<property name="margin_top">1</property>
|
||||
<property name="margin_bottom">1</property>
|
||||
<property name="active">0</property>
|
||||
<property name="has_entry">True</property>
|
||||
<child internal-child="entry">
|
||||
<object class="GtkEntry" id="profile_entry">
|
||||
<property name="can_focus">False</property>
|
||||
<property name="stock">gtk-connect</property>
|
||||
<property name="has_tooltip">True</property>
|
||||
<property name="halign">baseline</property>
|
||||
<property name="valign">baseline</property>
|
||||
<property name="margin_top">1</property>
|
||||
<property name="margin_bottom">1</property>
|
||||
<property name="editable">False</property>
|
||||
<property name="has_frame">False</property>
|
||||
<property name="max_width_chars">9</property>
|
||||
<property name="overwrite_mode">True</property>
|
||||
<property name="caps_lock_warning">False</property>
|
||||
<property name="primary_icon_stock">gtk-connect</property>
|
||||
<signal name="changed" handler="on_profile_changed" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="ip_status_label">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="halign">center</property>
|
||||
<property name="label" translatable="yes">Current IP:</property>
|
||||
<attributes>
|
||||
<attribute name="size" value="8000"/>
|
||||
</attributes>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="ip_label">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">127.0.0.1</property>
|
||||
<attributes>
|
||||
<attribute name="size" value="8000"/>
|
||||
</attributes>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="padding">5</property>
|
||||
<property name="position">3</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkImage" id="http_status_image">
|
||||
<property name="can_focus">False</property>
|
||||
<property name="tooltip_text" translatable="yes">No connection to the receiver</property>
|
||||
<property name="margin_left">10</property>
|
||||
<property name="margin_right">10</property>
|
||||
<property name="icon_name">network-offline</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="pack_type">end</property>
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkBox" id="signal_box">
|
||||
<property name="can_focus">False</property>
|
||||
@@ -2477,21 +2473,6 @@ Author: Dmitriy Yefremov
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkImage" id="http_status_image">
|
||||
<property name="can_focus">False</property>
|
||||
<property name="tooltip_text" translatable="yes">No connection to the receiver</property>
|
||||
<property name="margin_left">10</property>
|
||||
<property name="margin_right">10</property>
|
||||
<property name="icon_name">network-offline</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="pack_type">end</property>
|
||||
<property name="position">3</property>
|
||||
</packing>
|
||||
</child>
|
||||
<style>
|
||||
<class name="primary-toolbar"/>
|
||||
</style>
|
||||
|
||||
@@ -9,7 +9,7 @@ from gi.repository import GLib, GdkPixbuf
|
||||
from app.commons import run_idle, run_task
|
||||
from app.connections import upload_data, DownloadType
|
||||
from app.tools.picons import PiconsParser, parse_providers, Provider, convert_to
|
||||
from app.settings import Profile
|
||||
from app.settings import SettingsType
|
||||
from app.tools.satellites import SatellitesParser, SatelliteSource
|
||||
from .uicommons import Gtk, Gdk, UI_RESOURCES_PATH, TEXT_DOMAIN, TV_ICON
|
||||
from .dialogs import show_dialog, DialogType, get_message
|
||||
@@ -86,13 +86,13 @@ class PiconsDialog:
|
||||
self._url_entry.get_style_context().add_provider_for_screen(Gdk.Screen.get_default(), self._style_provider,
|
||||
Gtk.STYLE_PROVIDER_PRIORITY_USER)
|
||||
self._settings = settings
|
||||
self._profile = settings.profile
|
||||
self._s_type = settings.setting_type
|
||||
self._ip_entry.set_text(self._settings.host)
|
||||
self._picons_entry.set_text(self._settings.picons_path)
|
||||
self._picons_path = self._settings.picons_dir_path
|
||||
self._picons_path = self._settings.picons_local_path
|
||||
self._picons_dir_entry.set_text(self._picons_path)
|
||||
|
||||
if not len(self._picon_ids) and self._profile is Profile.ENIGMA_2:
|
||||
if not len(self._picon_ids) and self._s_type is SettingsType.ENIGMA_2:
|
||||
message = get_message("To automatically set the identifiers for picons,\n"
|
||||
"first load the required services list into the main application window.")
|
||||
self.show_info_message(message, Gtk.MessageType.WARNING)
|
||||
@@ -342,7 +342,7 @@ class PiconsDialog:
|
||||
self._expander.set_expanded(True)
|
||||
convert_to(src_path=picons_path,
|
||||
dest_path=save_path,
|
||||
profile=Profile.ENIGMA_2,
|
||||
s_type=SettingsType.ENIGMA_2,
|
||||
callback=self.append_output,
|
||||
done_callback=lambda: self.show_info_message(get_message("Done!"), Gtk.MessageType.INFO))
|
||||
|
||||
@@ -362,10 +362,10 @@ class PiconsDialog:
|
||||
show_dialog(dialog_type, self._dialog, message)
|
||||
|
||||
def get_picons_format(self):
|
||||
picon_format = Profile.ENIGMA_2
|
||||
picon_format = SettingsType.ENIGMA_2
|
||||
|
||||
if self._neutrino_mp_radio_button.get_active():
|
||||
picon_format = Profile.NEUTRINO_MP
|
||||
picon_format = SettingsType.NEUTRINO_MP
|
||||
|
||||
return picon_format
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ class SatellitesDialog:
|
||||
_aggr = [None for x in range(9)] # aggregate
|
||||
|
||||
def __init__(self, transient, settings):
|
||||
self._data_path = settings.data_dir_path + "satellites.xml"
|
||||
self._data_path = settings.data_local_path + "satellites.xml"
|
||||
self._settings = settings
|
||||
|
||||
handlers = {"on_open": self.on_open,
|
||||
|
||||
@@ -6,7 +6,7 @@ from app.eparser import Service
|
||||
from app.eparser.ecommons import MODULATION, Inversion, ROLL_OFF, Pilot, Flag, Pids, POLARIZATION, \
|
||||
get_key_by_value, get_value_by_name, FEC_DEFAULT, PLS_MODE, SERVICE_TYPE, T_MODULATION, C_MODULATION, TrType, \
|
||||
SystemCable, T_SYSTEM, BANDWIDTH, TRANSMISSION_MODE, GUARD_INTERVAL, HIERARCHY, T_FEC
|
||||
from app.settings import Profile
|
||||
from app.settings import SettingsType
|
||||
from .uicommons import Gtk, Gdk, UI_RESOURCES_PATH, HIDE_ICON, TEXT_DOMAIN, CODED_ICON, Column, IS_GNOME_SESSION
|
||||
from .dialogs import show_dialog, DialogType, Action, get_dialogs_string
|
||||
from .main_helper import get_base_model
|
||||
@@ -52,10 +52,10 @@ class ServiceDetailsDialog:
|
||||
|
||||
self._dialog = builder.get_object("service_details_dialog")
|
||||
self._dialog.set_transient_for(transient)
|
||||
self._profile = settings.profile
|
||||
self._s_type = settings.setting_type
|
||||
self._tr_type = None
|
||||
self._satellites_xml_path = settings.data_dir_path + "satellites.xml"
|
||||
self._picons_dir_path = settings.picons_dir_path
|
||||
self._satellites_xml_path = settings.data_local_path + "satellites.xml"
|
||||
self._picons_dir_path = settings.picons_local_path
|
||||
self._services_view = srv_view
|
||||
self._fav_view = fav_view
|
||||
self._action = action
|
||||
@@ -69,7 +69,7 @@ class ServiceDetailsDialog:
|
||||
# Patterns
|
||||
self._DIGIT_PATTERN = re.compile("\\D")
|
||||
self._NON_EMPTY_PATTERN = re.compile("(?:^[\\s]*$|\\D)")
|
||||
self._CAID_PATTERN = re.compile("(?:^[\\s]*$)|(C:[0-9a-z]{4})(,C:[0-9a-z]{4})*")
|
||||
self._CAID_PATTERN = re.compile("(?:^[\\s]*$)|(C:[0-9a-fA-F]{1,4})(,C:[0-9a-fA-F]{1,4})*")
|
||||
# Buttons
|
||||
self._apply_button = builder.get_object("apply_button")
|
||||
self._create_button = builder.get_object("create_button")
|
||||
@@ -197,7 +197,7 @@ class ServiceDetailsDialog:
|
||||
self._package_entry.set_text(srv.package)
|
||||
self._sid_entry.set_text(str(int(srv.ssid, 16)))
|
||||
# Transponder
|
||||
if self._profile is Profile.ENIGMA_2:
|
||||
if self._s_type is SettingsType.ENIGMA_2:
|
||||
self._tr_type = TrType(srv.transponder_type)
|
||||
self._freq_entry.set_text(srv.freq)
|
||||
self._rate_entry.set_text(srv.rate)
|
||||
@@ -211,10 +211,10 @@ class ServiceDetailsDialog:
|
||||
else:
|
||||
self.set_sat_positions(srv.pos)
|
||||
|
||||
if self._profile is Profile.ENIGMA_2:
|
||||
if self._s_type is SettingsType.ENIGMA_2:
|
||||
self.init_enigma2_service_data(srv)
|
||||
self.init_enigma2_transponder_data(srv)
|
||||
elif self._profile is Profile.NEUTRINO_MP:
|
||||
elif self._s_type is SettingsType.NEUTRINO_MP:
|
||||
self.init_neutrino_data(srv)
|
||||
self.init_neutrino_ui_elements()
|
||||
|
||||
@@ -484,9 +484,9 @@ class ServiceDetailsDialog:
|
||||
transponder=transponder)
|
||||
|
||||
def get_flags(self):
|
||||
if self._profile is Profile.ENIGMA_2:
|
||||
if self._s_type is SettingsType.ENIGMA_2:
|
||||
return self.get_enigma2_flags()
|
||||
elif self._profile is Profile.NEUTRINO_MP:
|
||||
elif self._s_type is SettingsType.NEUTRINO_MP:
|
||||
return self._old_service.flags_cas
|
||||
|
||||
def get_enigma2_flags(self):
|
||||
@@ -532,12 +532,12 @@ class ServiceDetailsDialog:
|
||||
net_id, tr_id = int(self._network_id_entry.get_text()), int(self._transponder_id_entry.get_text())
|
||||
service_type = self._srv_type_entry.get_text()
|
||||
|
||||
if self._profile is Profile.ENIGMA_2:
|
||||
if self._s_type is SettingsType.ENIGMA_2:
|
||||
namespace = int(self._namespace_entry.get_text())
|
||||
data_id = self._ENIGMA2_DATA_ID.format(ssid, namespace, tr_id, net_id, service_type, 0)
|
||||
fav_id = self._ENIGMA2_FAV_ID.format(ssid, tr_id, net_id, namespace)
|
||||
return fav_id, data_id
|
||||
elif self._profile is Profile.NEUTRINO_MP:
|
||||
elif self._s_type is SettingsType.NEUTRINO_MP:
|
||||
fav_id = self._NEUTRINO_FAV_ID.format(tr_id, net_id, ssid)
|
||||
return fav_id, self._old_service.data_id
|
||||
|
||||
@@ -548,7 +548,7 @@ class ServiceDetailsDialog:
|
||||
fec = self._fec_combo_box.get_active_id()
|
||||
system = self._sys_combo_box.get_active_id()
|
||||
|
||||
if self._tr_type is TrType.Satellite or self._profile is Profile.NEUTRINO_MP:
|
||||
if self._tr_type is TrType.Satellite or self._s_type is SettingsType.NEUTRINO_MP:
|
||||
freq = self._freq_entry.get_text()
|
||||
rate = self._rate_entry.get_text()
|
||||
pol = self._pol_combo_box.get_active_id()
|
||||
@@ -571,7 +571,7 @@ class ServiceDetailsDialog:
|
||||
inv = get_value_by_name(Inversion, self._invertion_combo_box.get_active_id())
|
||||
srv_sys = "0" # !!!
|
||||
|
||||
if self._profile is Profile.ENIGMA_2:
|
||||
if self._s_type is SettingsType.ENIGMA_2:
|
||||
dvb_s_tr = self._ENIGMA2_TRANSPONDER_DATA.format("s", freq, rate, pol, fec, sat_pos, inv, srv_sys)
|
||||
if sys == "DVB-S":
|
||||
return dvb_s_tr
|
||||
@@ -585,7 +585,7 @@ class ServiceDetailsDialog:
|
||||
st_id = self._stream_id_entry.get_text()
|
||||
pls = ":{}:{}:{}".format(st_id, pls_code, pls_mode) if pls_mode and pls_code and st_id else ""
|
||||
return "{}:{}:{}:{}:{}{}".format(dvb_s_tr, flag, mod, roll_off, pilot, pls)
|
||||
elif self._profile is Profile.NEUTRINO_MP:
|
||||
elif self._s_type is SettingsType.NEUTRINO_MP:
|
||||
on_id, tr_id = int(self._network_id_entry.get_text()), int(self._transponder_id_entry.get_text())
|
||||
mod = self.get_value_from_combobox_id(self._mod_combo_box, MODULATION) if sys == "DVB-S2" else None
|
||||
srv_sys = None
|
||||
@@ -682,7 +682,7 @@ class ServiceDetailsDialog:
|
||||
return True
|
||||
|
||||
def update_reference(self, entry, event=None):
|
||||
if not self.is_data_correct() or (event is None and self._profile is Profile.NEUTRINO_MP):
|
||||
if not self.is_data_correct() or (event is None and self._s_type is SettingsType.NEUTRINO_MP):
|
||||
return
|
||||
self.update_reference_entry()
|
||||
|
||||
@@ -691,7 +691,7 @@ class ServiceDetailsDialog:
|
||||
ssid = int(self._sid_entry.get_text())
|
||||
tid = int(self._transponder_id_entry.get_text())
|
||||
nid = int(self._network_id_entry.get_text())
|
||||
if self._profile is Profile.ENIGMA_2:
|
||||
if self._s_type is SettingsType.ENIGMA_2:
|
||||
on_id = int(self._namespace_entry.get_text())
|
||||
ref = "1:0:{:X}:{:X}:{:X}:{:X}:{:X}:0:0:0".format(srv_type, ssid, tid, nid, on_id)
|
||||
self._reference_entry.set_text(ref)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,10 +1,13 @@
|
||||
import os
|
||||
from enum import Enum
|
||||
from pathlib import Path
|
||||
|
||||
from app.commons import run_task, run_idle
|
||||
from app.connections import test_telnet, test_ftp, TestException, test_http
|
||||
from app.settings import Profile
|
||||
from .uicommons import Gtk, Gdk, UI_RESOURCES_PATH, TEXT_DOMAIN, FavClickMode
|
||||
from .main_helper import update_entry_data
|
||||
from app.connections import test_telnet, test_ftp, TestException, test_http, HttpApiException
|
||||
from app.settings import SettingsType, Settings
|
||||
from app.ui.dialogs import show_dialog, DialogType
|
||||
from .main_helper import update_entry_data, scroll_to
|
||||
from .uicommons import Gtk, Gdk, UI_RESOURCES_PATH, FavClickMode, DEFAULT_ICON
|
||||
|
||||
|
||||
def show_settings_dialog(transient, options):
|
||||
@@ -19,26 +22,42 @@ class Property(Enum):
|
||||
|
||||
class SettingsDialog:
|
||||
|
||||
def __init__(self, transient, settings):
|
||||
def __init__(self, transient, settings: Settings):
|
||||
handlers = {"on_field_icon_press": self.on_field_icon_press,
|
||||
"on_profile_changed": self.on_profile_changed,
|
||||
"on_settings_type_changed": self.on_settings_type_changed,
|
||||
"on_reset": self.on_reset,
|
||||
"on_response": self.on_response,
|
||||
"apply_settings": self.apply_settings,
|
||||
"on_apply_profile_settings": self.on_apply_profile_settings,
|
||||
"on_connection_test": self.on_connection_test,
|
||||
"on_info_bar_close": self.on_info_bar_close,
|
||||
"on_set_color_switch_state": self.on_set_color_switch_state,
|
||||
"on_http_mode_switch_state": self.on_http_mode_switch_state,
|
||||
"on_yt_dl_switch_state": self.on_yt_dl_switch_state,
|
||||
"on_send_to_switch_state": self.on_send_to_switch_state}
|
||||
"on_send_to_switch_state": self.on_send_to_switch_state,
|
||||
"on_profile_add": self.on_profile_add,
|
||||
"on_profile_edit": self.on_profile_edit,
|
||||
"on_profile_remove": self.on_profile_remove,
|
||||
"on_profile_deleted": self.on_profile_deleted,
|
||||
"on_profile_inserted": self.on_profile_inserted,
|
||||
"on_profile_edited": self.on_profile_edited,
|
||||
"on_profile_selected": self.on_profile_selected,
|
||||
"on_profile_set_default": self.on_profile_set_default,
|
||||
"on_lang_changed": self.on_lang_changed,
|
||||
"on_main_settings_visible": self.on_main_settings_visible,
|
||||
"on_network_settings_visible": self.on_network_settings_visible,
|
||||
"on_http_use_ssl_toggled": self.on_http_use_ssl_toggled,
|
||||
"on_click_mode_togged": self.on_click_mode_togged,
|
||||
"on_view_popup_menu": self.on_view_popup_menu}
|
||||
|
||||
builder = Gtk.Builder()
|
||||
builder.set_translation_domain(TEXT_DOMAIN)
|
||||
builder.add_from_file(UI_RESOURCES_PATH + "settings_dialog.glade")
|
||||
builder.connect_signals(handlers)
|
||||
|
||||
self._dialog = builder.get_object("settings_dialog")
|
||||
self._dialog.set_transient_for(transient)
|
||||
self._header_bar = builder.get_object("header_bar")
|
||||
self._main_stack = builder.get_object("main_stack")
|
||||
# Network
|
||||
self._host_field = builder.get_object("host_field")
|
||||
self._port_field = builder.get_object("port_field")
|
||||
@@ -47,6 +66,7 @@ class SettingsDialog:
|
||||
self._http_login_field = builder.get_object("http_login_field")
|
||||
self._http_password_field = builder.get_object("http_password_field")
|
||||
self._http_port_field = builder.get_object("http_port_field")
|
||||
self._http_use_ssl_check_button = builder.get_object("http_use_ssl_check_button")
|
||||
self._telnet_login_field = builder.get_object("telnet_login_field")
|
||||
self._telnet_password_field = builder.get_object("telnet_password_field")
|
||||
self._telnet_port_field = builder.get_object("telnet_port_field")
|
||||
@@ -64,7 +84,7 @@ class SettingsDialog:
|
||||
self._info_bar = builder.get_object("info_bar")
|
||||
self._message_label = builder.get_object("info_bar_message_label")
|
||||
self._test_spinner = builder.get_object("test_spinner")
|
||||
# Profile
|
||||
# Settings type
|
||||
self._enigma_radio_button = builder.get_object("enigma_radio_button")
|
||||
self._neutrino_radio_button = builder.get_object("neutrino_radio_button")
|
||||
self._support_ver5_switch = builder.get_object("support_ver5_switch")
|
||||
@@ -77,6 +97,7 @@ class SettingsDialog:
|
||||
self._set_color_switch = builder.get_object("set_color_switch")
|
||||
self._new_color_button = builder.get_object("new_color_button")
|
||||
self._extra_color_button = builder.get_object("extra_color_button")
|
||||
self._load_on_startup_switch = builder.get_object("load_on_startup_switch")
|
||||
# HTTP API
|
||||
self._support_http_api_switch = builder.get_object("support_http_api_switch")
|
||||
self._enable_y_dl_switch = builder.get_object("enable_y_dl_switch")
|
||||
@@ -85,46 +106,77 @@ class SettingsDialog:
|
||||
self._click_mode_stream_button = builder.get_object("click_mode_stream_button")
|
||||
self._click_mode_play_button = builder.get_object("click_mode_play_button")
|
||||
self._click_mode_zap_button = builder.get_object("click_mode_zap_button")
|
||||
self._click_mode_zap_and_play_button = builder.get_object("click_mode_zap_and_play_button")
|
||||
self._click_mode_zap_button.bind_property("sensitive", self._click_mode_play_button, "sensitive")
|
||||
self._click_mode_zap_button.bind_property("sensitive", self._click_mode_zap_and_play_button, "sensitive")
|
||||
self._click_mode_zap_button.bind_property("sensitive", self._enable_send_to_switch, "sensitive")
|
||||
self._enable_send_to_switch.bind_property("sensitive", builder.get_object("enable_send_to_label"), "sensitive")
|
||||
self._extra_support_grid.bind_property("sensitive", builder.get_object("v5_support_grid"), "sensitive")
|
||||
# Profiles
|
||||
self._profile_view = builder.get_object("profile_tree_view")
|
||||
self._profile_add_button = builder.get_object("profile_add_button")
|
||||
self._profile_remove_button = builder.get_object("profile_remove_button")
|
||||
self._apply_profile_button = builder.get_object("apply_profile_button")
|
||||
self._apply_profile_button.bind_property("visible", builder.get_object("header_separator"), "visible")
|
||||
# Language
|
||||
self._lang_combo_box = builder.get_object("lang_combo_box")
|
||||
# Settings
|
||||
self._settings = settings
|
||||
self._active_profile = settings.profile
|
||||
self._ext_settings = settings
|
||||
self._settings = Settings(settings.settings)
|
||||
self._profiles = self._settings.profiles
|
||||
self._s_type = self._settings.setting_type
|
||||
self.set_settings()
|
||||
self.init_ui_elements(self._active_profile)
|
||||
self.init_ui_elements(self._s_type)
|
||||
self.init_profiles()
|
||||
|
||||
def init_ui_elements(self, profile):
|
||||
is_enigma_profile = profile is Profile.ENIGMA_2
|
||||
self._neutrino_radio_button.set_active(profile is Profile.NEUTRINO_MP)
|
||||
@run_idle
|
||||
def init_ui_elements(self, s_type):
|
||||
is_enigma_profile = s_type is SettingsType.ENIGMA_2
|
||||
self._neutrino_radio_button.set_active(s_type is SettingsType.NEUTRINO_MP)
|
||||
self.update_header_bar()
|
||||
self._settings_stack.get_child_by_name(Property.HTTP.value).set_visible(is_enigma_profile)
|
||||
self._program_frame.set_sensitive(is_enigma_profile)
|
||||
self._extra_support_grid.set_sensitive(is_enigma_profile)
|
||||
http_active = self._support_http_api_switch.get_active()
|
||||
self._click_mode_zap_button.set_sensitive(is_enigma_profile and http_active)
|
||||
self._lang_combo_box.set_active_id(self._settings.language)
|
||||
self.on_info_bar_close() if is_enigma_profile else self.show_info_message(
|
||||
"The Neutrino has only experimental support. Not all features are supported!", Gtk.MessageType.WARNING)
|
||||
|
||||
def show(self):
|
||||
response = self._dialog.run()
|
||||
if response == Gtk.ResponseType.OK:
|
||||
self.apply_settings()
|
||||
self._dialog.destroy()
|
||||
def init_profiles(self):
|
||||
p_def = self._settings.default_profile
|
||||
for p in self._profiles:
|
||||
self._profile_view.get_model().append((p, DEFAULT_ICON if p == p_def else None))
|
||||
self._profile_remove_button.set_sensitive(len(self._profile_view.get_model()) > 1)
|
||||
|
||||
return response
|
||||
def update_header_bar(self):
|
||||
label, sep, st = self._header_bar.get_subtitle().partition(":")
|
||||
if self._s_type is SettingsType.ENIGMA_2:
|
||||
self._header_bar.set_subtitle("{}: {}".format(label, self._enigma_radio_button.get_label()))
|
||||
elif self._s_type is SettingsType.NEUTRINO_MP:
|
||||
self._header_bar.set_subtitle("{}: {}".format(label, self._neutrino_radio_button.get_label()))
|
||||
|
||||
def show(self):
|
||||
self._dialog.run()
|
||||
|
||||
def on_response(self, dialog, resp):
|
||||
if resp == Gtk.ResponseType.OK and not self.apply_settings():
|
||||
return
|
||||
|
||||
self._dialog.destroy()
|
||||
return resp
|
||||
|
||||
def on_field_icon_press(self, entry, icon, event_button):
|
||||
update_entry_data(entry, self._dialog, self._settings)
|
||||
|
||||
def on_profile_changed(self, item):
|
||||
profile = Profile.ENIGMA_2 if self._enigma_radio_button.get_active() else Profile.NEUTRINO_MP
|
||||
self._active_profile = profile
|
||||
self._settings.profile = profile
|
||||
self.set_settings()
|
||||
def on_settings_type_changed(self, item):
|
||||
profile = SettingsType.ENIGMA_2 if self._enigma_radio_button.get_active() else SettingsType.NEUTRINO_MP
|
||||
self._s_type = profile
|
||||
self._settings.setting_type = profile
|
||||
self.on_reset()
|
||||
self.init_ui_elements(profile)
|
||||
|
||||
def on_reset(self, item):
|
||||
def on_reset(self, item=None):
|
||||
self._settings.reset()
|
||||
self.set_settings()
|
||||
|
||||
@@ -136,6 +188,7 @@ class SettingsDialog:
|
||||
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._http_use_ssl_check_button.set_active(self._settings.http_use_ssl)
|
||||
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)
|
||||
@@ -144,14 +197,15 @@ class SettingsDialog:
|
||||
self._user_bouquet_field.set_text(self._settings.user_bouquet_path)
|
||||
self._satellites_xml_field.set_text(self._settings.satellites_xml_path)
|
||||
self._picons_field.set_text(self._settings.picons_path)
|
||||
self._data_dir_field.set_text(self._settings.data_dir_path)
|
||||
self._picons_dir_field.set_text(self._settings.picons_dir_path)
|
||||
self._backup_dir_field.set_text(self._settings.backup_dir_path)
|
||||
self._data_dir_field.set_text(self._settings.data_local_path)
|
||||
self._picons_dir_field.set_text(self._settings.picons_local_path)
|
||||
self._backup_dir_field.set_text(self._settings.backup_local_path)
|
||||
self._before_save_switch.set_active(self._settings.backup_before_save)
|
||||
self._before_downloading_switch.set_active(self._settings.backup_before_downloading)
|
||||
self.set_fav_click_mode(self._settings.fav_click_mode)
|
||||
self._load_on_startup_switch.set_active(self._settings.load_last_config)
|
||||
|
||||
if self._active_profile is Profile.ENIGMA_2:
|
||||
if self._s_type is SettingsType.ENIGMA_2:
|
||||
self._support_ver5_switch.set_active(self._settings.v5_support)
|
||||
self._support_http_api_switch.set_active(self._settings.http_api_support)
|
||||
self._enable_y_dl_switch.set_active(self._settings.enable_yt_dl)
|
||||
@@ -164,9 +218,9 @@ class SettingsDialog:
|
||||
self._new_color_button.set_rgba(new_rgb)
|
||||
self._extra_color_button.set_rgba(extra_rgb)
|
||||
|
||||
def apply_settings(self, item=None):
|
||||
self._active_profile = Profile.ENIGMA_2 if self._enigma_radio_button.get_active() else Profile.NEUTRINO_MP
|
||||
self._settings.profile = self._active_profile
|
||||
def on_apply_profile_settings(self, item):
|
||||
self._s_type = SettingsType.ENIGMA_2 if self._enigma_radio_button.get_active() else SettingsType.NEUTRINO_MP
|
||||
self._settings.setting_type = self._s_type
|
||||
self._settings.host = self._host_field.get_text()
|
||||
self._settings.port = self._port_field.get_text()
|
||||
self._settings.user = self._login_field.get_text()
|
||||
@@ -174,6 +228,7 @@ class SettingsDialog:
|
||||
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.http_use_ssl = self._http_use_ssl_check_button.get_active()
|
||||
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()
|
||||
@@ -182,23 +237,33 @@ class SettingsDialog:
|
||||
self._settings.user_bouquet_path = self._user_bouquet_field.get_text()
|
||||
self._settings.satellites_xml_path = self._satellites_xml_field.get_text()
|
||||
self._settings.picons_path = self._picons_field.get_text()
|
||||
self._settings.data_dir_path = self._data_dir_field.get_text()
|
||||
self._settings.picons_dir_path = self._picons_dir_field.get_text()
|
||||
self._settings.backup_dir_path = self._backup_dir_field.get_text()
|
||||
self._settings.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()
|
||||
self._settings.data_local_path = self._data_dir_field.get_text()
|
||||
self._settings.picons_local_path = self._picons_dir_field.get_text()
|
||||
self._settings.backup_local_path = self._backup_dir_field.get_text()
|
||||
|
||||
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()
|
||||
def apply_settings(self, item=None):
|
||||
if show_dialog(DialogType.QUESTION, self._dialog) == Gtk.ResponseType.CANCEL:
|
||||
return
|
||||
|
||||
self._settings.save()
|
||||
self._ext_settings.profiles = self._settings.profiles
|
||||
self._ext_settings.backup_before_save = self._before_save_switch.get_active()
|
||||
self._ext_settings.backup_before_downloading = self._before_downloading_switch.get_active()
|
||||
self._ext_settings.fav_click_mode = self.get_fav_click_mode()
|
||||
self._ext_settings.language = self._lang_combo_box.get_active_id()
|
||||
self._ext_settings.load_last_config = self._load_on_startup_switch.get_active()
|
||||
|
||||
if self._s_type is SettingsType.ENIGMA_2:
|
||||
self._ext_settings.use_colors = self._set_color_switch.get_active()
|
||||
self._ext_settings.new_color = self._new_color_button.get_rgba().to_string()
|
||||
self._ext_settings.extra_color = self._extra_color_button.get_rgba().to_string()
|
||||
self._ext_settings.v5_support = self._support_ver5_switch.get_active()
|
||||
self._ext_settings.http_api_support = self._support_http_api_switch.get_active()
|
||||
self._ext_settings.enable_yt_dl = self._enable_y_dl_switch.get_active()
|
||||
self._ext_settings.enable_send_to = self._enable_send_to_switch.get_active()
|
||||
|
||||
self._ext_settings.default_profile = list(filter(lambda r: r[1], self._profile_view.get_model()))[0][0]
|
||||
self._ext_settings.save()
|
||||
return True
|
||||
|
||||
@run_task
|
||||
def on_connection_test(self, item):
|
||||
@@ -216,10 +281,13 @@ class SettingsDialog:
|
||||
def test_http(self):
|
||||
user, password = self._http_login_field.get_text(), self._http_password_field.get_text()
|
||||
host, port = self._host_field.get_text(), self._http_port_field.get_text()
|
||||
use_ssl = self._http_use_ssl_check_button.get_active()
|
||||
try:
|
||||
self.show_info_message(test_http(host, port, user, password), Gtk.MessageType.INFO)
|
||||
self.show_info_message(test_http(host, port, user, password, use_ssl=use_ssl), Gtk.MessageType.INFO)
|
||||
except TestException as e:
|
||||
self.show_info_message(str(e), Gtk.MessageType.ERROR)
|
||||
except HttpApiException as e:
|
||||
self.show_info_message(str(e), Gtk.MessageType.WARNING)
|
||||
finally:
|
||||
self.show_spinner(False)
|
||||
|
||||
@@ -263,7 +331,9 @@ class SettingsDialog:
|
||||
|
||||
def on_http_mode_switch_state(self, switch, state):
|
||||
self._click_mode_zap_button.set_sensitive(state)
|
||||
if self._click_mode_play_button.get_active() or self._click_mode_zap_button.get_active():
|
||||
if any((self._click_mode_play_button.get_active(),
|
||||
self._click_mode_zap_button.get_active(),
|
||||
self._click_mode_zap_and_play_button.get_active())):
|
||||
self._click_mode_disabled_button.set_active(True)
|
||||
|
||||
def on_yt_dl_switch_state(self, switch, state):
|
||||
@@ -272,6 +342,123 @@ class SettingsDialog:
|
||||
def on_send_to_switch_state(self, switch, state):
|
||||
self.show_info_message("Not implemented yet!", Gtk.MessageType.WARNING)
|
||||
|
||||
def on_profile_add(self, item):
|
||||
model = self._profile_view.get_model()
|
||||
count = 0
|
||||
name = "profile"
|
||||
while name in self._profiles:
|
||||
count += 1
|
||||
name = "profile{}".format(count)
|
||||
|
||||
self._profiles[name] = self._s_type.get_default_settings()
|
||||
model.append((name, None))
|
||||
scroll_to(len(model) - 1, self._profile_view)
|
||||
self.on_profile_selected(self._profile_view)
|
||||
p = name + "/"
|
||||
self._settings.data_local_path += p
|
||||
self._settings.picons_local_path += p
|
||||
self._settings.backup_local_path += p
|
||||
self.on_reset()
|
||||
|
||||
def on_profile_edit(self, item=None):
|
||||
model, paths = self._profile_view.get_selection().get_selected_rows()
|
||||
self._profile_view.set_cursor(paths, self._profile_view.get_column(0), True)
|
||||
|
||||
def on_profile_remove(self, item):
|
||||
model, paths = self._profile_view.get_selection().get_selected_rows()
|
||||
if paths:
|
||||
row = model[paths]
|
||||
is_default = row[1]
|
||||
self._profiles.pop(row[0], None)
|
||||
del model[paths]
|
||||
|
||||
if is_default:
|
||||
model.set_value(model.get_iter_first(), 1, DEFAULT_ICON)
|
||||
|
||||
def on_profile_deleted(self, model, paths):
|
||||
self._profile_remove_button.set_sensitive(len(model) > 1)
|
||||
|
||||
def on_profile_edited(self, render, path, new_value):
|
||||
p_name = render.get_property("text")
|
||||
p_name = self._profiles.pop(p_name, None)
|
||||
if p_name:
|
||||
row = self._profile_view.get_model()[path]
|
||||
row[0] = new_value
|
||||
self._profiles[new_value] = p_name
|
||||
|
||||
if p_name != new_value:
|
||||
self.update_local_paths(new_value)
|
||||
self.on_profile_selected(self._profile_view)
|
||||
|
||||
def update_local_paths(self, p_name, force_rename=False):
|
||||
data_path = self._settings.data_local_path
|
||||
picons_path = self._settings.picons_local_path
|
||||
backup_path = self._settings.backup_local_path
|
||||
|
||||
self._settings.data_local_path = "{}/{}/".format(Path(data_path).parent, p_name)
|
||||
self._settings.picons_local_path = "{}/{}/".format(Path(picons_path).parent, p_name)
|
||||
self._settings.backup_local_path = "{}/{}/".format(Path(backup_path).parent, p_name)
|
||||
|
||||
if force_rename:
|
||||
try:
|
||||
if os.path.isdir(picons_path):
|
||||
os.rename(picons_path, self._settings.picons_local_path)
|
||||
if os.path.isdir(data_path):
|
||||
os.rename(data_path, self._settings.data_local_path)
|
||||
if os.path.isdir(backup_path):
|
||||
os.rename(backup_path, self._settings.backup_local_path)
|
||||
except OSError as e:
|
||||
self.show_info_message(str(e), Gtk.MessageType.ERROR)
|
||||
|
||||
def on_profile_selected(self, view):
|
||||
model, paths = self._profile_view.get_selection().get_selected_rows()
|
||||
if paths:
|
||||
profile = model.get_value(model.get_iter(paths), 0)
|
||||
self._settings.current_profile = profile
|
||||
if self._settings.setting_type is SettingsType.ENIGMA_2:
|
||||
self._enigma_radio_button.activate()
|
||||
else:
|
||||
self._neutrino_radio_button.activate()
|
||||
self.set_settings()
|
||||
|
||||
def on_profile_set_default(self, item):
|
||||
model, paths = self._profile_view.get_selection().get_selected_rows()
|
||||
if paths:
|
||||
itr = model.get_iter(paths)
|
||||
model.foreach(lambda m, p, i: model.set_value(i, 1, None))
|
||||
model.set_value(itr, 1, DEFAULT_ICON)
|
||||
self._settings.default_profile = model.get_value(itr, 0)
|
||||
|
||||
def on_profile_inserted(self, model, path, itr):
|
||||
self._profile_remove_button.set_sensitive(len(model) > 1)
|
||||
|
||||
def on_lang_changed(self, box):
|
||||
if box.get_active_id() != self._settings.language:
|
||||
self.show_info_message("Save and restart the program to apply the settings.", Gtk.MessageType.WARNING)
|
||||
|
||||
def on_main_settings_visible(self, stack, param):
|
||||
self._apply_profile_button.set_visible(stack.get_visible_child_name() == "profiles")
|
||||
|
||||
def on_network_settings_visible(self, stack, param):
|
||||
self._http_use_ssl_check_button.set_visible(Property(stack.get_visible_child_name()) is Property.HTTP)
|
||||
|
||||
def on_http_use_ssl_toggled(self, button):
|
||||
active = button.get_active()
|
||||
self._settings.http_use_ssl = active
|
||||
port = "443" if active else "80"
|
||||
self._http_port_field.set_text(port)
|
||||
self._settings.http_port = port
|
||||
|
||||
def on_click_mode_togged(self, button):
|
||||
if self._main_stack.get_visible_child_name() != "extra":
|
||||
return
|
||||
|
||||
mode = self.get_fav_click_mode()
|
||||
if mode is FavClickMode.PLAY:
|
||||
self.show_info_message("Operates in standby mode or current active transponder!", Gtk.MessageType.WARNING)
|
||||
else:
|
||||
self.on_info_bar_close()
|
||||
|
||||
@run_idle
|
||||
def set_fav_click_mode(self, mode):
|
||||
mode = FavClickMode(mode)
|
||||
@@ -279,17 +466,24 @@ class SettingsDialog:
|
||||
self._click_mode_stream_button.set_active(mode is FavClickMode.STREAM)
|
||||
self._click_mode_play_button.set_active(mode is FavClickMode.PLAY)
|
||||
self._click_mode_zap_button.set_active(mode is FavClickMode.ZAP)
|
||||
self._click_mode_zap_and_play_button.set_active(mode is FavClickMode.ZAP_PLAY)
|
||||
|
||||
def get_fav_click_mode(self):
|
||||
if self._click_mode_zap_button.get_active():
|
||||
return FavClickMode.ZAP
|
||||
if self._click_mode_play_button.get_active():
|
||||
return FavClickMode.PLAY
|
||||
if self._click_mode_zap_and_play_button.get_active():
|
||||
return FavClickMode.ZAP_PLAY
|
||||
if self._click_mode_stream_button.get_active():
|
||||
return FavClickMode.STREAM
|
||||
|
||||
return FavClickMode.DISABLED
|
||||
|
||||
def on_view_popup_menu(self, menu, event):
|
||||
if event.get_event_type() == Gdk.EventType.BUTTON_PRESS and event.button == Gdk.BUTTON_SECONDARY:
|
||||
menu.popup(None, None, None, None, event.button, event.time)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
pass
|
||||
|
||||
@@ -87,7 +87,7 @@ class LinksTransmitter:
|
||||
def on_play(self, res):
|
||||
""" Play callback """
|
||||
GLib.idle_add(self._url_entry.set_sensitive, True)
|
||||
res = res.get("result", None) if res else res
|
||||
res = res.get("e2state", None) if res else res
|
||||
self._url_entry.set_name("GtkEntry" if res else "digit-entry")
|
||||
|
||||
def on_exit(self, item=None):
|
||||
|
||||
@@ -6,6 +6,10 @@ import sys
|
||||
import gi
|
||||
from enum import Enum, IntEnum
|
||||
|
||||
import gi
|
||||
|
||||
from app.settings import Settings, SettingsException
|
||||
|
||||
gi.require_version('Gtk', '3.0')
|
||||
from gi.repository import Gtk, Gdk
|
||||
|
||||
@@ -21,12 +25,22 @@ IS_GNOME_SESSION = int(bool(os.environ.get("GNOME_DESKTOP_SESSION_ID")))
|
||||
# translation
|
||||
os.environ["LANG"] = "{}.{}".format(*locale.getlocale())
|
||||
TEXT_DOMAIN = "demon-editor"
|
||||
try:
|
||||
settings = Settings.get_instance()
|
||||
except SettingsException:
|
||||
pass
|
||||
else:
|
||||
os.environ["LANGUAGE"] = settings.language
|
||||
if UI_RESOURCES_PATH == "app/ui/":
|
||||
locale.bindtextdomain(TEXT_DOMAIN, UI_RESOURCES_PATH + "lang")
|
||||
LANG_PATH = GTK_PATH + "/share/locale" if GTK_PATH else UI_RESOURCES_PATH + "lang"
|
||||
gettext.bindtextdomain(TEXT_DOMAIN, LANG_PATH)
|
||||
if sys.platform != "darwin":
|
||||
locale.bindtextdomain(TEXT_DOMAIN, LANG_PATH)
|
||||
|
||||
theme = Gtk.IconTheme.get_default()
|
||||
theme.append_search_path(UI_RESOURCES_PATH + "icons")
|
||||
|
||||
_IMAGE_MISSING = theme.load_icon("image-missing", 16, 0) if theme.lookup_icon("image-missing", 16, 0) else None
|
||||
CODED_ICON = theme.load_icon("emblem-readonly", 16, 0) if theme.lookup_icon(
|
||||
"emblem-readonly", 16, 0) else _IMAGE_MISSING
|
||||
@@ -36,6 +50,7 @@ HIDE_ICON = theme.load_icon("go-jump", 16, 0) if theme.lookup_icon("go-jump", 16
|
||||
TV_ICON = theme.load_icon("tv-symbolic", 16, 0) if theme.lookup_icon("tv-symbolic", 16, 0) else _IMAGE_MISSING
|
||||
IPTV_ICON = theme.load_icon("emblem-shared", 16, 0) if theme.lookup_icon("emblem-shared", 16, 0) else None
|
||||
EPG_ICON = theme.load_icon("gtk-index", 16, 0) if theme.lookup_icon("gtk-index", 16, 0) else None
|
||||
DEFAULT_ICON = theme.load_icon("emblem-default", 16, 0) if theme.lookup_icon("emblem-default", 16, 0) else None
|
||||
|
||||
|
||||
class KeyboardKey(Enum):
|
||||
@@ -94,6 +109,7 @@ class FavClickMode(IntEnum):
|
||||
STREAM = 1
|
||||
PLAY = 2
|
||||
ZAP = 3
|
||||
ZAP_PLAY = 4
|
||||
|
||||
|
||||
class ViewTarget(Enum):
|
||||
|
||||
4
deb/usr/share/demoneditor/start.py
Executable file
4
deb/usr/share/demoneditor/start.py
Executable file
@@ -0,0 +1,4 @@
|
||||
#!/usr/bin/env python3
|
||||
from app.ui.main_app_window import start_app
|
||||
|
||||
start_app()
|
||||
BIN
deb/usr/share/icons/hicolor/96x96/apps/demon-editor.png
Normal file
BIN
deb/usr/share/icons/hicolor/96x96/apps/demon-editor.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.2 KiB |
634
deb/usr/share/icons/hicolor/scalable/apps/demon-editor.svg
Normal file
634
deb/usr/share/icons/hicolor/scalable/apps/demon-editor.svg
Normal file
@@ -0,0 +1,634 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
id="svg1541"
|
||||
version="1.1"
|
||||
viewBox="0 0 16.743339 16.72816"
|
||||
height="63.224556"
|
||||
width="63.281971"
|
||||
sodipodi:docname="demon-editor.svg"
|
||||
inkscape:version="0.92.4 (unknown)">
|
||||
<sodipodi:namedview
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="1366"
|
||||
inkscape:window-height="716"
|
||||
id="namedview127"
|
||||
showgrid="true"
|
||||
fit-margin-left="0"
|
||||
inkscape:zoom="6.1714295"
|
||||
inkscape:cx="40.088627"
|
||||
inkscape:cy="31.742631"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg1541"
|
||||
fit-margin-top="0"
|
||||
fit-margin-right="0"
|
||||
fit-margin-bottom="0">
|
||||
<inkscape:grid
|
||||
type="xygrid"
|
||||
id="grid128"
|
||||
originx="-10.604603"
|
||||
originy="-1.1727724" />
|
||||
</sodipodi:namedview>
|
||||
<title
|
||||
id="title1088">DeamonEditor Icons</title>
|
||||
<defs
|
||||
id="defs1535">
|
||||
<linearGradient
|
||||
id="linearGradient2198">
|
||||
<stop
|
||||
id="stop2194"
|
||||
style="stop-color:#ffb320;stop-opacity:1"
|
||||
offset="0" />
|
||||
<stop
|
||||
id="stop2196"
|
||||
style="stop-color:#e7ff00;stop-opacity:1"
|
||||
offset="1" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient2192">
|
||||
<stop
|
||||
offset="0"
|
||||
style="stop-color:#ffb320;stop-opacity:1"
|
||||
id="stop2188" />
|
||||
<stop
|
||||
offset="1"
|
||||
style="stop-color:#b3c54c;stop-opacity:1"
|
||||
id="stop2190" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient3700-8">
|
||||
<stop
|
||||
offset="0"
|
||||
style="stop-color:#2e4f84;stop-opacity:1"
|
||||
id="stop2183" />
|
||||
<stop
|
||||
offset="1"
|
||||
style="stop-color:#4c77c5;stop-opacity:1"
|
||||
id="stop2185" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
x1="0"
|
||||
y1="0"
|
||||
x2="1"
|
||||
y2="0"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(13.526835,20.525875,20.525875,-13.526835,19.18986,1021.0543)"
|
||||
spreadMethod="pad"
|
||||
id="linearGradient1844">
|
||||
<stop
|
||||
style="stop-opacity:1;stop-color:#29282b"
|
||||
offset="0"
|
||||
id="stop1826" />
|
||||
<stop
|
||||
id="stop1828"
|
||||
offset="0.13293758"
|
||||
style="stop-color:#b5bdcf;stop-opacity:1" />
|
||||
<stop
|
||||
style="stop-opacity:1;stop-color:#92979f"
|
||||
offset="0.21261224"
|
||||
id="stop1832" />
|
||||
<stop
|
||||
style="stop-opacity:1;stop-color:#737881"
|
||||
offset="0.29780689"
|
||||
id="stop1834" />
|
||||
<stop
|
||||
style="stop-opacity:1;stop-color:#70757e"
|
||||
offset="0.29780689"
|
||||
id="stop1836" />
|
||||
<stop
|
||||
style="stop-opacity:1;stop-color:#e4e6e8"
|
||||
offset="0.45395693"
|
||||
id="stop1838" />
|
||||
<stop
|
||||
style="stop-opacity:1;stop-color:#696c76"
|
||||
offset="0.71871042"
|
||||
id="stop1840" />
|
||||
<stop
|
||||
style="stop-opacity:1;stop-color:#29282b"
|
||||
offset="1"
|
||||
id="stop1842" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient1754"
|
||||
spreadMethod="pad"
|
||||
gradientTransform="matrix(13.526835,20.525875,20.525875,-13.526835,19.18986,1021.0543)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
y2="0"
|
||||
x2="1"
|
||||
y1="0"
|
||||
x1="0">
|
||||
<stop
|
||||
id="stop1736"
|
||||
offset="0"
|
||||
style="stop-opacity:1;stop-color:#29282b" />
|
||||
<stop
|
||||
style="stop-color:#b5bdcf;stop-opacity:1"
|
||||
offset="0.02582242"
|
||||
id="stop1738" />
|
||||
<stop
|
||||
id="stop1740"
|
||||
offset="0.14669065"
|
||||
style="stop-opacity:1;stop-color:#868c95" />
|
||||
<stop
|
||||
id="stop1742"
|
||||
offset="0.21261224"
|
||||
style="stop-opacity:1;stop-color:#92979f" />
|
||||
<stop
|
||||
id="stop1744"
|
||||
offset="0.29780689"
|
||||
style="stop-opacity:1;stop-color:#737881" />
|
||||
<stop
|
||||
id="stop1746"
|
||||
offset="0.29780689"
|
||||
style="stop-opacity:1;stop-color:#70757e" />
|
||||
<stop
|
||||
id="stop1748"
|
||||
offset="0.45395693"
|
||||
style="stop-opacity:1;stop-color:#e4e6e8" />
|
||||
<stop
|
||||
id="stop1750"
|
||||
offset="0.71871042"
|
||||
style="stop-opacity:1;stop-color:#ffffff" />
|
||||
<stop
|
||||
id="stop1752"
|
||||
offset="1"
|
||||
style="stop-opacity:1;stop-color:#1d191a" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
x1="0"
|
||||
y1="0"
|
||||
x2="1"
|
||||
y2="0"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(13.526835,20.525875,20.525875,-13.526835,19.18986,1021.0543)"
|
||||
spreadMethod="pad"
|
||||
id="linearGradient1606">
|
||||
<stop
|
||||
style="stop-opacity:1;stop-color:#29282b"
|
||||
offset="0"
|
||||
id="stop1590" />
|
||||
<stop
|
||||
id="stop1608"
|
||||
offset="0.03065561"
|
||||
style="stop-color:#b5bdcf;stop-opacity:1" />
|
||||
<stop
|
||||
style="stop-opacity:1;stop-color:#868c95"
|
||||
offset="0.1125849"
|
||||
id="stop1592" />
|
||||
<stop
|
||||
style="stop-opacity:1;stop-color:#92979f"
|
||||
offset="0.13955873"
|
||||
id="stop1594" />
|
||||
<stop
|
||||
style="stop-opacity:1;stop-color:#737881"
|
||||
offset="0.1915853"
|
||||
id="stop1596" />
|
||||
<stop
|
||||
style="stop-opacity:1;stop-color:#70757e"
|
||||
offset="0.25400829"
|
||||
id="stop1598" />
|
||||
<stop
|
||||
style="stop-opacity:1;stop-color:#e4e6e8"
|
||||
offset="0.45395693"
|
||||
id="stop1600" />
|
||||
<stop
|
||||
style="stop-opacity:1;stop-color:#ffffff"
|
||||
offset="0.71871042"
|
||||
id="stop1602" />
|
||||
<stop
|
||||
style="stop-opacity:1;stop-color:#1d191a"
|
||||
offset="1"
|
||||
id="stop1604" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient886-0">
|
||||
<stop
|
||||
style="stop-color:#535353;stop-opacity:1"
|
||||
offset="0"
|
||||
id="stop888" />
|
||||
<stop
|
||||
style="stop-color:#f0f0f0;stop-opacity:1"
|
||||
offset="1"
|
||||
id="stop890" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient1473">
|
||||
<stop
|
||||
id="stop1469"
|
||||
offset="0"
|
||||
style="stop-color:#ffffff;stop-opacity:0" />
|
||||
<stop
|
||||
id="stop1471"
|
||||
offset="1"
|
||||
style="stop-color:#ffffff;stop-opacity:0.96088022" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient2447-35">
|
||||
<stop
|
||||
id="stop2449"
|
||||
offset="0"
|
||||
style="stop-color:#00c62e;stop-opacity:1" />
|
||||
<stop
|
||||
id="stop2451"
|
||||
offset="1"
|
||||
style="stop-color:#136100;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient3795-1">
|
||||
<stop
|
||||
id="stop3797-1"
|
||||
offset="0"
|
||||
style="stop-color:#803400;stop-opacity:1" />
|
||||
<stop
|
||||
id="stop3799-3"
|
||||
offset="1"
|
||||
style="stop-color:#c87137;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient2447-3">
|
||||
<stop
|
||||
style="stop-color:#c60300;stop-opacity:1"
|
||||
offset="0"
|
||||
id="stop2443" />
|
||||
<stop
|
||||
style="stop-color:#c40e00;stop-opacity:1"
|
||||
offset="1"
|
||||
id="stop2445" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient2458">
|
||||
<stop
|
||||
id="stop2454"
|
||||
offset="0"
|
||||
style="stop-color:#c60300;stop-opacity:1" />
|
||||
<stop
|
||||
id="stop2456"
|
||||
offset="1"
|
||||
style="stop-color:#ee6000;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient3519">
|
||||
<stop
|
||||
id="stop3521"
|
||||
offset="0"
|
||||
style="stop-color:#1d2120;stop-opacity:1" />
|
||||
<stop
|
||||
id="stop3523"
|
||||
offset="1"
|
||||
style="stop-color:#545d5d;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient1446">
|
||||
<stop
|
||||
id="stop1442"
|
||||
offset="0"
|
||||
style="stop-color:#ffffff;stop-opacity:1;" />
|
||||
<stop
|
||||
id="stop1444"
|
||||
offset="1"
|
||||
style="stop-color:#ffffff;stop-opacity:0;" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient1547"
|
||||
spreadMethod="pad"
|
||||
gradientTransform="matrix(13.526835,20.525875,20.525875,-13.526835,19.18986,1021.0543)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
y2="0"
|
||||
x2="1"
|
||||
y1="0"
|
||||
x1="0">
|
||||
<stop
|
||||
id="stop1529"
|
||||
offset="0"
|
||||
style="stop-opacity:1;stop-color:#ffffff" />
|
||||
<stop
|
||||
id="stop1531"
|
||||
offset="0.0263736"
|
||||
style="stop-opacity:1;stop-color:#29282b" />
|
||||
<stop
|
||||
id="stop1533"
|
||||
offset="0.263736"
|
||||
style="stop-opacity:1;stop-color:#868c95" />
|
||||
<stop
|
||||
id="stop1535"
|
||||
offset="0.395604"
|
||||
style="stop-opacity:1;stop-color:#92979f" />
|
||||
<stop
|
||||
id="stop1537"
|
||||
offset="0.39560401"
|
||||
style="stop-opacity:1;stop-color:#737881" />
|
||||
<stop
|
||||
id="stop1539"
|
||||
offset="0.42333773"
|
||||
style="stop-opacity:1;stop-color:#70757e" />
|
||||
<stop
|
||||
id="stop1541"
|
||||
offset="0.56268591"
|
||||
style="stop-opacity:1;stop-color:#e4e6e8" />
|
||||
<stop
|
||||
id="stop1543"
|
||||
offset="0.62400264"
|
||||
style="stop-opacity:1;stop-color:#ffffff" />
|
||||
<stop
|
||||
id="stop1545"
|
||||
offset="1"
|
||||
style="stop-opacity:1;stop-color:#1d191a" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient1527"
|
||||
spreadMethod="pad"
|
||||
gradientTransform="matrix(13.526835,20.525875,20.525875,-13.526835,19.18986,1021.0543)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
y2="0"
|
||||
x2="1"
|
||||
y1="0"
|
||||
x1="0">
|
||||
<stop
|
||||
id="stop1509"
|
||||
offset="0"
|
||||
style="stop-opacity:1;stop-color:#ffffff" />
|
||||
<stop
|
||||
id="stop1511"
|
||||
offset="0.0527472"
|
||||
style="stop-opacity:1;stop-color:#29282b" />
|
||||
<stop
|
||||
id="stop1513"
|
||||
offset="0.142147"
|
||||
style="stop-opacity:1;stop-color:#868c95" />
|
||||
<stop
|
||||
id="stop1515"
|
||||
offset="0.19700864"
|
||||
style="stop-opacity:1;stop-color:#92979f" />
|
||||
<stop
|
||||
id="stop1517"
|
||||
offset="0.25031137"
|
||||
style="stop-opacity:1;stop-color:#737881" />
|
||||
<stop
|
||||
id="stop1519"
|
||||
offset="0.3710371"
|
||||
style="stop-opacity:1;stop-color:#70757e" />
|
||||
<stop
|
||||
id="stop1521"
|
||||
offset="0.53961843"
|
||||
style="stop-opacity:1;stop-color:#e4e6e8" />
|
||||
<stop
|
||||
id="stop1523"
|
||||
offset="0.76283824"
|
||||
style="stop-opacity:1;stop-color:#ffffff" />
|
||||
<stop
|
||||
id="stop1525"
|
||||
offset="1"
|
||||
style="stop-opacity:1;stop-color:#1d191a" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
gradientTransform="matrix(0.26458333,0,0,0.26458333,-5.8254634,-78.732754)"
|
||||
y2="1179.7145"
|
||||
x2="66.791626"
|
||||
y1="1188.7661"
|
||||
x1="99.044022"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="linearGradient1485"
|
||||
xlink:href="#linearGradient1473" />
|
||||
<linearGradient
|
||||
gradientTransform="matrix(0.26458333,0,0,0.26458333,-2.2733744,-70.526334)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
y2="1155.8046"
|
||||
x2="67.311417"
|
||||
y1="1161.6112"
|
||||
x1="77.19442"
|
||||
id="linearGradient1448"
|
||||
xlink:href="#linearGradient1446" />
|
||||
<linearGradient
|
||||
gradientTransform="matrix(0.26458333,0,0,0.26458333,-18.198747,-79.859424)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
y2="1186.8096"
|
||||
x2="146.16808"
|
||||
y1="1186.8096"
|
||||
x1="131.86871"
|
||||
id="linearGradient2180"
|
||||
xlink:href="#linearGradient886-0" />
|
||||
<linearGradient
|
||||
gradientTransform="matrix(0.20863982,0,0,0.20863982,-0.63935937,-13.031239)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
y2="1184.73"
|
||||
x2="101.19952"
|
||||
y1="1184.73"
|
||||
x1="83.066002"
|
||||
id="linearGradient2160"
|
||||
xlink:href="#linearGradient886-0" />
|
||||
<linearGradient
|
||||
gradientTransform="matrix(0.26458333,0,0,0.26458333,-6.2370714,-102.12414)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
y2="1267.1335"
|
||||
x2="117.99127"
|
||||
y1="1267.1335"
|
||||
x1="75.853806"
|
||||
id="linearGradient2131"
|
||||
xlink:href="#linearGradient886-0" />
|
||||
<linearGradient
|
||||
id="linearGradient3700-8-3">
|
||||
<stop
|
||||
id="stop3702-1"
|
||||
style="stop-color:#2e4f84;stop-opacity:1"
|
||||
offset="0" />
|
||||
<stop
|
||||
id="stop3704-8"
|
||||
style="stop-color:#4c77c5;stop-opacity:1"
|
||||
offset="1" />
|
||||
</linearGradient>
|
||||
<defs
|
||||
id="defs4922">
|
||||
<filter
|
||||
style="color-interpolation-filters:sRGB"
|
||||
id="Adobe_OpacityMaskFilter"
|
||||
filterUnits="userSpaceOnUse"
|
||||
x="3.7850001"
|
||||
y="4.6750002"
|
||||
width="5.8829999"
|
||||
height="73.013">
|
||||
<feColorMatrix
|
||||
type="matrix"
|
||||
values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 1 0"
|
||||
id="feColorMatrix4925" />
|
||||
</filter>
|
||||
</defs>
|
||||
<mask
|
||||
maskUnits="userSpaceOnUse"
|
||||
x="3.785"
|
||||
y="4.675"
|
||||
width="5.883"
|
||||
height="73.013"
|
||||
id="SVGID_2_">
|
||||
<g
|
||||
style="filter:url(#Adobe_OpacityMaskFilter)"
|
||||
id="g4928">
|
||||
<linearGradient
|
||||
id="SVGID_3_"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="3.7852001"
|
||||
y1="41.181198"
|
||||
x2="9.6680002"
|
||||
y2="41.181198">
|
||||
<stop
|
||||
offset="0"
|
||||
style="stop-color:#FFFFFF"
|
||||
id="stop4931" />
|
||||
<stop
|
||||
offset="0.0029"
|
||||
style="stop-color:#FAFBFB"
|
||||
id="stop4933" />
|
||||
<stop
|
||||
offset="0.0756"
|
||||
style="stop-color:#BBBDBF"
|
||||
id="stop4935" />
|
||||
<stop
|
||||
offset="0.1438"
|
||||
style="stop-color:#898B8E"
|
||||
id="stop4937" />
|
||||
<stop
|
||||
offset="0.2053"
|
||||
style="stop-color:#646567"
|
||||
id="stop4939" />
|
||||
<stop
|
||||
offset="0.259"
|
||||
style="stop-color:#444446"
|
||||
id="stop4941" />
|
||||
<stop
|
||||
offset="0.3028"
|
||||
style="stop-color:#1D1C1D"
|
||||
id="stop4943" />
|
||||
<stop
|
||||
offset="0.3313"
|
||||
style="stop-color:#000000"
|
||||
id="stop4945" />
|
||||
</linearGradient>
|
||||
<rect
|
||||
style="fill:url(#SVGID_3_)"
|
||||
x="3.7850001"
|
||||
y="4.6750002"
|
||||
width="5.8829999"
|
||||
height="73.013"
|
||||
id="rect4947" />
|
||||
</g>
|
||||
</mask>
|
||||
<filter
|
||||
style="color-interpolation-filters:sRGB"
|
||||
id="filter1268"
|
||||
filterUnits="userSpaceOnUse"
|
||||
x="3.7850001"
|
||||
y="4.6750002"
|
||||
width="5.8829999"
|
||||
height="73.013">
|
||||
<feColorMatrix
|
||||
type="matrix"
|
||||
values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 1 0"
|
||||
id="feColorMatrix1266" />
|
||||
</filter>
|
||||
<linearGradient
|
||||
gradientUnits="userSpaceOnUse"
|
||||
y2="203.22046"
|
||||
x2="23.551136"
|
||||
y1="203.22046"
|
||||
x1="13.487289"
|
||||
id="linearGradient1160"
|
||||
xlink:href="#linearGradient3519" />
|
||||
<clipPath
|
||||
id="clipPath1890"
|
||||
clipPathUnits="userSpaceOnUse">
|
||||
<circle
|
||||
style="opacity:1;fill:#2d2d2d;fill-opacity:1;stroke:#434242;stroke-width:0.0575568;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="circle1892"
|
||||
cx="78.548424"
|
||||
cy="-31.019459"
|
||||
r="6.4721422"
|
||||
transform="scale(1,-1)" />
|
||||
</clipPath>
|
||||
<linearGradient
|
||||
gradientTransform="translate(-0.24389927,14.877856)"
|
||||
y2="27.314217"
|
||||
x2="84.864914"
|
||||
y1="27.314217"
|
||||
x1="72.164909"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="linearGradient2134"
|
||||
xlink:href="#linearGradient3700-8-3" />
|
||||
</defs>
|
||||
<metadata
|
||||
id="metadata1538">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title>DeamonEditor Icons</dc:title>
|
||||
<dc:publisher>
|
||||
<cc:Agent>
|
||||
<dc:title>mfgeg</dc:title>
|
||||
</cc:Agent>
|
||||
</dc:publisher>
|
||||
<dc:date>7.1.2020</dc:date>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
transform="matrix(1.1690805,0,0,1.1690805,-14.929261,-167.45253)"
|
||||
id="layer1">
|
||||
<g
|
||||
transform="translate(0.86526724,-82.691658)"
|
||||
id="g1351">
|
||||
<path
|
||||
d="m 13.715358,227.65981 h 2.97616 v 12.57332 h -2.97616 z m 5.79826,-1.73376 -0.327076,0.42227 c -0.179879,0.23226 -0.586303,0.67932 -0.903223,0.99412 -0.480677,0.47726 -0.640897,0.57235 -0.967777,0.57235 -0.223557,0 -0.380895,-0.0584 -0.624024,-0.25067 v 3.18307 c 0.172884,-0.0214 0.359647,-0.0333 0.575071,-0.0352 0.68403,-0.006 0.91815,0.0416 1.436333,0.29581 1.14586,0.56274 1.762492,1.5589 1.768251,2.8566 0.0094,2.10836 -1.870896,3.55161 -3.779655,3.16529 v 3.10345 c 4.345352,0.0758 4.093104,-2.37537 6.573781,-2.25837 1.486139,0.0748 1.333421,0.16555 1.796225,-1.064 l 0.259834,-0.6913 -0.803704,-0.66493 c -0.960855,-0.79478 -1.303983,-1.29079 -1.205013,-1.74132 0.06631,-0.30189 1.20818,-1.50093 1.779011,-1.86778 0.240181,-0.1543 0.244255,-0.17878 0.09575,-0.59661 -0.08522,-0.23979 -0.259522,-0.65746 -0.386789,-0.92744 l -0.23132,-0.49115 h -1.412666 c -1.868582,0 -1.802386,0.068 -1.805368,-1.82472 l -0.0021,-1.43582 -0.917745,-0.37171 z"
|
||||
style="opacity:1;fill:#000000;fill-opacity:0.5372549;stroke:none;stroke-width:0.18460207;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.5012225"
|
||||
id="path2133"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
id="path2106"
|
||||
style="opacity:1;fill:url(#linearGradient2131);fill-opacity:1;stroke:none;stroke-width:0.17733108;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.5012225"
|
||||
d="m 13.832581,227.93117 h 2.858936 v 12.07807 h -2.858936 z m 5.569881,-1.6655 -0.314194,0.40566 c -0.172793,0.22309 -0.563209,0.65257 -0.867647,0.95496 -0.46174,0.45847 -0.615654,0.5498 -0.929658,0.5498 -0.214752,0 -0.365893,-0.056 -0.599446,-0.24079 v 3.05768 c 0.166077,-0.0206 0.345483,-0.0319 0.55242,-0.0338 0.657089,-0.006 0.881987,0.0399 1.379764,0.28416 1.100724,0.54057 1.693073,1.49747 1.698606,2.74408 0.009,2.02535 -1.797211,3.41174 -3.63079,3.04061 v 2.98122 c 4.174205,0.0728 3.931888,-2.28179 6.314859,-2.1694 1.427604,0.0718 1.2809,0.15902 1.725477,-1.02213 l 0.249597,-0.66408 -0.772046,-0.63871 c -0.923009,-0.76345 -1.252622,-1.23996 -1.157552,-1.67277 0.0637,-0.29001 1.160592,-1.44177 1.708939,-1.79419 0.230721,-0.14825 0.234635,-0.17174 0.09198,-0.57312 -0.08187,-0.23032 -0.249296,-0.63156 -0.371555,-0.89088 l -0.222207,-0.4718 h -1.357022 c -1.794983,0 -1.731396,0.0653 -1.734262,-1.75284 l -0.0021,-1.3793 -0.881603,-0.35705 z"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
id="path2151"
|
||||
d="m 17.266983,230.95755 c -0.215637,0.003 -0.402408,0.014 -0.575465,0.0354 v 6.28854 c 1.910653,0.38671 3.792716,-1.05815 3.783336,-3.16865 -0.0058,-1.29897 -0.623064,-2.29597 -1.770059,-2.85927 -0.518699,-0.25451 -0.753103,-0.30234 -1.437812,-0.29607 z m 0.482341,1.17002 c 0.433747,-0.004 0.582202,0.0265 0.910784,0.18761 0.726595,0.35682 1.117604,0.98848 1.121256,1.81134 0.0059,1.33694 -1.186343,2.25211 -2.396695,2.00715 v -3.98359 c 0.109628,-0.0135 0.228054,-0.0214 0.364655,-0.0225 z"
|
||||
style="opacity:1;fill:url(#linearGradient2160);fill-opacity:1;stroke:none;stroke-width:0.18478528;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.5012225"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
style="opacity:1;fill:none;fill-opacity:1;stroke:url(#linearGradient2180);stroke-width:0.18478528;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.5012225"
|
||||
d="m 17.266981,230.95753 c -0.215636,0.003 -0.402408,0.014 -0.575464,0.0354 v 6.28853 c 1.910652,0.38672 3.792715,-1.05815 3.783336,-3.16865 -0.0057,-1.29897 -0.623065,-2.29597 -1.77006,-2.85927 -0.518697,-0.2545 -0.753103,-0.30234 -1.437812,-0.29607 z m 0.482343,1.17001 c 0.433747,-0.004 0.5822,0.0265 0.910783,0.18762 0.726594,0.35681 1.117605,0.98848 1.121257,1.81133 0.006,1.33694 -1.186344,2.25211 -2.396697,2.00716 v -3.98359 c 0.109628,-0.0135 0.228055,-0.0214 0.364657,-0.0225 z"
|
||||
id="path2170"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
id="path1422"
|
||||
d="m 17.266983,230.95755 c -0.215637,0.003 -0.402408,0.014 -0.575464,0.0354 v 6.28854 c 1.910652,0.38671 3.792715,-1.05815 3.783335,-3.16865 -0.0057,-1.29897 -0.623064,-2.29597 -1.770059,-2.85927 -0.518699,-0.25451 -0.753103,-0.30234 -1.437812,-0.29607 z m 0.482342,1.17002 c 0.433748,-0.004 0.582201,0.0265 0.910784,0.18761 0.726594,0.35682 1.117605,0.98848 1.121257,1.81134 0.006,1.33694 -1.186344,2.25211 -2.396697,2.00715 v -3.98359 c 0.109628,-0.0135 0.228054,-0.0214 0.364656,-0.0225 z"
|
||||
style="opacity:1;fill:none;fill-opacity:1;stroke:url(#linearGradient1448);stroke-width:0.1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
id="path1454"
|
||||
d="m 19.402462,226.26149 -0.314194,0.40566 c -0.172793,0.22309 -0.563209,0.65259 -0.867644,0.95499 -0.461741,0.45847 -0.615657,0.54983 -0.929661,0.54983 -0.214752,0 -0.365893,-0.056 -0.599446,-0.2408 v -0.004 h -2.858741 v 12.0783 h 2.858741 c 4.174205,0.0728 3.931888,-2.28177 6.314861,-2.16937 1.427604,0.0718 1.280898,0.15899 1.725475,-1.02217 l 0.249597,-0.66402 -0.772046,-0.63873 c -0.923009,-0.76346 -1.252622,-1.23997 -1.157552,-1.67278 0.0637,-0.29001 1.160595,-1.44176 1.708939,-1.79419 0.230721,-0.14825 0.234635,-0.17171 0.09198,-0.57309 -0.08187,-0.23032 -0.249296,-0.63158 -0.371555,-0.8909 l -0.222207,-0.47181 h -1.357025 c -1.794983,0 -1.731393,0.0653 -1.734259,-1.75286 l -0.0021,-1.37925 -0.8816,-0.35708 z"
|
||||
style="opacity:1;fill:none;fill-opacity:1;stroke:url(#linearGradient1485);stroke-width:0.1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
inkscape:connector-curvature="0" />
|
||||
</g>
|
||||
<g
|
||||
transform="translate(63.49728,-55.136805)"
|
||||
id="g1174" />
|
||||
<g
|
||||
transform="translate(50.48327,11.624037)"
|
||||
id="g1812" />
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 22 KiB |
Reference in New Issue
Block a user