mirror of
https://github.com/DYefremov/DemonEditor.git
synced 2026-05-09 03:06:35 +02:00
Compare commits
60 Commits
1.0.5-b1-m
...
1.0.7-a1-w
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cf6fc09a20 | ||
|
|
e3df33e50f | ||
|
|
cece51e069 | ||
|
|
cb6fbb5107 | ||
|
|
8329149b1f | ||
|
|
137ee213dc | ||
|
|
7cb1787de7 | ||
|
|
b4bca084de | ||
|
|
3990ee6572 | ||
|
|
46e8be54dd | ||
|
|
4d35f71ddc | ||
|
|
38ff00bfb3 | ||
|
|
d8f9dfe50e | ||
|
|
b6f3d888cb | ||
|
|
043a0371d2 | ||
|
|
e613f9f55e | ||
|
|
2806d95972 | ||
|
|
a34798f215 | ||
|
|
d6738826d3 | ||
|
|
a437ec6030 | ||
|
|
9cc8605994 | ||
|
|
a972ee353f | ||
|
|
c8d38161ae | ||
|
|
3758c738fe | ||
|
|
be2d64e480 | ||
|
|
76e732c8a0 | ||
|
|
b18c4e254e | ||
|
|
1ad7781de7 | ||
|
|
2325c0e541 | ||
|
|
e81e13a5c0 | ||
|
|
5b12777223 | ||
|
|
e0c953ee05 | ||
|
|
3dc4caf65d | ||
|
|
8308b715dd | ||
|
|
3a8831f0f9 | ||
|
|
a020a23211 | ||
|
|
a67c81235c | ||
|
|
3ef587841e | ||
|
|
55a21fbc18 | ||
|
|
b60a9a69b6 | ||
|
|
8b517f6f88 | ||
|
|
81dd12a038 | ||
|
|
17de78f169 | ||
|
|
3411f32868 | ||
|
|
5f669f4480 | ||
|
|
f56e4b616a | ||
|
|
653ef1422f | ||
|
|
9d5e07af1f | ||
|
|
399c1ff01b | ||
|
|
ad8e6975b1 | ||
|
|
5f79b27daa | ||
|
|
3b23ddc1a7 | ||
|
|
a0e3566bec | ||
|
|
1cada0408f | ||
|
|
e1a5b8e39d | ||
|
|
a46c6ae816 | ||
|
|
33137ee879 | ||
|
|
51181057b1 | ||
|
|
ca1e823bf1 | ||
|
|
7fb2d9ac4a |
@@ -1,67 +1,57 @@
|
||||
import os
|
||||
import datetime
|
||||
import distutils.util
|
||||
# -*- mode: python ; coding: utf-8 -*-
|
||||
|
||||
EXE_NAME = 'start.py'
|
||||
DIR_PATH = os.getcwd()
|
||||
COMPILING_PLATFORM = distutils.util.get_platform()
|
||||
PATH_EXE = [os.path.join(DIR_PATH, EXE_NAME)]
|
||||
STRIP = True
|
||||
BUILD_DATE = datetime.datetime.now().strftime("%Y%m%d")
|
||||
|
||||
block_cipher = None
|
||||
|
||||
ui_files = [('app/ui/*.glade', 'ui'),
|
||||
('app/ui/*.css', 'ui'),
|
||||
('app/ui/*.ui', 'ui'),
|
||||
('app/ui/lang*', 'share/locale'),
|
||||
('app/ui/icons*', 'share/icons')
|
||||
|
||||
excludes = ['app.tools.mpv',
|
||||
'gi.repository.Gst',
|
||||
'gi.repository.GstBase',
|
||||
'gi.repository.GstVideo',
|
||||
'youtube_dl',
|
||||
'tkinter']
|
||||
|
||||
|
||||
ui_files = [('app\\ui\\*.glade', 'ui'),
|
||||
('app\\ui\\*.css', 'ui'),
|
||||
('app\\ui\\*.ui', 'ui'),
|
||||
('app\\ui\\lang*', 'share\\locale'),
|
||||
('app\\ui\\icons*', 'share\\icons')
|
||||
]
|
||||
|
||||
|
||||
a = Analysis([EXE_NAME],
|
||||
pathex=PATH_EXE,
|
||||
binaries=None,
|
||||
binaries=[],
|
||||
datas=ui_files,
|
||||
hiddenimports=['fileinput', 'uuid'],
|
||||
hiddenimports=[],
|
||||
hookspath=[],
|
||||
runtime_hooks=[],
|
||||
excludes=['youtube_dl', 'tkinter'],
|
||||
excludes=excludes,
|
||||
win_no_prefer_redirects=False,
|
||||
win_private_assemblies=False,
|
||||
cipher=block_cipher,
|
||||
noarchive=False)
|
||||
pyz = PYZ(a.pure, a.zipped_data,
|
||||
cipher=block_cipher)
|
||||
|
||||
pyz = PYZ(a.pure,
|
||||
a.zipped_data,
|
||||
cipher=block_cipher)
|
||||
|
||||
exe = EXE(pyz,
|
||||
a.scripts,
|
||||
[],
|
||||
exclude_binaries=True,
|
||||
name='DemonEditor',
|
||||
debug=False,
|
||||
strip=STRIP,
|
||||
bootloader_ignore_signals=False,
|
||||
strip=False,
|
||||
upx=True,
|
||||
console=False)
|
||||
|
||||
console=False, icon='icon.ico')
|
||||
coll = COLLECT(exe,
|
||||
a.binaries,
|
||||
a.zipfiles,
|
||||
a.datas,
|
||||
strip=STRIP,
|
||||
strip=False,
|
||||
upx=True,
|
||||
upx_exclude=[],
|
||||
name='DemonEditor')
|
||||
|
||||
app = BUNDLE(coll,
|
||||
name='DemonEditor.app',
|
||||
icon='icon.icns',
|
||||
bundle_identifier=None,
|
||||
info_plist={
|
||||
'NSPrincipalClass': 'NSApplication',
|
||||
'CFBundleName': 'DemonEditor',
|
||||
'CFBundleDisplayName': 'DemonEditor',
|
||||
'CFBundleGetInfoString': "Enigma2 channel and satellites editor",
|
||||
'LSApplicationCategoryType': 'public.app-category.utilities',
|
||||
'CFBundleShortVersionString': "1.0.4 Beta (Build: {})".format(BUILD_DATE),
|
||||
'NSHumanReadableCopyright': u"Copyright © 2020, Dmitriy Yefremov",
|
||||
'NSRequiresAquaSystemAppearance': 'false'
|
||||
})
|
||||
|
||||
86
README.md
86
README.md
@@ -1,8 +1,8 @@
|
||||
# <img src="app/ui/icons/hicolor/96x96/apps/demon-editor.png" width="32" /> DemonEditor
|
||||
[](LICENSE) 
|
||||
## Enigma2 channel and satellite list editor for macOS (experimental).
|
||||
[](LICENSE) 
|
||||
## Enigma2 channel and satellite list editor for MS Windows (experimental).
|
||||
|
||||

|
||||
[<img src="https://user-images.githubusercontent.com/7511379/116426009-6bd3fa80-a84b-11eb-81ea-3407396a0c4b.png" width="850"/>](https://user-images.githubusercontent.com/7511379/116426009-6bd3fa80-a84b-11eb-81ea-3407396a0c4b.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).
|
||||
@@ -18,65 +18,40 @@ Focused on the convenience of working in lists from the keyboard. The mouse is a
|
||||
* Import to bouquet(Neutrino WEBTV) from m3u.
|
||||
* Export of bouquets with IPTV services in m3u.
|
||||
* Assignment of EPGs from DVB or XML for IPTV services (only Enigma2, experimental).
|
||||
* Preview (playback) of IPTV or other streams directly from the bouquet list (should be installed [VLC](https://www.videolan.org/vlc/)).
|
||||
* Preview (playback) of IPTV or other streams directly from the bouquet list.
|
||||
* Control panel with the ability to view EPG and manage timers (via HTTP API, experimental).
|
||||
* Simple FTP client (experimental).
|
||||
|
||||
#### Keyboard shortcuts
|
||||
* **⌘ + X** - only in bouquet list.
|
||||
* **⌘ + C** - only in services list.
|
||||
Clipboard is **"rubber"**. There is an accumulation before the insertion!
|
||||
* **⌘ + E** - edit.
|
||||
* **⌘ + R, F2** - rename.
|
||||
* **⌘ + S, T** in Satellites edit tool for create satellite or transponder.
|
||||
* **⌘ + L** - parental lock.
|
||||
* **⌘ + H** - hide/skip.
|
||||
* **⌘ + P** - start play IPTV or other stream in the bouquet list.
|
||||
* **⌘ + Z** - switch(**zap**) the channel(works when the HTTP API is enabled, Enigma2 only).
|
||||
* **⌘ + W** - switch to the channel and watch in the program.
|
||||
* **⌘ + Up/Down** - move selected items in the list.
|
||||
* **⌘ + O** - (re)load user data from current dir.
|
||||
* **⌘ + D** - load data from receiver.
|
||||
* **⌘ + U/B** - upload data/bouquets to receiver.
|
||||
* **⌘ + F** - show/hide search bar.
|
||||
* **⇧ + ⌘ + F** - show/hide filter bar.
|
||||
* **Left/Right** - remove selection.
|
||||
#### Keyboard shortcuts
|
||||
* **Ctrl + X** - only in bouquet list.
|
||||
* **Ctrl + C** - only in services list.
|
||||
Clipboard is **"rubber"**. There is an accumulation before the insertion!
|
||||
* **Ctrl + Insert** - copies the selected channels from the main list to the bouquet
|
||||
beginning or inserts (creates) a new bouquet.
|
||||
* **Ctrl + BackSpace** - copies the selected channels from the main list to the bouquet end.
|
||||
* **Ctrl + E** - edit.
|
||||
* **Ctrl + R, F2** - rename.
|
||||
* **Ctrl + S, T** in Satellites edit tool for create satellite or transponder.
|
||||
* **Ctrl + L** - parental lock.
|
||||
* **Ctrl + H** - hide/skip.
|
||||
* **Ctrl + P** - start play IPTV or other stream in the bouquet list.
|
||||
* **Ctrl + Z** - switch(**zap**) the channel(works when the HTTP API is enabled, Enigma2 only).
|
||||
* **Ctrl + W** - switch to the channel and watch in the program.
|
||||
* **Space** - select/deselect.
|
||||
* **Left/Right** - remove selection.
|
||||
* **Ctrl + Up, Down, PageUp, PageDown, Home, End**- move selected items in the list.
|
||||
* **Ctrl + O** - (re)load user data from current dir.
|
||||
* **Ctrl + D** - load data from receiver.
|
||||
* **Ctrl + U/B** - upload data/bouquets to receiver.
|
||||
* **Ctrl + I** - extra info, details.
|
||||
* **Ctrl + F** - show/hide search bar.
|
||||
* **Ctrl + Shift + F** - show/hide filter bar.
|
||||
|
||||
For **multiple** selection with the mouse, press and hold the **⌘** key!
|
||||
For **multiple** selection with the mouse, press and hold the **Ctrl** key!
|
||||
|
||||
## Minimum requirements
|
||||
*Python >= 3.5.2, GTK+ >= 3.16 with PyGObject bindings, python3-requests.*
|
||||
*Python >= 3.5.2, GTK+ >= 3.22 with PyGObject bindings, python3-requests.*
|
||||
|
||||
## Installation and Launch
|
||||
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```
|
||||
#### Optional:
|
||||
```brew install wget```
|
||||
```pip3 install pillow, pyobjc```
|
||||
|
||||
To start the program, just download the [archive](https://github.com/DYefremov/DemonEditor/archive/experimental-mac.zip), unpack and run it from the terminal
|
||||
with the command: ```./start.py```
|
||||
## Standalone package
|
||||
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!
|
||||
**The package may not contain all the latest changes. Not all features can be supported and tested!**
|
||||
|
||||
THIS SOFTWARE COMES WITH ABSOLUTELY NO WARRANTY.
|
||||
AUTHOR IS NOT LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY CONNECTION WITH THIS SOFTWARE.
|
||||
The package 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 package you agree to the terms of this [license](http://www.gnu.org/licenses/gpl-3.0.html) and the possible inconvenience associated with this!
|
||||
|
||||
#### Building your own package
|
||||
Install [PyInstaller](https://www.pyinstaller.org/) with the command from the terminal:
|
||||
|
||||
```pip3 install pyinstaller```
|
||||
|
||||
and in the root dir run command:
|
||||
|
||||
```pyinstaller DemonEditor.spec```
|
||||
## Important
|
||||
**This version is not fully tested and has experimental status!**
|
||||
|
||||
@@ -88,6 +63,7 @@ When using the multiple import feature, from *lamedb* will be taken data **only
|
||||
If you need full set of the data, including *[satellites, terrestrial, cables].xml* (current files will be overwritten),
|
||||
just load your data via *"File/Open"* and press *"Save"*. When importing separate bouquet files, only those services
|
||||
(excluding IPTV) that are in the **current open lamedb** (main list of services) will be imported.
|
||||
|
||||
#### Command line arguments:
|
||||
* **-l** - write logs to file.
|
||||
* **-d on/off** - turn on/off debug mode. Allows to display more information in the logs.
|
||||
|
||||
@@ -67,7 +67,7 @@ def run_with_delay(timeout=5):
|
||||
timer.cancel()
|
||||
|
||||
def run():
|
||||
GLib.idle_add(func, *args, **kwargs, priority=GLib.PRIORITY_LOW)
|
||||
GLib.idle_add(func, priority=GLib.PRIORITY_LOW, *args, **kwargs)
|
||||
|
||||
timer = Timer(interval=timeout, function=run)
|
||||
timer.start()
|
||||
|
||||
@@ -6,7 +6,6 @@ import urllib
|
||||
import xml.etree.ElementTree as ETree
|
||||
from enum import Enum
|
||||
from ftplib import FTP, CRLF, Error, error_perm
|
||||
from http.client import RemoteDisconnected
|
||||
from telnetlib import Telnet
|
||||
from urllib.error import HTTPError, URLError
|
||||
from urllib.parse import urlencode
|
||||
@@ -654,7 +653,7 @@ def get_response(req_type, url, data=None):
|
||||
if req_type is HttpAPI.Request.TEST:
|
||||
raise e
|
||||
return {"error_code": e.code}
|
||||
except (URLError, RemoteDisconnected, ConnectionResetError) as e:
|
||||
except (URLError, ConnectionResetError) as e:
|
||||
if req_type is HttpAPI.Request.TEST:
|
||||
raise e
|
||||
except ETree.ParseError as e:
|
||||
@@ -713,7 +712,7 @@ def test_http(host, port, user, password, timeout=5, use_ssl=False, skip_message
|
||||
|
||||
try:
|
||||
return get_response(HttpAPI.Request.TEST, "{}/web/{}".format(base_url, params), data).get("e2statetext", "")
|
||||
except (RemoteDisconnected, URLError, HTTPError) as e:
|
||||
except (URLError, HTTPError) as e:
|
||||
raise TestException(e)
|
||||
|
||||
|
||||
|
||||
@@ -35,6 +35,7 @@ class TrType(Enum):
|
||||
Satellite = "s"
|
||||
Terrestrial = "t"
|
||||
Cable = "c"
|
||||
ATSC = "a"
|
||||
|
||||
|
||||
class BqType(Enum):
|
||||
@@ -147,6 +148,10 @@ T_SYSTEM = {"0": "DVB-T", "1": "DVB-T2", "-1": "DVB-T/T2"}
|
||||
# Cable
|
||||
C_MODULATION = {"0": "Auto", "1": "QAM16", "2": "QAM32", "3": "QAM64", "4": "QAM128", "5": "QAM256"}
|
||||
|
||||
# ATSC
|
||||
A_MODULATION = {"0": "Auto", "1": "QAM16", "2": "QAM32", "3": "QAM64", "4": "QAM128", "5": "QAM256", "6": "8VSB",
|
||||
"7": "16VSB"}
|
||||
|
||||
# CAS
|
||||
CAS = {"C:26": "BISS", "C:0B": "Conax", "C:06": "Irdeto", "C:18": "Nagravision", "C:05": "Viaccess", "C:01": "SECA",
|
||||
"C:0E": "PowerVu", "C:4A": "DRE-Crypt", "C:7B": "DRE-Crypt", "C:56": "Verimatrix", "C:09": "VideoGuard"}
|
||||
|
||||
@@ -9,14 +9,15 @@ __FILE_NAME = "blacklist"
|
||||
|
||||
def get_blacklist(path):
|
||||
with suppress(FileNotFoundError):
|
||||
with open(path + __FILE_NAME, "r") as file:
|
||||
with open(path + __FILE_NAME, "r", encoding="utf-8") as file:
|
||||
# filter empty values and "\n"
|
||||
return {*list(filter(None, (x.strip() for x in file.readlines())))}
|
||||
return {}
|
||||
|
||||
return set(filter(None, (x.strip() for x in file.readlines())))
|
||||
return set()
|
||||
|
||||
|
||||
def write_blacklist(path, channels):
|
||||
with open(path + __FILE_NAME, "w") as file:
|
||||
with open(path + __FILE_NAME, "w", encoding="utf-8") as file:
|
||||
if channels:
|
||||
file.writelines("\n".join(channels))
|
||||
|
||||
|
||||
@@ -47,7 +47,7 @@ class LameDbReader:
|
||||
tr_type = tr[0:1]
|
||||
if tr_type == "c":
|
||||
tr += ":0:0:0"
|
||||
elif tr_type == "t":
|
||||
elif tr_type == "t" or tr_type == "a":
|
||||
tr += ":0:0"
|
||||
else:
|
||||
tr_data = tr.split(_SEP)
|
||||
@@ -81,7 +81,7 @@ class LameDbReader:
|
||||
lns = file.readlines()
|
||||
|
||||
if lns and not lns[0].endswith("/5/\n"):
|
||||
raise SyntaxError("lamedb v.5 parsing error: unsupported format.")
|
||||
raise SyntaxError("lamedb ver.5 parsing error: unsupported format.")
|
||||
|
||||
trs, srvs = {}, [""]
|
||||
for line in lns:
|
||||
@@ -95,8 +95,13 @@ class LameDbReader:
|
||||
srv_data.append("p:")
|
||||
srvs.extend(srv_data)
|
||||
elif line.startswith("t:"):
|
||||
tr, srv = line.split(",")
|
||||
trs[tr.strip("t:")] = srv.strip().replace(":", " ", 1)
|
||||
data = line.split(",")
|
||||
len_data = len(data)
|
||||
if len_data > 1:
|
||||
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))
|
||||
|
||||
return self.parse_services(srvs, trs)
|
||||
|
||||
@@ -177,6 +182,10 @@ class LameDbReader:
|
||||
system = "DVB-C"
|
||||
pos = "C"
|
||||
fec = FEC_DEFAULT.get(tr[4])
|
||||
elif tr_type is TrType.ATSC:
|
||||
system = "ATSC"
|
||||
pos = "T"
|
||||
fec = FEC_DEFAULT.get("0")
|
||||
|
||||
# Formatting displayed values.
|
||||
try:
|
||||
@@ -274,7 +283,7 @@ class LameDbWriter:
|
||||
def write(self):
|
||||
if self._fmt == 4:
|
||||
# Writing lamedb file ver.4
|
||||
with open(self._path + _FILE_NAME, "w") as file:
|
||||
with open(self._path + _FILE_NAME, "w", encoding="utf-8") as file:
|
||||
file.writelines(LameDbReader.get_services_lines(self._services))
|
||||
elif self._fmt == 5:
|
||||
self.write_to_lamedb5()
|
||||
@@ -299,7 +308,7 @@ class LameDbWriter:
|
||||
lines.extend(services_lines)
|
||||
lines.append(_END_LINE)
|
||||
|
||||
with open(self._path + "lamedb5", "w") as file:
|
||||
with open(self._path + "lamedb5", "w", encoding="utf-8") as file:
|
||||
file.writelines(lines)
|
||||
|
||||
|
||||
|
||||
@@ -68,7 +68,8 @@ def parse_m3u(path, s_type, detect_encoding=True, params=None):
|
||||
groups.add(grp_name)
|
||||
fav_id = MARKER_FORMAT.format(marker_counter, grp_name, grp_name)
|
||||
marker_counter += 1
|
||||
mr = Service(None, None, None, grp_name, *aggr[0:3], BqServiceType.MARKER.name, *aggr, fav_id, None)
|
||||
mr = Service(None, None, None, grp_name, None, None, None, BqServiceType.MARKER.name, None, None,
|
||||
None, None, None, None, None, None, None, None, fav_id, None)
|
||||
services.append(mr)
|
||||
elif line.startswith("#EXTGRP") and s_type is SettingsType.ENIGMA_2:
|
||||
grp_name = line.strip("#EXTGRP:").strip()
|
||||
@@ -76,7 +77,8 @@ def parse_m3u(path, s_type, detect_encoding=True, params=None):
|
||||
groups.add(grp_name)
|
||||
fav_id = MARKER_FORMAT.format(marker_counter, grp_name, grp_name)
|
||||
marker_counter += 1
|
||||
mr = Service(None, None, None, grp_name, *aggr[0:3], BqServiceType.MARKER.name, *aggr, fav_id, None)
|
||||
mr = Service(None, None, None, grp_name, None, None, None, BqServiceType.MARKER.name, None, None,
|
||||
None, None, None, None, None, None, None, None, fav_id, None)
|
||||
services.append(mr)
|
||||
elif not line.startswith("#"):
|
||||
url = line.strip()
|
||||
@@ -84,7 +86,8 @@ def parse_m3u(path, s_type, detect_encoding=True, params=None):
|
||||
sid_counter += 1
|
||||
fav_id = get_fav_id(url, name, s_type, params)
|
||||
if all((name, url, fav_id)):
|
||||
srv = Service(None, None, IPTV_ICON, name, *aggr[0:3], st, picon, p_id, *s_aggr, url, fav_id, None)
|
||||
srv = Service(None, None, IPTV_ICON, name, None, None, None, st, picon, p_id, None, None, None,
|
||||
None, None, None, None, url, fav_id, None)
|
||||
services.append(srv)
|
||||
else:
|
||||
log("*.m3u* parse error ['{}']: name[{}], url[{}], fav id[{}]".format(path, name, url, fav_id))
|
||||
@@ -120,7 +123,9 @@ def get_fav_id(url, service_name, settings_type, params=None, stream_type=None,
|
||||
if settings_type is SettingsType.ENIGMA_2:
|
||||
stream_type = stream_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)
|
||||
v1, v2, v3, v4 = params
|
||||
return ENIGMA2_FAV_ID_FORMAT.format(stream_type, s_type, v1, v2, v3, v4, quote(url),
|
||||
service_name, service_name, None)
|
||||
elif settings_type is SettingsType.NEUTRINO_MP:
|
||||
return NEUTRINO_FAV_ID_FORMAT.format(url, "", 0, None, None, None, None, "", "", 1)
|
||||
|
||||
|
||||
@@ -53,9 +53,9 @@ def write_satellites(satellites, data_path):
|
||||
transponder_child.setAttribute("frequency", tr.frequency)
|
||||
transponder_child.setAttribute("symbol_rate", tr.symbol_rate)
|
||||
transponder_child.setAttribute("polarization", get_key_by_value(POLARIZATION, tr.polarization))
|
||||
transponder_child.setAttribute("fec_inner", get_key_by_value(FEC, tr.fec_inner))
|
||||
transponder_child.setAttribute("system", get_key_by_value(SYSTEM, tr.system))
|
||||
transponder_child.setAttribute("modulation", get_key_by_value(MODULATION, tr.modulation))
|
||||
transponder_child.setAttribute("fec_inner", get_key_by_value(FEC, tr.fec_inner) or "0")
|
||||
transponder_child.setAttribute("system", get_key_by_value(SYSTEM, tr.system) or "0")
|
||||
transponder_child.setAttribute("modulation", get_key_by_value(MODULATION, tr.modulation) or "0")
|
||||
if tr.pls_mode:
|
||||
transponder_child.setAttribute("pls_mode", tr.pls_mode)
|
||||
if tr.pls_code:
|
||||
@@ -90,7 +90,6 @@ def parse_transponders(elem, sat_name):
|
||||
atr["is_id"].value if "is_id" in atr else None)
|
||||
except Exception as e:
|
||||
message = "Error: can't parse transponder for '{}' satellite! {}".format(sat_name, repr(e))
|
||||
print(message)
|
||||
log(message)
|
||||
else:
|
||||
transponders.append(tr)
|
||||
|
||||
135
app/settings.py
135
app/settings.py
@@ -5,16 +5,17 @@ import os
|
||||
import sys
|
||||
from enum import Enum, IntEnum
|
||||
from functools import lru_cache
|
||||
from pathlib import Path
|
||||
from pprint import pformat
|
||||
from textwrap import dedent
|
||||
|
||||
HOME_PATH = str(Path.home())
|
||||
CONFIG_PATH = HOME_PATH + "/.config/demon-editor/"
|
||||
SEP = os.sep
|
||||
HOME_PATH = os.path.expanduser("~")
|
||||
CONFIG_PATH = HOME_PATH + "{}.config{}demon-editor{}".format(SEP, SEP, SEP)
|
||||
CONFIG_FILE = CONFIG_PATH + "config.json"
|
||||
DATA_PATH = HOME_PATH + "/DemonEditor/data/"
|
||||
DATA_PATH = HOME_PATH + "{}DemonEditor{}data{}".format(SEP, SEP, SEP)
|
||||
|
||||
IS_DARWIN = sys.platform == "darwin"
|
||||
IS_WIN = sys.platform == "win32"
|
||||
|
||||
|
||||
class Defaults(Enum):
|
||||
@@ -24,25 +25,28 @@ class Defaults(Enum):
|
||||
BACKUP_BEFORE_SAVE = True
|
||||
V5_SUPPORT = False
|
||||
FORCE_BQ_NAMES = False
|
||||
HTTP_API_SUPPORT = False
|
||||
HTTP_API_SUPPORT = IS_WIN
|
||||
ENABLE_YT_DL = False
|
||||
ENABLE_SEND_TO = False
|
||||
USE_COLORS = True
|
||||
NEW_COLOR = "rgb(255,230,204)"
|
||||
EXTRA_COLOR = "rgb(179,230,204)"
|
||||
TOOLTIP_LOGO_SIZE = 96
|
||||
LIST_PICON_SIZE = 32
|
||||
FAV_CLICK_MODE = 0
|
||||
PLAY_STREAMS_MODE = 1 if IS_DARWIN else 0
|
||||
STREAM_LIB = "mpv" if IS_WIN else "vlc"
|
||||
PROFILE_FOLDER_DEFAULT = False
|
||||
RECORDS_PATH = DATA_PATH + "records/"
|
||||
RECORDS_PATH = DATA_PATH + "records{}".format(SEP)
|
||||
ACTIVATE_TRANSCODING = False
|
||||
ACTIVE_TRANSCODING_PRESET = "720p TV/device"
|
||||
ACTIVE_TRANSCODING_PRESET = "720p TV{}device".format(SEP)
|
||||
|
||||
|
||||
def get_settings():
|
||||
if not os.path.isfile(CONFIG_FILE) or os.stat(CONFIG_FILE).st_size == 0:
|
||||
write_settings(get_default_settings())
|
||||
|
||||
with open(CONFIG_FILE, "r") as config_file:
|
||||
with open(CONFIG_FILE, "r", encoding="utf-8") as config_file:
|
||||
return json.load(config_file)
|
||||
|
||||
|
||||
@@ -76,18 +80,18 @@ def get_default_transcoding_presets():
|
||||
|
||||
def write_settings(config):
|
||||
os.makedirs(os.path.dirname(CONFIG_PATH), exist_ok=True)
|
||||
with open(CONFIG_FILE, "w") as config_file:
|
||||
with open(CONFIG_FILE, "w", encoding="utf-8") as config_file:
|
||||
json.dump(config, config_file, indent=" ")
|
||||
|
||||
|
||||
def set_local_paths(settings, profile_name, data_path=DATA_PATH, use_profile_folder=False):
|
||||
settings["data_local_path"] = "{}{}/".format(data_path, profile_name)
|
||||
settings["data_local_path"] = "{}{}{}".format(data_path, profile_name, SEP)
|
||||
if use_profile_folder:
|
||||
settings["picons_local_path"] = "{}{}/{}/".format(data_path, profile_name, "picons")
|
||||
settings["backup_local_path"] = "{}{}/{}/".format(data_path, profile_name, "backup")
|
||||
settings["picons_local_path"] = "{}{}{}{}{}".format(data_path, profile_name, SEP, "picons", SEP)
|
||||
settings["backup_local_path"] = "{}{}{}{}{}".format(data_path, profile_name, SEP, "backup", SEP)
|
||||
else:
|
||||
settings["picons_local_path"] = "{}{}/{}/".format(data_path, "picons", profile_name)
|
||||
settings["backup_local_path"] = "{}{}/{}/".format(data_path, "backup", profile_name)
|
||||
settings["picons_local_path"] = "{}{}{}{}{}".format(data_path, "picons", SEP, profile_name, SEP)
|
||||
settings["backup_local_path"] = "{}{}{}{}{}".format(data_path, "backup", SEP, profile_name, SEP)
|
||||
|
||||
|
||||
class SettingsType(IntEnum):
|
||||
@@ -97,28 +101,29 @@ class SettingsType(IntEnum):
|
||||
|
||||
def get_default_settings(self):
|
||||
""" Returns default settings for current type """
|
||||
if self is self.ENIGMA_2:
|
||||
if self is SettingsType.ENIGMA_2:
|
||||
return {"setting_type": self.value,
|
||||
"host": "127.0.0.1", "port": "21", "timeout": 5,
|
||||
"user": "root", "password": "root",
|
||||
"http_port": "80", "http_timeout": 5, "http_use_ssl": False,
|
||||
"telnet_port": "23", "telnet_timeout": 5,
|
||||
"services_path": "/etc/enigma2/", "user_bouquet_path": "/etc/enigma2/",
|
||||
"satellites_xml_path": "/etc/tuxbox/", "data_local_path": DATA_PATH + "enigma2/",
|
||||
"satellites_xml_path": "/etc/tuxbox/", "data_local_path": "{}enigma2{}".format(DATA_PATH, SEP),
|
||||
"picons_path": "/usr/share/enigma2/picon/",
|
||||
"picons_local_path": DATA_PATH + "enigma2/picons/",
|
||||
"backup_local_path": DATA_PATH + "enigma2/backup/"}
|
||||
elif self is self.NEUTRINO_MP:
|
||||
"picons_local_path": "{}enigma2{}picons{}".format(DATA_PATH, SEP, SEP),
|
||||
"backup_local_path": "{}enigma2{}backup{}".format(DATA_PATH, SEP, SEP)}
|
||||
elif self is SettingsType.NEUTRINO_MP:
|
||||
return {"setting_type": self,
|
||||
"host": "127.0.0.1", "port": "21", "timeout": 5,
|
||||
"user": "root", "password": "root",
|
||||
"http_port": "80", "http_timeout": 2, "http_use_ssl": False,
|
||||
"telnet_port": "23", "telnet_timeout": 1,
|
||||
"services_path": "/var/tuxbox/config/zapit/", "user_bouquet_path": "/var/tuxbox/config/zapit/",
|
||||
"satellites_xml_path": "/var/tuxbox/config/", "data_local_path": DATA_PATH + "neutrino/",
|
||||
"satellites_xml_path": "/var/tuxbox/config/",
|
||||
"data_local_path": "{}neutrino{}".format(DATA_PATH, SEP),
|
||||
"picons_path": "/usr/share/tuxbox/neutrino/icons/logo/",
|
||||
"picons_local_path": DATA_PATH + "neutrino/picons/",
|
||||
"backup_local_path": DATA_PATH + "neutrino/backup/"}
|
||||
"picons_local_path": "{}neutrino{}picons{}".format(DATA_PATH, SEP, SEP),
|
||||
"backup_local_path": "{}neutrino{}backup{}".format(DATA_PATH, SEP, SEP)}
|
||||
|
||||
|
||||
class SettingsException(Exception):
|
||||
@@ -180,7 +185,7 @@ class Settings:
|
||||
self._cp_settings[k] = v
|
||||
|
||||
def_path = self.default_data_path
|
||||
def_path += "enigma2/" if self.setting_type is SettingsType.ENIGMA_2 else "neutrino/"
|
||||
def_path += "enigma2{}".format(SEP) if self.setting_type is SettingsType.ENIGMA_2 else "neutrino{}".format(SEP)
|
||||
set_local_paths(self._cp_settings, self._current_profile, def_path, self.profile_folder_is_default)
|
||||
|
||||
if force_write:
|
||||
@@ -436,6 +441,14 @@ class Settings:
|
||||
def play_streams_mode(self, value):
|
||||
self._settings["play_streams_mode"] = value
|
||||
|
||||
@property
|
||||
def stream_lib(self):
|
||||
return self._settings.get("stream_lib", Defaults.STREAM_LIB.value)
|
||||
|
||||
@stream_lib.setter
|
||||
def stream_lib(self, value):
|
||||
self._settings["stream_lib"] = value
|
||||
|
||||
# *********** EPG ************ #
|
||||
|
||||
@property
|
||||
@@ -513,30 +526,6 @@ class Settings:
|
||||
def enable_send_to(self, value):
|
||||
self._settings["enable_send_to"] = value
|
||||
|
||||
@property
|
||||
def use_colors(self):
|
||||
return self._settings.get("use_colors", Defaults.USE_COLORS.value)
|
||||
|
||||
@use_colors.setter
|
||||
def use_colors(self, value):
|
||||
self._settings["use_colors"] = value
|
||||
|
||||
@property
|
||||
def new_color(self):
|
||||
return self._settings.get("new_color", Defaults.NEW_COLOR.value)
|
||||
|
||||
@new_color.setter
|
||||
def new_color(self, value):
|
||||
self._settings["new_color"] = value
|
||||
|
||||
@property
|
||||
def extra_color(self):
|
||||
return self._settings.get("extra_color", Defaults.EXTRA_COLOR.value)
|
||||
|
||||
@extra_color.setter
|
||||
def extra_color(self, value):
|
||||
self._settings["extra_color"] = value
|
||||
|
||||
@property
|
||||
def fav_click_mode(self):
|
||||
return self._settings.get("fav_click_mode", Defaults.FAV_CLICK_MODE.value)
|
||||
@@ -581,6 +570,54 @@ class Settings:
|
||||
|
||||
# *********** Appearance *********** #
|
||||
|
||||
@property
|
||||
def list_font(self):
|
||||
return self._settings.get("list_font", "")
|
||||
|
||||
@list_font.setter
|
||||
def list_font(self, value):
|
||||
self._settings["list_font"] = value
|
||||
|
||||
@property
|
||||
def list_picon_size(self):
|
||||
return self._settings.get("list_picon_size", Defaults.LIST_PICON_SIZE.value)
|
||||
|
||||
@list_picon_size.setter
|
||||
def list_picon_size(self, value):
|
||||
self._settings["list_picon_size"] = value
|
||||
|
||||
@property
|
||||
def tooltip_logo_size(self):
|
||||
return self._settings.get("tooltip_logo_size", Defaults.TOOLTIP_LOGO_SIZE.value)
|
||||
|
||||
@tooltip_logo_size.setter
|
||||
def tooltip_logo_size(self, value):
|
||||
self._settings["tooltip_logo_size"] = value
|
||||
|
||||
@property
|
||||
def use_colors(self):
|
||||
return self._settings.get("use_colors", Defaults.USE_COLORS.value)
|
||||
|
||||
@use_colors.setter
|
||||
def use_colors(self, value):
|
||||
self._settings["use_colors"] = value
|
||||
|
||||
@property
|
||||
def new_color(self):
|
||||
return self._settings.get("new_color", Defaults.NEW_COLOR.value)
|
||||
|
||||
@new_color.setter
|
||||
def new_color(self, value):
|
||||
self._settings["new_color"] = value
|
||||
|
||||
@property
|
||||
def extra_color(self):
|
||||
return self._settings.get("extra_color", Defaults.EXTRA_COLOR.value)
|
||||
|
||||
@extra_color.setter
|
||||
def extra_color(self, value):
|
||||
self._settings["extra_color"] = value
|
||||
|
||||
@property
|
||||
def dark_mode(self):
|
||||
return self._settings.get("dark_mode", False)
|
||||
@@ -624,7 +661,7 @@ class Settings:
|
||||
@property
|
||||
@lru_cache(1)
|
||||
def themes_path(self):
|
||||
return "{}/.themes/".format(HOME_PATH)
|
||||
return "{}{}.themes{}".format(HOME_PATH, SEP, SEP)
|
||||
|
||||
@property
|
||||
def icon_theme(self):
|
||||
@@ -637,7 +674,7 @@ class Settings:
|
||||
@property
|
||||
@lru_cache(1)
|
||||
def icon_themes_path(self):
|
||||
return "{}/.icons/".format(HOME_PATH)
|
||||
return "{}{}.icons{}".format(HOME_PATH, SEP, SEP)
|
||||
|
||||
@property
|
||||
def is_darwin(self):
|
||||
|
||||
@@ -1,32 +1,353 @@
|
||||
import os
|
||||
import sys
|
||||
from abc import ABC, abstractmethod
|
||||
from datetime import datetime
|
||||
|
||||
from app.commons import run_task, log, _DATE_FORMAT
|
||||
from gi.repository import Gdk, Gtk
|
||||
|
||||
from app.commons import run_task, log, _DATE_FORMAT, run_with_delay
|
||||
|
||||
|
||||
class Player:
|
||||
""" Simple wrapper for VLC media player. """
|
||||
__VLC_INSTANCE = None
|
||||
class Player(ABC):
|
||||
""" Base player class. Also used as a factory. """
|
||||
|
||||
def __init__(self, mode, rewind_cb, position_cb, error_cb, playing_cb):
|
||||
@abstractmethod
|
||||
def get_play_mode(self):
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def play(self, mrl=None):
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def stop(self):
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def pause(self):
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def set_time(self, time):
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def release(self):
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def is_playing(self):
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def get_instance(self, mode, widget, buf_cb, position_cb, error_cb, playing_cb):
|
||||
pass
|
||||
|
||||
def get_window_handle(self, widget):
|
||||
""" Returns the identifier [pointer] for the window.
|
||||
|
||||
Based on gtkvlc.py[get_window_pointer] example from here:
|
||||
https://github.com/oaubert/python-vlc/tree/master/examples
|
||||
"""
|
||||
if sys.platform == "linux":
|
||||
return widget.get_window().get_xid()
|
||||
else:
|
||||
is_darwin = sys.platform == "darwin"
|
||||
try:
|
||||
import ctypes
|
||||
|
||||
libgdk = ctypes.CDLL("libgdk-3.0.dylib" if is_darwin else "libgdk-3-0.dll")
|
||||
except OSError as e:
|
||||
log("{}: Load library error: {}".format(__class__.__name__, e))
|
||||
else:
|
||||
# https://gitlab.gnome.org/GNOME/pygobject/-/issues/112
|
||||
ctypes.pythonapi.PyCapsule_GetPointer.restype = ctypes.c_void_p
|
||||
ctypes.pythonapi.PyCapsule_GetPointer.argtypes = [ctypes.py_object]
|
||||
gpointer = ctypes.pythonapi.PyCapsule_GetPointer(widget.get_window().__gpointer__, None)
|
||||
get_pointer = libgdk.gdk_quartz_window_get_nsview if is_darwin else libgdk.gdk_win32_window_get_handle
|
||||
get_pointer.restype = ctypes.c_void_p
|
||||
get_pointer.argtypes = [ctypes.c_void_p]
|
||||
|
||||
return get_pointer(gpointer)
|
||||
|
||||
def get_video_widget(self, widget):
|
||||
area = Gtk.DrawingArea(visible=True)
|
||||
area.connect("draw", self.on_drawing_area_draw)
|
||||
area.connect("motion-notify-event", self.on_mouse_motion)
|
||||
area.set_events(Gdk.EventMask.BUTTON_PRESS_MASK | Gdk.EventMask.POINTER_MOTION_MASK)
|
||||
widget.add(area)
|
||||
|
||||
return area
|
||||
|
||||
def on_drawing_area_draw(self, widget, cr):
|
||||
""" Used for black background drawing in the player drawing area. """
|
||||
cr.set_source_rgb(0, 0, 0)
|
||||
cr.paint()
|
||||
|
||||
def on_mouse_motion(self, widget, event):
|
||||
display = widget.get_display()
|
||||
window = widget.get_window()
|
||||
cursor = Gdk.Cursor.new_from_name(display, "default")
|
||||
window.set_cursor(cursor)
|
||||
|
||||
self.hide_mouse_cursor(window, display)
|
||||
|
||||
@run_with_delay(3)
|
||||
def hide_mouse_cursor(self, window, display):
|
||||
cursor = Gdk.Cursor.new_for_display(display, Gdk.CursorType.BLANK_CURSOR)
|
||||
window.set_cursor(cursor)
|
||||
|
||||
@staticmethod
|
||||
def make(name, mode, widget, buf_cb=None, position_cb=None, error_cb=None, playing_cb=None):
|
||||
""" Factory method. We will not use a separate factory to return a specific implementation.
|
||||
|
||||
@param name: implementation name.
|
||||
@param mode: current player mode [Built-in or windowed].
|
||||
@param widget: parent of video widget.
|
||||
@param buf_cb: buffering callback.
|
||||
@param position_cb: time (position) callback.
|
||||
@param error_cb: error callback.
|
||||
@param playing_cb: playing state callback.
|
||||
|
||||
Throws a NameError if there is no implementation for the given name.
|
||||
"""
|
||||
if name == "mpv":
|
||||
return MpvPlayer.get_instance(mode, widget, buf_cb, position_cb, error_cb, playing_cb)
|
||||
elif name == "gst":
|
||||
return GstPlayer.get_instance(mode, widget, buf_cb, position_cb, error_cb, playing_cb)
|
||||
elif name == "vlc":
|
||||
return VlcPlayer.get_instance(mode, widget, buf_cb, position_cb, error_cb, playing_cb)
|
||||
else:
|
||||
raise NameError("There is no such [{}] implementation.".format(name))
|
||||
|
||||
|
||||
class MpvPlayer(Player):
|
||||
""" Simple wrapper for MPV media player.
|
||||
|
||||
Uses python-mvp [https://github.com/jaseg/python-mpv].
|
||||
"""
|
||||
__INSTANCE = None
|
||||
|
||||
def __init__(self, mode, widget, buf_cb, position_cb, error_cb, playing_cb):
|
||||
try:
|
||||
from app.tools import vlc
|
||||
from app.tools.vlc import EventType
|
||||
from app.tools import mpv
|
||||
|
||||
self._player = mpv.MPV(wid=str(self.get_window_handle(self.get_video_widget(widget), )),
|
||||
input_default_bindings=False,
|
||||
input_cursor=False,
|
||||
cursor_autohide="no")
|
||||
except OSError as e:
|
||||
log("{}: Load library error: {}".format(__class__.__name__, e))
|
||||
raise ImportError
|
||||
raise ImportError("No libmpv is found. Check that it is installed!")
|
||||
else:
|
||||
self._mode = mode
|
||||
self._is_playing = False
|
||||
|
||||
@self._player.event_callback(mpv.MpvEventID.FILE_LOADED)
|
||||
def on_open(event):
|
||||
log("Starting playback...")
|
||||
playing_cb()
|
||||
|
||||
@self._player.event_callback(mpv.MpvEventID.END_FILE)
|
||||
def on_end(event):
|
||||
event = event.get("event", {})
|
||||
if event.get("reason", mpv.MpvEventEndFile.ERROR) == mpv.MpvEventEndFile.ERROR:
|
||||
log("Stream playback error: {}".format(event.get("error", mpv.ErrorCode.GENERIC)))
|
||||
error_cb()
|
||||
|
||||
@classmethod
|
||||
def get_instance(cls, mode, widget, buf_cb, position_cb, error_cb, playing_cb):
|
||||
if not cls.__INSTANCE:
|
||||
cls.__INSTANCE = MpvPlayer(mode, widget, buf_cb, position_cb, error_cb, playing_cb)
|
||||
return cls.__INSTANCE
|
||||
|
||||
def get_play_mode(self):
|
||||
return self._mode
|
||||
|
||||
@run_task
|
||||
def play(self, mrl=None):
|
||||
if not mrl:
|
||||
return
|
||||
|
||||
self._player.play(mrl)
|
||||
self._is_playing = True
|
||||
|
||||
@run_task
|
||||
def stop(self):
|
||||
self._player.stop()
|
||||
self._is_playing = True
|
||||
|
||||
def pause(self):
|
||||
pass
|
||||
|
||||
def set_time(self, time):
|
||||
pass
|
||||
|
||||
@run_task
|
||||
def release(self):
|
||||
self._player.terminate()
|
||||
self.__INSTANCE = None
|
||||
|
||||
def is_playing(self):
|
||||
return self._is_playing
|
||||
|
||||
|
||||
class GstPlayer(Player):
|
||||
""" Simple wrapper for GStreamer playbin. """
|
||||
|
||||
__INSTANCE = None
|
||||
|
||||
def __init__(self, mode, widget, buf_cb, position_cb, error_cb, playing_cb):
|
||||
try:
|
||||
import gi
|
||||
|
||||
gi.require_version("Gst", "1.0")
|
||||
gi.require_version("GstVideo", "1.0")
|
||||
from gi.repository import Gst, GstVideo
|
||||
# Initialization of GStreamer.
|
||||
Gst.init(sys.argv)
|
||||
except (OSError, ValueError) as e:
|
||||
log("{}: Load library error: {}".format(__class__.__name__, e))
|
||||
raise ImportError("No GStreamer is found. Check that it is installed!")
|
||||
else:
|
||||
self._error_cb = error_cb
|
||||
self._playing_cb = playing_cb
|
||||
|
||||
self.STATE = Gst.State
|
||||
self.STAT_RETURN = Gst.StateChangeReturn
|
||||
|
||||
self._mode = mode
|
||||
self._is_playing = False
|
||||
self._player = Gst.ElementFactory.make("playbin", "player")
|
||||
# Initialization of the playback widget.
|
||||
vid_widget = self.get_video_widget(widget)
|
||||
widget.add(vid_widget)
|
||||
vid_widget.show()
|
||||
self._player.set_window_handle(self.get_window_handle(vid_widget))
|
||||
|
||||
bus = self._player.get_bus()
|
||||
bus.add_signal_watch()
|
||||
bus.connect("message::error", self.on_error)
|
||||
bus.connect("message::state-changed", self.on_state_changed)
|
||||
bus.connect("message::eos", self.on_eos)
|
||||
|
||||
@classmethod
|
||||
def get_instance(cls, mode, widget, buf_cb=None, position_cb=None, error_cb=None, playing_cb=None):
|
||||
if not cls.__INSTANCE:
|
||||
cls.__INSTANCE = GstPlayer(mode, widget, buf_cb, position_cb, error_cb, playing_cb)
|
||||
return cls.__INSTANCE
|
||||
|
||||
def get_play_mode(self):
|
||||
return self._mode
|
||||
|
||||
def play(self, mrl=None):
|
||||
self._player.set_state(self.STATE.READY)
|
||||
if not mrl:
|
||||
return
|
||||
|
||||
self._player.set_property("uri", mrl)
|
||||
|
||||
log("Setting the URL for playback: {}".format(mrl))
|
||||
ret = self._player.set_state(self.STATE.PLAYING)
|
||||
|
||||
if ret == self.STAT_RETURN.FAILURE:
|
||||
log("ERROR: Unable to set the 'PLAYING' state for '{}'.".format(mrl))
|
||||
else:
|
||||
self._is_playing = True
|
||||
|
||||
def stop(self):
|
||||
log("Stop playback...")
|
||||
self._player.set_state(self.STATE.READY)
|
||||
self._is_playing = False
|
||||
|
||||
def pause(self):
|
||||
self._player.set_state(self.STATE.PAUSED)
|
||||
|
||||
def set_time(self, time):
|
||||
pass
|
||||
|
||||
@run_task
|
||||
def release(self):
|
||||
self._is_playing = False
|
||||
self._player.set_state(self.STATE.NULL)
|
||||
self.__INSTANCE = None
|
||||
|
||||
def set_mrl(self, mrl):
|
||||
self._player.set_property("uri", mrl)
|
||||
|
||||
def is_playing(self):
|
||||
return self._is_playing
|
||||
|
||||
def on_error(self, bus, msg):
|
||||
err, dbg = msg.parse_error()
|
||||
log(err)
|
||||
self._error_cb()
|
||||
|
||||
def on_state_changed(self, bus, msg):
|
||||
if not msg.src == self._player:
|
||||
# Not from the player.
|
||||
return
|
||||
|
||||
old_state, new_state, pending = msg.parse_state_changed()
|
||||
if new_state is self.STATE.PLAYING:
|
||||
log("Starting playback...")
|
||||
self._playing_cb()
|
||||
self.get_stream_info()
|
||||
|
||||
def on_eos(self, bus, msg):
|
||||
""" Called when an end-of-stream message appears. """
|
||||
self._player.set_state(self.STATE.READY)
|
||||
self._is_playing = False
|
||||
|
||||
def get_stream_info(self):
|
||||
log("Getting stream info...")
|
||||
nr_video = self._player.get_property("n-video")
|
||||
for i in range(nr_video):
|
||||
# Retrieve the stream's video tags.
|
||||
tags = self._player.emit("get-video-tags", i)
|
||||
if tags:
|
||||
_, cod = tags.get_string("video-codec")
|
||||
log("Video codec: {}".format(cod or "unknown"))
|
||||
|
||||
nr_audio = self._player.get_property("n-audio")
|
||||
for i in range(nr_audio):
|
||||
# Retrieve the stream's video tags.
|
||||
tags = self._player.emit("get-audio-tags", i)
|
||||
if tags:
|
||||
_, cod = tags.get_string("audio-codec")
|
||||
log("Audio codec: {}".format(cod or "unknown"))
|
||||
|
||||
|
||||
class VlcPlayer(Player):
|
||||
""" Simple wrapper for VLC media player.
|
||||
|
||||
Uses python-vlc [https://github.com/oaubert/python-vlc].
|
||||
"""
|
||||
|
||||
__VLC_INSTANCE = None
|
||||
|
||||
def __init__(self, mode, widget, buf_cb, position_cb, error_cb, playing_cb):
|
||||
try:
|
||||
from app.tools import vlc
|
||||
from app.tools.vlc import EventType
|
||||
|
||||
args = "--quiet {}".format("" if sys.platform == "darwin" else "--no-xlib")
|
||||
self._player = vlc.Instance(args).media_player_new()
|
||||
vlc.libvlc_video_set_key_input(self._player, False)
|
||||
vlc.libvlc_video_set_mouse_input(self._player, False)
|
||||
except (OSError, AttributeError) as e:
|
||||
log("{}: Load library error: {}".format(__class__.__name__, e))
|
||||
raise ImportError("No VLC is found. Check that it is installed!")
|
||||
else:
|
||||
self._mode = mode
|
||||
self._is_playing = False
|
||||
|
||||
ev_mgr = self._player.event_manager()
|
||||
|
||||
if rewind_cb:
|
||||
if buf_cb:
|
||||
# TODO look other EventType options
|
||||
ev_mgr.event_attach(EventType.MediaPlayerBuffering,
|
||||
lambda et, p: rewind_cb(p.get_media().get_duration()),
|
||||
lambda et, p: buf_cb(p.get_media().get_duration()),
|
||||
self._player)
|
||||
if position_cb:
|
||||
ev_mgr.event_attach(EventType.MediaPlayerTimeChanged,
|
||||
@@ -42,10 +363,12 @@ class Player:
|
||||
lambda et, p: playing_cb(),
|
||||
self._player)
|
||||
|
||||
self.init_video_widget(widget)
|
||||
|
||||
@classmethod
|
||||
def get_instance(cls, mode, rewind_cb=None, position_cb=None, error_cb=None, playing_cb=None):
|
||||
def get_instance(cls, mode, widget, buf_cb=None, position_cb=None, error_cb=None, playing_cb=None):
|
||||
if not cls.__VLC_INSTANCE:
|
||||
cls.__VLC_INSTANCE = Player(mode, rewind_cb, position_cb, error_cb, playing_cb)
|
||||
cls.__VLC_INSTANCE = VlcPlayer(mode, widget, buf_cb, position_cb, error_cb, playing_cb)
|
||||
return cls.__VLC_INSTANCE
|
||||
|
||||
def get_play_mode(self):
|
||||
@@ -78,37 +401,20 @@ class Player:
|
||||
self._player.release()
|
||||
self.__VLC_INSTANCE = None
|
||||
|
||||
def set_xwindow(self, xid):
|
||||
self._player.set_xwindow(xid)
|
||||
|
||||
def set_nso(self, widget):
|
||||
""" Used on MacOS to set NSObject.
|
||||
|
||||
Based on gtkvlc.py[get_window_pointer] example from here:
|
||||
https://github.com/oaubert/python-vlc/tree/master/examples
|
||||
"""
|
||||
try:
|
||||
import ctypes
|
||||
g_dll = ctypes.CDLL("libgdk-3.0.dylib")
|
||||
except OSError as e:
|
||||
log("{}: Load library error: {}".format(__class__.__name__, e))
|
||||
else:
|
||||
get_nsview = g_dll.gdk_quartz_window_get_nsview
|
||||
get_nsview.restype, get_nsview.argtypes = ctypes.c_void_p, [ctypes.c_void_p]
|
||||
ctypes.pythonapi.PyCapsule_GetPointer.restype = ctypes.c_void_p
|
||||
ctypes.pythonapi.PyCapsule_GetPointer.argtypes = [ctypes.py_object]
|
||||
# Get the C void* pointer to the window
|
||||
pointer = ctypes.pythonapi.PyCapsule_GetPointer(widget.get_window().__gpointer__, None)
|
||||
self._player.set_nsobject(get_nsview(pointer))
|
||||
|
||||
def set_mrl(self, mrl):
|
||||
self._player.set_mrl(mrl)
|
||||
|
||||
def is_playing(self):
|
||||
return self._is_playing
|
||||
|
||||
def set_full_screen(self, full):
|
||||
self._player.set_fullscreen(full)
|
||||
def init_video_widget(self, widget):
|
||||
video_widget = self.get_video_widget(widget)
|
||||
if sys.platform == "linux":
|
||||
self._player.set_xwindow(video_widget.get_window().get_xid())
|
||||
elif sys.platform == "darwin":
|
||||
self._player.set_nsobject(self.get_window_handle(video_widget))
|
||||
else:
|
||||
log("Video widget initialization error: platform '{}' is not supported. ".format(sys.platform))
|
||||
|
||||
|
||||
class Recorder:
|
||||
|
||||
1941
app/tools/mpv.py
Normal file
1941
app/tools/mpv.py
Normal file
File diff suppressed because it is too large
Load Diff
@@ -15,7 +15,7 @@ _ENIGMA2_PICON_KEY = "{:X}:{:X}:{}"
|
||||
_NEUTRINO_PICON_KEY = "{:x}{:04x}{:04x}.png"
|
||||
|
||||
Provider = namedtuple("Provider", ["logo", "name", "pos", "url", "on_id", "ssid", "single", "selected"])
|
||||
Picon = namedtuple("Picon", ["ref", "ssid", "v_pid"])
|
||||
Picon = namedtuple("Picon", ["ref", "ssid"])
|
||||
|
||||
|
||||
class PiconsParser(HTMLParser):
|
||||
@@ -63,18 +63,15 @@ class PiconsParser(HTMLParser):
|
||||
ln = len(row)
|
||||
|
||||
if self._single and ln == 4 and row[0].startswith("/logo/"):
|
||||
self.picons.append(Picon(row[0].strip(), "0", "0"))
|
||||
self.picons.append(Picon(row[0].strip(), "0"))
|
||||
else:
|
||||
if 9 < ln < 13:
|
||||
if ln > 8:
|
||||
url = None
|
||||
if row[0].startswith("/logo/"):
|
||||
url = row[0]
|
||||
elif row[1].startswith("/logo/"):
|
||||
url = row[1]
|
||||
if row[2].startswith("/logo/"):
|
||||
url = row[2]
|
||||
|
||||
ssid = row[-4]
|
||||
if url and len(ssid) > 2:
|
||||
self.picons.append(Picon(url, ssid, row[-3]))
|
||||
if url and row[0].isdigit():
|
||||
self.picons.append(Picon(url, row[0]))
|
||||
|
||||
self._current_row = []
|
||||
|
||||
@@ -112,6 +109,9 @@ class PiconsParser(HTMLParser):
|
||||
namespace = "{:X}{:X}".format(int(pos), int(freq))
|
||||
else:
|
||||
namespace = "{:X}0000".format(int(pos))
|
||||
|
||||
if single and not ssid.isdigit():
|
||||
ssid = "".join(c for c in ssid if c.isdigit()) or "0"
|
||||
name = PiconsParser.format(ssid if single else p.ssid, on_id, namespace, picon_ids, s_type)
|
||||
p_name = picons_path + (name if name else os.path.basename(p.ref))
|
||||
picons_data.append(("{}{}".format(PiconsParser._BASE_URL, p.ref), p_name))
|
||||
@@ -173,20 +173,11 @@ class ProviderParser(HTMLParser):
|
||||
url = attrs[0][1]
|
||||
if any(d in url for d in self._DOMAINS):
|
||||
self._current_row.append(url)
|
||||
if tag == "font" and len(attrs) == 1:
|
||||
atr = attrs[0]
|
||||
if len(atr) == 2 and atr[1] == "darkgreen":
|
||||
self._is_onid_tid = True
|
||||
|
||||
def handle_data(self, data):
|
||||
""" Save content to a cell """
|
||||
if self._is_td or self._is_th:
|
||||
self._current_cell.append(data.strip())
|
||||
if self._is_onid_tid:
|
||||
m = self._ONID_TID_PATTERN.match(data)
|
||||
if m:
|
||||
self._on_id, tid = m.group().split("-")
|
||||
self._is_onid_tid = False
|
||||
|
||||
def handle_endtag(self, tag):
|
||||
if tag == 'td':
|
||||
@@ -208,32 +199,34 @@ class ProviderParser(HTMLParser):
|
||||
|
||||
len_row = len(row)
|
||||
if len_row > 2:
|
||||
m = self._TRANSPONDER_FREQUENCY_PATTERN.match(row[1])
|
||||
m = self._TRANSPONDER_FREQUENCY_PATTERN.match(row[0])
|
||||
if m:
|
||||
self._freq = m.group().split()[0]
|
||||
|
||||
if len_row == 14:
|
||||
if len_row > 12:
|
||||
# Providers
|
||||
name = row[6]
|
||||
name = row[5]
|
||||
self._prv_names.add(name)
|
||||
m = self._ONID_TID_PATTERN.match(str(row[9]))
|
||||
m = self._ONID_TID_PATTERN.match(str(row[-5]))
|
||||
if m:
|
||||
on_id, tid = m.group().split("-")
|
||||
if on_id not in self._ids:
|
||||
self._on_id = on_id
|
||||
row[-2] = on_id
|
||||
self._ids.add(on_id)
|
||||
row[0] = self._positon
|
||||
if name + on_id not in self._prv_names:
|
||||
self._prv_names.add(name + on_id)
|
||||
logo_data = None
|
||||
req = requests.get(self._BASE_URL + row[3], timeout=5)
|
||||
if req.status_code == 200:
|
||||
logo_data = req.content
|
||||
else:
|
||||
log("Downloading provider logo error: {}".format(req.reason))
|
||||
self.rows.append(Provider(logo=logo_data, name=name, pos=self._positon, url=row[5], on_id=on_id,
|
||||
if row[2].startswith("/logo/"):
|
||||
req = requests.get(self._BASE_URL + row[2], timeout=5)
|
||||
if req.status_code == 200:
|
||||
logo_data = req.content
|
||||
else:
|
||||
log("Downloading provider logo error: {}".format(req.reason))
|
||||
self.rows.append(Provider(logo=logo_data, name=name, pos=self._positon, url=row[6], on_id=on_id,
|
||||
ssid=None, single=False, selected=True))
|
||||
elif 6 < len_row < 14:
|
||||
elif 6 < len_row < 12:
|
||||
# Single services
|
||||
name, url, ssid = None, None, None
|
||||
if row[0].startswith("http"):
|
||||
|
||||
@@ -14,13 +14,14 @@ from app.eparser import Satellite, Transponder, is_transponder_valid
|
||||
from app.eparser.ecommons import (PLS_MODE, get_key_by_value, FEC, SYSTEM, POLARIZATION, MODULATION, SERVICE_TYPE,
|
||||
Service, CAS)
|
||||
|
||||
_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:85.0) Gecko/20100101 Firefox/85.0"}
|
||||
|
||||
|
||||
class SatelliteSource(Enum):
|
||||
FLYSAT = ("https://www.flysat.com/satlist.php",)
|
||||
LYNGSAT = ("https://www.lyngsat.com/asia.html", "https://www.lyngsat.com/europe.html",
|
||||
"https://www.lyngsat.com/atlantic.html", "https://www.lyngsat.com/america.html")
|
||||
KINGOFSAT = ("https://en.kingofsat.net/satellites.php",)
|
||||
|
||||
@staticmethod
|
||||
def get_sources(src):
|
||||
@@ -76,6 +77,8 @@ class Cell:
|
||||
class SatellitesParser(HTMLParser):
|
||||
""" Parser for satellite html page. """
|
||||
|
||||
POS_PAT = re.compile(r".*?(\d+\.\d°?[EW]).*")
|
||||
|
||||
def __init__(self, source=SatelliteSource.FLYSAT, entities=False, separator=' '):
|
||||
|
||||
HTMLParser.__init__(self)
|
||||
@@ -96,7 +99,9 @@ class SatellitesParser(HTMLParser):
|
||||
if tag == "tr":
|
||||
self._is_th = True
|
||||
if tag == "a":
|
||||
self._current_row.append(attrs[0][1])
|
||||
for atr in attrs:
|
||||
if atr[0] == "href":
|
||||
self._current_row.append(atr[1])
|
||||
|
||||
def handle_data(self, data):
|
||||
""" Save content to a cell """
|
||||
@@ -147,41 +152,27 @@ class SatellitesParser(HTMLParser):
|
||||
|
||||
return list(map(get_sat, filter(lambda x: all(x) and len(x) == 5, self._rows)))
|
||||
elif self._source is SatelliteSource.LYNGSAT:
|
||||
extra_pattern = re.compile(r"^https://www\.lyngsat\.com/[\w-]+\.html")
|
||||
base_url = "https://www.lyngsat.com/"
|
||||
sats = []
|
||||
names = set()
|
||||
current_pos = "0"
|
||||
for row in filter(lambda x: len(x) in (5, 7, 8), self._rows):
|
||||
r_len = len(row)
|
||||
if r_len == 7:
|
||||
current_pos = self.parse_position(row[2])
|
||||
name = row[1].rsplit("/")[-1].rstrip(".html").replace("-", " ")
|
||||
if name not in names:
|
||||
# [all in one] satellites
|
||||
sats.append((name, current_pos, row[5], base_url + row[1], False))
|
||||
names.add(name)
|
||||
name = row[4]
|
||||
if name not in names:
|
||||
sats.append((name, current_pos, row[5], base_url + row[3], False))
|
||||
names.add(name)
|
||||
if r_len == 8: # for a very limited number of satellites
|
||||
data = list(filter(None, row))
|
||||
urls = set()
|
||||
sat_type = ""
|
||||
for d in data:
|
||||
url = re.match(extra_pattern, d)
|
||||
if url:
|
||||
urls.add(url.group(0))
|
||||
if d in ("C", "Ku", "CKu"):
|
||||
sat_type = d
|
||||
current_pos = self.parse_position(data[1])
|
||||
for url in urls:
|
||||
name = url.rsplit("/")[-1].rstrip(".html").replace("-", " ")
|
||||
sats.append((name, current_pos, sat_type, base_url + url, False))
|
||||
elif r_len == 5:
|
||||
sats.append((row[2], current_pos, row[3], base_url + row[1], False))
|
||||
cur_pos = "0"
|
||||
for row in filter(lambda x: 3 < len(x) < 8, self._rows):
|
||||
if not row[0]:
|
||||
row = row[1:]
|
||||
|
||||
pos = self.parse_position(row[1])
|
||||
if not self.POS_PAT.match(pos):
|
||||
if len(row) == 4 and row[0].endswith(".html"):
|
||||
sats.append((row[1], cur_pos, row[-2], base_url + row[0], False))
|
||||
continue
|
||||
|
||||
sats.append((row[-3], pos, row[-2], base_url + row[0], False))
|
||||
cur_pos = pos
|
||||
return sats
|
||||
elif source is SatelliteSource.KINGOFSAT:
|
||||
def get_sat(r):
|
||||
return r[3], self.parse_position(r[1]), None, r[0], False
|
||||
|
||||
return list(map(get_sat, filter(lambda x: len(x) == 17, self._rows)))
|
||||
|
||||
def get_satellite(self, sat):
|
||||
pos = sat[1]
|
||||
@@ -201,18 +192,29 @@ class SatellitesParser(HTMLParser):
|
||||
def get_transponders(self, sat_url):
|
||||
""" Getting transponders(sorted by frequency). """
|
||||
self._rows.clear()
|
||||
url = "https://www.flysat.com/" + sat_url if self._source is SatelliteSource.FLYSAT else sat_url
|
||||
request = requests.get(url=url, headers=_HEADERS)
|
||||
|
||||
trs = []
|
||||
if request.status_code == 200:
|
||||
self.feed(request.text)
|
||||
if self._source is SatelliteSource.FLYSAT:
|
||||
self.get_transponders_for_fly_sat(trs)
|
||||
elif self._source is SatelliteSource.LYNGSAT:
|
||||
self.get_transponders_for_lyng_sat(trs)
|
||||
|
||||
url = sat_url
|
||||
if self._source is SatelliteSource.FLYSAT:
|
||||
url = "https://www.flysat.com/" + sat_url
|
||||
elif self._source is SatelliteSource.KINGOFSAT:
|
||||
url = "https://en.kingofsat.net/" + sat_url
|
||||
|
||||
try:
|
||||
request = requests.get(url=url, headers=_HEADERS)
|
||||
except requests.exceptions.ConnectionError as e:
|
||||
log("Getting transponders error: {}".format(e))
|
||||
else:
|
||||
log("SatellitesParser [get transponders] error: {} {}".format(url, request.reason))
|
||||
if request.status_code == 200:
|
||||
self.feed(request.text)
|
||||
if self._source is SatelliteSource.FLYSAT:
|
||||
self.get_transponders_for_fly_sat(trs)
|
||||
elif self._source is SatelliteSource.LYNGSAT:
|
||||
self.get_transponders_for_lyng_sat(trs)
|
||||
elif self._source is SatelliteSource.KINGOFSAT:
|
||||
self.get_transponders_for_king_of_sat(trs)
|
||||
else:
|
||||
log("SatellitesParser [get transponders] error: {} {}".format(url, request.reason))
|
||||
|
||||
return sorted(trs, key=lambda x: int(x.frequency))
|
||||
|
||||
@@ -296,6 +298,25 @@ class SatellitesParser(HTMLParser):
|
||||
if is_transponder_valid(tr):
|
||||
trs.append(tr)
|
||||
|
||||
def get_transponders_for_king_of_sat(self, trs):
|
||||
""" Getting transponders for KingOfSat source.
|
||||
|
||||
Since the *.ini file contains incomplete information, it is not used.
|
||||
"""
|
||||
zeros = "000"
|
||||
pat = re.compile(
|
||||
r"(\d+).00\s+([RLHV])\s+(DVB-S[2]?)\s+(?:T2-MI, PLP (\d+)\s+)?(.*PSK).*?(?:Stream\s+(\d+))?\s+(\d+)\s+(\d+/\d+)$")
|
||||
|
||||
for row in filter(lambda r: len(r) == 16 and self.POS_PAT.match(r[0]), self._rows):
|
||||
res = pat.search(" ".join((row[0], row[2], row[3], row[8], row[9], row[10])))
|
||||
if res:
|
||||
freq, sr, pol, fec, sys = res.group(1), res.group(7), res.group(2), res.group(8), res.group(3)
|
||||
mod, pls_id, pls_code = res.group(5), res.group(4), res.group(6)
|
||||
|
||||
tr = Transponder(freq + zeros, sr + zeros, pol, fec, sys, mod, None, pls_code, pls_id)
|
||||
if is_transponder_valid(tr):
|
||||
trs.append(tr)
|
||||
|
||||
|
||||
class ServicesParser(HTMLParser):
|
||||
""" Services parser for LYNGSAT source. """
|
||||
@@ -304,9 +325,10 @@ class ServicesParser(HTMLParser):
|
||||
|
||||
HTMLParser.__init__(self)
|
||||
|
||||
self._S_TYPES = {"": "2", "MPEG-2 SD": "1", "SD": "1", "MPEG-4 SD": "22", "HEVC SD": "22", "MPEG-4 HD": "25",
|
||||
"MPEG-4 HD 1080": "25", "MPEG-4 HD 720": "25", "HEVC HD": "25", "HEVC UHD": "31",
|
||||
"HEVC UHD 4K": "31"}
|
||||
self._S_TYPES = {"": "2", "MPEG-2 SD": "1", "MPEG-2/SD": "1", "SD": "1", "MPEG-4 SD": "22", "MPEG-4/SD": "22",
|
||||
"MPEG-4": "22", "HEVC SD": "22", "MPEG-4/HD": "25", "MPEG-4 HD": "25", "MPEG-4 HD 1080": "25",
|
||||
"MPEG-4 HD 720": "25", "HEVC HD": "25", "HEVC/HD": "25", "HEVC": "31", "HEVC/UHD": "31",
|
||||
"HEVC UHD": "31", "HEVC UHD 4K": "31"}
|
||||
self._TR_PAT = re.compile(
|
||||
r".*?(\d+)\s+([RLHV]).*(DVB-S[2]?)/?(.*PSK)?\s(T2-MI)?\s?SR-FEC:\s(\d+)-(\d/\d)\s+.*ONID-TID:\s+(\d+)-(\d+).*")
|
||||
self._POS_PAT = re.compile(r".*?(\d+\.\d°[EW]).*")
|
||||
@@ -382,8 +404,8 @@ class ServicesParser(HTMLParser):
|
||||
log(e)
|
||||
else:
|
||||
url = "https://www.lyngsat.com/muxes/"
|
||||
return [row[1] for row in
|
||||
filter(lambda x: x and len(x) > 8 and x[1].url and x[1].url.startswith(url), self._rows)]
|
||||
return [row[0] for row in
|
||||
filter(lambda x: x and len(x) > 8 and x[0].url and x[0].url.startswith(url), self._rows)]
|
||||
return []
|
||||
|
||||
def get_transponder_services(self, tr_url, sat_position=None, use_pids=False):
|
||||
@@ -470,27 +492,8 @@ class ServicesParser(HTMLParser):
|
||||
else:
|
||||
flags = ",".join(filter(None, (flags, cas)))
|
||||
|
||||
srv = Service(flags_cas=flags,
|
||||
transponder_type="s",
|
||||
coded=None,
|
||||
service=name,
|
||||
locked=None,
|
||||
hide=None,
|
||||
package=pkg,
|
||||
service_type=_s_type,
|
||||
picon=r[1].img,
|
||||
picon_id=picon_id,
|
||||
ssid=sid,
|
||||
freq=freq,
|
||||
rate=sr,
|
||||
pol=pol,
|
||||
fec=fec,
|
||||
system=sys,
|
||||
pos=pos,
|
||||
data_id=data_id,
|
||||
fav_id=fav_id,
|
||||
transponder=tr)
|
||||
services.append(srv)
|
||||
services.append(Service(flags, "s", None, name, None, None, pkg, _s_type, r[1].img, picon_id,
|
||||
sid, freq, sr, pol, fec, sys, pos, data_id, fav_id, tr))
|
||||
except ValueError as e:
|
||||
log("ServicesParser error [get transponder services]: {}".format(e))
|
||||
|
||||
|
||||
@@ -6,12 +6,12 @@ import re
|
||||
import shutil
|
||||
import sys
|
||||
from html.parser import HTMLParser
|
||||
from json import JSONDecodeError
|
||||
from urllib.error import URLError
|
||||
from urllib.parse import unquote
|
||||
from urllib.request import Request, urlopen, urlretrieve
|
||||
|
||||
from app.commons import log
|
||||
from app.settings import SEP
|
||||
from app.ui.uicommons import show_notification
|
||||
|
||||
_YT_PATTERN = re.compile(r"https://www.youtube.com/.+(?:v=)([\w-]{11}).*")
|
||||
@@ -100,7 +100,7 @@ class YouTube:
|
||||
if player_resp:
|
||||
try:
|
||||
resp = json.loads(player_resp)
|
||||
except JSONDecodeError as e:
|
||||
except Exception as e:
|
||||
log("{}: Parsing player response error: {}".format(__class__.__name__, e))
|
||||
else:
|
||||
det = resp.get("videoDetails", None)
|
||||
@@ -170,7 +170,7 @@ class PlayListParser(HTMLParser):
|
||||
|
||||
try:
|
||||
resp = json.loads(data)
|
||||
except JSONDecodeError as e:
|
||||
except YouTubeException as e:
|
||||
log("{}: Parsing data error: {}".format(__class__.__name__, e))
|
||||
else:
|
||||
sb = resp.get("sidebar", None)
|
||||
@@ -230,7 +230,7 @@ class YouTubeDL:
|
||||
"cookiefile": "cookies.txt"} # File name where cookies should be read from and dumped to.
|
||||
|
||||
def __init__(self, settings, callback):
|
||||
self._path = settings.default_data_path + "tools/"
|
||||
self._path = settings.default_data_path + "tools{}".format(SEP)
|
||||
self._update = settings.enable_yt_dl_update
|
||||
self._supported = {"22", "18"}
|
||||
self._dl = None
|
||||
@@ -247,7 +247,7 @@ class YouTubeDL:
|
||||
return cls._DL_INSTANCE
|
||||
|
||||
def init(self):
|
||||
if not os.path.isfile(self._path + "youtube_dl/version.py"):
|
||||
if not os.path.isfile(self._path + "youtube_dl{}version.py".format(SEP)):
|
||||
self.get_latest_release()
|
||||
|
||||
if self._path not in sys.path:
|
||||
@@ -314,7 +314,7 @@ class YouTubeDL:
|
||||
os.makedirs(os.path.dirname(self._path), exist_ok=True)
|
||||
|
||||
for info in arch.infolist():
|
||||
pref, sep, f = info.filename.partition("/youtube_dl/")
|
||||
pref, sep, f = info.filename.partition("{}youtube_dl{}".format(SEP, SEP))
|
||||
if sep:
|
||||
arch.extract(info.filename)
|
||||
shutil.move(info.filename, "{}{}{}".format(self._path, sep, f))
|
||||
|
||||
@@ -1,28 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<interface>
|
||||
<menu id="app-menu">
|
||||
<section>
|
||||
<item>
|
||||
<attribute name="label" translatable="yes">About</attribute>
|
||||
<attribute name="action">app.on_about_app</attribute>
|
||||
</item>
|
||||
</section>
|
||||
<section>
|
||||
<item>
|
||||
<attribute name="label" translatable="yes">Settings</attribute>
|
||||
<attribute name="action">app.on_settings</attribute>
|
||||
</item>
|
||||
</section>
|
||||
<section>
|
||||
<item>
|
||||
<attribute name="label" translatable="yes">Exit</attribute>
|
||||
<attribute name="action">app.on_close_app</attribute>
|
||||
</item>
|
||||
</section>
|
||||
</menu>
|
||||
<menu id="menu_bar">
|
||||
<submenu>
|
||||
<attribute name="label" translatable="yes">File</attribute>
|
||||
<attribute name="action">app.hide_menu_bar</attribute>
|
||||
<attribute name="hidden-when">action-disabled</attribute>
|
||||
<section>
|
||||
<submenu>
|
||||
<attribute name="label" translatable="yes">Import</attribute>
|
||||
@@ -66,22 +48,38 @@
|
||||
<attribute name="action">app.on_download</attribute>
|
||||
</item>
|
||||
</section>
|
||||
<section>
|
||||
<item>
|
||||
<attribute name="label" translatable="yes">Settings</attribute>
|
||||
<attribute name="action">app.on_settings</attribute>
|
||||
</item>
|
||||
</section>
|
||||
<section>
|
||||
<item>
|
||||
<attribute name="label" translatable="yes">Exit</attribute>
|
||||
<attribute name="action">app.on_close_app</attribute>
|
||||
</item>
|
||||
</section>
|
||||
</submenu>
|
||||
<submenu>
|
||||
<attribute name="label" translatable="yes">Edit</attribute>
|
||||
<attribute name="action">app.hide_menu_bar</attribute>
|
||||
<attribute name="hidden-when">action-disabled</attribute>
|
||||
<section>
|
||||
<item>
|
||||
<attribute name="label" translatable="yes">Lock</attribute>
|
||||
<attribute name="action">app.on_locked</attribute>
|
||||
</item>
|
||||
<item>
|
||||
<attribute name="label" translatable="yes">Hide</attribute>
|
||||
<attribute name="action">app.on_hide</attribute>
|
||||
</item>
|
||||
</section>
|
||||
<item>
|
||||
<attribute name="label" translatable="yes">Lock</attribute>
|
||||
<attribute name="action">app.on_locked</attribute>
|
||||
</item>
|
||||
<item>
|
||||
<attribute name="label" translatable="yes">Hide</attribute>
|
||||
<attribute name="action">app.on_hide</attribute>
|
||||
</item>
|
||||
</section>
|
||||
</submenu>
|
||||
<submenu>
|
||||
<attribute name="label" translatable="yes">View</attribute>
|
||||
<attribute name="action">app.hide_menu_bar</attribute>
|
||||
<attribute name="hidden-when">action-disabled</attribute>
|
||||
<section>
|
||||
<item>
|
||||
<attribute name="label" translatable="yes">Search</attribute>
|
||||
@@ -95,6 +93,8 @@
|
||||
</submenu>
|
||||
<submenu>
|
||||
<attribute name="label" translatable="yes">Tools</attribute>
|
||||
<attribute name="action">app.hide_menu_bar</attribute>
|
||||
<attribute name="hidden-when">action-disabled</attribute>
|
||||
<section>
|
||||
<item>
|
||||
<attribute name="label" translatable="yes">Satellites editor</attribute>
|
||||
@@ -114,6 +114,8 @@
|
||||
</submenu>
|
||||
<submenu>
|
||||
<attribute name="label" translatable="yes">IPTV</attribute>
|
||||
<attribute name="action">app.hide_menu_bar</attribute>
|
||||
<attribute name="hidden-when">action-disabled</attribute>
|
||||
<item>
|
||||
<attribute name="label" translatable="yes">Add IPTV or stream service</attribute>
|
||||
<attribute name="action">app.on_iptv</attribute>
|
||||
@@ -147,5 +149,23 @@
|
||||
</item>
|
||||
</section>
|
||||
</submenu>
|
||||
<submenu>
|
||||
<attribute name="label" translatable="yes">FTP client</attribute>
|
||||
<attribute name="action">app.show_ftp_menu</attribute>
|
||||
<attribute name="hidden-when">action-disabled</attribute>
|
||||
<item>
|
||||
<attribute name="label" translatable="yes">Close</attribute>
|
||||
<attribute name="action">app.on_ftp_client_close</attribute>
|
||||
</item>
|
||||
</submenu>
|
||||
<submenu>
|
||||
<attribute name="label" translatable="yes">Help</attribute>
|
||||
<section>
|
||||
<item>
|
||||
<attribute name="label" translatable="yes">About</attribute>
|
||||
<attribute name="action">app.on_about_app</attribute>
|
||||
</item>
|
||||
</section>
|
||||
</submenu>
|
||||
</menu>
|
||||
</interface>
|
||||
@@ -8,7 +8,7 @@ from enum import Enum
|
||||
|
||||
from app.commons import run_idle
|
||||
from app.settings import SettingsType
|
||||
from app.ui.dialogs import show_dialog, DialogType
|
||||
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
|
||||
|
||||
@@ -30,10 +30,7 @@ class BackupDialog:
|
||||
"on_resize": self.on_resize,
|
||||
"on_key_release": self.on_key_release}
|
||||
|
||||
builder = Gtk.Builder()
|
||||
builder.set_translation_domain("demon-editor")
|
||||
builder.add_from_file(UI_RESOURCES_PATH + "backup_dialog.glade")
|
||||
builder.connect_signals(handlers)
|
||||
builder = get_builder(UI_RESOURCES_PATH + "backup_dialog.glade", handlers)
|
||||
|
||||
self._settings = settings
|
||||
self._s_type = settings.setting_type
|
||||
|
||||
@@ -57,7 +57,7 @@ Author: Dmitriy Yefremov
|
||||
<property name="can_focus">False</property>
|
||||
<property name="use_stock">False</property>
|
||||
<signal name="activate" handler="on_restore_bouquets" swapped="no"/>
|
||||
<accelerator key="r" signal="activate" modifiers="Primary"/>
|
||||
<accelerator key="r" signal="activate" modifiers="GDK_CONTROL_MASK"/>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
@@ -67,7 +67,7 @@ Author: Dmitriy Yefremov
|
||||
<property name="can_focus">False</property>
|
||||
<property name="use_stock">False</property>
|
||||
<signal name="activate" handler="on_restore_all" swapped="no"/>
|
||||
<accelerator key="e" signal="activate" modifiers="Primary"/>
|
||||
<accelerator key="e" signal="activate" modifiers="GDK_CONTROL_MASK"/>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
@@ -115,7 +115,6 @@ Author: Dmitriy Yefremov
|
||||
<property name="window_position">center-on-parent</property>
|
||||
<property name="destroy_with_parent">True</property>
|
||||
<property name="icon_name">document-revert</property>
|
||||
<property name="gravity">center</property>
|
||||
<signal name="check-resize" handler="on_resize" swapped="no"/>
|
||||
<child>
|
||||
<placeholder/>
|
||||
@@ -297,7 +296,7 @@ Author: Dmitriy Yefremov
|
||||
<property name="always_show_image">True</property>
|
||||
<property name="draw_indicator">False</property>
|
||||
<signal name="toggled" handler="on_info_button_toggled" swapped="no"/>
|
||||
<accelerator key="i" signal="clicked" modifiers="Primary"/>
|
||||
<accelerator key="i" signal="clicked" modifiers="GDK_CONTROL_MASK"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
|
||||
@@ -431,7 +431,9 @@ Author: Dmitriy Yefremov
|
||||
<property name="can_focus">False</property>
|
||||
<property name="valign">center</property>
|
||||
<property name="margin_top">5</property>
|
||||
<property name="margin_bottom">5</property>
|
||||
<property name="margin_bottom">10</property>
|
||||
<property name="margin_left">10</property>
|
||||
<property name="margin_right">10</property>
|
||||
<property name="homogeneous">True</property>
|
||||
<child>
|
||||
<object class="GtkButton" id="standby_button">
|
||||
@@ -1176,7 +1178,7 @@ audio-volume-medium-symbolic</property>
|
||||
<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>
|
||||
@@ -1187,7 +1189,7 @@ audio-volume-medium-symbolic</property>
|
||||
<object class="GtkEntry" id="timer_desc_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>
|
||||
@@ -1198,7 +1200,7 @@ audio-volume-medium-symbolic</property>
|
||||
<object class="GtkEntry" id="timer_name_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>
|
||||
@@ -1485,7 +1487,7 @@ audio-volume-medium-symbolic</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="editable">False</property>
|
||||
<property name="primary_icon_name">document-edit-symbolic</property>
|
||||
|
||||
</object>
|
||||
</child>
|
||||
<style>
|
||||
@@ -1556,7 +1558,7 @@ audio-volume-medium-symbolic</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="editable">False</property>
|
||||
<property name="primary_icon_name">document-edit-symbolic</property>
|
||||
|
||||
</object>
|
||||
</child>
|
||||
<style>
|
||||
@@ -1605,7 +1607,7 @@ audio-volume-medium-symbolic</property>
|
||||
<property name="spacing">5</property>
|
||||
<child>
|
||||
<object class="GtkImage" id="timer_location_image">
|
||||
<property name="visible">True</property>
|
||||
<property name="visible">False</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="icon_name">document-edit-symbolic</property>
|
||||
</object>
|
||||
@@ -1638,7 +1640,7 @@ audio-volume-medium-symbolic</property>
|
||||
<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>
|
||||
|
||||
@@ -6,7 +6,9 @@ from urllib.parse import quote
|
||||
|
||||
from gi.repository import GLib
|
||||
|
||||
from .dialogs import get_dialogs_string, show_dialog, DialogType, get_message
|
||||
from app.settings import IS_WIN
|
||||
from .dialogs import get_builder
|
||||
from .dialogs import show_dialog, DialogType, get_message
|
||||
from .uicommons import Gtk, Gdk, UI_RESOURCES_PATH, Column
|
||||
from ..commons import run_task, run_with_delay, log, run_idle
|
||||
from ..connections import HttpAPI
|
||||
@@ -61,19 +63,19 @@ class ControlBox(Gtk.HBox):
|
||||
|
||||
@property
|
||||
def event_data(self):
|
||||
return self._event_data
|
||||
return self._event_data or {}
|
||||
|
||||
@property
|
||||
def title(self):
|
||||
return self._title
|
||||
return self._title or ""
|
||||
|
||||
@property
|
||||
def desc(self):
|
||||
return self._desc
|
||||
return self._desc or ""
|
||||
|
||||
@property
|
||||
def time_header(self):
|
||||
return self._time_header
|
||||
return self._time_header or ""
|
||||
|
||||
class TimerRow(Gtk.ListBoxRow):
|
||||
|
||||
@@ -84,8 +86,7 @@ class ControlBox(Gtk.HBox):
|
||||
|
||||
self._timer = timer
|
||||
|
||||
builder = Gtk.Builder()
|
||||
builder.add_from_string(get_dialogs_string(self._UI_PATH))
|
||||
builder = get_builder(self._UI_PATH, None, use_str=True)
|
||||
row_box = builder.get_object("timer_row_box")
|
||||
name_label = builder.get_object("timer_name_label")
|
||||
description_label = builder.get_object("timer_description_label")
|
||||
@@ -130,9 +131,7 @@ class ControlBox(Gtk.HBox):
|
||||
"on_timers_press": self.on_timers_press,
|
||||
"on_timers_drag_data_received": self.on_timers_drag_data_received}
|
||||
|
||||
builder = Gtk.Builder()
|
||||
builder.add_from_file(UI_RESOURCES_PATH + "control.glade")
|
||||
builder.connect_signals(handlers)
|
||||
builder = get_builder(UI_RESOURCES_PATH + "control.glade", handlers)
|
||||
|
||||
self.add(builder.get_object("main_box_frame"))
|
||||
self._stack = builder.get_object("stack")
|
||||
@@ -313,18 +312,17 @@ class ControlBox(Gtk.HBox):
|
||||
|
||||
img = data.get("img_data", None)
|
||||
if img:
|
||||
is_darwin = self._settings.is_darwin
|
||||
GLib.idle_add(self._screenshot_button_box.set_sensitive, is_darwin)
|
||||
path = os.path.expanduser("~/Desktop") if is_darwin else None
|
||||
GLib.idle_add(self._screenshot_button_box.set_sensitive, IS_WIN)
|
||||
path = os.path.expanduser("~/Desktop") if IS_WIN else None
|
||||
|
||||
try:
|
||||
import tempfile
|
||||
import subprocess
|
||||
|
||||
with tempfile.NamedTemporaryFile(mode="wb", suffix=".jpg", dir=path, delete=not is_darwin) as tf:
|
||||
with tempfile.NamedTemporaryFile(mode="wb", suffix=".jpg", dir=path, delete=not IS_WIN) as tf:
|
||||
tf.write(img)
|
||||
cmd = ["open" if is_darwin else "xdg-open", tf.name]
|
||||
subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()
|
||||
f_name = tf.name
|
||||
subprocess.Popen([f_name], stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True).communicate()
|
||||
finally:
|
||||
GLib.idle_add(self._screenshot_button_box.set_sensitive, True)
|
||||
|
||||
@@ -340,7 +338,7 @@ class ControlBox(Gtk.HBox):
|
||||
|
||||
def on_service_changed(self, ref):
|
||||
self._app._wait_dialog.show()
|
||||
self._http_api.send(HttpAPI.Request.EPG, ref, self.update_epg_data)
|
||||
self._http_api.send(HttpAPI.Request.EPG, quote(ref), self.update_epg_data)
|
||||
|
||||
@run_idle
|
||||
def update_epg_data(self, epg):
|
||||
@@ -357,7 +355,7 @@ class ControlBox(Gtk.HBox):
|
||||
def on_epg_filter_changed(self, entry):
|
||||
self._epg_list_box.invalidate_filter()
|
||||
|
||||
def epg_filter_function(self, row: EpgRow):
|
||||
def epg_filter_function(self, row):
|
||||
txt = self._epg_filter_entry.get_text().upper()
|
||||
return any((not txt, txt in row.time_header.upper(), txt in row.title.upper(), txt in row.desc.upper()))
|
||||
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
* {
|
||||
-GtkDialog-action-area-border: 5em;
|
||||
}
|
||||
|
||||
entry {
|
||||
min-height: 2em;
|
||||
}
|
||||
|
||||
button {
|
||||
min-height: 1.5em;
|
||||
padding: 0.1em;
|
||||
}
|
||||
|
||||
spinbutton {
|
||||
min-height: 1.5em;
|
||||
}
|
||||
|
||||
toolbutton {
|
||||
padding: 0.1em;
|
||||
}
|
||||
|
||||
spinner {
|
||||
padding-left: 1em;
|
||||
padding-right: 1em;
|
||||
}
|
||||
|
||||
infobar {
|
||||
min-height: 2em;
|
||||
}
|
||||
|
||||
switch slider {
|
||||
min-height: 1.5em;
|
||||
min-width: 1.5em;
|
||||
}
|
||||
|
||||
paned > separator {
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
background-size: 1px 24px;
|
||||
}
|
||||
@@ -40,16 +40,16 @@ Author: Dmitriy Yefremov
|
||||
<property name="icon_name">system-help</property>
|
||||
<property name="type_hint">normal</property>
|
||||
<property name="program_name">DemonEditor</property>
|
||||
<property name="version">1.0.5 Beta</property>
|
||||
<property name="version">1.0.7 Alpha</property>
|
||||
<property name="copyright">2018-2021 Dmitriy Yefremov
|
||||
</property>
|
||||
<property name="comments" translatable="yes">Enigma2 channel and satellite list editor for MacOS.
|
||||
(Experimental)</property>
|
||||
<property name="website">https://github.com/DYefremov/DemonEditor/tree/experimental-mac</property>
|
||||
</property>
|
||||
<property name="comments" translatable="yes">Enigma2 channel and satellite list editor for MS Windows.
|
||||
(Experimental)</property>
|
||||
<property name="website">https://github.com/DYefremov/DemonEditor/tree/experimental-win</property>
|
||||
<property name="license" translatable="yes">Это приложение распространяется без каких-либо гарантий.
|
||||
Подробнее в <a href="http://opensource.org/licenses/mit-license.php">The MIT License (MIT)</a>.</property>
|
||||
Подробнее в <a href="http://opensource.org/licenses/mit-license.php">The MIT License (MIT)</a>.</property>
|
||||
<property name="authors">Dmitriy Yefremov
|
||||
</property>
|
||||
</property>
|
||||
<property name="translator_credits" translatable="yes">translator-credits</property>
|
||||
<property name="artists">Program logo: <a href="http://ihad.tv">mfgeg</a></property>
|
||||
<property name="logo_icon_name">demon-editor</property>
|
||||
@@ -92,7 +92,6 @@ Author: Dmitriy Yefremov
|
||||
<property name="type_hint">utility</property>
|
||||
<property name="skip_taskbar_hint">True</property>
|
||||
<property name="skip_pager_hint">True</property>
|
||||
<property name="gravity">center</property>
|
||||
<child type="titlebar">
|
||||
<placeholder/>
|
||||
</child>
|
||||
@@ -126,6 +125,7 @@ Author: Dmitriy Yefremov
|
||||
<child>
|
||||
<object class="GtkButton" id="input_dialog_ok_button">
|
||||
<property name="label" translatable="yes">OK</property>
|
||||
<property name="width_request">100</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
@@ -153,7 +153,7 @@ Author: Dmitriy Yefremov
|
||||
<property name="margin_right">2</property>
|
||||
<property name="margin_top">2</property>
|
||||
<property name="margin_bottom">2</property>
|
||||
<property name="primary_icon_name">document-edit-symbolic</property>
|
||||
|
||||
<property name="primary_icon_activatable">False</property>
|
||||
<property name="secondary_icon_activatable">False</property>
|
||||
<property name="secondary_icon_sensitive">False</property>
|
||||
@@ -181,10 +181,6 @@ Author: Dmitriy Yefremov
|
||||
<property name="skip_taskbar_hint">True</property>
|
||||
<property name="skip_pager_hint">True</property>
|
||||
<property name="decorated">False</property>
|
||||
<property name="gravity">center</property>
|
||||
<child type="titlebar">
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkBox" id="wait_dialog_box">
|
||||
<property name="width_request">100</property>
|
||||
@@ -222,7 +218,7 @@ Author: Dmitriy Yefremov
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</child> <!-- NOP -->
|
||||
<style>
|
||||
<class name="app-notification"/>
|
||||
</style>
|
||||
|
||||
@@ -5,6 +5,7 @@ from functools import lru_cache
|
||||
from pathlib import Path
|
||||
|
||||
from app.commons import run_idle
|
||||
from app.settings import SEP, IS_WIN
|
||||
from .uicommons import Gtk, UI_RESOURCES_PATH, TEXT_DOMAIN, IS_GNOME_SESSION
|
||||
|
||||
|
||||
@@ -17,12 +18,11 @@ class Dialog(Enum):
|
||||
<property name="use-header-bar">{use_header}</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="modal">True</property>
|
||||
<property name="default_width">320</property>
|
||||
<property name="width_request">250</property>
|
||||
<property name="destroy_with_parent">True</property>
|
||||
<property name="type_hint">dialog</property>
|
||||
<property name="skip_taskbar_hint">True</property>
|
||||
<property name="skip_pager_hint">True</property>
|
||||
<property name="gravity">center</property>
|
||||
<property name="message_type">{message_type}</property>
|
||||
<property name="buttons">{buttons_type}</property>
|
||||
</object>
|
||||
@@ -104,10 +104,12 @@ def get_chooser_dialog(transient, settings, name, patterns, title=None):
|
||||
|
||||
|
||||
def get_file_chooser_dialog(transient, text, settings, action_type, file_filter, buttons=None, title=None, dirs=False):
|
||||
text = get_message(text) if text else ""
|
||||
action_type = Gtk.FileChooserAction.SELECT_FOLDER if action_type is None else action_type
|
||||
dialog = Gtk.FileChooserNative.new(get_message(title) if title else "", transient, action_type)
|
||||
buttons = buttons or (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, Gtk.STOCK_OPEN, Gtk.ResponseType.OK)
|
||||
dialog = Gtk.FileChooserDialog(text, transient, action_type, buttons, use_header_bar=IS_GNOME_SESSION)
|
||||
dialog.set_title(get_message(title) if title else "")
|
||||
dialog.set_create_folders(dirs)
|
||||
dialog.set_modal(True)
|
||||
|
||||
if file_filter is not None:
|
||||
dialog.add_filter(file_filter)
|
||||
@@ -115,10 +117,10 @@ def get_file_chooser_dialog(transient, text, settings, action_type, file_filter,
|
||||
dialog.set_current_folder(settings.data_local_path)
|
||||
response = dialog.run()
|
||||
|
||||
if response == Gtk.ResponseType.ACCEPT:
|
||||
if response not in (Gtk.ResponseType.CANCEL, Gtk.ResponseType.DELETE_EVENT):
|
||||
path = Path(dialog.get_filename() or dialog.get_current_folder())
|
||||
if path.is_dir():
|
||||
response = "{}/".format(path.resolve())
|
||||
response = "{}{}".format(path.resolve(), SEP)
|
||||
elif path.is_file():
|
||||
response = str(path.resolve())
|
||||
dialog.destroy()
|
||||
@@ -179,9 +181,52 @@ def get_message(message):
|
||||
|
||||
@lru_cache(maxsize=5)
|
||||
def get_dialogs_string(path):
|
||||
with open(path, "r") as f:
|
||||
with open(path, "r", encoding="utf-8") as f:
|
||||
return "".join(f)
|
||||
|
||||
|
||||
def get_builder(path, handlers=None, use_str=False, objects=None):
|
||||
""" Creates and returns a Gtk.Builder instance. """
|
||||
builder = Gtk.Builder()
|
||||
builder.set_translation_domain(TEXT_DOMAIN)
|
||||
|
||||
if use_str:
|
||||
if objects:
|
||||
builder.add_objects_from_string(get_dialogs_string(path).format(use_header=IS_GNOME_SESSION), objects)
|
||||
else:
|
||||
builder.add_from_string(get_dialogs_string(path).format(use_header=IS_GNOME_SESSION))
|
||||
else:
|
||||
if objects:
|
||||
builder.add_objects_from_file(path, objects)
|
||||
else:
|
||||
builder.add_from_file(path)
|
||||
|
||||
builder.connect_signals(handlers or {})
|
||||
if IS_WIN:
|
||||
translate_objects(builder.get_objects())
|
||||
|
||||
return builder
|
||||
|
||||
|
||||
def translate_objects(objects):
|
||||
"""
|
||||
Used to translate GUI from * .glade files in MS Windows.
|
||||
|
||||
More info: https://gitlab.gnome.org/GNOME/gtk/-/issues/569
|
||||
"""
|
||||
for o in objects:
|
||||
if hasattr(o, "get_label"):
|
||||
label = o.get_label()
|
||||
if label:
|
||||
o.set_label(get_message(label))
|
||||
t_text = o.get_tooltip_text()
|
||||
if t_text:
|
||||
o.set_tooltip_text(get_message(t_text))
|
||||
elif hasattr(o, "get_title"):
|
||||
title = o.get_title()
|
||||
if title:
|
||||
o.set_title(get_message(title))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
pass
|
||||
|
||||
@@ -55,7 +55,6 @@ Author: Dmitriy Yefremov
|
||||
<property name="icon_name">mail-send-receive</property>
|
||||
<property name="skip_taskbar_hint">True</property>
|
||||
<property name="skip_pager_hint">True</property>
|
||||
<property name="gravity">center</property>
|
||||
<child type="titlebar">
|
||||
<placeholder/>
|
||||
</child>
|
||||
@@ -372,7 +371,6 @@ Author: Dmitriy Yefremov
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="tooltip_text" translatable="yes">Options</property>
|
||||
<property name="valign">center</property>
|
||||
<signal name="clicked" handler="on_settings" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
|
||||
@@ -8,7 +8,7 @@ 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 .dialogs import show_dialog, DialogType, get_message
|
||||
from .dialogs import show_dialog, DialogType, get_message, get_builder
|
||||
from .uicommons import Gtk, UI_RESOURCES_PATH
|
||||
|
||||
|
||||
@@ -27,9 +27,7 @@ class DownloadDialog:
|
||||
"on_remove_unused_bouquets_toggled": self.on_remove_unused_bouquets_toggled,
|
||||
"on_info_bar_close": self.on_info_bar_close}
|
||||
|
||||
builder = Gtk.Builder()
|
||||
builder.add_from_file(UI_RESOURCES_PATH + "download_dialog.glade")
|
||||
builder.connect_signals(handlers)
|
||||
builder = get_builder(UI_RESOURCES_PATH + "download_dialog.glade", handlers)
|
||||
|
||||
self._dialog_window = builder.get_object("download_dialog_window")
|
||||
self._dialog_window.set_transient_for(transient)
|
||||
|
||||
@@ -87,7 +87,7 @@ Author: Dmitriy Yefremov
|
||||
<property name="image">copy_image</property>
|
||||
<property name="use_stock">False</property>
|
||||
<signal name="activate" handler="on_copy_ref" swapped="no"/>
|
||||
<accelerator key="c" signal="activate" modifiers="Primary"/>
|
||||
<accelerator key="c" signal="activate" modifiers="GDK_CONTROL_MASK"/>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
@@ -118,7 +118,7 @@ Author: Dmitriy Yefremov
|
||||
<property name="image">insert_link_image</property>
|
||||
<property name="use_stock">False</property>
|
||||
<signal name="activate" handler="on_assign_ref" swapped="no"/>
|
||||
<accelerator key="v" signal="activate" modifiers="Primary"/>
|
||||
<accelerator key="v" signal="activate" modifiers="GDK_CONTROL_MASK"/>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
@@ -436,7 +436,7 @@ Author: Dmitriy Yefremov
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="text" translatable="yes">/data/epg/</property>
|
||||
<property name="primary_icon_name">document-edit-symbolic</property>
|
||||
|
||||
<property name="secondary_icon_name">folder-open-symbolic</property>
|
||||
<property name="primary_icon_activatable">False</property>
|
||||
<property name="secondary_icon_tooltip_text" translatable="yes">Select</property>
|
||||
@@ -466,7 +466,7 @@ Author: Dmitriy Yefremov
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="text" translatable="yes">/etc/enigma2/</property>
|
||||
<property name="primary_icon_name">document-edit-symbolic</property>
|
||||
|
||||
<property name="primary_icon_activatable">False</property>
|
||||
</object>
|
||||
<packing>
|
||||
@@ -601,7 +601,6 @@ Author: Dmitriy Yefremov
|
||||
<property name="destroy_with_parent">True</property>
|
||||
<property name="skip_taskbar_hint">True</property>
|
||||
<property name="skip_pager_hint">True</property>
|
||||
<property name="gravity">center</property>
|
||||
<signal name="check-resize" handler="on_resize" swapped="no"/>
|
||||
<signal name="delete-event" handler="on_close_dialog" swapped="no"/>
|
||||
<child>
|
||||
|
||||
@@ -8,13 +8,14 @@ from enum import Enum
|
||||
from urllib.error import HTTPError, URLError
|
||||
|
||||
from gi.repository import GLib
|
||||
|
||||
from app.commons import run_idle, run_task
|
||||
from app.connections import download_data, DownloadType
|
||||
from app.eparser.ecommons import BouquetService, BqServiceType
|
||||
from app.tools.epg import EPG, ChannelsParser
|
||||
from app.ui.dialogs import get_message, show_dialog, DialogType
|
||||
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, TEXT_DOMAIN, Column, EPG_ICON, KeyboardKey, MOD_MASK
|
||||
from .uicommons import Gtk, Gdk, UI_RESOURCES_PATH, Column, EPG_ICON, KeyboardKey, MOD_MASK
|
||||
|
||||
|
||||
class RefsSource(Enum):
|
||||
@@ -66,10 +67,7 @@ class EpgDialog:
|
||||
self._show_tooltips = True
|
||||
self._download_xml_is_active = False
|
||||
|
||||
builder = Gtk.Builder()
|
||||
builder.set_translation_domain(TEXT_DOMAIN)
|
||||
builder.add_from_file(UI_RESOURCES_PATH + "epg_dialog.glade")
|
||||
builder.connect_signals(handlers)
|
||||
builder = get_builder(UI_RESOURCES_PATH + "epg_dialog.glade", handlers)
|
||||
|
||||
self._dialog = builder.get_object("epg_dialog_window")
|
||||
self._dialog.set_transient_for(transient)
|
||||
@@ -185,7 +183,7 @@ class EpgDialog:
|
||||
|
||||
def init_bouquet_data(self):
|
||||
for r in self._ex_fav_model:
|
||||
row = [*r[:]]
|
||||
row = list(r[:])
|
||||
fav_id = r[Column.FAV_ID]
|
||||
self._services[fav_id] = self._ex_services[fav_id].fav_id
|
||||
yield self._bouquet_model.append(row)
|
||||
|
||||
709
app/ui/ftp.glade
709
app/ui/ftp.glade
@@ -93,27 +93,97 @@ Author: Dmitriy Yefremov
|
||||
<property name="label_xalign">0</property>
|
||||
<property name="shadow_type">in</property>
|
||||
<child>
|
||||
<object class="GtkPaned" id="paned">
|
||||
<property name="width_request">320</property>
|
||||
<property name="height_request">240</property>
|
||||
<object class="GtkBox" id="main_ftp_box">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="margin_left">5</property>
|
||||
<property name="margin_right">5</property>
|
||||
<property name="margin_top">5</property>
|
||||
<property name="margin_bottom">5</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<property name="wide_handle">True</property>
|
||||
<property name="spacing">2</property>
|
||||
<child>
|
||||
<object class="GtkBox" id="ftp_bpx">
|
||||
<object class="GtkBox" id="ftp_button_box">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<property name="margin_left">10</property>
|
||||
<property name="margin_right">10</property>
|
||||
<property name="margin_top">5</property>
|
||||
<property name="margin_bottom">5</property>
|
||||
<property name="spacing">2</property>
|
||||
<child>
|
||||
<object class="GtkBox">
|
||||
<object class="GtkButton" id="connect_button">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="tooltip_text" translatable="yes">Connect</property>
|
||||
<signal name="clicked" handler="on_connect" swapped="no"/>
|
||||
<child>
|
||||
<object class="GtkImage" id="connect_button_image">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="stock">gtk-connect</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="disconnect_button">
|
||||
<property name="can_focus">False</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="tooltip_text" translatable="yes">Disconnect</property>
|
||||
<signal name="clicked" handler="on_disconnect" swapped="no"/>
|
||||
<child>
|
||||
<object class="GtkImage" id="disconnect_button_image">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="stock">gtk-disconnect</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkComboBox" id="bookmark_button">
|
||||
<property name="can_focus">False</property>
|
||||
<property name="model">bookmarks_list_store</property>
|
||||
<property name="id_column">0</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkPaned" id="paned">
|
||||
<property name="width_request">320</property>
|
||||
<property name="height_request">240</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="margin_left">5</property>
|
||||
<property name="margin_right">5</property>
|
||||
<property name="margin_top">5</property>
|
||||
<property name="margin_bottom">5</property>
|
||||
<property name="wide_handle">True</property>
|
||||
<child>
|
||||
<object class="GtkBox" id="ftp_bpx">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<property name="spacing">2</property>
|
||||
<child>
|
||||
<object class="GtkBox" id="ftp_info_box">
|
||||
<property name="visible">True</property>
|
||||
@@ -123,9 +193,7 @@ Author: Dmitriy Yefremov
|
||||
<object class="GtkLabel" id="ftp_label">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_left">10</property>
|
||||
<property name="label">FTP:</property>
|
||||
<property name="yalign">1</property>
|
||||
<attributes>
|
||||
<attribute name="weight" value="semibold"/>
|
||||
</attributes>
|
||||
@@ -141,7 +209,7 @@ Author: Dmitriy Yefremov
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="ellipsize">end</property>
|
||||
<property name="max_width_chars">25</property>
|
||||
<property name="max_width_chars">75</property>
|
||||
<property name="yalign">1</property>
|
||||
</object>
|
||||
<packing>
|
||||
@@ -158,24 +226,158 @@ Author: Dmitriy Yefremov
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkBox" id="ftp_button_box">
|
||||
<object class="GtkScrolledWindow" id="ftp_view_scrolled_window">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="spacing">2</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="shadow_type">in</property>
|
||||
<property name="min_content_height">100</property>
|
||||
<child>
|
||||
<object class="GtkButton" id="connect_button">
|
||||
<object class="GtkTreeView" id="ftp_view">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="tooltip_text" translatable="yes">Connect</property>
|
||||
<signal name="clicked" handler="on_connect" swapped="no"/>
|
||||
<child>
|
||||
<object class="GtkImage" id="connect_button_image">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="stock">gtk-connect</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="model">ftp_list_store</property>
|
||||
<property name="search_column">1</property>
|
||||
<property name="rubber_banding">True</property>
|
||||
<signal name="button-press-event" handler="on_view_popup_menu" object="ftp_popup_menu" swapped="no"/>
|
||||
<signal name="button-press-event" handler="on_view_press" swapped="no"/>
|
||||
<signal name="button-release-event" handler="on_view_release" swapped="no"/>
|
||||
<signal name="drag-begin" handler="on_view_drag_begin" after="yes" swapped="no"/>
|
||||
<signal name="drag-data-get" handler="on_ftp_drag_data_get" swapped="no"/>
|
||||
<signal name="drag-data-received" handler="on_ftp_drag_data_received" swapped="no"/>
|
||||
<signal name="drag-end" handler="on_view_drag_end" swapped="no"/>
|
||||
<signal name="key-press-event" handler="on_view_key_press" swapped="no"/>
|
||||
<signal name="row-activated" handler="on_ftp_row_activated" swapped="no"/>
|
||||
<child internal-child="selection">
|
||||
<object class="GtkTreeSelection" id="ftp_selection">
|
||||
<property name="mode">multiple</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkTreeViewColumn" id="ftp_name_column">
|
||||
<property name="resizable">True</property>
|
||||
<property name="min_width">100</property>
|
||||
<property name="title" translatable="yes">Name</property>
|
||||
<property name="expand">True</property>
|
||||
<property name="alignment">0.5</property>
|
||||
<property name="sort_column_id">1</property>
|
||||
<child>
|
||||
<object class="GtkCellRendererPixbuf" id="ftp_icon_column_renderer">
|
||||
<property name="xalign">0.019999999552965164</property>
|
||||
</object>
|
||||
<attributes>
|
||||
<attribute name="pixbuf">0</attribute>
|
||||
</attributes>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkCellRendererText" id="ftp_name_column_renderer">
|
||||
<property name="xalign">0.019999999552965164</property>
|
||||
<property name="ellipsize">end</property>
|
||||
<signal name="edited" handler="on_ftp_edited" swapped="no"/>
|
||||
</object>
|
||||
<attributes>
|
||||
<attribute name="text">1</attribute>
|
||||
</attributes>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkTreeViewColumn" id="ftp_size_column">
|
||||
<property name="sizing">fixed</property>
|
||||
<property name="min_width">75</property>
|
||||
<property name="title" translatable="yes">Size</property>
|
||||
<property name="alignment">0.5</property>
|
||||
<property name="sort_column_id">2</property>
|
||||
<child>
|
||||
<object class="GtkCellRendererText" id="ftp_size_column_renderer">
|
||||
<property name="xalign">0.94999998807907104</property>
|
||||
</object>
|
||||
<attributes>
|
||||
<attribute name="text">2</attribute>
|
||||
</attributes>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkTreeViewColumn" id="ftp_date_column">
|
||||
<property name="min_width">75</property>
|
||||
<property name="title" translatable="yes">Date</property>
|
||||
<property name="alignment">0.5</property>
|
||||
<property name="sort_column_id">3</property>
|
||||
<child>
|
||||
<object class="GtkCellRendererText" id="ftp_date_column_renderer"/>
|
||||
<attributes>
|
||||
<attribute name="text">3</attribute>
|
||||
</attributes>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkTreeViewColumn" id="ftp_attr_column">
|
||||
<property name="sizing">fixed</property>
|
||||
<property name="min_width">75</property>
|
||||
<property name="title" translatable="yes">Attr.</property>
|
||||
<property name="alignment">0.5</property>
|
||||
<property name="sort_column_id">4</property>
|
||||
<child>
|
||||
<object class="GtkCellRendererText" id="ftp_attr_column_renderer">
|
||||
<property name="xalign">0.50999999046325684</property>
|
||||
<property name="ellipsize">end</property>
|
||||
</object>
|
||||
<attributes>
|
||||
<attribute name="text">4</attribute>
|
||||
</attributes>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkTreeViewColumn" id="ftp_extra_column">
|
||||
<property name="visible">False</property>
|
||||
<property name="title" translatable="yes">Extra</property>
|
||||
<child>
|
||||
<object class="GtkCellRendererText" id="ftp_extra_column_renderer"/>
|
||||
<attributes>
|
||||
<attribute name="text">5</attribute>
|
||||
</attributes>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="resize">True</property>
|
||||
<property name="shrink">True</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkBox" id="file_box">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<property name="spacing">2</property>
|
||||
<child>
|
||||
<object class="GtkBox" id="pc_info_box">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_left">5</property>
|
||||
<property name="spacing">5</property>
|
||||
<child>
|
||||
<object class="GtkLabel" id="pc_label">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="halign">start</property>
|
||||
<property name="margin_left">10</property>
|
||||
<property name="label" translatable="yes">PC:</property>
|
||||
<attributes>
|
||||
<attribute name="weight" value="semibold"/>
|
||||
</attributes>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
@@ -184,18 +386,11 @@ Author: Dmitriy Yefremov
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="disconnect_button">
|
||||
<object class="GtkLabel" id="pc_info_label">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="tooltip_text" translatable="yes">Disconnect</property>
|
||||
<signal name="clicked" handler="on_disconnect" swapped="no"/>
|
||||
<child>
|
||||
<object class="GtkImage" id="disconnect_button_image">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="stock">gtk-disconnect</property>
|
||||
</object>
|
||||
</child>
|
||||
<property name="ellipsize">end</property>
|
||||
<property name="max_width_chars">75</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
@@ -203,189 +398,6 @@ Author: Dmitriy Yefremov
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkComboBox" id="bookmark_button">
|
||||
<property name="can_focus">False</property>
|
||||
<property name="model">bookmarks_list_store</property>
|
||||
<property name="id_column">0</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="pack_type">end</property>
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="pack_type">end</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkScrolledWindow" id="ftp_view_scrolled_window">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="shadow_type">in</property>
|
||||
<property name="min_content_height">100</property>
|
||||
<child>
|
||||
<object class="GtkTreeView" id="ftp_view">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="model">ftp_list_store</property>
|
||||
<property name="search_column">1</property>
|
||||
<property name="rubber_banding">True</property>
|
||||
<signal name="button-press-event" handler="on_view_popup_menu" object="ftp_popup_menu" swapped="no"/>
|
||||
<signal name="button-press-event" handler="on_view_press" swapped="no"/>
|
||||
<signal name="button-release-event" handler="on_view_release" swapped="no"/>
|
||||
<signal name="drag-begin" handler="on_view_drag_begin" after="yes" swapped="no"/>
|
||||
<signal name="drag-data-get" handler="on_ftp_drag_data_get" swapped="no"/>
|
||||
<signal name="drag-data-received" handler="on_ftp_drag_data_received" swapped="no"/>
|
||||
<signal name="drag-end" handler="on_view_drag_end" swapped="no"/>
|
||||
<signal name="key-press-event" handler="on_view_key_press" swapped="no"/>
|
||||
<signal name="row-activated" handler="on_ftp_row_activated" swapped="no"/>
|
||||
<child internal-child="selection">
|
||||
<object class="GtkTreeSelection" id="ftp_selection">
|
||||
<property name="mode">multiple</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkTreeViewColumn" id="ftp_name_column">
|
||||
<property name="resizable">True</property>
|
||||
<property name="min_width">100</property>
|
||||
<property name="title" translatable="yes">Name</property>
|
||||
<property name="expand">True</property>
|
||||
<property name="alignment">0.5</property>
|
||||
<property name="sort_column_id">1</property>
|
||||
<child>
|
||||
<object class="GtkCellRendererPixbuf" id="ftp_icon_column_renderer">
|
||||
<property name="xalign">0.019999999552965164</property>
|
||||
</object>
|
||||
<attributes>
|
||||
<attribute name="pixbuf">0</attribute>
|
||||
</attributes>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkCellRendererText" id="ftp_name_column_renderer">
|
||||
<property name="xalign">0.019999999552965164</property>
|
||||
<property name="ellipsize">end</property>
|
||||
<signal name="edited" handler="on_ftp_edited" swapped="no"/>
|
||||
</object>
|
||||
<attributes>
|
||||
<attribute name="text">1</attribute>
|
||||
</attributes>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkTreeViewColumn" id="ftp_size_column">
|
||||
<property name="sizing">fixed</property>
|
||||
<property name="min_width">75</property>
|
||||
<property name="title" translatable="yes">Size</property>
|
||||
<property name="alignment">0.5</property>
|
||||
<property name="sort_column_id">2</property>
|
||||
<child>
|
||||
<object class="GtkCellRendererText" id="ftp_size_column_renderer">
|
||||
<property name="xalign">0.94999998807907104</property>
|
||||
</object>
|
||||
<attributes>
|
||||
<attribute name="text">2</attribute>
|
||||
</attributes>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkTreeViewColumn" id="ftp_date_column">
|
||||
<property name="min_width">75</property>
|
||||
<property name="title" translatable="yes">Date</property>
|
||||
<property name="alignment">0.5</property>
|
||||
<property name="sort_column_id">3</property>
|
||||
<child>
|
||||
<object class="GtkCellRendererText" id="ftp_date_column_renderer"/>
|
||||
<attributes>
|
||||
<attribute name="text">3</attribute>
|
||||
</attributes>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkTreeViewColumn" id="ftp_attr_column">
|
||||
<property name="sizing">fixed</property>
|
||||
<property name="min_width">50</property>
|
||||
<property name="title" translatable="yes">Attr.</property>
|
||||
<property name="alignment">0.5</property>
|
||||
<property name="sort_column_id">4</property>
|
||||
<child>
|
||||
<object class="GtkCellRendererText" id="ftp_attr_column_renderer">
|
||||
<property name="xalign">0.50999999046325684</property>
|
||||
<property name="ellipsize">end</property>
|
||||
</object>
|
||||
<attributes>
|
||||
<attribute name="text">4</attribute>
|
||||
</attributes>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkTreeViewColumn" id="ftp_extra_column">
|
||||
<property name="visible">False</property>
|
||||
<property name="title" translatable="yes">Extra</property>
|
||||
<child>
|
||||
<object class="GtkCellRendererText" id="ftp_extra_column_renderer"/>
|
||||
<attributes>
|
||||
<attribute name="text">5</attribute>
|
||||
</attributes>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="resize">True</property>
|
||||
<property name="shrink">True</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkBox" id="file_box">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<property name="spacing">2</property>
|
||||
<child>
|
||||
<object class="GtkBox" id="pc_info_box">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="spacing">5</property>
|
||||
<child>
|
||||
<object class="GtkLabel" id="pc_label">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="halign">start</property>
|
||||
<property name="margin_left">10</property>
|
||||
<property name="label" translatable="yes">PC:</property>
|
||||
<attributes>
|
||||
<attribute name="weight" value="semibold"/>
|
||||
</attributes>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
@@ -394,150 +406,137 @@ Author: Dmitriy Yefremov
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="pc_info_label">
|
||||
<object class="GtkScrolledWindow" id="file_view_scrolled_window">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="ellipsize">end</property>
|
||||
<property name="max_width_chars">32</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="shadow_type">in</property>
|
||||
<property name="min_content_height">100</property>
|
||||
<child>
|
||||
<object class="GtkTreeView" id="file_view">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="model">file_list_store</property>
|
||||
<property name="search_column">1</property>
|
||||
<property name="rubber_banding">True</property>
|
||||
<signal name="button-press-event" handler="on_view_popup_menu" object="file_popup_menu" swapped="no"/>
|
||||
<signal name="button-press-event" handler="on_view_press" swapped="no"/>
|
||||
<signal name="button-release-event" handler="on_view_release" swapped="no"/>
|
||||
<signal name="drag-begin" handler="on_view_drag_begin" after="yes" swapped="no"/>
|
||||
<signal name="drag-data-get" handler="on_file_drag_data_get" swapped="no"/>
|
||||
<signal name="drag-data-received" handler="on_file_drag_data_received" swapped="no"/>
|
||||
<signal name="drag-end" handler="on_view_drag_end" swapped="no"/>
|
||||
<signal name="key-press-event" handler="on_view_key_press" swapped="no"/>
|
||||
<signal name="row-activated" handler="on_file_row_activated" swapped="no"/>
|
||||
<child internal-child="selection">
|
||||
<object class="GtkTreeSelection" id="file_selection">
|
||||
<property name="mode">multiple</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkTreeViewColumn" id="file_name_column">
|
||||
<property name="resizable">True</property>
|
||||
<property name="min_width">100</property>
|
||||
<property name="title" translatable="yes">Name</property>
|
||||
<property name="expand">True</property>
|
||||
<property name="alignment">0.5</property>
|
||||
<property name="sort_column_id">1</property>
|
||||
<child>
|
||||
<object class="GtkCellRendererPixbuf" id="file_icon_column_renderer">
|
||||
<property name="xalign">0.20000000298023224</property>
|
||||
</object>
|
||||
<attributes>
|
||||
<attribute name="pixbuf">0</attribute>
|
||||
</attributes>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkCellRendererText" id="file_name_column_renderer">
|
||||
<property name="ellipsize">end</property>
|
||||
<signal name="edited" handler="on_file_edited" swapped="no"/>
|
||||
</object>
|
||||
<attributes>
|
||||
<attribute name="text">1</attribute>
|
||||
</attributes>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkTreeViewColumn" id="file_size_column">
|
||||
<property name="sizing">fixed</property>
|
||||
<property name="min_width">75</property>
|
||||
<property name="title" translatable="yes">Size</property>
|
||||
<property name="alignment">0.5</property>
|
||||
<property name="sort_column_id">2</property>
|
||||
<child>
|
||||
<object class="GtkCellRendererText" id="file_size_column_renderer">
|
||||
<property name="xalign">0.94999998807907104</property>
|
||||
</object>
|
||||
<attributes>
|
||||
<attribute name="text">2</attribute>
|
||||
</attributes>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkTreeViewColumn" id="file_date_column">
|
||||
<property name="min_width">75</property>
|
||||
<property name="title" translatable="yes">Date</property>
|
||||
<property name="alignment">0.5</property>
|
||||
<property name="sort_column_id">3</property>
|
||||
<child>
|
||||
<object class="GtkCellRendererText" id="file_date_column_renderer"/>
|
||||
<attributes>
|
||||
<attribute name="text">3</attribute>
|
||||
</attributes>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkTreeViewColumn" id="file_type_column">
|
||||
<property name="visible">False</property>
|
||||
<property name="sizing">fixed</property>
|
||||
<property name="min_width">50</property>
|
||||
<property name="title" translatable="yes">Path</property>
|
||||
<property name="alignment">0.5</property>
|
||||
<child>
|
||||
<object class="GtkCellRendererText" id="file_path_column_renderer"/>
|
||||
<attributes>
|
||||
<attribute name="text">4</attribute>
|
||||
</attributes>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkTreeViewColumn" id="file_extra_column">
|
||||
<property name="visible">False</property>
|
||||
<property name="title" translatable="yes">Extra</property>
|
||||
<child>
|
||||
<object class="GtkCellRendererText" id="file_extra_column_renderer"/>
|
||||
<attributes>
|
||||
<attribute name="text">5</attribute>
|
||||
</attributes>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkScrolledWindow" id="file_view_scrolled_window">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="shadow_type">in</property>
|
||||
<property name="min_content_height">100</property>
|
||||
<child>
|
||||
<object class="GtkTreeView" id="file_view">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="model">file_list_store</property>
|
||||
<property name="search_column">1</property>
|
||||
<property name="rubber_banding">True</property>
|
||||
<signal name="button-press-event" handler="on_view_popup_menu" object="file_popup_menu" swapped="no"/>
|
||||
<signal name="button-press-event" handler="on_view_press" swapped="no"/>
|
||||
<signal name="button-release-event" handler="on_view_release" swapped="no"/>
|
||||
<signal name="drag-begin" handler="on_view_drag_begin" after="yes" swapped="no"/>
|
||||
<signal name="drag-data-get" handler="on_file_drag_data_get" swapped="no"/>
|
||||
<signal name="drag-data-received" handler="on_file_drag_data_received" swapped="no"/>
|
||||
<signal name="drag-end" handler="on_view_drag_end" swapped="no"/>
|
||||
<signal name="key-press-event" handler="on_view_key_press" swapped="no"/>
|
||||
<signal name="row-activated" handler="on_file_row_activated" swapped="no"/>
|
||||
<child internal-child="selection">
|
||||
<object class="GtkTreeSelection" id="file_selection">
|
||||
<property name="mode">multiple</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkTreeViewColumn" id="file_name_column">
|
||||
<property name="resizable">True</property>
|
||||
<property name="min_width">100</property>
|
||||
<property name="title" translatable="yes">Name</property>
|
||||
<property name="expand">True</property>
|
||||
<property name="alignment">0.5</property>
|
||||
<property name="sort_column_id">1</property>
|
||||
<child>
|
||||
<object class="GtkCellRendererPixbuf" id="file_icon_column_renderer">
|
||||
<property name="xalign">0.20000000298023224</property>
|
||||
</object>
|
||||
<attributes>
|
||||
<attribute name="pixbuf">0</attribute>
|
||||
</attributes>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkCellRendererText" id="file_name_column_renderer">
|
||||
<property name="ellipsize">end</property>
|
||||
<signal name="edited" handler="on_file_edited" swapped="no"/>
|
||||
</object>
|
||||
<attributes>
|
||||
<attribute name="text">1</attribute>
|
||||
</attributes>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkTreeViewColumn" id="file_size_column">
|
||||
<property name="sizing">fixed</property>
|
||||
<property name="min_width">75</property>
|
||||
<property name="title" translatable="yes">Size</property>
|
||||
<property name="alignment">0.5</property>
|
||||
<property name="sort_column_id">2</property>
|
||||
<child>
|
||||
<object class="GtkCellRendererText" id="file_size_column_renderer">
|
||||
<property name="xalign">0.94999998807907104</property>
|
||||
</object>
|
||||
<attributes>
|
||||
<attribute name="text">2</attribute>
|
||||
</attributes>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkTreeViewColumn" id="file_date_column">
|
||||
<property name="min_width">75</property>
|
||||
<property name="title" translatable="yes">Date</property>
|
||||
<property name="alignment">0.5</property>
|
||||
<property name="sort_column_id">3</property>
|
||||
<child>
|
||||
<object class="GtkCellRendererText" id="file_date_column_renderer"/>
|
||||
<attributes>
|
||||
<attribute name="text">3</attribute>
|
||||
</attributes>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkTreeViewColumn" id="file_type_column">
|
||||
<property name="visible">False</property>
|
||||
<property name="sizing">fixed</property>
|
||||
<property name="min_width">50</property>
|
||||
<property name="title" translatable="yes">Path</property>
|
||||
<property name="alignment">0.5</property>
|
||||
<child>
|
||||
<object class="GtkCellRendererText" id="file_path_column_renderer"/>
|
||||
<attributes>
|
||||
<attribute name="text">4</attribute>
|
||||
</attributes>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkTreeViewColumn" id="file_extra_column">
|
||||
<property name="visible">False</property>
|
||||
<property name="title" translatable="yes">Extra</property>
|
||||
<child>
|
||||
<object class="GtkCellRendererText" id="file_extra_column_renderer"/>
|
||||
<attributes>
|
||||
<attribute name="text">5</attribute>
|
||||
</attributes>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
<property name="resize">True</property>
|
||||
<property name="shrink">True</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="resize">True</property>
|
||||
<property name="shrink">True</property>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
@@ -583,7 +582,7 @@ Author: Dmitriy Yefremov
|
||||
<property name="image">rename_image</property>
|
||||
<property name="use_stock">False</property>
|
||||
<signal name="activate" handler="on_ftp_edit" object="ftp_name_column_renderer" swapped="no"/>
|
||||
<accelerator key="r" signal="activate" modifiers="Primary"/>
|
||||
<accelerator key="r" signal="activate" modifiers="GDK_CONTROL_MASK"/>
|
||||
<accelerator key="F2" signal="activate"/>
|
||||
</object>
|
||||
</child>
|
||||
@@ -632,7 +631,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="Primary"/>
|
||||
<accelerator key="r" signal="activate" modifiers="GDK_CONTROL_MASK"/>
|
||||
<accelerator key="F2" signal="activate"/>
|
||||
</object>
|
||||
</child>
|
||||
|
||||
@@ -12,7 +12,8 @@ from gi.repository import GLib
|
||||
|
||||
from app.commons import log, run_task, run_idle
|
||||
from app.connections import UtfFTP
|
||||
from app.ui.dialogs import show_dialog, DialogType
|
||||
from app.settings import IS_WIN, SEP
|
||||
from app.ui.dialogs import show_dialog, DialogType, get_builder
|
||||
from app.ui.main_helper import on_popup_menu
|
||||
from .uicommons import Gtk, Gdk, UI_RESOURCES_PATH, KeyboardKey, MOD_MASK
|
||||
|
||||
@@ -68,9 +69,7 @@ class FtpClientBox(Gtk.HBox):
|
||||
"on_view_press": self.on_view_press,
|
||||
"on_view_release": self.on_view_release}
|
||||
|
||||
builder = Gtk.Builder()
|
||||
builder.add_from_file(UI_RESOURCES_PATH + "ftp.glade")
|
||||
builder.connect_signals(handlers)
|
||||
builder = get_builder(UI_RESOURCES_PATH + "ftp.glade", handlers)
|
||||
|
||||
self.add(builder.get_object("main_frame"))
|
||||
self._ftp_info_label = builder.get_object("ftp_info_label")
|
||||
@@ -233,22 +232,21 @@ class FtpClientBox(Gtk.HBox):
|
||||
def open_file(self, path):
|
||||
GLib.idle_add(self._file_view.set_sensitive, False)
|
||||
try:
|
||||
cmd = ["open" if self._settings.is_darwin else "xdg-open", path]
|
||||
subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()
|
||||
cmd = ["start" if IS_WIN else "xdg-open", path]
|
||||
subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=IS_WIN).communicate()
|
||||
finally:
|
||||
GLib.idle_add(self._file_view.set_sensitive, True)
|
||||
|
||||
@run_task
|
||||
def open_ftp_file(self, f_path):
|
||||
is_darwin = self._settings.is_darwin
|
||||
GLib.idle_add(self._ftp_view.set_sensitive, False)
|
||||
|
||||
try:
|
||||
import tempfile
|
||||
import os
|
||||
path = os.path.expanduser("~/Desktop") if is_darwin else None
|
||||
path = os.path.expanduser("~/Desktop") if IS_WIN else None
|
||||
|
||||
with tempfile.NamedTemporaryFile(mode="wb", dir=path, delete=not is_darwin) as tf:
|
||||
with tempfile.NamedTemporaryFile(mode="wb", dir=path, delete=not IS_WIN) as tf:
|
||||
msg = "Downloading file: {}. Status: {}"
|
||||
try:
|
||||
status = self._ftp.retrbinary("RETR " + f_path, tf.write)
|
||||
@@ -257,8 +255,8 @@ class FtpClientBox(Gtk.HBox):
|
||||
self.update_ftp_info(msg.format(f_path, e))
|
||||
|
||||
tf.flush()
|
||||
cmd = ["open" if is_darwin else "xdg-open", tf.name]
|
||||
subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()
|
||||
cmd = ["start" if IS_WIN else "xdg-open", tf.name]
|
||||
subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=IS_WIN).communicate()
|
||||
finally:
|
||||
GLib.idle_add(self._ftp_view.set_sensitive, True)
|
||||
|
||||
@@ -429,11 +427,12 @@ class FtpClientBox(Gtk.HBox):
|
||||
def on_ftp_drag_data_get(self, view, context, data, info, time):
|
||||
model, paths = view.get_selection().get_selected_rows()
|
||||
if len(paths) > 0:
|
||||
sep = self.URI_SEP if self._settings.is_darwin else "\n"
|
||||
sep = self.URI_SEP if IS_WIN else "\n"
|
||||
uris = []
|
||||
for r in [model[p][:] for p in paths]:
|
||||
if r[self.Column.SIZE] != self.LINK and r[self.Column.NAME] != self.ROOT:
|
||||
uris.append(Path("/{}:{}".format(r[self.Column.NAME], r[self.Column.ATTR])).as_uri())
|
||||
path = Path("/{}:{}".format(r[self.Column.NAME], r[self.Column.ATTR]))
|
||||
uris.append(str(path.resolve()) if IS_WIN else path.as_uri())
|
||||
data.set_uris([sep.join(uris)])
|
||||
|
||||
@run_task
|
||||
@@ -446,12 +445,12 @@ class FtpClientBox(Gtk.HBox):
|
||||
GLib.idle_add(self._app._wait_dialog.show)
|
||||
|
||||
uris = data.get_uris()
|
||||
if self._settings.is_darwin and len(uris) == 1:
|
||||
if IS_WIN and len(uris) == 1:
|
||||
uris = uris[0].split(self.URI_SEP)
|
||||
|
||||
for uri in uris:
|
||||
uri = urlparse(unquote(uri)).path
|
||||
path = Path(uri)
|
||||
uri = urlparse(unquote(uri)).path.strip()
|
||||
path = Path(uri.lstrip("/") if IS_WIN else uri)
|
||||
if path.is_dir():
|
||||
try:
|
||||
self._ftp.mkd(path.name)
|
||||
@@ -473,18 +472,18 @@ class FtpClientBox(Gtk.HBox):
|
||||
def on_file_drag_data_get(self, view, context, data: Gtk.SelectionData, info, time):
|
||||
model, paths = view.get_selection().get_selected_rows()
|
||||
if len(paths) > 0:
|
||||
sep = self.URI_SEP if self._settings.is_darwin else "\n"
|
||||
sep = self.URI_SEP if IS_WIN else "\n"
|
||||
uris = [sep.join([Path(model[p][self.Column.ATTR]).as_uri() for p in paths])]
|
||||
data.set_uris(uris)
|
||||
|
||||
@run_task
|
||||
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) + "/"
|
||||
cur_path = self._file_model.get_value(self._file_model.get_iter_first(), self.Column.ATTR) + SEP
|
||||
try:
|
||||
GLib.idle_add(self._app._wait_dialog.show)
|
||||
|
||||
uris = data.get_uris()
|
||||
if self._settings.is_darwin and len(uris) == 1:
|
||||
if IS_WIN and len(uris) == 1:
|
||||
uris = uris[0].split(self.URI_SEP)
|
||||
|
||||
for uri in uris:
|
||||
|
||||
@@ -101,7 +101,6 @@ Author: Dmitriy Yefremov
|
||||
<property name="default_height">320</property>
|
||||
<property name="destroy_with_parent">True</property>
|
||||
<property name="type_hint">dialog</property>
|
||||
<property name="gravity">center</property>
|
||||
<signal name="check-resize" handler="on_resize" swapped="no"/>
|
||||
<child>
|
||||
<placeholder/>
|
||||
|
||||
@@ -6,7 +6,7 @@ from app.eparser import get_bouquets, get_services, BouquetsReader
|
||||
from app.eparser.ecommons import BqType, BqServiceType, Bouquet
|
||||
from app.eparser.neutrino.bouquets import parse_webtv, parse_bouquets as get_neutrino_bouquets
|
||||
from app.settings import SettingsType
|
||||
from app.ui.dialogs import show_dialog, DialogType, get_chooser_dialog, get_message
|
||||
from app.ui.dialogs import show_dialog, DialogType, get_chooser_dialog, get_message, get_builder
|
||||
from app.ui.main_helper import on_popup_menu
|
||||
from .uicommons import Gtk, UI_RESOURCES_PATH, KeyboardKey, Column
|
||||
|
||||
@@ -84,10 +84,7 @@ class ImportDialog:
|
||||
"on_resize": self.on_resize,
|
||||
"on_key_press": self.on_key_press}
|
||||
|
||||
builder = Gtk.Builder()
|
||||
builder.set_translation_domain("demon-editor")
|
||||
builder.add_from_file(UI_RESOURCES_PATH + "import_dialog.glade")
|
||||
builder.connect_signals(handlers)
|
||||
builder = get_builder(UI_RESOURCES_PATH + "import_dialog.glade", handlers)
|
||||
|
||||
self._bq_services = {}
|
||||
self._services = {}
|
||||
@@ -128,8 +125,15 @@ class ImportDialog:
|
||||
for bq in bqs.bouquets:
|
||||
self._main_model.append((bq.name, bq.type, True))
|
||||
self._bq_services[(bq.name, bq.type)] = bq.services
|
||||
# Note! Getting default format ver. 4
|
||||
services = get_services(path, self._profile, 4 if self._profile is SettingsType.ENIGMA_2 else 0)
|
||||
|
||||
if self._profile is SettingsType.ENIGMA_2:
|
||||
services = get_services(path, self._profile, 5 if self._settings.v5_support else 4)
|
||||
elif self._profile is SettingsType.NEUTRINO_MP:
|
||||
services = get_services(path, self._profile, 0)
|
||||
else:
|
||||
self.show_info_message("Setting format not supported!", Gtk.MessageType.ERROR)
|
||||
return
|
||||
|
||||
for srv in services:
|
||||
self._services[srv.fav_id] = srv
|
||||
except FileNotFoundError as e:
|
||||
|
||||
@@ -75,7 +75,6 @@ Author: Dmitriy Yefremov
|
||||
<property name="skip_taskbar_hint">True</property>
|
||||
<property name="skip_pager_hint">True</property>
|
||||
<property name="decorated">False</property>
|
||||
<property name="gravity">center</property>
|
||||
<signal name="response" handler="on_response" swapped="no"/>
|
||||
<child internal-child="vbox">
|
||||
<object class="GtkBox" id="search_unavailable_dialog_box">
|
||||
@@ -268,7 +267,6 @@ Author: Dmitriy Yefremov
|
||||
<property name="type_hint">dialog</property>
|
||||
<property name="skip_taskbar_hint">True</property>
|
||||
<property name="skip_pager_hint">True</property>
|
||||
<property name="gravity">center</property>
|
||||
<signal name="response" handler="on_response" swapped="no"/>
|
||||
<child internal-child="vbox">
|
||||
<object class="GtkBox" id="iptv_list_configuration_dialog_box">
|
||||
@@ -458,7 +456,7 @@ Author: Dmitriy Yefremov
|
||||
<property name="width_chars">5</property>
|
||||
<property name="max_width_chars">5</property>
|
||||
<property name="text">0</property>
|
||||
<property name="primary_icon_name">document-edit-symbolic</property>
|
||||
|
||||
<signal name="changed" handler="on_entry_changed" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
@@ -501,7 +499,7 @@ Author: Dmitriy Yefremov
|
||||
<property name="width_chars">5</property>
|
||||
<property name="max_width_chars">5</property>
|
||||
<property name="text">0</property>
|
||||
<property name="primary_icon_name">document-edit-symbolic</property>
|
||||
|
||||
<signal name="changed" handler="on_entry_changed" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
@@ -544,7 +542,7 @@ Author: Dmitriy Yefremov
|
||||
<property name="width_chars">5</property>
|
||||
<property name="max_width_chars">5</property>
|
||||
<property name="text">0</property>
|
||||
<property name="primary_icon_name">document-edit-symbolic</property>
|
||||
|
||||
<signal name="changed" handler="on_entry_changed" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
@@ -587,7 +585,7 @@ Author: Dmitriy Yefremov
|
||||
<property name="width_chars">5</property>
|
||||
<property name="max_width_chars">5</property>
|
||||
<property name="text">0</property>
|
||||
<property name="primary_icon_name">document-edit-symbolic</property>
|
||||
|
||||
<signal name="changed" handler="on_entry_changed" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
@@ -630,7 +628,7 @@ Author: Dmitriy Yefremov
|
||||
<property name="width_chars">5</property>
|
||||
<property name="max_width_chars">5</property>
|
||||
<property name="text">1</property>
|
||||
<property name="primary_icon_name">document-edit-symbolic</property>
|
||||
|
||||
<signal name="changed" handler="on_entry_changed" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
@@ -919,7 +917,7 @@ Author: Dmitriy Yefremov
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="hexpand">True</property>
|
||||
<property name="primary_icon_name">document-edit-symbolic</property>
|
||||
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
@@ -942,7 +940,7 @@ Author: Dmitriy Yefremov
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="hexpand">True</property>
|
||||
<property name="primary_icon_name">document-edit-symbolic</property>
|
||||
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
@@ -1042,7 +1040,7 @@ Author: Dmitriy Yefremov
|
||||
<property name="margin_left">5</property>
|
||||
<property name="margin_right">5</property>
|
||||
<property name="margin_bottom">5</property>
|
||||
<property name="primary_icon_name">document-edit-symbolic</property>
|
||||
|
||||
<property name="secondary_icon_tooltip_text" translatable="yes">Link to YouTube resource.</property>
|
||||
<signal name="changed" handler="on_url_changed" swapped="no"/>
|
||||
</object>
|
||||
@@ -1169,7 +1167,7 @@ Author: Dmitriy Yefremov
|
||||
<property name="width_chars">5</property>
|
||||
<property name="max_width_chars">5</property>
|
||||
<property name="text">1</property>
|
||||
<property name="primary_icon_name">document-edit-symbolic</property>
|
||||
|
||||
<signal name="changed" handler="on_entry_changed" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
@@ -1184,7 +1182,7 @@ Author: Dmitriy Yefremov
|
||||
<property name="width_chars">5</property>
|
||||
<property name="max_width_chars">5</property>
|
||||
<property name="text">0</property>
|
||||
<property name="primary_icon_name">document-edit-symbolic</property>
|
||||
|
||||
<signal name="changed" handler="on_entry_changed" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
@@ -1199,7 +1197,7 @@ Author: Dmitriy Yefremov
|
||||
<property name="width_chars">5</property>
|
||||
<property name="max_width_chars">5</property>
|
||||
<property name="text">0</property>
|
||||
<property name="primary_icon_name">document-edit-symbolic</property>
|
||||
|
||||
<signal name="changed" handler="on_entry_changed" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
@@ -1214,7 +1212,7 @@ Author: Dmitriy Yefremov
|
||||
<property name="width_chars">5</property>
|
||||
<property name="max_width_chars">5</property>
|
||||
<property name="text">0</property>
|
||||
<property name="primary_icon_name">document-edit-symbolic</property>
|
||||
|
||||
<signal name="changed" handler="on_entry_changed" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
@@ -1229,7 +1227,7 @@ Author: Dmitriy Yefremov
|
||||
<property name="width_chars">5</property>
|
||||
<property name="max_width_chars">5</property>
|
||||
<property name="text">0</property>
|
||||
<property name="primary_icon_name">document-edit-symbolic</property>
|
||||
|
||||
<signal name="changed" handler="on_entry_changed" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
@@ -1337,7 +1335,6 @@ Author: Dmitriy Yefremov
|
||||
<property name="destroy_with_parent">True</property>
|
||||
<property name="skip_taskbar_hint">True</property>
|
||||
<property name="skip_pager_hint">True</property>
|
||||
<property name="gravity">center</property>
|
||||
<child type="titlebar">
|
||||
<placeholder/>
|
||||
</child>
|
||||
|
||||
@@ -15,10 +15,9 @@ from app.eparser.iptv import (NEUTRINO_FAV_ID_FORMAT, StreamType, ENIGMA2_FAV_ID
|
||||
parse_m3u)
|
||||
from app.settings import SettingsType
|
||||
from app.tools.yt import YouTubeException, YouTube
|
||||
from app.ui.dialogs import Action, show_dialog, DialogType, get_dialogs_string, get_message
|
||||
from app.ui.dialogs import Action, show_dialog, DialogType, get_message, get_builder
|
||||
from app.ui.main_helper import get_base_model, get_iptv_url, on_popup_menu, get_picon_pixbuf
|
||||
from app.ui.uicommons import (Gtk, Gdk, TEXT_DOMAIN, UI_RESOURCES_PATH, IPTV_ICON, Column, IS_GNOME_SESSION,
|
||||
KeyboardKey, get_yt_icon)
|
||||
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"
|
||||
@@ -67,11 +66,8 @@ class IptvDialog:
|
||||
self._yt_links = None
|
||||
self._yt_dl = None
|
||||
|
||||
builder = Gtk.Builder()
|
||||
builder.set_translation_domain(TEXT_DOMAIN)
|
||||
builder.add_objects_from_string(get_dialogs_string(_UI_PATH).format(use_header=IS_GNOME_SESSION),
|
||||
("iptv_dialog", "stream_type_liststore", "yt_quality_liststore"))
|
||||
builder.connect_signals(handlers)
|
||||
builder = get_builder(_UI_PATH, handlers, use_str=True,
|
||||
objects=("iptv_dialog", "stream_type_liststore", "yt_quality_liststore"))
|
||||
|
||||
self._dialog = builder.get_object("iptv_dialog")
|
||||
self._dialog.set_transient_for(transient)
|
||||
@@ -298,14 +294,14 @@ class IptvDialog:
|
||||
self._bouquet[self._paths[0][0]] = fav_id
|
||||
self._model.set(self._model.get_iter(self._paths), {Column.FAV_SERVICE: name, Column.FAV_ID: fav_id})
|
||||
else:
|
||||
aggr = [None] * 10
|
||||
s_type = BqServiceType.IPTV.name
|
||||
srv = (None, None, name, None, None, s_type, None, fav_id, *aggr[0:3])
|
||||
srv = (None, None, name, None, None, s_type, None, fav_id, None, None, None)
|
||||
itr = self._model.insert_after(self._model.get_iter(self._paths[0]),
|
||||
srv) if self._paths else self._model.insert(0, srv)
|
||||
self._model.set_value(itr, 1, IPTV_ICON)
|
||||
self._bouquet.insert(self._model.get_path(itr)[0], fav_id)
|
||||
self._services[fav_id] = Service(None, None, IPTV_ICON, name, *aggr[0:3], s_type, *aggr, fav_id, None)
|
||||
self._services[fav_id] = Service(None, None, IPTV_ICON, name, None, None, None, s_type, None,
|
||||
None, None, None, None, None, None, None, None, None, fav_id, None)
|
||||
|
||||
@run_idle
|
||||
def on_info_bar_close(self, bar=None, resp=None):
|
||||
@@ -323,10 +319,8 @@ class SearchUnavailableDialog:
|
||||
def __init__(self, transient, model, fav_bouquet, iptv_rows, s_type):
|
||||
handlers = {"on_response": self.on_response}
|
||||
|
||||
builder = Gtk.Builder()
|
||||
builder.set_translation_domain(TEXT_DOMAIN)
|
||||
builder.add_objects_from_file(UI_RESOURCES_PATH + "iptv.glade", ("search_unavailable_streams_dialog",))
|
||||
builder.connect_signals(handlers)
|
||||
builder = get_builder(UI_RESOURCES_PATH + "iptv.glade", handlers,
|
||||
objects=("search_unavailable_streams_dialog",))
|
||||
|
||||
self._dialog = builder.get_object("search_unavailable_streams_dialog")
|
||||
self._dialog.set_transient_for(transient)
|
||||
@@ -421,11 +415,8 @@ class IptvListDialog:
|
||||
|
||||
self._s_type = s_type
|
||||
|
||||
builder = Gtk.Builder()
|
||||
builder.set_translation_domain(TEXT_DOMAIN)
|
||||
builder.add_objects_from_string(get_dialogs_string(_UI_PATH).format(use_header=IS_GNOME_SESSION),
|
||||
("iptv_list_configuration_dialog", "stream_type_liststore"))
|
||||
builder.connect_signals(handlers)
|
||||
builder = get_builder(_UI_PATH, handlers, use_str=True,
|
||||
objects=("iptv_list_configuration_dialog", "stream_type_liststore"))
|
||||
|
||||
self._dialog = builder.get_object("iptv_list_configuration_dialog")
|
||||
self._dialog.set_transient_for(transient)
|
||||
@@ -756,7 +747,7 @@ 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(get_message("Done!"), self._errors_count))
|
||||
self._info_label.set_text("Errors: {}.".format(self._errors_count))
|
||||
self._is_download = False
|
||||
|
||||
gen = self.update_fav_model()
|
||||
@@ -813,13 +804,10 @@ class YtListImportDialog:
|
||||
self._settings = settings
|
||||
self._yt = None
|
||||
|
||||
builder = Gtk.Builder()
|
||||
builder.set_translation_domain(TEXT_DOMAIN)
|
||||
builder.add_objects_from_string(get_dialogs_string(_UI_PATH).format(use_header=IS_GNOME_SESSION),
|
||||
("yt_import_dialog_window", "yt_liststore", "yt_quality_liststore",
|
||||
"yt_popup_menu", "remove_selection_image", "yt_receive_image",
|
||||
"yt_import_image"))
|
||||
builder.connect_signals(handlers)
|
||||
builder = get_builder(_UI_PATH, handlers, use_str=True,
|
||||
objects=("yt_import_dialog_window", "yt_liststore", "yt_quality_liststore",
|
||||
"yt_popup_menu", "remove_selection_image", "yt_receive_image",
|
||||
"yt_import_image"))
|
||||
|
||||
self._dialog = builder.get_object("yt_import_dialog_window")
|
||||
self._dialog.set_transient_for(transient)
|
||||
@@ -920,13 +908,13 @@ class YtListImportDialog:
|
||||
|
||||
@run_idle
|
||||
def append_services(self, links):
|
||||
aggr = [None] * 9
|
||||
srvs = []
|
||||
|
||||
if self._yt_list_title:
|
||||
title = self._yt_list_title
|
||||
fav_id = MARKER_FORMAT.format(0, title, title)
|
||||
mk = Service(None, None, None, title, *aggr[0:3], BqServiceType.MARKER.name, *aggr, 0, fav_id, None)
|
||||
mk = Service(None, None, None, title, None, None, None, BqServiceType.MARKER.name, None,
|
||||
None, None, None, None, None, None, None, None, 0, fav_id, None)
|
||||
srvs.append(mk)
|
||||
|
||||
act = self._quality_model.get_value(self._quality_box.get_active_iter(), 0)
|
||||
@@ -936,7 +924,8 @@ class YtListImportDialog:
|
||||
continue
|
||||
ln = lnk.get(act) if act in lnk else lnk[sorted(lnk, key=lambda x: int(x.rstrip("p")), reverse=True)[0]]
|
||||
fav_id = get_fav_id(ln, title, self._s_type)
|
||||
srv = Service(None, None, IPTV_ICON, title, *aggr[0:3], BqServiceType.IPTV.name, *aggr, None, fav_id, None)
|
||||
srv = Service(None, None, IPTV_ICON, title, None, None, None, BqServiceType.IPTV.name, None, None, None,
|
||||
None, None, None, None, None, None, None, fav_id, None)
|
||||
srvs.append(srv)
|
||||
self.appender(srvs)
|
||||
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -17,12 +17,12 @@ from app.eparser.ecommons import CAS, Flag, BouquetService
|
||||
from app.eparser.enigma.bouquets import BqServiceType
|
||||
from app.eparser.iptv import export_to_m3u
|
||||
from app.eparser.neutrino.bouquets import BqType
|
||||
from app.settings import SettingsType, Settings, SettingsException, PlayStreamsMode, SettingsReadException
|
||||
from app.settings import SettingsType, Settings, SettingsException, PlayStreamsMode, SettingsReadException, IS_WIN
|
||||
from app.tools.media import Player, Recorder
|
||||
from app.ui.epg_dialog import EpgDialog
|
||||
from app.ui.transmitter import LinksTransmitter
|
||||
from .backup import BackupDialog, backup_data, clear_data_path
|
||||
from .dialogs import show_dialog, DialogType, get_chooser_dialog, WaitDialog, get_message
|
||||
from .dialogs import show_dialog, DialogType, get_chooser_dialog, WaitDialog, get_message, get_builder
|
||||
from .download_dialog import DownloadDialog
|
||||
from .imports import ImportDialog, import_bouquet
|
||||
from .iptv import IptvDialog, SearchUnavailableDialog, IptvListConfigurationDialog, YtListImportDialog, M3uImportDialog
|
||||
@@ -36,7 +36,7 @@ from .search import SearchProvider
|
||||
from .service_details_dialog import ServiceDetailsDialog, Action
|
||||
from .settings_dialog import show_settings_dialog
|
||||
from .uicommons import (Gtk, Gdk, UI_RESOURCES_PATH, LOCKED_ICON, HIDE_ICON, IPTV_ICON, MOVE_KEYS, KeyboardKey, Column,
|
||||
FavClickMode, MOD_MASK)
|
||||
FavClickMode, MOD_MASK, TEXT_DOMAIN, APP_FONT)
|
||||
|
||||
|
||||
class Application(Gtk.Application):
|
||||
@@ -58,8 +58,8 @@ class Application(Gtk.Application):
|
||||
|
||||
_FAV_ELEMENTS = ("fav_cut_popup_item", "fav_paste_popup_item", "fav_locate_popup_item", "fav_iptv_popup_item",
|
||||
"fav_insert_marker_popup_item", "fav_insert_space_popup_item", "fav_edit_sub_menu_popup_item",
|
||||
"fav_edit_popup_item", "fav_picon_popup_item", "fav_copy_popup_item",
|
||||
"fav_epg_configuration_popup_item", "fav_add_alt_popup_item")
|
||||
"fav_edit_popup_item", "fav_picon_popup_item", "fav_copy_popup_item", "fav_add_alt_popup_item",
|
||||
"fav_epg_configuration_popup_item", "fav_mark_dup_popup_item")
|
||||
|
||||
_BOUQUET_ELEMENTS = ("bouquets_new_popup_item", "bouquets_edit_popup_item", "bouquets_cut_popup_item",
|
||||
"bouquets_copy_popup_item", "bouquets_paste_popup_item", "bouquet_import_popup_item",
|
||||
@@ -135,8 +135,11 @@ class Application(Gtk.Application):
|
||||
"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_picons_manager_show": self.on_picons_manager_show,
|
||||
"on_filter_changed": self.on_filter_changed,
|
||||
"on_filter_type_toggled": self.on_filter_type_toggled,
|
||||
"on_filter_satellite_toggled": self.on_filter_satellite_toggled,
|
||||
"on_assign_picon": self.on_assign_picon,
|
||||
"on_remove_picon": self.on_remove_picon,
|
||||
"on_reference_picon": self.on_reference_picon,
|
||||
@@ -157,8 +160,8 @@ class Application(Gtk.Application):
|
||||
"on_player_close": self.on_player_close,
|
||||
"on_player_press": self.on_player_press,
|
||||
"on_full_screen": self.on_full_screen,
|
||||
"on_drawing_area_realize": self.on_drawing_area_realize,
|
||||
"on_player_drawing_area_draw": self.on_player_drawing_area_draw,
|
||||
"on_player_box_realize": self.on_player_box_realize,
|
||||
"on_player_box_visibility": self.on_player_box_visibility,
|
||||
"on_ftp_realize": self.on_ftp_realize,
|
||||
"on_record": self.on_record,
|
||||
"on_remove_all_unavailable": self.on_remove_all_unavailable,
|
||||
@@ -192,8 +195,9 @@ class Application(Gtk.Application):
|
||||
self._bq_selected = "" # Current selected bouquet
|
||||
self._select_enabled = True # Multiple selection
|
||||
# Current satellite positions in the services list
|
||||
self._sat_positions = []
|
||||
self._marker_types = {BqServiceType.MARKER.name, BqServiceType.SPACE.name}
|
||||
self._sat_positions = set()
|
||||
self._service_types = set()
|
||||
self._marker_types = {BqServiceType.MARKER.name, BqServiceType.SPACE.name, BqServiceType.ALT.name}
|
||||
# Player
|
||||
self._player = None
|
||||
self._full_screen = False
|
||||
@@ -207,14 +211,14 @@ class Application(Gtk.Application):
|
||||
self._links_transmitter = None
|
||||
self._control_box = None
|
||||
self._ftp_client = None
|
||||
# Colors
|
||||
# Appearance
|
||||
self._current_font = APP_FONT
|
||||
self._picons_size = self._settings.list_picon_size
|
||||
self._use_colors = False
|
||||
self._NEW_COLOR = None # Color for new services in the main list
|
||||
self._EXTRA_COLOR = None # Color for services with a extra name for the bouquet
|
||||
|
||||
builder = Gtk.Builder()
|
||||
builder.add_from_file(UI_RESOURCES_PATH + "main_window.glade")
|
||||
builder.connect_signals(self._handlers)
|
||||
builder = get_builder(UI_RESOURCES_PATH + "main_window.glade", self._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
|
||||
@@ -229,21 +233,28 @@ class Application(Gtk.Application):
|
||||
self._main_data_box = builder.get_object("main_data_box")
|
||||
self._status_bar_box = builder.get_object("status_bar_box")
|
||||
self._services_main_box = builder.get_object("services_main_box")
|
||||
self._bouquets_main_box = builder.get_object("bouquets_main_box")
|
||||
self._header_bar = builder.get_object("header_bar")
|
||||
self._bq_name_label = builder.get_object("bq_name_label")
|
||||
tool_bar = builder.get_object("top_toolbar")
|
||||
self._main_data_box.bind_property("visible", tool_bar, "visible")
|
||||
self._main_data_box.bind_property("visible", builder.get_object("top_toolbar"), "visible")
|
||||
self._telnet_tool_button = builder.get_object("telnet_tool_button")
|
||||
self._top_box = builder.get_object("top_box")
|
||||
self._toolbar_extra_tools_box = builder.get_object("toolbar_extra_tools_box")
|
||||
self._add_bouquet_button = builder.get_object("add_bouquet_tool_button")
|
||||
# Setting custom sort function for position column.
|
||||
self._services_view.get_model().set_sort_func(Column.SRV_POS, self.position_sort_func, Column.SRV_POS)
|
||||
# Tool bar elements.
|
||||
self._main_box = builder.get_object("main_box")
|
||||
self._toolbar_search_box = builder.get_object("toolbar_search_box")
|
||||
toolbar_tools_box = builder.get_object("toolbar_tools_box")
|
||||
self._toolbar_search_box.bind_property("visible", toolbar_tools_box, "visible")
|
||||
self._toolbar_search_box.bind_property("visible", self._toolbar_extra_tools_box, "visible")
|
||||
# App info
|
||||
self._app_info_box = builder.get_object("app_info_box")
|
||||
self._app_info_box.bind_property("visible", builder.get_object("main_paned"), "visible", 4)
|
||||
self._app_info_box.bind_property("visible", builder.get_object("toolbar_extra_box"), "visible", 4)
|
||||
self._app_info_box.bind_property("visible", builder.get_object("toolbar_tools_box"), "visible", 4)
|
||||
self._app_info_box.bind_property("visible", self._toolbar_search_box, "visible", 4)
|
||||
self._app_info_box.bind_property("visible", self._toolbar_extra_tools_box, "visible", 4)
|
||||
self._app_info_box.bind_property("visible", toolbar_tools_box, "visible", 4)
|
||||
self._app_info_box.bind_property("visible", builder.get_object("save_tool_button"), "visible", 4)
|
||||
self._app_info_box.bind_property("visible", builder.get_object("add_bouquet_tool_button"), "visible", 4)
|
||||
self._app_info_box.bind_property("visible", self._add_bouquet_button, "visible", 4)
|
||||
# Status bar
|
||||
self._profile_combo_box = builder.get_object("profile_combo_box")
|
||||
self._receiver_info_box = builder.get_object("receiver_info_box")
|
||||
@@ -277,6 +288,9 @@ class Application(Gtk.Application):
|
||||
self._ftp_button = builder.get_object("ftp_button")
|
||||
self._ftp_revealer = builder.get_object("ftp_revealer")
|
||||
self._ftp_button.bind_property("active", self._ftp_revealer, "visible")
|
||||
self._ftp_revealer.bind_property("visible", self._main_box, "visible", 4)
|
||||
self._ftp_revealer.bind_property("visible", builder.get_object("toolbar_main_box"), "visible", 4)
|
||||
self._ftp_button.connect("toggled", self.on_ftp_toggle)
|
||||
# Force Ctrl press event for view. Multiple selections in lists only with Space key(as in file managers)!!!
|
||||
self._services_view.connect("key-press-event", self.force_ctrl)
|
||||
self._fav_view.connect("key-press-event", self.force_ctrl)
|
||||
@@ -288,39 +302,32 @@ class Application(Gtk.Application):
|
||||
self._services_model_filter = builder.get_object("services_model_filter")
|
||||
self._services_model_filter.set_visible_func(self.services_filter_function)
|
||||
self._filter_entry = builder.get_object("filter_entry")
|
||||
self._filter_bar = builder.get_object("filter_bar")
|
||||
self._filter_types_box = builder.get_object("filter_types_box")
|
||||
self._filter_sat_positions_box = builder.get_object("filter_sat_positions_box")
|
||||
self._filter_box = builder.get_object("filter_box")
|
||||
self._filter_types_model = builder.get_object("filter_types_list_store")
|
||||
self._filter_sat_positions_model = builder.get_object("filter_sat_positions_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_bar.bind_property("search-mode-enabled", self._filter_bar, "visible")
|
||||
# Player
|
||||
self._player_box = builder.get_object("player_box")
|
||||
self._player_event_box = builder.get_object("player_event_box")
|
||||
self._player_scale = builder.get_object("player_scale")
|
||||
self._player_full_time_label = builder.get_object("player_full_time_label")
|
||||
self._player_current_time_label = builder.get_object("player_current_time_label")
|
||||
self._player_rewind_box = builder.get_object("player_rewind_box")
|
||||
self._player_drawing_area = builder.get_object("player_drawing_area")
|
||||
self._player_tool_bar = builder.get_object("player_tool_bar")
|
||||
self._player_prev_button = builder.get_object("player_prev_button")
|
||||
self._player_next_button = builder.get_object("player_next_button")
|
||||
self._player_box.bind_property("visible", tool_bar, "visible", 4)
|
||||
self._player_box.bind_property("visible", self._top_box, "visible", 4)
|
||||
self._player_box.bind_property("visible", self._services_main_box, "visible", 4)
|
||||
self._player_box.bind_property("visible", self._bouquets_main_box, "visible", 4)
|
||||
self._fav_bouquets_paned = builder.get_object("fav_bouquets_paned")
|
||||
self._player_box.bind_property("visible", builder.get_object("fav_pos_column"), "visible", 4)
|
||||
self._player_box.bind_property("visible", builder.get_object("fav_pos_column"), "visible", 4)
|
||||
self._player_box.bind_property("visible", self._profile_combo_box, "sensitive", 4)
|
||||
self._player_box.bind_property("visible", self._player_event_box, "visible")
|
||||
self._fav_view.bind_property("sensitive", self._player_prev_button, "sensitive")
|
||||
self._fav_view.bind_property("sensitive", self._player_next_button, "sensitive")
|
||||
# Record
|
||||
self._record_image = builder.get_object("record_button_image")
|
||||
# Enabling events for the drawing area
|
||||
self._player_drawing_area.set_events(Gdk.ModifierType.BUTTON1_MASK)
|
||||
self._player_frame = builder.get_object("player_frame")
|
||||
# Search
|
||||
self._search_bar = builder.get_object("search_bar")
|
||||
self._search_bar.bind_property("search-mode-enabled", self._search_bar, "visible")
|
||||
self._search_box = builder.get_object("search_box")
|
||||
self._search_entry = builder.get_object("search_entry")
|
||||
self._search_provider = SearchProvider((self._services_view, self._fav_view, self._bouquets_view),
|
||||
builder.get_object("search_down_button"),
|
||||
@@ -334,78 +341,29 @@ class Application(Gtk.Application):
|
||||
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,
|
||||
Gtk.STYLE_PROVIDER_PRIORITY_USER)
|
||||
# Layout
|
||||
if self._settings.is_darwin and self._settings.alternate_layout:
|
||||
self._main_paned = builder.get_object("main_data_paned")
|
||||
self._fav_paned = builder.get_object("fav_bouquets_paned")
|
||||
self._fav_box = self._fav_paned.get_child1()
|
||||
self._bouquets_box = self._fav_paned.get_child2()
|
||||
self._left_ar_bq_button = builder.get_object("left_arrow_bq_button")
|
||||
self._left_ar_bq_button.bind_property("visible", builder.get_object("right_arrow_bq_button"), "visible", 4)
|
||||
self._left_ar_bq_button.set_visible(True)
|
||||
self.init_layout(builder)
|
||||
|
||||
def init_layout(self, builder):
|
||||
""" Initializes an alternate layout, if enabled. """
|
||||
top_box = builder.get_object("top_box")
|
||||
top_toolbar = builder.get_object("top_toolbar")
|
||||
top_toolbar.set_margin_left(0)
|
||||
top_toolbar.set_margin_right(10)
|
||||
|
||||
extra_box = builder.get_object("toolbar_extra_tools_box")
|
||||
extra_box.set_margin_left(10)
|
||||
extra_box.set_margin_right(0)
|
||||
extra_box.reorder_child(self._ftp_button, 0)
|
||||
extra_box.reorder_child(builder.get_object("add_bouquet_tool_button"), 2)
|
||||
|
||||
top_box.set_child_packing(extra_box, False, True, 0, Gtk.PackType.START)
|
||||
top_box.set_child_packing(top_toolbar, False, True, 0, Gtk.PackType.END)
|
||||
top_box.reorder_child(extra_box, 0)
|
||||
top_box.reorder_child(top_toolbar, 1)
|
||||
|
||||
center_box = builder.get_object("center_box")
|
||||
center_box.reorder_child(self._ftp_revealer, 0)
|
||||
center_box.reorder_child(self._control_revealer, 1)
|
||||
center_box.reorder_child(builder.get_object("main_box"), 2)
|
||||
|
||||
services_box = self._main_paned.get_child1()
|
||||
self._main_paned.remove(services_box)
|
||||
self._main_paned.remove(self._fav_paned)
|
||||
self._main_paned.pack1(self._fav_paned, True, True)
|
||||
self._main_paned.pack2(services_box, True, True)
|
||||
|
||||
self._left_ar_bq_button.set_visible(not self._settings.bq_details_first)
|
||||
self.init_bq_position()
|
||||
|
||||
def init_bq_position(self):
|
||||
self._fav_paned.remove(self._fav_box)
|
||||
self._fav_paned.remove(self._bouquets_box)
|
||||
|
||||
if self._settings.bq_details_first:
|
||||
self._fav_paned.pack1(self._fav_box, False, False)
|
||||
self._fav_paned.pack2(self._bouquets_box, False, False)
|
||||
else:
|
||||
self._fav_paned.pack1(self._bouquets_box, False, False)
|
||||
self._fav_paned.pack2(self._fav_box, False, False)
|
||||
# Menu bar
|
||||
main_box = builder.get_object("main_window_box")
|
||||
builder.set_translation_domain(TEXT_DOMAIN)
|
||||
builder.add_from_file(UI_RESOURCES_PATH + "app_menu_bar.ui")
|
||||
menu_bar = Gtk.MenuBar.new_from_model(builder.get_object("menu_bar"))
|
||||
menu_bar.set_visible(True)
|
||||
main_box.pack_start(menu_bar, False, False, 0)
|
||||
main_box.reorder_child(menu_bar, 0)
|
||||
self._main_data_box.bind_property("visible", menu_bar, "visible")
|
||||
self._player_box.bind_property("visible", menu_bar, "sensitive", 4)
|
||||
if self._settings.get("telnet"):
|
||||
self.init_telnet(builder)
|
||||
|
||||
def do_startup(self):
|
||||
Gtk.Application.do_startup(self)
|
||||
|
||||
self.init_keys()
|
||||
self.set_accels()
|
||||
|
||||
builder = Gtk.Builder()
|
||||
builder.set_translation_domain("demon-editor")
|
||||
builder.add_from_file(UI_RESOURCES_PATH + "app_menu_bar.ui")
|
||||
self.set_menubar(builder.get_object("menu_bar"))
|
||||
self.set_app_menu(builder.get_object("app-menu"))
|
||||
|
||||
if self._settings.get("telnet"):
|
||||
self.init_telnet(builder)
|
||||
|
||||
self.update_profile_label()
|
||||
self.init_drag_and_drop()
|
||||
self.init_colors()
|
||||
self.init_appearance()
|
||||
self.filter_set_default()
|
||||
|
||||
if self._settings.load_last_config:
|
||||
config = self._settings.get("last_config") or {}
|
||||
@@ -472,6 +430,16 @@ class Application(Gtk.Application):
|
||||
remote_action = Gio.SimpleAction.new_stateful("on_remote", None, GLib.Variant.new_boolean(False))
|
||||
remote_action.connect("change-state", self.on_control)
|
||||
self.add_action(remote_action)
|
||||
# FTP client. Hiding the app menu bar when the client is shown.
|
||||
# We are working with the "hidden-when" submenu attribute. See 'app_menu_bar.ui' file.
|
||||
hide_bar_action = Gio.SimpleAction.new("hide_menu_bar", None)
|
||||
self._ftp_revealer.bind_property("visible", hide_bar_action, "enabled", 4)
|
||||
self.add_action(hide_bar_action)
|
||||
show_ftp_menu_action = Gio.SimpleAction.new("show_ftp_menu", None)
|
||||
show_ftp_menu_action.set_enabled(False)
|
||||
self._ftp_revealer.bind_property("visible", show_ftp_menu_action, "enabled")
|
||||
self.add_action(show_ftp_menu_action)
|
||||
self.set_action("on_ftp_client_close", lambda a, v: self._ftp_button.set_active(False))
|
||||
# Layout
|
||||
self.set_action("on_switch_fav_position", self.on_switch_fav_position)
|
||||
|
||||
@@ -607,11 +575,21 @@ class Application(Gtk.Application):
|
||||
self._fav_view.get_selection().set_select_function(lambda *args: self._select_enabled)
|
||||
self._bouquets_view.get_selection().set_select_function(lambda *args: self._select_enabled)
|
||||
|
||||
def init_colors(self, update=False):
|
||||
""" Initialisation of background colors for the services.
|
||||
def init_appearance(self, update=False):
|
||||
""" Appearance initialisation.
|
||||
|
||||
If update=False - first call on program start, else - after options changes!
|
||||
"""
|
||||
if self._current_font != self._settings.list_font:
|
||||
from gi.repository import Pango
|
||||
|
||||
font_desc = Pango.FontDescription.from_string(self._settings.list_font)
|
||||
list(map(lambda v: v.modify_font(font_desc), (self._services_view, self._fav_view, self._bouquets_view)))
|
||||
self._current_font = self._settings.list_font
|
||||
|
||||
if self._picons_size != self._settings.list_picon_size:
|
||||
self.update_picons_size()
|
||||
|
||||
if self._s_type is SettingsType.ENIGMA_2:
|
||||
self._use_colors = self._settings.use_colors
|
||||
|
||||
@@ -627,6 +605,15 @@ class Application(Gtk.Application):
|
||||
self._NEW_COLOR = new_rgb
|
||||
self._EXTRA_COLOR = extra_rgb
|
||||
|
||||
@run_idle
|
||||
def update_picons_size(self):
|
||||
self._picons_size = self._settings.list_picon_size
|
||||
update_picons_data(self._settings.picons_local_path, self._picons, self._picons_size)
|
||||
self._fav_model.foreach(lambda m, p, itr: m.set_value(itr, Column.FAV_PICON, self._picons.get(
|
||||
self._services.get(m.get_value(itr, Column.FAV_ID)).picon_id, None)))
|
||||
self._services_model.foreach(lambda m, p, itr: m.set_value(itr, Column.SRV_PICON, self._picons.get(
|
||||
m.get_value(itr, Column.SRV_PICON_ID), None)))
|
||||
|
||||
def update_background_colors(self, new_color, extra_color):
|
||||
if extra_color != self._EXTRA_COLOR:
|
||||
for row in self._fav_model:
|
||||
@@ -651,7 +638,9 @@ class Application(Gtk.Application):
|
||||
def on_close_app(self, *args):
|
||||
""" Performing operations before closing the application. """
|
||||
# Saving the current size of the application window.
|
||||
self._settings.add("window_size", self._main_window.get_size())
|
||||
self._main_window.unfullscreen()
|
||||
if not self._main_window.is_maximized():
|
||||
self._settings.add("window_size", self._main_window.get_size())
|
||||
|
||||
if self._recorder:
|
||||
if self._recorder.is_record():
|
||||
@@ -1099,7 +1088,8 @@ class Application(Gtk.Application):
|
||||
target_column = Column.FAV_ID if target is ViewTarget.FAV else Column.SRV_FAV_ID
|
||||
srv = self._services.get(model[path][target_column], None)
|
||||
if srv and srv.picon_id:
|
||||
tooltip.set_icon(get_picon_pixbuf(self._settings.picons_local_path + srv.picon_id, size=96))
|
||||
tooltip.set_icon(get_picon_pixbuf(self._settings.picons_local_path + srv.picon_id,
|
||||
size=self._settings.tooltip_logo_size))
|
||||
tooltip.set_text(
|
||||
self.get_hint_for_bq_list(srv) if target is ViewTarget.FAV else self.get_hint_for_srv_list(srv))
|
||||
view.set_tooltip_row(tooltip, path)
|
||||
@@ -1252,15 +1242,18 @@ class Application(Gtk.Application):
|
||||
|
||||
if txt:
|
||||
if txt.startswith("file://") and name == self.SERVICE_MODEL_NAME:
|
||||
self.on_import_data(urlparse(unquote(txt)).path.strip())
|
||||
path = urlparse(unquote(txt)).path
|
||||
self.on_import_data(path.lstrip("/") if IS_WIN else 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)
|
||||
|
||||
if uris:
|
||||
src, sep, dest = uris[0].partition("::::")
|
||||
src_path = urlparse(unquote(src)).path
|
||||
src_path = src_path.lstrip("/") if IS_WIN else src_path.strip()
|
||||
if dest:
|
||||
dest_path = urlparse(unquote(dest)).path + "/"
|
||||
dest_path = urlparse(unquote(dest)).path
|
||||
dest_path = dest_path.lstrip("/") + "/" if IS_WIN else dest_path.strip()
|
||||
self.picons_buffer = self.on_assign_picon(view, src_path, dest_path)
|
||||
elif name == self.SERVICE_MODEL_NAME:
|
||||
self.on_import_data(src_path)
|
||||
@@ -1272,7 +1265,9 @@ class Application(Gtk.Application):
|
||||
|
||||
uris = data.get_uris()
|
||||
if uris:
|
||||
self.on_import_bouquet(None, file_path=urlparse(unquote(uris[0])).path.strip())
|
||||
path = urlparse(unquote(uris[0])).path
|
||||
path = path.lstrip("/") if IS_WIN else path.strip()
|
||||
self.on_import_bouquet(None, file_path=path)
|
||||
return
|
||||
|
||||
data = data.get_text()
|
||||
@@ -1280,7 +1275,9 @@ class Application(Gtk.Application):
|
||||
return
|
||||
|
||||
if data.startswith("file://"):
|
||||
self.on_import_bouquet(None, file_path=urlparse(unquote(data)).path.strip())
|
||||
path = urlparse(unquote(data)).path
|
||||
path = path.lstrip("/") if IS_WIN else path.strip()
|
||||
self.on_import_bouquet(None, file_path=path)
|
||||
return
|
||||
|
||||
itr_str, sep, source = data.partition(self.DRAG_SEP)
|
||||
@@ -1483,8 +1480,12 @@ class Application(Gtk.Application):
|
||||
""" Opening the data archive via "File/Open archive". """
|
||||
file_filter = Gtk.FileFilter()
|
||||
file_filter.set_name("*.zip, *.gz")
|
||||
file_filter.add_mime_type("application/zip")
|
||||
file_filter.add_mime_type("application/gzip")
|
||||
if IS_WIN:
|
||||
file_filter.add_pattern("*.zip")
|
||||
file_filter.add_pattern("*.gz")
|
||||
else:
|
||||
file_filter.add_mime_type("application/zip")
|
||||
file_filter.add_mime_type("application/gzip")
|
||||
|
||||
response = show_dialog(DialogType.CHOOSER, self._main_window,
|
||||
action_type=Gtk.FileChooserAction.OPEN,
|
||||
@@ -1528,8 +1529,10 @@ class Application(Gtk.Application):
|
||||
except (UnicodeEncodeError, UnicodeDecodeError) as e:
|
||||
log("Filename [{}] error in zip archive: {}".format(zip_info.filename, e))
|
||||
else:
|
||||
zip_info.filename = os.path.basename(f_name)
|
||||
zip_file.extract(zip_info, path=tmp_path_name)
|
||||
base_name = os.path.basename(f_name)
|
||||
if base_name:
|
||||
zip_info.filename = os.path.basename(f_name)
|
||||
zip_file.extract(zip_info, path=tmp_path_name)
|
||||
elif tarfile.is_tarfile(data_path):
|
||||
with tarfile.open(data_path) as tar:
|
||||
for mb in tar.getmembers():
|
||||
@@ -1582,7 +1585,7 @@ class Application(Gtk.Application):
|
||||
yield True
|
||||
services = get_services(data_path, prf, self.get_format_version() if prf is SettingsType.ENIGMA_2 else 0)
|
||||
yield True
|
||||
update_picons_data(self._settings.picons_local_path, self._picons)
|
||||
update_picons_data(self._settings.picons_local_path, self._picons, self._picons_size)
|
||||
yield True
|
||||
except FileNotFoundError as e:
|
||||
msg = get_message("Please, download files from receiver or setup your path for read data!")
|
||||
@@ -1609,6 +1612,9 @@ class Application(Gtk.Application):
|
||||
yield True
|
||||
self._data_hash = self.get_data_hash()
|
||||
yield True
|
||||
if self._filter_box.get_visible():
|
||||
self.on_filter_changed()
|
||||
yield True
|
||||
|
||||
def append_data(self, bouquets, services):
|
||||
if self._app_info_box.get_visible():
|
||||
@@ -2008,7 +2014,7 @@ class Application(Gtk.Application):
|
||||
c_gen = self.clear_current_data()
|
||||
yield from c_gen
|
||||
|
||||
self.init_colors(True)
|
||||
self.init_appearance(True)
|
||||
self.init_profiles()
|
||||
yield True
|
||||
gen = self.init_http_api()
|
||||
@@ -2375,6 +2381,7 @@ class Application(Gtk.Application):
|
||||
self.import_data(response)
|
||||
|
||||
def import_data(self, path, force=None, callback=None):
|
||||
path = os.path.normpath(path)
|
||||
if os.path.isdir(path) and not path.endswith(os.sep):
|
||||
path += os.sep
|
||||
elif os.path.isfile(path):
|
||||
@@ -2428,7 +2435,7 @@ class Application(Gtk.Application):
|
||||
yield True
|
||||
self._wait_dialog.hide()
|
||||
|
||||
# ***************** Backup ********************#
|
||||
# ***************** Backup ******************** #
|
||||
|
||||
def on_backup_tool_show(self, action, value=None):
|
||||
""" Shows backup tool dialog """
|
||||
@@ -2445,7 +2452,6 @@ class Application(Gtk.Application):
|
||||
def on_play_stream(self, item=None):
|
||||
self.on_player_play()
|
||||
|
||||
@run_idle
|
||||
def on_player_play(self, item=None):
|
||||
path, column = self._fav_view.get_cursor()
|
||||
if path:
|
||||
@@ -2481,9 +2487,9 @@ class Application(Gtk.Application):
|
||||
self.show_playback_window()
|
||||
elif self._playback_window:
|
||||
title = self.get_playback_title()
|
||||
GLib.idle_add(self._playback_window.set_title, title)
|
||||
GLib.idle_add(self._player.play, url, priority=GLib.PRIORITY_LOW)
|
||||
GLib.idle_add(self._playback_window.show)
|
||||
self._playback_window.set_title(title)
|
||||
self._playback_window.show()
|
||||
GLib.idle_add(self._player.play, url)
|
||||
else:
|
||||
self.show_error_dialog("Init player error!")
|
||||
finally:
|
||||
@@ -2495,11 +2501,13 @@ class Application(Gtk.Application):
|
||||
if not self._player_box.get_visible():
|
||||
self.set_player_area_size(self._player_box)
|
||||
|
||||
GLib.idle_add(self._player.play, url, priority=GLib.PRIORITY_LOW)
|
||||
GLib.idle_add(self._player.play, url)
|
||||
|
||||
self._player_box.set_visible(True)
|
||||
|
||||
def on_player_stop(self, item=None):
|
||||
if self._player:
|
||||
self._fav_view.set_sensitive(True)
|
||||
self._player.stop()
|
||||
|
||||
def on_player_previous(self, item):
|
||||
@@ -2512,6 +2520,7 @@ class Application(Gtk.Application):
|
||||
|
||||
@run_with_delay(1)
|
||||
def set_player_action(self):
|
||||
self._fav_view.set_sensitive(False)
|
||||
if self._fav_click_mode is FavClickMode.PLAY:
|
||||
self.on_stream()
|
||||
elif self._fav_click_mode is FavClickMode.ZAP_PLAY:
|
||||
@@ -2531,7 +2540,7 @@ class Application(Gtk.Application):
|
||||
|
||||
def on_player_close(self, window=None, event=None):
|
||||
if self._player:
|
||||
self._player.stop()
|
||||
GLib.idle_add(self._player.stop)
|
||||
|
||||
self.set_playback_elms_active()
|
||||
if self._playback_window:
|
||||
@@ -2547,12 +2556,15 @@ class Application(Gtk.Application):
|
||||
self._player_scale.get_adjustment().set_upper(duration)
|
||||
GLib.idle_add(self._player_rewind_box.set_visible, duration > 0, priority=GLib.PRIORITY_LOW)
|
||||
GLib.idle_add(self._player_current_time_label.set_text, "0", priority=GLib.PRIORITY_LOW)
|
||||
GLib.idle_add(self._player_full_time_label.set_text, self.get_time_str(duration), priority=GLib.PRIORITY_LOW)
|
||||
GLib.idle_add(self._player_full_time_label.set_text, self.get_time_str(duration),
|
||||
priority=GLib.PRIORITY_LOW)
|
||||
|
||||
def on_player_time_changed(self, t):
|
||||
if not self._full_screen and self._player_rewind_box.get_visible():
|
||||
GLib.idle_add(self._player_current_time_label.set_text, self.get_time_str(t), priority=GLib.PRIORITY_LOW)
|
||||
GLib.idle_add(self._player_current_time_label.set_text, self.get_time_str(t),
|
||||
priority=GLib.PRIORITY_LOW)
|
||||
|
||||
@run_with_delay(2)
|
||||
def on_player_error(self):
|
||||
self.set_playback_elms_active()
|
||||
self.show_error_dialog("Can't Playback!")
|
||||
@@ -2568,54 +2580,46 @@ class Application(Gtk.Application):
|
||||
h, m = divmod(m, 60)
|
||||
return "{}{:02d}:{:02d}".format(str(h) + ":" if h else "", m, s)
|
||||
|
||||
def on_drawing_area_realize(self, widget):
|
||||
def on_player_box_realize(self, widget):
|
||||
if not self._player:
|
||||
try:
|
||||
self._player = Player.get_instance(mode=self._settings.play_streams_mode,
|
||||
rewind_cb=self.on_player_duration_changed,
|
||||
position_cb=self.on_player_time_changed,
|
||||
error_cb=self.on_player_error,
|
||||
playing_cb=self.set_playback_elms_active)
|
||||
except (ImportError, NameError, AttributeError):
|
||||
self.show_error_dialog("No VLC is found. Check that it is installed!")
|
||||
self._player = Player.make(name=self._settings.stream_lib,
|
||||
mode=self._settings.play_streams_mode,
|
||||
widget=widget,
|
||||
buf_cb=self.on_player_duration_changed,
|
||||
position_cb=self.on_player_time_changed,
|
||||
error_cb=self.on_player_error,
|
||||
playing_cb=self.set_playback_elms_active)
|
||||
except (ImportError, NameError) as e:
|
||||
self.show_error_dialog(str(e))
|
||||
return True
|
||||
else:
|
||||
if self._settings.is_darwin:
|
||||
self._player.set_nso(widget)
|
||||
else:
|
||||
self._player.set_xwindow(widget.get_window().get_xid())
|
||||
self._main_window.connect("key-press-event", self.on_player_key_press)
|
||||
self._player.play(self._current_mrl)
|
||||
finally:
|
||||
self.set_playback_elms_active()
|
||||
if self._settings.play_streams_mode is PlayStreamsMode.BUILT_IN:
|
||||
self.set_player_area_size(widget)
|
||||
|
||||
def on_player_box_visibility(self, box):
|
||||
visible = box.get_visible()
|
||||
self._fav_bouquets_paned.set_orientation(Gtk.Orientation.VERTICAL if visible else Gtk.Orientation.HORIZONTAL)
|
||||
|
||||
@run_idle
|
||||
def set_player_area_size(self, widget):
|
||||
w, h = self._main_window.get_size()
|
||||
widget.set_size_request(w * 0.6, -1)
|
||||
|
||||
def on_player_drawing_area_draw(self, widget, cr):
|
||||
""" Used for black background drawing in the player drawing area.
|
||||
|
||||
Required for Gtk >= 3.20.
|
||||
More info: https://developer.gnome.org/gtk3/stable/ch32s10.html,
|
||||
https://developer.gnome.org/gtk3/stable/GtkStyleContext.html#gtk-render-background
|
||||
"""
|
||||
context = widget.get_style_context()
|
||||
width = widget.get_allocated_width()
|
||||
height = widget.get_allocated_height()
|
||||
Gtk.render_background(context, cr, 0, 0, width, height)
|
||||
r, g, b, a = 0, 0, 0, 1 # black color
|
||||
cr.set_source_rgba(r, g, b, a)
|
||||
cr.rectangle(0, 0, width, height)
|
||||
cr.fill()
|
||||
|
||||
def on_player_press(self, area, event):
|
||||
if event.button == Gdk.BUTTON_PRIMARY:
|
||||
if event.type == Gdk.EventType.DOUBLE_BUTTON_PRESS:
|
||||
self.on_full_screen()
|
||||
|
||||
def on_player_key_press(self, widget, event):
|
||||
if self._player and self._player_event_box.get_visible():
|
||||
key = event.keyval
|
||||
if any((key == Gdk.KEY_F11, key == Gdk.KEY_f, self._full_screen and key == Gdk.KEY_Escape)):
|
||||
self.on_full_screen()
|
||||
|
||||
def on_full_screen(self, item=None):
|
||||
self._full_screen = not self._full_screen
|
||||
if self._settings.play_streams_mode is PlayStreamsMode.BUILT_IN:
|
||||
@@ -2625,7 +2629,6 @@ class Application(Gtk.Application):
|
||||
self._player_tool_bar.set_visible(not self._full_screen)
|
||||
self._playback_window.fullscreen() if self._full_screen else self._playback_window.unfullscreen()
|
||||
|
||||
@run_idle
|
||||
def update_state_on_full_screen(self, visible):
|
||||
self._main_data_box.set_visible(visible)
|
||||
self._player_tool_bar.set_visible(visible)
|
||||
@@ -2644,18 +2647,19 @@ class Application(Gtk.Application):
|
||||
icon_name="demon-editor")
|
||||
self._playback_window.resize(width, height)
|
||||
self._playback_window.connect("delete-event", self.on_player_close)
|
||||
self._playback_window.connect("key-press-event", self.on_player_key_press)
|
||||
|
||||
if self._settings.is_darwin:
|
||||
self._player_drawing_area.reparent(self._playback_window)
|
||||
else:
|
||||
box = Gtk.HBox(visible=True, orientation="vertical")
|
||||
self._player_event_box.reparent(box)
|
||||
self._playback_window.bind_property("visible", self._player_event_box, "visible")
|
||||
|
||||
if not self._settings.is_darwin:
|
||||
self._player_prev_button.set_visible(False)
|
||||
self._player_next_button.set_visible(False)
|
||||
box = Gtk.HBox(visible=True, orientation="vertical")
|
||||
self._player_drawing_area.reparent(box)
|
||||
self._player_box.remove(self._player_tool_bar)
|
||||
box.pack_end(self._player_tool_bar, False, False, 0)
|
||||
self._playback_window.add(box)
|
||||
|
||||
self._playback_window.add(box)
|
||||
self._playback_window.set_application(self)
|
||||
self._playback_window.show()
|
||||
|
||||
@@ -2754,7 +2758,7 @@ class Application(Gtk.Application):
|
||||
""" Switch to the channel and watch in the player """
|
||||
if not self._app_info_box.get_visible() and self._settings.play_streams_mode is PlayStreamsMode.BUILT_IN:
|
||||
self.set_player_area_size(self._player_box)
|
||||
self._player_box.set_visible(True)
|
||||
GLib.idle_add(self._player_box.set_visible, True)
|
||||
GLib.idle_add(self._app_info_box.set_visible, False)
|
||||
|
||||
self._http_api.send(HttpAPI.Request.STREAM_CURRENT, None, self.watch)
|
||||
@@ -2931,6 +2935,10 @@ class Application(Gtk.Application):
|
||||
|
||||
# ****************** FTP client ********************* #
|
||||
|
||||
def on_ftp_toggle(self, button):
|
||||
if not self._app_info_box.get_visible():
|
||||
self._toolbar_search_box.set_visible(not button.get_active())
|
||||
|
||||
def on_ftp_realize(self, revealer):
|
||||
if not self._ftp_client:
|
||||
from app.ui.ftp import FtpClientBox
|
||||
@@ -2945,105 +2953,111 @@ class Application(Gtk.Application):
|
||||
return True
|
||||
|
||||
action.set_state(value)
|
||||
if value:
|
||||
self.update_filter_sat_positions()
|
||||
self._filter_entry.grab_focus()
|
||||
else:
|
||||
self.filter_set_default()
|
||||
|
||||
self._filter_bar.set_search_mode(value)
|
||||
self._filter_entry.grab_focus() if value else self.on_filter_changed()
|
||||
self.filter_set_default()
|
||||
self._filter_box.set_visible(value)
|
||||
|
||||
@run_idle
|
||||
def filter_set_default(self):
|
||||
""" Setting defaults for filter elements. """
|
||||
self._filter_entry.set_text("")
|
||||
self._filter_sat_positions_box.set_active(0)
|
||||
self._filter_types_box.set_active(0)
|
||||
self._filter_only_free_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()
|
||||
|
||||
def init_sat_positions(self):
|
||||
self._sat_positions.clear()
|
||||
first = (self._filter_sat_positions_model[0][0],)
|
||||
self._filter_sat_positions_model.clear()
|
||||
self._filter_sat_positions_model.append(first)
|
||||
self._filter_sat_positions_box.set_active(0)
|
||||
first = self._filter_sat_pos_model[0][:]
|
||||
self._filter_sat_pos_model.clear()
|
||||
self._filter_sat_pos_model.append(first)
|
||||
|
||||
def update_sat_positions(self):
|
||||
""" Updates positions values for the filtering function """
|
||||
""" Updates positions values for the filtering function. """
|
||||
self._sat_positions.clear()
|
||||
sat_positions = set()
|
||||
|
||||
if self._s_type is SettingsType.ENIGMA_2:
|
||||
terrestrial = False
|
||||
cable = False
|
||||
atsc = False
|
||||
|
||||
for srv in self._services.values():
|
||||
tr_type = srv.transponder_type
|
||||
if tr_type == "s" and srv.pos:
|
||||
sat_positions.add(srv.pos)
|
||||
elif tr_type == "t":
|
||||
self._sat_positions.add(srv.pos)
|
||||
elif tr_type == "t" or tr_type == "a":
|
||||
terrestrial = True
|
||||
elif tr_type == "c":
|
||||
cable = True
|
||||
|
||||
if terrestrial:
|
||||
self._sat_positions.append("T")
|
||||
self._sat_positions.add("T")
|
||||
if cable:
|
||||
self._sat_positions.append("C")
|
||||
self._sat_positions.add("C")
|
||||
elif self._s_type is SettingsType.NEUTRINO_MP:
|
||||
list(map(lambda s: sat_positions.add(s.pos), filter(lambda s: s.pos, self._services.values())))
|
||||
list(map(lambda s: self._sat_positions.add(s.pos), filter(lambda s: s.pos, self._services.values())))
|
||||
|
||||
self._sat_positions.extend(map(str, sorted(sat_positions)))
|
||||
if self._filter_bar.is_visible():
|
||||
self.update_filter_sat_positions()
|
||||
self.update_filter_sat_positions()
|
||||
|
||||
@run_idle
|
||||
def update_filter_sat_positions(self):
|
||||
model = self._filter_sat_positions_model
|
||||
if len(model) < 2:
|
||||
list(map(self._filter_sat_positions_model.append, map(lambda x: (str(x),), self._sat_positions)))
|
||||
else:
|
||||
selected = self._filter_sat_positions_box.get_active_id()
|
||||
active = self._filter_sat_positions_box.get_active()
|
||||
itrs = list(filter(lambda it: model[it][0] not in self._sat_positions, [row.iter for row in model][1:]))
|
||||
list(map(model.remove, itrs))
|
||||
""" Updates the values for the satellite positions button model. """
|
||||
first = self._filter_sat_pos_model[self._filter_sat_pos_model.get_iter_first()][:]
|
||||
self._filter_sat_pos_model.clear()
|
||||
self._filter_sat_pos_model.append((first[0], True))
|
||||
self._sat_positions.discard(first[0])
|
||||
list(map(lambda pos: self._filter_sat_pos_model.append((pos, True)),
|
||||
sorted(self._sat_positions, key=self.get_pos_num, reverse=True)))
|
||||
|
||||
if active != 0 and selected not in self._sat_positions:
|
||||
self._filter_sat_positions_box.set_active(0)
|
||||
|
||||
@run_with_delay(1)
|
||||
def on_filter_changed(self, item):
|
||||
GLib.idle_add(self._services_model_filter.refilter, priority=GLib.PRIORITY_LOW)
|
||||
@run_with_delay(2)
|
||||
def on_filter_changed(self, item=None):
|
||||
model = self._services_view.get_model()
|
||||
self._services_view.set_model(None)
|
||||
self._services_model_filter.refilter()
|
||||
self._services_view.set_model(model)
|
||||
|
||||
def services_filter_function(self, model, itr, data):
|
||||
if self._services_model_filter is None or self._services_model_filter == "None":
|
||||
if not self._filter_box.is_visible():
|
||||
return True
|
||||
else:
|
||||
r_txt = str(model.get(itr, Column.SRV_SERVICE, Column.SRV_PACKAGE, Column.SRV_TYPE, Column.SRV_SSID,
|
||||
Column.SRV_FREQ, Column.SRV_RATE, Column.SRV_POL, Column.SRV_FEC, Column.SRV_SYSTEM,
|
||||
Column.SRV_POS)).upper()
|
||||
txt = self._filter_entry.get_text().upper() in r_txt
|
||||
type_active = self._filter_types_box.get_active() > 0
|
||||
pos_active = self._filter_sat_positions_box.get_active() > 0
|
||||
free = not model.get(itr, Column.SRV_CODED)[0] if self._filter_only_free_button.get_active() else True
|
||||
srv_type, pos = model.get(itr, Column.SRV_TYPE, Column.SRV_POS)
|
||||
|
||||
if type_active and pos_active:
|
||||
active_id = self._filter_types_box.get_active_id() == model.get(itr, Column.SRV_TYPE)[0]
|
||||
pos = self._filter_sat_positions_box.get_active_id() == model.get(itr, Column.SRV_POS)[0]
|
||||
return active_id and pos and txt and free
|
||||
elif type_active:
|
||||
return self._filter_types_box.get_active_id() == model.get(itr, Column.SRV_TYPE)[0] and txt and free
|
||||
elif pos_active:
|
||||
pos = self._filter_sat_positions_box.get_active_id() == model.get(itr, Column.SRV_POS)[0]
|
||||
return pos and txt and free
|
||||
return all((srv_type in self._service_types,
|
||||
pos in self._sat_positions,
|
||||
txt, free))
|
||||
|
||||
return txt and free
|
||||
def on_filter_type_toggled(self, toggle, path):
|
||||
self.update_filter_toogle_model(self._filter_types_model, toggle, path, self._service_types)
|
||||
|
||||
def on_filter_satellite_toggled(self, toggle, path):
|
||||
self.update_filter_toogle_model(self._filter_sat_pos_model, toggle, path, self._sat_positions)
|
||||
|
||||
def update_filter_toogle_model(self, model, toggle, path, values_set):
|
||||
active = not toggle.get_active()
|
||||
if path == "0":
|
||||
model.foreach(lambda m, p, i: m.set_value(i, 1, active))
|
||||
else:
|
||||
model.set_value(model.get_iter(path), 1, active)
|
||||
if active:
|
||||
model.set_value(model.get_iter_first(), 1, len({r[0] for r in model if r[1]}) == len(model) - 1)
|
||||
else:
|
||||
model.set_value(model.get_iter_first(), 1, False)
|
||||
|
||||
values_set.clear()
|
||||
values_set.update({r[0] for r in model if r[1]})
|
||||
self.on_filter_changed()
|
||||
|
||||
def on_search_toggled(self, action, value):
|
||||
if self._app_info_box.get_visible():
|
||||
return True
|
||||
|
||||
action.set_state(value)
|
||||
self._search_bar.set_search_mode(value)
|
||||
self._search_box.set_visible(value)
|
||||
if value:
|
||||
self._search_entry.grab_focus()
|
||||
else:
|
||||
@@ -3201,6 +3215,17 @@ class Application(Gtk.Application):
|
||||
def on_locate_in_services(self, view):
|
||||
locate_in_services(view, self._services_view, self._main_window)
|
||||
|
||||
def on_mark_duplicates(self, item):
|
||||
""" Marks services with duplicate [names] in the fav list. """
|
||||
from collections import Counter
|
||||
|
||||
dup = Counter(r[Column.FAV_SERVICE] for r in self._fav_model if r[Column.FAV_TYPE] not in self._marker_types)
|
||||
dup = {k for k, v in dup.items() if v > 1}
|
||||
|
||||
for r in self._fav_model:
|
||||
if r[Column.FAV_SERVICE] in dup:
|
||||
r[Column.FAV_BACKGROUND] = self._NEW_COLOR
|
||||
|
||||
# ***************** Picons *********************#
|
||||
|
||||
def on_picons_manager_show(self, action, value=None):
|
||||
@@ -3214,7 +3239,7 @@ class Application(Gtk.Application):
|
||||
|
||||
@run_task
|
||||
def update_picons(self):
|
||||
update_picons_data(self._settings.picons_local_path, self._picons)
|
||||
update_picons_data(self._settings.picons_local_path, self._picons, self._picons_size)
|
||||
append_picons(self._picons, self._services_model)
|
||||
|
||||
def on_assign_picon(self, view, src_path=None, dst_path=None):
|
||||
@@ -3258,8 +3283,8 @@ class Application(Gtk.Application):
|
||||
self.create_bouquets(BqGenType.EACH_TYPE)
|
||||
|
||||
def create_bouquets(self, g_type):
|
||||
gen_bouquets(self._services_view, self._bouquets_view, self._main_window, g_type, self._TV_TYPES,
|
||||
self._s_type, self.append_bouquet)
|
||||
gen_bouquets(self._services_view, self._bouquets_view, self._main_window, g_type, self._s_type,
|
||||
self.append_bouquet)
|
||||
|
||||
# ***************** Alternatives ********************* #
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ def insert_marker(view, bouquets, selected_bouquet, services, parent_window, m_t
|
||||
marker = (None, None, text, None, None, s_type, None, fav_id, None, None, None)
|
||||
itr = model.insert_before(model.get_iter(paths[0]), marker) if paths else model.insert(0, marker)
|
||||
bouquets[selected_bouquet].insert(model.get_path(itr)[0], fav_id)
|
||||
services[fav_id] = Service(None, None, None, text, None, None, None, s_type, *[None] * 9, 0, fav_id, None)
|
||||
services[fav_id] = Service(None, None, None, text, None, None, None, s_type, None, None, None, None, None, None, None, None, None, 0, fav_id, None)
|
||||
|
||||
|
||||
# ***************** Movement *******************#
|
||||
@@ -280,7 +280,7 @@ def set_hide(services, model, paths):
|
||||
for path in paths:
|
||||
itr = model.get_iter(path)
|
||||
model.set_value(itr, col_num, None if hide else HIDE_ICON)
|
||||
flags = [*model.get_value(itr, 0).split(",")]
|
||||
flags = list(model.get_value(itr, 0).split(","))
|
||||
index, flag = None, None
|
||||
for i, fl in enumerate(flags):
|
||||
if fl.startswith("f:"):
|
||||
@@ -353,12 +353,12 @@ def scroll_to(index, view, paths=None):
|
||||
|
||||
# ***************** Picons *********************#
|
||||
|
||||
def update_picons_data(path, picons):
|
||||
def update_picons_data(path, picons, size=32):
|
||||
if not os.path.exists(path):
|
||||
return
|
||||
|
||||
for file in os.listdir(path):
|
||||
pf = get_picon_pixbuf(path + file)
|
||||
pf = get_picon_pixbuf(path + file, size)
|
||||
if pf:
|
||||
picons[file] = pf
|
||||
|
||||
@@ -530,7 +530,7 @@ def get_picon_pixbuf(path, size=32):
|
||||
|
||||
# ***************** Bouquets *********************#
|
||||
|
||||
def gen_bouquets(view, bq_view, transient, gen_type, tv_types, s_type, callback):
|
||||
def gen_bouquets(view, bq_view, transient, gen_type, s_type, callback):
|
||||
""" Auto-generate and append list of bouquets """
|
||||
fav_id_index = Column.SRV_FAV_ID
|
||||
index = Column.SRV_TYPE
|
||||
@@ -545,8 +545,6 @@ def gen_bouquets(view, bq_view, transient, gen_type, tv_types, s_type, callback)
|
||||
if not is_only_one_item_selected(paths, transient):
|
||||
return
|
||||
service = Service(*model[paths][:Column.SRV_TOOLTIP])
|
||||
if service.service_type not in tv_types:
|
||||
bq_type = BqType.RADIO.value
|
||||
append_bouquets(bq_type, bq_view, callback, fav_id_index, index, model,
|
||||
[service.package if gen_type is BqGenType.PACKAGE else
|
||||
service.pos if gen_type is BqGenType.SAT else service.service_type], s_type)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -275,7 +275,6 @@ Author: Dmitriy Yefremov
|
||||
<property name="destroy_with_parent">True</property>
|
||||
<property name="skip_taskbar_hint">True</property>
|
||||
<property name="skip_pager_hint">True</property>
|
||||
<property name="gravity">center</property>
|
||||
<signal name="delete-event" handler="on_close" swapped="no"/>
|
||||
<child>
|
||||
<placeholder/>
|
||||
@@ -457,7 +456,7 @@ Author: Dmitriy Yefremov
|
||||
<property name="can_focus">True</property>
|
||||
<property name="model">picons_src_sort_model</property>
|
||||
<property name="headers_visible">False</property>
|
||||
<property name="tooltip_column">1</property>
|
||||
<property name="tooltip_column">0</property>
|
||||
<property name="activate_on_single_click">True</property>
|
||||
<signal name="button-press-event" handler="on_popup_menu" object="picons_src_view_popup_menu" swapped="no"/>
|
||||
<signal name="drag-data-get" handler="on_picons_view_drag_data_get" swapped="no"/>
|
||||
@@ -465,6 +464,7 @@ Author: Dmitriy Yefremov
|
||||
<signal name="drag-drop" handler="on_picons_src_view_drag_drop" swapped="no"/>
|
||||
<signal name="drag-end" handler="on_picons_src_view_drag_end" swapped="no"/>
|
||||
<signal name="key-press-event" handler="on_tree_view_key_press" swapped="no"/>
|
||||
<signal name="query-tooltip" handler="on_view_query_tooltip" swapped="no"/>
|
||||
<child internal-child="selection">
|
||||
<object class="GtkTreeSelection" id="picons_src_view_selection"/>
|
||||
</child>
|
||||
@@ -611,12 +611,13 @@ Author: Dmitriy Yefremov
|
||||
<property name="can_focus">True</property>
|
||||
<property name="model">picons_dst_sort_model</property>
|
||||
<property name="headers_visible">False</property>
|
||||
<property name="tooltip_column">1</property>
|
||||
<property name="tooltip_column">0</property>
|
||||
<property name="activate_on_single_click">True</property>
|
||||
<signal name="button-press-event" handler="on_popup_menu" object="picons_dest_view_popup_menu" swapped="no"/>
|
||||
<signal name="cursor-changed" handler="on_picon_activated" swapped="no"/>
|
||||
<signal name="drag-data-get" handler="on_picons_view_drag_data_get" swapped="no"/>
|
||||
<signal name="key-press-event" handler="on_tree_view_key_press" swapped="no"/>
|
||||
<signal name="query-tooltip" handler="on_view_query_tooltip" swapped="no"/>
|
||||
<signal name="realize" handler="on_picons_dest_view_realize" swapped="no"/>
|
||||
<child internal-child="selection">
|
||||
<object class="GtkTreeSelection" id="picons_dest_view_selection"/>
|
||||
@@ -1022,7 +1023,9 @@ Author: Dmitriy Yefremov
|
||||
</attributes>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkCellRendererText" id="name_cellrenderertext"/>
|
||||
<object class="GtkCellRendererText" id="name_cellrenderertext">
|
||||
<property name="ellipsize">end</property>
|
||||
</object>
|
||||
<attributes>
|
||||
<attribute name="text">1</attribute>
|
||||
</attributes>
|
||||
@@ -1609,7 +1612,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 | Primary"/>
|
||||
<accelerator key="f" signal="clicked" modifiers="GDK_SHIFT_MASK | GDK_CONTROL_MASK"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
@@ -1625,7 +1628,7 @@ Author: Dmitriy Yefremov
|
||||
<property name="receives_default">True</property>
|
||||
<property name="image">info_toggle_button_image</property>
|
||||
<property name="always_show_image">True</property>
|
||||
<accelerator key="i" signal="clicked" modifiers="Primary"/>
|
||||
<accelerator key="i" signal="clicked" modifiers="GDK_CONTROL_MASK"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
@@ -1656,7 +1659,7 @@ Author: Dmitriy Yefremov
|
||||
<property name="image">cancel_image</property>
|
||||
<property name="always_show_image">True</property>
|
||||
<signal name="clicked" handler="on_cancel" swapped="no"/>
|
||||
<accelerator key="z" signal="clicked" modifiers="Primary"/>
|
||||
<accelerator key="z" signal="clicked" modifiers="GDK_CONTROL_MASK"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
|
||||
@@ -12,10 +12,10 @@ from app.connections import upload_data, DownloadType, download_data, remove_pic
|
||||
from app.settings import SettingsType, Settings
|
||||
from app.tools.picons import PiconsParser, parse_providers, Provider, convert_to, download_picon
|
||||
from app.tools.satellites import SatellitesParser, SatelliteSource
|
||||
from .dialogs import show_dialog, DialogType, get_message
|
||||
from .dialogs import show_dialog, DialogType, get_message, get_builder
|
||||
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, GTK_PATH, KeyboardKey
|
||||
from .uicommons import Gtk, Gdk, UI_RESOURCES_PATH, TV_ICON, Column, KeyboardKey
|
||||
|
||||
|
||||
class PiconsDialog:
|
||||
@@ -72,12 +72,11 @@ class PiconsDialog:
|
||||
"on_fiter_srcs_toggled": self.on_fiter_srcs_toggled,
|
||||
"on_filter_services_switch": self.on_filter_services_switch,
|
||||
"on_picon_activated": self.on_picon_activated,
|
||||
"on_view_query_tooltip": self.on_view_query_tooltip,
|
||||
"on_tree_view_key_press": self.on_tree_view_key_press,
|
||||
"on_popup_menu": on_popup_menu}
|
||||
|
||||
builder = Gtk.Builder()
|
||||
builder.add_from_file(UI_RESOURCES_PATH + "picons_manager.glade")
|
||||
builder.connect_signals(handlers)
|
||||
builder = get_builder(UI_RESOURCES_PATH + "picons_manager.glade", handlers)
|
||||
|
||||
self._dialog = builder.get_object("picons_dialog")
|
||||
self._dialog.set_transient_for(transient)
|
||||
@@ -508,7 +507,8 @@ class PiconsDialog:
|
||||
@run_idle
|
||||
def append_providers(self, providers, model):
|
||||
for p in providers:
|
||||
model.append((self.get_pixbuf(p[0]) if p[0] else TV_ICON, *p[1:]))
|
||||
prv = p._replace(logo=self.get_pixbuf(p[0]) if p[0] else TV_ICON)
|
||||
model.append(prv)
|
||||
self.update_receive_button_state()
|
||||
|
||||
def get_pixbuf(self, img_data):
|
||||
@@ -554,9 +554,12 @@ class PiconsDialog:
|
||||
executor.shutdown()
|
||||
return
|
||||
|
||||
picons.extend(future.result())
|
||||
pic = future.result()
|
||||
if pic:
|
||||
picons.extend(pic)
|
||||
|
||||
# Getting picon images.
|
||||
futures = {executor.submit(download_picon, *pic, self.append_output): pic for pic in picons}
|
||||
futures = {executor.submit(download_picon, pic[0], pic[1], self.append_output): pic for pic in picons}
|
||||
done, not_done = concurrent.futures.wait(futures, timeout=0)
|
||||
while self._is_downloading and not_done:
|
||||
done, not_done = concurrent.futures.wait(not_done, timeout=5)
|
||||
@@ -754,6 +757,20 @@ class PiconsDialog:
|
||||
get_message("System"), srv.system, get_message("Freq"), srv.freq,
|
||||
ref)
|
||||
|
||||
def on_view_query_tooltip(self, view, x, y, keyboard_mode, tooltip):
|
||||
dest = view.get_dest_row_at_pos(x, y)
|
||||
if not dest:
|
||||
return False
|
||||
|
||||
path, pos = dest
|
||||
model = view.get_model()
|
||||
row = model[path][:]
|
||||
tooltip.set_icon(get_picon_pixbuf(row[-1], size=self._settings.tooltip_logo_size))
|
||||
tooltip.set_text(row[1])
|
||||
view.set_tooltip_row(tooltip, path)
|
||||
|
||||
return True
|
||||
|
||||
def on_tree_view_key_press(self, view, event):
|
||||
key_code = event.hardware_keycode
|
||||
if not KeyboardKey.value_exist(key_code):
|
||||
|
||||
@@ -25,33 +25,6 @@ THE SOFTWARE.
|
||||
|
||||
Author: Dmitriy Yefremov
|
||||
|
||||
-->
|
||||
<!--
|
||||
|
||||
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>
|
||||
<requires lib="gtk+" version="3.16"/>
|
||||
@@ -296,7 +269,7 @@ Author: Dmitriy Yefremov
|
||||
<property name="image">popup_menu_add_image</property>
|
||||
<property name="use_stock">False</property>
|
||||
<signal name="activate" handler="on_satellite_add" swapped="no"/>
|
||||
<accelerator key="s" signal="activate" modifiers="Primary"/>
|
||||
<accelerator key="s" signal="activate" modifiers="GDK_CONTROL_MASK"/>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
@@ -307,7 +280,7 @@ Author: Dmitriy Yefremov
|
||||
<property name="image">popup_menu_add_image_2</property>
|
||||
<property name="use_stock">False</property>
|
||||
<signal name="activate" handler="on_transponder_add" swapped="no"/>
|
||||
<accelerator key="t" signal="activate" modifiers="Primary"/>
|
||||
<accelerator key="t" signal="activate" modifiers="GDK_CONTROL_MASK"/>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
@@ -324,7 +297,7 @@ Author: Dmitriy Yefremov
|
||||
<property name="use_underline">True</property>
|
||||
<property name="use_stock">True</property>
|
||||
<signal name="activate" handler="on_edit" object="satellites_editor_tree_view" swapped="no"/>
|
||||
<accelerator key="e" signal="activate" modifiers="Primary"/>
|
||||
<accelerator key="e" signal="activate" modifiers="GDK_CONTROL_MASK"/>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
@@ -463,7 +436,6 @@ Author: Dmitriy Yefremov
|
||||
<property name="icon_name">applications-utilities</property>
|
||||
<property name="skip_taskbar_hint">True</property>
|
||||
<property name="skip_pager_hint">True</property>
|
||||
<property name="gravity">center</property>
|
||||
<signal name="check-resize" handler="on_resize" swapped="no"/>
|
||||
<child>
|
||||
<object class="GtkBox" id="sat_editor_main_box">
|
||||
@@ -577,11 +549,14 @@ Author: Dmitriy Yefremov
|
||||
<child>
|
||||
<object class="GtkTreeViewColumn" id="satellite_column">
|
||||
<property name="resizable">True</property>
|
||||
<property name="min_width">20</property>
|
||||
<property name="min_width">250</property>
|
||||
<property name="title" translatable="yes">Satellite</property>
|
||||
<property name="expand">True</property>
|
||||
<property name="alignment">0.5</property>
|
||||
<child>
|
||||
<object class="GtkCellRendererText" id="satellite_cellrenderertext"/>
|
||||
<object class="GtkCellRendererText" id="satellite_cellrenderertext">
|
||||
<property name="ellipsize">end</property>
|
||||
</object>
|
||||
<attributes>
|
||||
<attribute name="text">0</attribute>
|
||||
</attributes>
|
||||
@@ -594,6 +569,7 @@ Author: Dmitriy Yefremov
|
||||
<property name="min_width">20</property>
|
||||
<property name="title" translatable="yes">Freq</property>
|
||||
<property name="expand">True</property>
|
||||
<property name="alignment">0.5</property>
|
||||
<child>
|
||||
<object class="GtkCellRendererText" id="frequency_cellrenderertext"/>
|
||||
<attributes>
|
||||
@@ -608,6 +584,7 @@ Author: Dmitriy Yefremov
|
||||
<property name="min_width">20</property>
|
||||
<property name="title" translatable="yes">Rate</property>
|
||||
<property name="expand">True</property>
|
||||
<property name="alignment">0.5</property>
|
||||
<child>
|
||||
<object class="GtkCellRendererText" id="sat_rate_cellrenderertext"/>
|
||||
<attributes>
|
||||
@@ -622,6 +599,7 @@ Author: Dmitriy Yefremov
|
||||
<property name="min_width">20</property>
|
||||
<property name="title" translatable="yes">Pol</property>
|
||||
<property name="expand">True</property>
|
||||
<property name="alignment">0.5</property>
|
||||
<child>
|
||||
<object class="GtkCellRendererText" id="sat_pol_cellrenderertext"/>
|
||||
<attributes>
|
||||
@@ -636,6 +614,7 @@ Author: Dmitriy Yefremov
|
||||
<property name="min_width">20</property>
|
||||
<property name="title" translatable="yes">FEC</property>
|
||||
<property name="expand">True</property>
|
||||
<property name="alignment">0.5</property>
|
||||
<child>
|
||||
<object class="GtkCellRendererText" id="set_fec_cellrenderertext"/>
|
||||
<attributes>
|
||||
@@ -650,6 +629,7 @@ Author: Dmitriy Yefremov
|
||||
<property name="min_width">20</property>
|
||||
<property name="title" translatable="yes">System</property>
|
||||
<property name="expand">True</property>
|
||||
<property name="alignment">0.5</property>
|
||||
<child>
|
||||
<object class="GtkCellRendererText" id="sys_cellrenderertext"/>
|
||||
<attributes>
|
||||
@@ -664,6 +644,7 @@ Author: Dmitriy Yefremov
|
||||
<property name="min_width">20</property>
|
||||
<property name="title" translatable="yes">Mod</property>
|
||||
<property name="expand">True</property>
|
||||
<property name="alignment">0.5</property>
|
||||
<child>
|
||||
<object class="GtkCellRendererText" id="mod_cellrenderertext"/>
|
||||
<attributes>
|
||||
@@ -797,7 +778,6 @@ Author: Dmitriy Yefremov
|
||||
<property name="type_hint">dialog</property>
|
||||
<property name="skip_taskbar_hint">True</property>
|
||||
<property name="skip_pager_hint">True</property>
|
||||
<property name="gravity">center</property>
|
||||
<child internal-child="vbox">
|
||||
<object class="GtkBox" id="satelitte_dialog_vbox">
|
||||
<property name="can_focus">False</property>
|
||||
@@ -887,7 +867,6 @@ Author: Dmitriy Yefremov
|
||||
<object class="GtkEntry" id="sat_name_entry">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="primary_icon_name">document-edit-symbolic</property>
|
||||
<property name="primary_icon_activatable">False</property>
|
||||
<property name="secondary_icon_activatable">False</property>
|
||||
<property name="secondary_icon_sensitive">False</property>
|
||||
@@ -988,7 +967,6 @@ Author: Dmitriy Yefremov
|
||||
<property name="type_hint">dialog</property>
|
||||
<property name="skip_taskbar_hint">True</property>
|
||||
<property name="skip_pager_hint">True</property>
|
||||
<property name="gravity">center</property>
|
||||
<child internal-child="vbox">
|
||||
<object class="GtkBox" id="tr_dialog_vbox">
|
||||
<property name="can_focus">False</property>
|
||||
@@ -1130,7 +1108,6 @@ Author: Dmitriy Yefremov
|
||||
<property name="can_focus">True</property>
|
||||
<property name="width_chars">12</property>
|
||||
<property name="max_width_chars">10</property>
|
||||
<property name="primary_icon_name">document-edit-symbolic</property>
|
||||
<property name="primary_icon_activatable">False</property>
|
||||
<property name="secondary_icon_activatable">False</property>
|
||||
<property name="secondary_icon_sensitive">False</property>
|
||||
@@ -1149,7 +1126,6 @@ Author: Dmitriy Yefremov
|
||||
<property name="can_focus">True</property>
|
||||
<property name="width_chars">12</property>
|
||||
<property name="max_width_chars">10</property>
|
||||
<property name="primary_icon_name">document-edit-symbolic</property>
|
||||
<property name="placeholder_text" translatable="yes">27500000</property>
|
||||
<property name="input_purpose">digits</property>
|
||||
<signal name="changed" handler="on_entry_changed" swapped="no"/>
|
||||
@@ -1305,7 +1281,6 @@ Author: Dmitriy Yefremov
|
||||
<property name="can_focus">True</property>
|
||||
<property name="width_chars">5</property>
|
||||
<property name="max_width_chars">12</property>
|
||||
<property name="primary_icon_name">document-edit-symbolic</property>
|
||||
<property name="primary_icon_activatable">False</property>
|
||||
<property name="placeholder_text" translatable="yes">0 - 262142</property>
|
||||
<property name="input_purpose">digits</property>
|
||||
@@ -1322,7 +1297,6 @@ Author: Dmitriy Yefremov
|
||||
<property name="can_focus">True</property>
|
||||
<property name="width_chars">5</property>
|
||||
<property name="max_width_chars">12</property>
|
||||
<property name="primary_icon_name">document-edit-symbolic</property>
|
||||
<property name="primary_icon_activatable">False</property>
|
||||
<property name="placeholder_text" translatable="yes">0 - 255</property>
|
||||
<property name="input_purpose">digits</property>
|
||||
@@ -1392,20 +1366,6 @@ Author: Dmitriy Yefremov
|
||||
<object class="GtkTreeModelSort" id="update_sat_list_model_sort">
|
||||
<property name="model">update_sat_list_model_filter</property>
|
||||
</object>
|
||||
<object class="GtkListStore" id="update_source_store">
|
||||
<columns>
|
||||
<!-- column-name source -->
|
||||
<column type="gchararray"/>
|
||||
</columns>
|
||||
<data>
|
||||
<row>
|
||||
<col id="0">FlySat</col>
|
||||
</row>
|
||||
<row>
|
||||
<col id="0">LyngSat</col>
|
||||
</row>
|
||||
</data>
|
||||
</object>
|
||||
<object class="GtkListStore" id="update_service_store">
|
||||
<columns>
|
||||
<!-- column-name picon -->
|
||||
@@ -1440,7 +1400,6 @@ Author: Dmitriy Yefremov
|
||||
<property name="destroy_with_parent">True</property>
|
||||
<property name="skip_taskbar_hint">True</property>
|
||||
<property name="skip_pager_hint">True</property>
|
||||
<property name="gravity">center</property>
|
||||
<signal name="delete-event" handler="on_quit" swapped="no"/>
|
||||
<child>
|
||||
<placeholder/>
|
||||
@@ -1490,7 +1449,6 @@ Author: Dmitriy Yefremov
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="tooltip_text" translatable="yes">Cancel</property>
|
||||
<property name="valign">center</property>
|
||||
<property name="image">sat_update_cancel_image</property>
|
||||
<property name="always_show_image">True</property>
|
||||
<signal name="clicked" handler="on_cancel_receive" swapped="no"/>
|
||||
@@ -1528,7 +1486,7 @@ Author: Dmitriy Yefremov
|
||||
<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 | Primary"/>
|
||||
<accelerator key="f" signal="clicked" modifiers="GDK_SHIFT_MASK | GDK_CONTROL_MASK"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
@@ -1546,7 +1504,7 @@ Author: Dmitriy Yefremov
|
||||
<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="Primary"/>
|
||||
<accelerator key="f" signal="clicked" modifiers="GDK_CONTROL_MASK"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
@@ -1569,18 +1527,15 @@ Author: Dmitriy Yefremov
|
||||
<property name="valign">center</property>
|
||||
<property name="layout_style">expand</property>
|
||||
<child>
|
||||
<object class="GtkComboBox" id="source_combo_box">
|
||||
<object class="GtkComboBoxText" id="source_combo_box">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="valign">center</property>
|
||||
<property name="model">update_source_store</property>
|
||||
<property name="active">0</property>
|
||||
<child>
|
||||
<object class="GtkCellRendererText" id="source_cellrenderertext"/>
|
||||
<attributes>
|
||||
<attribute name="text">0</attribute>
|
||||
</attributes>
|
||||
</child>
|
||||
<items>
|
||||
<item id="FLYSAT" translatable="yes">FlySat</item>
|
||||
<item id="LYNGSAT" translatable="yes">LyngSat</item>
|
||||
<item id="KINGOFSAT" translatable="yes">KingOfSat</item>
|
||||
</items>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
|
||||
@@ -9,10 +9,10 @@ from app.commons import run_idle, run_task, log
|
||||
from app.eparser import get_satellites, write_satellites, Satellite, Transponder
|
||||
from app.eparser.ecommons import PLS_MODE, get_key_by_value
|
||||
from app.tools.satellites import SatellitesParser, SatelliteSource, ServicesParser
|
||||
from .dialogs import show_dialog, DialogType, get_dialogs_string, get_chooser_dialog, get_message
|
||||
from .dialogs import show_dialog, DialogType, get_chooser_dialog, get_message, get_builder
|
||||
from .main_helper import move_items, scroll_to, append_text_to_tview, get_base_model, on_popup_menu
|
||||
from .search import SearchProvider
|
||||
from .uicommons import Gtk, Gdk, UI_RESOURCES_PATH, TEXT_DOMAIN, MOVE_KEYS, KeyboardKey, IS_GNOME_SESSION, MOD_MASK
|
||||
from .uicommons import Gtk, Gdk, UI_RESOURCES_PATH, MOVE_KEYS, KeyboardKey, MOD_MASK
|
||||
|
||||
_UI_PATH = UI_RESOURCES_PATH + "satellites_dialog.glade"
|
||||
|
||||
@@ -44,13 +44,10 @@ class SatellitesDialog:
|
||||
"on_resize": self.on_resize,
|
||||
"on_quit": self.on_quit}
|
||||
|
||||
builder = Gtk.Builder()
|
||||
builder.set_translation_domain(TEXT_DOMAIN)
|
||||
builder.add_objects_from_string(get_dialogs_string(_UI_PATH),
|
||||
("satellites_editor_window", "satellites_tree_store", "popup_menu",
|
||||
"left_header_menu", "popup_menu_add_image", "popup_menu_add_image_2",
|
||||
"sat_editor_save_image", "sat_editor_update_image"))
|
||||
builder.connect_signals(handlers)
|
||||
builder = get_builder(_UI_PATH, handlers, use_str=True,
|
||||
objects=("satellites_editor_window", "satellites_tree_store", "popup_menu",
|
||||
"left_header_menu", "popup_menu_add_image", "popup_menu_add_image_2",
|
||||
"sat_editor_save_image", "sat_editor_update_image"))
|
||||
|
||||
self._window = builder.get_object("satellites_editor_window")
|
||||
self._window.set_transient_for(transient)
|
||||
@@ -185,7 +182,8 @@ class SatellitesDialog:
|
||||
model.set(edited_itr, {0: sat.name, 10: sat.flags, 11: sat.position})
|
||||
else:
|
||||
index = self.get_sat_position_index(sat.position, model)
|
||||
model.insert(None, index, [sat.name, *self._aggr, sat.flags, sat.position])
|
||||
model.insert(None, index,
|
||||
[sat.name, None, None, None, None, None, None, None, None, None, sat.flags, sat.position])
|
||||
scroll_to(index, view)
|
||||
|
||||
def on_transponder(self, transponder=None, edited_itr=None):
|
||||
@@ -210,7 +208,8 @@ class SatellitesDialog:
|
||||
4: tr.fec_inner, 5: tr.system, 6: tr.modulation,
|
||||
7: tr.pls_mode, 8: tr.pls_code, 9: tr.is_id})
|
||||
else:
|
||||
row = ["Transponder:", *tr, None, None]
|
||||
row = ["Transponder:", tr.frequency, tr.symbol_rate, tr.polarization, tr.fec_inner,
|
||||
tr.system, tr.modulation, tr.pls_mode, tr.pls_code, tr.is_id, None, None]
|
||||
model, paths = view.get_selection().get_selected_rows()
|
||||
itr = model.get_iter(paths[0])
|
||||
view.expand_row(paths[0], 0)
|
||||
@@ -254,13 +253,22 @@ class SatellitesDialog:
|
||||
|
||||
return paths
|
||||
|
||||
@staticmethod
|
||||
def on_remove(view):
|
||||
@run_idle
|
||||
def on_remove(self, view):
|
||||
""" Removal of selected satellites and transponders.
|
||||
|
||||
The satellites are removed first! Then transponders.
|
||||
"""
|
||||
selection = view.get_selection()
|
||||
model, paths = selection.get_selected_rows()
|
||||
|
||||
for itr in [model.get_iter(path) for path in paths]:
|
||||
model.remove(itr)
|
||||
itrs = [model.get_iter(path) for path in paths]
|
||||
satellites = list(filter(model.iter_has_child, itrs))
|
||||
if len(satellites):
|
||||
# Removing selected satellites.
|
||||
list(map(model.remove, satellites))
|
||||
else:
|
||||
# Removing selected transponders.
|
||||
list(map(model.remove, itrs))
|
||||
|
||||
@run_idle
|
||||
def on_save(self, view):
|
||||
@@ -307,13 +315,8 @@ class TransponderDialog:
|
||||
def __init__(self, transient, transponder: Transponder = None):
|
||||
|
||||
handlers = {"on_entry_changed": self.on_entry_changed}
|
||||
|
||||
builder = Gtk.Builder()
|
||||
builder.set_translation_domain(TEXT_DOMAIN)
|
||||
builder.add_objects_from_string(get_dialogs_string(_UI_PATH).format(use_header=IS_GNOME_SESSION),
|
||||
("transponder_dialog", "pol_store", "fec_store", "mod_store", "system_store",
|
||||
"pls_mode_store"))
|
||||
builder.connect_signals(handlers)
|
||||
objects = ("transponder_dialog", "pol_store", "fec_store", "mod_store", "system_store", "pls_mode_store")
|
||||
builder = get_builder(_UI_PATH, handlers, use_str=True, objects=objects)
|
||||
|
||||
self._dialog = builder.get_object("transponder_dialog")
|
||||
self._dialog.set_transient_for(transient)
|
||||
@@ -392,10 +395,7 @@ class SatelliteDialog:
|
||||
""" Shows dialog for adding or edit satellite """
|
||||
|
||||
def __init__(self, transient, satellite: Satellite = None):
|
||||
builder = Gtk.Builder()
|
||||
builder.set_translation_domain(TEXT_DOMAIN)
|
||||
builder.add_objects_from_string(get_dialogs_string(_UI_PATH).format(use_header=IS_GNOME_SESSION),
|
||||
("satellite_dialog", "side_store", "pos_adjustment"))
|
||||
builder = get_builder(_UI_PATH, use_str=True, objects=("satellite_dialog", "side_store", "pos_adjustment"))
|
||||
|
||||
self._dialog = builder.get_object("satellite_dialog")
|
||||
self._dialog.set_transient_for(transient)
|
||||
@@ -457,16 +457,13 @@ class UpdateDialog:
|
||||
self._parser = None
|
||||
self._size_name = "{}_window_size".format("_".join(re.findall("[A-Z][^A-Z]*", self.__class__.__name__))).lower()
|
||||
|
||||
builder = Gtk.Builder()
|
||||
builder.set_translation_domain(TEXT_DOMAIN)
|
||||
builder.add_objects_from_file(UI_RESOURCES_PATH + "satellites_dialog.glade",
|
||||
("satellites_update_window", "update_source_store", "update_sat_list_store",
|
||||
builder = get_builder(UI_RESOURCES_PATH + "satellites_dialog.glade", handlers,
|
||||
objects=("satellites_update_window", "update_source_store", "update_sat_list_store",
|
||||
"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"))
|
||||
builder.connect_signals(handlers)
|
||||
|
||||
self._window = builder.get_object("satellites_update_window")
|
||||
self._window.set_transient_for(transient)
|
||||
@@ -532,7 +529,13 @@ class UpdateDialog:
|
||||
|
||||
@run_task
|
||||
def get_sat_list(self, src, callback):
|
||||
sats = self._parser.get_satellites_list(SatelliteSource.FLYSAT if src == 0 else SatelliteSource.LYNGSAT)
|
||||
sat_src = SatelliteSource.FLYSAT
|
||||
if src == 1:
|
||||
sat_src = SatelliteSource.LYNGSAT
|
||||
elif src == 2:
|
||||
sat_src = SatelliteSource.KINGOFSAT
|
||||
|
||||
sats = self._parser.get_satellites_list(sat_src)
|
||||
if sats:
|
||||
callback(sats)
|
||||
self.is_download = False
|
||||
@@ -714,7 +717,8 @@ class SatellitesUpdateDialog(UpdateDialog):
|
||||
self._main_model.remove(ch.iter)
|
||||
|
||||
for tr in sat[3]:
|
||||
self._main_model.append(itr, ["Transponder:", *tr, None, None])
|
||||
self._main_model.append(itr, ["Transponder:", tr.frequency, tr.symbol_rate, tr.polarization, tr.fec_inner,
|
||||
tr.system, tr.modulation, tr.pls_mode, tr.pls_code, tr.is_id, None, None])
|
||||
|
||||
|
||||
class ServicesUpdateDialog(UpdateDialog):
|
||||
@@ -731,8 +735,8 @@ class ServicesUpdateDialog(UpdateDialog):
|
||||
self._services_parser = ServicesParser(source=SatelliteSource.LYNGSAT)
|
||||
|
||||
self._transponder_paned.set_visible(True)
|
||||
s_model = self._source_box.get_model()
|
||||
s_model.remove(s_model.get_iter_first())
|
||||
self._source_box.remove(0)
|
||||
self._source_box.remove(1)
|
||||
self._source_box.set_active(0)
|
||||
# Transponder view popup menu
|
||||
tr_popup_menu = Gtk.Menu()
|
||||
@@ -941,9 +945,10 @@ class ServicesUpdateDialog(UpdateDialog):
|
||||
def append_satellite(model, sat):
|
||||
""" Common function for append satellite to the model """
|
||||
name, flags, pos, transponders = sat
|
||||
parent = model.append(None, [name, *(None,) * 9, flags, pos])
|
||||
for transponder in transponders:
|
||||
model.append(parent, ["Transponder:", *transponder, None, None])
|
||||
parent = model.append(None, [name, None, None, None, None, None, None, None, None, None, flags, pos])
|
||||
for tr in transponders:
|
||||
model.append(parent, ["Transponder:", tr.frequency, tr.symbol_rate, tr.polarization, tr.fec_inner, tr.system,
|
||||
tr.modulation, tr.pls_mode, tr.pls_code, tr.is_id, None, None])
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -40,37 +40,37 @@ Author: Dmitriy Yefremov
|
||||
</columns>
|
||||
<data>
|
||||
<row>
|
||||
<col id="0" translatable="yes">Auto</col>
|
||||
<col id="0">Auto</col>
|
||||
</row>
|
||||
<row>
|
||||
<col id="0" translatable="yes">1/2</col>
|
||||
<col id="0">1/2</col>
|
||||
</row>
|
||||
<row>
|
||||
<col id="0" translatable="yes">2/3</col>
|
||||
</row>
|
||||
<row>
|
||||
<col id="0" translatable="yes">3/4</col>
|
||||
<col id="0">3/4</col>
|
||||
</row>
|
||||
<row>
|
||||
<col id="0" translatable="yes">5/6</col>
|
||||
<col id="0">5/6</col>
|
||||
</row>
|
||||
<row>
|
||||
<col id="0" translatable="yes">7/8</col>
|
||||
<col id="0">7/8</col>
|
||||
</row>
|
||||
<row>
|
||||
<col id="0" translatable="yes">8/9</col>
|
||||
<col id="0">8/9</col>
|
||||
</row>
|
||||
<row>
|
||||
<col id="0" translatable="yes">3/5</col>
|
||||
<col id="0">3/5</col>
|
||||
</row>
|
||||
<row>
|
||||
<col id="0" translatable="yes">4/5</col>
|
||||
<col id="0">4/5</col>
|
||||
</row>
|
||||
<row>
|
||||
<col id="0" translatable="yes">6/7</col>
|
||||
<col id="0">6/7</col>
|
||||
</row>
|
||||
<row>
|
||||
<col id="0" translatable="yes">9/10</col>
|
||||
<col id="0">9/10</col>
|
||||
</row>
|
||||
</data>
|
||||
</object>
|
||||
@@ -94,13 +94,13 @@ Author: Dmitriy Yefremov
|
||||
</columns>
|
||||
<data>
|
||||
<row>
|
||||
<col id="0" translatable="yes">Off</col>
|
||||
<col id="0">Off</col>
|
||||
</row>
|
||||
<row>
|
||||
<col id="0" translatable="yes">On</col>
|
||||
<col id="0">On</col>
|
||||
</row>
|
||||
<row>
|
||||
<col id="0" translatable="yes">Auto</col>
|
||||
<col id="0">Auto</col>
|
||||
</row>
|
||||
</data>
|
||||
</object>
|
||||
@@ -111,19 +111,19 @@ Author: Dmitriy Yefremov
|
||||
</columns>
|
||||
<data>
|
||||
<row>
|
||||
<col id="0" translatable="yes">Auto</col>
|
||||
<col id="0">Auto</col>
|
||||
</row>
|
||||
<row>
|
||||
<col id="0" translatable="yes">QPSK</col>
|
||||
<col id="0">QPSK</col>
|
||||
</row>
|
||||
<row>
|
||||
<col id="0" translatable="yes">8PSK</col>
|
||||
<col id="0">8PSK</col>
|
||||
</row>
|
||||
<row>
|
||||
<col id="0" translatable="yes">16APSK</col>
|
||||
<col id="0">16APSK</col>
|
||||
</row>
|
||||
<row>
|
||||
<col id="0" translatable="yes">32APSK</col>
|
||||
<col id="0">32APSK</col>
|
||||
</row>
|
||||
</data>
|
||||
</object>
|
||||
@@ -134,13 +134,13 @@ Author: Dmitriy Yefremov
|
||||
</columns>
|
||||
<data>
|
||||
<row>
|
||||
<col id="0" translatable="yes">Off</col>
|
||||
<col id="0">Off</col>
|
||||
</row>
|
||||
<row>
|
||||
<col id="0" translatable="yes">On</col>
|
||||
<col id="0">On</col>
|
||||
</row>
|
||||
<row>
|
||||
<col id="0" translatable="yes">Auto</col>
|
||||
<col id="0">Auto</col>
|
||||
</row>
|
||||
</data>
|
||||
</object>
|
||||
@@ -151,13 +151,13 @@ Author: Dmitriy Yefremov
|
||||
</columns>
|
||||
<data>
|
||||
<row>
|
||||
<col id="0" translatable="yes">Root</col>
|
||||
<col id="0">Root</col>
|
||||
</row>
|
||||
<row>
|
||||
<col id="0" translatable="yes">Gold</col>
|
||||
<col id="0">Gold</col>
|
||||
</row>
|
||||
<row>
|
||||
<col id="0" translatable="yes">Combo</col>
|
||||
<col id="0">Combo</col>
|
||||
</row>
|
||||
</data>
|
||||
</object>
|
||||
@@ -168,16 +168,16 @@ Author: Dmitriy Yefremov
|
||||
</columns>
|
||||
<data>
|
||||
<row>
|
||||
<col id="0" translatable="yes">H</col>
|
||||
<col id="0">H</col>
|
||||
</row>
|
||||
<row>
|
||||
<col id="0" translatable="yes">V</col>
|
||||
<col id="0">V</col>
|
||||
</row>
|
||||
<row>
|
||||
<col id="0" translatable="yes">R</col>
|
||||
<col id="0">R</col>
|
||||
</row>
|
||||
<row>
|
||||
<col id="0" translatable="yes">L</col>
|
||||
<col id="0">L</col>
|
||||
</row>
|
||||
</data>
|
||||
</object>
|
||||
@@ -188,16 +188,16 @@ Author: Dmitriy Yefremov
|
||||
</columns>
|
||||
<data>
|
||||
<row>
|
||||
<col id="0" translatable="yes">35%</col>
|
||||
<col id="0">35%</col>
|
||||
</row>
|
||||
<row>
|
||||
<col id="0" translatable="yes">25%</col>
|
||||
<col id="0">25%</col>
|
||||
</row>
|
||||
<row>
|
||||
<col id="0" translatable="yes">20%</col>
|
||||
<col id="0">20%</col>
|
||||
</row>
|
||||
<row>
|
||||
<col id="0" translatable="yes">Auto</col>
|
||||
<col id="0">Auto</col>
|
||||
</row>
|
||||
</data>
|
||||
</object>
|
||||
@@ -253,10 +253,10 @@ Author: Dmitriy Yefremov
|
||||
</columns>
|
||||
<data>
|
||||
<row>
|
||||
<col id="0" translatable="yes">DVB-S</col>
|
||||
<col id="0">DVB-S</col>
|
||||
</row>
|
||||
<row>
|
||||
<col id="0" translatable="yes">DVB-S2</col>
|
||||
<col id="0">DVB-S2</col>
|
||||
</row>
|
||||
</data>
|
||||
</object>
|
||||
@@ -272,7 +272,6 @@ Author: Dmitriy Yefremov
|
||||
<property name="type_hint">dialog</property>
|
||||
<property name="skip_taskbar_hint">True</property>
|
||||
<property name="skip_pager_hint">True</property>
|
||||
<property name="gravity">center</property>
|
||||
<child type="titlebar">
|
||||
<placeholder/>
|
||||
</child>
|
||||
@@ -380,7 +379,6 @@ Author: Dmitriy Yefremov
|
||||
<object class="GtkEntry" id="name_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">0</property>
|
||||
@@ -402,7 +400,7 @@ Author: Dmitriy Yefremov
|
||||
<object class="GtkEntry" id="package_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>
|
||||
@@ -426,7 +424,7 @@ Author: Dmitriy Yefremov
|
||||
<property name="can_focus">True</property>
|
||||
<property name="width_chars">10</property>
|
||||
<property name="max_width_chars">10</property>
|
||||
<property name="primary_icon_name">document-edit-symbolic</property>
|
||||
|
||||
<signal name="changed" handler="on_non_empty_entry_changed" swapped="no"/>
|
||||
<signal name="key-release-event" handler="update_reference" swapped="no"/>
|
||||
</object>
|
||||
@@ -471,7 +469,7 @@ Author: Dmitriy Yefremov
|
||||
<property name="can_focus">True</property>
|
||||
<property name="width_chars">7</property>
|
||||
<property name="max_width_chars">7</property>
|
||||
<property name="primary_icon_name">document-edit-symbolic</property>
|
||||
|
||||
<signal name="changed" handler="on_non_empty_entry_changed" swapped="no"/>
|
||||
<signal name="changed" handler="update_reference" swapped="no"/>
|
||||
</object>
|
||||
@@ -909,7 +907,7 @@ Author: Dmitriy Yefremov
|
||||
<property name="tooltip_text">C:0000,C:a1b2,etc.</property>
|
||||
<property name="width_chars">15</property>
|
||||
<property name="max_width_chars">26</property>
|
||||
<property name="primary_icon_name">document-edit-symbolic</property>
|
||||
|
||||
<property name="placeholder_text" translatable="yes">C:0000,C:a1b2,etc.</property>
|
||||
<signal name="changed" handler="on_cas_entry_changed" swapped="no"/>
|
||||
</object>
|
||||
@@ -1012,7 +1010,7 @@ Author: Dmitriy Yefremov
|
||||
<property name="can_focus">True</property>
|
||||
<property name="width_chars">12</property>
|
||||
<property name="max_width_chars">12</property>
|
||||
<property name="primary_icon_name">document-edit-symbolic</property>
|
||||
|
||||
<signal name="changed" handler="on_non_empty_entry_changed" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
@@ -1038,7 +1036,7 @@ Author: Dmitriy Yefremov
|
||||
<property name="can_focus">True</property>
|
||||
<property name="width_chars">12</property>
|
||||
<property name="max_width_chars">12</property>
|
||||
<property name="primary_icon_name">document-edit-symbolic</property>
|
||||
|
||||
<signal name="changed" handler="on_non_empty_entry_changed" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
@@ -1117,7 +1115,7 @@ Author: Dmitriy Yefremov
|
||||
<property name="can_focus">True</property>
|
||||
<property name="width_chars">12</property>
|
||||
<property name="max_width_chars">12</property>
|
||||
<property name="primary_icon_name">document-edit-symbolic</property>
|
||||
|
||||
<signal name="changed" handler="on_non_empty_entry_changed" swapped="no"/>
|
||||
<signal name="key-release-event" handler="update_reference" swapped="no"/>
|
||||
</object>
|
||||
@@ -1155,7 +1153,7 @@ Author: Dmitriy Yefremov
|
||||
<property name="can_focus">True</property>
|
||||
<property name="width_chars">8</property>
|
||||
<property name="max_width_chars">10</property>
|
||||
<property name="primary_icon_name">document-edit-symbolic</property>
|
||||
|
||||
<signal name="changed" handler="on_non_empty_entry_changed" swapped="no"/>
|
||||
<signal name="key-release-event" handler="update_reference" swapped="no"/>
|
||||
</object>
|
||||
@@ -1182,7 +1180,7 @@ Author: Dmitriy Yefremov
|
||||
<property name="can_focus">True</property>
|
||||
<property name="width_chars">8</property>
|
||||
<property name="max_width_chars">10</property>
|
||||
<property name="primary_icon_name">document-edit-symbolic</property>
|
||||
|
||||
<signal name="changed" handler="on_non_empty_entry_changed" swapped="no"/>
|
||||
<signal name="key-release-event" handler="update_reference" swapped="no"/>
|
||||
</object>
|
||||
@@ -1391,7 +1389,7 @@ Author: Dmitriy Yefremov
|
||||
<property name="can_focus">True</property>
|
||||
<property name="width_chars">8</property>
|
||||
<property name="max_width_chars">10</property>
|
||||
<property name="primary_icon_name">document-edit-symbolic</property>
|
||||
|
||||
<signal name="changed" handler="on_digit_entry_changed" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
@@ -1417,7 +1415,7 @@ Author: Dmitriy Yefremov
|
||||
<property name="can_focus">True</property>
|
||||
<property name="width_chars">8</property>
|
||||
<property name="max_width_chars">10</property>
|
||||
<property name="primary_icon_name">document-edit-symbolic</property>
|
||||
|
||||
<signal name="changed" handler="on_digit_entry_changed" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
@@ -1443,7 +1441,7 @@ Author: Dmitriy Yefremov
|
||||
<property name="can_focus">True</property>
|
||||
<property name="width_chars">8</property>
|
||||
<property name="max_width_chars">10</property>
|
||||
<property name="primary_icon_name">document-edit-symbolic</property>
|
||||
|
||||
<signal name="changed" handler="on_digit_entry_changed" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
@@ -1569,7 +1567,7 @@ Author: Dmitriy Yefremov
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkImage" id="tr_edit_switch_image">
|
||||
<property name="visible">True</property>
|
||||
<property name="visible">False</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_left">10</property>
|
||||
<property name="icon_name">document-edit-symbolic</property>
|
||||
@@ -1629,7 +1627,6 @@ Author: Dmitriy Yefremov
|
||||
<property name="type_hint">dialog</property>
|
||||
<property name="skip_taskbar_hint">True</property>
|
||||
<property name="skip_pager_hint">True</property>
|
||||
<property name="gravity">center</property>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
|
||||
@@ -6,11 +6,11 @@ from app.eparser import Service
|
||||
from app.eparser.ecommons import (MODULATION, Inversion, ROLL_OFF, Pilot, Flag, Pids, POLARIZATION, get_key_by_value,
|
||||
get_value_by_name, FEC_DEFAULT, PLS_MODE, SERVICE_TYPE, T_MODULATION, C_MODULATION,
|
||||
TrType, SystemCable, T_SYSTEM, BANDWIDTH, TRANSMISSION_MODE, GUARD_INTERVAL, T_FEC,
|
||||
HIERARCHY)
|
||||
HIERARCHY, A_MODULATION)
|
||||
from app.settings import SettingsType
|
||||
from .dialogs import show_dialog, DialogType, Action, get_dialogs_string
|
||||
from .dialogs import show_dialog, DialogType, Action, get_builder
|
||||
from .main_helper import get_base_model
|
||||
from .uicommons import Gtk, Gdk, UI_RESOURCES_PATH, HIDE_ICON, TEXT_DOMAIN, CODED_ICON, Column, IS_GNOME_SESSION
|
||||
from .uicommons import Gtk, Gdk, UI_RESOURCES_PATH, HIDE_ICON, CODED_ICON, Column
|
||||
|
||||
_UI_PATH = UI_RESOURCES_PATH + "service_details_dialog.glade"
|
||||
|
||||
@@ -46,10 +46,7 @@ class ServiceDetailsDialog:
|
||||
"on_non_empty_entry_changed": self.on_non_empty_entry_changed,
|
||||
"on_cancel": lambda item: self._dialog.destroy()}
|
||||
|
||||
builder = Gtk.Builder()
|
||||
builder.set_translation_domain(TEXT_DOMAIN)
|
||||
builder.add_from_string(get_dialogs_string(_UI_PATH).format(use_header=IS_GNOME_SESSION))
|
||||
builder.connect_signals(handlers)
|
||||
builder = get_builder(_UI_PATH, handlers, use_str=True)
|
||||
self._builder = builder
|
||||
|
||||
self._dialog = builder.get_object("service_details_dialog")
|
||||
@@ -206,6 +203,8 @@ class ServiceDetailsDialog:
|
||||
self.update_ui_for_terrestrial()
|
||||
elif self._tr_type is TrType.Cable:
|
||||
self.update_ui_for_cable()
|
||||
elif self._tr_type is TrType.ATSC:
|
||||
self.update_ui_for_atsc()
|
||||
else:
|
||||
self.set_sat_positions(srv.pos)
|
||||
|
||||
@@ -307,6 +306,11 @@ class ServiceDetailsDialog:
|
||||
self.select_active_text(self._pls_mode_combo_box, HIERARCHY.get(tr_data[7]))
|
||||
self.select_active_text(self._invertion_combo_box, Inversion(tr_data[8]).name)
|
||||
self.select_active_text(self._sys_combo_box, T_SYSTEM.get(tr_data[9]))
|
||||
elif tr_type is TrType.ATSC:
|
||||
self._sys_combo_box.set_active(0)
|
||||
self.select_active_text(self._mod_combo_box, A_MODULATION.get(tr_data[2]))
|
||||
self.select_active_text(self._invertion_combo_box, Inversion(tr_data[1]).name)
|
||||
|
||||
# Should be called last to properly initialize the reference
|
||||
self._srv_type_entry.set_text(data[4])
|
||||
|
||||
@@ -379,7 +383,6 @@ class ServiceDetailsDialog:
|
||||
|
||||
def on_new(self):
|
||||
""" Create new service. """
|
||||
service = self.get_service(*self.get_srv_data(), self.get_satellite_transponder_data())
|
||||
show_dialog(DialogType.ERROR, transient=self._dialog, text="Not implemented yet!")
|
||||
return True
|
||||
|
||||
@@ -396,6 +399,8 @@ class ServiceDetailsDialog:
|
||||
transponder = self.get_terrestrial_transponder_data()
|
||||
elif self._tr_type is TrType.Cable:
|
||||
transponder = self.get_cable_transponder_data()
|
||||
elif self._tr_type is TrType.ATSC:
|
||||
transponder = self.get_atsc_transponder_data()
|
||||
except Exception as e:
|
||||
log("Edit service error: {}".format(e))
|
||||
show_dialog(DialogType.ERROR, transient=self._dialog, text="Error getting transponder parameters!")
|
||||
@@ -560,6 +565,7 @@ class ServiceDetailsDialog:
|
||||
freq = self._freq_entry.get_text()
|
||||
fec = self._fec_combo_box.get_active_id()
|
||||
system = self._sys_combo_box.get_active_id()
|
||||
o_srv = self._old_service
|
||||
|
||||
if self._tr_type is TrType.Satellite or self._s_type is SettingsType.NEUTRINO_MP:
|
||||
freq = self._freq_entry.get_text()
|
||||
@@ -567,11 +573,9 @@ class ServiceDetailsDialog:
|
||||
pol = self._pol_combo_box.get_active_id()
|
||||
pos = "{}{}".format(round(self._sat_pos_button.get_value(), 1), self._pos_side_box.get_active_id())
|
||||
return freq, rate, pol, fec, system, pos
|
||||
elif self._tr_type is TrType.Terrestrial:
|
||||
o_srv = self._old_service
|
||||
elif self._tr_type in (TrType.Terrestrial, TrType.ATSC):
|
||||
return freq, o_srv.rate, o_srv.pol, fec, system, o_srv.pos
|
||||
elif self._tr_type is TrType.Cable:
|
||||
o_srv = self._old_service
|
||||
return freq, self._rate_entry.get_text(), o_srv.pol, fec, o_srv.system, o_srv.pos
|
||||
|
||||
def get_satellite_transponder_data(self):
|
||||
@@ -625,6 +629,7 @@ class ServiceDetailsDialog:
|
||||
tr_data[8] = self.get_value_from_combobox_id(self._pls_mode_combo_box, HIERARCHY)
|
||||
tr_data[9] = get_value_by_name(Inversion, self._invertion_combo_box.get_active_id())
|
||||
tr_data[10] = self.get_value_from_combobox_id(self._sys_combo_box, T_SYSTEM)
|
||||
|
||||
return "{} {}".format(tr_data[0], ":".join(tr_data[1:]))
|
||||
|
||||
def get_cable_transponder_data(self):
|
||||
@@ -636,6 +641,16 @@ class ServiceDetailsDialog:
|
||||
tr_data[4] = self.get_value_from_combobox_id(self._mod_combo_box, C_MODULATION)
|
||||
tr_data[5] = self.get_value_from_combobox_id(self._fec_combo_box, FEC_DEFAULT)
|
||||
tr_data[6] = get_value_by_name(SystemCable, self._sys_combo_box.get_active_id())
|
||||
|
||||
return "{} {}".format(tr_data[0], ":".join(tr_data[1:]))
|
||||
|
||||
def get_atsc_transponder_data(self):
|
||||
tr_data = re.split("\s|:", self._old_service.transponder)
|
||||
# frequency, inversion, modulation, system
|
||||
tr_data[1] = "{}000".format(self._freq_entry.get_text())
|
||||
tr_data[2] = get_value_by_name(Inversion, self._invertion_combo_box.get_active_id())
|
||||
tr_data[3] = self.get_value_from_combobox_id(self._mod_combo_box, A_MODULATION)
|
||||
|
||||
return "{} {}".format(tr_data[0], ":".join(tr_data[1:]))
|
||||
|
||||
def update_transponder_services(self, transponder, sat_pos):
|
||||
@@ -695,7 +710,7 @@ class ServiceDetailsDialog:
|
||||
return
|
||||
|
||||
self.update_dvb_s2_elements(active and (self._sys_combo_box.get_active_id() == "DVB-S2"
|
||||
or self._old_service.transponder_type in "tc"))
|
||||
or self._old_service.transponder_type in "tca"))
|
||||
|
||||
for elem in self._TRANSPONDER_ELEMENTS:
|
||||
elem.set_sensitive(active)
|
||||
@@ -826,6 +841,18 @@ class ServiceDetailsDialog:
|
||||
self._transponder_id_entry.set_max_width_chars(8)
|
||||
self._network_id_entry.set_max_width_chars(8)
|
||||
|
||||
def update_ui_for_atsc(self):
|
||||
self.update_ui_for_cable()
|
||||
tr_grid = self._builder.get_object("tr_grid")
|
||||
tr_grid.remove_column(1)
|
||||
tr_grid.remove_column(1)
|
||||
# Init models
|
||||
fec_model, modulation_model, system_model = self.get_models_for_non_satellite()
|
||||
system_model.append((TrType.ATSC.name,))
|
||||
[modulation_model.append((v,)) for k, v in A_MODULATION.items()]
|
||||
# Extra
|
||||
self._namespace_entry.set_max_width_chars(25)
|
||||
|
||||
def get_transponder_grid_for_non_satellite(self):
|
||||
self._pids_grid.set_visible(False)
|
||||
tr_grid = self._builder.get_object("tr_grid")
|
||||
@@ -844,10 +871,7 @@ class ServiceDetailsDialog:
|
||||
|
||||
class TransponderServicesDialog:
|
||||
def __init__(self, transient, services_view, transponder, tr_iters):
|
||||
builder = Gtk.Builder()
|
||||
builder.set_translation_domain(TEXT_DOMAIN)
|
||||
builder.add_objects_from_string(get_dialogs_string(_UI_PATH).format(use_header=IS_GNOME_SESSION),
|
||||
("tr_services_dialog", "transponder_services_liststore"))
|
||||
builder = get_builder(_UI_PATH, use_str=True, objects=("tr_services_dialog", "transponder_services_liststore"))
|
||||
self._dialog = builder.get_object("tr_services_dialog")
|
||||
self._dialog.set_transient_for(transient)
|
||||
self._srv_model = builder.get_object("transponder_services_liststore")
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -3,10 +3,10 @@ import re
|
||||
|
||||
from app.commons import run_task, run_idle, log
|
||||
from app.connections import test_telnet, test_ftp, TestException, test_http, HttpApiException
|
||||
from app.settings import SettingsType, Settings, PlayStreamsMode
|
||||
from app.ui.dialogs import show_dialog, DialogType, get_message, get_chooser_dialog
|
||||
from app.settings import SettingsType, Settings, PlayStreamsMode, 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
|
||||
from .uicommons import Gtk, Gdk, UI_RESOURCES_PATH, FavClickMode, DEFAULT_ICON, APP_FONT
|
||||
|
||||
|
||||
def show_settings_dialog(transient, options):
|
||||
@@ -50,6 +50,7 @@ class SettingsDialog:
|
||||
"on_apply_presets": self.on_apply_presets,
|
||||
"on_digit_entry_changed": self.on_digit_entry_changed,
|
||||
"on_view_popup_menu": self.on_view_popup_menu,
|
||||
"on_list_font_reset": self.on_list_font_reset,
|
||||
"on_theme_changed": self.on_theme_changed,
|
||||
"on_theme_add": self.on_theme_add,
|
||||
"on_theme_remove": self.on_theme_remove,
|
||||
@@ -63,9 +64,7 @@ class SettingsDialog:
|
||||
self._profiles = self._settings.profiles
|
||||
self._s_type = self._settings.setting_type
|
||||
|
||||
builder = Gtk.Builder()
|
||||
builder.add_from_file(UI_RESOURCES_PATH + "settings_dialog.glade")
|
||||
builder.connect_signals(handlers)
|
||||
builder = get_builder(UI_RESOURCES_PATH + "settings_dialog.glade", handlers)
|
||||
|
||||
self._dialog = builder.get_object("settings_dialog")
|
||||
self._dialog.set_transient_for(transient)
|
||||
@@ -122,18 +121,24 @@ class SettingsDialog:
|
||||
self._play_in_built_radio_button = builder.get_object("play_in_built_radio_button")
|
||||
self._play_in_window_radio_button = builder.get_object("play_in_window_radio_button")
|
||||
self._get_m3u_radio_button = builder.get_object("get_m3u_radio_button")
|
||||
self._gst_lib_button = builder.get_object("gst_lib_button")
|
||||
self._vlc_lib_button = builder.get_object("vlc_lib_button")
|
||||
self._mpv_lib_button = builder.get_object("mpv_lib_button")
|
||||
# Program
|
||||
self._before_save_switch = builder.get_object("before_save_switch")
|
||||
self._before_downloading_switch = builder.get_object("before_downloading_switch")
|
||||
self._enable_experimental_box = builder.get_object("enable_experimental_box")
|
||||
self._colors_grid = builder.get_object("colors_grid")
|
||||
self._set_color_switch = builder.get_object("set_color_switch")
|
||||
self._new_color_button = builder.get_object("new_color_button")
|
||||
self._extra_color_button = builder.get_object("extra_color_button")
|
||||
self._load_on_startup_switch = builder.get_object("load_on_startup_switch")
|
||||
self._bouquet_hints_switch = builder.get_object("bouquet_hints_switch")
|
||||
self._services_hints_switch = builder.get_object("services_hints_switch")
|
||||
self._lang_combo_box = builder.get_object("lang_combo_box")
|
||||
# Appearance
|
||||
self._list_font_button = builder.get_object("list_font_button")
|
||||
self._picons_size_button = builder.get_object("picons_size_button")
|
||||
self._tooltip_logo_size_button = builder.get_object("tooltip_logo_size_button")
|
||||
self._colors_grid = builder.get_object("colors_grid")
|
||||
self._set_color_switch = builder.get_object("set_color_switch")
|
||||
self._new_color_button = builder.get_object("new_color_button")
|
||||
self._extra_color_button = builder.get_object("extra_color_button")
|
||||
# Extra
|
||||
self._support_http_api_switch = builder.get_object("support_http_api_switch")
|
||||
self._enable_yt_dl_switch = builder.get_object("enable_yt_dl_switch")
|
||||
@@ -175,19 +180,24 @@ class SettingsDialog:
|
||||
self.init_ui_elements(self._s_type)
|
||||
self.init_profiles()
|
||||
|
||||
if self._settings.is_darwin:
|
||||
# Appearance
|
||||
self._appearance_box = builder.get_object("appearance_box")
|
||||
self._appearance_box.set_visible(True)
|
||||
if IS_WIN or True:
|
||||
self._gst_lib_button.set_visible(False)
|
||||
self._vlc_lib_button.set_sensitive(self._settings.is_enable_experimental)
|
||||
# Themes
|
||||
enable_exp = self._settings.is_enable_experimental
|
||||
builder.get_object("style_frame").set_visible(enable_exp)
|
||||
builder.get_object("themes_support_frame").set_visible(enable_exp)
|
||||
self._layout_switch = builder.get_object("layout_switch")
|
||||
self._layout_switch.set_active(self._ext_settings.alternate_layout)
|
||||
self._theme_frame = builder.get_object("theme_frame")
|
||||
self._theme_frame.set_visible(enable_exp)
|
||||
self._theme_thumbnail_image = builder.get_object("theme_thumbnail_image")
|
||||
self._theme_combo_box = builder.get_object("theme_combo_box")
|
||||
self._icon_theme_combo_box = builder.get_object("icon_theme_combo_box")
|
||||
self._dark_mode_switch = builder.get_object("dark_mode_switch")
|
||||
self._layout_switch = builder.get_object("layout_switch")
|
||||
self._themes_support_switch = builder.get_object("themes_support_switch")
|
||||
self._themes_support_switch.bind_property("active", builder.get_object("gtk_theme_frame"), "sensitive")
|
||||
self._themes_support_switch.bind_property("active", builder.get_object("icon_theme_frame"), "sensitive")
|
||||
self.init_appearance()
|
||||
self._themes_support_switch.bind_property("active", self._theme_frame, "sensitive")
|
||||
self.init_themes()
|
||||
|
||||
@run_idle
|
||||
def init_ui_elements(self, s_type):
|
||||
@@ -266,6 +276,7 @@ class SettingsDialog:
|
||||
self._before_downloading_switch.set_active(self._settings.backup_before_downloading)
|
||||
self.set_fav_click_mode(self._settings.fav_click_mode)
|
||||
self.set_play_stream_mode(self._settings.play_streams_mode)
|
||||
self.set_stream_lib(self._settings.stream_lib)
|
||||
self._load_on_startup_switch.set_active(self._settings.load_last_config)
|
||||
self._bouquet_hints_switch.set_active(self._settings.show_bq_hints)
|
||||
self._services_hints_switch.set_active(self._settings.show_srv_hints)
|
||||
@@ -273,6 +284,9 @@ class SettingsDialog:
|
||||
self._transcoding_switch.set_active(self._settings.activate_transcoding)
|
||||
self._presets_combo_box.set_active_id(self._settings.active_preset)
|
||||
self.on_transcoding_preset_changed(self._presets_combo_box)
|
||||
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)
|
||||
|
||||
if self._s_type is SettingsType.ENIGMA_2:
|
||||
self._enable_exp_switch.set_active(self._settings.is_enable_experimental)
|
||||
@@ -328,6 +342,7 @@ class SettingsDialog:
|
||||
self._ext_settings.backup_before_downloading = self._before_downloading_switch.get_active()
|
||||
self._ext_settings.fav_click_mode = self.get_fav_click_mode()
|
||||
self._ext_settings.play_streams_mode = self.get_play_stream_mode()
|
||||
self._ext_settings.stream_lib = self.get_stream_lib()
|
||||
self._ext_settings.language = self._lang_combo_box.get_active_id()
|
||||
self._ext_settings.load_last_config = self._load_on_startup_switch.get_active()
|
||||
self._ext_settings.show_bq_hints = self._bouquet_hints_switch.get_active()
|
||||
@@ -337,9 +352,11 @@ class SettingsDialog:
|
||||
self._ext_settings.records_path = self._record_data_dir_field.get_text()
|
||||
self._ext_settings.activate_transcoding = self._transcoding_switch.get_active()
|
||||
self._ext_settings.active_preset = self._presets_combo_box.get_active_id()
|
||||
self._ext_settings.list_picon_size = int(self._picons_size_button.get_active_id())
|
||||
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()
|
||||
|
||||
if self._ext_settings.is_darwin:
|
||||
self._ext_settings.dark_mode = self._dark_mode_switch.get_active()
|
||||
if IS_WIN:
|
||||
self._ext_settings.alternate_layout = self._layout_switch.get_active()
|
||||
self._ext_settings.is_themes_support = self._themes_support_switch.get_active()
|
||||
self._ext_settings.theme = self._theme_combo_box.get_active_id()
|
||||
@@ -594,19 +611,26 @@ class SettingsDialog:
|
||||
return FavClickMode.DISABLED
|
||||
|
||||
def on_play_mode_changed(self, button):
|
||||
if self._main_stack.get_visible_child_name() != "streaming":
|
||||
if self._main_stack.get_visible_child_name() != "streaming" or not button.get_active():
|
||||
return
|
||||
|
||||
if self._settings.is_darwin:
|
||||
is_gst = self._gst_lib_button.get_active()
|
||||
self._play_in_built_radio_button.set_sensitive(is_gst)
|
||||
self._play_in_window_radio_button.set_active(not is_gst and self._play_in_built_radio_button.get_active())
|
||||
|
||||
if button.get_active():
|
||||
self.show_info_message("Save and restart the program to apply the settings.", Gtk.MessageType.WARNING)
|
||||
|
||||
@run_idle
|
||||
def set_play_stream_mode(self, mode):
|
||||
self._play_in_built_radio_button.set_sensitive(not self._settings.is_darwin)
|
||||
self._play_in_built_radio_button.set_active(mode is PlayStreamsMode.BUILT_IN)
|
||||
self._play_in_window_radio_button.set_active(mode is PlayStreamsMode.WINDOW)
|
||||
self._get_m3u_radio_button.set_active(mode is PlayStreamsMode.M3U)
|
||||
|
||||
if self._settings.is_darwin and self._settings.stream_lib != "gst":
|
||||
self._play_in_built_radio_button.set_sensitive(False)
|
||||
|
||||
def get_play_stream_mode(self):
|
||||
if self._play_in_built_radio_button.get_active():
|
||||
return PlayStreamsMode.BUILT_IN
|
||||
@@ -617,6 +641,18 @@ class SettingsDialog:
|
||||
|
||||
return self._settings.play_streams_mode
|
||||
|
||||
def set_stream_lib(self, mode):
|
||||
self._vlc_lib_button.set_active(mode == "vlc")
|
||||
self._gst_lib_button.set_active(mode == "gst")
|
||||
self._mpv_lib_button.set_active(mode == "mpv")
|
||||
|
||||
def get_stream_lib(self):
|
||||
if self._gst_lib_button.get_active():
|
||||
return "gst"
|
||||
elif self._vlc_lib_button.get_active():
|
||||
return "vlc"
|
||||
return "mpv"
|
||||
|
||||
def on_transcoding_preset_changed(self, button):
|
||||
presets = self._settings.transcoding_presets
|
||||
prs = presets.get(button.get_active_id())
|
||||
@@ -661,6 +697,11 @@ class SettingsDialog:
|
||||
if event.get_event_type() == Gdk.EventType.BUTTON_PRESS and event.button == Gdk.BUTTON_SECONDARY:
|
||||
menu.popup(None, None, None, None, event.button, event.time)
|
||||
|
||||
def on_list_font_reset(self, button):
|
||||
self._list_font_button.set_font(APP_FONT)
|
||||
|
||||
# ******************* Themes *********************** #
|
||||
|
||||
def on_theme_changed(self, button):
|
||||
if self._main_stack.get_visible_child_name() != "appearance":
|
||||
return
|
||||
@@ -699,7 +740,7 @@ class SettingsDialog:
|
||||
response = get_chooser_dialog(self._dialog, self._settings, "Themes Archive [*.xz, *.zip]", ("*.xz", "*.zip"))
|
||||
if response in (Gtk.ResponseType.CANCEL, Gtk.ResponseType.DELETE_EVENT):
|
||||
return
|
||||
self._appearance_box.set_sensitive(False)
|
||||
self._theme_frame.set_sensitive(False)
|
||||
self.unpack_theme(response, path, button)
|
||||
|
||||
@run_task
|
||||
@@ -716,7 +757,6 @@ class SettingsDialog:
|
||||
log("Unpacking end.")
|
||||
finally:
|
||||
self.update_theme_button(button, dst)
|
||||
self._appearance_box.set_sensitive(True)
|
||||
|
||||
@run_idle
|
||||
def update_theme_button(self, button, dst):
|
||||
@@ -729,6 +769,7 @@ class SettingsDialog:
|
||||
button.append(theme, theme)
|
||||
button.set_active_id(theme)
|
||||
self.show_info_message("Done!", Gtk.MessageType.INFO)
|
||||
self._theme_frame.set_sensitive(True)
|
||||
|
||||
@run_idle
|
||||
def remove_theme(self, button, path):
|
||||
@@ -752,9 +793,7 @@ class SettingsDialog:
|
||||
button.set_active(0)
|
||||
|
||||
@run_idle
|
||||
def init_appearance(self):
|
||||
self._dark_mode_switch.set_active(self._ext_settings.dark_mode)
|
||||
self._layout_switch.set_active(self._ext_settings.alternate_layout)
|
||||
def init_themes(self):
|
||||
t_support = self._ext_settings.is_themes_support
|
||||
self._themes_support_switch.set_active(t_support)
|
||||
if t_support:
|
||||
|
||||
@@ -1,10 +1,16 @@
|
||||
#digit-entry {
|
||||
border-color: Red;
|
||||
border-width: 0.15em;
|
||||
}
|
||||
|
||||
#status-bar-button {
|
||||
margin: 0.1em;
|
||||
padding: 1px;
|
||||
margin: 1px;
|
||||
}
|
||||
|
||||
paned > separator {
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
background-size: 2px 24px;
|
||||
}
|
||||
|
||||
.red-button {
|
||||
@@ -27,27 +33,14 @@
|
||||
background-color: blue;
|
||||
}
|
||||
|
||||
#textview-large {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.time-entry {
|
||||
padding: 0px;
|
||||
margin: 0px;
|
||||
}
|
||||
|
||||
.arrow-button {
|
||||
padding: 0px;
|
||||
margin: 1px;
|
||||
min-width: 12px;
|
||||
min-height: 12px;
|
||||
}
|
||||
|
||||
.group {}
|
||||
|
||||
.group :first-child {
|
||||
padding-left: 0.5em;
|
||||
padding-right: 0.5em;
|
||||
border-top-right-radius: 0;
|
||||
border-bottom-right-radius: 0;
|
||||
}
|
||||
|
||||
@@ -42,7 +42,6 @@ Author: Dmitriy Yefremov
|
||||
<property name="skip_taskbar_hint">True</property>
|
||||
<property name="skip_pager_hint">True</property>
|
||||
<property name="decorated">False</property>
|
||||
<property name="gravity">center</property>
|
||||
<property name="has_resize_grip">True</property>
|
||||
<child>
|
||||
<placeholder/>
|
||||
|
||||
@@ -5,9 +5,10 @@ import gi
|
||||
from gi.repository import GLib
|
||||
|
||||
from app.commons import log
|
||||
from app.settings import IS_DARWIN
|
||||
from app.connections import HttpAPI
|
||||
from app.settings import IS_WIN
|
||||
from app.tools.yt import YouTube
|
||||
from app.ui.dialogs import get_builder
|
||||
from app.ui.iptv import get_yt_icon
|
||||
from .uicommons import Gtk, Gdk, UI_RESOURCES_PATH
|
||||
|
||||
@@ -35,9 +36,7 @@ class LinksTransmitter:
|
||||
self._app_window = app_window
|
||||
self._is_status_icon = True
|
||||
|
||||
builder = Gtk.Builder()
|
||||
builder.add_from_file(UI_RESOURCES_PATH + "transmitter.glade")
|
||||
builder.connect_signals(handlers)
|
||||
builder = get_builder(UI_RESOURCES_PATH + "transmitter.glade", handlers)
|
||||
|
||||
self._main_window = builder.get_object("main_window")
|
||||
self._url_entry = builder.get_object("url_entry")
|
||||
@@ -48,7 +47,7 @@ class LinksTransmitter:
|
||||
self._status_passive = None
|
||||
self._yt = YouTube.get_instance(settings)
|
||||
|
||||
if IS_DARWIN:
|
||||
if IS_WIN:
|
||||
self._tray = builder.get_object("status_icon")
|
||||
else:
|
||||
try:
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import locale
|
||||
import os
|
||||
from enum import Enum, IntEnum
|
||||
from functools import lru_cache
|
||||
from app.settings import Settings, SettingsException, IS_DARWIN
|
||||
|
||||
from app.settings import Settings, SettingsException, IS_WIN, SEP
|
||||
|
||||
import gi
|
||||
|
||||
@@ -10,59 +12,35 @@ gi.require_version("Gdk", "3.0")
|
||||
from gi.repository import Gtk, Gdk, GLib
|
||||
|
||||
# Setting mod mask for keyboard depending on platform
|
||||
MOD_MASK = Gdk.ModifierType.MOD2_MASK if IS_DARWIN else Gdk.ModifierType.CONTROL_MASK
|
||||
MOD_MASK = Gdk.ModifierType.CONTROL_MASK
|
||||
# Path to *.glade files
|
||||
UI_RESOURCES_PATH = "app/ui/" if os.path.exists("app/ui/") else "ui/"
|
||||
UI_PATH = "app{}ui{}".format(SEP, SEP)
|
||||
UI_RESOURCES_PATH = UI_PATH if os.path.exists(UI_PATH) else "ui{}".format(SEP)
|
||||
LANG_PATH = UI_RESOURCES_PATH + "lang"
|
||||
GTK_PATH = os.environ.get("GTK_PATH", None)
|
||||
NOTIFY_IS_INIT = False
|
||||
IS_GNOME_SESSION = int(bool(os.environ.get("GNOME_DESKTOP_SESSION_ID")))
|
||||
# Translation.
|
||||
TEXT_DOMAIN = "demon-editor"
|
||||
APP_FONT = None
|
||||
|
||||
try:
|
||||
settings = Settings.get_instance()
|
||||
except SettingsException:
|
||||
pass
|
||||
else:
|
||||
locale.setlocale(locale.LC_NUMERIC, "C")
|
||||
os.environ["LANGUAGE"] = settings.language
|
||||
|
||||
st = Gtk.Settings().get_default()
|
||||
st.set_property("gtk-application-prefer-dark-theme", settings.dark_mode)
|
||||
APP_FONT = st.get_property("gtk-font-name")
|
||||
|
||||
if settings.is_themes_support:
|
||||
st.set_property("gtk-theme-name", settings.theme)
|
||||
st.set_property("gtk-icon-theme-name", settings.icon_theme)
|
||||
else:
|
||||
style_provider = Gtk.CssProvider()
|
||||
s_path = "{}default_style.css".format(GTK_PATH + "/" + UI_RESOURCES_PATH if GTK_PATH else UI_RESOURCES_PATH)
|
||||
style_provider.load_from_path(s_path)
|
||||
Gtk.StyleContext.add_provider_for_screen(Gdk.Screen.get_default(), style_provider,
|
||||
Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION)
|
||||
|
||||
if IS_DARWIN:
|
||||
import gettext
|
||||
|
||||
if GTK_PATH:
|
||||
LANG_PATH = GTK_PATH + "/share/locale"
|
||||
gettext.bindtextdomain(TEXT_DOMAIN, LANG_PATH)
|
||||
# For launching from the bundle.
|
||||
if os.getcwd() == "/" and GTK_PATH:
|
||||
os.chdir(GTK_PATH)
|
||||
else:
|
||||
import locale
|
||||
|
||||
locale.bindtextdomain(TEXT_DOMAIN, LANG_PATH)
|
||||
# Init notify
|
||||
try:
|
||||
gi.require_version("Notify", "0.7")
|
||||
from gi.repository import Notify
|
||||
except ImportError:
|
||||
pass
|
||||
else:
|
||||
NOTIFY_IS_INIT = Notify.init("DemonEditor")
|
||||
|
||||
theme = Gtk.IconTheme.get_default()
|
||||
theme.append_search_path(GTK_PATH + "/share/icons" if GTK_PATH else UI_RESOURCES_PATH + "icons")
|
||||
theme.append_search_path(GTK_PATH + "{}share{}icons".format(SEP, SEP) if GTK_PATH else UI_RESOURCES_PATH + "icons")
|
||||
|
||||
|
||||
def get_theme_icon(icon_theme, name, size):
|
||||
@@ -93,9 +71,11 @@ def get_yt_icon(icon_name, size=24):
|
||||
return default_theme.load_icon(icon_name, size, 0)
|
||||
|
||||
n_theme = Gtk.IconTheme.new()
|
||||
p_path = "{}usr{}share{}icons{}*".format(SEP, SEP, SEP, SEP)
|
||||
|
||||
import glob
|
||||
|
||||
for theme_name in map(os.path.basename, filter(os.path.isdir, glob.glob("/usr/share/icons/*"))):
|
||||
for theme_name in map(os.path.basename, filter(os.path.isdir, glob.glob(p_path))):
|
||||
theme.set_custom_theme(theme_name)
|
||||
if n_theme.has_icon(icon_name):
|
||||
return n_theme.load_icon(icon_name, size, 0)
|
||||
@@ -111,47 +91,38 @@ def show_notification(message, timeout=10000, urgency=1):
|
||||
@param timeout: milliseconds
|
||||
@param urgency: 0 - low, 1 - normal, 2 - critical
|
||||
"""
|
||||
if IS_DARWIN:
|
||||
# Since NSUserNotification has been deprecated, osascript will be used.
|
||||
os.system("""osascript -e 'display notification "{}" with title "DemonEditor"'""".format(message))
|
||||
elif NOTIFY_IS_INIT:
|
||||
notify = Notify.Notification.new("DemonEditor", message, "demon-editor")
|
||||
notify.set_urgency(urgency)
|
||||
notify.set_timeout(timeout)
|
||||
notify.show()
|
||||
pass
|
||||
|
||||
|
||||
class KeyboardKey(Enum):
|
||||
""" The raw(hardware) codes of the keyboard keys. """
|
||||
F = 3 if IS_DARWIN else 41
|
||||
E = 14 if IS_DARWIN else 26
|
||||
R = 15 if IS_DARWIN else 27
|
||||
T = 17 if IS_DARWIN else 28
|
||||
P = 35 if IS_DARWIN else 33
|
||||
S = 1 if IS_DARWIN else 39
|
||||
H = 4 if IS_DARWIN else 43
|
||||
L = 37 if IS_DARWIN else 46
|
||||
X = 7 if IS_DARWIN else 53
|
||||
C = 8 if IS_DARWIN else 54
|
||||
V = 9 if IS_DARWIN else 55
|
||||
W = 13 if IS_DARWIN else 25
|
||||
Z = 6 if IS_DARWIN else 52
|
||||
INSERT = -1 if IS_DARWIN else 118
|
||||
HOME = -1 if IS_DARWIN else 110
|
||||
END = -1 if IS_DARWIN else 115
|
||||
UP = 126 if IS_DARWIN else 111
|
||||
DOWN = 125 if IS_DARWIN else 116
|
||||
PAGE_UP = -1 if IS_DARWIN else 112
|
||||
PAGE_DOWN = -1 if IS_DARWIN else 117
|
||||
LEFT = 123 if IS_DARWIN else 113
|
||||
RIGHT = 123 if IS_DARWIN else 114
|
||||
F2 = 120 if IS_DARWIN else 68
|
||||
F7 = 98 if IS_DARWIN else 73
|
||||
SPACE = 49 if IS_DARWIN else 65
|
||||
DELETE = 51 if IS_DARWIN else 119
|
||||
BACK_SPACE = 76 if IS_DARWIN else 22
|
||||
CTRL_L = 55 if IS_DARWIN else 37
|
||||
CTRL_R = 55 if IS_DARWIN else 105
|
||||
E = 69 if IS_WIN else 26
|
||||
R = 82 if IS_WIN else 27
|
||||
T = 84 if IS_WIN else 28
|
||||
P = 80 if IS_WIN else 33
|
||||
S = 83 if IS_WIN else 39
|
||||
F = 70 if IS_WIN else 41
|
||||
X = 88 if IS_WIN else 53
|
||||
C = 67 if IS_WIN else 54
|
||||
V = 86 if IS_WIN else 55
|
||||
W = 87 if IS_WIN else 25
|
||||
Z = 90 if IS_WIN else 52
|
||||
INSERT = 45 if IS_WIN else 118
|
||||
HOME = 36 if IS_WIN else 110
|
||||
END = 35 if IS_WIN else 115
|
||||
UP = 38 if IS_WIN else 111
|
||||
DOWN = 40 if IS_WIN else 116
|
||||
PAGE_UP = 33 if IS_WIN else 112
|
||||
PAGE_DOWN = 34 if IS_WIN else 117
|
||||
LEFT = 37 if IS_WIN else 113
|
||||
RIGHT = 39 if IS_WIN else 114
|
||||
F2 = 113 if IS_WIN else 68
|
||||
F7 = 118 if IS_WIN else 73
|
||||
SPACE = 32 if IS_WIN else 65
|
||||
DELETE = 46 if IS_WIN else 119
|
||||
BACK_SPACE = 8 if IS_WIN else 22
|
||||
CTRL_L = 17 if IS_WIN else 37
|
||||
CTRL_R = 163 if IS_WIN else 105
|
||||
# Laptop codes
|
||||
HOME_KP = 79
|
||||
END_KP = 87
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Copyright (C) 2018-2020 Dmitriy Yefremov
|
||||
# Copyright (C) 2018-2021 Dmitriy Yefremov
|
||||
# This file is distributed under the MIT license.
|
||||
#
|
||||
#
|
||||
@@ -909,7 +909,7 @@ msgid "Sample rate (Hz):"
|
||||
msgstr "Частата дыскр. (Гц):"
|
||||
|
||||
msgid "Play streams mode:"
|
||||
msgstr "Рэжым прайгравання струменяў:"
|
||||
msgstr "Рэжым прайгравання патокаў:"
|
||||
|
||||
msgid "Built-in player"
|
||||
msgstr "Убудаваны плэер"
|
||||
@@ -1216,3 +1216,15 @@ msgstr "Загрузіць пiконы"
|
||||
|
||||
msgid "Errors:"
|
||||
msgstr "Памылак:"
|
||||
|
||||
msgid "Use to play streams:"
|
||||
msgstr "Скарыстаць для прайгравання патокаў:"
|
||||
|
||||
msgid "Font in the lists:"
|
||||
msgstr "Шрыфт у спісах:"
|
||||
|
||||
msgid "Picons size in the lists:"
|
||||
msgstr "Памер пiконаў у спісах:"
|
||||
|
||||
msgid "Logo size in tooltips:"
|
||||
msgstr "Памер лагатыпа ва ўсплыўных падказках:"
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
# Copyright (C) 2018-2020 Dmitriy Yefremov
|
||||
# Copyright (C) 2018-2021 Dmitriy Yefremov
|
||||
# This file is distributed under the MIT license.
|
||||
#
|
||||
# Charly, 2019.
|
||||
# Dmitriy Yefremov, 2020.
|
||||
# Dmitriy Yefremov, 2020-2021.
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Last-Translator: Dmitriy Yefremov\n"
|
||||
"Last-Translator: Thomas Schmidt\n"
|
||||
"Language: de\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
|
||||
msgid "translator-credits"
|
||||
msgstr "Charly\nDmitriy Yefremov"
|
||||
msgstr "Charly\nDmitriy Yefremov\nThomas Schmidt"
|
||||
|
||||
# Main
|
||||
msgid "Service"
|
||||
@@ -31,7 +31,7 @@ msgid "Freq"
|
||||
msgstr "Freq"
|
||||
|
||||
msgid "Rate"
|
||||
msgstr "Bewertung"
|
||||
msgstr "SR"
|
||||
|
||||
msgid "Pol"
|
||||
msgstr "Pol."
|
||||
@@ -666,16 +666,16 @@ msgid "Test connection"
|
||||
msgstr "Test Verbindung"
|
||||
|
||||
msgid "Double click on the service in the bouquet list:"
|
||||
msgstr "Doppelklicke auf das Service in der Bouquetliste:"
|
||||
msgstr "Doppelklick auf das Service in der Bouquet-Liste:"
|
||||
|
||||
msgid "Zap"
|
||||
msgstr "Zap"
|
||||
|
||||
msgid "Play stream"
|
||||
msgstr "Play Stream"
|
||||
msgstr "Stream abspielen"
|
||||
|
||||
msgid "Disabled"
|
||||
msgstr "Ausgeschaltet"
|
||||
msgstr "Deaktiviert"
|
||||
|
||||
msgid "Enable lamedb ver. 5 support"
|
||||
msgstr "Lamedb ver. 5 Unterstützung aktivieren"
|
||||
@@ -693,7 +693,7 @@ msgid "Play IPTV or other stream in the program(Ctrl + P)"
|
||||
msgstr "Wiedergabe von IPTV oder anderen Streams im Programm(Strg + P)"
|
||||
|
||||
msgid "Export to m3u"
|
||||
msgstr "Export nach m3u"
|
||||
msgstr "Exportieren nach m3u"
|
||||
|
||||
msgid "EPG configuration"
|
||||
msgstr "EPG Konfiguration"
|
||||
@@ -720,13 +720,13 @@ msgid "Url to *.xml.gz file:"
|
||||
msgstr "Url zur *.xml.gz Datei:"
|
||||
|
||||
msgid "Enable filtering"
|
||||
msgstr "Filterung einschalten"
|
||||
msgstr "Filter aktivieren"
|
||||
|
||||
msgid "Filter by presence in the epg.dat file."
|
||||
msgstr "Filtern nach dem Vorhandensein in der epg.dat Datei."
|
||||
|
||||
msgid "Paths to the epg.dat file:"
|
||||
msgstr "Pfade zur epg.dat Datei:"
|
||||
msgstr "Pfad zur epg.dat Datei:"
|
||||
|
||||
msgid "Local path:"
|
||||
msgstr "Local path:"
|
||||
@@ -750,7 +750,7 @@ msgid "Unsupported file type:"
|
||||
msgstr "Nicht unterstützter Dateityp:"
|
||||
|
||||
msgid "Unpacking data error."
|
||||
msgstr "Fehler beim Entpacken von Daten."
|
||||
msgstr "Fehler beim Entpacken der Daten."
|
||||
|
||||
msgid "XML parsing error:"
|
||||
msgstr "XML Parsing-Fehler:"
|
||||
@@ -765,7 +765,7 @@ msgid "Use HTTP"
|
||||
msgstr "HTTP verwenden"
|
||||
|
||||
msgid "Close playback"
|
||||
msgstr "Wiedergabe schliessen"
|
||||
msgstr "Wiedergabe schließen"
|
||||
|
||||
msgid "Import YouTube playlist"
|
||||
msgstr "YouTube-Wiedergabeliste importieren"
|
||||
@@ -774,8 +774,8 @@ msgid ""
|
||||
"Found a link to the YouTube resource!\n"
|
||||
"Try to get a direct link to the video?"
|
||||
msgstr ""
|
||||
"Ich habe einen Link zur YouTube-Ressource gefunden!\n"
|
||||
"Versuchen einen direkten Link zum Video zu bekommen?"
|
||||
"Link zur YouTube-Ressource gefunden!\n"
|
||||
"Soll versucht werden, einen Direkt-Link zum Video zu erzeugen?"
|
||||
|
||||
msgid "Playlist import"
|
||||
msgstr "Playlist-Import"
|
||||
@@ -784,7 +784,7 @@ msgid "Getting link error:"
|
||||
msgstr "Link-Fehler erhalten:"
|
||||
|
||||
msgid "Extra"
|
||||
msgstr "Extra"
|
||||
msgstr "Extras"
|
||||
|
||||
msgid "Apply profile settings"
|
||||
msgstr "Profileinstellungen anwenden"
|
||||
@@ -793,7 +793,7 @@ msgid "Settings type:"
|
||||
msgstr "Art der Einstellungen:"
|
||||
|
||||
msgid "Set default"
|
||||
msgstr "Standard setzen"
|
||||
msgstr "Standard wiederherstellen"
|
||||
|
||||
msgid "Language:"
|
||||
msgstr "Sprache:"
|
||||
@@ -805,16 +805,16 @@ msgid "Enable direct playback bar"
|
||||
msgstr "Aktivieren der direkten Wiedergabeleiste"
|
||||
|
||||
msgid "Enables direct sending and playback of media links on the receiver"
|
||||
msgstr "Ermöglicht das direkte Senden und Abspielen von Medienlinks auf dem Box"
|
||||
msgstr "Ermöglicht das direkte Senden und Abspielen von Medienlinks auf dem Receiver"
|
||||
|
||||
msgid "Watch the channel in the program"
|
||||
msgstr "Gucken den Kanal im Programm an"
|
||||
msgstr "Kanal im Programm ansehen"
|
||||
|
||||
msgid "Zap and Play"
|
||||
msgstr "Zap und Abspielen"
|
||||
|
||||
msgid "Drag or paste the link here"
|
||||
msgstr "Ziehe den Link hierher oder füge ihn ein"
|
||||
msgstr "Link hineinziehen oder einfügen"
|
||||
|
||||
msgid "Remove added links in the playlist"
|
||||
msgstr "Hinzugefügte Links in der Wiedergabeliste entfernen"
|
||||
@@ -832,13 +832,13 @@ msgid "Reset"
|
||||
msgstr "Reset"
|
||||
|
||||
msgid "File"
|
||||
msgstr "Ablage"
|
||||
msgstr "Datei"
|
||||
|
||||
msgid "Picons manager"
|
||||
msgstr "Picons-Manager"
|
||||
|
||||
msgid "Explorer"
|
||||
msgstr ""
|
||||
msgstr "Explorer"
|
||||
|
||||
msgid "Satellite url:"
|
||||
msgstr "Satellit URL:"
|
||||
@@ -916,13 +916,13 @@ msgid "Height (px):"
|
||||
msgstr "Höhe (px):"
|
||||
|
||||
msgid "Channels:"
|
||||
msgstr "Kanälen:"
|
||||
msgstr "Kanäle:"
|
||||
|
||||
msgid "Sample rate (Hz):"
|
||||
msgstr "Samplerate (Hz):"
|
||||
msgstr "Sample-Rate (Hz):"
|
||||
|
||||
msgid "Play streams mode:"
|
||||
msgstr "Streams Abspielen-Modus:"
|
||||
msgstr "Stream-Abspielmodus:"
|
||||
|
||||
msgid "Built-in player"
|
||||
msgstr "Integrierter Player"
|
||||
@@ -934,22 +934,22 @@ msgid "Only get m3u file"
|
||||
msgstr "Nur m3u-Datei erhalten"
|
||||
|
||||
msgid "Save and restart the program to apply the settings."
|
||||
msgstr "Speicher und starte das Programm neu, um die Einstellungen zu übernehmen."
|
||||
msgstr "Änderungen speichern und anschließend das Programm neustarten, um die Einstellungen zu übernehmen."
|
||||
|
||||
msgid "Some images may have problems displaying the favorites list!"
|
||||
msgstr "Einige Images können Probleme mit der Anzeige der Favoritenliste haben!"
|
||||
msgstr "Einige Bilder können Probleme in der Anzeige der Favoritenliste haben!"
|
||||
|
||||
msgid "Operates in standby mode or current active transponder!"
|
||||
msgstr "Arbeitet im Standby-Modus oder auf dem aktuell aktiven Transponder!"
|
||||
msgstr "Arbeitet im Standby-Modus oder der Transponder ist bereits in Verwendung!"
|
||||
|
||||
msgid "No connection to the receiver!"
|
||||
msgstr "Keine Verbindung zum Box!"
|
||||
msgstr "Keine Verbindung zum Receiver!"
|
||||
|
||||
msgid "Signal level"
|
||||
msgstr "Signalpegel"
|
||||
|
||||
msgid "Receiver info"
|
||||
msgstr "Box-Info"
|
||||
msgstr "Receiver-Info"
|
||||
|
||||
msgid "A profile with that name exists!"
|
||||
msgstr "Ein Profil mit diesem Namen existiert!"
|
||||
@@ -958,10 +958,10 @@ msgid "Show short info as hints in the main services list"
|
||||
msgstr "Kurzinfos als Tooltips in der Hauptliste anzeigen"
|
||||
|
||||
msgid "Show detailed info as hints in the bouquet list"
|
||||
msgstr "Detaillierteinfos als Tooltips in der Bouquetliste anzeigen"
|
||||
msgstr "Detaillierte Informationen als Tooltips in der Bouquetliste anzeigen"
|
||||
|
||||
msgid "Enable alternate bouquet file naming"
|
||||
msgstr "Aktivieren der Alternativerbenennung für Bouquet-Dateien"
|
||||
msgstr "Aktivieren der alternativen Benennung für Bouquet-Dateien"
|
||||
|
||||
msgid "Allows you to name bouquet files using their names."
|
||||
msgstr "Ermöglicht Bouquet-Dateien mit ihren Namen zu benennen."
|
||||
@@ -970,7 +970,7 @@ msgid "Appearance"
|
||||
msgstr "Aussehen"
|
||||
|
||||
msgid "Enable Themes support"
|
||||
msgstr "Unterstützung von Themen aktivieren"
|
||||
msgstr "Theme Unterstützung aktivieren"
|
||||
|
||||
msgid "Gtk3 Theme:"
|
||||
msgstr "Gtk3-Theme:"
|
||||
@@ -979,7 +979,7 @@ msgid "Icon Theme:"
|
||||
msgstr "Icon-Theme:"
|
||||
|
||||
msgid "Gtk3 Themes and Icons:"
|
||||
msgstr "Gtk3 Themes and Icons:"
|
||||
msgstr "Gtk3 Themes und Icons:"
|
||||
|
||||
msgid "Deleting data..."
|
||||
msgstr "Daten löschen..."
|
||||
@@ -988,7 +988,7 @@ msgid "Download from the receiver"
|
||||
msgstr "Downloaden vom Receiver"
|
||||
|
||||
msgid "Remove all picons from the receiver"
|
||||
msgstr "Alle Picons aus dem Receiver entfernen"
|
||||
msgstr "Alle Picons vom dem Receiver entfernen"
|
||||
|
||||
msgid "Service reference"
|
||||
msgstr "Kanalreferenz"
|
||||
@@ -1021,7 +1021,7 @@ msgid "Are you sure you want to change the order\n\t of services in this bouquet
|
||||
msgstr "Bist du sicher, dass du die Reihenfolge der Dienstleistungen\n\t in diesem Bouquet ändern willst?"
|
||||
|
||||
msgid "Remove from the receiver"
|
||||
msgstr "Aus dem Receiver entfernen"
|
||||
msgstr "Vom Receiver entfernen"
|
||||
|
||||
msgid "Screenshot"
|
||||
msgstr "Screenshot"
|
||||
@@ -1039,7 +1039,7 @@ msgid "Can't Playback!"
|
||||
msgstr "Kann nicht abgespielt werden!"
|
||||
|
||||
msgid "Enable Dark Mode"
|
||||
msgstr "Dunkelmodus aktivieren"
|
||||
msgstr "Dunkler Modus aktivieren"
|
||||
|
||||
msgid "Extract..."
|
||||
msgstr "Entpacken..."
|
||||
@@ -1051,7 +1051,7 @@ msgid "Combine with the current data?"
|
||||
msgstr "Mit den aktuellen Daten kombinieren?"
|
||||
|
||||
msgid "Importing data done!"
|
||||
msgstr "Daten importieren erledigt!"
|
||||
msgstr "Daten-Import abgeschlossen!"
|
||||
|
||||
msgid "Current service"
|
||||
msgstr "Aktueller Service"
|
||||
@@ -1063,13 +1063,13 @@ msgid "Open archive"
|
||||
msgstr "Archiv öffnen"
|
||||
|
||||
msgid "Import from Web"
|
||||
msgstr "Import aus dem Web"
|
||||
msgstr "Import aus dem Internet"
|
||||
|
||||
msgid "Control"
|
||||
msgstr "Steuerung"
|
||||
|
||||
msgid "Timers"
|
||||
msgstr "Timers"
|
||||
msgstr "Timer"
|
||||
|
||||
msgid "Timer"
|
||||
msgstr "Timer"
|
||||
@@ -1111,7 +1111,7 @@ msgid "Auto"
|
||||
msgstr "Auto"
|
||||
|
||||
msgid "Grab screenshot"
|
||||
msgstr "Screenshot schnappen"
|
||||
msgstr "Screenshot aufnehmen"
|
||||
|
||||
msgid "Enabled:"
|
||||
msgstr "Aktiviert:"
|
||||
@@ -1138,7 +1138,7 @@ msgid "Ends:"
|
||||
msgstr "Endet:"
|
||||
|
||||
msgid "Repeated:"
|
||||
msgstr "Wiederhole:"
|
||||
msgstr "Wiederholt:"
|
||||
|
||||
msgid "Action:"
|
||||
msgstr "Aktion:"
|
||||
@@ -1216,7 +1216,7 @@ msgid "A similar service is already in this list!"
|
||||
msgstr "Ein ähnlicher Dienst ist bereits in dieser Liste enthalten!"
|
||||
|
||||
msgid "Play mode has been changed!\nRestart the program to apply the settings."
|
||||
msgstr "Abspielen-Modus wurde geändert!\nStarte das Programm neu, um die Einstellungen zu übernehmen."
|
||||
msgstr "Abspiel-Modus wurde geändert!\nStarte das Programm neu, um die Einstellungen zu übernehmen."
|
||||
|
||||
msgid "Set values for TID, NID and Namespace for correct naming of the picons!"
|
||||
msgstr "Stelle die Werte für TID, NID und Namespace für die korrekte Benennung der Picons ein!"
|
||||
@@ -1229,3 +1229,15 @@ msgstr "Download picons"
|
||||
|
||||
msgid "Errors:"
|
||||
msgstr "Fehler:"
|
||||
|
||||
msgid "Use to play streams:"
|
||||
msgstr "Zum Abspielen von Streams verwenden:"
|
||||
|
||||
msgid "Font in the lists:"
|
||||
msgstr "Schrift in den Listen:"
|
||||
|
||||
msgid "Picons size in the lists:"
|
||||
msgstr "Picons Größe in den Listen:"
|
||||
|
||||
msgid "Logo size in tooltips:"
|
||||
msgstr "Logo-Größe in Tooltips:"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Copyright (C) 2018-2020 Dmitriy Yefremov
|
||||
# Copyright (C) 2018-2021 Dmitriy Yefremov
|
||||
# This file is distributed under the MIT license.
|
||||
#
|
||||
#
|
||||
@@ -1213,3 +1213,15 @@ msgstr "Загрузить пиконы"
|
||||
|
||||
msgid "Errors:"
|
||||
msgstr "Ошибок:"
|
||||
|
||||
msgid "Use to play streams:"
|
||||
msgstr "Использовать для воспроизведения потоков:"
|
||||
|
||||
msgid "Font in the lists:"
|
||||
msgstr "Шрифт в списках:"
|
||||
|
||||
msgid "Picons size in the lists:"
|
||||
msgstr "Размер пиконов в списках:"
|
||||
|
||||
msgid "Logo size in tooltips:"
|
||||
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: 2020-06-08 21:53+0300\n"
|
||||
"PO-Revision-Date: 2021-02-22 23:53+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"
|
||||
"Language-Team: \n"
|
||||
"X-Generator: Poedit 2.2.1\n"
|
||||
"X-Generator: Poedit 2.4.1\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
"Language: tr\n"
|
||||
|
||||
@@ -107,6 +107,9 @@ msgstr "Varsayılan adı ayarla"
|
||||
msgid "Insert marker"
|
||||
msgstr "İşaretçi ekle"
|
||||
|
||||
msgid "Insert space"
|
||||
msgstr "Boşluk ekle"
|
||||
|
||||
msgid "Locate in services"
|
||||
msgstr "Hizmetlerde bulun"
|
||||
|
||||
@@ -518,6 +521,9 @@ msgstr "Lütfen sadece bir ürün seçiniz!"
|
||||
msgid "No png file is selected!"
|
||||
msgstr "Hiçbir png dosyası seçilmedi!"
|
||||
|
||||
msgid "No profile selected!"
|
||||
msgstr "Profil seçilmedi!"
|
||||
|
||||
msgid "No reference is present!"
|
||||
msgstr "Referans yok!"
|
||||
|
||||
@@ -923,8 +929,8 @@ msgstr "Akışları oynatma modu:"
|
||||
msgid "Built-in player"
|
||||
msgstr "Dahili oynatıcı"
|
||||
|
||||
msgid "VLC media player"
|
||||
msgstr "VLC media player"
|
||||
msgid "In a separate window"
|
||||
msgstr "Ayrı bir pencerede"
|
||||
|
||||
msgid "Only get m3u file"
|
||||
msgstr "Sadece m3u dosyası al"
|
||||
@@ -988,3 +994,254 @@ msgstr "Alıcıdaki tüm piconları kaldırın"
|
||||
|
||||
msgid "Service reference"
|
||||
msgstr "Servis referansı"
|
||||
|
||||
msgid "Enable support for"
|
||||
msgstr "Temalar için desteği etkinleştir"
|
||||
|
||||
msgid "Auto-check for updates"
|
||||
msgstr "Güncellemeleri otomatik kontrol et"
|
||||
|
||||
msgid "Filter services"
|
||||
msgstr "Services filtrele"
|
||||
|
||||
msgid "Filter services in the main list."
|
||||
msgstr "Ana listedeki services filtreleyin."
|
||||
|
||||
msgid "Destination:"
|
||||
msgstr "Hedef:"
|
||||
|
||||
msgid "EXPERIMENTAL!"
|
||||
msgstr "EXPERIMENTAL!"
|
||||
|
||||
msgid "Sorting data..."
|
||||
msgstr "Verilerin sıralanması..."
|
||||
|
||||
msgid ""
|
||||
"There are unsaved changes.\n"
|
||||
"\n"
|
||||
"\t Save them now?"
|
||||
msgstr ""
|
||||
"Kaydedilmemiş değişiklikler var.\n"
|
||||
"\n"
|
||||
"\tŞimdi kaydedilsin mi?"
|
||||
|
||||
msgid ""
|
||||
"Are you sure you want to change the order\n"
|
||||
"\t of services in this bouquet?"
|
||||
msgstr ""
|
||||
"Sırayı değiştirmek istediğinizden emin misiniz\n"
|
||||
"\t Bu buketdeki hizmetlerin sayısı?"
|
||||
|
||||
msgid "Remove from the receiver"
|
||||
msgstr "Alıcıdaki tüm piconları kaldırın"
|
||||
|
||||
msgid "Screenshot"
|
||||
msgstr "Ekran görüntüsü"
|
||||
|
||||
msgid "Video"
|
||||
msgstr "Video"
|
||||
|
||||
msgid "The Neutrino has only experimental support. Not all features are supported!"
|
||||
msgstr "Neutrino'nun yalnızca experimental desteği var. Tüm özellikler desteklenmiyor!"
|
||||
|
||||
msgid "Enable experimental features"
|
||||
msgstr "Experimental özellikleri etkinleştirin"
|
||||
|
||||
msgid "Can't Playback!"
|
||||
msgstr "Oynatılamıyor!"
|
||||
|
||||
msgid "Enable Dark Mode"
|
||||
msgstr "Koyu Modu Etkinleştir"
|
||||
|
||||
msgid "Extract..."
|
||||
msgstr "Çıkart..."
|
||||
|
||||
msgid "Unsupported format!"
|
||||
msgstr "Desteklenmeyen format!"
|
||||
|
||||
msgid "Combine with the current data?"
|
||||
msgstr "Mevcut verilerle birleştirilsin mi?"
|
||||
|
||||
msgid "Importing data done!"
|
||||
msgstr "Verilerin içe aktarılması tamamlandı!"
|
||||
|
||||
msgid "Current service"
|
||||
msgstr "Mevcut service"
|
||||
|
||||
msgid "Open folder"
|
||||
msgstr "Dosya aç"
|
||||
|
||||
msgid "Open archive"
|
||||
msgstr "Arşiv aç"
|
||||
|
||||
msgid "Import from Web"
|
||||
msgstr "Web'den içe aktar"
|
||||
|
||||
msgid "Control"
|
||||
msgstr "Control"
|
||||
|
||||
msgid "Timers"
|
||||
msgstr "Zamanlayıcılar"
|
||||
|
||||
msgid "Timer"
|
||||
msgstr "Zamanlayıcı"
|
||||
|
||||
msgid "Add timer"
|
||||
msgstr "Zamanlayıcı ekle"
|
||||
|
||||
msgid "Hr."
|
||||
msgstr "Hr."
|
||||
|
||||
msgid "Min."
|
||||
msgstr "Min."
|
||||
|
||||
msgid "Power"
|
||||
msgstr "Power"
|
||||
|
||||
msgid "Standby"
|
||||
msgstr "Standby"
|
||||
|
||||
msgid "Wake Up"
|
||||
msgstr "Wake Up"
|
||||
|
||||
msgid "Reboot"
|
||||
msgstr "Reboot"
|
||||
|
||||
msgid "Restart GUI"
|
||||
msgstr "Restart GUI"
|
||||
|
||||
msgid "Shutdown"
|
||||
msgstr "Shutdown"
|
||||
|
||||
msgid "Shut down"
|
||||
msgstr "Kapat"
|
||||
|
||||
msgid "Do Nothing"
|
||||
msgstr "Hiçbir şey yapma"
|
||||
|
||||
msgid "Auto"
|
||||
msgstr "Auto"
|
||||
|
||||
msgid "Grab screenshot"
|
||||
msgstr "Ekran görüntüsü al"
|
||||
|
||||
msgid "Enabled:"
|
||||
msgstr "Etkin:"
|
||||
|
||||
msgid "Name:"
|
||||
msgstr "Ad:"
|
||||
|
||||
msgid "Description:"
|
||||
msgstr "Açıklama:"
|
||||
|
||||
msgid "Service:"
|
||||
msgstr "Service:"
|
||||
|
||||
msgid "Service reference:"
|
||||
msgstr "Servis referansı:"
|
||||
|
||||
msgid "Event ID:"
|
||||
msgstr "Olay Kimliği:"
|
||||
|
||||
msgid "Begins:"
|
||||
msgstr "Başlıyor:"
|
||||
|
||||
msgid "Ends:"
|
||||
msgstr "Bitiş:"
|
||||
|
||||
msgid "Repeated:"
|
||||
msgstr "Tekrar:"
|
||||
|
||||
msgid "Action:"
|
||||
msgstr "Aksiyon:"
|
||||
|
||||
msgid "After event:"
|
||||
msgstr "Olaydan sonra:"
|
||||
|
||||
msgid "Location:"
|
||||
msgstr "Konum:"
|
||||
|
||||
msgid "Mo"
|
||||
msgstr "Pzt"
|
||||
|
||||
msgid "Tu"
|
||||
msgstr "Sal"
|
||||
|
||||
msgid "We"
|
||||
msgstr "Çar"
|
||||
|
||||
msgid "Th"
|
||||
msgstr "Per"
|
||||
|
||||
msgid "Fr"
|
||||
msgstr "Cum"
|
||||
|
||||
msgid "Sa"
|
||||
msgstr "Cmt"
|
||||
|
||||
msgid "Su"
|
||||
msgstr "Paz"
|
||||
|
||||
msgid "Set"
|
||||
msgstr "Yüklemek"
|
||||
|
||||
msgid "Services update"
|
||||
msgstr "Servisleri güncelle"
|
||||
|
||||
msgid "Create folder"
|
||||
msgstr "Klasör oluştur"
|
||||
|
||||
msgid "FTP client"
|
||||
msgstr "FTP istemcisi"
|
||||
|
||||
msgid "The file size is too large!"
|
||||
msgstr "Dosya boyutu çok büyük!"
|
||||
|
||||
msgid "Connect"
|
||||
msgstr "Bağlan"
|
||||
|
||||
msgid "Disconnect"
|
||||
msgstr "Bağlantıyı kes"
|
||||
|
||||
msgid "Size"
|
||||
msgstr "Boyut"
|
||||
|
||||
msgid "Date"
|
||||
msgstr "Saat"
|
||||
|
||||
msgid "Attr."
|
||||
msgstr "Özellik."
|
||||
|
||||
msgid "Toggle display position"
|
||||
msgstr "Görüntü konumunu değiştir"
|
||||
|
||||
msgid "Alternatives"
|
||||
msgstr "Alternatifler"
|
||||
|
||||
msgid "Add alternatives"
|
||||
msgstr "Alternatif ekleyin"
|
||||
|
||||
msgid "DreamOS only!"
|
||||
msgstr "Sadece DreamOS!"
|
||||
|
||||
msgid "A similar service is already in this list!"
|
||||
msgstr "Bu listede zaten benzer bir hizmet var!"
|
||||
|
||||
msgid ""
|
||||
"Play mode has been changed!\n"
|
||||
"Restart the program to apply the settings."
|
||||
msgstr ""
|
||||
"Oynatma modu değiştirildi!\n"
|
||||
"Ayarları uygulamak için programı yeniden başlatın."
|
||||
|
||||
msgid "Set values for TID, NID and Namespace for correct naming of the picons!"
|
||||
msgstr "Piconların doğru isimlendirilmesi için TID, NID ve Namespace değerlerini ayarlayın!"
|
||||
|
||||
msgid "Streams detected:"
|
||||
msgstr "Akışlar algılandı:"
|
||||
|
||||
msgid "Download picons"
|
||||
msgstr "Picon'lar indirin"
|
||||
|
||||
msgid "Errors:"
|
||||
msgstr "Hatalar:"
|
||||
|
||||
16
start.py
16
start.py
@@ -1,19 +1,7 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
try:
|
||||
from Cocoa import NSBundle
|
||||
except ImportError as e:
|
||||
print(e)
|
||||
else:
|
||||
ns_bundle = NSBundle.mainBundle()
|
||||
if ns_bundle:
|
||||
ns_bundle = ns_bundle.localizedInfoDictionary() or ns_bundle.infoDictionary()
|
||||
if ns_bundle:
|
||||
ns_bundle["CFBundleName"] = "DemonEditor"
|
||||
|
||||
if __name__ == "__main__":
|
||||
from multiprocessing import set_start_method
|
||||
from multiprocessing import freeze_support
|
||||
from app.ui.main_app_window import start_app
|
||||
|
||||
set_start_method("fork") # For compatibility [Python > 3.7]
|
||||
freeze_support()
|
||||
start_app()
|
||||
|
||||
Reference in New Issue
Block a user