mirror of
https://github.com/DYefremov/DemonEditor.git
synced 2026-05-09 05:56:32 +02:00
Compare commits
40 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e2aa21060b | ||
|
|
b15691207b | ||
|
|
ab3ca134a7 | ||
|
|
7f839f3fa0 | ||
|
|
c3e8eac4b9 | ||
|
|
04f808843d | ||
|
|
eea4a68993 | ||
|
|
bfba5b5237 | ||
|
|
e203a38966 | ||
|
|
a3b5609138 | ||
|
|
b3c131b753 | ||
|
|
a8ea5ad974 | ||
|
|
831184af2e | ||
|
|
1f51766dea | ||
|
|
f8f1536213 | ||
|
|
0accfbd3d1 | ||
|
|
ee462b24f7 | ||
|
|
17ee189db8 | ||
|
|
8389293b4b | ||
|
|
99e0c79b6c | ||
|
|
02cdbc4e56 | ||
|
|
d57f0490d2 | ||
|
|
9680347180 | ||
|
|
7fbdc32f91 | ||
|
|
a69f54435d | ||
|
|
0d47433f80 | ||
|
|
d16a8e44f6 | ||
|
|
053a834d6d | ||
|
|
62c1ef852c | ||
|
|
b5234c55e8 | ||
|
|
472ebba8e9 | ||
|
|
d427cf66b0 | ||
|
|
9fe3d8077f | ||
|
|
2f12ef7bdd | ||
|
|
b6ad661e39 | ||
|
|
6355e0d75a | ||
|
|
29016056c2 | ||
|
|
23fe71e5cc | ||
|
|
80e4edd084 | ||
|
|
3d0bb6ad3c |
78
README.md
78
README.md
@@ -1,10 +1,12 @@
|
||||
# <img src="app/ui/icons/hicolor/96x96/apps/demon-editor.png" width="32" /> DemonEditor
|
||||
[](LICENSE)
|
||||
## Enigma2 channel and satellites list editor for GNU/Linux.
|
||||
[](LICENSE) 
|
||||
### Enigma2 channel and satellites list editor for GNU/Linux.
|
||||
Experimental support of Neutrino-MP or others on the same basis (BPanther, etc).
|
||||
Focused on the convenience of working in lists from the keyboard. The mouse is also fully supported (Drag and Drop etc)
|
||||
Focused on the convenience of working in lists from the keyboard. The mouse is also fully supported (Drag and Drop etc).
|
||||
|
||||

|
||||
|
||||
### Main features of the program:
|
||||
## Main features of the program
|
||||
* Editing bouquets, channels, satellites.
|
||||
* Import function.
|
||||
* Backup function.
|
||||
@@ -13,12 +15,12 @@ Focused on the convenience of working in lists from the keyboard. The mouse is a
|
||||
* Downloading of picons and updating of satellites (transponders) from the web.
|
||||
* Import to bouquet(Neutrino WEBTV) from m3u.
|
||||
* Export of bouquets with IPTV services in m3u.
|
||||
* Assignment of EPGs from DVB or XML for IPTV services (only Enigma2, experimental).
|
||||
* Assignment of EPG from DVB or XML for IPTV services (only Enigma2, experimental).
|
||||
* Preview (playback) of IPTV or other streams directly from the bouquet list (should be installed [VLC](https://www.videolan.org/vlc/)).
|
||||
|
||||
### Keyboard shortcuts:
|
||||
#### Keyboard shortcuts
|
||||
* **Ctrl + X** - only in bouquet list.
|
||||
* **Ctrl + C** - only in services list.
|
||||
* **Ctrl + C** - only in services list.
|
||||
Clipboard is **"rubber"**. There is an accumulation before the insertion!
|
||||
* **Ctrl + Insert** - copies the selected channels from the main list to the the bouquet beginning
|
||||
or inserts (creates) a new bouquet.
|
||||
@@ -30,40 +32,50 @@ Clipboard is **"rubber"**. There is an accumulation before the insertion!
|
||||
* **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 + W** - switch to the channel and watch in the program.
|
||||
* **Space** - select/deselect.
|
||||
* **Left/Right** - remove selection.
|
||||
* **Ctrl + Up, Down, PageUp, PageDown, Home, End** - move selected items in the list.
|
||||
* **Ctrl + Up, Down, PageUp, PageDown, Home, End**- move selected items in the list.
|
||||
* **Ctrl + O** - (re)load user data from current dir.
|
||||
* **Ctrl + D** - load data from receiver.
|
||||
* **Ctrl + U/B** upload data/bouquets to receiver.
|
||||
* **Ctrl + U/B** - upload data/bouquets to receiver.
|
||||
* **Ctrl + I** - extra info, details.
|
||||
* **Ctrl + F** - show/hide search bar.
|
||||
* **Ctrl + Shift + F** - show/hide filter bar.
|
||||
* **Ctrl + Shift + F** - show/hide filter bar.
|
||||
|
||||
For **multiple** selection with the mouse, press and hold the **Ctrl** key!
|
||||
|
||||
For multiple mouse selection (including Drag and Drop), press and hold the **Ctrl** key!
|
||||
|
||||
### Minimum requirements:
|
||||
Python >= 3.5.2 and GTK+ >= 3.16 with PyGObject bindings, python3-requests.
|
||||
|
||||
### Launching:
|
||||
To start the program, in most cases it is enough to download the archive, unpack and run it by
|
||||
double clicking on DemonEditor.desktop in the root directory, or launching from the console
|
||||
with the command: ```./start.py```
|
||||
## Minimum requirements
|
||||
*Python >= 3.5.2, GTK+ >= 3.16 with PyGObject bindings, python3-requests.*
|
||||
## Installation and Launch
|
||||
* ### Linux
|
||||
To start the program, in most cases it is enough to download the [archive](https://github.com/DYefremov/DemonEditor/archive/master.zip), unpack and run it by
|
||||
double clicking on DemonEditor.desktop in the root directory, or launching from the console
|
||||
with the command:
|
||||
```./start.py```
|
||||
Extra folders can be deleted, excluding the *app* folder and root files like *DemonEditor.desktop* and *start.py*!
|
||||
|
||||
### Note:
|
||||
|
||||
To create a simple **debian package**, you can use the *build-deb.sh.*
|
||||
Users of **LTS** versions of [Ubuntu](https://ubuntu.com/) or those based on them can use [PPA](https://launchpad.net/~dmitriy-yefremov/+archive/ubuntu/demon-editor) repository.
|
||||
Users of **LTS** versions of [Ubuntu](https://ubuntu.com/) or distributions based on them can use [PPA](https://launchpad.net/~dmitriy-yefremov/+archive/ubuntu/demon-editor) repository.
|
||||
* ### macOS (experimental)
|
||||
**This program can also be run on macOS.**
|
||||
To work in this OS, you must use a [separate branch](https://github.com/DYefremov/DemonEditor/tree/experimental-mac).
|
||||
**The functionality and performance of this version may be different from the Linux version!**
|
||||
|
||||
## Important
|
||||
The program is tested only with [openATV](https://www.opena.tv/) image and **Formuler F1** receiver in [Linux Mint](https://linuxmint.com/) (MATE 64-bit) distribution!
|
||||
|
||||
The program is tested only with [openATV](https://www.opena.tv/) image and **Formuler F1** receiver in my favourite Linux distributions
|
||||
(the latest versions of [Linux Mint](https://linuxmint.com/) 18.* and 19* MATE 64-bit)!
|
||||
|
||||
### Important:
|
||||
Terrestrial(DVB-T/T2) and cable(DVB-C) channels are only supported for Enigma2!
|
||||
Terrestrial(DVB-T/T2) and cable(DVB-C) channels are only supported for Enigma2.
|
||||
Main supported *lamedb* format is version **4**. Versions **3** and **5** has only **experimental** support!
|
||||
For version **3** is only read mode available. When saving, version **4** format is used instead!
|
||||
For version **3** is only read mode available. When saving, version **4** format is used instead.
|
||||
|
||||
When using the multiple import feature, from *lamedb* will be taken data **only for channels that are in the
|
||||
selected bouquets!** If you need full set of the data, including *[satellites, terrestrial, cables].xml* (current files will be overwritten),
|
||||
just load your data via *"File/Open"* and press *"Save"*. When importing separate bouquet files, only those services
|
||||
(excluding IPTV) that are in the **current open lamedb** (main list of services) will be imported.
|
||||
When using the multiple import feature, from *lamedb* will be taken data **only for channels that are in the selected bouquets!**
|
||||
If you need full set of the data, including *[satellites, terrestrial, cables].xml* (current files will be overwritten),
|
||||
just load your data via *"File/Open"* and press *"Save"*. When importing separate bouquet files, only those services
|
||||
(excluding IPTV) that are in the **current open lamedb** (main list of services) will be imported.
|
||||
#### Command line arguments:
|
||||
* **-l** - write logs to file.
|
||||
* **-d on/off** - turn on/off debug mode. Allows to display more information in the logs.
|
||||
|
||||
## License
|
||||
Licensed under the [MIT](LICENSE) license.
|
||||
|
||||
@@ -72,8 +72,7 @@ def download_data(*, settings, download_type=DownloadType.ALL, callback=print, f
|
||||
if download_type is DownloadType.ALL or download_type is DownloadType.BOUQUETS:
|
||||
ftp.cwd(settings.services_path)
|
||||
file_list = BQ_FILES_LIST + DATA_FILES_LIST if download_type is DownloadType.ALL else BQ_FILES_LIST
|
||||
for file in filter(lambda f: f.endswith(file_list), ftp.nlst()):
|
||||
download_file(ftp, file, save_path, callback)
|
||||
download_files(ftp, save_path, file_list, callback)
|
||||
# *.xml and webtv
|
||||
if download_type in (DownloadType.ALL, DownloadType.SATELLITES):
|
||||
download_xml(ftp, save_path, settings.satellites_xml_path, STC_XML_FILE, callback)
|
||||
@@ -93,8 +92,7 @@ def download_data(*, settings, download_type=DownloadType.ALL, callback=print, f
|
||||
save_path = epg_options.get("epg_dat_path", save_path)
|
||||
|
||||
ftp.cwd(stb_path)
|
||||
for file in filter(lambda f: f.endswith("epg.dat"), ftp.nlst()):
|
||||
download_file(ftp, file, save_path, callback)
|
||||
download_files(ftp, save_path, "epg.dat", callback)
|
||||
|
||||
callback("\nDone.\n")
|
||||
|
||||
@@ -119,6 +117,8 @@ def upload_data(*, settings, download_type=DownloadType.ALL, remove_unused=False
|
||||
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!"
|
||||
|
||||
params = urlencode({"text": message, "type": 2, "timeout": 5})
|
||||
ht.send((url + "message?{}".format(params), "Sending info message... "))
|
||||
@@ -128,14 +128,15 @@ def upload_data(*, settings, download_type=DownloadType.ALL, remove_unused=False
|
||||
ht.send((url + "powerstate?newstate=0", "Toggle Standby "))
|
||||
time.sleep(2)
|
||||
else:
|
||||
# telnet
|
||||
tn = telnet(host=host,
|
||||
user=settings.telnet_user,
|
||||
password=settings.telnet_password,
|
||||
timeout=settings.telnet_timeout)
|
||||
next(tn)
|
||||
# terminate enigma or neutrino
|
||||
tn.send("init 4")
|
||||
if download_type is not DownloadType.PICONS:
|
||||
# telnet
|
||||
tn = telnet(host=host,
|
||||
user=settings.telnet_user,
|
||||
password=settings.telnet_password,
|
||||
timeout=settings.telnet_timeout)
|
||||
next(tn)
|
||||
# terminate enigma or neutrino
|
||||
tn.send("init 4")
|
||||
|
||||
with FTP(host=host, user=settings.user, passwd=settings.password) as ftp:
|
||||
ftp.encoding = "utf-8"
|
||||
@@ -199,7 +200,11 @@ def upload_files(ftp, data_path, file_list, callback):
|
||||
|
||||
|
||||
def remove_unused_bouquets(ftp, callback):
|
||||
for file in filter(lambda f: f.endswith(("tv", "radio", "bouquets.xml", "ubouquets.xml")), ftp.nlst()):
|
||||
files = []
|
||||
ftp.dir(files.append)
|
||||
bq_files = ("tv", "radio", "bouquets.xml", "ubouquets.xml")
|
||||
|
||||
for file in filter(lambda f: f.endswith(bq_files), map(lambda f: f.split()[-1], map(str.rstrip, files))):
|
||||
callback("Deleting file: {}. Status: {}\n".format(file, ftp.delete(file)))
|
||||
|
||||
|
||||
@@ -213,7 +218,7 @@ def upload_xml(ftp, data_path, xml_path, xml_files, callback):
|
||||
def download_xml(ftp, data_path, xml_path, xml_files, callback):
|
||||
""" Used for download *.xml files. """
|
||||
ftp.cwd(xml_path)
|
||||
list(map(lambda f: download_file(ftp, f, data_path, callback), (f for f in ftp.nlst() if f.endswith(xml_files))))
|
||||
download_files(ftp, data_path, xml_files, callback)
|
||||
|
||||
|
||||
# ***************** Picons *******************#
|
||||
@@ -237,7 +242,10 @@ def download_picons(ftp, src, dest, callback, files_filter=None):
|
||||
callback(str(e))
|
||||
return
|
||||
|
||||
for file in filter(picons_filter_function(files_filter), ftp.nlst()):
|
||||
files = []
|
||||
ftp.dir(files.append)
|
||||
|
||||
for file in filter(picons_filter_function(files_filter), map(lambda f: f.split()[-1], map(str.rstrip, files))):
|
||||
download_file(ftp, file, dest, callback)
|
||||
|
||||
|
||||
@@ -249,7 +257,10 @@ def delete_picons(ftp, callback, dest=None, files_filter=None):
|
||||
callback(str(e))
|
||||
return
|
||||
|
||||
for file in filter(picons_filter_function(files_filter), ftp.nlst()):
|
||||
files = []
|
||||
ftp.dir(files.append)
|
||||
|
||||
for file in filter(picons_filter_function(files_filter), map(lambda f: f.split()[-1], map(str.rstrip, files))):
|
||||
callback("Delete file: {}. Status: {}\n".format(file, ftp.delete(file)))
|
||||
|
||||
|
||||
@@ -266,6 +277,15 @@ def picons_filter_function(files_filter=None):
|
||||
return lambda f: f in files_filter if files_filter else f.endswith(PICONS_SUF)
|
||||
|
||||
|
||||
def download_files(ftp, save_path, file_list, callback):
|
||||
""" Downloads files from the receiver via FTP. """
|
||||
files = []
|
||||
ftp.dir(files.append)
|
||||
|
||||
for file in map(lambda f: f.split()[-1], filter(lambda s: s.endswith(file_list), map(str.rstrip, files))):
|
||||
download_file(ftp, file, save_path, callback)
|
||||
|
||||
|
||||
def download_file(ftp, name, save_path, callback):
|
||||
with open(save_path + name, "wb") as f:
|
||||
callback("Downloading file: {}. Status: {}\n".format(name, str(ftp.retrbinary("RETR " + name, f.write))))
|
||||
|
||||
@@ -612,6 +612,14 @@ class Settings:
|
||||
|
||||
# *********** Appearance *********** #
|
||||
|
||||
@property
|
||||
def dark_mode(self):
|
||||
return self._settings.get("dark_mode", False)
|
||||
|
||||
@dark_mode.setter
|
||||
def dark_mode(self, value):
|
||||
self._settings["dark_mode"] = value
|
||||
|
||||
@property
|
||||
def is_themes_support(self):
|
||||
return self._settings.get("is_themes_support", False)
|
||||
@@ -678,6 +686,17 @@ class Settings:
|
||||
def debug_mode(self, value):
|
||||
self._settings["debug_mode"] = value
|
||||
|
||||
# **************** Experimental **************** #
|
||||
|
||||
@property
|
||||
def is_enable_experimental(self):
|
||||
""" Allows experimental functionality. """
|
||||
return self._settings.get("enable_experimental", False)
|
||||
|
||||
@is_enable_experimental.setter
|
||||
def is_enable_experimental(self, value):
|
||||
self._settings["enable_experimental"] = value
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
pass
|
||||
|
||||
@@ -2,11 +2,10 @@ import glob
|
||||
import os
|
||||
import re
|
||||
import shutil
|
||||
|
||||
from collections import namedtuple
|
||||
from html.parser import HTMLParser
|
||||
|
||||
from app.commons import run_task
|
||||
from app.commons import run_task, log
|
||||
from app.settings import SettingsType
|
||||
|
||||
_ENIGMA2_PICON_KEY = "{:X}:{:X}:{}"
|
||||
@@ -33,9 +32,9 @@ class PiconsParser(HTMLParser):
|
||||
self.picons = []
|
||||
|
||||
def handle_starttag(self, tag, attrs):
|
||||
if tag == 'td':
|
||||
if tag == "td":
|
||||
self._is_td = True
|
||||
if tag == 'th':
|
||||
if tag == "th":
|
||||
self._is_th = True
|
||||
if tag == "img":
|
||||
self._current_row.append(attrs[0][1])
|
||||
@@ -46,16 +45,16 @@ class PiconsParser(HTMLParser):
|
||||
self._current_cell.append(data.strip())
|
||||
|
||||
def handle_endtag(self, tag):
|
||||
if tag == 'td':
|
||||
if tag == "td":
|
||||
self._is_td = False
|
||||
elif tag == 'th':
|
||||
elif tag == "th":
|
||||
self._is_th = False
|
||||
|
||||
if tag in ('td', 'th'):
|
||||
if tag in ("td", "th"):
|
||||
final_cell = self._separator.join(self._current_cell).strip()
|
||||
self._current_row.append(final_cell)
|
||||
self._current_cell = []
|
||||
elif tag == 'tr':
|
||||
elif tag == "tr":
|
||||
row = self._current_row
|
||||
ln = len(row)
|
||||
|
||||
@@ -80,6 +79,10 @@ class PiconsParser(HTMLParser):
|
||||
|
||||
@staticmethod
|
||||
def parse(open_path, picons_path, tmp_path, provider, picon_ids, s_type=SettingsType.ENIGMA_2):
|
||||
if not os.path.isfile(open_path):
|
||||
log("PiconsParser error [parse]. No such file or directory: {}".format(open_path))
|
||||
return
|
||||
|
||||
with open(open_path, encoding="utf-8", errors="replace") as f:
|
||||
on_id, pos, ssid, single = provider.on_id, provider.pos, provider.ssid, provider.single
|
||||
neg_pos = pos.endswith("W")
|
||||
@@ -105,8 +108,7 @@ class PiconsParser(HTMLParser):
|
||||
shutil.copyfile(tmp_path + "www.lyngsat.com/" + p.ref.lstrip("."), p_name)
|
||||
except (TypeError, ValueError) as e:
|
||||
msg = "Picons format parse error: {}".format(p) + "\n" + str(e)
|
||||
# log(msg)
|
||||
print(msg)
|
||||
log(msg)
|
||||
|
||||
@staticmethod
|
||||
def format(ssid, on_id, namespace, picon_ids, s_type):
|
||||
|
||||
@@ -43,7 +43,10 @@ class YouTube:
|
||||
self._callback = callback
|
||||
|
||||
if self._settings.enable_yt_dl:
|
||||
self._yt_dl = YouTubeDL.get_instance(self._settings, callback=self._callback)
|
||||
try:
|
||||
self._yt_dl = YouTubeDL.get_instance(self._settings, callback=self._callback)
|
||||
except YouTubeException:
|
||||
pass # NOP
|
||||
|
||||
@classmethod
|
||||
def get_instance(cls, settings, callback=log):
|
||||
@@ -161,7 +164,7 @@ class PlayListParser(HTMLParser):
|
||||
|
||||
ct = resp.get("contents", None)
|
||||
if ct:
|
||||
for d in [(d.get("title", {}).get("simpleText", ""),
|
||||
for d in [(d.get("title", {}).get("runs", [{}])[0].get("text", ""),
|
||||
d.get("videoId", "")) for d in flat("playlistVideoRenderer", ct)]:
|
||||
self._playlist.append(d)
|
||||
self._is_script = False
|
||||
@@ -245,7 +248,7 @@ class YouTubeDL:
|
||||
if hasattr(youtube_dl.version, "__version__"):
|
||||
l_ver = self.get_last_release_id()
|
||||
cur_ver = youtube_dl.version.__version__
|
||||
if youtube_dl.version.__version__ < l_ver:
|
||||
if l_ver and youtube_dl.version.__version__ < l_ver:
|
||||
msg = "youtube-dl has new release!\nCurrent: {}. Last: {}.".format(cur_ver, l_ver)
|
||||
show_notification(msg)
|
||||
log(msg)
|
||||
@@ -262,8 +265,11 @@ class YouTubeDL:
|
||||
def get_last_release_id():
|
||||
""" Getting last release id. """
|
||||
url = "https://api.github.com/repos/ytdl-org/youtube-dl/releases/latest"
|
||||
with urlopen(url, timeout=10) as resp:
|
||||
return json.loads(resp.read().decode("utf-8")).get("tag_name", "0")
|
||||
try:
|
||||
with urlopen(url, timeout=10) as resp:
|
||||
return json.loads(resp.read().decode("utf-8")).get("tag_name", "0")
|
||||
except URLError as e:
|
||||
log("YouTubeDLHelper error [get last release id]: {}".format(e))
|
||||
|
||||
def get_latest_release(self):
|
||||
try:
|
||||
|
||||
@@ -59,14 +59,13 @@ class BackupDialog:
|
||||
def show(self):
|
||||
self._dialog_window.show()
|
||||
|
||||
@run_idle
|
||||
def init_data(self):
|
||||
try:
|
||||
files = os.listdir(self._backup_path)
|
||||
except FileNotFoundError as e:
|
||||
self.show_info_message(str(e), Gtk.MessageType.ERROR)
|
||||
else:
|
||||
for file in filter(lambda x: x.endswith(".zip"), files):
|
||||
if os.path.isdir(self._backup_path):
|
||||
for file in filter(lambda x: x.endswith(".zip"), os.listdir(self._backup_path)):
|
||||
self._model.append((file.rstrip(".zip"), False))
|
||||
else:
|
||||
os.makedirs(os.path.dirname(self._backup_path), exist_ok=True)
|
||||
|
||||
def on_restore_bouquets(self, item):
|
||||
self.restore(RestoreType.BOUQUETS)
|
||||
@@ -129,6 +128,8 @@ class BackupDialog:
|
||||
append_text_to_tview(name + "\n", self._text_view)
|
||||
except FileNotFoundError as e:
|
||||
self.show_info_message(str(e), Gtk.MessageType.ERROR)
|
||||
else:
|
||||
self._text_view.get_buffer().set_text("")
|
||||
|
||||
def restore(self, restore_type):
|
||||
model, paths = self._main_view.get_selection().get_selected_rows()
|
||||
|
||||
@@ -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">1.0.0 Alpha</property>
|
||||
<property name="version">1.0.1 Beta</property>
|
||||
<property name="copyright">2018-2020 Dmitriy Yefremov
|
||||
</property>
|
||||
<property name="comments" translatable="yes">Enigma2 channel and satellites list editor for GNU/Linux</property>
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
import locale
|
||||
from enum import Enum
|
||||
from functools import lru_cache
|
||||
from pathlib import Path
|
||||
|
||||
from app.commons import run_idle
|
||||
from .uicommons import Gtk, UI_RESOURCES_PATH, TEXT_DOMAIN, IS_GNOME_SESSION
|
||||
@@ -72,12 +73,13 @@ class WaitDialog:
|
||||
self._dialog.destroy()
|
||||
|
||||
|
||||
def show_dialog(dialog_type: DialogType, transient, text=None, settings=None, action_type=None, file_filter=None):
|
||||
""" Shows dialogs by name """
|
||||
def show_dialog(dialog_type, transient, text=None, settings=None, action_type=None, file_filter=None, buttons=None,
|
||||
title=None):
|
||||
""" Shows dialogs by name. """
|
||||
if dialog_type in (DialogType.INFO, DialogType.ERROR):
|
||||
return get_message_dialog(transient, dialog_type, Gtk.ButtonsType.OK, text)
|
||||
elif dialog_type is DialogType.CHOOSER and settings:
|
||||
return get_file_chooser_dialog(transient, text, settings, action_type, file_filter)
|
||||
return get_file_chooser_dialog(transient, text, settings, action_type, file_filter, buttons, title)
|
||||
elif dialog_type is DialogType.INPUT:
|
||||
return get_input_dialog(transient, text)
|
||||
elif dialog_type is DialogType.QUESTION:
|
||||
@@ -87,7 +89,7 @@ def show_dialog(dialog_type: DialogType, transient, text=None, settings=None, ac
|
||||
return get_about_dialog(transient)
|
||||
|
||||
|
||||
def get_chooser_dialog(transient, settings, name, patterns):
|
||||
def get_chooser_dialog(transient, settings, name, patterns, title=None):
|
||||
file_filter = Gtk.FileFilter()
|
||||
file_filter.set_name(name)
|
||||
for p in patterns:
|
||||
@@ -97,26 +99,30 @@ def get_chooser_dialog(transient, settings, name, patterns):
|
||||
transient=transient,
|
||||
settings=settings,
|
||||
action_type=Gtk.FileChooserAction.OPEN,
|
||||
file_filter=file_filter)
|
||||
file_filter=file_filter,
|
||||
title=title)
|
||||
|
||||
|
||||
def get_file_chooser_dialog(transient, text, settings, action_type, file_filter):
|
||||
dialog = Gtk.FileChooserDialog(get_message(text) if text else "", transient,
|
||||
action_type if action_type is not None else Gtk.FileChooserAction.SELECT_FOLDER,
|
||||
(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, Gtk.STOCK_OPEN, Gtk.ResponseType.OK),
|
||||
use_header_bar=IS_GNOME_SESSION)
|
||||
def get_file_chooser_dialog(transient, text, settings, action_type, file_filter, buttons=None, title=None):
|
||||
text = get_message(text) if text else ""
|
||||
action_type = Gtk.FileChooserAction.SELECT_FOLDER if action_type is None else action_type
|
||||
buttons = buttons or (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, Gtk.STOCK_OPEN, Gtk.ResponseType.OK)
|
||||
dialog = Gtk.FileChooserDialog(text, transient, action_type, buttons, use_header_bar=IS_GNOME_SESSION)
|
||||
dialog.set_title(get_message(title) if title else "")
|
||||
dialog.set_create_folders(False)
|
||||
|
||||
if file_filter is not None:
|
||||
dialog.add_filter(file_filter)
|
||||
|
||||
path = settings.data_local_path
|
||||
dialog.set_current_folder(path)
|
||||
dialog.set_current_folder(settings.data_local_path)
|
||||
response = dialog.run()
|
||||
if response == Gtk.ResponseType.OK:
|
||||
if dialog.get_filename():
|
||||
path = dialog.get_filename()
|
||||
if action_type is not Gtk.FileChooserAction.OPEN:
|
||||
path = path + "/"
|
||||
response = path
|
||||
|
||||
if response not in (Gtk.ResponseType.CANCEL, Gtk.ResponseType.DELETE_EVENT):
|
||||
path = Path(dialog.get_filename() or dialog.get_current_folder())
|
||||
if path.is_dir():
|
||||
response = "{}/".format(path.resolve())
|
||||
elif path.is_file():
|
||||
response = str(path.resolve())
|
||||
dialog.destroy()
|
||||
|
||||
return response
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
from contextlib import suppress
|
||||
from pathlib import Path
|
||||
|
||||
from app.commons import run_idle
|
||||
from app.commons import run_idle, log
|
||||
from app.eparser import get_bouquets, get_services
|
||||
from app.eparser.ecommons import BqType, BqServiceType, Bouquet
|
||||
from app.eparser.enigma.bouquets import get_bouquet
|
||||
@@ -119,6 +119,7 @@ class ImportDialog:
|
||||
self._services_model.clear()
|
||||
try:
|
||||
if not self._bouquets:
|
||||
log("Import [init data]: getting bouquets...")
|
||||
self._bouquets = get_bouquets(path, self._profile)
|
||||
for bqs in self._bouquets:
|
||||
for bq in bqs.bouquets:
|
||||
@@ -129,6 +130,7 @@ class ImportDialog:
|
||||
for srv in services:
|
||||
self._services[srv.fav_id] = srv
|
||||
except FileNotFoundError as e:
|
||||
log("Import error [init data]: {}".format(e))
|
||||
self.show_info_message(str(e), Gtk.MessageType.ERROR)
|
||||
|
||||
def on_import(self, item):
|
||||
@@ -139,9 +141,17 @@ class ImportDialog:
|
||||
if not self._bouquets or show_dialog(DialogType.QUESTION, self._dialog_window) == Gtk.ResponseType.CANCEL:
|
||||
return
|
||||
|
||||
self.import_data()
|
||||
|
||||
@run_idle
|
||||
def import_data(self):
|
||||
""" Importing data into models. """
|
||||
if not self._bouquets:
|
||||
return
|
||||
|
||||
log("Importing data...")
|
||||
services = set()
|
||||
to_delete = set()
|
||||
|
||||
for row in self._main_model:
|
||||
bq = (row[0], row[1])
|
||||
if row[-1]:
|
||||
@@ -151,19 +161,16 @@ class ImportDialog:
|
||||
services.add(srv)
|
||||
else:
|
||||
to_delete.add(bq)
|
||||
|
||||
bqs_to_delete = []
|
||||
for bqs in self._bouquets:
|
||||
for bq in bqs.bouquets:
|
||||
if (bq.name, bq.type) in to_delete:
|
||||
bqs_to_delete.append(bq)
|
||||
|
||||
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._dialog_window.destroy()
|
||||
|
||||
|
||||
BIN
app/ui/lang/be/LC_MESSAGES/demon-editor.mo
Normal file
BIN
app/ui/lang/be/LC_MESSAGES/demon-editor.mo
Normal file
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.
Binary file not shown.
@@ -4,6 +4,7 @@ from contextlib import suppress
|
||||
from datetime import datetime
|
||||
from functools import lru_cache
|
||||
from itertools import chain
|
||||
from urllib.parse import urlparse, unquote
|
||||
|
||||
from gi.repository import GLib, Gio
|
||||
|
||||
@@ -108,12 +109,14 @@ class Application(Gtk.Application):
|
||||
"on_fav_view_query_tooltip": self.on_fav_view_query_tooltip,
|
||||
"on_services_view_query_tooltip": self.on_services_view_query_tooltip,
|
||||
"on_view_drag_begin": self.on_view_drag_begin,
|
||||
"on_view_drag_end": self.on_view_drag_end,
|
||||
"on_view_drag_data_get": self.on_view_drag_data_get,
|
||||
"on_services_view_drag_drop": self.on_services_view_drag_drop,
|
||||
"on_services_view_drag_data_received": self.on_services_view_drag_data_received,
|
||||
"on_view_drag_data_received": self.on_view_drag_data_received,
|
||||
"on_bq_view_drag_data_received": self.on_bq_view_drag_data_received,
|
||||
"on_view_press": self.on_view_press,
|
||||
"on_view_release": self.on_view_release,
|
||||
"on_view_popup_menu": self.on_view_popup_menu,
|
||||
"on_view_focus": self.on_view_focus,
|
||||
"on_model_changed": self.on_model_changed,
|
||||
@@ -178,6 +181,7 @@ class Application(Gtk.Application):
|
||||
self._blacklist = set()
|
||||
self._current_bq_name = None
|
||||
self._bq_selected = "" # Current selected bouquet
|
||||
self._select_enabled = True # Multiple selection
|
||||
# Current satellite positions in the services list
|
||||
self._sat_positions = []
|
||||
self._marker_types = {BqServiceType.MARKER.name, BqServiceType.SPACE.name}
|
||||
@@ -339,6 +343,7 @@ class Application(Gtk.Application):
|
||||
set_action("on_data_save", self.on_data_save)
|
||||
set_action("on_download", self.on_download)
|
||||
set_action("on_data_open", self.on_data_open)
|
||||
set_action("on_archive_open", self.on_archive_open)
|
||||
# Search, Filter
|
||||
search_action = Gio.SimpleAction.new_stateful("search", None, GLib.Variant.new_boolean(False))
|
||||
search_action.connect("change-state", self.on_search_toggled)
|
||||
@@ -429,7 +434,7 @@ class Application(Gtk.Application):
|
||||
self.set_profile(profile)
|
||||
|
||||
def init_drag_and_drop(self):
|
||||
""" Enable drag-and-drop """
|
||||
""" Enable drag-and-drop. """
|
||||
target = []
|
||||
bq_target = []
|
||||
|
||||
@@ -449,6 +454,7 @@ class Application(Gtk.Application):
|
||||
|
||||
self._services_view.drag_source_set_target_list(None)
|
||||
self._services_view.drag_source_add_text_targets()
|
||||
self._services_view.drag_dest_add_text_targets()
|
||||
self._services_view.drag_dest_add_uri_targets()
|
||||
|
||||
self._bouquets_view.drag_dest_set_target_list(None)
|
||||
@@ -456,6 +462,13 @@ class Application(Gtk.Application):
|
||||
self._bouquets_view.drag_dest_add_text_targets()
|
||||
self._bouquets_view.drag_source_add_text_targets()
|
||||
|
||||
self._app_info_box.drag_dest_set(Gtk.DestDefaults.ALL, [], Gdk.DragAction.COPY)
|
||||
self._app_info_box.drag_dest_add_text_targets()
|
||||
# For multiple selection.
|
||||
self._services_view.get_selection().set_select_function(lambda *args: self._select_enabled)
|
||||
self._fav_view.get_selection().set_select_function(lambda *args: self._select_enabled)
|
||||
self._bouquets_view.get_selection().set_select_function(lambda *args: self._select_enabled)
|
||||
|
||||
def init_colors(self, update=False):
|
||||
""" Initialisation of background colors for the services.
|
||||
|
||||
@@ -716,6 +729,8 @@ class Application(Gtk.Application):
|
||||
self._bouquets.pop("{}:{}".format(b_row[Column.BQ_NAME], b_row[Column.BQ_TYPE]), None)
|
||||
self._bouquets_model.remove(itr)
|
||||
|
||||
self._bq_selected = ""
|
||||
self._bq_name_label.set_text(self._bq_selected)
|
||||
self._wait_dialog.hide()
|
||||
yield True
|
||||
|
||||
@@ -862,7 +877,6 @@ class Application(Gtk.Application):
|
||||
self._wait_dialog.show(get_message("Sorting data..."))
|
||||
GLib.idle_add(self.sort_fav, c_num, bq, paths, order, 0 if c_num == Column.FAV_NUM else "")
|
||||
|
||||
@run_idle
|
||||
def sort_fav(self, c_num, bq, paths, rev=False, nv=""):
|
||||
""" Sorting function for the bouquet details list.
|
||||
|
||||
@@ -930,30 +944,116 @@ class Application(Gtk.Application):
|
||||
|
||||
pol = ", {}: {},".format(get_message("Pol"), srv.pol) if srv.pol else ","
|
||||
fec = "{}: {}".format("FEC", srv.fec) if srv.fec else ","
|
||||
ht = "{}{}: {}\n{}: {}\n{}: {}\n{}: {}{} {}, {}: {}\n{}"
|
||||
ht = "{}{}: {}\n{}: {}\n{}: {}\n{}: {}{} {}, {}\n{}"
|
||||
return ht.format(header,
|
||||
get_message("Package"), srv.package,
|
||||
get_message("System"), srv.system,
|
||||
get_message("Freq"), srv.freq,
|
||||
get_message("Rate"), srv.rate, pol, fec, "SID", srv.ssid,
|
||||
get_message("Rate"), srv.rate, pol, fec, self.get_ssid_info(srv),
|
||||
ref)
|
||||
|
||||
def get_hint_for_srv_list(self, srv):
|
||||
""" Returns short info about service as formatted string for using as hint. """
|
||||
return "{}{}".format(*self.get_hint_header_info(srv))
|
||||
header, ref = self.get_hint_header_info(srv)
|
||||
return "{}{}\n{}".format(header, self.get_ssid_info(srv), ref)
|
||||
|
||||
def get_hint_header_info(self, srv):
|
||||
header = "{}: {}\n{}: {}\n".format(get_message("Name"), srv.service, get_message("Type"), srv.service_type)
|
||||
ref = "{}: {}".format(get_message("Service reference"), srv.picon_id.rstrip(".png"))
|
||||
return header, ref
|
||||
|
||||
def get_ssid_info(self, srv):
|
||||
""" Returns SID representation in hex and dec formats. """
|
||||
sid = srv.ssid or "0"
|
||||
try:
|
||||
dec = "{0:04d}".format(int(sid, 16))
|
||||
except ValueError as e:
|
||||
log("SID value conversion error: {}".format(e))
|
||||
else:
|
||||
return "SID: 0x{} ({})".format(sid.upper(), dec)
|
||||
|
||||
return "SID: 0x{}".format(sid.upper())
|
||||
|
||||
# ***************** Drag-and-drop *********************#
|
||||
|
||||
def on_view_drag_begin(self, view, context):
|
||||
""" Selects a row under the cursor in the view at the dragging beginning. """
|
||||
path, column = view.get_cursor()
|
||||
if path:
|
||||
view.get_selection().select_path(path)
|
||||
""" Sets its own icon for dragging.
|
||||
|
||||
We have to use "connect_after" (after="yes" in xml) to override what the default handler did.
|
||||
https://lazka.github.io/pgi-docs/Gtk-3.0/classes/Widget.html#Gtk.Widget.signals.drag_begin
|
||||
"""
|
||||
model, paths = view.get_selection().get_selected_rows()
|
||||
if len(paths) < 1:
|
||||
return
|
||||
|
||||
name, model = get_model_data(view)
|
||||
name_column, type_column = Column.SRV_SERVICE, Column.SRV_TYPE
|
||||
if name == self.FAV_MODEL_NAME:
|
||||
name_column, type_column = Column.FAV_SERVICE, Column.FAV_TYPE
|
||||
elif name == self.BQ_MODEL_NAME:
|
||||
name_column, type_column = Column.BQ_NAME, Column.BQ_TYPE
|
||||
# https://stackoverflow.com/a/52248549
|
||||
Gtk.drag_set_icon_pixbuf(context, self.get_drag_icon_pixbuf(model, paths, name_column, type_column), 0, 0)
|
||||
return True
|
||||
|
||||
def on_view_drag_end(self, view, context):
|
||||
self._select_enabled = True
|
||||
view.get_selection().unselect_all()
|
||||
|
||||
def get_drag_icon_pixbuf(self, model, paths, text_column, type_column):
|
||||
""" Creates and returns Pixbuf for a dragging icon. """
|
||||
import cairo
|
||||
|
||||
window = Gtk.OffscreenWindow()
|
||||
window.get_style_context().add_class(Gtk.STYLE_CLASS_DND)
|
||||
frame = Gtk.Frame()
|
||||
list_box = Gtk.ListBox()
|
||||
list_box.set_selection_mode(Gtk.SelectionMode.NONE)
|
||||
padding = 10
|
||||
|
||||
for index, row in enumerate([model[p] for p in paths]):
|
||||
if index == 25:
|
||||
list_box.add(Gtk.Arrow(Gtk.ArrowType.DOWN))
|
||||
break
|
||||
|
||||
h_box = Gtk.HBox()
|
||||
h_box.set_spacing(10)
|
||||
h_box.get_style_context().add_class(Gtk.STYLE_CLASS_LIST_ROW)
|
||||
label = Gtk.Label(row[text_column])
|
||||
label.set_alignment(0, 0)
|
||||
label.set_padding(padding, 2)
|
||||
h_box.add(label)
|
||||
label = Gtk.Label(row[type_column])
|
||||
label.set_halign(Gtk.Align.END)
|
||||
label.set_padding(padding, 2)
|
||||
h_box.add(label)
|
||||
list_box.add(h_box)
|
||||
|
||||
if len(paths) > 1:
|
||||
list_box.add(Gtk.Separator())
|
||||
h_box = Gtk.HBox()
|
||||
h_box.set_spacing(2)
|
||||
img = Gtk.Image.new_from_icon_name("document-properties", 0)
|
||||
h_box.add(img)
|
||||
h_box.add(Gtk.Label(len(paths)))
|
||||
h_box.set_halign(Gtk.Align.START)
|
||||
h_box.set_margin_left(10)
|
||||
h_box.set_margin_bottom(5)
|
||||
h_box.set_margin_top(2)
|
||||
list_box.add(h_box)
|
||||
|
||||
frame.add(list_box)
|
||||
frame.show_all()
|
||||
window.add(frame)
|
||||
window.show()
|
||||
alloc = frame.get_allocation()
|
||||
w, h = alloc.width, alloc.height
|
||||
surf = cairo.ImageSurface(cairo.FORMAT_ARGB32, w, h)
|
||||
frame.draw(cairo.Context(surf))
|
||||
pix = Gdk.pixbuf_get_from_surface(surf, 0, 0, w, h)
|
||||
window.destroy()
|
||||
|
||||
return pix
|
||||
|
||||
def on_view_drag_data_get(self, view, drag_context, data, info, time):
|
||||
selection = self.get_selection(view)
|
||||
@@ -976,11 +1076,15 @@ class Application(Gtk.Application):
|
||||
txt = data.get_text()
|
||||
uris = data.get_uris()
|
||||
if txt:
|
||||
self.receive_selection(view=view, drop_info=view.get_dest_row_at_pos(x, y), data=txt)
|
||||
name, model = get_model_data(view)
|
||||
if txt.startswith("file://") and name == self.SERVICE_MODEL_NAME:
|
||||
self.on_import_data(urlparse(unquote(txt)).path.strip())
|
||||
elif name == self.FAV_MODEL_NAME:
|
||||
self.receive_selection(view=view, drop_info=view.get_dest_row_at_pos(x, y), data=txt)
|
||||
elif len(uris) == 2:
|
||||
from urllib.parse import unquote, urlparse
|
||||
self.picons_buffer = self.on_assign_picon(view, urlparse(unquote(uris[0])).path,
|
||||
urlparse(unquote(uris[1])).path + "/")
|
||||
urlparse(unquote(uris[1])).path + os.sep)
|
||||
drag_context.finish(True, False, time)
|
||||
|
||||
def on_bq_view_drag_data_received(self, view, drag_context, x, y, data, info, time):
|
||||
model_name, model = get_model_data(view)
|
||||
@@ -990,7 +1094,6 @@ class Application(Gtk.Application):
|
||||
return
|
||||
|
||||
if data.startswith("file://"):
|
||||
from urllib.parse import unquote, urlparse
|
||||
self.on_import_bouquet(None, file_path=urlparse(unquote(data)).path.strip())
|
||||
return
|
||||
|
||||
@@ -1011,7 +1114,8 @@ class Application(Gtk.Application):
|
||||
if not p_itr:
|
||||
break
|
||||
if p_itr and model.get_path(p_itr)[0] == p_path:
|
||||
top_iter = model.move_before(itr, top_iter)
|
||||
model.move_after(itr, top_iter)
|
||||
top_iter = itr
|
||||
else:
|
||||
model.insert(parent_itr, model.get_path(top_iter)[1], model[itr][:])
|
||||
to_del.append(itr)
|
||||
@@ -1081,10 +1185,22 @@ class Application(Gtk.Application):
|
||||
self.show_error_dialog(str(e))
|
||||
|
||||
def on_view_press(self, view, event):
|
||||
""" Handles a mouse click (press) to view. """
|
||||
if event.get_event_type() == Gdk.EventType.BUTTON_PRESS and event.button == Gdk.BUTTON_PRIMARY:
|
||||
target = view.get_path_at_pos(event.x, event.y)
|
||||
# Idea taken from here: https://kevinmehall.net/2010/pygtk_multi_select_drag_drop
|
||||
mask = not (event.state & (Gdk.ModifierType.CONTROL_MASK | Gdk.ModifierType.SHIFT_MASK))
|
||||
if target and mask and view.get_selection().path_is_selected(target[0]):
|
||||
self._select_enabled = False
|
||||
|
||||
name, model = get_model_data(view)
|
||||
self.delete_views_selection(name)
|
||||
|
||||
def on_view_release(self, view, event):
|
||||
""" Handles a mouse click (release) to view. """
|
||||
# Enable selection.
|
||||
self._select_enabled = True
|
||||
|
||||
def delete_views_selection(self, name):
|
||||
if name == self.SERVICE_MODEL_NAME:
|
||||
self.delete_selection(self._fav_view)
|
||||
@@ -1168,15 +1284,71 @@ class Application(Gtk.Application):
|
||||
self.show_error_dialog(str(e))
|
||||
|
||||
def on_data_open(self, action=None, value=None):
|
||||
response = show_dialog(DialogType.CHOOSER, self._main_window, settings=self._settings)
|
||||
""" Opening data via "File/Open". """
|
||||
response = show_dialog(DialogType.CHOOSER, self._main_window, settings=self._settings, title="Open folder")
|
||||
if response in (Gtk.ResponseType.CANCEL, Gtk.ResponseType.DELETE_EVENT):
|
||||
return
|
||||
self.open_data(response)
|
||||
|
||||
def on_archive_open(self, action=None, value=None):
|
||||
""" Opening the data archive via "File/Open archive". """
|
||||
file_filter = Gtk.FileFilter()
|
||||
file_filter.set_name("*.zip, *.gz")
|
||||
file_filter.add_mime_type("application/zip")
|
||||
file_filter.add_mime_type("application/gzip")
|
||||
|
||||
response = show_dialog(DialogType.CHOOSER, self._main_window,
|
||||
action_type=Gtk.FileChooserAction.OPEN,
|
||||
file_filter=file_filter,
|
||||
settings=self._settings,
|
||||
title="Open archive")
|
||||
if response in (Gtk.ResponseType.CANCEL, Gtk.ResponseType.DELETE_EVENT):
|
||||
return
|
||||
self.open_data(response)
|
||||
|
||||
def open_data(self, data_path=None, callback=None):
|
||||
""" Opening data and fill views. """
|
||||
gen = self.update_data(data_path, callback)
|
||||
GLib.idle_add(lambda: next(gen, False), priority=GLib.PRIORITY_LOW)
|
||||
if data_path and os.path.isfile(data_path):
|
||||
self.open_compressed_data(data_path)
|
||||
else:
|
||||
gen = self.update_data(data_path, callback)
|
||||
GLib.idle_add(lambda: next(gen, False), priority=GLib.PRIORITY_LOW)
|
||||
|
||||
def open_compressed_data(self, data_path):
|
||||
""" Opening archived data. """
|
||||
arch_path = self.get_archive_path(data_path)
|
||||
if arch_path:
|
||||
gen = self.update_data("{}{}".format(arch_path.name, os.sep), callback=arch_path.cleanup)
|
||||
GLib.idle_add(lambda: next(gen, False), priority=GLib.PRIORITY_LOW)
|
||||
|
||||
def get_archive_path(self, data_path):
|
||||
""" Returns the temp dir path for the extracted data, or None if the archive format is not supported. """
|
||||
import zipfile
|
||||
import tarfile
|
||||
import tempfile
|
||||
|
||||
tmp_path = tempfile.TemporaryDirectory()
|
||||
tmp_path_name = tmp_path.name
|
||||
|
||||
if zipfile.is_zipfile(data_path):
|
||||
with zipfile.ZipFile(data_path) as zip_file:
|
||||
for zip_info in zip_file.infolist():
|
||||
if not zip_info.filename.endswith(os.sep):
|
||||
zip_info.filename = os.path.basename(zip_info.filename)
|
||||
zip_file.extract(zip_info, path=tmp_path_name)
|
||||
elif tarfile.is_tarfile(data_path):
|
||||
with tarfile.open(data_path) as tar:
|
||||
for mb in tar.getmembers():
|
||||
if mb.isfile():
|
||||
mb.name = os.path.basename(mb.name)
|
||||
tar.extract(mb, path=tmp_path_name)
|
||||
else:
|
||||
tmp_path.cleanup()
|
||||
log("Error getting the path for the archive. Unsupported file format: {}".format(data_path))
|
||||
self.show_error_dialog("Unsupported format!")
|
||||
return
|
||||
|
||||
return tmp_path
|
||||
|
||||
def update_data(self, data_path, callback=None):
|
||||
self._profile_combo_box.set_sensitive(False)
|
||||
@@ -1492,7 +1664,7 @@ class Application(Gtk.Application):
|
||||
|
||||
if len(path) > 1:
|
||||
gen = self.update_bouquet_services(model, path)
|
||||
GLib.idle_add(lambda: next(gen, False), priority=GLib.PRIORITY_LOW)
|
||||
GLib.idle_add(lambda: next(gen, False))
|
||||
|
||||
def update_bouquet_services(self, model, path, bq_key=None):
|
||||
""" Updates list of bouquet services """
|
||||
@@ -1504,12 +1676,12 @@ class Application(Gtk.Application):
|
||||
services = self._bouquets.get(key, [])
|
||||
ex_services = self._extra_bouquets.get(key, None)
|
||||
|
||||
factor = self.FAV_FACTOR * 20
|
||||
if len(services) > factor or len(self._fav_model) > factor:
|
||||
if len(services) > self.FAV_FACTOR * 20:
|
||||
self._bouquets_view.set_sensitive(False)
|
||||
yield True
|
||||
|
||||
self._fav_view.set_model(None)
|
||||
self._fav_model.clear()
|
||||
yield True
|
||||
|
||||
num = 0
|
||||
for srv_id in services:
|
||||
@@ -1528,11 +1700,11 @@ class Application(Gtk.Application):
|
||||
self._fav_model.append((0 if is_marker else num, srv.coded, ex_srv_name if ex_srv_name else srv.service,
|
||||
srv.locked, srv.hide, srv_type, srv.pos, srv.fav_id,
|
||||
self._picons.get(srv.picon_id, None), None, background))
|
||||
if num % self.FAV_FACTOR == 0:
|
||||
yield True
|
||||
|
||||
GLib.idle_add(self._bouquets_view.set_sensitive, True)
|
||||
GLib.idle_add(self._bouquets_view.grab_focus)
|
||||
yield True
|
||||
self._fav_view.set_model(self._fav_model)
|
||||
self._bouquets_view.set_sensitive(True)
|
||||
self._bouquets_view.grab_focus()
|
||||
yield True
|
||||
|
||||
def check_bouquet_selection(self):
|
||||
@@ -1919,6 +2091,16 @@ class Application(Gtk.Application):
|
||||
else:
|
||||
show_dialog(DialogType.INFO, self._main_window, "Done!")
|
||||
|
||||
def on_import_data(self, path):
|
||||
msg = "Combine with the current data?"
|
||||
if len(self._services_model) > 0 and show_dialog(DialogType.QUESTION, self._main_window,
|
||||
msg) == Gtk.ResponseType.OK:
|
||||
self.import_data(path, force=True)
|
||||
else:
|
||||
if os.path.isdir(path) and not path.endswith(os.sep):
|
||||
path += os.sep
|
||||
self.open_data(path)
|
||||
|
||||
def on_import_bouquet(self, action, value=None, file_path=None):
|
||||
model, paths = self._bouquets_view.get_selection().get_selected_rows()
|
||||
if not paths:
|
||||
@@ -1933,17 +2115,34 @@ class Application(Gtk.Application):
|
||||
if response in (Gtk.ResponseType.CANCEL, Gtk.ResponseType.DELETE_EVENT):
|
||||
return
|
||||
|
||||
self.import_data(response)
|
||||
|
||||
def import_data(self, path, force=None, callback=None):
|
||||
if os.path.isdir(path) and not path.endswith(os.sep):
|
||||
path += os.sep
|
||||
elif os.path.isfile(path):
|
||||
arch_path = self.get_archive_path(path)
|
||||
if not arch_path:
|
||||
return
|
||||
|
||||
path = arch_path.name + os.sep
|
||||
callback = arch_path.cleanup
|
||||
|
||||
def append(b, s):
|
||||
gen = self.append_imported_data(b, s)
|
||||
gen = self.append_imported_data(b, s, callback)
|
||||
GLib.idle_add(lambda: next(gen, False))
|
||||
|
||||
ImportDialog(self._main_window, response, self._settings, self._services.keys(), append).show()
|
||||
dialog = ImportDialog(self._main_window, path, self._settings, self._services.keys(), append)
|
||||
dialog.import_data() if force else dialog.show()
|
||||
|
||||
def append_imported_data(self, bouquets, services):
|
||||
def append_imported_data(self, bouquets, services, callback=None):
|
||||
try:
|
||||
self._wait_dialog.show()
|
||||
yield from self.append_data(bouquets, services)
|
||||
finally:
|
||||
log("Importing data done!")
|
||||
if callback:
|
||||
callback()
|
||||
self._wait_dialog.hide()
|
||||
|
||||
# ***************** Backup ********************#
|
||||
@@ -1996,8 +2195,7 @@ class Application(Gtk.Application):
|
||||
self.set_playback_elms_active()
|
||||
else:
|
||||
if not self._player_box.get_visible():
|
||||
w, h = self._main_window.get_size()
|
||||
self._player_box.set_size_request(w * 0.6, -1)
|
||||
self.set_player_area_size(self._player_box)
|
||||
self._current_mrl = url
|
||||
self._player_box.set_visible(True)
|
||||
|
||||
@@ -2067,14 +2265,13 @@ class Application(Gtk.Application):
|
||||
self._fav_view.do_grab_focus(self._fav_view)
|
||||
|
||||
def get_time_str(self, duration):
|
||||
""" returns a string representation of time from duration in milliseconds """
|
||||
""" Returns a string representation of time from duration in milliseconds """
|
||||
m, s = divmod(duration // 1000, 60)
|
||||
h, m = divmod(m, 60)
|
||||
return "{}{:02d}:{:02d}".format(str(h) + ":" if h else "", m, s)
|
||||
|
||||
def on_drawing_area_realize(self, widget):
|
||||
w, h = self._main_window.get_size()
|
||||
widget.set_size_request(w * 0.6, -1)
|
||||
self.set_player_area_size(widget)
|
||||
|
||||
if not self._player:
|
||||
try:
|
||||
@@ -2094,6 +2291,10 @@ class Application(Gtk.Application):
|
||||
finally:
|
||||
self.set_playback_elms_active()
|
||||
|
||||
def set_player_area_size(self, widget):
|
||||
w, h = self._main_window.get_size()
|
||||
widget.set_size_request(w * 0.6, -1)
|
||||
|
||||
def on_player_drawing_area_draw(self, widget, cr):
|
||||
""" Used for black background drawing in the player drawing area.
|
||||
|
||||
@@ -2215,6 +2416,11 @@ class Application(Gtk.Application):
|
||||
|
||||
def on_watch(self, item=None):
|
||||
""" Switch to the channel and watch in the player """
|
||||
if self._app_info_box.get_visible() and self._settings.play_streams_mode is PlayStreamsMode.BUILT_IN:
|
||||
self.set_player_area_size(self._player_box)
|
||||
self._player_box.set_visible(True)
|
||||
GLib.idle_add(self._app_info_box.set_visible, False)
|
||||
|
||||
self._http_api.send(HttpRequestType.STREAM_CURRENT, None, self.watch)
|
||||
|
||||
def watch(self, data):
|
||||
@@ -2550,7 +2756,7 @@ class Application(Gtk.Application):
|
||||
dialog.show()
|
||||
|
||||
def on_bouquets_edit(self, view):
|
||||
""" Rename bouquets """
|
||||
""" Renaming bouquets. """
|
||||
if not self._bq_selected:
|
||||
self.show_error_dialog("This item is not allowed to edit!")
|
||||
return
|
||||
@@ -2570,10 +2776,15 @@ class Application(Gtk.Application):
|
||||
return
|
||||
|
||||
model.set_value(itr, 0, response)
|
||||
self._bouquets[bq] = self._bouquets.pop("{}:{}".format(bq_name, bq_type))
|
||||
old_bq_name = "{}:{}".format(bq_name, bq_type)
|
||||
self._bouquets[bq] = self._bouquets.pop(old_bq_name)
|
||||
self._current_bq_name = response
|
||||
self._bq_name_label.set_text(self._current_bq_name)
|
||||
self._bq_selected = "{}:{}".format(response, bq_type)
|
||||
self._bq_selected = bq
|
||||
# services with extra names for the bouquet
|
||||
ext_bq = self._extra_bouquets.get(old_bq_name, None)
|
||||
if ext_bq:
|
||||
self._extra_bouquets[bq] = ext_bq
|
||||
|
||||
def on_rename(self, view):
|
||||
name, model = get_model_data(view)
|
||||
|
||||
@@ -46,54 +46,57 @@ def move_items(key, view: Gtk.TreeView):
|
||||
""" Move items in the tree view """
|
||||
selection = view.get_selection()
|
||||
model, paths = selection.get_selected_rows()
|
||||
if not paths:
|
||||
return
|
||||
|
||||
if paths:
|
||||
mod_length = len(model)
|
||||
if mod_length == len(paths):
|
||||
return
|
||||
cursor_path = view.get_cursor()[0]
|
||||
max_path = Gtk.TreePath.new_from_indices((mod_length,))
|
||||
min_path = Gtk.TreePath.new_from_indices((0,))
|
||||
is_tree_store = type(model) is Gtk.TreeStore
|
||||
mod_length = len(model)
|
||||
if not is_tree_store and mod_length == len(paths):
|
||||
return
|
||||
|
||||
cursor_path = view.get_cursor()[0]
|
||||
max_path = Gtk.TreePath.new_from_indices((mod_length,))
|
||||
min_path = Gtk.TreePath.new_from_indices((0,))
|
||||
|
||||
if is_tree_store:
|
||||
is_tree_store = False
|
||||
parent_paths = list(filter(lambda p: p.get_depth() == 1, paths))
|
||||
if parent_paths:
|
||||
paths = parent_paths
|
||||
min_path = model.get_path(model.get_iter_first())
|
||||
view.collapse_all()
|
||||
if mod_length == len(paths):
|
||||
return
|
||||
else:
|
||||
if not is_some_level(paths):
|
||||
return
|
||||
parent_itr = model.iter_parent(model.get_iter(paths[0]))
|
||||
parent_index = model.get_path(parent_itr)
|
||||
children_num = model.iter_n_children(parent_itr)
|
||||
if key in (KeyboardKey.PAGE_DOWN, KeyboardKey.END, KeyboardKey.END_KP, KeyboardKey.PAGE_DOWN_KP):
|
||||
children_num -= 1
|
||||
min_path = Gtk.TreePath.new_from_string("{}:{}".format(parent_index, 0))
|
||||
max_path = Gtk.TreePath.new_from_string("{}:{}".format(parent_index, children_num))
|
||||
is_tree_store = True
|
||||
|
||||
if type(model) is Gtk.TreeStore:
|
||||
parent_paths = list(filter(lambda p: p.get_depth() == 1, paths))
|
||||
if parent_paths:
|
||||
paths = parent_paths
|
||||
min_path = model.get_path(model.get_iter_first())
|
||||
view.collapse_all()
|
||||
if mod_length == len(paths):
|
||||
return
|
||||
else:
|
||||
if not is_some_level(paths):
|
||||
return
|
||||
parent_itr = model.iter_parent(model.get_iter(paths[0]))
|
||||
parent_index = model.get_path(parent_itr)
|
||||
children_num = model.iter_n_children(parent_itr)
|
||||
if key in (KeyboardKey.PAGE_DOWN, KeyboardKey.END, KeyboardKey.END_KP, KeyboardKey.PAGE_DOWN_KP):
|
||||
children_num -= 1
|
||||
min_path = Gtk.TreePath.new_from_string("{}:{}".format(parent_index, 0))
|
||||
max_path = Gtk.TreePath.new_from_string("{}:{}".format(parent_index, children_num))
|
||||
is_tree_store = True
|
||||
|
||||
if key is KeyboardKey.UP:
|
||||
top_path = Gtk.TreePath(paths[0])
|
||||
set_cursor(top_path, paths, selection, view)
|
||||
top_path.prev()
|
||||
move_up(top_path, model, paths)
|
||||
elif key is KeyboardKey.DOWN:
|
||||
down_path = Gtk.TreePath(paths[-1])
|
||||
set_cursor(down_path, paths, selection, view)
|
||||
down_path.next()
|
||||
if down_path < max_path:
|
||||
move_down(down_path, model, paths)
|
||||
else:
|
||||
max_path.prev()
|
||||
move_down(max_path, model, paths)
|
||||
elif key in (KeyboardKey.PAGE_UP, KeyboardKey.HOME, KeyboardKey.PAGE_UP_KP, KeyboardKey.HOME_KP):
|
||||
move_up(min_path if is_tree_store else cursor_path, model, paths)
|
||||
elif key in (KeyboardKey.PAGE_DOWN, KeyboardKey.END, KeyboardKey.END_KP, KeyboardKey.PAGE_DOWN_KP):
|
||||
move_down(max_path if is_tree_store else cursor_path, model, paths)
|
||||
if key is KeyboardKey.UP:
|
||||
top_path = Gtk.TreePath(paths[0])
|
||||
set_cursor(top_path, paths, selection, view)
|
||||
top_path.prev()
|
||||
move_up(top_path, model, paths)
|
||||
elif key is KeyboardKey.DOWN:
|
||||
down_path = Gtk.TreePath(paths[-1])
|
||||
set_cursor(down_path, paths, selection, view)
|
||||
down_path.next()
|
||||
if down_path < max_path:
|
||||
move_down(down_path, model, paths)
|
||||
else:
|
||||
max_path.prev()
|
||||
move_down(max_path, model, paths)
|
||||
elif key in (KeyboardKey.PAGE_UP, KeyboardKey.HOME, KeyboardKey.PAGE_UP_KP, KeyboardKey.HOME_KP):
|
||||
move_up(min_path if is_tree_store else cursor_path, model, paths)
|
||||
elif key in (KeyboardKey.PAGE_DOWN, KeyboardKey.END, KeyboardKey.END_KP, KeyboardKey.PAGE_DOWN_KP):
|
||||
move_down(max_path if is_tree_store else cursor_path, model, paths)
|
||||
|
||||
|
||||
def move_up(top_path, model, paths):
|
||||
@@ -161,7 +164,8 @@ def rename(view, parent_window, target, fav_view=None, service_view=None, servic
|
||||
return
|
||||
|
||||
srv_name = response
|
||||
model.set_value(itr, Column.FAV_SERVICE, response)
|
||||
if not model.get_value(itr, Column.FAV_BACKGROUND):
|
||||
model.set_value(itr, Column.FAV_SERVICE, response)
|
||||
|
||||
if service_view is not None:
|
||||
for row in get_base_model(service_view.get_model()):
|
||||
@@ -401,15 +405,19 @@ def assign_picons(target, srv_view, fav_view, transient, picons, settings, servi
|
||||
picons_path = dst_path or settings.picons_local_path
|
||||
os.makedirs(os.path.dirname(picons_path), exist_ok=True)
|
||||
picon_file = picons_path + picon_id
|
||||
shutil.copy(src_path, picon_file)
|
||||
picons_files.append(picon_file)
|
||||
picon = get_picon_pixbuf(picon_file)
|
||||
picons[picon_id] = picon
|
||||
model.set_value(itr, p_pos, picon)
|
||||
if target is ViewTarget.SERVICES:
|
||||
set_picon(fav_id, fav_view.get_model(), picon, Column.FAV_ID, p_pos)
|
||||
try:
|
||||
shutil.copy(src_path, picon_file)
|
||||
except shutil.SameFileError:
|
||||
pass # NOP
|
||||
else:
|
||||
set_picon(fav_id, get_base_model(srv_view.get_model()), picon, Column.SRV_FAV_ID, p_pos)
|
||||
picons_files.append(picon_file)
|
||||
picon = get_picon_pixbuf(picon_file)
|
||||
picons[picon_id] = picon
|
||||
model.set_value(itr, p_pos, picon)
|
||||
if target is ViewTarget.SERVICES:
|
||||
set_picon(fav_id, fav_view.get_model(), picon, Column.FAV_ID, p_pos)
|
||||
else:
|
||||
set_picon(fav_id, get_base_model(srv_view.get_model()), picon, Column.SRV_FAV_ID, p_pos)
|
||||
|
||||
return picons_files
|
||||
|
||||
|
||||
@@ -416,7 +416,7 @@ Author: Dmitriy Yefremov
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">2</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
@@ -430,7 +430,7 @@ Author: Dmitriy Yefremov
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">3</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
@@ -441,7 +441,7 @@ Author: Dmitriy Yefremov
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">4</property>
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
@@ -455,7 +455,21 @@ Author: Dmitriy Yefremov
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">5</property>
|
||||
<property name="position">3</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkModelButton" id="open_archive_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_archive_open</property>
|
||||
<property name="text" translatable="yes">Extract...</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">4</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
@@ -1880,10 +1894,12 @@ Author: Dmitriy Yefremov
|
||||
<property name="activate_on_single_click">True</property>
|
||||
<signal name="button-press-event" handler="on_view_popup_menu" object="services_popup_menu" swapped="no"/>
|
||||
<signal name="button-press-event" handler="on_view_press" swapped="no"/>
|
||||
<signal name="drag-begin" handler="on_view_drag_begin" swapped="no"/>
|
||||
<signal name="button-release-event" handler="on_view_release" swapped="no"/>
|
||||
<signal name="drag-begin" handler="on_view_drag_begin" after="yes" swapped="no"/>
|
||||
<signal name="drag-data-get" handler="on_view_drag_data_get" swapped="no"/>
|
||||
<signal name="drag-data-received" handler="on_services_view_drag_data_received" swapped="no"/>
|
||||
<signal name="drag-drop" handler="on_services_view_drag_drop" swapped="no"/>
|
||||
<signal name="drag-end" handler="on_view_drag_end" swapped="no"/>
|
||||
<signal name="focus-in-event" handler="on_view_focus" swapped="no"/>
|
||||
<signal name="key-press-event" handler="on_tree_view_key_press" swapped="no"/>
|
||||
<signal name="key-release-event" handler="on_tree_view_key_release" swapped="no"/>
|
||||
@@ -2425,9 +2441,11 @@ Author: Dmitriy Yefremov
|
||||
<property name="activate_on_single_click">True</property>
|
||||
<signal name="button-press-event" handler="on_fav_press" object="fav_popup_menu" swapped="no"/>
|
||||
<signal name="button-press-event" handler="on_view_press" swapped="yes"/>
|
||||
<signal name="drag-begin" handler="on_view_drag_begin" swapped="no"/>
|
||||
<signal name="button-release-event" handler="on_view_release" swapped="no"/>
|
||||
<signal name="drag-begin" handler="on_view_drag_begin" after="yes" swapped="no"/>
|
||||
<signal name="drag-data-get" handler="on_view_drag_data_get" swapped="no"/>
|
||||
<signal name="drag-data-received" handler="on_view_drag_data_received" swapped="no"/>
|
||||
<signal name="drag-end" handler="on_view_drag_end" swapped="no"/>
|
||||
<signal name="focus-in-event" handler="on_view_focus" swapped="no"/>
|
||||
<signal name="key-press-event" handler="on_tree_view_key_press" swapped="no"/>
|
||||
<signal name="key-release-event" handler="on_tree_view_key_release" swapped="no"/>
|
||||
@@ -2710,9 +2728,11 @@ Author: Dmitriy Yefremov
|
||||
<property name="activate_on_single_click">True</property>
|
||||
<signal name="button-press-event" handler="on_view_popup_menu" object="bouquets_popup_menu" swapped="no"/>
|
||||
<signal name="button-press-event" handler="on_view_press" swapped="yes"/>
|
||||
<signal name="drag-begin" handler="on_view_drag_begin" swapped="no"/>
|
||||
<signal name="button-release-event" handler="on_view_release" swapped="no"/>
|
||||
<signal name="drag-begin" handler="on_view_drag_begin" after="yes" swapped="no"/>
|
||||
<signal name="drag-data-get" handler="on_view_drag_data_get" swapped="no"/>
|
||||
<signal name="drag-data-received" handler="on_bq_view_drag_data_received" swapped="no"/>
|
||||
<signal name="drag-end" handler="on_view_drag_end" swapped="no"/>
|
||||
<signal name="focus-in-event" handler="on_view_focus" swapped="no"/>
|
||||
<signal name="key-press-event" handler="on_tree_view_key_press" swapped="no"/>
|
||||
<signal name="key-release-event" handler="on_tree_view_key_release" swapped="no"/>
|
||||
@@ -2867,6 +2887,7 @@ Author: Dmitriy Yefremov
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<signal name="drag-data-received" handler="on_view_drag_data_received" object="services_tree_view" swapped="no"/>
|
||||
<signal name="key-press-event" handler="on_tree_view_key_press" object="services_tree_view" swapped="no"/>
|
||||
<signal name="key-release-event" handler="on_tree_view_key_release" object="services_tree_view" swapped="no"/>
|
||||
<child>
|
||||
@@ -2902,7 +2923,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" translatable="yes">1.0.0 Alpha</property>
|
||||
<property name="label" translatable="yes">1.0.1 Beta</property>
|
||||
<attributes>
|
||||
<attribute name="weight" value="bold"/>
|
||||
</attributes>
|
||||
|
||||
@@ -228,7 +228,6 @@ Author: Dmitriy Yefremov
|
||||
<object class="GtkHeaderBar" id="header">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="title" translatable="yes">Picons manager</property>
|
||||
<property name="spacing">2</property>
|
||||
<property name="show_close_button">True</property>
|
||||
<child>
|
||||
@@ -299,16 +298,6 @@ Author: Dmitriy Yefremov
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkSeparator" id="download_box_separator">
|
||||
<property name="can_focus">False</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="send_button">
|
||||
<property name="visible">True</property>
|
||||
@@ -381,6 +370,13 @@ Author: Dmitriy Yefremov
|
||||
<property name="position">6</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child type="title">
|
||||
<object class="GtkStackSwitcher" id="sctack">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="stack">stack</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="convert_button">
|
||||
<property name="can_focus">False</property>
|
||||
@@ -456,7 +452,6 @@ Author: Dmitriy Yefremov
|
||||
<object class="GtkFrame" id="stack_frame">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_top">5</property>
|
||||
<property name="label_xalign">0.5</property>
|
||||
<property name="shadow_type">none</property>
|
||||
<child>
|
||||
@@ -465,7 +460,7 @@ Author: Dmitriy Yefremov
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_left">5</property>
|
||||
<property name="margin_right">5</property>
|
||||
<property name="margin_top">10</property>
|
||||
<property name="margin_top">5</property>
|
||||
<property name="margin_bottom">5</property>
|
||||
<signal name="notify::visible-child" handler="on_visible_page" swapped="no"/>
|
||||
<child>
|
||||
@@ -1681,12 +1676,8 @@ Author: Dmitriy Yefremov
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child type="label">
|
||||
<object class="GtkStackSwitcher" id="sctack">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="stack">stack</property>
|
||||
</object>
|
||||
<child type="label_item">
|
||||
<placeholder/>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
|
||||
@@ -14,8 +14,8 @@ from app.settings import SettingsType, Settings
|
||||
from app.tools.picons import PiconsParser, parse_providers, Provider, convert_to
|
||||
from app.tools.satellites import SatellitesParser, SatelliteSource
|
||||
from .dialogs import show_dialog, DialogType, get_message
|
||||
from .main_helper import update_entry_data, append_text_to_tview, scroll_to, on_popup_menu, get_base_model, set_picon, \
|
||||
get_picon_pixbuf
|
||||
from .main_helper import (update_entry_data, append_text_to_tview, scroll_to, on_popup_menu, get_base_model, set_picon,
|
||||
get_picon_pixbuf)
|
||||
from .uicommons import Gtk, Gdk, UI_RESOURCES_PATH, TV_ICON, Column, KeyboardKey
|
||||
|
||||
|
||||
@@ -130,13 +130,15 @@ class PiconsDialog:
|
||||
self._cancel_button.bind_property("visible", self._header_download_box, "visible", 4)
|
||||
self._convert_button.bind_property("visible", self._header_download_box, "visible", 4)
|
||||
self._load_providers_button.bind_property("visible", self._receive_button, "visible")
|
||||
self._load_providers_button.bind_property("visible", builder.get_object("download_box_separator"), "visible")
|
||||
self._filter_bar.bind_property("search-mode-enabled", self._filter_bar, "visible")
|
||||
self._explorer_src_path_button.bind_property("sensitive", builder.get_object("picons_view_sw"), "sensitive")
|
||||
self._filter_button.bind_property("active", builder.get_object("filter_service_box"), "visible")
|
||||
self._filter_button.bind_property("active", builder.get_object("src_title_grid"), "visible")
|
||||
self._filter_button.bind_property("active", builder.get_object("dst_title_grid"), "visible")
|
||||
self._filter_button.bind_property("visible", self._info_check_button, "visible")
|
||||
self._filter_button.bind_property("visible", self._send_button, "visible")
|
||||
self._filter_button.bind_property("visible", self._download_button, "visible")
|
||||
self._filter_button.bind_property("visible", self._remove_button, "visible")
|
||||
explorer_info_bar = builder.get_object("explorer_info_bar")
|
||||
explorer_info_bar.bind_property("visible", builder.get_object("explorer_info_bar_frame"), "visible")
|
||||
self._info_check_button.bind_property("active", explorer_info_bar, "visible")
|
||||
@@ -561,10 +563,10 @@ class PiconsDialog:
|
||||
return
|
||||
self.process_provider(Provider(*prv))
|
||||
|
||||
if self._resize_no_radio_button.get_active():
|
||||
if not self._resize_no_radio_button.get_active():
|
||||
self.resize(self._picons_dir_entry.get_text())
|
||||
|
||||
self.show_info_message(get_message("Done!"), Gtk.MessageType.INFO)
|
||||
else:
|
||||
self.show_info_message(get_message("Done!"), Gtk.MessageType.INFO)
|
||||
finally:
|
||||
GLib.idle_add(self._cancel_button.hide)
|
||||
self._terminate = False
|
||||
@@ -593,15 +595,24 @@ class PiconsDialog:
|
||||
def append_output(self, char):
|
||||
append_text_to_tview(char, self._text_view)
|
||||
|
||||
@run_task
|
||||
def resize(self, path):
|
||||
self.show_info_message(get_message("Resizing..."), Gtk.MessageType.INFO)
|
||||
command = "mogrify -resize {}! *.png".format(
|
||||
"220x132" if self._resize_220_132_radio_button.get_active() else "100x60").split()
|
||||
|
||||
try:
|
||||
self._current_process = subprocess.Popen(command, universal_newlines=True, cwd=path)
|
||||
self._current_process.wait()
|
||||
except FileNotFoundError as e:
|
||||
self.show_info_message("Conversion error. " + str(e), Gtk.MessageType.ERROR)
|
||||
from pathlib import Path
|
||||
from PIL import Image
|
||||
except ImportError as e:
|
||||
self.show_info_message("{} {}".format(get_message("Conversion error."), e), Gtk.MessageType.ERROR)
|
||||
else:
|
||||
res = (220, 132) if self._resize_220_132_radio_button.get_active() else (100, 60)
|
||||
|
||||
for img_file in Path(path).glob("*.png"):
|
||||
img = Image.open(img_file)
|
||||
img = img.resize(res, Image.ANTIALIAS)
|
||||
img.save(img_file, "PNG", optimize=True)
|
||||
|
||||
self.show_info_message(get_message("Done!"), Gtk.MessageType.INFO)
|
||||
|
||||
def on_cancel(self, item=None):
|
||||
if self.is_task_running() and show_dialog(DialogType.QUESTION, self._dialog) == Gtk.ResponseType.CANCEL:
|
||||
|
||||
@@ -728,6 +728,7 @@ Author: Dmitriy Yefremov
|
||||
<property name="use-header-bar">{use_header}</property>
|
||||
<property name="title" translatable="yes">Satellite</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="resizable">False</property>
|
||||
<property name="modal">True</property>
|
||||
<property name="window_position">center-on-parent</property>
|
||||
<property name="destroy_with_parent">True</property>
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
import re
|
||||
import os
|
||||
import re
|
||||
|
||||
from app.commons import run_idle
|
||||
from app.eparser import Service
|
||||
from app.eparser.ecommons import MODULATION, Inversion, ROLL_OFF, Pilot, Flag, Pids, POLARIZATION, \
|
||||
get_key_by_value, get_value_by_name, FEC_DEFAULT, PLS_MODE, SERVICE_TYPE, T_MODULATION, C_MODULATION, TrType, \
|
||||
SystemCable, T_SYSTEM, BANDWIDTH, TRANSMISSION_MODE, GUARD_INTERVAL, HIERARCHY, T_FEC
|
||||
from app.eparser.ecommons import (MODULATION, Inversion, ROLL_OFF, Pilot, Flag, Pids, POLARIZATION, get_key_by_value,
|
||||
get_value_by_name, FEC_DEFAULT, PLS_MODE, SERVICE_TYPE, T_MODULATION, C_MODULATION,
|
||||
TrType, SystemCable, T_SYSTEM, BANDWIDTH, TRANSMISSION_MODE, GUARD_INTERVAL, T_FEC,
|
||||
HIERARCHY)
|
||||
from app.settings import SettingsType
|
||||
from .uicommons import Gtk, Gdk, UI_RESOURCES_PATH, HIDE_ICON, TEXT_DOMAIN, CODED_ICON, Column, IS_GNOME_SESSION
|
||||
from .dialogs import show_dialog, DialogType, Action, get_dialogs_string
|
||||
from .main_helper import get_base_model
|
||||
from .uicommons import Gtk, Gdk, UI_RESOURCES_PATH, HIDE_ICON, TEXT_DOMAIN, CODED_ICON, Column, IS_GNOME_SESSION
|
||||
|
||||
_UI_PATH = UI_RESOURCES_PATH + "service_details_dialog.glade"
|
||||
|
||||
@@ -438,14 +439,17 @@ class ServiceDetailsDialog:
|
||||
def update_fav_view(self, old_service, new_service):
|
||||
model = self._fav_view.get_model()
|
||||
for row in filter(lambda r: old_service.fav_id == r[7], model):
|
||||
model.set(row.iter, {1: new_service.coded,
|
||||
2: new_service.service,
|
||||
3: new_service.locked,
|
||||
4: new_service.hide,
|
||||
5: new_service.service_type,
|
||||
6: new_service.pos,
|
||||
7: new_service.fav_id,
|
||||
8: new_service.picon})
|
||||
itr = row.iter
|
||||
if not model.get_value(itr, Column.FAV_BACKGROUND):
|
||||
model.set_value(itr, Column.FAV_SERVICE, new_service.service)
|
||||
|
||||
model.set(itr, {Column.FAV_CODED: new_service.coded,
|
||||
Column.FAV_LOCKED: new_service.locked,
|
||||
Column.FAV_HIDE: new_service.hide,
|
||||
Column.FAV_TYPE: new_service.service_type,
|
||||
Column.FAV_POS: new_service.pos,
|
||||
Column.FAV_ID: new_service.fav_id,
|
||||
Column.FAV_PICON: new_service.picon})
|
||||
|
||||
def update_picon_name(self, old_name, new_name):
|
||||
if not os.path.isdir(self._picons_dir_path):
|
||||
|
||||
@@ -981,6 +981,8 @@ Author: Dmitriy Yefremov
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="hexpand">True</property>
|
||||
<property name="visibility">False</property>
|
||||
<property name="invisible_char">●</property>
|
||||
<property name="width_chars">12</property>
|
||||
<property name="max_width_chars">15</property>
|
||||
<property name="primary_icon_name">emblem-readonly</property>
|
||||
@@ -2066,6 +2068,7 @@ Author: Dmitriy Yefremov
|
||||
<item id="pl_PL" translatable="yes">Polski</item>
|
||||
<item id="pt_PT" translatable="yes">Português</item>
|
||||
<item id="tr_TR" translatable="yes">Türkçe</item>
|
||||
<item id="be_BY" translatable="yes">Беларуская</item>
|
||||
<item id="ru_RU" translatable="yes">Русский</item>
|
||||
</items>
|
||||
<signal name="changed" handler="on_lang_changed" swapped="no"/>
|
||||
@@ -2230,7 +2233,6 @@ Author: Dmitriy Yefremov
|
||||
<child>
|
||||
<object class="GtkFrame" id="program_frame">
|
||||
<property name="visible">True</property>
|
||||
<property name="sensitive">False</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_left">5</property>
|
||||
<property name="margin_right">5</property>
|
||||
@@ -2462,6 +2464,57 @@ Author: Dmitriy Yefremov
|
||||
<property name="margin_bottom">5</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<property name="spacing">5</property>
|
||||
<child>
|
||||
<object class="GtkFrame" id="dark_mode_frame">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label_xalign">0</property>
|
||||
<property name="shadow_type">in</property>
|
||||
<child>
|
||||
<object class="GtkBox" id="dark_mode_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>
|
||||
<child>
|
||||
<object class="GtkLabel" id="dark_mode_label">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">Enable Dark Mode</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="dark_mode_switch">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<signal name="state-set" handler="on_icon_theme_changed" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="pack_type">end</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child type="label_item">
|
||||
<placeholder/>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkFrame" id="hemes_support_frame">
|
||||
<property name="visible">True</property>
|
||||
@@ -2469,7 +2522,7 @@ Author: Dmitriy Yefremov
|
||||
<property name="label_xalign">0</property>
|
||||
<property name="shadow_type">in</property>
|
||||
<child>
|
||||
<object class="GtkBox">
|
||||
<object class="GtkBox" id="themes_support_box">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_left">5</property>
|
||||
@@ -2488,6 +2541,18 @@ Author: Dmitriy Yefremov
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child type="center">
|
||||
<object class="GtkLabel">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">EXPERIMENTAL!</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkSwitch" id="themes_support_switch">
|
||||
<property name="visible">True</property>
|
||||
@@ -2510,7 +2575,7 @@ Author: Dmitriy Yefremov
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
@@ -2657,7 +2722,47 @@ Author: Dmitriy Yefremov
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkBox" id="themes_source_box">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="halign">center</property>
|
||||
<child>
|
||||
<object class="GtkLabel" id="themes_source_label">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">Gtk3 Themes and Icons:</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLinkButton" id="themes_source_link_button">
|
||||
<property name="label" translatable="yes">www.gnome-look.org</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="relief">none</property>
|
||||
<property name="uri">https://www.gnome-look.org/</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="pack_type">end</property>
|
||||
<property name="position">3</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
@@ -2753,47 +2858,7 @@ Author: Dmitriy Yefremov
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkBox" id="themes_source_box">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="halign">center</property>
|
||||
<child>
|
||||
<object class="GtkLabel" id="themes_source_label">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">Gtk3 Themes and Icons:</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLinkButton" id="themes_source_link_button">
|
||||
<property name="label" translatable="yes">www.gnome-look.org</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="relief">none</property>
|
||||
<property name="uri">https://www.gnome-look.org/</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="pack_type">end</property>
|
||||
<property name="position">3</property>
|
||||
<property name="position">4</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
@@ -2807,72 +2872,17 @@ Author: Dmitriy Yefremov
|
||||
<object class="GtkBox" id="extra_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="orientation">vertical</property>
|
||||
<child>
|
||||
<object class="GtkFrame" id="v5_frame">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_left">5</property>
|
||||
<property name="margin_right">5</property>
|
||||
<property name="margin_top">5</property>
|
||||
<property name="margin_bottom">5</property>
|
||||
<property name="label_xalign">0.019999999552965164</property>
|
||||
<property name="shadow_type">in</property>
|
||||
<child>
|
||||
<object class="GtkGrid" id="v5_support_grid">
|
||||
<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="row_spacing">5</property>
|
||||
<property name="column_spacing">5</property>
|
||||
<child>
|
||||
<object class="GtkLabel">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="halign">start</property>
|
||||
<property name="hexpand">True</property>
|
||||
<property name="label" translatable="yes">Enable ver. 5 support (experimental)</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkSwitch" id="support_ver5_switch">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="halign">end</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child type="label_item">
|
||||
<placeholder/>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<property name="spacing">10</property>
|
||||
<child>
|
||||
<object class="GtkFrame" id="bq_naming_frame">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="tooltip_text" translatable="yes">Allows you to name bouquet files using their names.</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="label_xalign">0.019999999552965164</property>
|
||||
<property name="shadow_type">in</property>
|
||||
<child>
|
||||
@@ -2919,181 +2929,35 @@ Author: Dmitriy Yefremov
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkFrame" id="yt_dl_frame">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_left">5</property>
|
||||
<property name="margin_right">5</property>
|
||||
<property name="margin_top">5</property>
|
||||
<property name="margin_bottom">5</property>
|
||||
<property name="label_xalign">0.019999999552965164</property>
|
||||
<property name="shadow_type">in</property>
|
||||
<child>
|
||||
<object class="GtkBox" id="yt_dl_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="orientation">vertical</property>
|
||||
<property name="spacing">5</property>
|
||||
<child>
|
||||
<object class="GtkBox" id="enable_yt_dl_box">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="tooltip_text" translatable="yes">Enables parsing links using youtube-dl to get direct media links.</property>
|
||||
<child>
|
||||
<object class="GtkLabel" id="enable_yt_dl_label">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="halign">start</property>
|
||||
<property name="label" translatable="yes">Enable support for</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLinkButton" id="yt_dl_link_button">
|
||||
<property name="label">youtube-dl</property>
|
||||
<property name="name">status-bar-button</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="halign">start</property>
|
||||
<property name="valign">center</property>
|
||||
<property name="relief">none</property>
|
||||
<property name="uri">https://youtube-dl.org/</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">3</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child type="center">
|
||||
<object class="GtkLabel">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="halign">center</property>
|
||||
<property name="label" translatable="yes">EXPERIMENTAL!</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">3</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkSwitch" id="enable_yt_dl_switch">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="tooltip_text" translatable="yes">Enables parsing links using youtube-dl to get direct links to media</property>
|
||||
<property name="halign">end</property>
|
||||
<property name="valign">center</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="pack_type">end</property>
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkBox" id="yt_dl_update_box">
|
||||
<property name="visible">True</property>
|
||||
<property name="sensitive">False</property>
|
||||
<property name="can_focus">False</property>
|
||||
<child>
|
||||
<object class="GtkLabel" id="auto_update_yt_dl_label">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="halign">start</property>
|
||||
<property name="label" translatable="yes">Auto-check for updates</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkSwitch" id="enable_update_yt_dl_switch">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="halign">end</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>
|
||||
</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">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">2</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkFrame" id="http_frame">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_left">5</property>
|
||||
<property name="margin_right">5</property>
|
||||
<property name="margin_top">5</property>
|
||||
<property name="margin_bottom">5</property>
|
||||
<property name="label_xalign">0.019999999552965164</property>
|
||||
<property name="shadow_type">in</property>
|
||||
<child>
|
||||
<object class="GtkGrid" id="extra_support_grid">
|
||||
<object class="GtkBox" id="enable_http_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="row_spacing">5</property>
|
||||
<property name="column_spacing">5</property>
|
||||
<child>
|
||||
<object class="GtkLabel">
|
||||
<object class="GtkLabel" id="enable_http_label">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="halign">start</property>
|
||||
<property name="hexpand">True</property>
|
||||
<property name="label" translatable="yes">Enable HTTP API (experimental)</property>
|
||||
<property name="label" translatable="yes">Enable HTTP API</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">0</property>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
@@ -3104,35 +2968,9 @@ Author: Dmitriy Yefremov
|
||||
<signal name="state-set" handler="on_http_mode_switch" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="enable_send_to_label">
|
||||
<property name="visible">True</property>
|
||||
<property name="sensitive">False</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="halign">start</property>
|
||||
<property name="hexpand">True</property>
|
||||
<property name="label" translatable="yes">Enable direct playback bar (experimental)</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkSwitch" id="enable_send_to_switch">
|
||||
<property name="visible">True</property>
|
||||
<property name="sensitive">False</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="tooltip_text" translatable="yes">Enables direct sending and playback of media links on the receiver</property>
|
||||
<property name="halign">end</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">1</property>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
@@ -3144,17 +2982,13 @@ Author: Dmitriy Yefremov
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">3</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkFrame" id="double_click_frame">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_left">5</property>
|
||||
<property name="margin_right">5</property>
|
||||
<property name="margin_top">5</property>
|
||||
<property name="margin_bottom">5</property>
|
||||
<property name="label_xalign">0.019999999552965164</property>
|
||||
<property name="shadow_type">in</property>
|
||||
<child>
|
||||
@@ -3263,12 +3097,328 @@ Author: Dmitriy Yefremov
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkFrame" id="enable_experimental_frame">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label_xalign">0.019999999552965164</property>
|
||||
<property name="shadow_type">in</property>
|
||||
<child>
|
||||
<object class="GtkBox" id="enable_experimental_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="spacing">5</property>
|
||||
<child>
|
||||
<object class="GtkLabel" id="enable_experimental_label">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="halign">start</property>
|
||||
<property name="label" translatable="yes">Enable experimental features</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkSwitch" id="enable_experimental_switch">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="halign">end</property>
|
||||
<property name="valign">center</property>
|
||||
<signal name="state-set" handler="on_experimental_switch" swapped="no"/>
|
||||
</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>
|
||||
</child>
|
||||
<child type="label_item">
|
||||
<placeholder/>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">3</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_top">15</property>
|
||||
<property name="label" translatable="yes">EXPERIMENTAL!</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">4</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkBox" id="experimental_box">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<property name="spacing">10</property>
|
||||
<child>
|
||||
<object class="GtkFrame" id="v5_frame">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label_xalign">0.019999999552965164</property>
|
||||
<property name="shadow_type">in</property>
|
||||
<child>
|
||||
<object class="GtkBox" id="v5_support_box">
|
||||
<property name="visible">True</property>
|
||||
<property name="sensitive">False</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>
|
||||
<child>
|
||||
<object class="GtkLabel">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="halign">start</property>
|
||||
<property name="hexpand">True</property>
|
||||
<property name="label" translatable="yes">Enable lamedb ver. 5 support</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkSwitch" id="support_ver5_switch">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="halign">end</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="pack_type">end</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child type="label_item">
|
||||
<placeholder/>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkFrame" id="yt_dl_frame">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label_xalign">0.019999999552965164</property>
|
||||
<property name="shadow_type">in</property>
|
||||
<child>
|
||||
<object class="GtkBox" id="yt_dl_box">
|
||||
<property name="visible">True</property>
|
||||
<property name="sensitive">False</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="orientation">vertical</property>
|
||||
<property name="spacing">5</property>
|
||||
<child>
|
||||
<object class="GtkBox" id="enable_yt_dl_box">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="tooltip_text" translatable="yes">Enables parsing links using youtube-dl to get direct media links.</property>
|
||||
<child>
|
||||
<object class="GtkLabel" id="enable_yt_dl_label">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="halign">start</property>
|
||||
<property name="label" translatable="yes">Enable support for</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkSwitch" id="enable_yt_dl_switch">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="tooltip_text" translatable="yes">Enables parsing links using youtube-dl to get direct links to media</property>
|
||||
<property name="halign">end</property>
|
||||
<property name="valign">center</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="pack_type">end</property>
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLinkButton" id="yt_dl_link_button">
|
||||
<property name="label">youtube-dl</property>
|
||||
<property name="name">status-bar-button</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="halign">start</property>
|
||||
<property name="valign">center</property>
|
||||
<property name="relief">none</property>
|
||||
<property name="uri">https://youtube-dl.org/</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">3</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkBox" id="yt_dl_update_box">
|
||||
<property name="visible">True</property>
|
||||
<property name="sensitive">False</property>
|
||||
<property name="can_focus">False</property>
|
||||
<child>
|
||||
<object class="GtkLabel" id="auto_update_yt_dl_label">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="halign">start</property>
|
||||
<property name="label" translatable="yes">Auto-check for updates</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkSwitch" id="enable_update_yt_dl_switch">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="halign">end</property>
|
||||
<property name="valign">center</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="pack_type">end</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child type="label_item">
|
||||
<placeholder/>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkFrame" id="direct_playback_frame">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label_xalign">0</property>
|
||||
<property name="shadow_type">in</property>
|
||||
<child>
|
||||
<object class="GtkBox" id="enable_direct_playback_box">
|
||||
<property name="visible">True</property>
|
||||
<property name="sensitive">False</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>
|
||||
<child>
|
||||
<object class="GtkLabel" id="enable_send_to_label">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="halign">start</property>
|
||||
<property name="hexpand">True</property>
|
||||
<property name="label" translatable="yes">Enable direct playback bar</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkSwitch" id="enable_send_to_switch">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="tooltip_text" translatable="yes">Enables direct sending and playback of media links on the receiver</property>
|
||||
<property name="halign">end</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="pack_type">end</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child type="label_item">
|
||||
<placeholder/>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">8</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="name">extra</property>
|
||||
|
||||
@@ -36,6 +36,7 @@ class SettingsDialog:
|
||||
"on_set_color_switch": self.on_set_color_switch,
|
||||
"on_force_bq_name": self.on_force_bq_name,
|
||||
"on_http_mode_switch": self.on_http_mode_switch,
|
||||
"on_experimental_switch": self.on_experimental_switch,
|
||||
"on_yt_dl_switch": self.on_yt_dl_switch,
|
||||
"on_default_path_mode_switch": self.on_default_path_mode_switch,
|
||||
"on_default_data_path_changed": self.on_default_data_path_changed,
|
||||
@@ -136,8 +137,7 @@ class SettingsDialog:
|
||||
# Program
|
||||
self._before_save_switch = builder.get_object("before_save_switch")
|
||||
self._before_downloading_switch = builder.get_object("before_downloading_switch")
|
||||
self._program_frame = builder.get_object("program_frame")
|
||||
self._extra_support_grid = builder.get_object("extra_support_grid")
|
||||
self._enable_experimental_box = builder.get_object("enable_experimental_box")
|
||||
self._colors_grid = builder.get_object("colors_grid")
|
||||
self._set_color_switch = builder.get_object("set_color_switch")
|
||||
self._new_color_button = builder.get_object("new_color_button")
|
||||
@@ -158,10 +158,18 @@ class SettingsDialog:
|
||||
self._click_mode_zap_and_play_button = builder.get_object("click_mode_zap_and_play_button")
|
||||
self._click_mode_zap_button.bind_property("sensitive", self._click_mode_play_button, "sensitive")
|
||||
self._click_mode_zap_button.bind_property("sensitive", self._click_mode_zap_and_play_button, "sensitive")
|
||||
self._click_mode_zap_button.bind_property("sensitive", self._enable_send_to_switch, "sensitive")
|
||||
self._enable_send_to_switch.bind_property("sensitive", builder.get_object("enable_send_to_label"), "sensitive")
|
||||
self._extra_support_grid.bind_property("sensitive", builder.get_object("v5_support_grid"), "sensitive")
|
||||
# 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("enable_http_box"), "sensitive")
|
||||
self._enigma_radio_button.bind_property("active", builder.get_object("enable_experimental_box"), "sensitive")
|
||||
self._enigma_radio_button.bind_property("active", builder.get_object("program_frame"), "sensitive")
|
||||
self._enigma_radio_button.bind_property("active", builder.get_object("experimental_box"), "sensitive")
|
||||
# Profiles
|
||||
self._profile_view = builder.get_object("profile_tree_view")
|
||||
self._profile_add_button = builder.get_object("profile_add_button")
|
||||
@@ -187,6 +195,7 @@ class SettingsDialog:
|
||||
self._theme_thumbnail_image = builder.get_object("theme_thumbnail_image")
|
||||
self._theme_combo_box = builder.get_object("theme_combo_box")
|
||||
self._icon_theme_combo_box = builder.get_object("icon_theme_combo_box")
|
||||
self._dark_mode_switch = builder.get_object("dark_mode_switch")
|
||||
self._themes_support_switch = builder.get_object("themes_support_switch")
|
||||
self._themes_support_switch.bind_property("active", builder.get_object("gtk_theme_frame"), "sensitive")
|
||||
self._themes_support_switch.bind_property("active", builder.get_object("icon_theme_frame"), "sensitive")
|
||||
@@ -198,8 +207,6 @@ class SettingsDialog:
|
||||
self._neutrino_radio_button.set_active(s_type is SettingsType.NEUTRINO_MP)
|
||||
self.update_header_bar()
|
||||
self._settings_stack.get_child_by_name(Property.HTTP.value).set_visible(is_enigma_profile)
|
||||
self._program_frame.set_sensitive(is_enigma_profile)
|
||||
self._extra_support_grid.set_sensitive(is_enigma_profile)
|
||||
http_active = self._support_http_api_switch.get_active()
|
||||
self._click_mode_zap_button.set_sensitive(is_enigma_profile and http_active)
|
||||
self._lang_combo_box.set_active_id(self._ext_settings.language)
|
||||
@@ -285,6 +292,7 @@ class SettingsDialog:
|
||||
self.on_transcoding_preset_changed(self._presets_combo_box)
|
||||
|
||||
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._force_bq_name_switch.set_active(self._settings.force_bq_names)
|
||||
self._support_http_api_switch.set_active(self._settings.http_api_support)
|
||||
@@ -352,11 +360,13 @@ class SettingsDialog:
|
||||
self._ext_settings.active_preset = self._presets_combo_box.get_active_id()
|
||||
|
||||
if self._ext_settings.is_darwin:
|
||||
self._ext_settings.dark_mode = self._dark_mode_switch.get_active()
|
||||
self._ext_settings.is_themes_support = self._themes_support_switch.get_active()
|
||||
self._ext_settings.theme = self._theme_combo_box.get_active_id()
|
||||
self._ext_settings.icon_theme = self._icon_theme_combo_box.get_active_id()
|
||||
|
||||
if self._s_type is SettingsType.ENIGMA_2:
|
||||
self._ext_settings.is_enable_experimental = self._enable_exp_switch.get_active()
|
||||
self._ext_settings.use_colors = self._set_color_switch.get_active()
|
||||
self._ext_settings.new_color = self._new_color_button.get_rgba().to_string()
|
||||
self._ext_settings.extra_color = self._extra_color_button.get_rgba().to_string()
|
||||
@@ -442,6 +452,12 @@ class SettingsDialog:
|
||||
self._click_mode_zap_and_play_button.get_active())):
|
||||
self._click_mode_disabled_button.set_active(True)
|
||||
|
||||
def on_experimental_switch(self, switch, state):
|
||||
if not state:
|
||||
self._support_ver5_switch.set_active(state)
|
||||
self._enable_send_to_switch.set_active(state)
|
||||
self._enable_yt_dl_switch.set_active(state)
|
||||
|
||||
def on_force_bq_name(self, switch, state):
|
||||
if self._main_stack.get_visible_child_name() != "extra":
|
||||
return
|
||||
@@ -685,7 +701,9 @@ class SettingsDialog:
|
||||
self.add_theme(self._ext_settings.themes_path, self._theme_combo_box)
|
||||
|
||||
def on_theme_remove(self, button):
|
||||
self.remove_theme(self._theme_combo_box, self._ext_settings.themes_path)
|
||||
if show_dialog(DialogType.QUESTION, self._dialog) == Gtk.ResponseType.OK:
|
||||
Gtk.Settings().get_default().set_property("gtk-theme-name", "")
|
||||
self.remove_theme(self._theme_combo_box, self._ext_settings.themes_path)
|
||||
|
||||
def on_icon_theme_changed(self, button, state=False):
|
||||
if self._main_stack.get_visible_child_name() != "appearance":
|
||||
@@ -696,7 +714,9 @@ class SettingsDialog:
|
||||
self.add_theme(self._ext_settings.icon_themes_path, self._icon_theme_combo_box)
|
||||
|
||||
def on_icon_theme_remove(self, button):
|
||||
self.remove_theme(self._icon_theme_combo_box, self._ext_settings.icon_themes_path)
|
||||
if show_dialog(DialogType.QUESTION, self._dialog) == Gtk.ResponseType.OK:
|
||||
Gtk.Settings().get_default().set_property("gtk-icon-theme-name", "")
|
||||
self.remove_theme(self._icon_theme_combo_box, self._ext_settings.icon_themes_path)
|
||||
|
||||
@run_idle
|
||||
def add_theme(self, path, button):
|
||||
@@ -709,14 +729,17 @@ class SettingsDialog:
|
||||
@run_task
|
||||
def unpack_theme(self, src, dst, button):
|
||||
try:
|
||||
from shutil import unpack_archive
|
||||
os.makedirs(os.path.dirname(dst), exist_ok=True)
|
||||
|
||||
unpack_archive(src, dst)
|
||||
except (KeyError, EOFError) as e:
|
||||
self.show_info_message(str(e), Gtk.MessageType.ERROR)
|
||||
else:
|
||||
self.update_theme_button(button, dst)
|
||||
import subprocess
|
||||
log("Unpacking '{}' started...".format(src))
|
||||
p = subprocess.Popen(["tar", "-xvf", src, "-C", dst],
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.STDOUT)
|
||||
p.communicate()
|
||||
log("Unpacking end.")
|
||||
finally:
|
||||
self.update_theme_button(button, dst)
|
||||
self._appearance_box.set_sensitive(True)
|
||||
|
||||
@run_idle
|
||||
@@ -738,9 +761,6 @@ class SettingsDialog:
|
||||
self.show_info_message("No selected item!", Gtk.MessageType.ERROR)
|
||||
return
|
||||
|
||||
if show_dialog(DialogType.QUESTION, self._dialog) != Gtk.ResponseType.OK:
|
||||
return
|
||||
|
||||
from shutil import rmtree
|
||||
|
||||
try:
|
||||
@@ -748,11 +768,16 @@ class SettingsDialog:
|
||||
except OSError as e:
|
||||
self.show_info_message(str(e), Gtk.MessageType.ERROR)
|
||||
else:
|
||||
button.remove(button.get_active())
|
||||
button.set_active(0)
|
||||
self.theme_button_remove_active(button)
|
||||
|
||||
@run_idle
|
||||
def theme_button_remove_active(self, button):
|
||||
button.remove(button.get_active())
|
||||
button.set_active(0)
|
||||
|
||||
@run_idle
|
||||
def init_appearance(self):
|
||||
self._dark_mode_switch.set_active(self._ext_settings.dark_mode)
|
||||
t_support = self._ext_settings.is_themes_support
|
||||
self._themes_support_switch.set_active(t_support)
|
||||
if t_support:
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/bin/bash
|
||||
VER="1.0.0_Alpha"
|
||||
VER="1.0.1_Beta"
|
||||
B_PATH="dist/DemonEditor"
|
||||
DEB_PATH="$B_PATH/usr/share/demoneditor"
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ DemonEditor
|
||||
Enigma2 channel and satellites list editor for GNU/Linux.
|
||||
|
||||
Experimental support of Neutrino-MP or others on the same basis (BPanther, etc).
|
||||
Focused on the convenience of working in lists from the keyboard. The mouse is also fully supported (Drag and Drop etc)
|
||||
Focused on the convenience of working in lists from the keyboard. The mouse is also fully supported (Drag and Drop etc).
|
||||
|
||||
Main features of the program:
|
||||
Editing bouquets, channels, satellites.
|
||||
@@ -40,7 +40,7 @@ Keyboard shortcuts:
|
||||
Ctrl + F - show/hide search bar.
|
||||
Ctrl + Shift + F - show/hide filter bar.
|
||||
|
||||
For multiple mouse selection (including Drag and Drop), press and hold the **Ctrl** key!
|
||||
For multiple selection with the mouse, press and hold the Ctrl key!
|
||||
|
||||
Minimum requirements:
|
||||
Python >= 3.5.2 and GTK+ >= 3.16 with PyGObject bindings, python3-requests.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
Package: demon-editor
|
||||
Version: 1.0.0-Alpha
|
||||
Version: 1.0.1-Beta
|
||||
Section: utils
|
||||
Priority: optional
|
||||
Architecture: all
|
||||
|
||||
BIN
deb/usr/share/locale/be/LC_MESSAGES/demon-editor.mo
Normal file
BIN
deb/usr/share/locale/be/LC_MESSAGES/demon-editor.mo
Normal file
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.
Binary file not shown.
1050
po/be/demon-editor.po
Normal file
1050
po/be/demon-editor.po
Normal file
File diff suppressed because it is too large
Load Diff
@@ -12,7 +12,7 @@ msgstr ""
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
|
||||
msgid "translator-credits"
|
||||
msgstr "Charly, Dmitriy Yefremov"
|
||||
msgstr "Charly\nDmitriy Yefremov"
|
||||
|
||||
# Main
|
||||
msgid "Service"
|
||||
@@ -105,6 +105,9 @@ msgstr "Standardbezeichnung setzen"
|
||||
msgid "Insert marker"
|
||||
msgstr "Marker einfügen"
|
||||
|
||||
msgid "Insert space"
|
||||
msgstr "Leerzeichen einfügen"
|
||||
|
||||
msgid "Locate in services"
|
||||
msgstr "In den Diensten suchen"
|
||||
|
||||
@@ -160,7 +163,7 @@ msgid "Remove"
|
||||
msgstr "Entfernen"
|
||||
|
||||
msgid "Remove all unavailable"
|
||||
msgstr "Entfernt alle nicht verfügbaren"
|
||||
msgstr "Entfernen alle nicht verfügbaren"
|
||||
|
||||
msgid "Satellites editor"
|
||||
msgstr "Satelliten-Editor"
|
||||
@@ -516,6 +519,9 @@ msgstr "Bitte wählen Sie nur einen Eintrag aus!"
|
||||
msgid "No png file is selected!"
|
||||
msgstr "Es ist keine png-Datei ausgewählt!"
|
||||
|
||||
msgid "No profile selected!"
|
||||
msgstr "Kein Profil ausgewählt!"
|
||||
|
||||
msgid "No reference is present!"
|
||||
msgstr "Es liegt keine Referenz vor!"
|
||||
|
||||
@@ -671,11 +677,11 @@ msgstr "Play Stream"
|
||||
msgid "Disabled"
|
||||
msgstr "Ausgeschaltet"
|
||||
|
||||
msgid "Enable ver. 5 support (experimental)"
|
||||
msgstr "Lamedb ver. 5 Unterstützung aktivieren (experimentell)"
|
||||
msgid "Enable lamedb ver. 5 support"
|
||||
msgstr "Lamedb ver. 5 Unterstützung aktivieren"
|
||||
|
||||
msgid "Enable HTTP API (experimental)"
|
||||
msgstr "HTTP-API aktivieren (experimentell)"
|
||||
msgid "Enable HTTP API"
|
||||
msgstr "HTTP-API aktivieren"
|
||||
|
||||
msgid "Switch(zap) the channel(Ctrl + Z)"
|
||||
msgstr "Umschalten des Kanals (Strg + Z)"
|
||||
@@ -795,8 +801,8 @@ msgstr "Sprache:"
|
||||
msgid "Load the last open configuration at program startup"
|
||||
msgstr "Laden der zuletzt geöffneten Konfiguration beim Programmstart"
|
||||
|
||||
msgid "Enable direct playback bar (experimental)"
|
||||
msgstr "Aktivieren der direkten Wiedergabeleiste (experimentell)"
|
||||
msgid "Enable direct playback bar"
|
||||
msgstr "Aktivieren der direkten Wiedergabeleiste"
|
||||
|
||||
msgid "Enables direct sending and playback of media links on the receiver"
|
||||
msgstr "Ermöglicht das direkte Senden und Abspielen von Medienlinks auf dem Box"
|
||||
@@ -1023,3 +1029,41 @@ msgstr "Screenshot"
|
||||
msgid "Video"
|
||||
msgstr "Video"
|
||||
|
||||
msgid "The Neutrino has only experimental support. Not all features are supported!"
|
||||
msgstr "Die Neutrino hat nur experimentelle Unterstützung. Nicht alle Funktionen werden unterstützt!"
|
||||
|
||||
msgid "Enable experimental features"
|
||||
msgstr "Experimentelle Funktionen aktivieren"
|
||||
|
||||
msgid "Can't Playback!"
|
||||
msgstr "Kann nicht abgespielt werden!"
|
||||
|
||||
msgid "Enable Dark Mode"
|
||||
msgstr "Dunkelmodus aktivieren"
|
||||
|
||||
msgid "Extract..."
|
||||
msgstr "Entpacken..."
|
||||
|
||||
msgid "Unsupported format!"
|
||||
msgstr "Nicht unterstütztes Format!"
|
||||
|
||||
msgid "Combine with the current data?"
|
||||
msgstr "Mit den aktuellen Daten kombinieren?"
|
||||
|
||||
msgid "Importing data done!"
|
||||
msgstr "Daten importieren erledigt!"
|
||||
|
||||
msgid "Current service"
|
||||
msgstr "Aktueller Service"
|
||||
|
||||
msgid "Open folder"
|
||||
msgstr "Ordner öffnen"
|
||||
|
||||
msgid "Open archive"
|
||||
msgstr "Archiv öffnen"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -54,7 +54,7 @@ msgid "Current IP:"
|
||||
msgstr "IP actual:"
|
||||
|
||||
msgid "Assign"
|
||||
msgstr "Assignar"
|
||||
msgstr "Asignar"
|
||||
|
||||
msgid "Bouquet details"
|
||||
msgstr "Detalles bouquet"
|
||||
@@ -66,7 +66,7 @@ msgid "Copy"
|
||||
msgstr "Copiar"
|
||||
|
||||
msgid "Copy reference"
|
||||
msgstr "Copia de referencia"
|
||||
msgstr "Copiar referencia"
|
||||
|
||||
msgid "Download"
|
||||
msgstr "Descargar"
|
||||
@@ -123,7 +123,7 @@ msgid "New"
|
||||
msgstr "Nuevo"
|
||||
|
||||
msgid "New bouquet"
|
||||
msgstr "Bouquet nuevo"
|
||||
msgstr "Nuevo bouquet"
|
||||
|
||||
msgid "Create bouquet"
|
||||
msgstr "Crear bouquet"
|
||||
@@ -156,10 +156,10 @@ msgid "Picons"
|
||||
msgstr "Picons"
|
||||
|
||||
msgid "Picons downloader"
|
||||
msgstr "Descargar picons"
|
||||
msgstr "Descarga de picons"
|
||||
|
||||
msgid "Satellites downloader"
|
||||
msgstr "Descargar satélites"
|
||||
msgstr "Descarga de satélites"
|
||||
|
||||
msgid "Remove"
|
||||
msgstr "Quitar"
|
||||
@@ -210,7 +210,7 @@ msgid "Enigma2 channel and satellites list editor for GNU/Linux"
|
||||
msgstr "Editor de canales y satélites Enigma2 para GNU/Linux"
|
||||
|
||||
msgid "Host:"
|
||||
msgstr "Anfitrión:"
|
||||
msgstr "Host:"
|
||||
|
||||
msgid "Loading data..."
|
||||
msgstr "Cargando datos..."
|
||||
@@ -256,7 +256,7 @@ msgstr "Extra:"
|
||||
|
||||
# Filter bar
|
||||
msgid "Only free"
|
||||
msgstr "Solamente gratis"
|
||||
msgstr "Solamente libres"
|
||||
|
||||
msgid "All positions"
|
||||
msgstr "Todas las posiciones"
|
||||
@@ -272,10 +272,10 @@ msgid "Stop playback"
|
||||
msgstr "Detener la reproducción"
|
||||
|
||||
msgid "Previous stream in the list"
|
||||
msgstr "Anterior flujo en la lista"
|
||||
msgstr "Anterior stream en la lista"
|
||||
|
||||
msgid "Next stream in the list"
|
||||
msgstr "Siguiente flujo en la lista"
|
||||
msgstr "Siguiente stream en la lista"
|
||||
|
||||
msgid "Toggle in fullscreen"
|
||||
msgstr "Cambiar a pantalla completa"
|
||||
@@ -294,7 +294,7 @@ msgid "Receive picons"
|
||||
msgstr "Recibir picons"
|
||||
|
||||
msgid "Picons name format:"
|
||||
msgstr "Picons formato nombres:"
|
||||
msgstr "Formato del nombre de los picons:"
|
||||
|
||||
msgid "Resize:"
|
||||
msgstr "Redimensionar:"
|
||||
@@ -433,7 +433,7 @@ msgstr "Buscar"
|
||||
|
||||
# IPTV dialog
|
||||
msgid "Stream data"
|
||||
msgstr "Transmitir flujo"
|
||||
msgstr "Transmitir stream"
|
||||
|
||||
# IPTV list configuration dialog
|
||||
msgid "Starting values"
|
||||
@@ -443,7 +443,7 @@ msgid "Reset to default"
|
||||
msgstr "Restablecer a predeterminado"
|
||||
|
||||
msgid "IPTV streams list configuration"
|
||||
msgstr "Configurar lista de flujos IPTV"
|
||||
msgstr "Configurar lista de streams IPTV"
|
||||
|
||||
# Settings dialog
|
||||
msgid "Preferences"
|
||||
@@ -511,13 +511,13 @@ msgid "No m3u file is selected!"
|
||||
msgstr "¡No se ha seleccionado ningún fichero m3u!"
|
||||
|
||||
msgid "Not implemented yet!"
|
||||
msgstr "¡Aún sin implementar!"
|
||||
msgstr "¡No implementado!"
|
||||
|
||||
msgid "The text of marker is empty, please try again!"
|
||||
msgstr "¡El texto del marcador está vacío, inténtalo de nuevo!"
|
||||
|
||||
msgid "Please, select only one item!"
|
||||
msgstr "¡Por favor, seleccione sólo un elemento!"
|
||||
msgstr "¡Por favor, seleccione un único elemento!"
|
||||
|
||||
msgid "No png file is selected!"
|
||||
msgstr "¡No se ha seleccionado ningún fichero png!"
|
||||
@@ -526,7 +526,7 @@ msgid "No reference is present!"
|
||||
msgstr "¡Ninguna referencia presente!"
|
||||
|
||||
msgid "No selected item!"
|
||||
msgstr "¡Ningún elemento seleccionado!"
|
||||
msgstr "¡No se ha seleccionado ningún elemento!"
|
||||
|
||||
msgid "The task is already running!"
|
||||
msgstr "¡La tarea ya se está ejecutando!"
|
||||
@@ -562,23 +562,23 @@ msgid "Operation not allowed in this context!"
|
||||
msgstr "¡Operación no permitida en este contexto!"
|
||||
|
||||
msgid "No VLC is found. Check that it is installed!"
|
||||
msgstr "VLC no encontrado. ¡Verifique que está instalado!"
|
||||
msgstr "VLC no encontrado. ¡Compruebe que está instalado!"
|
||||
|
||||
# Search unavailable streams dialog
|
||||
msgid "Please wait, streams testing in progress..."
|
||||
msgstr "Por favor espere, hay una prueba de flujo en progreso..."
|
||||
msgstr "Por favor espere, hay una prueba de stream en progreso..."
|
||||
|
||||
msgid "Found"
|
||||
msgstr "Encontrado"
|
||||
|
||||
msgid "unavailable streams."
|
||||
msgstr "Flujos no presentes."
|
||||
msgstr "Streams no presentes."
|
||||
|
||||
msgid "No changes required!"
|
||||
msgstr "¡Ningún cambio requerido!"
|
||||
msgstr "¡No se requieren cambios!"
|
||||
|
||||
msgid "This list does not contains IPTV streams!"
|
||||
msgstr "¡La lista no contiene flujos IPTV!"
|
||||
msgstr "¡La lista no contiene streams IPTV!"
|
||||
|
||||
msgid "New empty configuration"
|
||||
msgstr "Nueva configuración vacía"
|
||||
@@ -672,16 +672,16 @@ msgid "Zap"
|
||||
msgstr "Zapear"
|
||||
|
||||
msgid "Play stream"
|
||||
msgstr "Reproducir flujo"
|
||||
msgstr "Reproducir stream"
|
||||
|
||||
msgid "Disabled"
|
||||
msgstr "Desactivado"
|
||||
|
||||
msgid "Enable ver. 5 support (experimental)"
|
||||
msgstr "Soporte para ver. 5 (experimental)"
|
||||
msgid "Enable lamedb ver. 5 support"
|
||||
msgstr "Soporte para lamedb ver. 5"
|
||||
|
||||
msgid "Enable HTTP API (experimental)"
|
||||
msgstr "Habilitar API HTTP (experimental)"
|
||||
msgid "Enable HTTP API"
|
||||
msgstr "Habilitar API HTTP"
|
||||
|
||||
msgid "Switch(zap) the channel(Ctrl + Z)"
|
||||
msgstr "Poner el canal (Ctrl + Z)"
|
||||
@@ -690,7 +690,7 @@ msgid "Switch the channel and watch in the program(Ctrl + W)"
|
||||
msgstr "Poner el canal y ver en el programa (Ctrl + W)"
|
||||
|
||||
msgid "Play IPTV or other stream in the program(Ctrl + P)"
|
||||
msgstr "Reproducir IPTV u otro flujo en el programa (Ctrl + P)"
|
||||
msgstr "Reproducir IPTV u otro stream en el programa (Ctrl + P)"
|
||||
|
||||
msgid "Export to m3u"
|
||||
msgstr "Exportar a m3u"
|
||||
@@ -795,10 +795,10 @@ msgid "Language:"
|
||||
msgstr "Idioma:"
|
||||
|
||||
msgid "Load the last open configuration at program startup"
|
||||
msgstr "Cargar la última configuración abierta al iniciar el programa"
|
||||
msgstr "Cargar la última configuración usada al iniciar el programa"
|
||||
|
||||
msgid "Enable direct playback bar (experimental)"
|
||||
msgstr "Habilitar la barra de reproducción directa (experimental)"
|
||||
msgid "Enable direct playback bar"
|
||||
msgstr "Habilitar la barra de reproducción directa"
|
||||
|
||||
msgid "Enables direct sending and playback of media links on the receiver"
|
||||
msgstr "Habilita el envío directo y la reproducción de enlaces de medios en el receptor"
|
||||
@@ -828,16 +828,16 @@ msgid "Reset"
|
||||
msgstr "Reset"
|
||||
|
||||
msgid "File"
|
||||
msgstr "Archivo"
|
||||
msgstr "Fichero"
|
||||
|
||||
msgid "Picons manager"
|
||||
msgstr "Picons manager"
|
||||
msgstr "Gestor de picons"
|
||||
|
||||
msgid "Explorer"
|
||||
msgstr "Explorador"
|
||||
|
||||
msgid "Satellite url:"
|
||||
msgstr "Url Satelite:"
|
||||
msgstr "Url satélite:"
|
||||
|
||||
msgid "Cut"
|
||||
msgstr "Cortar"
|
||||
@@ -867,43 +867,43 @@ msgid "IPTV tools"
|
||||
msgstr "Intrumentos IPTV"
|
||||
|
||||
msgid "Make profile folder as default for the additional data"
|
||||
msgstr "Has folder de perfil estandar para datos adicionales"
|
||||
msgstr "Usar por defecto el directorio de perfil para los datos adionales"
|
||||
|
||||
msgid "Default data path:"
|
||||
msgstr "Ruta estandar de datos:"
|
||||
msgstr "Ruta estándar de datos:"
|
||||
|
||||
msgid "Streams record path:"
|
||||
msgstr "Ruta de gravacion de stream:"
|
||||
msgstr "Ruta de grabación del stream:"
|
||||
|
||||
msgid "Record"
|
||||
msgstr "Gravar"
|
||||
msgstr "Grabación"
|
||||
|
||||
msgid "Record:"
|
||||
msgstr "Gravar:"
|
||||
msgstr "Grabación:"
|
||||
|
||||
msgid "Record to disk:"
|
||||
msgstr "Gravar en disco:"
|
||||
msgstr "Grabación en disco:"
|
||||
|
||||
msgid "Streaming"
|
||||
msgstr "Streameando"
|
||||
msgstr "Streaming"
|
||||
|
||||
msgid "Activate transcoding"
|
||||
msgstr "Activer transcodificacion"
|
||||
msgstr "Activar transcodificación"
|
||||
|
||||
msgid "Presets:"
|
||||
msgstr "Presets:"
|
||||
msgstr "Preajustes:"
|
||||
|
||||
msgid "Video options:"
|
||||
msgstr "Opciones Video:"
|
||||
msgstr "Opciones vídeo:"
|
||||
|
||||
msgid "Audio options:"
|
||||
msgstr "Opciones Audio:"
|
||||
msgstr "Opciones audio:"
|
||||
|
||||
msgid "Bitrate (kb/s):"
|
||||
msgstr "Bitrate (kb/s):"
|
||||
|
||||
msgid "Codec:"
|
||||
msgstr "Codec:"
|
||||
msgstr "Códec:"
|
||||
|
||||
msgid "Width (px):"
|
||||
msgstr "Ancho (px):"
|
||||
@@ -915,10 +915,10 @@ msgid "Channels:"
|
||||
msgstr "Canales:"
|
||||
|
||||
msgid "Sample rate (Hz):"
|
||||
msgstr "Sample rate (Гц):"
|
||||
msgstr "Frecuencia de muestreo (Hz):"
|
||||
|
||||
msgid "Play streams mode:"
|
||||
msgstr "Tocar en modo streams:"
|
||||
msgstr "Modo de reproducción de los streams:"
|
||||
|
||||
msgid "Built-in player"
|
||||
msgstr "Reproductor interno"
|
||||
@@ -927,40 +927,40 @@ msgid "VLC media player"
|
||||
msgstr "Reproductor VLC"
|
||||
|
||||
msgid "Only get m3u file"
|
||||
msgstr "Solo bajar archivo *.m3u"
|
||||
msgstr "Sólo bajar fichero m3u"
|
||||
|
||||
msgid "Save and restart the program to apply the settings."
|
||||
msgstr "Guarde y reinicie el programa para aplicar la configuración."
|
||||
msgstr "Guardar y reiniciar el programa para aplicar la configuración."
|
||||
|
||||
msgid "Some images may have problems displaying the favorites list!"
|
||||
msgstr "Algunas imágenes pueden tener problemas para mostrar la lista de favoritos!"
|
||||
msgstr "Algunas imágenes pueden tener problemas al mostrar la lista de favoritos!"
|
||||
|
||||
msgid "Operates in standby mode or current active transponder!"
|
||||
msgstr "Funciona en modo de espera o transpondedor activo actual!"
|
||||
msgstr "¡Funciona en modo de espera o transpondedor activo actual!"
|
||||
|
||||
msgid "No connection to the receiver!"
|
||||
msgstr "Sin conexión al receptor!"
|
||||
msgstr "¡Desconectado del receptor!"
|
||||
|
||||
msgid "Signal level"
|
||||
msgstr "Nivel de señal"
|
||||
|
||||
msgid "Receiver info"
|
||||
msgstr "Informacion sobre receptor"
|
||||
msgstr "Información sobre el receptor"
|
||||
|
||||
msgid "A profile with that name exists!"
|
||||
msgstr "Existe un perfil con ese nombre!"
|
||||
msgstr "¡Ya existe un perfil con ese nombre!"
|
||||
|
||||
msgid "Show short info as hints in the main services list"
|
||||
msgstr "Mostrar información breve como sugerencias en la lista de servicios principal"
|
||||
|
||||
msgid "Show detailed info as hints in the bouquet list"
|
||||
msgstr "Mostrar información detallada como pistas en la lista de bouquet"
|
||||
msgstr "Mostrar información detallada como consejo en la lista de bouquets"
|
||||
|
||||
msgid "Enable alternate bouquet file naming"
|
||||
msgstr "Habilitar nombres alternativos de archivos de bouquet"
|
||||
msgstr "Habilitar nombres alternativos para los ficheros de bouquets"
|
||||
|
||||
msgid "Allows you to name bouquet files using their names."
|
||||
msgstr "Le permite nombrar archivos de bouquet usando sus nombres."
|
||||
msgstr "Permite nombrar ficheros de bouquets usando sus propios nombres."
|
||||
|
||||
msgid "Appearance"
|
||||
msgstr "Apariencia"
|
||||
@@ -969,16 +969,16 @@ msgid "Enable Themes support"
|
||||
msgstr "Habilitar compatibilidad con temas"
|
||||
|
||||
msgid "Gtk3 Theme:"
|
||||
msgstr "Тема Gtk3:"
|
||||
msgstr "Теma Gtk3:"
|
||||
|
||||
msgid "Icon Theme:"
|
||||
msgstr "Тема Icono:"
|
||||
msgstr "Icono del tema:"
|
||||
|
||||
msgid "Gtk3 Themes and Icons:"
|
||||
msgstr "Tema Gtk3 e Iconos:"
|
||||
msgstr "Tema Gtk3 e iconos:"
|
||||
|
||||
msgid "Deleting data..."
|
||||
msgstr "Borrando datos ..."
|
||||
msgstr "Borrando datos..."
|
||||
|
||||
msgid "Download from the receiver"
|
||||
msgstr "Descargar desde el receptor"
|
||||
@@ -987,13 +987,13 @@ msgid "Remove all picons from the receiver"
|
||||
msgstr "Eliminar todos los picons del receptor"
|
||||
|
||||
msgid "Service reference"
|
||||
msgstr "Referencia de servicio"
|
||||
msgstr "Referencia del servicio"
|
||||
|
||||
msgid "Enable support for"
|
||||
msgstr "Habilitar soporte para"
|
||||
|
||||
msgid "Auto-check for updates"
|
||||
msgstr "Verificación automática de actualizaciones"
|
||||
msgstr "Comprobación automática de actualizaciones"
|
||||
|
||||
msgid "Filter services"
|
||||
msgstr "Filtrar servicios"
|
||||
@@ -1005,23 +1005,23 @@ msgid "Destination:"
|
||||
msgstr "Destino:"
|
||||
|
||||
msgid "EXPERIMENTAL!"
|
||||
msgstr "EXPERIMENTAL!"
|
||||
msgstr "¡EXPERIMENTAL!"
|
||||
|
||||
msgid "Sorting data..."
|
||||
msgstr "Ordenando datos..."
|
||||
|
||||
msgid "There are unsaved changes.\n\n\t Save them now?"
|
||||
msgstr "Hay cambios sin guardar.\n\n\t ¿Guardarlos ahora?"
|
||||
msgstr "Hay cambios sin guardar.\n\n\t ¿Desea guardarlos ahora?"
|
||||
|
||||
msgid "Are you sure you want to change the order\n\t of services in this bouquet?"
|
||||
msgstr "¿Está seguro de que desea cambiar el orden\n\t de servicios en este bouquet?"
|
||||
msgstr "¿Está seguro de querer cambiar el orden\n\t de servicios en este bouquet?"
|
||||
|
||||
msgid "Remove from the receiver"
|
||||
msgstr "Retirar del receptor"
|
||||
msgstr "Eliminar del receptor"
|
||||
|
||||
msgid "Screenshot"
|
||||
msgstr "Captura de pantalla"
|
||||
|
||||
msgid "Video"
|
||||
msgstr "Vidео"
|
||||
msgstr "Vídео"
|
||||
|
||||
|
||||
@@ -671,11 +671,11 @@ msgstr "Speel stream af"
|
||||
msgid "Disabled"
|
||||
msgstr "Uitgeschakeld"
|
||||
|
||||
msgid "Enable ver. 5 support (experimental)"
|
||||
msgstr "Ondersteuning voor ver. 5 inschakelen (experimenteel)"
|
||||
msgid "Enable lamedb ver. 5 support"
|
||||
msgstr "Ondersteuning voor lamedb ver. 5 inschakelen"
|
||||
|
||||
msgid "Enable HTTP API (experimental)"
|
||||
msgstr "HTTP API inschakelen (experimenteel)"
|
||||
msgid "Enable HTTP API"
|
||||
msgstr "HTTP API inschakelen"
|
||||
|
||||
msgid "Switch(zap) the channel(Ctrl + Z)"
|
||||
msgstr "Schakelaar (ZAP) naar het kanaal (CTRL + Z)"
|
||||
@@ -791,8 +791,8 @@ msgstr "Taal:"
|
||||
msgid "Load the last open configuration at program startup"
|
||||
msgstr "Laad de laatst geopende configuratie op bij opstart programma"
|
||||
|
||||
msgid "Enable direct playback bar (experimental)"
|
||||
msgstr "Laat onmiddelijk playback bar toe (experimenteel)"
|
||||
msgid "Enable direct playback bar"
|
||||
msgstr "Laat onmiddelijk playback bar toe"
|
||||
|
||||
msgid "Enables direct sending and playback of media links on the receiver"
|
||||
msgstr "Laat rechtstreeks versturen van and playback en media links op de ontvanger toe"
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
# Copyright (C) 2018-2019 Dmitriy Yefremov
|
||||
# Copyright (C) 2018-2020 Dmitriy Yefremov
|
||||
# This file is distributed under the MIT license.
|
||||
#
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Last-Translator: wwns\n"
|
||||
@@ -265,6 +266,9 @@ msgstr "Ukryj/Pomiń"
|
||||
msgid "Parent lock"
|
||||
msgstr "Blokada rodzicielska"
|
||||
|
||||
msgid "There are unsaved changes.\n\n\t Save them now?"
|
||||
msgstr "Istnieją niezapisane zmiany.\n\n\t Zapisać je teraz?"
|
||||
|
||||
# Filter bar
|
||||
msgid "Only free"
|
||||
msgstr "Tylko FTA"
|
||||
@@ -386,6 +390,12 @@ msgid "Remove selection"
|
||||
msgstr "Usuń wybrane"
|
||||
|
||||
# Service details dialog
|
||||
msgid "To the top"
|
||||
msgstr "Przenieś na góre bukietu"
|
||||
|
||||
msgid "To the end"
|
||||
msgstr "Przenieś na koniec bukietu"
|
||||
|
||||
msgid "Service data:"
|
||||
msgstr "Dane usług:"
|
||||
|
||||
@@ -596,9 +606,6 @@ msgstr "Brak danych do zapisania!"
|
||||
msgid "Network"
|
||||
msgstr "Sieć"
|
||||
|
||||
msgid "Program"
|
||||
msgstr "Program"
|
||||
|
||||
msgid "Backup"
|
||||
msgstr "Kopia"
|
||||
|
||||
@@ -626,6 +633,15 @@ msgstr "Wyjście"
|
||||
msgid "Tools"
|
||||
msgstr "Narzędzia"
|
||||
|
||||
msgid "Cut"
|
||||
msgstr "Wytnij"
|
||||
|
||||
msgid "Paste"
|
||||
msgstr "Wklej"
|
||||
|
||||
msgid "Insert space"
|
||||
msgstr "Wstaw spację"
|
||||
|
||||
# Import
|
||||
msgid "Import"
|
||||
msgstr "Importuj"
|
||||
@@ -648,12 +664,12 @@ msgstr "Usuń wszystkie nieużywane"
|
||||
msgid "Test"
|
||||
msgstr "Test"
|
||||
|
||||
msgid "Details"
|
||||
msgstr "Właściwości"
|
||||
|
||||
msgid "Test connection"
|
||||
msgstr "Testuj połączenie"
|
||||
|
||||
msgid "Double click on the service in the bouquet list:"
|
||||
msgstr "Kliknij dwukrotnie usługę na liście bukietów:"
|
||||
|
||||
msgid "Switch(zap) the channel(Ctrl + Z)"
|
||||
msgstr "Przełącz(zap) kanał(Ctrl + Z)"
|
||||
|
||||
@@ -667,7 +683,7 @@ msgid "Export to m3u"
|
||||
msgstr "Eksportuj do m3u"
|
||||
|
||||
msgid "EPG configuration"
|
||||
msgstr "Koniguruj EPG"
|
||||
msgstr "Konfiguruj EPG"
|
||||
|
||||
msgid "Apply"
|
||||
msgstr "Zatwierdź"
|
||||
@@ -754,17 +770,11 @@ msgstr "Import listy odtwarzania"
|
||||
msgid "Getting link error:"
|
||||
msgstr "Błąd pobierania łącza:"
|
||||
|
||||
msgid "Apply profile settings"
|
||||
msgstr "Zastosuj ustawienia profilu"
|
||||
|
||||
msgid "Settings type:"
|
||||
msgstr "Ustawienia dla:"
|
||||
|
||||
msgid "Set default"
|
||||
msgstr "Uataw domyślnie"
|
||||
|
||||
msgid "Enable direct playback bar (experimental)"
|
||||
msgstr "Włącz pasek bezpośredniego odtwarzania (eksperymentalnie)"
|
||||
msgstr "Ustaw domyślnie"
|
||||
|
||||
msgid "Enables direct sending and playback of media links on the receiver"
|
||||
msgstr "Umożliwia bezpośrednie wysyłanie i odtwarzanie łączy multimedialnych w odbiorniku"
|
||||
@@ -785,7 +795,10 @@ msgid "Remove picons from the receiver"
|
||||
msgstr "Usuń pikony z odbiornika"
|
||||
|
||||
msgid "Use http to reload data in the receiver."
|
||||
msgstr "Użyj http aby ponownie załadować dane do odbiornika."
|
||||
msgstr "Użyj http aby przeładować dane w odbiorniku."
|
||||
|
||||
msgid "Apply profile settings"
|
||||
msgstr "Zastosuj ustawienia profilu"
|
||||
|
||||
msgid "Drag or paste the link here"
|
||||
msgstr "Przeciągnij lub wklej tutaj link"
|
||||
@@ -808,10 +821,16 @@ msgstr "Pobierz z odbiornika"
|
||||
msgid "The Neutrino has only experimental support. Not all features are supported!"
|
||||
msgstr "Neutrino ma jedynie wsparcie eksperymentalne. Nie wszystkie funkcje są obsługiwane!"
|
||||
|
||||
msgid "Some images may have problems displaying the favorites list!"
|
||||
msgstr "Niektóre obrazy mogą mieć problemy z wyświetlaniem listy ulubionych!"
|
||||
|
||||
# Appearance
|
||||
msgid "Appearance"
|
||||
msgstr "Wygląd"
|
||||
|
||||
msgid "Enable Dark Mode"
|
||||
msgstr "Włącz tryb ciemny"
|
||||
|
||||
msgid "Enable Themes support"
|
||||
msgstr "Włącz obsługę motywów"
|
||||
|
||||
@@ -834,23 +853,17 @@ msgstr "Zapisz i uruchom ponownie program, aby zastosować ustawienia."
|
||||
msgid "Extra"
|
||||
msgstr "Ekstra"
|
||||
|
||||
msgid "Enable ver. 5 support (experimental)"
|
||||
msgstr "Włącz wer. 5 wsparcie (eksperymentalne)"
|
||||
|
||||
msgid "Enable alternate bouquet file naming"
|
||||
msgstr "Włącz alternatywne nazewnictwo plików bukietów"
|
||||
|
||||
msgid "Some images may have problems displaying the favorites list!"
|
||||
msgstr "Niektóre obrazy mogą mieć problemy z wyświetlaniem listy ulubionych!"
|
||||
|
||||
msgid "Allows you to name bouquet files using their names."
|
||||
msgstr "Pozwala nazwać pliki bukietów przy użyciu ich nazw."
|
||||
|
||||
msgid "Enable HTTP API (experimental)"
|
||||
msgstr "Włącz API HTTP (eksperymentalne)"
|
||||
msgid "Enable HTTP API"
|
||||
msgstr "Włącz API HTTP"
|
||||
|
||||
msgid "Enable send to receiver (experimental)"
|
||||
msgstr "Włącz wysyłanie do odbiornika (eksperymentalne)"
|
||||
msgid "Double click on the service in the bouquet list:"
|
||||
msgstr "Kliknij dwukrotnie usługę na liście bukietów:"
|
||||
|
||||
msgid "Zap"
|
||||
msgstr "Przełącz"
|
||||
@@ -867,6 +880,28 @@ msgstr "Odtwórz strumień"
|
||||
msgid "Disabled"
|
||||
msgstr "Wyłączone"
|
||||
|
||||
msgid "Enable experimental features"
|
||||
msgstr "Włącz funkcje eksperymentalne"
|
||||
|
||||
msgid "Enable lamedb ver. 5 support"
|
||||
msgstr "Włącz wsparcie dla lamedb w wer.5"
|
||||
|
||||
msgid "Enable support for"
|
||||
msgstr "Włącz obsługę"
|
||||
|
||||
msgid "Enables parsing links using youtube-dl to get direct links to media"
|
||||
msgstr "Umożliwia analizowanie linków za pomocą youtube-dl, aby uzyskać bezpośrednie linki do multimediów"
|
||||
|
||||
msgid "Auto-check for updates"
|
||||
msgstr "Automatyczne sprawdzanie aktualizacji"
|
||||
|
||||
msgid "Enable direct playback bar"
|
||||
msgstr "Włącz pasek bezpośredniego odtwarzania"
|
||||
|
||||
#Program
|
||||
msgid "Program"
|
||||
msgstr "Program"
|
||||
|
||||
msgid "Language:"
|
||||
msgstr "Język:"
|
||||
|
||||
@@ -876,7 +911,7 @@ msgstr "Załaduj ostatnią otwartą konfigurację podczas uruchamiania programu"
|
||||
msgid "Show short info as hints in the main services list"
|
||||
msgstr "Pokaż krótkie informacje jako wskazówki na głównej liście usług"
|
||||
|
||||
msgid "Show detailed info as hints in the bouquet list"
|
||||
msgid "Show detailed info as hints in the bouquet list"
|
||||
msgstr "Pokaż szczegółowe informacje jako wskazówki na liście bukietów"
|
||||
|
||||
msgid "Set background color for the services"
|
||||
@@ -962,8 +997,8 @@ msgstr "Ustawia folder profilu jako domyślny do przechowywania pikonów, kopii
|
||||
msgid "Default data path:"
|
||||
msgstr "Domyślna ścieżka danych:"
|
||||
|
||||
msgid "Record"
|
||||
msgstr "Nagrania"
|
||||
msgid "Record:"
|
||||
msgstr "Nagrania:"
|
||||
|
||||
msgid "Streams record path:"
|
||||
msgstr "Ścieżka zapisu nagrań:"
|
||||
|
||||
@@ -663,11 +663,11 @@ msgstr "Play stream"
|
||||
msgid "Disabled"
|
||||
msgstr "Desativado"
|
||||
|
||||
msgid "Enable ver. 5 support (experimental)"
|
||||
msgstr "Ativar ver. 5 suporte (experimental)"
|
||||
msgid "Enable lamedb ver. 5 support"
|
||||
msgstr "Ativar lamedb ver. 5 suporte"
|
||||
|
||||
msgid "Enable HTTP API (experimental)"
|
||||
msgstr "Ativar HTTP API (experimental)"
|
||||
msgid "Enable HTTP API"
|
||||
msgstr "Ativar HTTP API"
|
||||
|
||||
msgid "Switch(zap) the channel(Ctrl + Z)"
|
||||
msgstr "Mudar(zap) o canal(Ctrl + Z)"
|
||||
@@ -783,8 +783,8 @@ msgstr "Idioma:"
|
||||
msgid "Load the last open configuration at program startup"
|
||||
msgstr "Cargar la última configuración abierta al iniciar el programa"
|
||||
|
||||
msgid "Enable direct playback bar (experimental)"
|
||||
msgstr "Habilitar la barra de reproducción directa (experimental)"
|
||||
msgid "Enable direct playback bar"
|
||||
msgstr "Habilitar la barra de reproducción directa"
|
||||
|
||||
msgid "Enables direct sending and playback of media links on the receiver"
|
||||
msgstr "Habilita el envío directo y la reproducción de enlaces de medios en el receptor"
|
||||
|
||||
@@ -104,6 +104,9 @@ msgstr "Имя по умолчанию"
|
||||
msgid "Insert marker"
|
||||
msgstr "Вставить маркер"
|
||||
|
||||
msgid "Insert space"
|
||||
msgstr "Вставить пробел"
|
||||
|
||||
msgid "Locate in services"
|
||||
msgstr "Найти в списке сервисов"
|
||||
|
||||
@@ -507,6 +510,9 @@ msgstr "Пожалуйста, выберите только один элеме
|
||||
msgid "No png file is selected!"
|
||||
msgstr "Не выбран png файл!"
|
||||
|
||||
msgid "No profile selected!"
|
||||
msgstr "Не выбран профиль!"
|
||||
|
||||
msgid "No reference is present!"
|
||||
msgstr "Ссылка не найдена!"
|
||||
|
||||
@@ -662,17 +668,17 @@ msgstr "Воспр. потока"
|
||||
msgid "Disabled"
|
||||
msgstr "Выкл."
|
||||
|
||||
msgid "Enable ver. 5 support (experimental)"
|
||||
msgstr "Включить поддержку lamedb вер. 5 (экспериментально)"
|
||||
msgid "Enable lamedb ver. 5 support"
|
||||
msgstr "Включить поддержку lamedb вер. 5"
|
||||
|
||||
msgid "Enable HTTP API (experimental)"
|
||||
msgstr "Включить HTTP API (экспериментально)"
|
||||
msgid "Enable HTTP API"
|
||||
msgstr "Включить HTTP API"
|
||||
|
||||
msgid "Switch(zap) the channel(Ctrl + Z)"
|
||||
msgstr "Переключить канал(Ctrl + Z)"
|
||||
|
||||
msgid "Switch the channel and watch in the program(Ctrl + W)"
|
||||
msgstr "Переклють канал и просмотр в программе(Ctrl + W)."
|
||||
msgstr "Переключить канал и просмотр в программе(Ctrl + W)."
|
||||
|
||||
msgid "Play IPTV or other stream in the program(Ctrl + P)"
|
||||
msgstr "Воспроизведение IPTV или другого потока в программе(Ctrl + P)"
|
||||
@@ -782,8 +788,8 @@ msgstr "Язык:"
|
||||
msgid "Load the last open configuration at program startup"
|
||||
msgstr "Загружать последнюю открытую конфигурацию при запуске программы"
|
||||
|
||||
msgid "Enable direct playback bar (experimental)"
|
||||
msgstr "Включить панель прямого воспроизведения (экспериментально)"
|
||||
msgid "Enable direct playback bar"
|
||||
msgstr "Включить панель прямого воспроизведения"
|
||||
|
||||
msgid "Enables direct sending and playback of media links on the receiver"
|
||||
msgstr "Включает прямую отправку и воспроизведение медиа-ссылок на ресивере"
|
||||
@@ -1010,3 +1016,35 @@ msgstr "Скриншот"
|
||||
msgid "Video"
|
||||
msgstr "Видео"
|
||||
|
||||
msgid "The Neutrino has only experimental support. Not all features are supported!"
|
||||
msgstr "Neutrino имеет только экспериментальную поддержку. Поддерживаются не все функции!"
|
||||
|
||||
msgid "Enable experimental features"
|
||||
msgstr "Включить экспериментальные функции"
|
||||
|
||||
msgid "Can't Playback!"
|
||||
msgstr "Не удается воспроизвести!"
|
||||
|
||||
msgid "Enable Dark Mode"
|
||||
msgstr "Включить темный режим"
|
||||
|
||||
msgid "Extract..."
|
||||
msgstr "Извлечь..."
|
||||
|
||||
msgid "Unsupported format!"
|
||||
msgstr "Неподдерживаемый формат!"
|
||||
|
||||
msgid "Combine with the current data?"
|
||||
msgstr "Объединить с текущими данными?"
|
||||
|
||||
msgid "Importing data done!"
|
||||
msgstr "Импорт данных завершен!"
|
||||
|
||||
msgid "Current service"
|
||||
msgstr "Текущий сервис"
|
||||
|
||||
msgid "Open folder"
|
||||
msgstr "Открыть папку"
|
||||
|
||||
msgid "Open archive"
|
||||
msgstr "Открыть архив"
|
||||
|
||||
@@ -673,11 +673,11 @@ msgstr "Akışı oynat"
|
||||
msgid "Disabled"
|
||||
msgstr "Devre dışı"
|
||||
|
||||
msgid "Enable ver. 5 support (experimental)"
|
||||
msgstr "Sürüm 5 desteğini etkinleştir (deneysel)"
|
||||
msgid "Enable lamedb ver. 5 support"
|
||||
msgstr "Sürüm 5 desteğini etkinleştir"
|
||||
|
||||
msgid "Enable HTTP API (experimental)"
|
||||
msgstr "HTTP API'sini etkinleştir (deneysel)"
|
||||
msgid "Enable HTTP API"
|
||||
msgstr "HTTP API'sini etkinleştir"
|
||||
|
||||
msgid "Switch(zap) the channel(Ctrl + Z)"
|
||||
msgstr "Kanalı değiştir (zap) (Ctrl + Z)"
|
||||
@@ -797,8 +797,8 @@ msgstr "Dil:"
|
||||
msgid "Load the last open configuration at program startup"
|
||||
msgstr "Program açılışında son açık yapılandırmayı yükle"
|
||||
|
||||
msgid "Enable direct playback bar (experimental)"
|
||||
msgstr "Doğrudan oynatma çubuğunu etkinleştir (deneysel)"
|
||||
msgid "Enable direct playback bar"
|
||||
msgstr "Doğrudan oynatma çubuğunu etkinleştir"
|
||||
|
||||
msgid "Enables direct sending and playback of media links on the receiver"
|
||||
msgstr "Alıcıdaki medya bağlantılarının doğrudan gönderilmesini ve oynatılmasını sağlar"
|
||||
|
||||
Reference in New Issue
Block a user