mirror of
https://github.com/DYefremov/DemonEditor.git
synced 2026-05-09 10:25:36 +02:00
Compare commits
41 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3dab8ef7b7 | ||
|
|
dd1a543e5c | ||
|
|
0966489024 | ||
|
|
052187359d | ||
|
|
6ca6867ea9 | ||
|
|
d9cdc6458c | ||
|
|
70b9851324 | ||
|
|
2a3b558d83 | ||
|
|
21ea841f34 | ||
|
|
1db0ce3fc5 | ||
|
|
2804a9bc54 | ||
|
|
8976f42974 | ||
|
|
8330104f3c | ||
|
|
3ede2e2b07 | ||
|
|
dd796c0f88 | ||
|
|
f3a432c002 | ||
|
|
4c1cdc4850 | ||
|
|
a062d74e0e | ||
|
|
7bf36c8d6d | ||
|
|
6aad0344c8 | ||
|
|
bb4665d180 | ||
|
|
a455c4569d | ||
|
|
8d5af301fb | ||
|
|
f342b99769 | ||
|
|
a0612e6a98 | ||
|
|
60b8f7642d | ||
|
|
8730cbdb7c | ||
|
|
cefb96ea20 | ||
|
|
b383c2572a | ||
|
|
ee2fcf8082 | ||
|
|
44234fa534 | ||
|
|
4bb66b5cc1 | ||
|
|
3b1ecbfbbf | ||
|
|
a7f7a59c8a | ||
|
|
9068028662 | ||
|
|
40fbc7809f | ||
|
|
6397c2c7f3 | ||
|
|
81e8e30682 | ||
|
|
9b4c6ab14a | ||
|
|
cad1437c33 | ||
|
|
4799a0d464 |
15
README.md
15
README.md
@@ -41,10 +41,7 @@ Experimental support of Neutrino-MP or others on the same basis (BPanther, etc).
|
||||
* **Ctrl + Alt + R** - rename for bouquet.
|
||||
* **Ctrl + S, T** in Satellites edit tool for create satellite or transponder.
|
||||
* **Ctrl + L** - parental lock.
|
||||
* **Ctrl + H** - hide/skip.
|
||||
* **Ctrl + P** - start play IPTV or other stream in the bouquet list.
|
||||
* **Ctrl + Z** - switch(**zap**) the channel(works when the HTTP API is enabled, Enigma2 only).
|
||||
* **Ctrl + W** - switch to the channel and watch in the program.
|
||||
* **Ctrl + H** - hide/skip.
|
||||
* **Space** - select/deselect.
|
||||
* **Left/Right** - remove selection.
|
||||
* **Ctrl + Up, Down, PageUp, PageDown, Home, End**- move selected items in the list.
|
||||
@@ -56,6 +53,9 @@ Experimental support of Neutrino-MP or others on the same basis (BPanther, etc).
|
||||
* **Ctrl + Shift + F** - show/hide filter bar.
|
||||
* **Ctrl + T** - show/hide built-in Telnet client.
|
||||
* **Ctrl + Shift + L** - show/hide logging panel.
|
||||
* **Shift + P** - start play IPTV or other stream in the bouquet list.
|
||||
* **Shift + Z** - switch(**zap**) the channel(works when the HTTP API is enabled, Enigma2 only).
|
||||
* **Shift + W** - switch to the channel and watch in the program.
|
||||
|
||||
For **multiple** selection with the mouse, press and hold the **Ctrl** key!
|
||||
|
||||
@@ -75,10 +75,11 @@ Users of **LTS** versions of [Ubuntu](https://ubuntu.com/) or distributions base
|
||||
A ready-made [package](https://aur.archlinux.org/packages/demoneditor-bin) is also available for [Arch Linux](https://archlinux.org/) users in the [AUR](https://aur.archlinux.org/) repository.
|
||||
* ### macOS
|
||||
**This program can be run on macOS.**
|
||||
To run the program on macOS, you need to install [brew](https://brew.sh/).
|
||||
To run the program on macOS, you need to install [Homebrew](https://brew.sh/).
|
||||
Then install the required components via terminal:
|
||||
```brew install python3 gtk+3 pygobject3 adwaita-icon-theme```
|
||||
```pip3 install requests, pillow```
|
||||
```brew install python3 gtk+3 pygobject3 adwaita-icon-theme python-requests gtksourceview3```
|
||||
|
||||
*Optional:* ```brew install pillow python-chardet```
|
||||
|
||||
Launch is similar to Linux.
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
#
|
||||
# The MIT License (MIT)
|
||||
#
|
||||
# Copyright (c) 2018-2023 Dmitriy Yefremov
|
||||
# Copyright (c) 2018-2024 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
|
||||
@@ -59,10 +59,15 @@ PICONS_MAX_NUM = 1000 # Maximum picon number for sending without compression.
|
||||
class DownloadType(Enum):
|
||||
ALL = 0
|
||||
BOUQUETS = 1
|
||||
SATELLITES = 2
|
||||
PICONS = 3
|
||||
WEBTV = 4
|
||||
EPG = 5
|
||||
SERVICES = 2
|
||||
SATELLITES = 3
|
||||
PICONS = 4
|
||||
WEBTV = 5
|
||||
EPG = 6
|
||||
|
||||
@classmethod
|
||||
def _missing_(cls, value):
|
||||
return cls.ALL
|
||||
|
||||
|
||||
class TestException(Exception):
|
||||
@@ -373,9 +378,11 @@ def download_data(*, settings, download_type=DownloadType.ALL, callback=log, fil
|
||||
save_path = settings.profile_data_path
|
||||
os.makedirs(os.path.dirname(save_path), exist_ok=True)
|
||||
# bouquets
|
||||
if download_type is DownloadType.ALL or download_type is DownloadType.BOUQUETS:
|
||||
if download_type in (DownloadType.ALL, DownloadType.BOUQUETS, DownloadType.SERVICES):
|
||||
ftp.cwd(settings.services_path)
|
||||
file_list = BQ_FILES_LIST + DATA_FILES_LIST if download_type is DownloadType.ALL else BQ_FILES_LIST
|
||||
file_list = BQ_FILES_LIST
|
||||
if download_type is DownloadType.ALL or DownloadType.SERVICES:
|
||||
file_list += DATA_FILES_LIST
|
||||
ftp.download_files(save_path, file_list, callback)
|
||||
# *.xml and webtv
|
||||
if download_type in (DownloadType.ALL, DownloadType.SATELLITES):
|
||||
@@ -426,7 +433,7 @@ def upload_data(*, settings, download_type=DownloadType.ALL, callback=log, done_
|
||||
|
||||
ht.send((f"{url}message?{params}", "Sending info message... "))
|
||||
|
||||
if s_type is SettingsType.ENIGMA_2 and download_type is DownloadType.ALL:
|
||||
if s_type is SettingsType.ENIGMA_2 and download_type in (DownloadType.ALL, DownloadType.SERVICES):
|
||||
time.sleep(5)
|
||||
if not settings.keep_power_mode:
|
||||
ht.send((f"{url}powerstate?newstate=0", "Toggle Standby "))
|
||||
@@ -457,8 +464,10 @@ def upload_data(*, settings, download_type=DownloadType.ALL, callback=log, done_
|
||||
ftp.cwd(services_path)
|
||||
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)
|
||||
if download_type is DownloadType.ALL or download_type is DownloadType.SERVICES:
|
||||
if download_type is DownloadType.ALL:
|
||||
ftp.upload_xml(data_path, sat_xml_path, STC_XML_FILE, callback)
|
||||
|
||||
if s_type is SettingsType.NEUTRINO_MP:
|
||||
ftp.upload_xml(data_path, sat_xml_path, WEB_TV_XML_FILE, callback)
|
||||
|
||||
@@ -518,7 +527,7 @@ def upload_data(*, settings, download_type=DownloadType.ALL, callback=log, done_
|
||||
if s_type is SettingsType.ENIGMA_2:
|
||||
if download_type is DownloadType.BOUQUETS:
|
||||
ht.send((f"{url}servicelistreload?mode=2", "Reloading Userbouquets."))
|
||||
elif download_type is DownloadType.ALL:
|
||||
elif download_type is DownloadType.ALL or download_type is DownloadType.SERVICES:
|
||||
ht.send((f"{url}servicelistreload?mode=0", "Reloading lamedb and Userbouquets."))
|
||||
if not settings.keep_power_mode:
|
||||
ht.send((f"{url}powerstate?newstate=4", "Wakeup from Standby."))
|
||||
@@ -537,6 +546,8 @@ def upload_data(*, settings, download_type=DownloadType.ALL, callback=log, done_
|
||||
def get_upload_info_message(download_type):
|
||||
if download_type is DownloadType.BOUQUETS:
|
||||
return "User bouquets will be updated!"
|
||||
if download_type is DownloadType.SERVICES:
|
||||
return "User bouquets and services list will be updated!"
|
||||
elif download_type is DownloadType.ALL:
|
||||
return "All user data will be reloaded!"
|
||||
elif download_type is DownloadType.SATELLITES:
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
#
|
||||
# The MIT License (MIT)
|
||||
#
|
||||
# Copyright (c) 2018-2023 Dmitriy Yefremov
|
||||
# Copyright (c) 2018-2024 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
|
||||
@@ -181,6 +181,8 @@ class ServiceType(Enum):
|
||||
class BouquetsReader:
|
||||
""" Class for reading and parsing bouquets. """
|
||||
_BQ_PAT = re.compile(r".*FROM BOUQUET\s+\"((.*bouquet|alternatives)?\.?([\w-]+)\.?(\w+)?)\"\s+.*$", re.IGNORECASE)
|
||||
_BQ_PAT2 = re.compile(r"#SERVICE:+\s+(?:[0-9a-f]+:+)+([^:]+[.](?:tv|radio))$", re.IGNORECASE)
|
||||
_BQ_POST_PAT = re.compile(r".*FROM BOUQUET\s+\"((.*bouquet|alternatives)?\.?(.*)\.?(\w+)?)\"\s+.*$", re.IGNORECASE)
|
||||
_STREAM_TYPES = {"4097", "5001", "5002", "8193", "8739"}
|
||||
|
||||
__slots__ = ["_path"]
|
||||
@@ -206,11 +208,24 @@ class BouquetsReader:
|
||||
|
||||
for line in file.readlines():
|
||||
if "#SERVICE" in line:
|
||||
mt = re.match(self._BQ_PAT, line)
|
||||
s_data = line.split(":")
|
||||
s_type = ServiceType(s_data[1])
|
||||
s_type = ServiceType.BOUQUET
|
||||
|
||||
mt = re.match(self._BQ_PAT, line) or re.match(self._BQ_PAT2, line)
|
||||
if not mt:
|
||||
# Additional file name checking.
|
||||
mt = re.match(self._BQ_POST_PAT, line)
|
||||
if mt:
|
||||
log(f"Warning: The bouquet file name may be formed incorrectly. -> {mt.group(1)}")
|
||||
|
||||
if mt:
|
||||
file_name, prefix, b_name = mt.group(1), mt.group(2), mt.group(3)
|
||||
if len(mt.groups()) > 1:
|
||||
file_name, prefix, b_name = mt.group(1), mt.group(2), mt.group(3)
|
||||
s_data[:2] = "10"
|
||||
else:
|
||||
file_name, prefix, b_name = mt.group(1), "", ""
|
||||
s_type = ServiceType(s_data[2])
|
||||
|
||||
if b_name in b_names:
|
||||
log(f"The list of bouquets contains duplicate [{b_name}] names!")
|
||||
else:
|
||||
@@ -224,7 +239,6 @@ class BouquetsReader:
|
||||
else:
|
||||
real_b_names[rb_name] = 0
|
||||
# Locked, hidden.
|
||||
s_data[:2] = "10"
|
||||
locked = ":".join(s_data).rstrip()
|
||||
hidden = s_type is ServiceType.HIDDEN
|
||||
bouquets[2].append(Bouquet(rb_name, bq_type, services, locked, hidden, file_name))
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
#
|
||||
# The MIT License (MIT)
|
||||
#
|
||||
# Copyright (c) 2018-2022 Dmitriy Yefremov
|
||||
# Copyright (c) 2018-2024 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
|
||||
@@ -119,7 +119,10 @@ class LameDbReader:
|
||||
srv_data[1] = srv_data[1].strip("\"\n")
|
||||
data_len = len(srv_data)
|
||||
if data_len == 3:
|
||||
srv_data[2] = srv_data[2].strip()
|
||||
s_data = srv_data[2].strip()
|
||||
if not s_data.startswith("p:"):
|
||||
s_data = f"p:,{s_data}"
|
||||
srv_data[2] = s_data
|
||||
elif data_len == 2:
|
||||
srv_data.append("p:")
|
||||
srvs.extend(srv_data)
|
||||
|
||||
@@ -134,18 +134,18 @@ def export_to_m3u(path, bouquet, s_type, url=None):
|
||||
current_grp = None
|
||||
|
||||
for s in bouquet.services:
|
||||
s_type = s.type
|
||||
if s_type is BqServiceType.IPTV:
|
||||
srv_type = s.type
|
||||
if srv_type is BqServiceType.IPTV:
|
||||
res = re.match(pattern, s.data)
|
||||
if not res:
|
||||
continue
|
||||
lines.append(f"#EXTINF:-1,{s.name}\n")
|
||||
lines.append(current_grp) if current_grp else None
|
||||
u = res.group(1)
|
||||
lines.append(f"{unquote(u[:u.rfind(':')])}\n")
|
||||
elif s_type is BqServiceType.MARKER:
|
||||
lines.append(f"{unquote(u[:u.rfind(':')]) if s_type is SettingsType.ENIGMA_2 else u}\n")
|
||||
elif srv_type is BqServiceType.MARKER:
|
||||
current_grp = f"#EXTGRP:{s.name}\n"
|
||||
elif s_type is BqServiceType.DEFAULT and url:
|
||||
elif srv_type is BqServiceType.DEFAULT and url:
|
||||
lines.append(f"#EXTINF:-1,{s.name}\n")
|
||||
lines.append(current_grp) if current_grp else None
|
||||
lines.append(f"{url}{s.data}\n")
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
#
|
||||
# The MIT License (MIT)
|
||||
#
|
||||
# Copyright (c) 2018-2023 Dmitriy Yefremov
|
||||
# Copyright (c) 2018-2024 Dmitriy Yefremov
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -37,6 +37,7 @@ from ..ecommons import Bouquets, Bouquet, BouquetService, BqServiceType, PROVIDE
|
||||
_FILE = "bouquets.xml"
|
||||
_U_FILE = "ubouquets.xml"
|
||||
_W_FILE = "webtv_usr.xml"
|
||||
_WEB_TV_NAME = "[Web TV]"
|
||||
|
||||
_COMMENT = " File was created in DemonEditor. Enjoy watching! "
|
||||
|
||||
@@ -100,13 +101,20 @@ def parse_webtv(path, name, bq_type):
|
||||
return bouquets
|
||||
|
||||
dom = XmlHandler.parse(path)
|
||||
# Display name.
|
||||
name = None
|
||||
for e in dom.childNodes:
|
||||
if e.nodeType == e.ELEMENT_NODE:
|
||||
name = e.getAttribute("name")
|
||||
break
|
||||
|
||||
services = []
|
||||
for elem in dom.getElementsByTagName("webtv"):
|
||||
if elem.hasAttributes():
|
||||
web_attrs = get_xml_attributes(elem)
|
||||
services.append(get_webtv_service(web_attrs))
|
||||
|
||||
bouquet = Bouquet(name="default", type=bq_type, services=services, locked=None, hidden=None, file=None)
|
||||
bouquet = Bouquet(name=name or _WEB_TV_NAME, type=bq_type, services=services, locked=None, hidden=None, file=None)
|
||||
bouquets[2].append(bouquet)
|
||||
|
||||
return bouquets
|
||||
@@ -195,6 +203,7 @@ def write_webtv(file, bouquet):
|
||||
doc.appendChild(comment)
|
||||
|
||||
for bq in bouquet.bouquets:
|
||||
root.setAttribute("name", bq.name or _WEB_TV_NAME)
|
||||
for srv in bq.services:
|
||||
url, description, urlkey, account, usrname, psw, s_type, iconsrc, iconsrc_b, group = srv.fav_id.split("::")
|
||||
srv_elem = doc.createElement("webtv")
|
||||
|
||||
@@ -443,9 +443,9 @@ class ServicesParser(HTMLParser):
|
||||
|
||||
self._POS_PAT = re.compile(r".*?(\d+\.\d°[EW]).*")
|
||||
# LyngSat.
|
||||
self._TR_PAT = re.compile((r".*?(\d+)\.?\d?\s+([RLHV]).*(DVB-S[2]?)/?(.*PSK)?\s"
|
||||
self._TR_PAT = re.compile((r".*?(\d{4,5})\.?\d?\s+([RLHV]).*(DVB-S[2]?)/?(.*PSK)?\s"
|
||||
r"?(T2-MI)?\s?(PLS\s+Multistream)?\s?"
|
||||
r"SR-FEC:\s(\d+)-(\d/\d)\s+.*ONID-TID:\s+(\d+)-(\d+).*"))
|
||||
r"SR-FEC:\s(\d+)-(\d+/\d+)\s+?(?:.*ONID-TID:\s+(\d+)-(\d+))?"))
|
||||
|
||||
self._MULTI_PAT = re.compile(r"PLS\s+(Root|Gold|Combo)+\s(\d+)?\s+(?:Stream\s(\d+))")
|
||||
# KingOfSat.
|
||||
@@ -635,12 +635,18 @@ class ServicesParser(HTMLParser):
|
||||
if pos_found:
|
||||
text = " ".join(c.text for c in r[1:])
|
||||
td = re.match(self._TR_PAT, text)
|
||||
|
||||
if td:
|
||||
freq, pol = int(td.group(1)), get_key_by_value(POLARIZATION, td.group(2))
|
||||
sys, mod, sr, _fec, = td.group(3), td.group(4), td.group(7), td.group(8)
|
||||
nid, tid = td.group(9), td.group(10)
|
||||
sys, mod, fec, nsp, s2_flags, roll_off, pilot, inv = self.get_transponder_data(pos, _fec, sys, mod)
|
||||
nid, tid = int(nid), int(tid)
|
||||
# The ONID-TID values may not present!
|
||||
_nid, _tid = td.group(9), td.group(10)
|
||||
if _nid and _tid:
|
||||
nid, tid = int(_nid), int(_tid)
|
||||
else:
|
||||
log((f"Values 'ONID-TID' for transponder [{self._t_url}] are not present."
|
||||
" Default values are used."))
|
||||
|
||||
if td.group(5):
|
||||
log(f"Detected T2-MI transponder! [{freq} {sr} {pol}]")
|
||||
|
||||
@@ -41,6 +41,10 @@ from app.ui.dialogs import show_dialog, DialogType, get_builder
|
||||
from app.ui.main_helper import append_text_to_tview, show_info_bar_message
|
||||
from .uicommons import Gtk, Gdk, UI_RESOURCES_PATH, KeyboardKey, MOD_MASK, HeaderBar
|
||||
|
||||
KEEP_DATA = {"satellites.xml",
|
||||
"terrestrial.xml",
|
||||
"cables.xml"}
|
||||
|
||||
|
||||
class RestoreType(Enum):
|
||||
BOUQUETS = 0
|
||||
@@ -232,18 +236,19 @@ class BackupDialog:
|
||||
self.restore(RestoreType.BOUQUETS)
|
||||
|
||||
|
||||
def backup_data(path, backup_path, move=True):
|
||||
def backup_data(path, backup_path, move=True, keep=None):
|
||||
""" Creating data backup from a folder at the specified path
|
||||
|
||||
Returns full path to the compressed file.
|
||||
"""
|
||||
keep = keep or KEEP_DATA
|
||||
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.
|
||||
for file in filter(lambda f: 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)
|
||||
shutil.move(src, dst) if move and file not in keep else shutil.copy(src, dst)
|
||||
# Compressing to zip and delete remaining files.
|
||||
zip_file = shutil.make_archive(backup_path.rstrip(SEP), "zip", backup_path)
|
||||
shutil.rmtree(backup_path)
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2018-2023 Dmitriy Yefremov
|
||||
Copyright (c) 2018-2024 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,7 +40,7 @@ Author: Dmitriy Yefremov
|
||||
<property name="icon_name">system-help</property>
|
||||
<property name="type_hint">normal</property>
|
||||
<property name="program_name">DemonEditor</property>
|
||||
<property name="version">3.9.0 Alpha</property>
|
||||
<property name="version">3.10.2 Beta</property>
|
||||
<property name="copyright">2018-2024 Dmitriy Yefremov
|
||||
</property>
|
||||
<property name="comments" translatable="yes">Enigma2 channel and satellite list editor.</property>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
#
|
||||
# The MIT License (MIT)
|
||||
#
|
||||
# Copyright (c) 2018-2023 Dmitriy Yefremov
|
||||
# Copyright (c) 2018-2024 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
|
||||
@@ -47,7 +47,7 @@ class BaseDialog(Gtk.Dialog):
|
||||
title=translate(title),
|
||||
modal=True,
|
||||
resizable=False,
|
||||
default_width=240,
|
||||
default_width=255,
|
||||
skip_taskbar_hint=True,
|
||||
skip_pager_hint=True,
|
||||
destroy_with_parent=True,
|
||||
@@ -61,12 +61,12 @@ class Dialog(Enum):
|
||||
MESSAGE = """
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<interface>
|
||||
<requires lib="gtk+" version="3.16"/>
|
||||
<requires lib="gtk+" version="3.22"/>
|
||||
<object class="GtkMessageDialog" id="message_dialog">
|
||||
<property name="use-header-bar">{use_header}</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="modal">True</property>
|
||||
<property name="width_request">250</property>
|
||||
<property name="width_request">255</property>
|
||||
<property name="destroy_with_parent">True</property>
|
||||
<property name="type_hint">dialog</property>
|
||||
<property name="skip_taskbar_hint">True</property>
|
||||
|
||||
@@ -211,7 +211,7 @@ class FavEpgCache(EpgCache):
|
||||
@run_task
|
||||
def update_xml_data(self):
|
||||
services = self._app.current_services
|
||||
names = {services[s].service for s in self._app.current_bouquets.get(self._current_bq, [])}
|
||||
names = {services[s].service for s in self._app.current_bouquets.get(self._current_bq, []) if s in services}
|
||||
for name, events in self._reader.get_current_events(names).items():
|
||||
ev = min(events, key=lambda x: x.start, default=None)
|
||||
if ev:
|
||||
@@ -311,7 +311,7 @@ class TabEpgCache(EpgCache):
|
||||
|
||||
def update_epg_data(self) -> bool:
|
||||
services = self._app.current_services
|
||||
names = {services[s].service for s in chain.from_iterable(self._app.current_bouquets.values())}
|
||||
names = {services[s].service for s in chain.from_iterable(self._app.current_bouquets.values()) if s in services}
|
||||
for name, events in self._reader.get_current_events(names).items():
|
||||
self.events[name] = events
|
||||
|
||||
@@ -747,7 +747,8 @@ class EpgTool(Gtk.Box):
|
||||
api.send(HttpAPI.Request.EPG_MULTI, f'1:7:1:0:0:0:0:0:0:0:{req}', self.update_http_epg_data, timeout=15)
|
||||
else:
|
||||
srvs = self._app.current_services
|
||||
self.update_xmltv_epg_data(srvs[s].service for s in self._app.current_bouquets.get(self._current_bq, []))
|
||||
bq_names = (srvs[s].service for s in self._app.current_bouquets.get(self._current_bq, []) if s in srvs)
|
||||
self.update_xmltv_epg_data(bq_names)
|
||||
|
||||
# ****************** Timers ***************** #
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2018-2023 Dmitriy Yefremov
|
||||
Copyright (c) 2018-2024 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,7 +32,7 @@ Author: Dmitriy Yefremov
|
||||
<!-- interface-license-type mit -->
|
||||
<!-- interface-name DemonEditor -->
|
||||
<!-- interface-description Enigma2 channel and satellites list editor for GNU/Linux. -->
|
||||
<!-- interface-copyright 2018-2023 Dmitriy Yefremov -->
|
||||
<!-- interface-copyright 2018-2024 Dmitriy Yefremov -->
|
||||
<!-- interface-authors Dmitriy Yefremov -->
|
||||
<object class="GtkImage" id="remove_selection_image">
|
||||
<property name="visible">True</property>
|
||||
@@ -270,13 +270,14 @@ Author: Dmitriy Yefremov
|
||||
</object>
|
||||
<object class="GtkDialog" id="iptv_list_configuration_dialog">
|
||||
<property name="use-header-bar">{use_header}</property>
|
||||
<property name="width-request">400</property>
|
||||
<property name="width-request">680</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="title" translatable="yes">IPTV streams list configuration</property>
|
||||
<property name="resizable">False</property>
|
||||
<property name="modal">True</property>
|
||||
<property name="window-position">center</property>
|
||||
<property name="destroy-with-parent">True</property>
|
||||
<property name="icon-name">demon-editor</property>
|
||||
<property name="type-hint">dialog</property>
|
||||
<property name="skip-taskbar-hint">True</property>
|
||||
<property name="skip-pager-hint">True</property>
|
||||
@@ -512,9 +513,9 @@ Author: Dmitriy Yefremov
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkEntry" id="list_namespace_entry">
|
||||
<property name="width-request">120</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="sensitive">False</property>
|
||||
<property name="hexpand">True</property>
|
||||
<property name="can-focus">True</property>
|
||||
<property name="width-chars">5</property>
|
||||
<property name="max-width-chars">5</property>
|
||||
@@ -879,6 +880,7 @@ Author: Dmitriy Yefremov
|
||||
<property name="resizable">False</property>
|
||||
<property name="modal">True</property>
|
||||
<property name="destroy-with-parent">True</property>
|
||||
<property name="icon-name">demon-editor</property>
|
||||
<property name="type-hint">dialog</property>
|
||||
<property name="skip-taskbar-hint">True</property>
|
||||
<property name="skip-pager-hint">True</property>
|
||||
|
||||
@@ -42,7 +42,7 @@ from gi.repository import GLib, Gio, GdkPixbuf
|
||||
from app.commons import run_idle, run_task, log
|
||||
from app.eparser.ecommons import BqServiceType, BouquetService, Service
|
||||
from app.eparser.iptv import (NEUTRINO_FAV_ID_FORMAT, StreamType, ENIGMA2_FAV_ID_FORMAT, get_fav_id, MARKER_FORMAT,
|
||||
parse_m3u, PICON_FORMAT, export_to_m3u)
|
||||
parse_m3u, PICON_FORMAT)
|
||||
from app.settings import SettingsType
|
||||
from app.tools.yt import YouTubeException, YouTube
|
||||
from app.ui.dialogs import Action, show_dialog, DialogType, translate, get_builder, BaseDialog
|
||||
@@ -52,7 +52,8 @@ from app.ui.uicommons import (Gtk, Gdk, UI_RESOURCES_PATH, IPTV_ICON, Column, Ke
|
||||
_DIGIT_ENTRY_NAME = "digit-entry"
|
||||
_ENIGMA2_REFERENCE = "{}:{}:{:X}:{:X}:{:X}:{:X}:{:X}:0:0:0"
|
||||
_PATTERN = re.compile("(?:^[\\s]*$|\\D)")
|
||||
_UI_PATH = UI_RESOURCES_PATH + "iptv.glade"
|
||||
_UI_PATH = f"{UI_RESOURCES_PATH}iptv.glade"
|
||||
_CSS_PATH = f"{UI_RESOURCES_PATH}style.css"
|
||||
_URL_PREFIXES = {"YT-DLP": "YT-DLP://", "YT-DL": "YT-DL://", "STREAMLINK": "streamlink://", "No": None}
|
||||
|
||||
|
||||
@@ -126,7 +127,7 @@ class IptvDialog:
|
||||
self._model, self._paths = view.get_selection().get_selected_rows()
|
||||
# Style.
|
||||
self._style_provider = Gtk.CssProvider()
|
||||
self._style_provider.load_from_path(UI_RESOURCES_PATH + "style.css")
|
||||
self._style_provider.load_from_path(_CSS_PATH)
|
||||
self._digit_elems = (self._srv_id_entry, self._srv_type_entry, self._sid_entry, self._tr_id_entry,
|
||||
self._net_id_entry, self._namespace_entry)
|
||||
for el in self._digit_elems:
|
||||
@@ -249,11 +250,8 @@ class IptvDialog:
|
||||
return get_stream_type(self._stream_type_combobox)
|
||||
|
||||
def on_entry_changed(self, entry):
|
||||
if _PATTERN.search(entry.get_text()):
|
||||
entry.set_name(_DIGIT_ENTRY_NAME)
|
||||
else:
|
||||
entry.set_name("GtkEntry")
|
||||
self.update_reference_entry()
|
||||
entry.set_name(_DIGIT_ENTRY_NAME if _PATTERN.search(entry.get_text()) else "GtkEntry")
|
||||
self.update_reference_entry()
|
||||
|
||||
def on_url_changed(self, entry):
|
||||
url_str = entry.get_text()
|
||||
@@ -532,7 +530,7 @@ class IptvListDialog:
|
||||
self._ok_button.bind_property("visible", self._cancel_button, "visible", 4)
|
||||
# Style
|
||||
style_provider = Gtk.CssProvider()
|
||||
style_provider.load_from_path(UI_RESOURCES_PATH + "style.css")
|
||||
style_provider.load_from_path(_CSS_PATH)
|
||||
self._default_elems = (self._stream_type_check_button, self._id_default_check_button, self._type_check_button,
|
||||
self._sid_auto_check_button, self._tid_check_button, self._nid_check_button,
|
||||
self._namespace_check_button)
|
||||
@@ -830,7 +828,7 @@ class M3uImportDialog(IptvListDialog):
|
||||
def_bq_name = gen_bouquet_name(bqs, f"IPTV {date.today()} ", bq_type)
|
||||
|
||||
if self._single_bq_button.get_active():
|
||||
self.append_bouquet(def_bq_name, bq_type, bqs, model, itr, services)
|
||||
self.append_bouquet(def_bq_name, bq_type, bqs, model, itr, services)
|
||||
else:
|
||||
# Sub-bouquets.
|
||||
if self._sub_bq_button.get_active():
|
||||
@@ -841,7 +839,7 @@ class M3uImportDialog(IptvListDialog):
|
||||
gr = self.get_services_groups(filter(lambda s: s.service_type != m_name, services), def_bq_name)
|
||||
[self.append_bouquet(gen_bouquet_name(bqs, g, bq_type), bq_type, bqs, model, itr, s) for g, s in gr.items()]
|
||||
|
||||
def append_bouquet(self, bq_name, bq_type, bqs, model, itr, services):
|
||||
def append_bouquet(self, bq_name, bq_type, bqs, model, itr, services):
|
||||
""" Adds new bouquet and returns iter of appended row. """
|
||||
cur_services = self._app.current_services
|
||||
bqs[f"{bq_name}:{bq_type}"] = [s.fav_id for s in services]
|
||||
@@ -981,6 +979,7 @@ class ExportM3uDialog(BaseDialog):
|
||||
self._app = app
|
||||
self._bouquets = bouquets
|
||||
self._url = None
|
||||
self._default_port = "8001"
|
||||
|
||||
builder = get_builder(f"{UI_RESOURCES_PATH}m3u.glade", use_str=True, objects=("export_m3u_box",))
|
||||
self._main_grid = builder.get_object("export_m3u_grid")
|
||||
@@ -1007,12 +1006,36 @@ class ExportM3uDialog(BaseDialog):
|
||||
self._bq_count_label.set_text(str(len(self._bouquets)))
|
||||
self._services_count_label.set_text(str(len(list(chain.from_iterable(self._bouquets.values())))))
|
||||
|
||||
if self._app.is_enigma:
|
||||
self._port_entry.connect("changed", self.on_port_changed)
|
||||
self._port_auto_button.connect("toggled", self.on_port_auto_toggled)
|
||||
# Add style for the port entry.
|
||||
style_provider = Gtk.CssProvider()
|
||||
style_provider.load_from_path(_CSS_PATH)
|
||||
context = self._port_entry.get_style_context()
|
||||
context.add_provider_for_screen(Gdk.Screen.get_default(), style_provider, Gtk.STYLE_PROVIDER_PRIORITY_USER)
|
||||
|
||||
def on_port_changed(self, entry):
|
||||
entry.set_name(_DIGIT_ENTRY_NAME if _PATTERN.search(entry.get_text()) else "GtkEntry")
|
||||
|
||||
def on_port_auto_toggled(self, button):
|
||||
if not button.get_active() and not self._port_entry.get_text():
|
||||
self._port_entry.set_text(self._default_port)
|
||||
|
||||
def on_response(self, dialog, response):
|
||||
if response != Gtk.ResponseType.OK:
|
||||
self.destroy()
|
||||
else:
|
||||
if self._app.is_enigma and self._port_auto_button.get_active():
|
||||
self.do_export_auto()
|
||||
if self._app.is_enigma:
|
||||
if self._port_auto_button.get_active():
|
||||
self.do_export_auto()
|
||||
else:
|
||||
if self._port_entry.get_name() == _DIGIT_ENTRY_NAME:
|
||||
self._app.show_error_message("Error. Verify the data!")
|
||||
else:
|
||||
st = self._app.app_settings
|
||||
self._url = f"http{'s' if st.http_use_ssl else ''}://{st.host}:{self._port_entry.get_text()}/"
|
||||
self.do_export()
|
||||
else:
|
||||
self.do_export()
|
||||
return True
|
||||
@@ -1142,7 +1165,7 @@ class YtListImportDialog:
|
||||
self._dialog.resize(*window_size)
|
||||
# Style.
|
||||
style_provider = Gtk.CssProvider()
|
||||
style_provider.load_from_path(f"{UI_RESOURCES_PATH}style.css")
|
||||
style_provider.load_from_path(_CSS_PATH)
|
||||
self._url_entry.get_style_context().add_provider_for_screen(Gdk.Screen.get_default(), style_provider,
|
||||
Gtk.STYLE_PROVIDER_PRIORITY_USER)
|
||||
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -749,6 +749,7 @@ Author: Dmitriy Yefremov
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkModelButton" id="save_as_menu_button">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">True</property>
|
||||
<property name="receives-default">True</property>
|
||||
<property name="action-name">app.on_data_save_as</property>
|
||||
@@ -1721,7 +1722,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.9.0 Alpha</property>
|
||||
<property name="label">3.10.2 Beta</property>
|
||||
<attributes>
|
||||
<attribute name="weight" value="bold"/>
|
||||
</attributes>
|
||||
|
||||
@@ -77,6 +77,8 @@ from .xml.edit import SatellitesTool
|
||||
|
||||
class Application(Gtk.Application):
|
||||
""" Main application class. """
|
||||
VERSION = "3.10.2"
|
||||
|
||||
SERVICE_MODEL = "services_list_store"
|
||||
FAV_MODEL = "fav_list_store"
|
||||
BQ_MODEL = "bouquets_tree_store"
|
||||
@@ -623,7 +625,7 @@ class Application(Gtk.Application):
|
||||
self._main_window.set_wmclass("DemonEditor", "DemonEditor")
|
||||
self._main_window.present()
|
||||
|
||||
self.init_profiles()
|
||||
self.init_profiles(True)
|
||||
gen = self.init_http_api()
|
||||
GLib.idle_add(lambda: next(gen, False), priority=GLib.PRIORITY_LOW)
|
||||
|
||||
@@ -784,7 +786,7 @@ class Application(Gtk.Application):
|
||||
self.set_action("on_locked", self.on_locked)
|
||||
# Open and download/upload data.
|
||||
self.set_action("open_data", lambda a, v: self.open_data())
|
||||
self.set_action("upload_all", lambda a, v: self.on_upload_data(DownloadType.ALL))
|
||||
self.set_action("upload_all", lambda a, v: self.emit("data-send", self._page))
|
||||
self.set_action("upload_bouquets", lambda a, v: self.on_upload_data(DownloadType.BOUQUETS))
|
||||
sa = self.set_action("on_data_save", lambda a, v: self.emit("data-save", self._page), False)
|
||||
self.bind_property("is-data-save-enabled", sa, "enabled")
|
||||
@@ -875,9 +877,9 @@ class Application(Gtk.Application):
|
||||
self.set_accels_for_action("app.on_logs_show", ["<shift><primary>l"])
|
||||
self.set_accels_for_action("win.filter", ["<shift><primary>f"])
|
||||
|
||||
def init_profiles(self):
|
||||
def init_profiles(self, last_config=False):
|
||||
self.update_profiles()
|
||||
if self._settings.load_last_config:
|
||||
if last_config and self._settings.load_last_config:
|
||||
config = self._settings.get("last_config") or {}
|
||||
if config.get("last_bouquet", None):
|
||||
self.connect("data-load-done", self.open_last_bouquet)
|
||||
@@ -1170,7 +1172,10 @@ class Application(Gtk.Application):
|
||||
self._stack.set_visible_child_name(page_name)
|
||||
|
||||
def on_page_changed(self, app, page):
|
||||
if not len(self._bouquets_model):
|
||||
if page is not Page.SERVICES:
|
||||
return
|
||||
|
||||
if all((not self._settings.load_last_config, not self.is_data_loading(), not len(self._bouquets_model))):
|
||||
self.open_data()
|
||||
|
||||
def set_use_alt_layout(self, action, value):
|
||||
@@ -2143,12 +2148,16 @@ class Application(Gtk.Application):
|
||||
self.show_error_message("Not allowed in this context!")
|
||||
|
||||
def on_download(self, app, page):
|
||||
if page is Page.SERVICES or page is Page.INFO:
|
||||
if page is Page.INFO:
|
||||
self.on_download_data()
|
||||
elif page is Page.SERVICES:
|
||||
self.on_download_data(DownloadType.SERVICES)
|
||||
|
||||
def on_upload(self, app, page):
|
||||
if page is Page.SERVICES or page is Page.INFO:
|
||||
if page is Page.INFO:
|
||||
self.on_upload_data()
|
||||
elif page is Page.SERVICES:
|
||||
self.on_upload_data(DownloadType.SERVICES)
|
||||
|
||||
def on_bg_task_add(self, app, task):
|
||||
if len(self._task_box) <= self.BG_TASK_LIMIT:
|
||||
@@ -2220,7 +2229,7 @@ class Application(Gtk.Application):
|
||||
|
||||
def on_data_extract(self, app, page):
|
||||
""" Opening the data archive via "File/Extract...". """
|
||||
if page is Page.SERVICES:
|
||||
if page is Page.INFO or page is Page.SERVICES:
|
||||
file_filter = None
|
||||
if IS_DARWIN:
|
||||
file_filter = Gtk.FileFilter()
|
||||
@@ -2285,6 +2294,7 @@ class Application(Gtk.Application):
|
||||
return tmp_path
|
||||
|
||||
def update_data(self, data_path, callback=None):
|
||||
self._services_load_spinner.start()
|
||||
self._profile_combo_box.set_sensitive(False)
|
||||
self._alt_revealer.set_visible(False)
|
||||
self._filter_services_button.set_active(False)
|
||||
@@ -2477,7 +2487,6 @@ class Application(Gtk.Application):
|
||||
self._services[srv.fav_id] = srv
|
||||
self.update_services_counts(len(self._services.values()))
|
||||
self._wait_dialog.hide()
|
||||
self._services_load_spinner.start()
|
||||
factor = self.DEL_FACTOR / 4
|
||||
|
||||
for index, srv in enumerate(to_add):
|
||||
@@ -2945,21 +2954,33 @@ class Application(Gtk.Application):
|
||||
|
||||
key = KeyboardKey(key_code)
|
||||
ctrl = event.state & MOD_MASK
|
||||
shift = event.state & Gdk.ModifierType.SHIFT_MASK
|
||||
model_name, model = get_model_data(view)
|
||||
|
||||
if key is KeyboardKey.LEFT or key is KeyboardKey.RIGHT:
|
||||
view.do_unselect_all(view)
|
||||
elif ctrl and model_name == self.FAV_MODEL:
|
||||
if key is KeyboardKey.P:
|
||||
self.emit("fav-clicked", PlaybackMode.STREAM)
|
||||
if key is KeyboardKey.W:
|
||||
self.emit("fav-clicked", PlaybackMode.ZAP_PLAY)
|
||||
if key is KeyboardKey.Z:
|
||||
self.emit("fav-clicked", PlaybackMode.ZAP)
|
||||
elif key is KeyboardKey.CTRL_L or key is KeyboardKey.CTRL_R:
|
||||
|
||||
if model_name == self.FAV_MODEL:
|
||||
if ctrl and key in (KeyboardKey.CTRL_L, KeyboardKey.CTRL_R):
|
||||
self.update_fav_num_column(model)
|
||||
self.update_bouquet_list()
|
||||
|
||||
if shift:
|
||||
if key is KeyboardKey.P:
|
||||
self.emit("fav-clicked", PlaybackMode.STREAM)
|
||||
if key is KeyboardKey.W:
|
||||
self.emit("fav-clicked", PlaybackMode.ZAP_PLAY)
|
||||
if key is KeyboardKey.Z:
|
||||
self.emit("fav-clicked", PlaybackMode.ZAP)
|
||||
elif model_name == self.SERVICE_MODEL:
|
||||
if shift:
|
||||
if key is KeyboardKey.P:
|
||||
self.emit("srv-clicked", PlaybackMode.STREAM)
|
||||
if key is KeyboardKey.W:
|
||||
self.emit("srv-clicked", PlaybackMode.ZAP_PLAY)
|
||||
if key is KeyboardKey.Z:
|
||||
self.emit("srv-clicked", PlaybackMode.ZAP)
|
||||
|
||||
def on_view_focus(self, view, focus_event=None):
|
||||
# Preventing focus lack for some cases.
|
||||
if not focus_event and not view.is_focus():
|
||||
@@ -4101,8 +4122,8 @@ class Application(Gtk.Application):
|
||||
|
||||
for index, row in enumerate(self._services_model):
|
||||
fav_id = row[Column.SRV_FAV_ID]
|
||||
if fav_id not in ids:
|
||||
row[Column.SRV_BACKGROUND] = self._EXTRA_COLOR
|
||||
bg = self.get_new_background(row[Column.SRV_CAS_FLAGS]) if fav_id in ids else self._EXTRA_COLOR
|
||||
row[Column.SRV_BACKGROUND] = bg
|
||||
|
||||
if index % self.FAV_FACTOR == 0:
|
||||
yield True
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2018-2023 Dmitriy Yefremov
|
||||
Copyright (c) 2018-2024 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-2023 Dmitriy Yefremov -->
|
||||
<!-- interface-copyright 2018-2024 Dmitriy Yefremov -->
|
||||
<!-- interface-authors Dmitriy Yefremov -->
|
||||
<object class="GtkMenu" id="add_menu">
|
||||
<property name="visible">True</property>
|
||||
@@ -428,8 +428,7 @@ Author: Dmitriy Yefremov
|
||||
<object class="GtkImage">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="icon-name">list-add</property>
|
||||
<property name="icon_size">0</property>
|
||||
<property name="icon-name">list-add-symbolic</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
@@ -451,7 +450,7 @@ Author: Dmitriy Yefremov
|
||||
<object class="GtkImage" id="receive_button_image">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="stock">gtk-goto-bottom</property>
|
||||
<property name="icon-name">go-bottom-symbolic</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
@@ -499,7 +498,7 @@ Author: Dmitriy Yefremov
|
||||
<object class="GtkImage">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="icon-name">window-new</property>
|
||||
<property name="icon-name">window-new-symbolic</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
#
|
||||
# The MIT License (MIT)
|
||||
#
|
||||
# Copyright (c) 2018-2023 Dmitriy Yefremov
|
||||
# Copyright (c) 2018-2024 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
|
||||
@@ -158,7 +158,14 @@ class PlayerBox(Gtk.Overlay):
|
||||
return
|
||||
|
||||
ref = self._app.get_service_ref_data(srv)
|
||||
self.zap(ref, self.play_current)
|
||||
if mode is PlaybackMode.PLAY:
|
||||
self.play_service(ref)
|
||||
elif mode is PlaybackMode.ZAP:
|
||||
self.zap(ref)
|
||||
elif mode is PlaybackMode.ZAP_PLAY:
|
||||
self.zap(ref, self.play_current)
|
||||
elif mode is PlaybackMode.STREAM:
|
||||
self._app.show_error_message("Not allowed in this context!")
|
||||
|
||||
def on_iptv_clicked(self, app, mode):
|
||||
if not self._app.http_api:
|
||||
@@ -439,15 +446,17 @@ class PlayerBox(Gtk.Overlay):
|
||||
self.play(url) if url else self.on_error(None, "No reference is present!")
|
||||
|
||||
def on_play_service(self, item=None):
|
||||
""" Playback without switching channel on the Box [returns current reference]"""
|
||||
""" Playback without switching channel on the Box."""
|
||||
ref, path = self.get_ref()
|
||||
if not ref:
|
||||
return
|
||||
|
||||
self.play_service(ref)
|
||||
|
||||
def play_service(self, ref):
|
||||
s_type = self._app.app_settings.setting_type
|
||||
req = HttpAPI.Request.STREAM if s_type is SettingsType.ENIGMA_2 else HttpAPI.Request.N_STREAM
|
||||
self._app.http_api.send(req, ref, self.watch)
|
||||
return ref
|
||||
|
||||
def on_zap(self, callback=None):
|
||||
""" Switch(zap) the channel. """
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2018-2023 Dmitriy Yefremov
|
||||
Copyright (c) 2018-2024 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,7 +32,7 @@ Author: Dmitriy Yefremov
|
||||
<!-- interface-license-type mit -->
|
||||
<!-- interface-name DemonEditor -->
|
||||
<!-- interface-description Enigma2 channel and satellite list editor for GNU/Linux. -->
|
||||
<!-- interface-copyright 2018-2023 Dmitriy Yefremov -->
|
||||
<!-- interface-copyright 2018-2024 Dmitriy Yefremov -->
|
||||
<!-- interface-authors Dmitriy Yefremov -->
|
||||
<object class="GtkListStore" id="fec_list_store">
|
||||
<columns>
|
||||
@@ -267,6 +267,7 @@ Author: Dmitriy Yefremov
|
||||
<property name="title" translatable="yes">Service data</property>
|
||||
<property name="resizable">False</property>
|
||||
<property name="modal">True</property>
|
||||
<property name="width-request">800</property>
|
||||
<property name="window-position">center-on-parent</property>
|
||||
<property name="destroy-with-parent">True</property>
|
||||
<property name="icon-name">document-properties-symbolic</property>
|
||||
|
||||
@@ -119,6 +119,7 @@ Author: Dmitriy Yefremov
|
||||
<property name="modal">True</property>
|
||||
<property name="window-position">center-on-parent</property>
|
||||
<property name="destroy-with-parent">True</property>
|
||||
<property name="icon-name">demon-editor</property>
|
||||
<property name="type-hint">dialog</property>
|
||||
<property name="skip-taskbar-hint">True</property>
|
||||
<property name="skip-pager-hint">True</property>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2018-2022 Dmitriy Yefremov
|
||||
Copyright (c) 2018-2024 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.20"/>
|
||||
<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. -->
|
||||
<!-- interface-copyright 2018-2022 Dmitriy Yefremov -->
|
||||
<!-- interface-copyright 2018-2024 Dmitriy Yefremov -->
|
||||
<!-- interface-authors Dmitriy Yefremov -->
|
||||
<object class="GtkAdjustment" id="begins_hour_adjustment">
|
||||
<property name="upper">23</property>
|
||||
@@ -307,8 +307,11 @@ Author: Dmitriy Yefremov
|
||||
<object class="GtkFrame" id="timer_dialog_frame">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="width-request">325</property>
|
||||
<property name="margin-start">5</property>
|
||||
<property name="margin-end">5</property>
|
||||
<property name="margin-top">5</property>
|
||||
<property name="margin-bottom">5</property>
|
||||
<property name="label-xalign">0</property>
|
||||
<property name="shadow-type">in</property>
|
||||
<child>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
#
|
||||
# The MIT License (MIT)
|
||||
#
|
||||
# Copyright (c) 2018-2023 Dmitriy Yefremov
|
||||
# Copyright (c) 2018-2024 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,9 +31,8 @@ from datetime import datetime, timedelta
|
||||
from enum import Enum
|
||||
from urllib.parse import quote
|
||||
|
||||
from app.settings import USE_HEADER_BAR
|
||||
from app.ui.main_helper import on_popup_menu
|
||||
from .dialogs import get_builder, translate, show_dialog, DialogType
|
||||
from .dialogs import get_builder, translate, show_dialog, DialogType, BaseDialog
|
||||
from .uicommons import Gtk, Gdk, GLib, UI_RESOURCES_PATH, Page, Column, KeyboardKey, MOD_MASK
|
||||
from ..commons import run_idle, log
|
||||
from ..connections import HttpAPI
|
||||
@@ -55,9 +54,11 @@ class TimerTool(Gtk.Box):
|
||||
EVENT = 1
|
||||
CHANGE = 2
|
||||
|
||||
class TimerDialog(Gtk.Dialog):
|
||||
class TimerDialog(BaseDialog):
|
||||
def __init__(self, parent, action=None, timer_data=None, *args, **kwargs):
|
||||
super().__init__(use_header_bar=USE_HEADER_BAR, *args, **kwargs)
|
||||
super().__init__(parent=parent, title="Timer",
|
||||
buttons=(translate("Cancel"), Gtk.ResponseType.CANCEL,
|
||||
translate("Save"), Gtk.ResponseType.OK), *args, **kwargs)
|
||||
|
||||
self._action = action or TimerTool.TimerAction.ADD
|
||||
self._timer_data = timer_data or {}
|
||||
@@ -71,14 +72,6 @@ class TimerTool(Gtk.Box):
|
||||
"min_end_adjustment", "timer_begins_popover", "begins_hour_adjustment",
|
||||
"min_begins_adjustment"))
|
||||
|
||||
self.set_title(translate("Timer"))
|
||||
self.set_modal(True)
|
||||
self.set_skip_pager_hint(True)
|
||||
self.set_skip_taskbar_hint(True)
|
||||
self.set_transient_for(parent)
|
||||
self.set_position(Gtk.WindowPosition.CENTER_ON_PARENT)
|
||||
self.set_resizable(False)
|
||||
|
||||
self._timer_name_entry = builder.get_object("timer_name_entry")
|
||||
self._timer_desc_entry = builder.get_object("timer_desc_entry")
|
||||
self._timer_service_entry = builder.get_object("timer_service_entry")
|
||||
@@ -111,8 +104,7 @@ class TimerTool(Gtk.Box):
|
||||
self._timer_desc_entry.drag_dest_unset()
|
||||
self._timer_service_entry.drag_dest_unset()
|
||||
|
||||
self.add_buttons(translate("Cancel"), Gtk.ResponseType.CANCEL, translate("Save"), Gtk.ResponseType.OK)
|
||||
self.get_content_area().pack_start(builder.get_object("timer_dialog_frame"), True, True, 5)
|
||||
self.get_content_area().pack_start(builder.get_object("timer_dialog_frame"), True, True, 0)
|
||||
|
||||
if self._action is TimerTool.TimerAction.ADD:
|
||||
self.set_timer_for_add()
|
||||
@@ -363,9 +355,11 @@ class TimerTool(Gtk.Box):
|
||||
|
||||
if p_count == 1:
|
||||
service = self._app.current_services.get(model[paths][Column.FAV_ID], None)
|
||||
if service:
|
||||
if service and service.picon_id:
|
||||
self.add_timer({"e2servicename": service.service,
|
||||
"e2servicereference": service.picon_id.rstrip(".png").replace("_", ":")})
|
||||
else:
|
||||
self._app.show_error_message("Not allowed in this context!")
|
||||
elif p_count > 1:
|
||||
self._app.show_error_message("Please, select only one item!")
|
||||
else:
|
||||
|
||||
@@ -120,7 +120,7 @@ HIDE_ICON = get_icon("go-jump", 16, _IMAGE_MISSING)
|
||||
TV_ICON = get_icon("tv-symbolic", 16, _IMAGE_MISSING)
|
||||
IPTV_ICON = get_icon("emblem-shared", 16, _IMAGE_MISSING)
|
||||
EPG_ICON = get_icon("gtk-index", 16, _IMAGE_MISSING)
|
||||
DEFAULT_ICON = get_icon("emblem-default", 16, _IMAGE_MISSING)
|
||||
DEFAULT_ICON = get_icon("emblem-default", 16, get_icon("emblem-default-symbolic", 16, _IMAGE_MISSING))
|
||||
|
||||
|
||||
@lru_cache(maxsize=1)
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2018-2023 Dmitriy Yefremov
|
||||
Copyright (c) 2018-2024 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-2023 Dmitriy Yefremov -->
|
||||
<!-- interface-copyright 2018-2024 Dmitriy Yefremov -->
|
||||
<!-- interface-authors Dmitriy Yefremov -->
|
||||
<!-- n-columns=2 n-rows=4 -->
|
||||
<object class="GtkGrid" id="cable_tr_box">
|
||||
@@ -471,7 +471,7 @@ Author: Dmitriy Yefremov
|
||||
<object class="GtkSpinButton" id="sat_position_button">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">True</property>
|
||||
<property name="width-chars">5</property>
|
||||
<property name="width-chars">6</property>
|
||||
<property name="secondary-icon-activatable">False</property>
|
||||
<property name="secondary-icon-sensitive">False</property>
|
||||
<property name="input-purpose">number</property>
|
||||
|
||||
@@ -8,8 +8,14 @@ The best way to run this program from source is using of [MSYS2](https://www.msy
|
||||
`pacman -S mingw-w64-x86_64-gtk3 mingw-w64-x86_64-python3 mingw-w64-x86_64-python3-gobject mingw-w64-x86_64-python3-pip mingw-w64-x86_64-python3-requests`
|
||||
Optional: `pacman -S mingw-w64-x86_64-python3-pillow`
|
||||
To support streams playback, install the following packages (the list may not be complete):
|
||||
For [MPV](https://mpv.io/) `pacman -S mingw-w64-x86_64-mpv`,
|
||||
For [GStreamer](https://gstreamer.freedesktop.org/) `pacman -S mingw-w64-x86_64-gst-libav mingw-w64-x86_64-gst-plugins-bad mingw-w64-x86_64-gst-plugins-base mingw-w64-x86_64-gst-plugins-good mingw-w64-x86_64-gstreamer`
|
||||
* For [GStreamer](https://gstreamer.freedesktop.org/) `pacman -S mingw-w64-x86_64-gst-libav mingw-w64-x86_64-gst-plugins-bad mingw-w64-x86_64-gst-plugins-base mingw-w64-x86_64-gst-plugins-good mingw-w64-x86_64-gstreamer`
|
||||
* For [MPV](https://mpv.io/) `pacman -S mingw-w64-x86_64-mpv`,
|
||||
To reduce installation size or try the latest changes, we can install the *libmpv* [build](https://github.com/shinchiro/mpv-winbuild-cmake/releases) (**mpv-dev**-x86_64-v3-*.7z) by [shinchiro](https://github.com/shinchiro).
|
||||
* Download and extract 7z archive.
|
||||
* Copy libmpv-2.dll to *C:\msys64\mingw64\bin*
|
||||
* libmpv.dll.a to *C:\msys64\mingw64\lib*
|
||||
and folder *include\mpv to *C:\msys64\mingw64\include* path.
|
||||
|
||||
5. Download and unzip the archive with sources from preferred branch (e.g. [master](https://github.com/DYefremov/DemonEditor/archive/refs/heads/master.zip)) in to folder where MSYS2 is installed. E.g: `c:\msys64\home\username\`
|
||||
6. Run mingw64 shell. Go to the folder where the program was unpacked. E.g: `cd DemonEditor/`
|
||||
And run: `./start.py`
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/bin/bash
|
||||
VER="3.9.0_Alpha"
|
||||
VER="3.10.2_Beta"
|
||||
B_PATH="dist/DemonEditor"
|
||||
DEB_PATH="$B_PATH/usr/share/demoneditor"
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
Package: demon-editor
|
||||
Version: 3.9.0-Alpha
|
||||
Version: 3.10.2-Beta
|
||||
Section: utils
|
||||
Priority: optional
|
||||
Architecture: all
|
||||
|
||||
@@ -5,7 +5,7 @@ Source: https://github.com/DYefremov/DemonEditor
|
||||
Files: *
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2018-2023 Dmitriy Yefremov
|
||||
Copyright (c) 2018-2024 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
|
||||
|
||||
@@ -2,14 +2,27 @@
|
||||
Version=1.0
|
||||
Name=DemonEditor
|
||||
GenericName=Enigma2 bouquets editor
|
||||
GenericName[it]=Editor di bouquet per Enigma2
|
||||
GenericName[be]=Рэдактар букетаў Enigma2
|
||||
GenericName[de]=Enigma2 Bouquet-Editor
|
||||
GenericName[es]=Editor de ramos de Enigma2
|
||||
GenericName[it]=Editor di bouquet Enigma2
|
||||
GenericName[nl]=Enigma2 boeket editor
|
||||
GenericName[pl]=Edytor bukietów Enigma2
|
||||
GenericName[pt]=Editor de buquês Enigma2
|
||||
GenericName[ru]=Редактор букетов Enigma2
|
||||
GenericName[tr]=Enigma2 buket düzenleyici
|
||||
GenericName[zh_CN]=Enigma2频道编辑器
|
||||
Comment=Channel and satellite list editor for Enigma2
|
||||
Comment[be]=Рэдактар спісу каналаў і супутнікаў для Enigma2
|
||||
Comment[de]=Kanal- und Satellitenlisten-Editor für Enigma2
|
||||
Comment[es]=Editor de lista de canales y satélites para Enigma2
|
||||
Comment[it]=Editor di elenchi di canali e satelliti per Enigma2
|
||||
Comment[nl]=Kanaal- en satellietlijsteditor voor Enigma2
|
||||
Comment[pl]=Edytor list kanałów i satelitów dla Enigma2
|
||||
Comment[pt]=Editor de lista de canais e satélites para Enigma2
|
||||
Comment[ru]=Редактор списка каналов и спутников для Enigma2
|
||||
Comment[be]=Рэдактар спіса каналаў і спадарожнікаў для Enigma2
|
||||
Comment[de]=Programm- und Satellitenlisten-Editor für Enigma2
|
||||
Comment[it]=Editor di liste canali e satelliti per Enigma2
|
||||
Comment[tr]=Enigma2 için kanal ve uydu listesi editörü
|
||||
Comment[es]=Editor de listas de canales y satélites para Enigma2
|
||||
Comment[tr]=Enigma2 için Kanal ve uydu listesi düzenleyici
|
||||
Comment[zh_CN]=Enigma2频道和卫星列表编辑器
|
||||
Icon=demon-editor
|
||||
Exec=/usr/bin/demon-editor
|
||||
Terminal=false
|
||||
|
||||
@@ -81,8 +81,8 @@ app = BUNDLE(coll,
|
||||
'CFBundleGetInfoString': "Enigma2 channel and satellite editor",
|
||||
'LSApplicationCategoryType': 'public.app-category.utilities',
|
||||
'LSMinimumSystemVersion': '10.13',
|
||||
'CFBundleShortVersionString': f"3.9.0.{BUILD_DATE} Alpha",
|
||||
'NSHumanReadableCopyright': u"Copyright © 2023, Dmitriy Yefremov",
|
||||
'CFBundleShortVersionString': f"3.10.2.{BUILD_DATE} Beta",
|
||||
'NSHumanReadableCopyright': u"Copyright © 2024, Dmitriy Yefremov",
|
||||
'NSRequiresAquaSystemAppearance': 'false',
|
||||
'NSHighResolutionCapable': 'true'
|
||||
})
|
||||
|
||||
@@ -2,14 +2,27 @@
|
||||
Version=1.0
|
||||
Name=DemonEditor
|
||||
GenericName=Enigma2 bouquets editor
|
||||
GenericName[it]=Editor di bouquet per Enigma2
|
||||
GenericName[be]=Рэдактар букетаў Enigma2
|
||||
GenericName[de]=Enigma2 Bouquet-Editor
|
||||
GenericName[es]=Editor de ramos de Enigma2
|
||||
GenericName[it]=Editor di bouquet Enigma2
|
||||
GenericName[nl]=Enigma2 boeket editor
|
||||
GenericName[pl]=Edytor bukietów Enigma2
|
||||
GenericName[pt]=Editor de buquês Enigma2
|
||||
GenericName[ru]=Редактор букетов Enigma2
|
||||
GenericName[tr]=Enigma2 buket düzenleyici
|
||||
GenericName[zh_CN]=Enigma2频道编辑器
|
||||
Comment=Channel and satellite list editor for Enigma2
|
||||
Comment[be]=Рэдактар спісу каналаў і супутнікаў для Enigma2
|
||||
Comment[de]=Kanal- und Satellitenlisten-Editor für Enigma2
|
||||
Comment[es]=Editor de lista de canales y satélites para Enigma2
|
||||
Comment[it]=Editor di elenchi di canali e satelliti per Enigma2
|
||||
Comment[nl]=Kanaal- en satellietlijsteditor voor Enigma2
|
||||
Comment[pl]=Edytor list kanałów i satelitów dla Enigma2
|
||||
Comment[pt]=Editor de lista de canais e satélites para Enigma2
|
||||
Comment[ru]=Редактор списка каналов и спутников для Enigma2
|
||||
Comment[be]=Рэдактар спіса каналаў і спадарожнікаў для Enigma2
|
||||
Comment[de]=Programm- und Satellitenlisten-Editor für Enigma2
|
||||
Comment[it]=Editor di liste canali e satelliti per Enigma2
|
||||
Comment[tr]=Enigma2 için kanal ve uydu listesi editörü
|
||||
Comment[es]=Editor de listas de canales y satélites para Enigma2
|
||||
Comment[tr]=Enigma2 için Kanal ve uydu listesi düzenleyici
|
||||
Comment[zh_CN]=Enigma2频道和卫星列表编辑器
|
||||
Icon=demon-editor
|
||||
Exec=bash -c 'cd $(dirname %k) && ./start.py'
|
||||
Terminal=false
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Copyright (C) 2018-2023 Dmitriy Yefremov
|
||||
# Copyright (C) 2018-2024 Dmitriy Yefremov
|
||||
# This file is distributed under the MIT license.
|
||||
#
|
||||
#
|
||||
@@ -1503,3 +1503,42 @@ msgstr "Скарыстаць агульную тэчку для піконаў"
|
||||
|
||||
msgid "Activates single folder use for several profiles."
|
||||
msgstr "Актывуе выкарыстанне адзінай тэчкі для некалькіх профіляў."
|
||||
|
||||
msgid "Events"
|
||||
msgstr "Падзеі"
|
||||
|
||||
msgid "Markers"
|
||||
msgstr "Маркеры"
|
||||
|
||||
msgid "IPTV only"
|
||||
msgstr "Толькі IPTV"
|
||||
|
||||
msgid "No"
|
||||
msgstr "Не"
|
||||
|
||||
msgid "Not found."
|
||||
msgstr "Не знойдзена."
|
||||
|
||||
msgid "Current EPG cache contents."
|
||||
msgstr "Змесціва бягучага EPG кэша."
|
||||
|
||||
msgid "Source error!"
|
||||
msgstr "Памылка крыніцы!"
|
||||
|
||||
msgid "The EPG source for the favorites list is not set!"
|
||||
msgstr "Не ўсталявана крыніца EPG для спіса абранага!"
|
||||
|
||||
msgid "Add to EPG sources list"
|
||||
msgstr "Дадаць у спіс крыніц EPG"
|
||||
|
||||
msgid "Current bouquet"
|
||||
msgstr "Абраны букет"
|
||||
|
||||
msgid "Single bouquet"
|
||||
msgstr "Адзінкавы букет"
|
||||
|
||||
msgid "Split by groups"
|
||||
msgstr "Падзел па групах"
|
||||
|
||||
msgid "Create sub-bouquets"
|
||||
msgstr "Стварыць падбукеты"
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
# Copyright (C) 2018-2021 Dmitriy Yefremov
|
||||
# Copyright (C) 2018-2024 Dmitriy Yefremov
|
||||
# This file is distributed under the MIT license.
|
||||
#
|
||||
# Charly, 2019.
|
||||
# Dmitriy Yefremov, 2020-2023.
|
||||
# Dmitriy Yefremov, 2020-2024.
|
||||
# Thomas Schmidt, 2021.
|
||||
msgid ""
|
||||
msgstr ""
|
||||
@@ -1517,3 +1517,42 @@ msgstr "Gemeinsamen Ordner für Picons verwenden"
|
||||
|
||||
msgid "Activates single folder use for several profiles."
|
||||
msgstr "Aktiviert die Verwendung eines einzelnen Ordners für mehrere Profile."
|
||||
|
||||
msgid "Events"
|
||||
msgstr "Events"
|
||||
|
||||
msgid "Markers"
|
||||
msgstr "Markers"
|
||||
|
||||
msgid "IPTV only"
|
||||
msgstr "Nur IPTV"
|
||||
|
||||
msgid "No"
|
||||
msgstr "Nein"
|
||||
|
||||
msgid "Not found."
|
||||
msgstr "Nicht gefunden."
|
||||
|
||||
msgid "Current EPG cache contents."
|
||||
msgstr "Aktueller EPG-Cache-Inhalt."
|
||||
|
||||
msgid "Source error!"
|
||||
msgstr "Quellfehler!"
|
||||
|
||||
msgid "The EPG source for the favorites list is not set!"
|
||||
msgstr "Die EPG-Quelle für die Favoritenliste ist nicht eingestellt!"
|
||||
|
||||
msgid "Add to EPG sources list"
|
||||
msgstr "Zur EPG-Quellenliste hinzufügen"
|
||||
|
||||
msgid "Current bouquet"
|
||||
msgstr "Aktuelles Bouquet"
|
||||
|
||||
msgid "Single bouquet"
|
||||
msgstr "Einzel Bouquet"
|
||||
|
||||
msgid "Split by groups"
|
||||
msgstr "Aufteilung nach Gruppen"
|
||||
|
||||
msgid "Create sub-bouquets"
|
||||
msgstr "Sub-Bouquets erstellen"
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
# Copyright (C) 2018-2022 Dmitriy Yefremov
|
||||
# Copyright (C) 2018-2024 Dmitriy Yefremov
|
||||
# This file is distributed under the MIT license.
|
||||
#
|
||||
# SPDX-FileCopyrightText: 2022, 2023 Massimo Pissarello <mapi68@gmail.com>
|
||||
# SPDX-FileCopyrightText: 2022, 2023, 2024 Massimo Pissarello <mapi68@gmail.com>
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"PO-Revision-Date: 2023-12-18 05:40+0100\n"
|
||||
"PO-Revision-Date: 2024-02-20 21:33+0100\n"
|
||||
"Last-Translator: Massimo Pissarello <mapi68@gmail.com>\n"
|
||||
"Language-Team: Italian <>\n"
|
||||
"Language: it\n"
|
||||
@@ -824,7 +824,7 @@ msgstr "Abilita barra di riproduzione diretta"
|
||||
|
||||
msgid "Enables direct sending and playback of media links on the receiver"
|
||||
msgstr ""
|
||||
"Consenti l'invio diretto e la riproduzione di collegamenti multimediali sul"
|
||||
"Abilita l'invio e la riproduzione diretta di collegamenti multimediali sul"
|
||||
" ricevitore"
|
||||
|
||||
msgid "Watch the channel in the program"
|
||||
@@ -1412,8 +1412,8 @@ msgstr "Riproduci dall'elenco principale"
|
||||
|
||||
msgid "Enables URL parsing using yt-dlp to get direct links to media."
|
||||
msgstr ""
|
||||
"Abilita l'analisi degli URL usando yt-dlp per ottenere collegamenti"
|
||||
" diretti ai media."
|
||||
"Abilita l'analisi degli URL utilizzando yt-dlp per ottenere collegamenti"
|
||||
" diretti ai contenuti multimediali."
|
||||
|
||||
msgid "Permissions..."
|
||||
msgstr "Permessi..."
|
||||
@@ -1509,10 +1509,11 @@ msgid "Removed"
|
||||
msgstr "Rimosso"
|
||||
|
||||
msgid "Enables overwriting existing main list services."
|
||||
msgstr "Consente di sovrascrivere i servizi esistenti dell'elenco principale."
|
||||
msgstr ""
|
||||
"Abilita la sovrascrittura dei servizi esistenti dell'elenco principale."
|
||||
|
||||
msgid "Enables skipping services import from lamedb."
|
||||
msgstr "Consente di saltare l'importazione dei servizi da lamedb."
|
||||
msgstr "Abilita il salto dell'importazione dei servizi da lamedb."
|
||||
|
||||
msgid "Bouquets data only"
|
||||
msgstr "Bouquet solo dati"
|
||||
@@ -1565,4 +1566,41 @@ msgstr "Usa la cartella comune per i picon"
|
||||
msgid "Activates single folder use for several profiles."
|
||||
msgstr "Attiva l'uso di una singola cartella per diversi profili."
|
||||
|
||||
msgid "Events"
|
||||
msgstr "Eventi"
|
||||
|
||||
msgid "Markers"
|
||||
msgstr "Marcatori"
|
||||
|
||||
msgid "IPTV only"
|
||||
msgstr "Solo IPTV"
|
||||
|
||||
msgid "No"
|
||||
msgstr "No"
|
||||
|
||||
msgid "Not found."
|
||||
msgstr "Non trovato."
|
||||
|
||||
msgid "Current EPG cache contents."
|
||||
msgstr "Contenuto attuale cache EPG."
|
||||
|
||||
msgid "Source error!"
|
||||
msgstr "Errore fonte!"
|
||||
|
||||
msgid "The EPG source for the favorites list is not set!"
|
||||
msgstr "La fonte EPG per l'elenco dei preferiti non è impostata!"
|
||||
|
||||
msgid "Add to EPG sources list"
|
||||
msgstr "Aggiungi all'elenco delle fonti EPG"
|
||||
|
||||
msgid "Current bouquet"
|
||||
msgstr "Bouquet attuale"
|
||||
|
||||
msgid "Single bouquet"
|
||||
msgstr "Bouquet singolo"
|
||||
|
||||
msgid "Split by groups"
|
||||
msgstr "Dividi per gruppi"
|
||||
|
||||
msgid "Create sub-bouquets"
|
||||
msgstr "Crea sotto-bouquet"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Copyright (C) 2018-2023 Dmitriy Yefremov
|
||||
# Copyright (C) 2018-2024 Dmitriy Yefremov
|
||||
# This file is distributed under the MIT license.
|
||||
#
|
||||
#
|
||||
@@ -1311,7 +1311,7 @@ msgid "Sets the profile folder as default to store picons, backups, etc."
|
||||
msgstr "Устанавливает папку профиля по умолчанию для хранения пиконов, резервных копий и т. п."
|
||||
|
||||
msgid "New sub-bouquet"
|
||||
msgstr "Создать суббукет"
|
||||
msgstr "Создать подбукет"
|
||||
|
||||
msgid "Mark not presented in Bouquets"
|
||||
msgstr "Отметить отсутствующие в букетах"
|
||||
@@ -1500,3 +1500,42 @@ msgstr "Использовать общую папку для пиконов"
|
||||
|
||||
msgid "Activates single folder use for several profiles."
|
||||
msgstr "Активирует использование одной папки для нескольких профилей."
|
||||
|
||||
msgid "Events"
|
||||
msgstr "События"
|
||||
|
||||
msgid "Markers"
|
||||
msgstr "Маркеры"
|
||||
|
||||
msgid "IPTV only"
|
||||
msgstr "Только IPTV"
|
||||
|
||||
msgid "No"
|
||||
msgstr "Нет"
|
||||
|
||||
msgid "Not found."
|
||||
msgstr "Не найдено."
|
||||
|
||||
msgid "Current EPG cache contents."
|
||||
msgstr "Содержимое текущего EPG кэша."
|
||||
|
||||
msgid "Source error!"
|
||||
msgstr "Ошибка источника!"
|
||||
|
||||
msgid "The EPG source for the favorites list is not set!"
|
||||
msgstr "Не установлен источник EPG для списка избранного!"
|
||||
|
||||
msgid "Add to EPG sources list"
|
||||
msgstr "Добавить в список источников EPG"
|
||||
|
||||
msgid "Current bouquet"
|
||||
msgstr "Текущий букет"
|
||||
|
||||
msgid "Single bouquet"
|
||||
msgstr "Одиночный букет"
|
||||
|
||||
msgid "Split by groups"
|
||||
msgstr "Разбить по группам"
|
||||
|
||||
msgid "Create sub-bouquets"
|
||||
msgstr "Создать подбукеты"
|
||||
|
||||
Reference in New Issue
Block a user