mirror of
https://github.com/DYefremov/DemonEditor.git
synced 2026-05-09 10:25:36 +02:00
Compare commits
77 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4cf19e5413 | ||
|
|
cd4a814838 | ||
|
|
d7c49f50f2 | ||
|
|
b61b8e16fa | ||
|
|
56d2a3e991 | ||
|
|
b65ea9c0d3 | ||
|
|
a62ee8f378 | ||
|
|
0db8ee6d47 | ||
|
|
ed41b01f63 | ||
|
|
c2eaecb8b8 | ||
|
|
f5313f2c40 | ||
|
|
c6a0b80fdd | ||
|
|
7813aeb059 | ||
|
|
3a0e5c09a1 | ||
|
|
ab6a44dc3f | ||
|
|
caefb4587d | ||
|
|
93ff78d7ce | ||
|
|
1d583ecd99 | ||
|
|
d4914ac451 | ||
|
|
6207b6a10d | ||
|
|
1eb8fe621d | ||
|
|
79b41b1661 | ||
|
|
7a36ba8148 | ||
|
|
08e970fc96 | ||
|
|
9681fcbc79 | ||
|
|
7b002b208f | ||
|
|
95a1732f01 | ||
|
|
57e4fdff7f | ||
|
|
89c456993f | ||
|
|
2f3fc31023 | ||
|
|
5c18e49cf7 | ||
|
|
64530bcb85 | ||
|
|
4d472609b4 | ||
|
|
640b995ab8 | ||
|
|
42980a988f | ||
|
|
64c5f28957 | ||
|
|
a32bf230cf | ||
|
|
b8cac728a8 | ||
|
|
079f07cfd2 | ||
|
|
9a5884cc9a | ||
|
|
115237a10f | ||
|
|
994bd0ee1c | ||
|
|
27e5b373a3 | ||
|
|
43c05b1739 | ||
|
|
bd96c286e9 | ||
|
|
1bded41eab | ||
|
|
5f0f51679c | ||
|
|
380bb3150b | ||
|
|
d1a7a486a2 | ||
|
|
1be167bec3 | ||
|
|
dd3e88589c | ||
|
|
c5a2df6d7d | ||
|
|
c9fc3803c7 | ||
|
|
6afd518cfc | ||
|
|
02a51c9b56 | ||
|
|
c96cfa0e1b | ||
|
|
08bc4ff4c4 | ||
|
|
ae2b78e990 | ||
|
|
88be9fe49c | ||
|
|
9dae9b7219 | ||
|
|
f296a6c90b | ||
|
|
0486776d83 | ||
|
|
3e6146d825 | ||
|
|
9b97341e70 | ||
|
|
41714136e6 | ||
|
|
f781cbb9f6 | ||
|
|
177be7679b | ||
|
|
a65914a48c | ||
|
|
e3ffc2e24b | ||
|
|
79415c69c5 | ||
|
|
54c4e02cee | ||
|
|
f580c5d83c | ||
|
|
e0cbdb2f8d | ||
|
|
839855c076 | ||
|
|
87db39590b | ||
|
|
ebe58903e5 | ||
|
|
ff8d4e5321 |
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
*.pyc
|
||||
*.pyo
|
||||
*__pycache__
|
||||
.idea
|
||||
@@ -26,7 +26,9 @@ Experimental support of Neutrino-MP or others on the same basis (BPanther, etc).
|
||||
[<img src="https://user-images.githubusercontent.com/7511379/141684475-4511ea4f-b152-42d5-b9c8-f3e1e9a160d0.png" width="480"/>](https://user-images.githubusercontent.com/7511379/141684475-4511ea4f-b152-42d5-b9c8-f3e1e9a160d0.png)
|
||||
* Ability to view EPG and manage timers (via HTTP API).
|
||||
* Simple FTP client (experimental).
|
||||
[<img src="https://user-images.githubusercontent.com/7511379/141681165-5679c331-72e7-4044-b365-dcdb30b1433c.png" width="480"/>](https://user-images.githubusercontent.com/7511379/141681165-5679c331-72e7-4044-b365-dcdb30b1433c.png)
|
||||
[<img src="https://user-images.githubusercontent.com/7511379/141681165-5679c331-72e7-4044-b365-dcdb30b1433c.png" width="480"/>](https://user-images.githubusercontent.com/7511379/141681165-5679c331-72e7-4044-b365-dcdb30b1433c.png)
|
||||
|
||||
**To increase program functionality you can use [extensions](https://github.com/DYefremov/demoneditor-extensions).**
|
||||
|
||||
#### Keyboard shortcuts
|
||||
* **Ctrl + X** - only in bouquet list.
|
||||
@@ -108,7 +110,7 @@ just load your data via *"File/Open"* and press *"Save"*. When importing separat
|
||||
|
||||
**The built-in Telnet client does not support ANSI escape sequences!**
|
||||
|
||||
For streams playback, this app supports [VLC](https://www.videolan.org/vlc/), [MPV](https://mpv.io/) and [GStreamer](https://gstreamer.freedesktop.org/). Depending on your distro, you may need to install additional packages and libraries.
|
||||
For streams playback, this app supports [VLC](https://www.videolan.org/vlc/), [MPV](https://mpv.io/) and [GStreamer](https://gstreamer.freedesktop.org/). Depending on your distro, you may need to install additional packages and libraries.
|
||||
#### Command line arguments:
|
||||
* **-l** - write logs to file.
|
||||
* **-d on/off** - turn on/off debug mode. Allows to display more information in the logs.
|
||||
|
||||
@@ -131,33 +131,37 @@ class UtfFTP(FTP):
|
||||
def download_dir(self, path, save_path, callback=None):
|
||||
""" Downloads directory from FTP with all contents.
|
||||
|
||||
Creates a leaf directory and all intermediate ones. This is recursive.
|
||||
Creates a leaf directory and all intermediate ones. This is recursive.
|
||||
"""
|
||||
os.makedirs(os.path.join(save_path, path), exist_ok=True)
|
||||
dir_path = os.path.join(save_path, path, "")
|
||||
os.makedirs(dir_path, exist_ok=True)
|
||||
current_path = self.pwd()
|
||||
|
||||
files = []
|
||||
self.dir(path, files.append)
|
||||
|
||||
try:
|
||||
self.cwd(path)
|
||||
except all_errors as e:
|
||||
msg = f"Download dir error: {e}".rstrip()
|
||||
log(msg)
|
||||
return f"500 {msg}"
|
||||
|
||||
for f in files:
|
||||
f_data = self.get_file_data(f)
|
||||
f_path = f_data[8]
|
||||
|
||||
if f_data[0][0] == "d":
|
||||
try:
|
||||
os.makedirs(os.path.join(save_path, f_path), exist_ok=True)
|
||||
except OSError as e:
|
||||
msg = "Download dir error: {}".format(e).rstrip()
|
||||
log(msg)
|
||||
return "500 " + msg
|
||||
else:
|
||||
self.download_dir(f_path, save_path, callback)
|
||||
self.download_dir(f_path, dir_path, callback)
|
||||
else:
|
||||
try:
|
||||
self.download_file(f_path, save_path, callback)
|
||||
self.download_file(f_path, dir_path, callback)
|
||||
except OSError as e:
|
||||
log("Download dir error: {}".format(e).rstrip())
|
||||
log(f"Download dir error: {e}".rstrip())
|
||||
|
||||
self.cwd(current_path)
|
||||
resp = "226 Transfer complete."
|
||||
msg = "Copy directory {}. Status: {}".format(path, resp)
|
||||
msg = f"Copying directory: {path}. Status: {resp}"
|
||||
log(msg)
|
||||
|
||||
if callback:
|
||||
@@ -648,6 +652,9 @@ class HttpAPI:
|
||||
N_ZAP = "zapto"
|
||||
N_STREAM = "build_playlist?id="
|
||||
|
||||
def __str__(self):
|
||||
return self.value
|
||||
|
||||
class Remote(str, Enum):
|
||||
""" Args for HttpRequestType [REMOTE] class. """
|
||||
ONE = "2"
|
||||
@@ -682,6 +689,9 @@ class HttpAPI:
|
||||
NEXT = "407"
|
||||
BACK = "412"
|
||||
|
||||
def __str__(self):
|
||||
return self.value
|
||||
|
||||
class Power(str, Enum):
|
||||
""" Args for HttpRequestType [POWER] class. """
|
||||
TOGGLE_STANDBY = "0"
|
||||
@@ -691,6 +701,9 @@ class HttpAPI:
|
||||
WAKEUP = "4"
|
||||
STANDBY = "5"
|
||||
|
||||
def __str__(self):
|
||||
return self.value
|
||||
|
||||
PARAM_REQUESTS = {Request.REMOTE,
|
||||
Request.POWER,
|
||||
Request.VOL,
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
import re
|
||||
|
||||
from app.commons import log
|
||||
from app.eparser.satxml import get_pos_str
|
||||
from app.ui.uicommons import CODED_ICON, LOCKED_ICON, HIDE_ICON
|
||||
from .blacklist import get_blacklist
|
||||
from ..ecommons import Service, POLARIZATION, FEC, SERVICE_TYPE, Flag, T_FEC, TrType, FEC_DEFAULT, T_SYSTEM
|
||||
@@ -220,8 +221,7 @@ class LameDbReader:
|
||||
freq = f"{int(freq) // 1000}"
|
||||
rate = f"{int(rate) // 1000}"
|
||||
if tr_type is TrType.Satellite:
|
||||
pos = int(pos)
|
||||
pos = f"{abs(pos / 10):0.1f}{'W' if pos < 0 else 'E'}"
|
||||
pos = get_pos_str(int(pos))
|
||||
except ValueError as e:
|
||||
log(f"Parse error [parse_services]: {e}")
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
#
|
||||
# The MIT License (MIT)
|
||||
#
|
||||
# Copyright (c) 2018-2022 Dmitriy Yefremov
|
||||
# Copyright (c) 2018-2023 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
|
||||
@@ -38,7 +38,7 @@ from app.ui.uicommons import IPTV_ICON
|
||||
|
||||
# url, description, urlkey, account, usrname, psw, s_type, iconsrc, iconsrc_b, group
|
||||
NEUTRINO_FAV_ID_FORMAT = "{}::{}::{}::{}::{}::{}::{}::{}::{}::{}"
|
||||
ENIGMA2_FAV_ID_FORMAT = " {}:{}:{}:{:X}:{:X}:{:X}:{:X}:0:0:0:{}:{}\n#DESCRIPTION {}\n"
|
||||
ENIGMA2_FAV_ID_FORMAT = " {}:{}:{:X}:{:X}:{:X}:{:X}:{:X}:0:0:0:{}:{}\n#DESCRIPTION {}\n"
|
||||
MARKER_FORMAT = " 1:64:{}:0:0:0:0:0:0:0::{}\n#DESCRIPTION {}\n"
|
||||
PICON_FORMAT = "{}_{}_{:X}_{:X}_{:X}_{:X}_{:X}_0_0_0.png"
|
||||
|
||||
@@ -153,12 +153,13 @@ def export_to_m3u(path, bouquet, s_type, url=None):
|
||||
file.writelines(lines)
|
||||
|
||||
|
||||
def get_fav_id(url, name, settings_type, params=None, st_type=None, s_id=0, srv_type=1):
|
||||
""" Returns fav id depending on the profile. """
|
||||
def get_fav_id(url, name, settings_type, params=None, st_type=None, s_id=0, srv_type=1, force_quote=True):
|
||||
""" Returns fav id depending on the settings type. """
|
||||
if settings_type is SettingsType.ENIGMA_2:
|
||||
st_type = st_type or StreamType.NONE_TS.value
|
||||
params = params or (0, 0, 0, 0)
|
||||
return ENIGMA2_FAV_ID_FORMAT.format(st_type, s_id, srv_type, *params, quote(url), name, name, None)
|
||||
url = quote(url) if force_quote else url
|
||||
return ENIGMA2_FAV_ID_FORMAT.format(st_type, s_id, srv_type, *params, url, name, name, None)
|
||||
elif settings_type is SettingsType.NEUTRINO_MP:
|
||||
return NEUTRINO_FAV_ID_FORMAT.format(url, "", 0, None, None, None, None, "", "", 1)
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
#
|
||||
# The MIT License (MIT)
|
||||
#
|
||||
# Copyright (c) 2018-2022 Dmitriy Yefremov
|
||||
# Copyright (c) 2018-2023 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
|
||||
@@ -192,5 +192,10 @@ def indent(elem, parent=None, index=-1, level=0, space=" "):
|
||||
elem.tail = f"\n{space * (level - 1)}"
|
||||
|
||||
|
||||
def get_pos_str(pos: int) -> str:
|
||||
""" Converts satellite position int value to readable string. """
|
||||
return f"{abs(pos / 10):0.1f}{'W' if pos < 0 else 'E'}"
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
pass
|
||||
|
||||
@@ -48,6 +48,8 @@ IS_DARWIN = sys.platform == "darwin"
|
||||
IS_WIN = sys.platform == "win32"
|
||||
IS_LINUX = sys.platform == "linux"
|
||||
|
||||
USE_HEADER_BAR = int(bool(os.environ.get("GNOME_DESKTOP_SESSION_ID")))
|
||||
|
||||
|
||||
class Defaults(Enum):
|
||||
""" Default program settings """
|
||||
@@ -427,7 +429,7 @@ class Settings:
|
||||
|
||||
@default_data_path.setter
|
||||
def default_data_path(self, value):
|
||||
self._settings["default_data_path"] = value
|
||||
self._settings["default_data_path"] = Settings.normalize_path(value)
|
||||
|
||||
@property
|
||||
def default_backup_path(self):
|
||||
@@ -435,7 +437,7 @@ class Settings:
|
||||
|
||||
@default_backup_path.setter
|
||||
def default_backup_path(self, value):
|
||||
self._settings["default_backup_path"] = value
|
||||
self._settings["default_backup_path"] = Settings.normalize_path(value)
|
||||
|
||||
@property
|
||||
def default_picon_path(self):
|
||||
@@ -443,7 +445,7 @@ class Settings:
|
||||
|
||||
@default_picon_path.setter
|
||||
def default_picon_path(self, value):
|
||||
self._settings["default_picon_path"] = value
|
||||
self._settings["default_picon_path"] = Settings.normalize_path(value)
|
||||
|
||||
@property
|
||||
def profile_data_path(self):
|
||||
@@ -479,7 +481,7 @@ class Settings:
|
||||
|
||||
@recordings_path.setter
|
||||
def recordings_path(self, value):
|
||||
self._settings["recordings_path"] = value
|
||||
self._settings["recordings_path"] = Settings.normalize_path(value)
|
||||
|
||||
# ******** Streaming ********* #
|
||||
|
||||
@@ -702,6 +704,14 @@ class Settings:
|
||||
|
||||
# *********** Appearance *********** #
|
||||
|
||||
@property
|
||||
def use_header_bar(self):
|
||||
return self._settings.get("use_header_bar", USE_HEADER_BAR)
|
||||
|
||||
@use_header_bar.setter
|
||||
def use_header_bar(self, value):
|
||||
self._settings["use_header_bar"] = value
|
||||
|
||||
@property
|
||||
def list_font(self):
|
||||
return self._settings.get("list_font", "")
|
||||
@@ -893,13 +903,14 @@ class Settings:
|
||||
# **************** Get-Set settings **************** #
|
||||
|
||||
@staticmethod
|
||||
def get_settings():
|
||||
if not os.path.isfile(CONFIG_FILE) or os.stat(CONFIG_FILE).st_size == 0:
|
||||
Settings.write_settings(Settings.get_default_settings())
|
||||
def get_settings(config_file=CONFIG_FILE, default_settings=None):
|
||||
if not os.path.isfile(config_file) or os.stat(config_file).st_size == 0:
|
||||
df = Settings.get_default_settings() if default_settings is None else default_settings
|
||||
Settings.write_settings(df, config_file=config_file)
|
||||
|
||||
with open(CONFIG_FILE, "r", encoding="utf-8") as config_file:
|
||||
with open(config_file, "r", encoding="utf-8") as cf:
|
||||
try:
|
||||
return json.load(config_file)
|
||||
return json.load(cf)
|
||||
except ValueError as e:
|
||||
raise SettingsReadException(e)
|
||||
|
||||
@@ -931,10 +942,14 @@ class Settings:
|
||||
"ab": "192", "channels": "2", "samplerate": "44100", "scodec": "none"}}
|
||||
|
||||
@staticmethod
|
||||
def write_settings(config):
|
||||
os.makedirs(os.path.dirname(CONFIG_PATH), exist_ok=True)
|
||||
with open(CONFIG_FILE, "w", encoding="utf-8") as config_file:
|
||||
json.dump(config, config_file, indent=" ")
|
||||
def write_settings(config, config_path=CONFIG_PATH, config_file=CONFIG_FILE):
|
||||
os.makedirs(os.path.dirname(config_path), exist_ok=True)
|
||||
with open(config_file, "w", encoding="utf-8") as cf:
|
||||
json.dump(config, cf, indent=" ")
|
||||
|
||||
@staticmethod
|
||||
def normalize_path(path):
|
||||
return f"{os.path.normpath(path)}{SEP}"
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -29,12 +29,13 @@ from functools import partial, wraps
|
||||
from warnings import warn
|
||||
|
||||
if os.name == 'nt':
|
||||
dll = ctypes.util.find_library('mpv-1.dll')
|
||||
dll = ctypes.util.find_library('libmpv-2.dll') or ctypes.util.find_library('mpv-1.dll')
|
||||
if dll is None:
|
||||
raise OSError('Cannot find mpv-1.dll in your system %PATH%. One way to deal with this is to ship mpv-1.dll '
|
||||
'with your script and put the directory your script is in into %PATH% before "import mpv": '
|
||||
'os.environ["PATH"] = os.path.dirname(__file__) + os.pathsep + os.environ["PATH"] '
|
||||
'If mpv-1.dll is located elsewhere, you can add that path to os.environ["PATH"].')
|
||||
raise OSError(
|
||||
'Cannot find [lib]mpv-*.dll in your system %PATH%. One way to deal with this is to ship [lib]mpv-*.dll '
|
||||
'with your script and put the directory your script is in into %PATH% before "import mpv": '
|
||||
'os.environ["PATH"] = os.path.dirname(__file__) + os.pathsep + os.environ["PATH"] '
|
||||
'If mpv-1.dll is located elsewhere, you can add that path to os.environ["PATH"].')
|
||||
backend = CDLL(dll)
|
||||
fs_enc = 'utf-8'
|
||||
else:
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
#
|
||||
# The MIT License (MIT)
|
||||
#
|
||||
# Copyright (c) 2018-2022 Dmitriy Yefremov
|
||||
# Copyright (c) 2018-2023 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
|
||||
@@ -429,7 +429,7 @@ class SatellitesParser(HTMLParser):
|
||||
class ServicesParser(HTMLParser):
|
||||
""" Services parser for LYNGSAT source. """
|
||||
|
||||
def __init__(self, source=SatelliteSource.LYNGSAT, entities=False, separator=' '):
|
||||
def __init__(self, source=SatelliteSource.LYNGSAT, entities=False, separator=' ', lang=None):
|
||||
|
||||
HTMLParser.__init__(self)
|
||||
|
||||
@@ -452,6 +452,12 @@ class ServicesParser(HTMLParser):
|
||||
self._KING_TR_PAT = re.compile((r"(DVB-S[2]?)\s?(?:T2-MI,\s+PLP\s+(\d+))?.*"
|
||||
r"?(?:PLS:\s+(Root|Gold|Combo)\+(\d+))?"
|
||||
r"\s+(.*PSK).*?(?:.*Stream\s+(\d+))?.*"))
|
||||
self._lang = "en"
|
||||
if lang:
|
||||
langs = {"en", "fr", "nl", "de", "se", "no", "pt", "es", "it", "pl",
|
||||
"cz", "gr", "fi", "ar", "tr", "ru", "sc", "ro", "hu", "sq"}
|
||||
lang, _, _ = lang.partition("_")
|
||||
self._lang = lang if lang in langs else self._lang
|
||||
|
||||
self._parse_html_entities = entities
|
||||
self._separator = separator
|
||||
@@ -580,7 +586,7 @@ class ServicesParser(HTMLParser):
|
||||
if len(r) == 13 and SatellitesParser.POS_PAT.match(r[0].text):
|
||||
t_cell = r[4]
|
||||
if t_cell.url and t_cell.url.startswith("tp.php?tp="):
|
||||
t_cell.url = f"https://en.kingofsat.net/{t_cell.url}"
|
||||
t_cell.url = f"https://{self._lang}.kingofsat.net/{t_cell.url}"
|
||||
t_cell.text = f"{r[2].text} {r[3].text} {r[6].text} {r[8].text}"
|
||||
trs.append(t_cell)
|
||||
return trs
|
||||
@@ -730,11 +736,12 @@ class ServicesParser(HTMLParser):
|
||||
|
||||
s_type = self._S_TYPES.get(s_type, "3")
|
||||
_s_type = SERVICE_TYPE.get(s_type, SERVICE_TYPE.get("3"))
|
||||
reg, grp = r[3].text, r[4].text
|
||||
|
||||
name, pkg, cas, sid, v_pid, a_pid = r[2].text, r[5].text, r[6].text, r[7].text, None, None
|
||||
flags, sid, fav_id, picon_id, data_id = self.get_service_data(s_type, pkg, sid, tid, nid, nsp,
|
||||
v_pid, a_pid, cas, use_pids)
|
||||
services.append(Service(flags, "s", None, name, None, None, pkg, _s_type, None, picon_id,
|
||||
services.append(Service(flags, "s", None, name, reg, grp, pkg, _s_type, None, picon_id,
|
||||
sid, str(freq), sr, pol, fec, sys, pos, data_id, fav_id, multi_tr or tr))
|
||||
|
||||
return services
|
||||
@@ -743,9 +750,9 @@ class ServicesParser(HTMLParser):
|
||||
""" Returns converted transponder data. """
|
||||
sys = get_key_by_value(SYSTEM, sys)
|
||||
mod = get_key_by_value(MODULATION, mod)
|
||||
fec = get_key_by_value(FEC, fec)
|
||||
fec = get_key_by_value(FEC, fec) or "0"
|
||||
# For negative (West) positions: 3600 - numeric position value!!!
|
||||
namespace = f"{3600 - pos if pos < 0 else pos:04x}0000"
|
||||
namespace = f"{3600 - abs(pos) if pos < 0 else pos:04x}0000"
|
||||
tr_flag = 1
|
||||
roll_off = 0 # 35% DVB-S2/DVB-S (default)
|
||||
pilot = 2 # Auto
|
||||
|
||||
5322
app/tools/vlc.py
5322
app/tools/vlc.py
File diff suppressed because it is too large
Load Diff
@@ -2,7 +2,7 @@
|
||||
#
|
||||
# The MIT License (MIT)
|
||||
#
|
||||
# Copyright (c) 2018-2022 Dmitriy Yefremov
|
||||
# Copyright (c) 2018-2023 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
|
||||
@@ -129,8 +129,7 @@ class YouTube:
|
||||
fmts = streaming_data.get("formats", None) if streaming_data else None
|
||||
|
||||
if fmts:
|
||||
links = {Quality[i["itag"]]: i["url"] for i in filter(
|
||||
lambda i: i.get("itag", -1) in Quality, fmts) if "url" in i}
|
||||
links = {Quality[i["itag"]]: i["url"] for i in fmts if i.get("itag", -1) in Quality and "url" in i}
|
||||
|
||||
if links and title:
|
||||
return links, title.replace("+", " ")
|
||||
|
||||
@@ -75,13 +75,13 @@
|
||||
<section>
|
||||
<item>
|
||||
<attribute name="label" translatable="yes">Settings</attribute>
|
||||
<attribute name="action">app.on_settings</attribute>
|
||||
<attribute name="action">app.preferences</attribute>
|
||||
</item>
|
||||
</section>
|
||||
<section>
|
||||
<item>
|
||||
<attribute name="label" translatable="yes">Exit</attribute>
|
||||
<attribute name="action">app.on_close_app</attribute>
|
||||
<attribute name="action">app.quit</attribute>
|
||||
</item>
|
||||
</section>
|
||||
</submenu>
|
||||
@@ -156,6 +156,11 @@
|
||||
<attribute name="label" translatable="yes">Alternate layout</attribute>
|
||||
<attribute name="action">app.set_alternate_layout</attribute>
|
||||
</item>
|
||||
<item>
|
||||
<attribute name="label" translatable="yes">Alternate window title</attribute>
|
||||
<attribute name="action">app.set_alternate_title</attribute>
|
||||
<attribute name="hidden-when">action-disabled</attribute>
|
||||
</item>
|
||||
</section>
|
||||
</submenu>
|
||||
<submenu id="tools_menu">
|
||||
@@ -193,7 +198,7 @@
|
||||
<section>
|
||||
<item>
|
||||
<attribute name="label" translatable="yes">About</attribute>
|
||||
<attribute name="action">app.on_about_app</attribute>
|
||||
<attribute name="action">app.about</attribute>
|
||||
</item>
|
||||
</section>
|
||||
</submenu>
|
||||
@@ -202,19 +207,19 @@
|
||||
<section>
|
||||
<item>
|
||||
<attribute name="label" translatable="yes">About</attribute>
|
||||
<attribute name="action">app.on_about_app</attribute>
|
||||
<attribute name="action">app.about</attribute>
|
||||
</item>
|
||||
</section>
|
||||
<section>
|
||||
<item>
|
||||
<attribute name="label" translatable="yes">Settings</attribute>
|
||||
<attribute name="action">app.on_settings</attribute>
|
||||
<attribute name="action">app.preferences</attribute>
|
||||
</item>
|
||||
</section>
|
||||
<section>
|
||||
<item>
|
||||
<attribute name="label" translatable="yes">Exit</attribute>
|
||||
<attribute name="action">app.on_close_app</attribute>
|
||||
<attribute name="action">app.quit</attribute>
|
||||
</item>
|
||||
</section>
|
||||
</menu>
|
||||
@@ -372,6 +377,11 @@
|
||||
<attribute name="label" translatable="yes">Alternate layout</attribute>
|
||||
<attribute name="action">app.set_alternate_layout</attribute>
|
||||
</item>
|
||||
<item>
|
||||
<attribute name="label" translatable="yes">Alternate window title</attribute>
|
||||
<attribute name="action">app.set_alternate_title</attribute>
|
||||
<attribute name="hidden-when">action-disabled</attribute>
|
||||
</item>
|
||||
</section>
|
||||
</submenu>
|
||||
<submenu>
|
||||
|
||||
@@ -36,10 +36,10 @@ from enum import Enum
|
||||
from pathlib import Path
|
||||
|
||||
from app.commons import run_idle, get_size_from_bytes
|
||||
from app.settings import SettingsType, SEP, IS_DARWIN
|
||||
from app.settings import SettingsType, SEP
|
||||
from app.ui.dialogs import show_dialog, DialogType, get_builder
|
||||
from app.ui.main_helper import append_text_to_tview
|
||||
from .uicommons import Gtk, Gdk, UI_RESOURCES_PATH, KeyboardKey, MOD_MASK, IS_GNOME_SESSION, HeaderBar
|
||||
from .uicommons import Gtk, Gdk, UI_RESOURCES_PATH, KeyboardKey, MOD_MASK, HeaderBar
|
||||
|
||||
|
||||
class RestoreType(Enum):
|
||||
@@ -77,7 +77,7 @@ class BackupDialog:
|
||||
self._message_label = builder.get_object("message_label")
|
||||
self._file_count_label = builder.get_object("file_count_label")
|
||||
|
||||
if IS_GNOME_SESSION or IS_DARWIN:
|
||||
if self._settings.use_header_bar:
|
||||
header_bar = HeaderBar()
|
||||
self._dialog_window.set_titlebar(header_bar)
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ Author: Dmitriy Yefremov
|
||||
|
||||
-->
|
||||
<interface domain="demon-editor">
|
||||
<requires lib="gtk+" version="3.16"/>
|
||||
<requires lib="gtk+" version="3.22"/>
|
||||
<!-- interface-license-type mit -->
|
||||
<!-- interface-name DemonEditor -->
|
||||
<!-- interface-description Enigma2 channel and satellites list editor. -->
|
||||
@@ -40,7 +40,7 @@ Author: Dmitriy Yefremov
|
||||
<property name="icon_name">system-help</property>
|
||||
<property name="type_hint">normal</property>
|
||||
<property name="program_name">DemonEditor</property>
|
||||
<property name="version">3.4.0 Beta</property>
|
||||
<property name="version">3.6.1 Beta</property>
|
||||
<property name="copyright">2018-2023 Dmitriy Yefremov
|
||||
</property>
|
||||
<property name="comments" translatable="yes">Enigma2 channel and satellite list editor.</property>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
#
|
||||
# The MIT License (MIT)
|
||||
#
|
||||
# Copyright (c) 2018-2022 Dmitriy Yefremov
|
||||
# Copyright (c) 2018-2023 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
|
||||
@@ -34,8 +34,8 @@ from functools import lru_cache
|
||||
from pathlib import Path
|
||||
|
||||
from app.commons import run_idle
|
||||
from app.settings import SEP, IS_WIN
|
||||
from .uicommons import Gtk, UI_RESOURCES_PATH, TEXT_DOMAIN, IS_GNOME_SESSION
|
||||
from app.settings import SEP, IS_WIN, USE_HEADER_BAR
|
||||
from .uicommons import Gtk, UI_RESOURCES_PATH, TEXT_DOMAIN
|
||||
|
||||
|
||||
class Dialog(Enum):
|
||||
@@ -157,7 +157,7 @@ def get_file_chooser_dialog(transient, text, settings, action_type, file_filter,
|
||||
|
||||
|
||||
def get_input_dialog(transient, text):
|
||||
builder, dialog = get_dialog_from_xml(DialogType.INPUT, transient, use_header=IS_GNOME_SESSION)
|
||||
builder, dialog = get_dialog_from_xml(DialogType.INPUT, transient, use_header=USE_HEADER_BAR)
|
||||
entry = builder.get_object("input_entry")
|
||||
entry.set_text(text if text else "")
|
||||
response = dialog.run()
|
||||
@@ -223,9 +223,9 @@ def get_builder(path, handlers=None, use_str=False, objects=None, tag="property"
|
||||
|
||||
if use_str:
|
||||
if objects:
|
||||
builder.add_objects_from_string(get_dialogs_string(path, tag).format(use_header=IS_GNOME_SESSION), objects)
|
||||
builder.add_objects_from_string(get_dialogs_string(path, tag).format(use_header=USE_HEADER_BAR), objects)
|
||||
else:
|
||||
builder.add_from_string(get_dialogs_string(path, tag).format(use_header=IS_GNOME_SESSION))
|
||||
builder.add_from_string(get_dialogs_string(path, tag).format(use_header=USE_HEADER_BAR))
|
||||
else:
|
||||
if objects:
|
||||
builder.add_objects_from_string(get_dialogs_string(path, tag), objects)
|
||||
|
||||
@@ -43,13 +43,13 @@ from gi.repository import GLib
|
||||
from app.commons import run_idle, run_task, run_with_delay
|
||||
from app.connections import download_data, DownloadType, HttpAPI
|
||||
from app.eparser.ecommons import BouquetService, BqServiceType
|
||||
from app.settings import SEP, EpgSource, IS_DARWIN, IS_WIN
|
||||
from app.settings import SEP, EpgSource, IS_WIN
|
||||
from app.tools.epg import EPG, ChannelsParser, EpgEvent, XmlTvReader
|
||||
from app.ui.dialogs import get_message, show_dialog, DialogType, get_builder
|
||||
from app.ui.tasks import BGTaskWidget
|
||||
from app.ui.timers import TimerTool
|
||||
from ..main_helper import on_popup_menu, update_entry_data, scroll_to, update_toggle_model, update_filter_sat_positions
|
||||
from ..uicommons import Gtk, Gdk, UI_RESOURCES_PATH, Column, EPG_ICON, KeyboardKey, IS_GNOME_SESSION, Page, HeaderBar
|
||||
from ..uicommons import Gtk, Gdk, UI_RESOURCES_PATH, Column, EPG_ICON, KeyboardKey, Page, HeaderBar
|
||||
|
||||
|
||||
class RefsSource(Enum):
|
||||
@@ -197,8 +197,8 @@ class EpgSettingsPopover(Gtk.Popover):
|
||||
|
||||
|
||||
class EpgTool(Gtk.Box):
|
||||
def __init__(self, app, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
def __init__(self, app, **kwargs):
|
||||
super().__init__(**kwargs)
|
||||
|
||||
self._current_bq = None
|
||||
self._app = app
|
||||
@@ -477,7 +477,7 @@ class EpgDialog:
|
||||
self._update_on_start_switch = builder.get_object("update_on_start_switch")
|
||||
self._epg_dat_source_box = builder.get_object("epg_dat_source_box")
|
||||
|
||||
if IS_GNOME_SESSION or IS_DARWIN:
|
||||
if self._settings.use_header_bar:
|
||||
header_bar = HeaderBar(title="EPG", subtitle=get_message("List configuration"))
|
||||
self._dialog.set_titlebar(header_bar)
|
||||
builder.get_object("left_action_box").reparent(header_bar)
|
||||
@@ -676,7 +676,7 @@ class EpgDialog:
|
||||
data = ":".join(ref_data[3:7])
|
||||
pos, ch_id = srv.num
|
||||
pos = pos or " "
|
||||
self._services_model.append((srv.name, pos, data, "_".join(ref_data), ch_id))
|
||||
self._services_model.append((srv.name, pos, data, "_".join(ref_data).rstrip("_"), ch_id))
|
||||
|
||||
if index % factor == 0:
|
||||
yield True
|
||||
@@ -804,7 +804,7 @@ class EpgDialog:
|
||||
fav_id_data[3:7] = data[-3].split(":")
|
||||
|
||||
if data[-2]:
|
||||
row[Column.FAV_POS] = data[-1]
|
||||
row[Column.FAV_POS] = data[-2]
|
||||
p_data = data[-2].split("_")
|
||||
if p_data:
|
||||
fav_id_data[2] = p_data[2]
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
#
|
||||
# The MIT License (MIT)
|
||||
#
|
||||
# Copyright (c) 2018-2022 Dmitriy Yefremov
|
||||
# Copyright (c) 2018-2023 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
|
||||
@@ -42,10 +42,10 @@ from gi.repository import GLib
|
||||
|
||||
from app.commons import log, run_task, run_idle, get_size_from_bytes
|
||||
from app.connections import UtfFTP
|
||||
from app.settings import IS_LINUX, IS_DARWIN, IS_WIN, SEP
|
||||
from app.settings import IS_LINUX, IS_DARWIN, IS_WIN, SEP, USE_HEADER_BAR
|
||||
from app.ui.dialogs import show_dialog, DialogType, get_builder, get_message
|
||||
from app.ui.main_helper import on_popup_menu
|
||||
from .uicommons import Gtk, Gdk, UI_RESOURCES_PATH, KeyboardKey, MOD_MASK, IS_GNOME_SESSION, Page
|
||||
from .uicommons import Gtk, Gdk, UI_RESOURCES_PATH, KeyboardKey, MOD_MASK, Page
|
||||
|
||||
File = namedtuple("File", ["icon", "name", "size", "date", "attr", "extra"])
|
||||
|
||||
@@ -529,7 +529,7 @@ class FtpClientBox(Gtk.HBox):
|
||||
|
||||
@run_idle
|
||||
def show_edit_dialog(self, f_path, data):
|
||||
dialog = TextEditDialog(f_path, IS_GNOME_SESSION)
|
||||
dialog = TextEditDialog(f_path, USE_HEADER_BAR)
|
||||
dialog.text = data
|
||||
ok = Gtk.ResponseType.OK
|
||||
if dialog.run() == ok and show_dialog(DialogType.QUESTION, self._app.app_window) == ok:
|
||||
@@ -578,7 +578,7 @@ class FtpClientBox(Gtk.HBox):
|
||||
log(f"Init attributes error [{attrs}]. Invalid length!")
|
||||
return
|
||||
|
||||
dialog = AttributesDialog(attrs, IS_GNOME_SESSION)
|
||||
dialog = AttributesDialog(attrs, USE_HEADER_BAR)
|
||||
ok = Gtk.ResponseType.OK
|
||||
if dialog.run() == ok and show_dialog(DialogType.QUESTION, self._app.app_window) == ok:
|
||||
log(self._ftp.sendcmd(f"SITE CHMOD {dialog.permissions} {file}"))
|
||||
|
||||
@@ -55,6 +55,113 @@ Author: Dmitriy Yefremov
|
||||
<property name="icon-name">document-revert-symbolic-rtl</property>
|
||||
<property name="icon_size">1</property>
|
||||
</object>
|
||||
<object class="GtkPopover" id="options_popover">
|
||||
<property name="can-focus">False</property>
|
||||
<child>
|
||||
<object class="GtkBox" id="options_popover_box">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="margin-start">10</property>
|
||||
<property name="margin-end">10</property>
|
||||
<property name="margin-top">5</property>
|
||||
<property name="margin-bottom">5</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<property name="spacing">5</property>
|
||||
<child>
|
||||
<object class="GtkBox" id="replace_existing_settings_box">
|
||||
<property name="visible">True</property>
|
||||
<property name="sensitive" bind-source="bouquets_only_switch" bind-property="active" bind-flags="invert-boolean">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="tooltip-text" translatable="yes">Enables overwriting existing main list services.</property>
|
||||
<property name="spacing">5</property>
|
||||
<child>
|
||||
<object class="GtkLabel" id="replace_existing_label">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="label" translatable="yes">Replace existing</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkSwitch" id="replace_existing_switch">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">True</property>
|
||||
<property name="valign">center</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="pack-type">end</property>
|
||||
<property name="position">1</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" id="bouquets_settings_box">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="tooltip-text" translatable="yes">Enables skipping services import from lamedb.</property>
|
||||
<property name="spacing">5</property>
|
||||
<child>
|
||||
<object class="GtkLabel" id="bouquets_only_label">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="label" translatable="yes">Bouquets data only</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkSwitch" id="bouquets_only_switch">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">True</property>
|
||||
<property name="valign">center</property>
|
||||
<signal name="state-set" handler="on_bouquets_only_switch" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="pack-type">end</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="GtkModelButton">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">True</property>
|
||||
<property name="receives-default">True</property>
|
||||
<property name="text" translatable="yes">Close</property>
|
||||
<property name="centered">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<object class="GtkImage" id="remove_selection_image">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
@@ -233,30 +340,6 @@ Author: Dmitriy Yefremov
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="spacing">5</property>
|
||||
<child>
|
||||
<object class="GtkLabel" id="replace_existing_label">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="label" translatable="yes">Replace existing</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkSwitch" id="replace_existing_switch">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">True</property>
|
||||
<property name="valign">center</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkToggleButton" id="details_button">
|
||||
<property name="visible">True</property>
|
||||
@@ -275,6 +358,53 @@ Author: Dmitriy Yefremov
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkMenuButton" id="options_menu_button">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">True</property>
|
||||
<property name="receives-default">True</property>
|
||||
<property name="direction">none</property>
|
||||
<property name="popover">options_popover</property>
|
||||
<child>
|
||||
<object class="GtkBox" id="options_nutton_box">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="spacing">2</property>
|
||||
<child>
|
||||
<object class="GtkImage" id="options_button_image">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="tooltip-text" translatable="yes">Options</property>
|
||||
<property name="icon-name">applications-system-symbolic</property>
|
||||
<property name="icon_size">1</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="options_label">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="label" translatable="yes">Options</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">3</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
|
||||
@@ -37,7 +37,7 @@ from app.eparser.neutrino.bouquets import parse_webtv, parse_bouquets as get_neu
|
||||
from app.settings import SettingsType, IS_DARWIN, SEP
|
||||
from app.ui.dialogs import show_dialog, DialogType, get_chooser_dialog, get_message, get_builder
|
||||
from app.ui.main_helper import on_popup_menu, get_iptv_data
|
||||
from .uicommons import Gtk, Gdk, UI_RESOURCES_PATH, KeyboardKey, Column, IS_GNOME_SESSION, Page, HeaderBar
|
||||
from .uicommons import Gtk, Gdk, UI_RESOURCES_PATH, KeyboardKey, Column, Page, HeaderBar
|
||||
|
||||
|
||||
def import_bouquet(app, model, path, appender, file_path=None):
|
||||
@@ -121,6 +121,7 @@ class ImportDialog:
|
||||
"on_resize": self.on_resize,
|
||||
"on_main_paned_realize": self.on_main_paned_realize,
|
||||
"on_visible_page": self.on_visible_page,
|
||||
"on_bouquets_only_switch": self.on_bouquets_only_switch,
|
||||
"on_key_press": self.on_key_press}
|
||||
|
||||
builder = get_builder(f"{UI_RESOURCES_PATH}imports.glade", handlers)
|
||||
@@ -144,7 +145,10 @@ class ImportDialog:
|
||||
self._dialog_window.set_transient_for(app.app_window)
|
||||
self._info_bar = builder.get_object("info_bar")
|
||||
self._message_label = builder.get_object("message_label")
|
||||
# Options.
|
||||
self._replace_existing_switch = builder.get_object("replace_existing_switch")
|
||||
self._bouquets_only_switch = builder.get_object("bouquets_only_switch")
|
||||
self._bouquets_settings_box = builder.get_object("bouquets_settings_box")
|
||||
# Bouquets page.
|
||||
self._bq_model = builder.get_object("bq_list_store")
|
||||
self._bq_view = builder.get_object("bq_view")
|
||||
@@ -159,7 +163,7 @@ class ImportDialog:
|
||||
self._sat_model = builder.get_object("sat_list_store")
|
||||
self._sat_count_label = builder.get_object("sat_count_label")
|
||||
|
||||
if IS_GNOME_SESSION or IS_DARWIN:
|
||||
if self._settings.use_header_bar:
|
||||
actions_box = builder.get_object("actions_box")
|
||||
builder.get_object("toolbar_box").set_visible(False)
|
||||
header_bar = HeaderBar()
|
||||
@@ -260,7 +264,12 @@ class ImportDialog:
|
||||
with suppress(ValueError):
|
||||
bq.remove(b)
|
||||
|
||||
self._append(self._bouquets, list(filter(lambda s: s.fav_id not in self._ids, services)))
|
||||
if self._bouquets_only_switch.get_active():
|
||||
services = ()
|
||||
else:
|
||||
services = list(filter(lambda s: s.fav_id not in self._ids, services))
|
||||
|
||||
self._append(self._bouquets, services)
|
||||
|
||||
if self._replace_existing_switch.get_active():
|
||||
self._app.emit("services_update", {s.fav_id: s for s in filter(lambda s: s.fav_id in self._ids, services)})
|
||||
@@ -408,6 +417,11 @@ class ImportDialog:
|
||||
|
||||
def on_visible_page(self, stack, param):
|
||||
self._page = Page(stack.get_visible_child_name())
|
||||
self._bouquets_settings_box.set_sensitive(self._page is Page.SERVICES)
|
||||
|
||||
def on_bouquets_only_switch(self, switch, state):
|
||||
if state:
|
||||
self._replace_existing_switch.set_active(False)
|
||||
|
||||
def on_key_press(self, view, event):
|
||||
""" Handling keystrokes """
|
||||
|
||||
1083
app/ui/iptv.glade
1083
app/ui/iptv.glade
File diff suppressed because it is too large
Load Diff
141
app/ui/iptv.py
141
app/ui/iptv.py
@@ -41,17 +41,17 @@ 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,
|
||||
parse_m3u, PICON_FORMAT)
|
||||
from app.settings import SettingsType, IS_DARWIN
|
||||
from app.settings import SettingsType
|
||||
from app.tools.yt import YouTubeException, YouTube
|
||||
from app.ui.dialogs import Action, show_dialog, DialogType, get_message, get_builder
|
||||
from app.ui.main_helper import get_iptv_url, on_popup_menu, get_picon_pixbuf
|
||||
from app.ui.uicommons import (Gtk, Gdk, UI_RESOURCES_PATH, IPTV_ICON, Column, KeyboardKey, get_yt_icon,
|
||||
IS_GNOME_SESSION, HeaderBar)
|
||||
from app.ui.uicommons import (Gtk, Gdk, UI_RESOURCES_PATH, IPTV_ICON, Column, KeyboardKey, get_yt_icon, HeaderBar)
|
||||
|
||||
_DIGIT_ENTRY_NAME = "digit-entry"
|
||||
_ENIGMA2_REFERENCE = "{}:{}:{}:{:X}:{:X}:{:X}:{:X}:0:0:0"
|
||||
_ENIGMA2_REFERENCE = "{}:{}:{:X}:{:X}:{:X}:{:X}:{:X}:0:0:0"
|
||||
_PATTERN = re.compile("(?:^[\\s]*$|\\D)")
|
||||
_UI_PATH = UI_RESOURCES_PATH + "iptv.glade"
|
||||
_URL_PREFIXES = {"YT-DLP": "YT-DLP://", "YT-DL": "YT-DL://", "STREAMLINK": "streamlink://", "No": None}
|
||||
|
||||
|
||||
def is_data_correct(elems):
|
||||
@@ -82,6 +82,7 @@ class IptvDialog:
|
||||
handlers = {"on_response": self.on_response,
|
||||
"on_entry_changed": self.on_entry_changed,
|
||||
"on_url_changed": self.on_url_changed,
|
||||
"on_url_paste": self.on_url_paste,
|
||||
"on_save": self.on_save,
|
||||
"on_stream_type_changed": self.on_stream_type_changed,
|
||||
"on_yt_quality_changed": self.on_yt_quality_changed,
|
||||
@@ -94,6 +95,7 @@ class IptvDialog:
|
||||
self._bouquet = bouquet
|
||||
self._yt_links = None
|
||||
self._yt_dl = None
|
||||
self._inserted_url = False
|
||||
|
||||
builder = get_builder(_UI_PATH, handlers, use_str=True,
|
||||
objects=("iptv_dialog", "stream_type_liststore", "yt_quality_liststore"))
|
||||
@@ -117,8 +119,10 @@ class IptvDialog:
|
||||
self._info_bar = builder.get_object("info_bar")
|
||||
self._message_label = builder.get_object("info_bar_message_label")
|
||||
self._yt_quality_box = builder.get_object("yt_iptv_quality_combobox")
|
||||
self._url_prefix_box = builder.get_object("iptv_url_prefix_box")
|
||||
self._url_prefix_combobox = builder.get_object("iptv_url_prefix_combobox")
|
||||
self._model, self._paths = view.get_selection().get_selected_rows()
|
||||
# style
|
||||
# Style.
|
||||
self._style_provider = Gtk.CssProvider()
|
||||
self._style_provider.load_from_path(UI_RESOURCES_PATH + "style.css")
|
||||
self._digit_elems = (self._srv_id_entry, self._srv_type_entry, self._sid_entry, self._tr_id_entry,
|
||||
@@ -135,6 +139,8 @@ class IptvDialog:
|
||||
else:
|
||||
self._description_entry.set_visible(False)
|
||||
builder.get_object("iptv_description_label").set_visible(False)
|
||||
[self._url_prefix_combobox.append(v, k) for k, v in _URL_PREFIXES.items()]
|
||||
self._url_prefix_combobox.set_active(0)
|
||||
|
||||
if self._action is Action.ADD:
|
||||
self._save_button.set_visible(False)
|
||||
@@ -161,6 +167,13 @@ class IptvDialog:
|
||||
self.show_info_message(get_message("Error. Verify the data!"), Gtk.MessageType.ERROR)
|
||||
return
|
||||
|
||||
url = self._url_entry.get_text()
|
||||
if all((self._url_prefix_box.get_visible(),
|
||||
self._url_prefix_combobox.get_active_id(),
|
||||
url.count("http") > 1 or urlparse(url).scheme.upper() in _URL_PREFIXES)):
|
||||
self.show_info_message(get_message("Invalid prefix for the given URL!"), Gtk.MessageType.ERROR)
|
||||
return
|
||||
|
||||
if show_dialog(DialogType.QUESTION, self._dialog) in (Gtk.ResponseType.CANCEL, Gtk.ResponseType.DELETE_EVENT):
|
||||
return
|
||||
|
||||
@@ -195,15 +208,25 @@ class IptvDialog:
|
||||
elif stream_type is StreamType.E_SERVICE_HLS:
|
||||
self._stream_type_combobox.set_active(5)
|
||||
except ValueError:
|
||||
self.show_info_message("Unknown stream type {}".format(s_type), Gtk.MessageType.ERROR)
|
||||
self.show_info_message(f"Unknown stream type {s_type}", Gtk.MessageType.ERROR)
|
||||
|
||||
self._srv_id_entry.set_text(data[1])
|
||||
self._srv_type_entry.set_text(data[2])
|
||||
self._srv_type_entry.set_text(str(int(data[2], 16)))
|
||||
self._sid_entry.set_text(str(int(data[3], 16)))
|
||||
self._tr_id_entry.set_text(str(int(data[4], 16)))
|
||||
self._net_id_entry.set_text(str(int(data[5], 16)))
|
||||
self._namespace_entry.set_text(str(int(data[6], 16)))
|
||||
self._url_entry.set_text(unquote(data[10].strip()))
|
||||
# URL.
|
||||
url = unquote(data[10].strip())
|
||||
sch = urlparse(url).scheme.upper()
|
||||
if YouTube.get_yt_id(url) and sch in _URL_PREFIXES:
|
||||
active_prefix = _URL_PREFIXES.get(sch)
|
||||
url = re.sub(active_prefix, "", url, 1, re.IGNORECASE)
|
||||
self._url_prefix_combobox.set_active_id(active_prefix)
|
||||
else:
|
||||
self._url_prefix_combobox.set_active(len(_URL_PREFIXES) - 1)
|
||||
|
||||
self._url_entry.set_text(url)
|
||||
self.update_reference_entry()
|
||||
|
||||
def init_neutrino_data(self, fav_id):
|
||||
@@ -213,10 +236,9 @@ class IptvDialog:
|
||||
|
||||
def update_reference_entry(self):
|
||||
if self._s_type is SettingsType.ENIGMA_2 and is_data_correct(self._digit_elems):
|
||||
self.on_url_changed(self._url_entry)
|
||||
self._reference_entry.set_text(_ENIGMA2_REFERENCE.format(self.get_type(),
|
||||
self._srv_id_entry.get_text(),
|
||||
self._srv_type_entry.get_text(),
|
||||
int(self._srv_type_entry.get_text()),
|
||||
int(self._sid_entry.get_text()),
|
||||
int(self._tr_id_entry.get_text()),
|
||||
int(self._net_id_entry.get_text()),
|
||||
@@ -243,15 +265,25 @@ class IptvDialog:
|
||||
if yt_id:
|
||||
entry.set_icon_from_pixbuf(Gtk.EntryIconPosition.SECONDARY, get_yt_icon("youtube", 32))
|
||||
text = "Found a link to the YouTube resource!\nTry to get a direct link to the video?"
|
||||
if show_dialog(DialogType.QUESTION, self._dialog, text=text) == Gtk.ResponseType.OK:
|
||||
entry.set_sensitive(False)
|
||||
gen = self.set_yt_url(entry, yt_id)
|
||||
GLib.idle_add(lambda: next(gen, False), priority=GLib.PRIORITY_LOW)
|
||||
if self._inserted_url and url_str.count("http") == 1:
|
||||
if show_dialog(DialogType.QUESTION, self._dialog, text=text) == Gtk.ResponseType.OK:
|
||||
entry.set_sensitive(False)
|
||||
gen = self.set_yt_url(entry, yt_id)
|
||||
GLib.idle_add(lambda: next(gen, False), priority=GLib.PRIORITY_LOW)
|
||||
else:
|
||||
self._url_prefix_box.set_visible(self._s_type is SettingsType.ENIGMA_2)
|
||||
else:
|
||||
self._url_prefix_box.set_visible(self._s_type is SettingsType.ENIGMA_2)
|
||||
self._inserted_url = False
|
||||
elif YouTube.is_yt_video_link(url_str):
|
||||
entry.set_icon_from_pixbuf(Gtk.EntryIconPosition.SECONDARY, get_yt_icon("youtube", 32))
|
||||
else:
|
||||
entry.set_icon_from_stock(Gtk.EntryIconPosition.SECONDARY, None)
|
||||
self._yt_quality_box.set_visible(False)
|
||||
self._url_prefix_box.set_visible(False)
|
||||
|
||||
def on_url_paste(self, entry):
|
||||
self._inserted_url = True
|
||||
self._yt_quality_box.set_visible(False)
|
||||
|
||||
def set_yt_url(self, entry, video_id):
|
||||
try:
|
||||
@@ -265,7 +297,7 @@ class IptvDialog:
|
||||
links, title = self._yt_dl.get_yt_link(video_id, entry.get_text())
|
||||
yield True
|
||||
except urllib.error.URLError as e:
|
||||
self.show_info_message(get_message("Getting link error:") + (str(e)), Gtk.MessageType.ERROR)
|
||||
self.show_info_message(f"{get_message('Getting link error:')} {e}", Gtk.MessageType.ERROR)
|
||||
return
|
||||
except YouTubeException as e:
|
||||
self.show_info_message((str(e)), Gtk.MessageType.ERROR)
|
||||
@@ -280,7 +312,7 @@ class IptvDialog:
|
||||
entry.set_text(links[sorted(links, key=lambda x: int(x.rstrip("p")), reverse=True)[0]])
|
||||
self._yt_links = links
|
||||
else:
|
||||
msg = get_message("Getting link error:") + " No link received for id: {}".format(video_id)
|
||||
msg = f"{get_message('Getting link error:')} No link received for id: {video_id}"
|
||||
self.show_info_message(msg, Gtk.MessageType.ERROR)
|
||||
finally:
|
||||
entry.set_sensitive(True)
|
||||
@@ -292,22 +324,33 @@ class IptvDialog:
|
||||
self.update_reference_entry()
|
||||
|
||||
def on_yt_quality_changed(self, box):
|
||||
if not self._yt_links:
|
||||
return
|
||||
|
||||
model = box.get_model()
|
||||
active = model.get_value(box.get_active_iter(), 0)
|
||||
if self._yt_links and active in self._yt_links:
|
||||
if active in self._yt_links:
|
||||
self._url_entry.set_text(self._yt_links[active])
|
||||
else:
|
||||
self._url_entry.set_text(self._yt_links.get(max(self._yt_links, default=None), ""))
|
||||
|
||||
def save_enigma2_data(self):
|
||||
name = self._name_entry.get_text().strip()
|
||||
if self._url_prefix_box.get_visible():
|
||||
prefix = self._url_prefix_combobox.get_active_id()
|
||||
url = self._url_entry.get_text().replace(':', '%3A', 1 if prefix else -1)
|
||||
url = f"{quote(prefix) if prefix else ''}{url}"
|
||||
else:
|
||||
url = quote(self._url_entry.get_text())
|
||||
|
||||
fav_id = ENIGMA2_FAV_ID_FORMAT.format(self.get_type(),
|
||||
self._srv_id_entry.get_text(),
|
||||
self._srv_type_entry.get_text(),
|
||||
int(self._srv_type_entry.get_text()),
|
||||
int(self._sid_entry.get_text()),
|
||||
int(self._tr_id_entry.get_text()),
|
||||
int(self._net_id_entry.get_text()),
|
||||
int(self._namespace_entry.get_text()),
|
||||
quote(self._url_entry.get_text()),
|
||||
name, name)
|
||||
url, name, name)
|
||||
|
||||
self.update_bouquet_data(name, fav_id)
|
||||
|
||||
@@ -602,7 +645,7 @@ class IptvListConfigurationDialog(IptvListDialog):
|
||||
|
||||
st_type = get_stream_type(self._stream_type_combobox)
|
||||
s_id = "0" if id_default else self._list_srv_id_entry.get_text()
|
||||
srv_type = "1" if type_default else self._list_srv_type_entry.get_text()
|
||||
srv_type = int("1" if type_default else self._list_srv_type_entry.get_text())
|
||||
sid = "0" if sid_auto else self._list_sid_entry.get_text()
|
||||
tid = "0" if tid_default else f"{int(self._list_tid_entry.get_text()):X}"
|
||||
nid = "0" if nid_default else f"{int(self._list_nid_entry.get_text()):X}"
|
||||
@@ -617,12 +660,13 @@ class IptvListConfigurationDialog(IptvListDialog):
|
||||
if all_default:
|
||||
data[1], data[2], data[3], data[4], data[5], data[6] = "010000"
|
||||
else:
|
||||
data[0], data[1], data[2], data[4], data[5], data[6] = st_type, s_id, srv_type, tid, nid, namespace
|
||||
data[0], data[1], data[4], data[5], data[6] = st_type, s_id, tid, nid, namespace
|
||||
data[2] = f"{srv_type:X}"
|
||||
|
||||
data[3] = f"{index:X}" if sid_auto else sid
|
||||
if sid_auto:
|
||||
params[0] = index
|
||||
picon_id = PICON_FORMAT.format(st_type, int(s_id), int(srv_type), *params)
|
||||
picon_id = PICON_FORMAT.format(st_type, int(s_id), srv_type, *params)
|
||||
data = ":".join(data)
|
||||
new_fav_id = f"{data}{sep}{desc}"
|
||||
row[Column.FAV_ID] = new_fav_id
|
||||
@@ -887,11 +931,16 @@ class YtListImportDialog:
|
||||
self._import_button = builder.get_object("yt_import_button")
|
||||
self._quality_box = builder.get_object("yt_quality_combobox")
|
||||
self._quality_model = builder.get_object("yt_quality_liststore")
|
||||
self._import_button.bind_property("visible", self._quality_box, "visible")
|
||||
self._import_button.bind_property("sensitive", self._quality_box, "sensitive")
|
||||
self._receive_button.bind_property("sensitive", self._import_button, "sensitive")
|
||||
self._extract_switch = builder.get_object("yt_extract_links_switch")
|
||||
|
||||
if IS_GNOME_SESSION or IS_DARWIN:
|
||||
self._url_prefix_combobox = builder.get_object("yt_url_prefix_combobox")
|
||||
[self._url_prefix_combobox.append(v, k) for k, v in _URL_PREFIXES.items()]
|
||||
self._url_prefix_combobox.set_active(0)
|
||||
|
||||
builder.get_object("yt_extract_links_box").set_visible(self._s_type is SettingsType.ENIGMA_2)
|
||||
builder.get_object("yt_url_prefix_box").set_visible(self._s_type is SettingsType.ENIGMA_2)
|
||||
|
||||
if self._settings.use_header_bar:
|
||||
header_bar = HeaderBar(title="YouTube", subtitle=get_message("Playlist import"))
|
||||
self._dialog.set_titlebar(header_bar)
|
||||
actions_box = builder.get_object("yt_actions_box")
|
||||
@@ -905,19 +954,31 @@ class YtListImportDialog:
|
||||
window_size = self._settings.get("yt_import_dialog_size")
|
||||
if window_size:
|
||||
self._dialog.resize(*window_size)
|
||||
# Style
|
||||
# Style.
|
||||
style_provider = Gtk.CssProvider()
|
||||
style_provider.load_from_path(UI_RESOURCES_PATH + "style.css")
|
||||
style_provider.load_from_path(f"{UI_RESOURCES_PATH}style.css")
|
||||
self._url_entry.get_style_context().add_provider_for_screen(Gdk.Screen.get_default(), style_provider,
|
||||
Gtk.STYLE_PROVIDER_PRIORITY_USER)
|
||||
|
||||
def show(self):
|
||||
self._dialog.show()
|
||||
|
||||
@run_task
|
||||
def on_import(self, item):
|
||||
self.on_info_bar_close()
|
||||
self.update_active_elements(False)
|
||||
|
||||
if self._extract_switch.get_active():
|
||||
self.extract_direct_links()
|
||||
else:
|
||||
prefix = self._url_prefix_combobox.get_active_id()
|
||||
selected = filter(lambda r: r[2], self._model)
|
||||
prefix = quote(prefix) if prefix else ''
|
||||
links = [(f"{prefix}https{quote(':')}//www.youtube.com/watch?v={r[1]}", r[0]) for r in selected]
|
||||
self.append_services(links)
|
||||
self.update_active_elements(True)
|
||||
|
||||
@run_task
|
||||
def extract_direct_links(self):
|
||||
self._download_task = True
|
||||
|
||||
try:
|
||||
@@ -946,7 +1007,6 @@ class YtListImportDialog:
|
||||
self.show_info_message(str(e), Gtk.MessageType.ERROR)
|
||||
else:
|
||||
if self._download_task:
|
||||
self.show_info_message(get_message("Done!"), Gtk.MessageType.INFO)
|
||||
self.append_services([done_links[r] for r in rows])
|
||||
finally:
|
||||
self._download_task = False
|
||||
@@ -989,22 +1049,31 @@ class YtListImportDialog:
|
||||
aggr = [None] * 9
|
||||
srvs = []
|
||||
|
||||
if self._yt_list_title:
|
||||
if self._yt_list_title and self._s_type is SettingsType.ENIGMA_2:
|
||||
title = self._yt_list_title
|
||||
fav_id = MARKER_FORMAT.format(0, title, title)
|
||||
mk = Service(None, None, None, title, *aggr[0:3], BqServiceType.MARKER.name, *aggr, 0, fav_id, None)
|
||||
srvs.append(mk)
|
||||
|
||||
act = self._quality_model.get_value(self._quality_box.get_active_iter(), 0)
|
||||
extract = self._extract_switch.get_active()
|
||||
|
||||
act = self._quality_model.get_value(self._quality_box.get_active_iter(), 0) if extract else None
|
||||
for link in links:
|
||||
lnk, title = link or (None, None)
|
||||
if not lnk:
|
||||
continue
|
||||
ln = lnk.get(act) if act in lnk else lnk[sorted(lnk, key=lambda x: int(x.rstrip("p")), reverse=True)[0]]
|
||||
fav_id = get_fav_id(ln, title, self._s_type)
|
||||
|
||||
if extract:
|
||||
ln = lnk.get(act) if act in lnk else lnk[sorted(lnk, key=lambda x: int(x.rstrip("p")), reverse=True)[0]]
|
||||
else:
|
||||
ln = lnk
|
||||
|
||||
fav_id = get_fav_id(ln, title, self._s_type, force_quote=extract)
|
||||
srv = Service(None, None, IPTV_ICON, title, *aggr[0:3], BqServiceType.IPTV.name, *aggr, None, fav_id, None)
|
||||
srvs.append(srv)
|
||||
|
||||
self.appender(srvs)
|
||||
self.show_info_message(get_message("Done!"), Gtk.MessageType.INFO)
|
||||
|
||||
@run_idle
|
||||
def update_active_elements(self, sensitive):
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -898,7 +898,7 @@ Author: Dmitriy Yefremov
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="margin_bottom">2</property>
|
||||
<property name="action_name">app.on_settings</property>
|
||||
<property name="action_name">app.preferences</property>
|
||||
<property name="text" translatable="yes">Settings</property>
|
||||
</object>
|
||||
<packing>
|
||||
@@ -949,7 +949,7 @@ Author: Dmitriy Yefremov
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="action_name">app.on_about_app</property>
|
||||
<property name="action_name">app.about</property>
|
||||
<property name="text" translatable="yes">About</property>
|
||||
</object>
|
||||
<packing>
|
||||
@@ -963,8 +963,8 @@ Author: Dmitriy Yefremov
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="action_name">app.quit</property>
|
||||
<property name="text" translatable="yes">Exit</property>
|
||||
<signal name="clicked" handler="on_close_app" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
@@ -1003,6 +1003,11 @@ Author: Dmitriy Yefremov
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="stock">gtk-find-and-replace</property>
|
||||
</object>
|
||||
<object class="GtkImage" id="remove_duplicates_image">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="icon_name">edit-select-all</property>
|
||||
</object>
|
||||
<object class="GtkImage" id="mark_not_in_bq_image">
|
||||
<property name="visible">True</property>
|
||||
@@ -1652,7 +1657,7 @@ Author: Dmitriy Yefremov
|
||||
<object class="GtkLabel" id="app_ver_label">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label">3.4.0 Beta</property>
|
||||
<property name="label">3.6.1 Beta</property>
|
||||
<attributes>
|
||||
<attribute name="weight" value="bold"/>
|
||||
</attributes>
|
||||
@@ -4751,6 +4756,17 @@ Author: Dmitriy Yefremov
|
||||
<signal name="activate" handler="on_mark_duplicates" swapped="no"/>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkImageMenuItem" id="fav_remove_dup_popup_item">
|
||||
<property name="label" translatable="yes">Remove duplicates</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="sensitive">False</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="image">remove_duplicates_image</property>
|
||||
<property name="use_stock">False</property>
|
||||
<signal name="activate" handler="on_remove_duplicates" swapped="no"/>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkSeparatorMenuItem" id="fav_popup_separator_3">
|
||||
<property name="visible">True</property>
|
||||
|
||||
151
app/ui/main.py
151
app/ui/main.py
@@ -48,7 +48,7 @@ from app.eparser.enigma.bouquets import BqServiceType
|
||||
from app.eparser.iptv import export_to_m3u, StreamType
|
||||
from app.eparser.neutrino.bouquets import BqType
|
||||
from app.settings import (SettingsType, Settings, SettingsException, SettingsReadException,
|
||||
IS_DARWIN, PlayStreamsMode, IS_LINUX)
|
||||
IS_DARWIN, PlayStreamsMode, IS_LINUX, USE_HEADER_BAR)
|
||||
from app.tools.media import Recorder
|
||||
from app.ui.control import ControlTool
|
||||
from app.ui.epg.epg import EpgCache, EpgSettingsPopover, EpgDialog, EpgTool
|
||||
@@ -69,7 +69,7 @@ from .search import SearchProvider
|
||||
from .service_details_dialog import ServiceDetailsDialog, Action
|
||||
from .settings_dialog import SettingsDialog
|
||||
from .uicommons import (Gtk, Gdk, UI_RESOURCES_PATH, LOCKED_ICON, HIDE_ICON, IPTV_ICON, MOVE_KEYS, KeyboardKey, Column,
|
||||
FavClickMode, MOD_MASK, APP_FONT, Page, IS_GNOME_SESSION, HeaderBar)
|
||||
FavClickMode, MOD_MASK, APP_FONT, Page, HeaderBar)
|
||||
from .xml.dialogs import ServicesUpdateDialog
|
||||
from .xml.edit import SatellitesTool
|
||||
|
||||
@@ -98,7 +98,8 @@ class Application(Gtk.Application):
|
||||
_FAV_ELEMENTS = ("fav_cut_popup_item", "fav_paste_popup_item", "fav_locate_popup_item", "fav_iptv_popup_item",
|
||||
"fav_insert_marker_popup_item", "fav_insert_space_popup_item", "fav_edit_sub_menu_popup_item",
|
||||
"fav_edit_popup_item", "fav_picon_popup_item", "fav_copy_popup_item", "fav_add_alt_popup_item",
|
||||
"fav_epg_configuration_popup_item", "fav_mark_dup_popup_item", "fav_reference_popup_item")
|
||||
"fav_epg_configuration_popup_item", "fav_mark_dup_popup_item", "fav_remove_dup_popup_item",
|
||||
"fav_reference_popup_item")
|
||||
|
||||
_BOUQUET_ELEMENTS = ("bouquets_new_popup_item", "bouquets_edit_popup_item", "bouquets_cut_popup_item",
|
||||
"bouquets_copy_popup_item", "bouquets_paste_popup_item", "new_header_button",
|
||||
@@ -174,6 +175,7 @@ class Application(Gtk.Application):
|
||||
"on_fav_press": self.on_fav_press,
|
||||
"on_locate_in_services": self.on_locate_in_services,
|
||||
"on_mark_duplicates": self.on_mark_duplicates,
|
||||
"on_remove_duplicates": self.on_remove_duplicates,
|
||||
"on_services_mark_not_in_bouquets": self.on_services_mark_not_in_bouquets,
|
||||
"on_services_clear_marked": self.on_services_clear_marked,
|
||||
"on_services_clear_new_marked": self.on_services_clear_new_marked,
|
||||
@@ -505,7 +507,7 @@ class Application(Gtk.Application):
|
||||
# Header bar.
|
||||
profile_box = builder.get_object("profile_combo_box")
|
||||
toolbar_box = builder.get_object("toolbar_main_box")
|
||||
if IS_GNOME_SESSION or IS_DARWIN:
|
||||
if self._settings.use_header_bar:
|
||||
header_bar = HeaderBar()
|
||||
if not IS_DARWIN:
|
||||
header_bar.pack_start(builder.get_object("file_header_button"))
|
||||
@@ -647,9 +649,10 @@ class Application(Gtk.Application):
|
||||
|
||||
def init_app_menu(self):
|
||||
builder = get_builder(UI_RESOURCES_PATH + "app_menu.ui", tag="attribute")
|
||||
if not IS_GNOME_SESSION:
|
||||
if not USE_HEADER_BAR:
|
||||
if IS_DARWIN:
|
||||
self.set_app_menu(builder.get_object("mac_app_menu"))
|
||||
if not self.get_app_menu():
|
||||
self.set_app_menu(builder.get_object("mac_app_menu"))
|
||||
self.set_menubar(builder.get_object("mac_menu_bar"))
|
||||
else:
|
||||
self.set_menubar(builder.get_object("menu_bar"))
|
||||
@@ -684,6 +687,19 @@ class Application(Gtk.Application):
|
||||
ext_path = f"{self._settings.default_data_path}tools{os.sep}extensions"
|
||||
ext_paths = [f"{os.path.dirname(__file__)}{os.sep}extensions", ext_path, "extensions"]
|
||||
extensions = {}
|
||||
switchable = []
|
||||
default = []
|
||||
|
||||
def ac(a, v):
|
||||
c = extensions[a.get_name()]
|
||||
e = c(self)
|
||||
e.exec()
|
||||
|
||||
def sw(a, v):
|
||||
c = extensions[a.get_name()]
|
||||
a.set_state(v)
|
||||
e = c(self)
|
||||
e.exec() if v else e.stop()
|
||||
|
||||
for importer, name, is_package in pkgutil.iter_modules(ext_paths):
|
||||
if is_package:
|
||||
@@ -691,27 +707,38 @@ class Application(Gtk.Application):
|
||||
cls_name = name.capitalize()
|
||||
if hasattr(m, cls_name):
|
||||
cls = getattr(m, cls_name)
|
||||
if cls.EMBEDDED:
|
||||
cls(self)
|
||||
continue
|
||||
|
||||
action_name = f"on_{name}_extension"
|
||||
item = Gio.MenuItem.new(cls.LABEL, f"app.{action_name}")
|
||||
ext_section.append_item(item)
|
||||
extensions[action_name] = cls
|
||||
|
||||
def ac(a, v):
|
||||
c = extensions[a.get_name()]
|
||||
e = c(self)
|
||||
e.exec()
|
||||
if cls.SWITCHABLE:
|
||||
switchable.append(item)
|
||||
self.set_state_action(action_name, sw, False)
|
||||
else:
|
||||
default.append(item)
|
||||
self.set_action(action_name, ac)
|
||||
|
||||
self.set_action(action_name, ac)
|
||||
switchable.sort(key=lambda i: i.get_attribute_value("label"), reverse=True)
|
||||
default.sort(key=lambda i: i.get_attribute_value("label"), reverse=True)
|
||||
[ext_section.append_item(item) for item in switchable]
|
||||
[ext_section.append_item(item) for item in default]
|
||||
|
||||
def init_actions(self):
|
||||
# Main actions.
|
||||
self.set_action("preferences", self.on_settings)
|
||||
self.set_action("about", self.on_about_app)
|
||||
self.set_action("quit", self.on_close_app)
|
||||
# Import.
|
||||
self.set_action("on_import_bouquet", self.on_import_bouquet)
|
||||
self.set_action("on_import_bouquets", self.on_import_bouquets)
|
||||
self.set_action("on_new_configuration", self.on_new_configuration)
|
||||
self.set_action("on_import_from_web", self.on_import_from_web)
|
||||
self.set_action("on_settings", self.on_settings)
|
||||
# Tools.
|
||||
self.set_action("on_backup_tool_show", self.on_backup_tool_show)
|
||||
self.set_action("on_about_app", self.on_about_app)
|
||||
self.set_action("on_close_app", self.on_close_app)
|
||||
self.set_state_action("on_telnet_show", self.on_telnet_show, False)
|
||||
self.set_state_action("on_logs_show", self.on_logs_show, False)
|
||||
# Filter.
|
||||
@@ -768,9 +795,12 @@ class Application(Gtk.Application):
|
||||
# Alternate layout.
|
||||
sa = self.set_state_action("set_alternate_layout", self.set_use_alt_layout, self._settings.alternate_layout)
|
||||
sa.connect("change-state", self.on_layout_change)
|
||||
# Header bar for macOS.
|
||||
sa = self.set_state_action("set_alternate_title", self.set_use_alt_title, self._settings.use_header_bar)
|
||||
sa.set_enabled(IS_DARWIN)
|
||||
# Menu bar and playback.
|
||||
self.set_action("on_playback_close", self._player_box.on_close)
|
||||
if not IS_GNOME_SESSION:
|
||||
if not USE_HEADER_BAR:
|
||||
# We are working with the "hidden-when" submenu attribute. See 'app_menu_.ui' file.
|
||||
hide_bar_action = Gio.SimpleAction.new("hide_menu_bar", None)
|
||||
self._player_box.bind_property("visible", hide_bar_action, "enabled", 4)
|
||||
@@ -804,7 +834,7 @@ class Application(Gtk.Application):
|
||||
self.set_accels_for_action("app.open_data", ["<primary>o"])
|
||||
self.set_accels_for_action("app.on_hide", ["<primary>h"])
|
||||
self.set_accels_for_action("app.on_locked", ["<primary>l"])
|
||||
self.set_accels_for_action("app.on_close_app", ["<primary>q"])
|
||||
self.set_accels_for_action("app.quit", ["<primary>q"])
|
||||
self.set_accels_for_action("app.on_edit", ["<primary>e"])
|
||||
self.set_accels_for_action("app.on_telnet_show", ["<primary>t"])
|
||||
self.set_accels_for_action("app.on_logs_show", ["<shift><primary>l"])
|
||||
@@ -1106,6 +1136,17 @@ class Application(Gtk.Application):
|
||||
action.set_state(value)
|
||||
self._settings.alternate_layout = bool(value)
|
||||
|
||||
def set_use_alt_title(self, action, value):
|
||||
action.set_state(value)
|
||||
value = bool(value)
|
||||
self._settings.use_header_bar = bool(value)
|
||||
|
||||
msg = get_message("Restart the program to apply all changes.")
|
||||
if value:
|
||||
warn = "It can cause some problems."
|
||||
msg = f"{get_message('EXPERIMENTAL!')} {warn} {msg}"
|
||||
self.show_info_message(msg, Gtk.MessageType.WARNING)
|
||||
|
||||
@run_idle
|
||||
def on_layout_change(self, action, value):
|
||||
is_alt = bool(value)
|
||||
@@ -1344,6 +1385,10 @@ class Application(Gtk.Application):
|
||||
|
||||
selection = view.get_selection()
|
||||
model, paths = selection.get_selected_rows()
|
||||
if not paths:
|
||||
self.show_error_message("No selected item!")
|
||||
return
|
||||
|
||||
model_name = get_base_model(model).get_name()
|
||||
itrs = [model.get_iter(path) for path in paths]
|
||||
rows = [model[in_itr][:] for in_itr in itrs]
|
||||
@@ -2394,7 +2439,10 @@ class Application(Gtk.Application):
|
||||
break
|
||||
|
||||
def append_services(self, services):
|
||||
to_add = []
|
||||
for srv in services:
|
||||
if srv.fav_id not in self._services:
|
||||
to_add.append(srv)
|
||||
# Adding channels to dict with fav_id as keys.
|
||||
self._services[srv.fav_id] = srv
|
||||
self.update_services_counts(len(self._services.values()))
|
||||
@@ -2402,7 +2450,7 @@ class Application(Gtk.Application):
|
||||
self._services_load_spinner.start()
|
||||
factor = self.DEL_FACTOR / 4
|
||||
|
||||
for index, srv in enumerate(services):
|
||||
for index, srv in enumerate(to_add):
|
||||
background = self.get_new_background(srv.flags_cas)
|
||||
s = srv + (None, background)
|
||||
self._services_model.append(s)
|
||||
@@ -2881,6 +2929,11 @@ class Application(Gtk.Application):
|
||||
self.update_bouquet_list()
|
||||
|
||||
def on_view_focus(self, view, focus_event=None):
|
||||
# Preventing focus lack for some cases.
|
||||
if not focus_event and not view.is_focus():
|
||||
view.grab_focus()
|
||||
return True
|
||||
|
||||
model_name, model = get_model_data(view)
|
||||
not_empty = len(model) > 0 if model else False
|
||||
is_service = model_name == self.SERVICE_MODEL
|
||||
@@ -3225,26 +3278,26 @@ class Application(Gtk.Application):
|
||||
if self._s_type is not SettingsType.ENIGMA_2:
|
||||
self.show_error_message("Not allowed in this context!")
|
||||
return
|
||||
ServicesUpdateDialog(self._main_window, self._settings, self.on_import_data_from_web).show()
|
||||
ServicesUpdateDialog(self).show()
|
||||
|
||||
@run_idle
|
||||
def on_import_data_from_web(self, services):
|
||||
def on_import_data_from_web(self, services, bouquets=None):
|
||||
msg = "Combine with the current data?"
|
||||
|
||||
def clb():
|
||||
self.show_info_message("Done!")
|
||||
|
||||
if len(self._services_model) > 0 and show_dialog(DialogType.QUESTION, self._main_window,
|
||||
msg) == Gtk.ResponseType.OK:
|
||||
gen = self.append_imported_data([], services)
|
||||
GLib.idle_add(lambda: next(gen, False), priority=GLib.PRIORITY_LOW)
|
||||
gen = self.append_imported_data(bouquets or [], services, clb)
|
||||
else:
|
||||
gen = self.import_data_from_web(services)
|
||||
GLib.idle_add(lambda: next(gen, False), priority=GLib.PRIORITY_LOW)
|
||||
gen = self.import_data_from_web(services, bouquets, clb)
|
||||
GLib.idle_add(lambda: next(gen, False), priority=GLib.PRIORITY_LOW)
|
||||
|
||||
def import_data_from_web(self, services):
|
||||
def import_data_from_web(self, services, bouquets, callback=None):
|
||||
self._wait_dialog.show()
|
||||
if self._app_info_box.get_visible():
|
||||
yield from self.create_new_configuration(self._s_type)
|
||||
yield from self.append_services(services)
|
||||
self.update_sat_positions()
|
||||
yield True
|
||||
yield from self.create_new_configuration(self._s_type)
|
||||
yield from self.append_imported_data(bouquets or [], services, callback)
|
||||
self._wait_dialog.hide()
|
||||
|
||||
# ***************** Export ******************** #
|
||||
@@ -3369,7 +3422,7 @@ class Application(Gtk.Application):
|
||||
def on_playback_full_screen(self, box, state):
|
||||
self._data_paned.set_visible(state)
|
||||
self._main_window.unfullscreen() if state else self._main_window.fullscreen()
|
||||
if not IS_GNOME_SESSION:
|
||||
if not USE_HEADER_BAR:
|
||||
self._main_window.set_show_menubar(state)
|
||||
|
||||
def on_playback_show(self, box):
|
||||
@@ -3867,7 +3920,7 @@ class Application(Gtk.Application):
|
||||
return self.show_error_message("Data loading in progress!")
|
||||
|
||||
model, paths = view.get_selection().get_selected_rows()
|
||||
if is_only_one_item_selected(paths, self._main_window):
|
||||
if is_only_one_item_selected(paths, self):
|
||||
model_name = get_base_model(model).get_name()
|
||||
if model_name == self.FAV_MODEL:
|
||||
srv_type = model.get_value(model.get_iter(paths), Column.FAV_TYPE)
|
||||
@@ -3892,7 +3945,7 @@ class Application(Gtk.Application):
|
||||
|
||||
def on_bouquets_edit(self, view):
|
||||
""" Renaming bouquets. """
|
||||
if not self._bq_selected:
|
||||
if not self._bq_selected and self._s_type is SettingsType.NEUTRINO_MP:
|
||||
self.show_error_message("This item is not allowed to edit!")
|
||||
return
|
||||
|
||||
@@ -3900,7 +3953,7 @@ class Application(Gtk.Application):
|
||||
|
||||
if paths:
|
||||
itr = model.get_iter(paths[0])
|
||||
bq_name, bq_type = model.get(itr, 0, 3)
|
||||
bq_name, bq_type = model.get(itr, Column.BQ_NAME, Column.BQ_TYPE)
|
||||
response = show_dialog(DialogType.INPUT, self._main_window, bq_name)
|
||||
if response == Gtk.ResponseType.CANCEL:
|
||||
return
|
||||
@@ -3910,14 +3963,17 @@ class Application(Gtk.Application):
|
||||
self.show_error_message(get_message("A bouquet with that name exists!"))
|
||||
return
|
||||
|
||||
model.set_value(itr, 0, response)
|
||||
model.set_value(itr, Column.BQ_NAME, response)
|
||||
if not model.iter_parent(itr):
|
||||
return
|
||||
|
||||
old_bq_name = f"{bq_name}:{bq_type}"
|
||||
self._bouquets[bq] = self._bouquets.pop(old_bq_name)
|
||||
self._bq_file[bq] = self._bq_file.pop(old_bq_name, None)
|
||||
self._current_bq_name = response
|
||||
self._bq_name_label.set_text(self._current_bq_name)
|
||||
self._bq_selected = bq
|
||||
# services with extra names for the bouquet
|
||||
# Services with extra names for the bouquet.
|
||||
ext_bq = self._extra_bouquets.get(old_bq_name, None)
|
||||
if ext_bq:
|
||||
self._extra_bouquets[bq] = ext_bq
|
||||
@@ -4004,6 +4060,26 @@ class Application(Gtk.Application):
|
||||
if r[Column.FAV_SERVICE] in dup:
|
||||
r[Column.FAV_BACKGROUND] = self._NEW_COLOR
|
||||
|
||||
def on_remove_duplicates(self, item):
|
||||
exist = set()
|
||||
to_remove = []
|
||||
for r in self._fav_model:
|
||||
fav_id = r[Column.FAV_ID]
|
||||
if fav_id in exist:
|
||||
to_remove.append(r.iter)
|
||||
else:
|
||||
exist.add(fav_id)
|
||||
|
||||
count = len(to_remove)
|
||||
if count:
|
||||
if show_dialog(DialogType.QUESTION, self._main_window) != Gtk.ResponseType.OK:
|
||||
return
|
||||
gen = self.remove_favs(to_remove, self._fav_model)
|
||||
GLib.idle_add(lambda: next(gen, False))
|
||||
self.show_info_message(f"{get_message('Done!')} {get_message('Removed')}: {count}")
|
||||
else:
|
||||
self.show_info_message(f"{get_message('Done!')} {get_message('Found')}: {count}")
|
||||
|
||||
def on_services_mark_not_in_bouquets(self, item):
|
||||
if self.is_data_loading():
|
||||
self.show_error_message("Data loading in progress!")
|
||||
@@ -4183,8 +4259,7 @@ class Application(Gtk.Application):
|
||||
self.show_error_message("No bouquets config is loaded. Load or create a new config!")
|
||||
return
|
||||
|
||||
gen_bouquets(self._services_view, self._bouquets_view, self._main_window, g_type, self._s_type,
|
||||
self.append_bouquet)
|
||||
gen_bouquets(self, g_type)
|
||||
|
||||
# ***************** Alternatives ********************* #
|
||||
|
||||
|
||||
@@ -33,14 +33,14 @@ __all__ = ("insert_marker", "move_items", "rename", "ViewTarget", "set_flags", "
|
||||
"is_only_one_item_selected", "gen_bouquets", "BqGenType", "get_selection", "get_service_reference",
|
||||
"get_model_data", "remove_all_unused_picons", "get_picon_pixbuf", "get_base_itrs", "get_iptv_url",
|
||||
"get_iptv_data", "update_entry_data", "append_text_to_tview", "on_popup_menu", "get_picon_file_name",
|
||||
"update_toggle_model", "update_filter_sat_positions", "get_pos_num")
|
||||
"update_toggle_model", "update_popup_filter_model", "update_filter_sat_positions", "get_pos_num")
|
||||
|
||||
import os
|
||||
import re
|
||||
import shutil
|
||||
import unicodedata
|
||||
from collections import defaultdict
|
||||
from functools import lru_cache
|
||||
from itertools import groupby
|
||||
from pathlib import Path
|
||||
from urllib.parse import unquote
|
||||
|
||||
@@ -546,13 +546,13 @@ def remove_picons(settings, picon_ids, picons):
|
||||
shutil.move(src, backup_path + p_id)
|
||||
|
||||
|
||||
def is_only_one_item_selected(paths, transient):
|
||||
def is_only_one_item_selected(paths, app):
|
||||
if len(paths) > 1:
|
||||
show_dialog(DialogType.ERROR, transient, "Please, select only one item!")
|
||||
app.show_error_message("Please, select only one item!")
|
||||
return False
|
||||
|
||||
if not paths:
|
||||
show_dialog(DialogType.ERROR, transient, "No selected item!")
|
||||
app.show_error_message("No selected item!")
|
||||
return False
|
||||
|
||||
return True
|
||||
@@ -574,44 +574,89 @@ def get_picon_file_name(service_name):
|
||||
|
||||
# ***************** Bouquets ********************* #
|
||||
|
||||
def gen_bouquets(view, bq_view, transient, gen_type, s_type, callback):
|
||||
def gen_bouquets(app, gen_type):
|
||||
""" Auto-generate and append list of bouquets. """
|
||||
model, paths = view.get_selection().get_selected_rows()
|
||||
single_types = (BqGenType.SAT, BqGenType.PACKAGE, BqGenType.TYPE)
|
||||
if gen_type in single_types:
|
||||
if not is_only_one_item_selected(paths, transient):
|
||||
return
|
||||
model, paths = app.services_view.get_selection().get_selected_rows()
|
||||
single_types = {BqGenType.SAT, BqGenType.PACKAGE, BqGenType.TYPE}
|
||||
if gen_type in single_types and not is_only_one_item_selected(paths, app):
|
||||
return
|
||||
|
||||
fav_id_index = Column.SRV_FAV_ID
|
||||
index = Column.SRV_TYPE
|
||||
if gen_type in (BqGenType.PACKAGE, BqGenType.EACH_PACKAGE):
|
||||
index = Column.SRV_PACKAGE
|
||||
elif gen_type in (BqGenType.SAT, BqGenType.EACH_SAT):
|
||||
index = Column.SRV_POS
|
||||
|
||||
# Splitting services [caching] by column value.
|
||||
s_data = defaultdict(list)
|
||||
for row in model:
|
||||
s_data[row[index]].append(BouquetService(None, BqServiceType.DEFAULT, row[fav_id_index], 0))
|
||||
ids = {row[Column.SRV_FAV_ID] for row in model}
|
||||
services = [v for k, v in app.current_services.items() if k in ids]
|
||||
|
||||
bq_type = BqType.BOUQUET.value if s_type is SettingsType.NEUTRINO_MP else BqType.TV.value
|
||||
bq_index = 0 if s_type is SettingsType.ENIGMA_2 else 1
|
||||
bq_root_iter = bq_view.get_model().get_iter(bq_index)
|
||||
srv = Service(*model[paths][:Column.SRV_TOOLTIP])
|
||||
cond = srv.package if gen_type is BqGenType.PACKAGE else srv.pos if gen_type is BqGenType.SAT else srv.service_type
|
||||
bq_view.expand_row(Gtk.TreePath(bq_index), 0)
|
||||
|
||||
if gen_type is BqGenType.TYPE and cond == "Data":
|
||||
msg = f"{get_message('Selected type:')} '{cond}'\n\n{get_message('Are you sure?')}"
|
||||
if show_dialog(DialogType.QUESTION, app.app_window, msg) != Gtk.ResponseType.OK:
|
||||
return
|
||||
|
||||
def grouper(s):
|
||||
data = s[index]
|
||||
return data if data else "None"
|
||||
|
||||
services = {k: list(v) for k, v in groupby(sorted(services, key=grouper), key=grouper)}
|
||||
|
||||
bq_view = app.bouquets_view
|
||||
bq_type = BqType.TV.value if app.is_enigma else BqType.BOUQUET.value
|
||||
bq_index = 0 if app.is_enigma else 1
|
||||
bq_root_iter = bq_view.get_model().get_iter(bq_index)
|
||||
|
||||
bq_names = get_bouquets_names(bq_view.get_model())
|
||||
|
||||
if gen_type in single_types:
|
||||
if cond in bq_names:
|
||||
show_dialog(DialogType.ERROR, transient, "A bouquet with that name exists!")
|
||||
else:
|
||||
callback(Bouquet(cond, bq_type, s_data.get(cond)), bq_root_iter)
|
||||
app.show_error_message("A bouquet with that name exists!")
|
||||
return
|
||||
|
||||
bq_services = get_services_type_groups(services.get(cond, []))
|
||||
if app.is_enigma:
|
||||
if srv.service_type == "Radio":
|
||||
bq_index = 1
|
||||
bq_type = BqType.RADIO.value
|
||||
bq_root_iter = bq_view.get_model().get_iter(bq_index)
|
||||
bq_view.expand_row(Gtk.TreePath(bq_index), 1)
|
||||
bq_services = bq_services.get("Radio", [])
|
||||
else:
|
||||
bq_view.expand_row(Gtk.TreePath(bq_index), 0)
|
||||
bq_services = bq_services.get("Data" if srv.service_type == "Data" else "TV", [])
|
||||
app.append_bouquet(Bouquet(cond, bq_type, get_bouquet_services(bq_services)), bq_root_iter)
|
||||
else:
|
||||
bq_view.expand_row(Gtk.TreePath(bq_index), 0)
|
||||
# We add a bouquet only if the given name is missing [keys - names]!
|
||||
for name in sorted(s_data.keys() - bq_names):
|
||||
callback(Bouquet(name, BqType.TV.value, s_data.get(name)), bq_root_iter)
|
||||
if gen_type is BqGenType.EACH_SAT:
|
||||
bq_names = sorted(services.keys() - bq_names, key=get_pos_num, reverse=True)
|
||||
else:
|
||||
bq_names = sorted(services.keys() - bq_names)
|
||||
|
||||
tv_bqs = []
|
||||
radio_bqs = []
|
||||
for n in bq_names:
|
||||
bqs = services.get(n, [])
|
||||
# TV and Radio separation.
|
||||
bq_grp = get_services_type_groups(bqs)
|
||||
tv_bq = bq_grp.get("TV", [])
|
||||
tv_bqs.append(Bouquet(n, BqType.TV.value, get_bouquet_services(tv_bq))) if tv_bq else None
|
||||
radio_bq = bq_grp.get("Radio", [])
|
||||
radio_bqs.append(Bouquet(n, BqType.RADIO.value, get_bouquet_services(radio_bq))) if radio_bq else None
|
||||
|
||||
[app.append_bouquet(b, bq_root_iter) for b in tv_bqs]
|
||||
if app.is_enigma:
|
||||
bq_root_iter = bq_view.get_model().get_iter(bq_index + 1)
|
||||
bq_view.expand_row(Gtk.TreePath(bq_index + 1), 0)
|
||||
[app.append_bouquet(b, bq_root_iter) for b in radio_bqs]
|
||||
|
||||
|
||||
def get_bouquet_services(services):
|
||||
services.sort(key=lambda s: s.service)
|
||||
return [BouquetService(None, BqServiceType.DEFAULT, s.fav_id, 0) for s in services]
|
||||
|
||||
|
||||
def get_bouquets_names(model):
|
||||
@@ -627,12 +672,28 @@ def get_bouquets_names(model):
|
||||
return bouquets_names
|
||||
|
||||
|
||||
def get_services_type_groups(services):
|
||||
""" Returns services grouped by main types [TV, Radio, Data]. -> dict """
|
||||
|
||||
def type_grouper(s):
|
||||
s_type = s.service_type
|
||||
|
||||
if s_type == "Data":
|
||||
return s_type
|
||||
elif s_type == "Radio":
|
||||
return s_type
|
||||
else:
|
||||
return "TV"
|
||||
|
||||
return {k: list(v) for k, v in groupby(sorted(services, key=type_grouper), key=type_grouper)}
|
||||
|
||||
|
||||
# ***************** Others ********************* #
|
||||
|
||||
def copy_reference(view, app):
|
||||
""" Copying picon id to clipboard. """
|
||||
model, paths = view.get_selection().get_selected_rows()
|
||||
if not is_only_one_item_selected(paths, app.app_window):
|
||||
if not is_only_one_item_selected(paths, app):
|
||||
return
|
||||
|
||||
target = app.get_target_view(view)
|
||||
@@ -711,12 +772,16 @@ def update_toggle_model(model, path, toggle):
|
||||
model.set_value(model.get_iter_first(), 1, False)
|
||||
|
||||
|
||||
def update_filter_sat_positions(model, sat_positions):
|
||||
""" Updates the values for the satellite positions button model. """
|
||||
def update_popup_filter_model(model, elements: set):
|
||||
first = model[model.get_iter_first()][:]
|
||||
model.clear()
|
||||
model.append((first[0], True))
|
||||
sat_positions.discard(first[0])
|
||||
elements.discard(first[0])
|
||||
|
||||
|
||||
def update_filter_sat_positions(model, sat_positions):
|
||||
""" Updates the values for the satellite positions button model. """
|
||||
update_popup_filter_model(model, sat_positions)
|
||||
list(map(lambda pos: model.append((pos, True)), sorted(sat_positions, key=get_pos_num, reverse=True)))
|
||||
|
||||
|
||||
@@ -755,7 +820,7 @@ def get_iptv_data(fav_id):
|
||||
data, sep, desc = fav_id.partition("#DESCRIPTION")
|
||||
data = data.split(":")
|
||||
if len(data) < 11:
|
||||
return None, None, desc
|
||||
return None, desc
|
||||
return ":".join(data[:10]), unquote(data[10].strip())
|
||||
|
||||
|
||||
|
||||
@@ -34,17 +34,17 @@ from gi.repository import GLib, GObject, Gio
|
||||
from app.commons import run_idle, run_with_delay
|
||||
from app.connections import HttpAPI
|
||||
from app.eparser.ecommons import BqServiceType
|
||||
from app.settings import PlayStreamsMode, IS_DARWIN, SettingsType
|
||||
from app.settings import PlayStreamsMode, IS_DARWIN, SettingsType, USE_HEADER_BAR
|
||||
from app.tools.media import Player
|
||||
from app.ui.dialogs import get_builder, get_message
|
||||
from app.ui.main_helper import get_iptv_url
|
||||
from app.ui.uicommons import Gtk, Gdk, UI_RESOURCES_PATH, FavClickMode, Column, IS_GNOME_SESSION, Page
|
||||
from app.ui.uicommons import Gtk, Gdk, UI_RESOURCES_PATH, FavClickMode, Column, Page
|
||||
|
||||
|
||||
class PlayerBox(Gtk.Box):
|
||||
|
||||
def __init__(self, app, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
def __init__(self, app, **kwargs):
|
||||
super().__init__(**kwargs)
|
||||
# Signals.
|
||||
GObject.signal_new("playback-full-screen", self, GObject.SIGNAL_RUN_LAST,
|
||||
GObject.TYPE_PYOBJECT, (GObject.TYPE_PYOBJECT,))
|
||||
@@ -108,6 +108,9 @@ class PlayerBox(Gtk.Box):
|
||||
if mode is not FavClickMode.STREAM and not self._app.http_api:
|
||||
return
|
||||
|
||||
if len(self._fav_view.get_model()) == 0:
|
||||
return
|
||||
|
||||
self._fav_view.set_sensitive(False)
|
||||
if mode is FavClickMode.STREAM:
|
||||
self.on_play_stream()
|
||||
@@ -191,7 +194,7 @@ class PlayerBox(Gtk.Box):
|
||||
video_menu = builder.get_object("video_menu")
|
||||
subtitle_menu = builder.get_object("subtitle_menu")
|
||||
|
||||
if not IS_GNOME_SESSION:
|
||||
if not USE_HEADER_BAR:
|
||||
menu_bar = self._app.get_menubar()
|
||||
menu_bar.insert_section(1, None, audio_menu)
|
||||
menu_bar.insert_section(2, None, video_menu)
|
||||
|
||||
@@ -1671,47 +1671,137 @@ Author: Dmitriy Yefremov
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkFrame" id="load_on_startup_frame">
|
||||
<object class="GtkFrame" id="program_options_frame">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="label-xalign">0</property>
|
||||
<property name="shadow-type">in</property>
|
||||
<child>
|
||||
<object class="GtkBox" id="load_on_startup_box">
|
||||
<object class="GtkBox" id="program_options_box">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="margin-start">5</property>
|
||||
<property name="margin-end">5</property>
|
||||
<property name="margin-top">5</property>
|
||||
<property name="margin-bottom">5</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<property name="spacing">5</property>
|
||||
<child>
|
||||
<object class="GtkLabel" id="load_on_startup_label">
|
||||
<object class="GtkBox" id="load_on_startup_box">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="halign">start</property>
|
||||
<property name="margin-top">5</property>
|
||||
<property name="margin-bottom">5</property>
|
||||
<property name="label" translatable="yes">Load the last open configuration at program startup</property>
|
||||
<child>
|
||||
<object class="GtkLabel" id="load_on_startup_label">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="halign">start</property>
|
||||
<property name="margin-top">5</property>
|
||||
<property name="margin-bottom">5</property>
|
||||
<property name="label" translatable="yes">Load the last open configuration at program startup</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkSwitch" id="load_on_startup_switch">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">True</property>
|
||||
<property name="valign">center</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="pack-type">end</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkSwitch" id="load_on_startup_switch">
|
||||
<object class="GtkBox" id="enable_extensions_box">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">True</property>
|
||||
<property name="valign">center</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="tooltip-text" translatable="yes">EXPERIMENTAL!</property>
|
||||
<child>
|
||||
<object class="GtkLabel" id="enable_extensions_label">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="halign">start</property>
|
||||
<property name="hexpand">True</property>
|
||||
<property name="label" translatable="yes">Enable extensions support</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkSwitch" id="enable_extensions_switch">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">True</property>
|
||||
<property name="halign">end</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="pack-type">end</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="pack-type">end</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkBox" id="enable_http_box">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<child>
|
||||
<object class="GtkLabel" id="enable_http_label">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="halign">start</property>
|
||||
<property name="hexpand">True</property>
|
||||
<property name="label" translatable="yes">Enable HTTP API</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkSwitch" id="support_http_api_switch">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">True</property>
|
||||
<property name="halign">end</property>
|
||||
<signal name="state-set" handler="on_http_mode_switch" swapped="no"/>
|
||||
</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">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child type="label_item">
|
||||
@@ -1882,59 +1972,6 @@ Author: Dmitriy Yefremov
|
||||
<property name="position">3</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkFrame" id="http_frame">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="label-xalign">0.019999999552965164</property>
|
||||
<property name="shadow-type">in</property>
|
||||
<child>
|
||||
<object class="GtkBox" id="enable_http_box">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="margin-start">5</property>
|
||||
<property name="margin-end">5</property>
|
||||
<property name="margin-top">5</property>
|
||||
<property name="margin-bottom">5</property>
|
||||
<child>
|
||||
<object class="GtkLabel" id="enable_http_label">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="halign">start</property>
|
||||
<property name="hexpand">True</property>
|
||||
<property name="label" translatable="yes">Enable HTTP API</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkSwitch" id="support_http_api_switch">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">True</property>
|
||||
<property name="halign">end</property>
|
||||
<signal name="state-set" handler="on_http_mode_switch" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child type="label_item">
|
||||
<placeholder/>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">4</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="name">program</property>
|
||||
@@ -3029,45 +3066,6 @@ Author: Dmitriy Yefremov
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkBox" id="enable_extensions_box">
|
||||
<property name="visible">True</property>
|
||||
<property name="sensitive" bind-source="enable_experimental_switch" bind-property="active">False</property>
|
||||
<property name="can-focus">False</property>
|
||||
<child>
|
||||
<object class="GtkLabel" id="enable_extensions_label">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="halign">start</property>
|
||||
<property name="hexpand">True</property>
|
||||
<property name="label" translatable="yes">Enable extensions support</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkSwitch" id="enable_extensions_switch">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">True</property>
|
||||
<property name="halign">end</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="pack-type">end</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkBox" id="yt_dl_box">
|
||||
<property name="visible">True</property>
|
||||
|
||||
@@ -32,10 +32,10 @@ from collections import Counter
|
||||
|
||||
from app.commons import run_task, run_idle, log
|
||||
from app.connections import test_telnet, test_ftp, TestException, test_http, HttpApiException
|
||||
from app.settings import SettingsType, Settings, PlayStreamsMode, IS_LINUX, SEP, IS_WIN, IS_DARWIN
|
||||
from app.settings import SettingsType, Settings, PlayStreamsMode, IS_LINUX, SEP, IS_WIN
|
||||
from app.ui.dialogs import show_dialog, DialogType, get_message, get_chooser_dialog, get_builder
|
||||
from .main_helper import update_entry_data, scroll_to, get_picon_pixbuf
|
||||
from .uicommons import Gtk, Gdk, UI_RESOURCES_PATH, FavClickMode, DEFAULT_ICON, APP_FONT, IS_GNOME_SESSION, HeaderBar
|
||||
from .uicommons import Gtk, Gdk, UI_RESOURCES_PATH, FavClickMode, DEFAULT_ICON, APP_FONT, HeaderBar
|
||||
|
||||
|
||||
class SettingsDialog:
|
||||
@@ -208,7 +208,7 @@ class SettingsDialog:
|
||||
[self.init_element_style(el, screen, style_provider) for el in self._digit_elems]
|
||||
self.init_element_style(self._host_field, screen, style_provider)
|
||||
|
||||
if IS_GNOME_SESSION or IS_DARWIN:
|
||||
if self._settings.use_header_bar:
|
||||
switcher = builder.get_object("main_stack_switcher")
|
||||
switcher.set_margin_top(0)
|
||||
switcher.set_margin_bottom(0)
|
||||
@@ -519,7 +519,6 @@ class SettingsDialog:
|
||||
if not state:
|
||||
self._support_ver5_switch.set_active(state)
|
||||
self._unlimited_buffer_switch.set_active(state)
|
||||
self._enable_extensions_switch.set_active(state)
|
||||
self._enable_send_to_switch.set_active(state)
|
||||
self._enable_yt_dl_switch.set_active(state)
|
||||
|
||||
|
||||
@@ -31,9 +31,10 @@ from datetime import datetime, timedelta
|
||||
from enum import Enum
|
||||
from urllib.parse import quote
|
||||
|
||||
from app.settings import USE_HEADER_BAR
|
||||
from app.ui.main_helper import on_popup_menu
|
||||
from .dialogs import get_builder, get_message, show_dialog, DialogType
|
||||
from .uicommons import Gtk, Gdk, GLib, UI_RESOURCES_PATH, Page, Column, KeyboardKey, IS_GNOME_SESSION, MOD_MASK
|
||||
from .uicommons import Gtk, Gdk, GLib, UI_RESOURCES_PATH, Page, Column, KeyboardKey, MOD_MASK
|
||||
from ..commons import run_idle, log
|
||||
from ..connections import HttpAPI
|
||||
from ..eparser.ecommons import BqServiceType
|
||||
@@ -56,7 +57,7 @@ class TimerTool(Gtk.Box):
|
||||
|
||||
class TimerDialog(Gtk.Dialog):
|
||||
def __init__(self, parent, action=None, timer_data=None, *args, **kwargs):
|
||||
super().__init__(use_header_bar=IS_GNOME_SESSION, *args, **kwargs)
|
||||
super().__init__(use_header_bar=USE_HEADER_BAR, *args, **kwargs)
|
||||
|
||||
self._action = action or TimerTool.TimerAction.ADD
|
||||
self._timer_data = timer_data or {}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
#
|
||||
# The MIT License (MIT)
|
||||
#
|
||||
# Copyright (c) 2018-2022 Dmitriy Yefremov
|
||||
# Copyright (c) 2018-2023 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
|
||||
@@ -52,7 +52,6 @@ TEXT_DOMAIN = "demon-editor"
|
||||
|
||||
NOTIFY_IS_INIT = False
|
||||
APP_FONT = None
|
||||
IS_GNOME_SESSION = int(bool(os.environ.get("GNOME_DESKTOP_SESSION_ID")))
|
||||
|
||||
try:
|
||||
settings = Settings.get_instance()
|
||||
@@ -164,6 +163,7 @@ def show_notification(message, timeout=10000, urgency=1):
|
||||
|
||||
class HeaderBar(Gtk.HeaderBar):
|
||||
""" Custom header bar widget. """
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
super().__init__(**kwargs)
|
||||
self.set_visible(True)
|
||||
|
||||
@@ -30,6 +30,8 @@ import concurrent.futures
|
||||
import os
|
||||
import re
|
||||
import time
|
||||
from collections import OrderedDict
|
||||
from itertools import groupby
|
||||
from math import fabs
|
||||
|
||||
from gi.repository import GLib
|
||||
@@ -38,13 +40,15 @@ from app.commons import run_idle, run_task, log
|
||||
from app.eparser import Satellite, Transponder
|
||||
from app.eparser.ecommons import (PLS_MODE, get_key_by_value, POLARIZATION, FEC, SYSTEM, MODULATION, Terrestrial, Cable,
|
||||
T_SYSTEM, BANDWIDTH, CONSTELLATION, T_FEC, GUARD_INTERVAL, TRANSMISSION_MODE,
|
||||
HIERARCHY, Inversion, C_MODULATION, FEC_DEFAULT, TerTransponder, CableTransponder)
|
||||
from app.settings import IS_DARWIN
|
||||
HIERARCHY, Inversion, C_MODULATION, FEC_DEFAULT, TerTransponder, CableTransponder,
|
||||
Bouquet, BouquetService, BqServiceType, Bouquets, BqType)
|
||||
from app.eparser.satxml import get_pos_str
|
||||
from app.settings import USE_HEADER_BAR, Settings, CONFIG_PATH
|
||||
from app.tools.satellites import SatellitesParser, SatelliteSource, ServicesParser
|
||||
from ..dialogs import show_dialog, DialogType, get_message, get_builder
|
||||
from ..main_helper import append_text_to_tview, get_base_model, on_popup_menu
|
||||
from ..main_helper import append_text_to_tview, get_base_model, on_popup_menu, get_services_type_groups
|
||||
from ..search import SearchProvider
|
||||
from ..uicommons import Gtk, Gdk, UI_RESOURCES_PATH, IS_GNOME_SESSION, HeaderBar
|
||||
from ..uicommons import Gtk, Gdk, UI_RESOURCES_PATH, HeaderBar
|
||||
|
||||
_DIALOGS_UI_PATH = f"{UI_RESOURCES_PATH}xml{os.sep}dialogs.glade"
|
||||
|
||||
@@ -61,7 +65,7 @@ class DVBDialog(Gtk.Dialog):
|
||||
skip_taskbar_hint=True,
|
||||
skip_pager_hint=True,
|
||||
destroy_with_parent=True,
|
||||
use_header_bar=IS_GNOME_SESSION,
|
||||
use_header_bar=USE_HEADER_BAR,
|
||||
window_position=Gtk.WindowPosition.CENTER_ON_PARENT,
|
||||
buttons=(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, Gtk.STOCK_OK, Gtk.ResponseType.OK),
|
||||
*args, **kwargs)
|
||||
@@ -408,14 +412,13 @@ class UpdateDialog:
|
||||
self._settings = settings
|
||||
self._download_task = False
|
||||
self._parser = None
|
||||
self._size_name = f"{'_'.join(re.findall('[A-Z][^A-Z]*', self.__class__.__name__))}_window_size".lower()
|
||||
self._selected_satellites = set()
|
||||
|
||||
builder = get_builder(f"{UI_RESOURCES_PATH}xml{os.sep}update.glade", handlers)
|
||||
|
||||
self._window = builder.get_object("satellites_update_window")
|
||||
self._window.set_transient_for(transient)
|
||||
if title:
|
||||
self._window.set_title(title)
|
||||
self._window.set_title(title if title else "")
|
||||
|
||||
self._transponder_paned = builder.get_object("sat_update_tr_paned")
|
||||
self._sat_view = builder.get_object("sat_update_tree_view")
|
||||
@@ -435,6 +438,8 @@ class UpdateDialog:
|
||||
self._sat_view.bind_property("sensitive", self._source_box, "sensitive")
|
||||
self._sat_view.bind_property("sensitive", self._receive_button, "sensitive")
|
||||
self._receive_button.bind_property("visible", update_button, "visible")
|
||||
self._left_action_box = builder.get_object("sat_update_left_action_box")
|
||||
self._right_action_box = builder.get_object("sat_update_right_action_box")
|
||||
# Filter
|
||||
self._filter_bar = builder.get_object("sat_update_filter_bar")
|
||||
self._from_pos_button = builder.get_object("from_pos_button")
|
||||
@@ -456,22 +461,36 @@ class UpdateDialog:
|
||||
builder.get_object("sat_update_search_down_button"),
|
||||
builder.get_object("sat_update_search_up_button"))
|
||||
builder.get_object("sat_update_find_button").connect("toggled", search_provider.on_search_toggled)
|
||||
# Satellite lists init on dialog start.
|
||||
self._sat_view.connect("realize", self.on_update_satellites_list)
|
||||
# Options.
|
||||
self._general_options_box = builder.get_object("general_options_box")
|
||||
self._save_sat_selection_switch = builder.get_object("save_sat_selection_switch")
|
||||
self._skip_c_band_switch = builder.get_object("skip_c_band_switch")
|
||||
|
||||
if IS_GNOME_SESSION or IS_DARWIN:
|
||||
if self._settings.use_header_bar:
|
||||
header_bar = HeaderBar()
|
||||
builder.get_object("sat_update_header").set_visible(False)
|
||||
header_box = builder.get_object("satellites_update_header_box")
|
||||
header_box.remove(self._source_box)
|
||||
header_bar.pack_start(self._source_box)
|
||||
action_box = builder.get_object("sat_update_left_action_box")
|
||||
header_box.remove(action_box)
|
||||
header_bar.pack_start(action_box)
|
||||
action_box = builder.get_object("sat_update_right_action_box")
|
||||
header_box.remove(action_box)
|
||||
header_bar.pack_end(action_box)
|
||||
header_box.remove(self._left_action_box)
|
||||
header_bar.pack_start(self._left_action_box)
|
||||
header_box.remove(self._right_action_box)
|
||||
header_bar.pack_end(self._right_action_box)
|
||||
self._window.set_titlebar(header_bar)
|
||||
|
||||
window_size = self._settings.get(self._size_name)
|
||||
# Dialog settings.
|
||||
self._dialog_name = f"{'_'.join(re.findall('[A-Z][^A-Z]*', self.__class__.__name__))}".lower()
|
||||
self._dialog_settings = self._settings.get(self._dialog_name, {})
|
||||
self._source_box.set_active(self._dialog_settings.get("source", 1))
|
||||
self._save_sat_selection_switch.set_active(self._dialog_settings.get("save_sat_selection", False))
|
||||
self._skip_c_band_switch.set_active(self._dialog_settings.get("skip_c_band", False))
|
||||
|
||||
if self._save_sat_selection_switch.get_active():
|
||||
self._selected_satellites.update(self.get_selected_satellites())
|
||||
|
||||
window_size = self._dialog_settings.get("window_size", None)
|
||||
if window_size:
|
||||
self._window.resize(*window_size)
|
||||
|
||||
@@ -497,11 +516,11 @@ class UpdateDialog:
|
||||
|
||||
self.is_download = True
|
||||
self._sat_view.set_sensitive(False)
|
||||
src = self._source_box.get_active()
|
||||
|
||||
if not self._parser:
|
||||
self._parser = SatellitesParser()
|
||||
|
||||
self.get_sat_list(src, self.append_satellites)
|
||||
self.get_sat_list(self._source_box.get_active(), self.append_satellites)
|
||||
|
||||
def clear_data(self):
|
||||
get_base_model(self._sat_view.get_model()).clear()
|
||||
@@ -526,11 +545,14 @@ class UpdateDialog:
|
||||
@run_idle
|
||||
def append_satellites(self, sats):
|
||||
model = get_base_model(self._sat_view.get_model())
|
||||
|
||||
for sat in sats:
|
||||
model.append(sat)
|
||||
itr = model.append(sat)
|
||||
model[itr][-1] = sat[-2] in self._selected_satellites
|
||||
|
||||
self._sat_view.set_sensitive(True)
|
||||
self._satellites_count_label.set_text(str(len(model)))
|
||||
self.update_receive_button_state(self._filter_model)
|
||||
|
||||
@run_idle
|
||||
def on_receive_data(self, item):
|
||||
@@ -625,10 +647,33 @@ class UpdateDialog:
|
||||
itr = self._filter_model.convert_iter_to_child_iter(model.convert_iter_to_child_iter(model.get_iter(path)))
|
||||
self._filter_model.get_model().set_value(itr, 4, select)
|
||||
|
||||
if self._save_sat_selection_switch.get_active():
|
||||
sat = model[path][-2]
|
||||
self._selected_satellites.add(sat) if select else self._selected_satellites.discard(sat)
|
||||
|
||||
def on_quit(self, window, event):
|
||||
self._settings.add(self._size_name, window.get_size())
|
||||
self.save_settings()
|
||||
self.is_download = False
|
||||
|
||||
def save_settings(self):
|
||||
self._dialog_settings["window_size"] = self._window.get_size()
|
||||
self._dialog_settings["source"] = self._source_box.get_active()
|
||||
self._dialog_settings["save_sat_selection"] = self._save_sat_selection_switch.get_active()
|
||||
self._dialog_settings["skip_c_band"] = self._skip_c_band_switch.get_active()
|
||||
self._settings.add(self._dialog_name, self._dialog_settings)
|
||||
self.save_selected_satellites()
|
||||
|
||||
def get_selected_satellites(self):
|
||||
""" Returns selected satellites set from the last session. """
|
||||
c_file = f"{CONFIG_PATH}{self._dialog_name}_satellites"
|
||||
return Settings.get_settings(c_file, default_settings=[])
|
||||
|
||||
def save_selected_satellites(self):
|
||||
""" Saves current selected satellites to a file. """
|
||||
if self._save_sat_selection_switch.get_active():
|
||||
c_file = f"{CONFIG_PATH}{self._dialog_name}_satellites"
|
||||
Settings.write_settings(list(self._selected_satellites), config_file=c_file)
|
||||
|
||||
|
||||
class SatellitesUpdateDialog(UpdateDialog):
|
||||
""" Dialog for update satellites from the Web. """
|
||||
@@ -638,6 +683,16 @@ class SatellitesUpdateDialog(UpdateDialog):
|
||||
|
||||
self._main_model = main_model
|
||||
self._source_box.connect("changed", self.on_update_satellites_list)
|
||||
# Options.
|
||||
self._merge_sat_switch = Gtk.Switch(active=self._dialog_settings.get("merge_satellites", False))
|
||||
self._merge_sat_switch.connect("state-set", lambda b, s: self._dialog_settings.update({"merge_satellites": s}))
|
||||
box = Gtk.Box(spacing=5, orientation=Gtk.Orientation.HORIZONTAL)
|
||||
box.pack_start(Gtk.Label(get_message("Merge satellites by positions")), False, True, 0)
|
||||
box.pack_end(self._merge_sat_switch, False, True, 0)
|
||||
self._general_options_box.pack_start(box, True, True, 0)
|
||||
self._general_options_box.show_all()
|
||||
|
||||
self._skip_c_band_switch.get_parent().set_visible(False)
|
||||
|
||||
@run_idle
|
||||
def on_receive_data(self, item):
|
||||
@@ -653,6 +708,7 @@ class SatellitesUpdateDialog(UpdateDialog):
|
||||
self.update_log_visibility()
|
||||
model = self._sat_view.get_model()
|
||||
start = time.time()
|
||||
_len = 75
|
||||
|
||||
with concurrent.futures.ProcessPoolExecutor(max_workers=4) as executor:
|
||||
text = "Processing: {}\n"
|
||||
@@ -672,10 +728,39 @@ class SatellitesUpdateDialog(UpdateDialog):
|
||||
appender.send(text.format(data[0]))
|
||||
sats.append(data)
|
||||
|
||||
appender.send("-" * 75 + "\n")
|
||||
appender.send("-" * _len + "\n")
|
||||
sat_count = len(sats)
|
||||
|
||||
sats = {s[0]: s for s in sats} # key = name, v = satellite
|
||||
if self._merge_sat_switch.get_active():
|
||||
def grouper(sat):
|
||||
try:
|
||||
return int(sat.position)
|
||||
except ValueError:
|
||||
pass
|
||||
return 0
|
||||
|
||||
sat_groups = groupby(sorted(sats, key=grouper, reverse=True), key=grouper)
|
||||
sats = {}
|
||||
for pos, satellites in sat_groups:
|
||||
satellites = list(satellites)
|
||||
if len(satellites) > 1:
|
||||
position = get_pos_str(pos)
|
||||
appender.send(f"Merging satellites for position: {position}\n")
|
||||
names = []
|
||||
transponders = []
|
||||
for s in satellites:
|
||||
names.append(s.name.lstrip(position).strip().split())
|
||||
transponders.extend(s.transponders)
|
||||
|
||||
transponders.sort(key=lambda t: int(t.frequency))
|
||||
sat = Satellite(self.get_grouped_satellite_name(names, position), "0", str(pos), transponders)
|
||||
sats[sat.name] = sat
|
||||
else:
|
||||
sat = satellites.pop()
|
||||
sats[sat.name] = sat
|
||||
appender.send("-" * _len + "\n")
|
||||
else:
|
||||
sats = {s.name: s for s in sats} # key = name, v = satellite
|
||||
|
||||
for row in self._main_model:
|
||||
pos = row[0]
|
||||
@@ -688,11 +773,39 @@ class SatellitesUpdateDialog(UpdateDialog):
|
||||
appender.send(f"Adding satellite: {s.name}\n")
|
||||
self.append_satellite(s)
|
||||
|
||||
appender.send("-" * 75 + "\n")
|
||||
appender.send("-" * _len + "\n")
|
||||
appender.send(f"Consumed: {time.time() - start:0.0f}s, {sat_count} satellites received.\n")
|
||||
appender.close()
|
||||
self.is_download = False
|
||||
|
||||
def get_grouped_satellite_name(self, sat_names, pos):
|
||||
""" Forms name for merged satellites. """
|
||||
|
||||
def name_grouper(nd):
|
||||
if nd:
|
||||
return nd[0]
|
||||
return ""
|
||||
|
||||
name_groups = groupby(sorted(sat_names, key=name_grouper), key=name_grouper)
|
||||
names = []
|
||||
for s, s_names in name_groups:
|
||||
tk = set()
|
||||
name = s
|
||||
for i, n_data in enumerate(s_names):
|
||||
if i == 0:
|
||||
name = " ".join(n_data)
|
||||
tk.update(n_data)
|
||||
else:
|
||||
for n in n_data:
|
||||
if n in tk:
|
||||
continue
|
||||
name = f"{name}/{n}"
|
||||
tk.add(n)
|
||||
|
||||
names.append(name)
|
||||
|
||||
return f"{pos} {' & '.join(names)}"
|
||||
|
||||
@run_idle
|
||||
def append_satellite(self, sat):
|
||||
self._main_model.append(sat)
|
||||
@@ -701,16 +814,16 @@ class SatellitesUpdateDialog(UpdateDialog):
|
||||
class ServicesUpdateDialog(UpdateDialog):
|
||||
""" Dialog for updating services from the Web. """
|
||||
|
||||
def __init__(self, transient, settings, callback):
|
||||
super().__init__(transient=transient, settings=settings, title="Services update")
|
||||
def __init__(self, app):
|
||||
super().__init__(transient=app.app_window, settings=app.app_settings, title="Services update")
|
||||
|
||||
self._callback = callback
|
||||
self._callback = app.on_import_data_from_web
|
||||
self._satellite_paths = {}
|
||||
self._transponders = {}
|
||||
self._services = {}
|
||||
self._selected_transponders = set()
|
||||
self._services_parser = ServicesParser(source=SatelliteSource.LYNGSAT)
|
||||
# Transponder view popup menu
|
||||
# Transponder view popup menu.
|
||||
tr_popup_menu = Gtk.Menu()
|
||||
select_all_item = Gtk.ImageMenuItem.new_from_stock("gtk-select-all")
|
||||
select_all_item.connect("activate", lambda w: self.update_transponder_selection(True))
|
||||
@@ -728,6 +841,24 @@ class ServicesUpdateDialog(UpdateDialog):
|
||||
|
||||
self._transponder_paned.set_visible(True)
|
||||
self._source_box.connect("changed", self.on_update_satellites_list)
|
||||
self._source_box.connect("changed", self.on_source_changed)
|
||||
# Options for KingOfSat source.
|
||||
self._kos_bq_groups_switch = Gtk.Switch(active=self._dialog_settings.get("kos_bq_groups", False))
|
||||
self._kos_bq_groups_switch.connect("state-set", lambda b, s: self._dialog_settings.update({"kos_bq_groups": s}))
|
||||
self._kos_bq_lang_switch = Gtk.Switch(active=self._dialog_settings.get("kos_bq_lang", False))
|
||||
self._kos_bq_lang_switch.connect("state-set", lambda b, s: self._dialog_settings.update({"kos_bq_lang": s}))
|
||||
self._kos_options_box = Gtk.Box(spacing=5, orientation=Gtk.Orientation.VERTICAL)
|
||||
box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=5, margin_top=5)
|
||||
box.pack_start(Gtk.Label(get_message("Create Category bouquets")), False, True, 0)
|
||||
box.pack_end(self._kos_bq_groups_switch, False, True, 0)
|
||||
self._kos_options_box.add(box)
|
||||
box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=5, margin_bottom=5)
|
||||
box.pack_start(Gtk.Label(get_message("Create Regional bouquets")), False, True, 0)
|
||||
box.pack_end(self._kos_bq_lang_switch, False, True, 0)
|
||||
self._kos_options_box.add(box)
|
||||
self._kos_options_box.connect("realize", self.on_source_changed)
|
||||
self._general_options_box.pack_start(self._kos_options_box, True, True, 0)
|
||||
self._general_options_box.show_all()
|
||||
|
||||
@run_idle
|
||||
def on_receive_data(self, item):
|
||||
@@ -737,6 +868,14 @@ class ServicesUpdateDialog(UpdateDialog):
|
||||
|
||||
self.receive_services()
|
||||
|
||||
def on_source_changed(self, item):
|
||||
is_kos = self._source_box.get_active_id() == SatelliteSource.KINGOFSAT.name
|
||||
self._kos_options_box.set_sensitive(is_kos)
|
||||
if not is_kos:
|
||||
self._kos_bq_groups_switch.set_active(False)
|
||||
self._kos_bq_lang_switch.set_active(False)
|
||||
self._kos_options_box.set_tooltip_text(None if is_kos else get_message("KingOfSat only!"))
|
||||
|
||||
@run_task
|
||||
def receive_services(self):
|
||||
self.is_download = True
|
||||
@@ -805,6 +944,7 @@ class ServicesUpdateDialog(UpdateDialog):
|
||||
log(f"Getting services error: {e} [{t_names.get(futures[future])}]")
|
||||
|
||||
appender.send("-" * 75 + "\n")
|
||||
services = OrderedDict({s.fav_id: s for s in services}).values()
|
||||
appender.send(f"Consumed: {time.time() - start:0.0f}s, {len(services)} services received.")
|
||||
|
||||
try:
|
||||
@@ -815,10 +955,47 @@ class ServicesUpdateDialog(UpdateDialog):
|
||||
except ValueError as e:
|
||||
log(f"ServicesUpdateDialog [on receive data] error: {e}")
|
||||
else:
|
||||
self._callback(srvs)
|
||||
bouquets = None
|
||||
if self._source_box.get_active_id() == SatelliteSource.KINGOFSAT.name:
|
||||
bouquets = self.get_bouquets([srv._replace(fav_id=srvs[i].fav_id) for i, srv in enumerate(services)])
|
||||
|
||||
def c_filter(s):
|
||||
try:
|
||||
return int(s.freq) > 10000
|
||||
except ValueError:
|
||||
return False
|
||||
|
||||
self._callback(filter(c_filter, srvs) if self._skip_c_band_switch.get_active() else srvs, bouquets)
|
||||
|
||||
self.is_download = False
|
||||
|
||||
def get_bouquets(self, services):
|
||||
type_groups = get_services_type_groups(services)
|
||||
tv_bouquets, radio_bouquets = [], []
|
||||
|
||||
tv_services = sorted(type_groups.get("TV", []), key=lambda s: s.service)
|
||||
rd_services = sorted(type_groups.get("Radio", []), key=lambda s: s.service)
|
||||
no_lb = "No Category"
|
||||
|
||||
if self._kos_bq_groups_switch.get_active():
|
||||
self.gen_bouquet_group(tv_services, tv_bouquets, lambda s: s[4] or no_lb)
|
||||
self.gen_bouquet_group(rd_services, radio_bouquets, lambda s: s[4] or no_lb, bq_type=BqType.RADIO.value)
|
||||
|
||||
if self._kos_bq_lang_switch.get_active():
|
||||
lb = "" if no_lb in {b.name for b in tv_bouquets} else "No Region"
|
||||
self.gen_bouquet_group(tv_services, tv_bouquets, lambda s: s[5] or lb)
|
||||
lb = "" if no_lb in {b.name for b in radio_bouquets} else "No Region"
|
||||
self.gen_bouquet_group(rd_services, radio_bouquets, lambda s: s[5] or lb, bq_type=BqType.RADIO.value)
|
||||
|
||||
return Bouquets("", BqType.TV.value, tv_bouquets), Bouquets("", BqType.RADIO.value, radio_bouquets)
|
||||
|
||||
def gen_bouquet_group(self, services, bouquets, grouper, bq_type=BqType.TV.value):
|
||||
""" Generates bouquets depending on <grouper>. """
|
||||
s_type = BqServiceType.DEFAULT
|
||||
[bouquets.append(Bouquet(name=g[0], type=bq_type,
|
||||
services=[BouquetService(None, s_type, s.fav_id, 0) for s in g[1]])) for g in
|
||||
groupby(sorted(services, key=grouper), key=grouper) if g[0]]
|
||||
|
||||
@run_task
|
||||
def get_sat_list(self, src, callback):
|
||||
sat_src = SatelliteSource.LYNGSAT
|
||||
@@ -831,10 +1008,9 @@ class ServicesUpdateDialog(UpdateDialog):
|
||||
self.is_download = False
|
||||
|
||||
def on_satellite_toggled(self, toggle, path):
|
||||
model = self._sat_view.get_model()
|
||||
self.update_state(model, path, not toggle.get_active())
|
||||
self.update_receive_button_state(self._filter_model)
|
||||
super().on_satellite_toggled(toggle, path)
|
||||
|
||||
model = self._sat_view.get_model()
|
||||
url = model.get_value(model.get_iter(path), 3)
|
||||
selected = toggle.get_active()
|
||||
transponders = self._transponders.get(url, None)
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
#
|
||||
# The MIT License (MIT)
|
||||
#
|
||||
# Copyright (c) 2018-2022 Dmitriy Yefremov
|
||||
# Copyright (c) 2018-2023 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
|
||||
@@ -37,9 +37,9 @@ from app.eparser import get_satellites, write_satellites, Satellite, Transponder
|
||||
from app.eparser.ecommons import (POLARIZATION, FEC, SYSTEM, MODULATION, T_SYSTEM, BANDWIDTH, CONSTELLATION, T_FEC,
|
||||
GUARD_INTERVAL, TRANSMISSION_MODE, HIERARCHY, Inversion, FEC_DEFAULT, C_MODULATION,
|
||||
Terrestrial, Cable, CableTransponder, TerTransponder)
|
||||
from app.eparser.satxml import get_terrestrial, get_cable, write_terrestrial, write_cable
|
||||
from .dialogs import SatelliteDialog, SatellitesUpdateDialog, TerrestrialDialog, CableDialog, SatTransponderDialog, \
|
||||
CableTransponderDialog, TerTransponderDialog
|
||||
from app.eparser.satxml import get_terrestrial, get_cable, write_terrestrial, write_cable, get_pos_str
|
||||
from .dialogs import (SatelliteDialog, SatellitesUpdateDialog, TerrestrialDialog, CableDialog, SatTransponderDialog,
|
||||
CableTransponderDialog, TerTransponderDialog)
|
||||
from ..dialogs import show_dialog, DialogType, get_chooser_dialog, get_message, get_builder
|
||||
from ..main_helper import move_items, on_popup_menu, scroll_to
|
||||
from ..uicommons import Gtk, Gdk, UI_RESOURCES_PATH, MOVE_KEYS, KeyboardKey, MOD_MASK, Page
|
||||
@@ -56,8 +56,8 @@ class SatellitesTool(Gtk.Box):
|
||||
def __str__(self):
|
||||
return self.value
|
||||
|
||||
def __init__(self, app, settings, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
def __init__(self, app, settings, **kwargs):
|
||||
super().__init__(**kwargs)
|
||||
|
||||
self._app = app
|
||||
self._app.connect("data-save", self.on_save)
|
||||
@@ -162,8 +162,7 @@ class SatellitesTool(Gtk.Box):
|
||||
|
||||
def sat_pos_func(self, column, renderer, model, itr, data):
|
||||
""" Converts and sets the satellite position value to a readable format. """
|
||||
pos = int(model.get_value(itr, 2))
|
||||
renderer.set_property("text", f"{abs(pos / 10):0.1f}{'W' if pos < 0 else 'E'}")
|
||||
renderer.set_property("text", get_pos_str(int(model.get_value(itr, 2))))
|
||||
|
||||
def sat_pol_func(self, column, renderer, model, itr, data):
|
||||
renderer.set_property("text", POLARIZATION.get(model.get_value(itr, 2), None))
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- Generated with glade 3.22.2
|
||||
<!-- Generated with glade 3.38.2
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
@@ -65,19 +65,19 @@ Author: Dmitriy Yefremov
|
||||
</object>
|
||||
<object class="GtkImage" id="popup_menu_add_image">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="stock">gtk-add</property>
|
||||
</object>
|
||||
<object class="GtkMenu" id="popup_menu">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="can-focus">False</property>
|
||||
<child>
|
||||
<object class="GtkImageMenuItem" id="add_sat_popup_menu_item">
|
||||
<property name="label" translatable="yes">Add</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="image">popup_menu_add_image</property>
|
||||
<property name="use_stock">False</property>
|
||||
<property name="use-stock">False</property>
|
||||
<signal name="activate" handler="on_add" swapped="no"/>
|
||||
<accelerator key="Insert" signal="activate"/>
|
||||
</object>
|
||||
@@ -85,16 +85,16 @@ Author: Dmitriy Yefremov
|
||||
<child>
|
||||
<object class="GtkSeparatorMenuItem" id="popup_sat_menu_separator">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="can-focus">False</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkImageMenuItem" id="edit_sat_popup_menu_item">
|
||||
<property name="label">gtk-edit</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="use_underline">True</property>
|
||||
<property name="use_stock">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="use-underline">True</property>
|
||||
<property name="use-stock">True</property>
|
||||
<signal name="activate" handler="on_edit" swapped="no"/>
|
||||
<accelerator key="e" signal="activate" modifiers="Primary"/>
|
||||
</object>
|
||||
@@ -102,16 +102,16 @@ Author: Dmitriy Yefremov
|
||||
<child>
|
||||
<object class="GtkSeparatorMenuItem" id="popup_sat_menu_separator_2">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="can-focus">False</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkImageMenuItem" id="remove_sat_popup_menu_item">
|
||||
<property name="label">gtk-remove</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="use_underline">True</property>
|
||||
<property name="use_stock">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="use-underline">True</property>
|
||||
<property name="use-stock">True</property>
|
||||
<signal name="activate" handler="on_remove" swapped="no"/>
|
||||
<accelerator key="Delete" signal="activate"/>
|
||||
</object>
|
||||
@@ -119,19 +119,19 @@ Author: Dmitriy Yefremov
|
||||
</object>
|
||||
<object class="GtkImage" id="popup_menu_add_image_2">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="stock">gtk-add</property>
|
||||
</object>
|
||||
<object class="GtkMenu" id="transponder_popup_menu">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="can-focus">False</property>
|
||||
<child>
|
||||
<object class="GtkImageMenuItem" id="add_tr_popup_menu_item">
|
||||
<property name="label" translatable="yes">Add</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="image">popup_menu_add_image_2</property>
|
||||
<property name="use_stock">False</property>
|
||||
<property name="use-stock">False</property>
|
||||
<signal name="activate" handler="on_transponder_add" swapped="no"/>
|
||||
<accelerator key="Insert" signal="activate"/>
|
||||
</object>
|
||||
@@ -139,16 +139,16 @@ Author: Dmitriy Yefremov
|
||||
<child>
|
||||
<object class="GtkSeparatorMenuItem" id="popup_tr_menu_separator">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="can-focus">False</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkImageMenuItem" id="edit_tr_popup_menu_item">
|
||||
<property name="label">gtk-edit</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="use_underline">True</property>
|
||||
<property name="use_stock">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="use-underline">True</property>
|
||||
<property name="use-stock">True</property>
|
||||
<signal name="activate" handler="on_transponder_edit" swapped="no"/>
|
||||
<accelerator key="e" signal="activate" modifiers="Primary"/>
|
||||
</object>
|
||||
@@ -156,16 +156,16 @@ Author: Dmitriy Yefremov
|
||||
<child>
|
||||
<object class="GtkSeparatorMenuItem" id="popup_tr_menu_separator_2">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="can-focus">False</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkImageMenuItem" id="remove_tr_popup_menu_item">
|
||||
<property name="label">gtk-remove</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="use_underline">True</property>
|
||||
<property name="use_stock">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="use-underline">True</property>
|
||||
<property name="use-stock">True</property>
|
||||
<signal name="activate" handler="on_transponder_remove" swapped="no"/>
|
||||
<accelerator key="Delete" signal="activate"/>
|
||||
</object>
|
||||
@@ -255,45 +255,58 @@ Author: Dmitriy Yefremov
|
||||
</object>
|
||||
<object class="GtkPaned" id="main_paned">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="wide_handle">True</property>
|
||||
<property name="can-focus">True</property>
|
||||
<property name="wide-handle">True</property>
|
||||
<child>
|
||||
<object class="GtkFrame" id="editor_sat_frame">
|
||||
<property name="width_request">360</property>
|
||||
<property name="width-request">360</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label_xalign">0.49000000953674316</property>
|
||||
<property name="shadow_type">in</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="label-xalign">0.49000000953674316</property>
|
||||
<property name="shadow-type">in</property>
|
||||
<child>
|
||||
<object class="GtkBox" id="editor_sat_box">
|
||||
<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="can-focus">False</property>
|
||||
<property name="margin-start">5</property>
|
||||
<property name="margin-end">5</property>
|
||||
<property name="margin-top">5</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<property name="spacing">5</property>
|
||||
<child>
|
||||
<object class="GtkBox" id="editor_header_box">
|
||||
<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">2</property>
|
||||
<property name="margin_bottom">5</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="margin-start">5</property>
|
||||
<property name="margin-end">5</property>
|
||||
<property name="margin-top">2</property>
|
||||
<property name="margin-bottom">5</property>
|
||||
<property name="spacing">5</property>
|
||||
<child type="center">
|
||||
<object class="GtkStackSwitcher" id="stack_switcher">
|
||||
<property name="name">header-stack-switcher</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="stack">sat_stack</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="add_header_button">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="tooltip_text" translatable="yes">Add</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="receives-default">True</property>
|
||||
<property name="tooltip-text" translatable="yes">Add</property>
|
||||
<signal name="clicked" handler="on_add" swapped="no"/>
|
||||
<child>
|
||||
<object class="GtkImage" id="add_satellite_image">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="icon_name">document-new-symbolic</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="icon-name">document-new-symbolic</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
@@ -306,15 +319,15 @@ Author: Dmitriy Yefremov
|
||||
<child>
|
||||
<object class="GtkButton" id="update_header_button">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="tooltip_text" translatable="yes">Update</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="receives-default">True</property>
|
||||
<property name="tooltip-text" translatable="yes">Update</property>
|
||||
<signal name="clicked" handler="on_update" swapped="no"/>
|
||||
<child>
|
||||
<object class="GtkImage" id="update_header_button_img">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="icon_name">emblem-synchronizing-symbolic</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="icon-name">emblem-synchronizing-symbolic</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
@@ -324,19 +337,6 @@ Author: Dmitriy Yefremov
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child type="center">
|
||||
<object class="GtkStackSwitcher" id="stack_switcher">
|
||||
<property name="visible">True</property>
|
||||
<property name="name">header-stack-switcher</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="stack">sat_stack</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
@@ -347,26 +347,26 @@ Author: Dmitriy Yefremov
|
||||
<child>
|
||||
<object class="GtkStack" id="sat_stack">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="can-focus">False</property>
|
||||
<signal name="notify::visible-child-name" handler="on_visible_page" swapped="no"/>
|
||||
<child>
|
||||
<object class="GtkBox" id="satellite_box">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="tooltip_text" translatable="yes">Satellites</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="tooltip-text" translatable="yes">Satellites</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<object class="GtkScrolledWindow" id="satellite_view_scrolled">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="shadow_type">in</property>
|
||||
<property name="can-focus">True</property>
|
||||
<property name="shadow-type">in</property>
|
||||
<child>
|
||||
<object class="GtkTreeView" id="satellite_view">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="can-focus">True</property>
|
||||
<property name="model">satellite_model</property>
|
||||
<property name="rubber_banding">True</property>
|
||||
<property name="activate_on_single_click">True</property>
|
||||
<property name="rubber-banding">True</property>
|
||||
<property name="activate-on-single-click">True</property>
|
||||
<signal name="button-press-event" handler="on_button_press" object="popup_menu" swapped="no"/>
|
||||
<signal name="cursor-changed" handler="on_satellite_selection" swapped="no"/>
|
||||
<signal name="key-press-event" handler="on_key_press" swapped="no"/>
|
||||
@@ -393,7 +393,7 @@ Author: Dmitriy Yefremov
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkTreeViewColumn" id="sat_pos_column">
|
||||
<property name="fixed_width">85</property>
|
||||
<property name="fixed-width">85</property>
|
||||
<property name="title" translatable="yes">Pos</property>
|
||||
<property name="alignment">0.49000000953674316</property>
|
||||
<child>
|
||||
@@ -417,17 +417,17 @@ Author: Dmitriy Yefremov
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkBox" id="sat_status_box">
|
||||
<property name="height_request">26</property>
|
||||
<property name="height-request">26</property>
|
||||
<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="can-focus">False</property>
|
||||
<property name="margin-start">5</property>
|
||||
<property name="margin-end">5</property>
|
||||
<property name="spacing">5</property>
|
||||
<child>
|
||||
<object class="GtkImage" id="sat_count_image">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="icon_name">document-properties</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="icon-name">document-properties</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
@@ -438,9 +438,9 @@ Author: Dmitriy Yefremov
|
||||
<child>
|
||||
<object class="GtkLabel" id="sat_count_label">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="label" translatable="yes">0</property>
|
||||
<property name="width_chars">4</property>
|
||||
<property name="width-chars">4</property>
|
||||
<property name="xalign">0</property>
|
||||
</object>
|
||||
<packing>
|
||||
@@ -465,22 +465,22 @@ Author: Dmitriy Yefremov
|
||||
<child>
|
||||
<object class="GtkBox" id="terrestrial_box">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="tooltip_text" translatable="yes">Terrestrial</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="tooltip-text" translatable="yes">Terrestrial</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<object class="GtkScrolledWindow" id="terrestrial_view_scrolled">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="shadow_type">in</property>
|
||||
<property name="can-focus">True</property>
|
||||
<property name="shadow-type">in</property>
|
||||
<child>
|
||||
<object class="GtkTreeView" id="terrestrial_view">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="can-focus">True</property>
|
||||
<property name="model">terrestrial_model</property>
|
||||
<property name="search_column">0</property>
|
||||
<property name="rubber_banding">True</property>
|
||||
<property name="activate_on_single_click">True</property>
|
||||
<property name="search-column">0</property>
|
||||
<property name="rubber-banding">True</property>
|
||||
<property name="activate-on-single-click">True</property>
|
||||
<signal name="button-press-event" handler="on_button_press" object="popup_menu" swapped="no"/>
|
||||
<signal name="cursor-changed" handler="on_terrestrial_selection" swapped="no"/>
|
||||
<signal name="key-press-event" handler="on_key_press" swapped="no"/>
|
||||
@@ -516,17 +516,17 @@ Author: Dmitriy Yefremov
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkBox" id="ter_status_box">
|
||||
<property name="height_request">26</property>
|
||||
<property name="height-request">26</property>
|
||||
<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="can-focus">False</property>
|
||||
<property name="margin-left">5</property>
|
||||
<property name="margin-right">5</property>
|
||||
<property name="spacing">5</property>
|
||||
<child>
|
||||
<object class="GtkImage" id="ter_count_image">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="icon_name">document-properties</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="icon-name">document-properties</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
@@ -537,9 +537,9 @@ Author: Dmitriy Yefremov
|
||||
<child>
|
||||
<object class="GtkLabel" id="ter_count_label">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="label" translatable="yes">0</property>
|
||||
<property name="width_chars">4</property>
|
||||
<property name="width-chars">4</property>
|
||||
<property name="xalign">0</property>
|
||||
</object>
|
||||
<packing>
|
||||
@@ -565,22 +565,22 @@ Author: Dmitriy Yefremov
|
||||
<child>
|
||||
<object class="GtkBox" id="cable_box">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="tooltip_text" translatable="yes">Cable</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="tooltip-text" translatable="yes">Cable</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<object class="GtkScrolledWindow" id="cable_view_scrolled">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="shadow_type">in</property>
|
||||
<property name="can-focus">True</property>
|
||||
<property name="shadow-type">in</property>
|
||||
<child>
|
||||
<object class="GtkTreeView" id="cable_view">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="can-focus">True</property>
|
||||
<property name="model">cable_model</property>
|
||||
<property name="search_column">0</property>
|
||||
<property name="rubber_banding">True</property>
|
||||
<property name="activate_on_single_click">True</property>
|
||||
<property name="search-column">0</property>
|
||||
<property name="rubber-banding">True</property>
|
||||
<property name="activate-on-single-click">True</property>
|
||||
<signal name="button-press-event" handler="on_button_press" object="popup_menu" swapped="no"/>
|
||||
<signal name="cursor-changed" handler="on_cable_selection" swapped="no"/>
|
||||
<signal name="key-press-event" handler="on_key_press" swapped="no"/>
|
||||
@@ -616,17 +616,17 @@ Author: Dmitriy Yefremov
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkBox" id="cable_status_box">
|
||||
<property name="height_request">26</property>
|
||||
<property name="height-request">26</property>
|
||||
<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="can-focus">False</property>
|
||||
<property name="margin-left">5</property>
|
||||
<property name="margin-right">5</property>
|
||||
<property name="spacing">5</property>
|
||||
<child>
|
||||
<object class="GtkImage" id="cable_count_image">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="icon_name">document-properties</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="icon-name">document-properties</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
@@ -637,9 +637,9 @@ Author: Dmitriy Yefremov
|
||||
<child>
|
||||
<object class="GtkLabel" id="cable_count_label">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="label" translatable="yes">0</property>
|
||||
<property name="width_chars">4</property>
|
||||
<property name="width-chars">4</property>
|
||||
<property name="xalign">0</property>
|
||||
</object>
|
||||
<packing>
|
||||
@@ -674,7 +674,7 @@ Author: Dmitriy Yefremov
|
||||
<child type="label">
|
||||
<object class="GtkLabel" id="editor_sat_frame_label">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="label" translatable="yes">DVB</property>
|
||||
</object>
|
||||
</child>
|
||||
@@ -687,39 +687,39 @@ Author: Dmitriy Yefremov
|
||||
<child>
|
||||
<object class="GtkFrame" id="editor_transponder_frame">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label_xalign">0.49000000953674316</property>
|
||||
<property name="shadow_type">in</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="label-xalign">0.49000000953674316</property>
|
||||
<property name="shadow-type">in</property>
|
||||
<child>
|
||||
<object class="GtkBox" id="transponders_box">
|
||||
<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="can-focus">False</property>
|
||||
<property name="margin-left">5</property>
|
||||
<property name="margin-right">5</property>
|
||||
<property name="margin-top">5</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<property name="spacing">5</property>
|
||||
<child>
|
||||
<object class="GtkBox" id="editor_tr_header_box">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_left">15</property>
|
||||
<property name="margin_right">15</property>
|
||||
<property name="margin_top">2</property>
|
||||
<property name="margin_bottom">5</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="margin-left">15</property>
|
||||
<property name="margin-right">15</property>
|
||||
<property name="margin-top">2</property>
|
||||
<property name="margin-bottom">5</property>
|
||||
<property name="spacing">5</property>
|
||||
<child>
|
||||
<object class="GtkButton" id="transponder_add_button">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="tooltip_text" translatable="yes">Add</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="receives-default">True</property>
|
||||
<property name="tooltip-text" translatable="yes">Add</property>
|
||||
<signal name="clicked" handler="on_transponder_add" swapped="no"/>
|
||||
<child>
|
||||
<object class="GtkImage" id="add_transponder_image">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="icon_name">document-new-symbolic</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="icon-name">document-new-symbolic</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
@@ -742,25 +742,25 @@ Author: Dmitriy Yefremov
|
||||
<child>
|
||||
<object class="GtkStack" id="transponders_stack">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="can-focus">False</property>
|
||||
<child>
|
||||
<object class="GtkBox" id="sat_tr_box">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<object class="GtkScrolledWindow" id="sat_tr_view_scrolled">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="shadow_type">in</property>
|
||||
<property name="can-focus">True</property>
|
||||
<property name="shadow-type">in</property>
|
||||
<child>
|
||||
<object class="GtkTreeView" id="sat_tr_view">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="can-focus">True</property>
|
||||
<property name="model">sat_tr_view_model</property>
|
||||
<property name="search_column">0</property>
|
||||
<property name="rubber_banding">True</property>
|
||||
<property name="enable_grid_lines">both</property>
|
||||
<property name="search-column">0</property>
|
||||
<property name="rubber-banding">True</property>
|
||||
<property name="enable-grid-lines">both</property>
|
||||
<signal name="button-press-event" handler="on_tr_button_press" object="transponder_popup_menu" swapped="no"/>
|
||||
<signal name="key-press-event" handler="on_tr_key_press" swapped="no"/>
|
||||
<child internal-child="selection">
|
||||
@@ -771,7 +771,7 @@ Author: Dmitriy Yefremov
|
||||
<child>
|
||||
<object class="GtkTreeViewColumn" id="freq_column">
|
||||
<property name="resizable">True</property>
|
||||
<property name="min_width">20</property>
|
||||
<property name="min-width">20</property>
|
||||
<property name="title" translatable="yes">Freq</property>
|
||||
<property name="expand">True</property>
|
||||
<property name="alignment">0.5</property>
|
||||
@@ -788,7 +788,7 @@ Author: Dmitriy Yefremov
|
||||
<child>
|
||||
<object class="GtkTreeViewColumn" id="rate_column">
|
||||
<property name="resizable">True</property>
|
||||
<property name="min_width">20</property>
|
||||
<property name="min-width">20</property>
|
||||
<property name="title" translatable="yes">Rate</property>
|
||||
<property name="expand">True</property>
|
||||
<property name="alignment">0.5</property>
|
||||
@@ -805,7 +805,7 @@ Author: Dmitriy Yefremov
|
||||
<child>
|
||||
<object class="GtkTreeViewColumn" id="pol_column">
|
||||
<property name="resizable">True</property>
|
||||
<property name="min_width">20</property>
|
||||
<property name="min-width">20</property>
|
||||
<property name="title" translatable="yes">Pol</property>
|
||||
<property name="expand">True</property>
|
||||
<property name="alignment">0.5</property>
|
||||
@@ -822,7 +822,7 @@ Author: Dmitriy Yefremov
|
||||
<child>
|
||||
<object class="GtkTreeViewColumn" id="fec_column">
|
||||
<property name="resizable">True</property>
|
||||
<property name="min_width">20</property>
|
||||
<property name="min-width">20</property>
|
||||
<property name="title" translatable="yes">FEC</property>
|
||||
<property name="expand">True</property>
|
||||
<property name="alignment">0.5</property>
|
||||
@@ -839,7 +839,7 @@ Author: Dmitriy Yefremov
|
||||
<child>
|
||||
<object class="GtkTreeViewColumn" id="sys_column">
|
||||
<property name="resizable">True</property>
|
||||
<property name="min_width">20</property>
|
||||
<property name="min-width">20</property>
|
||||
<property name="title" translatable="yes">System</property>
|
||||
<property name="expand">True</property>
|
||||
<property name="alignment">0.5</property>
|
||||
@@ -856,7 +856,7 @@ Author: Dmitriy Yefremov
|
||||
<child>
|
||||
<object class="GtkTreeViewColumn" id="mod_column">
|
||||
<property name="resizable">True</property>
|
||||
<property name="min_width">20</property>
|
||||
<property name="min-width">20</property>
|
||||
<property name="title" translatable="yes">Mod</property>
|
||||
<property name="expand">True</property>
|
||||
<property name="alignment">0.5</property>
|
||||
@@ -935,17 +935,17 @@ Author: Dmitriy Yefremov
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkBox" id="sat_tr_status_box">
|
||||
<property name="height_request">26</property>
|
||||
<property name="height-request">26</property>
|
||||
<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="can-focus">False</property>
|
||||
<property name="margin-left">5</property>
|
||||
<property name="margin-right">5</property>
|
||||
<property name="spacing">5</property>
|
||||
<child>
|
||||
<object class="GtkImage" id="sat_tr_count_image">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="icon_name">document-properties</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="icon-name">document-properties</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
@@ -956,9 +956,9 @@ Author: Dmitriy Yefremov
|
||||
<child>
|
||||
<object class="GtkLabel" id="sat_tr_count_label">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="label" translatable="yes">0</property>
|
||||
<property name="width_chars">4</property>
|
||||
<property name="width-chars">4</property>
|
||||
<property name="xalign">0</property>
|
||||
</object>
|
||||
<packing>
|
||||
@@ -983,21 +983,21 @@ Author: Dmitriy Yefremov
|
||||
<child>
|
||||
<object class="GtkBox" id="ter_tr_box">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<object class="GtkScrolledWindow" id="ter_tr_view_scrolled">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="shadow_type">in</property>
|
||||
<property name="can-focus">True</property>
|
||||
<property name="shadow-type">in</property>
|
||||
<child>
|
||||
<object class="GtkTreeView" id="ter_tr_view">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="can-focus">True</property>
|
||||
<property name="model">ter_tr_view_model</property>
|
||||
<property name="search_column">0</property>
|
||||
<property name="rubber_banding">True</property>
|
||||
<property name="enable_grid_lines">both</property>
|
||||
<property name="search-column">0</property>
|
||||
<property name="rubber-banding">True</property>
|
||||
<property name="enable-grid-lines">both</property>
|
||||
<signal name="button-press-event" handler="on_tr_button_press" object="transponder_popup_menu" swapped="no"/>
|
||||
<signal name="key-press-event" handler="on_tr_key_press" swapped="no"/>
|
||||
<child internal-child="selection">
|
||||
@@ -1008,7 +1008,7 @@ Author: Dmitriy Yefremov
|
||||
<child>
|
||||
<object class="GtkTreeViewColumn" id="ter_freq_column">
|
||||
<property name="resizable">True</property>
|
||||
<property name="min_width">20</property>
|
||||
<property name="min-width">20</property>
|
||||
<property name="title" translatable="yes">Freq</property>
|
||||
<property name="expand">True</property>
|
||||
<property name="alignment">0.5</property>
|
||||
@@ -1025,7 +1025,7 @@ Author: Dmitriy Yefremov
|
||||
<child>
|
||||
<object class="GtkTreeViewColumn" id="ter_system_column">
|
||||
<property name="resizable">True</property>
|
||||
<property name="min_width">20</property>
|
||||
<property name="min-width">20</property>
|
||||
<property name="title" translatable="yes">System</property>
|
||||
<property name="expand">True</property>
|
||||
<property name="alignment">0.5</property>
|
||||
@@ -1042,7 +1042,7 @@ Author: Dmitriy Yefremov
|
||||
<child>
|
||||
<object class="GtkTreeViewColumn" id="ter_bandwidth_column">
|
||||
<property name="resizable">True</property>
|
||||
<property name="min_width">20</property>
|
||||
<property name="min-width">20</property>
|
||||
<property name="title" translatable="yes">Bandwidth</property>
|
||||
<property name="expand">True</property>
|
||||
<property name="alignment">0.5</property>
|
||||
@@ -1059,7 +1059,7 @@ Author: Dmitriy Yefremov
|
||||
<child>
|
||||
<object class="GtkTreeViewColumn" id="ter_constellation_column">
|
||||
<property name="resizable">True</property>
|
||||
<property name="min_width">20</property>
|
||||
<property name="min-width">20</property>
|
||||
<property name="title" translatable="yes">Constellation</property>
|
||||
<property name="expand">True</property>
|
||||
<property name="alignment">0.5</property>
|
||||
@@ -1076,7 +1076,7 @@ Author: Dmitriy Yefremov
|
||||
<child>
|
||||
<object class="GtkTreeViewColumn" id="ter_rate_hp_column">
|
||||
<property name="resizable">True</property>
|
||||
<property name="min_width">20</property>
|
||||
<property name="min-width">20</property>
|
||||
<property name="title" translatable="yes">SR (HP)</property>
|
||||
<property name="expand">True</property>
|
||||
<property name="alignment">0.5</property>
|
||||
@@ -1107,7 +1107,7 @@ Author: Dmitriy Yefremov
|
||||
<child>
|
||||
<object class="GtkTreeViewColumn" id="ter_guard_column">
|
||||
<property name="resizable">True</property>
|
||||
<property name="min_width">20</property>
|
||||
<property name="min-width">20</property>
|
||||
<property name="title" translatable="yes">Guard</property>
|
||||
<property name="expand">True</property>
|
||||
<property name="alignment">0.5</property>
|
||||
@@ -1177,17 +1177,17 @@ Author: Dmitriy Yefremov
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkBox" id="ter_tr_status_box">
|
||||
<property name="height_request">26</property>
|
||||
<property name="height-request">26</property>
|
||||
<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="can-focus">False</property>
|
||||
<property name="margin-left">5</property>
|
||||
<property name="margin-right">5</property>
|
||||
<property name="spacing">5</property>
|
||||
<child>
|
||||
<object class="GtkImage" id="ter_tr_count_image">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="icon_name">document-properties</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="icon-name">document-properties</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
@@ -1198,9 +1198,9 @@ Author: Dmitriy Yefremov
|
||||
<child>
|
||||
<object class="GtkLabel" id="ter_tr_count_label">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="label" translatable="yes">0</property>
|
||||
<property name="width_chars">4</property>
|
||||
<property name="width-chars">4</property>
|
||||
<property name="xalign">0</property>
|
||||
</object>
|
||||
<packing>
|
||||
@@ -1226,21 +1226,21 @@ Author: Dmitriy Yefremov
|
||||
<child>
|
||||
<object class="GtkBox" id="cable_tr_box">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<object class="GtkScrolledWindow" id="cable_tr_view_scrolled">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="shadow_type">in</property>
|
||||
<property name="can-focus">True</property>
|
||||
<property name="shadow-type">in</property>
|
||||
<child>
|
||||
<object class="GtkTreeView" id="cable_tr_view">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="can-focus">True</property>
|
||||
<property name="model">cable_tr_view_model</property>
|
||||
<property name="search_column">0</property>
|
||||
<property name="rubber_banding">True</property>
|
||||
<property name="enable_grid_lines">both</property>
|
||||
<property name="search-column">0</property>
|
||||
<property name="rubber-banding">True</property>
|
||||
<property name="enable-grid-lines">both</property>
|
||||
<signal name="button-press-event" handler="on_tr_button_press" object="transponder_popup_menu" swapped="no"/>
|
||||
<signal name="key-press-event" handler="on_tr_key_press" swapped="no"/>
|
||||
<child internal-child="selection">
|
||||
@@ -1251,7 +1251,7 @@ Author: Dmitriy Yefremov
|
||||
<child>
|
||||
<object class="GtkTreeViewColumn" id="cable_freq_column">
|
||||
<property name="resizable">True</property>
|
||||
<property name="min_width">20</property>
|
||||
<property name="min-width">20</property>
|
||||
<property name="title" translatable="yes">Freq</property>
|
||||
<property name="expand">True</property>
|
||||
<property name="alignment">0.5</property>
|
||||
@@ -1268,7 +1268,7 @@ Author: Dmitriy Yefremov
|
||||
<child>
|
||||
<object class="GtkTreeViewColumn" id="cable_rate_column">
|
||||
<property name="resizable">True</property>
|
||||
<property name="min_width">20</property>
|
||||
<property name="min-width">20</property>
|
||||
<property name="title" translatable="yes">Rate</property>
|
||||
<property name="expand">True</property>
|
||||
<property name="alignment">0.5</property>
|
||||
@@ -1285,7 +1285,7 @@ Author: Dmitriy Yefremov
|
||||
<child>
|
||||
<object class="GtkTreeViewColumn" id="cable_fec_column">
|
||||
<property name="resizable">True</property>
|
||||
<property name="min_width">20</property>
|
||||
<property name="min-width">20</property>
|
||||
<property name="title" translatable="yes">FEC</property>
|
||||
<property name="expand">True</property>
|
||||
<property name="alignment">0.5</property>
|
||||
@@ -1302,7 +1302,7 @@ Author: Dmitriy Yefremov
|
||||
<child>
|
||||
<object class="GtkTreeViewColumn" id="cable_mod_column">
|
||||
<property name="resizable">True</property>
|
||||
<property name="min_width">20</property>
|
||||
<property name="min-width">20</property>
|
||||
<property name="title" translatable="yes">Mod</property>
|
||||
<property name="expand">True</property>
|
||||
<property name="alignment">0.5</property>
|
||||
@@ -1327,17 +1327,17 @@ Author: Dmitriy Yefremov
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkBox" id="cable_tr_status_box">
|
||||
<property name="height_request">26</property>
|
||||
<property name="height-request">26</property>
|
||||
<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="can-focus">False</property>
|
||||
<property name="margin-left">5</property>
|
||||
<property name="margin-right">5</property>
|
||||
<property name="spacing">5</property>
|
||||
<child>
|
||||
<object class="GtkImage" id="cable_tr_count_image">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="icon_name">document-properties</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="icon-name">document-properties</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
@@ -1348,9 +1348,9 @@ Author: Dmitriy Yefremov
|
||||
<child>
|
||||
<object class="GtkLabel" id="cable_tr_count_label">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="label" translatable="yes">0</property>
|
||||
<property name="width_chars">4</property>
|
||||
<property name="width-chars">4</property>
|
||||
<property name="xalign">0</property>
|
||||
</object>
|
||||
<packing>
|
||||
@@ -1385,7 +1385,7 @@ Author: Dmitriy Yefremov
|
||||
<child type="label">
|
||||
<object class="GtkLabel" id="editor_transponder_frame_label">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="label" translatable="yes">Transponder</property>
|
||||
</object>
|
||||
</child>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,5 @@
|
||||
#!/bin/bash
|
||||
VER="3.4.0_Beta"
|
||||
VER="3.6.1_Beta"
|
||||
B_PATH="dist/DemonEditor"
|
||||
DEB_PATH="$B_PATH/usr/share/demoneditor"
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
Package: demon-editor
|
||||
Version: 3.4.0-Beta
|
||||
Version: 3.6.1-Beta
|
||||
Section: utils
|
||||
Priority: optional
|
||||
Architecture: all
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -81,7 +81,7 @@ app = BUNDLE(coll,
|
||||
'CFBundleGetInfoString': "Enigma2 channel and satellite editor",
|
||||
'LSApplicationCategoryType': 'public.app-category.utilities',
|
||||
'LSMinimumSystemVersion': '10.13',
|
||||
'CFBundleShortVersionString': f"3.4.0.{BUILD_DATE} Beta",
|
||||
'CFBundleShortVersionString': f"3.6.1.{BUILD_DATE} Beta",
|
||||
'NSHumanReadableCopyright': u"Copyright © 2023, Dmitriy Yefremov",
|
||||
'NSRequiresAquaSystemAppearance': 'false',
|
||||
'NSHighResolutionCapable': 'true'
|
||||
|
||||
@@ -46,6 +46,10 @@ class BaseExtension(metaclass=Singleton):
|
||||
""" Base extension (plugin) class. """
|
||||
# The label that will be displayed in the "Tools" menu.
|
||||
LABEL = "Base extension"
|
||||
VERSION = "1.0"
|
||||
# Additional flags.
|
||||
EMBEDDED = False
|
||||
SWITCHABLE = False
|
||||
|
||||
_LOGGER_NAME = "main_logger"
|
||||
|
||||
@@ -64,6 +68,10 @@ class BaseExtension(metaclass=Singleton):
|
||||
"""
|
||||
self.app.show_info_message(f"Hello from {self.__class__.__name__} class!")
|
||||
|
||||
def stop(self):
|
||||
""" Stops (terminates) the task or the extension itself. """
|
||||
self.log("Terminating a task...")
|
||||
|
||||
def log(self, message, level=logging.ERROR):
|
||||
""" Shows log messages. """
|
||||
logging.getLogger(self._LOGGER_NAME).log(level, f"[{self.__class__.__name__}] {message}")
|
||||
|
||||
@@ -1440,3 +1440,51 @@ msgstr "Уключыць падтрымку пашырэнняў"
|
||||
|
||||
msgid "After uploading the changes you may need to completely reboot the receiver!"
|
||||
msgstr "Пасля загрузкі змен можа запатрабавацца перазапуск рэсівера!"
|
||||
|
||||
msgid "Remove duplicates"
|
||||
msgstr "Выдаліць дублікаты"
|
||||
|
||||
msgid "Removed"
|
||||
msgstr "Выдалена"
|
||||
|
||||
msgid "Enables overwriting existing main list services."
|
||||
msgstr "Улучае перазапіс існых сэрвісаў асноўнага спіса."
|
||||
|
||||
msgid "Enables skipping services import from lamedb."
|
||||
msgstr "Улучае пропуск імпарту сэрвісаў з lamedb."
|
||||
|
||||
msgid "Bouquets data only"
|
||||
msgstr "Толькі дадзеныя букетаў"
|
||||
|
||||
msgid "Create Category bouquets"
|
||||
msgstr "Стварыць букеты па катэгорыях"
|
||||
|
||||
msgid "Create Regional bouquets"
|
||||
msgstr "Стварыць букеты па рэгіёнах"
|
||||
|
||||
msgid "Skip C-band"
|
||||
msgstr "Прапусціць C-дыяпазон"
|
||||
|
||||
msgid "Automatically set the name selected in the bouquet list."
|
||||
msgstr "Аўтаматычная ўсталёўка імя, абранага ў спісе букетаў."
|
||||
|
||||
msgid "Merge satellites by positions"
|
||||
msgstr "Аб'яднаць спадарожнікі па пазіцыях"
|
||||
|
||||
msgid "Save satellite selection"
|
||||
msgstr "Захоўваць выбар спадарожнікаў"
|
||||
|
||||
msgid "Extract direct links"
|
||||
msgstr "Выняць простыя спасылкі"
|
||||
|
||||
msgid "URL prefix:"
|
||||
msgstr "Префикс URL:"
|
||||
|
||||
msgid "Invalid prefix for the given URL!"
|
||||
msgstr "Недапушчальны прэфікс для дадзенага URL!"
|
||||
|
||||
msgid "Alternate window title"
|
||||
msgstr "Альтэрнатыўны загаловак акна"
|
||||
|
||||
msgid "Selected type:"
|
||||
msgstr "Абраны тып:"
|
||||
|
||||
@@ -1454,3 +1454,51 @@ msgstr "Erweiterungen Unterstützung aktivieren"
|
||||
|
||||
msgid "After uploading the changes you may need to completely reboot the receiver!"
|
||||
msgstr "Nach dem Hochladen der Änderungen muss den Receiver eventuell komplett neu starten!"
|
||||
|
||||
msgid "Remove duplicates"
|
||||
msgstr "Duplikate entfernen"
|
||||
|
||||
msgid "Removed"
|
||||
msgstr "Entfernt"
|
||||
|
||||
msgid "Enables overwriting existing main list services."
|
||||
msgstr "Ermöglicht das Überschreiben vorhandener Hauptlistendienste."
|
||||
|
||||
msgid "Enables skipping services import from lamedb."
|
||||
msgstr "Ermöglicht das Überspringen von Dienstimporten aus lamedb."
|
||||
|
||||
msgid "Bouquets data only"
|
||||
msgstr "Nur Bouquets-Daten"
|
||||
|
||||
msgid "Create Category bouquets"
|
||||
msgstr "Erstellen Kategories-Bouquets"
|
||||
|
||||
msgid "Create Regional bouquets"
|
||||
msgstr "Erstellen Regionale-Bouquets"
|
||||
|
||||
msgid "Skip C-band"
|
||||
msgstr "C-Band überspringen"
|
||||
|
||||
msgid "Automatically set the name selected in the bouquet list."
|
||||
msgstr "Stellen in der Bouquet-Liste ausgewählten Namen automatisch ein."
|
||||
|
||||
msgid "Merge satellites by positions"
|
||||
msgstr "Satelliten nach Pos. zusammenführen"
|
||||
|
||||
msgid "Save satellite selection"
|
||||
msgstr "Satellitenauswahl speichern"
|
||||
|
||||
msgid "Extract direct links"
|
||||
msgstr "Direktlinks extrahieren"
|
||||
|
||||
msgid "URL prefix:"
|
||||
msgstr "URL-Präfix:"
|
||||
|
||||
msgid "Invalid prefix for the given URL!"
|
||||
msgstr "Ungültiges Präfix für die angegebene URL!"
|
||||
|
||||
msgid "Alternate window title"
|
||||
msgstr "Alternativer Fenstertitel"
|
||||
|
||||
msgid "Selected type:"
|
||||
msgstr "Ausgewählt Typ:"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# Copyright (C) 2018-2022 Víctor Pont
|
||||
# Copyright (C) 2018-2023 Frank Neirynck
|
||||
# This file is distributed under the MIT license.
|
||||
#
|
||||
# Frank Neirynck <frank@insink.be>, 2018-2020.
|
||||
# Frank Neirynck <frank@insink.be>, 2018-2023.
|
||||
# Víctor Pont <victor@pont.cat>, 2021-2022.
|
||||
msgid ""
|
||||
msgstr ""
|
||||
@@ -1441,3 +1441,57 @@ msgstr "Habilita la carga como un archivo si se selecciona una gran cantidad de
|
||||
|
||||
msgid "Clear \"New\" flag"
|
||||
msgstr "Limpiar \"Nuevo\" flag"
|
||||
|
||||
msgid "Group by"
|
||||
msgstr "Agrupar por"
|
||||
|
||||
msgid "Replace existing"
|
||||
msgstr "Sustituir los existentes"
|
||||
|
||||
msgid "Already exists"
|
||||
msgstr "Ya existe"
|
||||
|
||||
msgid "Enable unlimited copy buffer"
|
||||
msgstr "Activar búfer de copia ilimitado"
|
||||
|
||||
msgid "Enables unlimited copy buffer for the bouquets tab."
|
||||
msgstr "Activa el búfer de copia ilimitado para la pestaña de bouquets."
|
||||
|
||||
msgid "Start time"
|
||||
msgstr "Hora de inicio"
|
||||
|
||||
msgid "End time"
|
||||
msgstr "Hora final"
|
||||
|
||||
msgid "Enable extensions support"
|
||||
msgstr "Activar el soporte de extensiones"
|
||||
|
||||
msgid "After uploading the changes you may need to completely reboot the receiver!"
|
||||
msgstr "Después de cargar los cambios, es posible que tenga que reiniciar completamente el receptor."
|
||||
|
||||
msgid "Remove duplicates"
|
||||
msgstr "Eliminar duplicados"
|
||||
|
||||
msgid "Removed"
|
||||
msgstr "Eliminado"
|
||||
|
||||
msgid "Enables overwriting existing main list services."
|
||||
msgstr "Permite sobrescribir los servicios existentes de la lista principal."
|
||||
|
||||
msgid "Enables skipping services import from lamedb."
|
||||
msgstr "Permite omitir la importación de servicios desde lamedb."
|
||||
|
||||
msgid "Bouquets data only"
|
||||
msgstr "Sólo datos de bouquets"
|
||||
|
||||
msgid "Create Category bouquets"
|
||||
msgstr "Crear categoría de bouquets"
|
||||
|
||||
msgid "Create Regional bouquets"
|
||||
msgstr "Crear bouquets regionales"
|
||||
|
||||
msgid "Skip C-band"
|
||||
msgstr "Omitir banda C"
|
||||
|
||||
msgid "Automatically set the name selected in the bouquet list."
|
||||
msgstr "Establece automáticamente el nombre seleccionado en la lista de bouquets."
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"PO-Revision-Date: 2023-02-12 07:10+0100\n"
|
||||
"PO-Revision-Date: 2023-04-13 02:15+0200\n"
|
||||
"Last-Translator: Massimo Pissarello <mapi68@gmail.com>\n"
|
||||
"Language-Team: Italian <>\n"
|
||||
"Language: it\n"
|
||||
@@ -14,7 +14,7 @@ msgstr ""
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
"X-Generator: Lokalize 22.12.2\n"
|
||||
"X-Generator: Lokalize 22.12.3\n"
|
||||
|
||||
msgid "translator-credits"
|
||||
msgstr "Massimo Pissarello"
|
||||
@@ -1230,6 +1230,9 @@ msgstr "Dimensione"
|
||||
msgid "Date"
|
||||
msgstr "Data"
|
||||
|
||||
msgid "Attr."
|
||||
msgstr "Attr"
|
||||
|
||||
msgid "Toggle display position"
|
||||
msgstr "Commuta posizione visualizzazione"
|
||||
|
||||
@@ -1492,3 +1495,58 @@ msgstr "Ora di fine"
|
||||
|
||||
msgid "Enable extensions support"
|
||||
msgstr "Abilita supporto estensioni"
|
||||
|
||||
msgid ""
|
||||
"After uploading the changes you may need to completely reboot the receiver!"
|
||||
msgstr ""
|
||||
"Dopo aver caricato le modifiche potrebbe essere necessario riavviare"
|
||||
" completamente il ricevitore!"
|
||||
|
||||
msgid "Remove duplicates"
|
||||
msgstr "Rimuovi duplicati"
|
||||
|
||||
msgid "Removed"
|
||||
msgstr "Rimosso"
|
||||
|
||||
msgid "Enables overwriting existing main list services."
|
||||
msgstr "Consente di sovrascrivere i servizi esistenti dell'elenco principale."
|
||||
|
||||
msgid "Enables skipping services import from lamedb."
|
||||
msgstr "Consente di saltare l'importazione dei servizi da lamedb."
|
||||
|
||||
msgid "Bouquets data only"
|
||||
msgstr "Bouquet solo dati"
|
||||
|
||||
msgid "Create Category bouquets"
|
||||
msgstr "Crea bouquet categorie"
|
||||
|
||||
msgid "Create Regional bouquets"
|
||||
msgstr "Crea bouquet regionali"
|
||||
|
||||
msgid "Skip C-band"
|
||||
msgstr "Salta banda C"
|
||||
|
||||
msgid "Automatically set the name selected in the bouquet list."
|
||||
msgstr "Imposta automaticamente il nome selezionato nell'elenco dei bouquet."
|
||||
|
||||
msgid "Merge satellites by positions"
|
||||
msgstr "Unisci i satelliti per posizione"
|
||||
|
||||
msgid "Save satellite selection"
|
||||
msgstr "Salva la selezione dei satelliti"
|
||||
|
||||
msgid "Extract direct links"
|
||||
msgstr "Estrai link diretti"
|
||||
|
||||
msgid "URL prefix:"
|
||||
msgstr "Prefisso URL:"
|
||||
|
||||
msgid "Invalid prefix for the given URL!"
|
||||
msgstr "Prefisso non valido per l'URL specificata!"
|
||||
|
||||
msgid "Alternate window title"
|
||||
msgstr "Titolo alternativo della finestra"
|
||||
|
||||
msgid "Selected type:"
|
||||
msgstr "Tipo selezionato:"
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Copyright (C) 2018-2020 Frank Neirynck
|
||||
# Copyright (C) 2018-2023 Frank Neirynck
|
||||
# This file is distributed under the MIT license.
|
||||
#
|
||||
# Frank Neirynck <frank@insink.be>, 2018-2022.
|
||||
@@ -1401,3 +1401,57 @@ msgstr "Maakt uploaden als archief mogelijk als een groot aantal pictogrammen (>
|
||||
|
||||
msgid "Clear \"New\" flag"
|
||||
msgstr "Opruimen \"Niew\" flag"
|
||||
|
||||
msgid "Group by"
|
||||
msgstr "Groeperen per"
|
||||
|
||||
msgid "Replace existing"
|
||||
msgstr "Vervang bestaande"
|
||||
|
||||
msgid "Already exists"
|
||||
msgstr "Bestaat reeds"
|
||||
|
||||
msgid "Enable unlimited copy buffer"
|
||||
msgstr "Onbeperkte kopieerbuffer inschakelen"
|
||||
|
||||
msgid "Enables unlimited copy buffer for the bouquets tab."
|
||||
msgstr "Schakelt een onbeperkte kopieerbuffer in voor het boeketten tabblad."
|
||||
|
||||
msgid "Start time"
|
||||
msgstr "Starttijd"
|
||||
|
||||
msgid "End time"
|
||||
msgstr "Eindtijd"
|
||||
|
||||
msgid "Enable extensions support"
|
||||
msgstr "Ondersteuning voor extensies inschakelen"
|
||||
|
||||
msgid "After uploading the changes you may need to completely reboot the receiver!"
|
||||
msgstr "Na het uploaden van de wijzigingen kan het nodig zijn de ontvanger volledig opnieuw op te starten!"
|
||||
|
||||
msgid "Remove duplicates"
|
||||
msgstr "Duplicaten verwijderen"
|
||||
|
||||
msgid "Removed"
|
||||
msgstr "Verwijderd"
|
||||
|
||||
msgid "Enables overwriting existing main list services."
|
||||
msgstr "Maakt het overschrijven van bestaande hoofdlijst diensten mogelijk."
|
||||
|
||||
msgid "Enables skipping services import from lamedb."
|
||||
msgstr "Maakt het overslaan van dienstenimport uit lamedb mogelijk."
|
||||
|
||||
msgid "Bouquets data only"
|
||||
msgstr "Alleen gegevens over boeketten"
|
||||
|
||||
msgid "Create Category bouquets"
|
||||
msgstr "Maak categorie boeketten"
|
||||
|
||||
msgid "Create Regional bouquets"
|
||||
msgstr "Maak regionale boeketten"
|
||||
|
||||
msgid "Skip C-band"
|
||||
msgstr "C-band overslaan"
|
||||
|
||||
msgid "Automatically set the name selected in the bouquet list."
|
||||
msgstr "Stel automatisch de naam in die is geselecteerd in de boeketlijst."
|
||||
|
||||
@@ -1437,3 +1437,51 @@ msgstr "Включить поддержку расширений"
|
||||
|
||||
msgid "After uploading the changes you may need to completely reboot the receiver!"
|
||||
msgstr "После загрузки изменений может потребоваться перезапуск ресивера!"
|
||||
|
||||
msgid "Remove duplicates"
|
||||
msgstr "Удалить дубликаты"
|
||||
|
||||
msgid "Removed"
|
||||
msgstr "Удалено"
|
||||
|
||||
msgid "Enables overwriting existing main list services."
|
||||
msgstr "Включает перезапись существующих сервисов основного списка."
|
||||
|
||||
msgid "Enables skipping services import from lamedb."
|
||||
msgstr "Включает пропуск импорта сервисов из lamedb."
|
||||
|
||||
msgid "Bouquets data only"
|
||||
msgstr "Только данные букетов"
|
||||
|
||||
msgid "Create Category bouquets"
|
||||
msgstr "Создать букеты по категориям"
|
||||
|
||||
msgid "Create Regional bouquets"
|
||||
msgstr "Создать букеты по регионам"
|
||||
|
||||
msgid "Skip C-band"
|
||||
msgstr "Пропустить C-диапазон"
|
||||
|
||||
msgid "Automatically set the name selected in the bouquet list."
|
||||
msgstr "Автоматическая установка имени, выбранного в списке букетов."
|
||||
|
||||
msgid "Merge satellites by positions"
|
||||
msgstr "Объединять спутники по позициям"
|
||||
|
||||
msgid "Save satellite selection"
|
||||
msgstr "Сохранять выбор спутников"
|
||||
|
||||
msgid "Extract direct links"
|
||||
msgstr "Извлечь прямые ссылки"
|
||||
|
||||
msgid "URL prefix:"
|
||||
msgstr "Префикс URL:"
|
||||
|
||||
msgid "Invalid prefix for the given URL!"
|
||||
msgstr "Недопустимый префикс для данного URL!"
|
||||
|
||||
msgid "Alternate window title"
|
||||
msgstr "Альтернативный заголовок окна"
|
||||
|
||||
msgid "Selected type:"
|
||||
msgstr "Выбран тип:"
|
||||
|
||||
@@ -3,9 +3,9 @@ msgstr ""
|
||||
"Project-Id-Version: DemonEditor\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2020-04-16 15:59+0300\n"
|
||||
"PO-Revision-Date: 2023-02-06 15:41+0300\n"
|
||||
"PO-Revision-Date: 2023-04-22 16:34+0300\n"
|
||||
"Last-Translator: audi06_19 <info@dreamosat-forum.com>\n"
|
||||
"Language-Team: \n"
|
||||
"Language-Team: audi06_19 <info@dreamosat-forum.com>\n"
|
||||
"Language: tr\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
@@ -1465,3 +1465,57 @@ msgstr "Başlangıç saati"
|
||||
|
||||
msgid "End time"
|
||||
msgstr "Bitiş zamanı"
|
||||
|
||||
msgid "Enable extensions support"
|
||||
msgstr "Uzantı desteğini etkinleştir"
|
||||
|
||||
msgid "After uploading the changes you may need to completely reboot the receiver!"
|
||||
msgstr "Değişiklikleri yükledikten sonra, alıcıyı tamamen yeniden başlatmanız gerekebilir!"
|
||||
|
||||
msgid "Remove duplicates"
|
||||
msgstr "Kopyaları kaldır"
|
||||
|
||||
msgid "Removed"
|
||||
msgstr "Kaldırıldı"
|
||||
|
||||
msgid "Enables overwriting existing main list services."
|
||||
msgstr "Mevcut ana liste hizmetlerinin üzerine yazılmasını sağlar."
|
||||
|
||||
msgid "Enables skipping services import from lamedb."
|
||||
msgstr "Lamedb'den içe aktarılan hizmetlerin atlanmasını sağlar."
|
||||
|
||||
msgid "Bouquets data only"
|
||||
msgstr "Yalnızca buket verileri"
|
||||
|
||||
msgid "Create Category bouquets"
|
||||
msgstr "Kategori buketleri oluştur"
|
||||
|
||||
msgid "Create Regional bouquets"
|
||||
msgstr "Bölgesel buketler oluşturun"
|
||||
|
||||
msgid "Skip C-band"
|
||||
msgstr "C-band atla"
|
||||
|
||||
msgid "Automatically set the name selected in the bouquet list."
|
||||
msgstr "Buket listesinde seçilen isimleri otomatik olarak ayarlayın."
|
||||
|
||||
msgid "Merge satellites by positions"
|
||||
msgstr "Uyduları konumlara göre birleştir"
|
||||
|
||||
msgid "Save satellite selection"
|
||||
msgstr "Uydu seçimini kaydet"
|
||||
|
||||
msgid "Extract direct links"
|
||||
msgstr "Doğrudan bağlantıları ayıklayın"
|
||||
|
||||
msgid "URL prefix:"
|
||||
msgstr "URL öneki:"
|
||||
|
||||
msgid "Invalid prefix for the given URL!"
|
||||
msgstr "Belirtilen URL için geçersiz önek!"
|
||||
|
||||
msgid "Alternate window title"
|
||||
msgstr "Alternatif pencere başlığı"
|
||||
|
||||
msgid "Selected type:"
|
||||
msgstr "Seçilen tip:"
|
||||
|
||||
Reference in New Issue
Block a user