mirror of
https://github.com/DYefremov/DemonEditor.git
synced 2026-05-09 07:57:48 +02:00
Compare commits
67 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ed16fb0195 | ||
|
|
833b386356 | ||
|
|
83424124d3 | ||
|
|
d77aa68a39 | ||
|
|
94266e13b8 | ||
|
|
8db6fb1b0b | ||
|
|
e07c5d4bf7 | ||
|
|
6f3090a7e1 | ||
|
|
b73c1d1118 | ||
|
|
df127c05f3 | ||
|
|
9b579af528 | ||
|
|
8290e723c9 | ||
|
|
b12c29be84 | ||
|
|
1780fbadbd | ||
|
|
4fe4e92442 | ||
|
|
bfad5cf9ac | ||
|
|
5d285f61c0 | ||
|
|
f61d9a1f61 | ||
|
|
8d115677d1 | ||
|
|
f7c6cd6908 | ||
|
|
2d2a90542c | ||
|
|
535c9c9102 | ||
|
|
866e18762d | ||
|
|
aef5027d23 | ||
|
|
3a142eca4a | ||
|
|
606bad7716 | ||
|
|
fa07f8bf85 | ||
|
|
92280162c6 | ||
|
|
5d285e88d8 | ||
|
|
0355714e92 | ||
|
|
b06e877a0c | ||
|
|
9b479b051d | ||
|
|
b953ee8762 | ||
|
|
0e7d6bec69 | ||
|
|
cb9824d404 | ||
|
|
c87adb256f | ||
|
|
899d05a186 | ||
|
|
bd4f86e91e | ||
|
|
42fb365b45 | ||
|
|
67d6ea861e | ||
|
|
d887a61636 | ||
|
|
9d9efb7577 | ||
|
|
b6d331a311 | ||
|
|
1060e169a1 | ||
|
|
562c1a5955 | ||
|
|
3c4dec323f | ||
|
|
3bafe08030 | ||
|
|
722f8df813 | ||
|
|
8f6984dbaf | ||
|
|
bf6e9617ec | ||
|
|
ec27c32d35 | ||
|
|
bfa3b1aa66 | ||
|
|
a1e32abd07 | ||
|
|
f93370293b | ||
|
|
79a2a034eb | ||
|
|
791c073d1a | ||
|
|
de5ec53a18 | ||
|
|
156ac7d364 | ||
|
|
5680423f14 | ||
|
|
fa256c5a0b | ||
|
|
d9a5d9a972 | ||
|
|
4e59bdf38e | ||
|
|
7edb03836a | ||
|
|
0ea0c889d4 | ||
|
|
e494a34bc4 | ||
|
|
4a57234293 | ||
|
|
aca4875ee6 |
@@ -5,6 +5,7 @@ Comment=Channel and satellite list editor for Enigma2
|
||||
Comment[ru]=Редактор списка каналов и спутников для Enigma2
|
||||
Comment[be]=Рэдактар спіса каналаў і спадарожнікаў для Enigma2
|
||||
Comment[de]=Programm- und Satellitenlisten-Editor für Enigma2
|
||||
Comment[tr]=Enigma2 için kanal ve uydu listesi editörü
|
||||
Icon=demon-editor
|
||||
Exec=bash -c 'cd $(dirname %k) && ./start.py'
|
||||
Terminal=false
|
||||
|
||||
66
README.md
66
README.md
@@ -1,33 +1,32 @@
|
||||
# <img src="app/ui/icons/hicolor/96x96/apps/demon-editor.png" width="32" /> DemonEditor
|
||||
[](LICENSE) 
|
||||
### Enigma2 channel and satellite list editor for GNU/Linux.
|
||||
[<img src="https://user-images.githubusercontent.com/7511379/118884719-8277e980-b8ff-11eb-8621-c8c4afd6181b.png" width="560"/>](https://user-images.githubusercontent.com/7511379/118884719-8277e980-b8ff-11eb-8621-c8c4afd6181b.png)
|
||||
|
||||
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).
|
||||
### Enigma2 channel and satellite list editor for GNU/Linux.
|
||||
Experimental support of Neutrino-MP or others on the same basis (BPanther, etc).
|
||||
|
||||
## Main features of the program
|
||||
* Editing bouquets, channels, satellites.
|
||||
[<img src="https://user-images.githubusercontent.com/7511379/118884747-8ad02480-b8ff-11eb-9104-8cf8fb6e785d.png" width="480"/>](https://user-images.githubusercontent.com/7511379/118884747-8ad02480-b8ff-11eb-9104-8cf8fb6e785d.png)
|
||||
[<img src="https://user-images.githubusercontent.com/7511379/141680963-9b8eb6cc-c712-46b2-aefe-19769e21a7d5.png" width="480"/>](https://user-images.githubusercontent.com/7511379/141680963-9b8eb6cc-c712-46b2-aefe-19769e21a7d5.png)
|
||||
* Import function.
|
||||
[<img src="https://user-images.githubusercontent.com/7511379/118526825-4dc23180-b749-11eb-8197-e9bbccbc3bdf.png" width="480"/>](https://user-images.githubusercontent.com/7511379/118526825-4dc23180-b749-11eb-8197-e9bbccbc3bdf.png)
|
||||
[<img src="https://user-images.githubusercontent.com/7511379/141681059-68bc1b55-6fab-436c-aa73-ef24e2e5113b.png" width="480"/>](https://user-images.githubusercontent.com/7511379/141681059-68bc1b55-6fab-436c-aa73-ef24e2e5113b.png)
|
||||
* Backup function.
|
||||
[<img src="https://user-images.githubusercontent.com/7511379/118528402-f58c2f00-b74a-11eb-9b84-edf220526e6e.png" width="480"/>](https://user-images.githubusercontent.com/7511379/118528402-f58c2f00-b74a-11eb-9b84-edf220526e6e.png)
|
||||
[<img src="https://user-images.githubusercontent.com/7511379/141681104-ed9b5d35-25de-426f-b9bb-2a6e4db022bb.png" width="480"/>](https://user-images.githubusercontent.com/7511379/141681104-ed9b5d35-25de-426f-b9bb-2a6e4db022bb.png)
|
||||
* Support of picons.
|
||||
[<img src="https://user-images.githubusercontent.com/7511379/118526864-5c104d80-b749-11eb-8497-6e8c78542ab1.png" width="480"/>](https://user-images.githubusercontent.com/7511379/118526864-5c104d80-b749-11eb-8497-6e8c78542ab1.png)
|
||||
[<img src="https://user-images.githubusercontent.com/7511379/141681115-957c63a3-4113-422d-bb27-2d96b1463cd1.png" width="480"/>](https://user-images.githubusercontent.com/7511379/141681115-957c63a3-4113-422d-bb27-2d96b1463cd1.png)
|
||||
* Importing services, downloading picons and updating satellites from the Web.
|
||||
[<img src="https://user-images.githubusercontent.com/7511379/118530243-1a81a180-b74d-11eb-8e01-aea904d954af.png" width="250"/>](https://user-images.githubusercontent.com/7511379/118530243-1a81a180-b74d-11eb-8e01-aea904d954af.png)
|
||||
[<img src="https://user-images.githubusercontent.com/7511379/118526706-31be9000-b749-11eb-9956-c4bf2e13f968.png" width="292"/>](https://user-images.githubusercontent.com/7511379/118526706-31be9000-b749-11eb-9956-c4bf2e13f968.png)
|
||||
[<img src="https://user-images.githubusercontent.com/7511379/141681075-28f18ea5-e456-4e84-bf64-1b7d9a95324d.png" width="262"/>](https://user-images.githubusercontent.com/7511379/141681075-28f18ea5-e456-4e84-bf64-1b7d9a95324d.png)
|
||||
[<img src="https://user-images.githubusercontent.com/7511379/141681040-b1ad190a-6bc2-4741-bb42-1fb219a0fcab.png" width="250"/>](https://user-images.githubusercontent.com/7511379/141681040-b1ad190a-6bc2-4741-bb42-1fb219a0fcab.png)
|
||||
* Extended support of IPTV.
|
||||
* Import to bouquet(Neutrino WEBTV) from m3u.
|
||||
* Export of bouquets with IPTV services in m3u.
|
||||
* 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.
|
||||
[<img src="https://user-images.githubusercontent.com/7511379/118884891-b3f0b500-b8ff-11eb-8717-3588d6e089de.png" width="480"/>](https://user-images.githubusercontent.com/7511379/118884891-b3f0b500-b8ff-11eb-8717-3588d6e089de.png)
|
||||
* Control panel with the ability to view EPG and manage timers (via HTTP API, experimental).
|
||||
[<img src="https://user-images.githubusercontent.com/7511379/118886284-66754780-b901-11eb-9068-29b5a607ccaf.png" width="480"/>](https://user-images.githubusercontent.com/7511379/118886284-66754780-b901-11eb-9068-29b5a607ccaf.png)
|
||||
* Assignment of EPG from DVB or XML for IPTV services (Enigma2 only).
|
||||
[<img src="https://user-images.githubusercontent.com/7511379/141681187-fae4e784-c9e0-43df-b499-4d38e83d6560.png" width="480"/>](https://user-images.githubusercontent.com/7511379/141681187-fae4e784-c9e0-43df-b499-4d38e83d6560.png)
|
||||
* Playback of IPTV or other streams directly from the bouquet list.
|
||||
[<img src="https://user-images.githubusercontent.com/7511379/141681129-98f78cdc-9a98-46ef-b738-618a327634d4.png" width="480"/>](https://user-images.githubusercontent.com/7511379/141681129-98f78cdc-9a98-46ef-b738-618a327634d4.png)
|
||||
* Control panel (via HTTP API).
|
||||
[<img src="https://user-images.githubusercontent.com/7511379/141684475-4511ea4f-b152-42d5-b9c8-f3e1e9a160d0.png" width="480"/>](https://user-images.githubusercontent.com/7511379/141684475-4511ea4f-b152-42d5-b9c8-f3e1e9a160d0.png)
|
||||
* Ability to view EPG and manage timers (via HTTP API).
|
||||
* Simple FTP client (experimental).
|
||||
[<img src="https://user-images.githubusercontent.com/7511379/118527372-e8bb0b80-b749-11eb-9653-4ad64c99a05a.png" width="480"/>](https://user-images.githubusercontent.com/7511379/118527372-e8bb0b80-b749-11eb-9653-4ad64c99a05a.png)
|
||||
[<img src="https://user-images.githubusercontent.com/7511379/141681165-5679c331-72e7-4044-b365-dcdb30b1433c.png" width="480"/>](https://user-images.githubusercontent.com/7511379/141681165-5679c331-72e7-4044-b365-dcdb30b1433c.png)
|
||||
|
||||
#### Keyboard shortcuts
|
||||
* **Ctrl + X** - only in bouquet list.
|
||||
@@ -51,8 +50,10 @@ Clipboard is **"rubber"**. There is an accumulation before the insertion!
|
||||
* **Ctrl + D** - load data from receiver.
|
||||
* **Ctrl + U/B** - upload data/bouquets to receiver.
|
||||
* **Ctrl + I** - extra info, details.
|
||||
* **Ctrl + F** - show/hide search bar.
|
||||
* **Ctrl + F** - show search bar.
|
||||
* **Ctrl + Shift + F** - show/hide filter bar.
|
||||
* **Ctrl + T** - show/hide built-in Telnet client.
|
||||
* **Ctrl + Shift + L** - show/hide logging panel.
|
||||
|
||||
For **multiple** selection with the mouse, press and hold the **Ctrl** key!
|
||||
|
||||
@@ -71,13 +72,32 @@ To create a simple **debian package**, you can use the *build-deb.sh.* You can a
|
||||
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.
|
||||
A ready-made [package](https://aur.archlinux.org/packages/demoneditor-bin) is also available for [Arch Linux](https://archlinux.org/) users in the [AUR](https://aur.archlinux.org/) repository.
|
||||
* ### macOS
|
||||
**This program can be run on macOS.** To work in this OS, you must use a [separate branch](https://github.com/DYefremov/DemonEditor/tree/experimental-mac). A ready-made package can be downloaded from the [releases](https://github.com/DYefremov/DemonEditor/releases) page.
|
||||
**The functionality and performance of this version may be different from the Linux version!**
|
||||
**This program can be run on macOS.**
|
||||
To run the program on macOS, you need to install [brew](https://brew.sh/).
|
||||
Then install the required components via terminal:
|
||||
```brew install python3 gtk+3 pygobject3 adwaita-icon-theme```
|
||||
```pip3 install requests, pillow```
|
||||
|
||||
Launch is similar to Linux.
|
||||
|
||||
You can also download the ready-made package as a ***.dmg** file from the [releases](https://github.com/DYefremov/DemonEditor/releases) page.
|
||||
Recommended copy the package to the **Application** directory.
|
||||
Perhaps in the security settings it will be necessary to allow the launch of this application!
|
||||
|
||||
* ### MS Windows
|
||||
**Windows users can also run this program.** One way is to use the [MSYS2](https://www.msys2.org/) platform.
|
||||
In addition, you can download a ready-made build (**64-bit**) from the [releases](https://github.com/DYefremov/DemonEditor/releases) page.
|
||||
|
||||
**All builds may contain components distributed under the GPL [v3](http://www.gnu.org/licenses/gpl-3.0.html) or lower license.
|
||||
By downloading and using this packages you agree to the terms of this [license](http://www.gnu.org/licenses/gpl-3.0.html) and the possible inconvenience associated with this!**
|
||||
|
||||
THIS SOFTWARE COMES WITH ABSOLUTELY NO WARRANTY.
|
||||
AUTHOR IS NOT LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY CONNECTION WITH THIS SOFTWARE.
|
||||
|
||||
## 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 [Linux Mint](https://linuxmint.com/) (MATE 64-bit) distribution!
|
||||
Support for DVB-T/T2 and DVB-C channels for Neutrino is not fully implemented and has an experimental status.
|
||||
|
||||
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.
|
||||
|
||||
When using the multiple import feature, from *lamedb* will be taken data **only for channels that are in the selected bouquets!**
|
||||
@@ -85,6 +105,8 @@ If you need full set of the data, including *[satellites, terrestrial, cables].x
|
||||
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.
|
||||
|
||||
**The built-in Telnet client does not support ANSI escape sequences!**
|
||||
|
||||
For streams playback, this app supports [VLC](https://www.videolan.org/vlc/), [MPV](https://mpv.io/) and [GStreamer](https://gstreamer.freedesktop.org/). Depending on your distro, you may need to install additional packages and libraries.
|
||||
#### Command line arguments:
|
||||
* **-l** - write logs to file.
|
||||
|
||||
@@ -7,13 +7,12 @@ from gi.repository import GLib
|
||||
|
||||
_LOG_FILE = "demon-editor.log"
|
||||
_DATE_FORMAT = "%d-%m-%y %H:%M:%S"
|
||||
_LOGGER_NAME = None
|
||||
|
||||
LOGGER_NAME = "main_logger"
|
||||
|
||||
|
||||
def init_logger():
|
||||
global _LOGGER_NAME
|
||||
_LOGGER_NAME = "main_logger"
|
||||
logging.Logger(_LOGGER_NAME)
|
||||
logging.Logger(LOGGER_NAME)
|
||||
logging.basicConfig(level=logging.INFO,
|
||||
format="%(asctime)s %(message)s",
|
||||
datefmt=_DATE_FORMAT,
|
||||
@@ -23,7 +22,7 @@ def init_logger():
|
||||
|
||||
def log(message, level=logging.ERROR, debug=False, fmt_message="{}"):
|
||||
""" The main logging function. """
|
||||
logger = logging.getLogger(_LOGGER_NAME)
|
||||
logger = logging.getLogger(LOGGER_NAME)
|
||||
if debug:
|
||||
from traceback import format_exc
|
||||
logger.log(level, fmt_message.format(format_exc()))
|
||||
|
||||
@@ -1,3 +1,31 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# The MIT License (MIT)
|
||||
#
|
||||
# Copyright (c) 2018-2021 Dmitriy Yefremov
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
# THE SOFTWARE.
|
||||
#
|
||||
# Author: Dmitriy Yefremov
|
||||
#
|
||||
|
||||
|
||||
import os
|
||||
import re
|
||||
import socket
|
||||
@@ -181,7 +209,7 @@ class UtfFTP(FTP):
|
||||
self.send_file(file_name, src, callback)
|
||||
|
||||
def remove_unused_bouquets(self, callback):
|
||||
bq_files = ("userbouquet.", "bouquets.xml", "ubouquets.xml")
|
||||
bq_files = ("userbouquet.", "subbouquet.", "bouquets.xml", "ubouquets.xml")
|
||||
|
||||
for file in filter(lambda f: f.startswith(bq_files), self.nlst()):
|
||||
self.delete_file(file, callback)
|
||||
|
||||
@@ -62,7 +62,7 @@ def get_bouquets(path, s_type):
|
||||
def write_bouquet(path, bq, s_type):
|
||||
if s_type is SettingsType.ENIGMA_2:
|
||||
writer = BouquetsWriter(path, None)
|
||||
writer.write_bouquet(path + "userbouquet.{}.{}".format(bq.name, bq.type), bq.name, bq.services)
|
||||
writer.write_bouquet(f"{path}userbouquet.{bq.name}.{bq.type}", bq.name, bq.services)
|
||||
elif s_type is SettingsType.NEUTRINO_MP:
|
||||
from .neutrino.bouquets import write_bouquet
|
||||
write_bouquet(path, bq)
|
||||
|
||||
@@ -1,7 +1,37 @@
|
||||
""" Common elements module """
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# The MIT License (MIT)
|
||||
#
|
||||
# Copyright (c) 2018-2021 Dmitriy Yefremov
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
# THE SOFTWARE.
|
||||
#
|
||||
# Author: Dmitriy Yefremov
|
||||
#
|
||||
|
||||
|
||||
""" Common elements module. """
|
||||
from collections import namedtuple
|
||||
from enum import Enum
|
||||
|
||||
from app.commons import log
|
||||
|
||||
Service = namedtuple("Service", ["flags_cas", "transponder_type", "coded", "service", "locked", "hide", "package",
|
||||
"service_type", "picon", "picon_id", "ssid", "freq", "rate", "pol", "fec",
|
||||
"system", "pos", "data_id", "fav_id", "transponder"])
|
||||
@@ -82,6 +112,21 @@ class Flag(Enum):
|
||||
def is_new(value: int):
|
||||
return value & 1 << 5
|
||||
|
||||
@staticmethod
|
||||
def parse(value: str) -> int:
|
||||
""" Returns an int representation of the flag value.
|
||||
|
||||
The flag value is usually represented by the number [int],
|
||||
but can also be appear in hex format.
|
||||
"""
|
||||
if len(value) < 3:
|
||||
return 0
|
||||
|
||||
value = value[2:]
|
||||
if value.isdigit():
|
||||
return int(value)
|
||||
return int(value, 16)
|
||||
|
||||
|
||||
class Pids(Enum):
|
||||
VIDEO = "c:00"
|
||||
@@ -191,14 +236,15 @@ def get_value_by_name(en, name):
|
||||
|
||||
|
||||
def is_transponder_valid(tr: Transponder):
|
||||
""" Checks transponder validity """
|
||||
""" Checks transponder validity. """
|
||||
try:
|
||||
int(tr.frequency)
|
||||
int(tr.symbol_rate)
|
||||
tr.pls_mode is None or int(tr.pls_mode)
|
||||
tr.pls_code is None or int(tr.pls_code)
|
||||
tr.is_id is None or int(tr.is_id)
|
||||
except TypeError:
|
||||
except (TypeError, ValueError) as e:
|
||||
log(f"Transponder validation error: {e}\n{tr}")
|
||||
return False
|
||||
|
||||
if tr.polarization not in POLARIZATION.values():
|
||||
|
||||
@@ -60,14 +60,14 @@ class BouquetsWriter:
|
||||
self._marker_index = 1
|
||||
self._space_index = 0
|
||||
self._alt_names = set()
|
||||
self._NAME_PATTERN = re.compile("[^\\w_()]+")
|
||||
|
||||
def write(self):
|
||||
line = []
|
||||
pattern = re.compile("[^\\w_()]+")
|
||||
|
||||
for bqs in self._bouquets:
|
||||
line.clear()
|
||||
line.append("#NAME {}\n".format(bqs.name))
|
||||
line.append(f"#NAME {bqs.name}\n")
|
||||
bq_file_names = {b.file for b in bqs.bouquets}
|
||||
count = 1
|
||||
m_count = 0
|
||||
@@ -76,24 +76,29 @@ class BouquetsWriter:
|
||||
bq_name = bq.file
|
||||
if not bq_name:
|
||||
if self._force_bq_names:
|
||||
bq_name = re.sub(pattern, "_", bq.name)
|
||||
bq_name = re.sub(self._NAME_PATTERN, "_", bq.name)
|
||||
else:
|
||||
bq_name = "de{0:02d}".format(count)
|
||||
bq_name = f"de{count:02d}"
|
||||
while bq_name in bq_file_names:
|
||||
count += 1
|
||||
bq_name = "de{0:02d}".format(count)
|
||||
bq_name = f"de{count:02d}"
|
||||
bq_file_names.add(bq_name)
|
||||
|
||||
if BqType(bq.type) is BqType.MARKER:
|
||||
bq_type = BqType(bq.type)
|
||||
if bq_type is BqType.MARKER:
|
||||
m_data = bq.file.split(":") if bq.file else None
|
||||
b_name = m_data[-1].strip() if m_data else bq.name.lstrip(_MARKER_PREFIX)
|
||||
line.append(self._MARKER.format(m_count, b_name))
|
||||
m_count += 1
|
||||
else:
|
||||
line.append(self._SERVICE.format(2 if bq.type == BqType.RADIO.value else 1, bq_name, bq.type))
|
||||
self.write_bouquet(f"{self._path}userbouquet.{bq_name}.{bq.type}", bq.name, bq.services)
|
||||
if bq_type is BqType.BOUQUET:
|
||||
bq_name = re.sub(self._NAME_PATTERN, "_", bq.name)
|
||||
self.write_sub_bouquet(self._path, bq_name, bq, bqs.type)
|
||||
else:
|
||||
self.write_bouquet(f"{self._path}userbouquet.{bq_name}.{bqs.type}", bq.name, bq.services)
|
||||
line.append(self._SERVICE.format(2 if bqs.type == BqType.RADIO.value else 1, bq_name, bqs.type))
|
||||
|
||||
with open(self._path + "bouquets.{}".format(bqs.type), "w", encoding="utf-8") as file:
|
||||
with open(f"{self._path}bouquets.{bqs.type}", "w", encoding="utf-8") as file:
|
||||
file.writelines(line)
|
||||
|
||||
def write_bouquet(self, path, name, services):
|
||||
@@ -134,6 +139,18 @@ class BouquetsWriter:
|
||||
with open(path, "w", encoding="utf-8") as file:
|
||||
file.writelines(bouquet)
|
||||
|
||||
def write_sub_bouquet(self, path, file_name, bq, bq_type):
|
||||
bouquet = [f"#NAME {bq.name}\n"]
|
||||
sb_type = 2 if bq_type == BqType.RADIO.value else 1
|
||||
|
||||
for sb in bq.services:
|
||||
bq_name = f"subbouquet.{re.sub(self._NAME_PATTERN, '_', sb.name)}.{sb.type}"
|
||||
self.write_bouquet(f"{path}{bq_name}", sb.name, sb.services)
|
||||
bouquet.append(f"#SERVICE 1:7:{sb_type}:0:0:0:0:0:0:0:FROM BOUQUET \"{bq_name}\" ORDER BY bouquet\n")
|
||||
|
||||
with open(f"{self._path}userbouquet.{file_name}.{bq_type}", "w", encoding="utf-8") as file:
|
||||
file.writelines(bouquet)
|
||||
|
||||
|
||||
class ServiceType(Enum):
|
||||
SERVICE = "0"
|
||||
@@ -141,6 +158,7 @@ class ServiceType(Enum):
|
||||
MARKER = "64"
|
||||
SPACE = "832" # Hidden marker.
|
||||
ALT = "134" # Alternatives.
|
||||
UDP = "256"
|
||||
|
||||
@classmethod
|
||||
def _missing_(cls, value):
|
||||
@@ -198,7 +216,7 @@ class BouquetsReader:
|
||||
else:
|
||||
s_data = line.split(":")
|
||||
if len(s_data) == 12 and s_data[1] == ServiceType.MARKER.value:
|
||||
b_name = "{}{}".format(_MARKER_PREFIX, s_data[-1].strip())
|
||||
b_name = f"{_MARKER_PREFIX}{s_data[-1].strip()}"
|
||||
bouquets[2].append(Bouquet(b_name, BqType.MARKER.value, [], None, None, line.strip()))
|
||||
else:
|
||||
log(f"Unsupported or invalid data format: [{line}].")
|
||||
@@ -210,14 +228,14 @@ class BouquetsReader:
|
||||
@staticmethod
|
||||
def get_bouquet(path, bq_name, bq_type, prefix="userbouquet"):
|
||||
""" Parsing services ids from bouquet file. """
|
||||
with open(path + "{}.{}.{}".format(prefix, bq_name, bq_type), encoding="utf-8", errors="replace") as file:
|
||||
with open(f"{path}{prefix}.{bq_name}.{bq_type}", encoding="utf-8", errors="replace") as file:
|
||||
chs_list = file.read()
|
||||
services = []
|
||||
srvs = list(filter(None, chs_list.split("\n#SERVICE"))) # filtering ['']
|
||||
# May come across empty[wrong] files!
|
||||
if not srvs:
|
||||
log("Bouquet file 'userbouquet.{}.{}' is empty or wrong!".format(bq_name, bq_type))
|
||||
return "{} [empty]".format(bq_name), services
|
||||
log(f"Bouquet file 'userbouquet.{bq_name}.{bq_type}' is empty or wrong!")
|
||||
return f"{bq_name} [empty]", services
|
||||
|
||||
bq_name = srvs.pop(0)
|
||||
|
||||
@@ -225,7 +243,7 @@ class BouquetsReader:
|
||||
srv_data = srv.strip().split(":")
|
||||
data_len = len(srv_data)
|
||||
if data_len < 10:
|
||||
log("The bouquet [{}] service [{}] has the wrong data format: [{}]".format(bq_name, num, srv))
|
||||
log(f"The bouquet [{bq_name}] service [{num}] has the wrong data format: [{srv}]")
|
||||
continue
|
||||
|
||||
s_type = ServiceType(srv_data[1])
|
||||
@@ -253,7 +271,7 @@ class BouquetsReader:
|
||||
desc = desc.lstrip(":").strip() if desc else srv_data[-1].strip()
|
||||
services.append(BouquetService(desc, BqServiceType.IPTV, srv, num))
|
||||
else:
|
||||
fav_id = "{}:{}:{}:{}".format(srv_data[3], srv_data[4], srv_data[5], srv_data[6])
|
||||
fav_id = f"{srv_data[3]}:{srv_data[4]}:{srv_data[5]}:{srv_data[6]}"
|
||||
name = None
|
||||
if data_len == 12:
|
||||
name, sep, desc = str(srv_data[-1]).partition("\n#DESCRIPTION")
|
||||
|
||||
@@ -52,7 +52,7 @@ class LameDbReader:
|
||||
""" Lamedb parser class.
|
||||
|
||||
Reads and parses the Enigma2 lamedb[5] file.
|
||||
Supports versions 3, 4 and 5..
|
||||
Supports versions 3, 4 and 5.
|
||||
"""
|
||||
__slots__ = ["_path", "_fmt"]
|
||||
|
||||
@@ -99,7 +99,7 @@ class LameDbReader:
|
||||
try:
|
||||
data = str(file.read())
|
||||
except UnicodeDecodeError as e:
|
||||
log("lamedb parse error: " + str(e))
|
||||
log(f"lamedb parse error: {e}")
|
||||
else:
|
||||
return self.get_services_list(data)
|
||||
|
||||
@@ -115,7 +115,7 @@ class LameDbReader:
|
||||
for line in lns:
|
||||
if line.startswith("s:"):
|
||||
srv_data = line.strip("s:").split(",", 2)
|
||||
srv_data[1] = srv_data[1].strip("\"")
|
||||
srv_data[1] = srv_data[1].strip("\"\n")
|
||||
data_len = len(srv_data)
|
||||
if data_len == 3:
|
||||
srv_data[2] = srv_data[2].strip()
|
||||
@@ -129,7 +129,7 @@ class LameDbReader:
|
||||
tr, srv = data[0].strip("t:"), data[1].strip().replace(":", " ", 1)
|
||||
trs[tr] = srv
|
||||
else:
|
||||
log("Error while parsing transponder data [ver. 5] for line: {}".format(line))
|
||||
log(f"Error while parsing transponder data [ver. 5] for line: {line}")
|
||||
|
||||
return self.parse_services(srvs, trs)
|
||||
|
||||
@@ -151,33 +151,33 @@ class LameDbReader:
|
||||
is_v3 = False
|
||||
if len(tid) < 4:
|
||||
is_v3 = True
|
||||
tid = "{:0>4}".format(tid)
|
||||
tid = f"{tid:0>4}"
|
||||
data[2] = tid
|
||||
if len(nid) < 4:
|
||||
is_v3 = True
|
||||
nid = "{:0>4}".format(nid)
|
||||
nid = f"{nid:0>4}"
|
||||
data[3] = nid
|
||||
if is_v3:
|
||||
data[0] = "{:0>4}".format(data[0])
|
||||
data[0] = f"{data[0]:0>4}"
|
||||
data_id = _SEP.join(data)
|
||||
|
||||
srv_type = int(data[4])
|
||||
transponder_id = "{}:{}:{}".format(data[1], tid, nid)
|
||||
transponder_id = f"{data[1]}:{tid}:{nid}"
|
||||
transponder = transponders.get(transponder_id, None)
|
||||
|
||||
tid = tid.lstrip(sp).upper()
|
||||
nid = nid.lstrip(sp).upper()
|
||||
# The tid and nid values can be 0.
|
||||
tid = tid.lstrip(sp).upper() or "0"
|
||||
nid = nid.lstrip(sp).upper() or "0"
|
||||
ssid = str(data[0]).lstrip(sp).upper()
|
||||
onid = str(data[1]).lstrip(sp).upper()
|
||||
# For comparison in bouquets. Needed in upper case!!!
|
||||
fav_id = "{}:{}:{}:{}".format(ssid, tid, nid, onid)
|
||||
picon_id = "1_0_{:X}_{}_{}_{}_{}_0_0_0.png".format(srv_type, ssid, tid, nid, onid)
|
||||
s_id = "1:0:{:X}:{}:{}:{}:{}:0:0:0:".format(srv_type, ssid, tid, nid, onid)
|
||||
fav_id = f"{ssid}:{tid}:{nid}:{onid}"
|
||||
picon_id = f"1_0_{srv_type:X}_{ssid}_{tid}_{nid}_{onid}_0_0_0.png"
|
||||
s_id = f"1:0:{srv_type:X}:{ssid}:{tid}:{nid}:{onid}:0:0:0:"
|
||||
|
||||
all_flags = srv[2].split(",")
|
||||
coded = CODED_ICON if list(filter(lambda x: x.startswith("C:"), all_flags)) else None
|
||||
flags = list(filter(lambda x: x.startswith("f:"), all_flags))
|
||||
hide = HIDE_ICON if flags and Flag.is_hide(int(flags[0][2:])) else None
|
||||
hide = HIDE_ICON if flags and Flag.is_hide(Flag.parse(flags[0])) else None
|
||||
locked = LOCKED_ICON if s_id in blacklist else None
|
||||
|
||||
package = list(filter(lambda x: x.startswith("p:"), all_flags))
|
||||
@@ -188,7 +188,7 @@ class LameDbReader:
|
||||
tr_type = TrType(tr_type)
|
||||
tr = tr.split(_SEP)
|
||||
service_type = SERVICE_TYPE.get(data[4], SERVICE_TYPE["-2"])
|
||||
# Removing all non printable symbols!
|
||||
# Removing all non-printable symbols!
|
||||
srv_name = "".join(c for c in srv[1] if c.isprintable())
|
||||
freq = tr[0]
|
||||
rate = tr[1]
|
||||
@@ -203,7 +203,7 @@ class LameDbReader:
|
||||
system = "DVB-S2" if len(tr) > 7 else "DVB-S"
|
||||
pos = tr[4]
|
||||
if tr_type is TrType.Terrestrial:
|
||||
system = T_SYSTEM.get(tr[9], None)
|
||||
system = T_SYSTEM.get(tr[10] if len(tr) > 10 else "0", None)
|
||||
pos = "T"
|
||||
fec = T_FEC.get(tr[3], None)
|
||||
elif tr_type is TrType.Cable:
|
||||
@@ -217,13 +217,13 @@ class LameDbReader:
|
||||
|
||||
# Formatting displayed values.
|
||||
try:
|
||||
freq = "{}".format(int(freq) // 1000)
|
||||
rate = "{}".format(int(rate) // 1000)
|
||||
freq = f"{int(freq) // 1000}"
|
||||
rate = f"{int(rate) // 1000}"
|
||||
if tr_type is TrType.Satellite:
|
||||
pos = int(pos)
|
||||
pos = "{:0.1f}{}".format(abs(pos / 10), "W" if pos < 0 else "E")
|
||||
pos = f"{abs(pos / 10):0.1f}{'W' if pos < 0 else 'E'}"
|
||||
except ValueError as e:
|
||||
log("Parse error [parse_services]: {}".format(e))
|
||||
log(f"Parse error [parse_services]: {e}")
|
||||
|
||||
s = Service(srv[2], tr_type.value, coded, srv_name, locked, hide, package, service_type, None,
|
||||
picon_id, data[0], freq, rate, pol, fec, system, pos, data_id, fav_id, transponder)
|
||||
@@ -258,18 +258,17 @@ class LameDbReader:
|
||||
tr_set = set()
|
||||
for srv in services:
|
||||
data_id = str(srv.data_id).split(_SEP)
|
||||
tr_id = "{}:{}:{}".format(data_id[1], data_id[2], data_id[3])
|
||||
tr_id = f"{data_id[1]}:{data_id[2]}:{data_id[3]}"
|
||||
if tr_id not in tr_set:
|
||||
transponder = "{}\n\t{}\n/\n".format(tr_id, srv.transponder)
|
||||
tr_lines.append(transponder)
|
||||
tr_lines.append(f"{tr_id}\n\t{srv.transponder}\n/\n")
|
||||
tr_set.add(tr_id)
|
||||
# Services
|
||||
services_lines.append("{}\n{}\n{}\n".format(srv.data_id, srv.service, srv.flags_cas))
|
||||
services_lines.append(f"{srv.data_id}\n{srv.service}\n{srv.flags_cas}\n")
|
||||
|
||||
tr_lines.sort()
|
||||
lines.extend(tr_lines)
|
||||
lines.extend(services_lines)
|
||||
lines.append("end\n" + _END_LINE)
|
||||
lines.append(f"end\n{_END_LINE}")
|
||||
|
||||
return lines
|
||||
|
||||
@@ -324,13 +323,13 @@ class LameDbWriter:
|
||||
|
||||
for srv in self._services:
|
||||
data_id = str(srv.data_id).split(_SEP)
|
||||
tr_id = "{}:{}:{}".format(data_id[1], data_id[2], data_id[3])
|
||||
tr_set.add("t:{},{}\n".format(tr_id, srv.transponder.replace(" ", ":", 1)))
|
||||
tr_id = f"{data_id[1]}:{data_id[2]}:{data_id[3]}"
|
||||
tr_set.add(f"t:{tr_id},{srv.transponder.replace(' ', ':', 1)}\n")
|
||||
# Removing empty packages
|
||||
flags = list(filter(lambda x: x != "p:", srv.flags_cas.split(",")))
|
||||
flags = ",".join(flags)
|
||||
flags = "," + flags if flags else ""
|
||||
services_lines.append("s:{},\"{}\"{}\n".format(srv.data_id, srv.service, flags))
|
||||
services_lines.append(f"s:{srv.data_id},\"{srv.service}\"{flags}\n")
|
||||
|
||||
lines.extend(sorted(tr_set))
|
||||
lines.extend(services_lines)
|
||||
|
||||
@@ -1,3 +1,31 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# The MIT License (MIT)
|
||||
#
|
||||
# Copyright (c) 2018-2021 Dmitriy Yefremov
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
# THE SOFTWARE.
|
||||
#
|
||||
# Author: Dmitriy Yefremov
|
||||
#
|
||||
|
||||
|
||||
""" Module for IPTV and streams support """
|
||||
import re
|
||||
from enum import Enum
|
||||
@@ -10,7 +38,7 @@ from app.ui.uicommons import IPTV_ICON
|
||||
|
||||
# url, description, urlkey, account, usrname, psw, s_type, iconsrc, iconsrc_b, group
|
||||
NEUTRINO_FAV_ID_FORMAT = "{}::{}::{}::{}::{}::{}::{}::{}::{}::{}"
|
||||
ENIGMA2_FAV_ID_FORMAT = " {}:0:{}:{:X}:{:X}:{:X}:{:X}:0:0:0:{}:{}\n#DESCRIPTION: {}\n"
|
||||
ENIGMA2_FAV_ID_FORMAT = " {}:{}:{}:{:X}:{:X}:{:X}:{:X}:0:0:0:{}:{}\n#DESCRIPTION: {}\n"
|
||||
MARKER_FORMAT = " 1:64:{}:0:0:0:0:0:0:0::{}\n#DESCRIPTION {}\n"
|
||||
|
||||
|
||||
@@ -51,9 +79,6 @@ def parse_m3u(path, s_type, detect_encoding=True, params=None):
|
||||
|
||||
for line in str(data, encoding=encoding, errors="ignore").splitlines():
|
||||
if line.startswith("#EXTINF"):
|
||||
inf, sep, line = line.partition(" ")
|
||||
if not line:
|
||||
line = inf
|
||||
line, sep, name = line.rpartition(",")
|
||||
|
||||
data = re.split('"', line)
|
||||
@@ -115,12 +140,12 @@ def export_to_m3u(path, bouquet, s_type):
|
||||
file.writelines(lines)
|
||||
|
||||
|
||||
def get_fav_id(url, service_name, settings_type, params=None, stream_type=None, s_type=1):
|
||||
def get_fav_id(url, name, settings_type, params=None, st_type=None, s_id=0, srv_type=1):
|
||||
""" Returns fav id depending on the profile. """
|
||||
if settings_type is SettingsType.ENIGMA_2:
|
||||
stream_type = stream_type or StreamType.NONE_TS.value
|
||||
st_type = st_type or StreamType.NONE_TS.value
|
||||
params = params or (0, 0, 0, 0)
|
||||
return ENIGMA2_FAV_ID_FORMAT.format(stream_type, s_type, *params, quote(url), service_name, service_name, None)
|
||||
return ENIGMA2_FAV_ID_FORMAT.format(st_type, s_id, srv_type, *params, quote(url), name, name, None)
|
||||
elif settings_type is SettingsType.NEUTRINO_MP:
|
||||
return NEUTRINO_FAV_ID_FORMAT.format(url, "", 0, None, None, None, None, "", "", 1)
|
||||
|
||||
|
||||
@@ -57,7 +57,7 @@ class PiconsCzDownloader:
|
||||
_PERM_URL = "https://picon.cz/download/7337"
|
||||
_BASE_URL = "https://picon.cz/download/"
|
||||
_BASE_LOGO_URL = "https://picon.cz/picon/0/"
|
||||
_HEADER = {"User-Agent": "DemonEditor/2.0.0", "Referer": ""}
|
||||
_HEADER = {"User-Agent": "DemonEditor/2.0.3", "Referer": ""}
|
||||
_LINK_PATTERN = re.compile(r"((.*)-\d+x\d+)-(.*)_by_chocholousek.7z$")
|
||||
_FILE_PATTERN = re.compile(b"\\s+(1_.*\\.png).*")
|
||||
|
||||
@@ -130,7 +130,7 @@ class PiconsCzDownloader:
|
||||
# TODO: think about https://github.com/miurahr/py7zr
|
||||
exe = "7z"
|
||||
if IS_DARWIN and GTK_PATH:
|
||||
exe = "./7z"
|
||||
exe = "./7zr"
|
||||
|
||||
if IS_LINUX and not os.path.isfile(f"/usr/bin/{exe}"):
|
||||
raise PiconsError("7-zip [7z] archiver not found!")
|
||||
@@ -219,7 +219,8 @@ class PiconsCzDownloader:
|
||||
"picontransparentdark": "td220",
|
||||
"piconoled": "o96",
|
||||
"piconblack80": "b50",
|
||||
"piconblack3d": "b50"
|
||||
"piconblack3d": "b50",
|
||||
"piconwin11": "win11220"
|
||||
}
|
||||
|
||||
def get_name_map(self):
|
||||
|
||||
@@ -539,7 +539,7 @@ class ServicesParser(HTMLParser):
|
||||
|
||||
tr = tr[0]
|
||||
s_pos, freq, pol, sys, mod, sr_fec = tr[0].text, tr[2].text, tr[3].text, tr[6].text, tr[7].text, tr[8].text
|
||||
tid, nid = tr[10].text, tr[11].text
|
||||
nid, tid = tr[10].text, tr[11].text
|
||||
|
||||
pos = sat_position
|
||||
if not sat_position:
|
||||
|
||||
@@ -46,7 +46,7 @@ from app.ui.uicommons import show_notification
|
||||
_YT_PATTERN = re.compile(r"https://www.youtube.com/.+(?:v=)([\w-]{11}).*")
|
||||
_YT_LIST_PATTERN = re.compile(r"https://www.youtube.com/.+?(?:list=)([\w-]{18,})?.*")
|
||||
_YT_VIDEO_PATTERN = re.compile(r"https://r\d+---sn-[\w]{10}-[\w]{3,5}.googlevideo.com/videoplayback?.*")
|
||||
_HEADERS = {"User-Agent": "Mozilla/5.0 (X11; Linux i586; rv:31.0) Gecko/20100101 Firefox/69.0",
|
||||
_HEADERS = {"User-Agent": "Mozilla/5.0 (Linux x86_64; rv:92.0) Gecko/20100101 Firefox/92.0",
|
||||
"DNT": "1",
|
||||
"Accept-Encoding": "gzip, deflate"}
|
||||
|
||||
@@ -109,6 +109,8 @@ class YouTube:
|
||||
if self._settings.enable_yt_dl and url:
|
||||
if not self._yt_dl:
|
||||
self._yt_dl = YouTubeDL.get_instance(self._settings, self._callback)
|
||||
if not self._yt_dl:
|
||||
raise YouTubeException("youtube-dl initialization error.")
|
||||
return self._yt_dl.get_yt_link(url, skip_errors)
|
||||
|
||||
return self.get_yt_link_by_id(video_id)
|
||||
@@ -130,7 +132,7 @@ class YouTube:
|
||||
try:
|
||||
resp = json.loads(player_resp)
|
||||
except JSONDecodeError as e:
|
||||
log("{}: Parsing player response error: {}".format(__class__.__name__, e))
|
||||
log(f"{__class__.__name__}: Parsing player response error: {e}")
|
||||
else:
|
||||
det = resp.get("videoDetails", None)
|
||||
title = det.get("title", None) if det else None
|
||||
@@ -162,14 +164,19 @@ class YouTube:
|
||||
""" Returns tuple from the playlist header and list of tuples (title, video id). """
|
||||
if self._settings.enable_yt_dl and url:
|
||||
try:
|
||||
if not self._yt_dl:
|
||||
raise YouTubeException("youtube-dl is not initialized!")
|
||||
|
||||
self._yt_dl.update_options({"noplaylist": False, "extract_flat": True})
|
||||
info = self._yt_dl.get_info(url, skip_errors=False)
|
||||
if "url" in info:
|
||||
info = self._yt_dl.get_info(info.get("url"), skip_errors=False)
|
||||
|
||||
return info.get("title", ""), [(e.get("title", ""), e.get("id", "")) for e in info.get("entries", [])]
|
||||
finally:
|
||||
# Restoring default options
|
||||
self._yt_dl.update_options({"noplaylist": True, "extract_flat": False})
|
||||
if self._yt_dl:
|
||||
self._yt_dl.update_options({"noplaylist": True, "extract_flat": False})
|
||||
|
||||
return PlayListParser.get_yt_playlist(list_id)
|
||||
|
||||
@@ -218,7 +225,7 @@ class PlayListParser(HTMLParser):
|
||||
self._is_script = False
|
||||
|
||||
def error(self, message):
|
||||
log("{} Parsing error: {}".format(__class__.__name__, message))
|
||||
log(f"{__class__.__name__} Parsing error: {message}")
|
||||
|
||||
@property
|
||||
def header(self):
|
||||
@@ -259,7 +266,7 @@ class YouTubeDL:
|
||||
"cookiefile": "cookies.txt"} # File name where cookies should be read from and dumped to.
|
||||
|
||||
def __init__(self, settings, callback):
|
||||
self._path = "{}tools{}".format(settings.default_data_path, SEP)
|
||||
self._path = f"{settings.default_data_path}tools{SEP}"
|
||||
self._update = settings.enable_yt_dl_update
|
||||
self._supported = {"22", "18"}
|
||||
self._dl = None
|
||||
@@ -276,7 +283,7 @@ class YouTubeDL:
|
||||
return cls._DL_INSTANCE
|
||||
|
||||
def init(self):
|
||||
if not os.path.isfile("{}youtube_dl{}version.py".format(self._path, SEP)):
|
||||
if not os.path.isfile(f"{self._path}youtube_dl{SEP}version.py"):
|
||||
self.get_latest_release()
|
||||
|
||||
if self._path not in sys.path:
|
||||
@@ -293,12 +300,17 @@ class YouTubeDL:
|
||||
except ImportError as e:
|
||||
log("YouTubeDLHelper error: {}".format(str(e)))
|
||||
else:
|
||||
if self._path not in youtube_dl.__file__:
|
||||
msg = "Another version of youtube-dl was found on your system!"
|
||||
log(msg)
|
||||
raise YouTubeException(msg)
|
||||
|
||||
if self._update:
|
||||
if hasattr(youtube_dl.version, "__version__"):
|
||||
l_ver = self.get_last_release_id()
|
||||
cur_ver = youtube_dl.version.__version__
|
||||
if l_ver and youtube_dl.version.__version__ < l_ver:
|
||||
msg = "youtube-dl has new release!\nCurrent: {}. Last: {}.".format(cur_ver, l_ver)
|
||||
msg = f"youtube-dl has new release!\nCurrent: {cur_ver}. Last: {l_ver}."
|
||||
show_notification(msg)
|
||||
log(msg)
|
||||
self._callback(msg, False)
|
||||
@@ -318,7 +330,7 @@ class YouTubeDL:
|
||||
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))
|
||||
log(f"YouTubeDLHelper error [get last release id]: {e}")
|
||||
|
||||
def get_latest_release(self):
|
||||
try:
|
||||
@@ -329,6 +341,9 @@ class YouTubeDL:
|
||||
r = json.loads(resp.read().decode("utf-8"))
|
||||
zip_url = r.get("zipball_url", None)
|
||||
if zip_url:
|
||||
if os.path.isdir(self._path):
|
||||
shutil.rmtree(self._path)
|
||||
|
||||
zip_file = self._path + "yt.zip"
|
||||
os.makedirs(os.path.dirname(self._path), exist_ok=True)
|
||||
f_name, headers = urlretrieve(zip_url, filename=zip_file)
|
||||
@@ -336,25 +351,22 @@ class YouTubeDL:
|
||||
import zipfile
|
||||
|
||||
with zipfile.ZipFile(f_name) as arch:
|
||||
|
||||
if os.path.isdir(self._path):
|
||||
shutil.rmtree(self._path)
|
||||
else:
|
||||
os.makedirs(os.path.dirname(self._path), exist_ok=True)
|
||||
|
||||
for info in arch.infolist():
|
||||
pref, sep, f = info.filename.partition("/youtube_dl/")
|
||||
if sep:
|
||||
arch.extract(info.filename)
|
||||
shutil.move(info.filename, "{}{}{}".format(self._path, sep, f))
|
||||
shutil.move(info.filename, f"{self._path}{sep}{f}")
|
||||
shutil.rmtree(pref)
|
||||
msg = "Getting the last youtube-dl release is done!"
|
||||
show_notification(msg)
|
||||
log(msg)
|
||||
self._callback(msg, False)
|
||||
return True
|
||||
|
||||
if os.path.isfile(zip_file):
|
||||
os.remove(zip_file)
|
||||
return True
|
||||
except URLError as e:
|
||||
log("YouTubeDLHelper error: {}".format(e))
|
||||
log(f"YouTubeDLHelper error: {e}")
|
||||
raise YouTubeException(e)
|
||||
finally:
|
||||
self._is_update_process = False
|
||||
@@ -377,10 +389,10 @@ class YouTubeDL:
|
||||
try:
|
||||
return self._dl.extract_info(url, download=False)
|
||||
except URLError as e:
|
||||
log(str(e))
|
||||
log(f"YouTubeDLHelper error [get info]: {e}")
|
||||
raise YouTubeException(e)
|
||||
except self._DownloadError as e:
|
||||
log(str(e))
|
||||
log(f"YouTubeDLHelper error [get info]: {e}")
|
||||
if not skip_errors:
|
||||
raise YouTubeException(e)
|
||||
|
||||
|
||||
@@ -134,7 +134,7 @@
|
||||
</item>
|
||||
</section>
|
||||
</submenu>
|
||||
<submenu>
|
||||
<submenu id="tools_menu">
|
||||
<attribute name="label" translatable="yes">Tools</attribute>
|
||||
<attribute name="action">app.hide_menu_bar</attribute>
|
||||
<attribute name="hidden-when">action-disabled</attribute>
|
||||
@@ -147,6 +147,10 @@
|
||||
<attribute name="label" translatable="yes">Telnet</attribute>
|
||||
<attribute name="action">app.on_telnet_show</attribute>
|
||||
</item>
|
||||
<item>
|
||||
<attribute name="label" translatable="yes">Logs</attribute>
|
||||
<attribute name="action">app.on_logs_show</attribute>
|
||||
</item>
|
||||
</section>
|
||||
</submenu>
|
||||
<submenu>
|
||||
@@ -333,6 +337,10 @@
|
||||
<attribute name="label" translatable="yes">Telnet</attribute>
|
||||
<attribute name="action">app.on_telnet_show</attribute>
|
||||
</item>
|
||||
<item>
|
||||
<attribute name="label" translatable="yes">Logs</attribute>
|
||||
<attribute name="action">app.on_logs_show</attribute>
|
||||
</item>
|
||||
</section>
|
||||
</submenu>
|
||||
<submenu>
|
||||
|
||||
@@ -38,7 +38,7 @@ from app.commons import run_idle
|
||||
from app.settings import SettingsType, SEP
|
||||
from app.ui.dialogs import show_dialog, DialogType, get_builder
|
||||
from app.ui.main_helper import append_text_to_tview
|
||||
from .uicommons import Gtk, Gdk, UI_RESOURCES_PATH, KeyboardKey, MOD_MASK
|
||||
from .uicommons import Gtk, Gdk, UI_RESOURCES_PATH, KeyboardKey, MOD_MASK, IS_GNOME_SESSION
|
||||
|
||||
|
||||
class RestoreType(Enum):
|
||||
@@ -74,6 +74,24 @@ class BackupDialog:
|
||||
self._info_check_button = builder.get_object("info_check_button")
|
||||
self._info_bar = builder.get_object("info_bar")
|
||||
self._message_label = builder.get_object("message_label")
|
||||
|
||||
if IS_GNOME_SESSION:
|
||||
header_bar = Gtk.HeaderBar(visible=True, show_close_button=True)
|
||||
self._dialog_window.set_titlebar(header_bar)
|
||||
|
||||
button_box = builder.get_object("main_button_box")
|
||||
button_box.set_margin_top(0)
|
||||
button_box.set_margin_bottom(0)
|
||||
button_box.set_margin_left(0)
|
||||
button_box.reparent(header_bar)
|
||||
|
||||
ch_button = builder.get_object("info_check_button")
|
||||
ch_button.set_margin_right(0)
|
||||
h_bar = builder.get_object("header_bar")
|
||||
h_bar.remove(ch_button)
|
||||
h_bar.set_visible(False)
|
||||
header_bar.pack_end(ch_button)
|
||||
|
||||
# Setting the last size of the dialog window if it was saved
|
||||
window_size = self._settings.get("backup_tool_window_size")
|
||||
if window_size:
|
||||
|
||||
@@ -124,7 +124,6 @@ Author: Dmitriy Yefremov
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<property name="spacing">5</property>
|
||||
<child>
|
||||
<object class="GtkBox" id="header_bar">
|
||||
<property name="visible">True</property>
|
||||
@@ -232,6 +231,7 @@ Author: Dmitriy Yefremov
|
||||
<object class="GtkPaned" id="main_paned">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="margin_top">5</property>
|
||||
<property name="margin_left">5</property>
|
||||
<property name="margin_right">5</property>
|
||||
<property name="margin_bottom">5</property>
|
||||
|
||||
@@ -598,18 +598,6 @@ Author: Dmitriy Yefremov
|
||||
<property name="margin_bottom">5</property>
|
||||
<property name="row_spacing">5</property>
|
||||
<property name="column_spacing">5</property>
|
||||
<child>
|
||||
<object class="GtkLabel" id="timer_service_label">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">Service:</property>
|
||||
<property name="xalign">0</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">3</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="timer_description_label">
|
||||
<property name="visible">True</property>
|
||||
@@ -634,17 +622,6 @@ Author: Dmitriy Yefremov
|
||||
<property name="top_attach">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkEntry" id="timer_service_entry">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="primary_icon_name">document-edit-symbolic</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">3</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkEntry" id="timer_desc_entry">
|
||||
<property name="visible">True</property>
|
||||
@@ -690,6 +667,115 @@ Author: Dmitriy Yefremov
|
||||
<property name="top_attach">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="timer_service_ref_label">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">Service reference:</property>
|
||||
<property name="xalign">0</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">9</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkEntry" id="timer_service_ref_entry">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="editable">False</property>
|
||||
<property name="width_chars">25</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">9</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="timer_event_id_label">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">Event ID:</property>
|
||||
<property name="xalign">0</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">10</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkEntry" id="timer_event_id_entry">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="editable">False</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">10</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="timer_location_label">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">Location:</property>
|
||||
<property name="xalign">0</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">12</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkEntry" id="timer_location_entry">
|
||||
<property name="visible">True</property>
|
||||
<property name="sensitive">False</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="primary_icon_name">document-edit-symbolic</property>
|
||||
<property name="placeholder_text" translatable="yes">Default</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">12</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkBox" id="timer_dialog_location_box">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="halign">end</property>
|
||||
<property name="margin_top">10</property>
|
||||
<property name="spacing">5</property>
|
||||
<child>
|
||||
<object class="GtkImage" id="timer_location_image">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="icon_name">document-edit-symbolic</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkSwitch" id="timer_location_switch">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
<property name="pack_type">end</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">11</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="timer_begins_label">
|
||||
<property name="visible">True</property>
|
||||
@@ -699,7 +785,7 @@ Author: Dmitriy Yefremov
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">4</property>
|
||||
<property name="top_attach">3</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
@@ -724,7 +810,7 @@ Author: Dmitriy Yefremov
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">4</property>
|
||||
<property name="top_attach">3</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
@@ -736,7 +822,7 @@ Author: Dmitriy Yefremov
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">5</property>
|
||||
<property name="top_attach">4</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
@@ -761,7 +847,7 @@ Author: Dmitriy Yefremov
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">5</property>
|
||||
<property name="top_attach">4</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
@@ -773,7 +859,7 @@ Author: Dmitriy Yefremov
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">6</property>
|
||||
<property name="top_attach">5</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
@@ -954,7 +1040,7 @@ Author: Dmitriy Yefremov
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">6</property>
|
||||
<property name="top_attach">5</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
@@ -966,7 +1052,7 @@ Author: Dmitriy Yefremov
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">7</property>
|
||||
<property name="top_attach">6</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
@@ -981,7 +1067,7 @@ Author: Dmitriy Yefremov
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">7</property>
|
||||
<property name="top_attach">6</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
@@ -993,7 +1079,7 @@ Author: Dmitriy Yefremov
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">8</property>
|
||||
<property name="top_attach">7</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
@@ -1010,115 +1096,30 @@ Author: Dmitriy Yefremov
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">7</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="timer_service_label">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">Service:</property>
|
||||
<property name="xalign">0</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">8</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="timer_service_ref_label">
|
||||
<object class="GtkEntry" id="timer_service_entry">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">Service reference:</property>
|
||||
<property name="xalign">0</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">9</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkEntry" id="timer_service_ref_entry">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="editable">False</property>
|
||||
<property name="width_chars">25</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">9</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="timer_event_id_label">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">Event ID:</property>
|
||||
<property name="xalign">0</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">10</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkEntry" id="timer_event_id_entry">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="editable">False</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">10</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="timer_location_label">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">Location:</property>
|
||||
<property name="xalign">0</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">12</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkEntry" id="timer_location_entry">
|
||||
<property name="visible">True</property>
|
||||
<property name="sensitive">False</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="primary_icon_name">document-edit-symbolic</property>
|
||||
<property name="placeholder_text" translatable="yes">Default</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">12</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkBox" id="timer_dialog_location_box">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="halign">end</property>
|
||||
<property name="spacing">5</property>
|
||||
<child>
|
||||
<object class="GtkImage" id="timer_location_image">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="icon_name">document-edit-symbolic</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkSwitch" id="timer_location_switch">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
<property name="pack_type">end</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">11</property>
|
||||
<property name="top_attach">8</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
|
||||
@@ -36,7 +36,7 @@ from urllib.parse import quote
|
||||
from gi.repository import GLib
|
||||
|
||||
from .dialogs import get_builder, show_dialog, DialogType, get_message
|
||||
from .uicommons import Gtk, Gdk, UI_RESOURCES_PATH, Page, Column, KeyboardKey
|
||||
from .uicommons import Gtk, Gdk, UI_RESOURCES_PATH, Page, Column, KeyboardKey, IS_GNOME_SESSION
|
||||
from ..commons import run_task, run_with_delay, log, run_idle
|
||||
from ..connections import HttpAPI, UtfFTP
|
||||
from ..eparser.ecommons import BqServiceType
|
||||
@@ -159,7 +159,7 @@ class TimerTool(Gtk.Box):
|
||||
|
||||
class TimerDialog(Gtk.Dialog):
|
||||
def __init__(self, parent, action=None, timer_data=None, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
super().__init__(use_header_bar=IS_GNOME_SESSION, *args, **kwargs)
|
||||
|
||||
self._action = action or TimerTool.TimerAction.ADD
|
||||
self._timer_data = timer_data or {}
|
||||
@@ -213,7 +213,7 @@ class TimerTool(Gtk.Box):
|
||||
self._timer_desc_entry.drag_dest_unset()
|
||||
self._timer_service_entry.drag_dest_unset()
|
||||
|
||||
self.add_buttons(get_message("Cancel"), Gtk.ResponseType.CLOSE, get_message("Save"), Gtk.ResponseType.OK)
|
||||
self.add_buttons(get_message("Cancel"), Gtk.ResponseType.CANCEL, get_message("Save"), Gtk.ResponseType.OK)
|
||||
self.get_content_area().pack_start(builder.get_object("timer_dialog_frame"), True, True, 5)
|
||||
|
||||
if self._action is TimerTool.TimerAction.ADD:
|
||||
@@ -716,10 +716,14 @@ class RecordingsTool(Gtk.Box):
|
||||
|
||||
for f in files:
|
||||
f_data = f.split()
|
||||
if len(f_data) < 9:
|
||||
log(f"{__class__.__name__}. Folder data parsing error. [{f}]")
|
||||
continue
|
||||
|
||||
f_type = f_data[0][0]
|
||||
|
||||
if f_type == "d":
|
||||
model.append((self._icon, f_data[-1], self._ftp.pwd()))
|
||||
model.append((self._icon, " ".join(f_data[8:]), self._ftp.pwd()))
|
||||
|
||||
def on_path_activated(self, view, path, column):
|
||||
row = view.get_model()[path][:]
|
||||
|
||||
@@ -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">2.0.0 Alpha</property>
|
||||
<property name="version">2.0.3 Beta</property>
|
||||
<property name="copyright">2018-2021 Dmitriy Yefremov
|
||||
</property>
|
||||
<property name="comments" translatable="yes">Enigma2 channel and satellite list editor.</property>
|
||||
@@ -91,51 +91,36 @@ Author: Dmitriy Yefremov
|
||||
<property name="type_hint">utility</property>
|
||||
<property name="skip_taskbar_hint">True</property>
|
||||
<property name="skip_pager_hint">True</property>
|
||||
<child type="titlebar">
|
||||
<placeholder/>
|
||||
<child type="action">
|
||||
<object class="GtkButton" id="input_dialog_cancel_button">
|
||||
<property name="label" translatable="yes">Cancel</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="valign">center</property>
|
||||
</object>
|
||||
</child>
|
||||
<child type="action">
|
||||
<object class="GtkButton" id="input_dialog_ok_button">
|
||||
<property name="label" translatable="yes">OK</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="valign">center</property>
|
||||
<accelerator key="Return" signal="activate"/>
|
||||
</object>
|
||||
</child>
|
||||
<child internal-child="vbox">
|
||||
<object class="GtkBox">
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_left">5</property>
|
||||
<property name="margin_right">5</property>
|
||||
<property name="margin_top">4</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<property name="spacing">2</property>
|
||||
<child internal-child="action_area">
|
||||
<object class="GtkButtonBox">
|
||||
<property name="can_focus">False</property>
|
||||
<property name="valign">center</property>
|
||||
<property name="layout_style">end</property>
|
||||
<child>
|
||||
<object class="GtkButton" id="input_dialog_cancel_button">
|
||||
<property name="label" translatable="yes">Cancel</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="valign">center</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="input_dialog_ok_button">
|
||||
<property name="label" translatable="yes">OK</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="valign">center</property>
|
||||
<accelerator key="Return" signal="activate"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
@@ -147,10 +132,10 @@ Author: Dmitriy Yefremov
|
||||
<object class="GtkEntry" id="input_entry">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="margin_left">2</property>
|
||||
<property name="margin_right">2</property>
|
||||
<property name="margin_top">2</property>
|
||||
<property name="margin_bottom">2</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="primary_icon_name">document-edit-symbolic</property>
|
||||
<property name="primary_icon_activatable">False</property>
|
||||
<property name="secondary_icon_activatable">False</property>
|
||||
|
||||
@@ -225,8 +225,8 @@ Author: Dmitriy Yefremov
|
||||
<object class="GtkFrame" id="main_settings_box_frame">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_left">10</property>
|
||||
<property name="margin_right">10</property>
|
||||
<property name="margin_left">5</property>
|
||||
<property name="margin_right">5</property>
|
||||
<property name="margin_top">5</property>
|
||||
<property name="label_xalign">0.019999999552965164</property>
|
||||
<property name="shadow_type">in</property>
|
||||
@@ -239,12 +239,13 @@ Author: Dmitriy Yefremov
|
||||
<property name="margin_top">10</property>
|
||||
<property name="margin_bottom">10</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<property name="spacing">10</property>
|
||||
<child>
|
||||
<object class="GtkGrid" id="main_settings_bo">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="row_spacing">2</property>
|
||||
<property name="column_spacing">2</property>
|
||||
<property name="row_spacing">5</property>
|
||||
<property name="column_spacing">10</property>
|
||||
<property name="column_homogeneous">True</property>
|
||||
<child>
|
||||
<object class="GtkLabel" id="ip_label">
|
||||
@@ -310,9 +311,8 @@ 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_left">10</property>
|
||||
<property name="margin_right">10</property>
|
||||
<child>
|
||||
<object class="GtkCheckButton" id="remove_unused_check_button">
|
||||
<property name="label" translatable="yes">Remove unused bouquets</property>
|
||||
@@ -384,7 +384,7 @@ Author: Dmitriy Yefremov
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">2</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
@@ -438,54 +438,85 @@ Author: Dmitriy Yefremov
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
<property name="position">6</property>
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkExpander" id="expander">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="margin_left">1</property>
|
||||
<property name="margin_right">1</property>
|
||||
<property name="margin_bottom">1</property>
|
||||
<property name="resize_toplevel">True</property>
|
||||
<object class="GtkFrame" id="log_bar_frame">
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_left">5</property>
|
||||
<property name="margin_right">5</property>
|
||||
<property name="margin_bottom">5</property>
|
||||
<property name="label_xalign">0</property>
|
||||
<property name="shadow_type">in</property>
|
||||
<child>
|
||||
<object class="GtkScrolledWindow" id="scrolled_window">
|
||||
<property name="height_request">120</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="shadow_type">in</property>
|
||||
<child>
|
||||
<object class="GtkTextView" id="text_view">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="editable">False</property>
|
||||
<property name="left_margin">5</property>
|
||||
<property name="right_margin">5</property>
|
||||
<object class="GtkInfoBar" id="log_bar">
|
||||
<property name="can_focus">False</property>
|
||||
<property name="baseline_position">bottom</property>
|
||||
<property name="message_type">other</property>
|
||||
<property name="show_close_button">True</property>
|
||||
<child internal-child="action_area">
|
||||
<object class="GtkButtonBox" id="log_bar_button_box">
|
||||
<property name="can_focus">False</property>
|
||||
<property name="layout_style">end</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child internal-child="content_area">
|
||||
<object class="GtkBox" id="log_bar_box">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<child>
|
||||
<object class="GtkScrolledWindow" id="scrolled_window">
|
||||
<property name="height_request">100</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="shadow_type">in</property>
|
||||
<child>
|
||||
<object class="GtkTextView" id="text_view">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="editable">False</property>
|
||||
<property name="left_margin">5</property>
|
||||
<property name="right_margin">5</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child type="label">
|
||||
<object class="GtkLabel" id="expander_label">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">Extra:</property>
|
||||
</object>
|
||||
<child type="label_item">
|
||||
<placeholder/>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">7</property>
|
||||
<property name="position">3</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkInfoBar" id="info_bar">
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_left">1</property>
|
||||
<property name="margin_right">1</property>
|
||||
<property name="margin_bottom">1</property>
|
||||
<property name="show_close_button">True</property>
|
||||
<signal name="response" handler="on_info_bar_close" swapped="no"/>
|
||||
<child internal-child="action_area">
|
||||
|
||||
@@ -35,7 +35,7 @@ from app.connections import download_data, DownloadType, upload_data
|
||||
from app.settings import SettingsType
|
||||
from app.ui.backup import backup_data, restore_data
|
||||
from app.ui.main_helper import append_text_to_tview
|
||||
from app.ui.settings_dialog import show_settings_dialog
|
||||
from app.ui.settings_dialog import SettingsDialog
|
||||
from .dialogs import show_dialog, DialogType, get_message, get_builder
|
||||
from .uicommons import Gtk, UI_RESOURCES_PATH
|
||||
|
||||
@@ -59,10 +59,6 @@ class DownloadDialog:
|
||||
|
||||
self._dialog_window = builder.get_object("download_dialog_window")
|
||||
self._dialog_window.set_transient_for(transient)
|
||||
self._info_bar = builder.get_object("info_bar")
|
||||
self._message_label = builder.get_object("info_bar_message_label")
|
||||
self._text_view = builder.get_object("text_view")
|
||||
self._expander = builder.get_object("expander")
|
||||
self._host_entry = builder.get_object("host_entry")
|
||||
self._data_path_entry = builder.get_object("data_path_entry")
|
||||
self._remove_unused_check_button = builder.get_object("remove_unused_check_button")
|
||||
@@ -73,6 +69,13 @@ class DownloadDialog:
|
||||
self._use_http_switch = builder.get_object("use_http_switch")
|
||||
self._http_radio_button = builder.get_object("http_radio_button")
|
||||
self._profile_combo_box = builder.get_object("profile_combo_box")
|
||||
# Info.
|
||||
self._info_bar = builder.get_object("info_bar")
|
||||
self._message_label = builder.get_object("info_bar_message_label")
|
||||
self._text_view = builder.get_object("text_view")
|
||||
self._log_bar = builder.get_object("log_bar")
|
||||
self._log_bar.bind_property("visible", builder.get_object("log_bar_frame"), "visible")
|
||||
self._log_bar.connect("response", lambda b, r: b.set_visible(False))
|
||||
|
||||
self.init_settings()
|
||||
|
||||
@@ -120,8 +123,9 @@ class DownloadDialog:
|
||||
self._dialog_window.destroy()
|
||||
|
||||
def on_settings(self, item):
|
||||
response = show_settings_dialog(self._dialog_window, self._settings)
|
||||
if response != Gtk.ResponseType.CANCEL:
|
||||
dialog = SettingsDialog(self._dialog_window, self._settings)
|
||||
dialog.show()
|
||||
if dialog.is_updated():
|
||||
self._s_type = self._settings.setting_type
|
||||
self.update_profiles()
|
||||
gen = self._update_settings_callback()
|
||||
@@ -147,7 +151,7 @@ class DownloadDialog:
|
||||
@run_task
|
||||
def download(self, download, d_type):
|
||||
""" Download/upload data from/to receiver """
|
||||
GLib.idle_add(self._expander.set_expanded, True)
|
||||
GLib.idle_add(self._log_bar.set_visible, True)
|
||||
self.clear_output()
|
||||
backup, backup_src, data_path = self._settings.backup_before_downloading, None, None
|
||||
|
||||
|
||||
@@ -158,6 +158,8 @@ Author: Dmitriy Yefremov
|
||||
<columns>
|
||||
<!-- column-name service -->
|
||||
<column type="gchararray"/>
|
||||
<!-- column-name pos -->
|
||||
<column type="gchararray"/>
|
||||
<!-- column-name service_id -->
|
||||
<column type="gchararray"/>
|
||||
</columns>
|
||||
@@ -671,7 +673,7 @@ Author: Dmitriy Yefremov
|
||||
<property name="image">filter_image</property>
|
||||
<property name="always_show_image">True</property>
|
||||
<signal name="toggled" handler="on_filter_toggled" swapped="no"/>
|
||||
<accelerator key="f" signal="clicked" modifiers="GDK_SHIFT_MASK | GDK_CONTROL_MASK"/>
|
||||
<accelerator key="f" signal="clicked" modifiers="GDK_SHIFT_MASK | Primary"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
@@ -851,7 +853,6 @@ Author: Dmitriy Yefremov
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_left">5</property>
|
||||
<property name="margin_right">5</property>
|
||||
<property name="margin_bottom">5</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<object class="GtkScrolledWindow">
|
||||
@@ -868,22 +869,22 @@ Author: Dmitriy Yefremov
|
||||
<signal name="button-press-event" handler="on_popup_menu" object="source_popup_menu" swapped="no"/>
|
||||
<signal name="drag-begin" handler="on_drag_begin" swapped="no"/>
|
||||
<signal name="drag-data-get" handler="on_drag_data_get" swapped="no"/>
|
||||
<signal name="key-release-event" handler="on_key_release" swapped="no"/>
|
||||
<signal name="key-press-event" handler="on_key_press" swapped="no"/>
|
||||
<child internal-child="selection">
|
||||
<object class="GtkTreeSelection"/>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkTreeViewColumn" id="service_column">
|
||||
<property name="resizable">True</property>
|
||||
<property name="sizing">autosize</property>
|
||||
<property name="min_width">50</property>
|
||||
<property name="title" translatable="yes">Service</property>
|
||||
<property name="expand">True</property>
|
||||
<property name="alignment">0.5</property>
|
||||
<property name="sort_column_id">0</property>
|
||||
<child>
|
||||
<object class="GtkCellRendererText" id="source_service_cellrenderertext">
|
||||
<object class="GtkCellRendererText" id="source_service_renderertext">
|
||||
<property name="xpad">5</property>
|
||||
<property name="ellipsize">end</property>
|
||||
</object>
|
||||
<attributes>
|
||||
<attribute name="text">0</attribute>
|
||||
@@ -892,15 +893,14 @@ Author: Dmitriy Yefremov
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkTreeViewColumn" id="ref_column">
|
||||
<property name="resizable">True</property>
|
||||
<property name="sizing">autosize</property>
|
||||
<property name="min_width">50</property>
|
||||
<property name="title" translatable="yes">Reference</property>
|
||||
<property name="expand">True</property>
|
||||
<object class="GtkTreeViewColumn" id="pos_column">
|
||||
<property name="min_width">70</property>
|
||||
<property name="title" translatable="yes">Pos</property>
|
||||
<property name="alignment">0.5</property>
|
||||
<property name="sort_column_id">1</property>
|
||||
<child>
|
||||
<object class="GtkCellRendererText" id="source_reference_cellrenderertext">
|
||||
<object class="GtkCellRendererText" id="source_pos_renderertext">
|
||||
<property name="xpad">5</property>
|
||||
<property name="xalign">0.50999999046325684</property>
|
||||
</object>
|
||||
<attributes>
|
||||
@@ -909,6 +909,23 @@ Author: Dmitriy Yefremov
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkTreeViewColumn" id="ref_column">
|
||||
<property name="resizable">True</property>
|
||||
<property name="min_width">50</property>
|
||||
<property name="title" translatable="yes">Reference</property>
|
||||
<property name="alignment">0.5</property>
|
||||
<child>
|
||||
<object class="GtkCellRendererText" id="source_reference_renderertext">
|
||||
<property name="xpad">10</property>
|
||||
<property name="xalign">0.50999999046325684</property>
|
||||
</object>
|
||||
<attributes>
|
||||
<attribute name="text">2</attribute>
|
||||
</attributes>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
@@ -1039,7 +1056,6 @@ Author: Dmitriy Yefremov
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_left">5</property>
|
||||
<property name="margin_right">5</property>
|
||||
<property name="margin_bottom">5</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<object class="GtkScrolledWindow" id="fav_scrolled_window">
|
||||
@@ -1056,7 +1072,7 @@ Author: Dmitriy Yefremov
|
||||
<property name="tooltip_column">9</property>
|
||||
<signal name="button-press-event" handler="on_bouquet_popup_menu" object="bouquet_popup_menu" swapped="no"/>
|
||||
<signal name="drag-data-received" handler="on_drag_data_received" swapped="no"/>
|
||||
<signal name="key-release-event" handler="on_key_release" swapped="no"/>
|
||||
<signal name="key-release-event" handler="on_key_press" swapped="no"/>
|
||||
<child internal-child="selection">
|
||||
<object class="GtkTreeSelection">
|
||||
<property name="mode">multiple</property>
|
||||
|
||||
@@ -44,7 +44,7 @@ from app.settings import SEP
|
||||
from app.tools.epg import EPG, ChannelsParser
|
||||
from app.ui.dialogs import get_message, show_dialog, DialogType, get_builder
|
||||
from .main_helper import on_popup_menu, update_entry_data
|
||||
from .uicommons import Gtk, Gdk, UI_RESOURCES_PATH, Column, EPG_ICON, KeyboardKey
|
||||
from .uicommons import Gtk, Gdk, UI_RESOURCES_PATH, Column, EPG_ICON, KeyboardKey, IS_GNOME_SESSION
|
||||
|
||||
|
||||
class RefsSource(Enum):
|
||||
@@ -80,7 +80,7 @@ class EpgDialog:
|
||||
"on_enable_filtering_switch": self.on_enable_filtering_switch,
|
||||
"on_update_on_start_switch": self.on_update_on_start_switch,
|
||||
"on_field_icon_press": self.on_field_icon_press,
|
||||
"on_key_release": self.on_key_release}
|
||||
"on_key_press": self.on_key_press}
|
||||
|
||||
self._services = {}
|
||||
self._ex_services = services
|
||||
@@ -93,7 +93,6 @@ class EpgDialog:
|
||||
self._use_web_source = False
|
||||
self._update_epg_data_on_start = False
|
||||
self._refs_source = RefsSource.SERVICES
|
||||
self._show_tooltips = True
|
||||
self._download_xml_is_active = False
|
||||
|
||||
builder = get_builder(UI_RESOURCES_PATH + "epg.glade", handlers)
|
||||
@@ -132,6 +131,17 @@ class EpgDialog:
|
||||
self._epg_dat_stb_path_entry = builder.get_object("epg_dat_stb_path_entry")
|
||||
self._update_on_start_switch = builder.get_object("update_on_start_switch")
|
||||
self._epg_dat_source_box = builder.get_object("epg_dat_source_box")
|
||||
|
||||
if IS_GNOME_SESSION:
|
||||
header_bar = Gtk.HeaderBar(visible=True, show_close_button=True, title="EPG",
|
||||
subtitle=get_message("List configuration"))
|
||||
self._dialog.set_titlebar(header_bar)
|
||||
builder.get_object("left_action_box").reparent(header_bar)
|
||||
right_box = builder.get_object("right_action_box")
|
||||
builder.get_object("main_actions_box").remove(right_box)
|
||||
header_bar.pack_end(right_box)
|
||||
builder.get_object("toolbar_box").set_visible(False)
|
||||
|
||||
# Setting the last size of the dialog window
|
||||
window_size = self._settings.get("epg_tool_window_size")
|
||||
if window_size:
|
||||
@@ -188,14 +198,14 @@ class EpgDialog:
|
||||
try:
|
||||
self.download_epg_from_stb()
|
||||
except OSError as e:
|
||||
self.show_info_message("Download epg.dat file error: {}".format(e), Gtk.MessageType.ERROR)
|
||||
self.show_info_message(f"Download epg.dat file error: {e}", Gtk.MessageType.ERROR)
|
||||
return
|
||||
yield True
|
||||
|
||||
try:
|
||||
refs = EPG.get_epg_refs(self._epg_dat_path_entry.get_text() + "epg.dat")
|
||||
except FileNotFoundError as e:
|
||||
self.show_info_message("Read data error: {}".format(e), Gtk.MessageType.ERROR)
|
||||
self.show_info_message(f"Read data error: {e}", Gtk.MessageType.ERROR)
|
||||
return
|
||||
yield True
|
||||
|
||||
@@ -225,7 +235,7 @@ class EpgDialog:
|
||||
s_types = (BqServiceType.MARKER.value, BqServiceType.IPTV.value)
|
||||
filtered = filter(None, [srvs.get(ref) for ref in refs]) if refs else filter(
|
||||
lambda s: s.service_type not in s_types, self._ex_services.values())
|
||||
list(map(self._services_model.append, map(lambda s: (s.service, s.fav_id), filtered)))
|
||||
list(map(self._services_model.append, map(lambda s: (s.service, s.pos, s.fav_id), filtered)))
|
||||
self.update_source_count_info()
|
||||
|
||||
def init_xml_source(self, refs):
|
||||
@@ -274,7 +284,7 @@ class EpgDialog:
|
||||
|
||||
path = tfp.name.rstrip(".gz")
|
||||
except (HTTPError, URLError) as e:
|
||||
raise ValueError("{} {}".format(get_message("Download XML file error."), e))
|
||||
raise ValueError(f"{get_message('Download XML file error.')} {e}")
|
||||
else:
|
||||
try:
|
||||
with open(path, "wb") as f_out:
|
||||
@@ -282,7 +292,7 @@ class EpgDialog:
|
||||
shutil.copyfileobj(f, f_out)
|
||||
os.remove(tfp.name)
|
||||
except Exception as e:
|
||||
raise ValueError("{} {}".format(get_message("Unpacking data error."), e))
|
||||
raise ValueError(f"{get_message('Unpacking data error.')} {e}")
|
||||
finally:
|
||||
self._download_xml_is_active = False
|
||||
self.update_active_header_elements(True)
|
||||
@@ -291,7 +301,7 @@ class EpgDialog:
|
||||
s_refs, info = ChannelsParser.get_refs_from_xml(path)
|
||||
yield True
|
||||
except Exception as e:
|
||||
raise ValueError("{} {}".format(get_message("XML parsing error:"), e))
|
||||
raise ValueError(f"{get_message('XML parsing error:')} {e}")
|
||||
else:
|
||||
if refs:
|
||||
s_refs = filter(lambda x: x.num in refs, s_refs)
|
||||
@@ -300,7 +310,7 @@ class EpgDialog:
|
||||
self.update_source_count_info()
|
||||
yield True
|
||||
|
||||
def on_key_release(self, view, event):
|
||||
def on_key_press(self, view, event):
|
||||
""" Handling keystrokes """
|
||||
key_code = event.hardware_keycode
|
||||
if not KeyboardKey.value_exist(key_code):
|
||||
@@ -348,7 +358,7 @@ class EpgDialog:
|
||||
for row in self._services_model:
|
||||
name = re.sub("\\W+", "", str(row[0])).upper()
|
||||
name = name.translate(tr) if use_cyrillic else name
|
||||
source[name] = row[1]
|
||||
source[name] = row
|
||||
|
||||
success_count = 0
|
||||
not_founded = {}
|
||||
@@ -378,7 +388,7 @@ class EpgDialog:
|
||||
get_message("Count of successfully configured services:"),
|
||||
success_count), Gtk.MessageType.INFO)
|
||||
|
||||
def assign_data(self, row, ref, show_error=False):
|
||||
def assign_data(self, row, data, show_error=False):
|
||||
if row[Column.FAV_TYPE] != BqServiceType.IPTV.value:
|
||||
if not show_error:
|
||||
self.show_info_message(get_message("Not allowed in this context!"), Gtk.MessageType.ERROR)
|
||||
@@ -386,14 +396,15 @@ class EpgDialog:
|
||||
|
||||
fav_id = row[Column.FAV_ID]
|
||||
fav_id_data = fav_id.split(":")
|
||||
fav_id_data[3:7] = ref.split(":")
|
||||
fav_id_data[3:7] = data[-1].split(":")
|
||||
new_fav_id = ":".join(fav_id_data)
|
||||
service = self._services.pop(fav_id, None)
|
||||
if service:
|
||||
self._services[new_fav_id] = service
|
||||
row[Column.FAV_ID] = new_fav_id
|
||||
row[Column.FAV_LOCKED] = EPG_ICON
|
||||
row[Column.FAV_TOOLTIP] = ":".join(fav_id_data[:10]) if self._show_tooltips else None
|
||||
src = f"{get_message('EPG source')}: {data[0]} ({data[1]})"
|
||||
row[Column.FAV_TOOLTIP] = f"{get_message('Service reference')}: {':'.join(fav_id_data[:10])}\n{src}"
|
||||
|
||||
def on_filter_toggled(self, button: Gtk.ToggleButton):
|
||||
self._filter_bar.set_search_mode(button.get_active())
|
||||
@@ -412,7 +423,7 @@ class EpgDialog:
|
||||
model, paths = self._source_view.get_selection().get_selected_rows()
|
||||
self._current_ref.clear()
|
||||
if paths:
|
||||
self._current_ref.append(model[paths][1])
|
||||
self._current_ref.append(model[paths][:])
|
||||
|
||||
def on_assign_ref(self, item=None):
|
||||
if self._current_ref:
|
||||
@@ -481,7 +492,7 @@ class EpgDialog:
|
||||
# ***************** Drag-and-drop *********************#
|
||||
|
||||
def init_drag_and_drop(self):
|
||||
""" Enable drag-and-drop """
|
||||
""" Enable drag-and-drop. """
|
||||
target = []
|
||||
self._source_view.enable_model_drag_source(Gdk.ModifierType.BUTTON1_MASK, target, Gdk.DragAction.COPY)
|
||||
self._source_view.drag_source_add_text_targets()
|
||||
@@ -494,17 +505,22 @@ class EpgDialog:
|
||||
if selection.count_selected_rows() > 1:
|
||||
view.do_toggle_cursor_row(view)
|
||||
|
||||
def on_drag_data_get(self, view: Gtk.TreeView, drag_context, data, info, time):
|
||||
def on_drag_data_get(self, view, drag_context, data, info, time):
|
||||
model, paths = view.get_selection().get_selected_rows()
|
||||
if paths:
|
||||
val = model.get_value(model.get_iter(paths), 1)
|
||||
data.set_text(val, -1)
|
||||
s_data = model[paths][:]
|
||||
if all(s_data):
|
||||
data.set_text("::::".join(s_data), -1)
|
||||
else:
|
||||
self.show_info_message(get_message("Source error!"), Gtk.MessageType.ERROR)
|
||||
|
||||
def on_drag_data_received(self, view: Gtk.TreeView, drag_context, x, y, data, info, time):
|
||||
def on_drag_data_received(self, view, drag_context, x, y, data, info, time):
|
||||
path, pos = view.get_dest_row_at_pos(x, y)
|
||||
model = view.get_model()
|
||||
self.assign_data(model[path], data.get_text())
|
||||
self.update_epg_count()
|
||||
data = data.get_text()
|
||||
if data:
|
||||
self.assign_data(model[path], data.split("::::"))
|
||||
self.update_epg_count()
|
||||
return False
|
||||
|
||||
# ***************** Options *********************#
|
||||
|
||||
@@ -632,7 +632,7 @@ Author: Dmitriy Yefremov
|
||||
<property name="image">rename_image_2</property>
|
||||
<property name="use_stock">False</property>
|
||||
<signal name="activate" handler="on_file_edit" object="file_name_column_renderer" swapped="no"/>
|
||||
<accelerator key="r" signal="activate" modifiers="GDK_CONTROL_MASK"/>
|
||||
<accelerator key="r" signal="activate" modifiers="Primary"/>
|
||||
<accelerator key="F2" signal="activate"/>
|
||||
</object>
|
||||
</child>
|
||||
|
||||
@@ -469,7 +469,7 @@ class FtpClientBox(Gtk.HBox):
|
||||
|
||||
resp = "2"
|
||||
try:
|
||||
GLib.idle_add(self._app._wait_dialog.show)
|
||||
GLib.idle_add(self._app.wait_dialog.show)
|
||||
|
||||
uris = data.get_uris()
|
||||
if self._settings.is_darwin and len(uris) == 1:
|
||||
@@ -488,7 +488,7 @@ class FtpClientBox(Gtk.HBox):
|
||||
else:
|
||||
resp = self._ftp.send_file(path.name, str(path.parent) + "/", callback=self.update_ftp_info)
|
||||
finally:
|
||||
GLib.idle_add(self._app._wait_dialog.hide)
|
||||
GLib.idle_add(self._app.wait_dialog.hide)
|
||||
if resp and resp[0] == "2":
|
||||
itr = self._ftp_model.get_iter_first()
|
||||
if itr:
|
||||
@@ -507,7 +507,7 @@ class FtpClientBox(Gtk.HBox):
|
||||
def on_file_drag_data_received(self, view, context, x, y, data, info, time):
|
||||
cur_path = self._file_model.get_value(self._file_model.get_iter_first(), self.Column.ATTR) + "/"
|
||||
try:
|
||||
GLib.idle_add(self._app._wait_dialog.show)
|
||||
GLib.idle_add(self._app.wait_dialog.show)
|
||||
|
||||
uris = data.get_uris()
|
||||
if self._settings.is_darwin and len(uris) == 1:
|
||||
@@ -525,7 +525,7 @@ class FtpClientBox(Gtk.HBox):
|
||||
except OSError as e:
|
||||
log(e)
|
||||
finally:
|
||||
GLib.idle_add(self._app._wait_dialog.hide)
|
||||
GLib.idle_add(self._app.wait_dialog.hide)
|
||||
self.init_file_data(cur_path)
|
||||
|
||||
Gtk.drag_finish(context, True, False, time)
|
||||
@@ -564,6 +564,10 @@ class FtpClientBox(Gtk.HBox):
|
||||
self.on_ftp_file_remove()
|
||||
elif self._file_view.is_focus():
|
||||
self.on_file_remove()
|
||||
elif key is KeyboardKey.RETURN:
|
||||
path, column = view.get_cursor()
|
||||
if path:
|
||||
view.emit("row-activated", path, column)
|
||||
|
||||
def on_view_press(self, view, event):
|
||||
if event.get_event_type() == Gdk.EventType.BUTTON_PRESS and event.button == Gdk.BUTTON_PRIMARY:
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
106
app/ui/iptv.py
106
app/ui/iptv.py
@@ -48,7 +48,7 @@ from app.ui.main_helper import get_base_model, get_iptv_url, on_popup_menu, get_
|
||||
from app.ui.uicommons import (Gtk, Gdk, UI_RESOURCES_PATH, IPTV_ICON, Column, KeyboardKey, get_yt_icon)
|
||||
|
||||
_DIGIT_ENTRY_NAME = "digit-entry"
|
||||
_ENIGMA2_REFERENCE = "{}:0:{}:{:X}:{:X}:{:X}:{:X}:0:0:0"
|
||||
_ENIGMA2_REFERENCE = "{}:{}:{}:{:X}:{:X}:{:X}:{:X}:0:0:0"
|
||||
_PATTERN = re.compile("(?:^[\\s]*$|\\D)")
|
||||
_UI_PATH = UI_RESOURCES_PATH + "iptv.glade"
|
||||
|
||||
@@ -104,6 +104,7 @@ class IptvDialog:
|
||||
self._url_entry = builder.get_object("url_entry")
|
||||
self._reference_entry = builder.get_object("reference_entry")
|
||||
self._srv_type_entry = builder.get_object("srv_type_entry")
|
||||
self._srv_id_entry = builder.get_object("srv_id_entry")
|
||||
self._sid_entry = builder.get_object("sid_entry")
|
||||
self._tr_id_entry = builder.get_object("tr_id_entry")
|
||||
self._net_id_entry = builder.get_object("net_id_entry")
|
||||
@@ -119,8 +120,8 @@ class IptvDialog:
|
||||
# style
|
||||
self._style_provider = Gtk.CssProvider()
|
||||
self._style_provider.load_from_path(UI_RESOURCES_PATH + "style.css")
|
||||
self._digit_elems = (self._srv_type_entry, self._sid_entry, self._tr_id_entry, self._net_id_entry,
|
||||
self._namespace_entry)
|
||||
self._digit_elems = (self._srv_id_entry, self._srv_type_entry, self._sid_entry, self._tr_id_entry,
|
||||
self._net_id_entry, self._namespace_entry)
|
||||
for el in self._digit_elems:
|
||||
el.get_style_context().add_provider_for_screen(Gdk.Screen.get_default(), self._style_provider,
|
||||
Gtk.STYLE_PROVIDER_PRIORITY_USER)
|
||||
@@ -195,6 +196,7 @@ class IptvDialog:
|
||||
except ValueError:
|
||||
self.show_info_message("Unknown stream type {}".format(s_type), Gtk.MessageType.ERROR)
|
||||
|
||||
self._srv_id_entry.set_text(data[1])
|
||||
self._srv_type_entry.set_text(data[2])
|
||||
self._sid_entry.set_text(str(int(data[3], 16)))
|
||||
self._tr_id_entry.set_text(str(int(data[4], 16)))
|
||||
@@ -212,6 +214,7 @@ class IptvDialog:
|
||||
if self._s_type is SettingsType.ENIGMA_2 and is_data_correct(self._digit_elems):
|
||||
self.on_url_changed(self._url_entry)
|
||||
self._reference_entry.set_text(_ENIGMA2_REFERENCE.format(self.get_type(),
|
||||
self._srv_id_entry.get_text(),
|
||||
self._srv_type_entry.get_text(),
|
||||
int(self._sid_entry.get_text()),
|
||||
int(self._tr_id_entry.get_text()),
|
||||
@@ -296,6 +299,7 @@ class IptvDialog:
|
||||
def save_enigma2_data(self):
|
||||
name = self._name_entry.get_text().strip()
|
||||
fav_id = ENIGMA2_FAV_ID_FORMAT.format(self.get_type(),
|
||||
self._srv_id_entry.get_text(),
|
||||
self._srv_type_entry.get_text(),
|
||||
int(self._sid_entry.get_text()),
|
||||
int(self._tr_id_entry.get_text()),
|
||||
@@ -432,6 +436,7 @@ class IptvListDialog:
|
||||
"on_response": self.on_response,
|
||||
"on_stream_type_default_togged": self.on_stream_type_default_togged,
|
||||
"on_stream_type_changed": self.on_stream_type_changed,
|
||||
"on_default_id_toggled": self.on_default_id_toggled,
|
||||
"on_default_type_toggled": self.on_default_type_toggled,
|
||||
"on_auto_sid_toggled": self.on_auto_sid_toggled,
|
||||
"on_default_tid_toggled": self.on_default_tid_toggled,
|
||||
@@ -453,12 +458,14 @@ class IptvListDialog:
|
||||
self._info_bar = builder.get_object("list_configuration_info_bar")
|
||||
self._reference_label = builder.get_object("reference_label")
|
||||
self._stream_type_check_button = builder.get_object("stream_type_default_check_button")
|
||||
self._id_default_check_button = builder.get_object("id_default_check_button")
|
||||
self._type_check_button = builder.get_object("type_default_check_button")
|
||||
self._sid_auto_check_button = builder.get_object("sid_auto_check_button")
|
||||
self._tid_check_button = builder.get_object("tid_default_check_button")
|
||||
self._nid_check_button = builder.get_object("nid_default_check_button")
|
||||
self._namespace_check_button = builder.get_object("namespace_default_check_button")
|
||||
self._stream_type_combobox = builder.get_object("stream_type_list_combobox")
|
||||
self._list_srv_id_entry = builder.get_object("list_srv_id_entry")
|
||||
self._list_srv_type_entry = builder.get_object("list_srv_type_entry")
|
||||
self._list_sid_entry = builder.get_object("list_sid_entry")
|
||||
self._list_tid_entry = builder.get_object("list_tid_entry")
|
||||
@@ -467,13 +474,16 @@ class IptvListDialog:
|
||||
self._apply_button = builder.get_object("list_configuration_apply_button")
|
||||
self._cancel_button = builder.get_object("cancel_config_list_button")
|
||||
self._ok_button = builder.get_object("list_configuration_ok_button")
|
||||
self._ok_button.bind_property("visible", self._apply_button, "visible", 4)
|
||||
self._ok_button.bind_property("visible", self._cancel_button, "visible", 4)
|
||||
# Style
|
||||
style_provider = Gtk.CssProvider()
|
||||
style_provider.load_from_path(UI_RESOURCES_PATH + "style.css")
|
||||
self._default_elems = (self._stream_type_check_button, self._type_check_button, self._sid_auto_check_button,
|
||||
self._tid_check_button, self._nid_check_button, self._namespace_check_button)
|
||||
self._digit_elems = (self._list_srv_type_entry, self._list_sid_entry, self._list_tid_entry,
|
||||
self._list_nid_entry, self._list_namespace_entry)
|
||||
self._default_elems = (self._stream_type_check_button, self._id_default_check_button, self._type_check_button,
|
||||
self._sid_auto_check_button, self._tid_check_button, self._nid_check_button,
|
||||
self._namespace_check_button)
|
||||
self._digit_elems = (self._list_srv_id_entry, self._list_srv_type_entry, self._list_sid_entry,
|
||||
self._list_tid_entry, self._list_nid_entry, self._list_namespace_entry)
|
||||
for el in self._digit_elems:
|
||||
el.get_style_context().add_provider_for_screen(Gdk.Screen.get_default(), style_provider,
|
||||
Gtk.STYLE_PROVIDER_PRIORITY_USER)
|
||||
@@ -495,30 +505,28 @@ class IptvListDialog:
|
||||
self._stream_type_combobox.set_active(1)
|
||||
self._stream_type_combobox.set_sensitive(not button.get_active())
|
||||
|
||||
def on_default_id_toggled(self, button):
|
||||
self.set_default(button, self._list_srv_id_entry, "0")
|
||||
|
||||
def on_default_type_toggled(self, button):
|
||||
if button.get_active():
|
||||
self._list_srv_type_entry.set_text("1")
|
||||
self._list_srv_type_entry.set_sensitive(not button.get_active())
|
||||
self.set_default(button, self._list_srv_type_entry, "1")
|
||||
|
||||
def on_auto_sid_toggled(self, button):
|
||||
if button.get_active():
|
||||
self._list_sid_entry.set_text("0")
|
||||
self._list_sid_entry.set_sensitive(not button.get_active())
|
||||
self.set_default(button, self._list_sid_entry, "0")
|
||||
|
||||
def on_default_tid_toggled(self, button):
|
||||
if button.get_active():
|
||||
self._list_tid_entry.set_text("0")
|
||||
self._list_tid_entry.set_sensitive(not button.get_active())
|
||||
self.set_default(button, self._list_tid_entry, "0")
|
||||
|
||||
def on_default_nid_toggled(self, button):
|
||||
if button.get_active():
|
||||
self._list_nid_entry.set_text("0")
|
||||
self._list_nid_entry.set_sensitive(not button.get_active())
|
||||
self.set_default(button, self._list_nid_entry, "0")
|
||||
|
||||
def on_default_namespace_toggled(self, button):
|
||||
self.set_default(button, self._list_namespace_entry, "0")
|
||||
|
||||
def set_default(self, button, entry, value):
|
||||
if button.get_active():
|
||||
self._list_namespace_entry.set_text("0")
|
||||
self._list_namespace_entry.set_sensitive(not button.get_active())
|
||||
entry.set_text(value)
|
||||
entry.set_sensitive(not button.get_active())
|
||||
|
||||
@run_idle
|
||||
def on_reset_to_default(self, item):
|
||||
@@ -550,7 +558,7 @@ class IptvListDialog:
|
||||
self.update_reference()
|
||||
|
||||
def is_default_values(self):
|
||||
return any(el.get_text() == "0" for el in self._digit_elems[2:])
|
||||
return any(el.get_text() == "0" for el in self._digit_elems[3:])
|
||||
|
||||
def is_all_data_default(self):
|
||||
return all(el.get_active() for el in self._default_elems)
|
||||
@@ -573,31 +581,35 @@ class IptvListConfigurationDialog(IptvListDialog):
|
||||
return
|
||||
|
||||
if self._s_type is SettingsType.ENIGMA_2:
|
||||
id_default = self._id_default_check_button.get_active()
|
||||
type_default = self._type_check_button.get_active()
|
||||
tid_default = self._tid_check_button.get_active()
|
||||
sid_auto = self._sid_auto_check_button.get_active()
|
||||
nid_default = self._nid_check_button.get_active()
|
||||
namespace_default = self._namespace_check_button.get_active()
|
||||
all_default = self.is_all_data_default()
|
||||
|
||||
stream_type = get_stream_type(self._stream_type_combobox)
|
||||
st_type = get_stream_type(self._stream_type_combobox)
|
||||
s_id = "0" if id_default else self._list_srv_id_entry.get_text()
|
||||
srv_type = "1" if type_default else self._list_srv_type_entry.get_text()
|
||||
tid = "0" if tid_default else "{:X}".format(int(self._list_tid_entry.get_text()))
|
||||
nid = "0" if nid_default else "{:X}".format(int(self._list_nid_entry.get_text()))
|
||||
namespace = "0" if namespace_default else "{:X}".format(int(self._list_namespace_entry.get_text()))
|
||||
sid = "0" if sid_auto else self._list_sid_entry.get_text()
|
||||
tid = "0" if tid_default else f"{int(self._list_tid_entry.get_text()):X}"
|
||||
nid = "0" if nid_default else f"{int(self._list_nid_entry.get_text()):X}"
|
||||
namespace = "0" if namespace_default else f"{int(self._list_namespace_entry.get_text()):X}"
|
||||
|
||||
for index, row in enumerate(self._rows):
|
||||
fav_id = row[Column.FAV_ID]
|
||||
data, sep, desc = fav_id.partition("http")
|
||||
data = data.split(":")
|
||||
|
||||
if self.is_all_data_default():
|
||||
data[2], data[3], data[4], data[5], data[6] = "10000"
|
||||
if all_default:
|
||||
data[1], data[2], data[3], data[4], data[5], data[6] = "010000"
|
||||
else:
|
||||
data[0], data[2], data[4], data[5], data[6] = stream_type, srv_type, tid, nid, namespace
|
||||
data[3] = "{:X}".format(index) if sid_auto else "0"
|
||||
data[0], data[1], data[2], data[4], data[5], data[6] = st_type, s_id, srv_type, tid, nid, namespace
|
||||
|
||||
data[3] = f"{index:X}" if sid_auto else sid
|
||||
data = ":".join(data)
|
||||
new_fav_id = "{}{}{}".format(data, sep, desc)
|
||||
new_fav_id = f"{data}{sep}{desc}"
|
||||
row[Column.FAV_ID] = new_fav_id
|
||||
srv = self._services.pop(fav_id, None)
|
||||
|
||||
@@ -608,6 +620,7 @@ class IptvListConfigurationDialog(IptvListDialog):
|
||||
list(map(lambda r: self._bouquet.append(r[Column.FAV_ID]), self._fav_model))
|
||||
|
||||
self._info_bar.set_visible(True)
|
||||
self._ok_button.set_visible(True)
|
||||
|
||||
|
||||
class M3uImportDialog(IptvListDialog):
|
||||
@@ -628,8 +641,6 @@ class M3uImportDialog(IptvListDialog):
|
||||
self._dialog.set_title(get_message("Playlist import"))
|
||||
self._dialog.connect("delete-event", self.on_close)
|
||||
self._apply_button.set_label(get_message("Import"))
|
||||
self._ok_button.bind_property("visible", self._apply_button, "visible", 4)
|
||||
self._ok_button.bind_property("visible", self._cancel_button, "visible", 4)
|
||||
# Progress
|
||||
self._progress_bar = Gtk.ProgressBar(visible=False, valign="center")
|
||||
self._spinner = Gtk.Spinner(active=False)
|
||||
@@ -654,7 +665,7 @@ class M3uImportDialog(IptvListDialog):
|
||||
extra_box.pack_start(self._info_label, False, False, 5)
|
||||
extra_box.pack_end(self._picon_box, True, True, 5)
|
||||
|
||||
frame = Gtk.Frame(visible=True)
|
||||
frame = Gtk.Frame(visible=True, margin_bottom=5)
|
||||
frame.add(extra_box)
|
||||
self._data_box.add(frame)
|
||||
|
||||
@@ -670,7 +681,7 @@ class M3uImportDialog(IptvListDialog):
|
||||
GLib.idle_add(self._picon_box.set_sensitive, True)
|
||||
break
|
||||
finally:
|
||||
msg = "{} {}.".format(get_message("Streams detected:"), len(self._services) if self._services else 0)
|
||||
msg = f"{get_message('Streams detected:')} {len(self._services) if self._services else 0}."
|
||||
GLib.idle_add(self._info_label.set_text, msg)
|
||||
GLib.idle_add(self._spinner.set_property, "active", False)
|
||||
|
||||
@@ -685,9 +696,12 @@ class M3uImportDialog(IptvListDialog):
|
||||
if not self.is_all_data_default():
|
||||
services = []
|
||||
params = [int(el.get_text()) for el in self._digit_elems]
|
||||
s_type = params[0]
|
||||
params = params[1:]
|
||||
stream_type = get_stream_type(self._stream_type_combobox)
|
||||
s_id = params[0]
|
||||
s_type = params[1]
|
||||
params = params[2:]
|
||||
st_type = get_stream_type(self._stream_type_combobox)
|
||||
sid_auto = self._sid_auto_check_button.get_active()
|
||||
sid = 0 if sid_auto else int(self._list_sid_entry.get_text())
|
||||
|
||||
for i, s in enumerate(self._services, start=params[0]):
|
||||
# Skipping markers.
|
||||
@@ -695,9 +709,9 @@ class M3uImportDialog(IptvListDialog):
|
||||
services.append(s)
|
||||
continue
|
||||
|
||||
params[0] = i
|
||||
picon_id = "{}_0_{:X}_{:X}_{:X}_{:X}_{:X}_0_0_0.png".format(stream_type, s_type, *params)
|
||||
fav_id = get_fav_id(s.data_id, s.service, self._s_type, params, stream_type, s_type)
|
||||
params[0] = i if sid_auto else sid
|
||||
picon_id = "{}_{}_{:X}_{:X}_{:X}_{:X}_{:X}_0_0_0.png".format(st_type, s_id, s_type, *params)
|
||||
fav_id = get_fav_id(s.data_id, s.service, self._s_type, params, st_type, s_id, s_type)
|
||||
if s.picon:
|
||||
picons[s.picon] = picon_id
|
||||
|
||||
@@ -755,16 +769,16 @@ class M3uImportDialog(IptvListDialog):
|
||||
@run_idle
|
||||
def on_picon_load_done(self, data, user_data):
|
||||
try:
|
||||
self._info_label.set_text("Processing: {}".format(user_data))
|
||||
self._info_label.set_text(f"Processing: {user_data}")
|
||||
f = Gio.MemoryInputStream.new_from_data(data)
|
||||
pixbuf = GdkPixbuf.Pixbuf.new_from_stream_at_scale(f, 220, 132, False, self._cancellable)
|
||||
path = "{}{}".format(self._pic_path, user_data)
|
||||
path = f"{self._pic_path}{user_data}"
|
||||
pixbuf.savev(path, "png", [], [])
|
||||
self._picons[user_data] = get_picon_pixbuf(path)
|
||||
except GLib.GError as e:
|
||||
self.update_progress(1)
|
||||
if e.code != Gio.IOErrorEnum.CANCELLED:
|
||||
log("Loading picon [{}] data error: {}".format(user_data, e))
|
||||
log(f"Loading picon [{user_data}] data error: {e}")
|
||||
else:
|
||||
self.update_progress()
|
||||
|
||||
@@ -780,14 +794,14 @@ class M3uImportDialog(IptvListDialog):
|
||||
self._progress_bar.set_visible(False)
|
||||
self._progress_bar.set_fraction(0.0)
|
||||
self._apply_button.set_sensitive(True)
|
||||
self._info_label.set_text("Errors: {}.".format(self._errors_count))
|
||||
self._info_label.set_text(f"Errors: {self._errors_count}.")
|
||||
self._is_download = False
|
||||
|
||||
gen = self.update_fav_model()
|
||||
GLib.idle_add(lambda: next(gen, False), priority=GLib.PRIORITY_LOW)
|
||||
|
||||
def update_fav_model(self):
|
||||
services = self._app._services
|
||||
services = self._app.current_services
|
||||
picons = self._app._picons
|
||||
model = self._app.fav_view.get_model()
|
||||
for r in model:
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
125
app/ui/logs.glade
Normal file
125
app/ui/logs.glade
Normal file
@@ -0,0 +1,125 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- Generated with glade 3.22.2
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2018-2021 Dmitriy Yefremov
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
Author: Dmitriy Yefremov
|
||||
|
||||
-->
|
||||
<interface domain="demon-editor">
|
||||
<requires lib="gtk+" version="3.18"/>
|
||||
<!-- interface-css-provider-path style.css -->
|
||||
<!-- interface-license-type mit -->
|
||||
<!-- interface-name DemonEditor -->
|
||||
<!-- interface-description Enigma2 channel and satellite list editor. -->
|
||||
<!-- interface-copyright 2018-2021 Dmitriy Yefremov -->
|
||||
<!-- interface-authors Dmitriy Yefremov -->
|
||||
<object class="GtkFrame" id="log_frame">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label_xalign">0.49000000953674316</property>
|
||||
<property name="shadow_type">in</property>
|
||||
<child>
|
||||
<object class="GtkBox" id="main_box">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_left">5</property>
|
||||
<property name="margin_right">5</property>
|
||||
<property name="margin_bottom">5</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<property name="spacing">5</property>
|
||||
<child>
|
||||
<object class="GtkBox" id="header_box">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_left">5</property>
|
||||
<property name="margin_right">5</property>
|
||||
<property name="margin_bottom">2</property>
|
||||
<property name="spacing">5</property>
|
||||
<child>
|
||||
<object class="GtkButton" id="clear_button">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="tooltip_text" translatable="yes">Clear</property>
|
||||
<property name="halign">center</property>
|
||||
<property name="valign">center</property>
|
||||
<signal name="clicked" handler="on_clear" swapped="no"/>
|
||||
<child>
|
||||
<object class="GtkImage" id="clear_button_image">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="stock">gtk-clear</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkScrolledWindow" id="scrolled_window">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="shadow_type">in</property>
|
||||
<child>
|
||||
<object class="GtkTextView" id="log_view">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="editable">False</property>
|
||||
<property name="left_margin">5</property>
|
||||
<property name="right_margin">5</property>
|
||||
<property name="top_margin">5</property>
|
||||
<property name="bottom_margin">5</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child type="label">
|
||||
<object class="GtkLabel" id="log_label">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">Logs</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</interface>
|
||||
66
app/ui/logs.py
Normal file
66
app/ui/logs.py
Normal file
@@ -0,0 +1,66 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# The MIT License (MIT)
|
||||
#
|
||||
# Copyright (c) 2018-2021 Dmitriy Yefremov
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
# THE SOFTWARE.
|
||||
#
|
||||
# Author: Dmitriy Yefremov
|
||||
#
|
||||
|
||||
|
||||
import logging
|
||||
|
||||
from gi.repository import GLib
|
||||
|
||||
from app.commons import LOGGER_NAME
|
||||
from app.ui.dialogs import get_builder
|
||||
from app.ui.main_helper import append_text_to_tview
|
||||
from app.ui.uicommons import Gtk, UI_RESOURCES_PATH
|
||||
|
||||
|
||||
class LogsClient(Gtk.Box):
|
||||
""" Logger GUI client. """
|
||||
|
||||
class LogHandler(logging.Handler):
|
||||
def __init__(self, view):
|
||||
logging.Handler.__init__(self)
|
||||
self._view = view
|
||||
|
||||
def handle(self, rec):
|
||||
GLib.idle_add(append_text_to_tview, f"{rec.msg}\n", self._view)
|
||||
|
||||
def __init__(self, app, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self._app = app
|
||||
|
||||
handlers = {"on_clear": self.on_clear}
|
||||
builder = get_builder(UI_RESOURCES_PATH + "logs.glade", handlers)
|
||||
|
||||
self._log_view = builder.get_object("log_view")
|
||||
self.pack_start(builder.get_object("log_frame"), True, True, 0)
|
||||
|
||||
logger = logging.getLogger(LOGGER_NAME)
|
||||
logger.addHandler(LogsClient.LogHandler(self._log_view))
|
||||
|
||||
self.show()
|
||||
|
||||
def on_clear(self, button):
|
||||
GLib.idle_add(self._log_view.get_buffer().set_text, "")
|
||||
@@ -1,5 +1,6 @@
|
||||
* {
|
||||
-GtkDialog-action-area-border: 5em;
|
||||
-GtkDialog-action-area-border: 6em;
|
||||
-GtkDialog-button-spacing: 12;
|
||||
}
|
||||
|
||||
entry {
|
||||
@@ -36,3 +37,7 @@ switch slider {
|
||||
min-height: 1.5em;
|
||||
min-width: 1.5em;
|
||||
}
|
||||
|
||||
.dialog-action-area button {
|
||||
margin-bottom: 0.6em;
|
||||
}
|
||||
|
||||
4038
app/ui/main.glade
4038
app/ui/main.glade
File diff suppressed because it is too large
Load Diff
354
app/ui/main.py
354
app/ui/main.py
@@ -51,6 +51,7 @@ from app.tools.media import Recorder
|
||||
from app.ui.control import ControlTool, EpgTool, TimerTool, RecordingsTool
|
||||
from app.ui.epg import EpgDialog
|
||||
from app.ui.ftp import FtpClientBox
|
||||
from app.ui.logs import LogsClient
|
||||
from app.ui.playback import PlayerBox
|
||||
from app.ui.telnet import TelnetClient
|
||||
from app.ui.transmitter import LinksTransmitter
|
||||
@@ -59,16 +60,12 @@ from .dialogs import show_dialog, DialogType, get_chooser_dialog, WaitDialog, ge
|
||||
from .download_dialog import DownloadDialog
|
||||
from .imports import ImportDialog, import_bouquet
|
||||
from .iptv import IptvDialog, SearchUnavailableDialog, IptvListConfigurationDialog, YtListImportDialog, M3uImportDialog
|
||||
from .main_helper import (insert_marker, move_items, rename, ViewTarget, set_flags, locate_in_services,
|
||||
scroll_to, get_base_model, update_picons_data, copy_picon_reference, assign_picons,
|
||||
remove_picon, is_only_one_item_selected, gen_bouquets, BqGenType, append_picons,
|
||||
get_selection, get_model_data, remove_all_unused_picons, get_picon_pixbuf, get_base_itrs,
|
||||
get_iptv_url)
|
||||
from .main_helper import *
|
||||
from .picons import PiconManager
|
||||
from .satellites import SatellitesTool, ServicesUpdateDialog
|
||||
from .search import SearchProvider
|
||||
from .service_details_dialog import ServiceDetailsDialog, Action
|
||||
from .settings_dialog import show_settings_dialog
|
||||
from .settings_dialog import SettingsDialog
|
||||
from .uicommons import (Gtk, Gdk, UI_RESOURCES_PATH, LOCKED_ICON, HIDE_ICON, IPTV_ICON, MOVE_KEYS, KeyboardKey, Column,
|
||||
FavClickMode, MOD_MASK, APP_FONT, Page, IS_GNOME_SESSION)
|
||||
|
||||
@@ -158,15 +155,20 @@ class Application(Gtk.Application):
|
||||
"on_import_m3u": self.on_import_m3u,
|
||||
"on_bouquet_export": self.on_bouquet_export,
|
||||
"on_export_to_m3u": self.on_export_to_m3u,
|
||||
"on_import_bouquet": self.on_import_bouquet,
|
||||
"on_insert_marker": self.on_insert_marker,
|
||||
"on_insert_space": self.on_insert_space,
|
||||
"on_fav_press": self.on_fav_press,
|
||||
"on_locate_in_services": self.on_locate_in_services,
|
||||
"on_mark_duplicates": self.on_mark_duplicates,
|
||||
"on_services_mark_not_in_bouquets": self.on_services_mark_not_in_bouquets,
|
||||
"on_services_clear_marked": self.on_services_clear_marked,
|
||||
"on_filter_changed": self.on_filter_changed,
|
||||
"on_filter_type_toggled": self.on_filter_type_toggled,
|
||||
"on_services_filter_toggled": self.on_services_filter_toggled,
|
||||
"on_filter_satellite_toggled": self.on_filter_satellite_toggled,
|
||||
"on_filter_in_bq_toggled": self.on_filter_in_bq_toggled,
|
||||
"on_assign_picon_file": self.on_assign_picon_file,
|
||||
"on_assign_picon": self.on_assign_picon,
|
||||
"on_remove_picon": self.on_remove_picon,
|
||||
"on_reference_picon": self.on_reference_picon,
|
||||
@@ -180,6 +182,7 @@ class Application(Gtk.Application):
|
||||
"on_record": self.on_record,
|
||||
"on_remove_all_unavailable": self.on_remove_all_unavailable,
|
||||
"on_new_bouquet": self.on_new_bouquet,
|
||||
"on_new_sub_bouquet": self.on_new_sub_bouquet,
|
||||
"on_create_bouquet_for_current_satellite": self.on_create_bouquet_for_current_satellite,
|
||||
"on_create_bouquet_for_each_satellite": self.on_create_bouquet_for_each_satellite,
|
||||
"on_create_bouquet_for_current_package": self.on_create_bouquet_for_current_package,
|
||||
@@ -194,7 +197,8 @@ class Application(Gtk.Application):
|
||||
"on_recordings_realize": self.on_recordings_realize,
|
||||
"on_control_realize": self.on_control_realize,
|
||||
"on_ftp_realize": self.on_ftp_realize,
|
||||
"on_telnet_relize": self.on_telnet_relize,
|
||||
"on_telnet_realize": self.on_telnet_realize,
|
||||
"on_logs_realize": self.on_logs_realize,
|
||||
"on_visible_page": self.on_visible_page,
|
||||
"on_data_paned_realize": self.init_main_paned_position}
|
||||
|
||||
@@ -213,6 +217,7 @@ class Application(Gtk.Application):
|
||||
self._alt_counter = 1
|
||||
self._data_hash = 0
|
||||
self._filter_cache = {}
|
||||
self._in_bouquets = set()
|
||||
# For bouquets with different names of services in bouquet and main list
|
||||
self._extra_bouquets = {}
|
||||
self._picons = DefaultDict(self.get_picon)
|
||||
@@ -266,14 +271,11 @@ class Application(Gtk.Application):
|
||||
GObject.TYPE_PYOBJECT, (GObject.TYPE_PYOBJECT,))
|
||||
GObject.signal_new("filter-toggled", self, GObject.SIGNAL_RUN_LAST,
|
||||
GObject.TYPE_PYOBJECT, (GObject.TYPE_PYOBJECT,))
|
||||
GObject.signal_new("picon-assign", self, GObject.SIGNAL_RUN_LAST,
|
||||
GObject.TYPE_PYOBJECT, (GObject.TYPE_PYOBJECT,))
|
||||
|
||||
builder = get_builder(UI_RESOURCES_PATH + "main.glade", handlers)
|
||||
self._main_window = builder.get_object("main_window")
|
||||
main_window_size = self._settings.get("window_size")
|
||||
# Setting the last size of the window if it was saved
|
||||
if main_window_size:
|
||||
self._main_window.resize(*main_window_size)
|
||||
|
||||
self._stack = builder.get_object("stack")
|
||||
self._fav_paned = builder.get_object("fav_paned")
|
||||
self._services_view = builder.get_object("services_tree_view")
|
||||
@@ -315,6 +317,7 @@ class Application(Gtk.Application):
|
||||
self._signal_level_bar.bind_property("visible", builder.get_object("record_button"), "visible")
|
||||
self._receiver_info_box.bind_property("visible", self._http_status_image, "visible", 4)
|
||||
self._receiver_info_box.bind_property("visible", self._signal_box, "visible")
|
||||
self._save_tool_button.bind_property("visible", builder.get_object("fav_assign_picon_popup_item"), "sensitive")
|
||||
# Alternatives
|
||||
self._alt_view = builder.get_object("alt_tree_view")
|
||||
self._alt_model = builder.get_object("alt_list_store")
|
||||
@@ -336,6 +339,7 @@ class Application(Gtk.Application):
|
||||
self._filter_types_model = builder.get_object("filter_types_list_store")
|
||||
self._filter_sat_pos_model = builder.get_object("filter_sat_pos_list_store")
|
||||
self._filter_only_free_button = builder.get_object("filter_only_free_button")
|
||||
self._filter_not_in_bq_button = builder.get_object("filter_not_in_bq_button")
|
||||
self._services_load_spinner.bind_property("active", self._filter_services_button, "sensitive", 4)
|
||||
self._services_load_spinner.bind_property("active", self._filter_box, "sensitive", 4)
|
||||
# Search.
|
||||
@@ -377,6 +381,8 @@ class Application(Gtk.Application):
|
||||
# Lock, Hide.
|
||||
self.bind_property("is-enigma", self._tool_elements.get(self._LOCK_HIDE_ELEMENTS[0]), "visible")
|
||||
self.bind_property("is-enigma", self._tool_elements.get(self._LOCK_HIDE_ELEMENTS[1]), "visible", 4)
|
||||
# Sub-bouquets menu item.
|
||||
self.bind_property("is_enigma", builder.get_object("bouquets_new_sub_popup_item"), "visible")
|
||||
# Stack page widgets.
|
||||
self._stack_services_frame = builder.get_object("services_frame")
|
||||
self._stack_satellite_box = builder.get_object("satellite_box")
|
||||
@@ -386,8 +392,11 @@ class Application(Gtk.Application):
|
||||
self._stack_recordings_box = builder.get_object("recordings_box")
|
||||
self._stack_ftp_box = builder.get_object("ftp_box")
|
||||
self._stack_control_box = builder.get_object("control_box")
|
||||
self._telnet_box = builder.get_object("telnet_box")
|
||||
self.connect("change-page", self.on_page_change)
|
||||
# Extra tools.
|
||||
self._telnet_box = builder.get_object("telnet_box")
|
||||
self._logs_box = builder.get_object("logs_box")
|
||||
self._bottom_paned = builder.get_object("bottom_paned")
|
||||
# Header bar.
|
||||
profile_box = builder.get_object("profile_combo_box")
|
||||
toolbar_box = builder.get_object("toolbar_main_box")
|
||||
@@ -415,7 +424,11 @@ class Application(Gtk.Application):
|
||||
self._data_paned.bind_property("visible", main_header_box, "visible")
|
||||
self._player_box.bind_property("visible", profile_box, "visible", 4)
|
||||
self._player_box.bind_property("visible", toolbar_box, "visible", 4)
|
||||
# Style
|
||||
# Setting the last size of the window if it was saved.
|
||||
main_window_size = self._settings.get("window_size")
|
||||
if main_window_size:
|
||||
self._main_window.resize(*main_window_size)
|
||||
# Style.
|
||||
style_provider = Gtk.CssProvider()
|
||||
style_provider.load_from_path(UI_RESOURCES_PATH + "style.css")
|
||||
self._status_bar_box.get_style_context().add_provider_for_screen(Gdk.Screen.get_default(), style_provider,
|
||||
@@ -424,7 +437,7 @@ class Application(Gtk.Application):
|
||||
def do_startup(self):
|
||||
Gtk.Application.do_startup(self)
|
||||
# App menu.
|
||||
builder = get_builder(UI_RESOURCES_PATH + "app_menu.ui")
|
||||
builder = get_builder(UI_RESOURCES_PATH + "app_menu.ui", tag="attribute")
|
||||
if not IS_GNOME_SESSION:
|
||||
if IS_DARWIN:
|
||||
self.set_app_menu(builder.get_object("mac_app_menu"))
|
||||
@@ -432,10 +445,19 @@ class Application(Gtk.Application):
|
||||
else:
|
||||
self.set_menubar(builder.get_object("menu_bar"))
|
||||
else:
|
||||
tools_menu = builder.get_object("tools_menu")
|
||||
tools_button = Gtk.MenuButton(visible=True, menu_model=tools_menu, direction=Gtk.ArrowType.NONE)
|
||||
tools_button.set_tooltip_text(get_message("Tools"))
|
||||
tools_button.set_image(Gtk.Image.new_from_icon_name("applications-utilities-symbolic", Gtk.IconSize.BUTTON))
|
||||
|
||||
view_menu = builder.get_object("view_menu")
|
||||
view_button = Gtk.MenuButton(visible=True, menu_model=view_menu, direction=Gtk.ArrowType.NONE)
|
||||
view_button.set_tooltip_text(get_message("View"))
|
||||
self._main_window.get_titlebar().pack_end(view_button)
|
||||
|
||||
box = Gtk.ButtonBox(visible=True, layout_style="expand")
|
||||
box.add(tools_button)
|
||||
box.add(view_button)
|
||||
self._main_window.get_titlebar().pack_end(box)
|
||||
# IPTV menu.
|
||||
self._iptv_menu_button.set_menu_model(builder.get_object("iptv_menu"))
|
||||
iptv_elem = self._tool_elements.get("fav_iptv_popup_item")
|
||||
@@ -464,6 +486,7 @@ class Application(Gtk.Application):
|
||||
self.set_action("on_about_app", self.on_about_app)
|
||||
self.set_action("on_close_app", self.on_close_app)
|
||||
self.set_state_action("on_telnet_show", self.on_telnet_show, False)
|
||||
self.set_state_action("on_logs_show", self.on_logs_show, False)
|
||||
# Filter.
|
||||
filter_action = Gio.SimpleAction.new("filter", None)
|
||||
filter_action.connect("activate", lambda a, v: self.emit("filter-toggled", None))
|
||||
@@ -544,6 +567,7 @@ class Application(Gtk.Application):
|
||||
self.set_accels_for_action("app.on_close_app", ["<primary>q"])
|
||||
self.set_accels_for_action("app.on_edit", ["<primary>e"])
|
||||
self.set_accels_for_action("app.on_telnet_show", ["<primary>t"])
|
||||
self.set_accels_for_action("app.on_logs_show", ["<shift><primary>l"])
|
||||
self.set_accels_for_action("win.filter", ["<shift><primary>f"])
|
||||
|
||||
def do_activate(self):
|
||||
@@ -584,7 +608,7 @@ class Application(Gtk.Application):
|
||||
else:
|
||||
log("No valid [on, off] arguments for -d found!")
|
||||
return 1
|
||||
log("Debug mode is {}.".format(d_op))
|
||||
log(f"Debug mode is {d_op}.")
|
||||
self._settings.save()
|
||||
|
||||
self.activate()
|
||||
@@ -678,8 +702,10 @@ class Application(Gtk.Application):
|
||||
def init_main_paned_position(self, paned):
|
||||
""" Initializes starting positions of main paned widgets. """
|
||||
width = paned.get_allocated_width()
|
||||
paned.set_position(width * 0.5)
|
||||
self._fav_paned.set_position(width * 0.27)
|
||||
main_position = self._settings.get("data_paned_position", width * 0.5)
|
||||
fav_position = self._settings.get("fav_paned_position", width * 0.27)
|
||||
paned.set_position(main_position)
|
||||
self._fav_paned.set_position(fav_position)
|
||||
|
||||
@run_task
|
||||
def update_picons_size(self):
|
||||
@@ -721,15 +747,18 @@ class Application(Gtk.Application):
|
||||
# Saving the current size of the application window.
|
||||
if not self._main_window.is_maximized():
|
||||
self._settings.add("window_size", self._main_window.get_size())
|
||||
# Saving the state of the main paned widgets.
|
||||
self._settings.add("data_paned_position", self._data_paned.get_position())
|
||||
self._settings.add("fav_paned_position", self._fav_paned.get_position())
|
||||
|
||||
if self._services_load_spinner.get_property("active"):
|
||||
msg = "{}\n\n\t{}".format(get_message("Data loading in progress!"), get_message("Are you sure?"))
|
||||
msg = f"{get_message('Data loading in progress!')}\n\n\t{get_message('Are you sure?')}"
|
||||
if show_dialog(DialogType.QUESTION, self._main_window, msg) != Gtk.ResponseType.OK:
|
||||
return True
|
||||
|
||||
if self._recorder:
|
||||
if self._recorder.is_record():
|
||||
msg = "{}\n\n\t{}".format(get_message("Recording in progress!"), get_message("Are you sure?"))
|
||||
msg = f"{get_message('Recording in progress!')}\n\n\t{get_message('Are you sure?')}"
|
||||
if show_dialog(DialogType.QUESTION, self._main_window, msg) != Gtk.ResponseType.OK:
|
||||
return True
|
||||
self._recorder.release()
|
||||
@@ -768,7 +797,7 @@ class Application(Gtk.Application):
|
||||
if self._s_type is SettingsType.ENIGMA_2:
|
||||
for r in self._services_model:
|
||||
data = r[Column.SRV_PICON_ID].split("_")
|
||||
ids["{}:{}:{}".format(data[3], data[5], data[6])] = r[Column.SRV_PICON_ID]
|
||||
ids[f"{data[3]}:{data[5]}:{data[6]}"] = r[Column.SRV_PICON_ID]
|
||||
|
||||
self._picon_manager = PiconManager(self, self._settings, ids, self._sat_positions)
|
||||
box.pack_start(self._picon_manager, True, True, 0)
|
||||
@@ -795,9 +824,11 @@ class Application(Gtk.Application):
|
||||
self._control_tool = ControlTool(self, self._http_api, self._settings)
|
||||
box.pack_start(self._control_tool, True, True, 0)
|
||||
|
||||
def on_telnet_relize(self, box):
|
||||
telnet_tool = TelnetClient(self, self._settings)
|
||||
box.pack_start(telnet_tool, True, True, 0)
|
||||
def on_telnet_realize(self, box):
|
||||
box.pack_start(TelnetClient(self), True, True, 0)
|
||||
|
||||
def on_logs_realize(self, box):
|
||||
box.pack_start(LogsClient(self), True, True, 0)
|
||||
|
||||
def on_visible_page(self, stack, param):
|
||||
self._page = Page(stack.get_visible_child_name())
|
||||
@@ -1006,7 +1037,7 @@ class Application(Gtk.Application):
|
||||
self._fav_model.clear()
|
||||
yield True
|
||||
b_row = self._bouquets_model[itr][:]
|
||||
self._bouquets.pop("{}:{}".format(b_row[Column.BQ_NAME], b_row[Column.BQ_TYPE]), None)
|
||||
self._bouquets.pop(f"{b_row[Column.BQ_NAME]}:{b_row[Column.BQ_TYPE]}", None)
|
||||
self._bouquets_model.remove(itr)
|
||||
|
||||
self._bq_selected = ""
|
||||
@@ -1022,28 +1053,32 @@ class Application(Gtk.Application):
|
||||
*bouquet.split(":"))
|
||||
return bouquet_file_name
|
||||
|
||||
def on_new_bouquet(self, view):
|
||||
def on_new_bouquet(self, view, sub=False):
|
||||
""" Creates a new item in the bouquets tree """
|
||||
model, paths = view.get_selection().get_selected_rows()
|
||||
|
||||
if paths:
|
||||
itr = model.get_iter(paths[0])
|
||||
bq_type = model.get_value(itr, 3)
|
||||
if not model.iter_parent(itr) and sub:
|
||||
self.show_error_message("Not allowed in this context!")
|
||||
return
|
||||
|
||||
bq_type = model.get_value(itr, Column.BQ_TYPE)
|
||||
bq_name = "bouquet"
|
||||
count = 0
|
||||
key = "{}:{}".format(bq_name, bq_type)
|
||||
# Generating name of new bouquet
|
||||
key = f"{bq_name}:{bq_type}"
|
||||
# Generating name of new bouquet.
|
||||
while key in self._bouquets:
|
||||
count += 1
|
||||
bq_name = "bouquet{}".format(count)
|
||||
key = "{}:{}".format(bq_name, bq_type)
|
||||
bq_name = f"bouquet{count}"
|
||||
key = f"{bq_name}:{bq_type}"
|
||||
|
||||
response = show_dialog(DialogType.INPUT, self._main_window, bq_name)
|
||||
if response == Gtk.ResponseType.CANCEL:
|
||||
return
|
||||
|
||||
bq = response, None, None, bq_type
|
||||
key = "{}:{}".format(response, bq_type)
|
||||
key = f"{response}:{bq_type}"
|
||||
|
||||
while key in self._bouquets:
|
||||
self.show_error_message(get_message("A bouquet with that name exists!"))
|
||||
@@ -1051,20 +1086,34 @@ class Application(Gtk.Application):
|
||||
if response == Gtk.ResponseType.CANCEL:
|
||||
return
|
||||
|
||||
key = "{}:{}".format(response, bq_type)
|
||||
key = f"{response}:{bq_type}"
|
||||
bq = response, None, None, bq_type
|
||||
|
||||
self._current_bq_name = response
|
||||
|
||||
if model.iter_n_children(itr): # parent
|
||||
ch_itr = model.insert(itr, 0, bq)
|
||||
scroll_to(model.get_path(ch_itr), view, paths)
|
||||
if not model.iter_parent(itr): # root parent
|
||||
scroll_to(model.get_path(model.insert(itr, Column.BQ_NAME, bq)), view, paths)
|
||||
else:
|
||||
p_itr = model.iter_parent(itr)
|
||||
it = model.insert(p_itr, int(model.get_path(itr)[1]) + 1, bq) if p_itr else model.append(itr, bq)
|
||||
scroll_to(model.get_path(it), view, paths)
|
||||
if sub:
|
||||
if model.iter_parent(p_itr):
|
||||
self.show_error_message("Not allowed in this context!")
|
||||
return
|
||||
else:
|
||||
if len(self._fav_model):
|
||||
msg = "This bouquet already contains data.\n\nThey may be lost when saved!"
|
||||
if show_dialog(DialogType.QUESTION, self._main_window, msg) != Gtk.ResponseType.OK:
|
||||
return
|
||||
|
||||
scroll_to(model.get_path(model.append(itr, bq)), view, paths)
|
||||
else:
|
||||
it = model.insert(p_itr, int(model.get_path(itr)[1]) + 1, bq) if p_itr else model.append(itr, bq)
|
||||
scroll_to(model.get_path(it), view, paths)
|
||||
self._bouquets[key] = []
|
||||
|
||||
def on_new_sub_bouquet(self, item=None):
|
||||
self.on_new_bouquet(self._bouquets_view, True)
|
||||
|
||||
def on_edit(self, *args):
|
||||
""" Edit header bar button """
|
||||
if self._services_view.is_focus():
|
||||
@@ -1209,6 +1258,9 @@ class Application(Gtk.Application):
|
||||
|
||||
def on_fav_view_query_tooltip(self, view, x, y, keyboard_mode, tooltip):
|
||||
""" Sets detailed info about service in the tooltip [fav view]. """
|
||||
if not self._main_window.is_active():
|
||||
return False
|
||||
|
||||
result = view.get_dest_row_at_pos(x, y)
|
||||
if not result or not self._settings.show_bq_hints:
|
||||
return False
|
||||
@@ -1217,6 +1269,9 @@ class Application(Gtk.Application):
|
||||
|
||||
def on_services_view_query_tooltip(self, view, x, y, keyboard_mode, tooltip):
|
||||
""" Sets short info about service in the tooltip [main services view]. """
|
||||
if not self._main_window.is_active():
|
||||
return False
|
||||
|
||||
result = view.get_dest_row_at_pos(x, y)
|
||||
if not result or not self._settings.show_srv_hints:
|
||||
return False
|
||||
@@ -1243,7 +1298,7 @@ class Application(Gtk.Application):
|
||||
header, ref = self.get_hint_header_info(srv)
|
||||
|
||||
if srv.service_type == "IPTV":
|
||||
return "{}{}".format(header, ref)
|
||||
return f"{header}{ref}"
|
||||
|
||||
pol = ", {}: {},".format(get_message("Pol"), srv.pol) if srv.pol else ","
|
||||
fec = "{}: {}".format("FEC", srv.fec) if srv.fec else ","
|
||||
@@ -1252,17 +1307,18 @@ class Application(Gtk.Application):
|
||||
get_message("Package"), srv.package,
|
||||
get_message("System"), srv.system,
|
||||
get_message("Freq"), srv.freq,
|
||||
get_message("Rate"), srv.rate, pol, fec, self.get_ssid_info(srv),
|
||||
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. """
|
||||
header, ref = self.get_hint_header_info(srv)
|
||||
return "{}{}\n{}".format(header, self.get_ssid_info(srv), ref)
|
||||
return f"{header}{self.get_ssid_info(srv)}\n{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"))
|
||||
header = f"{get_message('Name')}: {srv.service}\n{get_message('Type')}: {srv.service_type}\n"
|
||||
ref = f"{get_message('Service reference')}: {srv.picon_id.rstrip('.png')}"
|
||||
return header, ref
|
||||
|
||||
def get_ssid_info(self, srv):
|
||||
@@ -1271,11 +1327,11 @@ class Application(Gtk.Application):
|
||||
try:
|
||||
dec = "{0:04d}".format(int(sid, 16))
|
||||
except ValueError as e:
|
||||
log("SID value conversion error: {}".format(e))
|
||||
log(f"SID value conversion error: {e}")
|
||||
else:
|
||||
return "SID: 0x{} ({})".format(sid.upper(), dec)
|
||||
return f"SID: 0x{sid.upper()} ({dec})"
|
||||
|
||||
return "SID: 0x{}".format(sid.upper())
|
||||
return f"SID: 0x{sid.upper()}"
|
||||
|
||||
# ***************** Drag-and-drop *********************#
|
||||
|
||||
@@ -1380,15 +1436,25 @@ class Application(Gtk.Application):
|
||||
def on_view_drag_data_received(self, view, drag_context, x, y, data, info, time):
|
||||
txt = data.get_text()
|
||||
uris = data.get_uris()
|
||||
name, model = get_model_data(view)
|
||||
|
||||
if 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:
|
||||
self.picons_buffer = self.on_assign_picon(view, urlparse(unquote(uris[0])).path,
|
||||
urlparse(unquote(uris[1])).path + os.sep)
|
||||
|
||||
if uris:
|
||||
if len(uris) == 2:
|
||||
self.picons_buffer = self.on_assign_picon_file(view, urlparse(unquote(uris[0])).path,
|
||||
urlparse(unquote(uris[1])).path + os.sep)
|
||||
elif IS_DARWIN and len(uris) == 1:
|
||||
src, sep, dest = uris[0].partition(self.DRAG_SEP)
|
||||
src_path = urlparse(unquote(src)).path
|
||||
if dest:
|
||||
dest_path = urlparse(unquote(dest)).path + os.sep
|
||||
self.picons_buffer = self.on_assign_picon_file(view, src_path, dest_path)
|
||||
|
||||
drag_context.finish(True, False, time)
|
||||
|
||||
def on_bq_view_drag_data_received(self, view, drag_context, x, y, data, info, time):
|
||||
@@ -1440,7 +1506,7 @@ class Application(Gtk.Application):
|
||||
|
||||
if len(paths) > 0:
|
||||
itrs = [model.get_iter(path) for path in paths]
|
||||
return "{}::::{}".format(",".join([model.get_string_from_iter(itr) for itr in itrs]), model.get_name())
|
||||
return f"{','.join([model.get_string_from_iter(itr) for itr in itrs])}::::{model.get_name()}"
|
||||
|
||||
def receive_selection(self, *, view, drop_info, data):
|
||||
""" Update fav view after data received """
|
||||
@@ -1626,7 +1692,7 @@ class Application(Gtk.Application):
|
||||
""" 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)
|
||||
gen = self.update_data(f"{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):
|
||||
@@ -1654,7 +1720,7 @@ class Application(Gtk.Application):
|
||||
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))
|
||||
log(f"Error getting the path for the archive. Unsupported file format: {data_path}")
|
||||
self.show_error_message("Unsupported format!")
|
||||
return
|
||||
|
||||
@@ -1780,7 +1846,7 @@ class Application(Gtk.Application):
|
||||
def append_bouquet(self, bq, parent):
|
||||
name, bq_type, locked, hidden = bq.name, bq.type, bq.locked, bq.hidden
|
||||
bouquet = self._bouquets_model.append(parent, [name, locked, hidden, bq_type])
|
||||
bq_id = "{}:{}".format(name, bq_type)
|
||||
bq_id = f"{name}:{bq_type}"
|
||||
services = []
|
||||
extra_services = {} # for services with different names in bouquet and main list
|
||||
agr = [None] * 7
|
||||
@@ -1805,13 +1871,13 @@ class Application(Gtk.Application):
|
||||
self._picons.get(picon_id, None), picon_id, *agr, data_id, fav_id, None)
|
||||
self._services[fav_id] = srv
|
||||
elif s_type is BqServiceType.ALT:
|
||||
self._alt_file.add("{}:{}".format(srv.data, bq_type))
|
||||
self._alt_file.add(f"{srv.data}:{bq_type}")
|
||||
srv = Service(None, None, None, srv.name, locked, None, None, s_type.name,
|
||||
None, None, *agr, srv.data, fav_id, srv.num)
|
||||
self._services[fav_id] = srv
|
||||
elif s_type is BqServiceType.BOUQUET:
|
||||
# Sub bouquets!
|
||||
msg = "Detected sub-bouquets! This feature is not fully supported. Saving may cause bouquet data loss!"
|
||||
msg = "Detected sub-bouquets. This feature is still experimental!"
|
||||
self.show_info_message(msg, Gtk.MessageType.WARNING)
|
||||
self.append_bouquet(srv.data, bouquet)
|
||||
elif srv.name:
|
||||
@@ -1848,15 +1914,8 @@ class Application(Gtk.Application):
|
||||
factor = self.DEL_FACTOR
|
||||
|
||||
for index, srv in enumerate(services):
|
||||
tooltip, background = None, None
|
||||
if self._use_colors:
|
||||
flags = srv.flags_cas
|
||||
if flags:
|
||||
f_flags = list(filter(lambda x: x.startswith("f:"), flags.split(",")))
|
||||
if f_flags and Flag.is_new(int(f_flags[0][2:])):
|
||||
background = self._NEW_COLOR
|
||||
|
||||
s = srv._replace(picon=self._picons.get(srv.picon_id, None)) + (tooltip, background)
|
||||
background = self.get_new_background(srv.flags_cas)
|
||||
s = srv._replace(picon=self._picons.get(srv.picon_id, None)) + (None, background)
|
||||
self._services_model.append(s)
|
||||
if index % factor == 0:
|
||||
yield True
|
||||
@@ -1864,6 +1923,12 @@ class Application(Gtk.Application):
|
||||
self._services_load_spinner.stop()
|
||||
yield True
|
||||
|
||||
def get_new_background(self, flags):
|
||||
if self._use_colors and flags:
|
||||
f_flags = list(filter(lambda x: x.startswith("f:"), flags.split(",")))
|
||||
if f_flags and Flag.is_new(Flag.parse(f_flags[0])):
|
||||
return self._NEW_COLOR
|
||||
|
||||
def clear_current_data(self):
|
||||
""" Clearing current data from lists """
|
||||
if len(self._services_model) > self.DEL_FACTOR * 50:
|
||||
@@ -1954,10 +2019,7 @@ class Application(Gtk.Application):
|
||||
def parse_bouquets(model, b_path, itr):
|
||||
bqs = None
|
||||
if model.iter_has_child(itr):
|
||||
bqs = []
|
||||
num_of_children = model.iter_n_children(itr)
|
||||
for num in range(num_of_children):
|
||||
bqs.append(self.get_bouquet(model.iter_nth_child(itr, num), model))
|
||||
bqs = [self.get_bouquet(model.iter_nth_child(itr, n), model) for n in range(model.iter_n_children(itr))]
|
||||
if len(b_path) == 1:
|
||||
bouquets.append(Bouquets(*model.get(itr, Column.BQ_NAME, Column.BQ_TYPE), bqs if bqs else []))
|
||||
|
||||
@@ -1984,12 +2046,15 @@ class Application(Gtk.Application):
|
||||
|
||||
def get_bouquet(self, itr, model):
|
||||
""" Constructs and returns Bouquet class instance. """
|
||||
bq_name, locked, hidden, bq_type = model.get(itr, Column.BQ_NAME, Column.BQ_LOCKED, Column.BQ_HIDDEN,
|
||||
Column.BQ_TYPE)
|
||||
bq_id = "{}:{}".format(bq_name, bq_type)
|
||||
bq_name, locked, hidden, bq_type = model[itr][:]
|
||||
bq_id = f"{bq_name}:{bq_type}"
|
||||
favs = self._bouquets.get(bq_id, [])
|
||||
ex_s = self._extra_bouquets.get(bq_id, None)
|
||||
bq_s = list(filter(None, [self._services.get(f_id, None) for f_id in favs]))
|
||||
# Sub bouquets.
|
||||
if model.iter_has_child(itr):
|
||||
s_bs = [self.get_bouquet(model.iter_nth_child(itr, n), model) for n in range(model.iter_n_children(itr))]
|
||||
return Bouquet(bq_name, BqType.BOUQUET.value, s_bs, locked, hidden, bq_name)
|
||||
|
||||
if self._s_type is SettingsType.ENIGMA_2:
|
||||
bq_s = self.get_enigma_bq_services(bq_s, ex_s)
|
||||
@@ -2081,7 +2146,7 @@ class Application(Gtk.Application):
|
||||
|
||||
if self._current_bq_name:
|
||||
ch_row = model[model.get_iter(path)][:]
|
||||
self._bq_selected = "{}:{}".format(ch_row[Column.BQ_NAME], ch_row[Column.BQ_TYPE])
|
||||
self._bq_selected = f"{ch_row[Column.BQ_NAME]}:{ch_row[Column.BQ_TYPE]}"
|
||||
else:
|
||||
self._bq_selected = ""
|
||||
|
||||
@@ -2165,20 +2230,21 @@ class Application(Gtk.Application):
|
||||
if bqs_rows:
|
||||
bq_type = row[-1]
|
||||
for b_row in bqs_rows:
|
||||
bq_id = "{}:{}".format(b_row[Column.BQ_NAME], b_row[Column.BQ_TYPE])
|
||||
bq_id = f"{b_row[Column.BQ_NAME]}:{b_row[Column.BQ_TYPE]}"
|
||||
bq = self._bouquets.get(bq_id, None)
|
||||
if bq:
|
||||
b_row[Column.BQ_TYPE] = bq_type
|
||||
self._bouquets["{}:{}".format(b_row[Column.BQ_NAME], b_row[Column.BQ_TYPE])] = bq
|
||||
self._bouquets[f"{b_row[Column.BQ_NAME]}:{b_row[Column.BQ_TYPE]}"] = bq
|
||||
|
||||
def delete_selection(self, view, *args):
|
||||
""" Used for clear selection on given view(s) """
|
||||
""" Used for clear selection on given view(s). """
|
||||
for v in [view, *args]:
|
||||
v.get_selection().unselect_all()
|
||||
|
||||
def on_settings(self, action, value=None):
|
||||
response = show_settings_dialog(self._main_window, self._settings)
|
||||
if response != Gtk.ResponseType.CANCEL:
|
||||
dialog = SettingsDialog(self._main_window, self._settings)
|
||||
dialog.show()
|
||||
if dialog.is_updated():
|
||||
gen = self.update_settings()
|
||||
GLib.idle_add(lambda: next(gen, False), priority=GLib.PRIORITY_LOW)
|
||||
|
||||
@@ -2516,6 +2582,10 @@ class Application(Gtk.Application):
|
||||
GLib.idle_add(lambda: next(gen, False), priority=GLib.PRIORITY_LOW)
|
||||
|
||||
def on_import_data(self, path):
|
||||
if self._services_load_spinner.get_property("active"):
|
||||
self.show_error_message("Data loading in progress!")
|
||||
return
|
||||
|
||||
msg = "Combine with the current data?"
|
||||
if len(self._services_model) > 0 and show_dialog(DialogType.QUESTION, self._main_window,
|
||||
msg) == Gtk.ResponseType.OK:
|
||||
@@ -2657,11 +2727,21 @@ class Application(Gtk.Application):
|
||||
""" Shows backup tool dialog """
|
||||
BackupDialog(self._main_window, self._settings, self.open_data).show()
|
||||
|
||||
# ***************** Telnet ******************** #
|
||||
# ***************** Extra tools ******************** #
|
||||
|
||||
def on_telnet_show(self, action, value=False):
|
||||
action.set_state(value)
|
||||
GLib.idle_add(self._telnet_box.set_visible, value)
|
||||
self._telnet_box.set_visible(value)
|
||||
self.update_tools_visibility()
|
||||
|
||||
def on_logs_show(self, action, value=False):
|
||||
action.set_state(value)
|
||||
self._logs_box.set_visible(value)
|
||||
self.update_tools_visibility()
|
||||
|
||||
@run_idle
|
||||
def update_tools_visibility(self):
|
||||
self._bottom_paned.set_visible(self._telnet_box.get_visible() or self._logs_box.get_visible())
|
||||
|
||||
# ************************* Streams ***************************** #
|
||||
|
||||
@@ -2796,8 +2876,8 @@ class Application(Gtk.Application):
|
||||
if response in (Gtk.ResponseType.CANCEL, Gtk.ResponseType.DELETE_EVENT):
|
||||
return
|
||||
|
||||
with open("{}{}.m3u".format(response, s_name), "w", encoding="utf-8") as file:
|
||||
file.writelines("#EXTM3U\n#EXTVLCOPT--http-reconnect=true\n#EXTINF:-1,{}\n{}\n".format(s_name, url))
|
||||
with open(f"{response}{s_name}.m3u", "w", encoding="utf-8") as file:
|
||||
file.writelines(f"#EXTM3U\n#EXTVLCOPT--http-reconnect=true\n#EXTINF:-1,{s_name}\n{url}\n")
|
||||
except IOError as e:
|
||||
self.show_error_message(str(e))
|
||||
else:
|
||||
@@ -2898,6 +2978,8 @@ class Application(Gtk.Application):
|
||||
error_code = info.get("error_code", 0) if info else 0
|
||||
GLib.idle_add(self._receiver_info_box.set_visible, error_code == 0, priority=GLib.PRIORITY_LOW)
|
||||
if error_code < 0:
|
||||
if self._page is Page.CONTROL:
|
||||
GLib.idle_add(self._control_tool.update_signal, None)
|
||||
return
|
||||
elif error_code == 412:
|
||||
self._http_api.init()
|
||||
@@ -3005,6 +3087,7 @@ class Application(Gtk.Application):
|
||||
""" Setting defaults for filter elements. """
|
||||
self._filter_entry.set_text("")
|
||||
self._filter_only_free_button.set_active(False)
|
||||
self._filter_not_in_bq_button.set_active(False)
|
||||
self._filter_types_model.foreach(lambda m, p, i: m.set_value(i, 1, True))
|
||||
self._service_types.update({r[0] for r in self._filter_types_model})
|
||||
self.update_sat_positions()
|
||||
@@ -3071,14 +3154,16 @@ class Application(Gtk.Application):
|
||||
|
||||
txt = self._filter_entry.get_text().upper()
|
||||
for r in self._services_model:
|
||||
fav_id = r[Column.SRV_FAV_ID]
|
||||
free = not r[Column.SRV_CODED] if self._filter_only_free_button.get_active() else True
|
||||
self._filter_cache[r[Column.SRV_FAV_ID]] = all((r[Column.SRV_TYPE] in self._service_types,
|
||||
r[Column.SRV_POS] in self._sat_positions, free,
|
||||
txt in "".join((r[Column.SRV_SERVICE],
|
||||
r[Column.SRV_PACKAGE],
|
||||
r[Column.SRV_TYPE],
|
||||
r[Column.SRV_SSID],
|
||||
r[Column.SRV_POS])).upper()))
|
||||
self._filter_cache[fav_id] = all((free, fav_id not in self._in_bouquets,
|
||||
r[Column.SRV_TYPE] in self._service_types,
|
||||
r[Column.SRV_POS] in self._sat_positions,
|
||||
txt in "".join((r[Column.SRV_SERVICE],
|
||||
r[Column.SRV_PACKAGE],
|
||||
r[Column.SRV_TYPE],
|
||||
r[Column.SRV_SSID],
|
||||
r[Column.SRV_POS])).upper()))
|
||||
|
||||
def services_filter_function(self, model, itr, data):
|
||||
return self._filter_cache.get(model.get_value(itr, Column.SRV_FAV_ID), True)
|
||||
@@ -3089,6 +3174,16 @@ class Application(Gtk.Application):
|
||||
def on_filter_satellite_toggled(self, toggle, path):
|
||||
self.update_filter_toogle_model(self._filter_sat_pos_model, toggle, path, self._sat_positions)
|
||||
|
||||
@run_idle
|
||||
def on_filter_in_bq_toggled(self, button):
|
||||
if button.get_active():
|
||||
self._in_bouquets.update(chain.from_iterable(self._bouquets.values()))
|
||||
else:
|
||||
self._in_bouquets.clear()
|
||||
|
||||
if self._filter_services_button.get_active():
|
||||
self.on_filter_changed()
|
||||
|
||||
def update_filter_toogle_model(self, model, toggle, path, values_set):
|
||||
active = not toggle.get_active()
|
||||
if path == "0":
|
||||
@@ -3166,13 +3261,13 @@ class Application(Gtk.Application):
|
||||
if response == Gtk.ResponseType.CANCEL:
|
||||
return
|
||||
|
||||
bq = "{}:{}".format(response, bq_type)
|
||||
bq = f"{response}:{bq_type}"
|
||||
if bq in self._bouquets:
|
||||
self.show_error_message(get_message("A bouquet with that name exists!"))
|
||||
return
|
||||
|
||||
model.set_value(itr, 0, response)
|
||||
old_bq_name = "{}:{}".format(bq_name, bq_type)
|
||||
old_bq_name = f"{bq_name}:{bq_type}"
|
||||
self._bouquets[bq] = self._bouquets.pop(old_bq_name)
|
||||
self._bq_file[bq] = self._bq_file.pop(old_bq_name, None)
|
||||
self._current_bq_name = response
|
||||
@@ -3263,6 +3358,47 @@ class Application(Gtk.Application):
|
||||
if r[Column.FAV_SERVICE] in dup:
|
||||
r[Column.FAV_BACKGROUND] = self._NEW_COLOR
|
||||
|
||||
def on_services_mark_not_in_bouquets(self, item):
|
||||
if self._services_load_spinner.get_property("active"):
|
||||
self.show_error_message("Data loading in progress!")
|
||||
return
|
||||
|
||||
gen = self.mark_not_in_bouquets()
|
||||
GLib.idle_add(lambda: next(gen, False), priority=GLib.PRIORITY_LOW)
|
||||
|
||||
def mark_not_in_bouquets(self):
|
||||
self._services_load_spinner.start()
|
||||
ids = set(chain.from_iterable(self._bouquets.values()))
|
||||
|
||||
for index, row in enumerate(self._services_model):
|
||||
fav_id = row[Column.SRV_FAV_ID]
|
||||
if fav_id not in ids:
|
||||
row[Column.SRV_BACKGROUND] = self._EXTRA_COLOR
|
||||
|
||||
if index % self.FAV_FACTOR == 0:
|
||||
yield True
|
||||
|
||||
self._services_load_spinner.stop()
|
||||
yield True
|
||||
|
||||
def on_services_clear_marked(self, item):
|
||||
if self._services_load_spinner.get_property("active"):
|
||||
self.show_error_message("Data loading in progress!")
|
||||
return
|
||||
|
||||
gen = self.clear_marked()
|
||||
GLib.idle_add(lambda: next(gen, False), priority=GLib.PRIORITY_LOW)
|
||||
|
||||
def clear_marked(self):
|
||||
self._services_load_spinner.start()
|
||||
for index, row in enumerate(self._services_model):
|
||||
row[Column.SRV_BACKGROUND] = self.get_new_background(row[Column.SRV_CAS_FLAGS])
|
||||
if index % self.FAV_FACTOR == 0:
|
||||
yield True
|
||||
|
||||
self._services_load_spinner.stop()
|
||||
yield True
|
||||
|
||||
# ***************** Picons *********************#
|
||||
|
||||
@run_task
|
||||
@@ -3274,6 +3410,10 @@ class Application(Gtk.Application):
|
||||
return get_picon_pixbuf(f"{self._settings.profile_picons_path}{p_id}", self._settings.list_picon_size)
|
||||
|
||||
def on_assign_picon(self, view, src_path=None, dst_path=None):
|
||||
self._stack.set_visible_child_name(Page.PICONS.value)
|
||||
self.emit("picon-assign", self.get_target_view(view))
|
||||
|
||||
def on_assign_picon_file(self, view, src_path=None, dst_path=None):
|
||||
return assign_picons(self.get_target_view(view), self._services_view, self._fav_view, self._main_window,
|
||||
self._picons, self._settings, self._services, src_path, dst_path)
|
||||
|
||||
@@ -3318,6 +3458,10 @@ class Application(Gtk.Application):
|
||||
self.show_error_message("Data loading in progress!")
|
||||
return
|
||||
|
||||
if not len(self._bouquets_model):
|
||||
self.show_error_message("No bouquets config is loaded. Load or create a new config!")
|
||||
return
|
||||
|
||||
gen_bouquets(self._services_view, self._bouquets_view, self._main_window, g_type, self._s_type,
|
||||
self.append_bouquet)
|
||||
|
||||
@@ -3347,14 +3491,14 @@ class Application(Gtk.Application):
|
||||
bq_name, sep, bq_type = self._bq_selected.partition(":")
|
||||
fav_id = srv.fav_id
|
||||
|
||||
key = "de{:02d}:{}".format(self._alt_counter, bq_type)
|
||||
key = f"de{self._alt_counter:02d}:{bq_type}"
|
||||
# Generating file name for alternative
|
||||
while key in self._alt_file:
|
||||
self._alt_counter += 1
|
||||
key = "de{:02d}:{}".format(self._alt_counter, bq_type)
|
||||
key = f"de{self._alt_counter:02d}:{bq_type}"
|
||||
|
||||
alt_name = "de{:02d}".format(self._alt_counter)
|
||||
alt_id = "alternatives_{}_{}".format(self._bq_selected, fav_id)
|
||||
alt_name = f"de{self._alt_counter:02d}"
|
||||
alt_id = f"alternatives_{self._bq_selected}_{fav_id}"
|
||||
if alt_id in bq:
|
||||
self.show_error_message("A similar service is already in this list!")
|
||||
return
|
||||
@@ -3366,7 +3510,7 @@ class Application(Gtk.Application):
|
||||
try:
|
||||
index = bq.index(fav_id)
|
||||
except ValueError as e:
|
||||
log("[on_add_alternatives] error: {}".format(e))
|
||||
log(f"[on_add_alternatives] error: {e}")
|
||||
else:
|
||||
bq[index] = alt_id
|
||||
self._services[alt_id] = a_srv
|
||||
@@ -3485,16 +3629,16 @@ class Application(Gtk.Application):
|
||||
@run_idle
|
||||
def update_profile_label(self):
|
||||
label, sep, ip = self._current_ip_label.get_text().partition(":")
|
||||
self._current_ip_label.set_text("{}: {}".format(label, self._settings.host))
|
||||
self._current_ip_label.set_text(f"{label}: {self._settings.host}")
|
||||
|
||||
profile_name = self._profile_combo_box.get_active_text()
|
||||
msg = get_message("Profile:")
|
||||
|
||||
if self._s_type is SettingsType.ENIGMA_2:
|
||||
title = "DemonEditor [{} {} - Enigma2 v.{}]".format(msg, profile_name, self.get_format_version())
|
||||
title = f"DemonEditor [{msg} {profile_name} - Enigma2 v.{self.get_format_version()}]"
|
||||
self._main_window.set_title(title)
|
||||
elif self._s_type is SettingsType.NEUTRINO_MP:
|
||||
self._main_window.set_title("DemonEditor [{} {} - Neutrino-MP]".format(msg, profile_name))
|
||||
self._main_window.set_title(f"DemonEditor [{msg} {profile_name} - Neutrino-MP]")
|
||||
|
||||
def get_format_version(self):
|
||||
return 5 if self._settings.v5_support else 4
|
||||
@@ -3595,12 +3739,16 @@ def start_app():
|
||||
try:
|
||||
Settings.get_instance()
|
||||
except SettingsReadException as e:
|
||||
msg = "{}\n {}".format(get_message("Error reading or writing program settings!"), e)
|
||||
msg = f"{get_message('Error reading or writing program settings!')}\n {e}"
|
||||
show_dialog(DialogType.INFO, transient=Gtk.Dialog(), text=msg)
|
||||
except SettingsException as e:
|
||||
msg = "{} \n{}".format(e, get_message("All setting were reset. Restart the program!"))
|
||||
show_dialog(DialogType.INFO, transient=Gtk.Dialog(), text=msg)
|
||||
msg = f"{e}\n\n{get_message('It is recommended to load the default settings!')}"
|
||||
dlg = Gtk.Dialog()
|
||||
if show_dialog(DialogType.QUESTION, dlg, msg) != Gtk.ResponseType.OK:
|
||||
return True
|
||||
|
||||
Settings.reset_to_default()
|
||||
show_dialog(DialogType.INFO, transient=dlg, text=get_message("All setting were reset. Restart the program!"))
|
||||
else:
|
||||
app = Application()
|
||||
app.run(sys.argv)
|
||||
|
||||
@@ -26,7 +26,14 @@
|
||||
#
|
||||
|
||||
|
||||
""" Helper module for the ui. """
|
||||
""" Helper module for the GUI. """
|
||||
|
||||
__all__ = ("insert_marker", "move_items", "rename", "ViewTarget", "set_flags", "locate_in_services",
|
||||
"scroll_to", "get_base_model", "update_picons_data", "copy_picon_reference", "assign_picons",
|
||||
"remove_picon", "is_only_one_item_selected", "gen_bouquets", "BqGenType", "append_picons",
|
||||
"get_selection", "get_model_data", "remove_all_unused_picons", "get_picon_pixbuf", "get_base_itrs",
|
||||
"get_iptv_url", "update_entry_data", "append_text_to_tview", "on_popup_menu")
|
||||
|
||||
import os
|
||||
import shutil
|
||||
from collections import defaultdict
|
||||
|
||||
@@ -33,6 +33,26 @@ Author: Dmitriy Yefremov
|
||||
<!-- interface-name DemonEditor -->
|
||||
<!-- interface-copyright 2018-2021 Dmitriy Yefremov -->
|
||||
<!-- interface-authors Dmitriy Yefremov -->
|
||||
<object class="GtkMenu" id="add_menu">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<child>
|
||||
<object class="GtkMenuItem" id="open_menu_item">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">Open folder</property>
|
||||
<signal name="activate" handler="on_add" swapped="no"/>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkMenuItem" id="extract_menu_item">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">Extract...</property>
|
||||
<signal name="activate" handler="on_extract" swapped="no"/>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<object class="GtkListStore" id="picons_dest_list_store">
|
||||
<columns>
|
||||
<!-- column-name picon -->
|
||||
@@ -176,11 +196,6 @@ Author: Dmitriy Yefremov
|
||||
<column type="gboolean"/>
|
||||
</columns>
|
||||
</object>
|
||||
<object class="GtkImage" id="remove_selection_image">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="icon_name">edit-undo</property>
|
||||
</object>
|
||||
<object class="GtkMenu" id="providers_popup_menu">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
@@ -199,12 +214,16 @@ Author: Dmitriy Yefremov
|
||||
<property name="label" translatable="yes">Remove selection</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="image">remove_selection_image</property>
|
||||
<property name="use_stock">False</property>
|
||||
<signal name="activate" handler="on_unselect_all" object="providers_view" swapped="no"/>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<object class="GtkImage" id="remove_selection_image">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="icon_name">edit-undo</property>
|
||||
</object>
|
||||
<object class="GtkListStore" id="satellites_list_store">
|
||||
<columns>
|
||||
<!-- column-name satellite -->
|
||||
@@ -215,7 +234,7 @@ Author: Dmitriy Yefremov
|
||||
<column type="gchararray"/>
|
||||
</columns>
|
||||
</object>
|
||||
<object class="GtkFrame" id="picon_manager_frame">
|
||||
<object class="GtkFrame" id="main_frame">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label_xalign">0.49000000953674316</property>
|
||||
@@ -227,7 +246,6 @@ Author: Dmitriy Yefremov
|
||||
<property name="margin_left">5</property>
|
||||
<property name="margin_right">5</property>
|
||||
<property name="margin_top">2</property>
|
||||
<property name="margin_bottom">5</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<object class="GtkBox" id="header_box">
|
||||
@@ -249,7 +267,7 @@ Author: Dmitriy Yefremov
|
||||
<object class="GtkImage" id="convert_button_image">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="icon_name">edit-redo-symbolic</property>
|
||||
<property name="icon_name">object-rotate-right-symbolic</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
@@ -270,7 +288,7 @@ Author: Dmitriy Yefremov
|
||||
<object class="GtkImage" id="cancel_button_image">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="stock">gtk-cancel</property>
|
||||
<property name="stock">gtk-close</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
@@ -285,6 +303,29 @@ Author: Dmitriy Yefremov
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="spacing">5</property>
|
||||
<child>
|
||||
<object class="GtkMenuButton" id="add_menu_button">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="focus_on_click">False</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="tooltip_text" translatable="yes">Add picons</property>
|
||||
<property name="popup">add_menu</property>
|
||||
<child>
|
||||
<object class="GtkImage">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="icon_name">list-add</property>
|
||||
<property name="icon_size">0</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="receive_button">
|
||||
<property name="sensitive">False</property>
|
||||
@@ -385,56 +426,84 @@ Author: Dmitriy Yefremov
|
||||
<object class="GtkButtonBox" id="header_button_box">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="halign">center</property>
|
||||
<property name="layout_style">expand</property>
|
||||
<child>
|
||||
<object class="GtkRadioButton" id="manager_button">
|
||||
<property name="label" translatable="yes">Explorer</property>
|
||||
<property name="width_request">50</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">False</property>
|
||||
<property name="tooltip_text" translatable="yes">Explorer</property>
|
||||
<property name="active">True</property>
|
||||
<property name="draw_indicator">False</property>
|
||||
<property name="group">converter_button</property>
|
||||
<signal name="toggled" handler="on_tool_switched" swapped="no"/>
|
||||
<child>
|
||||
<object class="GtkImage">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="icon_name">folder-open-symbolic</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
<property name="non_homogeneous">True</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkRadioButton" id="downloader_button">
|
||||
<property name="label" translatable="yes">Downloader</property>
|
||||
<property name="width_request">50</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">False</property>
|
||||
<property name="tooltip_text" translatable="yes">Downloader</property>
|
||||
<property name="active">True</property>
|
||||
<property name="draw_indicator">False</property>
|
||||
<property name="group">manager_button</property>
|
||||
<signal name="toggled" handler="on_tool_switched" swapped="no"/>
|
||||
<child>
|
||||
<object class="GtkImage">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="icon_name">insert-image-symbolic</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
<property name="non_homogeneous">True</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkRadioButton" id="converter_button">
|
||||
<property name="label" translatable="yes">Converter</property>
|
||||
<property name="width_request">50</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">False</property>
|
||||
<property name="tooltip_text" translatable="yes">Converter</property>
|
||||
<property name="active">True</property>
|
||||
<property name="draw_indicator">False</property>
|
||||
<property name="group">manager_button</property>
|
||||
<signal name="toggled" handler="on_tool_switched" swapped="no"/>
|
||||
<child>
|
||||
<object class="GtkImage">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="icon_name">object-rotate-right-symbolic</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">2</property>
|
||||
<property name="non_homogeneous">True</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
@@ -521,11 +590,11 @@ Author: Dmitriy Yefremov
|
||||
<object class="GtkStack" id="stack">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_bottom">5</property>
|
||||
<child>
|
||||
<object class="GtkBox" id="explorer_box">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_top">3</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<property name="spacing">5</property>
|
||||
<child>
|
||||
@@ -540,6 +609,7 @@ Author: Dmitriy Yefremov
|
||||
<property name="primary_icon_name">edit-find-replace-symbolic</property>
|
||||
<property name="primary_icon_activatable">False</property>
|
||||
<property name="primary_icon_sensitive">False</property>
|
||||
<property name="placeholder_text" translatable="yes">Filter: N1|N2|N3, etc..</property>
|
||||
<signal name="search-changed" handler="on_picons_filter_changed" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
@@ -549,10 +619,10 @@ Author: Dmitriy Yefremov
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkSwitch" id="auto_filer_switch">
|
||||
<object class="GtkSwitch" id="auto_filter_switch">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="tooltip_text" translatable="yes">Automatically set the name selected in the favorites list. </property>
|
||||
<property name="tooltip_text" translatable="yes">Automatically set the name selected in the favorites list.</property>
|
||||
<property name="valign">center</property>
|
||||
<property name="margin_right">5</property>
|
||||
</object>
|
||||
@@ -564,7 +634,7 @@ Author: Dmitriy Yefremov
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="auto_filer_label">
|
||||
<object class="GtkLabel" id="auto_filter_label">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">Auto</property>
|
||||
@@ -580,7 +650,7 @@ Author: Dmitriy Yefremov
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
@@ -606,7 +676,7 @@ Author: Dmitriy Yefremov
|
||||
<property name="can_focus">False</property>
|
||||
<child>
|
||||
<object class="GtkCheckButton" id="src_filter_button">
|
||||
<property name="label" translatable="yes">Filer</property>
|
||||
<property name="label" translatable="yes">Filter</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="focus_on_click">False</property>
|
||||
<property name="receives_default">False</property>
|
||||
@@ -636,7 +706,7 @@ Author: Dmitriy Yefremov
|
||||
<object class="GtkTreeView" id="picons_src_view">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="tooltip_text" translatable="yes">Drag the services to the desired picon or picon to the list of selected services. </property>
|
||||
<property name="tooltip_text" translatable="yes">Drag the services to the desired picon or picon to the list of selected services.</property>
|
||||
<property name="model">picons_src_sort_model</property>
|
||||
<property name="enable_grid_lines">horizontal</property>
|
||||
<property name="tooltip_column">0</property>
|
||||
@@ -725,7 +795,7 @@ Author: Dmitriy Yefremov
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<property name="spacing">5</property>
|
||||
<property name="spacing">2</property>
|
||||
<child>
|
||||
<object class="GtkBox" id="dst_title_box">
|
||||
<property name="visible">True</property>
|
||||
@@ -826,7 +896,7 @@ Author: Dmitriy Yefremov
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">3</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
@@ -847,71 +917,48 @@ Author: Dmitriy Yefremov
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">2</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkFrame" id="explorer_info_bar_frame">
|
||||
<object class="GtkFrame" id="explorer_info_box_frame">
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label_xalign">0</property>
|
||||
<property name="shadow_type">in</property>
|
||||
<child>
|
||||
<object class="GtkInfoBar" id="explorer_info_bar">
|
||||
<object class="GtkBox" id="info_box">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="message_type">other</property>
|
||||
<child internal-child="action_area">
|
||||
<object class="GtkButtonBox">
|
||||
<property name="margin_top">5</property>
|
||||
<property name="margin_bottom">5</property>
|
||||
<property name="spacing">5</property>
|
||||
<child>
|
||||
<object class="GtkImage" id="picon_info_image">
|
||||
<property name="width_request">128</property>
|
||||
<property name="height_request">72</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="spacing">6</property>
|
||||
<property name="layout_style">end</property>
|
||||
<property name="stock">gtk-missing-image</property>
|
||||
<property name="icon_size">6</property>
|
||||
<signal name="drag-data-received" handler="on_picon_info_image_drag_data_received" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child internal-child="content_area">
|
||||
<object class="GtkBox" id="explorer_info_bar_box">
|
||||
<property name="can_focus">False</property>
|
||||
<property name="spacing">10</property>
|
||||
<child>
|
||||
<object class="GtkImage" id="picon_info_image">
|
||||
<property name="width_request">128</property>
|
||||
<property name="height_request">72</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="stock">gtk-missing-image</property>
|
||||
<property name="icon_size">6</property>
|
||||
<signal name="drag-data-received" handler="on_picon_info_image_drag_data_received" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="picon_info_label">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="ellipsize">end</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">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
<object class="GtkLabel" id="picon_info_label">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="ellipsize">end</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
@@ -922,7 +969,7 @@ Author: Dmitriy Yefremov
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">6</property>
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
@@ -1220,7 +1267,6 @@ Author: Dmitriy Yefremov
|
||||
<property name="height_request">150</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="margin_bottom">2</property>
|
||||
<property name="shadow_type">out</property>
|
||||
<child>
|
||||
<object class="GtkTreeView" id="providers_view">
|
||||
@@ -1347,33 +1393,6 @@ Author: Dmitriy Yefremov
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="picons_dir_label">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">Current picons path:</property>
|
||||
<property name="xalign">0.019999999552965164</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">4</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkEntry" id="picons_dir_entry">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="secondary_icon_name">folder-open</property>
|
||||
<property name="primary_icon_activatable">False</property>
|
||||
<signal name="icon-press" handler="on_picons_dir_open" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">5</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="resize">True</property>
|
||||
@@ -1403,7 +1422,7 @@ Author: Dmitriy Yefremov
|
||||
<object class="GtkGrid" id="format_box">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="column_spacing">2</property>
|
||||
<property name="column_spacing">5</property>
|
||||
<property name="column_homogeneous">True</property>
|
||||
<child>
|
||||
<object class="GtkFrame">
|
||||
@@ -1415,6 +1434,7 @@ Author: Dmitriy Yefremov
|
||||
<object class="GtkBox" id="name_format_box">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_bottom">5</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<object class="GtkBox" id="box2">
|
||||
@@ -1489,6 +1509,7 @@ Author: Dmitriy Yefremov
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="halign">center</property>
|
||||
<property name="margin_bottom">5</property>
|
||||
<child>
|
||||
<object class="GtkRadioButton" id="resize_no_radio_button">
|
||||
<property name="label" translatable="yes">No(default)</property>
|
||||
@@ -1696,41 +1717,162 @@ Author: Dmitriy Yefremov
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkExpander" id="expander">
|
||||
<property name="visible">True</property>
|
||||
<object class="GtkFrame" id="info_bar_frame">
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_top">5</property>
|
||||
<property name="label_xalign">0</property>
|
||||
<property name="shadow_type">in</property>
|
||||
<child>
|
||||
<object class="GtkScrolledWindow" id="scrolled_window">
|
||||
<property name="height_request">150</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="shadow_type">in</property>
|
||||
<property name="min_content_width">240</property>
|
||||
<child>
|
||||
<object class="GtkTextView" id="text_view">
|
||||
<object class="GtkInfoBar" id="info_bar">
|
||||
<property name="can_focus">False</property>
|
||||
<property name="baseline_position">bottom</property>
|
||||
<property name="message_type">other</property>
|
||||
<property name="show_close_button">True</property>
|
||||
<child internal-child="action_area">
|
||||
<object class="GtkButtonBox">
|
||||
<property name="can_focus">False</property>
|
||||
<property name="layout_style">end</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child internal-child="content_area">
|
||||
<object class="GtkBox" id="info_bar_box">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="editable">False</property>
|
||||
<property name="wrap_mode">word-char</property>
|
||||
<property name="left_margin">5</property>
|
||||
<property name="right_margin">5</property>
|
||||
<property name="overwrite">True</property>
|
||||
<child>
|
||||
<object class="GtkScrolledWindow" id="info_scrolled_window">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="shadow_type">in</property>
|
||||
<child>
|
||||
<object class="GtkTextView" id="info_view">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="editable">False</property>
|
||||
<property name="left_margin">5</property>
|
||||
<property name="right_margin">5</property>
|
||||
<property name="top_margin">2</property>
|
||||
<property name="bottom_margin">2</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child type="label">
|
||||
<object class="GtkLabel" id="expander_label">
|
||||
<child type="label_item">
|
||||
<placeholder/>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">3</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkBox" id="bottom_bar_box">
|
||||
<property name="height_request">24</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_left">5</property>
|
||||
<property name="margin_right">5</property>
|
||||
<property name="margin_top">2</property>
|
||||
<property name="spacing">5</property>
|
||||
<child>
|
||||
<object class="GtkImage" id="count_image">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">Extra:</property>
|
||||
<property name="icon_name">document-properties</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="dst_count_label">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">0</property>
|
||||
<property name="width_chars">4</property>
|
||||
<property name="xalign">0</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkSeparator">
|
||||
<property name="visible">True</property>
|
||||
<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="GtkImage" id="bottom_bar_path_image">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="tooltip_text" translatable="yes">Current picons path</property>
|
||||
<property name="icon_name">document-open-symbolic</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">3</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="current_path_label">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="tooltip_text" translatable="yes">Current picons path</property>
|
||||
<property name="label" translatable="yes">/</property>
|
||||
<property name="ellipsize">end</property>
|
||||
<property name="xalign">0</property>
|
||||
<attributes>
|
||||
<attribute name="size" value="8000"/>
|
||||
</attributes>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">4</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">2</property>
|
||||
<property name="position">4</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
|
||||
195
app/ui/picons.py
195
app/ui/picons.py
@@ -35,16 +35,16 @@ from urllib.parse import urlparse, unquote
|
||||
|
||||
from gi.repository import GLib, GdkPixbuf, Gio
|
||||
|
||||
from app.commons import run_idle, run_task, run_with_delay
|
||||
from app.commons import run_idle, run_task, run_with_delay, log
|
||||
from app.connections import upload_data, DownloadType, download_data, remove_picons
|
||||
from app.settings import SettingsType, Settings, SEP
|
||||
from app.settings import SettingsType, Settings, SEP, IS_LINUX, IS_DARWIN
|
||||
from app.tools.picons import (PiconsParser, parse_providers, Provider, convert_to, download_picon, PiconsCzDownloader,
|
||||
PiconsError)
|
||||
from app.tools.satellites import SatellitesParser, SatelliteSource
|
||||
from .dialogs import show_dialog, DialogType, get_message, get_builder
|
||||
from .dialogs import show_dialog, DialogType, get_message, get_builder, get_chooser_dialog
|
||||
from .main_helper import (update_entry_data, append_text_to_tview, scroll_to, on_popup_menu, get_base_model, set_picon,
|
||||
get_picon_pixbuf)
|
||||
from .uicommons import Gtk, Gdk, UI_RESOURCES_PATH, TV_ICON, Column, KeyboardKey, Page
|
||||
from .uicommons import Gtk, Gdk, UI_RESOURCES_PATH, TV_ICON, Column, KeyboardKey, Page, ViewTarget
|
||||
|
||||
|
||||
class PiconManager(Gtk.Box):
|
||||
@@ -58,6 +58,8 @@ class PiconManager(Gtk.Box):
|
||||
self._app = app
|
||||
self._app.connect("page-changed", self.update_picons_dest)
|
||||
self._app.connect("filter-toggled", self.on_app_filter_toggled)
|
||||
self._app.connect("profile-changed", self.on_profile_changed)
|
||||
self._app.connect("picon-assign", self.on_picon_assign)
|
||||
self._app.fav_view.connect("row-activated", self.on_fav_changed)
|
||||
self._picon_ids = picon_ids
|
||||
self._sat_positions = sat_positions
|
||||
@@ -78,6 +80,8 @@ class PiconManager(Gtk.Box):
|
||||
self._picon_cz_downloader = None
|
||||
|
||||
handlers = {"on_tool_switched": self.on_tool_switched,
|
||||
"on_add": self.on_add,
|
||||
"on_extract": self.on_extract,
|
||||
"on_receive": self.on_receive,
|
||||
"on_cancel": self.on_cancel,
|
||||
"on_send": self.on_send,
|
||||
@@ -127,15 +131,10 @@ class PiconManager(Gtk.Box):
|
||||
self._picons_src_filter_model.set_visible_func(self.picons_src_filter_function)
|
||||
self._picons_dst_filter_model = builder.get_object("picons_dst_filter_model")
|
||||
self._picons_dst_filter_model.set_visible_func(self.picons_dst_filter_function)
|
||||
self._expander = builder.get_object("expander")
|
||||
self._text_view = builder.get_object("text_view")
|
||||
self._src_filter_button = builder.get_object("src_filter_button")
|
||||
self._dst_filter_button = builder.get_object("dst_filter_button")
|
||||
self._picons_filter_entry = builder.get_object("picons_filter_entry")
|
||||
self._picons_dir_entry = builder.get_object("picons_dir_entry")
|
||||
self._info_check_button = builder.get_object("info_check_button")
|
||||
self._picon_info_image = builder.get_object("picon_info_image")
|
||||
self._picon_info_label = builder.get_object("picon_info_label")
|
||||
self._current_path_label = builder.get_object("current_path_label")
|
||||
self._download_source_button = builder.get_object("download_source_button")
|
||||
self._receive_button = builder.get_object("receive_button")
|
||||
self._convert_button = builder.get_object("convert_button")
|
||||
@@ -163,9 +162,18 @@ class PiconManager(Gtk.Box):
|
||||
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._download_source_button.bind_property("visible", self._receive_button, "visible")
|
||||
# Info.
|
||||
self._dst_count_label = builder.get_object("dst_count_label")
|
||||
self._info_check_button = builder.get_object("info_check_button")
|
||||
self._picon_info_image = builder.get_object("picon_info_image")
|
||||
self._picon_info_label = builder.get_object("picon_info_label")
|
||||
self._info_view = builder.get_object("info_view")
|
||||
self._info_bar = builder.get_object("info_bar")
|
||||
self._info_bar.bind_property("visible", builder.get_object("info_bar_frame"), "visible")
|
||||
self._info_bar.connect("response", lambda b, r: b.set_visible(False))
|
||||
# Filter.
|
||||
self._filter_bar = builder.get_object("filter_bar")
|
||||
self._auto_filer_switch = builder.get_object("auto_filer_switch")
|
||||
self._auto_filter_switch = builder.get_object("auto_filter_switch")
|
||||
self._filter_button = builder.get_object("filter_button")
|
||||
self._filter_button.bind_property("active", self._filter_bar, "visible")
|
||||
self._filter_button.bind_property("active", self._src_filter_button, "visible")
|
||||
@@ -178,9 +186,7 @@ class PiconManager(Gtk.Box):
|
||||
self._src_button.bind_property("active", builder.get_object("explorer_dst_label"), "visible")
|
||||
self._src_button.bind_property("active", builder.get_object("src_picon_box_frame"), "visible")
|
||||
self._filter_button.bind_property("visible", self._src_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")
|
||||
self._info_check_button.bind_property("active", builder.get_object("explorer_info_box_frame"), "visible")
|
||||
# Header buttons. -> Used instead stack switcher.
|
||||
self._manager_button = builder.get_object("manager_button")
|
||||
self._manager_button.bind_property("active", builder.get_object("manager_label"), "visible")
|
||||
@@ -188,14 +194,15 @@ class PiconManager(Gtk.Box):
|
||||
self._downloader_button.bind_property("active", builder.get_object("downloader_label"), "visible")
|
||||
self._converter_button = builder.get_object("converter_button")
|
||||
self._converter_button.bind_property("active", builder.get_object("converter_label"), "visible")
|
||||
self._manager_button.bind_property("active", builder.get_object("add_menu_button"), "visible")
|
||||
# Init drag-and-drop
|
||||
self.init_drag_and_drop()
|
||||
# Settings
|
||||
self._settings = settings
|
||||
self._s_type = settings.setting_type
|
||||
self._picons_dir_entry.set_text(self._settings.profile_picons_path)
|
||||
self._current_path_label.set_text(self._settings.profile_picons_path)
|
||||
|
||||
self.pack_start(builder.get_object("picon_manager_frame"), True, True, 0)
|
||||
self.pack_start(builder.get_object("main_frame"), True, True, 0)
|
||||
self.show()
|
||||
|
||||
if not len(self._picon_ids) and self._s_type is SettingsType.ENIGMA_2:
|
||||
@@ -240,6 +247,23 @@ class PiconManager(Gtk.Box):
|
||||
self._services = {s.picon_id: s for s in self._app.current_services.values() if s.picon_id}
|
||||
self.update_picons_data(self._picons_dest_view)
|
||||
|
||||
def on_profile_changed(self, app, data):
|
||||
self._current_path_label.set_text(self._settings.profile_picons_path)
|
||||
self.update_picons_dest(app, self._app.page)
|
||||
|
||||
def on_picon_assign(self, app, target):
|
||||
if target is ViewTarget.SERVICES:
|
||||
model, paths = app.services_view.get_selection().get_selected_rows()
|
||||
ids = {model[p][Column.SRV_FAV_ID] for p in paths}
|
||||
else:
|
||||
model, paths = app.fav_view.get_selection().get_selected_rows()
|
||||
ids = {model[p][Column.FAV_ID] for p in paths}
|
||||
|
||||
self._filter_button.set_active(True)
|
||||
self._dst_filter_button.set_active(True)
|
||||
self._picons_filter_entry.set_text(
|
||||
"|".join(s.service for f, s in self._app.current_services.items() if f in ids))
|
||||
|
||||
def update_picons_data(self, view, path=None):
|
||||
if view is self._picons_dest_view:
|
||||
self.update_picon_info()
|
||||
@@ -257,18 +281,24 @@ class PiconManager(Gtk.Box):
|
||||
if index % factor == 0:
|
||||
yield True
|
||||
|
||||
self._dst_count_label.set_text("0")
|
||||
if not os.path.isdir(path):
|
||||
return
|
||||
|
||||
for file in os.listdir(path):
|
||||
for index, file in enumerate(os.listdir(path)):
|
||||
if self._terminate:
|
||||
return
|
||||
|
||||
p_path = "{}{}{}".format(path, SEP, file)
|
||||
p_path = f"{path}{SEP}{file}"
|
||||
p = self.get_pixbuf_at_scale(p_path, 72, 48, True)
|
||||
if p:
|
||||
yield model.append((p, file, p_path))
|
||||
model.append((p, file, p_path))
|
||||
|
||||
if index % factor == 0:
|
||||
self._dst_count_label.set_text(str(len(model)))
|
||||
yield True
|
||||
|
||||
self._dst_count_label.set_text(str(len(model)))
|
||||
yield True
|
||||
|
||||
def update_picons_from_file(self, view, uri):
|
||||
@@ -323,8 +353,11 @@ class PiconManager(Gtk.Box):
|
||||
def on_picons_view_drag_data_get(self, view, drag_context, data, info, time):
|
||||
model, path = view.get_selection().get_selected_rows()
|
||||
if path:
|
||||
data.set_uris([Path(model[path][-1]).as_uri(),
|
||||
Path(self._settings.profile_picons_path).as_uri()])
|
||||
dest_uri = Path(self._settings.profile_picons_path).as_uri()
|
||||
if IS_DARWIN:
|
||||
data.set_uris([f"{Path(model[path][-1]).as_uri()}{self._app.DRAG_SEP}{dest_uri}"])
|
||||
else:
|
||||
data.set_uris([Path(model[path][-1]).as_uri(), dest_uri])
|
||||
|
||||
def on_picons_view_drag_drop(self, view, drag_context, x, y, time):
|
||||
view.stop_emission_by_name("drag_drop")
|
||||
@@ -341,7 +374,7 @@ class PiconManager(Gtk.Box):
|
||||
self.update_picons_from_file(view, txt)
|
||||
return
|
||||
|
||||
itr_str, sep, src = txt.partition("::::")
|
||||
itr_str, sep, src = txt.partition(self._app.DRAG_SEP)
|
||||
if src == self._app.BQ_MODEL_NAME:
|
||||
return
|
||||
|
||||
@@ -359,7 +392,7 @@ class PiconManager(Gtk.Box):
|
||||
|
||||
t_mod = target_view.get_model()
|
||||
dest_path = self._settings.profile_picons_path
|
||||
self.update_picons_dest_view(self._app.on_assign_picon(target_view, model[path][-1], dest_path))
|
||||
self.update_picons_dest_view(self._app.on_assign_picon_file(target_view, model[path][-1], dest_path))
|
||||
self.show_assign_info([t_mod.get_value(t_mod.get_iter_from_string(itr), c_id) for itr in itr_str.split(",")])
|
||||
|
||||
@run_idle
|
||||
@@ -380,15 +413,17 @@ class PiconManager(Gtk.Box):
|
||||
itr = dest_model.append((p, p_name, p_path))
|
||||
scroll_to(dest_model.get_path(itr), self._picons_dest_view)
|
||||
|
||||
self._dst_count_label.set_text(str(len(dest_model)))
|
||||
|
||||
@run_idle
|
||||
def show_assign_info(self, fav_ids):
|
||||
self._expander.set_expanded(True)
|
||||
self._text_view.get_buffer().set_text("")
|
||||
self._info_bar.show()
|
||||
self._info_view.get_buffer().set_text("")
|
||||
for i in fav_ids:
|
||||
srv = self._app.current_services.get(i, None)
|
||||
if srv:
|
||||
info = self._app.get_hint_for_srv_list(srv)
|
||||
self.append_output("Picon assignment for the service:\n{}\n{}\n".format(info, " * " * 30))
|
||||
self.append_output(f"Picon assignment for the service:\n{info}\n{' * ' * 30}\n")
|
||||
|
||||
def on_picons_view_drag_end(self, view, drag_context):
|
||||
self.update_picons_dest_view(self._app.picons_buffer)
|
||||
@@ -402,7 +437,7 @@ class PiconManager(Gtk.Box):
|
||||
if len(uris) == 2:
|
||||
name, fav_id = self._current_picon_info
|
||||
src = urlparse(unquote(uris[0])).path
|
||||
dst = "{}{}{}".format(urlparse(unquote(uris[1])).path, SEP, name)
|
||||
dst = f"{urlparse(unquote(uris[1])).path}{SEP}{name}"
|
||||
if src != dst:
|
||||
shutil.copy(src, dst)
|
||||
for row in get_base_model(self._picons_dest_view.get_model()):
|
||||
@@ -439,6 +474,81 @@ class PiconManager(Gtk.Box):
|
||||
yield set_picon(fav_id, get_base_model(self._app.services_view.get_model()), picon, Column.SRV_FAV_ID, p_pos)
|
||||
yield set_picon(fav_id, get_base_model(self._app.fav_view.get_model()), picon, Column.FAV_ID, p_pos)
|
||||
|
||||
# ************************ Add/Extract ******************************** #
|
||||
|
||||
def on_add(self, item):
|
||||
""" Adds (copies) picons from an external folder to the profile picons folder. """
|
||||
dialog = self.get_copy_dialog()
|
||||
if dialog.run() in (Gtk.ResponseType.CANCEL, Gtk.ResponseType.DELETE_EVENT):
|
||||
return
|
||||
|
||||
self.copy_picons_file(dialog.get_filenames())
|
||||
|
||||
def get_copy_dialog(self):
|
||||
""" Returns a copy dialog with a preview of images [picons -> *.png]. """
|
||||
dialog = Gtk.FileChooserNative.new(get_message("Add picons"), self._app_window,
|
||||
Gtk.FileChooserAction.OPEN, get_message("Add"))
|
||||
dialog.set_select_multiple(True)
|
||||
dialog.set_modal(True)
|
||||
# Filter.
|
||||
file_filter = Gtk.FileFilter()
|
||||
file_filter.set_name("*.png")
|
||||
file_filter.add_pattern("*.png")
|
||||
file_filter.add_mime_type("image/png") if IS_DARWIN else None
|
||||
dialog.add_filter(file_filter)
|
||||
|
||||
if IS_LINUX:
|
||||
preview_image = Gtk.Image(margin_right=10)
|
||||
dialog.set_preview_widget(preview_image)
|
||||
|
||||
def update_preview_widget(dlg):
|
||||
path = dialog.get_preview_filename()
|
||||
if not path:
|
||||
return
|
||||
|
||||
pix = get_picon_pixbuf(path, 220)
|
||||
preview_image.set_from_pixbuf(pix)
|
||||
dlg.set_preview_widget_active(bool(pix))
|
||||
|
||||
dialog.connect("update-preview", update_preview_widget)
|
||||
|
||||
return dialog
|
||||
|
||||
def on_extract(self, item):
|
||||
""" Extracts picons from an archives to the profile picons folder. """
|
||||
file_filter = None
|
||||
if IS_DARWIN:
|
||||
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 = get_chooser_dialog(self._app_window, self._settings, "*.zip, *.gz files",
|
||||
("*.zip", "*.gz"), "Extract picons", file_filter)
|
||||
if response in (Gtk.ResponseType.CANCEL, Gtk.ResponseType.DELETE_EVENT):
|
||||
return
|
||||
|
||||
arch_path = self._app.get_archive_path(response)
|
||||
if arch_path:
|
||||
self.copy_picons_file(Path(arch_path.name).glob("*.png"), arch_path.cleanup)
|
||||
|
||||
def copy_picons_file(self, files, callback=None):
|
||||
""" Copies files to the profile picons folder. """
|
||||
picon_path = self._settings.profile_picons_path
|
||||
os.makedirs(os.path.dirname(picon_path), exist_ok=True)
|
||||
|
||||
try:
|
||||
picons = [shutil.copy(p, picon_path) for p in files]
|
||||
except shutil.SameFileError as e:
|
||||
log(e)
|
||||
self.show_info_message(str(e), Gtk.MessageType.ERROR)
|
||||
else:
|
||||
self.update_picons_dest_view(picons)
|
||||
self._app.update_picons()
|
||||
finally:
|
||||
if callback:
|
||||
callback()
|
||||
|
||||
# ******************** Download/Upload/Remove ************************* #
|
||||
|
||||
def on_selective_send(self, view):
|
||||
@@ -468,10 +578,14 @@ class PiconManager(Gtk.Box):
|
||||
itr = filter_model.convert_iter_to_child_iter(model.convert_iter_to_child_iter(itr))
|
||||
base_model.remove(itr)
|
||||
|
||||
if view is self._picons_dest_view:
|
||||
self._dst_count_label.set_text(str(len(model)))
|
||||
|
||||
def on_send(self, item=None, files_filter=None, path=None):
|
||||
dest_path = path or self._settings.profile_picons_path
|
||||
settings = Settings(self._settings.settings)
|
||||
settings.profile_picons_path = "{}{}".format(dest_path, SEP)
|
||||
settings.profile_picons_path = f"{dest_path}{SEP}"
|
||||
settings.current_profile = self._settings.current_profile
|
||||
self.show_info_message(get_message("Please, wait..."), Gtk.MessageType.INFO)
|
||||
self.run_func(lambda: upload_data(settings=settings,
|
||||
download_type=DownloadType.PICONS,
|
||||
@@ -484,6 +598,7 @@ class PiconManager(Gtk.Box):
|
||||
path = path or self._settings.profile_picons_path
|
||||
settings = Settings(self._settings.settings)
|
||||
settings.profile_picons_path = path + SEP
|
||||
settings.current_profile = self._settings.current_profile
|
||||
self.run_func(lambda: download_data(settings=settings,
|
||||
download_type=DownloadType.PICONS,
|
||||
callback=self.append_output,
|
||||
@@ -602,7 +717,7 @@ class PiconManager(Gtk.Box):
|
||||
try:
|
||||
for sat in sorted(sats):
|
||||
pos = sat[1]
|
||||
name = "{} ({})".format(sat[0], pos)
|
||||
name = f"{sat[0]} ({pos})"
|
||||
if is_filter and pos not in self._sat_positions:
|
||||
continue
|
||||
if not model:
|
||||
@@ -663,7 +778,7 @@ class PiconManager(Gtk.Box):
|
||||
@run_task
|
||||
def start_download(self, providers):
|
||||
self._is_downloading = True
|
||||
GLib.idle_add(self._expander.set_expanded, True)
|
||||
GLib.idle_add(self._info_bar.set_visible, True)
|
||||
|
||||
for prv in providers:
|
||||
if self._download_src is self.DownloadSource.LYNG_SAT and not self._POS_PATTERN.match(prv[2]):
|
||||
@@ -673,7 +788,7 @@ class PiconManager(Gtk.Box):
|
||||
return
|
||||
|
||||
try:
|
||||
picons_path = self._picons_dir_entry.get_text()
|
||||
picons_path = self._current_path_label.get_text()
|
||||
os.makedirs(os.path.dirname(picons_path), exist_ok=True)
|
||||
self.show_info_message(get_message("Please, wait..."), Gtk.MessageType.INFO)
|
||||
providers = (Provider(*p) for p in providers)
|
||||
@@ -731,7 +846,7 @@ class PiconManager(Gtk.Box):
|
||||
for p in providers:
|
||||
self._picon_cz_downloader.download(p, path, p_ids)
|
||||
except PiconsError as e:
|
||||
self.append_output("Error: {}\n".format(str(e)))
|
||||
self.append_output(f"Error: {str(e)}\n")
|
||||
self.show_info_message(str(e), Gtk.MessageType.ERROR)
|
||||
else:
|
||||
self.show_info_message(get_message("Done!"), Gtk.MessageType.INFO)
|
||||
@@ -752,12 +867,12 @@ class PiconManager(Gtk.Box):
|
||||
return {services.get(fav_id).picon_id for fav_id in fav_bouquet}
|
||||
|
||||
def process_provider(self, prv, picons_path):
|
||||
self.append_output("Getting links to picons for: {}.\n".format(prv.name))
|
||||
self.append_output(f"Getting links to picons for: {prv.name}.\n")
|
||||
return PiconsParser.parse(prv, picons_path, self._picon_ids, self.get_picons_format())
|
||||
|
||||
@run_idle
|
||||
def append_output(self, char):
|
||||
append_text_to_tview(char, self._text_view)
|
||||
append_text_to_tview(char, self._info_view)
|
||||
|
||||
@run_task
|
||||
def resize(self, path):
|
||||
@@ -767,7 +882,7 @@ class PiconManager(Gtk.Box):
|
||||
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)
|
||||
self.show_info_message(f"{get_message('Conversion error.')} {e}", Gtk.MessageType.ERROR)
|
||||
else:
|
||||
res = (220, 132) if self._resize_220_132_radio_button.get_active() else (100, 60)
|
||||
|
||||
@@ -802,7 +917,7 @@ class PiconManager(Gtk.Box):
|
||||
@run_task
|
||||
def run_func(self, func, update=False):
|
||||
try:
|
||||
GLib.idle_add(self._expander.set_expanded, True)
|
||||
GLib.idle_add(self._info_bar.set_visible, True)
|
||||
GLib.idle_add(self._header_download_box.set_sensitive, False)
|
||||
func()
|
||||
except OSError as e:
|
||||
@@ -841,7 +956,7 @@ class PiconManager(Gtk.Box):
|
||||
self._filter_button.set_active(not self._filter_button.get_active())
|
||||
|
||||
def on_fav_changed(self, view, path, column):
|
||||
if self._app.page is Page.PICONS and self._auto_filer_switch.get_active():
|
||||
if self._app.page is Page.PICONS and self._auto_filter_switch.get_active():
|
||||
model = view.get_model()
|
||||
self._picons_filter_entry.set_text(model.get_value(model.get_iter(path), Column.FAV_SERVICE))
|
||||
|
||||
@@ -856,10 +971,10 @@ class PiconManager(Gtk.Box):
|
||||
|
||||
@run_with_delay(0.5)
|
||||
def on_picons_filter_changed(self, entry):
|
||||
txt = entry.get_text().upper()
|
||||
self._filter_cache.clear()
|
||||
txt = entry.get_text().upper().split("|")
|
||||
for s in self._app.current_services.values():
|
||||
self._filter_cache[s.picon_id] = txt in s.service.upper()
|
||||
self._filter_cache[s.picon_id] = any(t in s.service.upper() or t in str(s.picon_id) for t in txt)
|
||||
|
||||
GLib.idle_add(self._picons_src_filter_model.refilter, priority=GLib.PRIORITY_LOW)
|
||||
GLib.idle_add(self._picons_dst_filter_model.refilter, priority=GLib.PRIORITY_LOW)
|
||||
@@ -954,7 +1069,7 @@ class PiconManager(Gtk.Box):
|
||||
self._app.show_error_message("Select paths!")
|
||||
return
|
||||
|
||||
self._expander.set_expanded(True)
|
||||
self._info_bar.set_visible(True)
|
||||
convert_to(src_path=picons_path,
|
||||
dest_path=save_path,
|
||||
s_type=SettingsType.ENIGMA_2,
|
||||
|
||||
@@ -148,7 +148,7 @@
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="tooltip_text" translatable="yes">Audio Track</property>
|
||||
<property name="icon_name">multimedia-volume-control</property>
|
||||
<property name="icon_name">audio-volume-high</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
|
||||
@@ -36,9 +36,9 @@ from app.connections import HttpAPI
|
||||
from app.eparser.ecommons import BqServiceType
|
||||
from app.settings import PlayStreamsMode, IS_DARWIN, SettingsType
|
||||
from app.tools.media import Player
|
||||
from app.ui.dialogs import get_builder
|
||||
from app.ui.dialogs import get_builder, get_message
|
||||
from app.ui.main_helper import get_iptv_url
|
||||
from app.ui.uicommons import Gtk, Gdk, UI_RESOURCES_PATH, FavClickMode, Column, IS_GNOME_SESSION
|
||||
from app.ui.uicommons import Gtk, Gdk, UI_RESOURCES_PATH, FavClickMode, Column, IS_GNOME_SESSION, Page
|
||||
|
||||
|
||||
class PlayerBox(Gtk.Box):
|
||||
@@ -295,7 +295,7 @@ class PlayerBox(Gtk.Box):
|
||||
""" 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)
|
||||
return f"{str(h) + ':' if h else ''}{m:02d}:{s:02d}"
|
||||
|
||||
def set_player_area_size(self, widget):
|
||||
w, h = self._app.app_window.get_size()
|
||||
@@ -310,6 +310,7 @@ class PlayerBox(Gtk.Box):
|
||||
|
||||
if self._playback_window:
|
||||
self._playback_window.show()
|
||||
self._playback_window.set_title(self.get_playback_title())
|
||||
else:
|
||||
self._playback_window = Gtk.Window(title=self.get_playback_title(),
|
||||
window_position=Gtk.WindowPosition.CENTER,
|
||||
@@ -331,10 +332,13 @@ class PlayerBox(Gtk.Box):
|
||||
self._playback_window.show()
|
||||
|
||||
def get_playback_title(self):
|
||||
path, column = self._fav_view.get_cursor()
|
||||
if path:
|
||||
return "DemonEditor [{}]".format(self._app.fav_view.get_model()[path][:][Column.FAV_SERVICE])
|
||||
return "DemonEditor [Playback]"
|
||||
if self._app.page is not Page.RECORDINGS:
|
||||
path, column = self._fav_view.get_cursor()
|
||||
if path:
|
||||
return f"DemonEditor [{self._app.fav_view.get_model()[path][:][Column.FAV_SERVICE]}]"
|
||||
else:
|
||||
return f"DemonEditor [{get_message('Recordings')}]"
|
||||
return f"DemonEditor [{get_message('Playback')}]"
|
||||
|
||||
def on_play_stream(self):
|
||||
path, column = self._fav_view.get_cursor()
|
||||
@@ -396,11 +400,13 @@ class PlayerBox(Gtk.Box):
|
||||
else:
|
||||
self._current_mrl = url
|
||||
|
||||
@run_idle
|
||||
def on_played(self, player, duration):
|
||||
GLib.idle_add(self._fav_view.set_sensitive, True)
|
||||
self._fav_view.set_sensitive(True)
|
||||
if not IS_DARWIN:
|
||||
self.on_duration_changed(duration)
|
||||
|
||||
@run_idle
|
||||
def on_error(self, player, msg):
|
||||
self._app.show_error_message(msg)
|
||||
self._fav_view.set_sensitive(True)
|
||||
|
||||
@@ -166,7 +166,7 @@ Author: Dmitriy Yefremov
|
||||
<property name="use_underline">True</property>
|
||||
<property name="use_stock">True</property>
|
||||
<signal name="activate" handler="on_edit" object="satellite_view" swapped="no"/>
|
||||
<accelerator key="e" signal="activate" modifiers="GDK_CONTROL_MASK"/>
|
||||
<accelerator key="e" signal="activate" modifiers="Primary"/>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
@@ -220,7 +220,7 @@ Author: Dmitriy Yefremov
|
||||
<property name="use_underline">True</property>
|
||||
<property name="use_stock">True</property>
|
||||
<signal name="activate" handler="on_edit" object="transponder_view" swapped="no"/>
|
||||
<accelerator key="e" signal="activate" modifiers="GDK_CONTROL_MASK"/>
|
||||
<accelerator key="e" signal="activate" modifiers="Primary"/>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
@@ -292,24 +292,12 @@ Author: Dmitriy Yefremov
|
||||
<property name="icon_name">window-close-symbolic</property>
|
||||
<property name="icon_size">1</property>
|
||||
</object>
|
||||
<object class="GtkImage" id="sat_update_filter_image">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="icon_name">edit-find-replace-symbolic</property>
|
||||
<property name="icon_size">1</property>
|
||||
</object>
|
||||
<object class="GtkImage" id="sat_update_image">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="icon_name">emblem-synchronizing-symbolic</property>
|
||||
<property name="icon_size">1</property>
|
||||
</object>
|
||||
<object class="GtkImage" id="sat_update_search_image">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="icon_name">edit-find-symbolic</property>
|
||||
<property name="icon_size">1</property>
|
||||
</object>
|
||||
<object class="GtkListStore" id="satellite_view_model">
|
||||
<columns>
|
||||
<!-- column-name name -->
|
||||
@@ -1413,9 +1401,7 @@ Author: Dmitriy Yefremov
|
||||
<object class="GtkBox" id="satellites_update_main_box">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_bottom">2</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<property name="spacing">5</property>
|
||||
<child>
|
||||
<object class="GtkBox" id="sat_update_header">
|
||||
<property name="visible">True</property>
|
||||
@@ -1496,7 +1482,7 @@ Author: Dmitriy Yefremov
|
||||
<property name="image">sat_update_cancel_image</property>
|
||||
<property name="always_show_image">True</property>
|
||||
<signal name="clicked" handler="on_cancel_receive" swapped="no"/>
|
||||
<accelerator key="z" signal="clicked" modifiers="GDK_CONTROL_MASK"/>
|
||||
<accelerator key="z" signal="clicked" modifiers="Primary"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
@@ -1518,15 +1504,21 @@ Author: Dmitriy Yefremov
|
||||
<property name="layout_style">expand</property>
|
||||
<child>
|
||||
<object class="GtkToggleButton" id="sat_update_filter_button">
|
||||
<property name="label" translatable="yes">Filter</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="tooltip_text" translatable="yes">Filter</property>
|
||||
<property name="image">sat_update_filter_image</property>
|
||||
<property name="always_show_image">True</property>
|
||||
<signal name="toggled" handler="on_filter_toggled" swapped="no"/>
|
||||
<accelerator key="f" signal="clicked" modifiers="GDK_SHIFT_MASK | GDK_CONTROL_MASK"/>
|
||||
<child>
|
||||
<object class="GtkImage" id="sat_update_filter_image">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="icon_name">edit-find-replace-symbolic</property>
|
||||
<property name="icon_size">1</property>
|
||||
</object>
|
||||
</child>
|
||||
<accelerator key="f" signal="clicked" modifiers="GDK_SHIFT_MASK | Primary"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
@@ -1536,15 +1528,21 @@ Author: Dmitriy Yefremov
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkToggleButton" id="sat_update_find_button">
|
||||
<property name="label" translatable="yes">Search</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="tooltip_text" translatable="yes">Find</property>
|
||||
<property name="image">sat_update_search_image</property>
|
||||
<property name="always_show_image">True</property>
|
||||
<signal name="toggled" handler="on_find_toggled" swapped="no"/>
|
||||
<accelerator key="f" signal="clicked" modifiers="GDK_CONTROL_MASK"/>
|
||||
<child>
|
||||
<object class="GtkImage" id="sat_update_search_image">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="icon_name">edit-find-symbolic</property>
|
||||
<property name="icon_size">1</property>
|
||||
</object>
|
||||
</child>
|
||||
<accelerator key="f" signal="clicked" modifiers="Primary"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
@@ -1584,6 +1582,8 @@ Author: Dmitriy Yefremov
|
||||
<object class="GtkBox" id="search_bar_box">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_top">2</property>
|
||||
<property name="margin_bottom">2</property>
|
||||
<child>
|
||||
<object class="GtkSearchEntry" id="sat_update_search_entry">
|
||||
<property name="width_request">200</property>
|
||||
@@ -1658,6 +1658,7 @@ Author: Dmitriy Yefremov
|
||||
<object class="GtkGrid" id="source_header_grid">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_top">2</property>
|
||||
<property name="margin_bottom">2</property>
|
||||
<property name="column_spacing">2</property>
|
||||
<child>
|
||||
@@ -1779,6 +1780,8 @@ Author: Dmitriy Yefremov
|
||||
<property name="can_focus">True</property>
|
||||
<property name="margin_left">5</property>
|
||||
<property name="margin_right">5</property>
|
||||
<property name="margin_top">5</property>
|
||||
<property name="margin_bottom">5</property>
|
||||
<property name="wide_handle">True</property>
|
||||
<child>
|
||||
<object class="GtkFrame" id="sat_update_frame">
|
||||
@@ -2099,42 +2102,70 @@ Author: Dmitriy Yefremov
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkExpander" id="sat_update_expander">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="margin_left">1</property>
|
||||
<property name="margin_right">1</property>
|
||||
<property name="margin_top">2</property>
|
||||
<property name="margin_bottom">2</property>
|
||||
<object class="GtkFrame" id="log_frame">
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_left">4</property>
|
||||
<property name="margin_right">5</property>
|
||||
<property name="margin_bottom">5</property>
|
||||
<property name="label_xalign">0</property>
|
||||
<property name="shadow_type">in</property>
|
||||
<child>
|
||||
<object class="GtkScrolledWindow" id="text_view_scrolled_window">
|
||||
<property name="height_request">120</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="shadow_type">in</property>
|
||||
<child>
|
||||
<object class="GtkTextView" id="text_view">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="editable">False</property>
|
||||
<property name="left_margin">5</property>
|
||||
<property name="right_margin">5</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child type="label">
|
||||
<object class="GtkLabel" id="Extra:">
|
||||
<object class="GtkInfoBar" id="log_info_bar">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">Extra:</property>
|
||||
<property name="message_type">other</property>
|
||||
<property name="show_close_button">True</property>
|
||||
<child internal-child="action_area">
|
||||
<object class="GtkButtonBox" id="log_info_bar_button_box">
|
||||
<property name="can_focus">False</property>
|
||||
<property name="spacing">6</property>
|
||||
<property name="layout_style">end</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child internal-child="content_area">
|
||||
<object class="GtkBox" id="log_info_bar_box">
|
||||
<property name="can_focus">False</property>
|
||||
<child>
|
||||
<object class="GtkScrolledWindow" id="text_view_scrolled_window">
|
||||
<property name="height_request">120</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="shadow_type">in</property>
|
||||
<child>
|
||||
<object class="GtkTextView" id="text_view">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="editable">False</property>
|
||||
<property name="left_margin">5</property>
|
||||
<property name="right_margin">5</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">5</property>
|
||||
<property name="position">4</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
@@ -2148,9 +2179,6 @@ Author: Dmitriy Yefremov
|
||||
<property name="can_focus">False</property>
|
||||
<property name="spacing">6</property>
|
||||
<property name="layout_style">end</property>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
@@ -2181,9 +2209,6 @@ Author: Dmitriy Yefremov
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
|
||||
@@ -435,8 +435,7 @@ class UpdateDialog:
|
||||
"update_sat_list_model_filter", "update_sat_list_model_sort", "side_store",
|
||||
"pos_adjustment", "pos_adjustment2", "satellites_update_popup_menu",
|
||||
"remove_selection_image", "sat_update_cancel_image", "sat_receive_image",
|
||||
"sat_update_filter_image", "sat_update_search_image", "sat_update_image",
|
||||
"update_transponder_store", "update_service_store"))
|
||||
"sat_update_image", "update_transponder_store", "update_service_store"))
|
||||
|
||||
self._window = builder.get_object("satellites_update_window")
|
||||
self._window.set_transient_for(transient)
|
||||
@@ -448,7 +447,6 @@ class UpdateDialog:
|
||||
self._transponder_view = builder.get_object("sat_update_tr_view")
|
||||
self._service_view = builder.get_object("sat_update_srv_view")
|
||||
self._source_box = builder.get_object("source_combo_box")
|
||||
self._sat_update_expander = builder.get_object("sat_update_expander")
|
||||
self._text_view = builder.get_object("text_view")
|
||||
self._receive_button = builder.get_object("receive_data_button")
|
||||
self._sat_update_info_bar = builder.get_object("sat_update_info_bar")
|
||||
@@ -470,7 +468,10 @@ class UpdateDialog:
|
||||
self._filter_model.set_visible_func(self.filter_function)
|
||||
self._filter_positions = (0, 0)
|
||||
self._filter_bar.bind_property("search-mode-enabled", self._filter_bar, "visible")
|
||||
# Search
|
||||
# Log.
|
||||
self._log_frame = builder.get_object("log_frame")
|
||||
builder.get_object("log_info_bar").connect("response", lambda b, r: self._log_frame.set_visible(False))
|
||||
# Search.
|
||||
self._search_bar = builder.get_object("sat_update_search_bar")
|
||||
self._search_bar.bind_property("search-mode-enabled", self._search_bar, "visible")
|
||||
search_provider = SearchProvider(self._sat_view,
|
||||
@@ -541,8 +542,8 @@ class UpdateDialog:
|
||||
return
|
||||
|
||||
@run_idle
|
||||
def update_expander(self):
|
||||
self._sat_update_expander.set_expanded(True)
|
||||
def update_log_visibility(self):
|
||||
self._log_frame.set_visible(True)
|
||||
self._text_view.get_buffer().set_text("", 0)
|
||||
|
||||
def append_output(self):
|
||||
@@ -652,7 +653,7 @@ class SatellitesUpdateDialog(UpdateDialog):
|
||||
@run_task
|
||||
def receive_satellites(self):
|
||||
self.is_download = True
|
||||
self.update_expander()
|
||||
self.update_log_visibility()
|
||||
model = self._sat_view.get_model()
|
||||
start = time.time()
|
||||
|
||||
@@ -744,7 +745,7 @@ class ServicesUpdateDialog(UpdateDialog):
|
||||
@run_task
|
||||
def receive_services(self):
|
||||
self.is_download = True
|
||||
self.update_expander()
|
||||
self.update_log_visibility()
|
||||
model = self._sat_view.get_model()
|
||||
appender = self.append_output()
|
||||
next(appender)
|
||||
|
||||
@@ -275,6 +275,38 @@ Author: Dmitriy Yefremov
|
||||
<child type="titlebar">
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child type="action">
|
||||
<object class="GtkButton" id="cancel_button">
|
||||
<property name="label" translatable="yes">Cancel</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="valign">center</property>
|
||||
<signal name="clicked" handler="on_cancel" swapped="no"/>
|
||||
</object>
|
||||
</child>
|
||||
<child type="action">
|
||||
<object class="GtkButton" id="apply_button">
|
||||
<property name="label" translatable="yes">Apply</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="tooltip_text" translatable="yes">Save current service</property>
|
||||
<property name="valign">center</property>
|
||||
<signal name="clicked" handler="on_save" swapped="no"/>
|
||||
<accelerator key="Return" signal="activate"/>
|
||||
</object>
|
||||
</child>
|
||||
<child type="action">
|
||||
<object class="GtkButton" id="create_button">
|
||||
<property name="label" translatable="yes">Create</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="tooltip_text" translatable="yes">Create and save as new service</property>
|
||||
<property name="valign">center</property>
|
||||
<signal name="clicked" handler="on_create_new" swapped="no"/>
|
||||
</object>
|
||||
</child>
|
||||
<child internal-child="vbox">
|
||||
<object class="GtkBox" id="dialog_vbox">
|
||||
<property name="can_focus">False</property>
|
||||
@@ -287,53 +319,6 @@ Author: Dmitriy Yefremov
|
||||
<property name="can_focus">False</property>
|
||||
<property name="valign">center</property>
|
||||
<property name="layout_style">end</property>
|
||||
<child>
|
||||
<object class="GtkButton" id="cancel_button">
|
||||
<property name="label" translatable="yes">Cancel</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="valign">center</property>
|
||||
<signal name="clicked" handler="on_cancel" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="apply_button">
|
||||
<property name="label" translatable="yes">Apply</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="tooltip_text" translatable="yes">Save current service</property>
|
||||
<property name="valign">center</property>
|
||||
<signal name="clicked" handler="on_save" swapped="no"/>
|
||||
<accelerator key="Return" signal="activate"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="create_button">
|
||||
<property name="label" translatable="yes">Create</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="tooltip_text" translatable="yes">Create and save as new service</property>
|
||||
<property name="valign">center</property>
|
||||
<signal name="clicked" handler="on_create_new" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
@@ -1644,6 +1629,9 @@ Author: Dmitriy Yefremov
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<action-widgets>
|
||||
<action-widget response="-6">cancel_button</action-widget>
|
||||
</action-widgets>
|
||||
</object>
|
||||
<object class="GtkListStore" id="transponder_services_liststore">
|
||||
<columns>
|
||||
@@ -1676,6 +1664,24 @@ Author: Dmitriy Yefremov
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child type="action">
|
||||
<object class="GtkButton" id="tr_services_no_button">
|
||||
<property name="label" translatable="yes">Cancel</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="valign">center</property>
|
||||
</object>
|
||||
</child>
|
||||
<child type="action">
|
||||
<object class="GtkButton" id="tr_services_ok_button">
|
||||
<property name="label" translatable="yes">OK</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="valign">center</property>
|
||||
</object>
|
||||
</child>
|
||||
<child internal-child="vbox">
|
||||
<object class="GtkBox" id="tr_services_dialog_vbox">
|
||||
<property name="can_focus">False</property>
|
||||
@@ -1685,34 +1691,6 @@ Author: Dmitriy Yefremov
|
||||
<object class="GtkButtonBox">
|
||||
<property name="can_focus">False</property>
|
||||
<property name="valign">center</property>
|
||||
<child>
|
||||
<object class="GtkButton" id="tr_services_no_button">
|
||||
<property name="label" translatable="yes">Cancel</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="valign">center</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="tr_services_ok_button">
|
||||
<property name="label" translatable="yes">OK</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="valign">center</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
|
||||
@@ -259,7 +259,7 @@ class ServiceDetailsDialog:
|
||||
def init_enigma2_flags(self, flags):
|
||||
f_flags = list(filter(lambda x: x.startswith("f:"), flags))
|
||||
if f_flags:
|
||||
value = int(f_flags[0][2:])
|
||||
value = Flag.parse(f_flags[0])
|
||||
self._keep_check_button.set_active(Flag.is_keep(value))
|
||||
self._hide_check_button.set_active(Flag.is_hide(value))
|
||||
self._use_pids_check_button.set_active(Flag.is_pids(value))
|
||||
@@ -468,7 +468,7 @@ class ServiceDetailsDialog:
|
||||
extra_data = {Column.SRV_TOOLTIP: None, Column.SRV_BACKGROUND: None}
|
||||
if self._s_type is SettingsType.ENIGMA_2 and flags:
|
||||
f_flags = list(filter(lambda x: x.startswith("f:"), flags.split(",")))
|
||||
if f_flags and Flag.is_new(int(f_flags[0][2:])):
|
||||
if f_flags and Flag.is_new(Flag.parse(f_flags[0])):
|
||||
extra_data[Column.SRV_BACKGROUND] = self._new_color
|
||||
|
||||
self._current_model.set(self._current_itr, extra_data)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -34,7 +34,7 @@ from app.connections import test_telnet, test_ftp, TestException, test_http, Htt
|
||||
from app.settings import SettingsType, Settings, PlayStreamsMode, IS_LINUX, SEP, IS_WIN
|
||||
from app.ui.dialogs import show_dialog, DialogType, get_message, get_chooser_dialog, get_builder
|
||||
from .main_helper import update_entry_data, scroll_to, get_picon_pixbuf
|
||||
from .uicommons import Gtk, Gdk, UI_RESOURCES_PATH, FavClickMode, DEFAULT_ICON, APP_FONT
|
||||
from .uicommons import Gtk, Gdk, UI_RESOURCES_PATH, FavClickMode, DEFAULT_ICON, APP_FONT, IS_GNOME_SESSION
|
||||
|
||||
|
||||
def show_settings_dialog(transient, options):
|
||||
@@ -50,7 +50,6 @@ class SettingsDialog:
|
||||
"on_settings_type_changed": self.on_settings_type_changed,
|
||||
"on_reset": self.on_reset,
|
||||
"on_response": self.on_response,
|
||||
"apply_settings": self.apply_settings,
|
||||
"on_connection_test": self.on_connection_test,
|
||||
"on_info_bar_close": self.on_info_bar_close,
|
||||
"on_set_color_switch": self.on_set_color_switch,
|
||||
@@ -86,11 +85,12 @@ class SettingsDialog:
|
||||
"on_icon_theme_add": self.on_icon_theme_add,
|
||||
"on_icon_theme_remove": self.on_icon_theme_remove}
|
||||
|
||||
# Settings
|
||||
# Settings.
|
||||
self._ext_settings = settings
|
||||
self._settings = Settings(settings.settings)
|
||||
self._profiles = self._settings.profiles
|
||||
self._s_type = self._settings.setting_type
|
||||
self._updated = False
|
||||
|
||||
builder = get_builder(UI_RESOURCES_PATH + "settings_dialog.glade", handlers)
|
||||
|
||||
@@ -135,7 +135,7 @@ class SettingsDialog:
|
||||
self._neutrino_radio_button = builder.get_object("neutrino_radio_button")
|
||||
self._support_ver5_switch = builder.get_object("support_ver5_switch")
|
||||
self._force_bq_name_switch = builder.get_object("force_bq_name_switch")
|
||||
# Streaming
|
||||
# Streaming.
|
||||
self._apply_presets_button = builder.get_object("apply_presets_button")
|
||||
self._transcoding_switch = builder.get_object("transcoding_switch")
|
||||
self._edit_preset_switch = builder.get_object("edit_preset_switch")
|
||||
@@ -192,7 +192,6 @@ class SettingsDialog:
|
||||
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")
|
||||
@@ -200,6 +199,9 @@ class SettingsDialog:
|
||||
self._profile_view = builder.get_object("profile_tree_view")
|
||||
self._profile_add_button = builder.get_object("profile_add_button")
|
||||
self._profile_remove_button = builder.get_object("profile_remove_button")
|
||||
# Network.
|
||||
# Separated due to a bug with response (presumably in the builder) in ubuntu 18.04 and derivatives.
|
||||
builder.get_object("network_settings_frame").add(builder.get_object("network_box"))
|
||||
# Style.
|
||||
self._style_provider = Gtk.CssProvider()
|
||||
self._style_provider.load_from_path(UI_RESOURCES_PATH + "style.css")
|
||||
@@ -208,6 +210,16 @@ class SettingsDialog:
|
||||
for el in self._digit_elems:
|
||||
el.get_style_context().add_provider_for_screen(Gdk.Screen.get_default(), self._style_provider,
|
||||
Gtk.STYLE_PROVIDER_PRIORITY_USER)
|
||||
|
||||
if IS_GNOME_SESSION:
|
||||
switcher = builder.get_object("main_stack_switcher")
|
||||
switcher.set_margin_top(0)
|
||||
switcher.set_margin_bottom(0)
|
||||
builder.get_object("main_box").remove(switcher)
|
||||
header_bar = Gtk.HeaderBar(visible=True, show_close_button=True)
|
||||
header_bar.set_custom_title(switcher)
|
||||
self._dialog.set_titlebar(header_bar)
|
||||
|
||||
self.init_ui_elements()
|
||||
self.init_profiles()
|
||||
|
||||
@@ -233,8 +245,7 @@ class SettingsDialog:
|
||||
self._neutrino_radio_button.set_active(self._s_type is SettingsType.NEUTRINO_MP)
|
||||
self.update_picon_paths()
|
||||
self.update_title()
|
||||
http_active = self._support_http_api_switch.get_active()
|
||||
self._click_mode_zap_button.set_sensitive(is_enigma_profile and http_active)
|
||||
self._click_mode_zap_button.set_sensitive(self._support_http_api_switch.get_active())
|
||||
self._lang_combo_box.set_active_id(self._ext_settings.language)
|
||||
self.on_info_bar_close() if is_enigma_profile else self.show_info_message(
|
||||
"The Neutrino has only experimental support. Not all features are supported!", Gtk.MessageType.WARNING)
|
||||
@@ -267,14 +278,15 @@ class SettingsDialog:
|
||||
self._picons_paths_box.set_active(0)
|
||||
|
||||
def show(self):
|
||||
self._dialog.run()
|
||||
return self._dialog.run()
|
||||
|
||||
def is_updated(self):
|
||||
return self._updated
|
||||
|
||||
def on_response(self, dialog, resp):
|
||||
if resp == Gtk.ResponseType.OK and not self.apply_settings():
|
||||
return
|
||||
|
||||
self._dialog.destroy()
|
||||
return resp
|
||||
if resp == Gtk.ResponseType.ACCEPT:
|
||||
self._updated = self.on_save_settings()
|
||||
dialog.destroy()
|
||||
|
||||
def on_field_icon_press(self, entry, icon, event_button):
|
||||
update_entry_data(entry, self._dialog, self._settings)
|
||||
@@ -324,12 +336,12 @@ class SettingsDialog:
|
||||
self._picons_size_button.set_active_id(str(self._settings.list_picon_size))
|
||||
self._tooltip_logo_size_button.set_active_id(str(self._settings.tooltip_logo_size))
|
||||
self._list_font_button.set_font(self._settings.list_font)
|
||||
self._support_http_api_switch.set_active(self._settings.http_api_support)
|
||||
|
||||
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)
|
||||
self._enable_yt_dl_switch.set_active(self._settings.enable_yt_dl)
|
||||
self._enable_update_yt_dl_switch.set_active(self._settings.enable_yt_dl_update)
|
||||
self._enable_send_to_switch.set_active(self._settings.enable_send_to)
|
||||
@@ -366,9 +378,9 @@ class SettingsDialog:
|
||||
self._settings.satellites_xml_path = self._satellites_xml_field.get_text()
|
||||
self._settings.picons_path = self._picons_paths_box.get_active_id()
|
||||
|
||||
def apply_settings(self, item=None):
|
||||
def on_save_settings(self, item=None):
|
||||
if show_dialog(DialogType.QUESTION, self._dialog) != Gtk.ResponseType.OK:
|
||||
return
|
||||
return False
|
||||
|
||||
self.on_apply_profile_settings()
|
||||
self._ext_settings.profiles = self._settings.profiles
|
||||
@@ -391,6 +403,7 @@ class SettingsDialog:
|
||||
self._ext_settings.list_picon_size = int(self._picons_size_button.get_active_id())
|
||||
self._ext_settings.tooltip_logo_size = int(self._tooltip_logo_size_button.get_active_id())
|
||||
self._ext_settings.list_font = self._list_font_button.get_font()
|
||||
self._ext_settings.http_api_support = self._support_http_api_switch.get_active()
|
||||
|
||||
if not IS_LINUX:
|
||||
self._ext_settings.dark_mode = self._dark_mode_switch.get_active()
|
||||
@@ -406,13 +419,13 @@ class SettingsDialog:
|
||||
self._ext_settings.extra_color = self._extra_color_button.get_rgba().to_string()
|
||||
self._ext_settings.v5_support = self._support_ver5_switch.get_active()
|
||||
self._ext_settings.force_bq_names = self._force_bq_name_switch.get_active()
|
||||
self._ext_settings.http_api_support = self._support_http_api_switch.get_active()
|
||||
self._ext_settings.enable_yt_dl = self._enable_yt_dl_switch.get_active()
|
||||
self._ext_settings.enable_yt_dl_update = self._enable_update_yt_dl_switch.get_active()
|
||||
self._ext_settings.enable_send_to = self._enable_send_to_switch.get_active()
|
||||
|
||||
self._ext_settings.default_profile = list(filter(lambda r: r[1], self._profile_view.get_model()))[0][0]
|
||||
self._ext_settings.save()
|
||||
|
||||
return True
|
||||
|
||||
@run_task
|
||||
@@ -594,7 +607,7 @@ class SettingsDialog:
|
||||
self._ext_settings.picons_paths = tuple(r[0] for r in model)
|
||||
|
||||
def on_remove_picon_path(self, button):
|
||||
msg = f"{get_message('This may change the settings of other profiles!')}\n\n\t\t{'Are you sure?'}"
|
||||
msg = f"{get_message('This may change the settings of other profiles!')}\n\n\t\t{get_message('Are you sure?')}"
|
||||
if show_dialog(DialogType.QUESTION, self._dialog, msg) != Gtk.ResponseType.OK:
|
||||
return
|
||||
|
||||
@@ -790,15 +803,16 @@ class SettingsDialog:
|
||||
@run_task
|
||||
def unpack_theme(self, src, dst, button):
|
||||
try:
|
||||
os.makedirs(os.path.dirname(dst), exist_ok=True)
|
||||
from shutil import unpack_archive
|
||||
|
||||
import subprocess
|
||||
log("Unpacking '{}' started...".format(src))
|
||||
p = subprocess.Popen(["tar", "-xvf", src, "-C", dst],
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.STDOUT)
|
||||
p.communicate()
|
||||
log(f"Unpacking '{src}' started...")
|
||||
os.makedirs(os.path.dirname(dst), exist_ok=True)
|
||||
unpack_archive(src, dst)
|
||||
log("Unpacking end.")
|
||||
except (ValueError, OSError) as e:
|
||||
msg = f"Unpacking error: {e}"
|
||||
log(msg)
|
||||
self.show_info_message(msg, Gtk.MessageType.ERROR)
|
||||
finally:
|
||||
self.update_theme_button(button, dst)
|
||||
|
||||
|
||||
@@ -61,12 +61,13 @@ paned > separator {
|
||||
}
|
||||
|
||||
.stack-switcher > button > label {
|
||||
padding-left: 2px;
|
||||
padding-right: 2px;
|
||||
min-width: 75px;
|
||||
padding-left: 5px;
|
||||
padding-right: 5px;
|
||||
min-width: 50px;
|
||||
}
|
||||
|
||||
.stack-switcher > button.text-button {
|
||||
padding-left: 2px;
|
||||
padding-right: 2px;
|
||||
padding-left: 5px;
|
||||
padding-right: 5px;
|
||||
min-width: 50px;
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ Author: Dmitriy Yefremov
|
||||
|
||||
-->
|
||||
<interface domain="demon-editor">
|
||||
<requires lib="gtk+" version="3.16"/>
|
||||
<requires lib="gtk+" version="3.18"/>
|
||||
<!-- interface-license-type mit -->
|
||||
<!-- interface-name DemonEditor -->
|
||||
<!-- interface-copyright 2018-2021 Dmitriy Yefremov -->
|
||||
@@ -46,16 +46,10 @@ Author: Dmitriy Yefremov
|
||||
<object class="GtkFrame" id="telnet_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.49000000953674316</property>
|
||||
<property name="shadow_type">in</property>
|
||||
<child>
|
||||
<object class="GtkBox" id="telnet_main_box">
|
||||
<property name="width_request">480</property>
|
||||
<property name="height_request">180</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_left">5</property>
|
||||
|
||||
@@ -1,3 +1,31 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# The MIT License (MIT)
|
||||
#
|
||||
# Copyright (c) 2018-2021 Dmitriy Yefremov
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
# THE SOFTWARE.
|
||||
#
|
||||
# Author: Dmitriy Yefremov
|
||||
#
|
||||
|
||||
|
||||
import re
|
||||
import selectors
|
||||
import socket
|
||||
@@ -44,7 +72,7 @@ class TelnetClient(Gtk.Box):
|
||||
_ALL_PATTERN = re.compile(r'(\x1b\[|\x9b)[0-?]*[@-~]')
|
||||
_NOT_SUPPORTED = {"mc", "mcedit", "vi", "nano"}
|
||||
|
||||
def __init__(self, app, settings, *args, **kwargs):
|
||||
def __init__(self, app, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self._app = app
|
||||
self._app.connect("profile-changed", self.on_profile_changed)
|
||||
|
||||
@@ -1,3 +1,31 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# The MIT License (MIT)
|
||||
#
|
||||
# Copyright (c) 2018-2021 Dmitriy Yefremov
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
# THE SOFTWARE.
|
||||
#
|
||||
# Author: Dmitriy Yefremov
|
||||
#
|
||||
|
||||
|
||||
import locale
|
||||
import os
|
||||
from enum import Enum, IntEnum
|
||||
@@ -7,7 +35,7 @@ import gi
|
||||
|
||||
gi.require_version("Gtk", "3.0")
|
||||
gi.require_version("Gdk", "3.0")
|
||||
from gi.repository import Gtk, Gdk
|
||||
from gi.repository import Gtk, Gdk, GLib
|
||||
|
||||
from app.settings import Settings, SettingsException, IS_DARWIN, GTK_PATH, IS_LINUX
|
||||
|
||||
@@ -52,7 +80,8 @@ else:
|
||||
Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION)
|
||||
|
||||
if IS_LINUX:
|
||||
locale.bindtextdomain(TEXT_DOMAIN, LANG_PATH)
|
||||
if UI_RESOURCES_PATH == BASE_PATH:
|
||||
locale.bindtextdomain(TEXT_DOMAIN, LANG_PATH)
|
||||
# Init notify
|
||||
try:
|
||||
gi.require_version("Notify", "0.7")
|
||||
@@ -77,16 +106,22 @@ else:
|
||||
theme = Gtk.IconTheme.get_default()
|
||||
theme.append_search_path(UI_RESOURCES_PATH + "icons")
|
||||
|
||||
_IMAGE_MISSING = theme.load_icon("image-missing", 16, 0) if theme.lookup_icon("image-missing", 16, 0) else None
|
||||
CODED_ICON = theme.load_icon("emblem-readonly", 16, 0) if theme.lookup_icon(
|
||||
"emblem-readonly", 16, 0) else _IMAGE_MISSING
|
||||
LOCKED_ICON = theme.load_icon("changes-prevent-symbolic", 16, 0) if theme.lookup_icon(
|
||||
"system-lock-screen", 16, 0) else _IMAGE_MISSING
|
||||
HIDE_ICON = theme.load_icon("go-jump", 16, 0) if theme.lookup_icon("go-jump", 16, 0) else _IMAGE_MISSING
|
||||
TV_ICON = theme.load_icon("tv-symbolic", 16, 0) if theme.lookup_icon("tv-symbolic", 16, 0) else _IMAGE_MISSING
|
||||
IPTV_ICON = theme.load_icon("emblem-shared", 16, 0) if theme.lookup_icon("emblem-shared", 16, 0) else None
|
||||
EPG_ICON = theme.load_icon("gtk-index", 16, 0) if theme.lookup_icon("gtk-index", 16, 0) else None
|
||||
DEFAULT_ICON = theme.load_icon("emblem-default", 16, 0) if theme.lookup_icon("emblem-default", 16, 0) else None
|
||||
|
||||
def get_icon(name, size, default=None):
|
||||
try:
|
||||
return theme.load_icon(name, size, 0) if theme.lookup_icon(name, size, 0) else default
|
||||
except GLib.Error:
|
||||
return default
|
||||
|
||||
|
||||
_IMAGE_MISSING = get_icon("image-missing", 16)
|
||||
CODED_ICON = get_icon("emblem-readonly", 16, _IMAGE_MISSING)
|
||||
LOCKED_ICON = get_icon("changes-prevent-symbolic", 16, _IMAGE_MISSING)
|
||||
HIDE_ICON = get_icon("go-jump", 16, _IMAGE_MISSING)
|
||||
TV_ICON = get_icon("tv-symbolic", 16, _IMAGE_MISSING)
|
||||
IPTV_ICON = get_icon("emblem-shared", 16, _IMAGE_MISSING)
|
||||
EPG_ICON = get_icon("gtk-index", 16, _IMAGE_MISSING)
|
||||
DEFAULT_ICON = get_icon("emblem-default", 16, _IMAGE_MISSING)
|
||||
|
||||
|
||||
@lru_cache(maxsize=1)
|
||||
@@ -262,10 +297,12 @@ if IS_LINUX:
|
||||
LEFT = 113
|
||||
RIGHT = 114
|
||||
F2 = 68
|
||||
F5 = 71
|
||||
F7 = 73
|
||||
SPACE = 65
|
||||
DELETE = 119
|
||||
BACK_SPACE = 22
|
||||
RETURN = 36
|
||||
CTRL_L = 37
|
||||
CTRL_R = 105
|
||||
# Laptop codes
|
||||
@@ -300,10 +337,12 @@ elif IS_DARWIN:
|
||||
LEFT = 123
|
||||
RIGHT = 123
|
||||
F2 = 120
|
||||
F5 = 96
|
||||
F7 = 98
|
||||
SPACE = 49
|
||||
DELETE = 51
|
||||
BACK_SPACE = 76
|
||||
RETURN = 36
|
||||
CTRL_L = 55
|
||||
CTRL_R = 55
|
||||
# Laptop codes.
|
||||
@@ -336,10 +375,12 @@ else:
|
||||
LEFT = 37
|
||||
RIGHT = 39
|
||||
F2 = 113
|
||||
F5 = 116
|
||||
F7 = 118
|
||||
SPACE = 32
|
||||
DELETE = 46
|
||||
BACK_SPACE = 8
|
||||
RETURN = 13
|
||||
CTRL_L = 17
|
||||
CTRL_R = 163
|
||||
# Laptop codes.
|
||||
|
||||
@@ -6,3 +6,11 @@ button {
|
||||
min-height: 24px;
|
||||
min-width: 24px;
|
||||
}
|
||||
|
||||
entry {
|
||||
min-height: 24px;
|
||||
}
|
||||
|
||||
switch {
|
||||
margin-right: 2px;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/bin/bash
|
||||
VER="2.0.0_Alpha"
|
||||
VER="2.0.3_Beta"
|
||||
B_PATH="dist/DemonEditor"
|
||||
DEB_PATH="$B_PATH/usr/share/demoneditor"
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
Package: demon-editor
|
||||
Version: 2.0.0-Alpha
|
||||
Version: 2.0.3-Beta
|
||||
Section: utils
|
||||
Priority: optional
|
||||
Architecture: all
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -69,7 +69,7 @@ app = BUNDLE(coll,
|
||||
'CFBundleGetInfoString': "Enigma2 channel and satellite editor",
|
||||
'LSApplicationCategoryType': 'public.app-category.utilities',
|
||||
'LSMinimumSystemVersion': '10.13',
|
||||
'CFBundleShortVersionString': f"2.0.0.{BUILD_DATE} Alpha",
|
||||
'CFBundleShortVersionString': f"2.0.3.{BUILD_DATE} Beta",
|
||||
'NSHumanReadableCopyright': u"Copyright © 2021, Dmitriy Yefremov",
|
||||
'NSRequiresAquaSystemAppearance': 'false'
|
||||
})
|
||||
|
||||
@@ -50,6 +50,9 @@ msgstr "Цякучы IP:"
|
||||
msgid "Assign"
|
||||
msgstr "Прывязаць"
|
||||
|
||||
msgid "Assign file"
|
||||
msgstr "Прывязаць файл"
|
||||
|
||||
msgid "Bouquet details"
|
||||
msgstr "Сэрвісы букета"
|
||||
|
||||
@@ -203,8 +206,8 @@ msgstr "Цякучы шлях да дадзеных:"
|
||||
msgid "Data:"
|
||||
msgstr "Дадзеныя:"
|
||||
|
||||
msgid "Enigma2 channel and satellite list editor for GNU/Linux."
|
||||
msgstr "Рэдактар спіса каналаў і спадарожнікаў Enigma2\n для GNU/Linux."
|
||||
msgid "Enigma2 channel and satellite list editor."
|
||||
msgstr "Рэдактар спіса каналаў і спадарожнікаў Enigma2."
|
||||
|
||||
msgid "Host:"
|
||||
msgstr "Адрас рэсівера:"
|
||||
@@ -296,8 +299,8 @@ msgstr "Фармат імя піконаў:"
|
||||
msgid "Resize:"
|
||||
msgstr "Змяніць памер:"
|
||||
|
||||
msgid "Current picons path:"
|
||||
msgstr "Цякучы шлях да піконаў:"
|
||||
msgid "Current picons path"
|
||||
msgstr "Цякучы шлях да піконаў"
|
||||
|
||||
msgid "Receiver picons path:"
|
||||
msgstr "Шлях да піконаў рэсівера:"
|
||||
@@ -1249,3 +1252,66 @@ msgstr "Запісы"
|
||||
|
||||
msgid "Help"
|
||||
msgstr "Даведка"
|
||||
|
||||
msgid "HTTP API is not activated. Check your settings!"
|
||||
msgstr "HTTP API не актывавана. Праверце налады!"
|
||||
|
||||
msgid "Add picons"
|
||||
msgstr "Дадаць піконы"
|
||||
|
||||
msgid "Logs"
|
||||
msgstr "Логі"
|
||||
|
||||
msgid "Title"
|
||||
msgstr "Назва"
|
||||
|
||||
msgid "Time"
|
||||
msgstr "Час"
|
||||
|
||||
msgid "Length"
|
||||
msgstr "Працягласць"
|
||||
|
||||
msgid "Additional source"
|
||||
msgstr "Дадатковая крыніца"
|
||||
|
||||
msgid "Automatically set the name selected in the favorites list."
|
||||
msgstr "Аўтаматычная ўсталёўка імя са спіса абранага."
|
||||
|
||||
msgid "Playback"
|
||||
msgstr "Прайграванне"
|
||||
|
||||
msgid "Audio"
|
||||
msgstr "Аўдыё"
|
||||
|
||||
msgid "Audio Track"
|
||||
msgstr "Аўдыёдарожка"
|
||||
|
||||
msgid "Subtitle"
|
||||
msgstr "Субтытры"
|
||||
|
||||
msgid "Subtitle Track"
|
||||
msgstr "Дарожка субтытраў"
|
||||
|
||||
msgid "Aspect ratio"
|
||||
msgstr "Стасунак бакоў"
|
||||
|
||||
msgid "This may change the settings of other profiles!"
|
||||
msgstr "Гэта можа змяніць налады іншых профіляў!"
|
||||
|
||||
msgid "Drag the services to the desired picon or picon to the list of selected services."
|
||||
msgstr "Перацягніце сэрвісы на патрэбны пікон ці пікон на спіс абраных сэрвісаў."
|
||||
|
||||
msgid "Sets the profile folder as default to store picons, backups, etc."
|
||||
msgstr "Усталёўвае тэчку профілю па змаўчанні для захоўвання піконаў, рэзервовых копій і т. п."
|
||||
|
||||
msgid "New sub-bouquet"
|
||||
msgstr "Стварыць саббукет"
|
||||
|
||||
msgid "Mark not presented in Bouquets"
|
||||
msgstr "Адзначыць адсутныя у букетах"
|
||||
|
||||
msgid "Not in Bouquets"
|
||||
msgstr "Адсутныя у букетах"
|
||||
|
||||
msgid "Do not show services present in Bouquets."
|
||||
msgstr "Не паказваць сэрвісы якія прысутнічаюць у букетах."
|
||||
|
||||
@@ -52,6 +52,9 @@ msgstr "Akt.IP:"
|
||||
msgid "Assign"
|
||||
msgstr "Zuweisen"
|
||||
|
||||
msgid "Assign file"
|
||||
msgstr "Datei zuweisen"
|
||||
|
||||
msgid "Bouquet details"
|
||||
msgstr "Bouquet-Details"
|
||||
|
||||
@@ -205,8 +208,8 @@ msgstr "Aktueller Datenpfad:"
|
||||
msgid "Data:"
|
||||
msgstr "Daten:"
|
||||
|
||||
msgid "Enigma2 channel and satellite list editor for GNU/Linux."
|
||||
msgstr "Enigma2 Kanal- und Satellitenlisteneditor für GNU/Linux."
|
||||
msgid "Enigma2 channel and satellite list editor."
|
||||
msgstr "Enigma2 Kanal- und Satellitenlisteneditor."
|
||||
|
||||
msgid "Host:"
|
||||
msgstr "Host:"
|
||||
@@ -298,8 +301,8 @@ msgstr "Picon Namens-Format:"
|
||||
msgid "Resize:"
|
||||
msgstr "Größe ändern:"
|
||||
|
||||
msgid "Current picons path:"
|
||||
msgstr "Aktueller Piconspfad:"
|
||||
msgid "Current picons path"
|
||||
msgstr "Aktueller Piconspfad"
|
||||
|
||||
msgid "Receiver picons path:"
|
||||
msgstr "Receiver Piconspfad:"
|
||||
@@ -1263,3 +1266,66 @@ msgstr "Aufnahmen"
|
||||
|
||||
msgid "Help"
|
||||
msgstr "Hilfe"
|
||||
|
||||
msgid "HTTP API is not activated. Check your settings!"
|
||||
msgstr "HTTP API ist nicht aktiviert. Überprüfe deine Einstellungen!"
|
||||
|
||||
msgid "Add picons"
|
||||
msgstr "Picons hinzufügen"
|
||||
|
||||
msgid "Logs"
|
||||
msgstr "Logs"
|
||||
|
||||
msgid "Title"
|
||||
msgstr "Titel"
|
||||
|
||||
msgid "Time"
|
||||
msgstr "Zeit"
|
||||
|
||||
msgid "Length"
|
||||
msgstr "Dauer"
|
||||
|
||||
msgid "Additional source"
|
||||
msgstr "Zusätzliche Quelle"
|
||||
|
||||
msgid "Automatically set the name selected in the favorites list."
|
||||
msgstr "Automatisch den in der Favoritenliste ausgewählten Namen einstellen."
|
||||
|
||||
msgid "Playback"
|
||||
msgstr "Wiedergabe"
|
||||
|
||||
msgid "Audio"
|
||||
msgstr ""
|
||||
|
||||
msgid "Audio Track"
|
||||
msgstr "Audio"
|
||||
|
||||
msgid "Subtitle"
|
||||
msgstr "Untertitel"
|
||||
|
||||
msgid "Subtitle Track"
|
||||
msgstr "Untertitelspur"
|
||||
|
||||
msgid "Aspect ratio"
|
||||
msgstr "Seitenverhältnis"
|
||||
|
||||
msgid "This may change the settings of other profiles!"
|
||||
msgstr "Dadurch können sich die Einstellungen anderer Profile ändern!"
|
||||
|
||||
msgid "Drag the services to the desired picon or picon to the list of selected services."
|
||||
msgstr "Ziehe die Dienste auf das gewünschte Picon oder Picon in die Liste der ausgewählten Dienste."
|
||||
|
||||
msgid "Sets the profile folder as default to store picons, backups, etc."
|
||||
msgstr "Legt den Profilordner als Standardordner zum Speichern von Piconen, Backups usw. fest."
|
||||
|
||||
msgid "New sub-bouquet"
|
||||
msgstr "Neues Sub-Bouquet"
|
||||
|
||||
msgid "Mark not presented in Bouquets"
|
||||
msgstr "Markieren die nicht in Bouquets vorhanden"
|
||||
|
||||
msgid "Not in Bouquets"
|
||||
msgstr "Nicht in Bouquets"
|
||||
|
||||
msgid "Do not show services present in Bouquets."
|
||||
msgstr "Vorhandene in Bouquets Dienste nicht anzeigen."
|
||||
|
||||
@@ -1233,7 +1233,7 @@ msgid "Mark duplicates"
|
||||
msgstr "Seleziona duplicati"
|
||||
|
||||
msgid "Load only for selected bouquet"
|
||||
msgstr "Larica solo per i bouquet selezionati"
|
||||
msgstr "Scarica solo per i bouquet selezionati"
|
||||
|
||||
msgid "The task is canceled!"
|
||||
msgstr "Operazione cancellata!"
|
||||
|
||||
@@ -1,30 +1,26 @@
|
||||
# Copyright (C) 2018-2020 Dmitriy Yefremov
|
||||
# Copyright (C) 2018-2019 Dmitriy Yefremov
|
||||
# This file is distributed under the MIT license.
|
||||
#
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Last-Translator: wwns\n"
|
||||
"Last-Translator: wwns <https://github.com/wwns>\n"
|
||||
"Language: pl\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Project-Id-Version: \n"
|
||||
"POT-Creation-Date: \n"
|
||||
"PO-Revision-Date: \n"
|
||||
"Language-Team: \n"
|
||||
"X-Generator: Poedit 3.0\n"
|
||||
|
||||
msgid "translator-credits"
|
||||
msgstr "wwns"
|
||||
|
||||
# Main
|
||||
msgid "File"
|
||||
msgstr "Plik"
|
||||
|
||||
msgid "View"
|
||||
msgstr "Widok"
|
||||
|
||||
msgid "Lock"
|
||||
msgstr "Zablokuj"
|
||||
|
||||
msgid "Service"
|
||||
msgstr "Serwis"
|
||||
msgstr "Usługa"
|
||||
|
||||
msgid "Package"
|
||||
msgstr "Pakiet"
|
||||
@@ -48,7 +44,7 @@ msgid "System"
|
||||
msgstr "System"
|
||||
|
||||
msgid "Pos"
|
||||
msgstr "Pos"
|
||||
msgstr "Poz"
|
||||
|
||||
msgid "Num"
|
||||
msgstr "Num"
|
||||
@@ -113,6 +109,9 @@ msgstr "Ustaw domyślną nazwę"
|
||||
msgid "Insert marker"
|
||||
msgstr "Wstaw znacznik"
|
||||
|
||||
msgid "Insert space"
|
||||
msgstr "Wstaw spację"
|
||||
|
||||
msgid "Locate in services"
|
||||
msgstr "Znajdź w usługach"
|
||||
|
||||
@@ -179,6 +178,9 @@ msgstr "Zapisz"
|
||||
msgid "Search"
|
||||
msgstr "Szukaj"
|
||||
|
||||
msgid "Services"
|
||||
msgstr "Kanały"
|
||||
|
||||
msgid "Services filter"
|
||||
msgstr "Filtr kanałów"
|
||||
|
||||
@@ -206,8 +208,8 @@ msgstr "Aktualna ścieżka danych:"
|
||||
msgid "Data:"
|
||||
msgstr "Dane:"
|
||||
|
||||
msgid "Enigma2 channel and satellite list editor for GNU/Linux."
|
||||
msgstr "Edytor kanałów Enigma2 i listy satelitów dla GNU/Linux."
|
||||
msgid "Enigma2 channel and satellite list editor."
|
||||
msgstr "Edytor kanałów i list satelitarnych Enigma2."
|
||||
|
||||
msgid "Host:"
|
||||
msgstr "Host:"
|
||||
@@ -222,7 +224,7 @@ msgid "Receive files from receiver"
|
||||
msgstr "Pobieranie plików z odbiornika"
|
||||
|
||||
msgid "Receiver IP:"
|
||||
msgstr "Odbiornik IP:"
|
||||
msgstr "Adres IP odbiornika:"
|
||||
|
||||
msgid "Remove unused bouquets"
|
||||
msgstr "Usuń nieużywany bouquet"
|
||||
@@ -252,22 +254,7 @@ msgid "User bouquet files:"
|
||||
msgstr "Pliki bukietu użytkownika:"
|
||||
|
||||
msgid "Extra:"
|
||||
msgstr "Dodatkowe"
|
||||
|
||||
msgid "IPTV tools"
|
||||
msgstr "Narzędzia IPTV"
|
||||
|
||||
msgid "Picons manager"
|
||||
msgstr "Menedżer pikonów"
|
||||
|
||||
msgid "Hide/Skip"
|
||||
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?"
|
||||
msgstr "Dodatkowe:"
|
||||
|
||||
# Filter bar
|
||||
msgid "Only free"
|
||||
@@ -280,6 +267,9 @@ msgid "All types"
|
||||
msgstr "Wszystkie typy"
|
||||
|
||||
# Streams player
|
||||
msgid "Play"
|
||||
msgstr "Odtwarzaj"
|
||||
|
||||
msgid "Stop playback"
|
||||
msgstr "Zatrzymaj odtwarzanie"
|
||||
|
||||
@@ -311,8 +301,8 @@ msgstr "Format nazw pikon:"
|
||||
msgid "Resize:"
|
||||
msgstr "Zmień rozmiar:"
|
||||
|
||||
msgid "Current picons path:"
|
||||
msgstr "Aktualna ścieżka pikon:"
|
||||
msgid "Current picons path"
|
||||
msgstr "Aktualna ścieżka pikon"
|
||||
|
||||
msgid "Receiver picons path:"
|
||||
msgstr "Ścieżka pikon odbiornika:"
|
||||
@@ -390,12 +380,6 @@ 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:"
|
||||
|
||||
@@ -467,7 +451,7 @@ msgid "Preferences"
|
||||
msgstr "Preferencje"
|
||||
|
||||
msgid "Profile:"
|
||||
msgstr "Profil:"
|
||||
msgstr "Profile:"
|
||||
|
||||
msgid "Timeout between commands in seconds"
|
||||
msgstr "Limit czasu między poleceniami w sekundach"
|
||||
@@ -481,6 +465,9 @@ msgstr "Login:"
|
||||
msgid "Options"
|
||||
msgstr "Opcje"
|
||||
|
||||
msgid "Rename"
|
||||
msgstr "Zmień nazwę"
|
||||
|
||||
msgid "Password:"
|
||||
msgstr "Hasło:"
|
||||
|
||||
@@ -497,13 +484,13 @@ msgid "Picons path:"
|
||||
msgstr "Ścieżka pikon:"
|
||||
|
||||
msgid "Network settings:"
|
||||
msgstr "Ustawienia sieci:"
|
||||
msgstr "Ustawienia połaczenia:"
|
||||
|
||||
msgid "STB file paths:"
|
||||
msgstr "Ścieżki do plików w STB:"
|
||||
|
||||
msgid "Local file paths:"
|
||||
msgstr "Lokalne ścieżki plików:"
|
||||
msgstr "Lokalne ścieżki do plików:"
|
||||
|
||||
# Dialogs messages
|
||||
msgid "Error. No bouquet is selected!"
|
||||
@@ -539,6 +526,9 @@ msgstr "Wybierz tylko jeden element!"
|
||||
msgid "No png file is selected!"
|
||||
msgstr "Nie wybrano pliku png!"
|
||||
|
||||
msgid "No profile selected!"
|
||||
msgstr "Nie wybroano profilu !"
|
||||
|
||||
msgid "No reference is present!"
|
||||
msgstr "Brak referencji!"
|
||||
|
||||
@@ -606,6 +596,15 @@ msgstr "Brak danych do zapisania!"
|
||||
msgid "Network"
|
||||
msgstr "Sieć"
|
||||
|
||||
msgid "Paths"
|
||||
msgstr "Ścieżki"
|
||||
|
||||
msgid "Program"
|
||||
msgstr "Program"
|
||||
|
||||
msgid "Backup:"
|
||||
msgstr "Kopia:"
|
||||
|
||||
msgid "Backup"
|
||||
msgstr "Kopia"
|
||||
|
||||
@@ -621,11 +620,26 @@ msgstr "Przywróć bukiety"
|
||||
msgid "Restore all"
|
||||
msgstr "Przywrócić wszystko"
|
||||
|
||||
msgid "Before saving"
|
||||
msgstr "Przed zapisaniem"
|
||||
|
||||
msgid "Before downloading from the receiver"
|
||||
msgstr "Przed pobraniem z odbiornika"
|
||||
|
||||
msgid "Set background color for the services"
|
||||
msgstr "Ustaw kolor tła dla usług"
|
||||
|
||||
msgid "Marked as new:"
|
||||
msgstr "Oznacz jako nowy:"
|
||||
|
||||
msgid "With an extra name in the bouquet:"
|
||||
msgstr "Z dodatkową nazwą w bukiecie:"
|
||||
|
||||
msgid "Select"
|
||||
msgstr "Wybierz"
|
||||
|
||||
msgid "About"
|
||||
msgstr "Wersja"
|
||||
msgstr "O programie"
|
||||
|
||||
msgid "Exit"
|
||||
msgstr "Wyjście"
|
||||
@@ -633,15 +647,6 @@ 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"
|
||||
@@ -664,12 +669,27 @@ 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 "Zap"
|
||||
msgstr "Przełącz"
|
||||
|
||||
msgid "Play stream"
|
||||
msgstr "Odtwórz strumień"
|
||||
|
||||
msgid "Disabled"
|
||||
msgstr "Wyłączone"
|
||||
|
||||
msgid "Enable lamedb ver. 5 support"
|
||||
msgstr "Włącz wsparcie dla lamedb w wer.5"
|
||||
|
||||
msgid "Enable HTTP API"
|
||||
msgstr "Włącz API HTTP"
|
||||
|
||||
msgid "Switch(zap) the channel(Ctrl + Z)"
|
||||
msgstr "Przełącz(zap) kanał(Ctrl + Z)"
|
||||
|
||||
@@ -770,11 +790,26 @@ msgstr "Import listy odtwarzania"
|
||||
msgid "Getting link error:"
|
||||
msgstr "Błąd pobierania łącza:"
|
||||
|
||||
msgid "Extra"
|
||||
msgstr "Ekstra"
|
||||
|
||||
msgid "Apply profile settings"
|
||||
msgstr "Zastosuj ustawienia profilu"
|
||||
|
||||
msgid "Settings type:"
|
||||
msgstr "Ustawienia dla:"
|
||||
|
||||
msgid "Set default"
|
||||
msgstr "Ustaw domyślnie"
|
||||
msgstr "Uataw domyślnie"
|
||||
|
||||
msgid "Language:"
|
||||
msgstr "Język:"
|
||||
|
||||
msgid "Load the last open configuration at program startup"
|
||||
msgstr "Załaduj ostatnią otwartą konfigurację podczas uruchamiania programu"
|
||||
|
||||
msgid "Enable direct playback bar"
|
||||
msgstr "Włącz pasek bezpośredniego odtwarzania"
|
||||
|
||||
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"
|
||||
@@ -782,23 +817,8 @@ msgstr "Umożliwia bezpośrednie wysyłanie i odtwarzanie łączy multimedialnyc
|
||||
msgid "Watch the channel in the program"
|
||||
msgstr "Obejrzyj kanał w programie"
|
||||
|
||||
msgid "Receiver info"
|
||||
msgstr "Informacje o odbiorniku"
|
||||
|
||||
msgid "Operates in standby mode or current active transponder!"
|
||||
msgstr "Działa w trybie gotowości lub aktualnie jest aktywny transponder!"
|
||||
|
||||
msgid "Download picons from the receiver"
|
||||
msgstr "Pobierz pikony z odbiornika"
|
||||
|
||||
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 przeładować dane w odbiorniku."
|
||||
|
||||
msgid "Apply profile settings"
|
||||
msgstr "Zastosuj ustawienia profilu"
|
||||
msgid "Zap and Play"
|
||||
msgstr "Przełącz i Odtwórz"
|
||||
|
||||
msgid "Drag or paste the link here"
|
||||
msgstr "Przeciągnij lub wklej tutaj link"
|
||||
@@ -809,34 +829,156 @@ msgstr "Usuń dodane linki z listy odtwarzania"
|
||||
msgid "A bouquet with that name exists!"
|
||||
msgstr "Istnieje bukiet o tej nazwie!"
|
||||
|
||||
msgid "Details"
|
||||
msgstr "Właściwości"
|
||||
|
||||
msgid "Profile"
|
||||
msgstr "Profil"
|
||||
|
||||
msgid "Reset"
|
||||
msgstr "Reset profilu"
|
||||
|
||||
msgid "File"
|
||||
msgstr "Plik"
|
||||
|
||||
msgid "Picons manager"
|
||||
msgstr "Menedżer pikonów"
|
||||
|
||||
msgid "Explorer"
|
||||
msgstr "Explorer"
|
||||
|
||||
msgid "Satellite url:"
|
||||
msgstr "Satelitarny url:"
|
||||
|
||||
msgid "Cut"
|
||||
msgstr "Wytnij"
|
||||
|
||||
msgid "Paste"
|
||||
msgstr "Wklej"
|
||||
|
||||
msgid "To the top"
|
||||
msgstr "Przenieś na góre bukietu"
|
||||
|
||||
msgid "To the end"
|
||||
msgstr "Przenieś na koniec bukietu"
|
||||
|
||||
msgid "View"
|
||||
msgstr "Widok"
|
||||
|
||||
msgid "Lock"
|
||||
msgstr "Zablokuj"
|
||||
|
||||
msgid "Parent lock"
|
||||
msgstr "Blokada rodzicielska"
|
||||
|
||||
msgid "Hide/Skip"
|
||||
msgstr "Ukryj/Pomiń"
|
||||
|
||||
msgid "IPTV tools"
|
||||
msgstr "Narzędzia IPTV"
|
||||
|
||||
msgid "Make profile folder as default for the additional data"
|
||||
msgstr "Ustaw folder profilu jako domyślny dla dodatkowych danych"
|
||||
|
||||
msgid "Default data path:"
|
||||
msgstr "Domyślna ścieżka danych:"
|
||||
|
||||
msgid "Streams record path:"
|
||||
msgstr "Ścieżka zapisu nagrań:"
|
||||
|
||||
msgid "Record"
|
||||
msgstr "Nagrania"
|
||||
|
||||
msgid "Record:"
|
||||
msgstr "Nagrania:"
|
||||
|
||||
msgid "Record to disk:"
|
||||
msgstr "Zapis na dysk:"
|
||||
|
||||
msgid "Streaming"
|
||||
msgstr "Transmisja"
|
||||
|
||||
msgid "Activate transcoding"
|
||||
msgstr "Aktywuj"
|
||||
|
||||
msgid "Presets:"
|
||||
msgstr "Ustawienia transkodowania:"
|
||||
|
||||
msgid "Video options:"
|
||||
msgstr "Opcje wideo:"
|
||||
|
||||
msgid "Audio options:"
|
||||
msgstr "Opcje audio:"
|
||||
|
||||
msgid "Bitrate (kb/s):"
|
||||
msgstr "Szybkość transmisji (kb/s):"
|
||||
|
||||
msgid "Codec:"
|
||||
msgstr "Kodek:"
|
||||
|
||||
msgid "Width (px):"
|
||||
msgstr "Szerokość (px):"
|
||||
|
||||
msgid "Height (px):"
|
||||
msgstr "Wysokość (px):"
|
||||
|
||||
msgid "Channels:"
|
||||
msgstr "Kanały:"
|
||||
|
||||
msgid "Remove all picons from the receiver"
|
||||
msgstr "Usuń wszystkie pikony z odbiornika"
|
||||
msgid "Sample rate (Hz):"
|
||||
msgstr "Częstotliwość próbkowania (Hz):"
|
||||
|
||||
msgid "Download from the receiver"
|
||||
msgstr "Pobierz z odbiornika"
|
||||
msgid "Play streams mode:"
|
||||
msgstr "Tryb odtwarzania strumieni:"
|
||||
|
||||
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 "Built-in player"
|
||||
msgstr "Wbudowany odtwarzacz"
|
||||
|
||||
msgid "In a separate window"
|
||||
msgstr "W osobnym oknie"
|
||||
|
||||
msgid "Only get m3u file"
|
||||
msgstr "Tylko pobierz plik m3u"
|
||||
|
||||
msgid "Save and restart the program to apply the settings."
|
||||
msgstr "Zapisz i uruchom ponownie program, aby zastosować ustawienia."
|
||||
|
||||
msgid "Some images may have problems displaying the favorites list!"
|
||||
msgstr "Niektóre obrazy mogą mieć problemy z wyświetlaniem listy ulubionych!"
|
||||
|
||||
# Appearance
|
||||
msgid "Operates in standby mode or current active transponder!"
|
||||
msgstr "Działa w trybie gotowości lub aktualnie jest aktywny transponder!"
|
||||
|
||||
msgid "No connection to the receiver!"
|
||||
msgstr "Brak połączenia z odbiornikiem!"
|
||||
|
||||
msgid "Signal level"
|
||||
msgstr "Poziom sygnału"
|
||||
|
||||
msgid "Receiver info"
|
||||
msgstr "Informacje o odbiorniku"
|
||||
|
||||
msgid "A profile with that name exists!"
|
||||
msgstr "Profil o tej nazwie już istnieje!"
|
||||
|
||||
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"
|
||||
msgstr "Pokaż szczegółowe informacje jako wskazówki na liście bukietów"
|
||||
|
||||
msgid "Enable alternate bouquet file naming"
|
||||
msgstr "Włącz alternatywne nazewnictwo plików bukietów"
|
||||
|
||||
msgid "Allows you to name bouquet files using their names."
|
||||
msgstr "Pozwala nazwać pliki bukietów przy użyciu ich nazw."
|
||||
|
||||
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"
|
||||
|
||||
msgid "EXPERIMENTAL!"
|
||||
msgstr "EKSPERYMENTALNE!"
|
||||
|
||||
msgid "Gtk3 Theme:"
|
||||
msgstr "Gtk3 motyw:"
|
||||
|
||||
@@ -846,159 +988,346 @@ msgstr "Motyw ikon:"
|
||||
msgid "Gtk3 Themes and Icons:"
|
||||
msgstr "Gtk3 motywy i ikony:"
|
||||
|
||||
msgid "Save and restart the program to apply the settings."
|
||||
msgstr "Zapisz i uruchom ponownie program, aby zastosować ustawienia."
|
||||
msgid "Deleting data..."
|
||||
msgstr "Usuwanie danych…"
|
||||
|
||||
# Extra
|
||||
msgid "Extra"
|
||||
msgstr "Ekstra"
|
||||
msgid "Download from the receiver"
|
||||
msgstr "Pobierz z odbiornika"
|
||||
|
||||
msgid "Enable alternate bouquet file naming"
|
||||
msgstr "Włącz alternatywne nazewnictwo plików bukietów"
|
||||
msgid "Remove all picons from the receiver"
|
||||
msgstr "Usuń wszystkie pikony z odbiornika"
|
||||
|
||||
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"
|
||||
msgstr "Włącz API HTTP"
|
||||
|
||||
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"
|
||||
|
||||
msgid "Play"
|
||||
msgstr "Odtwarzaj"
|
||||
|
||||
msgid "Zap and Play"
|
||||
msgstr "Przełącz i Odtwórz"
|
||||
|
||||
msgid "Play stream"
|
||||
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 "Service reference"
|
||||
msgstr "Odniesienie do usługi"
|
||||
|
||||
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"
|
||||
msgid "Filter services"
|
||||
msgstr "Filtruj usługi"
|
||||
|
||||
#Program
|
||||
msgid "Program"
|
||||
msgstr "Program"
|
||||
msgid "Filter services in the main list."
|
||||
msgstr "Filtruj usługi na głównej liście."
|
||||
|
||||
msgid "Language:"
|
||||
msgstr "Język:"
|
||||
msgid "Destination:"
|
||||
msgstr "Miejsce docelowe:"
|
||||
|
||||
msgid "Load the last open configuration at program startup"
|
||||
msgstr "Załaduj ostatnią otwartą konfigurację podczas uruchamiania programu"
|
||||
msgid "EXPERIMENTAL!"
|
||||
msgstr "EKSPERYMENTALNE!"
|
||||
|
||||
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 "Sorting data..."
|
||||
msgstr "Sortowanie danych…"
|
||||
|
||||
msgid "Show detailed info as hints in the bouquet list"
|
||||
msgstr "Pokaż szczegółowe informacje jako wskazówki na liście bukietów"
|
||||
msgid ""
|
||||
"There are unsaved changes.\n"
|
||||
"\n"
|
||||
"\t Save them now?"
|
||||
msgstr ""
|
||||
"Istnieją niezapisane zmiany.\n"
|
||||
"\n"
|
||||
"\t Zapisać je teraz?"
|
||||
|
||||
msgid "Set background color for the services"
|
||||
msgstr "Ustaw kolor tła dla usług"
|
||||
msgid ""
|
||||
"Are you sure you want to change the order\n"
|
||||
"\t of services in this bouquet?"
|
||||
msgstr ""
|
||||
"Czy na pewno chcesz zmienić kolejność?\n"
|
||||
"\t usług w tym bukiecie?"
|
||||
|
||||
msgid "Marked as new:"
|
||||
msgstr "Oznacz jako nowy:"
|
||||
msgid "Remove from the receiver"
|
||||
msgstr "Usuń z odbiornika"
|
||||
|
||||
msgid "With an extra name in the bouquet:"
|
||||
msgstr "Z dodatkową nazwą w bukiecie:"
|
||||
msgid "Screenshot"
|
||||
msgstr "Zrzut ekranu"
|
||||
|
||||
msgid "Backup:"
|
||||
msgstr "Kopia:"
|
||||
msgid "Video"
|
||||
msgstr "Opcje wideo"
|
||||
|
||||
msgid "Before saving"
|
||||
msgstr "Przed zapisaniem"
|
||||
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 "Before downloading from the receiver"
|
||||
msgstr "Przed pobraniem z odbiornika"
|
||||
msgid "Enable experimental features"
|
||||
msgstr "Włącz funkcje eksperymentalne"
|
||||
|
||||
#Streaming
|
||||
msgid "Streaming"
|
||||
msgstr "Transmisja"
|
||||
msgid "Can't Playback!"
|
||||
msgstr "Nie można odtworzyć!"
|
||||
|
||||
msgid "Record to disk:"
|
||||
msgstr "Nagrywanie na dysk:"
|
||||
msgid "Enable Dark Mode"
|
||||
msgstr "Włącz tryb ciemny"
|
||||
|
||||
msgid "Activate transcoding"
|
||||
msgstr "Aktywuj transkodowanie"
|
||||
msgid "Extract..."
|
||||
msgstr "Rozpakuj…"
|
||||
|
||||
msgid "Presets:"
|
||||
msgstr "Ustawienia wstępne:"
|
||||
msgid "Unsupported format!"
|
||||
msgstr "Format nieobsługiwany!"
|
||||
|
||||
msgid "720p TV/device"
|
||||
msgstr "Dla urządzeń z obsługą rozdzielczości 720p"
|
||||
msgid "Combine with the current data?"
|
||||
msgstr "Połączyć z aktualnymi danymi?"
|
||||
|
||||
msgid "1080p TV/device"
|
||||
msgstr "Dla urządzeń z obsługą rozdzielczości 1080p"
|
||||
msgid "Importing data done!"
|
||||
msgstr "Importowanie danych zakończone!"
|
||||
|
||||
msgid "Video options:"
|
||||
msgstr "Opcje wideo:"
|
||||
msgid "Current service"
|
||||
msgstr "Bieżąca usługa"
|
||||
|
||||
msgid "Width (px):"
|
||||
msgstr "Szerokość (px):"
|
||||
msgid "Open folder"
|
||||
msgstr "Otwórz folder"
|
||||
|
||||
msgid "Height (px):"
|
||||
msgstr "Wysokość (px):"
|
||||
msgid "Open archive"
|
||||
msgstr "Otwórz archiwum"
|
||||
|
||||
msgid "Codec:"
|
||||
msgstr "Kodek:"
|
||||
msgid "Import from Web"
|
||||
msgstr "Importuj z sieci"
|
||||
|
||||
msgid "Audio options:"
|
||||
msgstr "Opcje audio:"
|
||||
msgid "Control"
|
||||
msgstr "Kontrola"
|
||||
|
||||
msgid "Services"
|
||||
msgstr "Kanały"
|
||||
msgid "Timers"
|
||||
msgstr "Timery"
|
||||
|
||||
msgid "Sample rate (Hz):"
|
||||
msgstr "Częstotliwość próbkowania (Hz):"
|
||||
msgid "Timer"
|
||||
msgstr "Timer"
|
||||
|
||||
msgid "Play streams mode:"
|
||||
msgstr "Odtwarzaj tryb strumieni:"
|
||||
msgid "Add timer"
|
||||
msgstr "Dodaj timer"
|
||||
|
||||
msgid "Bulit-in player"
|
||||
msgstr "Wbudowany odtwarzacz"
|
||||
msgid "Hr."
|
||||
msgstr "Hr."
|
||||
|
||||
msgid "VLC media player"
|
||||
msgstr "Odtwarzacz multimedialny VLC"
|
||||
msgid "Min."
|
||||
msgstr "Min."
|
||||
|
||||
msgid "Only get m3u file"
|
||||
msgstr "Tylko pobierz plik m3u"
|
||||
msgid "Power"
|
||||
msgstr "Włącz"
|
||||
|
||||
# Paths
|
||||
msgid "Paths"
|
||||
msgstr "Ścieżki"
|
||||
msgid "Standby"
|
||||
msgstr "Czuwanie"
|
||||
|
||||
msgid "Make profile folder as default for the additional data"
|
||||
msgstr "Ustaw folder profilu jako domyślny dla dodatkowych danych"
|
||||
msgid "Wake Up"
|
||||
msgstr "Wybudź"
|
||||
|
||||
msgid "Reboot"
|
||||
msgstr "Restart"
|
||||
|
||||
msgid "Restart GUI"
|
||||
msgstr "Restart Interfejsu Graficznego"
|
||||
|
||||
msgid "Shutdown"
|
||||
msgstr "Wyłącz"
|
||||
|
||||
msgid "Shut down"
|
||||
msgstr "Wyłacz"
|
||||
|
||||
msgid "Do Nothing"
|
||||
msgstr "Nie rób nic"
|
||||
|
||||
msgid "Auto"
|
||||
msgstr "Auto"
|
||||
|
||||
msgid "Grab screenshot"
|
||||
msgstr "Zrób zrzut ekranu"
|
||||
|
||||
msgid "Enabled:"
|
||||
msgstr "Włączony:"
|
||||
|
||||
msgid "Name:"
|
||||
msgstr "Nazwa:"
|
||||
|
||||
msgid "Description:"
|
||||
msgstr "Opis:"
|
||||
|
||||
msgid "Service:"
|
||||
msgstr "Usługa:"
|
||||
|
||||
msgid "Service reference:"
|
||||
msgstr "Odniesienie do usługi:"
|
||||
|
||||
msgid "Event ID:"
|
||||
msgstr "Identyfikator wydarzenia:"
|
||||
|
||||
msgid "Begins:"
|
||||
msgstr "Początek:"
|
||||
|
||||
msgid "Ends:"
|
||||
msgstr "Koniec:"
|
||||
|
||||
msgid "Repeated:"
|
||||
msgstr "Powtórz:"
|
||||
|
||||
msgid "Action:"
|
||||
msgstr "Akcja:"
|
||||
|
||||
msgid "After event:"
|
||||
msgstr "Po wydarzeniu:"
|
||||
|
||||
msgid "Location:"
|
||||
msgstr "Lokalizacja:"
|
||||
|
||||
msgid "Mo"
|
||||
msgstr "Po"
|
||||
|
||||
msgid "Tu"
|
||||
msgstr "Wt"
|
||||
|
||||
msgid "We"
|
||||
msgstr "Śr"
|
||||
|
||||
msgid "Th"
|
||||
msgstr "Cz"
|
||||
|
||||
msgid "Fr"
|
||||
msgstr "Pt"
|
||||
|
||||
msgid "Sa"
|
||||
msgstr "So"
|
||||
|
||||
msgid "Su"
|
||||
msgstr "Ni"
|
||||
|
||||
msgid "Set"
|
||||
msgstr "Ustaw"
|
||||
|
||||
msgid "Services update"
|
||||
msgstr "Aktualizacja usług"
|
||||
|
||||
msgid "Create folder"
|
||||
msgstr "Utwórz folder"
|
||||
|
||||
msgid "FTP client"
|
||||
msgstr "Klient-FTP"
|
||||
|
||||
msgid "The file size is too large!"
|
||||
msgstr "Rozmiar pliku jest za duży!"
|
||||
|
||||
msgid "Connect"
|
||||
msgstr "Połącz"
|
||||
|
||||
msgid "Disconnect"
|
||||
msgstr "Rozłącz"
|
||||
|
||||
msgid "Size"
|
||||
msgstr "Rozmiar"
|
||||
|
||||
msgid "Date"
|
||||
msgstr "Data"
|
||||
|
||||
msgid "Toggle display position"
|
||||
msgstr "Przełącz pozycję wyświetlania"
|
||||
|
||||
msgid "Alternatives"
|
||||
msgstr "Alternatywy"
|
||||
|
||||
msgid "Add alternatives"
|
||||
msgstr "Dodaj alternatywy"
|
||||
|
||||
msgid "DreamOS only!"
|
||||
msgstr "Tylko DreamOS!"
|
||||
|
||||
msgid "A similar service is already in this list!"
|
||||
msgstr "Podobna usługa jest już na tej liście!"
|
||||
|
||||
msgid ""
|
||||
"Play mode has been changed!\n"
|
||||
"Restart the program to apply the settings."
|
||||
msgstr ""
|
||||
"Tryb odtwarzania został zmieniony!\n"
|
||||
"Uruchom ponownie, aby zastosować ustawienia."
|
||||
|
||||
msgid "Set values for TID, NID and Namespace for correct naming of the picons!"
|
||||
msgstr "Ustaw wartości dla TID, NID i Namespace dla poprawnego nazewnictwa pikonów!"
|
||||
|
||||
msgid "Streams detected:"
|
||||
msgstr "Wykryte strumienie:"
|
||||
|
||||
msgid "Download picons"
|
||||
msgstr "Pobierz pikony"
|
||||
|
||||
msgid "Errors:"
|
||||
msgstr "Błędy:"
|
||||
|
||||
msgid "Use to play streams:"
|
||||
msgstr "Użyj do odtwarzania strumieni:"
|
||||
|
||||
msgid "Font in the lists:"
|
||||
msgstr "Czcionka na listach:"
|
||||
|
||||
msgid "Picons size in the lists:"
|
||||
msgstr "Rozmiar pikonów na listach:"
|
||||
|
||||
msgid "Logo size in tooltips:"
|
||||
msgstr "Rozmiar logo w podpowiedziach:"
|
||||
|
||||
msgid "Save as"
|
||||
msgstr "Zapisz jako"
|
||||
|
||||
msgid "Mark duplicates"
|
||||
msgstr "Zaznacz duplikaty"
|
||||
|
||||
msgid "Load only for selected bouquet"
|
||||
msgstr "Załaduj tylko dla wybranego bukietu"
|
||||
|
||||
msgid "The task is canceled!"
|
||||
msgstr "Zadanie anulowane!"
|
||||
|
||||
msgid "Data loading in progress!"
|
||||
msgstr "Trwa ładowanie danych!"
|
||||
|
||||
msgid "Recordings"
|
||||
msgstr "Nagrania"
|
||||
|
||||
msgid "Help"
|
||||
msgstr "Pomoc"
|
||||
|
||||
msgid "HTTP API is not activated. Check your settings!"
|
||||
msgstr "Interfejs API HTTP nie jest aktywowany. Sprawdź swoje ustawienia!"
|
||||
|
||||
msgid "Add picons"
|
||||
msgstr "Dodaj pikony"
|
||||
|
||||
msgid "Logs"
|
||||
msgstr "Logi"
|
||||
|
||||
msgid "Title"
|
||||
msgstr "Tytuł"
|
||||
|
||||
msgid "Time"
|
||||
msgstr "Czas"
|
||||
|
||||
msgid "Length"
|
||||
msgstr "Długość"
|
||||
|
||||
msgid "Additional source"
|
||||
msgstr "Dodatkowe źródło"
|
||||
|
||||
msgid "Automatically set the name selected in the favorites list."
|
||||
msgstr "Automatycznie ustaw nazwę wybraną na liście ulubionych."
|
||||
|
||||
msgid "Playback"
|
||||
msgstr "Odtwarzanie"
|
||||
|
||||
msgid "Audio"
|
||||
msgstr "Audio"
|
||||
|
||||
msgid "Audio Track"
|
||||
msgstr "Ścieżka Audio"
|
||||
|
||||
msgid "Subtitle"
|
||||
msgstr "Napisy"
|
||||
|
||||
msgid "Subtitle Track"
|
||||
msgstr "Ścieżka napisów"
|
||||
|
||||
msgid "Aspect ratio"
|
||||
msgstr "Współczynnik proporcji"
|
||||
|
||||
msgid "This may change the settings of other profiles!"
|
||||
msgstr "Może to zmienić ustawienia innych profili!"
|
||||
|
||||
msgid "Drag the services to the desired picon or picon to the list of selected services."
|
||||
msgstr "Przeciągnij usługi na żądaną ikonę lub na listę wybranych usług."
|
||||
|
||||
msgid "Sets the profile folder as default to store picons, backups, etc."
|
||||
msgstr "Ustawia folder profilu jako domyślny do przechowywania pikonów, kopii zapasowych itp."
|
||||
|
||||
msgid "Default data path:"
|
||||
msgstr "Domyślna ścieżka danych:"
|
||||
|
||||
msgid "Record:"
|
||||
msgstr "Nagrania:"
|
||||
|
||||
msgid "Streams record path:"
|
||||
msgstr "Ścieżka zapisu nagrań:"
|
||||
|
||||
@@ -50,6 +50,9 @@ msgstr "Текущий IP:"
|
||||
msgid "Assign"
|
||||
msgstr "Привязать"
|
||||
|
||||
msgid "Assign file"
|
||||
msgstr "Привязать файл"
|
||||
|
||||
msgid "Bouquet details"
|
||||
msgstr "Сервисы букета"
|
||||
|
||||
@@ -203,8 +206,8 @@ msgstr "Текущий путь к данным:"
|
||||
msgid "Data:"
|
||||
msgstr "Данные:"
|
||||
|
||||
msgid "Enigma2 channel and satellite list editor for GNU/Linux."
|
||||
msgstr "Редактор списка каналов и спутников Enigma2\n для GNU/Linux."
|
||||
msgid "Enigma2 channel and satellite list editor."
|
||||
msgstr "Редактор списка каналов и спутников Enigma2."
|
||||
|
||||
msgid "Host:"
|
||||
msgstr "Адрес ресивера:"
|
||||
@@ -296,8 +299,8 @@ msgstr "Формат имени пиконов:"
|
||||
msgid "Resize:"
|
||||
msgstr "Обрезать:"
|
||||
|
||||
msgid "Current picons path:"
|
||||
msgstr "Текущий путь к пиконам:"
|
||||
msgid "Current picons path"
|
||||
msgstr "Текущий путь к пиконам"
|
||||
|
||||
msgid "Receiver picons path:"
|
||||
msgstr "Путь к пиконам ресивера:"
|
||||
@@ -1246,3 +1249,66 @@ msgstr "Записи"
|
||||
|
||||
msgid "Help"
|
||||
msgstr "Справка"
|
||||
|
||||
msgid "HTTP API is not activated. Check your settings!"
|
||||
msgstr "HTTP API не активировано. Проверьте настройки!"
|
||||
|
||||
msgid "Add picons"
|
||||
msgstr "Добавить пиконы"
|
||||
|
||||
msgid "Logs"
|
||||
msgstr "Журнал"
|
||||
|
||||
msgid "Title"
|
||||
msgstr "Название"
|
||||
|
||||
msgid "Time"
|
||||
msgstr "Время"
|
||||
|
||||
msgid "Length"
|
||||
msgstr "Длительность"
|
||||
|
||||
msgid "Additional source"
|
||||
msgstr "Дополнительный источник"
|
||||
|
||||
msgid "Automatically set the name selected in the favorites list."
|
||||
msgstr "Автоматическая установка имени из списка избранного."
|
||||
|
||||
msgid "Playback"
|
||||
msgstr "Воспроизведение"
|
||||
|
||||
msgid "Audio"
|
||||
msgstr "Аудио"
|
||||
|
||||
msgid "Audio Track"
|
||||
msgstr "Аудиодорожка"
|
||||
|
||||
msgid "Subtitle"
|
||||
msgstr "Субтитры"
|
||||
|
||||
msgid "Subtitle Track"
|
||||
msgstr "Дорожка субтитров"
|
||||
|
||||
msgid "Aspect ratio"
|
||||
msgstr "Соотношение сторон"
|
||||
|
||||
msgid "This may change the settings of other profiles!"
|
||||
msgstr "Это может изменить настройки других профилей!"
|
||||
|
||||
msgid "Drag the services to the desired picon or picon to the list of selected services."
|
||||
msgstr "Перетащите сервисы на нужный пикон или пикон на список выбранных сервисов."
|
||||
|
||||
msgid "Sets the profile folder as default to store picons, backups, etc."
|
||||
msgstr "Устанавливает папку профиля по умолчанию для хранения пиконов, резервных копий и т. п."
|
||||
|
||||
msgid "New sub-bouquet"
|
||||
msgstr "Создать суббукет"
|
||||
|
||||
msgid "Mark not presented in Bouquets"
|
||||
msgstr "Отметить отсутствующие в букетах"
|
||||
|
||||
msgid "Not in Bouquets"
|
||||
msgstr "Не в букетах"
|
||||
|
||||
msgid "Do not show services present in Bouquets."
|
||||
msgstr "Не показывать сервисы присутствующие в букетах."
|
||||
|
||||
@@ -3,13 +3,13 @@ msgstr ""
|
||||
"Project-Id-Version: DemonEditor\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2020-04-16 15:59+0300\n"
|
||||
"PO-Revision-Date: 2021-06-13 14:54+0300\n"
|
||||
"PO-Revision-Date: 2021-11-11 23:49+0300\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Last-Translator: audi06_19 <info@dreamosat-forum.com>\n"
|
||||
"Last-Translator: audi06_19 <audi06_19@hotmail.com>\n"
|
||||
"Language-Team: \n"
|
||||
"X-Generator: Poedit 2.4.1\n"
|
||||
"X-Generator: Poedit 3.0\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
"Language: tr\n"
|
||||
|
||||
@@ -206,8 +206,8 @@ msgstr "Mevcut veri yolu:"
|
||||
msgid "Data:"
|
||||
msgstr "Veri:"
|
||||
|
||||
msgid "Enigma2 channel and satellite list editor for GNU/Linux."
|
||||
msgstr "GNU/Linux için Enigma2 kanalı ve uydu listesi editörü."
|
||||
msgid "Enigma2 channel and satellite list editor."
|
||||
msgstr "Enigma2 kanal ve uydu listesi düzenleyicisi."
|
||||
|
||||
msgid "Host:"
|
||||
msgstr "Ana bilgisayar:"
|
||||
@@ -299,8 +299,8 @@ msgstr "Piconların adı biçimi:"
|
||||
msgid "Resize:"
|
||||
msgstr "Yeniden boyutlandır:"
|
||||
|
||||
msgid "Current picons path:"
|
||||
msgstr "Geçerli picon yolları:"
|
||||
msgid "Current picons path"
|
||||
msgstr "Mevcut piconların yolu"
|
||||
|
||||
msgid "Receiver picons path:"
|
||||
msgstr "Alıcı picon yolu:"
|
||||
@@ -1269,3 +1269,63 @@ msgstr "Yalnızca seçilen buket için yükle"
|
||||
|
||||
msgid "The task is canceled!"
|
||||
msgstr "Görev iptal edildi!"
|
||||
|
||||
msgid "Data loading in progress!"
|
||||
msgstr "Veri yükleme devam ediyor!"
|
||||
|
||||
msgid "Recordings"
|
||||
msgstr "Kayıtlar"
|
||||
|
||||
msgid "Help"
|
||||
msgstr "Yardım"
|
||||
|
||||
msgid "HTTP API is not activated. Check your settings!"
|
||||
msgstr "HTTP API etkinleştirilmedi. Ayarlarınızı kontrol edin!"
|
||||
|
||||
msgid "Add picons"
|
||||
msgstr "Piconlar ekle"
|
||||
|
||||
msgid "Logs"
|
||||
msgstr "Loglar"
|
||||
|
||||
msgid "Title"
|
||||
msgstr "Başlık"
|
||||
|
||||
msgid "Time"
|
||||
msgstr "Zaman"
|
||||
|
||||
msgid "Length"
|
||||
msgstr "Uzunluk"
|
||||
|
||||
msgid "Additional source"
|
||||
msgstr "Ek kaynak"
|
||||
|
||||
msgid "Automatically set the name selected in the favorites list."
|
||||
msgstr "Favoriler listesinde seçilen adı otomatik olarak ayarlayın."
|
||||
|
||||
msgid "Playback"
|
||||
msgstr "Oynatım"
|
||||
|
||||
msgid "Audio"
|
||||
msgstr "Ses"
|
||||
|
||||
msgid "Audio Track"
|
||||
msgstr "Ses parçası"
|
||||
|
||||
msgid "Subtitle"
|
||||
msgstr "Altyazı"
|
||||
|
||||
msgid "Subtitle Track"
|
||||
msgstr "Altyazı Parçası"
|
||||
|
||||
msgid "Aspect ratio"
|
||||
msgstr "En boy oranı"
|
||||
|
||||
msgid "This may change the settings of other profiles!"
|
||||
msgstr "Bu, diğer profillerin ayarlarını değiştirebilir!"
|
||||
|
||||
msgid "Drag the services to the desired picon or picon to the list of selected services."
|
||||
msgstr "Hizmetleri istediğiniz simgeye veya simgeyi seçili hizmetler listesine sürükleyin."
|
||||
|
||||
msgid "Sets the profile folder as default to store picons, backups, etc."
|
||||
msgstr "Picon'ları, yedekleri vb. depolamak için profil klasörünü varsayılan olarak ayarlar."
|
||||
|
||||
Reference in New Issue
Block a user