mirror of
https://github.com/DYefremov/DemonEditor.git
synced 2026-05-09 06:36:34 +02:00
Compare commits
86 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fd1c1bfd6e | ||
|
|
c48a08b239 | ||
|
|
b647b0a338 | ||
|
|
9b608eeb74 | ||
|
|
07e55b3f1e | ||
|
|
7e639f5637 | ||
|
|
5570d47cae | ||
|
|
5c49c0d123 | ||
|
|
ee6dd511b5 | ||
|
|
1f847233b3 | ||
|
|
835e1af8e4 | ||
|
|
50e0d8b66a | ||
|
|
fb0789664a | ||
|
|
5dd39492f2 | ||
|
|
33be9f21a2 | ||
|
|
7acc9ae74f | ||
|
|
d492022232 | ||
|
|
b034995130 | ||
|
|
f037b3554d | ||
|
|
7f1f27da57 | ||
|
|
d7f3afecb0 | ||
|
|
f309005c52 | ||
|
|
25661816e7 | ||
|
|
a2652cef4b | ||
|
|
adbc9ad322 | ||
|
|
2dc8611294 | ||
|
|
72bfd21056 | ||
|
|
65ef018f81 | ||
|
|
1236c5ebc9 | ||
|
|
f0011ebcf2 | ||
|
|
392e94e7ba | ||
|
|
c6de18271d | ||
|
|
71a65242c1 | ||
|
|
4efc956870 | ||
|
|
a605fdd545 | ||
|
|
285c1cae69 | ||
|
|
2e937a42a3 | ||
|
|
e208cf4656 | ||
|
|
3db82e3e18 | ||
|
|
38e9a85694 | ||
|
|
438e9c10d4 | ||
|
|
920fa01159 | ||
|
|
d2787364cd | ||
|
|
839c0fae23 | ||
|
|
f87548e12e | ||
|
|
463702c371 | ||
|
|
0b84a81439 | ||
|
|
6b68740961 | ||
|
|
92aa2400f6 | ||
|
|
2cf4e5b756 | ||
|
|
6cfa68e219 | ||
|
|
138aa54b44 | ||
|
|
3e1a3d1595 | ||
|
|
b833458c45 | ||
|
|
d1e88be1cc | ||
|
|
72e128aeb9 | ||
|
|
457b4e4645 | ||
|
|
076243b0ac | ||
|
|
e5ff185791 | ||
|
|
2b6b0dd827 | ||
|
|
a11fdd683e | ||
|
|
636bc5c52f | ||
|
|
90d64d46c7 | ||
|
|
115f77108c | ||
|
|
2082f8e973 | ||
|
|
7e586bf0a6 | ||
|
|
c5c4823534 | ||
|
|
6235519cf9 | ||
|
|
7d3a9f768c | ||
|
|
37bea3a93c | ||
|
|
33a22fdca7 | ||
|
|
d54e97b1b8 | ||
|
|
12c2a449ea | ||
|
|
7164f54773 | ||
|
|
84adeb994e | ||
|
|
69c3b4a6c1 | ||
|
|
cbec74c2a4 | ||
|
|
8d856bc989 | ||
|
|
a1ec7600da | ||
|
|
2fd71c3645 | ||
|
|
a0f6b1f651 | ||
|
|
671c2204bf | ||
|
|
d2a0419c06 | ||
|
|
fb48395d1e | ||
|
|
44fb760241 | ||
|
|
428a240416 |
2
LICENSE
2
LICENSE
@@ -1,6 +1,6 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2018-2021 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
|
||||
|
||||
@@ -30,8 +30,7 @@ Experimental support of Neutrino-MP or others on the same basis (BPanther, etc).
|
||||
|
||||
#### Keyboard shortcuts
|
||||
* **Ctrl + X** - only in bouquet list.
|
||||
* **Ctrl + C** - only in services list.
|
||||
Clipboard is **"rubber"**. There is an accumulation before the insertion!
|
||||
* **Ctrl + C** - only in services list.
|
||||
* **Ctrl + Insert** - copies the selected channels from the main list to the bouquet
|
||||
beginning or inserts (creates) a new bouquet.
|
||||
* **Ctrl + BackSpace** - copies the selected channels from the main list to the bouquet end.
|
||||
|
||||
@@ -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
|
||||
@@ -35,6 +35,7 @@ import xml.etree.ElementTree as ETree
|
||||
from enum import Enum
|
||||
from ftplib import FTP, CRLF, Error, all_errors
|
||||
from http.client import RemoteDisconnected
|
||||
from pathlib import Path
|
||||
from telnetlib import Telnet
|
||||
from urllib.error import HTTPError, URLError
|
||||
from urllib.parse import urlencode, quote
|
||||
@@ -89,11 +90,11 @@ class UtfFTP(FTP):
|
||||
while 1:
|
||||
line = fp.readline(self.maxline + 1)
|
||||
if len(line) > self.maxline:
|
||||
msg = "UtfFTP [retrlines] error: got more than {} bytes".format(self.maxline)
|
||||
msg = f"UtfFTP [retrlines] error: got more than {self.maxline} bytes"
|
||||
log(msg)
|
||||
raise Error(msg)
|
||||
if self.debugging > 2:
|
||||
log('UtfFTP [retrlines] *retr* {}'.format(repr(line)))
|
||||
log(f"UtfFTP [retrlines] *retr* {repr(line)}")
|
||||
if not line:
|
||||
break
|
||||
if line[-2:] == CRLF:
|
||||
@@ -112,9 +113,8 @@ class UtfFTP(FTP):
|
||||
|
||||
def download_file(self, name, save_path, callback=None):
|
||||
with open(save_path + name, "wb") as f:
|
||||
msg = "Downloading file: {}. Status: {}"
|
||||
resp = self.download_binary(name, f)
|
||||
msg = msg.format(name, resp)
|
||||
msg = f"Downloading file: {name}. Status: {resp}"
|
||||
callback(msg) if callback else log(msg.rstrip())
|
||||
|
||||
return resp
|
||||
@@ -391,11 +391,12 @@ def download_data(*, settings, download_type=DownloadType.ALL, callback=log, fil
|
||||
callback("*** Done. ***")
|
||||
|
||||
|
||||
def upload_data(*, settings, download_type=DownloadType.ALL, remove_unused=False,
|
||||
callback=log, done_callback=None, use_http=False, files_filter=None):
|
||||
def upload_data(*, settings, download_type=DownloadType.ALL, callback=log, done_callback=None,
|
||||
files_filter=None, ext_host=None):
|
||||
s_type = settings.setting_type
|
||||
use_http = s_type is SettingsType.ENIGMA_2 and settings.use_http
|
||||
data_path = settings.profile_data_path
|
||||
host, port, use_ssl = settings.host, settings.http_port, settings.http_use_ssl
|
||||
host, port, use_ssl = ext_host or settings.host, settings.http_port, settings.http_use_ssl
|
||||
user, password = settings.user, settings.password
|
||||
base_url = f"http{'s' if use_ssl else ''}://{host}:{port}"
|
||||
base = "web" if s_type is SettingsType.ENIGMA_2 else "control"
|
||||
@@ -412,15 +413,7 @@ def upload_data(*, settings, download_type=DownloadType.ALL, remove_unused=False
|
||||
if use_http:
|
||||
ht = http(user, password, base_url, callback, use_ssl, s_type)
|
||||
next(ht)
|
||||
message = ""
|
||||
if download_type is DownloadType.BOUQUETS:
|
||||
message = "User bouquets will be updated!"
|
||||
elif download_type is DownloadType.ALL:
|
||||
message = "All user data will be reloaded!"
|
||||
elif download_type is DownloadType.SATELLITES:
|
||||
message = "Satellites.xml file will be updated!"
|
||||
elif download_type is DownloadType.PICONS:
|
||||
message = "Picons will be updated!"
|
||||
message = get_upload_info_message(download_type)
|
||||
|
||||
if s_type is SettingsType.ENIGMA_2:
|
||||
params = urlencode({"text": message, "type": 2, "timeout": 5})
|
||||
@@ -431,7 +424,8 @@ def upload_data(*, settings, download_type=DownloadType.ALL, remove_unused=False
|
||||
|
||||
if s_type is SettingsType.ENIGMA_2 and download_type is DownloadType.ALL:
|
||||
time.sleep(5)
|
||||
ht.send((f"{url}powerstate?newstate=0", "Toggle Standby "))
|
||||
if not settings.keep_power_mode:
|
||||
ht.send((f"{url}powerstate?newstate=0", "Toggle Standby "))
|
||||
time.sleep(2)
|
||||
else:
|
||||
if download_type is not DownloadType.PICONS:
|
||||
@@ -457,7 +451,7 @@ def upload_data(*, settings, download_type=DownloadType.ALL, remove_unused=False
|
||||
|
||||
if download_type is DownloadType.BOUQUETS:
|
||||
ftp.cwd(services_path)
|
||||
ftp.upload_bouquets(data_path, remove_unused, callback)
|
||||
ftp.upload_bouquets(data_path, settings.remove_unused_bouquets, callback)
|
||||
|
||||
if download_type is DownloadType.ALL:
|
||||
ftp.upload_xml(data_path, sat_xml_path, STC_XML_FILE, callback)
|
||||
@@ -465,7 +459,7 @@ def upload_data(*, settings, download_type=DownloadType.ALL, remove_unused=False
|
||||
ftp.upload_xml(data_path, sat_xml_path, WEB_TV_XML_FILE, callback)
|
||||
|
||||
ftp.cwd(services_path)
|
||||
ftp.upload_bouquets(data_path, remove_unused, callback)
|
||||
ftp.upload_bouquets(data_path, settings.remove_unused_bouquets, callback)
|
||||
ftp.upload_files(data_path, DATA_FILES_LIST, callback)
|
||||
|
||||
if download_type is DownloadType.PICONS:
|
||||
@@ -476,7 +470,7 @@ def upload_data(*, settings, download_type=DownloadType.ALL, remove_unused=False
|
||||
|
||||
z_name = "picons.zip"
|
||||
zip_file = f"{p_src}{z_name}"
|
||||
p_dst = os.path.abspath(os.path.join(p_dst, os.pardir))
|
||||
p_dst = Path(p_dst).parent.as_posix()
|
||||
|
||||
if files_filter and z_name in files_filter:
|
||||
files_filter.remove(z_name)
|
||||
@@ -522,7 +516,8 @@ def upload_data(*, settings, download_type=DownloadType.ALL, remove_unused=False
|
||||
ht.send((f"{url}servicelistreload?mode=2", "Reloading Userbouquets."))
|
||||
elif download_type is DownloadType.ALL:
|
||||
ht.send((f"{url}servicelistreload?mode=0", "Reloading lamedb and Userbouquets."))
|
||||
ht.send((f"{url}powerstate?newstate=4", "Wakeup from Standby."))
|
||||
if not settings.keep_power_mode:
|
||||
ht.send((f"{url}powerstate?newstate=4", "Wakeup from Standby."))
|
||||
else:
|
||||
ht.send((f"{url}reloadchannels", "Reloading channels..."))
|
||||
|
||||
@@ -535,6 +530,18 @@ def upload_data(*, settings, download_type=DownloadType.ALL, remove_unused=False
|
||||
ht.close()
|
||||
|
||||
|
||||
def get_upload_info_message(download_type):
|
||||
if download_type is DownloadType.BOUQUETS:
|
||||
return "User bouquets will be updated!"
|
||||
elif download_type is DownloadType.ALL:
|
||||
return "All user data will be reloaded!"
|
||||
elif download_type is DownloadType.SATELLITES:
|
||||
return "Satellites.xml file will be updated!"
|
||||
elif download_type is DownloadType.PICONS:
|
||||
return "Picons will be updated!"
|
||||
return ""
|
||||
|
||||
|
||||
# ***************** Picons *******************#
|
||||
|
||||
def remove_picons(*, settings, callback=log, done_callback=None, files_filter=None):
|
||||
@@ -643,6 +650,16 @@ class HttpAPI:
|
||||
|
||||
class Remote(str, Enum):
|
||||
""" Args for HttpRequestType [REMOTE] class. """
|
||||
ONE = "2"
|
||||
TWO = "3"
|
||||
THREE = "4"
|
||||
FOUR = "5"
|
||||
FIVE = "6"
|
||||
SIX = "7"
|
||||
SEVEN = "8"
|
||||
EIGHT = "9"
|
||||
NINE = "10"
|
||||
ZERO = "11"
|
||||
UP = "103"
|
||||
LEFT = "105"
|
||||
RIGHT = "106"
|
||||
@@ -651,6 +668,7 @@ class HttpAPI:
|
||||
EXIT = "174"
|
||||
OK = "352"
|
||||
INFO = "358"
|
||||
EPG = "365"
|
||||
TV = "377"
|
||||
RADIO = "385"
|
||||
AUDIO = "392"
|
||||
@@ -661,6 +679,7 @@ class HttpAPI:
|
||||
BLUE = "401"
|
||||
CH_UP = "402"
|
||||
CH_DOWN = "403"
|
||||
NEXT = "407"
|
||||
BACK = "412"
|
||||
|
||||
class Power(str, Enum):
|
||||
|
||||
@@ -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}: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"
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -81,6 +81,7 @@ class Defaults(Enum):
|
||||
BACKUP_BEFORE_DOWNLOADING = True
|
||||
BACKUP_BEFORE_SAVE = True
|
||||
V5_SUPPORT = False
|
||||
UNLIMITED_COPY_BUFFER = False
|
||||
FORCE_BQ_NAMES = False
|
||||
HTTP_API_SUPPORT = True
|
||||
ENABLE_YT_DL = False
|
||||
@@ -95,7 +96,7 @@ class Defaults(Enum):
|
||||
STREAM_LIB = "mpv" if IS_WIN else "vlc"
|
||||
MAIN_LIST_PLAYBACK = False
|
||||
PROFILE_FOLDER_DEFAULT = False
|
||||
RECORDS_PATH = f"{DATA_PATH}records{SEP}"
|
||||
RECORDINGS_PATH = f"{DATA_PATH}recordings{SEP}"
|
||||
ACTIVATE_TRANSCODING = False
|
||||
ACTIVE_TRANSCODING_PRESET = f"720p TV{SEP}device"
|
||||
|
||||
@@ -281,6 +282,14 @@ class Settings:
|
||||
def host(self, value):
|
||||
self._cp_settings["host"] = value
|
||||
|
||||
@property
|
||||
def hosts(self):
|
||||
return self._cp_settings.get("hosts", [self.host, ])
|
||||
|
||||
@hosts.setter
|
||||
def hosts(self, value):
|
||||
self._cp_settings["hosts"] = value
|
||||
|
||||
@property
|
||||
def port(self):
|
||||
return self._cp_settings.get("port", self.get_default("port"))
|
||||
@@ -462,12 +471,12 @@ class Settings:
|
||||
self._cp_settings["profile_backup_path"] = value
|
||||
|
||||
@property
|
||||
def records_path(self):
|
||||
return self._settings.get("records_path", Defaults.RECORDS_PATH.value)
|
||||
def recordings_path(self):
|
||||
return self._settings.get("recordings_path", Defaults.RECORDINGS_PATH.value)
|
||||
|
||||
@records_path.setter
|
||||
def records_path(self, value):
|
||||
self._settings["records_path"] = value
|
||||
@recordings_path.setter
|
||||
def recordings_path(self, value):
|
||||
self._settings["recordings_path"] = value
|
||||
|
||||
# ******** Streaming ********* #
|
||||
|
||||
@@ -598,6 +607,14 @@ class Settings:
|
||||
def v5_support(self, value):
|
||||
self._settings["v5_support"] = value
|
||||
|
||||
@property
|
||||
def unlimited_copy_buffer(self):
|
||||
return self._settings.get("unlimited_copy_buffer", Defaults.UNLIMITED_COPY_BUFFER.value)
|
||||
|
||||
@unlimited_copy_buffer.setter
|
||||
def unlimited_copy_buffer(self, value):
|
||||
self._settings["unlimited_copy_buffer"] = value
|
||||
|
||||
@property
|
||||
def force_bq_names(self):
|
||||
return self._settings.get("force_bq_names", Defaults.FORCE_BQ_NAMES.value)
|
||||
@@ -825,6 +842,14 @@ class Settings:
|
||||
def remove_unused_bouquets(self, value):
|
||||
self._settings["remove_unused_bouquets"] = value
|
||||
|
||||
@property
|
||||
def keep_power_mode(self):
|
||||
return self._settings.get("keep_power_mode", False)
|
||||
|
||||
@keep_power_mode.setter
|
||||
def keep_power_mode(self, value):
|
||||
self._settings["keep_power_mode"] = value
|
||||
|
||||
@property
|
||||
def compress_picons(self):
|
||||
return self._settings.get("compress_picons", False)
|
||||
@@ -884,7 +909,7 @@ class Settings:
|
||||
"extra_color": Defaults.EXTRA_COLOR.value,
|
||||
"fav_click_mode": Defaults.FAV_CLICK_MODE.value,
|
||||
"profile_folder_is_default": Defaults.PROFILE_FOLDER_DEFAULT.value,
|
||||
"records_path": Defaults.RECORDS_PATH.value
|
||||
"records_path": Defaults.RECORDINGS_PATH.value
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
|
||||
@@ -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
|
||||
@@ -32,12 +32,12 @@ import os
|
||||
import shutil
|
||||
import struct
|
||||
import sys
|
||||
import xml.etree.ElementTree as ET
|
||||
from collections import namedtuple
|
||||
from datetime import datetime, timezone
|
||||
from tempfile import NamedTemporaryFile
|
||||
from urllib.parse import urlparse
|
||||
from xml.dom.minidom import parse, Node, Document
|
||||
import xml.etree.ElementTree as ET
|
||||
|
||||
import requests
|
||||
|
||||
@@ -54,8 +54,8 @@ except ModuleNotFoundError:
|
||||
else:
|
||||
DETECT_ENCODING = True
|
||||
|
||||
EpgEvent = namedtuple("EpgEvent", ["service_name", "title", "time", "desc", "event_data"])
|
||||
EpgEvent.__new__.__defaults__ = ("N/A", "N/A", "N/A", "N/A", None) # For Python3 < 3.7
|
||||
EpgEvent = namedtuple("EpgEvent", ["service_name", "title", "start", "end", "length", "desc", "event_data"])
|
||||
EpgEvent.__new__.__defaults__ = ("N/A", "N/A", 0, 0, 0, "N/A", None) # For Python3 < 3.7
|
||||
|
||||
|
||||
class Reader(metaclass=abc.ABCMeta):
|
||||
@@ -298,14 +298,15 @@ class XmlTvReader(Reader):
|
||||
offset = datetime.now() - dt
|
||||
|
||||
for srv in filter(lambda s: any(name in names for name in s.names), self._ids.values()):
|
||||
ev = list(filter(lambda s: s.start < utc, srv.events))
|
||||
ev = max(filter(lambda s: s.start < utc, srv.events), key=lambda x: x.start, default=None)
|
||||
if ev:
|
||||
ev = ev[-1]
|
||||
start = datetime.fromtimestamp(ev.start) + offset
|
||||
end_time = datetime.fromtimestamp(ev.duration) + offset
|
||||
tm = f"{start.strftime('%H:%M')} - {end_time.strftime('%H:%M')}"
|
||||
start = start.timestamp()
|
||||
end_time = end_time.timestamp()
|
||||
|
||||
for n in srv.names:
|
||||
events[n] = EpgEvent(n, ev.title, tm, ev.desc, ev)
|
||||
events[n] = EpgEvent(n, ev.title, start, end_time, int(ev.duration), ev.desc, ev)
|
||||
|
||||
return events
|
||||
|
||||
|
||||
@@ -526,7 +526,7 @@ class Recorder:
|
||||
if self._recorder:
|
||||
self._recorder.stop()
|
||||
|
||||
path = self._settings.records_path
|
||||
path = self._settings.recordings_path
|
||||
os.makedirs(os.path.dirname(path), exist_ok=True)
|
||||
d_now = datetime.now().strftime(LOG_DATE_FORMAT)
|
||||
d_now = d_now.replace(" ", "_").replace(":", "-") if IS_WIN else d_now.replace(" ", "_")
|
||||
|
||||
@@ -16,17 +16,17 @@
|
||||
# <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
from ctypes import *
|
||||
import ctypes.util
|
||||
import threading
|
||||
import os
|
||||
import sys
|
||||
from warnings import warn
|
||||
from functools import partial, wraps
|
||||
from contextlib import contextmanager
|
||||
import collections
|
||||
import ctypes.util
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
import threading
|
||||
import traceback
|
||||
from contextlib import contextmanager
|
||||
from ctypes import *
|
||||
from functools import partial, wraps
|
||||
from warnings import warn
|
||||
|
||||
if os.name == 'nt':
|
||||
dll = ctypes.util.find_library('mpv-1.dll')
|
||||
@@ -569,10 +569,13 @@ _mpv_free_node_contents = backend.mpv_free_node_contents
|
||||
backend.mpv_create.restype = MpvHandle
|
||||
_mpv_create = backend.mpv_create
|
||||
|
||||
_API_VER = _mpv_client_api_version()[0]
|
||||
|
||||
_handle_func('mpv_destroy' if _API_VER > 1 else 'mpv_detach_destroy', [], None, errcheck=None)
|
||||
_handle_func('mpv_create_client', [c_char_p], MpvHandle, notnull_errcheck)
|
||||
_handle_func('mpv_client_name', [], c_char_p, errcheck=None)
|
||||
_handle_func('mpv_initialize', [], c_int, ec_errcheck)
|
||||
_handle_func('mpv_detach_destroy', [], None, errcheck=None)
|
||||
|
||||
_handle_func('mpv_terminate_destroy', [], None, errcheck=None)
|
||||
_handle_func('mpv_load_config_file', [c_char_p], c_int, ec_errcheck)
|
||||
_handle_func('mpv_get_time_us', [], c_ulonglong, errcheck=None)
|
||||
@@ -608,28 +611,6 @@ _handle_func('mpv_get_wakeup_pipe', [], c_int, errcheck=None)
|
||||
|
||||
_handle_func('mpv_stream_cb_add_ro', [c_char_p, c_void_p, StreamOpenFn], c_int, ec_errcheck)
|
||||
|
||||
# Disabled for compatibility with the old version of mpv!!!
|
||||
# _handle_func('mpv_render_context_create', [MpvRenderCtxHandle, MpvHandle, POINTER(MpvRenderParam)], c_int, ec_errcheck, ctx=None)
|
||||
# _handle_func('mpv_render_context_set_parameter', [MpvRenderParam], c_int, ec_errcheck, ctx=MpvRenderCtxHandle)
|
||||
# _handle_func('mpv_render_context_get_info', [MpvRenderParam], c_int, ec_errcheck, ctx=MpvRenderCtxHandle)
|
||||
# _handle_func('mpv_render_context_set_update_callback', [RenderUpdateFn, c_void_p], None, errcheck=None, ctx=MpvRenderCtxHandle)
|
||||
# _handle_func('mpv_render_context_update', [], c_int64, errcheck=None, ctx=MpvRenderCtxHandle)
|
||||
# _handle_func('mpv_render_context_render', [POINTER(MpvRenderParam)], c_int, ec_errcheck, ctx=MpvRenderCtxHandle)
|
||||
# _handle_func('mpv_render_context_report_swap', [], None, errcheck=None, ctx=MpvRenderCtxHandle)
|
||||
# _handle_func('mpv_render_context_free', [], None, errcheck=None, ctx=MpvRenderCtxHandle)
|
||||
|
||||
|
||||
# Deprecated in v0.29.0 and may disappear eventually
|
||||
if hasattr(backend, 'mpv_get_sub_api'):
|
||||
_handle_func('mpv_get_sub_api', [MpvSubApi], c_void_p, notnull_errcheck, deprecated=True)
|
||||
|
||||
_handle_gl_func('mpv_opengl_cb_set_update_callback', [OpenGlCbUpdateFn, c_void_p], deprecated=True)
|
||||
_handle_gl_func('mpv_opengl_cb_init_gl', [c_char_p, OpenGlCbGetProcAddrFn, c_void_p], c_int, deprecated=True)
|
||||
_handle_gl_func('mpv_opengl_cb_draw', [c_int, c_int, c_int], c_int, deprecated=True)
|
||||
_handle_gl_func('mpv_opengl_cb_render', [c_int, c_int], c_int, deprecated=True)
|
||||
_handle_gl_func('mpv_opengl_cb_report_flip', [c_ulonglong], c_int, deprecated=True)
|
||||
_handle_gl_func('mpv_opengl_cb_uninit_gl', [], c_int, deprecated=True)
|
||||
|
||||
|
||||
def _mpv_coax_proptype(value, proptype=str):
|
||||
"""Intelligently coax the given python value into something that can be understood as a proptype property."""
|
||||
@@ -934,7 +915,7 @@ class MPV(object):
|
||||
self._message_handlers[target](*args)
|
||||
|
||||
if eid == MpvEventID.SHUTDOWN:
|
||||
_mpv_detach_destroy(self._event_handle)
|
||||
_mpv_destroy(self._event_handle) if _API_VER > 1 else _mpv_detach_destroy(self._event_handle)
|
||||
return
|
||||
|
||||
except Exception as e:
|
||||
|
||||
@@ -59,7 +59,7 @@ class PiconsCzDownloader:
|
||||
_BASE_LOGO_URL = "https://picon.cz/picon/0/"
|
||||
_HEADER = {"User-Agent": "DemonEditor/3.0.0", "Referer": ""}
|
||||
_LINK_PATTERN = re.compile(r"((.*)-\d+x\d+)-(.*)_by_chocholousek.7z$")
|
||||
_FILE_PATTERN = re.compile(b"\\s+(1_.*\\.png).*")
|
||||
_FILE_PATTERN = re.compile(b"\\s+(\\w+\\.png).*")
|
||||
|
||||
def __init__(self, picon_ids=set(), appender=log):
|
||||
self._perm_links = {}
|
||||
@@ -220,7 +220,8 @@ class PiconsCzDownloader:
|
||||
"piconoled": "o96",
|
||||
"piconblack80": "b50",
|
||||
"piconblack3d": "b50",
|
||||
"piconwin11": "win11220"
|
||||
"piconwin11": "win11220",
|
||||
"piconSNPtransparent": "t50"
|
||||
}
|
||||
|
||||
def get_name_map(self):
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
from app.settings import SettingsType, SEP, IS_DARWIN
|
||||
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
|
||||
from .uicommons import Gtk, Gdk, UI_RESOURCES_PATH, KeyboardKey, MOD_MASK, IS_GNOME_SESSION, HeaderBar
|
||||
|
||||
|
||||
class RestoreType(Enum):
|
||||
@@ -77,8 +77,8 @@ class BackupDialog:
|
||||
self._message_label = builder.get_object("message_label")
|
||||
self._file_count_label = builder.get_object("file_count_label")
|
||||
|
||||
if IS_GNOME_SESSION:
|
||||
header_bar = Gtk.HeaderBar(visible=True, show_close_button=True)
|
||||
if IS_GNOME_SESSION or IS_DARWIN:
|
||||
header_bar = HeaderBar()
|
||||
self._dialog_window.set_titlebar(header_bar)
|
||||
|
||||
button_box = builder.get_object("main_button_box")
|
||||
@@ -245,8 +245,8 @@ def backup_data(path, backup_path, move=True):
|
||||
backup_path = f"{backup_path}{datetime.now().strftime('%Y-%m-%d_%H-%M-%S')}{SEP}"
|
||||
os.makedirs(os.path.dirname(backup_path), exist_ok=True)
|
||||
os.makedirs(os.path.dirname(path), exist_ok=True)
|
||||
# Backup files in data dir(skipping dirs and satellites.xml).
|
||||
for file in filter(lambda f: f != "satellites.xml" and os.path.isfile(os.path.join(path, f)), os.listdir(path)):
|
||||
# Backup files in data dir(skipping dirs and *.xml).
|
||||
for file in filter(lambda f: not f.endswith(".xml") and os.path.isfile(os.path.join(path, f)), os.listdir(path)):
|
||||
src, dst = os.path.join(path, file), backup_path + file
|
||||
shutil.move(src, dst) if move else shutil.copy(src, dst)
|
||||
# Compressing to zip and delete remaining files.
|
||||
@@ -263,8 +263,8 @@ def restore_data(src, dst):
|
||||
|
||||
|
||||
def clear_data_path(path):
|
||||
""" Clearing data at the specified path excluding satellites.xml file """
|
||||
for file in filter(lambda f: f != "satellites.xml" and os.path.isfile(os.path.join(path, f)), os.listdir(path)):
|
||||
""" Clearing data at the specified path excluding *.xml file. """
|
||||
for file in filter(lambda f: not f.endswith(".xml") and os.path.isfile(os.path.join(path, f)), os.listdir(path)):
|
||||
os.remove(os.path.join(path, file))
|
||||
|
||||
|
||||
|
||||
1107
app/ui/control.glade
1107
app/ui/control.glade
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
|
||||
@@ -70,6 +70,7 @@ class ControlTool(Gtk.Box):
|
||||
self._agc_level_bar = builder.get_object("agc_level_bar")
|
||||
self._volume_button = builder.get_object("volume_button")
|
||||
self._header_box = builder.get_object("control_header_box")
|
||||
self._screenshot_button_box = builder.get_object("screenshot_button_box")
|
||||
# Network.
|
||||
self._network_button = builder.get_object("control_network_button")
|
||||
self._network_model = builder.get_object("network_model")
|
||||
@@ -83,15 +84,27 @@ class ControlTool(Gtk.Box):
|
||||
|
||||
def init_actions(self, app):
|
||||
# Remote controller actions.
|
||||
app.set_action("on_one", lambda a, v: self.on_remote_action(HttpAPI.Remote.ONE))
|
||||
app.set_action("on_two", lambda a, v: self.on_remote_action(HttpAPI.Remote.TWO))
|
||||
app.set_action("on_three", lambda a, v: self.on_remote_action(HttpAPI.Remote.THREE))
|
||||
app.set_action("on_four", lambda a, v: self.on_remote_action(HttpAPI.Remote.FOUR))
|
||||
app.set_action("on_five", lambda a, v: self.on_remote_action(HttpAPI.Remote.FIVE))
|
||||
app.set_action("on_six", lambda a, v: self.on_remote_action(HttpAPI.Remote.SIX))
|
||||
app.set_action("on_seven", lambda a, v: self.on_remote_action(HttpAPI.Remote.SEVEN))
|
||||
app.set_action("on_eight", lambda a, v: self.on_remote_action(HttpAPI.Remote.EIGHT))
|
||||
app.set_action("on_nine", lambda a, v: self.on_remote_action(HttpAPI.Remote.NINE))
|
||||
app.set_action("on_zero", lambda a, v: self.on_remote_action(HttpAPI.Remote.ZERO))
|
||||
app.set_action("on_up", lambda a, v: self.on_remote_action(HttpAPI.Remote.UP))
|
||||
app.set_action("on_down", lambda a, v: self.on_remote_action(HttpAPI.Remote.DOWN))
|
||||
app.set_action("on_left", lambda a, v: self.on_remote_action(HttpAPI.Remote.LEFT))
|
||||
app.set_action("on_right", lambda a, v: self.on_remote_action(HttpAPI.Remote.RIGHT))
|
||||
app.set_action("on_next", lambda a, v: self.on_remote_action(HttpAPI.Remote.NEXT))
|
||||
app.set_action("on_back", lambda a, v: self.on_remote_action(HttpAPI.Remote.BACK))
|
||||
app.set_action("on_info", lambda a, v: self.on_remote_action(HttpAPI.Remote.INFO))
|
||||
app.set_action("on_ok", lambda a, v: self.on_remote_action(HttpAPI.Remote.OK))
|
||||
app.set_action("on_menu", lambda a, v: self.on_remote_action(HttpAPI.Remote.MENU))
|
||||
app.set_action("on_exit", lambda a, v: self.on_remote_action(HttpAPI.Remote.EXIT))
|
||||
app.set_action("on_epg", lambda a, v: self.on_remote_action(HttpAPI.Remote.EPG))
|
||||
app.set_action("on_ch_up", lambda a, v: self.on_remote_action(HttpAPI.Remote.CH_UP))
|
||||
app.set_action("on_ch_down", lambda a, v: self.on_remote_action(HttpAPI.Remote.CH_DOWN))
|
||||
app.set_action("on_red", lambda a, v: self.on_remote_action(HttpAPI.Remote.RED))
|
||||
@@ -124,6 +137,8 @@ class ControlTool(Gtk.Box):
|
||||
self._remote_box.reorder_child(children[-1], 0)
|
||||
pack_type = Gtk.PackType.END if alt_layout else Gtk.PackType.START
|
||||
self._header_box.set_child_packing(self._network_button, False, False, 0, pack_type)
|
||||
pack_type = Gtk.PackType.START if alt_layout else Gtk.PackType.END
|
||||
self._header_box.set_child_packing(self._screenshot_button_box, False, False, 0, pack_type)
|
||||
|
||||
# ***************** Remote controller ********************* #
|
||||
|
||||
@@ -243,10 +258,11 @@ class ControlTool(Gtk.Box):
|
||||
|
||||
def update_signal(self, sig):
|
||||
snr = sig.get("e2snr", "0 %").strip() if sig else "0 %"
|
||||
snr_db = sig.get("e2snrdb", "0 dB").strip() if sig else "0 dB"
|
||||
acg = sig.get("e2acg", "0 %").strip() if sig else "0 %"
|
||||
ber = (sig.get("e2ber", None) or "").strip() if sig else ""
|
||||
# Labels.
|
||||
self._snr_value_label.set_text(snr)
|
||||
self._snr_value_label.set_text(f"{snr_db} ({snr})")
|
||||
self._agc_value_label.set_text(acg)
|
||||
self._ber_value_label.set_text(ber)
|
||||
# Level bars.
|
||||
|
||||
@@ -3,7 +3,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
|
||||
@@ -31,7 +31,7 @@ Author: Dmitriy Yefremov
|
||||
<!-- interface-license-type mit -->
|
||||
<!-- interface-name DemonEditor -->
|
||||
<!-- interface-description Enigma2 channel and satellites list editor. -->
|
||||
<!-- interface-copyright 2018-2022 Dmitriy Yefremov -->
|
||||
<!-- interface-copyright 2018-2023 Dmitriy Yefremov -->
|
||||
<!-- interface-authors Dmitriy Yefremov -->
|
||||
<object class="GtkAboutDialog" id="about_dialog">
|
||||
<property name="can_focus">False</property>
|
||||
@@ -40,8 +40,8 @@ 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.0.0 Beta</property>
|
||||
<property name="copyright">2018-2022 Dmitriy Yefremov
|
||||
<property name="version">3.3.0 Beta</property>
|
||||
<property name="copyright">2018-2023 Dmitriy Yefremov
|
||||
</property>
|
||||
<property name="comments" translatable="yes">Enigma2 channel and satellite list editor.</property>
|
||||
<property name="website">https://dyefremov.github.io/DemonEditor/</property>
|
||||
|
||||
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
|
||||
@@ -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
|
||||
from app.settings import SEP, EpgSource, IS_DARWIN
|
||||
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
|
||||
from ..uicommons import Gtk, Gdk, UI_RESOURCES_PATH, Column, EPG_ICON, KeyboardKey, IS_GNOME_SESSION, Page
|
||||
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
|
||||
|
||||
|
||||
class RefsSource(Enum):
|
||||
@@ -73,13 +73,14 @@ class EpgCache(dict):
|
||||
|
||||
self.init()
|
||||
|
||||
@run_idle
|
||||
@run_with_delay(5)
|
||||
def init(self):
|
||||
if self._src is EpgSource.XML:
|
||||
url = self._settings.epg_xml_source
|
||||
gz_file = f"{self._settings.profile_data_path}epg{os.sep}epg.gz"
|
||||
self._reader = XmlTvReader(gz_file, url)
|
||||
|
||||
@run_with_delay(2)
|
||||
def process_data():
|
||||
t = BGTaskWidget(self._app, "Processing XMLTV data...", self._reader.parse, )
|
||||
self._app.emit("add-background-task", t)
|
||||
@@ -202,7 +203,9 @@ class EpgTool(Gtk.Box):
|
||||
self._current_bq = None
|
||||
self._app = app
|
||||
self._app.connect("fav-changed", self.on_service_changed)
|
||||
self._app.connect("profile-changed", self.on_profile_changed)
|
||||
self._app.connect("bouquet-changed", self.on_bouquet_changed)
|
||||
self._app.connect("filter-toggled", self.on_filter_toggled)
|
||||
|
||||
handlers = {"on_epg_press": self.on_epg_press,
|
||||
"on_timer_add": self.on_timer_add,
|
||||
@@ -217,12 +220,24 @@ class EpgTool(Gtk.Box):
|
||||
self._model = builder.get_object("epg_model")
|
||||
self._filter_model = builder.get_object("epg_filter_model")
|
||||
self._filter_model.set_visible_func(self.epg_filter_function)
|
||||
self._filter_button = builder.get_object("epg_filter_button")
|
||||
self._filter_entry = builder.get_object("epg_filter_entry")
|
||||
self._multi_epg_button = builder.get_object("multi_epg_button")
|
||||
self._event_count_label = builder.get_object("event_count_label")
|
||||
self.pack_start(builder.get_object("epg_frame"), True, True, 0)
|
||||
# Custom sort function.
|
||||
self._view.get_model().set_sort_func(2, self.time_sort_func, 2)
|
||||
# Custom data functions.
|
||||
renderer = builder.get_object("epg_start_renderer")
|
||||
column = builder.get_object("epg_start_column")
|
||||
column.set_cell_data_func(renderer, self.start_data_func)
|
||||
renderer = builder.get_object("epg_end_renderer")
|
||||
column = builder.get_object("epg_end_column")
|
||||
column.set_cell_data_func(renderer, self.end_data_func)
|
||||
renderer = builder.get_object("epg_length_renderer")
|
||||
column = builder.get_object("epg_length_column")
|
||||
column.set_cell_data_func(renderer, self.duration_data_func)
|
||||
# Time formats.
|
||||
self._time_fmt = "%a %x - %H:%M"
|
||||
self._duration_fmt = f"%-Hh %Mm"
|
||||
|
||||
self.show()
|
||||
|
||||
@@ -279,29 +294,40 @@ class EpgTool(Gtk.Box):
|
||||
self._app.wait_dialog.show()
|
||||
self._app.send_http_request(HttpAPI.Request.EPG, quote(ref), self.update_epg_data)
|
||||
|
||||
def on_profile_changed(self, app, prf):
|
||||
self.update_epg_data()
|
||||
|
||||
@run_idle
|
||||
def update_epg_data(self, epg):
|
||||
def update_epg_data(self, epg=None):
|
||||
self._event_count_label.set_text("0")
|
||||
self._model.clear()
|
||||
list(map(self._model.append, (self.get_event(e) for e in epg.get("event_list", [])
|
||||
if e.get("e2eventid", "").isdigit())))
|
||||
if epg:
|
||||
list(map(self._model.append, (self.get_event(e) for e in epg.get("event_list", [])
|
||||
if e.get("e2eventid", "").isdigit())))
|
||||
self._event_count_label.set_text(str(len(self._model)))
|
||||
self._app.wait_dialog.hide()
|
||||
|
||||
@staticmethod
|
||||
def get_event(event, show_day=True):
|
||||
t_str = f"{'%a, ' if show_day else ''}%x, %H:%M"
|
||||
s_name = event.get("e2eventservicename", "")
|
||||
title = event.get("e2eventtitle", "") or ""
|
||||
desc = event.get("e2eventdescription", "") or ""
|
||||
desc = desc.strip()
|
||||
start, duration = int(event.get("e2eventstart", "0")), int(event.get("e2eventduration", "0"))
|
||||
|
||||
start = int(event.get("e2eventstart", "0"))
|
||||
start_time = datetime.fromtimestamp(start)
|
||||
end_time = datetime.fromtimestamp(start + int(event.get("e2eventduration", "0")))
|
||||
ev_time = f"{start_time.strftime(t_str)} - {end_time.strftime('%H:%M')}"
|
||||
return EpgEvent(s_name, title, start, start + duration, duration, desc, event)
|
||||
|
||||
return EpgEvent(s_name, title, ev_time, desc, event)
|
||||
def start_data_func(self, column, renderer, model, itr, data):
|
||||
value = datetime.fromtimestamp(model.get_value(itr, Column.EPG_START))
|
||||
renderer.set_property("text", value.strftime(self._time_fmt))
|
||||
|
||||
def end_data_func(self, column, renderer, model, itr, data):
|
||||
value = datetime.fromtimestamp(model.get_value(itr, Column.EPG_END))
|
||||
renderer.set_property("text", value.strftime(self._time_fmt))
|
||||
|
||||
def duration_data_func(self, column, renderer, model, itr, data):
|
||||
value = datetime.utcfromtimestamp(model.get_value(itr, Column.EPG_LENGTH))
|
||||
renderer.set_property("text", value.strftime(self._duration_fmt))
|
||||
|
||||
def on_epg_filter_changed(self, entry):
|
||||
self._filter_model.refilter()
|
||||
@@ -312,14 +338,17 @@ class EpgTool(Gtk.Box):
|
||||
|
||||
def epg_filter_function(self, model, itr, data):
|
||||
txt = self._filter_entry.get_text().upper()
|
||||
return next((s for s in model.get(itr, 0, 1, 2, 3) if txt in s.upper()), False)
|
||||
return next((s for s in model.get(itr,
|
||||
Column.EPG_SERVICE,
|
||||
Column.EPG_TITLE,
|
||||
Column.EPG_DESC) if txt in s.upper()), False)
|
||||
|
||||
def time_sort_func(self, model, iter1, iter2, column):
|
||||
""" Custom sort function for time column. """
|
||||
event1 = model.get_value(iter1, 4)
|
||||
event2 = model.get_value(iter2, 4)
|
||||
|
||||
return int(event1.get("e2eventstart", "0")) - int(event2.get("e2eventstart", "0"))
|
||||
def on_filter_toggled(self, app, value):
|
||||
if self._app.page is Page.EPG:
|
||||
active = not self._filter_button.get_active()
|
||||
self._filter_button.set_active(active)
|
||||
if active:
|
||||
self._filter_entry.grab_focus()
|
||||
|
||||
def on_view_query_tooltip(self, view, x, y, keyboard_mode, tooltip):
|
||||
dst = view.get_dest_row_at_pos(x, y)
|
||||
@@ -365,7 +394,7 @@ class EpgTool(Gtk.Box):
|
||||
|
||||
class EpgDialog:
|
||||
|
||||
def __init__(self, app, bouquet, bouquet_name):
|
||||
def __init__(self, app, bouquet_name):
|
||||
|
||||
handlers = {"on_close_dialog": self.on_close_dialog,
|
||||
"on_apply": self.on_apply,
|
||||
@@ -373,6 +402,7 @@ class EpgDialog:
|
||||
"on_save_to_xml": self.on_save_to_xml,
|
||||
"on_auto_configuration": self.on_auto_configuration,
|
||||
"on_filter_toggled": self.on_filter_toggled,
|
||||
"on_filter_satellite_toggled": self.on_filter_satellite_toggled,
|
||||
"on_filter_changed": self.on_filter_changed,
|
||||
"on_info_bar_close": self.on_info_bar_close,
|
||||
"on_popup_menu": on_popup_menu,
|
||||
@@ -395,11 +425,9 @@ class EpgDialog:
|
||||
"on_bq_cursor_changed": self.on_bq_cursor_changed}
|
||||
|
||||
self._app = app
|
||||
self._services = {}
|
||||
self._ex_services = self._app.current_services
|
||||
self._ex_fav_model = self._app.fav_view.get_model()
|
||||
self._settings = self._app.app_settings
|
||||
self._bouquet = bouquet
|
||||
self._bouquet_name = bouquet_name
|
||||
self._current_ref = []
|
||||
self._enable_dat_filter = False
|
||||
@@ -407,6 +435,7 @@ class EpgDialog:
|
||||
self._update_epg_data_on_start = False
|
||||
self._refs_source = RefsSource.SERVICES
|
||||
self._download_xml_is_active = False
|
||||
self._sat_positions = None
|
||||
|
||||
builder = get_builder(f"{UI_RESOURCES_PATH}epg{SEP}dialog.glade", handlers)
|
||||
|
||||
@@ -421,12 +450,14 @@ class EpgDialog:
|
||||
self._assign_ref_popup_item = builder.get_object("bouquet_assign_ref_popup_item")
|
||||
self._left_action_box = builder.get_object("left_action_box")
|
||||
self._xml_download_progress_bar = builder.get_object("xml_download_progress_bar")
|
||||
self._src_load_spinner = builder.get_object("src_load_spinner")
|
||||
# Filter
|
||||
self._filter_bar = builder.get_object("filter_bar")
|
||||
self._filter_entry = builder.get_object("filter_entry")
|
||||
self._filter_auto_switch = builder.get_object("filter_auto_switch")
|
||||
self._services_filter_model = builder.get_object("services_filter_model")
|
||||
self._services_filter_model.set_visible_func(self.services_filter_function)
|
||||
self._sat_pos_filter_model = builder.get_object("sat_pos_filter_model")
|
||||
# Info
|
||||
self._source_count_label = builder.get_object("source_count_label")
|
||||
self._source_info_label = builder.get_object("source_info_label")
|
||||
@@ -445,9 +476,8 @@ 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:
|
||||
header_bar = Gtk.HeaderBar(visible=True, show_close_button=True, title="EPG",
|
||||
subtitle=get_message("List configuration"))
|
||||
if IS_GNOME_SESSION or IS_DARWIN:
|
||||
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)
|
||||
right_box = builder.get_object("right_action_box")
|
||||
@@ -473,19 +503,29 @@ class EpgDialog:
|
||||
|
||||
@run_idle
|
||||
def on_apply(self, item):
|
||||
if show_dialog(DialogType.QUESTION, self._dialog) == Gtk.ResponseType.CANCEL:
|
||||
if show_dialog(DialogType.QUESTION, self._dialog) != Gtk.ResponseType.OK:
|
||||
return
|
||||
|
||||
self._bouquet.clear()
|
||||
list(map(self._bouquet.append, [r[Column.FAV_ID] for r in self._bouquet_model]))
|
||||
for index, row in enumerate(self._ex_fav_model):
|
||||
fav_id = self._bouquet[index]
|
||||
row[Column.FAV_ID] = fav_id
|
||||
if row[Column.FAV_TYPE] == BqServiceType.IPTV.name:
|
||||
old_fav_id = self._services[fav_id]
|
||||
srv = self._ex_services.pop(old_fav_id, None)
|
||||
p = re.compile(r"\d+")
|
||||
updated = {}
|
||||
|
||||
for i, row in enumerate(self._bouquet_model):
|
||||
if row[Column.FAV_LOCKED]:
|
||||
fav_id = self._ex_fav_model[row.path][Column.FAV_ID]
|
||||
srv = self._ex_services.pop(fav_id, None)
|
||||
if srv:
|
||||
self._ex_services[fav_id] = srv._replace(fav_id=fav_id)
|
||||
new_fav_id, picon_id = row[Column.FAV_ID], row[Column.FAV_POS]
|
||||
if picon_id:
|
||||
picon_id = re.sub(p, re.search(p, srv.picon_id).group(), picon_id, count=1)
|
||||
else:
|
||||
picon_id = srv.picon_id
|
||||
new = srv._replace(fav_id=new_fav_id, data_id=new_fav_id.strip(), picon_id=picon_id)
|
||||
self._ex_services[new_fav_id] = new
|
||||
updated[fav_id] = (srv, new)
|
||||
|
||||
if updated:
|
||||
self._app.emit("iptv-service-edited", updated)
|
||||
|
||||
self._dialog.destroy()
|
||||
|
||||
@run_idle
|
||||
@@ -501,7 +541,6 @@ class EpgDialog:
|
||||
def clear_data(self):
|
||||
self._services_model.clear()
|
||||
self._bouquet_model.clear()
|
||||
self._services.clear()
|
||||
self._source_info_label.set_text("")
|
||||
self._bouquet_epg_count_label.set_text("")
|
||||
self.on_info_bar_close()
|
||||
@@ -521,6 +560,8 @@ class EpgDialog:
|
||||
return
|
||||
yield True
|
||||
|
||||
self._src_load_spinner.start()
|
||||
|
||||
if self._refs_source is RefsSource.SERVICES:
|
||||
yield from self.init_lamedb_source(refs)
|
||||
elif self._refs_source is RefsSource.XML:
|
||||
@@ -531,13 +572,13 @@ class EpgDialog:
|
||||
self.show_info_message(str(e), Gtk.MessageType.ERROR)
|
||||
else:
|
||||
self.show_info_message("Unknown names source!", Gtk.MessageType.ERROR)
|
||||
|
||||
self._src_load_spinner.stop()
|
||||
yield True
|
||||
|
||||
def init_bouquet_data(self):
|
||||
for r in self._ex_fav_model:
|
||||
row = [*r[:]]
|
||||
fav_id = r[Column.FAV_ID]
|
||||
self._services[fav_id] = self._ex_services[fav_id].fav_id
|
||||
yield self._bouquet_model.append(row)
|
||||
self._bouquet_count_label.set_text(str(len(self._bouquet_model)))
|
||||
yield True
|
||||
@@ -550,7 +591,7 @@ class EpgDialog:
|
||||
|
||||
factor = self._app.DEL_FACTOR / 4
|
||||
for index, srv in enumerate(filtered):
|
||||
self._services_model.append((srv.service, srv.pos, srv.fav_id))
|
||||
self._services_model.append((srv.service, srv.pos, srv.fav_id, srv.picon_id))
|
||||
if index % factor == 0:
|
||||
yield True
|
||||
|
||||
@@ -627,7 +668,7 @@ class EpgDialog:
|
||||
|
||||
factor = self._app.DEL_FACTOR / 4
|
||||
for index, srv in enumerate(s_refs):
|
||||
self._services_model.append((srv.name, " ", srv.data))
|
||||
self._services_model.append((srv.name, " ", srv.data, ""))
|
||||
if index % factor == 0:
|
||||
yield True
|
||||
|
||||
@@ -687,7 +728,7 @@ class EpgDialog:
|
||||
tr = {ord(k): ord(v) for k, v in zip(*symbols)}
|
||||
|
||||
source = {}
|
||||
for row in self._services_model:
|
||||
for row in self._source_view.get_model():
|
||||
name = re.sub("\\W+", "", str(row[0])).upper()
|
||||
name = name.translate(tr) if use_cyrillic else name
|
||||
source[name] = row
|
||||
@@ -732,29 +773,46 @@ class EpgDialog:
|
||||
|
||||
fav_id = row[Column.FAV_ID]
|
||||
fav_id_data = fav_id.split(":")
|
||||
fav_id_data[3:7] = data[-1].split(":")
|
||||
fav_id_data[3:7] = data[-2].split(":")
|
||||
|
||||
if data[-1]:
|
||||
row[Column.FAV_POS] = data[-1]
|
||||
p_data = data[-1].split("_")
|
||||
if p_data:
|
||||
fav_id_data[2] = p_data[2]
|
||||
|
||||
new_fav_id = ":".join(fav_id_data)
|
||||
service = self._services.pop(fav_id, None)
|
||||
if service:
|
||||
self._services[new_fav_id] = service
|
||||
row[Column.FAV_ID] = new_fav_id
|
||||
row[Column.FAV_LOCKED] = EPG_ICON
|
||||
pos = f"({data[1] if self._refs_source is RefsSource.SERVICES else 'XML'})"
|
||||
src = f"{get_message('EPG source')}: {(GLib.markup_escape_text(data[0] or ''))} {pos}"
|
||||
row[Column.FAV_TOOLTIP] = f"{get_message('Service reference')}: {':'.join(fav_id_data[:10])}\n{src}"
|
||||
row[Column.FAV_ID] = new_fav_id
|
||||
row[Column.FAV_LOCKED] = EPG_ICON
|
||||
|
||||
pos = f"({data[1] if self._refs_source is RefsSource.SERVICES else 'XML'})"
|
||||
src = f"{get_message('EPG source')}: {(GLib.markup_escape_text(data[0] or ''))} {pos}"
|
||||
row[Column.FAV_TOOLTIP] = f"{get_message('Service reference')}: {':'.join(fav_id_data[:10])}\n{src}"
|
||||
|
||||
def on_filter_toggled(self, button):
|
||||
self._filter_bar.set_visible(button.get_active())
|
||||
if not button.get_active():
|
||||
self._filter_entry.set_text("")
|
||||
if button.get_active():
|
||||
self._sat_positions = {r[1] for r in self._services_model}
|
||||
update_filter_sat_positions(self._sat_pos_filter_model, self._sat_positions)
|
||||
else:
|
||||
self._sat_positions = None
|
||||
self._filter_entry.set_text("") if self._filter_entry.get_text() else self.on_filter_changed()
|
||||
|
||||
@run_with_delay(1)
|
||||
def on_filter_changed(self, entry):
|
||||
def on_filter_satellite_toggled(self, toggle, path):
|
||||
update_toggle_model(self._sat_pos_filter_model, path, toggle)
|
||||
self._sat_positions.clear()
|
||||
self._sat_positions.update({r[0] for r in self._sat_pos_filter_model if r[1]})
|
||||
self.on_filter_changed()
|
||||
|
||||
@run_with_delay(2)
|
||||
def on_filter_changed(self, entry=None):
|
||||
self._services_filter_model.refilter()
|
||||
|
||||
def services_filter_function(self, model, itr, data):
|
||||
txt = self._filter_entry.get_text().upper()
|
||||
return model is None or model == "None" or txt in model.get_value(itr, 0).upper()
|
||||
pos = model.get_value(itr, 1)
|
||||
pos = self._sat_positions is None or self._xml_radiobutton.get_active() or pos in self._sat_positions
|
||||
return model is None or model == "None" or (txt in model.get_value(itr, 0).upper() and pos)
|
||||
|
||||
def on_info_bar_close(self, bar=None, resp=None):
|
||||
self._info_bar.set_visible(False)
|
||||
@@ -784,10 +842,7 @@ class EpgDialog:
|
||||
self.update_epg_count()
|
||||
|
||||
def reset_row_data(self, row):
|
||||
default_fav_id = self._services.pop(row[Column.FAV_ID], None)
|
||||
if default_fav_id:
|
||||
self._services[default_fav_id] = default_fav_id
|
||||
row[Column.FAV_ID], row[Column.FAV_LOCKED], row[Column.FAV_TOOLTIP] = default_fav_id, None, None
|
||||
row[Column.FAV_LOCKED], row[Column.FAV_TOOLTIP], row[Column.FAV_POS] = None, None, None
|
||||
|
||||
@run_idle
|
||||
def show_info_message(self, text, message_type):
|
||||
@@ -848,7 +903,7 @@ class EpgDialog:
|
||||
model, paths = view.get_selection().get_selected_rows()
|
||||
if paths:
|
||||
s_data = model[paths][:]
|
||||
if all(s_data):
|
||||
if all(s_data[:-1]):
|
||||
data.set_text("::::".join(s_data), -1)
|
||||
else:
|
||||
self.show_info_message(get_message("Source error!"), Gtk.MessageType.ERROR)
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- Generated with glade 3.22.2
|
||||
<!-- Generated with glade 3.38.2
|
||||
|
||||
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
|
||||
@@ -26,12 +26,13 @@ THE SOFTWARE.
|
||||
Author: Dmitriy Yefremov
|
||||
|
||||
-->
|
||||
<interface>
|
||||
<requires lib="gtk+" version="3.16"/>
|
||||
<interface domain="demon-editor">
|
||||
<requires lib="gtk+" version="3.22"/>
|
||||
<!-- interface-css-provider-path style.css -->
|
||||
<!-- interface-license-type mit -->
|
||||
<!-- interface-name DemonEditor -->
|
||||
<!-- interface-description Enigma2 channel and satellites list editor for GNU/Linux. -->
|
||||
<!-- interface-copyright 2018-2022 Dmitriy Yefremov -->
|
||||
<!-- interface-copyright 2018-2023 Dmitriy Yefremov -->
|
||||
<!-- interface-authors Dmitriy Yefremov -->
|
||||
<object class="GtkListStore" id="epg_model">
|
||||
<columns>
|
||||
@@ -39,8 +40,12 @@ Author: Dmitriy Yefremov
|
||||
<column type="gchararray"/>
|
||||
<!-- column-name title -->
|
||||
<column type="gchararray"/>
|
||||
<!-- column-name time -->
|
||||
<column type="gchararray"/>
|
||||
<!-- column-name start -->
|
||||
<column type="gint"/>
|
||||
<!-- column-name end -->
|
||||
<column type="gint"/>
|
||||
<!-- column-name length -->
|
||||
<column type="gint"/>
|
||||
<!-- column-name description -->
|
||||
<column type="gchararray"/>
|
||||
<!-- column-name data -->
|
||||
@@ -48,49 +53,62 @@ Author: Dmitriy Yefremov
|
||||
</columns>
|
||||
</object>
|
||||
<object class="GtkTreeModelFilter" id="epg_filter_model">
|
||||
<property name="child_model">epg_model</property>
|
||||
<property name="child-model">epg_model</property>
|
||||
</object>
|
||||
<object class="GtkTreeModelSort" id="epg_sort_model">
|
||||
<property name="model">epg_filter_model</property>
|
||||
</object>
|
||||
<object class="GtkFrame" id="epg_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="epg_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="can-focus">False</property>
|
||||
<property name="margin-start">5</property>
|
||||
<property name="margin-end">5</property>
|
||||
<property name="margin-top">2</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<object class="GtkBox" id="epg_action_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="margin_bottom">5</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="border-width">5</property>
|
||||
<property name="spacing">5</property>
|
||||
<child type="center">
|
||||
<object class="GtkToggleButton" id="multi_epg_button">
|
||||
<property name="label" translatable="yes">Multi EPG</property>
|
||||
<property name="name">header-button</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="receives-default">True</property>
|
||||
<signal name="toggled" handler="on_multi_epg_toggled" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">3</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkComboBoxText" id="src_combo_box">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="tooltip_text" translatable="yes">EPG source</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="tooltip-text" translatable="yes">EPG source</property>
|
||||
<property name="active">0</property>
|
||||
<property name="has_entry">True</property>
|
||||
<property name="active_id">0</property>
|
||||
<property name="has-entry">True</property>
|
||||
<property name="active-id">0</property>
|
||||
<items>
|
||||
<item id="0" translatable="yes">Receiver</item>
|
||||
</items>
|
||||
<child internal-child="entry">
|
||||
<object class="GtkEntry">
|
||||
<property name="can_focus">False</property>
|
||||
<property name="name">header-entry</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="editable">False</property>
|
||||
<property name="width_chars">10</property>
|
||||
<property name="width-chars">10</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
@@ -103,16 +121,16 @@ Author: Dmitriy Yefremov
|
||||
<child>
|
||||
<object class="GtkToggleButton" id="epg_filter_button">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="focus_on_click">False</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="tooltip_text" translatable="yes">Filter</property>
|
||||
<property name="can-focus">True</property>
|
||||
<property name="focus-on-click">False</property>
|
||||
<property name="receives-default">True</property>
|
||||
<property name="tooltip-text" translatable="yes">Filter</property>
|
||||
<signal name="toggled" handler="on_epg_filter_toggled" swapped="no"/>
|
||||
<child>
|
||||
<object class="GtkImage" id="epg_filter_button_image">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="icon_name">edit-find-replace-symbolic</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="icon-name">edit-find-replace-symbolic</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
@@ -125,17 +143,17 @@ Author: Dmitriy Yefremov
|
||||
<child>
|
||||
<object class="GtkButton" id="epg_add_timer_button">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="focus_on_click">False</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="tooltip_text" translatable="yes">Add timer</property>
|
||||
<property name="always_show_image">True</property>
|
||||
<property name="can-focus">True</property>
|
||||
<property name="focus-on-click">False</property>
|
||||
<property name="receives-default">True</property>
|
||||
<property name="tooltip-text" translatable="yes">Add timer</property>
|
||||
<property name="always-show-image">True</property>
|
||||
<signal name="clicked" handler="on_timer_add" swapped="no"/>
|
||||
<child>
|
||||
<object class="GtkImage" id="add_timer_image">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="icon_name">alarm-symbolic</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="icon-name">alarm-symbolic</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
@@ -145,20 +163,6 @@ Author: Dmitriy Yefremov
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child type="center">
|
||||
<object class="GtkToggleButton" id="multi_epg_button">
|
||||
<property name="label" translatable="yes">Multi EPG</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="receives_default">True</property>
|
||||
<signal name="toggled" handler="on_multi_epg_toggled" swapped="no"/>
|
||||
</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>
|
||||
@@ -169,18 +173,18 @@ Author: Dmitriy Yefremov
|
||||
<child>
|
||||
<object class="GtkBox" id="epg_fs_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_bottom">5</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="margin-start">5</property>
|
||||
<property name="margin-end">5</property>
|
||||
<property name="margin-bottom">5</property>
|
||||
<property name="spacing">10</property>
|
||||
<child>
|
||||
<object class="GtkSearchEntry" id="epg_filter_entry">
|
||||
<property name="can_focus">True</property>
|
||||
<property name="primary_icon_name">edit-find-replace-symbolic</property>
|
||||
<property name="primary_icon_activatable">False</property>
|
||||
<property name="primary_icon_sensitive">False</property>
|
||||
<property name="visible" bind-source="epg_filter_button" bind-property="active"/>
|
||||
<property name="visible" bind-source="epg_filter_button" bind-property="active">False</property>
|
||||
<property name="can-focus">True</property>
|
||||
<property name="primary-icon-name">edit-find-replace-symbolic</property>
|
||||
<property name="primary-icon-activatable">False</property>
|
||||
<property name="primary-icon-sensitive">False</property>
|
||||
<signal name="search-changed" handler="on_epg_filter_changed" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
@@ -191,15 +195,15 @@ Author: Dmitriy Yefremov
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkBox" id="fav_search_box">
|
||||
<property name="can_focus">False</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="valign">center</property>
|
||||
<child>
|
||||
<object class="GtkSearchEntry" id="epg_search_entry">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="primary_icon_name">edit-find-symbolic</property>
|
||||
<property name="primary_icon_activatable">False</property>
|
||||
<property name="primary_icon_sensitive">False</property>
|
||||
<property name="can-focus">True</property>
|
||||
<property name="primary-icon-name">edit-find-symbolic</property>
|
||||
<property name="primary-icon-activatable">False</property>
|
||||
<property name="primary-icon-sensitive">False</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
@@ -211,13 +215,13 @@ Author: Dmitriy Yefremov
|
||||
<object class="GtkButton" id="epg_search_down_button">
|
||||
<property name="visible">True</property>
|
||||
<property name="sensitive">False</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="can-focus">True</property>
|
||||
<property name="receives-default">True</property>
|
||||
<child>
|
||||
<object class="GtkArrow" id="epg_down_arrow">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="arrow_type">down</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="arrow-type">down</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
@@ -231,13 +235,13 @@ Author: Dmitriy Yefremov
|
||||
<object class="GtkButton" id="epg_search_up_button">
|
||||
<property name="visible">True</property>
|
||||
<property name="sensitive">False</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="can-focus">True</property>
|
||||
<property name="receives-default">True</property>
|
||||
<child>
|
||||
<object class="GtkArrow" id="epg_up_arrow">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="arrow_type">up</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="arrow-type">up</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
@@ -267,18 +271,17 @@ Author: Dmitriy Yefremov
|
||||
<child>
|
||||
<object class="GtkScrolledWindow" id="epg_view_scrolled_window">
|
||||
<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="epg_view">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="can-focus">True</property>
|
||||
<property name="model">epg_sort_model</property>
|
||||
<property name="rules_hint">True</property>
|
||||
<property name="fixed_height_mode">True</property>
|
||||
<property name="rubber_banding">True</property>
|
||||
<property name="enable_grid_lines">both</property>
|
||||
<property name="tooltip_column">3</property>
|
||||
<property name="fixed-height-mode">True</property>
|
||||
<property name="rubber-banding">True</property>
|
||||
<property name="enable-grid-lines">both</property>
|
||||
<property name="tooltip-column">6</property>
|
||||
<signal name="button-press-event" handler="on_epg_press" swapped="no"/>
|
||||
<signal name="query-tooltip" handler="on_view_query_tooltip" swapped="no"/>
|
||||
<child internal-child="selection">
|
||||
@@ -288,15 +291,14 @@ Author: Dmitriy Yefremov
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkTreeViewColumn" id="epg_service_column">
|
||||
<property name="visible">False</property>
|
||||
<property name="visible" bind-source="multi_epg_button" bind-property="active">False</property>
|
||||
<property name="resizable">True</property>
|
||||
<property name="sizing">fixed</property>
|
||||
<property name="fixed_width">100</property>
|
||||
<property name="min_width">40</property>
|
||||
<property name="fixed-width">100</property>
|
||||
<property name="min-width">40</property>
|
||||
<property name="title" translatable="yes">Service</property>
|
||||
<property name="alignment">0.49000000953674316</property>
|
||||
<property name="sort_column_id">0</property>
|
||||
<property name="visible" bind-source="multi_epg_button" bind-property="active"/>
|
||||
<property name="alignment">0.49</property>
|
||||
<property name="sort-column-id">0</property>
|
||||
<child>
|
||||
<object class="GtkCellRendererText" id="epg_service_renderer">
|
||||
<property name="xpad">5</property>
|
||||
@@ -311,11 +313,11 @@ Author: Dmitriy Yefremov
|
||||
<object class="GtkTreeViewColumn" id="epg_title_column">
|
||||
<property name="resizable">True</property>
|
||||
<property name="sizing">fixed</property>
|
||||
<property name="fixed_width">170</property>
|
||||
<property name="min_width">50</property>
|
||||
<property name="fixed-width">185</property>
|
||||
<property name="min-width">50</property>
|
||||
<property name="title" translatable="yes">Title</property>
|
||||
<property name="alignment">0.49000000953674316</property>
|
||||
<property name="sort_column_id">1</property>
|
||||
<property name="alignment">0.49</property>
|
||||
<property name="sort-column-id">1</property>
|
||||
<child>
|
||||
<object class="GtkCellRendererText" id="epg_title_renderer">
|
||||
<property name="xpad">5</property>
|
||||
@@ -327,18 +329,19 @@ Author: Dmitriy Yefremov
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkTreeViewColumn" id="epg_time_column">
|
||||
<object class="GtkTreeViewColumn" id="epg_start_column">
|
||||
<property name="resizable">True</property>
|
||||
<property name="sizing">fixed</property>
|
||||
<property name="fixed_width">210</property>
|
||||
<property name="min_width">50</property>
|
||||
<property name="title" translatable="yes">Time</property>
|
||||
<property name="alignment">0.49000000953674316</property>
|
||||
<property name="sort_column_id">2</property>
|
||||
<property name="min-width">50</property>
|
||||
<property name="title" translatable="yes">Start time</property>
|
||||
<property name="expand">True</property>
|
||||
<property name="alignment">0.49</property>
|
||||
<property name="sort-column-id">2</property>
|
||||
<child>
|
||||
<object class="GtkCellRendererText" id="epg_time_renderer">
|
||||
<object class="GtkCellRendererText" id="epg_start_renderer">
|
||||
<property name="xpad">5</property>
|
||||
<property name="xalign">0.49000000953674316</property>
|
||||
<property name="xalign">0.49</property>
|
||||
<property name="width-chars">27</property>
|
||||
</object>
|
||||
<attributes>
|
||||
<attribute name="text">2</attribute>
|
||||
@@ -346,21 +349,62 @@ Author: Dmitriy Yefremov
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkTreeViewColumn" id="epg_end_column">
|
||||
<property name="resizable">True</property>
|
||||
<property name="sizing">fixed</property>
|
||||
<property name="min-width">50</property>
|
||||
<property name="title" translatable="yes">End time</property>
|
||||
<property name="expand">True</property>
|
||||
<property name="alignment">0.49</property>
|
||||
<property name="sort-column-id">3</property>
|
||||
<child>
|
||||
<object class="GtkCellRendererText" id="epg_end_renderer">
|
||||
<property name="xpad">5</property>
|
||||
<property name="xalign">0.49</property>
|
||||
<property name="width-chars">27</property>
|
||||
</object>
|
||||
<attributes>
|
||||
<attribute name="text">3</attribute>
|
||||
</attributes>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkTreeViewColumn" id="epg_length_column">
|
||||
<property name="resizable">True</property>
|
||||
<property name="sizing">fixed</property>
|
||||
<property name="fixed-width">100</property>
|
||||
<property name="min-width">50</property>
|
||||
<property name="title" translatable="yes">Length</property>
|
||||
<property name="alignment">0.49</property>
|
||||
<property name="sort-column-id">4</property>
|
||||
<child>
|
||||
<object class="GtkCellRendererText" id="epg_length_renderer">
|
||||
<property name="xpad">5</property>
|
||||
<property name="xalign">0.49</property>
|
||||
</object>
|
||||
<attributes>
|
||||
<attribute name="text">4</attribute>
|
||||
</attributes>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkTreeViewColumn" id="epg_desc_column">
|
||||
<property name="sizing">fixed</property>
|
||||
<property name="fixed_width">100</property>
|
||||
<property name="min_width">50</property>
|
||||
<property name="fixed-width">100</property>
|
||||
<property name="min-width">50</property>
|
||||
<property name="title" translatable="yes">Description</property>
|
||||
<property name="expand">True</property>
|
||||
<property name="alignment">0.49000000953674316</property>
|
||||
<property name="sort_column_id">3</property>
|
||||
<property name="alignment">0.49</property>
|
||||
<property name="sort-column-id">5</property>
|
||||
<child>
|
||||
<object class="GtkCellRendererText" id="epg_desc_renderer">
|
||||
<property name="ellipsize">end</property>
|
||||
</object>
|
||||
<attributes>
|
||||
<attribute name="text">3</attribute>
|
||||
<attribute name="text">5</attribute>
|
||||
</attributes>
|
||||
</child>
|
||||
</object>
|
||||
@@ -376,17 +420,17 @@ Author: Dmitriy Yefremov
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkBox" id="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="event_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>
|
||||
@@ -397,9 +441,9 @@ Author: Dmitriy Yefremov
|
||||
<child>
|
||||
<object class="GtkLabel" id="event_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>
|
||||
@@ -420,7 +464,7 @@ Author: Dmitriy Yefremov
|
||||
<child type="label">
|
||||
<object class="GtkLabel" id="epg_label">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="label" translatable="yes">EPG</property>
|
||||
</object>
|
||||
</child>
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- Generated with glade 3.22.2
|
||||
<!-- Generated with glade 3.38.2
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2018-2020 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
|
||||
@@ -27,25 +27,14 @@ Author: Dmitriy Yefremov
|
||||
|
||||
-->
|
||||
<interface domain="demon-editor">
|
||||
<requires lib="gtk+" version="3.16"/>
|
||||
<requires lib="gtk+" version="3.22"/>
|
||||
<!-- interface-css-provider-path style.css -->
|
||||
<!-- interface-license-type mit -->
|
||||
<!-- interface-name DemonEditor -->
|
||||
<!-- interface-description Enigma2 channel and satellite list editor. -->
|
||||
<!-- interface-copyright 2018-2021 Dmitriy Yefremov -->
|
||||
<!-- interface-copyright 2018-2023 Dmitriy Yefremov -->
|
||||
<!-- interface-authors Dmitriy Yefremov -->
|
||||
<object class="GtkImage" id="details_image">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="icon_name">emblem-important-symbolic</property>
|
||||
</object>
|
||||
<object class="GtkImage" id="import_image">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="icon_name">document-revert-symbolic-rtl</property>
|
||||
<property name="icon_size">1</property>
|
||||
</object>
|
||||
<object class="GtkListStore" id="main_list_store">
|
||||
<object class="GtkListStore" id="bq_list_store">
|
||||
<columns>
|
||||
<!-- column-name name -->
|
||||
<column type="gchararray"/>
|
||||
@@ -55,113 +44,243 @@ Author: Dmitriy Yefremov
|
||||
<column type="gboolean"/>
|
||||
</columns>
|
||||
</object>
|
||||
<object class="GtkImage" id="details_image">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="icon-name">emblem-important-symbolic</property>
|
||||
</object>
|
||||
<object class="GtkImage" id="import_image">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="icon-name">document-revert-symbolic-rtl</property>
|
||||
<property name="icon_size">1</property>
|
||||
</object>
|
||||
<object class="GtkImage" id="remove_selection_image">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="icon_name">edit-undo</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="icon-name">edit-undo</property>
|
||||
</object>
|
||||
<object class="GtkMenu" id="popup_menu">
|
||||
<object class="GtkMenu" id="bq_popup_menu">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="can-focus">False</property>
|
||||
<child>
|
||||
<object class="GtkImageMenuItem" id="select_all_popup_item">
|
||||
<property name="label">gtk-select-all</property>
|
||||
<property name="visible">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_select_all" object="main_view" swapped="no"/>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="use-underline">True</property>
|
||||
<property name="use-stock">True</property>
|
||||
<signal name="activate" handler="on_select_all" object="bq_view" swapped="no"/>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkImageMenuItem" id="unselect_all_popup_item">
|
||||
<property name="label" translatable="yes">Remove selection</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="image">remove_selection_image</property>
|
||||
<property name="use_stock">False</property>
|
||||
<signal name="activate" handler="on_unselect_all" object="main_view" swapped="no"/>
|
||||
<property name="use-stock">False</property>
|
||||
<signal name="activate" handler="on_unselect_all" object="bq_view" swapped="no"/>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<object class="GtkImage" id="remove_selection_image1">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="icon-name">edit-undo</property>
|
||||
</object>
|
||||
<object class="GtkMenu" id="sat_popup_menu">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<child>
|
||||
<object class="GtkImageMenuItem" id="sat_select_all_popup_item">
|
||||
<property name="label">gtk-select-all</property>
|
||||
<property name="visible">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_select_all" object="sat_view" swapped="no"/>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkImageMenuItem" id="sat_unselect_all_popup_item">
|
||||
<property name="label" translatable="yes">Remove selection</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="image">remove_selection_image1</property>
|
||||
<property name="use-stock">False</property>
|
||||
<signal name="activate" handler="on_unselect_all" object="sat_view" swapped="no"/>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<object class="GtkImage" id="remove_services_selection_image">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="icon-name">edit-undo</property>
|
||||
</object>
|
||||
<object class="GtkMenu" id="services_popup_menu">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<child>
|
||||
<object class="GtkImageMenuItem" id="select_all_services_popup_item">
|
||||
<property name="label">gtk-select-all</property>
|
||||
<property name="visible">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_select_all" object="services_view" swapped="no"/>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkImageMenuItem" id="unselect_all_services_popup_item">
|
||||
<property name="label" translatable="yes">Remove selection</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="image">remove_services_selection_image</property>
|
||||
<property name="use-stock">False</property>
|
||||
<signal name="activate" handler="on_unselect_all" object="services_view" swapped="no"/>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<object class="GtkListStore" id="sat_list_store">
|
||||
<columns>
|
||||
<!-- column-name position -->
|
||||
<column type="gchararray"/>
|
||||
<!-- column-name type -->
|
||||
<column type="gchararray"/>
|
||||
<!-- column-name selected -->
|
||||
<column type="gboolean"/>
|
||||
</columns>
|
||||
</object>
|
||||
<object class="GtkListStore" id="services_list_store">
|
||||
<columns>
|
||||
<!-- column-name name -->
|
||||
<column type="gchararray"/>
|
||||
<!-- column-name type -->
|
||||
<column type="gchararray"/>
|
||||
<!-- column-name selected -->
|
||||
<column type="gboolean"/>
|
||||
<!-- column-name background -->
|
||||
<column type="GdkRGBA"/>
|
||||
<!-- column-name id -->
|
||||
<column type="gchararray"/>
|
||||
</columns>
|
||||
<signal name="row-changed" handler="on_services_model_changed" swapped="no"/>
|
||||
</object>
|
||||
<object class="GtkWindow" id="dialog_window">
|
||||
<property name="can_focus">False</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="title" translatable="yes">Import</property>
|
||||
<property name="modal">True</property>
|
||||
<property name="window_position">center-on-parent</property>
|
||||
<property name="default_width">480</property>
|
||||
<property name="default_height">320</property>
|
||||
<property name="destroy_with_parent">True</property>
|
||||
<property name="type_hint">dialog</property>
|
||||
<property name="window-position">center-on-parent</property>
|
||||
<property name="default-width">480</property>
|
||||
<property name="default-height">320</property>
|
||||
<property name="destroy-with-parent">True</property>
|
||||
<property name="type-hint">dialog</property>
|
||||
<signal name="check-resize" handler="on_resize" swapped="no"/>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkBox" id="main_box">
|
||||
<property name="width_request">480</property>
|
||||
<property name="width-request">480</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<property name="spacing">5</property>
|
||||
<child>
|
||||
<object class="GtkBox" id="toolbar_box">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="can-focus">False</property>
|
||||
<child>
|
||||
<object class="GtkButtonBox" id="actions_box">
|
||||
<object class="GtkBox" id="actions_box">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="valign">center</property>
|
||||
<property name="margin_left">15</property>
|
||||
<property name="margin_right">15</property>
|
||||
<property name="margin_top">10</property>
|
||||
<property name="margin_bottom">10</property>
|
||||
<property name="layout_style">start</property>
|
||||
<property name="margin-start">15</property>
|
||||
<property name="margin-end">15</property>
|
||||
<property name="margin-top">10</property>
|
||||
<property name="margin-bottom">10</property>
|
||||
<property name="spacing">5</property>
|
||||
<child type="center">
|
||||
<object class="GtkStackSwitcher" id="stack_switcher">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="tooltip-text" translatable="yes">Group by</property>
|
||||
<property name="stack">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="import_button">
|
||||
<property name="label" translatable="yes">Import</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="tooltip_text" translatable="yes">Bouquets and services</property>
|
||||
<property name="valign">center</property>
|
||||
<property name="can-focus">True</property>
|
||||
<property name="receives-default">True</property>
|
||||
<property name="tooltip-text" translatable="yes">Bouquets and services</property>
|
||||
<property name="image">import_image</property>
|
||||
<property name="always_show_image">True</property>
|
||||
<property name="always-show-image">True</property>
|
||||
<signal name="clicked" handler="on_import" swapped="no"/>
|
||||
</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="GtkCheckButton" id="info_check_button">
|
||||
<property name="label" translatable="yes">Details</property>
|
||||
<object class="GtkBox" id="extra_header_box">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">False</property>
|
||||
<property name="tooltip_text" translatable="yes">Details</property>
|
||||
<property name="valign">center</property>
|
||||
<property name="image">details_image</property>
|
||||
<property name="always_show_image">True</property>
|
||||
<property name="draw_indicator">False</property>
|
||||
<accelerator key="i" signal="clicked" modifiers="Primary"/>
|
||||
<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>
|
||||
<property name="can-focus">True</property>
|
||||
<property name="focus-on-click">False</property>
|
||||
<property name="receives-default">True</property>
|
||||
<property name="tooltip-text" translatable="yes">Details</property>
|
||||
<property name="margin-start">5</property>
|
||||
<property name="image">details_image</property>
|
||||
<property name="always-show-image">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="pack-type">end</property>
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
<property name="secondary">True</property>
|
||||
<property name="pack-type">end</property>
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
@@ -182,88 +301,386 @@ Author: Dmitriy Yefremov
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkPaned" id="main_paned">
|
||||
<object class="GtkPaned" id="paned">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="margin_left">5</property>
|
||||
<property name="margin_right">5</property>
|
||||
<property name="margin_bottom">5</property>
|
||||
<property name="wide_handle">True</property>
|
||||
<property name="can-focus">True</property>
|
||||
<property name="margin-start">5</property>
|
||||
<property name="margin-end">5</property>
|
||||
<property name="margin-bottom">5</property>
|
||||
<property name="wide-handle">True</property>
|
||||
<signal name="realize" handler="on_main_paned_realize" swapped="no"/>
|
||||
<child>
|
||||
<object class="GtkFrame" id="bouquets_box_frame">
|
||||
<object class="GtkStack" id="stack">
|
||||
<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>
|
||||
<signal name="notify::visible-child-name" handler="on_visible_page" swapped="no"/>
|
||||
<child>
|
||||
<object class="GtkBox" id="bouquets_box">
|
||||
<object class="GtkFrame" id="bouquets_box_frame">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_left">5</property>
|
||||
<property name="margin_right">5</property>
|
||||
<property name="margin_bottom">5</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="label-xalign">0.49000000953674316</property>
|
||||
<property name="shadow-type">in</property>
|
||||
<child>
|
||||
<object class="GtkScrolledWindow" id="bouquets_screlled_window">
|
||||
<property name="width_request">200</property>
|
||||
<object class="GtkBox" id="bouquets_box">
|
||||
<property name="width-request">100</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="shadow_type">in</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="margin-start">5</property>
|
||||
<property name="margin-end">5</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<object class="GtkTreeView" id="main_view">
|
||||
<object class="GtkScrolledWindow" id="bouquets_screlled_window">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="model">main_list_store</property>
|
||||
<property name="headers_clickable">False</property>
|
||||
<property name="search_column">0</property>
|
||||
<signal name="button-press-event" handler="on_popup_menu" object="popup_menu" swapped="no"/>
|
||||
<signal name="cursor-changed" handler="on_cursor_changed" swapped="no"/>
|
||||
<signal name="key-press-event" handler="on_key_press" swapped="no"/>
|
||||
<signal name="select-all" handler="on_select_all" swapped="no"/>
|
||||
<child internal-child="selection">
|
||||
<object class="GtkTreeSelection">
|
||||
<property name="mode">multiple</property>
|
||||
<property name="can-focus">True</property>
|
||||
<property name="shadow-type">in</property>
|
||||
<child>
|
||||
<object class="GtkTreeView" id="bq_view">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">True</property>
|
||||
<property name="model">bq_list_store</property>
|
||||
<property name="headers-clickable">False</property>
|
||||
<property name="search-column">0</property>
|
||||
<signal name="button-press-event" handler="on_popup_menu" object="bq_popup_menu" swapped="no"/>
|
||||
<signal name="cursor-changed" handler="on_cursor_changed" swapped="no"/>
|
||||
<signal name="key-press-event" handler="on_key_press" swapped="no"/>
|
||||
<signal name="select-all" handler="on_select_all" swapped="no"/>
|
||||
<child internal-child="selection">
|
||||
<object class="GtkTreeSelection"/>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkTreeViewColumn" id="bouquet_name_column">
|
||||
<property name="title" translatable="yes">Name</property>
|
||||
<property name="expand">True</property>
|
||||
<property name="alignment">0.5</property>
|
||||
<child>
|
||||
<object class="GtkCellRendererText" id="bq_name_renderer">
|
||||
<property name="xalign">0.019999999552965164</property>
|
||||
<property name="ellipsize">end</property>
|
||||
</object>
|
||||
<attributes>
|
||||
<attribute name="text">0</attribute>
|
||||
</attributes>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkTreeViewColumn" id="bouquet_type_column">
|
||||
<property name="title" translatable="yes">Type</property>
|
||||
<property name="alignment">0.5</property>
|
||||
<child>
|
||||
<object class="GtkCellRendererText" id="bq_type_renderer">
|
||||
<property name="xalign">0.5099999904632568</property>
|
||||
</object>
|
||||
<attributes>
|
||||
<attribute name="text">1</attribute>
|
||||
</attributes>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkTreeViewColumn" id="bouquet_selected_column">
|
||||
<property name="title" translatable="yes">Selected</property>
|
||||
<property name="alignment">0.5</property>
|
||||
<child>
|
||||
<object class="GtkCellRendererToggle" id="bq_selected_renderer">
|
||||
<signal name="toggled" handler="on_bq_selected_toggled" swapped="no"/>
|
||||
</object>
|
||||
<attributes>
|
||||
<attribute name="active">2</attribute>
|
||||
</attributes>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkBox" id="bouquets_status_box">
|
||||
<property name="height-request">26</property>
|
||||
<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="spacing">5</property>
|
||||
<child>
|
||||
<object class="GtkTreeViewColumn" id="bouquet_name_column">
|
||||
<object class="GtkImage" id="bouquets_count_image">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="icon-name">document-properties</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="bouquets_count_label">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="label">0</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child type="label">
|
||||
<object class="GtkLabel">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="margin-top">5</property>
|
||||
<property name="margin-bottom">5</property>
|
||||
<property name="label" translatable="yes">Bouquets</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="name">services</property>
|
||||
<property name="title" translatable="yes">Bouquets</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkFrame" id="sat_box_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>
|
||||
<child>
|
||||
<object class="GtkBox" id="satellites_box">
|
||||
<property name="width-request">100</property>
|
||||
<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="orientation">vertical</property>
|
||||
<child>
|
||||
<object class="GtkScrolledWindow" id="satellites_screlled_window">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">True</property>
|
||||
<property name="shadow-type">in</property>
|
||||
<child>
|
||||
<object class="GtkTreeView" id="sat_view">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">True</property>
|
||||
<property name="model">sat_list_store</property>
|
||||
<property name="headers-clickable">False</property>
|
||||
<property name="search-column">0</property>
|
||||
<signal name="button-press-event" handler="on_popup_menu" object="sat_popup_menu" swapped="no"/>
|
||||
<signal name="cursor-changed" handler="on_cursor_changed" swapped="no"/>
|
||||
<signal name="key-press-event" handler="on_key_press" swapped="no"/>
|
||||
<signal name="realize" handler="on_sat_view_realize" swapped="no"/>
|
||||
<signal name="select-all" handler="on_select_all" swapped="no"/>
|
||||
<child internal-child="selection">
|
||||
<object class="GtkTreeSelection"/>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkTreeViewColumn" id="sat_position_column">
|
||||
<property name="title" translatable="yes">Position</property>
|
||||
<property name="expand">True</property>
|
||||
<property name="alignment">0.5</property>
|
||||
<child>
|
||||
<object class="GtkCellRendererText" id="sat_position_renderer">
|
||||
<property name="xalign">0.019999999552965164</property>
|
||||
</object>
|
||||
<attributes>
|
||||
<attribute name="text">0</attribute>
|
||||
</attributes>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkTreeViewColumn" id="sat_selected_column">
|
||||
<property name="title" translatable="yes">Selected</property>
|
||||
<property name="alignment">0.5</property>
|
||||
<child>
|
||||
<object class="GtkCellRendererToggle" id="sat_selected_renderer">
|
||||
<signal name="toggled" handler="on_sat_selected_toggled" swapped="no"/>
|
||||
</object>
|
||||
<attributes>
|
||||
<attribute name="active">2</attribute>
|
||||
</attributes>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkBox" id="sat_status_box">
|
||||
<property name="height-request">26</property>
|
||||
<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="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>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="sat_count_label">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="label">0</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child type="label">
|
||||
<object class="GtkLabel">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="margin-top">5</property>
|
||||
<property name="margin-bottom">5</property>
|
||||
<property name="label" translatable="yes">Satellites</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="name">satellite</property>
|
||||
<property name="title" translatable="yes">Satellites</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="resize">False</property>
|
||||
<property name="shrink">False</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkFrame" id="bq_services_box_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>
|
||||
<child>
|
||||
<object class="GtkBox" id="services_box">
|
||||
<property name="width-request">100</property>
|
||||
<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="orientation">vertical</property>
|
||||
<child>
|
||||
<object class="GtkScrolledWindow" id="services_view_scrolled_window">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">True</property>
|
||||
<property name="shadow-type">in</property>
|
||||
<child>
|
||||
<object class="GtkTreeView" id="services_view">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">True</property>
|
||||
<property name="model">services_list_store</property>
|
||||
<property name="headers-clickable">False</property>
|
||||
<property name="search-column">0</property>
|
||||
<signal name="button-press-event" handler="on_popup_menu" object="services_popup_menu" swapped="no"/>
|
||||
<signal name="cursor-changed" handler="on_service_changed" swapped="no"/>
|
||||
<signal name="key-press-event" handler="on_key_press" swapped="no"/>
|
||||
<signal name="realize" handler="on_services_view_realize" swapped="no"/>
|
||||
<signal name="select-all" handler="on_select_all" swapped="no"/>
|
||||
<child internal-child="selection">
|
||||
<object class="GtkTreeSelection"/>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkTreeViewColumn" id="service_name_column">
|
||||
<property name="sizing">fixed</property>
|
||||
<property name="min-width">50</property>
|
||||
<property name="title" translatable="yes">Name</property>
|
||||
<property name="expand">True</property>
|
||||
<property name="alignment">0.5</property>
|
||||
<child>
|
||||
<object class="GtkCellRendererText" id="bq_name_renderer">
|
||||
<object class="GtkCellRendererText" id="info_name_renderer">
|
||||
<property name="xalign">0.019999999552965164</property>
|
||||
<property name="ellipsize">end</property>
|
||||
</object>
|
||||
<attributes>
|
||||
<attribute name="text">0</attribute>
|
||||
<attribute name="background-rgba">3</attribute>
|
||||
</attributes>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkTreeViewColumn" id="bouquet_type_column">
|
||||
<object class="GtkTreeViewColumn" id="service_type_column">
|
||||
<property name="sizing">fixed</property>
|
||||
<property name="min-width">75</property>
|
||||
<property name="title" translatable="yes">Type</property>
|
||||
<property name="alignment">0.5</property>
|
||||
<child>
|
||||
<object class="GtkCellRendererText" id="bq_type_renderer">
|
||||
<property name="xalign">0.50999999046325684</property>
|
||||
<object class="GtkCellRendererText" id="info_type_renderer">
|
||||
<property name="xpad">5</property>
|
||||
<property name="xalign">0.5099999904632568</property>
|
||||
</object>
|
||||
<attributes>
|
||||
<attribute name="text">1</attribute>
|
||||
<attribute name="background-rgba">3</attribute>
|
||||
</attributes>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkTreeViewColumn" id="bouquet_selected_column">
|
||||
<object class="GtkTreeViewColumn" id="service_selected_column">
|
||||
<property name="sizing">fixed</property>
|
||||
<property name="min-width">75</property>
|
||||
<property name="title" translatable="yes">Selected</property>
|
||||
<property name="alignment">0.5</property>
|
||||
<child>
|
||||
<object class="GtkCellRendererToggle" id="bq_selected_renderer">
|
||||
<property name="xalign">0.50999999046325684</property>
|
||||
<signal name="toggled" handler="on_selected_toggled" swapped="no"/>
|
||||
<object class="GtkCellRendererToggle" id="service_selected_renderer">
|
||||
<property name="xpad">5</property>
|
||||
<signal name="toggled" handler="on_service_selected_toggled" swapped="no"/>
|
||||
</object>
|
||||
<attributes>
|
||||
<attribute name="cell-background-rgba">3</attribute>
|
||||
<attribute name="active">2</attribute>
|
||||
</attributes>
|
||||
</child>
|
||||
@@ -278,89 +695,120 @@ Author: Dmitriy Yefremov
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child type="label">
|
||||
<object class="GtkLabel">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_top">5</property>
|
||||
<property name="margin_bottom">5</property>
|
||||
<property name="label" translatable="yes">Bouquets</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="resize">True</property>
|
||||
<property name="shrink">True</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkFrame" id="services_box_frame">
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label_xalign">0.49000000953674316</property>
|
||||
<property name="shadow_type">in</property>
|
||||
<child>
|
||||
<object class="GtkBox" id="services_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_bottom">5</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<object class="GtkScrolledWindow" id="services_view_scrolled_window">
|
||||
<property name="width_request">150</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="shadow_type">in</property>
|
||||
<object class="GtkFrame" id="service_info_box_frame">
|
||||
<property name="visible" bind-source="details_button" bind-property="active">False</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="margin-top">5</property>
|
||||
<property name="label-xalign">0</property>
|
||||
<property name="shadow-type">in</property>
|
||||
<child>
|
||||
<object class="GtkTreeView" id="services_view">
|
||||
<object class="GtkBox" id="service_info_box">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="model">services_list_store</property>
|
||||
<property name="headers_clickable">False</property>
|
||||
<property name="search_column">0</property>
|
||||
<child internal-child="selection">
|
||||
<object class="GtkTreeSelection"/>
|
||||
</child>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="margin-top">5</property>
|
||||
<property name="margin-bottom">5</property>
|
||||
<property name="spacing">5</property>
|
||||
<child>
|
||||
<object class="GtkTreeViewColumn" id="service_name_column">
|
||||
<property name="title" translatable="yes">Service</property>
|
||||
<property name="expand">True</property>
|
||||
<property name="alignment">0.5</property>
|
||||
<child>
|
||||
<object class="GtkCellRendererText" id="info_name_renderer">
|
||||
<property name="xalign">0.02</property>
|
||||
<property name="ellipsize">end</property>
|
||||
</object>
|
||||
<attributes>
|
||||
<attribute name="text">0</attribute>
|
||||
</attributes>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkTreeViewColumn" id="service_type_column">
|
||||
<property name="title" translatable="yes">Type</property>
|
||||
<property name="alignment">0.5</property>
|
||||
<child>
|
||||
<object class="GtkCellRendererText" id="info_type_renderer">
|
||||
<property name="xalign">0.50999999046325684</property>
|
||||
</object>
|
||||
<attributes>
|
||||
<attribute name="text">1</attribute>
|
||||
</attributes>
|
||||
</child>
|
||||
<object class="GtkLabel" id="service_info_label">
|
||||
<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="ellipsize">end</property>
|
||||
</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">
|
||||
<placeholder/>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkBox" id="services_status_box">
|
||||
<property name="height-request">26</property>
|
||||
<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="spacing">5</property>
|
||||
<child>
|
||||
<object class="GtkImage" id="services_count_image">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="icon-name">document-properties</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="services_count_label">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="label">0</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkFrame" id="service_exists_frame">
|
||||
<property name="width-request">32</property>
|
||||
<property name="height-request">16</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="halign">center</property>
|
||||
<property name="valign">center</property>
|
||||
<property name="label-xalign">0</property>
|
||||
<property name="shadow-type">in</property>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child type="label_item">
|
||||
<placeholder/>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="pack-type">end</property>
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id=" service_exists_label">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="label" translatable="yes">Already exists</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="pack-type">end</property>
|
||||
<property name="position">3</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
@@ -368,10 +816,10 @@ Author: Dmitriy Yefremov
|
||||
<child type="label">
|
||||
<object class="GtkLabel">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_top">5</property>
|
||||
<property name="margin_bottom">5</property>
|
||||
<property name="label" translatable="yes">Bouquet details</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="margin-top">5</property>
|
||||
<property name="margin-bottom">5</property>
|
||||
<property name="label" translatable="yes">Services</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
@@ -389,14 +837,14 @@ Author: Dmitriy Yefremov
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkInfoBar" id="info_bar">
|
||||
<property name="can_focus">False</property>
|
||||
<property name="show_close_button">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="show-close-button">True</property>
|
||||
<signal name="response" handler="on_info_bar_close" swapped="no"/>
|
||||
<child internal-child="action_area">
|
||||
<object class="GtkButtonBox">
|
||||
<property name="can_focus">False</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="spacing">6</property>
|
||||
<property name="layout_style">end</property>
|
||||
<property name="layout-style">end</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
@@ -406,12 +854,12 @@ Author: Dmitriy Yefremov
|
||||
</child>
|
||||
<child internal-child="content_area">
|
||||
<object class="GtkBox">
|
||||
<property name="can_focus">False</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="spacing">16</property>
|
||||
<child>
|
||||
<object class="GtkLabel" id="message_label">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="label" translatable="yes">message</property>
|
||||
<property name="wrap">True</property>
|
||||
</object>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
#
|
||||
# The MIT License (MIT)
|
||||
#
|
||||
# Copyright (c) 2018-2021 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
|
||||
@@ -26,6 +26,7 @@
|
||||
#
|
||||
|
||||
|
||||
from collections import defaultdict
|
||||
from contextlib import suppress
|
||||
from pathlib import Path
|
||||
|
||||
@@ -35,15 +36,18 @@ from app.eparser.ecommons import BqType, BqServiceType, Bouquet
|
||||
from app.eparser.neutrino.bouquets import parse_webtv, parse_bouquets as get_neutrino_bouquets
|
||||
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
|
||||
from .uicommons import Gtk, UI_RESOURCES_PATH, KeyboardKey, Column
|
||||
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
|
||||
|
||||
|
||||
def import_bouquet(transient, model, path, settings, services, appender, file_path=None):
|
||||
def import_bouquet(app, model, path, appender, file_path=None):
|
||||
""" Import of single bouquet """
|
||||
itr = model.get_iter(path)
|
||||
bq_type = BqType(model.get(itr, Column.BQ_TYPE)[0])
|
||||
pattern, f_pattern = None, None
|
||||
settings = app.app_settings
|
||||
transient = app.app_window
|
||||
services = app.current_services
|
||||
profile = settings.setting_type
|
||||
|
||||
if profile is SettingsType.ENIGMA_2:
|
||||
@@ -88,7 +92,7 @@ def import_bouquet(transient, model, path, settings, services, appender, file_pa
|
||||
else:
|
||||
bqs = get_neutrino_bouquets(file_path, "", bq_type.value)
|
||||
file_path = f"{Path(file_path).parent}{SEP}"
|
||||
ImportDialog(transient, file_path, settings, services.keys(), lambda b, s: appender(b), (bqs,)).show()
|
||||
ImportDialog(app, file_path, lambda b, s: appender(b), (bqs,)).show()
|
||||
|
||||
|
||||
def get_enigma2_bouquet(path):
|
||||
@@ -100,37 +104,77 @@ def get_enigma2_bouquet(path):
|
||||
|
||||
|
||||
class ImportDialog:
|
||||
def __init__(self, transient, path, settings, service_ids, appender, bouquets=None):
|
||||
def __init__(self, app, path, appender, bouquets=None):
|
||||
handlers = {"on_import": self.on_import,
|
||||
"on_cursor_changed": self.on_cursor_changed,
|
||||
"on_selected_toggled": self.on_selected_toggled,
|
||||
"on_service_changed": self.on_service_changed,
|
||||
"on_bq_selected_toggled": self.on_bq_selected_toggled,
|
||||
"on_sat_selected_toggled": self.on_sat_selected_toggled,
|
||||
"on_service_selected_toggled": self.on_service_selected_toggled,
|
||||
"on_services_model_changed": self.on_services_model_changed,
|
||||
"on_info_bar_close": self.on_info_bar_close,
|
||||
"on_select_all": self.on_select_all,
|
||||
"on_unselect_all": self.on_unselect_all,
|
||||
"on_sat_view_realize": self.on_sat_view_realize,
|
||||
"on_services_view_realize": self.on_services_view_realize,
|
||||
"on_popup_menu": on_popup_menu,
|
||||
"on_resize": self.on_resize,
|
||||
"on_main_paned_realize": self.on_main_paned_realize,
|
||||
"on_visible_page": self.on_visible_page,
|
||||
"on_key_press": self.on_key_press}
|
||||
|
||||
builder = get_builder(UI_RESOURCES_PATH + "imports.glade", handlers)
|
||||
builder = get_builder(f"{UI_RESOURCES_PATH}imports.glade", handlers)
|
||||
|
||||
self._bq_services = {}
|
||||
self._app = app
|
||||
self._services = {}
|
||||
self._service_ids = service_ids
|
||||
self._bq_services = {}
|
||||
self._sat_services = defaultdict(list)
|
||||
self._ids = self._app.current_services.keys()
|
||||
self._skip_import = defaultdict(set)
|
||||
self._append = appender
|
||||
self._profile = settings.setting_type
|
||||
self._settings = settings
|
||||
self._profile = app.app_settings.setting_type
|
||||
self._settings = app.app_settings
|
||||
self._bouquets = bouquets
|
||||
self._current_bq = None
|
||||
self._current_sat = None
|
||||
self._existing_srv_background = None
|
||||
self._page = Page.SERVICES
|
||||
|
||||
self._dialog_window = builder.get_object("dialog_window")
|
||||
self._dialog_window.set_transient_for(transient)
|
||||
self._main_model = builder.get_object("main_list_store")
|
||||
self._main_view = builder.get_object("main_view")
|
||||
self._services_view = builder.get_object("services_view")
|
||||
self._services_model = builder.get_object("services_list_store")
|
||||
self._info_check_button = builder.get_object("info_check_button")
|
||||
self._info_check_button.bind_property("active", builder.get_object("services_box_frame"), "visible")
|
||||
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")
|
||||
self._replace_existing_switch = builder.get_object("replace_existing_switch")
|
||||
# Bouquets page.
|
||||
self._bq_model = builder.get_object("bq_list_store")
|
||||
self._bq_view = builder.get_object("bq_view")
|
||||
self._services_view = builder.get_object("services_view")
|
||||
self._services_model = builder.get_object("services_list_store")
|
||||
self._bouquets_count_label = builder.get_object("bouquets_count_label")
|
||||
self._services_count_label = builder.get_object("services_count_label")
|
||||
self._service_info_label = builder.get_object("service_info_label")
|
||||
self._service_exists_frame = builder.get_object("service_exists_frame")
|
||||
# Satellites page.
|
||||
self._sat_view = builder.get_object("sat_view")
|
||||
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:
|
||||
actions_box = builder.get_object("actions_box")
|
||||
builder.get_object("toolbar_box").set_visible(False)
|
||||
header_bar = HeaderBar()
|
||||
stack_switcher = builder.get_object("stack_switcher")
|
||||
actions_box.remove(stack_switcher)
|
||||
header_bar.set_custom_title(stack_switcher)
|
||||
button = builder.get_object("import_button")
|
||||
actions_box.remove(button)
|
||||
header_bar.pack_start(button)
|
||||
extra_box = builder.get_object("extra_header_box")
|
||||
actions_box.remove(extra_box)
|
||||
header_bar.pack_end(extra_box)
|
||||
|
||||
self._dialog_window.set_titlebar(header_bar)
|
||||
|
||||
window_size = self._settings.get("import_dialog_window_size")
|
||||
if window_size:
|
||||
self._dialog_window.resize(*window_size)
|
||||
@@ -142,7 +186,7 @@ class ImportDialog:
|
||||
|
||||
@run_idle
|
||||
def init_data(self, path):
|
||||
self._main_model.clear()
|
||||
self._bq_model.clear()
|
||||
self._services_model.clear()
|
||||
try:
|
||||
if not self._bouquets:
|
||||
@@ -150,8 +194,9 @@ class ImportDialog:
|
||||
self._bouquets = get_bouquets(path, self._profile)
|
||||
for bqs in self._bouquets:
|
||||
for bq in bqs.bouquets:
|
||||
self._main_model.append((bq.name, bq.type, True))
|
||||
self._bq_model.append((bq.name, bq.type, True))
|
||||
self._bq_services[(bq.name, bq.type)] = bq.services
|
||||
self._bouquets_count_label.set_text(str(len(self._bq_model)))
|
||||
|
||||
if self._profile is SettingsType.ENIGMA_2:
|
||||
services = get_services(path, self._profile, 5 if self._settings.v5_support else 4)
|
||||
@@ -164,21 +209,24 @@ class ImportDialog:
|
||||
for srv in services:
|
||||
self._services[srv.fav_id] = srv
|
||||
except FileNotFoundError as e:
|
||||
log("Import error [init data]: {}".format(e))
|
||||
log(f"Import error [init data]: {e}")
|
||||
self.show_info_message(str(e), Gtk.MessageType.ERROR)
|
||||
|
||||
def on_import(self, item):
|
||||
if not any(r[-1] for r in self._main_model):
|
||||
self.show_info_message(get_message("No selected item!"), Gtk.MessageType.ERROR)
|
||||
return
|
||||
if self._page is Page.SERVICES:
|
||||
if not any(r[-1] for r in self._bq_model):
|
||||
self.show_info_message(get_message("No selected item!"), Gtk.MessageType.ERROR)
|
||||
return
|
||||
|
||||
if not self._bouquets or show_dialog(DialogType.QUESTION, self._dialog_window) == Gtk.ResponseType.CANCEL:
|
||||
return
|
||||
if not self._bouquets or show_dialog(DialogType.QUESTION, self._dialog_window) != Gtk.ResponseType.OK:
|
||||
return
|
||||
|
||||
self.import_data()
|
||||
self.import_bouquets_data()
|
||||
else:
|
||||
self.import_satellites_data()
|
||||
|
||||
@run_idle
|
||||
def import_data(self):
|
||||
def import_bouquets_data(self):
|
||||
""" Importing data into models. """
|
||||
if not self._bouquets:
|
||||
return
|
||||
@@ -186,12 +234,13 @@ class ImportDialog:
|
||||
log("Importing data...")
|
||||
services = set()
|
||||
to_delete = set()
|
||||
for row in self._main_model:
|
||||
for row in self._bq_model:
|
||||
bq = (row[0], row[1])
|
||||
if row[-1]:
|
||||
skip = self._skip_import[bq]
|
||||
for bq_srv in self._bq_services.get(bq, []):
|
||||
srv = self._services.get(bq_srv.data, None)
|
||||
if srv:
|
||||
if srv and srv.fav_id not in skip:
|
||||
services.add(srv)
|
||||
else:
|
||||
to_delete.add(bq)
|
||||
@@ -200,35 +249,119 @@ class ImportDialog:
|
||||
for bq in bqs.bouquets:
|
||||
if (bq.name, bq.type) in to_delete:
|
||||
bqs_to_delete.append(bq)
|
||||
else:
|
||||
skip = self._skip_import[(bq.name, bq.type)]
|
||||
bq_services = [srv for srv in bq.services if srv.data not in skip]
|
||||
bq.services.clear()
|
||||
bq.services.extend(bq_services)
|
||||
for bqs in self._bouquets:
|
||||
bq = bqs.bouquets
|
||||
for b in bqs_to_delete:
|
||||
with suppress(ValueError):
|
||||
bq.remove(b)
|
||||
self._append(self._bouquets, list(filter(lambda s: s.fav_id not in self._service_ids, services)))
|
||||
|
||||
self._append(self._bouquets, list(filter(lambda s: s.fav_id not in self._ids, 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)})
|
||||
|
||||
self._dialog_window.destroy()
|
||||
|
||||
def import_satellites_data(self):
|
||||
if show_dialog(DialogType.QUESTION, self._dialog_window) != Gtk.ResponseType.OK:
|
||||
return
|
||||
|
||||
replace_existing = self._replace_existing_switch.get_active()
|
||||
services = []
|
||||
current_services = self._app.current_services
|
||||
to_replace = {}
|
||||
|
||||
for row in self._sat_model:
|
||||
if row[-1]:
|
||||
sat = (row[0], row[1])
|
||||
skip = self._skip_import[sat]
|
||||
for s in filter(lambda srv: srv.fav_id not in skip, self._sat_services.get(sat[0], ())):
|
||||
if replace_existing and s.fav_id in self._ids:
|
||||
current_services[s.fav_id] = s
|
||||
to_replace[s.fav_id] = s
|
||||
elif s.fav_id not in self._ids:
|
||||
services.append(s)
|
||||
|
||||
self._append((), services)
|
||||
if to_replace:
|
||||
self._app.emit("services_update", to_replace)
|
||||
|
||||
self._dialog_window.destroy()
|
||||
|
||||
@run_idle
|
||||
def on_cursor_changed(self, view):
|
||||
if not self._info_check_button.get_active():
|
||||
return
|
||||
|
||||
self._services_model.clear()
|
||||
self._service_info_label.set_text("")
|
||||
model, paths = view.get_selection().get_selected_rows()
|
||||
if not paths:
|
||||
return
|
||||
|
||||
bq_services = self._bq_services.get(model.get(model.get_iter(paths[0]), 0, 1))
|
||||
if self._page is Page.SERVICES:
|
||||
self._current_bq = model.get(model.get_iter(paths[0]), 0, 1)
|
||||
self.update_bq_services()
|
||||
else:
|
||||
self._current_sat = model.get(model.get_iter(paths[0]), 0, 1)
|
||||
self.update_sat_services()
|
||||
|
||||
self._services_count_label.set_text(str(len(self._services_model)))
|
||||
|
||||
def update_bq_services(self):
|
||||
bq_services = self._bq_services.get(self._current_bq)
|
||||
skip = self._skip_import[self._current_bq]
|
||||
|
||||
for bq_srv in bq_services:
|
||||
if bq_srv.type is BqServiceType.DEFAULT:
|
||||
srv = self._services.get(bq_srv.data, None)
|
||||
if srv:
|
||||
self._services_model.append((srv.service, srv.service_type))
|
||||
bg = self._existing_srv_background if srv.fav_id in self._ids else None
|
||||
self._services_model.append((srv.service, srv.service_type, srv.fav_id not in skip, bg, srv.fav_id))
|
||||
else:
|
||||
self._services_model.append((bq_srv.name, bq_srv.type.value))
|
||||
bg = self._existing_srv_background if bq_srv.data in self._ids else None
|
||||
self._services_model.append((bq_srv.name, bq_srv.type.value, bq_srv.data not in skip, bg, bq_srv.data))
|
||||
|
||||
def on_selected_toggled(self, toggle, path):
|
||||
self._main_model.set_value(self._main_model.get_iter(path), 2, not toggle.get_active())
|
||||
def update_sat_services(self):
|
||||
sat_services = self._sat_services.get(self._current_sat[0])
|
||||
skip = self._skip_import[self._current_sat]
|
||||
for srv in sat_services:
|
||||
bg = self._existing_srv_background if srv.fav_id in self._ids else None
|
||||
self._services_model.append((srv.service, srv.service_type, srv.fav_id not in skip, bg, srv.fav_id))
|
||||
|
||||
def on_service_changed(self, view):
|
||||
path, column = view.get_cursor()
|
||||
if path:
|
||||
row = self._services_model[path][:]
|
||||
if row[1] == "IPTV":
|
||||
ref, url = get_iptv_data(row[-1])
|
||||
ref = f"{get_message('Service reference')}: {ref}"
|
||||
info = f"{get_message('Name')}: {row[0]}\n{ref}\nURL: {url}"
|
||||
self._service_info_label.set_text(info)
|
||||
else:
|
||||
srv = self._services.get(row[-1], None)
|
||||
self._service_info_label.set_text(self._app.get_hint_for_fav_list(srv) if srv else "")
|
||||
|
||||
def on_bq_selected_toggled(self, toggle, path):
|
||||
self._bq_model.set_value(self._bq_model.get_iter(path), 2, not toggle.get_active())
|
||||
|
||||
def on_sat_selected_toggled(self, toggle, path):
|
||||
self._sat_model.set_value(self._sat_model.get_iter(path), 2, not toggle.get_active())
|
||||
|
||||
def on_service_selected_toggled(self, toggle, path):
|
||||
self._services_model.set_value(self._services_model.get_iter(path), 2, not toggle.get_active())
|
||||
|
||||
def on_services_model_changed(self, model, path, itr):
|
||||
row = model[itr][:]
|
||||
fav_id = row[-1]
|
||||
skip = self._skip_import[self._current_bq if self._page is Page.SERVICES else self._current_sat]
|
||||
if row[2]:
|
||||
if fav_id in skip:
|
||||
skip.remove(fav_id)
|
||||
else:
|
||||
skip.add(fav_id)
|
||||
|
||||
@run_idle
|
||||
def show_info_message(self, text, message_type):
|
||||
@@ -249,10 +382,33 @@ class ImportDialog:
|
||||
def update_selection(self, view, select):
|
||||
view.get_model().foreach(lambda mod, path, itr: mod.set_value(itr, 2, select))
|
||||
|
||||
def on_sat_view_realize(self, view):
|
||||
if not self._services:
|
||||
return True
|
||||
|
||||
for srv in self._services.values():
|
||||
self._sat_services[srv.pos].append(srv)
|
||||
|
||||
list(map(lambda s: self._sat_model.append((s, None, True)), self._sat_services))
|
||||
self._sat_count_label.set_text(str(len(self._sat_model)))
|
||||
|
||||
def on_services_view_realize(self, view):
|
||||
if self._settings.use_colors:
|
||||
background = Gdk.RGBA()
|
||||
self._existing_srv_background = background if background.parse(self._settings.extra_color) else None
|
||||
self._service_exists_frame.modify_bg(Gtk.StateType.NORMAL, background.to_color())
|
||||
|
||||
def on_resize(self, window):
|
||||
if self._settings:
|
||||
self._settings.add("import_dialog_window_size", window.get_size())
|
||||
|
||||
def on_main_paned_realize(self, paned):
|
||||
width = paned.get_allocated_width()
|
||||
paned.set_position(width * 0.35)
|
||||
|
||||
def on_visible_page(self, stack, param):
|
||||
self._page = Page(stack.get_visible_child_name())
|
||||
|
||||
def on_key_press(self, view, event):
|
||||
""" Handling keystrokes """
|
||||
key_code = event.hardware_keycode
|
||||
@@ -261,10 +417,11 @@ class ImportDialog:
|
||||
key = KeyboardKey(key_code)
|
||||
|
||||
if key is KeyboardKey.SPACE:
|
||||
model = view.get_model()
|
||||
path, column = view.get_cursor()
|
||||
itr = self._main_model.get_iter(path)
|
||||
selected = self._main_model.get_value(itr, 2)
|
||||
self._main_model.set_value(itr, 2, not selected)
|
||||
itr = model.get_iter(path)
|
||||
selected = model.get_value(itr, 2)
|
||||
model.set_value(itr, 2, not selected)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -3,7 +3,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
|
||||
@@ -27,11 +27,11 @@ Author: Dmitriy Yefremov
|
||||
|
||||
-->
|
||||
<interface>
|
||||
<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 for GNU/Linux. -->
|
||||
<!-- interface-copyright 2018-2022 Dmitriy Yefremov -->
|
||||
<!-- interface-copyright 2018-2023 Dmitriy Yefremov -->
|
||||
<!-- interface-authors Dmitriy Yefremov -->
|
||||
<object class="GtkImage" id="remove_selection_image">
|
||||
<property name="visible">True</property>
|
||||
@@ -1435,7 +1435,7 @@ Author: Dmitriy Yefremov
|
||||
<property name="image">yt_receive_image</property>
|
||||
<property name="always_show_image">True</property>
|
||||
<signal name="clicked" handler="on_receive" swapped="no"/>
|
||||
<accelerator key="d" signal="clicked"/>
|
||||
<accelerator key="d" signal="clicked" modifiers="Primary"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
@@ -1456,7 +1456,7 @@ Author: Dmitriy Yefremov
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButtonBox" id="yt_impotr_box">
|
||||
<object class="GtkButtonBox" id="yt_import_box">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="layout_style">expand</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
|
||||
@@ -40,12 +40,13 @@ from gi.repository import GLib, Gio, GdkPixbuf
|
||||
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)
|
||||
from app.settings import SettingsType
|
||||
parse_m3u, PICON_FORMAT)
|
||||
from app.settings import SettingsType, IS_DARWIN
|
||||
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)
|
||||
from app.ui.uicommons import (Gtk, Gdk, UI_RESOURCES_PATH, IPTV_ICON, Column, KeyboardKey, get_yt_icon,
|
||||
IS_GNOME_SESSION, HeaderBar)
|
||||
|
||||
_DIGIT_ENTRY_NAME = "digit-entry"
|
||||
_ENIGMA2_REFERENCE = "{}:{}:{}:{:X}:{:X}:{:X}:{:X}:0:0:0"
|
||||
@@ -328,7 +329,7 @@ class IptvDialog:
|
||||
old_srv = services.pop(self._current_srv.fav_id)
|
||||
new_service = old_srv._replace(service=name, fav_id=fav_id, picon_id=picon_id)
|
||||
services[fav_id] = new_service
|
||||
self._app.emit("iptv-service-edited", (old_srv, new_service))
|
||||
self._app.emit("iptv-service-edited", {self._current_srv.fav_id: (old_srv, new_service)})
|
||||
else:
|
||||
aggr = [None] * 8
|
||||
s_type = BqServiceType.IPTV.name
|
||||
@@ -606,6 +607,7 @@ class IptvListConfigurationDialog(IptvListDialog):
|
||||
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}"
|
||||
namespace = "0" if namespace_default else f"{int(self._list_namespace_entry.get_text()):X}"
|
||||
params = [int(el.get_text()) for el in self._digit_elems[2:]]
|
||||
|
||||
for index, row in enumerate(self._rows):
|
||||
fav_id = row[Column.FAV_ID]
|
||||
@@ -618,13 +620,16 @@ class IptvListConfigurationDialog(IptvListDialog):
|
||||
data[0], data[1], data[2], data[4], data[5], data[6] = st_type, s_id, srv_type, tid, nid, namespace
|
||||
|
||||
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)
|
||||
data = ":".join(data)
|
||||
new_fav_id = f"{data}{sep}{desc}"
|
||||
row[Column.FAV_ID] = new_fav_id
|
||||
srv = self._services.pop(fav_id, None)
|
||||
|
||||
if srv:
|
||||
self._services[new_fav_id] = srv._replace(fav_id=new_fav_id)
|
||||
self._services[new_fav_id] = srv._replace(fav_id=new_fav_id, picon_id=picon_id)
|
||||
|
||||
self._bouquet.clear()
|
||||
list(map(lambda r: self._bouquet.append(r[Column.FAV_ID]), self._fav_model))
|
||||
@@ -720,7 +725,7 @@ class M3uImportDialog(IptvListDialog):
|
||||
continue
|
||||
|
||||
params[0] = i if sid_auto else sid
|
||||
picon_id = "{}_{}_{:X}_{:X}_{:X}_{:X}_{:X}_0_0_0.png".format(st_type, s_id, s_type, *params)
|
||||
picon_id = PICON_FORMAT.format(st_type, s_id, s_type, *params)
|
||||
fav_id = get_fav_id(s.data_id, s.service, self._s_type, params, st_type, s_id, s_type)
|
||||
if s.picon:
|
||||
picons[s.picon] = picon_id
|
||||
@@ -855,7 +860,6 @@ class YtListImportDialog:
|
||||
"on_key_press": self.on_key_press,
|
||||
"on_close": self.on_close}
|
||||
|
||||
# self._main_window, self._settings, self.append_imported_services
|
||||
self.appender = app.append_imported_services
|
||||
self._settings = app.app_settings
|
||||
self._s_type = self._settings.setting_type
|
||||
@@ -887,6 +891,17 @@ class YtListImportDialog:
|
||||
self._import_button.bind_property("sensitive", self._quality_box, "sensitive")
|
||||
self._receive_button.bind_property("sensitive", self._import_button, "sensitive")
|
||||
|
||||
if IS_GNOME_SESSION or IS_DARWIN:
|
||||
header_bar = HeaderBar(title="YouTube", subtitle=get_message("Playlist import"))
|
||||
self._dialog.set_titlebar(header_bar)
|
||||
actions_box = builder.get_object("yt_actions_box")
|
||||
import_box = builder.get_object("yt_import_box")
|
||||
actions_box.remove(import_box)
|
||||
header_bar.pack_end(import_box)
|
||||
actions_box.remove(self._receive_button)
|
||||
header_bar.pack_start(self._receive_button)
|
||||
actions_box.set_visible(False)
|
||||
|
||||
window_size = self._settings.get("yt_import_dialog_size")
|
||||
if window_size:
|
||||
self._dialog.resize(*window_size)
|
||||
|
||||
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.
@@ -3,7 +3,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
|
||||
@@ -27,12 +27,12 @@ Author: Dmitriy Yefremov
|
||||
|
||||
-->
|
||||
<interface domain="demon-editor">
|
||||
<requires lib="gtk+" version="3.18"/>
|
||||
<requires lib="gtk+" version="3.22"/>
|
||||
<!-- interface-css-provider-path style.css -->
|
||||
<!-- interface-license-type mit -->
|
||||
<!-- interface-name DemonEditor -->
|
||||
<!-- interface-description Enigma2 channel and satellite list editor. -->
|
||||
<!-- interface-copyright 2018-2021 Dmitriy Yefremov -->
|
||||
<!-- interface-copyright 2018-2023 Dmitriy Yefremov -->
|
||||
<!-- interface-authors Dmitriy Yefremov -->
|
||||
<object class="GtkImage" id="alt_image">
|
||||
<property name="visible">True</property>
|
||||
@@ -106,6 +106,11 @@ Author: Dmitriy Yefremov
|
||||
<property name="can_focus">False</property>
|
||||
<property name="stock">gtk-clear</property>
|
||||
</object>
|
||||
<object class="GtkImage" id="clear_new_flag_image">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="icon_name">edit-undo</property>
|
||||
</object>
|
||||
<object class="GtkImage" id="copy_image">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
@@ -255,10 +260,7 @@ Author: Dmitriy Yefremov
|
||||
<property name="width_request">170</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="margin_left">5</property>
|
||||
<property name="margin_right">5</property>
|
||||
<property name="margin_top">5</property>
|
||||
<property name="margin_bottom">5</property>
|
||||
<property name="border_width">5</property>
|
||||
<property name="hscrollbar_policy">never</property>
|
||||
<property name="max_content_height">350</property>
|
||||
<property name="propagate_natural_height">True</property>
|
||||
@@ -323,10 +325,7 @@ Author: Dmitriy Yefremov
|
||||
<property name="width_request">135</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="margin_top">5</property>
|
||||
<property name="margin_bottom">5</property>
|
||||
<property name="border_width">5</property>
|
||||
<property name="hscrollbar_policy">never</property>
|
||||
<property name="max_content_height">350</property>
|
||||
<property name="propagate_natural_height">True</property>
|
||||
@@ -406,39 +405,49 @@ Author: Dmitriy Yefremov
|
||||
<object class="GtkPopover" id="filter_type_popover">
|
||||
<property name="can_focus">False</property>
|
||||
<child>
|
||||
<object class="GtkTreeView" id="filter_type_view">
|
||||
<property name="width_request">135</property>
|
||||
<object class="GtkBox" id="filter_type_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="margin_bottom">5</property>
|
||||
<property name="model">filter_types_list_store</property>
|
||||
<property name="headers_visible">False</property>
|
||||
<property name="enable_search">False</property>
|
||||
<child internal-child="selection">
|
||||
<object class="GtkTreeSelection"/>
|
||||
</child>
|
||||
<property name="border_width">5</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<object class="GtkTreeViewColumn" id="fiter_type_column">
|
||||
<property name="title" translatable="yes">Type</property>
|
||||
<child>
|
||||
<object class="GtkCellRendererText" id="filter_type_renderer_text"/>
|
||||
<attributes>
|
||||
<attribute name="text">0</attribute>
|
||||
</attributes>
|
||||
<object class="GtkTreeView" id="filter_type_view">
|
||||
<property name="width_request">135</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="border_width">5</property>
|
||||
<property name="model">filter_types_list_store</property>
|
||||
<property name="headers_visible">False</property>
|
||||
<property name="enable_search">False</property>
|
||||
<child internal-child="selection">
|
||||
<object class="GtkTreeSelection"/>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkCellRendererToggle" id="filter_type_renderer_toggle">
|
||||
<property name="xalign">0.98000001907348633</property>
|
||||
<signal name="toggled" handler="on_filter_type_toggled" swapped="no"/>
|
||||
<object class="GtkTreeViewColumn" id="fiter_type_column">
|
||||
<property name="title" translatable="yes">Type</property>
|
||||
<child>
|
||||
<object class="GtkCellRendererText" id="filter_type_renderer_text"/>
|
||||
<attributes>
|
||||
<attribute name="text">0</attribute>
|
||||
</attributes>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkCellRendererToggle" id="filter_type_renderer_toggle">
|
||||
<property name="xalign">0.98000001907348633</property>
|
||||
<signal name="toggled" handler="on_filter_type_toggled" swapped="no"/>
|
||||
</object>
|
||||
<attributes>
|
||||
<attribute name="active">1</attribute>
|
||||
</attributes>
|
||||
</child>
|
||||
</object>
|
||||
<attributes>
|
||||
<attribute name="active">1</attribute>
|
||||
</attributes>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
@@ -1402,6 +1411,15 @@ Author: Dmitriy Yefremov
|
||||
<signal name="activate" handler="on_services_clear_marked" swapped="no"/>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkImageMenuItem" id="services_clear_new_flag_item">
|
||||
<property name="label" translatable="yes">Clear "New" flag</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="image">clear_new_flag_image</property>
|
||||
<property name="use_stock">False</property>
|
||||
<signal name="activate" handler="on_services_clear_new_marked" swapped="no"/>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkSeparatorMenuItem" id="services_separatormenuitem_1">
|
||||
<property name="visible">True</property>
|
||||
@@ -1633,7 +1651,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.0.0 Beta</property>
|
||||
<property name="label">3.3.0 Beta</property>
|
||||
<attributes>
|
||||
<attribute name="weight" value="bold"/>
|
||||
</attributes>
|
||||
@@ -1759,12 +1777,12 @@ Author: Dmitriy Yefremov
|
||||
<child type="center">
|
||||
<object class="GtkButtonBox" id="services_button_box">
|
||||
<property name="visible">True</property>
|
||||
<property name="name">header-stack-switcher</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="layout_style">expand</property>
|
||||
<child>
|
||||
<object class="GtkRadioButton" id="dvb_button">
|
||||
<property name="label" translatable="yes">DVB</property>
|
||||
<property name="name">stack-switch-button</property>
|
||||
<property name="width_request">80</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
@@ -1782,7 +1800,6 @@ Author: Dmitriy Yefremov
|
||||
<child>
|
||||
<object class="GtkRadioButton" id="iptv_button">
|
||||
<property name="label" translatable="yes">IPTV</property>
|
||||
<property name="name">stack-switch-button</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="receives_default">False</property>
|
||||
@@ -2179,7 +2196,7 @@ Author: Dmitriy Yefremov
|
||||
<object class="GtkCellRendererText" id="service_renderer">
|
||||
<property name="xpad">5</property>
|
||||
<property name="ellipsize">end</property>
|
||||
<property name="width_chars">25</property>
|
||||
<property name="width_chars">50</property>
|
||||
</object>
|
||||
<attributes>
|
||||
<attribute name="cell-background-rgba">21</attribute>
|
||||
@@ -3409,7 +3426,6 @@ Author: Dmitriy Yefremov
|
||||
<property name="model">fav_list_store</property>
|
||||
<property name="enable_search">False</property>
|
||||
<property name="search_column">2</property>
|
||||
<property name="fixed_height_mode">True</property>
|
||||
<property name="rubber_banding">True</property>
|
||||
<property name="enable_grid_lines">both</property>
|
||||
<property name="tooltip_column">9</property>
|
||||
@@ -4155,7 +4171,7 @@ Author: Dmitriy Yefremov
|
||||
</object>
|
||||
<packing>
|
||||
<property name="resize">True</property>
|
||||
<property name="shrink">False</property>
|
||||
<property name="shrink">True</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
@@ -4508,6 +4524,27 @@ Author: Dmitriy Yefremov
|
||||
<property name="can_focus">False</property>
|
||||
<property name="homogeneous">True</property>
|
||||
<property name="layout_style">expand</property>
|
||||
<child>
|
||||
<object class="GtkButton" id="open_tool_button">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="tooltip_text" translatable="yes">Open</property>
|
||||
<property name="action_name">app.on_data_open</property>
|
||||
<child>
|
||||
<object class="GtkImage" id="open_image">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="icon_name">document-open-symbolic</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="receive_button">
|
||||
<property name="visible">True</property>
|
||||
@@ -4529,7 +4566,7 @@ Author: Dmitriy Yefremov
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
@@ -4552,7 +4589,7 @@ Author: Dmitriy Yefremov
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
@@ -4572,7 +4609,7 @@ Author: Dmitriy Yefremov
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">2</property>
|
||||
<property name="position">3</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
|
||||
287
app/ui/main.py
287
app/ui/main.py
@@ -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
|
||||
@@ -65,13 +65,13 @@ from .imports import ImportDialog, import_bouquet
|
||||
from .iptv import IptvDialog, SearchUnavailableDialog, IptvListConfigurationDialog, YtListImportDialog, M3uImportDialog
|
||||
from .main_helper import *
|
||||
from .picons import PiconManager
|
||||
from .xml.dialogs import ServicesUpdateDialog
|
||||
from .xml.edit import SatellitesTool
|
||||
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)
|
||||
FavClickMode, MOD_MASK, APP_FONT, Page, IS_GNOME_SESSION, HeaderBar)
|
||||
from .xml.dialogs import ServicesUpdateDialog
|
||||
from .xml.edit import SatellitesTool
|
||||
|
||||
|
||||
class Application(Gtk.Application):
|
||||
@@ -178,6 +178,7 @@ class Application(Gtk.Application):
|
||||
"on_mark_duplicates": self.on_mark_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,
|
||||
"on_filter_changed": self.on_filter_changed,
|
||||
"on_iptv_filter_changed": self.on_iptv_filter_changed,
|
||||
"on_filter_type_toggled": self.on_filter_type_toggled,
|
||||
@@ -228,7 +229,6 @@ class Application(Gtk.Application):
|
||||
# Clearing only after the insertion!
|
||||
self._rows_buffer = []
|
||||
self._bouquets_buffer = []
|
||||
self._picons_buffer = []
|
||||
self._services = {}
|
||||
self._bouquets = {}
|
||||
self._bq_file = {}
|
||||
@@ -240,11 +240,13 @@ class Application(Gtk.Application):
|
||||
self._in_bouquets = set()
|
||||
# For bouquets with different names of services in bouquet and main list
|
||||
self._extra_bouquets = {}
|
||||
self._picons = DefaultDict(self.get_picon)
|
||||
self._blacklist = set()
|
||||
self._current_bq_name = None
|
||||
self._bq_selected = "" # Current selected bouquet
|
||||
self._select_enabled = True # Multiple selection
|
||||
# Picons
|
||||
self._picons_buffer = []
|
||||
self._picons = DefaultDict(self.get_picon)
|
||||
# Current satellite positions in the services list
|
||||
self._sat_positions = set()
|
||||
self._service_types = set()
|
||||
@@ -306,6 +308,8 @@ class Application(Gtk.Application):
|
||||
GObject.TYPE_PYOBJECT, (GObject.TYPE_PYOBJECT,))
|
||||
GObject.signal_new("epg-dat-downloaded", self, GObject.SIGNAL_RUN_LAST,
|
||||
GObject.TYPE_PYOBJECT, (GObject.TYPE_PYOBJECT,))
|
||||
GObject.signal_new("services-update", self, GObject.SIGNAL_RUN_LAST,
|
||||
GObject.TYPE_PYOBJECT, (GObject.TYPE_PYOBJECT,))
|
||||
GObject.signal_new("iptv-service-edited", self, GObject.SIGNAL_RUN_LAST,
|
||||
GObject.TYPE_PYOBJECT, (GObject.TYPE_PYOBJECT,))
|
||||
GObject.signal_new("iptv-service-added", self, GObject.SIGNAL_RUN_LAST,
|
||||
@@ -328,6 +332,8 @@ class Application(Gtk.Application):
|
||||
GObject.TYPE_PYOBJECT, (GObject.TYPE_PYOBJECT,)),
|
||||
GObject.signal_new("list-font-changed", self, GObject.SIGNAL_RUN_LAST,
|
||||
GObject.TYPE_PYOBJECT, (GObject.TYPE_PYOBJECT,))
|
||||
GObject.signal_new("clipboard-changed", self, GObject.SIGNAL_RUN_LAST,
|
||||
GObject.TYPE_PYOBJECT, (GObject.TYPE_PYOBJECT,))
|
||||
|
||||
builder = get_builder(UI_RESOURCES_PATH + "main.glade", handlers)
|
||||
self._main_window = builder.get_object("main_window")
|
||||
@@ -393,7 +399,9 @@ class Application(Gtk.Application):
|
||||
self._clipboard = Gtk.Clipboard.get(Gdk.SELECTION_CLIPBOARD)
|
||||
ref_item = builder.get_object("fav_assign_ref_popup_item")
|
||||
self.bind_property("is_enigma", ref_item, "visible")
|
||||
self._clipboard.connect("owner-change", lambda c, o: ref_item.set_sensitive(c.wait_is_text_available()))
|
||||
# We use a custom event for observe clipboard state.
|
||||
# "owner-change" -> https://gitlab.gnome.org/GNOME/gtk/-/issues/1757
|
||||
self.connect("clipboard-changed", lambda a, o: ref_item.set_sensitive(o))
|
||||
# Wait dialog
|
||||
self._wait_dialog = WaitDialog(self._main_window)
|
||||
# Filter
|
||||
@@ -412,7 +420,6 @@ class Application(Gtk.Application):
|
||||
self._filter_only_free_button = builder.get_object("filter_only_free_button")
|
||||
self._filter_not_in_bq_button = builder.get_object("filter_not_in_bq_button")
|
||||
self._services_load_spinner.bind_property("active", self._filter_services_button, "sensitive", 4)
|
||||
self._services_load_spinner.bind_property("active", self._filter_box, "sensitive", 4)
|
||||
self._filter_iptv_services_button = builder.get_object("filter_iptv_services_button")
|
||||
# Search.
|
||||
services_search_provider = SearchProvider(self._services_view,
|
||||
@@ -457,6 +464,8 @@ class Application(Gtk.Application):
|
||||
# Lock, Hide.
|
||||
self.bind_property("is-enigma", self._tool_elements.get(self._LOCK_HIDE_ELEMENTS[0]), "visible")
|
||||
self.bind_property("is-enigma", self._tool_elements.get(self._LOCK_HIDE_ELEMENTS[1]), "visible", 4)
|
||||
# Clear "New" menu item
|
||||
self.bind_property("is-enigma", builder.get_object("services_clear_new_flag_item"), "visible")
|
||||
# Sub-bouquets menu item.
|
||||
self.bind_property("is-enigma", builder.get_object("bouquets_new_sub_popup_item"), "visible")
|
||||
# Export bouquet to m3u menu items.
|
||||
@@ -481,6 +490,7 @@ class Application(Gtk.Application):
|
||||
self._logs_box = builder.get_object("logs_box")
|
||||
self._logs_box.pack_start(LogsClient(self), True, True, 0)
|
||||
self._bottom_paned = builder.get_object("bottom_paned")
|
||||
self.connect("services-update", self.on_services_update)
|
||||
# Send/Receive.
|
||||
self.connect("data-receive", self.on_download)
|
||||
self.connect("data-send", self.on_upload)
|
||||
@@ -496,9 +506,11 @@ 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:
|
||||
header_bar = Gtk.HeaderBar(visible=True, show_close_button=True)
|
||||
header_bar.pack_start(builder.get_object("file_header_button"))
|
||||
if IS_GNOME_SESSION or IS_DARWIN:
|
||||
header_bar = HeaderBar()
|
||||
if not IS_DARWIN:
|
||||
header_bar.pack_start(builder.get_object("file_header_button"))
|
||||
|
||||
header_bar.pack_start(profile_box)
|
||||
header_bar.pack_start(toolbar_box)
|
||||
header_bar.set_custom_title(builder.get_object("stack_switcher"))
|
||||
@@ -560,12 +572,16 @@ class Application(Gtk.Application):
|
||||
self._epg_menu_button = builder.get_object("epg_menu_button")
|
||||
self._epg_menu_button.connect("realize", lambda b: b.set_popover(EpgSettingsPopover(self)))
|
||||
self.bind_property("is_enigma", self._epg_menu_button, "sensitive")
|
||||
self._epg_start_time_fmt = "%a, %H:%M"
|
||||
self._epg_end_time_fmt = "%H:%M"
|
||||
# Hiding for Neutrino.
|
||||
self.bind_property("is_enigma", builder.get_object("services_button_box"), "visible")
|
||||
# Setting the last size of the window if it was saved.
|
||||
main_window_size = self._settings.get("window_size")
|
||||
if main_window_size:
|
||||
self._main_window.resize(*main_window_size)
|
||||
# Layout.
|
||||
self.init_layout()
|
||||
# Style.
|
||||
style_provider = Gtk.CssProvider()
|
||||
style_provider.load_from_path(UI_RESOURCES_PATH + "style.css")
|
||||
@@ -610,7 +626,6 @@ class Application(Gtk.Application):
|
||||
|
||||
self.init_actions()
|
||||
self.set_accels()
|
||||
self.init_layout()
|
||||
|
||||
self.init_drag_and_drop()
|
||||
self.init_appearance()
|
||||
@@ -689,7 +704,8 @@ class Application(Gtk.Application):
|
||||
self.bind_property("is-receive-data-enabled", sa, "enabled")
|
||||
sa = self.set_action("on_send", self.on_send)
|
||||
self.bind_property("is-send-data-enabled", sa, "enabled")
|
||||
self.set_action("on_data_open", self.on_data_open)
|
||||
sa = self.set_action("on_data_open", self.on_data_open)
|
||||
self.bind_property("is-send-data-enabled", sa, "enabled")
|
||||
self.set_action("on_archive_open", self.on_archive_open)
|
||||
# Edit.
|
||||
self.set_action("on_edit", self.on_edit)
|
||||
@@ -861,12 +877,12 @@ class Application(Gtk.Application):
|
||||
def init_layout(self):
|
||||
""" Initializes an alternate layout, if enabled. """
|
||||
if self._settings.alternate_layout:
|
||||
self._main_paned.pack2(self._player_box, True, False)
|
||||
self._main_paned.pack2(self._player_box, True, True)
|
||||
self.reverse_main_elements(True)
|
||||
else:
|
||||
self._main_paned.remove(self._data_paned)
|
||||
self._main_paned.pack1(self._player_box, True, False)
|
||||
self._main_paned.pack2(self._data_paned, True, False)
|
||||
self._main_paned.pack1(self._player_box, True, True)
|
||||
self._main_paned.pack2(self._data_paned, True, True)
|
||||
|
||||
def init_bq_position(self):
|
||||
self._fav_paned.remove(self._fav_frame)
|
||||
@@ -883,7 +899,7 @@ class Application(Gtk.Application):
|
||||
""" Initializes starting positions of main paned widgets. """
|
||||
width = paned.get_allocated_width()
|
||||
main_position = self._settings.get("data_paned_position", width * 0.5)
|
||||
fav_position = self._settings.get("fav_paned_position", width * 0.27)
|
||||
fav_position = self._settings.get("fav_paned_position", width * 0.25)
|
||||
paned.set_position(main_position)
|
||||
self._fav_paned.set_position(fav_position)
|
||||
|
||||
@@ -1088,17 +1104,21 @@ class Application(Gtk.Application):
|
||||
renderer.set_property("text", f"{StreamType(f_data[0].strip() if f_data else '0').name}")
|
||||
|
||||
def iptv_picon_data_func(self, column, renderer, model, itr, data):
|
||||
renderer.set_property("pixbuf", self._picons.get(model.get_value(itr, Column.IPTV_PICON_ID)))
|
||||
picon_id, name = model.get_value(itr, Column.IPTV_PICON_ID), model.get_value(itr, Column.IPTV_SERVICE)
|
||||
renderer.set_property("pixbuf", self.get_picon_pixbuf(picon_id, name))
|
||||
|
||||
def picon_data_func(self, column, renderer, model, itr, data):
|
||||
renderer.set_property("pixbuf", self._picons.get(model.get_value(itr, Column.SRV_PICON_ID)))
|
||||
picon = self._picons.get(model.get_value(itr, Column.SRV_PICON_ID))
|
||||
if not picon:
|
||||
picon = self._picons.get(get_picon_file_name(model.get_value(itr, Column.SRV_SERVICE)))
|
||||
renderer.set_property("pixbuf", picon)
|
||||
|
||||
def fav_picon_data_func(self, column, renderer, model, itr, data):
|
||||
srv = self._services.get(model.get_value(itr, Column.FAV_ID), None)
|
||||
if not srv:
|
||||
return True
|
||||
|
||||
picon = self._picons.get(srv.picon_id, None)
|
||||
picon = self.get_picon_pixbuf(srv.picon_id, srv.service)
|
||||
# Alternatives.
|
||||
if srv.service_type == BqServiceType.ALT.name:
|
||||
alt_servs = srv.transponder
|
||||
@@ -1109,6 +1129,21 @@ class Application(Gtk.Application):
|
||||
|
||||
renderer.set_property("pixbuf", picon)
|
||||
|
||||
def get_picon_pixbuf(self, picon_id, srv_name):
|
||||
""" Returns a picon pixbuf by id or service name.
|
||||
|
||||
Used for models with IPTV services.
|
||||
"""
|
||||
picon = self._picons.get(picon_id)
|
||||
# Trying to get a satellite service piсon.
|
||||
if not picon and picon_id:
|
||||
picon = self._picons.get(picon_id.replace(picon_id[:picon_id.find("_")], "1", 1))
|
||||
# Getting picon by service name.
|
||||
if not picon:
|
||||
picon = self._picons.get(get_picon_file_name(srv_name))
|
||||
|
||||
return picon
|
||||
|
||||
def fav_service_data_func(self, column, renderer, model, itr, data):
|
||||
if self._display_epg and self._s_type is SettingsType.ENIGMA_2:
|
||||
srv_name = model.get_value(itr, Column.FAV_SERVICE)
|
||||
@@ -1117,10 +1152,16 @@ class Application(Gtk.Application):
|
||||
|
||||
event = self._epg_cache.get_current_event(srv_name)
|
||||
if event:
|
||||
if event.start:
|
||||
start = datetime.fromtimestamp(event.start).strftime(self._epg_start_time_fmt)
|
||||
end = datetime.fromtimestamp(event.end).strftime(self._epg_end_time_fmt)
|
||||
sep = "-"
|
||||
else:
|
||||
start, end, sep = "", "", ""
|
||||
# https://docs.gtk.org/Pango/pango_markup.html
|
||||
renderer.set_property("markup", (f'{escape(srv_name)}\n\n'
|
||||
f'<span size="small" weight="bold">{escape(event.title)}</span>\n'
|
||||
f'<span size="small" style="italic">{event.time}</span>'))
|
||||
f'<span size="small" style="italic">{start} {sep} {end}</span>'))
|
||||
return False
|
||||
return True
|
||||
|
||||
@@ -1146,6 +1187,9 @@ class Application(Gtk.Application):
|
||||
self.on_copy(view, target=ViewTarget.BOUQUET)
|
||||
|
||||
def on_copy(self, view, target):
|
||||
if not self._settings.unlimited_copy_buffer:
|
||||
self._bouquets_buffer.clear() if target is ViewTarget.BOUQUET else self._rows_buffer.clear()
|
||||
|
||||
model, paths = view.get_selection().get_selected_rows()
|
||||
|
||||
if target is ViewTarget.FAV:
|
||||
@@ -1165,7 +1209,7 @@ class Application(Gtk.Application):
|
||||
|
||||
def on_reference_copy(self, view):
|
||||
""" Copying picon id to clipboard. """
|
||||
copy_reference(self.get_target_view(view), view, self._services, self._clipboard, self._main_window)
|
||||
copy_reference(view, self)
|
||||
|
||||
def on_fav_cut(self, view):
|
||||
self.on_cut(view, ViewTarget.FAV)
|
||||
@@ -1174,6 +1218,9 @@ class Application(Gtk.Application):
|
||||
self.on_cut(view, ViewTarget.BOUQUET)
|
||||
|
||||
def on_cut(self, view, target=None):
|
||||
if not self._settings.unlimited_copy_buffer:
|
||||
self._bouquets_buffer.clear() if target is ViewTarget.BOUQUET else self._rows_buffer.clear()
|
||||
|
||||
if target is ViewTarget.FAV:
|
||||
for row in tuple(self.on_delete(view)):
|
||||
self._rows_buffer.append(row)
|
||||
@@ -1241,6 +1288,20 @@ class Application(Gtk.Application):
|
||||
self._bouquets_buffer.clear()
|
||||
self.update_bouquets_type()
|
||||
|
||||
def on_services_update(self, app, services):
|
||||
""" Updates services in the main model. """
|
||||
for r in self._fav_model:
|
||||
fav_id = r[Column.FAV_ID]
|
||||
if fav_id in services:
|
||||
service = services[fav_id]
|
||||
r[Column.FAV_SERVICE] = service.service
|
||||
|
||||
for r in self._services_model:
|
||||
fav_id = r[Column.SRV_FAV_ID]
|
||||
if fav_id in services:
|
||||
service = services[fav_id]
|
||||
r[Column.SRV_SERVICE] = service.service
|
||||
|
||||
# ***************** Deletion ********************* #
|
||||
|
||||
def on_delete(self, view):
|
||||
@@ -1532,7 +1593,7 @@ class Application(Gtk.Application):
|
||||
|
||||
for s_row, row in zip(sorted(map(
|
||||
lambda r: r[:], rows),
|
||||
key=lambda r: r[c_num] or nv if c_num != Column.FAV_POS else self.get_pos_num(r[c_num]),
|
||||
key=lambda r: r[c_num] or nv if c_num != Column.FAV_POS else get_pos_num(r[c_num]),
|
||||
reverse=rev), rows):
|
||||
self._fav_model.set(row.iter, columns, s_row)
|
||||
bq[index] = s_row[Column.FAV_ID]
|
||||
@@ -1548,18 +1609,7 @@ class Application(Gtk.Application):
|
||||
|
||||
def position_sort_func(self, model, iter1, iter2, column):
|
||||
""" Custom sort function for position column. """
|
||||
return self.get_pos_num(model.get_value(iter1, column)) - self.get_pos_num(model.get_value(iter2, column))
|
||||
|
||||
def get_pos_num(self, pos):
|
||||
""" Returns num [float] representation of satellite position. """
|
||||
if not pos:
|
||||
return -183.0
|
||||
|
||||
if len(pos) > 1:
|
||||
m = -1 if pos[-1] == "W" else 1
|
||||
return float(pos[:-1]) * m
|
||||
|
||||
return -181.0 if pos == "T" else -182.0
|
||||
return get_pos_num(model.get_value(iter1, column)) - get_pos_num(model.get_value(iter2, column))
|
||||
|
||||
# ********************* Hints ************************* #
|
||||
|
||||
@@ -1623,8 +1673,7 @@ class Application(Gtk.Application):
|
||||
path, pos = result
|
||||
srv = self._services.get(view.get_model()[path][Column.IPTV_FAV_ID], None)
|
||||
if srv and srv.picon_id:
|
||||
tooltip.set_icon(get_picon_pixbuf(self._settings.profile_picons_path + srv.picon_id,
|
||||
size=self._settings.tooltip_logo_size))
|
||||
tooltip.set_icon(self.get_tooltip_picon(srv))
|
||||
fav_id = srv.fav_id
|
||||
names = (b[:b.rindex(":")] for b, ids in self._bouquets.items() if fav_id in ids)
|
||||
text = f"{get_message('Name')}: {srv.service}\n{get_message('Bouquets')}: {', '.join(names)}"
|
||||
@@ -1640,8 +1689,7 @@ class Application(Gtk.Application):
|
||||
target_column = Column.FAV_ID if target is ViewTarget.FAV else Column.SRV_FAV_ID
|
||||
srv = self._services.get(model[path][target_column], None)
|
||||
if srv and srv.picon_id:
|
||||
tooltip.set_icon(get_picon_pixbuf(self._settings.profile_picons_path + srv.picon_id,
|
||||
size=self._settings.tooltip_logo_size))
|
||||
tooltip.set_icon(self.get_tooltip_picon(srv))
|
||||
txt = self.get_hint_for_fav_list(srv) if target is ViewTarget.FAV else self.get_hint_for_srv_list(srv)
|
||||
tooltip.set_text(txt)
|
||||
view.set_tooltip_row(tooltip, path)
|
||||
@@ -1652,7 +1700,7 @@ class Application(Gtk.Application):
|
||||
""" Returns detailed info about service as formatted string for using as hint. """
|
||||
header, ref = self.get_hint_header_info(srv)
|
||||
|
||||
if srv.service_type == "IPTV":
|
||||
if srv.service_type == BqServiceType.IPTV.name:
|
||||
return f"{header}{ref}"
|
||||
|
||||
pol = ", {}: {},".format(get_message("Pol"), srv.pol) if srv.pol else ","
|
||||
@@ -1673,7 +1721,7 @@ class Application(Gtk.Application):
|
||||
|
||||
def get_hint_header_info(self, srv):
|
||||
header = f"{get_message('Name')}: {srv.service}\n{get_message('Type')}: {srv.service_type}\n"
|
||||
ref = f"{get_message('Service reference')}: {srv.picon_id.rstrip('.png')}"
|
||||
ref = f"{get_message('Service reference')}: {get_service_reference(srv)}"
|
||||
return header, ref
|
||||
|
||||
def get_ssid_info(self, srv):
|
||||
@@ -2040,15 +2088,20 @@ class Application(Gtk.Application):
|
||||
|
||||
@run_task
|
||||
def upload_data(self, download_type):
|
||||
try:
|
||||
profile = self._s_type
|
||||
opts = self._settings
|
||||
use_http = profile is SettingsType.ENIGMA_2 and opts.use_http
|
||||
upload_data(settings=opts, download_type=download_type, remove_unused=True, use_http=use_http)
|
||||
except Exception as e:
|
||||
msg = "Uploading data error: {}"
|
||||
log(msg.format(e), debug=self._settings.debug_mode, fmt_message=msg)
|
||||
self.show_error_message(str(e))
|
||||
opts = self._settings
|
||||
use_http = self._s_type is SettingsType.ENIGMA_2 and opts.use_http
|
||||
multiple = len(self._settings.hosts) > 1
|
||||
for host in self._settings.hosts:
|
||||
if multiple:
|
||||
log(f"##### Uploading data on [{host}] #####")
|
||||
try:
|
||||
upload_data(settings=opts, download_type=download_type, ext_host=host)
|
||||
except Exception as e:
|
||||
msg = "Uploading data error: {}"
|
||||
log(msg.format(e), debug=self._settings.debug_mode, fmt_message=msg)
|
||||
if host == self._settings.host:
|
||||
self.show_error_message(str(e))
|
||||
log(f"##### Done! #####")
|
||||
|
||||
def on_data_open(self, action=None, value=None):
|
||||
""" Opening data via "File/Open". """
|
||||
@@ -2687,6 +2740,8 @@ class Application(Gtk.Application):
|
||||
|
||||
if changed:
|
||||
self.open_data()
|
||||
if self._settings.display_epg:
|
||||
self.change_action_state("display_epg", GLib.Variant.new_boolean(self._settings.display_epg))
|
||||
self.emit("profile-changed", None)
|
||||
|
||||
def set_profile(self, active):
|
||||
@@ -2920,24 +2975,29 @@ class Application(Gtk.Application):
|
||||
log(f"Error. Service with id '{fav_id}' not found!")
|
||||
|
||||
@run_idle
|
||||
def on_iptv_service_edited(self, app, services):
|
||||
old, new = services
|
||||
fav_id = old.fav_id
|
||||
name, new_fav_id = new.service, new.fav_id
|
||||
def on_iptv_service_edited(self, app, services: dict):
|
||||
for srvs in self._bouquets.values():
|
||||
for i, s in enumerate(srvs):
|
||||
if s == fav_id:
|
||||
if s in services:
|
||||
old, new = services[s]
|
||||
srvs[i] = new.fav_id
|
||||
|
||||
for r in self._fav_model:
|
||||
if r[Column.FAV_ID] == fav_id:
|
||||
fav_id = r[Column.FAV_ID]
|
||||
if fav_id in services:
|
||||
old, new = services[fav_id]
|
||||
name, new_fav_id = new.service, new.fav_id
|
||||
r[Column.FAV_SERVICE] = name
|
||||
r[Column.FAV_ID] = new_fav_id
|
||||
|
||||
for r in self._iptv_model:
|
||||
if r[Column.IPTV_FAV_ID] == fav_id:
|
||||
fav_id = r[Column.IPTV_FAV_ID]
|
||||
if fav_id in services:
|
||||
old, new = services[fav_id]
|
||||
name, new_fav_id = new.service, new.fav_id
|
||||
ref, url = get_iptv_data(new_fav_id)
|
||||
r[Column.IPTV_SERVICE] = name
|
||||
r[Column.IPTV_PICON_ID] = new.picon_id
|
||||
r[Column.IPTV_REF] = ref
|
||||
r[Column.IPTV_URL] = url
|
||||
r[Column.IPTV_FAV_ID] = new_fav_id
|
||||
@@ -2988,25 +3048,29 @@ class Application(Gtk.Application):
|
||||
return
|
||||
|
||||
ref = self._clipboard.wait_for_text()
|
||||
if ref and re.match(r"\d+_\d+_\d+_\w+_\d+_\d+_\d+_0_0_0", ref):
|
||||
if ref and re.match(r"\d+_\d+_\w+_\w+_\w+_\w+_\w+_0_0_0", ref):
|
||||
[self.assign_reference(model, p, ref) for p in iptv_paths]
|
||||
self._clipboard.clear()
|
||||
else:
|
||||
log(f"Error parsing reference [{ref}].")
|
||||
|
||||
self.emit("clipboard-changed", self._clipboard.wait_is_text_available())
|
||||
|
||||
def assign_reference(self, model, path, ref):
|
||||
ref_data = ref.split("_")
|
||||
row = model[path]
|
||||
fav_id = row[Column.FAV_ID]
|
||||
fav_id_data = fav_id.split(":")
|
||||
fav_id_data[3:7] = ref_data[3:7]
|
||||
fav_id_data[2:7] = ref_data[2:7]
|
||||
new_fav_id = ":".join(fav_id_data)
|
||||
new_data_id = ":".join(fav_id_data[:11]).strip()
|
||||
old_srv = self._services.pop(fav_id, None)
|
||||
if old_srv:
|
||||
picon_id_data = old_srv.picon_id.split("_")
|
||||
picon_id_data[3:7] = ref_data[3:7]
|
||||
picon_id_data[2:7] = ref_data[2:7]
|
||||
new_service = old_srv._replace(data_id=new_data_id, fav_id=new_fav_id, picon_id="_".join(picon_id_data))
|
||||
self._services[new_fav_id] = new_service
|
||||
self.emit("iptv-service-edited", (old_srv, new_service))
|
||||
self.emit("iptv-service-edited", {fav_id: (old_srv, new_service)})
|
||||
|
||||
# ****************** EPG ********************** #
|
||||
|
||||
@@ -3027,8 +3091,7 @@ class Application(Gtk.Application):
|
||||
self.show_error_message("This list does not contains IPTV streams!")
|
||||
return
|
||||
|
||||
bq = self._bouquets.get(self._bq_selected)
|
||||
EpgDialog(self, bq, self._current_bq_name).show()
|
||||
EpgDialog(self, self._current_bq_name).show()
|
||||
|
||||
# ***************** Import ******************** #
|
||||
|
||||
@@ -3084,7 +3147,7 @@ class Application(Gtk.Application):
|
||||
return
|
||||
|
||||
appender = self.append_bouquet if self._s_type is SettingsType.ENIGMA_2 else self.append_bouquets
|
||||
import_bouquet(self._main_window, model, paths[0], self._settings, self._services, appender, file_path)
|
||||
import_bouquet(self, model, paths[0], appender, file_path)
|
||||
|
||||
def on_import_bouquets(self, action, value=None):
|
||||
response = show_dialog(DialogType.CHOOSER, self._main_window, settings=self._settings)
|
||||
@@ -3108,8 +3171,8 @@ class Application(Gtk.Application):
|
||||
gen = self.append_imported_data(b, s, callback)
|
||||
GLib.idle_add(lambda: next(gen, False))
|
||||
|
||||
dialog = ImportDialog(self._main_window, path, self._settings, self._services.keys(), append)
|
||||
dialog.import_data() if force else dialog.show()
|
||||
dialog = ImportDialog(self, path, append)
|
||||
dialog.import_bouquets_data() if force else dialog.show()
|
||||
|
||||
def append_imported_data(self, bouquets, services, callback=None):
|
||||
try:
|
||||
@@ -3462,7 +3525,7 @@ class Application(Gtk.Application):
|
||||
elif self._s_type is SettingsType.NEUTRINO_MP:
|
||||
# It may require some correction for cable and terrestrial channels!
|
||||
try:
|
||||
pos, freq = int(self.get_pos_num(srv.pos)) * 10, int(srv.freq)
|
||||
pos, freq = int(get_pos_num(srv.pos)) * 10, int(srv.freq)
|
||||
tid, nid, sid = int(ref[: -8], 16), int(ref[-8: -4], 16), int(srv.ssid, 16)
|
||||
except ValueError:
|
||||
log(f"Error getting reference for: {srv}")
|
||||
@@ -3664,16 +3727,7 @@ class Application(Gtk.Application):
|
||||
elif self._s_type is SettingsType.NEUTRINO_MP:
|
||||
list(map(lambda s: self._sat_positions.add(s.pos), filter(lambda s: s.pos, self._services.values())))
|
||||
|
||||
self.update_filter_sat_positions()
|
||||
|
||||
def update_filter_sat_positions(self):
|
||||
""" Updates the values for the satellite positions button model. """
|
||||
first = self._filter_sat_pos_model[self._filter_sat_pos_model.get_iter_first()][:]
|
||||
self._filter_sat_pos_model.clear()
|
||||
self._filter_sat_pos_model.append((first[0], True))
|
||||
self._sat_positions.discard(first[0])
|
||||
list(map(lambda pos: self._filter_sat_pos_model.append((pos, True)),
|
||||
sorted(self._sat_positions, key=self.get_pos_num, reverse=True)))
|
||||
update_filter_sat_positions(self._filter_sat_pos_model, self._sat_positions)
|
||||
|
||||
@run_with_delay(2)
|
||||
def on_filter_changed(self, item=None):
|
||||
@@ -3683,7 +3737,6 @@ class Application(Gtk.Application):
|
||||
|
||||
@run_with_delay(2)
|
||||
def on_iptv_filter_changed(self, item=None):
|
||||
self._iptv_filter_box.set_sensitive(False)
|
||||
self.update_iptv_filter_cache()
|
||||
self.update_iptv_filter_state()
|
||||
|
||||
@@ -3695,7 +3748,6 @@ class Application(Gtk.Application):
|
||||
@run_idle
|
||||
def update_iptv_filter_state(self):
|
||||
self._iptv_services_model_filter.refilter()
|
||||
GLib.idle_add(self._iptv_filter_box.set_sensitive, True)
|
||||
|
||||
def update_filter_cache(self):
|
||||
self._filter_cache.clear()
|
||||
@@ -3758,16 +3810,7 @@ class Application(Gtk.Application):
|
||||
self.on_filter_changed()
|
||||
|
||||
def update_filter_toggle_model(self, model, toggle, path, values_set):
|
||||
active = not toggle.get_active()
|
||||
if path == "0":
|
||||
model.foreach(lambda m, p, i: m.set_value(i, 1, active))
|
||||
else:
|
||||
model.set_value(model.get_iter(path), 1, active)
|
||||
if active:
|
||||
model.set_value(model.get_iter_first(), 1, len({r[0] for r in model if r[1]}) == len(model) - 1)
|
||||
else:
|
||||
model.set_value(model.get_iter_first(), 1, False)
|
||||
|
||||
update_toggle_model(model, path, toggle)
|
||||
values_set.clear()
|
||||
values_set.update({r[0] for r in model if r[1]})
|
||||
self.on_iptv_filter_changed() if self._iptv_button.get_active() else self.on_filter_changed()
|
||||
@@ -3965,6 +4008,54 @@ class Application(Gtk.Application):
|
||||
self._services_load_spinner.stop()
|
||||
yield True
|
||||
|
||||
def on_services_clear_new_marked(self, item):
|
||||
if self.is_data_loading():
|
||||
self.show_error_message("Data loading in progress!")
|
||||
return
|
||||
|
||||
model, paths = self._services_view.get_selection().get_selected_rows()
|
||||
if not paths:
|
||||
self.show_error_message("No selected item!")
|
||||
return
|
||||
|
||||
gen = self.clear_new_marked(model, paths)
|
||||
GLib.idle_add(lambda: next(gen, False), priority=GLib.PRIORITY_LOW)
|
||||
|
||||
def clear_new_marked(self, model, paths):
|
||||
self._services_load_spinner.start()
|
||||
|
||||
paths = get_base_paths(paths, model)
|
||||
model = get_base_model(model)
|
||||
for index, p in enumerate(paths):
|
||||
flags = model[p][Column.SRV_CAS_FLAGS]
|
||||
if flags:
|
||||
flags_data = flags.split(",")
|
||||
for i, f in enumerate(flags_data):
|
||||
if f.startswith("f:"):
|
||||
flag = Flag.parse(f)
|
||||
if Flag.is_new(flag):
|
||||
flag -= Flag.NEW.value
|
||||
if flag:
|
||||
flags_data[i] = f"f:{flag:02d}"
|
||||
else:
|
||||
flags_data.remove(f)
|
||||
|
||||
flags = ",".join(flags_data)
|
||||
model[p][Column.SRV_BACKGROUND] = None
|
||||
model[p][Column.SRV_CAS_FLAGS] = flags
|
||||
fav_id = model[p][Column.SRV_FAV_ID]
|
||||
srv = self._services.get(fav_id, None)
|
||||
if srv:
|
||||
self._services[fav_id] = srv._replace(flags_cas=flags)
|
||||
break
|
||||
|
||||
if index % self.FAV_FACTOR == 0:
|
||||
yield True
|
||||
|
||||
self.show_info_message("Done!", Gtk.MessageType.INFO)
|
||||
self._services_load_spinner.stop()
|
||||
yield True
|
||||
|
||||
# ***************** Picons ********************* #
|
||||
|
||||
@run_idle
|
||||
@@ -3996,6 +4087,16 @@ class Application(Gtk.Application):
|
||||
def get_picon(self, p_id):
|
||||
return get_picon_pixbuf(f"{self._settings.profile_picons_path}{p_id}", self._picons_size)
|
||||
|
||||
def get_tooltip_picon(self, srv):
|
||||
size, path, picon_id = self._settings.tooltip_logo_size, self._settings.profile_picons_path, srv.picon_id
|
||||
pix = get_picon_pixbuf(f"{path}{picon_id}", size=size)
|
||||
if not pix:
|
||||
picon_id = picon_id.replace(picon_id[:picon_id.find("_")], "1", 1)
|
||||
pix = get_picon_pixbuf(f"{path}{picon_id}", size=size)
|
||||
if not pix:
|
||||
pix = get_picon_pixbuf(f"{path}{get_picon_file_name(srv.service)}", size=size)
|
||||
return pix
|
||||
|
||||
def on_assign_picon(self, view, src_path=None, dst_path=None):
|
||||
self._stack.set_visible_child_name(Page.PICONS.value)
|
||||
self.emit("picon-assign", self.get_target_view(view))
|
||||
@@ -4201,11 +4302,15 @@ class Application(Gtk.Application):
|
||||
return True
|
||||
|
||||
def on_alt_selection(self, model, path, column):
|
||||
if self._control_tool and self._control_tool.update_epg:
|
||||
if self._page is Page.EPG:
|
||||
row = model[path][:]
|
||||
srv = self._services.get(row[Column.ALT_FAV_ID], None)
|
||||
if srv and srv.transponder or row[Column.ALT_TYPE] == BqServiceType.IPTV.name:
|
||||
self._control_tool.on_service_changed(srv.picon_id.rstrip(".png").replace("_", ":"))
|
||||
ref = self.get_service_ref_data(srv)
|
||||
if not ref:
|
||||
return
|
||||
|
||||
self.emit("fav-changed", ref)
|
||||
|
||||
# ***************** Profile label ********************* #
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -29,14 +29,18 @@
|
||||
""" Helper module for the GUI. """
|
||||
|
||||
__all__ = ("insert_marker", "move_items", "rename", "ViewTarget", "set_flags", "locate_in_services",
|
||||
"scroll_to", "get_base_model", "copy_reference", "assign_picons", "remove_picon",
|
||||
"is_only_one_item_selected", "gen_bouquets", "BqGenType", "get_selection",
|
||||
"scroll_to", "get_base_model", "get_base_paths", "copy_reference", "assign_picons", "remove_picon",
|
||||
"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_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")
|
||||
|
||||
import os
|
||||
import re
|
||||
import shutil
|
||||
import unicodedata
|
||||
from collections import defaultdict
|
||||
from functools import lru_cache
|
||||
from pathlib import Path
|
||||
from urllib.parse import unquote
|
||||
|
||||
@@ -557,8 +561,15 @@ def is_only_one_item_selected(paths, transient):
|
||||
def get_picon_pixbuf(path, size=32):
|
||||
try:
|
||||
return GdkPixbuf.Pixbuf.new_from_file_at_scale(path, width=size, height=size, preserve_aspect_ratio=True)
|
||||
except GLib.GError as e:
|
||||
pass
|
||||
except GLib.GError:
|
||||
pass # NOP
|
||||
|
||||
|
||||
@lru_cache(50)
|
||||
def get_picon_file_name(service_name):
|
||||
""" Returns picon file name by service name. """
|
||||
name = unicodedata.normalize("NFKD", service_name).encode("ASCII", errors="ignore").decode(errors="ignore")
|
||||
return f"{re.sub('[^a-z0-9]', '', name.replace('&', 'and').replace('+', 'plus').replace('*', 'star').lower())}.png"
|
||||
|
||||
|
||||
# ***************** Bouquets ********************* #
|
||||
@@ -618,25 +629,34 @@ def get_bouquets_names(model):
|
||||
|
||||
# ***************** Others ********************* #
|
||||
|
||||
def copy_reference(target, view, services, clipboard, transient):
|
||||
""" Copying picon id to clipboard """
|
||||
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, transient):
|
||||
if not is_only_one_item_selected(paths, app.app_window):
|
||||
return
|
||||
|
||||
target = app.get_target_view(view)
|
||||
clipboard = app._clipboard
|
||||
|
||||
if target is ViewTarget.SERVICES:
|
||||
picon_id = model.get_value(model.get_iter(paths), Column.SRV_PICON_ID)
|
||||
if picon_id:
|
||||
clipboard.set_text(picon_id.rstrip(".png"), -1)
|
||||
else:
|
||||
show_dialog(DialogType.ERROR, transient, "No reference is present!")
|
||||
app.show_error_message("No reference is present!")
|
||||
elif target is ViewTarget.FAV:
|
||||
fav_id = model.get_value(model.get_iter(paths), Column.FAV_ID)
|
||||
srv = services.get(fav_id, None)
|
||||
srv = app.current_services.get(fav_id, None)
|
||||
if srv and srv.picon_id:
|
||||
clipboard.set_text(srv.picon_id.rstrip(".png"), -1)
|
||||
clipboard.set_text(get_service_reference(srv), -1)
|
||||
else:
|
||||
show_dialog(DialogType.ERROR, transient, "No reference is present!")
|
||||
app.show_error_message("No reference is present!")
|
||||
|
||||
app.emit("clipboard-changed", clipboard.wait_is_text_available())
|
||||
|
||||
|
||||
def get_service_reference(srv):
|
||||
return srv.picon_id.rstrip(".png")
|
||||
|
||||
|
||||
def update_entry_data(entry, dialog, settings):
|
||||
@@ -678,6 +698,40 @@ def get_model_data(view):
|
||||
return model_name, model
|
||||
|
||||
|
||||
def update_toggle_model(model, path, toggle):
|
||||
""" Updates the toggle state for the model. """
|
||||
active = not toggle.get_active()
|
||||
if path == "0":
|
||||
model.foreach(lambda m, p, i: m.set_value(i, 1, active))
|
||||
else:
|
||||
model.set_value(model.get_iter(path), 1, active)
|
||||
if active:
|
||||
model.set_value(model.get_iter_first(), 1, len({r[0] for r in model if r[1]}) == len(model) - 1)
|
||||
else:
|
||||
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. """
|
||||
first = model[model.get_iter_first()][:]
|
||||
model.clear()
|
||||
model.append((first[0], True))
|
||||
sat_positions.discard(first[0])
|
||||
list(map(lambda pos: model.append((pos, True)), sorted(sat_positions, key=get_pos_num, reverse=True)))
|
||||
|
||||
|
||||
def get_pos_num(pos):
|
||||
""" Returns num [float] representation of satellite position. """
|
||||
if not pos:
|
||||
return -183.0
|
||||
|
||||
if len(pos) > 1:
|
||||
m = -1 if pos[-1] == "W" else 1
|
||||
return float(pos[:-1]) * m
|
||||
|
||||
return -181.0 if pos == "T" else -182.0
|
||||
|
||||
|
||||
def append_text_to_tview(char, view):
|
||||
""" Appending text and scrolling to a given line in the text view. """
|
||||
buf = view.get_buffer()
|
||||
|
||||
@@ -3,7 +3,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
|
||||
@@ -31,7 +31,7 @@ Author: Dmitriy Yefremov
|
||||
<!-- interface-css-provider-path style.css -->
|
||||
<!-- interface-license-type mit -->
|
||||
<!-- interface-name DemonEditor -->
|
||||
<!-- interface-copyright 2018-2021 Dmitriy Yefremov -->
|
||||
<!-- interface-copyright 2018-2023 Dmitriy Yefremov -->
|
||||
<!-- interface-authors Dmitriy Yefremov -->
|
||||
<object class="GtkMenu" id="add_menu">
|
||||
<property name="visible">True</property>
|
||||
@@ -379,6 +379,7 @@ Author: Dmitriy Yefremov
|
||||
<child type="center">
|
||||
<object class="GtkButtonBox" id="header_button_box">
|
||||
<property name="visible">True</property>
|
||||
<property name="name">header-stack-switcher</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="halign">center</property>
|
||||
<property name="layout_style">expand</property>
|
||||
@@ -640,7 +641,6 @@ Author: Dmitriy Yefremov
|
||||
<property name="can_focus">True</property>
|
||||
<property name="tooltip_text" translatable="yes">Drag the services to the desired picon or picon to the list of selected services.</property>
|
||||
<property name="model">picons_src_sort_model</property>
|
||||
<property name="fixed_height_mode">True</property>
|
||||
<property name="rubber_banding">True</property>
|
||||
<property name="enable_grid_lines">horizontal</property>
|
||||
<property name="tooltip_column">0</property>
|
||||
@@ -665,7 +665,6 @@ Author: Dmitriy Yefremov
|
||||
<property name="alignment">0.49000000953674316</property>
|
||||
<child>
|
||||
<object class="GtkCellRendererPixbuf" id="picons_src_renderer">
|
||||
<property name="height">50</property>
|
||||
<property name="ypad">5</property>
|
||||
</object>
|
||||
<attributes>
|
||||
@@ -778,7 +777,6 @@ Author: Dmitriy Yefremov
|
||||
<property name="can_focus">True</property>
|
||||
<property name="tooltip_text" translatable="yes">Drag the services to the desired picon or picon to the list of selected services.</property>
|
||||
<property name="model">picons_dst_sort_model</property>
|
||||
<property name="fixed_height_mode">True</property>
|
||||
<property name="rubber_banding">True</property>
|
||||
<property name="enable_grid_lines">horizontal</property>
|
||||
<property name="tooltip_column">0</property>
|
||||
@@ -804,7 +802,6 @@ Author: Dmitriy Yefremov
|
||||
<property name="alignment">0.49000000953674316</property>
|
||||
<child>
|
||||
<object class="GtkCellRendererPixbuf" id="picons_dest_renderer">
|
||||
<property name="height">50</property>
|
||||
<property name="ypad">5</property>
|
||||
</object>
|
||||
<attributes>
|
||||
|
||||
@@ -42,8 +42,8 @@ from app.tools.picons import (PiconsParser, parse_providers, Provider, convert_t
|
||||
PiconsError)
|
||||
from app.tools.satellites import SatellitesParser, SatelliteSource
|
||||
from .dialogs import show_dialog, DialogType, get_message, get_builder, get_chooser_dialog
|
||||
from .main_helper import (update_entry_data, append_text_to_tview, scroll_to, on_popup_menu, get_base_model, set_picon,
|
||||
get_picon_pixbuf, get_picon_dialog)
|
||||
from .main_helper import (scroll_to, on_popup_menu, get_base_model, set_picon, get_picon_pixbuf, get_picon_dialog,
|
||||
get_picon_file_name)
|
||||
from .uicommons import Gtk, Gdk, UI_RESOURCES_PATH, TV_ICON, Column, KeyboardKey, Page, ViewTarget
|
||||
|
||||
|
||||
@@ -87,7 +87,6 @@ class PiconManager(Gtk.Box):
|
||||
"on_receive": self.on_receive,
|
||||
"on_cancel": self.on_cancel,
|
||||
"on_remove": self.on_remove,
|
||||
"on_picons_dir_open": self.on_picons_dir_open,
|
||||
"on_selected_toggled": self.on_selected_toggled,
|
||||
"on_url_changed": self.on_url_changed,
|
||||
"on_picons_filter_changed": self.on_picons_filter_changed,
|
||||
@@ -816,7 +815,12 @@ class PiconManager(Gtk.Box):
|
||||
|
||||
fav_bouquet = self._app.current_bouquets[bq_selected]
|
||||
services = self._app.current_services
|
||||
return {services.get(fav_id).picon_id for fav_id in fav_bouquet}
|
||||
|
||||
ids = set()
|
||||
for s in (services.get(fav_id) for fav_id in fav_bouquet):
|
||||
ids.add(s.picon_id)
|
||||
ids.add(get_picon_file_name(s.service))
|
||||
return ids
|
||||
|
||||
def process_provider(self, prv, picons_path):
|
||||
log(f"Getting links to picons for: {prv.name}.\n")
|
||||
@@ -869,9 +873,6 @@ class PiconManager(Gtk.Box):
|
||||
def show_info_message(self, text, message_type):
|
||||
self._app.show_info_message(text, message_type)
|
||||
|
||||
def on_picons_dir_open(self, entry, icon, event_button):
|
||||
update_entry_data(entry, self._app_window, settings=self._settings)
|
||||
|
||||
@run_idle
|
||||
def on_selected_toggled(self, toggle, path):
|
||||
model = self._providers_view.get_model()
|
||||
|
||||
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
|
||||
@@ -28,13 +28,14 @@
|
||||
|
||||
import os
|
||||
import re
|
||||
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
|
||||
from app.settings import SettingsType, Settings, PlayStreamsMode, IS_LINUX, SEP, IS_WIN, IS_DARWIN
|
||||
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
|
||||
from .uicommons import Gtk, Gdk, UI_RESOURCES_PATH, FavClickMode, DEFAULT_ICON, APP_FONT, IS_GNOME_SESSION, HeaderBar
|
||||
|
||||
|
||||
class SettingsDialog:
|
||||
@@ -42,7 +43,7 @@ class SettingsDialog:
|
||||
_DIGIT_PATTERN = re.compile("(?:^[\\s]*$|\\D)")
|
||||
|
||||
def __init__(self, transient, settings: Settings):
|
||||
handlers = {"on_field_icon_press": self.on_field_icon_press,
|
||||
handlers = {"on_field_button_press": self.on_field_button_press,
|
||||
"on_settings_type_changed": self.on_settings_type_changed,
|
||||
"on_reset": self.on_reset,
|
||||
"on_response": self.on_response,
|
||||
@@ -62,6 +63,10 @@ class SettingsDialog:
|
||||
"on_profile_edited": self.on_profile_edited,
|
||||
"on_profile_selected": self.on_profile_selected,
|
||||
"on_profile_set_default": self.on_profile_set_default,
|
||||
"on_host_focus_in": self.on_host_focus_in,
|
||||
"on_host_focus_out": self.on_host_focus_out,
|
||||
"on_add_host": self.on_add_host,
|
||||
"on_remove_host": self.on_remove_host,
|
||||
"on_add_picon_path": self.on_add_picon_path,
|
||||
"on_remove_picon_path": self.on_remove_picon_path,
|
||||
"on_lang_changed": self.on_lang_changed,
|
||||
@@ -96,7 +101,10 @@ class SettingsDialog:
|
||||
self._dialog.set_margin_left(0)
|
||||
self._main_stack = builder.get_object("main_stack")
|
||||
# Network.
|
||||
self._host_iter = None
|
||||
self._host_field = builder.get_object("host_field")
|
||||
self._hosts_box = builder.get_object("hosts_box")
|
||||
self._remove_host_button = builder.get_object("remove_host_button")
|
||||
self._port_field = builder.get_object("port_field")
|
||||
self._login_field = builder.get_object("login_field")
|
||||
self._password_field = builder.get_object("password_field")
|
||||
@@ -119,10 +127,10 @@ class SettingsDialog:
|
||||
self._picons_path_field = builder.get_object("picons_path_field")
|
||||
self._data_path_field = builder.get_object("data_path_field")
|
||||
self._backup_path_field = builder.get_object("backup_path_field")
|
||||
self._record_data_path_field = builder.get_object("record_data_path_field")
|
||||
self._recordings_path_field = builder.get_object("recordings_path_field")
|
||||
self._default_data_paths_switch = builder.get_object("default_data_paths_switch")
|
||||
self._default_data_paths_switch.bind_property("active", self._backup_path_field, "sensitive", 4)
|
||||
self._default_data_paths_switch.bind_property("active", self._picons_path_field, "sensitive", 4)
|
||||
self._default_data_paths_switch.bind_property("active", builder.get_object("picons_path_box"), "sensitive", 4)
|
||||
self._default_data_paths_switch.bind_property("active", builder.get_object("backup_path_box"), "sensitive", 4)
|
||||
# Info bar.
|
||||
self._info_bar = builder.get_object("info_bar")
|
||||
self._message_label = builder.get_object("info_bar_message_label")
|
||||
@@ -168,19 +176,16 @@ class SettingsDialog:
|
||||
# Extra.
|
||||
self._use_http_switch = builder.get_object("use_http_switch")
|
||||
self._remove_unused_bq_switch = builder.get_object("remove_unused_bq_switch")
|
||||
self._keep_power_mode_switch = builder.get_object("keep_power_mode_switch")
|
||||
self._compress_picons_switch = builder.get_object("compress_picons_switch")
|
||||
self._force_bq_name_switch = builder.get_object("force_bq_name_switch")
|
||||
self._support_ver5_switch = builder.get_object("support_ver5_switch")
|
||||
self._unlimited_buffer_switch = builder.get_object("unlimited_buffer_switch")
|
||||
self._support_http_api_switch = builder.get_object("support_http_api_switch")
|
||||
self._enable_yt_dl_switch = builder.get_object("enable_yt_dl_switch")
|
||||
self._enable_update_yt_dl_switch = builder.get_object("enable_update_yt_dl_switch")
|
||||
self._enable_send_to_switch = builder.get_object("enable_send_to_switch")
|
||||
# EXPERIMENTAL.
|
||||
self._enable_exp_switch = builder.get_object("enable_experimental_switch")
|
||||
self._enable_exp_switch.bind_property("active", builder.get_object("yt_dl_box"), "sensitive")
|
||||
self._enable_yt_dl_switch.bind_property("active", builder.get_object("yt_dl_update_box"), "sensitive")
|
||||
self._enable_exp_switch.bind_property("active", builder.get_object("v5_support_box"), "sensitive")
|
||||
self._enable_exp_switch.bind_property("active", builder.get_object("enable_direct_playback_box"), "sensitive")
|
||||
# Enigma2 only.
|
||||
self._enigma_radio_button.bind_property("active", builder.get_object("bq_naming_grid"), "sensitive")
|
||||
self._enigma_radio_button.bind_property("active", builder.get_object("program_frame"), "sensitive")
|
||||
@@ -194,21 +199,22 @@ class SettingsDialog:
|
||||
# Separated due to a bug with response (presumably in the builder) in ubuntu 18.04 and derivatives.
|
||||
builder.get_object("network_settings_frame").add(builder.get_object("network_grid"))
|
||||
# Style.
|
||||
self._style_provider = Gtk.CssProvider()
|
||||
self._style_provider.load_from_path(UI_RESOURCES_PATH + "style.css")
|
||||
style_provider = Gtk.CssProvider()
|
||||
style_provider.load_from_path(f"{UI_RESOURCES_PATH}style.css")
|
||||
screen = Gdk.Screen.get_default()
|
||||
self._digit_elems = (self._port_field, self._http_port_field, self._telnet_port_field, self._video_width_field,
|
||||
self._video_bitrate_field, self._video_height_field, self._audio_bitrate_field)
|
||||
for el in self._digit_elems:
|
||||
el.get_style_context().add_provider_for_screen(Gdk.Screen.get_default(), self._style_provider,
|
||||
Gtk.STYLE_PROVIDER_PRIORITY_USER)
|
||||
[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:
|
||||
if IS_GNOME_SESSION or IS_DARWIN:
|
||||
switcher = builder.get_object("main_stack_switcher")
|
||||
switcher.set_margin_top(0)
|
||||
switcher.set_margin_bottom(0)
|
||||
builder.get_object("main_box").remove(switcher)
|
||||
header_bar = Gtk.HeaderBar(visible=True, show_close_button=True)
|
||||
header_bar = HeaderBar()
|
||||
header_bar.set_custom_title(switcher)
|
||||
|
||||
self._dialog.set_titlebar(header_bar)
|
||||
|
||||
self.init_ui_elements()
|
||||
@@ -251,6 +257,9 @@ class SettingsDialog:
|
||||
self.on_profile_selected(self._profile_view, False)
|
||||
self._profile_remove_button.set_sensitive(len(self._profile_view.get_model()) > 1)
|
||||
|
||||
def init_element_style(self, elem, screen, provider):
|
||||
elem.get_style_context().add_provider_for_screen(screen, provider, Gtk.STYLE_PROVIDER_PRIORITY_USER)
|
||||
|
||||
def update_title(self):
|
||||
title = "{} [{}]"
|
||||
if self._s_type is SettingsType.ENIGMA_2:
|
||||
@@ -278,7 +287,7 @@ class SettingsDialog:
|
||||
self._updated = self.on_save_settings()
|
||||
dialog.destroy()
|
||||
|
||||
def on_field_icon_press(self, entry, icon, event_button):
|
||||
def on_field_button_press(self, entry):
|
||||
update_entry_data(entry, self._dialog, self._settings)
|
||||
|
||||
def on_settings_type_changed(self, item):
|
||||
@@ -295,7 +304,9 @@ class SettingsDialog:
|
||||
|
||||
def set_settings(self):
|
||||
self._s_type = self._settings.setting_type
|
||||
self._host_field.set_text(self._settings.host)
|
||||
self._hosts_box.remove_all()
|
||||
self._remove_host_button.set_sensitive(len([self._hosts_box.append(h, h) for h in self._settings.hosts]) > 1)
|
||||
self._hosts_box.set_active_id(self._settings.host)
|
||||
self._port_field.set_text(self._settings.port)
|
||||
self._login_field.set_text(self._settings.user)
|
||||
self._password_field.set_text(self._settings.password)
|
||||
@@ -311,7 +322,7 @@ class SettingsDialog:
|
||||
self._data_path_field.set_text(self._settings.default_data_path)
|
||||
self._picons_path_field.set_text(self._settings.default_picon_path)
|
||||
self._backup_path_field.set_text(self._settings.default_backup_path)
|
||||
self._record_data_path_field.set_text(self._settings.records_path)
|
||||
self._recordings_path_field.set_text(self._settings.recordings_path)
|
||||
self._before_save_switch.set_active(self._settings.backup_before_save)
|
||||
self._before_downloading_switch.set_active(self._settings.backup_before_downloading)
|
||||
self._play_streams_combo_box.set_active_id(str(self._settings.play_streams_mode.value))
|
||||
@@ -333,8 +344,10 @@ class SettingsDialog:
|
||||
if self._s_type is SettingsType.ENIGMA_2:
|
||||
self._enable_exp_switch.set_active(self._settings.is_enable_experimental)
|
||||
self._support_ver5_switch.set_active(self._settings.v5_support)
|
||||
self._unlimited_buffer_switch.set_active(self._settings.unlimited_copy_buffer)
|
||||
self._use_http_switch.set_active(self._settings.use_http)
|
||||
self._remove_unused_bq_switch.set_active(self._settings.remove_unused_bouquets)
|
||||
self._keep_power_mode_switch.set_active(self._settings.keep_power_mode)
|
||||
self._compress_picons_switch.set_active(self._settings.compress_picons)
|
||||
self._force_bq_name_switch.set_active(self._settings.force_bq_names)
|
||||
self._enable_yt_dl_switch.set_active(self._settings.enable_yt_dl)
|
||||
@@ -361,6 +374,7 @@ class SettingsDialog:
|
||||
self._s_type = SettingsType.ENIGMA_2 if self._enigma_radio_button.get_active() else SettingsType.NEUTRINO_MP
|
||||
self._settings.setting_type = self._s_type
|
||||
self._settings.host = self._host_field.get_text()
|
||||
self._settings.hosts = [h[1] for h in self._hosts_box.get_model()]
|
||||
self._settings.port = self._port_field.get_text()
|
||||
self._settings.user = self._login_field.get_text()
|
||||
self._settings.password = self._password_field.get_text()
|
||||
@@ -393,7 +407,7 @@ class SettingsDialog:
|
||||
self._ext_settings.default_data_path = self._data_path_field.get_text()
|
||||
self._ext_settings.default_backup_path = self._backup_path_field.get_text()
|
||||
self._ext_settings.default_picon_path = self._picons_path_field.get_text()
|
||||
self._ext_settings.records_path = self._record_data_path_field.get_text()
|
||||
self._ext_settings.recordings_path = self._recordings_path_field.get_text()
|
||||
self._ext_settings.activate_transcoding = self._transcoding_switch.get_active()
|
||||
self._ext_settings.active_preset = self._presets_combo_box.get_active_id()
|
||||
self._ext_settings.list_picon_size = int(self._picons_size_button.get_active_id())
|
||||
@@ -414,8 +428,10 @@ class SettingsDialog:
|
||||
self._ext_settings.new_color = self._new_color_button.get_rgba().to_string()
|
||||
self._ext_settings.extra_color = self._extra_color_button.get_rgba().to_string()
|
||||
self._ext_settings.v5_support = self._support_ver5_switch.get_active()
|
||||
self._ext_settings.unlimited_copy_buffer = self._unlimited_buffer_switch.get_active()
|
||||
self._ext_settings.use_http = self._use_http_switch.get_active()
|
||||
self._ext_settings.remove_unused_bouquets = self._remove_unused_bq_switch.get_active()
|
||||
self._ext_settings.keep_power_mode = self._keep_power_mode_switch.get_active()
|
||||
self._ext_settings.compress_picons = self._compress_picons_switch.get_active()
|
||||
self._ext_settings.force_bq_names = self._force_bq_name_switch.get_active()
|
||||
self._ext_settings.enable_yt_dl = self._enable_yt_dl_switch.get_active()
|
||||
@@ -499,6 +515,7 @@ class SettingsDialog:
|
||||
def on_experimental_switch(self, switch, state):
|
||||
if not state:
|
||||
self._support_ver5_switch.set_active(state)
|
||||
self._unlimited_buffer_switch.set_active(state)
|
||||
self._enable_send_to_switch.set_active(state)
|
||||
self._enable_yt_dl_switch.set_active(state)
|
||||
|
||||
@@ -587,6 +604,42 @@ class SettingsDialog:
|
||||
def on_profile_inserted(self, model, path, itr):
|
||||
self._profile_remove_button.set_sensitive(len(model) > 1)
|
||||
|
||||
def on_host_focus_in(self, entry, event):
|
||||
self._host_iter = self._hosts_box.get_active_iter()
|
||||
|
||||
def on_host_focus_out(self, entry, event=None):
|
||||
if self._host_iter:
|
||||
model = self._hosts_box.get_model()
|
||||
host = entry.get_text()
|
||||
model.set_value(self._host_iter, 0, host)
|
||||
model.set_value(self._host_iter, 1, host)
|
||||
|
||||
if Counter(r[0] for r in model).get(host, 0) > 1:
|
||||
self._host_field.set_name(self._DIGIT_ENTRY_NAME)
|
||||
self.show_info_message("The host already exists!", Gtk.MessageType.WARNING)
|
||||
else:
|
||||
self._host_field.set_name("GtkEntry")
|
||||
self.on_info_bar_close()
|
||||
|
||||
def on_add_host(self, button):
|
||||
model = self._hosts_box.get_model()
|
||||
count = 1
|
||||
host = "127.0.0.1"
|
||||
hosts = {r[0] for r in model}
|
||||
|
||||
while host in hosts:
|
||||
count += 1
|
||||
host = f"127.0.0.{count}"
|
||||
|
||||
self._hosts_box.append(host, host)
|
||||
self._hosts_box.set_active_id(host)
|
||||
self._remove_host_button.set_sensitive(len(model) > 1)
|
||||
|
||||
def on_remove_host(self, button):
|
||||
self._hosts_box.remove(self._hosts_box.get_active())
|
||||
self._hosts_box.set_active(0)
|
||||
self._remove_host_button.set_sensitive(len(self._hosts_box.get_model()) > 1)
|
||||
|
||||
def on_add_picon_path(self, button):
|
||||
response = show_dialog(DialogType.INPUT, self._dialog, self._settings.picons_path)
|
||||
if response is Gtk.ResponseType.CANCEL:
|
||||
|
||||
@@ -18,11 +18,24 @@
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
#stack-switch-button {
|
||||
#header-button {
|
||||
padding-top: 0;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
#header-entry {
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
#header-stack-switcher > button {
|
||||
padding-top: 0;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
buttonbox {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
paned > separator {
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
@@ -36,10 +49,6 @@ paned.vertical > separator {
|
||||
background-size: 24px 2px;
|
||||
}
|
||||
|
||||
popover .view {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.red-button {
|
||||
background-image: none;
|
||||
background-color: red;
|
||||
|
||||
@@ -162,6 +162,17 @@ def show_notification(message, timeout=10000, urgency=1):
|
||||
notify.show()
|
||||
|
||||
|
||||
class HeaderBar(Gtk.HeaderBar):
|
||||
""" Custom header bar widget. """
|
||||
def __init__(self, **kwargs):
|
||||
super().__init__(**kwargs)
|
||||
self.set_visible(True)
|
||||
self.set_show_close_button(True)
|
||||
|
||||
if IS_DARWIN:
|
||||
self.set_decoration_layout("close,minimize,maximize")
|
||||
|
||||
|
||||
class Page(Enum):
|
||||
""" Main stack widget page. """
|
||||
INFO = "info"
|
||||
@@ -269,6 +280,14 @@ class Column(IntEnum):
|
||||
IPTV_FAV_ID = 5
|
||||
IPTV_PICON_ID = 6
|
||||
IPTV_TOOLTIP = 7
|
||||
# EPG view
|
||||
EPG_SERVICE = 0
|
||||
EPG_TITLE = 1
|
||||
EPG_START = 2
|
||||
EPG_END = 3
|
||||
EPG_LENGTH = 4
|
||||
EPG_DESC = 5
|
||||
EPG_DATA = 6
|
||||
|
||||
def __index__(self):
|
||||
""" Overridden to get the index in slices directly """
|
||||
|
||||
@@ -18,3 +18,7 @@ grid > button {
|
||||
padding-left: 15px;
|
||||
padding-right: 15px;
|
||||
}
|
||||
|
||||
popover .view {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -39,11 +39,12 @@ 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
|
||||
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 ..search import SearchProvider
|
||||
from ..uicommons import Gtk, Gdk, UI_RESOURCES_PATH, IS_GNOME_SESSION
|
||||
from ..uicommons import Gtk, Gdk, UI_RESOURCES_PATH, IS_GNOME_SESSION, HeaderBar
|
||||
|
||||
_DIALOGS_UI_PATH = f"{UI_RESOURCES_PATH}xml{os.sep}dialogs.glade"
|
||||
|
||||
@@ -432,7 +433,6 @@ class UpdateDialog:
|
||||
update_button = builder.get_object("sat_update_button")
|
||||
self._sat_view.bind_property("sensitive", update_button, "sensitive")
|
||||
self._sat_view.bind_property("sensitive", self._source_box, "sensitive")
|
||||
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")
|
||||
# Filter
|
||||
@@ -457,6 +457,20 @@ class UpdateDialog:
|
||||
builder.get_object("sat_update_search_up_button"))
|
||||
builder.get_object("sat_update_find_button").connect("toggled", search_provider.on_search_toggled)
|
||||
|
||||
if IS_GNOME_SESSION or IS_DARWIN:
|
||||
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)
|
||||
self._window.set_titlebar(header_bar)
|
||||
|
||||
window_size = self._settings.get(self._size_name)
|
||||
if window_size:
|
||||
self._window.resize(*window_size)
|
||||
|
||||
@@ -3,7 +3,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
|
||||
@@ -27,11 +27,11 @@ Author: Dmitriy Yefremov
|
||||
|
||||
-->
|
||||
<interface>
|
||||
<requires lib="gtk+" version="3.20"/>
|
||||
<requires lib="gtk+" version="3.22"/>
|
||||
<!-- interface-css-provider-path style.css -->
|
||||
<!-- interface-license-type mit -->
|
||||
<!-- interface-name DemonEditor -->
|
||||
<!-- interface-copyright 2018-2022 Dmitriy Yefremov -->
|
||||
<!-- interface-copyright 2018-2023 Dmitriy Yefremov -->
|
||||
<!-- interface-authors Dmitriy Yefremov -->
|
||||
<object class="GtkListStore" id="cable_model">
|
||||
<columns>
|
||||
@@ -327,6 +327,7 @@ Author: Dmitriy Yefremov
|
||||
<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>
|
||||
|
||||
@@ -3,7 +3,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
|
||||
@@ -27,11 +27,11 @@ Author: Dmitriy Yefremov
|
||||
|
||||
-->
|
||||
<interface>
|
||||
<requires lib="gtk+" version="3.20"/>
|
||||
<requires lib="gtk+" version="3.22"/>
|
||||
<!-- interface-css-provider-path style.css -->
|
||||
<!-- interface-license-type mit -->
|
||||
<!-- interface-name DemonEditor -->
|
||||
<!-- interface-copyright 2018-2022 Dmitriy Yefremov -->
|
||||
<!-- interface-copyright 2018-2023 Dmitriy Yefremov -->
|
||||
<!-- interface-authors Dmitriy Yefremov -->
|
||||
<object class="GtkAdjustment" id="pos_adjustment">
|
||||
<property name="upper">180</property>
|
||||
@@ -199,7 +199,7 @@ Author: Dmitriy Yefremov
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButtonBox" id="sat_update_right_action_box">
|
||||
<object class="GtkButtonBox" id="sat_update_left_action_box">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="layout_style">expand</property>
|
||||
@@ -262,7 +262,7 @@ Author: Dmitriy Yefremov
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButtonBox" id="sat_update_left_action_box">
|
||||
<object class="GtkButtonBox" id="sat_update_right_action_box">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="layout_style">expand</property>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/bin/bash
|
||||
VER="3.0.0_Beta"
|
||||
VER="3.3.0_Beta"
|
||||
B_PATH="dist/DemonEditor"
|
||||
DEB_PATH="$B_PATH/usr/share/demoneditor"
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
Package: demon-editor
|
||||
Version: 3.0.0-Beta
|
||||
Version: 3.3.0-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.
@@ -80,8 +80,8 @@ app = BUNDLE(coll,
|
||||
'CFBundleGetInfoString': "Enigma2 channel and satellite editor",
|
||||
'LSApplicationCategoryType': 'public.app-category.utilities',
|
||||
'LSMinimumSystemVersion': '10.13',
|
||||
'CFBundleShortVersionString': f"3.0.0.{BUILD_DATE} Beta",
|
||||
'NSHumanReadableCopyright': u"Copyright © 2022, Dmitriy Yefremov",
|
||||
'CFBundleShortVersionString': f"3.3.0.{BUILD_DATE} Beta",
|
||||
'NSHumanReadableCopyright': u"Copyright © 2023, Dmitriy Yefremov",
|
||||
'NSRequiresAquaSystemAppearance': 'false',
|
||||
'NSHighResolutionCapable': 'true'
|
||||
})
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Copyright (C) 2018-2021 Dmitriy Yefremov
|
||||
# Copyright (C) 2018-2023 Dmitriy Yefremov
|
||||
# This file is distributed under the MIT license.
|
||||
#
|
||||
#
|
||||
@@ -1387,3 +1387,50 @@ msgstr "Штодня"
|
||||
|
||||
msgid "Assign reference"
|
||||
msgstr "Прысвоіць спасылку"
|
||||
|
||||
msgid "Specify hostname or IP address"
|
||||
msgstr "Задайце імя хоста або IP-адрас"
|
||||
|
||||
msgid "Default selection"
|
||||
msgstr "Выбар па змаўчанні"
|
||||
|
||||
msgid "Don't change power state"
|
||||
msgstr "Не змяняць стан сілкавання"
|
||||
|
||||
msgid "Don't toggle standby mode when updating bouquets and services."
|
||||
msgstr "Не перамыкаць у рэжым чакання пры абнаўленні букетаў і сэрвісаў."
|
||||
|
||||
msgid "Region"
|
||||
msgstr "Рэгіён"
|
||||
|
||||
msgid "Provider"
|
||||
msgstr "Правайдар"
|
||||
|
||||
msgid "Enables upload as an archive if a large number of picon (> 1000) is selected.\n"
|
||||
" Recommended only if you have external storage."
|
||||
msgstr "Улучае загрузку ў выглядзе архіва, калі абрана вялікая колькасць пiконаў (> 1000).\n"
|
||||
" Рэкамендуецца, толькі калі ў вас ёсць вонкавае сховішча."
|
||||
|
||||
msgid "Clear \"New\" flag"
|
||||
msgstr "Здаліць сцяжок \"New\""
|
||||
|
||||
msgid "Group by"
|
||||
msgstr "Групаваць па"
|
||||
|
||||
msgid "Replace existing"
|
||||
msgstr "Замяніць існуючыя"
|
||||
|
||||
msgid "Already exists"
|
||||
msgstr "Ужо існуе"
|
||||
|
||||
msgid "Enable unlimited copy buffer"
|
||||
msgstr "Уключыць неабмежаваны буфер капіявання"
|
||||
|
||||
msgid "Enables unlimited copy buffer for the bouquets tab."
|
||||
msgstr "Улучае неабмежаваны буфер капіявання для ўкладкі букетаў."
|
||||
|
||||
msgid "Start time"
|
||||
msgstr "Пачатак"
|
||||
|
||||
msgid "End time"
|
||||
msgstr "Сканчэнне"
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
# This file is distributed under the MIT license.
|
||||
#
|
||||
# Charly, 2019.
|
||||
# Dmitriy Yefremov, 2020-2021.
|
||||
# Thomas Schmidt, 2021
|
||||
# Dmitriy Yefremov, 2020-2023.
|
||||
# Thomas Schmidt, 2021.
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Last-Translator: Dmitriy Yefremov\n"
|
||||
@@ -1401,3 +1401,50 @@ msgstr "Täglich"
|
||||
|
||||
msgid "Assign reference"
|
||||
msgstr "Referenz zuweisen"
|
||||
|
||||
msgid "Specify hostname or IP address"
|
||||
msgstr "Hostname oder IP-Adresse angeben"
|
||||
|
||||
msgid "Default selection"
|
||||
msgstr "Standardauswahl"
|
||||
|
||||
msgid "Don't change power state"
|
||||
msgstr "Energiezustand nicht ändern"
|
||||
|
||||
msgid "Don't toggle standby mode when updating bouquets and services."
|
||||
msgstr "Schalten beim Aktualisieren von Bouquets und Services nicht in den Standby-Modus um."
|
||||
|
||||
msgid "Region"
|
||||
msgstr "Region"
|
||||
|
||||
msgid "Provider"
|
||||
msgstr "Provider"
|
||||
|
||||
msgid "Enables upload as an archive if a large number of picon (> 1000) is selected.\n"
|
||||
" Recommended only if you have external storage."
|
||||
msgstr "Ermöglicht das Hochladen als Archiv, wenn eine große Anzahl von Picons (> 1000)"
|
||||
" ausgewählt ist. Empfohlen nur, wenn einen externen Speicher verfügt wird."
|
||||
|
||||
msgid "Clear \"New\" flag"
|
||||
msgstr "Das \"Neu\"-Flag entfernen"
|
||||
|
||||
msgid "Group by"
|
||||
msgstr "Gruppieren nach"
|
||||
|
||||
msgid "Replace existing"
|
||||
msgstr "Vorhandene ersetzen"
|
||||
|
||||
msgid "Already exists"
|
||||
msgstr "Bereits vorhanden"
|
||||
|
||||
msgid "Enable unlimited copy buffer"
|
||||
msgstr "Unbegrenzte Zwischenablage aktivieren"
|
||||
|
||||
msgid "Enables unlimited copy buffer for the bouquets tab."
|
||||
msgstr "Aktiviert unbegrenzte Zwischenablage für die Bouquets-Tab."
|
||||
|
||||
msgid "Start time"
|
||||
msgstr "Anfangszeit"
|
||||
|
||||
msgid "End time"
|
||||
msgstr "Endzeit"
|
||||
|
||||
@@ -8,7 +8,7 @@ msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"POT-Creation-Date: \n"
|
||||
"PO-Revision-Date: \n"
|
||||
"Last-Translator: Víctor Pont\n"
|
||||
"Last-Translator: Frank Neirynck\n"
|
||||
"Language-Team: \n"
|
||||
"Language: es\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
@@ -1382,3 +1382,62 @@ msgstr "Añadir marcador"
|
||||
|
||||
msgid "All bouquets"
|
||||
msgstr "Todos los bouquets"
|
||||
|
||||
msgid "Playback from the main list"
|
||||
msgstr "Reproducción desde la lista principal"
|
||||
|
||||
msgid "Enables URL parsing using youtube-dl to get direct links to media."
|
||||
msgstr "Habilita el análisis de URL usando youtube-dl para obtener enlaces directos a los medios."
|
||||
|
||||
msgid "Permissions..."
|
||||
msgstr "Permisos..."
|
||||
|
||||
msgid "Display EPG in bouquet list"
|
||||
msgstr "Mostrar EPG en la lista de bouquet"
|
||||
|
||||
msgid "EPG *.dat file:"
|
||||
msgstr "Archivo EPG *.dat:"
|
||||
|
||||
msgid "Use HTTP to reload data in the receiver"
|
||||
msgstr "Use HTTP para recargar datos en el receptor"
|
||||
|
||||
msgid "Enable picons compression"
|
||||
msgstr "Habilitar la compresión de picons"
|
||||
|
||||
msgid "Update interval (sec):"
|
||||
msgstr "Intervalo de actualización (sec):"
|
||||
|
||||
msgid "Update:"
|
||||
msgstr "Actualización:"
|
||||
|
||||
msgid "Daily"
|
||||
msgstr "Diariamente"
|
||||
|
||||
msgid "Assign reference"
|
||||
msgstr "Asignar referencia"
|
||||
|
||||
msgid "Specify hostname or IP address"
|
||||
msgstr "Especifique el nombre de host o la dirección IP"
|
||||
|
||||
msgid "Default selection"
|
||||
msgstr "Selección predeterminada"
|
||||
|
||||
msgid "Don't change power state"
|
||||
msgstr "No apagues el dispositivo"
|
||||
|
||||
msgid "Don't toggle standby mode when updating bouquets and services."
|
||||
msgstr "No cambie el modo de espera al actualizar bouquets y servicios."
|
||||
|
||||
msgid "Region"
|
||||
msgstr "Región"
|
||||
|
||||
msgid "Provider"
|
||||
msgstr "Proveedor"
|
||||
|
||||
msgid "Enables upload as an archive if a large number of picon (> 1000) is selected.\n"
|
||||
" Recommended only if you have external storage."
|
||||
msgstr "Habilita la carga como un archivo si se selecciona una gran cantidad de picon (> 1000).\n"
|
||||
" Recomendado solo si tienes almacenamiento externo."
|
||||
|
||||
msgid "Clear \"New\" flag"
|
||||
msgstr "Limpiar \"Nuevo\" flag"
|
||||
|
||||
@@ -2,13 +2,19 @@
|
||||
# This file is distributed under the MIT license.
|
||||
#
|
||||
#
|
||||
# Massimo Pissarello <mapi68@gmail.com>, 2022, 2023.
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Last-Translator: Massimo Pissarello\n"
|
||||
"Project-Id-Version: \n"
|
||||
"PO-Revision-Date: 2023-01-27 19:45+0100\n"
|
||||
"Last-Translator: Massimo Pissarello <mapi68@gmail.com>\n"
|
||||
"Language-Team: Italian <>\n"
|
||||
"Language: it\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"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.1\n"
|
||||
|
||||
msgid "translator-credits"
|
||||
msgstr "Massimo Pissarello"
|
||||
@@ -36,7 +42,7 @@ msgid "Pol"
|
||||
msgstr "Pol"
|
||||
|
||||
msgid "System"
|
||||
msgstr "System"
|
||||
msgstr "Sistema"
|
||||
|
||||
msgid "Pos"
|
||||
msgstr "Pos"
|
||||
@@ -45,7 +51,7 @@ msgid "Num"
|
||||
msgstr "Num"
|
||||
|
||||
msgid "Current IP:"
|
||||
msgstr "IP/Host attuale:"
|
||||
msgstr "IP/Host connesso:"
|
||||
|
||||
msgid "Assign"
|
||||
msgstr "Assegna"
|
||||
@@ -84,7 +90,7 @@ msgid "Hide"
|
||||
msgstr "Nascondi"
|
||||
|
||||
msgid "Hide/Skip On/Off Ctrl + H"
|
||||
msgstr "Nascondi/Salta On/Off Ctrl + H"
|
||||
msgstr "Nascondi/Salta On/Off Ctrl + H"
|
||||
|
||||
msgid "Add IPTV or stream service"
|
||||
msgstr "Aggiungi IPTV o servizio stream"
|
||||
@@ -129,19 +135,19 @@ msgid "Create bouquet"
|
||||
msgstr "Crea bouquet"
|
||||
|
||||
msgid "For current satellite"
|
||||
msgstr "Per questo satellite"
|
||||
msgstr "Per il satellite attuale"
|
||||
|
||||
msgid "For current package"
|
||||
msgstr "Per questo pacchetto"
|
||||
msgstr "Per il pacchetto attuale"
|
||||
|
||||
msgid "For current type"
|
||||
msgstr "Per questo tipo"
|
||||
msgstr "Per il tipo attuale"
|
||||
|
||||
msgid "For each satellite"
|
||||
msgstr "Per tutti i satelliti"
|
||||
msgstr "Per ogni satellite"
|
||||
|
||||
msgid "For each package"
|
||||
msgstr "Per tutti i pacchetti"
|
||||
msgstr "Per ogni pacchetto"
|
||||
|
||||
msgid "For each type"
|
||||
msgstr "Per ogni tipo"
|
||||
@@ -150,7 +156,7 @@ msgid "Open"
|
||||
msgstr "Apri"
|
||||
|
||||
msgid "Parent lock On/Off Ctrl + L"
|
||||
msgstr "Blocco Genitori On/Off Ctrl + L"
|
||||
msgstr "Blocco genitori On/Off Ctrl + L"
|
||||
|
||||
msgid "Picons"
|
||||
msgstr "Picon"
|
||||
@@ -186,13 +192,13 @@ msgid "Settings"
|
||||
msgstr "Impostazioni"
|
||||
|
||||
msgid "Up"
|
||||
msgstr "Sopra"
|
||||
msgstr "Su"
|
||||
|
||||
msgid "Down"
|
||||
msgstr "Sotto"
|
||||
msgstr "Giù"
|
||||
|
||||
msgid "Active profile:"
|
||||
msgstr "Profilo attivo"
|
||||
msgstr "Profilo attivo:"
|
||||
|
||||
msgid "All"
|
||||
msgstr "Tutto"
|
||||
@@ -206,8 +212,8 @@ msgstr "Percorso dati corrente:"
|
||||
msgid "Data:"
|
||||
msgstr "Dati:"
|
||||
|
||||
msgid "Enigma2 channel and satellite list editor for GNU/Linux."
|
||||
msgstr "Editor di liste canali e satelliti per Enigma2 su GNU/Linux."
|
||||
msgid "Enigma2 channel and satellite list editor."
|
||||
msgstr "Editor di elenchi di canali e satelliti per Enigma2."
|
||||
|
||||
msgid "Host:"
|
||||
msgstr "Host:"
|
||||
@@ -234,13 +240,13 @@ msgid "Satellites"
|
||||
msgstr "Satelliti"
|
||||
|
||||
msgid "Transponders"
|
||||
msgstr "Transponders"
|
||||
msgstr "Transponder"
|
||||
|
||||
msgid "Satellites.xml file:"
|
||||
msgstr "File satellites.xml:"
|
||||
|
||||
msgid "Selected"
|
||||
msgstr "Selezionati"
|
||||
msgstr "Selezionato"
|
||||
|
||||
msgid "Send"
|
||||
msgstr "Invia"
|
||||
@@ -259,7 +265,7 @@ msgstr "Extra:"
|
||||
|
||||
# Filter bar
|
||||
msgid "Only free"
|
||||
msgstr "Solo FTA"
|
||||
msgstr "Solo gratis"
|
||||
|
||||
msgid "All positions"
|
||||
msgstr "Tutte le posizioni"
|
||||
@@ -275,20 +281,20 @@ msgid "Stop playback"
|
||||
msgstr "Ferma riproduzione"
|
||||
|
||||
msgid "Previous stream in the list"
|
||||
msgstr "Stream precedente della lista"
|
||||
msgstr "Stream precedente nell'elenco"
|
||||
|
||||
msgid "Next stream in the list"
|
||||
msgstr "Stream successivo della lista"
|
||||
msgstr "Stream successivo nell'elenco"
|
||||
|
||||
msgid "Toggle in fullscreen"
|
||||
msgstr "Attiva schermo intero"
|
||||
msgstr "Attiva/disattiva schermo intero"
|
||||
|
||||
msgid "Close"
|
||||
msgstr "Chiudi"
|
||||
|
||||
# Picons dialog
|
||||
msgid "Load providers"
|
||||
msgstr "Scarica provider"
|
||||
msgstr "Carica provider"
|
||||
|
||||
msgid "Providers"
|
||||
msgstr "Provider"
|
||||
@@ -302,8 +308,8 @@ msgstr "Formato nome picon:"
|
||||
msgid "Resize:"
|
||||
msgstr "Ridimensiona:"
|
||||
|
||||
msgid "Current picons path:"
|
||||
msgstr "Percorso attuale picon:"
|
||||
msgid "Current picons path"
|
||||
msgstr "Percorso picon attuale"
|
||||
|
||||
msgid "Receiver picons path:"
|
||||
msgstr "Percorso picon sul ricevitore:"
|
||||
@@ -318,7 +324,7 @@ msgid "Downloader"
|
||||
msgstr "Scarica"
|
||||
|
||||
msgid "Converter"
|
||||
msgstr "Convertitore"
|
||||
msgstr "Converti"
|
||||
|
||||
msgid "Convert"
|
||||
msgstr "Converti"
|
||||
@@ -330,23 +336,28 @@ msgid "Path to Enigma2 picons:"
|
||||
msgstr "Persorso picon su Enigma2:"
|
||||
|
||||
msgid "Specify the correct position value for the provider!"
|
||||
msgstr "Specifica ia posizione corretta del provider!"
|
||||
msgstr "Specifica il valore di posizione corretto per il provider!"
|
||||
|
||||
msgid "Converter between name formats"
|
||||
msgstr "Convertitore tra formati di nome"
|
||||
|
||||
msgid "Receive picons for providers"
|
||||
msgstr "Scarica picon per providers"
|
||||
msgstr "Scarica picon per provider"
|
||||
|
||||
msgid "Load satellite providers."
|
||||
msgstr "Carica providers satellitari"
|
||||
msgstr "Carica provider satellitari"
|
||||
|
||||
msgid "To automatically set the identifiers for picons,\nfirst load the required services list into the main application window."
|
||||
msgstr "Per impostare automaticamente gli identificatori dei picon,\ncarica prima la lista dei servizi nella finestra principale."
|
||||
msgid ""
|
||||
"To automatically set the identifiers for picons,\n"
|
||||
"first load the required services list into the main application window."
|
||||
msgstr ""
|
||||
"Per impostare automaticamente gli identificatori per i picon,\nprima carica"
|
||||
" il file richiesto dell'elenco dei servizi nella finestra principale"
|
||||
" dell'applicazione."
|
||||
|
||||
# Satellites editor
|
||||
msgid "Satellites edit tool"
|
||||
msgstr "Strumento per modificare i satelliti"
|
||||
msgstr "Strumento di modifica dei satelliti"
|
||||
|
||||
msgid "Add"
|
||||
msgstr "Aggiungi"
|
||||
@@ -358,10 +369,10 @@ msgid "Transponder"
|
||||
msgstr "Transponder"
|
||||
|
||||
msgid "Satellite properties:"
|
||||
msgstr "Proprietà del satellite:"
|
||||
msgstr "Proprietà satellite:"
|
||||
|
||||
msgid "Transponder properties:"
|
||||
msgstr "Proprietà del transponder:"
|
||||
msgstr "Proprietà transponder:"
|
||||
|
||||
msgid "Name"
|
||||
msgstr "Nome"
|
||||
@@ -374,32 +385,36 @@ msgid "Satellites update"
|
||||
msgstr "Aggiornamento satelliti"
|
||||
|
||||
msgid "Remove selection"
|
||||
msgstr "Elimina selezione"
|
||||
msgstr "Rimuovi selezione"
|
||||
|
||||
# Service details dialog
|
||||
msgid "Service data:"
|
||||
msgstr "Dati del servizio:"
|
||||
msgstr "Dati servizio:"
|
||||
|
||||
msgid "Transponder data:"
|
||||
msgstr "Dati del transponder:"
|
||||
msgstr "Dati transponder:"
|
||||
|
||||
msgid "Service data"
|
||||
msgstr "Dati del servizio"
|
||||
msgstr "Dati servizio"
|
||||
|
||||
msgid "Transponder details"
|
||||
msgstr "Dettagli transponder"
|
||||
|
||||
msgid "Changes will be applied to all services of this transponder!\nContinue?"
|
||||
msgstr "I cambiamenti saranno applicati a tutti i servizi di questo transponder!\nContinuo?"
|
||||
msgid ""
|
||||
"Changes will be applied to all services of this transponder!\n"
|
||||
"Continue?"
|
||||
msgstr ""
|
||||
"Le modifiche verranno applicate a tutti i servizi di questo"
|
||||
" transponder!\nContinuare?"
|
||||
|
||||
msgid "Reference"
|
||||
msgstr "Riferimento"
|
||||
|
||||
msgid "Namespace"
|
||||
msgstr "Nome"
|
||||
msgstr "Spazio dei nomi"
|
||||
|
||||
msgid "Flags:"
|
||||
msgstr "Flags:"
|
||||
msgstr "Flag:"
|
||||
|
||||
msgid "Delays (ms):"
|
||||
msgstr "Ritardo (ms)"
|
||||
@@ -423,7 +438,7 @@ msgid "Filter"
|
||||
msgstr "Filtro"
|
||||
|
||||
msgid "Find"
|
||||
msgstr "Cerca"
|
||||
msgstr "Trova"
|
||||
|
||||
# IPTV dialog
|
||||
msgid "Stream data"
|
||||
@@ -437,14 +452,14 @@ msgid "Reset to default"
|
||||
msgstr "Torna alle impostazioni predefinite"
|
||||
|
||||
msgid "IPTV streams list configuration"
|
||||
msgstr "Lista configurazione streams IPTV"
|
||||
msgstr "Configurazione elenchi stream IPTV"
|
||||
|
||||
#Settings dialog
|
||||
# Settings dialog
|
||||
msgid "Preferences"
|
||||
msgstr "Preferenze"
|
||||
|
||||
msgid "Profile:"
|
||||
msgstr "Profilo"
|
||||
msgstr "Profilo:"
|
||||
|
||||
msgid "Timeout between commands in seconds"
|
||||
msgstr "Timeout tra i comandi in secondi"
|
||||
@@ -484,7 +499,7 @@ msgstr "Percorso file locali:"
|
||||
|
||||
# Dialogs messages
|
||||
msgid "Error. No bouquet is selected!"
|
||||
msgstr "Errore. nessun bouquet selezionato!"
|
||||
msgstr "Errore. Nessun bouquet selezionato!"
|
||||
|
||||
msgid "This item is not allowed to be removed!"
|
||||
msgstr "Questo elemento non può essere rimosso!"
|
||||
@@ -493,10 +508,12 @@ msgid "This item is not allowed to edit!"
|
||||
msgstr "Questo elemento non può essere modificato!"
|
||||
|
||||
msgid "Not allowed in this context!"
|
||||
msgstr "Non è permesso in questo contesto!"
|
||||
msgstr "Non consentito in questo contesto!"
|
||||
|
||||
msgid "Please, download files from receiver or setup your path for read data!"
|
||||
msgstr "Per favore, scarica i file dal ricevitore o seleziona il percorso dei dati!"
|
||||
msgstr ""
|
||||
"Per favore, scarica i file dal ricevitore o imposta il percorso da cui"
|
||||
" leggere i dati!"
|
||||
|
||||
msgid "Reading data error!"
|
||||
msgstr "Errore lettura dati!"
|
||||
@@ -508,10 +525,10 @@ msgid "Not implemented yet!"
|
||||
msgstr "Funzionalità non ancora sviluppata!"
|
||||
|
||||
msgid "The text of marker is empty, please try again!"
|
||||
msgstr "Il testo del marcatore é vuoto, riprova di nuovo!"
|
||||
msgstr "Il testo del marcatore è vuoto, riprova!"
|
||||
|
||||
msgid "Please, select only one item!"
|
||||
msgstr "Seleziona un solo elemento!"
|
||||
msgstr "Per favore, seleziona solo un elemento!"
|
||||
|
||||
msgid "No png file is selected!"
|
||||
msgstr "Nessun file png selezionato!"
|
||||
@@ -520,13 +537,13 @@ msgid "No profile selected!"
|
||||
msgstr "Nessun profilo selezionato!"
|
||||
|
||||
msgid "No reference is present!"
|
||||
msgstr "Riferimento mancante!"
|
||||
msgstr "Nessun riferimento presente!"
|
||||
|
||||
msgid "No selected item!"
|
||||
msgstr "Nessun elemento selezionato!"
|
||||
|
||||
msgid "The task is already running!"
|
||||
msgstr "Operazione giá in corso!"
|
||||
msgstr "L'attività è già in esecuzione!"
|
||||
|
||||
msgid "Done!"
|
||||
msgstr "Fatto!"
|
||||
@@ -538,19 +555,19 @@ msgid "Resizing..."
|
||||
msgstr "Ridimensionamento..."
|
||||
|
||||
msgid "Select paths!"
|
||||
msgstr "Seleziona persorsi!"
|
||||
msgstr "Seleziona percorsi!"
|
||||
|
||||
msgid "No satellite is selected!"
|
||||
msgstr "Nessun satellite selezionato!"
|
||||
|
||||
msgid "Please, select only one satellite!"
|
||||
msgstr "Per favore, seleziona un solo satellite!"
|
||||
msgstr "Per favore, seleziona solo un satellite!"
|
||||
|
||||
msgid "Please check your parameters and try again."
|
||||
msgstr "Per favore, controlla i parametri e riprova di nuovo!"
|
||||
msgstr "Per favore, controlla i tuoi parametri e riprova di nuovo."
|
||||
|
||||
msgid "No satellites.xml file is selected!"
|
||||
msgstr "Nessun file satellites.xml è selezionato!"
|
||||
msgstr "Nessun file satellites.xml selezionato!"
|
||||
|
||||
msgid "Error. Verify the data!"
|
||||
msgstr "Errore. Verifica i dati!"
|
||||
@@ -575,7 +592,7 @@ msgid "No changes required!"
|
||||
msgstr "Non sono richiesti cambiamenti!"
|
||||
|
||||
msgid "This list does not contains IPTV streams!"
|
||||
msgstr "La lista non contiene stream IPTV!"
|
||||
msgstr "L'elenco non contiene stream IPTV!"
|
||||
|
||||
msgid "New empty configuration"
|
||||
msgstr "Nuova configurazione vuota"
|
||||
@@ -593,16 +610,16 @@ msgid "Program"
|
||||
msgstr "Programma"
|
||||
|
||||
msgid "Backup:"
|
||||
msgstr "Copia di sicurezza:"
|
||||
msgstr "Backup:"
|
||||
|
||||
msgid "Backup"
|
||||
msgstr "Copia di sicurezza"
|
||||
msgstr "Backup"
|
||||
|
||||
msgid "Backups"
|
||||
msgstr "Copie di sicurezza"
|
||||
msgstr "Backup"
|
||||
|
||||
msgid "Backup path:"
|
||||
msgstr "Percorso copia di sicurezza:"
|
||||
msgstr "Percorso backup:"
|
||||
|
||||
msgid "Restore bouquets"
|
||||
msgstr "Ripristina bouquet"
|
||||
@@ -617,7 +634,7 @@ msgid "Before downloading from the receiver"
|
||||
msgstr "Prima di scaricare dal ricevitore"
|
||||
|
||||
msgid "Set background color for the services"
|
||||
msgstr "Imposta colori di sfondo per i servizi"
|
||||
msgstr "Imposta colore di sfondo per i servizi"
|
||||
|
||||
msgid "Marked as new:"
|
||||
msgstr "Contrassegnato come nuovo:"
|
||||
@@ -629,7 +646,7 @@ msgid "Select"
|
||||
msgstr "Seleziona"
|
||||
|
||||
msgid "About"
|
||||
msgstr "A proposito"
|
||||
msgstr "Informazioni su"
|
||||
|
||||
msgid "Exit"
|
||||
msgstr "Esci"
|
||||
@@ -637,7 +654,7 @@ msgstr "Esci"
|
||||
msgid "Tools"
|
||||
msgstr "Strumenti"
|
||||
|
||||
#Import
|
||||
# Import
|
||||
msgid "Import"
|
||||
msgstr "Importa"
|
||||
|
||||
@@ -648,13 +665,13 @@ msgid "Bouquets and services"
|
||||
msgstr "Bouquet e servizi"
|
||||
|
||||
msgid "The main list does not contain services for this bouquet!"
|
||||
msgstr "La lista principale non contiene servizi per questo bouquet!"
|
||||
msgstr "L'elenco principale non contiene servizi per questo bouquet!"
|
||||
|
||||
msgid "No bouquet file is selected!"
|
||||
msgstr "Non è stato selezionato alcun file contenente bouquet"
|
||||
msgstr "Nessun file bouquet selezionato!"
|
||||
|
||||
msgid "Remove all unused"
|
||||
msgstr "Rimuovi tutti quelli non usati"
|
||||
msgstr "Rimuovi tutti quelli inutilizzati"
|
||||
|
||||
msgid "Test"
|
||||
msgstr "Test"
|
||||
@@ -663,7 +680,7 @@ msgid "Test connection"
|
||||
msgstr "Test connessione"
|
||||
|
||||
msgid "Double click on the service in the bouquet list:"
|
||||
msgstr "Doppio click sul servizio nella lista bouquet:"
|
||||
msgstr "Doppio clic sul servizio nell'elenco dei bouquet:"
|
||||
|
||||
msgid "Zap"
|
||||
msgstr "Zap"
|
||||
@@ -675,16 +692,16 @@ msgid "Disabled"
|
||||
msgstr "Disabilitato"
|
||||
|
||||
msgid "Enable lamedb ver. 5 support"
|
||||
msgstr "Abilita il supporto a lamedb v5"
|
||||
msgstr "Abilita supporto a lamedb v5"
|
||||
|
||||
msgid "Enable HTTP API"
|
||||
msgstr "Abilita le API HTTP"
|
||||
msgstr "Abilita API HTTP"
|
||||
|
||||
msgid "Switch(zap) the channel(Ctrl + Z)"
|
||||
msgstr "Cambia canale (Ctrl + Z)"
|
||||
msgstr "Cambia (zap) canale (Ctrl + Z)"
|
||||
|
||||
msgid "Switch the channel and watch in the program(Ctrl + W)"
|
||||
msgstr "Cambia canale e guarda il programma (Ctrl + W)."
|
||||
msgstr "Cambia canale e guarda nel programma (Ctrl + W)"
|
||||
|
||||
msgid "Play IPTV or other stream in the program(Ctrl + P)"
|
||||
msgstr "Riproduci IPTV o altro stream nel programma (Ctrl + P)"
|
||||
@@ -705,22 +722,22 @@ msgid "Service names source:"
|
||||
msgstr "Sorgente nomi dei servizi:"
|
||||
|
||||
msgid "Main service list"
|
||||
msgstr "Lista servizi principali:"
|
||||
msgstr "Elenco servizi principali:"
|
||||
|
||||
msgid "XML file"
|
||||
msgstr "File XML"
|
||||
|
||||
msgid "Use web source"
|
||||
msgstr "Usa sorgente Web"
|
||||
msgstr "Utilizza fonte web"
|
||||
|
||||
msgid "Url to *.xml.gz file:"
|
||||
msgstr "Da URL a file *.xml.gz:"
|
||||
|
||||
msgid "Enable filtering"
|
||||
msgstr "Abilita filtri"
|
||||
msgstr "Abilita filtro"
|
||||
|
||||
msgid "Filter by presence in the epg.dat file."
|
||||
msgstr "Filtra in base alla presenza su epg.dat."
|
||||
msgstr "Filtra per presenza nel file epg.dat."
|
||||
|
||||
msgid "Paths to the epg.dat file:"
|
||||
msgstr "Percorso file epg.dat:"
|
||||
@@ -738,10 +755,10 @@ msgid "Auto configuration by service names."
|
||||
msgstr "Configurazione automatica in base ai nomi dei servizi."
|
||||
|
||||
msgid "Save list to xml."
|
||||
msgstr "Salva lista in XML"
|
||||
msgstr "Salva elenco in XML."
|
||||
|
||||
msgid "Download XML file error."
|
||||
msgstr "Scarica file XML degli errori."
|
||||
msgstr "Errore di download del file XML."
|
||||
|
||||
msgid "Unsupported file type:"
|
||||
msgstr "Tipo di file non supportato:"
|
||||
@@ -755,20 +772,28 @@ msgstr "Errore analisi XML:"
|
||||
msgid "Count of successfully configured services:"
|
||||
msgstr "Conteggio servizi configurato correttamente:"
|
||||
|
||||
msgid "Current epg.dat file does not contains references for the services of this bouquet!"
|
||||
msgstr "Il file epg.dat non contiene riferimenti per i servizi di questo bouquet!"
|
||||
msgid ""
|
||||
"Current epg.dat file does not contains references for the services of this "
|
||||
"bouquet!"
|
||||
msgstr ""
|
||||
"L'attuale file epg.dat non contiene riferimenti per i servizi di questo"
|
||||
" bouquet!"
|
||||
|
||||
msgid "Use HTTP"
|
||||
msgstr "Usa HTTP"
|
||||
msgstr "Utilizza HTTP"
|
||||
|
||||
msgid "Close playback"
|
||||
msgstr "Ferma riproduzione"
|
||||
|
||||
msgid "Import YouTube playlist"
|
||||
msgstr "Importa playlist da YouTube"
|
||||
msgstr "Importa playlist YouTube"
|
||||
|
||||
msgid "Found a link to the YouTube resource!\nTry to get a direct link to the video?"
|
||||
msgstr "Trovato un link verso una risorsa YouTube!\nProvo ad ottenere un link diretto per questo video?"
|
||||
msgid ""
|
||||
"Found a link to the YouTube resource!\n"
|
||||
"Try to get a direct link to the video?"
|
||||
msgstr ""
|
||||
"Trovato un link verso una risorsa YouTube!\nProvo ad ottenere un link diretto"
|
||||
" per questo video?"
|
||||
|
||||
msgid "Playlist import"
|
||||
msgstr "Importa playlist"
|
||||
@@ -780,7 +805,7 @@ msgid "Extra"
|
||||
msgstr "Extra"
|
||||
|
||||
msgid "Apply profile settings"
|
||||
msgstr "Applica le impostazioni del profilo"
|
||||
msgstr "Applica impostazioni del profilo"
|
||||
|
||||
msgid "Settings type:"
|
||||
msgstr "Tipo di impostazioni:"
|
||||
@@ -792,13 +817,15 @@ msgid "Language:"
|
||||
msgstr "Lingua:"
|
||||
|
||||
msgid "Load the last open configuration at program startup"
|
||||
msgstr "Carica l'ultima configurazione utilizzata all'avvio"
|
||||
msgstr "Carica l'ultima configurazione aperta all'avvio del programma"
|
||||
|
||||
msgid "Enable direct playback bar"
|
||||
msgstr "Abilita barra di riproduzione diretta"
|
||||
|
||||
msgid "Enables direct sending and playback of media links on the receiver"
|
||||
msgstr "Abilita invio e riproduzione di links multimediali sul ricevitore"
|
||||
msgstr ""
|
||||
"Consenti l'invio diretto e la riproduzione di collegamenti multimediali sul"
|
||||
" ricevitore"
|
||||
|
||||
msgid "Watch the channel in the program"
|
||||
msgstr "Guarda canale nel programma"
|
||||
@@ -813,7 +840,7 @@ msgid "Remove added links in the playlist"
|
||||
msgstr "Rimuovi link aggiunti nella playlist"
|
||||
|
||||
msgid "A bouquet with that name exists!"
|
||||
msgstr "Nome bouquet già esistente!"
|
||||
msgstr "Esiste già un bouquet con quel nome!"
|
||||
|
||||
msgid "Details"
|
||||
msgstr "Dettagli"
|
||||
@@ -828,7 +855,7 @@ msgid "File"
|
||||
msgstr "File"
|
||||
|
||||
msgid "Picons manager"
|
||||
msgstr "Gestione picon"
|
||||
msgstr "Gestore picon"
|
||||
|
||||
msgid "Explorer"
|
||||
msgstr "Esplora"
|
||||
@@ -864,13 +891,13 @@ msgid "IPTV tools"
|
||||
msgstr "Strumenti IPTV"
|
||||
|
||||
msgid "Make profile folder as default for the additional data"
|
||||
msgstr "Imposta la cartella del profilo come predefinita per i profili aggiuntivi"
|
||||
msgstr "Imposta la cartella del profilo come predefinita per i dati aggiuntivi"
|
||||
|
||||
msgid "Default data path:"
|
||||
msgstr "Percorso predefinito per i dati:"
|
||||
msgstr "Percorso dati predefinito:"
|
||||
|
||||
msgid "Streams record path:"
|
||||
msgstr "Percorso per registrazioni da stream:"
|
||||
msgstr "Percorso registrazioni stream:"
|
||||
|
||||
msgid "Record"
|
||||
msgstr "Registra"
|
||||
@@ -888,7 +915,7 @@ msgid "Activate transcoding"
|
||||
msgstr "Attiva transcodifica"
|
||||
|
||||
msgid "Presets:"
|
||||
msgstr "Preimpostazioni:"
|
||||
msgstr "Profili:"
|
||||
|
||||
msgid "Video options:"
|
||||
msgstr "Opzioni video:"
|
||||
@@ -912,10 +939,10 @@ msgid "Channels:"
|
||||
msgstr "Canali:"
|
||||
|
||||
msgid "Sample rate (Hz):"
|
||||
msgstr "Frequenza di campionamento (Hz):"
|
||||
msgstr "Frequenza campionamento (Hz):"
|
||||
|
||||
msgid "Play streams mode:"
|
||||
msgstr "Modalità riprodzione stream:"
|
||||
msgstr "Modalità riproduzione stream:"
|
||||
|
||||
msgid "Built-in player"
|
||||
msgstr "Riproduttore integrato"
|
||||
@@ -930,10 +957,12 @@ msgid "Save and restart the program to apply the settings."
|
||||
msgstr "Salva e riavvia il programma per applicare le impostazioni."
|
||||
|
||||
msgid "Some images may have problems displaying the favorites list!"
|
||||
msgstr "Alcune immagini potrebbero avere dei problemi a visualizzare la lista dei preferiti!"
|
||||
msgstr ""
|
||||
"Alcune immagini potrebbero presentare problemi nella visualizzazione"
|
||||
" dell'elenco dei preferiti!"
|
||||
|
||||
msgid "Operates in standby mode or current active transponder!"
|
||||
msgstr "Funziona in standby o con il transponder attualmente attivo!"
|
||||
msgstr "Funziona in modalità standby o transponder attivo corrente!"
|
||||
|
||||
msgid "No connection to the receiver!"
|
||||
msgstr "Nessuna connessione con il ricevitore!"
|
||||
@@ -945,19 +974,21 @@ msgid "Receiver info"
|
||||
msgstr "Informazioni ricevitore"
|
||||
|
||||
msgid "A profile with that name exists!"
|
||||
msgstr "Nome del profilo già esistente!"
|
||||
msgstr "Esiste già un profilo con quel nome!"
|
||||
|
||||
msgid "Show short info as hints in the main services list"
|
||||
msgstr "Mostra suggerimenti nella lista principale dei servizi"
|
||||
msgstr ""
|
||||
"Mostra informazioni brevi come suggerimenti nell'elenco dei servizi principali"
|
||||
|
||||
msgid "Show detailed info as hints in the bouquet list"
|
||||
msgstr "Mostra informazioni dettagliate come suggerimenti nell'elenco dei bouquet"
|
||||
msgstr ""
|
||||
"Mostra informazioni dettagliate come suggerimenti nell'elenco dei bouquet"
|
||||
|
||||
msgid "Enable alternate bouquet file naming"
|
||||
msgstr "Abilita nomi alternativi bouquet"
|
||||
msgstr "Abilita denominazione alternativa file bouquet"
|
||||
|
||||
msgid "Allows you to name bouquet files using their names."
|
||||
msgstr "Permetti di nominare i files dei bouquet usando i nomi dei bouquet."
|
||||
msgstr "Ti permette di nominare i file del bouquet usando i loro nomi."
|
||||
|
||||
msgid "Appearance"
|
||||
msgstr "Aspetto"
|
||||
@@ -975,7 +1006,7 @@ msgid "Gtk3 Themes and Icons:"
|
||||
msgstr "Temi e icone Gtk3:"
|
||||
|
||||
msgid "Deleting data..."
|
||||
msgstr "Sto eliminando i dati..."
|
||||
msgstr "Eliminazione dati..."
|
||||
|
||||
msgid "Download from the receiver"
|
||||
msgstr "Scarica dal ricevitore"
|
||||
@@ -996,7 +1027,7 @@ msgid "Filter services"
|
||||
msgstr "Filtro servizi"
|
||||
|
||||
msgid "Filter services in the main list."
|
||||
msgstr "Filtra i servizi nella lista principale."
|
||||
msgstr "Filtra i servizi nell'elenco principale."
|
||||
|
||||
msgid "Destination:"
|
||||
msgstr "Destinazione:"
|
||||
@@ -1007,14 +1038,20 @@ msgstr "SPERIMENTALE!"
|
||||
msgid "Sorting data..."
|
||||
msgstr "Ordinamento dati..."
|
||||
|
||||
msgid "There are unsaved changes.\n\n\t Save them now?"
|
||||
msgstr "Ci sono cambiamenti non salvati.\n\n\t Vuoi salvarli adesso?"
|
||||
msgid ""
|
||||
"There are unsaved changes.\n"
|
||||
"\n"
|
||||
"\t Save them now?"
|
||||
msgstr "Sono presenti modifiche non salvate.\n\n\t Salvarle ora?"
|
||||
|
||||
msgid "Are you sure you want to change the order\n\t of services in this bouquet?"
|
||||
msgstr "Sei sicuro di voler cambiare l'ordine\n\t dei servizi di questo bouquet?"
|
||||
msgid ""
|
||||
"Are you sure you want to change the order\n"
|
||||
"\t of services in this bouquet?"
|
||||
msgstr ""
|
||||
"Sei sicuro di voler cambiare l'ordine\n\t dei servizi in questo bouquet?"
|
||||
|
||||
msgid "Remove from the receiver"
|
||||
msgstr "Elimina dal ricevitore"
|
||||
msgstr "Rimuovi dal ricevitore"
|
||||
|
||||
msgid "Screenshot"
|
||||
msgstr "Screenshot"
|
||||
@@ -1022,29 +1059,32 @@ msgstr "Screenshot"
|
||||
msgid "Video"
|
||||
msgstr "Video"
|
||||
|
||||
msgid "The Neutrino has only experimental support. Not all features are supported!"
|
||||
msgstr "Il Neutrino ha solo supporto sperimentale. Non tutte le funzionalità sono supportate!"
|
||||
msgid ""
|
||||
"The Neutrino has only experimental support. Not all features are supported!"
|
||||
msgstr ""
|
||||
"Il Neutrino ha solo supporto sperimentale. Non tutte le funzionalità sono"
|
||||
" supportate!"
|
||||
|
||||
msgid "Enable experimental features"
|
||||
msgstr "Abilita caratteristiche sperimentali"
|
||||
msgstr "Abilita funzionalità sperimentali"
|
||||
|
||||
msgid "Can't Playback!"
|
||||
msgstr "Riproduzione impossibile!"
|
||||
msgstr "Impossibile riprodurre!"
|
||||
|
||||
msgid "Enable Dark Mode"
|
||||
msgstr "Abilita modalità scura"
|
||||
|
||||
msgid "Extract..."
|
||||
msgstr "Estrai file compresso"
|
||||
msgstr "Estrai..."
|
||||
|
||||
msgid "Unsupported format!"
|
||||
msgstr "Formato non supportato!"
|
||||
|
||||
msgid "Combine with the current data?"
|
||||
msgstr "Combino con i dati attuali?"
|
||||
msgstr "Combinare con i dati attuali?"
|
||||
|
||||
msgid "Importing data done!"
|
||||
msgstr "Importazione dati effettuata!"
|
||||
msgstr "Importazione dati completata!"
|
||||
|
||||
msgid "Current service"
|
||||
msgstr "Servizio attuale"
|
||||
@@ -1056,10 +1096,10 @@ msgid "Open archive"
|
||||
msgstr "Apri archivio"
|
||||
|
||||
msgid "Import from Web"
|
||||
msgstr "Importa dal Web"
|
||||
msgstr "Importa dal web"
|
||||
|
||||
msgid "Control"
|
||||
msgstr "Controlli"
|
||||
msgstr "Controllo"
|
||||
|
||||
msgid "Timers"
|
||||
msgstr "Timer"
|
||||
@@ -1077,7 +1117,7 @@ msgid "Min."
|
||||
msgstr "Min."
|
||||
|
||||
msgid "Power"
|
||||
msgstr "Accendi"
|
||||
msgstr "Stato"
|
||||
|
||||
msgid "Standby"
|
||||
msgstr "Standby"
|
||||
@@ -1119,7 +1159,7 @@ msgid "Service:"
|
||||
msgstr "Servizio:"
|
||||
|
||||
msgid "Service reference:"
|
||||
msgstr "Servizio di riferimento:"
|
||||
msgstr "Riferimento servizio:"
|
||||
|
||||
msgid "Event ID:"
|
||||
msgstr "ID evento:"
|
||||
@@ -1176,7 +1216,7 @@ msgid "FTP client"
|
||||
msgstr "Client FTP"
|
||||
|
||||
msgid "The file size is too large!"
|
||||
msgstr "Dimensione file eccessiva!"
|
||||
msgstr "La dimensione del file è troppo grande!"
|
||||
|
||||
msgid "Connect"
|
||||
msgstr "Соnnetti"
|
||||
@@ -1203,13 +1243,19 @@ msgid "DreamOS only!"
|
||||
msgstr "Solo DreamOS!"
|
||||
|
||||
msgid "A similar service is already in this list!"
|
||||
msgstr "Un servizio simile è già presente nella lista!"
|
||||
msgstr "Un servizio simile è già presente nell'elenco!"
|
||||
|
||||
msgid "Play mode has been changed!\nRestart the program to apply the settings."
|
||||
msgstr "La modalitá di riproduzione è stata cambiata!\nRiavvia il programma per applicare i cambiamenti."
|
||||
msgid ""
|
||||
"Play mode has been changed!\n"
|
||||
"Restart the program to apply the settings."
|
||||
msgstr ""
|
||||
"La modalità di riproduzione è stata modificata!\nRiavvia il programma per"
|
||||
" applicare le impostazioni."
|
||||
|
||||
msgid "Set values for TID, NID and Namespace for correct naming of the picon!"
|
||||
msgstr "Stabilisci i valori TID, NID e Namespace per nominare correttamente i picon!"
|
||||
msgid "Set values for TID, NID and Namespace for correct naming of the picons!"
|
||||
msgstr ""
|
||||
"Imposta i valori per TID, NID e Namespace per la corretta denominazione dei"
|
||||
" picon!"
|
||||
|
||||
msgid "Streams detected:"
|
||||
msgstr "Rilevati stream:"
|
||||
@@ -1221,28 +1267,28 @@ msgid "Errors:"
|
||||
msgstr "Errori:"
|
||||
|
||||
msgid "Use to play streams:"
|
||||
msgstr "Riproduci gli stream con:"
|
||||
msgstr "Riproduci stream con:"
|
||||
|
||||
msgid "Font in the lists:"
|
||||
msgstr "Carattere nelle liste:"
|
||||
msgstr "Carattere negli elenchi:"
|
||||
|
||||
msgid "Picons size in the lists:"
|
||||
msgstr "Dimensione picon nelle liste:"
|
||||
msgstr "Dimensione picon negli elenchi:"
|
||||
|
||||
msgid "Logo size in tooltips:"
|
||||
msgstr "Dimensione logo nei suggerimenti:"
|
||||
msgstr "Dimensioni logo nelle descrizioni comandi:"
|
||||
|
||||
msgid "Save as"
|
||||
msgstr "Salva come"
|
||||
|
||||
msgid "Mark duplicates"
|
||||
msgstr "Contrassegna i duplicati"
|
||||
msgstr "Contrassegna duplicati"
|
||||
|
||||
msgid "Load only for selected bouquet"
|
||||
msgstr "Scarica solo per i bouquet selezionati"
|
||||
|
||||
msgid "The task is canceled!"
|
||||
msgstr "Operazione cancellata!"
|
||||
msgstr "L'attività è stata annullata!"
|
||||
|
||||
msgid "Data loading in progress!"
|
||||
msgstr "Caricamento dati in corso!"
|
||||
@@ -1257,7 +1303,7 @@ msgid "Help"
|
||||
msgstr "Aiuto"
|
||||
|
||||
msgid "HTTP API is not activated. Check your settings!"
|
||||
msgstr "Le API HTTP non sono attivate. Controlla le tue impostazioni!"
|
||||
msgstr "API HTTP non attivata. Controlla le tue impostazioni!"
|
||||
|
||||
msgid "Add picons"
|
||||
msgstr "Aggiungi picon"
|
||||
@@ -1269,7 +1315,7 @@ msgid "Title"
|
||||
msgstr "Titolo"
|
||||
|
||||
msgid "Time"
|
||||
msgstr "Time"
|
||||
msgstr "Orario"
|
||||
|
||||
msgid "Length"
|
||||
msgstr "Durata"
|
||||
@@ -1304,20 +1350,26 @@ msgstr "Proporzioni"
|
||||
msgid "This may change the settings of other profiles!"
|
||||
msgstr "Questo potrebbe modificare le impostazioni di altri profili!"
|
||||
|
||||
msgid "Drag the services to the desired picon or picon to the list of selected services."
|
||||
msgstr "Trascina i servizi sul picon desiderato o il picon sull'elenco dei servizi selezionati."
|
||||
msgid ""
|
||||
"Drag the services to the desired picon or picon to the list of selected "
|
||||
"services."
|
||||
msgstr ""
|
||||
"Trascina i servizi sul picon desiderato o il picon sull'elenco dei servizi"
|
||||
" selezionati."
|
||||
|
||||
msgid "Sets the profile folder as default to store picons, backups, etc."
|
||||
msgstr "Imposta la cartella del profilo come predefinita per memorizzare picon, backups, etc."
|
||||
msgstr ""
|
||||
"Imposta la cartella del profilo come predefinita per memorizzare picon,"
|
||||
" backup, ecc."
|
||||
|
||||
msgid "New sub-bouquet"
|
||||
msgstr "Nuovo sotto-bouquet"
|
||||
|
||||
msgid "Mark not presented in Bouquets"
|
||||
msgstr "Seleziona servizi non presenti nei bouquet"
|
||||
msgstr "Contrassegna non presenti nei bouquet"
|
||||
|
||||
msgid "Not in Bouquets"
|
||||
msgstr "Non è nei bouquet"
|
||||
msgstr "Non nei bouquet"
|
||||
|
||||
msgid "Do not show services present in Bouquets."
|
||||
msgstr "Non mostrare servizi presenti nei bouquet."
|
||||
@@ -1353,10 +1405,12 @@ msgid "All bouquets"
|
||||
msgstr "Tutti i bouquet"
|
||||
|
||||
msgid "Playback from the main list"
|
||||
msgstr "Riproduci dalla lista principale"
|
||||
msgstr "Riproduci dall'elenco principale"
|
||||
|
||||
msgid "Enables URL parsing using youtube-dl to get direct links to media."
|
||||
msgstr "Abilita l'analisi degli URL utilizzando youtube-dl per ottenere collegamenti diretti ai media."
|
||||
msgstr ""
|
||||
"Abilita l'analisi degli URL utilizzando youtube-dl per ottenere collegamenti"
|
||||
" diretti ai media."
|
||||
|
||||
msgid "Permissions..."
|
||||
msgstr "Permessi..."
|
||||
@@ -1383,4 +1437,56 @@ msgid "Daily"
|
||||
msgstr "Quotidiano"
|
||||
|
||||
msgid "Assign reference"
|
||||
msgstr "Assegna riferimento"
|
||||
msgstr "Assegna riferimento"
|
||||
|
||||
msgid "Specify hostname or IP address"
|
||||
msgstr "Specifica il nome host o l'indirizzo IP"
|
||||
|
||||
msgid "Default selection"
|
||||
msgstr "Selezione predefinita"
|
||||
|
||||
msgid "Don't change power state"
|
||||
msgstr "Non modificare lo stato di alimentazione"
|
||||
|
||||
msgid "Don't toggle standby mode when updating bouquets and services."
|
||||
msgstr ""
|
||||
"Non attivare la modalità standby durante l'aggiornamento di bouquet e servizi."
|
||||
|
||||
msgid "Region"
|
||||
msgstr "Regione"
|
||||
|
||||
msgid "Provider"
|
||||
msgstr "Provider"
|
||||
|
||||
msgid ""
|
||||
"Enables upload as an archive if a large number of picon (> 1000) is"
|
||||
" selected.\n"
|
||||
" Recommended only if you have external storage."
|
||||
msgstr ""
|
||||
"Abilita il caricamento come archivio compresso se viene selezionato un numero"
|
||||
" elevato di picon (> 1000). Consigliato solo se si dispone di memoria esterna."
|
||||
|
||||
msgid "Clear \"New\" flag"
|
||||
msgstr "Rimuovi il flag \"Nuovo\""
|
||||
|
||||
msgid "Group by"
|
||||
msgstr "Raggruppa per"
|
||||
|
||||
msgid "Replace existing"
|
||||
msgstr "Sostituisci esistente"
|
||||
|
||||
msgid "Already exists"
|
||||
msgstr "Esiste già"
|
||||
|
||||
msgid "Enable unlimited copy buffer"
|
||||
msgstr "Abilita buffer di copia illimitato"
|
||||
|
||||
msgid "Enables unlimited copy buffer for the bouquets tab."
|
||||
msgstr "Abilita buffer di copia illimitato per la scheda bouquet."
|
||||
|
||||
msgid "Start time"
|
||||
msgstr "Ora di inizio"
|
||||
|
||||
msgid "End time"
|
||||
msgstr "Ora di fine"
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# Copyright (C) 2018-2020 Frank Neirynck
|
||||
# This file is distributed under the MIT license.
|
||||
#
|
||||
# Frank Neirynck <frank@insink.be>, 2018-2020.
|
||||
# Frank Neirynck <frank@insink.be>, 2018-2022.
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
@@ -25,7 +25,7 @@ msgid "Type"
|
||||
msgstr "Type"
|
||||
|
||||
msgid "Picon"
|
||||
msgstr "Picon"
|
||||
msgstr "Pict."
|
||||
|
||||
msgid "Freq"
|
||||
msgstr "Freq."
|
||||
@@ -148,10 +148,10 @@ msgid "Parent lock On/Off Ctrl + L"
|
||||
msgstr "Ouderlijk slot aan/uit Ctrl + L"
|
||||
|
||||
msgid "Picons"
|
||||
msgstr "Picons"
|
||||
msgstr "Pictogrammen"
|
||||
|
||||
msgid "Picons downloader"
|
||||
msgstr "Picons downloader"
|
||||
msgstr "Pictogrammen downloader"
|
||||
|
||||
msgid "Satellites downloader"
|
||||
msgstr "Satellieten downloader"
|
||||
@@ -286,22 +286,22 @@ msgid "Providers"
|
||||
msgstr "Leveranciers"
|
||||
|
||||
msgid "Receive picons"
|
||||
msgstr "Ontvang picons"
|
||||
msgstr "Ontvang pictogrammen"
|
||||
|
||||
msgid "Picons name format:"
|
||||
msgstr "Picons naam formaat:"
|
||||
msgstr "Pictogrammen naam formaat:"
|
||||
|
||||
msgid "Resize:"
|
||||
msgstr "Verklein/vergroot:"
|
||||
|
||||
msgid "Current picons path:"
|
||||
msgstr "Huidig pad picons:"
|
||||
msgstr "Huidig pad Pictogrammen:"
|
||||
|
||||
msgid "Receiver picons path:"
|
||||
msgstr "Ontvanger picons pad:"
|
||||
msgstr "Ontvanger Pictogrammen pad:"
|
||||
|
||||
msgid "Picons download tool"
|
||||
msgstr "Picons download gereedschap"
|
||||
msgstr "Pictogrammen download gereedschap"
|
||||
|
||||
msgid "Transfer to receiver"
|
||||
msgstr "Transfereren naar ontvanger"
|
||||
@@ -319,7 +319,7 @@ msgid "Path to save:"
|
||||
msgstr "Pad om op te slaan:"
|
||||
|
||||
msgid "Path to Enigma2 picons:"
|
||||
msgstr "Pad naar Enigma2 picons:"
|
||||
msgstr "Pad naar Enigma2 Pictogrammen:"
|
||||
|
||||
msgid "Specify the correct position value for the provider!"
|
||||
msgstr "Geeft de juiste positie waarde voor de provider!"
|
||||
@@ -328,16 +328,16 @@ msgid "Converter between name formats"
|
||||
msgstr "Omvormer tussen naam formaten"
|
||||
|
||||
msgid "Receive picons for providers"
|
||||
msgstr "Ontvang picons voor leveranciers"
|
||||
msgstr "Ontvang Pictogrammen voor providers"
|
||||
|
||||
msgid "Load satellite providers."
|
||||
msgstr "Laad satelliet leveranciers."
|
||||
msgstr "Laad satelliet providers."
|
||||
|
||||
msgid ""
|
||||
"To automatically set the identifiers for picons,\n"
|
||||
"first load the required services list into the main application window."
|
||||
msgstr ""
|
||||
"Om automatisch de ID in te stellen voor picons,\n"
|
||||
"Om automatisch de ID in te stellen voor pictogrammen,\n"
|
||||
"laad eerst de vereiste serviceslijst in via het hoofdvenster van het programma."
|
||||
|
||||
# Satellites editor
|
||||
@@ -462,7 +462,7 @@ msgid "Password:"
|
||||
msgstr "Paswoord:"
|
||||
|
||||
msgid "Picons:"
|
||||
msgstr "Picons:"
|
||||
msgstr "Pictogrammen:"
|
||||
|
||||
msgid "Port:"
|
||||
msgstr "Poort:"
|
||||
@@ -471,7 +471,7 @@ msgid "Data path:"
|
||||
msgstr "Gegevenspad:"
|
||||
|
||||
msgid "Picons path:"
|
||||
msgstr "Picons pad:"
|
||||
msgstr "Pictogrammen pad:"
|
||||
|
||||
msgid "Network settings:"
|
||||
msgstr "Netwerk instellingen:"
|
||||
@@ -825,7 +825,7 @@ msgid "File"
|
||||
msgstr "File"
|
||||
|
||||
msgid "Picons manager"
|
||||
msgstr "Picons manager"
|
||||
msgstr "Pictogrammen manager"
|
||||
|
||||
msgid "Explorer"
|
||||
msgstr "Explorer"
|
||||
@@ -1013,3 +1013,391 @@ msgstr "Schermafbeelding"
|
||||
msgid "Video"
|
||||
msgstr "Vidео"
|
||||
|
||||
msgid "The Neutrino has only experimental support. Not all features are supported!"
|
||||
msgstr "Neutrino heeft alleen experimentele ondersteuning. Niet alle functies worden ondersteund!"
|
||||
|
||||
msgid "Enable experimental features"
|
||||
msgstr "Schakel experimentele functies in"
|
||||
|
||||
msgid "Can't Playback!"
|
||||
msgstr "Kan niet afspelen!"
|
||||
|
||||
msgid "Enable Dark Mode"
|
||||
msgstr "Schakel de donkere modus in"
|
||||
|
||||
msgid "Extract..."
|
||||
msgstr "Extract..."
|
||||
|
||||
msgid "Unsupported format!"
|
||||
msgstr "Niet ondersteund formaat!"
|
||||
|
||||
msgid "Combine with the current data?"
|
||||
msgstr "Combineren met de huidige gegevens?"
|
||||
|
||||
msgid "Importing data done!"
|
||||
msgstr "Gegevens importeren klaar!"
|
||||
|
||||
msgid "Current service"
|
||||
msgstr "Huidige dienst"
|
||||
|
||||
msgid "Open folder"
|
||||
msgstr "Open Map"
|
||||
|
||||
msgid "Open archive"
|
||||
msgstr "Open Archief"
|
||||
|
||||
msgid "Import from Web"
|
||||
msgstr "Importeren van internet"
|
||||
|
||||
msgid "Control"
|
||||
msgstr "Controle"
|
||||
|
||||
msgid "Timers"
|
||||
msgstr "Timers"
|
||||
|
||||
msgid "Timer"
|
||||
msgstr "Timer"
|
||||
|
||||
msgid "Add timer"
|
||||
msgstr "Timer toevoegen"
|
||||
|
||||
msgid "Hr."
|
||||
msgstr "U."
|
||||
|
||||
msgid "Min."
|
||||
msgstr "мin."
|
||||
|
||||
msgid "Power"
|
||||
msgstr "Power"
|
||||
|
||||
msgid "Standby"
|
||||
msgstr "Standby"
|
||||
|
||||
msgid "Wake Up"
|
||||
msgstr "Wake Up"
|
||||
|
||||
msgid "Reboot"
|
||||
msgstr "Herstart"
|
||||
|
||||
msgid "Restart GUI"
|
||||
msgstr "Herstart GUI"
|
||||
|
||||
msgid "Shutdown"
|
||||
msgstr "Uitschakelen"
|
||||
|
||||
msgid "Shut down"
|
||||
msgstr "Uitschakelen"
|
||||
|
||||
msgid "Do Nothing"
|
||||
msgstr "Doe niets"
|
||||
|
||||
msgid "Auto"
|
||||
msgstr "Auto"
|
||||
|
||||
msgid "Grab screenshot"
|
||||
msgstr "Maak schermafbeelding"
|
||||
|
||||
msgid "Enabled:"
|
||||
msgstr "Ingeschakeld:"
|
||||
|
||||
msgid "Name:"
|
||||
msgstr "Naam:"
|
||||
|
||||
msgid "Description:"
|
||||
msgstr "Omschrijving:"
|
||||
|
||||
msgid "Service:"
|
||||
msgstr "Dienst:"
|
||||
|
||||
msgid "Service reference:"
|
||||
msgstr "Service referentie:"
|
||||
|
||||
msgid "Event ID:"
|
||||
msgstr "Event ID:"
|
||||
|
||||
msgid "Begins:"
|
||||
msgstr "Begint:"
|
||||
|
||||
msgid "Ends:"
|
||||
msgstr "Stopt:"
|
||||
|
||||
msgid "Repeated:"
|
||||
msgstr "Herhaald:"
|
||||
|
||||
msgid "Action:"
|
||||
msgstr "Actie:"
|
||||
|
||||
msgid "After event:"
|
||||
msgstr "Na Event:"
|
||||
|
||||
msgid "Location:"
|
||||
msgstr "Locatiе:"
|
||||
|
||||
msgid "Mo"
|
||||
msgstr "Ma"
|
||||
|
||||
msgid "Tu"
|
||||
msgstr "Di"
|
||||
|
||||
msgid "We"
|
||||
msgstr "Wo"
|
||||
|
||||
msgid "Th"
|
||||
msgstr "Do"
|
||||
|
||||
msgid "Fr"
|
||||
msgstr "Vr"
|
||||
|
||||
msgid "Sa"
|
||||
msgstr "Za"
|
||||
|
||||
msgid "Su"
|
||||
msgstr "Zo"
|
||||
|
||||
msgid "Set"
|
||||
msgstr "Set"
|
||||
|
||||
msgid "Services update"
|
||||
msgstr "Services-update"
|
||||
|
||||
msgid "Create folder"
|
||||
msgstr "Map aanmaken"
|
||||
|
||||
msgid "FTP client"
|
||||
msgstr "FTP client"
|
||||
|
||||
msgid "The file size is too large!"
|
||||
msgstr "De bestandsgrootte is te groot!"
|
||||
|
||||
msgid "Connect"
|
||||
msgstr "Verbind"
|
||||
|
||||
msgid "Disconnect"
|
||||
msgstr "Verbreek Verbinding"
|
||||
|
||||
msgid "Size"
|
||||
msgstr "Grootte"
|
||||
|
||||
msgid "Date"
|
||||
msgstr "Datum"
|
||||
|
||||
msgid "Toggle display position"
|
||||
msgstr "Weergavepositie wisselen"
|
||||
|
||||
msgid "Alternatives"
|
||||
msgstr "Alternatieven"
|
||||
|
||||
msgid "Add alternatives"
|
||||
msgstr "Alternatieven toevoegen"
|
||||
|
||||
msgid "DreamOS only!"
|
||||
msgstr "Enkel DreamOS!"
|
||||
|
||||
msgid "A similar service is already in this list!"
|
||||
msgstr "Een vergelijkbare service staat al in deze lijst!"
|
||||
|
||||
msgid "Play mode has been changed!\nRestart the program to apply the settings."
|
||||
msgstr "De afspeelmodus is gewijzigd!\nStart het programma opnieuw om de instellingen toe te passen."
|
||||
|
||||
msgid "Set values for TID, NID and Namespace for correct naming of the picons!"
|
||||
msgstr "Stel waarden in voor TID, NID en Namespace voor correcte naamgeving van de pictogrammen!"
|
||||
|
||||
msgid "Streams detected:"
|
||||
msgstr "Streams gedetecteerd:"
|
||||
|
||||
msgid "Download picons"
|
||||
msgstr "Pictogrammen downloaden"
|
||||
|
||||
msgid "Errors:"
|
||||
msgstr "Fouten:"
|
||||
|
||||
msgid "Use to play streams:"
|
||||
msgstr "Gebruik om streams af te spelen:"
|
||||
|
||||
msgid "Font in the lists:"
|
||||
msgstr "Lettertype in de lijsten:"
|
||||
|
||||
msgid "Picons size in the lists:"
|
||||
msgstr "Pictogramgrootte in de lijsten:"
|
||||
|
||||
msgid "Logo size in tooltips:"
|
||||
msgstr "Logogrootte in knopinfo:"
|
||||
|
||||
msgid "Save as"
|
||||
msgstr "Opslaan als"
|
||||
|
||||
msgid "Mark duplicates"
|
||||
msgstr "Markeer duplicaten"
|
||||
|
||||
msgid "Load only for selected bouquet"
|
||||
msgstr "Laad alleen voor geselecteerd boeket"
|
||||
|
||||
msgid "The task is canceled!"
|
||||
msgstr "De taak is geannuleerd!"
|
||||
|
||||
msgid "Data loading in progress!"
|
||||
msgstr "Gegevens worden geladen!"
|
||||
|
||||
msgid "Recordings"
|
||||
msgstr "Opnames"
|
||||
|
||||
msgid "Recordings:"
|
||||
msgstr "Opnames:"
|
||||
|
||||
msgid "Help"
|
||||
msgstr "Help"
|
||||
|
||||
msgid "HTTP API is not activated. Check your settings!"
|
||||
msgstr "HTTP-API is niet geactiveerd. Controleer uw instellingen!"
|
||||
|
||||
msgid "Add picons"
|
||||
msgstr "Pictogrammen toevoegen"
|
||||
|
||||
msgid "Logs"
|
||||
msgstr "Logs"
|
||||
|
||||
msgid "Title"
|
||||
msgstr "Titel"
|
||||
|
||||
msgid "Time"
|
||||
msgstr "Tijd"
|
||||
|
||||
msgid "Length"
|
||||
msgstr "Lengte"
|
||||
|
||||
msgid "Additional source"
|
||||
msgstr "Extra bron"
|
||||
|
||||
msgid "Automatically set the name selected in the favorites list."
|
||||
msgstr "Stel automatisch de geselecteerde naam in de favorietenlijst in."
|
||||
|
||||
msgid "Playback"
|
||||
msgstr "Weergave"
|
||||
|
||||
msgid "Playback:"
|
||||
msgstr "Weergave:"
|
||||
|
||||
msgid "Audio"
|
||||
msgstr "Audio"
|
||||
|
||||
msgid "Audio Track"
|
||||
msgstr "Audio Spoor"
|
||||
|
||||
msgid "Subtitle"
|
||||
msgstr "Ondertitel"
|
||||
|
||||
msgid "Subtitle Track"
|
||||
msgstr "Ondertitel Spoor"
|
||||
|
||||
msgid "Aspect ratio"
|
||||
msgstr "Beeldverhouding"
|
||||
|
||||
msgid "This may change the settings of other profiles!"
|
||||
msgstr "Hierdoor kunnen de instellingen van andere profielen veranderen!"
|
||||
|
||||
msgid "Drag the services to the desired picon or picon to the list of selected services."
|
||||
msgstr "Sleep de services naar het gewenste pictogram of het pictogram naar de lijst met geselecteerde services."
|
||||
|
||||
msgid "Sets the profile folder as default to store picons, backups, etc."
|
||||
msgstr "Stelt de profielmap in als standaard om pictogrammen, back-ups, etc. op te slaan."
|
||||
|
||||
msgid "New sub-bouquet"
|
||||
msgstr "Nieuw sub-boeket"
|
||||
|
||||
msgid "Mark not presented in Bouquets"
|
||||
msgstr "Stel in als niet in Boeketten"
|
||||
|
||||
msgid "Not in Bouquets"
|
||||
msgstr "Niet in Boeketten"
|
||||
|
||||
msgid "Do not show services present in Bouquets."
|
||||
msgstr "Laat geen services zien die aanwezig zijn in Boeketten."
|
||||
|
||||
msgid "IPTV services only"
|
||||
msgstr "Enkel IPTV diensten"
|
||||
|
||||
msgid "Display picons"
|
||||
msgstr "Toon pictogrammen"
|
||||
|
||||
msgid "Alternate layout"
|
||||
msgstr "Alternatieve indeling"
|
||||
|
||||
msgid "Layout of elements has been changed!"
|
||||
msgstr "Lay-out van elementen is gewijzigd!"
|
||||
|
||||
msgid "Restart the program to apply all changes."
|
||||
msgstr "Start het programma opnieuw om alle wijzigingen toe te passen."
|
||||
|
||||
msgid "New folder"
|
||||
msgstr "Nieuwe map"
|
||||
|
||||
msgid "Rename"
|
||||
msgstr "Hernoem"
|
||||
|
||||
msgid "Bookmarks"
|
||||
msgstr "Bladwijzers"
|
||||
|
||||
msgid "Add bookmark"
|
||||
msgstr "Bladwijzer toevoegen"
|
||||
|
||||
msgid "All bouquets"
|
||||
msgstr "Alle boeketten"
|
||||
|
||||
msgid "Playback from the main list"
|
||||
msgstr "Afspelen vanuit de hoofdlijst"
|
||||
|
||||
msgid "Enables URL parsing using youtube-dl to get direct links to media."
|
||||
msgstr "Schakelt URL-parsing met behulp van youtube-dl in, om directe links naar media te krijgen."
|
||||
|
||||
msgid "Permissions..."
|
||||
msgstr "Rechten..."
|
||||
|
||||
msgid "Display EPG in bouquet list"
|
||||
msgstr "Toon EPG in boeketlijst"
|
||||
|
||||
msgid "EPG *.dat file:"
|
||||
msgstr "EPG *.dat file:"
|
||||
|
||||
msgid "Use HTTP to reload data in the receiver"
|
||||
msgstr "Gebruik HTTP om gegevens opnieuw in de ontvanger te laden"
|
||||
|
||||
msgid "Enable picons compression"
|
||||
msgstr "Pictogrammen-compressie inschakelen"
|
||||
|
||||
msgid "Update interval (sec):"
|
||||
msgstr "Update-interval (sec):"
|
||||
|
||||
msgid "Update:"
|
||||
msgstr "Update:"
|
||||
|
||||
msgid "Daily"
|
||||
msgstr "Dagelijks"
|
||||
|
||||
msgid "Assign reference"
|
||||
msgstr "Referentie toewijzen"
|
||||
|
||||
msgid "Specify hostname or IP address"
|
||||
msgstr "Geef de hostnaam of het IP-adres op"
|
||||
|
||||
msgid "Default selection"
|
||||
msgstr "Standaard selectie"
|
||||
|
||||
msgid "Don't change power state"
|
||||
msgstr "Schakel het toestel niet uit"
|
||||
|
||||
msgid "Don't toggle standby mode when updating bouquets and services."
|
||||
msgstr "Schakel de stand-bymodus niet in bij het updaten van boeketten en services."
|
||||
|
||||
msgid "Region"
|
||||
msgstr "Regio"
|
||||
|
||||
msgid "Provider"
|
||||
msgstr "Provider"
|
||||
|
||||
msgid "Enables upload as an archive if a large number of picon (> 1000) is selected.\n"
|
||||
" Recommended only if you have external storage."
|
||||
msgstr "Maakt uploaden als archief mogelijk als een groot aantal pictogrammen (> 1000) is geselecteerd.\n"
|
||||
" Alleen aanbevolen als u externe opslag heeft."
|
||||
|
||||
msgid "Clear \"New\" flag"
|
||||
msgstr "Opruimen \"Niew\" flag"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Copyright (C) 2018-2022 Dmitriy Yefremov
|
||||
# Copyright (C) 2018-2023 Dmitriy Yefremov
|
||||
# This file is distributed under the MIT license.
|
||||
#
|
||||
#
|
||||
@@ -1384,3 +1384,50 @@ msgstr "Ежедневно"
|
||||
|
||||
msgid "Assign reference"
|
||||
msgstr "Присвоить ссылку"
|
||||
|
||||
msgid "Specify hostname or IP address"
|
||||
msgstr "Укажите имя хоста или IP-адрес"
|
||||
|
||||
msgid "Default selection"
|
||||
msgstr "Выбор по умолчанию"
|
||||
|
||||
msgid "Don't change power state"
|
||||
msgstr "Не изменять состояние питания"
|
||||
|
||||
msgid "Don't toggle standby mode when updating bouquets and services."
|
||||
msgstr "Не переключать в режим ожидания при обновлении букетов и сервисов."
|
||||
|
||||
msgid "Region"
|
||||
msgstr "Регион"
|
||||
|
||||
msgid "Provider"
|
||||
msgstr "Провайдер"
|
||||
|
||||
msgid "Enables upload as an archive if a large number of picon (> 1000) is selected.\n"
|
||||
" Recommended only if you have external storage."
|
||||
msgstr "Включает загрузку в виде архива, если выбрано большое количество пиконов (> 1000).\n"
|
||||
" Рекомендуется, только если у вас есть внешнее хранилище."
|
||||
|
||||
msgid "Clear \"New\" flag"
|
||||
msgstr "Очистить флаг \"New\""
|
||||
|
||||
msgid "Group by"
|
||||
msgstr "Группировать по"
|
||||
|
||||
msgid "Replace existing"
|
||||
msgstr "Заменить существующие"
|
||||
|
||||
msgid "Already exists"
|
||||
msgstr "Уже существует"
|
||||
|
||||
msgid "Enable unlimited copy buffer"
|
||||
msgstr "Включить неограниченный буфер копирования"
|
||||
|
||||
msgid "Enables unlimited copy buffer for the bouquets tab."
|
||||
msgstr "Включает неограниченный буфер копирования для вкладки букетов."
|
||||
|
||||
msgid "Start time"
|
||||
msgstr "Начало"
|
||||
|
||||
msgid "End time"
|
||||
msgstr "Окончание"
|
||||
|
||||
@@ -3,7 +3,7 @@ msgstr ""
|
||||
"Project-Id-Version: DemonEditor\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2020-04-16 15:59+0300\n"
|
||||
"PO-Revision-Date: 2022-08-27 23:17+0300\n"
|
||||
"PO-Revision-Date: 2023-01-13 21:54+0300\n"
|
||||
"Last-Translator: audi06_19 <info@dreamosat-forum.com>\n"
|
||||
"Language-Team: \n"
|
||||
"Language: tr\n"
|
||||
@@ -1416,3 +1416,34 @@ msgstr "Günlük"
|
||||
|
||||
msgid "Assign reference"
|
||||
msgstr "Referans ata"
|
||||
|
||||
msgid "Specify hostname or IP address"
|
||||
msgstr "Ana bilgisayar adını veya IP adresini belirtin"
|
||||
|
||||
msgid "Default selection"
|
||||
msgstr "Varsayılan seçim"
|
||||
|
||||
msgid "Don't change power state"
|
||||
msgstr "Güç durumunu değiştirme"
|
||||
|
||||
msgid "Don't toggle standby mode when updating bouquets and services."
|
||||
msgstr "Buketleri ve servisleri güncellerken bekleme moduna geçmeyin."
|
||||
|
||||
msgid "Region"
|
||||
msgstr "Bölge"
|
||||
|
||||
msgid "Provider"
|
||||
msgstr "Sağlayıcı"
|
||||
|
||||
msgid ""
|
||||
"Enables upload as an archive if a large number of picon (> 1000) is selected.\n"
|
||||
" Recommended only if you have external storage."
|
||||
msgstr ""
|
||||
"Çok sayıda picon (> 1000) seçilirse arşiv olarak yüklemeyi etkinleştirir.\n"
|
||||
" Yalnızca harici depolamanız varsa önerilir."
|
||||
|
||||
msgid "Clear \"New\" flag"
|
||||
msgstr "\"Yeni\" bayrağını temizleyin"
|
||||
|
||||
msgid "Group by"
|
||||
msgstr ""
|
||||
|
||||
Reference in New Issue
Block a user