mirror of
https://github.com/DYefremov/DemonEditor.git
synced 2026-05-09 10:25:36 +02:00
Compare commits
404 Commits
1.0.9-b1
...
1.0.7-b1-m
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7cb1787de7 | ||
|
|
b4bca084de | ||
|
|
3990ee6572 | ||
|
|
46e8be54dd | ||
|
|
4d35f71ddc | ||
|
|
38ff00bfb3 | ||
|
|
d8f9dfe50e | ||
|
|
b6f3d888cb | ||
|
|
043a0371d2 | ||
|
|
e613f9f55e | ||
|
|
2806d95972 | ||
|
|
c8d38161ae | ||
|
|
3758c738fe | ||
|
|
b18c4e254e | ||
|
|
1ad7781de7 | ||
|
|
2325c0e541 | ||
|
|
e0c953ee05 | ||
|
|
3dc4caf65d | ||
|
|
8308b715dd | ||
|
|
3a8831f0f9 | ||
|
|
a020a23211 | ||
|
|
a67c81235c | ||
|
|
3ef587841e | ||
|
|
55a21fbc18 | ||
|
|
b60a9a69b6 | ||
|
|
8b517f6f88 | ||
|
|
81dd12a038 | ||
|
|
17de78f169 | ||
|
|
3411f32868 | ||
|
|
5f669f4480 | ||
|
|
f56e4b616a | ||
|
|
9d5e07af1f | ||
|
|
399c1ff01b | ||
|
|
ad8e6975b1 | ||
|
|
ca1e823bf1 | ||
|
|
7fb2d9ac4a | ||
|
|
f5a02ddf1d | ||
|
|
1266f8e04b | ||
|
|
2dcee99981 | ||
|
|
7053628e56 | ||
|
|
b8e1f0e7fd | ||
|
|
954f1c514a | ||
|
|
60e1f6c467 | ||
|
|
986f10c640 | ||
|
|
4c95972381 | ||
|
|
052dd3efbe | ||
|
|
4e867b6f22 | ||
|
|
c11278041e | ||
|
|
b89df3d65d | ||
|
|
d252c69628 | ||
|
|
6785e46745 | ||
|
|
bbffeaa30e | ||
|
|
ce11723d34 | ||
|
|
2cdefdca42 | ||
|
|
52b2bb28b4 | ||
|
|
672586e227 | ||
|
|
eaff4eec6c | ||
|
|
f068696aad | ||
|
|
f8eddd8710 | ||
|
|
a5206c89ef | ||
|
|
6555c3c882 | ||
|
|
155ed02f11 | ||
|
|
a1ce729ce2 | ||
|
|
33ffccf57a | ||
|
|
b9881fc345 | ||
|
|
35ce913ab0 | ||
|
|
29e1cb10a3 | ||
|
|
558843c728 | ||
|
|
c3534052ae | ||
|
|
1113fec26e | ||
|
|
2f55fb4e64 | ||
|
|
412a66e5e5 | ||
|
|
676bc14f73 | ||
|
|
ec6ebb2a0e | ||
|
|
f8710a4bf0 | ||
|
|
b48f638495 | ||
|
|
fd0559d76e | ||
|
|
6c6948ce23 | ||
|
|
573d755e31 | ||
|
|
912083f203 | ||
|
|
4df0553333 | ||
|
|
f0d535ba4e | ||
|
|
88ef5563cf | ||
|
|
6431f2ccd8 | ||
|
|
ca9b4a780d | ||
|
|
f74eead20b | ||
|
|
8d4d90fd9f | ||
|
|
4269d16d31 | ||
|
|
9cf3e97bd3 | ||
|
|
3b85d35b62 | ||
|
|
6bddd89206 | ||
|
|
e16f2cba82 | ||
|
|
59748aa9ba | ||
|
|
caf4925409 | ||
|
|
2ab540ccfa | ||
|
|
4b762802da | ||
|
|
41a6e54e90 | ||
|
|
7db02f2a9e | ||
|
|
fd40fd8d72 | ||
|
|
0b4313e4cf | ||
|
|
a74628ed5c | ||
|
|
443f6bf252 | ||
|
|
bb243ce281 | ||
|
|
44049c380e | ||
|
|
281fe2a8f4 | ||
|
|
39cc0ad8b3 | ||
|
|
a625dc9f8b | ||
|
|
53f69b8f67 | ||
|
|
94dfda0fa2 | ||
|
|
cfe3f4c707 | ||
|
|
d18734910d | ||
|
|
d843633043 | ||
|
|
b513d7a9b0 | ||
|
|
92b2f840f8 | ||
|
|
9e4c8f388c | ||
|
|
664c52cfe1 | ||
|
|
cc96cdd8fd | ||
|
|
15cca3f5f7 | ||
|
|
0ec2570043 | ||
|
|
97cb26cd60 | ||
|
|
1da3eacc8c | ||
|
|
cb6f185032 | ||
|
|
a8918bcf1f | ||
|
|
c358197080 | ||
|
|
474fc3ec58 | ||
|
|
c17bad215f | ||
|
|
7891aca6e2 | ||
|
|
608de65897 | ||
|
|
cbed3f7cca | ||
|
|
08c1dca06d | ||
|
|
1edbd7d771 | ||
|
|
0c3f6870dd | ||
|
|
f877872059 | ||
|
|
335dfc005a | ||
|
|
46450cf9b6 | ||
|
|
9ed82ea129 | ||
|
|
555699c2a1 | ||
|
|
83b810286a | ||
|
|
61a56f1989 | ||
|
|
50ce4a688a | ||
|
|
871b428b19 | ||
|
|
3cd864cd84 | ||
|
|
78c6a3c9fa | ||
|
|
4c0904cf6c | ||
|
|
7aa688df15 | ||
|
|
c91d58e0cf | ||
|
|
d071bb5d85 | ||
|
|
8cee77357c | ||
|
|
20f53dee33 | ||
|
|
c454a33b3c | ||
|
|
5642b8871c | ||
|
|
e7480ec622 | ||
|
|
ecce001ce4 | ||
|
|
7bae895458 | ||
|
|
5b3bd48746 | ||
|
|
4769a814bd | ||
|
|
b08d4ed7d7 | ||
|
|
233eb6bc53 | ||
|
|
8b3d24c006 | ||
|
|
48184c1fd9 | ||
|
|
46d91b93bc | ||
|
|
69989e784d | ||
|
|
ce1c978222 | ||
|
|
5bcac35deb | ||
|
|
3ec5d264a0 | ||
|
|
a2882b6589 | ||
|
|
31780bbf56 | ||
|
|
286f1ffc3f | ||
|
|
3bf97e5e0d | ||
|
|
c7c411c72b | ||
|
|
6b8a83511a | ||
|
|
f8e259293a | ||
|
|
7adbf6b8a9 | ||
|
|
b68535e88a | ||
|
|
b98ca359df | ||
|
|
cbfd1486e1 | ||
|
|
ad185f1efa | ||
|
|
cea4ed1a66 | ||
|
|
853d054a68 | ||
|
|
8a1cead2f7 | ||
|
|
37e0a8fdac | ||
|
|
29c66142ee | ||
|
|
4fd2a2a600 | ||
|
|
6b360d48c4 | ||
|
|
3a307b277c | ||
|
|
9e685058a2 | ||
|
|
3f07b09bb5 | ||
|
|
1dca45f18f | ||
|
|
8b5ebc132d | ||
|
|
b076db23bb | ||
|
|
41d479e18f | ||
|
|
cf540e5c9a | ||
|
|
3c7c8ebd83 | ||
|
|
9b0c173eb8 | ||
|
|
208ce53c48 | ||
|
|
bb6679eddf | ||
|
|
57f5e40439 | ||
|
|
dad02e8e5c | ||
|
|
844dab10a0 | ||
|
|
c1f5fd8006 | ||
|
|
86b974b632 | ||
|
|
cf7e3a1b1b | ||
|
|
bb07eb0a8a | ||
|
|
f6de7d0fce | ||
|
|
647b528899 | ||
|
|
7ed64c76ba | ||
|
|
bcfdb09169 | ||
|
|
c0c2ddef34 | ||
|
|
b02eb37f1c | ||
|
|
9b9f1d5492 | ||
|
|
caba789e02 | ||
|
|
5b1bffc078 | ||
|
|
7e35a081a0 | ||
|
|
ccbc7a4315 | ||
|
|
7f3f900725 | ||
|
|
921b936db0 | ||
|
|
bc6d372ade | ||
|
|
3c28d12579 | ||
|
|
e9544cc77f | ||
|
|
bd047e5f72 | ||
|
|
adf7262ed6 | ||
|
|
74a1ffea3a | ||
|
|
6e78a539c3 | ||
|
|
c3ce3fc82e | ||
|
|
8c61720423 | ||
|
|
25e0e6939a | ||
|
|
e3232e48cf | ||
|
|
aaa610852b | ||
|
|
04e9179025 | ||
|
|
bce5636eaa | ||
|
|
0e10631931 | ||
|
|
77a3edead2 | ||
|
|
8a8b249e14 | ||
|
|
4025f0933d | ||
|
|
bba4054bff | ||
|
|
e322d36023 | ||
|
|
bb5afb0206 | ||
|
|
115f3960a7 | ||
|
|
6d37da072e | ||
|
|
99c3b1d194 | ||
|
|
43afaf77b8 | ||
|
|
38aabb1b94 | ||
|
|
ef501f1557 | ||
|
|
4679f9379c | ||
|
|
b2ea39f8a6 | ||
|
|
638be67425 | ||
|
|
9ca5a597d5 | ||
|
|
d95ba7336f | ||
|
|
c78b18ddb7 | ||
|
|
92984c5fa6 | ||
|
|
ca65f64a4f | ||
|
|
78dcccbd51 | ||
|
|
f984d10c82 | ||
|
|
c4ea451f52 | ||
|
|
36ec6d5079 | ||
|
|
91706c722f | ||
|
|
4ef8c4d186 | ||
|
|
f9e92b28d0 | ||
|
|
832bab91a4 | ||
|
|
951c99338f | ||
|
|
ee91eb9413 | ||
|
|
912c38825b | ||
|
|
de4d012784 | ||
|
|
351ce81e94 | ||
|
|
3a0f096a6c | ||
|
|
29088ec19e | ||
|
|
4c144951f0 | ||
|
|
dae6ad765a | ||
|
|
b934407d7e | ||
|
|
3fb5b82cc6 | ||
|
|
ba3ad9a9ef | ||
|
|
7a4620a374 | ||
|
|
174634ecbc | ||
|
|
73ae57d07b | ||
|
|
055a700586 | ||
|
|
04203240a7 | ||
|
|
a433e01b65 | ||
|
|
8f591a8b9a | ||
|
|
dcc217b0de | ||
|
|
d06334b0af | ||
|
|
6957a960ca | ||
|
|
9fe328b54e | ||
|
|
b3dc9b72c9 | ||
|
|
b6a4d46227 | ||
|
|
53776bdf62 | ||
|
|
ba9ba4129f | ||
|
|
a2411ba86e | ||
|
|
a6d8573999 | ||
|
|
7510d42fb9 | ||
|
|
036e666c9b | ||
|
|
c9c962e129 | ||
|
|
ea71af9462 | ||
|
|
0a5b51de6e | ||
|
|
8cb413ec92 | ||
|
|
5dfb702484 | ||
|
|
0cab4e1238 | ||
|
|
85f5c37f28 | ||
|
|
3df6d7bba0 | ||
|
|
e45c56f4cc | ||
|
|
7d03631924 | ||
|
|
7b9ec6a4b1 | ||
|
|
d640210ab0 | ||
|
|
f7e8283355 | ||
|
|
f93c81de19 | ||
|
|
e1804755d2 | ||
|
|
1cf56639c1 | ||
|
|
943b4c540f | ||
|
|
4602c51c01 | ||
|
|
a84cc7727f | ||
|
|
250e03af5d | ||
|
|
2c5f8eb0ed | ||
|
|
6f4ff4c97d | ||
|
|
ee29659739 | ||
|
|
8a1496a84c | ||
|
|
23c3035162 | ||
|
|
a506356547 | ||
|
|
0c284fb0d9 | ||
|
|
b437385325 | ||
|
|
c60bba5535 | ||
|
|
1c2d0ab9ea | ||
|
|
f35f7fbc8a | ||
|
|
42aaad291f | ||
|
|
9c8c617393 | ||
|
|
98fc963fa1 | ||
|
|
fbb5cd0352 | ||
|
|
5abe3de3b6 | ||
|
|
0b3f26ab84 | ||
|
|
2666146b5e | ||
|
|
be90b518c9 | ||
|
|
adeae58488 | ||
|
|
b204f042ee | ||
|
|
79d0e9d256 | ||
|
|
4dcfde8b53 | ||
|
|
42f687020b | ||
|
|
14bf79dbf9 | ||
|
|
f660beef16 | ||
|
|
99d17b36c3 | ||
|
|
3113fadcca | ||
|
|
67a394359d | ||
|
|
1acb7fdd81 | ||
|
|
dced81581c | ||
|
|
6a52988f1a | ||
|
|
4a1d714604 | ||
|
|
2e12e1ec87 | ||
|
|
4c6336e75f | ||
|
|
b7d0ba7f4b | ||
|
|
405e07bbc4 | ||
|
|
6ded67147b | ||
|
|
4a0e2acd9c | ||
|
|
5876f70884 | ||
|
|
3cb4f1095d | ||
|
|
af46c2fb1d | ||
|
|
cbcdf19be6 | ||
|
|
f326a9c723 | ||
|
|
53888a45dc | ||
|
|
c2d3cb7673 | ||
|
|
9d4b507559 | ||
|
|
2993fcd7f7 | ||
|
|
5bea9887db | ||
|
|
dd0edfc811 | ||
|
|
5bf6500809 | ||
|
|
d05da3f44c | ||
|
|
b251ce8b69 | ||
|
|
df36860239 | ||
|
|
6f27040164 | ||
|
|
4d488dd224 | ||
|
|
c728a59e92 | ||
|
|
98341064d3 | ||
|
|
fc00b25fd2 | ||
|
|
638f33ac5a | ||
|
|
88167912b3 | ||
|
|
450d7f4c72 | ||
|
|
24729c064c | ||
|
|
df3c2a3938 | ||
|
|
44bf8b96ff | ||
|
|
6b86db4aa4 | ||
|
|
bf9ad139e5 | ||
|
|
20fc199d02 | ||
|
|
e0a22f72fc | ||
|
|
8cadc47da5 | ||
|
|
499ca31992 | ||
|
|
e198f0b1e6 | ||
|
|
a43ac0de02 | ||
|
|
b14e6fac16 | ||
|
|
2dcc9a85b5 | ||
|
|
7ce9ba0db2 | ||
|
|
fc56b047a1 | ||
|
|
78dc62d46e | ||
|
|
20e7ee3478 | ||
|
|
ffa144367f | ||
|
|
237d09a711 | ||
|
|
272cbdeb2f | ||
|
|
f9191a7465 | ||
|
|
f22abe1d87 | ||
|
|
b94c08284a | ||
|
|
e303f25f99 | ||
|
|
b80dcb7d74 | ||
|
|
63c5df0ef6 | ||
|
|
c69888a72d | ||
|
|
07606077e5 | ||
|
|
e1f63bfed7 | ||
|
|
c0865beb3c | ||
|
|
05eff28b75 | ||
|
|
6d2150b731 |
@@ -1,13 +0,0 @@
|
||||
[Desktop Entry]
|
||||
Version=1.0
|
||||
Name=DemonEditor
|
||||
Comment=Channel and satellite list editor for Enigma2
|
||||
Comment[ru]=Редактор списка каналов и спутников для Enigma2
|
||||
Comment[be]=Рэдактар спіса каналаў і спадарожнікаў для Enigma2
|
||||
Comment[de]=Programm- und Satellitenlisten-Editor für Enigma2
|
||||
Icon=demon-editor
|
||||
Exec=bash -c 'cd $(dirname %k) && ./start.py'
|
||||
Terminal=false
|
||||
Type=Application
|
||||
Categories=Utility;Application;
|
||||
StartupNotify=false
|
||||
74
DemonEditor.spec
Normal file
74
DemonEditor.spec
Normal file
@@ -0,0 +1,74 @@
|
||||
import os
|
||||
import datetime
|
||||
import distutils.util
|
||||
|
||||
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
|
||||
|
||||
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,
|
||||
datas=ui_files,
|
||||
hiddenimports=['fileinput', 'uuid'],
|
||||
hookspath=[],
|
||||
runtime_hooks=[],
|
||||
excludes=excludes,
|
||||
win_no_prefer_redirects=False,
|
||||
win_private_assemblies=False,
|
||||
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,
|
||||
upx=True,
|
||||
console=False)
|
||||
|
||||
coll = COLLECT(exe,
|
||||
a.binaries,
|
||||
a.zipfiles,
|
||||
a.datas,
|
||||
strip=STRIP,
|
||||
upx=True,
|
||||
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.7 Beta (Build: {})".format(BUILD_DATE),
|
||||
'NSHumanReadableCopyright': u"Copyright © 2021, Dmitriy Yefremov",
|
||||
'NSRequiresAquaSystemAppearance': 'false'
|
||||
})
|
||||
141
README.md
141
README.md
@@ -1,94 +1,97 @@
|
||||
# <img src="app/ui/icons/hicolor/96x96/apps/demon-editor.png" width="32" /> DemonEditor
|
||||
[](LICENSE) 
|
||||
### Enigma2 channel and satellite list editor for GNU/Linux.
|
||||
[<img src="https://user-images.githubusercontent.com/7511379/118884719-8277e980-b8ff-11eb-8621-c8c4afd6181b.png" width="560"/>](https://user-images.githubusercontent.com/7511379/118884719-8277e980-b8ff-11eb-8621-c8c4afd6181b.png)
|
||||
[](LICENSE) 
|
||||
## Enigma2 channel and satellite list editor for macOS (experimental).
|
||||
|
||||
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).
|
||||

|
||||
|
||||
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).
|
||||
**The functionality and performance of this version may be different from the [Linux version](https://github.com/DYefremov/DemonEditor)!**
|
||||
|
||||
## Main features of the program
|
||||
* Editing bouquets, channels, satellites.
|
||||
[<img src="https://user-images.githubusercontent.com/7511379/118884747-8ad02480-b8ff-11eb-9104-8cf8fb6e785d.png" width="480"/>](https://user-images.githubusercontent.com/7511379/118884747-8ad02480-b8ff-11eb-9104-8cf8fb6e785d.png)
|
||||
* Import function.
|
||||
[<img src="https://user-images.githubusercontent.com/7511379/118526825-4dc23180-b749-11eb-8197-e9bbccbc3bdf.png" width="480"/>](https://user-images.githubusercontent.com/7511379/118526825-4dc23180-b749-11eb-8197-e9bbccbc3bdf.png)
|
||||
* Backup function.
|
||||
[<img src="https://user-images.githubusercontent.com/7511379/118528402-f58c2f00-b74a-11eb-9b84-edf220526e6e.png" width="480"/>](https://user-images.githubusercontent.com/7511379/118528402-f58c2f00-b74a-11eb-9b84-edf220526e6e.png)
|
||||
* Support of picons.
|
||||
[<img src="https://user-images.githubusercontent.com/7511379/118526864-5c104d80-b749-11eb-8497-6e8c78542ab1.png" width="480"/>](https://user-images.githubusercontent.com/7511379/118526864-5c104d80-b749-11eb-8497-6e8c78542ab1.png)
|
||||
* Importing services, downloading picons and updating satellites from the Web.
|
||||
[<img src="https://user-images.githubusercontent.com/7511379/118530243-1a81a180-b74d-11eb-8e01-aea904d954af.png" width="250"/>](https://user-images.githubusercontent.com/7511379/118530243-1a81a180-b74d-11eb-8e01-aea904d954af.png)
|
||||
[<img src="https://user-images.githubusercontent.com/7511379/118526706-31be9000-b749-11eb-9956-c4bf2e13f968.png" width="292"/>](https://user-images.githubusercontent.com/7511379/118526706-31be9000-b749-11eb-9956-c4bf2e13f968.png)
|
||||
* Editing bouquets, channels, satellites.
|
||||
* Import function.
|
||||
* Backup function.
|
||||
* Extended support of IPTV.
|
||||
* Support of picons.
|
||||
* Importing services, downloading picons and updating satellites from the Web.
|
||||
* Import to bouquet(Neutrino WEBTV) from m3u.
|
||||
* Export of bouquets with IPTV services in m3u.
|
||||
* Assignment of EPG from DVB or XML for IPTV services (only Enigma2, experimental).
|
||||
* Preview (playback) of IPTV or other streams directly from the bouquet list.
|
||||
[<img src="https://user-images.githubusercontent.com/7511379/118884891-b3f0b500-b8ff-11eb-8717-3588d6e089de.png" width="480"/>](https://user-images.githubusercontent.com/7511379/118884891-b3f0b500-b8ff-11eb-8717-3588d6e089de.png)
|
||||
* Control panel with the ability to view EPG and manage timers (via HTTP API, experimental).
|
||||
[<img src="https://user-images.githubusercontent.com/7511379/118886284-66754780-b901-11eb-9068-29b5a607ccaf.png" width="480"/>](https://user-images.githubusercontent.com/7511379/118886284-66754780-b901-11eb-9068-29b5a607ccaf.png)
|
||||
* Simple FTP client (experimental).
|
||||
[<img src="https://user-images.githubusercontent.com/7511379/118527372-e8bb0b80-b749-11eb-9653-4ad64c99a05a.png" width="480"/>](https://user-images.githubusercontent.com/7511379/118527372-e8bb0b80-b749-11eb-9653-4ad64c99a05a.png)
|
||||
|
||||
#### 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 **Ctrl** key!
|
||||
* 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/)).
|
||||
* 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.
|
||||
|
||||
For **multiple** selection with the mouse, press and hold the **⌘** key!
|
||||
|
||||
## Minimum requirements
|
||||
*Python >= 3.5.2, GTK+ >= 3.22, python3-gi, python3-gi-cairo, python3-requests.*
|
||||
*Python >= 3.5.2, GTK+ >= 3.16 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```
|
||||
|
||||
***Optional:** python3-pil, python3-chardet.*
|
||||
## Installation and Launch
|
||||
* ### Linux
|
||||
To start the program, in most cases it is enough to download the [archive](https://github.com/DYefremov/DemonEditor/archive/master.zip), unpack
|
||||
and run it by double clicking on DemonEditor.desktop in the root directory,
|
||||
or launching from the console with the command:```./start.py```
|
||||
Extra folders can be deleted, excluding the *app* folder and root files like *DemonEditor.desktop* and *start.py*!
|
||||
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!**
|
||||
|
||||
To create a simple **debian package**, you can use the *build-deb.sh.* You can also download a ready-made *.deb package from the [releases](https://github.com/DYefremov/DemonEditor/releases) page.
|
||||
Users of **LTS** versions of [Ubuntu](https://ubuntu.com/) or distributions based on them can use [PPA](https://launchpad.net/~dmitriy-yefremov/+archive/ubuntu/demon-editor) repository.
|
||||
A ready-made [package](https://aur.archlinux.org/packages/demoneditor-bin) is also available for [Arch Linux](https://archlinux.org/) users in the [AUR](https://aur.archlinux.org/) repository.
|
||||
* ### macOS
|
||||
**This program can be run on macOS.** To work in this OS, you must use a [separate branch](https://github.com/DYefremov/DemonEditor/tree/experimental-mac). A ready-made package can be downloaded from the [releases](https://github.com/DYefremov/DemonEditor/releases) page.
|
||||
**The functionality and performance of this version may be different from the Linux version!**
|
||||
THIS 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
|
||||
The program is tested only with [openATV](https://www.opena.tv/) image and **Formuler F1** receiver in [Linux Mint](https://linuxmint.com/) (MATE 64-bit) distribution!
|
||||
**This version is not fully tested and has experimental status!**
|
||||
|
||||
Terrestrial(DVB-T/T2) and cable(DVB-C) channels are only supported for Enigma2.
|
||||
Main supported *lamedb* format is version **4**. Versions **3** and **5** has only **experimental** support! For version **3** is only read mode available. When saving, version **4** format is used instead.
|
||||
Main supported *lamedb* format is version **4**. Versions **3** and **5** has only **experimental** support!
|
||||
For version **3** is only read mode available. When saving, version **4** format is used instead.
|
||||
|
||||
When using the multiple import feature, from *lamedb* will be taken data **only for channels that are in the selected bouquets!**
|
||||
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.
|
||||
|
||||
For streams playback, this app supports [VLC](https://www.videolan.org/vlc/), [MPV](https://mpv.io/) and [GStreamer](https://gstreamer.freedesktop.org/). Depending on your distro, you may need to install additional packages and libraries.
|
||||
#### Command line arguments:
|
||||
* **-l** - write logs to file.
|
||||
* **-d on/off** - turn on/off debug mode. Allows to display more information in the logs.
|
||||
* **-t on/off** - show/hide simple built-in **telnet** client (experimental). **ANSI escape sequences are not supported!**
|
||||
|
||||
## License
|
||||
Licensed under the [MIT](LICENSE) license.
|
||||
Licensed under the [MIT](LICENSE) license.
|
||||
|
||||
@@ -1,30 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# The MIT License (MIT)
|
||||
#
|
||||
# Copyright (c) 2018-2021 Dmitriy Yefremov
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
# THE SOFTWARE.
|
||||
#
|
||||
# Author: Dmitriy Yefremov
|
||||
#
|
||||
|
||||
from app.commons import run_task
|
||||
from app.settings import SettingsType
|
||||
from .ecommons import Service, Satellite, Transponder, Bouquet, Bouquets, is_transponder_valid
|
||||
@@ -59,15 +32,6 @@ def get_bouquets(path, s_type):
|
||||
return get_neutrino_bouquets(path)
|
||||
|
||||
|
||||
def write_bouquet(path, bq, s_type):
|
||||
if s_type is SettingsType.ENIGMA_2:
|
||||
writer = BouquetsWriter(path, None)
|
||||
writer.write_bouquet(path + "userbouquet.{}.{}".format(bq.name, bq.type), bq.name, bq.services)
|
||||
elif s_type is SettingsType.NEUTRINO_MP:
|
||||
from .neutrino.bouquets import write_bouquet
|
||||
write_bouquet(path, bq)
|
||||
|
||||
|
||||
@run_task
|
||||
def write_bouquets(path, bouquets, s_type, force_bq_names=False):
|
||||
if s_type is SettingsType.ENIGMA_2:
|
||||
|
||||
@@ -1,31 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# The MIT License (MIT)
|
||||
#
|
||||
# Copyright (c) 2018-2021 Dmitriy Yefremov
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
# THE SOFTWARE.
|
||||
#
|
||||
# Author: Dmitriy Yefremov
|
||||
#
|
||||
|
||||
|
||||
""" Module for working with Enigma2 bouquets. """
|
||||
import re
|
||||
from collections import Counter
|
||||
@@ -194,11 +166,6 @@ class BouquetsReader:
|
||||
|
||||
for num, srv in enumerate(srvs, start=1):
|
||||
srv_data = srv.strip().split(":")
|
||||
data_len = len(srv_data)
|
||||
if data_len < 10:
|
||||
log("The bouquet [{}] service [{}] has the wrong data format: [{}]".format(bq_name, num, srv))
|
||||
continue
|
||||
|
||||
s_type = srv_data[1]
|
||||
if s_type == "64":
|
||||
m_data, sep, desc = srv.partition("#DESCRIPTION")
|
||||
@@ -219,7 +186,7 @@ class BouquetsReader:
|
||||
else:
|
||||
fav_id = "{}:{}:{}:{}".format(srv_data[3], srv_data[4], srv_data[5], srv_data[6])
|
||||
name = None
|
||||
if data_len == 12:
|
||||
if len(srv_data) == 12:
|
||||
name, sep, desc = str(srv_data[-1]).partition("\n#DESCRIPTION")
|
||||
services.append(BouquetService(name, BqServiceType.DEFAULT, fav_id.upper(), num))
|
||||
|
||||
|
||||
@@ -617,8 +617,13 @@ class Settings:
|
||||
self._settings["extra_color"] = value
|
||||
|
||||
@property
|
||||
@lru_cache(1)
|
||||
def dark_mode(self):
|
||||
return self._settings.get("dark_mode", False)
|
||||
import subprocess
|
||||
|
||||
cmd = ["defaults", "read", "-g", "AppleInterfaceStyle"]
|
||||
p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()
|
||||
return "Dark" in str(p[0])
|
||||
|
||||
@dark_mode.setter
|
||||
def dark_mode(self, value):
|
||||
|
||||
@@ -1,31 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# The MIT License (MIT)
|
||||
#
|
||||
# Copyright (c) 2018-2021 Dmitriy Yefremov
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
# THE SOFTWARE.
|
||||
#
|
||||
# Author: Dmitriy Yefremov
|
||||
#
|
||||
|
||||
|
||||
import os
|
||||
import sys
|
||||
from abc import ABC, abstractmethod
|
||||
@@ -369,7 +341,7 @@ class VlcPlayer(Player):
|
||||
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, NameError) as e:
|
||||
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:
|
||||
@@ -448,7 +420,7 @@ class VlcPlayer(Player):
|
||||
elif sys.platform == "darwin":
|
||||
self._player.set_nsobject(self.get_window_handle(video_widget))
|
||||
else:
|
||||
self._player.set_hwnd(self.get_window_handle(video_widget))
|
||||
log("Video widget initialization error: platform '{}' is not supported. ".format(sys.platform))
|
||||
|
||||
|
||||
class Recorder:
|
||||
|
||||
@@ -1,36 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# The MIT License (MIT)
|
||||
#
|
||||
# Copyright (c) 2018-2021 Dmitriy Yefremov
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
# THE SOFTWARE.
|
||||
#
|
||||
# Author: Dmitriy Yefremov
|
||||
#
|
||||
|
||||
|
||||
import glob
|
||||
import os
|
||||
import re
|
||||
import shutil
|
||||
import subprocess
|
||||
from collections import namedtuple
|
||||
from html.parser import HTMLParser
|
||||
|
||||
@@ -47,181 +18,6 @@ Provider = namedtuple("Provider", ["logo", "name", "pos", "url", "on_id", "ssid"
|
||||
Picon = namedtuple("Picon", ["ref", "ssid"])
|
||||
|
||||
|
||||
class PiconsError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class PiconsCzDownloader:
|
||||
""" The main class for loading picons from the https://picon.cz/ source (by Chocholoušek). """
|
||||
|
||||
_PERM_URL = "https://picon.cz/download/7337"
|
||||
_BASE_URL = "https://picon.cz/download/"
|
||||
_BASE_LOGO_URL = "https://picon.cz/picon/0/"
|
||||
_HEADER = {"User-Agent": "DemonEditor/1.0.9", "Referer": ""}
|
||||
_LINK_PATTERN = re.compile(r"((.*)-\d+x\d+)-(.*)_by_chocholousek.7z$")
|
||||
_FILE_PATTERN = re.compile(b"\\s+(1_.*\\.png).*")
|
||||
|
||||
def __init__(self, picon_ids=set(), appender=log):
|
||||
self._perm_links = {}
|
||||
self._providers = {}
|
||||
self._provider_logos = {}
|
||||
self._picon_ids = picon_ids
|
||||
self._appender = appender
|
||||
|
||||
def init(self):
|
||||
""" Initializes dict with values: download_id -> perm link and provider data. """
|
||||
if self._perm_links:
|
||||
return
|
||||
|
||||
self._HEADER["Referer"] = self._PERM_URL
|
||||
|
||||
with requests.get(url=self._PERM_URL, headers=self._HEADER, stream=True) as request:
|
||||
if request.reason == "OK":
|
||||
logo_map = self.get_logos_map()
|
||||
name_map = self.get_name_map()
|
||||
|
||||
for line in request.iter_lines():
|
||||
l_id, perm_link = line.decode(encoding="utf-8", errors="ignore").split(maxsplit=1)
|
||||
self._perm_links[str(l_id)] = str(perm_link)
|
||||
data = re.match(self._LINK_PATTERN, perm_link)
|
||||
if data:
|
||||
sat_pos = data.group(3)
|
||||
# Logo url.
|
||||
logo = logo_map.get(data.group(2), None)
|
||||
l_name = name_map.get(sat_pos, None) or sat_pos.replace(".", "")
|
||||
logo_url = "{}{}/{}.png".format(self._BASE_LOGO_URL, logo, l_name) if logo else None
|
||||
|
||||
prv = Provider(None, data.group(1), sat_pos, self._BASE_URL + l_id, l_id, logo_url, None, False)
|
||||
if sat_pos in self._providers:
|
||||
self._providers[sat_pos].append(prv)
|
||||
else:
|
||||
self._providers[sat_pos] = [prv]
|
||||
else:
|
||||
log("{} [get permalinks] error: {}".format(self.__class__.__name__, request.reason))
|
||||
raise PiconsError(request.reason)
|
||||
|
||||
@property
|
||||
def providers(self):
|
||||
return self._providers
|
||||
|
||||
def get_sat_providers(self, url):
|
||||
return self._providers.get(url, [])
|
||||
|
||||
def download(self, provider, picons_path, picon_ids=None):
|
||||
self._HEADER["Referer"] = provider.url
|
||||
with requests.get(url=provider.url, headers=self._HEADER, stream=True) as request:
|
||||
if request.reason == "OK":
|
||||
dest = "{}{}.7z".format(picons_path, provider.on_id)
|
||||
self._appender("Downloading: {}\n".format(provider.url))
|
||||
with open(dest, mode="bw") as f:
|
||||
for data in request.iter_content(chunk_size=1024):
|
||||
f.write(data)
|
||||
self._appender("Extracting: {}\n".format(provider.on_id))
|
||||
self.extract(dest, picons_path, picon_ids)
|
||||
else:
|
||||
log("{} [download] error: {}".format(self.__class__.__name__, request.reason))
|
||||
|
||||
def extract(self, src, dest, picon_ids=None):
|
||||
""" Extracts 7z archives. """
|
||||
# TODO: think about https://github.com/miurahr/py7zr
|
||||
exe = "7zr"
|
||||
if not os.path.isfile("/usr/bin/7zr"):
|
||||
raise PiconsError("7-zip [7zr] archiver not found!")
|
||||
|
||||
cmd = [exe, "l", src]
|
||||
try:
|
||||
out, err = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()
|
||||
if err:
|
||||
log("{} [extract] error: {}".format(self.__class__.__name__, err))
|
||||
raise PiconsError(err)
|
||||
except OSError as e:
|
||||
log("{} [extract] error: {}".format(self.__class__.__name__, e))
|
||||
raise PiconsError(e)
|
||||
|
||||
is_filter = bool(picon_ids)
|
||||
ids = picon_ids or self._picon_ids
|
||||
to_extract = []
|
||||
|
||||
for o in re.finditer(self._FILE_PATTERN, out):
|
||||
p_id = o.group(1).decode("utf-8", errors="ignore")
|
||||
if p_id in ids:
|
||||
to_extract.append(p_id)
|
||||
|
||||
if is_filter and not to_extract:
|
||||
if os.path.isfile(src):
|
||||
os.remove(src)
|
||||
raise PiconsError("No matching picons found!")
|
||||
|
||||
cmd = [exe, "e", src, "-o{}".format(dest), "-y", "-r"]
|
||||
cmd.extend(to_extract)
|
||||
try:
|
||||
out, err = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()
|
||||
if err:
|
||||
log("{} [extract] error: {}".format(self.__class__.__name__, err))
|
||||
raise PiconsError(err)
|
||||
else:
|
||||
if os.path.isfile(src):
|
||||
os.remove(src)
|
||||
except OSError as e:
|
||||
log(e)
|
||||
raise PiconsError(e)
|
||||
|
||||
def get_logo_data(self, url):
|
||||
""" Returns the logo data if present. """
|
||||
return self._provider_logos.get(url, None)
|
||||
|
||||
def get_provider_logo(self, url):
|
||||
""" Retrieves package logo. """
|
||||
# Getting package logo.
|
||||
logo = self._provider_logos.get(url, None)
|
||||
if logo:
|
||||
return logo
|
||||
|
||||
try:
|
||||
with requests.get(url=url, stream=True) as logo_request:
|
||||
if logo_request.reason == "OK":
|
||||
data = logo_request.content
|
||||
self._provider_logos[url] = data
|
||||
return data
|
||||
else:
|
||||
log("Downloading package logo error: {}".format(logo_request.reason))
|
||||
except requests.exceptions.ConnectionError as e:
|
||||
log("{} error [get provider logo]: {}".format(self.__class__.__name__, e))
|
||||
|
||||
def get_logos_map(self):
|
||||
return {"piconblack": "b50",
|
||||
"picontransparent": "t50",
|
||||
"piconwhite": "w50",
|
||||
"piconmirrorglass": "mr100",
|
||||
"piconnoName": "n100",
|
||||
"piconsrhd": "srhd100",
|
||||
"piconfreezeframe": "ff220",
|
||||
"piconfreezewhite": "fw100",
|
||||
"piconpoolrainbow": "r100",
|
||||
"piconsimpleblack": "s220",
|
||||
"piconjustblack": "jb220",
|
||||
"picondirtypaper": "dp220",
|
||||
"picongray": "g400",
|
||||
"piconmonochrom": "m220",
|
||||
"picontransparentwhite": "tw100",
|
||||
"picontransparentdark": "td220",
|
||||
"piconoled": "o96",
|
||||
"piconblack80": "b50",
|
||||
"piconblack3d": "b50"
|
||||
}
|
||||
|
||||
def get_name_map(self):
|
||||
return {"antiksat": "ANTIK",
|
||||
"digiczsk": "DIGI",
|
||||
"DTTitaly": "picon_trs-it",
|
||||
"dvbtCZSK": "picon_trs",
|
||||
"PolandDTT": "picon_trs-pl",
|
||||
"freeSAT": "UPC DIRECT",
|
||||
"orangesat": "ORANGE TV",
|
||||
"skylink": "M7 GROUP",
|
||||
}
|
||||
|
||||
|
||||
class PiconsParser(HTMLParser):
|
||||
""" Parser for package html page. (https://www.lyngsat.com/packages/*provider-name*.html) """
|
||||
_BASE_URL = "https://www.lyngsat.com"
|
||||
|
||||
170
app/ui/app_menu_bar.ui
Normal file
170
app/ui/app_menu_bar.ui
Normal file
@@ -0,0 +1,170 @@
|
||||
<?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>
|
||||
<section>
|
||||
<item>
|
||||
<attribute name="label" translatable="yes">Bouquet</attribute>
|
||||
<attribute name="action">app.on_import_bouquet</attribute>
|
||||
</item>
|
||||
<item>
|
||||
<attribute name="label" translatable="yes">Bouquets and services</attribute>
|
||||
<attribute name="action">app.on_import_bouquets</attribute>
|
||||
</item>
|
||||
</section>
|
||||
</submenu>
|
||||
<item>
|
||||
<attribute name="label" translatable="yes">Import from Web</attribute>
|
||||
<attribute name="action">app.on_import_from_web</attribute>
|
||||
</item>
|
||||
<item>
|
||||
<attribute name="label" translatable="yes">New empty configuration</attribute>
|
||||
<attribute name="action">app.on_new_configuration</attribute>
|
||||
</item>
|
||||
</section>
|
||||
<section>
|
||||
<item>
|
||||
<attribute name="label" translatable="yes">Open</attribute>
|
||||
<attribute name="action">app.on_data_open</attribute>
|
||||
</item>
|
||||
<item>
|
||||
<attribute name="label" translatable="yes">Extract...</attribute>
|
||||
<attribute name="action">app.on_archive_open</attribute>
|
||||
</item>
|
||||
<item>
|
||||
<attribute name="label" translatable="yes">Save</attribute>
|
||||
<attribute name="action">app.on_data_save</attribute>
|
||||
</item>
|
||||
</section>
|
||||
<section>
|
||||
<item>
|
||||
<attribute name="label" translatable="yes">FTP-transfer</attribute>
|
||||
<attribute name="action">app.on_download</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>
|
||||
</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>
|
||||
<attribute name="action">win.search</attribute>
|
||||
</item>
|
||||
<item>
|
||||
<attribute name="label" translatable="yes">Filter</attribute>
|
||||
<attribute name="action">win.filter</attribute>
|
||||
</item>
|
||||
</section>
|
||||
</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>
|
||||
<attribute name="action">app.on_satellite_editor_show</attribute>
|
||||
</item>
|
||||
<item>
|
||||
<attribute name="label" translatable="yes">Picons manager</attribute>
|
||||
<attribute name="action">app.on_picons_manager_show</attribute>
|
||||
</item>
|
||||
<item>
|
||||
<attribute name="label" translatable="yes">Backups</attribute>
|
||||
<attribute name="action">app.on_backup_tool_show</attribute>
|
||||
</item>
|
||||
</section>
|
||||
<section id="telnet_section">
|
||||
</section>
|
||||
</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>
|
||||
</item>
|
||||
<item>
|
||||
<attribute name="label" translatable="yes">Import YouTube playlist</attribute>
|
||||
<attribute name="action">app.on_import_yt_list</attribute>
|
||||
</item>
|
||||
<item>
|
||||
<attribute name="label" translatable="yes">Import m3u</attribute>
|
||||
<attribute name="action">app.on_import_m3u</attribute>
|
||||
</item>
|
||||
<item>
|
||||
<attribute name="label" translatable="yes">Export to m3u</attribute>
|
||||
<attribute name="action">app.on_export_to_m3u</attribute>
|
||||
</item>
|
||||
<section>
|
||||
<item>
|
||||
<attribute name="label" translatable="yes">EPG configuration</attribute>
|
||||
<attribute name="action">app.on_epg_list_configuration</attribute>
|
||||
</item>
|
||||
<item>
|
||||
<attribute name="label" translatable="yes">List configuration</attribute>
|
||||
<attribute name="action">app.on_iptv_list_configuration</attribute>
|
||||
</item>
|
||||
</section>
|
||||
<section>
|
||||
<item>
|
||||
<attribute name="label" translatable="yes">Remove all unavailable</attribute>
|
||||
<attribute name="action">app.on_remove_all_unavailable</attribute>
|
||||
</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>
|
||||
</menu>
|
||||
</interface>
|
||||
@@ -8,9 +8,9 @@ from enum import Enum
|
||||
|
||||
from app.commons import run_idle
|
||||
from app.settings import SettingsType
|
||||
from app.ui.dialogs import show_dialog, DialogType, get_builder
|
||||
from app.ui.dialogs import show_dialog, DialogType
|
||||
from app.ui.main_helper import append_text_to_tview
|
||||
from .uicommons import Gtk, Gdk, UI_RESOURCES_PATH, KeyboardKey
|
||||
from .uicommons import Gtk, Gdk, UI_RESOURCES_PATH, KeyboardKey, MOD_MASK
|
||||
|
||||
|
||||
class RestoreType(Enum):
|
||||
@@ -30,7 +30,10 @@ class BackupDialog:
|
||||
"on_resize": self.on_resize,
|
||||
"on_key_release": self.on_key_release}
|
||||
|
||||
builder = get_builder(UI_RESOURCES_PATH + "backup_dialog.glade", handlers)
|
||||
builder = Gtk.Builder()
|
||||
builder.set_translation_domain("demon-editor")
|
||||
builder.add_from_file(UI_RESOURCES_PATH + "backup_dialog.glade")
|
||||
builder.connect_signals(handlers)
|
||||
|
||||
self._settings = settings
|
||||
self._s_type = settings.setting_type
|
||||
@@ -173,7 +176,7 @@ class BackupDialog:
|
||||
if not KeyboardKey.value_exist(key_code):
|
||||
return
|
||||
key = KeyboardKey(key_code)
|
||||
ctrl = event.state & Gdk.ModifierType.CONTROL_MASK
|
||||
ctrl = event.state & MOD_MASK
|
||||
|
||||
if key is KeyboardKey.DELETE:
|
||||
self.on_remove(view)
|
||||
|
||||
@@ -33,6 +33,12 @@ Author: Dmitriy Yefremov
|
||||
<!-- interface-description Enigma2 channel and satellites list editor for GNU/Linux. -->
|
||||
<!-- interface-copyright 2018-2020 Dmitriy Yefremov -->
|
||||
<!-- interface-authors Dmitriy Yefremov -->
|
||||
<object class="GtkImage" id="details_image">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="icon_name">emblem-important-symbolic</property>
|
||||
<property name="icon_size">1</property>
|
||||
</object>
|
||||
<object class="GtkListStore" id="main_list_store">
|
||||
<columns>
|
||||
<!-- column-name date -->
|
||||
@@ -41,133 +47,89 @@ Author: Dmitriy Yefremov
|
||||
<column type="gboolean"/>
|
||||
</columns>
|
||||
</object>
|
||||
<object class="GtkMenu" id="popup_menu">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<child>
|
||||
<object class="GtkImageMenuItem" id="restore_bouquets_popup_menu_item">
|
||||
<property name="label" translatable="yes">Restore bouquets</property>
|
||||
<property name="visible">True</property>
|
||||
<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"/>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkImageMenuItem" id="restore_all_popup_menu_item">
|
||||
<property name="label" translatable="yes">Restore all</property>
|
||||
<property name="visible">True</property>
|
||||
<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"/>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkSeparatorMenuItem" id="popup_menu_separator">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkImageMenuItem" id="remove_popup_menu_item">
|
||||
<property name="label">gtk-remove</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="use_underline">True</property>
|
||||
<property name="use_stock">True</property>
|
||||
<signal name="activate" handler="on_remove" swapped="no"/>
|
||||
<accelerator key="Delete" signal="activate"/>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<object class="GtkImage" id="remove_image">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="icon_name">user-trash-symbolic</property>
|
||||
<property name="icon_size">1</property>
|
||||
</object>
|
||||
<object class="GtkImage" id="restore_all_image">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="icon_name">edit-select-all-symbolic</property>
|
||||
<property name="icon_size">1</property>
|
||||
</object>
|
||||
<object class="GtkImage" id="restore_bouquets_image">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="icon_name">document-revert-symbolic</property>
|
||||
<property name="icon_size">1</property>
|
||||
</object>
|
||||
<object class="GtkWindow" id="dialog_window">
|
||||
<property name="width_request">560</property>
|
||||
<property name="height_request">320</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="title" translatable="yes">Backups</property>
|
||||
<property name="modal">True</property>
|
||||
<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 type="titlebar">
|
||||
<object class="GtkHeaderBar" id="header_bar">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="title" translatable="yes">Backups</property>
|
||||
<property name="spacing">2</property>
|
||||
<property name="show_close_button">True</property>
|
||||
<child>
|
||||
<object class="GtkBox" id="header_box">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="spacing">2</property>
|
||||
<child>
|
||||
<object class="GtkButton" id="restore_bouquets_header_button">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="tooltip_text" translatable="yes">Restore bouquets</property>
|
||||
<signal name="clicked" handler="on_restore_bouquets" swapped="no"/>
|
||||
<child>
|
||||
<object class="GtkImage" id="restore_bouquets_header_button_image">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="icon_name">document-revert</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="restore_all_header_button">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="tooltip_text" translatable="yes">Restore all</property>
|
||||
<signal name="clicked" handler="on_restore_all" swapped="no"/>
|
||||
<child>
|
||||
<object class="GtkImage" id="restore_all_header_button_image">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="icon_name">edit-select-all</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="GtkSeparator">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="remove_header_button">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="tooltip_text" translatable="yes">Remove</property>
|
||||
<signal name="clicked" handler="on_remove" swapped="no"/>
|
||||
<child>
|
||||
<object class="GtkImage" id="remove_button_image">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="icon_name">user-trash</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">3</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkCheckButton" id="info_check_button">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">False</property>
|
||||
<property name="tooltip_text" translatable="yes">Details</property>
|
||||
<property name="draw_indicator">False</property>
|
||||
<signal name="toggled" handler="on_info_button_toggled" swapped="no"/>
|
||||
<child>
|
||||
<object class="GtkImage" id="info_check_button_image">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="stock">gtk-dialog-info</property>
|
||||
</object>
|
||||
</child>
|
||||
<accelerator key="i" signal="clicked" modifiers="GDK_CONTROL_MASK"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="pack_type">end</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkBox" id="main_box">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_left">1</property>
|
||||
<property name="margin_right">1</property>
|
||||
<property name="margin_top">1</property>
|
||||
<property name="margin_bottom">1</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<property name="spacing">5</property>
|
||||
<child>
|
||||
<object class="GtkPaned" id="main_paned">
|
||||
<property name="visible">True</property>
|
||||
@@ -251,6 +213,106 @@ Author: Dmitriy Yefremov
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkBox">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_left">15</property>
|
||||
<property name="margin_right">15</property>
|
||||
<child>
|
||||
<object class="GtkButtonBox">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="valign">center</property>
|
||||
<property name="margin_top">10</property>
|
||||
<property name="margin_bottom">10</property>
|
||||
<property name="layout_style">expand</property>
|
||||
<child>
|
||||
<object class="GtkButton" id="restore_bouquets_header_button">
|
||||
<property name="label" translatable="yes">Restore bouquets</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="valign">center</property>
|
||||
<property name="image">restore_bouquets_image</property>
|
||||
<property name="always_show_image">True</property>
|
||||
<signal name="clicked" handler="on_restore_bouquets" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="restore_all_header_button">
|
||||
<property name="label" translatable="yes">Restore all</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="valign">center</property>
|
||||
<property name="image">restore_all_image</property>
|
||||
<property name="always_show_image">True</property>
|
||||
<signal name="clicked" handler="on_restore_all" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="remove_header_button">
|
||||
<property name="label" translatable="yes">Remove</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="valign">center</property>
|
||||
<property name="image">remove_image</property>
|
||||
<property name="always_show_image">True</property>
|
||||
<signal name="clicked" handler="on_remove" swapped="no"/>
|
||||
<accelerator key="Delete" signal="clicked"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">3</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkCheckButton" id="info_check_button">
|
||||
<property name="label" translatable="yes">Details</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">False</property>
|
||||
<property name="valign">center</property>
|
||||
<property name="image">details_image</property>
|
||||
<property name="always_show_image">True</property>
|
||||
<property name="draw_indicator">False</property>
|
||||
<signal name="toggled" handler="on_info_button_toggled" swapped="no"/>
|
||||
<accelerator key="i" signal="clicked" modifiers="Primary"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="pack_type">end</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkInfoBar" id="info_bar">
|
||||
<property name="can_focus">False</property>
|
||||
@@ -301,57 +363,4 @@ Author: Dmitriy Yefremov
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<object class="GtkImage" id="restore_popup_menu_item_image">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="stock">gtk-revert-to-saved</property>
|
||||
</object>
|
||||
<object class="GtkImage" id="restore_popup_menu_item_image2">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="stock">gtk-select-all</property>
|
||||
</object>
|
||||
<object class="GtkMenu" id="popup_menu">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<child>
|
||||
<object class="GtkImageMenuItem" id="restore_bouquets_popup_menu_item">
|
||||
<property name="label" translatable="yes">Restore bouquets</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="image">restore_popup_menu_item_image</property>
|
||||
<property name="use_stock">False</property>
|
||||
<signal name="activate" handler="on_restore_bouquets" swapped="no"/>
|
||||
<accelerator key="r" signal="activate" modifiers="GDK_CONTROL_MASK"/>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkImageMenuItem" id="restore_all_popup_menu_item">
|
||||
<property name="label" translatable="yes">Restore all</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="image">restore_popup_menu_item_image2</property>
|
||||
<property name="use_stock">False</property>
|
||||
<signal name="activate" handler="on_restore_all" swapped="no"/>
|
||||
<accelerator key="e" signal="activate" modifiers="GDK_CONTROL_MASK"/>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkSeparatorMenuItem" id="popup_menu_separator">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkImageMenuItem" id="remove_popup_menu_item">
|
||||
<property name="label">gtk-remove</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="use_underline">True</property>
|
||||
<property name="use_stock">True</property>
|
||||
<signal name="activate" handler="on_remove" swapped="no"/>
|
||||
<accelerator key="Delete" signal="activate"/>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</interface>
|
||||
|
||||
@@ -367,7 +367,6 @@ Author: Dmitriy Yefremov
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_left">1</property>
|
||||
<property name="margin_right">1</property>
|
||||
<property name="margin_top">1</property>
|
||||
<property name="margin_bottom">1</property>
|
||||
<property name="label_xalign">0</property>
|
||||
<property name="shadow_type">in</property>
|
||||
@@ -969,7 +968,7 @@ audio-volume-medium-symbolic</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="tooltip_text" translatable="yes">Filter</property>
|
||||
<property name="primary_icon_stock">gtk-spell-check</property>
|
||||
<property name="primary_icon_name">tools-check-spelling</property>
|
||||
<property name="primary_icon_name">edit-find-replace-symbolic</property>
|
||||
<property name="primary_icon_activatable">False</property>
|
||||
<property name="primary_icon_sensitive">False</property>
|
||||
<property name="placeholder_text" translatable="yes">Filter</property>
|
||||
@@ -1177,7 +1176,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">gtk-edit</property>
|
||||
<property name="primary_icon_name">document-edit-symbolic</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
@@ -1188,7 +1187,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">gtk-edit</property>
|
||||
<property name="primary_icon_name">document-edit-symbolic</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
@@ -1199,7 +1198,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">gtk-edit</property>
|
||||
<property name="primary_icon_name">document-edit-symbolic</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
@@ -1486,7 +1485,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">gtk-edit</property>
|
||||
<property name="primary_icon_name">document-edit-symbolic</property>
|
||||
</object>
|
||||
</child>
|
||||
<style>
|
||||
@@ -1557,7 +1556,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">gtk-edit</property>
|
||||
<property name="primary_icon_name">document-edit-symbolic</property>
|
||||
</object>
|
||||
</child>
|
||||
<style>
|
||||
@@ -1608,11 +1607,11 @@ audio-volume-medium-symbolic</property>
|
||||
<object class="GtkImage" id="timer_location_image">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="icon_name">gtk-edit</property>
|
||||
<property name="icon_name">document-edit-symbolic</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
@@ -1622,8 +1621,8 @@ audio-volume-medium-symbolic</property>
|
||||
<property name="can_focus">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="pack_type">end</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
@@ -1639,7 +1638,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">gtk-edit</property>
|
||||
<property name="primary_icon_name">document-edit-symbolic</property>
|
||||
<property name="placeholder_text" translatable="yes">Default</property>
|
||||
</object>
|
||||
<packing>
|
||||
|
||||
@@ -6,7 +6,7 @@ from urllib.parse import quote
|
||||
|
||||
from gi.repository import GLib
|
||||
|
||||
from .dialogs import show_dialog, DialogType, get_message, get_builder
|
||||
from .dialogs import get_dialogs_string, 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
|
||||
@@ -84,7 +84,8 @@ class ControlBox(Gtk.HBox):
|
||||
|
||||
self._timer = timer
|
||||
|
||||
builder = get_builder(self._UI_PATH, None, use_str=True)
|
||||
builder = Gtk.Builder()
|
||||
builder.add_from_string(get_dialogs_string(self._UI_PATH))
|
||||
row_box = builder.get_object("timer_row_box")
|
||||
name_label = builder.get_object("timer_name_label")
|
||||
description_label = builder.get_object("timer_description_label")
|
||||
@@ -129,7 +130,9 @@ class ControlBox(Gtk.HBox):
|
||||
"on_timers_press": self.on_timers_press,
|
||||
"on_timers_drag_data_received": self.on_timers_drag_data_received}
|
||||
|
||||
builder = get_builder(UI_RESOURCES_PATH + "control.glade", handlers)
|
||||
builder = Gtk.Builder()
|
||||
builder.add_from_file(UI_RESOURCES_PATH + "control.glade")
|
||||
builder.connect_signals(handlers)
|
||||
|
||||
self.add(builder.get_object("main_box_frame"))
|
||||
self._stack = builder.get_object("stack")
|
||||
|
||||
43
app/ui/default_style.css
Normal file
43
app/ui/default_style.css
Normal file
@@ -0,0 +1,43 @@
|
||||
* {
|
||||
-GtkDialog-action-area-border: 5em;
|
||||
}
|
||||
|
||||
entry {
|
||||
min-height: 2em;
|
||||
}
|
||||
|
||||
button {
|
||||
min-height: 1.5em;
|
||||
padding-left: 0.5em;
|
||||
padding-right: 0.5em;
|
||||
padding-top: 0.1em;
|
||||
padding-bottom: 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;
|
||||
}
|
||||
@@ -30,7 +30,7 @@ Author: Dmitriy Yefremov
|
||||
<requires lib="gtk+" version="3.16"/>
|
||||
<!-- interface-license-type mit -->
|
||||
<!-- interface-name DemonEditor -->
|
||||
<!-- interface-description Enigma2 channel and satellites list editor for GNU/Linux. -->
|
||||
<!-- interface-description Enigma2 channel and satellites list editor for macOS. -->
|
||||
<!-- interface-copyright 2018-2021 Dmitriy Yefremov -->
|
||||
<!-- interface-authors Dmitriy Yefremov -->
|
||||
<object class="GtkAboutDialog" id="about_dialog">
|
||||
@@ -40,11 +40,12 @@ 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.9 Beta</property>
|
||||
<property name="version">1.0.7 Beta</property>
|
||||
<property name="copyright">2018-2021 Dmitriy Yefremov
|
||||
</property>
|
||||
<property name="comments" translatable="yes">Enigma2 channel and satellite list editor for GNU/Linux.</property>
|
||||
<property name="website">https://dyefremov.github.io/DemonEditor/</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 name="license" translatable="yes">Это приложение распространяется без каких-либо гарантий.
|
||||
Подробнее в <a href="http://opensource.org/licenses/mit-license.php">The MIT License (MIT)</a>.</property>
|
||||
<property name="authors">Dmitriy Yefremov
|
||||
@@ -65,7 +66,9 @@ Author: Dmitriy Yefremov
|
||||
<child internal-child="action_area">
|
||||
<object class="GtkButtonBox" id="aboutdialog_action_area">
|
||||
<property name="can_focus">False</property>
|
||||
<property name="layout_style">end</property>
|
||||
<property name="margin_left">50</property>
|
||||
<property name="margin_right">50</property>
|
||||
<property name="layout_style">expand</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
@@ -86,36 +89,62 @@ Author: Dmitriy Yefremov
|
||||
<property name="window_position">center</property>
|
||||
<property name="default_width">320</property>
|
||||
<property name="destroy_with_parent">True</property>
|
||||
<property name="type_hint">dialog</property>
|
||||
<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="action">
|
||||
<object class="GtkButton" id="input_dialog_cancel_button">
|
||||
<property name="label">gtk-cancel</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="use_stock">True</property>
|
||||
<property name="always_show_image">True</property>
|
||||
</object>
|
||||
</child>
|
||||
<child type="action">
|
||||
<object class="GtkButton" id="input_dialog_ok_button">
|
||||
<property name="label">gtk-ok</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="use_stock">True</property>
|
||||
<property name="always_show_image">True</property>
|
||||
<accelerator key="Return" signal="activate"/>
|
||||
</object>
|
||||
<child type="titlebar">
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child internal-child="vbox">
|
||||
<object class="GtkBox">
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_left">5</property>
|
||||
<property name="margin_right">5</property>
|
||||
<property name="margin_top">4</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<property name="spacing">2</property>
|
||||
<child internal-child="action_area">
|
||||
<object class="GtkButtonBox">
|
||||
<property name="can_focus">False</property>
|
||||
<property name="valign">center</property>
|
||||
<property name="layout_style">end</property>
|
||||
<child>
|
||||
<object class="GtkButton" id="input_dialog_cancel_button">
|
||||
<property name="label" translatable="yes">Cancel</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="valign">center</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="input_dialog_ok_button">
|
||||
<property name="label" translatable="yes">OK</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="valign">center</property>
|
||||
<accelerator key="Return" signal="activate"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkEntry" id="input_entry">
|
||||
<property name="visible">True</property>
|
||||
@@ -124,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_stock">gtk-edit</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>
|
||||
@@ -132,7 +161,7 @@ Author: Dmitriy Yefremov
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
@@ -142,7 +171,7 @@ Author: Dmitriy Yefremov
|
||||
<action-widget response="ok">input_dialog_ok_button</action-widget>
|
||||
</action-widgets>
|
||||
</object>
|
||||
<object class="GtkDialog" id="wait_dialog">
|
||||
<object class="GtkWindow" id="wait_dialog">
|
||||
<property name="can_focus">False</property>
|
||||
<property name="resizable">False</property>
|
||||
<property name="modal">True</property>
|
||||
@@ -152,63 +181,42 @@ Author: Dmitriy Yefremov
|
||||
<property name="skip_taskbar_hint">True</property>
|
||||
<property name="skip_pager_hint">True</property>
|
||||
<property name="decorated">False</property>
|
||||
<child>
|
||||
<property name="gravity">center</property>
|
||||
<child type="titlebar">
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child internal-child="vbox">
|
||||
<object class="GtkBox" id="wait_dialog_vbox">
|
||||
<property name="width_request">120</property>
|
||||
<child>
|
||||
<object class="GtkBox" id="wait_dialog_box">
|
||||
<property name="width_request">100</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_top">5</property>
|
||||
<property name="margin_bottom">5</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<child internal-child="action_area">
|
||||
<object class="GtkButtonBox" id="dialog-action_area4">
|
||||
<child>
|
||||
<object class="GtkSpinner" id="spinner">
|
||||
<property name="width_request">150</property>
|
||||
<property name="height_request">45</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="opacity">0</property>
|
||||
<property name="layout_style">end</property>
|
||||
<property name="active">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkBox" id="box4">
|
||||
<object class="GtkLabel" id="wait_dialog_label">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_top">2</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<object class="GtkSpinner" id="spinner">
|
||||
<property name="width_request">150</property>
|
||||
<property name="height_request">45</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="active">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="wait_dialog_label">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_left">10</property>
|
||||
<property name="margin_right">10</property>
|
||||
<property name="label" translatable="yes">Loading data...</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<property name="margin_left">10</property>
|
||||
<property name="margin_right">10</property>
|
||||
<property name="label" translatable="yes">Loading data...</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
""" Common module for showing dialogs """
|
||||
import locale
|
||||
import gettext
|
||||
from enum import Enum
|
||||
from functools import lru_cache
|
||||
from pathlib import Path
|
||||
@@ -104,12 +104,10 @@ 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
|
||||
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 = Gtk.FileChooserNative.new(get_message(title) if title else "", transient, action_type)
|
||||
dialog.set_create_folders(dirs)
|
||||
dialog.set_modal(True)
|
||||
|
||||
if file_filter is not None:
|
||||
dialog.add_filter(file_filter)
|
||||
@@ -117,7 +115,7 @@ 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 not in (Gtk.ResponseType.CANCEL, Gtk.ResponseType.DELETE_EVENT):
|
||||
if response == Gtk.ResponseType.ACCEPT:
|
||||
path = Path(dialog.get_filename() or dialog.get_current_folder())
|
||||
if path.is_dir():
|
||||
response = "{}/".format(path.resolve())
|
||||
@@ -176,7 +174,7 @@ def get_dialog_from_xml(dialog_type, transient, use_header=0, title=""):
|
||||
|
||||
def get_message(message):
|
||||
""" returns translated message """
|
||||
return locale.dgettext(TEXT_DOMAIN, message)
|
||||
return gettext.dgettext(TEXT_DOMAIN, message)
|
||||
|
||||
|
||||
@lru_cache(maxsize=5)
|
||||
@@ -185,26 +183,5 @@ def get_dialogs_string(path):
|
||||
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 {})
|
||||
|
||||
return builder
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
pass
|
||||
|
||||
464
app/ui/download_dialog.glade
Normal file → Executable file
464
app/ui/download_dialog.glade
Normal file → Executable file
@@ -3,7 +3,7 @@
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2018 Dmitriy Yefremov
|
||||
Copyright (c) 2018-2020 Dmitriy Yefremov
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -30,12 +30,25 @@ Author: Dmitriy Yefremov
|
||||
<requires lib="gtk+" version="3.16"/>
|
||||
<!-- interface-license-type mit -->
|
||||
<!-- interface-name DemonEditor -->
|
||||
<!-- interface-description Enigma2 channel and satellites list editor for GNU/Linux. -->
|
||||
<!-- interface-copyright 2018 Dmitriy Yefremov -->
|
||||
<!-- interface-description Enigma2 channel and satellites list editor for macOS. -->
|
||||
<!-- interface-copyright 2018-2020 Dmitriy Yefremov -->
|
||||
<!-- interface-authors Dmitriy Yefremov -->
|
||||
<object class="GtkImage" id="download_image">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="icon_name">network-receive-symbolic</property>
|
||||
<property name="icon_size">1</property>
|
||||
</object>
|
||||
<object class="GtkImage" id="send_image">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="icon_name">network-transmit-symbolic</property>
|
||||
<property name="icon_size">1</property>
|
||||
</object>
|
||||
<object class="GtkWindow" id="download_dialog_window">
|
||||
<property name="width_request">550</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="title" translatable="yes">FTP-transfer</property>
|
||||
<property name="resizable">False</property>
|
||||
<property name="modal">True</property>
|
||||
<property name="window_position">center-on-parent</property>
|
||||
@@ -44,226 +57,20 @@ Author: Dmitriy Yefremov
|
||||
<property name="skip_pager_hint">True</property>
|
||||
<property name="gravity">center</property>
|
||||
<child type="titlebar">
|
||||
<object class="GtkHeaderBar" id="header_bar">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="title" translatable="yes">FTP-transfer</property>
|
||||
<property name="spacing">5</property>
|
||||
<property name="show_close_button">True</property>
|
||||
<child>
|
||||
<object class="GtkBox" id="header_left_box">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="spacing">2</property>
|
||||
<child>
|
||||
<object class="GtkButton" id="receive_button">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="tooltip_text" translatable="yes">Receive</property>
|
||||
<signal name="clicked" handler="on_receive" swapped="no"/>
|
||||
<child>
|
||||
<object class="GtkImage" id="receive_button_image">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="stock">gtk-goto-bottom</property>
|
||||
</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="send_button">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="tooltip_text" translatable="yes">Send</property>
|
||||
<signal name="clicked" handler="on_send" swapped="no"/>
|
||||
<child>
|
||||
<object class="GtkImage" id="send_button_image">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="stock">gtk-goto-top</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="options_button">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="tooltip_text" translatable="yes">Options</property>
|
||||
<signal name="clicked" handler="on_settings" swapped="no"/>
|
||||
<child>
|
||||
<object class="GtkImage">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="stock">gtk-properties</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="pack_type">end</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkBox" id="main_dialog_box">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_left">1</property>
|
||||
<property name="margin_right">1</property>
|
||||
<property name="margin_bottom">1</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<property name="spacing">2</property>
|
||||
<child>
|
||||
<object class="GtkBox" id="selection_data_box">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="halign">center</property>
|
||||
<property name="margin_top">10</property>
|
||||
<property name="margin_bottom">5</property>
|
||||
<property name="spacing">5</property>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label10">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="xalign">0</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkRadioButton" id="all_radio_button">
|
||||
<property name="label" translatable="yes">All</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">False</property>
|
||||
<property name="draw_indicator">True</property>
|
||||
<property name="group">satellites_radio_button</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkRadioButton" id="bouquets_radio_button">
|
||||
<property name="label" translatable="yes">Bouquets</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">False</property>
|
||||
<property name="draw_indicator">True</property>
|
||||
<property name="group">satellites_radio_button</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkRadioButton" id="satellites_radio_button">
|
||||
<property name="label" translatable="yes">Satellites</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">False</property>
|
||||
<property name="draw_indicator">True</property>
|
||||
<property name="group">all_radio_button</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">3</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkRadioButton" id="webtv_radio_button">
|
||||
<property name="label" translatable="yes">WebTV</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">False</property>
|
||||
<property name="draw_indicator">True</property>
|
||||
<property name="group">all_radio_button</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">4</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkBox" id="profile_box">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="halign">center</property>
|
||||
<property name="margin_top">5</property>
|
||||
<property name="margin_bottom">5</property>
|
||||
<property name="spacing">5</property>
|
||||
<child>
|
||||
<object class="GtkLabel">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">Profile:</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkComboBoxText" id="profile_combo_box">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="focus_on_click">False</property>
|
||||
<property name="active">0</property>
|
||||
<property name="has_frame">False</property>
|
||||
<signal name="changed" handler="on_profile_changed" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkFrame" id="main_settings_box_frame">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_left">5</property>
|
||||
<property name="margin_right">5</property>
|
||||
<property name="margin_left">10</property>
|
||||
<property name="margin_right">10</property>
|
||||
<property name="margin_top">10</property>
|
||||
<property name="margin_bottom">5</property>
|
||||
<property name="label_xalign">0.019999999552965164</property>
|
||||
<property name="shadow_type">in</property>
|
||||
@@ -271,8 +78,8 @@ Author: Dmitriy Yefremov
|
||||
<object class="GtkBox" id="main_settings_box">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_left">5</property>
|
||||
<property name="margin_right">5</property>
|
||||
<property name="margin_left">10</property>
|
||||
<property name="margin_right">10</property>
|
||||
<property name="margin_top">5</property>
|
||||
<property name="margin_bottom">5</property>
|
||||
<property name="orientation">vertical</property>
|
||||
@@ -424,6 +231,229 @@ Author: Dmitriy Yefremov
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkFrame">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_left">10</property>
|
||||
<property name="margin_right">10</property>
|
||||
<property name="label_xalign">0</property>
|
||||
<property name="shadow_type">in</property>
|
||||
<child>
|
||||
<object class="GtkBox">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<object class="GtkBox" id="selection_data_box">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="halign">center</property>
|
||||
<property name="margin_top">10</property>
|
||||
<property name="margin_bottom">5</property>
|
||||
<property name="spacing">5</property>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label10">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="xalign">0</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkRadioButton" id="all_radio_button">
|
||||
<property name="label" translatable="yes">All</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">False</property>
|
||||
<property name="draw_indicator">True</property>
|
||||
<property name="group">satellites_radio_button</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkRadioButton" id="bouquets_radio_button">
|
||||
<property name="label" translatable="yes">Bouquets</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">False</property>
|
||||
<property name="draw_indicator">True</property>
|
||||
<property name="group">satellites_radio_button</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkRadioButton" id="satellites_radio_button">
|
||||
<property name="label" translatable="yes">Satellites</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">False</property>
|
||||
<property name="draw_indicator">True</property>
|
||||
<property name="group">all_radio_button</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">3</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkRadioButton" id="webtv_radio_button">
|
||||
<property name="label" translatable="yes">WebTV</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">False</property>
|
||||
<property name="draw_indicator">True</property>
|
||||
<property name="group">all_radio_button</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">4</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkBox">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="halign">center</property>
|
||||
<property name="margin_top">5</property>
|
||||
<property name="margin_bottom">5</property>
|
||||
<property name="spacing">5</property>
|
||||
<child>
|
||||
<object class="GtkLabel">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">Profile:</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkComboBoxText" id="profile_combo_box">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="focus_on_click">False</property>
|
||||
<property name="active">0</property>
|
||||
<property name="has_frame">False</property>
|
||||
<signal name="changed" handler="on_profile_changed" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="options_button">
|
||||
<property name="label" translatable="yes">Options</property>
|
||||
<property name="visible">True</property>
|
||||
<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>
|
||||
<property name="expand">True</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">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child type="label_item">
|
||||
<placeholder/>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">4</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButtonBox">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="valign">center</property>
|
||||
<property name="margin_left">20</property>
|
||||
<property name="margin_right">20</property>
|
||||
<property name="margin_top">10</property>
|
||||
<property name="margin_bottom">10</property>
|
||||
<property name="homogeneous">True</property>
|
||||
<property name="layout_style">expand</property>
|
||||
<child>
|
||||
<object class="GtkButton" id="receive_button">
|
||||
<property name="label" translatable="yes">Receive</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="tooltip_text" translatable="yes">Receive</property>
|
||||
<property name="valign">center</property>
|
||||
<property name="image">download_image</property>
|
||||
<property name="always_show_image">True</property>
|
||||
<signal name="clicked" handler="on_receive" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="send_button">
|
||||
<property name="label" translatable="yes">Send</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="tooltip_text" translatable="yes">Send</property>
|
||||
<property name="valign">center</property>
|
||||
<property name="image">send_image</property>
|
||||
<property name="always_show_image">True</property>
|
||||
<signal name="clicked" handler="on_send" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
<property name="position">6</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkExpander" id="expander">
|
||||
<property name="visible">True</property>
|
||||
@@ -460,7 +490,7 @@ Author: Dmitriy Yefremov
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">4</property>
|
||||
<property name="position">7</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
@@ -516,7 +546,7 @@ Author: Dmitriy Yefremov
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">5</property>
|
||||
<property name="position">8</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
|
||||
@@ -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, get_builder
|
||||
from .dialogs import show_dialog, DialogType, get_message
|
||||
from .uicommons import Gtk, UI_RESOURCES_PATH
|
||||
|
||||
|
||||
@@ -27,7 +27,9 @@ class DownloadDialog:
|
||||
"on_remove_unused_bouquets_toggled": self.on_remove_unused_bouquets_toggled,
|
||||
"on_info_bar_close": self.on_info_bar_close}
|
||||
|
||||
builder = get_builder(UI_RESOURCES_PATH + "download_dialog.glade", handlers)
|
||||
builder = Gtk.Builder()
|
||||
builder.add_from_file(UI_RESOURCES_PATH + "download_dialog.glade")
|
||||
builder.connect_signals(handlers)
|
||||
|
||||
self._dialog_window = builder.get_object("download_dialog_window")
|
||||
self._dialog_window.set_transient_for(transient)
|
||||
|
||||
@@ -33,6 +33,18 @@ Author: Dmitriy Yefremov
|
||||
<!-- interface-description Enigma2 channel and satellites list editor for GNU/Linux. -->
|
||||
<!-- interface-copyright 2018-2020 Dmitriy Yefremov -->
|
||||
<!-- interface-authors Dmitriy Yefremov -->
|
||||
<object class="GtkImage" id="apply_image">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="icon_name">document-save-symbolic</property>
|
||||
<property name="icon_size">1</property>
|
||||
</object>
|
||||
<object class="GtkImage" id="auto_config_image">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="icon_name">edit-select-all-symbolic</property>
|
||||
<property name="icon_size">1</property>
|
||||
</object>
|
||||
<object class="GtkListStore" id="bouquet_list_store">
|
||||
<columns>
|
||||
<!-- column-name num -->
|
||||
@@ -75,10 +87,16 @@ 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="GDK_CONTROL_MASK"/>
|
||||
<accelerator key="c" signal="activate" modifiers="Primary"/>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<object class="GtkImage" id="filter_image">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="icon_name">edit-find-replace-symbolic</property>
|
||||
<property name="icon_size">1</property>
|
||||
</object>
|
||||
<object class="GtkImage" id="insert_link_image">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
@@ -100,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="GDK_CONTROL_MASK"/>
|
||||
<accelerator key="v" signal="activate" modifiers="Primary"/>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
@@ -130,6 +148,12 @@ Author: Dmitriy Yefremov
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<object class="GtkImage" id="save_to_xml_image">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="icon_name">document-save-as-symbolic</property>
|
||||
<property name="icon_size">1</property>
|
||||
</object>
|
||||
<object class="GtkListStore" id="services_list_store">
|
||||
<columns>
|
||||
<!-- column-name service -->
|
||||
@@ -312,7 +336,7 @@ Author: Dmitriy Yefremov
|
||||
<object class="GtkEntry" id="url_to_xml_entry">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="primary_icon_stock">gtk-connect</property>
|
||||
<property name="primary_icon_name">network-transmit-receive-symbolic</property>
|
||||
<property name="primary_icon_activatable">False</property>
|
||||
</object>
|
||||
<packing>
|
||||
@@ -412,8 +436,8 @@ 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_stock">gtk-edit</property>
|
||||
<property name="secondary_icon_stock">gtk-directory</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>
|
||||
<signal name="icon-press" handler="on_field_icon_press" swapped="no"/>
|
||||
@@ -442,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_stock">gtk-edit</property>
|
||||
<property name="primary_icon_name">document-edit-symbolic</property>
|
||||
<property name="primary_icon_activatable">False</property>
|
||||
</object>
|
||||
<packing>
|
||||
@@ -559,58 +583,104 @@ Author: Dmitriy Yefremov
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<object class="GtkImage" id="update_image">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="icon_name">emblem-synchronizing-symbolic</property>
|
||||
<property name="icon_size">1</property>
|
||||
</object>
|
||||
<object class="GtkWindow" id="epg_dialog_window">
|
||||
<property name="width_request">480</property>
|
||||
<property name="height_request">320</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="title" translatable="yes">EPG</property>
|
||||
<property name="modal">True</property>
|
||||
<property name="window_position">center-on-parent</property>
|
||||
<property name="default_width">480</property>
|
||||
<property name="default_height">240</property>
|
||||
<property name="default_height">320</property>
|
||||
<property name="destroy_with_parent">True</property>
|
||||
<property name="icon_name">gtk-index</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 type="titlebar">
|
||||
<object class="GtkHeaderBar" id="header_bar">
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkBox" id="main_box">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="title" translatable="yes">EPG</property>
|
||||
<property name="subtitle" translatable="yes">List configuration</property>
|
||||
<property name="spacing">2</property>
|
||||
<property name="show_close_button">True</property>
|
||||
<property name="margin_left">1</property>
|
||||
<property name="margin_right">1</property>
|
||||
<property name="margin_bottom">1</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<object class="GtkBox" id="left_header_box">
|
||||
<object class="GtkBox" id="main_actions_box">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="spacing">2</property>
|
||||
<property name="margin_left">15</property>
|
||||
<property name="margin_right">15</property>
|
||||
<property name="margin_top">10</property>
|
||||
<property name="margin_bottom">10</property>
|
||||
<property name="spacing">10</property>
|
||||
<child>
|
||||
<object class="GtkButton" id="apply_button">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="tooltip_text" translatable="yes">Apply</property>
|
||||
<signal name="clicked" handler="on_apply" swapped="no"/>
|
||||
<child>
|
||||
<object class="GtkImage" id="apply_button_image">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="stock">gtk-apply</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="GtkSeparator">
|
||||
<object class="GtkButtonBox" id="left_action_box">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="layout_style">expand</property>
|
||||
<child>
|
||||
<object class="GtkButton" id="apply_button">
|
||||
<property name="label" translatable="yes">Apply</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="tooltip_text" translatable="yes">Apply</property>
|
||||
<property name="image">apply_image</property>
|
||||
<property name="always_show_image">True</property>
|
||||
<signal name="clicked" handler="on_apply" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="update_button">
|
||||
<property name="label" translatable="yes">Update</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="tooltip_text" translatable="yes">Update</property>
|
||||
<property name="image">update_image</property>
|
||||
<property name="always_show_image">True</property>
|
||||
<signal name="clicked" handler="on_update" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkToggleButton" id="filter_button">
|
||||
<property name="label" translatable="yes">Filter</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="tooltip_text" translatable="yes">Filter</property>
|
||||
<property name="image">filter_image</property>
|
||||
<property name="always_show_image">True</property>
|
||||
<signal name="toggled" handler="on_filter_toggled" swapped="no"/>
|
||||
<accelerator key="f" signal="clicked" modifiers="GDK_SHIFT_MASK | GDK_CONTROL_MASK"/>
|
||||
</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>
|
||||
@@ -618,20 +688,11 @@ Author: Dmitriy Yefremov
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="update_button">
|
||||
<child type="center">
|
||||
<object class="GtkLabel" id="list_label">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="tooltip_text" translatable="yes">Update</property>
|
||||
<signal name="clicked" handler="on_update" swapped="no"/>
|
||||
<child>
|
||||
<object class="GtkImage" id="update_image">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="stock">gtk-refresh</property>
|
||||
</object>
|
||||
</child>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">List configuration</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
@@ -640,111 +701,106 @@ Author: Dmitriy Yefremov
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkToggleButton" id="filter_button">
|
||||
<object class="GtkButtonBox" id="right_action_box">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="tooltip_text" translatable="yes">Filter</property>
|
||||
<signal name="toggled" handler="on_filter_toggled" swapped="no"/>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="layout_style">expand</property>
|
||||
<child>
|
||||
<object class="GtkImage" id="filter_button_image">
|
||||
<object class="GtkButton" id="auto_config_button">
|
||||
<property name="label" translatable="yes">Auto</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="stock">gtk-spell-check</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="tooltip_text" translatable="yes">Auto configuration by service names.</property>
|
||||
<property name="image">auto_config_image</property>
|
||||
<property name="always_show_image">True</property>
|
||||
<signal name="clicked" handler="on_auto_configuration" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<accelerator key="f" signal="clicked" modifiers="GDK_SHIFT_MASK | GDK_CONTROL_MASK"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">3</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkBox" id="right_header_box">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="spacing">2</property>
|
||||
<child>
|
||||
<object class="GtkButton" id="auto_config_button">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="tooltip_text" translatable="yes">Auto configuration by service names.</property>
|
||||
<signal name="clicked" handler="on_auto_configuration" swapped="no"/>
|
||||
<child>
|
||||
<object class="GtkImage" id="auto_config_button_image">
|
||||
<object class="GtkButton" id="save_to_xml_button">
|
||||
<property name="label" translatable="yes">Save</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="stock">gtk-find-and-replace</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="tooltip_text" translatable="yes">Save list to xml.</property>
|
||||
<property name="image">save_to_xml_image</property>
|
||||
<property name="always_show_image">True</property>
|
||||
<signal name="clicked" handler="on_save_to_xml" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkMenuButton" id="options_menu_button">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="direction">none</property>
|
||||
<property name="popover">options_popover</property>
|
||||
<child>
|
||||
<object class="GtkBox">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="spacing">2</property>
|
||||
<child>
|
||||
<object class="GtkImage" id="options_button_image">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="tooltip_text" translatable="yes">Options</property>
|
||||
<property name="icon_name">applications-system-symbolic</property>
|
||||
<property name="icon_size">1</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">Options</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="pack_type">end</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="save_to_xml_button">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="tooltip_text" translatable="yes">Save list to xml.</property>
|
||||
<signal name="clicked" handler="on_save_to_xml" swapped="no"/>
|
||||
<child>
|
||||
<object class="GtkImage" id="save_to_xml_button_image">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="stock">gtk-save-as</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="GtkMenuButton" id="options_menu_button">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="direction">none</property>
|
||||
<property name="popover">options_popover</property>
|
||||
<child>
|
||||
<object class="GtkImage" id="options_button_image">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="tooltip_text" translatable="yes">Options</property>
|
||||
<property name="stock">gtk-properties</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">3</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="pack_type">end</property>
|
||||
<property name="position">1</property>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkBox" id="main_box">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<object class="GtkSearchBar" id="filter_bar">
|
||||
<property name="visible">True</property>
|
||||
@@ -753,7 +809,7 @@ Author: Dmitriy Yefremov
|
||||
<object class="GtkSearchEntry" id="filter_entry">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="primary_icon_name">tools-check-spelling</property>
|
||||
<property name="primary_icon_name">edit-find-replace-symbolic</property>
|
||||
<property name="primary_icon_activatable">False</property>
|
||||
<property name="primary_icon_sensitive">False</property>
|
||||
<signal name="search-changed" handler="on_filter_changed" swapped="no"/>
|
||||
@@ -862,13 +918,14 @@ Author: Dmitriy Yefremov
|
||||
<property name="height_request">24</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_left">2</property>
|
||||
<property name="margin_left">5</property>
|
||||
<property name="spacing">2</property>
|
||||
<child>
|
||||
<object class="GtkImage">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="stock">gtk-properties</property>
|
||||
<property name="icon_name">document-properties-symbolic</property>
|
||||
<property name="icon_size">1</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
@@ -890,6 +947,7 @@ Author: Dmitriy Yefremov
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkBox" id="source_info_box">
|
||||
<property name="height_request">26</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="halign">center</property>
|
||||
@@ -1143,16 +1201,17 @@ Author: Dmitriy Yefremov
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkBox" id="bouquet_info_bar_box">
|
||||
<property name="height_request">24</property>
|
||||
<property name="height_request">26</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_left">2</property>
|
||||
<property name="margin_left">5</property>
|
||||
<property name="spacing">2</property>
|
||||
<child>
|
||||
<object class="GtkImage">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="stock">gtk-properties</property>
|
||||
<property name="icon_name">document-properties-symbolic</property>
|
||||
<property name="icon_size">1</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
@@ -1189,7 +1248,9 @@ Author: Dmitriy Yefremov
|
||||
<object class="GtkImage">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_left">2</property>
|
||||
<property name="stock">gtk-index</property>
|
||||
<property name="icon_size">1</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
|
||||
@@ -8,14 +8,13 @@ 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, get_builder
|
||||
from app.ui.dialogs import get_message, show_dialog, DialogType
|
||||
from .main_helper import on_popup_menu, update_entry_data
|
||||
from .uicommons import Gtk, Gdk, UI_RESOURCES_PATH, Column, EPG_ICON, KeyboardKey
|
||||
from .uicommons import Gtk, Gdk, UI_RESOURCES_PATH, TEXT_DOMAIN, Column, EPG_ICON, KeyboardKey, MOD_MASK
|
||||
|
||||
|
||||
class RefsSource(Enum):
|
||||
@@ -67,7 +66,10 @@ class EpgDialog:
|
||||
self._show_tooltips = True
|
||||
self._download_xml_is_active = False
|
||||
|
||||
builder = get_builder(UI_RESOURCES_PATH + "epg_dialog.glade", handlers)
|
||||
builder = Gtk.Builder()
|
||||
builder.set_translation_domain(TEXT_DOMAIN)
|
||||
builder.add_from_file(UI_RESOURCES_PATH + "epg_dialog.glade")
|
||||
builder.connect_signals(handlers)
|
||||
|
||||
self._dialog = builder.get_object("epg_dialog_window")
|
||||
self._dialog.set_transient_for(transient)
|
||||
@@ -276,7 +278,7 @@ class EpgDialog:
|
||||
if not KeyboardKey.value_exist(key_code):
|
||||
return
|
||||
key = KeyboardKey(key_code)
|
||||
ctrl = event.state & Gdk.ModifierType.CONTROL_MASK
|
||||
ctrl = event.state & MOD_MASK
|
||||
|
||||
if ctrl and key is KeyboardKey.C:
|
||||
self.on_copy_ref()
|
||||
|
||||
@@ -89,7 +89,6 @@ Author: Dmitriy Yefremov
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_left">1</property>
|
||||
<property name="margin_right">1</property>
|
||||
<property name="margin_top">1</property>
|
||||
<property name="margin_bottom">1</property>
|
||||
<property name="label_xalign">0</property>
|
||||
<property name="shadow_type">in</property>
|
||||
@@ -315,7 +314,7 @@ Author: Dmitriy Yefremov
|
||||
<child>
|
||||
<object class="GtkTreeViewColumn" id="ftp_attr_column">
|
||||
<property name="sizing">fixed</property>
|
||||
<property name="min_width">85</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>
|
||||
@@ -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="GDK_CONTROL_MASK"/>
|
||||
<accelerator key="r" signal="activate" modifiers="Primary"/>
|
||||
<accelerator key="F2" signal="activate"/>
|
||||
</object>
|
||||
</child>
|
||||
|
||||
@@ -12,7 +12,7 @@ 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, get_builder
|
||||
from app.ui.dialogs import show_dialog, DialogType
|
||||
from app.ui.main_helper import on_popup_menu
|
||||
from .uicommons import Gtk, Gdk, UI_RESOURCES_PATH, KeyboardKey, MOD_MASK
|
||||
|
||||
@@ -68,7 +68,9 @@ class FtpClientBox(Gtk.HBox):
|
||||
"on_view_press": self.on_view_press,
|
||||
"on_view_release": self.on_view_release}
|
||||
|
||||
builder = get_builder(UI_RESOURCES_PATH + "ftp.glade", handlers)
|
||||
builder = Gtk.Builder()
|
||||
builder.add_from_file(UI_RESOURCES_PATH + "ftp.glade")
|
||||
builder.connect_signals(handlers)
|
||||
|
||||
self.add(builder.get_object("main_frame"))
|
||||
self._ftp_info_label = builder.get_object("ftp_info_label")
|
||||
|
||||
@@ -26,13 +26,25 @@ THE SOFTWARE.
|
||||
Author: Dmitriy Yefremov
|
||||
|
||||
-->
|
||||
<interface>
|
||||
<interface domain="demon-editor">
|
||||
<requires lib="gtk+" version="3.16"/>
|
||||
<!-- interface-css-provider-path style.css -->
|
||||
<!-- interface-license-type mit -->
|
||||
<!-- interface-name DemonEditor -->
|
||||
<!-- interface-description Enigma2 channel and satellites list editor for GNU/Linux. -->
|
||||
<!-- interface-description Enigma2 channel and satellites list editor for macOS. -->
|
||||
<!-- interface-copyright 2018-2020 Dmitriy Yefremov -->
|
||||
<!-- interface-authors Dmitriy Yefremov -->
|
||||
<object class="GtkImage" id="details_image">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="icon_name">emblem-important-symbolic</property>
|
||||
</object>
|
||||
<object class="GtkImage" id="import_image">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="icon_name">document-revert-symbolic-rtl</property>
|
||||
<property name="icon_size">1</property>
|
||||
</object>
|
||||
<object class="GtkListStore" id="main_list_store">
|
||||
<columns>
|
||||
<!-- column-name name -->
|
||||
@@ -82,6 +94,7 @@ Author: Dmitriy Yefremov
|
||||
</object>
|
||||
<object class="GtkWindow" id="dialog_window">
|
||||
<property name="can_focus">False</property>
|
||||
<property name="title" translatable="yes">Import</property>
|
||||
<property name="modal">True</property>
|
||||
<property name="window_position">center-on-parent</property>
|
||||
<property name="default_width">480</property>
|
||||
@@ -90,53 +103,8 @@ Author: Dmitriy Yefremov
|
||||
<property name="type_hint">dialog</property>
|
||||
<property name="gravity">center</property>
|
||||
<signal name="check-resize" handler="on_resize" swapped="no"/>
|
||||
<child type="titlebar">
|
||||
<object class="GtkHeaderBar" id="header_bar">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="title" translatable="yes">Import</property>
|
||||
<property name="subtitle" translatable="yes">Bouquets and services</property>
|
||||
<property name="spacing">2</property>
|
||||
<property name="show_close_button">True</property>
|
||||
<child>
|
||||
<object class="GtkButton" id="import_button">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="tooltip_text" translatable="yes">Import</property>
|
||||
<signal name="clicked" handler="on_import" swapped="no"/>
|
||||
<child>
|
||||
<object class="GtkImage" id="import_button_image">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="stock">gtk-revert-to-saved</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkCheckButton" id="info_check_button">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">False</property>
|
||||
<property name="tooltip_text" translatable="yes">Details</property>
|
||||
<property name="draw_indicator">False</property>
|
||||
<signal name="toggled" handler="on_info_button_toggled" swapped="no"/>
|
||||
<child>
|
||||
<object class="GtkImage" id="info_check_button_image">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="stock">gtk-dialog-info</property>
|
||||
</object>
|
||||
</child>
|
||||
<accelerator key="i" signal="clicked" modifiers="GDK_CONTROL_MASK"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="pack_type">end</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkBox" id="main_box">
|
||||
@@ -161,8 +129,8 @@ Author: Dmitriy Yefremov
|
||||
<object class="GtkLabel">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_top">2</property>
|
||||
<property name="margin_bottom">2</property>
|
||||
<property name="margin_top">5</property>
|
||||
<property name="margin_bottom">5</property>
|
||||
<property name="label" translatable="yes">Bouquets</property>
|
||||
</object>
|
||||
<packing>
|
||||
@@ -260,8 +228,8 @@ Author: Dmitriy Yefremov
|
||||
<object class="GtkLabel">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_top">2</property>
|
||||
<property name="margin_bottom">2</property>
|
||||
<property name="margin_top">5</property>
|
||||
<property name="margin_bottom">5</property>
|
||||
<property name="label" translatable="yes">Bouquet details</property>
|
||||
</object>
|
||||
<packing>
|
||||
@@ -337,6 +305,62 @@ Author: Dmitriy Yefremov
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButtonBox" id="actions_box">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="valign">center</property>
|
||||
<property name="margin_left">15</property>
|
||||
<property name="margin_right">15</property>
|
||||
<property name="margin_top">5</property>
|
||||
<property name="margin_bottom">5</property>
|
||||
<property name="layout_style">start</property>
|
||||
<child>
|
||||
<object class="GtkButton" id="import_button">
|
||||
<property name="label" translatable="yes">Import</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="tooltip_text" translatable="yes">Bouquets and services</property>
|
||||
<property name="valign">center</property>
|
||||
<property name="image">import_image</property>
|
||||
<property name="always_show_image">True</property>
|
||||
<signal name="clicked" handler="on_import" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkCheckButton" id="info_check_button">
|
||||
<property name="label" translatable="yes">Details</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">False</property>
|
||||
<property name="tooltip_text" translatable="yes">Details</property>
|
||||
<property name="valign">center</property>
|
||||
<property name="image">details_image</property>
|
||||
<property name="always_show_image">True</property>
|
||||
<property name="draw_indicator">False</property>
|
||||
<signal name="toggled" handler="on_info_button_toggled" swapped="no"/>
|
||||
<accelerator key="i" signal="clicked"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
<property name="secondary">True</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkInfoBar" id="info_bar">
|
||||
<property name="can_focus">False</property>
|
||||
@@ -382,7 +406,7 @@ Author: Dmitriy Yefremov
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
|
||||
@@ -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, get_builder
|
||||
from app.ui.dialogs import show_dialog, DialogType, get_chooser_dialog, get_message
|
||||
from app.ui.main_helper import on_popup_menu
|
||||
from .uicommons import Gtk, UI_RESOURCES_PATH, KeyboardKey, Column
|
||||
|
||||
@@ -20,7 +20,7 @@ def import_bouquet(transient, model, path, settings, services, appender, file_pa
|
||||
|
||||
if profile is SettingsType.ENIGMA_2:
|
||||
pattern = ".{}".format(bq_type.value)
|
||||
f_pattern = "userbouquet.*{}".format(pattern)
|
||||
f_pattern = "*" + pattern if settings.is_darwin else "userbouquet.*{}".format(pattern)
|
||||
elif profile is SettingsType.NEUTRINO_MP:
|
||||
pattern = "webtv.xml" if bq_type is BqType.WEBTV else "bouquets.xml"
|
||||
f_pattern = "bouquets.xml"
|
||||
@@ -33,11 +33,15 @@ def import_bouquet(transient, model, path, settings, services, appender, file_pa
|
||||
if file_path == Gtk.ResponseType.CANCEL:
|
||||
return
|
||||
|
||||
if not str(file_path).endswith(pattern):
|
||||
if not file_path.endswith(pattern):
|
||||
show_dialog(DialogType.ERROR, transient, text="No bouquet file is selected!")
|
||||
return
|
||||
|
||||
if profile is SettingsType.ENIGMA_2:
|
||||
if settings.is_darwin and file_path.rfind("userbouquet.") < 0:
|
||||
show_dialog(DialogType.ERROR, transient, text="No bouquet file is selected!")
|
||||
return
|
||||
|
||||
bq = get_enigma2_bouquet(file_path)
|
||||
imported = list(filter(lambda x: x.data in services or x.type is BqServiceType.IPTV, bq.services))
|
||||
|
||||
@@ -80,7 +84,10 @@ class ImportDialog:
|
||||
"on_resize": self.on_resize,
|
||||
"on_key_press": self.on_key_press}
|
||||
|
||||
builder = get_builder(UI_RESOURCES_PATH + "import_dialog.glade", handlers)
|
||||
builder = Gtk.Builder()
|
||||
builder.set_translation_domain("demon-editor")
|
||||
builder.add_from_file(UI_RESOURCES_PATH + "import_dialog.glade")
|
||||
builder.connect_signals(handlers)
|
||||
|
||||
self._bq_services = {}
|
||||
self._services = {}
|
||||
@@ -121,7 +128,7 @@ 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
|
||||
|
||||
|
||||
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:
|
||||
|
||||
1336
app/ui/iptv.glade
1336
app/ui/iptv.glade
File diff suppressed because it is too large
Load Diff
@@ -1,31 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# The MIT License (MIT)
|
||||
#
|
||||
# Copyright (c) 2018-2021 Dmitriy Yefremov
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
# THE SOFTWARE.
|
||||
#
|
||||
# Author: Dmitriy Yefremov
|
||||
#
|
||||
|
||||
|
||||
import concurrent.futures
|
||||
import os
|
||||
import re
|
||||
@@ -43,9 +15,10 @@ 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_message, get_builder
|
||||
from app.ui.dialogs import Action, show_dialog, DialogType, get_dialogs_string, get_message
|
||||
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, UI_RESOURCES_PATH, IPTV_ICON, Column, KeyboardKey, get_yt_icon)
|
||||
from app.ui.uicommons import (Gtk, Gdk, TEXT_DOMAIN, UI_RESOURCES_PATH, IPTV_ICON, Column, IS_GNOME_SESSION,
|
||||
KeyboardKey, get_yt_icon)
|
||||
|
||||
_DIGIT_ENTRY_NAME = "digit-entry"
|
||||
_ENIGMA2_REFERENCE = "{}:0:{}:{:X}:{:X}:{:X}:{:X}:0:0:0"
|
||||
@@ -94,8 +67,11 @@ class IptvDialog:
|
||||
self._yt_links = None
|
||||
self._yt_dl = None
|
||||
|
||||
builder = get_builder(_UI_PATH, handlers, use_str=True,
|
||||
objects=("iptv_dialog", "stream_type_liststore", "yt_quality_liststore"))
|
||||
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)
|
||||
|
||||
self._dialog = builder.get_object("iptv_dialog")
|
||||
self._dialog.set_transient_for(transient)
|
||||
@@ -347,8 +323,10 @@ class SearchUnavailableDialog:
|
||||
def __init__(self, transient, model, fav_bouquet, iptv_rows, s_type):
|
||||
handlers = {"on_response": self.on_response}
|
||||
|
||||
builder = get_builder(UI_RESOURCES_PATH + "iptv.glade", handlers,
|
||||
objects=("search_unavailable_streams_dialog",))
|
||||
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)
|
||||
|
||||
self._dialog = builder.get_object("search_unavailable_streams_dialog")
|
||||
self._dialog.set_transient_for(transient)
|
||||
@@ -443,8 +421,11 @@ class IptvListDialog:
|
||||
|
||||
self._s_type = s_type
|
||||
|
||||
builder = get_builder(_UI_PATH, handlers, use_str=True,
|
||||
objects=("iptv_list_configuration_dialog", "stream_type_liststore"))
|
||||
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)
|
||||
|
||||
self._dialog = builder.get_object("iptv_list_configuration_dialog")
|
||||
self._dialog.set_transient_for(transient)
|
||||
@@ -465,8 +446,6 @@ class IptvListDialog:
|
||||
self._list_nid_entry = builder.get_object("list_nid_entry")
|
||||
self._list_namespace_entry = builder.get_object("list_namespace_entry")
|
||||
self._apply_button = builder.get_object("list_configuration_apply_button")
|
||||
self._cancel_button = builder.get_object("cancel_config_list_button")
|
||||
self._ok_button = builder.get_object("list_configuration_ok_button")
|
||||
# Style
|
||||
style_provider = Gtk.CssProvider()
|
||||
style_provider.load_from_path(UI_RESOURCES_PATH + "style.css")
|
||||
@@ -628,8 +607,6 @@ class M3uImportDialog(IptvListDialog):
|
||||
self._dialog.set_title(get_message("Playlist import"))
|
||||
self._dialog.connect("delete-event", self.on_close)
|
||||
self._apply_button.set_label(get_message("Import"))
|
||||
self._ok_button.bind_property("visible", self._apply_button, "visible", 4)
|
||||
self._ok_button.bind_property("visible", self._cancel_button, "visible", 4)
|
||||
# Progress
|
||||
self._progress_bar = Gtk.ProgressBar(visible=False, valign="center")
|
||||
self._spinner = Gtk.Spinner(active=False)
|
||||
@@ -711,7 +688,6 @@ class M3uImportDialog(IptvListDialog):
|
||||
|
||||
self.download_picons(picons)
|
||||
else:
|
||||
GLib.idle_add(self._ok_button.set_visible, True)
|
||||
GLib.idle_add(self._info_bar.set_visible, True, priority=GLib.PRIORITY_LOW)
|
||||
|
||||
self._app.append_imported_services(services)
|
||||
@@ -795,9 +771,7 @@ class M3uImportDialog(IptvListDialog):
|
||||
if s:
|
||||
model.set_value(r.iter, Column.FAV_PICON, picons.get(s.picon_id, None))
|
||||
yield True
|
||||
|
||||
self._info_bar.set_visible(True)
|
||||
self._ok_button.set_visible(True)
|
||||
yield True
|
||||
|
||||
def on_response(self, dialog, response):
|
||||
@@ -839,9 +813,13 @@ class YtListImportDialog:
|
||||
self._settings = settings
|
||||
self._yt = None
|
||||
|
||||
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"))
|
||||
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)
|
||||
|
||||
self._dialog = builder.get_object("yt_import_dialog_window")
|
||||
self._dialog.set_transient_for(transient)
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,31 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# The MIT License (MIT)
|
||||
#
|
||||
# Copyright (c) 2018-2021 Dmitriy Yefremov
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
# THE SOFTWARE.
|
||||
#
|
||||
# Author: Dmitriy Yefremov
|
||||
#
|
||||
|
||||
|
||||
import os
|
||||
import sys
|
||||
from contextlib import suppress
|
||||
@@ -39,7 +11,7 @@ from gi.repository import GLib, Gio
|
||||
from app.commons import run_idle, log, run_task, run_with_delay, init_logger
|
||||
from app.connections import (HttpAPI, download_data, DownloadType, upload_data, test_http, TestException,
|
||||
HttpApiException, STC_XML_FILE)
|
||||
from app.eparser import get_blacklist, write_blacklist, write_bouquet
|
||||
from app.eparser import get_blacklist, write_blacklist
|
||||
from app.eparser import get_services, get_bouquets, write_bouquets, write_services, Bouquets, Bouquet, Service
|
||||
from app.eparser.ecommons import CAS, Flag, BouquetService
|
||||
from app.eparser.enigma.bouquets import BqServiceType
|
||||
@@ -50,7 +22,7 @@ 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, get_builder
|
||||
from .dialogs import show_dialog, DialogType, get_chooser_dialog, WaitDialog, get_message
|
||||
from .download_dialog import DownloadDialog
|
||||
from .imports import ImportDialog, import_bouquet
|
||||
from .iptv import IptvDialog, SearchUnavailableDialog, IptvListConfigurationDialog, YtListImportDialog, M3uImportDialog
|
||||
@@ -90,119 +62,117 @@ class Application(Gtk.Application):
|
||||
"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", "new_header_button",
|
||||
"bouquet_import_popup_item")
|
||||
"bouquets_copy_popup_item", "bouquets_paste_popup_item", "bouquet_import_popup_item",
|
||||
"add_bouquet_tool_button")
|
||||
|
||||
_COMMONS_ELEMENTS = ("bouquets_remove_popup_item", "fav_remove_popup_item", "import_bq_menu_button")
|
||||
_COMMONS_ELEMENTS = ("bouquets_remove_popup_item", "fav_remove_popup_item")
|
||||
|
||||
_FAV_ENIGMA_ELEMENTS = ("fav_insert_marker_popup_item", "fav_epg_configuration_popup_item",
|
||||
"epg_configuration_header_button")
|
||||
_FAV_ENIGMA_ELEMENTS = ("fav_insert_marker_popup_item", "fav_epg_configuration_popup_item")
|
||||
|
||||
_FAV_IPTV_ELEMENTS = ("fav_iptv_popup_item", "import_m3u_header_button", "export_to_m3u_header_button",
|
||||
"epg_configuration_header_button")
|
||||
_FAV_IPTV_ELEMENTS = ("fav_iptv_popup_item",)
|
||||
|
||||
_LOCK_HIDE_ELEMENTS = ("locked_tool_button", "hide_tool_button")
|
||||
_LOCK_HIDE_ELEMENTS = ()
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
super().__init__(flags=Gio.ApplicationFlags.HANDLES_COMMAND_LINE, **kwargs)
|
||||
# Adding command line options
|
||||
self.add_main_option("log", ord("l"), GLib.OptionFlags.NONE, GLib.OptionArg.NONE, "", None)
|
||||
self.add_main_option("record", ord("r"), GLib.OptionFlags.NONE, GLib.OptionArg.NONE, "", None)
|
||||
self.add_main_option("telnet", ord("t"), GLib.OptionFlags.NONE, GLib.OptionArg.STRING, "", None)
|
||||
self.add_main_option("debug", ord("d"), GLib.OptionFlags.NONE, GLib.OptionArg.STRING, "", None)
|
||||
|
||||
handlers = {"on_close_app": self.on_close_app,
|
||||
"on_about_app": self.on_about_app,
|
||||
"on_settings": self.on_settings,
|
||||
"on_profile_changed": self.on_profile_changed,
|
||||
"on_new_configuration": self.on_new_configuration,
|
||||
"on_tree_view_key_press": self.on_tree_view_key_press,
|
||||
"on_tree_view_key_release": self.on_tree_view_key_release,
|
||||
"on_bouquets_selection": self.on_bouquets_selection,
|
||||
"on_satellite_editor_show": self.on_satellite_editor_show,
|
||||
"on_fav_selection": self.on_fav_selection,
|
||||
"on_alt_selection": self.on_alt_selection,
|
||||
"on_services_selection": self.on_services_selection,
|
||||
"on_fav_cut": self.on_fav_cut,
|
||||
"on_bouquets_cut": self.on_bouquets_cut,
|
||||
"on_services_copy": self.on_services_copy,
|
||||
"on_fav_copy": self.on_fav_copy,
|
||||
"on_bouquets_copy": self.on_bouquets_copy,
|
||||
"on_fav_paste": self.on_fav_paste,
|
||||
"on_bouquets_paste": self.on_bouquets_paste,
|
||||
"on_rename_for_bouquet": self.on_rename_for_bouquet,
|
||||
"on_set_default_name_for_bouquet": self.on_set_default_name_for_bouquet,
|
||||
"on_services_add_new": self.on_services_add_new,
|
||||
"on_delete": self.on_delete,
|
||||
"on_edit": self.on_edit,
|
||||
"on_to_fav_copy": self.on_to_fav_copy,
|
||||
"on_to_fav_end_copy": self.on_to_fav_end_copy,
|
||||
"on_fav_sort": self.on_fav_sort,
|
||||
"on_fav_view_query_tooltip": self.on_fav_view_query_tooltip,
|
||||
"on_services_view_query_tooltip": self.on_services_view_query_tooltip,
|
||||
"on_view_drag_begin": self.on_view_drag_begin,
|
||||
"on_view_drag_end": self.on_view_drag_end,
|
||||
"on_view_drag_data_get": self.on_view_drag_data_get,
|
||||
"on_services_view_drag_drop": self.on_services_view_drag_drop,
|
||||
"on_services_view_drag_data_received": self.on_services_view_drag_data_received,
|
||||
"on_view_drag_data_received": self.on_view_drag_data_received,
|
||||
"on_bq_view_drag_data_received": self.on_bq_view_drag_data_received,
|
||||
"on_alt_view_drag_data_received": self.on_alt_view_drag_data_received,
|
||||
"on_view_press": self.on_view_press,
|
||||
"on_view_release": self.on_view_release,
|
||||
"on_view_popup_menu": self.on_view_popup_menu,
|
||||
"on_view_focus": self.on_view_focus,
|
||||
"on_model_changed": self.on_model_changed,
|
||||
"on_import_yt_list": self.on_import_yt_list,
|
||||
"on_import_m3u": self.on_import_m3u,
|
||||
"on_bouquet_export": self.on_bouquet_export,
|
||||
"on_export_to_m3u": self.on_export_to_m3u,
|
||||
"on_import_bouquet": self.on_import_bouquet,
|
||||
"on_import_bouquets": self.on_import_bouquets,
|
||||
"on_backup_tool_show": self.on_backup_tool_show,
|
||||
"on_insert_marker": self.on_insert_marker,
|
||||
"on_insert_space": self.on_insert_space,
|
||||
"on_fav_press": self.on_fav_press,
|
||||
"on_locate_in_services": self.on_locate_in_services,
|
||||
"on_mark_duplicates": self.on_mark_duplicates,
|
||||
"on_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,
|
||||
"on_remove_unused_picons": self.on_remove_unused_picons,
|
||||
"on_search_down": self.on_search_down,
|
||||
"on_search_up": self.on_search_up,
|
||||
"on_search": self.on_search,
|
||||
"on_iptv": self.on_iptv,
|
||||
"on_epg_list_configuration": self.on_epg_list_configuration,
|
||||
"on_iptv_list_configuration": self.on_iptv_list_configuration,
|
||||
"on_play_stream": self.on_play_stream,
|
||||
"on_watch": self.on_watch,
|
||||
"on_player_play": self.on_player_play,
|
||||
"on_player_stop": self.on_player_stop,
|
||||
"on_player_previous": self.on_player_previous,
|
||||
"on_player_next": self.on_player_next,
|
||||
"on_player_rewind": self.on_player_rewind,
|
||||
"on_player_close": self.on_player_close,
|
||||
"on_player_press": self.on_player_press,
|
||||
"on_full_screen": self.on_full_screen,
|
||||
"on_main_window_state": self.on_main_window_state,
|
||||
"on_http_status_visible": self.on_http_status_visible,
|
||||
"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,
|
||||
"on_new_bouquet": self.on_new_bouquet,
|
||||
"on_create_bouquet_for_current_satellite": self.on_create_bouquet_for_current_satellite,
|
||||
"on_create_bouquet_for_each_satellite": self.on_create_bouquet_for_each_satellite,
|
||||
"on_create_bouquet_for_current_package": self.on_create_bouquet_for_current_package,
|
||||
"on_create_bouquet_for_each_package": self.on_create_bouquet_for_each_package,
|
||||
"on_create_bouquet_for_current_type": self.on_create_bouquet_for_current_type,
|
||||
"on_create_bouquet_for_each_type": self.on_create_bouquet_for_each_type,
|
||||
"on_add_alternatives": self.on_add_alternatives}
|
||||
self._handlers = {"on_close_app": self.on_close_app,
|
||||
"on_about_app": self.on_about_app,
|
||||
"on_settings": self.on_settings,
|
||||
"on_profile_changed": self.on_profile_changed,
|
||||
"on_download": self.on_download,
|
||||
"on_data_open": self.on_data_open,
|
||||
"on_new_configuration": self.on_new_configuration,
|
||||
"on_tree_view_key_press": self.on_tree_view_key_press,
|
||||
"on_tree_view_key_release": self.on_tree_view_key_release,
|
||||
"on_bouquets_selection": self.on_bouquets_selection,
|
||||
"on_satellite_editor_show": self.on_satellite_editor_show,
|
||||
"on_fav_selection": self.on_fav_selection,
|
||||
"on_alt_selection": self.on_alt_selection,
|
||||
"on_services_selection": self.on_services_selection,
|
||||
"on_fav_cut": self.on_fav_cut,
|
||||
"on_bouquets_cut": self.on_bouquets_cut,
|
||||
"on_services_copy": self.on_services_copy,
|
||||
"on_fav_copy": self.on_fav_copy,
|
||||
"on_bouquets_copy": self.on_bouquets_copy,
|
||||
"on_fav_paste": self.on_fav_paste,
|
||||
"on_bouquets_paste": self.on_bouquets_paste,
|
||||
"on_rename_for_bouquet": self.on_rename_for_bouquet,
|
||||
"on_set_default_name_for_bouquet": self.on_set_default_name_for_bouquet,
|
||||
"on_services_add_new": self.on_services_add_new,
|
||||
"on_edit": self.on_edit,
|
||||
"on_delete": self.on_delete,
|
||||
"on_to_fav_copy": self.on_to_fav_copy,
|
||||
"on_to_fav_end_copy": self.on_to_fav_end_copy,
|
||||
"on_fav_sort": self.on_fav_sort,
|
||||
"on_fav_view_query_tooltip": self.on_fav_view_query_tooltip,
|
||||
"on_services_view_query_tooltip": self.on_services_view_query_tooltip,
|
||||
"on_view_drag_begin": self.on_view_drag_begin,
|
||||
"on_view_drag_end": self.on_view_drag_end,
|
||||
"on_view_drag_data_get": self.on_view_drag_data_get,
|
||||
"on_services_view_drag_drop": self.on_services_view_drag_drop,
|
||||
"on_services_view_drag_data_received": self.on_services_view_drag_data_received,
|
||||
"on_view_drag_data_received": self.on_view_drag_data_received,
|
||||
"on_bq_view_drag_data_received": self.on_bq_view_drag_data_received,
|
||||
"on_alt_view_drag_data_received": self.on_alt_view_drag_data_received,
|
||||
"on_view_press": self.on_view_press,
|
||||
"on_view_release": self.on_view_release,
|
||||
"on_view_popup_menu": self.on_view_popup_menu,
|
||||
"on_view_focus": self.on_view_focus,
|
||||
"on_model_changed": self.on_model_changed,
|
||||
"on_import_yt_list": self.on_import_yt_list,
|
||||
"on_import_m3u": self.on_import_m3u,
|
||||
"on_export_to_m3u": self.on_export_to_m3u,
|
||||
"on_import_bouquet": self.on_import_bouquet,
|
||||
"on_import_bouquets": self.on_import_bouquets,
|
||||
"on_backup_tool_show": self.on_backup_tool_show,
|
||||
"on_insert_marker": self.on_insert_marker,
|
||||
"on_insert_space": self.on_insert_space,
|
||||
"on_fav_press": self.on_fav_press,
|
||||
"on_locate_in_services": self.on_locate_in_services,
|
||||
"on_mark_duplicates": self.on_mark_duplicates,
|
||||
"on_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,
|
||||
"on_remove_unused_picons": self.on_remove_unused_picons,
|
||||
"on_search_down": self.on_search_down,
|
||||
"on_search_up": self.on_search_up,
|
||||
"on_search": self.on_search,
|
||||
"on_iptv": self.on_iptv,
|
||||
"on_epg_list_configuration": self.on_epg_list_configuration,
|
||||
"on_iptv_list_configuration": self.on_iptv_list_configuration,
|
||||
"on_play_stream": self.on_play_stream,
|
||||
"on_watch": self.on_watch,
|
||||
"on_player_play": self.on_player_play,
|
||||
"on_player_stop": self.on_player_stop,
|
||||
"on_player_previous": self.on_player_previous,
|
||||
"on_player_next": self.on_player_next,
|
||||
"on_player_rewind": self.on_player_rewind,
|
||||
"on_player_close": self.on_player_close,
|
||||
"on_player_press": self.on_player_press,
|
||||
"on_full_screen": self.on_full_screen,
|
||||
"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,
|
||||
"on_new_bouquet": self.on_new_bouquet,
|
||||
"on_create_bouquet_for_current_satellite": self.on_create_bouquet_for_current_satellite,
|
||||
"on_create_bouquet_for_each_satellite": self.on_create_bouquet_for_each_satellite,
|
||||
"on_create_bouquet_for_current_package": self.on_create_bouquet_for_current_package,
|
||||
"on_create_bouquet_for_each_package": self.on_create_bouquet_for_each_package,
|
||||
"on_create_bouquet_for_current_type": self.on_create_bouquet_for_current_type,
|
||||
"on_create_bouquet_for_each_type": self.on_create_bouquet_for_each_type,
|
||||
"on_add_alternatives": self.on_add_alternatives}
|
||||
|
||||
self._settings = Settings.get_instance()
|
||||
self._s_type = self._settings.setting_type
|
||||
@@ -248,7 +218,9 @@ class Application(Gtk.Application):
|
||||
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 = get_builder(UI_RESOURCES_PATH + "main_window.glade", handlers)
|
||||
builder = Gtk.Builder()
|
||||
builder.add_from_file(UI_RESOURCES_PATH + "main_window.glade")
|
||||
builder.connect_signals(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
|
||||
@@ -263,21 +235,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")
|
||||
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)
|
||||
# Header bar elements.
|
||||
main_header_box = builder.get_object("main_header_box")
|
||||
main_popover_menu_box = builder.get_object("main_popover_menu_box")
|
||||
self._right_header_box = builder.get_object("right_header_box")
|
||||
self._left_header_box = builder.get_object("left_header_box")
|
||||
# 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", self._right_header_box, "visible", 4)
|
||||
self._app_info_box.bind_property("visible", self._left_header_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", 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")
|
||||
@@ -294,10 +273,6 @@ class Application(Gtk.Application):
|
||||
self._tv_count_label = builder.get_object("tv_count_label")
|
||||
self._radio_count_label = builder.get_object("radio_count_label")
|
||||
self._data_count_label = builder.get_object("data_count_label")
|
||||
self._services_load_spinner = builder.get_object("services_load_spinner")
|
||||
self._save_header_button = builder.get_object("save_header_button")
|
||||
self._app_info_box.bind_property("visible", self._save_header_button, "visible", 4)
|
||||
self._save_header_button.bind_property("visible", builder.get_object("save_menu_button"), "visible")
|
||||
self._signal_level_bar.bind_property("visible", builder.get_object("play_current_service_button"), "visible")
|
||||
self._signal_level_bar.bind_property("visible", builder.get_object("record_button"), "visible")
|
||||
self._receiver_info_box.bind_property("visible", self._http_status_image, "visible", 4)
|
||||
@@ -315,12 +290,8 @@ 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", builder.get_object("main_box"), "visible", 4)
|
||||
self._ftp_revealer.bind_property("visible", main_header_box, "visible", 4)
|
||||
self._ftp_revealer.bind_property("visible", main_popover_menu_box, "visible", 4)
|
||||
close_ftp_menu_button = builder.get_object("close_ftp_menu_button")
|
||||
self._ftp_revealer.bind_property("visible", close_ftp_menu_button, "visible")
|
||||
close_ftp_menu_button.connect("clicked", lambda b: self._ftp_button.set_active(False))
|
||||
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)
|
||||
@@ -347,21 +318,14 @@ class Application(Gtk.Application):
|
||||
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_play_button = builder.get_object("player_play_button")
|
||||
self._player_box.bind_property("visible", self._top_box, "visible", 4)
|
||||
self._player_box.bind_property("visible", self._services_main_box, "visible", 4)
|
||||
self._fav_bouquets_paned = builder.get_object("fav_bouquets_paned")
|
||||
self._player_box.bind_property("visible", builder.get_object("close_player_menu_button"), "visible")
|
||||
self._player_box.bind_property("visible", self._left_header_box, "visible", 4)
|
||||
self._player_box.bind_property("visible", self._right_header_box, "visible", 4)
|
||||
self._player_box.bind_property("visible", main_popover_menu_box, "visible", 4)
|
||||
self._player_box.bind_property("visible", main_header_box, "visible", 4)
|
||||
self._player_box.bind_property("visible", builder.get_object("left_header_separator"), "visible", 4)
|
||||
self._player_box.bind_property("visible", builder.get_object("tools_button_box"), "visible", 4)
|
||||
self._player_box.bind_property("visible", self._profile_combo_box, "visible", 4)
|
||||
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._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")
|
||||
self._fav_view.bind_property("sensitive", self._bouquets_view, "sensitive")
|
||||
# Record
|
||||
self._record_image = builder.get_object("record_button_image")
|
||||
# Search
|
||||
@@ -379,6 +343,62 @@ 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. """
|
||||
control_box = builder.get_object("control_button_box")
|
||||
control_box.set_child_packing(self._control_button, False, True, 0, Gtk.PackType.END)
|
||||
|
||||
extra_box = builder.get_object("toolbar_extra_box")
|
||||
extra_box.set_child_packing(self._toolbar_extra_tools_box, False, True, 0, Gtk.PackType.END)
|
||||
self._toolbar_search_box.reorder_child(builder.get_object("search_tool_button"), 0)
|
||||
|
||||
self._top_box.set_child_packing(extra_box, False, True, 0, Gtk.PackType.START)
|
||||
self._top_box.set_child_packing(self._toolbar_search_box, False, True, 0, Gtk.PackType.END)
|
||||
self._top_box.reorder_child(extra_box, 0)
|
||||
|
||||
center_box = builder.get_object("center_box")
|
||||
center_box.reorder_child(self._control_revealer, 0)
|
||||
center_box.reorder_child(self._ftp_revealer, 1)
|
||||
center_box.reorder_child(self._main_box, 2)
|
||||
center_box.set_child_packing(self._control_revealer, False, True, 0, Gtk.PackType.START)
|
||||
|
||||
builder.get_object("fs_box").set_child_packing(self._filter_box, False, True, 0, Gtk.PackType.END)
|
||||
top_toolbar = builder.get_object("top_toolbar")
|
||||
top_toolbar.set_child_packing(self._toolbar_search_box, False, True, 0, Gtk.PackType.END)
|
||||
|
||||
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)
|
||||
|
||||
pack = Gtk.PackType.END if self._settings.bq_details_first else Gtk.PackType.START
|
||||
self._toolbar_extra_tools_box.set_child_packing(self._add_bouquet_button, False, True, 0, pack)
|
||||
|
||||
def do_startup(self):
|
||||
Gtk.Application.do_startup(self)
|
||||
@@ -386,6 +406,16 @@ class Application(Gtk.Application):
|
||||
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_appearance()
|
||||
self.filter_set_default()
|
||||
@@ -400,34 +430,73 @@ class Application(Gtk.Application):
|
||||
gen = self.init_http_api()
|
||||
GLib.idle_add(lambda: next(gen, False), priority=GLib.PRIORITY_LOW)
|
||||
|
||||
def init_telnet(self, builder):
|
||||
t_section = builder.get_object("telnet_section")
|
||||
t_section.append_item(Gio.MenuItem.new("Telnet", "app.on_telnet_client_show"))
|
||||
ac = Gio.SimpleAction.new("on_telnet_client_show", None)
|
||||
ac.connect("activate", self.on_telnet_client_show)
|
||||
self.add_action(ac)
|
||||
self.set_accels_for_action("app.on_telnet_client_show", ["<primary>t"])
|
||||
self._telnet_tool_button.set_visible(True)
|
||||
|
||||
def init_keys(self):
|
||||
self.set_action("on_close_app", self.on_close_app)
|
||||
self.set_action("on_data_save", self.on_data_save)
|
||||
self.set_action("on_download", self.on_download)
|
||||
self.set_action("on_data_open", self.on_data_open)
|
||||
self.set_action("on_archive_open", self.on_archive_open)
|
||||
self.set_action("on_import_from_web", self.on_import_from_web)
|
||||
main_handlers = ("on_new_configuration", "on_data_open", "on_download", "on_settings",
|
||||
"on_close_app", "on_import_bouquet", "on_import_bouquets", "on_satellite_editor_show",
|
||||
"on_picons_manager_show", "on_backup_tool_show", "on_about_app")
|
||||
iptv_handlers = ("on_iptv", "on_import_yt_list", "on_import_m3u", "on_export_to_m3u",
|
||||
"on_epg_list_configuration", "on_iptv_list_configuration", "on_remove_all_unavailable")
|
||||
|
||||
list(map(lambda x: self.set_action(x, self._handlers.get(x)), main_handlers))
|
||||
# Import
|
||||
action = self.set_action("on_import_bouquet", self._handlers.get("on_import_bouquet"), False)
|
||||
self._tool_elements.get("bouquet_import_popup_item").bind_property("sensitive", action, "enabled")
|
||||
# IPTV
|
||||
iptv_elem = self._tool_elements.get("fav_iptv_popup_item")
|
||||
for h in iptv_handlers:
|
||||
action = self.set_action(h, self._handlers.get(h), False)
|
||||
iptv_elem.bind_property("sensitive", action, "enabled")
|
||||
# Search, Filter
|
||||
search_action = Gio.SimpleAction.new_stateful("search", None, GLib.Variant.new_boolean(False))
|
||||
search_action.connect("change-state", self.on_search_toggled)
|
||||
search_action.set_enabled(False)
|
||||
self._app_info_box.bind_property("visible", search_action, "enabled", 4)
|
||||
self._main_window.add_action(search_action) # For "win.*" actions!
|
||||
filter_action = Gio.SimpleAction.new_stateful("filter", None, GLib.Variant.new_boolean(False))
|
||||
filter_action.connect("change-state", self.on_filter_toggled)
|
||||
filter_action.set_enabled(False)
|
||||
self._app_info_box.bind_property("visible", filter_action, "enabled", 4)
|
||||
self._main_window.add_action(filter_action)
|
||||
# Lock, Hide
|
||||
self.set_action("on_hide", self.on_hide)
|
||||
self.set_action("on_locked", self.on_locked)
|
||||
self._app_info_box.bind_property("visible", self.set_action("on_hide", self.on_hide, False), "enabled", 4)
|
||||
self._app_info_box.bind_property("visible", self.set_action("on_locked", self.on_locked, False), "enabled", 4)
|
||||
# Open and download/upload data
|
||||
self.set_action("open_data", lambda a, v: self.open_data())
|
||||
self.set_action("on_download_data", self.on_download_data)
|
||||
self.set_action("upload_all", lambda a, v: self.on_upload_data(DownloadType.ALL))
|
||||
self.set_action("upload_bouquets", lambda a, v: self.on_upload_data(DownloadType.BOUQUETS))
|
||||
self.set_action("on_archive_open", self.on_archive_open)
|
||||
self.set_action("on_import_from_web", self.on_import_from_web)
|
||||
# Edit
|
||||
self.set_action("on_edit", self.on_edit)
|
||||
# Save
|
||||
self._app_info_box.bind_property("visible", self.set_action("on_data_save", self.on_data_save, False),
|
||||
"enabled", 4)
|
||||
# Control
|
||||
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)
|
||||
|
||||
def set_action(self, name, fun, enabled=True):
|
||||
ac = Gio.SimpleAction.new(name, None)
|
||||
@@ -482,6 +551,18 @@ class Application(Gtk.Application):
|
||||
log("Starting record of current stream...")
|
||||
log("Not implemented yet!")
|
||||
|
||||
if "telnet" in options:
|
||||
t_op = options.get("telnet", "off")
|
||||
if t_op == "on":
|
||||
self._settings.add("telnet", True)
|
||||
elif t_op == "off":
|
||||
self._settings.add("telnet", False)
|
||||
else:
|
||||
log("No valid [on, off] arguments for -t found!")
|
||||
return 1
|
||||
log("Telnet support is {}. Restart the program to apply the settings!".format(t_op))
|
||||
self._settings.save()
|
||||
|
||||
if "debug" in options:
|
||||
d_op = options.get("debug", "off")
|
||||
if d_op == "on":
|
||||
@@ -526,20 +607,24 @@ class Application(Gtk.Application):
|
||||
|
||||
self._services_view.drag_source_set_target_list(None)
|
||||
self._services_view.drag_source_add_text_targets()
|
||||
self._services_view.drag_dest_add_text_targets()
|
||||
self._services_view.drag_dest_add_uri_targets()
|
||||
|
||||
self._bouquets_view.drag_dest_set_target_list(None)
|
||||
self._bouquets_view.drag_source_set_target_list(None)
|
||||
self._bouquets_view.drag_dest_add_text_targets()
|
||||
self._bouquets_view.drag_source_add_text_targets()
|
||||
self._bouquets_view.drag_dest_add_uri_targets()
|
||||
|
||||
self._alt_view.drag_source_set_target_list(None)
|
||||
self._alt_view.drag_source_add_text_targets()
|
||||
self._alt_view.drag_dest_add_text_targets()
|
||||
|
||||
self._app_info_box.drag_dest_set(Gtk.DestDefaults.ALL, [], Gdk.DragAction.COPY)
|
||||
self._app_info_box.drag_dest_add_text_targets()
|
||||
if self._settings.is_darwin:
|
||||
self._app_info_box.drag_dest_add_uri_targets()
|
||||
else:
|
||||
self._app_info_box.drag_dest_add_text_targets()
|
||||
self._services_view.drag_dest_add_text_targets()
|
||||
# For multiple selection.
|
||||
self._services_view.get_selection().set_select_function(lambda *args: self._select_enabled)
|
||||
self._fav_view.get_selection().set_select_function(lambda *args: self._select_enabled)
|
||||
@@ -608,14 +693,10 @@ 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._main_window.unfullscreen()
|
||||
if not self._main_window.is_maximized():
|
||||
self._settings.add("window_size", self._main_window.get_size())
|
||||
|
||||
if self._services_load_spinner.get_property("active"):
|
||||
msg = "{}\n\n\t{}".format(get_message("Data loading in progress!"), get_message("Are you sure?"))
|
||||
if show_dialog(DialogType.QUESTION, self._main_window, msg) == Gtk.ResponseType.CANCEL:
|
||||
return True
|
||||
|
||||
if self._recorder:
|
||||
if self._recorder.is_record():
|
||||
msg = "{}\n\n\t{}".format(get_message("Recording in progress!"), get_message("Are you sure?"))
|
||||
@@ -641,6 +722,12 @@ class Application(Gtk.Application):
|
||||
return
|
||||
move_items(key, self._fav_view if self._fav_view.is_focus() else self._bouquets_view)
|
||||
|
||||
def on_switch_fav_position(self, action, value=None):
|
||||
visible = self._left_ar_bq_button.get_visible()
|
||||
self._settings.bq_details_first = visible
|
||||
self._left_ar_bq_button.set_visible(not visible)
|
||||
self.init_bq_position()
|
||||
|
||||
# ***************** Copy - Cut - Paste *********************#
|
||||
|
||||
def on_services_copy(self, view):
|
||||
@@ -1029,6 +1116,9 @@ class Application(Gtk.Application):
|
||||
|
||||
def on_fav_view_query_tooltip(self, view, x, y, keyboard_mode, tooltip):
|
||||
""" Sets detailed info about service in the tooltip [fav view]. """
|
||||
if not self._main_window.is_active():
|
||||
return False
|
||||
|
||||
result = view.get_dest_row_at_pos(x, y)
|
||||
if not result or not self._settings.show_bq_hints:
|
||||
return False
|
||||
@@ -1037,6 +1127,9 @@ class Application(Gtk.Application):
|
||||
|
||||
def on_services_view_query_tooltip(self, view, x, y, keyboard_mode, tooltip):
|
||||
""" Sets short info about service in the tooltip [main services view]. """
|
||||
if not self._main_window.is_active():
|
||||
return False
|
||||
|
||||
result = view.get_dest_row_at_pos(x, y)
|
||||
if not result or not self._settings.show_srv_hints:
|
||||
return False
|
||||
@@ -1189,7 +1282,7 @@ class Application(Gtk.Application):
|
||||
view.stop_emission_by_name("drag_drop")
|
||||
# https://stackoverflow.com/q/7661016 [Some data was dropped, get the data!]
|
||||
targets = drag_context.list_targets()
|
||||
view.drag_get_data(drag_context, targets[-1] if targets else Gdk.atom_intern("text/plain", False), time)
|
||||
view.drag_get_data(drag_context, targets[-1] if targets else Gdk.atom_intern("text/uri-list", False), time)
|
||||
|
||||
def on_services_view_drag_data_received(self, view, drag_context, x, y, data, info, time):
|
||||
# Needs for the GtkTreeView when using models [filter, sort]
|
||||
@@ -1200,20 +1293,33 @@ class Application(Gtk.Application):
|
||||
def on_view_drag_data_received(self, view, drag_context, x, y, data, info, time):
|
||||
txt = data.get_text()
|
||||
uris = data.get_uris()
|
||||
name, model = get_model_data(view)
|
||||
|
||||
if txt:
|
||||
name, model = get_model_data(view)
|
||||
if txt.startswith("file://") and name == self.SERVICE_MODEL_NAME:
|
||||
self.on_import_data(urlparse(unquote(txt)).path.strip())
|
||||
elif name == self.FAV_MODEL_NAME:
|
||||
self.receive_selection(view=view, drop_info=view.get_dest_row_at_pos(x, y), data=txt)
|
||||
elif len(uris) == 2:
|
||||
self.picons_buffer = self.on_assign_picon(view, urlparse(unquote(uris[0])).path,
|
||||
urlparse(unquote(uris[1])).path + os.sep)
|
||||
|
||||
if uris:
|
||||
src, sep, dest = uris[0].partition("::::")
|
||||
src_path = urlparse(unquote(src)).path
|
||||
if dest:
|
||||
dest_path = urlparse(unquote(dest)).path + "/"
|
||||
self.picons_buffer = self.on_assign_picon(view, src_path, dest_path)
|
||||
elif name == self.SERVICE_MODEL_NAME:
|
||||
self.on_import_data(src_path)
|
||||
drag_context.finish(True, False, time)
|
||||
|
||||
def on_bq_view_drag_data_received(self, view, drag_context, x, y, data, info, time):
|
||||
model_name, model = get_model_data(view)
|
||||
drop_info = view.get_dest_row_at_pos(x, y)
|
||||
|
||||
uris = data.get_uris()
|
||||
if uris:
|
||||
self.on_import_bouquet(None, file_path=urlparse(unquote(uris[0])).path.strip())
|
||||
return
|
||||
|
||||
data = data.get_text()
|
||||
if not data:
|
||||
return
|
||||
@@ -1232,19 +1338,22 @@ class Application(Gtk.Application):
|
||||
top_iter = model.get_iter(path)
|
||||
parent_itr = model.iter_parent(top_iter) # parent
|
||||
to_del = []
|
||||
is_darwin = self._settings.is_darwin
|
||||
|
||||
if parent_itr:
|
||||
p_path = model.get_path(parent_itr)[0]
|
||||
for itr in itrs:
|
||||
p_itr = model.iter_parent(itr)
|
||||
if not p_itr:
|
||||
break
|
||||
if p_itr and model.get_path(p_itr)[0] == p_path:
|
||||
|
||||
if all((not is_darwin, p_itr, model.get_path(p_itr)[0] == p_path)):
|
||||
model.move_after(itr, top_iter)
|
||||
top_iter = itr
|
||||
else:
|
||||
model.insert(parent_itr, model.get_path(top_iter)[1], model[itr][:])
|
||||
to_del.append(itr)
|
||||
elif not model.iter_has_child(top_iter):
|
||||
elif not model.iter_has_child(top_iter) or is_darwin:
|
||||
for itr in itrs:
|
||||
model.append(top_iter, model[itr][:])
|
||||
to_del.append(itr)
|
||||
@@ -1348,7 +1457,7 @@ class Application(Gtk.Application):
|
||||
self.delete_selection(self._services_view, self._fav_view)
|
||||
self.on_view_focus(self._bouquets_view)
|
||||
|
||||
menu.popup(None, None, None, None, event.button, event.time)
|
||||
menu.popup_at_pointer(None)
|
||||
return True
|
||||
|
||||
def on_satellite_editor_show(self, action, value=None):
|
||||
@@ -1433,9 +1542,6 @@ class Application(Gtk.Application):
|
||||
|
||||
def open_data(self, data_path=None, callback=None):
|
||||
""" Opening data and fill views. """
|
||||
if self._ftp_button.get_active():
|
||||
return
|
||||
|
||||
if data_path and os.path.isfile(data_path):
|
||||
self.open_compressed_data(data_path)
|
||||
else:
|
||||
@@ -1462,8 +1568,13 @@ class Application(Gtk.Application):
|
||||
with zipfile.ZipFile(data_path) as zip_file:
|
||||
for zip_info in zip_file.infolist():
|
||||
if not zip_info.filename.endswith(os.sep):
|
||||
zip_info.filename = os.path.basename(zip_info.filename)
|
||||
zip_file.extract(zip_info, path=tmp_path_name)
|
||||
try:
|
||||
f_name = zip_info.filename.encode("cp437").decode("utf-8")
|
||||
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)
|
||||
elif tarfile.is_tarfile(data_path):
|
||||
with tarfile.open(data_path) as tar:
|
||||
for mb in tar.getmembers():
|
||||
@@ -1533,6 +1644,8 @@ class Application(Gtk.Application):
|
||||
else:
|
||||
self.append_blacklist(black_list)
|
||||
yield from self.append_data(bouquets, services)
|
||||
finally:
|
||||
self._wait_dialog.hide()
|
||||
self._profile_combo_box.set_sensitive(True)
|
||||
if callback:
|
||||
callback()
|
||||
@@ -1544,8 +1657,6 @@ class Application(Gtk.Application):
|
||||
if self._filter_box.get_visible():
|
||||
self.on_filter_changed()
|
||||
yield True
|
||||
finally:
|
||||
self._wait_dialog.hide()
|
||||
|
||||
def append_data(self, bouquets, services):
|
||||
if self._app_info_box.get_visible():
|
||||
@@ -1637,9 +1748,7 @@ class Application(Gtk.Application):
|
||||
# Adding channels to dict with fav_id as keys.
|
||||
self._services[srv.fav_id] = srv
|
||||
self.update_services_counts(len(self._services.values()))
|
||||
self._wait_dialog.hide()
|
||||
self._services_load_spinner.start()
|
||||
factor = self.DEL_FACTOR
|
||||
factor = self.DEL_FACTOR * 2
|
||||
|
||||
for index, srv in enumerate(services):
|
||||
tooltip, background = None, None
|
||||
@@ -1654,8 +1763,6 @@ class Application(Gtk.Application):
|
||||
self._services_model.append(s)
|
||||
if index % factor == 0:
|
||||
yield True
|
||||
|
||||
self._services_load_spinner.stop()
|
||||
yield True
|
||||
|
||||
def clear_current_data(self):
|
||||
@@ -1707,7 +1814,6 @@ class Application(Gtk.Application):
|
||||
GLib.idle_add(lambda: next(gen, False), priority=GLib.PRIORITY_LOW)
|
||||
|
||||
def save_data(self, callback=None):
|
||||
self._save_header_button.set_sensitive(False)
|
||||
profile = self._s_type
|
||||
path = self._settings.data_local_path
|
||||
backup_path = self._settings.backup_local_path
|
||||
@@ -1723,7 +1829,19 @@ class Application(Gtk.Application):
|
||||
bqs = []
|
||||
num_of_children = model.iter_n_children(itr)
|
||||
for num in range(num_of_children):
|
||||
bqs.append(self.get_bouquet(model.iter_nth_child(itr, num), model))
|
||||
bq_itr = model.iter_nth_child(itr, num)
|
||||
bq_name, locked, hidden, bq_type = model.get(bq_itr, Column.BQ_NAME, Column.BQ_LOCKED,
|
||||
Column.BQ_HIDDEN, Column.BQ_TYPE)
|
||||
bq_id = "{}:{}".format(bq_name, bq_type)
|
||||
favs = self._bouquets[bq_id]
|
||||
ex_s = self._extra_bouquets.get(bq_id, None)
|
||||
bq_s = list(filter(None, [self._services.get(f_id, None) for f_id in favs]))
|
||||
|
||||
if profile is SettingsType.ENIGMA_2:
|
||||
bq_s = self.get_enigma_bq_services(bq_s, ex_s)
|
||||
|
||||
bq = Bouquet(bq_name, bq_type, bq_s, locked, hidden, self._bq_file.get(bq_id, None))
|
||||
bqs.append(bq)
|
||||
if len(b_path) == 1:
|
||||
bouquets.append(Bouquets(*model.get(itr, Column.BQ_NAME, Column.BQ_TYPE), bqs if bqs else []))
|
||||
|
||||
@@ -1740,28 +1858,12 @@ class Application(Gtk.Application):
|
||||
if profile is SettingsType.ENIGMA_2:
|
||||
# blacklist
|
||||
write_blacklist(path, self._blacklist)
|
||||
|
||||
self._save_header_button.set_sensitive(True)
|
||||
yield True
|
||||
self._data_hash = self.get_data_hash()
|
||||
yield True
|
||||
if callback:
|
||||
callback()
|
||||
|
||||
def get_bouquet(self, itr, model):
|
||||
""" Constructs and returns Bouquet class instance. """
|
||||
bq_name, locked, hidden, bq_type = model.get(itr, Column.BQ_NAME, Column.BQ_LOCKED, Column.BQ_HIDDEN,
|
||||
Column.BQ_TYPE)
|
||||
bq_id = "{}:{}".format(bq_name, bq_type)
|
||||
favs = self._bouquets[bq_id]
|
||||
ex_s = self._extra_bouquets.get(bq_id, None)
|
||||
bq_s = list(filter(None, [self._services.get(f_id, None) for f_id in favs]))
|
||||
|
||||
if self._s_type is SettingsType.ENIGMA_2:
|
||||
bq_s = self.get_enigma_bq_services(bq_s, ex_s)
|
||||
|
||||
return Bouquet(bq_name, bq_type, bq_s, locked, hidden, self._bq_file.get(bq_id, None))
|
||||
|
||||
def get_enigma_bq_services(self, services, ext_services):
|
||||
""" Preparing a list of services for the Enigma2 bouquet. """
|
||||
s_list = []
|
||||
@@ -1896,8 +1998,7 @@ class Application(Gtk.Application):
|
||||
alt_servs = srv.transponder
|
||||
if alt_servs:
|
||||
alt_srv = self._services.get(alt_servs[0].data, None)
|
||||
if alt_srv:
|
||||
picon = self._picons.get(alt_srv.picon_id, None) if srv else None
|
||||
picon = self._picons.get(alt_srv.picon_id, None) if srv else None
|
||||
|
||||
self._fav_model.append((0 if is_marker else num, srv.coded, ex_srv_name if ex_srv_name else srv.service,
|
||||
srv.locked, srv.hide, srv_type, srv.pos, srv.fav_id,
|
||||
@@ -2063,7 +2164,7 @@ class Application(Gtk.Application):
|
||||
|
||||
def on_view_focus(self, view, focus_event=None):
|
||||
model_name, model = get_model_data(view)
|
||||
not_empty = len(model) > 0 if model else False
|
||||
not_empty = len(model) > 0 # if > 0 model has items
|
||||
is_service = model_name == self.SERVICE_MODEL_NAME
|
||||
|
||||
if model_name == self.BQ_MODEL_NAME:
|
||||
@@ -2239,7 +2340,7 @@ class Application(Gtk.Application):
|
||||
bq = self._bouquets.get(self._bq_selected)
|
||||
EpgDialog(self._main_window, self._settings, self._services, bq, self._fav_model, self._current_bq_name).show()
|
||||
|
||||
# ***************** Import ******************** #
|
||||
# ***************** Import ********************#
|
||||
|
||||
def on_import_yt_list(self, action, value=None):
|
||||
""" Import playlist from YouTube """
|
||||
@@ -2271,6 +2372,30 @@ class Application(Gtk.Application):
|
||||
gen = self.update_bouquet_services(self._fav_model, None, self._bq_selected)
|
||||
GLib.idle_add(lambda: next(gen, False), priority=GLib.PRIORITY_LOW)
|
||||
|
||||
@run_idle
|
||||
def on_export_to_m3u(self, action, value=None):
|
||||
i_types = (BqServiceType.IPTV.value, BqServiceType.MARKER.value)
|
||||
bq_services = [BouquetService(r[Column.FAV_SERVICE],
|
||||
BqServiceType(r[Column.FAV_TYPE]),
|
||||
r[Column.FAV_ID],
|
||||
r[Column.FAV_NUM]) for r in self._fav_model if r[Column.FAV_TYPE] in i_types]
|
||||
|
||||
if not any(s.type is BqServiceType.IPTV for s in bq_services):
|
||||
self.show_error_dialog("This list does not contains IPTV streams!")
|
||||
return
|
||||
|
||||
response = show_dialog(DialogType.CHOOSER, self._main_window, settings=self._settings)
|
||||
if response in (Gtk.ResponseType.CANCEL, Gtk.ResponseType.DELETE_EVENT):
|
||||
return
|
||||
|
||||
try:
|
||||
bq = Bouquet(self._current_bq_name, None, bq_services, None, None)
|
||||
export_to_m3u(response, bq, self._s_type)
|
||||
except Exception as e:
|
||||
self.show_error_dialog(str(e))
|
||||
else:
|
||||
show_dialog(DialogType.INFO, self._main_window, "Done!")
|
||||
|
||||
def on_import_data(self, path):
|
||||
msg = "Combine with the current data?"
|
||||
if len(self._services_model) > 0 and show_dialog(DialogType.QUESTION, self._main_window,
|
||||
@@ -2314,7 +2439,6 @@ class Application(Gtk.Application):
|
||||
|
||||
dialog = ImportDialog(self._main_window, path, self._settings, self._services.keys(), append)
|
||||
dialog.import_data() if force else dialog.show()
|
||||
self.update_picons()
|
||||
|
||||
def append_imported_data(self, bouquets, services, callback=None):
|
||||
try:
|
||||
@@ -2352,68 +2476,19 @@ class Application(Gtk.Application):
|
||||
yield True
|
||||
self._wait_dialog.hide()
|
||||
|
||||
# ***************** Export ******************** #
|
||||
|
||||
def on_bouquet_export(self, item=None):
|
||||
""" Exports single bouquet to file. """
|
||||
bq_selected = self.check_bouquet_selection()
|
||||
if not bq_selected:
|
||||
return
|
||||
|
||||
model, paths = self._bouquets_view.get_selection().get_selected_rows()
|
||||
if len(paths) > 1:
|
||||
self.show_error_dialog("Please, select only one bouquet!")
|
||||
return
|
||||
|
||||
response = show_dialog(DialogType.CHOOSER, self._main_window, settings=self._settings,
|
||||
buttons=(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, Gtk.STOCK_SAVE, Gtk.ResponseType.OK))
|
||||
if response in (Gtk.ResponseType.CANCEL, Gtk.ResponseType.DELETE_EVENT):
|
||||
return
|
||||
|
||||
try:
|
||||
itr = model.get_iter(paths)
|
||||
bq = self.get_bouquet(itr, model)
|
||||
if self._s_type is SettingsType.NEUTRINO_MP:
|
||||
bq = Bouquets(*model.get(itr, Column.BQ_NAME, Column.BQ_TYPE), [bq])
|
||||
response += bq.name
|
||||
write_bouquet(response, bq, self._s_type)
|
||||
except OSError as e:
|
||||
self.show_error_dialog(str(e))
|
||||
else:
|
||||
show_dialog(DialogType.INFO, self._main_window, "Done!")
|
||||
|
||||
@run_idle
|
||||
def on_export_to_m3u(self, action, value=None):
|
||||
i_types = (BqServiceType.IPTV.value, BqServiceType.MARKER.value)
|
||||
bq_services = [BouquetService(r[Column.FAV_SERVICE],
|
||||
BqServiceType(r[Column.FAV_TYPE]),
|
||||
r[Column.FAV_ID],
|
||||
r[Column.FAV_NUM]) for r in self._fav_model if r[Column.FAV_TYPE] in i_types]
|
||||
|
||||
if not any(s.type is BqServiceType.IPTV for s in bq_services):
|
||||
self.show_error_dialog("This list does not contains IPTV streams!")
|
||||
return
|
||||
|
||||
response = show_dialog(DialogType.CHOOSER, self._main_window, settings=self._settings,
|
||||
buttons=(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, Gtk.STOCK_SAVE, Gtk.ResponseType.OK))
|
||||
if response in (Gtk.ResponseType.CANCEL, Gtk.ResponseType.DELETE_EVENT):
|
||||
return
|
||||
|
||||
try:
|
||||
bq = Bouquet(self._current_bq_name, None, bq_services, None, None)
|
||||
export_to_m3u(response, bq, self._s_type)
|
||||
except Exception as e:
|
||||
self.show_error_dialog(str(e))
|
||||
else:
|
||||
show_dialog(DialogType.INFO, self._main_window, "Done!")
|
||||
|
||||
# ***************** Backup ******************** #
|
||||
|
||||
def on_backup_tool_show(self, action, value=None):
|
||||
""" Shows backup tool dialog """
|
||||
BackupDialog(self._main_window, self._settings, self.open_data).show()
|
||||
|
||||
# ***************** Player ********************* #
|
||||
# ***************** Telnet *********************#
|
||||
|
||||
def on_telnet_client_show(self, action, value=None):
|
||||
from app.ui.telnet import TelnetDialog
|
||||
TelnetDialog(self._main_window, self._settings).show()
|
||||
|
||||
# ***************** Player *********************#
|
||||
|
||||
def on_play_stream(self, item=None):
|
||||
self.on_player_play()
|
||||
@@ -2600,11 +2675,6 @@ class Application(Gtk.Application):
|
||||
self._player_tool_bar.set_visible(visible)
|
||||
self._status_bar_box.set_visible(visible and not self._app_info_box.get_visible())
|
||||
|
||||
def on_main_window_state(self, window, event):
|
||||
if event.new_window_state & Gdk.WindowState.FULLSCREEN or event.new_window_state & Gdk.WindowState.MAXIMIZED:
|
||||
# Saving the current size of the application window.
|
||||
self._settings.add("window_size", self._main_window.get_size())
|
||||
|
||||
@run_idle
|
||||
def show_playback_window(self):
|
||||
width, height = 480, 240
|
||||
@@ -2908,9 +2978,7 @@ class Application(Gtk.Application):
|
||||
|
||||
def on_ftp_toggle(self, button):
|
||||
if not self._app_info_box.get_visible():
|
||||
active = not button.get_active()
|
||||
self._right_header_box.set_visible(active)
|
||||
self._left_header_box.set_visible(active)
|
||||
self._toolbar_search_box.set_visible(not button.get_active())
|
||||
|
||||
def on_ftp_realize(self, revealer):
|
||||
if not self._ftp_client:
|
||||
@@ -2953,6 +3021,7 @@ class Application(Gtk.Application):
|
||||
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
|
||||
@@ -3426,11 +3495,11 @@ class Application(Gtk.Application):
|
||||
|
||||
profile_name = self._profile_combo_box.get_active_text()
|
||||
msg = get_message("Profile:")
|
||||
|
||||
if self._s_type is SettingsType.ENIGMA_2:
|
||||
self._header_bar.set_subtitle("{} {} [Enigma2 v.{}]".format(msg, profile_name, self.get_format_version()))
|
||||
title = "DemonEditor [{} {} - Enigma2 v.{}]".format(msg, profile_name, self.get_format_version())
|
||||
self._main_window.set_title(title)
|
||||
elif self._s_type is SettingsType.NEUTRINO_MP:
|
||||
self._header_bar.set_subtitle("{} {} [Neutrino-MP]".format(msg, profile_name))
|
||||
self._main_window.set_title("DemonEditor [{} {} - Neutrino-MP]".format(msg, profile_name))
|
||||
|
||||
def get_format_version(self):
|
||||
return 5 if self._settings.v5_support else 4
|
||||
@@ -3474,10 +3543,6 @@ class Application(Gtk.Application):
|
||||
def current_services(self):
|
||||
return self._services
|
||||
|
||||
@property
|
||||
def current_bouquets(self):
|
||||
return self._bouquets
|
||||
|
||||
@property
|
||||
def picons_buffer(self):
|
||||
""" Returns a copy and clears the current buffer. """
|
||||
|
||||
@@ -590,7 +590,9 @@ def get_bouquets_names(model):
|
||||
|
||||
def update_entry_data(entry, dialog, settings):
|
||||
""" Updates value in text entry from chooser dialog. """
|
||||
response = show_dialog(dialog_type=DialogType.CHOOSER, transient=dialog, settings=settings, create_dir=True)
|
||||
response = show_dialog(dialog_type=DialogType.CHOOSER, transient=dialog, settings=settings,
|
||||
action_type=Gtk.FileChooserAction.CREATE_FOLDER if settings.is_darwin else None,
|
||||
create_dir=True)
|
||||
if response not in (Gtk.ResponseType.CANCEL, Gtk.ResponseType.DELETE_EVENT):
|
||||
entry.set_text(response)
|
||||
return response
|
||||
@@ -623,7 +625,7 @@ def get_base_paths(paths, model):
|
||||
def get_model_data(view):
|
||||
""" Returns model name and base model from the given view """
|
||||
model = get_base_model(view.get_model())
|
||||
model_name = model.get_name() if model else ""
|
||||
model_name = model.get_name()
|
||||
return model_name, model
|
||||
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,35 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# The MIT License (MIT)
|
||||
#
|
||||
# Copyright (c) 2018-2021 Dmitriy Yefremov
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
# THE SOFTWARE.
|
||||
#
|
||||
# Author: Dmitriy Yefremov
|
||||
#
|
||||
|
||||
|
||||
import os
|
||||
import re
|
||||
import shutil
|
||||
from enum import Enum
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
from urllib.parse import urlparse, unquote
|
||||
|
||||
@@ -38,24 +10,20 @@ from gi.repository import GLib, GdkPixbuf, Gio
|
||||
from app.commons import run_idle, run_task, run_with_delay
|
||||
from app.connections import upload_data, DownloadType, download_data, remove_picons
|
||||
from app.settings import SettingsType, Settings
|
||||
from app.tools.picons import (PiconsParser, parse_providers, Provider, convert_to, download_picon, PiconsCzDownloader,
|
||||
PiconsError)
|
||||
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, 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, KeyboardKey
|
||||
from .dialogs import show_dialog, DialogType, get_message
|
||||
from .main_helper import update_entry_data, append_text_to_tview, scroll_to, on_popup_menu, get_base_model, set_picon, \
|
||||
get_picon_pixbuf
|
||||
from .uicommons import Gtk, Gdk, UI_RESOURCES_PATH, TV_ICON, Column, GTK_PATH, KeyboardKey
|
||||
|
||||
|
||||
class PiconsDialog:
|
||||
class DownloadSource(Enum):
|
||||
LYNG_SAT = "lyngsat"
|
||||
PICON_CZ = "piconcz"
|
||||
|
||||
def __init__(self, transient, settings, picon_ids, sat_positions, app):
|
||||
self._picon_ids = picon_ids
|
||||
self._sat_positions = sat_positions
|
||||
self._app = app
|
||||
self._TMP_DIR = tempfile.gettempdir() + "/"
|
||||
self._BASE_URL = "www.lyngsat.com/packages/"
|
||||
self._PATTERN = re.compile(r"^https://www\.lyngsat\.com/[\w-]+\.html$")
|
||||
self._POS_PATTERN = re.compile(r"^\d+\.\d+[EW]?$")
|
||||
@@ -65,13 +33,9 @@ class PiconsDialog:
|
||||
self._filter_binding = None
|
||||
self._services = None
|
||||
self._current_picon_info = None
|
||||
# Downloader
|
||||
self._sats = None
|
||||
self._sat_names = None
|
||||
self._download_src = self.DownloadSource.PICON_CZ
|
||||
self._picon_cz_downloader = None
|
||||
|
||||
handlers = {"on_receive": self.on_receive,
|
||||
"on_load_providers": self.on_load_providers,
|
||||
"on_cancel": self.on_cancel,
|
||||
"on_close": self.on_close,
|
||||
"on_send": self.on_send,
|
||||
@@ -100,10 +64,7 @@ class PiconsDialog:
|
||||
"on_selective_remove": self.on_selective_remove,
|
||||
"on_local_remove": self.on_local_remove,
|
||||
"on_picons_dest_view_realize": self.on_picons_dest_view_realize,
|
||||
"on_download_source_changed": self.on_download_source_changed,
|
||||
"on_satellites_view_realize": self.on_satellites_view_realize,
|
||||
"on_satellite_filter_toggled": self.on_satellite_filter_toggled,
|
||||
"on_providers_view_query_tooltip": self.on_providers_view_query_tooltip,
|
||||
"on_satellite_selection": self.on_satellite_selection,
|
||||
"on_select_all": self.on_select_all,
|
||||
"on_unselect_all": self.on_unselect_all,
|
||||
@@ -115,7 +76,9 @@ class PiconsDialog:
|
||||
"on_tree_view_key_press": self.on_tree_view_key_press,
|
||||
"on_popup_menu": on_popup_menu}
|
||||
|
||||
builder = get_builder(UI_RESOURCES_PATH + "picons_manager.glade", handlers)
|
||||
builder = Gtk.Builder()
|
||||
builder.add_from_file(UI_RESOURCES_PATH + "picons_manager.glade")
|
||||
builder.connect_signals(handlers)
|
||||
|
||||
self._dialog = builder.get_object("picons_dialog")
|
||||
self._dialog.set_transient_for(transient)
|
||||
@@ -137,14 +100,15 @@ class PiconsDialog:
|
||||
self._src_filter_button = builder.get_object("src_filter_button")
|
||||
self._dst_filter_button = builder.get_object("dst_filter_button")
|
||||
self._picons_filter_entry = builder.get_object("picons_filter_entry")
|
||||
self._ip_entry = builder.get_object("ip_entry")
|
||||
self._picons_entry = builder.get_object("picons_entry")
|
||||
self._url_entry = builder.get_object("url_entry")
|
||||
self._picons_dir_entry = builder.get_object("picons_dir_entry")
|
||||
self._info_bar = builder.get_object("info_bar")
|
||||
self._info_bar = builder.get_object("info_bar")
|
||||
self._message_label = builder.get_object("info_bar_message_label")
|
||||
self._info_check_button = builder.get_object("info_check_button")
|
||||
self._info_toggle_button = builder.get_object("info_toggle_button")
|
||||
self._picon_info_image = builder.get_object("picon_info_image")
|
||||
self._picon_info_label = builder.get_object("picon_info_label")
|
||||
self._download_source_button = builder.get_object("download_source_button")
|
||||
self._load_providers_button = builder.get_object("load_providers_button")
|
||||
self._receive_button = builder.get_object("receive_button")
|
||||
self._convert_button = builder.get_object("convert_button")
|
||||
self._enigma2_path_button = builder.get_object("enigma2_path_button")
|
||||
@@ -159,35 +123,35 @@ class PiconsDialog:
|
||||
self._resize_220_132_radio_button = builder.get_object("resize_220_132_radio_button")
|
||||
self._resize_100_60_radio_button = builder.get_object("resize_100_60_radio_button")
|
||||
self._satellite_label = builder.get_object("satellite_label")
|
||||
self._provider_header_label = builder.get_object("provider_header_label")
|
||||
self._satellite_filter_switch = builder.get_object("satellite_filter_switch")
|
||||
self._bouquet_filter_switch = builder.get_object("bouquet_filter_switch")
|
||||
self._bouquet_filter_grid = builder.get_object("bouquet_filter_grid")
|
||||
self._header_download_box = builder.get_object("header_download_box")
|
||||
self._explorer_action_box = builder.get_object("explorer_action_box")
|
||||
self._satellite_label.bind_property("visible", builder.get_object("loading_data_label"), "visible", 4)
|
||||
self._satellite_label.bind_property("visible", builder.get_object("loading_data_spinner"), "visible", 4)
|
||||
self._satellite_label.bind_property("visible", self._download_source_button, "sensitive")
|
||||
self._satellite_label.bind_property("visible", self._satellites_view, "sensitive")
|
||||
self._cancel_button.bind_property("visible", self._header_download_box, "visible", 4)
|
||||
self._convert_button.bind_property("visible", self._header_download_box, "visible", 4)
|
||||
self._download_source_button.bind_property("visible", self._receive_button, "visible")
|
||||
self._cancel_button.bind_property("visible", builder.get_object("receive_button"), "visible", 4)
|
||||
self._cancel_button.bind_property("visible", self._load_providers_button, "visible", 4)
|
||||
self._convert_button.bind_property("visible", self._explorer_action_box, "visible", 4)
|
||||
downloader_action_box = builder.get_object("downloader_action_box")
|
||||
self._explorer_action_box.bind_property("visible", downloader_action_box, "visible", 4)
|
||||
self._convert_button.bind_property("visible", downloader_action_box, "visible", 4)
|
||||
self._filter_bar.bind_property("search-mode-enabled", self._filter_bar, "visible")
|
||||
self._explorer_src_path_button.bind_property("sensitive", builder.get_object("picons_view_sw"), "sensitive")
|
||||
self._filter_button.bind_property("active", builder.get_object("filter_service_box"), "visible")
|
||||
self._filter_button.bind_property("active", builder.get_object("src_title_grid"), "visible")
|
||||
self._filter_button.bind_property("active", builder.get_object("dst_title_grid"), "visible")
|
||||
self._filter_button.bind_property("visible", self._info_check_button, "visible")
|
||||
self._filter_button.bind_property("visible", self._send_button, "visible")
|
||||
self._filter_button.bind_property("visible", self._download_button, "visible")
|
||||
self._filter_button.bind_property("visible", self._remove_button, "visible")
|
||||
self._filter_button.bind_property("visible", self._info_toggle_button, "visible")
|
||||
explorer_info_bar = builder.get_object("explorer_info_bar")
|
||||
explorer_info_bar.bind_property("visible", builder.get_object("explorer_info_bar_frame"), "visible")
|
||||
self._info_check_button.bind_property("active", explorer_info_bar, "visible")
|
||||
self._info_toggle_button.bind_property("active", explorer_info_bar, "visible")
|
||||
# Init drag-and-drop
|
||||
self.init_drag_and_drop()
|
||||
# Settings
|
||||
# Style
|
||||
self._style_provider = Gtk.CssProvider()
|
||||
self._style_provider.load_from_path(UI_RESOURCES_PATH + "style.css")
|
||||
self._url_entry.get_style_context().add_provider_for_screen(Gdk.Screen.get_default(), self._style_provider,
|
||||
Gtk.STYLE_PROVIDER_PRIORITY_USER)
|
||||
self._settings = settings
|
||||
self._s_type = settings.setting_type
|
||||
self._ip_entry.set_text(self._settings.host)
|
||||
self._picons_entry.set_text(self._settings.picons_path)
|
||||
self._picons_dir_entry.set_text(self._settings.picons_local_path)
|
||||
|
||||
window_size = self._settings.get("picons_downloader_window_size")
|
||||
@@ -300,8 +264,9 @@ class PiconsDialog:
|
||||
def on_picons_view_drag_data_get(self, view, drag_context, data, info, time):
|
||||
model, path = view.get_selection().get_selected_rows()
|
||||
if path:
|
||||
data.set_uris([Path(model[path][-1]).as_uri(),
|
||||
Path(self._explorer_dest_path_button.get_filename()).as_uri()])
|
||||
p_uri = Path(model[path][-1]).as_uri()
|
||||
dest_uri = Path(self._explorer_dest_path_button.get_filename()).as_uri()
|
||||
data.set_uris(["{}::::{}".format(p_uri, dest_uri)])
|
||||
|
||||
def on_picons_src_view_drag_drop(self, view, drag_context, x, y, time):
|
||||
view.stop_emission_by_name("drag_drop")
|
||||
@@ -376,10 +341,11 @@ class PiconsDialog:
|
||||
return
|
||||
|
||||
uris = data.get_uris()
|
||||
if len(uris) == 2:
|
||||
if uris:
|
||||
name, fav_id = self._current_picon_info
|
||||
src = urlparse(unquote(uris[0])).path
|
||||
dst = "{}/{}".format(urlparse(unquote(uris[1])).path, name)
|
||||
src, sep, dst = uris[0].partition("::::")
|
||||
src = urlparse(unquote(src)).path
|
||||
dst = "{}/{}".format(urlparse(unquote(dst)).path, name)
|
||||
if src != dst:
|
||||
shutil.copy(src, dst)
|
||||
for row in get_base_model(self._picons_dest_view.get_model()):
|
||||
@@ -392,7 +358,7 @@ class PiconsDialog:
|
||||
|
||||
def on_send_button_drag_data_received(self, button, drag_context, x, y, data, info, time):
|
||||
path = self.get_path_from_uris(data)
|
||||
if path:
|
||||
if path and show_dialog(DialogType.QUESTION, self._dialog) == Gtk.ResponseType.OK:
|
||||
self.on_send(files_filter={path.name}, path=path.parent)
|
||||
|
||||
def on_download_button_drag_data_received(self, button, drag_context, x, y, data, info, time):
|
||||
@@ -407,8 +373,9 @@ class PiconsDialog:
|
||||
|
||||
def get_path_from_uris(self, data):
|
||||
uris = data.get_uris()
|
||||
if len(uris) == 2:
|
||||
return Path(urlparse(unquote(uris[0])).path).resolve()
|
||||
if uris:
|
||||
src, sep, dst = uris[0].partition("::::")
|
||||
return Path(urlparse(unquote(src)).path).resolve()
|
||||
|
||||
def update_picon_in_lists(self, dst, fav_id):
|
||||
picon = get_picon_pixbuf(dst)
|
||||
@@ -420,7 +387,7 @@ class PiconsDialog:
|
||||
|
||||
def on_selective_send(self, view):
|
||||
path = self.get_selected_path(view)
|
||||
if path:
|
||||
if path and show_dialog(DialogType.QUESTION, self._dialog) == Gtk.ResponseType.OK:
|
||||
self.on_send(files_filter={path.name}, path=path.parent)
|
||||
|
||||
def on_selective_download(self, view):
|
||||
@@ -473,7 +440,7 @@ class PiconsDialog:
|
||||
files_filter=files_filter), True)
|
||||
|
||||
def on_remove(self, item=None, files_filter=None):
|
||||
if show_dialog(DialogType.QUESTION, self._dialog) == Gtk.ResponseType.CANCEL:
|
||||
if show_dialog(DialogType.QUESTION, self._dialog) != Gtk.ResponseType.OK:
|
||||
return
|
||||
|
||||
self.run_func(lambda: remove_picons(settings=self._settings,
|
||||
@@ -500,168 +467,72 @@ class PiconsDialog:
|
||||
|
||||
# ******************** Downloader ************************* #
|
||||
|
||||
def on_download_source_changed(self, button):
|
||||
self._download_src = self.DownloadSource(button.get_active_id())
|
||||
self.set_providers_header()
|
||||
self._bouquet_filter_grid.set_sensitive(self._download_src is self.DownloadSource.PICON_CZ)
|
||||
GLib.idle_add(self._providers_view.get_model().clear)
|
||||
self.init_satellites(self._satellites_view)
|
||||
|
||||
def on_satellites_view_realize(self, view):
|
||||
self.set_providers_header()
|
||||
self.get_satellites(view)
|
||||
|
||||
def on_satellite_filter_toggled(self, button, state):
|
||||
self.init_satellites(self._satellites_view)
|
||||
|
||||
def on_providers_view_query_tooltip(self, view, x, y, keyboard_mode, tooltip):
|
||||
if self._download_src is self.DownloadSource.LYNG_SAT:
|
||||
return False
|
||||
|
||||
dest = view.get_dest_row_at_pos(x, y)
|
||||
if not dest:
|
||||
return False
|
||||
|
||||
path, pos = dest
|
||||
model = view.get_model()
|
||||
itr = model.get_iter(path)
|
||||
logo_url = model.get_value(itr, 5)
|
||||
if logo_url:
|
||||
pix_data = self._picon_cz_downloader.get_logo_data(logo_url)
|
||||
if pix_data:
|
||||
pix = self.get_pixbuf(pix_data)
|
||||
model.set_value(itr, 0, pix if pix else TV_ICON)
|
||||
size = self._settings.tooltip_logo_size
|
||||
tooltip.set_icon(self.get_pixbuf(pix_data, size, size))
|
||||
else:
|
||||
self.update_logo_data(itr, model, logo_url)
|
||||
tooltip.set_text(model.get_value(itr, 1))
|
||||
view.set_tooltip_row(tooltip, path)
|
||||
return True
|
||||
|
||||
@run_task
|
||||
def update_logo_data(self, itr, model, url):
|
||||
pix_data = self._picon_cz_downloader.get_provider_logo(url)
|
||||
if pix_data:
|
||||
pix = self.get_pixbuf(pix_data)
|
||||
GLib.idle_add(model.set_value, itr, 0, pix if pix else TV_ICON)
|
||||
|
||||
@run_idle
|
||||
def set_providers_header(self):
|
||||
msg = "{} [{}]"
|
||||
tooltip = ""
|
||||
if self._download_src is self.DownloadSource.PICON_CZ:
|
||||
tooltip = "https://picon.cz (by Chocholoušek)"
|
||||
msg = msg.format(get_message("Package"), tooltip)
|
||||
elif self._download_src is self.DownloadSource.LYNG_SAT:
|
||||
tooltip = "https://www.lyngsat.com"
|
||||
msg = msg.format(get_message("Providers"), tooltip)
|
||||
else:
|
||||
msg = ""
|
||||
|
||||
self._provider_header_label.set_text(msg)
|
||||
self._provider_header_label.set_tooltip_text(tooltip)
|
||||
|
||||
@run_task
|
||||
def get_satellites(self, view):
|
||||
self._sats = SatellitesParser().get_satellites_list(SatelliteSource.LYNGSAT)
|
||||
if not self._sats:
|
||||
sats = SatellitesParser().get_satellites_list(SatelliteSource.LYNGSAT)
|
||||
if not sats:
|
||||
self.show_info_message("Getting satellites list error!", Gtk.MessageType.ERROR)
|
||||
|
||||
self._sat_names = {s[1]: s[0] for s in self._sats} # position -> satellite name
|
||||
self._picon_cz_downloader = PiconsCzDownloader(self._picon_ids, self.append_output)
|
||||
self.init_satellites(view)
|
||||
|
||||
@run_task
|
||||
def init_satellites(self, view):
|
||||
sats = self._sats
|
||||
if self._download_src is self.DownloadSource.PICON_CZ:
|
||||
if not self._picon_cz_downloader:
|
||||
return
|
||||
try:
|
||||
self._picon_cz_downloader.init()
|
||||
except PiconsError as e:
|
||||
self.show_info_message(str(e), Gtk.MessageType.ERROR)
|
||||
else:
|
||||
providers = self._picon_cz_downloader.providers
|
||||
sats = ((self._sat_names.get(p, p), p, None, p, False) for p in providers)
|
||||
gen = self.append_satellites(view.get_model(), sats)
|
||||
GLib.idle_add(lambda: next(gen, False), priority=GLib.PRIORITY_LOW)
|
||||
|
||||
def append_satellites(self, model, sats):
|
||||
is_filter = self._satellite_filter_switch.get_active()
|
||||
if model:
|
||||
model.clear()
|
||||
|
||||
try:
|
||||
for sat in sorted(sats):
|
||||
for sat in sats:
|
||||
pos = sat[1]
|
||||
name = "{} ({})".format(sat[0], pos)
|
||||
if is_filter and pos not in self._sat_positions:
|
||||
continue
|
||||
if not model:
|
||||
return
|
||||
yield model.append((name, sat[3], pos))
|
||||
|
||||
if not self._terminate and model:
|
||||
if pos in self._sat_positions:
|
||||
yield model.append((name, sat[3], pos))
|
||||
finally:
|
||||
self._satellite_label.show()
|
||||
|
||||
def on_satellite_selection(self, view, path, column):
|
||||
model = view.get_model()
|
||||
self._url_entry.set_text(model.get(model.get_iter(path), 1)[0])
|
||||
|
||||
def on_load_providers(self, item):
|
||||
self.on_info_bar_close()
|
||||
model = self._providers_view.get_model()
|
||||
model.clear()
|
||||
self._satellite_label.set_visible(False)
|
||||
self.get_providers(view.get_model()[path][1], model)
|
||||
self.get_providers(model)
|
||||
|
||||
@run_task
|
||||
def get_providers(self, url, model):
|
||||
if self._download_src is self.DownloadSource.LYNG_SAT:
|
||||
providers = parse_providers(url)
|
||||
elif self._download_src is self.DownloadSource.PICON_CZ:
|
||||
providers = self._picon_cz_downloader.get_sat_providers(url)
|
||||
else:
|
||||
return
|
||||
|
||||
self.append_providers(providers or [], model)
|
||||
def get_providers(self, model):
|
||||
providers = parse_providers(self._url_entry.get_text())
|
||||
if providers:
|
||||
self.append_providers(providers, model)
|
||||
|
||||
@run_idle
|
||||
def append_providers(self, providers, model):
|
||||
if self._download_src is self.DownloadSource.LYNG_SAT:
|
||||
for p in providers:
|
||||
model.append(p._replace(logo=self.get_pixbuf(p.logo) if p.logo else TV_ICON))
|
||||
elif self._download_src is self.DownloadSource.PICON_CZ:
|
||||
for p in providers:
|
||||
logo_data = self._picon_cz_downloader.get_logo_data(p.ssid)
|
||||
model.append(p._replace(logo=self.get_pixbuf(logo_data) if logo_data else TV_ICON))
|
||||
|
||||
for p in providers:
|
||||
model.append((self.get_pixbuf(p[0]) if p[0] else TV_ICON, *p[1:]))
|
||||
self.update_receive_button_state()
|
||||
GLib.idle_add(self._satellite_label.set_visible, True)
|
||||
|
||||
def get_pixbuf(self, img_data, w=48, h=32):
|
||||
def get_pixbuf(self, img_data):
|
||||
if img_data:
|
||||
f = Gio.MemoryInputStream.new_from_data(img_data)
|
||||
return GdkPixbuf.Pixbuf.new_from_stream_at_scale(f, w, h, True, None)
|
||||
return GdkPixbuf.Pixbuf.new_from_stream_at_scale(f, 48, 32, True, None)
|
||||
|
||||
def on_receive(self, item):
|
||||
self._cancel_button.show()
|
||||
self.start_download()
|
||||
|
||||
@run_task
|
||||
def start_download(self):
|
||||
if self._is_downloading:
|
||||
self.show_dialog("The task is already running!", DialogType.ERROR)
|
||||
return
|
||||
|
||||
providers = self.get_selected_providers()
|
||||
|
||||
if self._download_src is self.DownloadSource.PICON_CZ and len(providers) > 1:
|
||||
self.show_dialog("Please, select only one item!", DialogType.ERROR)
|
||||
return
|
||||
|
||||
self._cancel_button.show()
|
||||
self.start_download(providers)
|
||||
|
||||
@run_task
|
||||
def start_download(self, providers):
|
||||
self._is_downloading = True
|
||||
GLib.idle_add(self._expander.set_expanded, True)
|
||||
|
||||
providers = self.get_selected_providers()
|
||||
for prv in providers:
|
||||
if self._download_src is self.DownloadSource.LYNG_SAT and not self._POS_PATTERN.match(prv[2]):
|
||||
if not self._POS_PATTERN.match(prv[2]):
|
||||
self.show_info_message(
|
||||
get_message("Specify the correct position value for the provider!"), Gtk.MessageType.ERROR)
|
||||
scroll_to(prv.path, self._providers_view)
|
||||
@@ -670,81 +541,45 @@ class PiconsDialog:
|
||||
try:
|
||||
picons_path = self._picons_dir_entry.get_text()
|
||||
os.makedirs(os.path.dirname(picons_path), exist_ok=True)
|
||||
self.show_info_message(get_message("Please, wait..."), Gtk.MessageType.INFO)
|
||||
providers = (Provider(*p) for p in providers)
|
||||
picons = []
|
||||
|
||||
if self._download_src is self.DownloadSource.LYNG_SAT:
|
||||
self.get_picons_for_lyngsat(picons_path, providers)
|
||||
elif self._download_src is self.DownloadSource.PICON_CZ:
|
||||
self.get_picons_for_picon_cz(picons_path, providers)
|
||||
self.show_info_message(get_message("Please, wait..."), Gtk.MessageType.INFO)
|
||||
|
||||
import concurrent.futures
|
||||
|
||||
with concurrent.futures.ThreadPoolExecutor(max_workers=10) as executor:
|
||||
# Getting links to picons.
|
||||
futures = {executor.submit(self.process_provider, Provider(*p), picons_path): p for p in providers}
|
||||
for future in concurrent.futures.as_completed(futures):
|
||||
if not self._is_downloading:
|
||||
executor.shutdown()
|
||||
return
|
||||
|
||||
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}
|
||||
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)
|
||||
|
||||
for future in not_done:
|
||||
future.cancel()
|
||||
concurrent.futures.wait(not_done)
|
||||
|
||||
if not self._is_downloading:
|
||||
return
|
||||
|
||||
if not self._resize_no_radio_button.get_active():
|
||||
self.resize(picons_path)
|
||||
else:
|
||||
self.show_info_message(get_message("Done!"), Gtk.MessageType.INFO)
|
||||
finally:
|
||||
GLib.idle_add(self._cancel_button.hide)
|
||||
self._is_downloading = False
|
||||
|
||||
def get_picons_for_lyngsat(self, path, providers):
|
||||
import concurrent.futures
|
||||
|
||||
with concurrent.futures.ThreadPoolExecutor(max_workers=10) as executor:
|
||||
picons = []
|
||||
# Getting links to picons.
|
||||
futures = {executor.submit(self.process_provider, p, path): p for p in providers}
|
||||
for future in concurrent.futures.as_completed(futures):
|
||||
if not self._is_downloading:
|
||||
executor.shutdown()
|
||||
return
|
||||
|
||||
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}
|
||||
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)
|
||||
|
||||
for future in not_done:
|
||||
future.cancel()
|
||||
concurrent.futures.wait(not_done)
|
||||
self.show_info_message(get_message("Done!"), Gtk.MessageType.INFO)
|
||||
|
||||
def get_picons_for_picon_cz(self, path, providers):
|
||||
p_ids = None
|
||||
if self._bouquet_filter_switch.get_active():
|
||||
p_ids = self.get_bouquet_picon_ids()
|
||||
if not p_ids:
|
||||
return
|
||||
|
||||
try:
|
||||
# We download it sequentially.
|
||||
for p in providers:
|
||||
self._picon_cz_downloader.download(p, path, p_ids)
|
||||
except PiconsError as e:
|
||||
self.append_output("Error: {}\n".format(str(e)))
|
||||
self.show_info_message(str(e), Gtk.MessageType.ERROR)
|
||||
else:
|
||||
self.show_info_message(get_message("Done!"), Gtk.MessageType.INFO)
|
||||
|
||||
def get_bouquet_picon_ids(self):
|
||||
""" Returns picon ids for selected bouquet or None. """
|
||||
bq_selected = self._app.check_bouquet_selection()
|
||||
if not bq_selected:
|
||||
return
|
||||
|
||||
model, paths = self._app.bouquets_view.get_selection().get_selected_rows()
|
||||
if len(paths) > 1:
|
||||
self.show_dialog("Please, select only one bouquet!", DialogType.ERROR)
|
||||
return
|
||||
|
||||
fav_bouquet = self._app.current_bouquets[bq_selected]
|
||||
services = self._app.current_services
|
||||
return {services.get(fav_id).picon_id for fav_id in fav_bouquet}
|
||||
|
||||
def process_provider(self, prv, picons_path):
|
||||
self.append_output("Getting links to picons for: {}.\n".format(prv.name))
|
||||
return PiconsParser.parse(prv, picons_path, self._picon_ids, self.get_picons_format())
|
||||
@@ -791,6 +626,7 @@ class PiconsDialog:
|
||||
self._terminate = True
|
||||
self._is_downloading = False
|
||||
self.save_window_size(window)
|
||||
self.clean_data()
|
||||
self._app.update_picons()
|
||||
GLib.idle_add(self._dialog.destroy)
|
||||
|
||||
@@ -799,16 +635,22 @@ class PiconsDialog:
|
||||
height = size.height - self._text_view.get_allocated_height() - self._info_bar.get_allocated_height()
|
||||
self._settings.add("picons_downloader_window_size", (size.width, height))
|
||||
|
||||
@run_task
|
||||
def clean_data(self):
|
||||
path = self._TMP_DIR + "www.lyngsat.com"
|
||||
if os.path.exists(path):
|
||||
shutil.rmtree(path)
|
||||
|
||||
@run_task
|
||||
def run_func(self, func, update=False):
|
||||
try:
|
||||
GLib.idle_add(self._expander.set_expanded, True)
|
||||
GLib.idle_add(self._header_download_box.set_sensitive, False)
|
||||
GLib.idle_add(self._explorer_action_box.set_sensitive, False)
|
||||
func()
|
||||
except OSError as e:
|
||||
self.show_info_message(str(e), Gtk.MessageType.ERROR)
|
||||
finally:
|
||||
GLib.idle_add(self._header_download_box.set_sensitive, True)
|
||||
GLib.idle_add(self._explorer_action_box.set_sensitive, True)
|
||||
if update:
|
||||
self.on_picons_dest_changed(self._explorer_dest_path_button)
|
||||
|
||||
@@ -888,7 +730,7 @@ class PiconsDialog:
|
||||
map(lambda s: s.picon_id, filter(lambda s: txt in s.service.upper(), self._app.current_services.values())))
|
||||
|
||||
def on_picon_activated(self, view):
|
||||
if self._info_check_button.get_active():
|
||||
if self._info_toggle_button.get_active():
|
||||
model, path = view.get_selection().get_selected_rows()
|
||||
if not path:
|
||||
return
|
||||
@@ -942,7 +784,7 @@ class PiconsDialog:
|
||||
def on_url_changed(self, entry):
|
||||
suit = self._PATTERN.search(entry.get_text())
|
||||
entry.set_name("GtkEntry" if suit else "digit-entry")
|
||||
self._download_source_button.set_sensitive(suit if suit else False)
|
||||
self._load_providers_button.set_sensitive(suit if suit else False)
|
||||
|
||||
def on_position_edited(self, render, path, value):
|
||||
model = self._providers_view.get_model()
|
||||
@@ -952,9 +794,8 @@ class PiconsDialog:
|
||||
def on_visible_page(self, stack: Gtk.Stack, param):
|
||||
name = stack.get_visible_child_name()
|
||||
self._convert_button.set_visible(name == "converter")
|
||||
self._download_source_button.set_visible(name == "downloader")
|
||||
is_explorer = name == "explorer"
|
||||
self._filter_button.set_visible(is_explorer)
|
||||
self._explorer_action_box.set_visible(is_explorer)
|
||||
if is_explorer:
|
||||
self.on_picons_dest_changed(self._explorer_dest_path_button)
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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_chooser_dialog, get_message, get_builder
|
||||
from .dialogs import show_dialog, DialogType, get_dialogs_string, get_chooser_dialog, get_message
|
||||
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, MOVE_KEYS, KeyboardKey, MOD_MASK
|
||||
from .uicommons import Gtk, Gdk, UI_RESOURCES_PATH, TEXT_DOMAIN, MOVE_KEYS, KeyboardKey, IS_GNOME_SESSION, MOD_MASK
|
||||
|
||||
_UI_PATH = UI_RESOURCES_PATH + "satellites_dialog.glade"
|
||||
|
||||
@@ -44,9 +44,13 @@ class SatellitesDialog:
|
||||
"on_resize": self.on_resize,
|
||||
"on_quit": self.on_quit}
|
||||
|
||||
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"))
|
||||
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)
|
||||
|
||||
self._window = builder.get_object("satellites_editor_window")
|
||||
self._window.set_transient_for(transient)
|
||||
@@ -312,8 +316,13 @@ class TransponderDialog:
|
||||
def __init__(self, transient, transponder: Transponder = None):
|
||||
|
||||
handlers = {"on_entry_changed": self.on_entry_changed}
|
||||
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)
|
||||
|
||||
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)
|
||||
|
||||
self._dialog = builder.get_object("transponder_dialog")
|
||||
self._dialog.set_transient_for(transient)
|
||||
@@ -392,7 +401,10 @@ class SatelliteDialog:
|
||||
""" Shows dialog for adding or edit satellite """
|
||||
|
||||
def __init__(self, transient, satellite: Satellite = None):
|
||||
builder = get_builder(_UI_PATH, use_str=True, objects=("satellite_dialog", "side_store", "pos_adjustment"))
|
||||
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"))
|
||||
|
||||
self._dialog = builder.get_object("satellite_dialog")
|
||||
self._dialog.set_transient_for(transient)
|
||||
@@ -454,11 +466,16 @@ class UpdateDialog:
|
||||
self._parser = None
|
||||
self._size_name = "{}_window_size".format("_".join(re.findall("[A-Z][^A-Z]*", self.__class__.__name__))).lower()
|
||||
|
||||
builder = get_builder(UI_RESOURCES_PATH + "satellites_dialog.glade", handlers,
|
||||
objects=("satellites_update_window", "update_source_store", "update_sat_list_store",
|
||||
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",
|
||||
"update_sat_list_model_filter", "update_sat_list_model_sort", "side_store",
|
||||
"pos_adjustment", "pos_adjustment2", "satellites_update_popup_menu",
|
||||
"remove_selection_image", "update_transponder_store", "update_service_store"))
|
||||
"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)
|
||||
|
||||
@@ -276,41 +276,6 @@ Author: Dmitriy Yefremov
|
||||
<child type="titlebar">
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child type="action">
|
||||
<object class="GtkButton" id="cancel_button">
|
||||
<property name="label">gtk-cancel</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="tooltip_text" translatable="yes">Cancel</property>
|
||||
<property name="use_stock">True</property>
|
||||
<property name="always_show_image">True</property>
|
||||
<signal name="clicked" handler="on_cancel" swapped="no"/>
|
||||
</object>
|
||||
</child>
|
||||
<child type="action">
|
||||
<object class="GtkButton" id="apply_button">
|
||||
<property name="label">gtk-save</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="tooltip_text" translatable="yes">Save current service</property>
|
||||
<property name="use_stock">True</property>
|
||||
<property name="always_show_image">True</property>
|
||||
<signal name="clicked" handler="on_save" swapped="no"/>
|
||||
</object>
|
||||
</child>
|
||||
<child type="action">
|
||||
<object class="GtkButton" id="create_button">
|
||||
<property name="label">gtk-new</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="tooltip_text" translatable="yes">Create and save as new service</property>
|
||||
<property name="use_stock">True</property>
|
||||
<property name="always_show_image">True</property>
|
||||
<signal name="clicked" handler="on_create_new" swapped="no"/>
|
||||
</object>
|
||||
</child>
|
||||
<child internal-child="vbox">
|
||||
<object class="GtkBox" id="dialog_vbox">
|
||||
<property name="can_focus">False</property>
|
||||
@@ -321,6 +286,55 @@ Author: Dmitriy Yefremov
|
||||
<child internal-child="action_area">
|
||||
<object class="GtkButtonBox">
|
||||
<property name="can_focus">False</property>
|
||||
<property name="valign">center</property>
|
||||
<property name="layout_style">end</property>
|
||||
<child>
|
||||
<object class="GtkButton" id="cancel_button">
|
||||
<property name="label" translatable="yes">Cancel</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="valign">center</property>
|
||||
<signal name="clicked" handler="on_cancel" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="apply_button">
|
||||
<property name="label" translatable="yes">Apply</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="tooltip_text" translatable="yes">Save current service</property>
|
||||
<property name="valign">center</property>
|
||||
<signal name="clicked" handler="on_save" swapped="no"/>
|
||||
<accelerator key="Return" signal="activate"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="create_button">
|
||||
<property name="label" translatable="yes">Create</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="tooltip_text" translatable="yes">Create and save as new service</property>
|
||||
<property name="valign">center</property>
|
||||
<signal name="clicked" handler="on_create_new" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
@@ -345,7 +359,7 @@ Author: Dmitriy Yefremov
|
||||
<property name="margin_right">5</property>
|
||||
<property name="margin_bottom">5</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<property name="spacing">5</property>
|
||||
<property name="spacing">2</property>
|
||||
<child>
|
||||
<object class="GtkGrid" id="srv_grid">
|
||||
<property name="visible">True</property>
|
||||
@@ -366,7 +380,7 @@ Author: Dmitriy Yefremov
|
||||
<object class="GtkEntry" id="name_entry">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="primary_icon_stock">gtk-edit</property>
|
||||
<property name="primary_icon_name">document-edit-symbolic</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
@@ -388,7 +402,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_stock">gtk-edit</property>
|
||||
<property name="primary_icon_name">document-edit-symbolic</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
@@ -412,7 +426,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_stock">gtk-edit</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>
|
||||
@@ -457,7 +471,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_stock">gtk-edit</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>
|
||||
@@ -807,14 +821,10 @@ Author: Dmitriy Yefremov
|
||||
<object class="GtkBox" id="flags_box">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="spacing">10</property>
|
||||
<child>
|
||||
<object class="GtkGrid" id="flags_grid">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="valign">center</property>
|
||||
<property name="margin_left">5</property>
|
||||
<property name="margin_right">10</property>
|
||||
<property name="column_spacing">2</property>
|
||||
<child>
|
||||
<object class="GtkLabel" id="flags_label">
|
||||
@@ -887,47 +897,6 @@ Author: Dmitriy Yefremov
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkBox" id="extra_flags_box">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="spacing">2</property>
|
||||
<child>
|
||||
<object class="GtkLabel" id="extra_pids_label">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">Extra:</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkEntry" id="extra_pids_entry">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="width_chars">20</property>
|
||||
<property name="max_width_chars">20</property>
|
||||
<property name="primary_icon_stock">gtk-edit</property>
|
||||
<property name="placeholder_text">c:000000,etc.</property>
|
||||
<signal name="changed" handler="on_extra_pids_entry_changed" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="pack_type">end</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkGrid" id="caids_grid">
|
||||
<property name="visible">True</property>
|
||||
@@ -938,10 +907,10 @@ Author: Dmitriy Yefremov
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="tooltip_text">C:0000,C:a1b2,etc.</property>
|
||||
<property name="width_chars">20</property>
|
||||
<property name="max_width_chars">20</property>
|
||||
<property name="primary_icon_stock">gtk-edit</property>
|
||||
<property name="placeholder_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>
|
||||
<packing>
|
||||
@@ -965,7 +934,7 @@ Author: Dmitriy Yefremov
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="pack_type">end</property>
|
||||
<property name="position">2</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
@@ -1043,7 +1012,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_stock">gtk-edit</property>
|
||||
<property name="primary_icon_name">document-edit-symbolic</property>
|
||||
<signal name="changed" handler="on_non_empty_entry_changed" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
@@ -1069,7 +1038,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_stock">gtk-edit</property>
|
||||
<property name="primary_icon_name">document-edit-symbolic</property>
|
||||
<signal name="changed" handler="on_non_empty_entry_changed" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
@@ -1148,7 +1117,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_stock">gtk-edit</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>
|
||||
@@ -1186,7 +1155,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_stock">gtk-edit</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>
|
||||
@@ -1213,7 +1182,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_stock">gtk-edit</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>
|
||||
@@ -1422,7 +1391,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_stock">gtk-edit</property>
|
||||
<property name="primary_icon_name">document-edit-symbolic</property>
|
||||
<signal name="changed" handler="on_digit_entry_changed" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
@@ -1448,7 +1417,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_stock">gtk-edit</property>
|
||||
<property name="primary_icon_name">document-edit-symbolic</property>
|
||||
<signal name="changed" handler="on_digit_entry_changed" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
@@ -1474,7 +1443,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_stock">gtk-edit</property>
|
||||
<property name="primary_icon_name">document-edit-symbolic</property>
|
||||
<signal name="changed" handler="on_digit_entry_changed" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
@@ -1603,7 +1572,7 @@ Author: Dmitriy Yefremov
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_left">10</property>
|
||||
<property name="stock">gtk-edit</property>
|
||||
<property name="icon_name">document-edit-symbolic</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
@@ -1631,9 +1600,6 @@ Author: Dmitriy Yefremov
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<action-widgets>
|
||||
<action-widget response="-6">cancel_button</action-widget>
|
||||
</action-widgets>
|
||||
</object>
|
||||
<object class="GtkListStore" id="transponder_services_liststore">
|
||||
<columns>
|
||||
@@ -1667,26 +1633,6 @@ Author: Dmitriy Yefremov
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child type="action">
|
||||
<object class="GtkButton" id="tr_services_no_button">
|
||||
<property name="label">gtk-cancel</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="use_stock">True</property>
|
||||
<property name="always_show_image">True</property>
|
||||
</object>
|
||||
</child>
|
||||
<child type="action">
|
||||
<object class="GtkButton" id="tr_services_ok_button">
|
||||
<property name="label">gtk-ok</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="use_stock">True</property>
|
||||
<property name="always_show_image">True</property>
|
||||
</object>
|
||||
</child>
|
||||
<child internal-child="vbox">
|
||||
<object class="GtkBox" id="tr_services_dialog_vbox">
|
||||
<property name="can_focus">False</property>
|
||||
@@ -1695,6 +1641,35 @@ Author: Dmitriy Yefremov
|
||||
<child internal-child="action_area">
|
||||
<object class="GtkButtonBox">
|
||||
<property name="can_focus">False</property>
|
||||
<property name="valign">center</property>
|
||||
<child>
|
||||
<object class="GtkButton" id="tr_services_no_button">
|
||||
<property name="label" translatable="yes">Cancel</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="valign">center</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="tr_services_ok_button">
|
||||
<property name="label" translatable="yes">OK</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="valign">center</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
@@ -1721,7 +1696,7 @@ Author: Dmitriy Yefremov
|
||||
<property name="margin_right">20</property>
|
||||
<property name="margin_top">5</property>
|
||||
<property name="margin_bottom">5</property>
|
||||
<property name="label" translatable="yes">Changes will be applied to all services of this transponder!
|
||||
<property name="label" translatable="yes">Changes will be applied to all services of this transponder!
|
||||
Continue?</property>
|
||||
<property name="justify">center</property>
|
||||
<property name="lines">2</property>
|
||||
|
||||
@@ -1,31 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# The MIT License (MIT)
|
||||
#
|
||||
# Copyright (c) 2018-2021 Dmitriy Yefremov
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
# THE SOFTWARE.
|
||||
#
|
||||
# Author: Dmitriy Yefremov
|
||||
#
|
||||
|
||||
|
||||
import os
|
||||
import re
|
||||
|
||||
@@ -36,9 +8,9 @@ from app.eparser.ecommons import (MODULATION, Inversion, ROLL_OFF, Pilot, Flag,
|
||||
TrType, SystemCable, T_SYSTEM, BANDWIDTH, TRANSMISSION_MODE, GUARD_INTERVAL, T_FEC,
|
||||
HIERARCHY, A_MODULATION)
|
||||
from app.settings import SettingsType
|
||||
from .dialogs import show_dialog, DialogType, Action, get_builder
|
||||
from .dialogs import show_dialog, DialogType, Action, get_dialogs_string
|
||||
from .main_helper import get_base_model
|
||||
from .uicommons import Gtk, Gdk, UI_RESOURCES_PATH, HIDE_ICON, CODED_ICON, Column
|
||||
from .uicommons import Gtk, Gdk, UI_RESOURCES_PATH, HIDE_ICON, TEXT_DOMAIN, CODED_ICON, Column, IS_GNOME_SESSION
|
||||
|
||||
_UI_PATH = UI_RESOURCES_PATH + "service_details_dialog.glade"
|
||||
|
||||
@@ -70,12 +42,14 @@ class ServiceDetailsDialog:
|
||||
"on_tr_edit_toggled": self.on_tr_edit_toggled,
|
||||
"update_reference": self.update_reference,
|
||||
"on_cas_entry_changed": self.on_cas_entry_changed,
|
||||
"on_extra_pids_entry_changed": self.on_extra_pids_entry_changed,
|
||||
"on_digit_entry_changed": self.on_digit_entry_changed,
|
||||
"on_non_empty_entry_changed": self.on_non_empty_entry_changed,
|
||||
"on_cancel": lambda item: self._dialog.destroy()}
|
||||
|
||||
builder = get_builder(_UI_PATH, handlers, use_str=True)
|
||||
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)
|
||||
self._builder = builder
|
||||
|
||||
self._dialog = builder.get_object("service_details_dialog")
|
||||
@@ -98,7 +72,6 @@ class ServiceDetailsDialog:
|
||||
self._DIGIT_PATTERN = re.compile("\\D")
|
||||
self._NON_EMPTY_PATTERN = re.compile("(?:^[\\s]*$|\\D)")
|
||||
self._CAID_PATTERN = re.compile("(?:^[\\s]*$)|(C:[0-9a-fA-F]{1,4})(,C:[0-9a-fA-F]{1,4})*")
|
||||
self._PIDS_PATTERN = re.compile("(?:^[\\s]*$)|(c:[0-9]{2}[0-9a-fA-F]{4})(,c:[0-9]{2}[0-9a-fA-F]{4})*")
|
||||
# Buttons
|
||||
self._apply_button = builder.get_object("apply_button")
|
||||
self._create_button = builder.get_object("create_button")
|
||||
@@ -134,7 +107,6 @@ class ServiceDetailsDialog:
|
||||
self._stream_id_entry = self._digit_elements.get("stream_id_entry")
|
||||
self._tr_flag_entry = self._digit_elements.get("tr_flag_entry")
|
||||
self._namespace_entry = self._non_empty_elements.get("namespace_entry")
|
||||
self._extra_pids_entry = builder.get_object("extra_pids_entry")
|
||||
# Service elements
|
||||
self._name_entry = builder.get_object("name_entry")
|
||||
self._package_entry = builder.get_object("package_entry")
|
||||
@@ -275,7 +247,6 @@ class ServiceDetailsDialog:
|
||||
def init_enigma2_pids(self, flags):
|
||||
pids = list(filter(lambda x: x.startswith("c:"), flags))
|
||||
if pids:
|
||||
extra_pids = []
|
||||
for pid in pids:
|
||||
if pid.startswith(Pids.VIDEO.value):
|
||||
self._video_pid_entry.set_text(str(int(pid[4:], 16)))
|
||||
@@ -288,19 +259,15 @@ class ServiceDetailsDialog:
|
||||
elif pid.startswith(Pids.AC3.value):
|
||||
self._ac3_pid_entry.set_text(str(int(pid[4:], 16)))
|
||||
elif pid.startswith(Pids.VIDEO_TYPE.value):
|
||||
extra_pids.append(pid)
|
||||
pass
|
||||
elif pid.startswith(Pids.AUDIO_CHANNEL.value):
|
||||
extra_pids.append(pid)
|
||||
pass
|
||||
elif pid.startswith(Pids.BIT_STREAM_DELAY.value):
|
||||
self._bitstream_entry.set_text(str(int(pid[4:], 16)))
|
||||
elif pid.startswith(Pids.PCM_DELAY.value):
|
||||
self._pcm_entry.set_text(str(int(pid[4:], 16)))
|
||||
elif pid.startswith(Pids.SUBTITLE.value):
|
||||
extra_pids.append(pid)
|
||||
else:
|
||||
extra_pids.append(pid)
|
||||
|
||||
self._extra_pids_entry.set_text(",".join(extra_pids))
|
||||
pass
|
||||
|
||||
def init_enigma2_transponder_data(self, srv):
|
||||
""" Transponder data initialisation """
|
||||
@@ -570,9 +537,6 @@ class ServiceDetailsDialog:
|
||||
pcm_pid = self._pcm_entry.get_text()
|
||||
if pcm_pid:
|
||||
flags.append("{}{:04x}".format(Pids.PCM_DELAY.value, int(pcm_pid)))
|
||||
extra_pids = self._extra_pids_entry.get_text()
|
||||
if extra_pids:
|
||||
flags.append(extra_pids)
|
||||
# flags
|
||||
f_flags = Flag.KEEP.value if self._keep_check_button.get_active() else 0
|
||||
f_flags = f_flags + Flag.HIDE.value if self._hide_check_button.get_active() else f_flags
|
||||
@@ -732,9 +696,6 @@ class ServiceDetailsDialog:
|
||||
def on_cas_entry_changed(self, entry):
|
||||
entry.set_name("GtkEntry" if self._CAID_PATTERN.fullmatch(entry.get_text()) else self._DIGIT_ENTRY_NAME)
|
||||
|
||||
def on_extra_pids_entry_changed(self, entry):
|
||||
entry.set_name("GtkEntry" if self._PIDS_PATTERN.fullmatch(entry.get_text()) else self._DIGIT_ENTRY_NAME)
|
||||
|
||||
def get_value_from_combobox_id(self, box: Gtk.ComboBox, dc: dict):
|
||||
cb_id = box.get_active_id()
|
||||
return get_key_by_value(dc, cb_id)
|
||||
@@ -767,8 +728,6 @@ class ServiceDetailsDialog:
|
||||
return False
|
||||
if self._cas_entry.get_name() == self._DIGIT_ENTRY_NAME:
|
||||
return False
|
||||
if self._extra_pids_entry.get_name() == self._DIGIT_ENTRY_NAME:
|
||||
return False
|
||||
return True
|
||||
|
||||
def update_reference(self, entry, event=None):
|
||||
@@ -916,7 +875,10 @@ class ServiceDetailsDialog:
|
||||
|
||||
class TransponderServicesDialog:
|
||||
def __init__(self, transient, services_view, transponder, tr_iters):
|
||||
builder = get_builder(_UI_PATH, use_str=True, objects=("tr_services_dialog", "transponder_services_liststore"))
|
||||
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"))
|
||||
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
@@ -4,7 +4,7 @@ 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, get_builder
|
||||
from app.ui.dialogs import show_dialog, DialogType, get_message, get_chooser_dialog
|
||||
from .main_helper import update_entry_data, scroll_to, get_picon_pixbuf
|
||||
from .uicommons import Gtk, Gdk, UI_RESOURCES_PATH, FavClickMode, DEFAULT_ICON, APP_FONT
|
||||
|
||||
@@ -64,7 +64,9 @@ class SettingsDialog:
|
||||
self._profiles = self._settings.profiles
|
||||
self._s_type = self._settings.setting_type
|
||||
|
||||
builder = get_builder(UI_RESOURCES_PATH + "settings_dialog.glade", handlers)
|
||||
builder = Gtk.Builder()
|
||||
builder.add_from_file(UI_RESOURCES_PATH + "settings_dialog.glade")
|
||||
builder.connect_signals(handlers)
|
||||
|
||||
self._dialog = builder.get_object("settings_dialog")
|
||||
self._dialog.set_transient_for(transient)
|
||||
@@ -103,7 +105,6 @@ class SettingsDialog:
|
||||
self._support_ver5_switch = builder.get_object("support_ver5_switch")
|
||||
self._force_bq_name_switch = builder.get_object("force_bq_name_switch")
|
||||
# Streaming
|
||||
header_separator = builder.get_object("header_separator")
|
||||
self._apply_presets_button = builder.get_object("apply_presets_button")
|
||||
self._transcoding_switch = builder.get_object("transcoding_switch")
|
||||
self._edit_preset_switch = builder.get_object("edit_preset_switch")
|
||||
@@ -115,7 +116,6 @@ class SettingsDialog:
|
||||
self._audio_channels_combo_box = builder.get_object("audio_channels_combo_box")
|
||||
self._audio_sample_rate_combo_box = builder.get_object("audio_sample_rate_combo_box")
|
||||
self._audio_codec_combo_box = builder.get_object("audio_codec_combo_box")
|
||||
self._apply_presets_button.bind_property("visible", header_separator, "visible")
|
||||
self._transcoding_switch.bind_property("active", builder.get_object("record_box"), "sensitive")
|
||||
self._edit_preset_switch.bind_property("active", self._apply_presets_button, "sensitive")
|
||||
self._edit_preset_switch.bind_property("active", builder.get_object("video_options_frame"), "sensitive")
|
||||
@@ -170,7 +170,7 @@ class SettingsDialog:
|
||||
self._profile_add_button = builder.get_object("profile_add_button")
|
||||
self._profile_remove_button = builder.get_object("profile_remove_button")
|
||||
self._apply_profile_button = builder.get_object("apply_profile_button")
|
||||
self._apply_profile_button.bind_property("visible", header_separator, "visible")
|
||||
self._apply_profile_button.bind_property("visible", builder.get_object("reset_button"), "visible")
|
||||
# Style
|
||||
self._style_provider = Gtk.CssProvider()
|
||||
self._style_provider.load_from_path(UI_RESOURCES_PATH + "style.css")
|
||||
@@ -184,8 +184,9 @@ class SettingsDialog:
|
||||
|
||||
if self._settings.is_darwin:
|
||||
# Themes
|
||||
builder.get_object("style_frame").set_visible(True)
|
||||
builder.get_object("themes_support_frame").set_visible(True)
|
||||
self._layout_switch = builder.get_object("layout_switch")
|
||||
self._layout_switch.bind_property("active", builder.get_object("bouquet_box"), "sensitive")
|
||||
self._layout_switch.set_active(self._ext_settings.alternate_layout)
|
||||
self._theme_frame = builder.get_object("theme_frame")
|
||||
self._theme_frame.set_visible(True)
|
||||
@@ -201,7 +202,7 @@ class SettingsDialog:
|
||||
def init_ui_elements(self, s_type):
|
||||
is_enigma_profile = s_type is SettingsType.ENIGMA_2
|
||||
self._neutrino_radio_button.set_active(s_type is SettingsType.NEUTRINO_MP)
|
||||
self.update_header_bar()
|
||||
self.update_title()
|
||||
http_active = self._support_http_api_switch.get_active()
|
||||
self._click_mode_zap_button.set_sensitive(is_enigma_profile and http_active)
|
||||
self._lang_combo_box.set_active_id(self._ext_settings.language)
|
||||
@@ -219,12 +220,12 @@ class SettingsDialog:
|
||||
self.on_profile_selected(self._profile_view, False)
|
||||
self._profile_remove_button.set_sensitive(len(self._profile_view.get_model()) > 1)
|
||||
|
||||
def update_header_bar(self):
|
||||
label, sep, st = self._header_bar.get_subtitle().partition(":")
|
||||
def update_title(self):
|
||||
title = "{} [{}]"
|
||||
if self._s_type is SettingsType.ENIGMA_2:
|
||||
self._header_bar.set_subtitle("{}: {}".format(label, self._enigma_radio_button.get_label()))
|
||||
self._dialog.set_title(title.format(get_message("Options"), self._enigma_radio_button.get_label()))
|
||||
elif self._s_type is SettingsType.NEUTRINO_MP:
|
||||
self._header_bar.set_subtitle("{}: {}".format(label, self._neutrino_radio_button.get_label()))
|
||||
self._dialog.set_title(title.format(get_message("Options"), self._neutrino_radio_button.get_label()))
|
||||
|
||||
def show(self):
|
||||
self._dialog.run()
|
||||
@@ -355,7 +356,6 @@ class SettingsDialog:
|
||||
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()
|
||||
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()
|
||||
@@ -623,7 +623,6 @@ class SettingsDialog:
|
||||
|
||||
@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)
|
||||
@@ -794,7 +793,6 @@ class SettingsDialog:
|
||||
|
||||
@run_idle
|
||||
def init_themes(self):
|
||||
self._dark_mode_switch.set_active(self._ext_settings.dark_mode)
|
||||
t_support = self._ext_settings.is_themes_support
|
||||
self._themes_support_switch.set_active(t_support)
|
||||
if t_support:
|
||||
|
||||
@@ -1,19 +1,10 @@
|
||||
#digit-entry {
|
||||
border-color: Red;
|
||||
border-width: 0.15em;
|
||||
}
|
||||
|
||||
#status-bar-button {
|
||||
padding-top: 1px;
|
||||
padding-bottom: 1px;
|
||||
padding-left: 3px;
|
||||
padding-right: 3px;
|
||||
margin: 1px;
|
||||
}
|
||||
|
||||
paned > separator {
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
background-size: 2px 24px;
|
||||
margin: 0.1em;
|
||||
}
|
||||
|
||||
.red-button {
|
||||
@@ -36,14 +27,27 @@ paned > separator {
|
||||
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;
|
||||
}
|
||||
|
||||
250
app/ui/telnet.glade
Executable file
250
app/ui/telnet.glade
Executable file
@@ -0,0 +1,250 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- Generated with glade 3.22.1
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2018-2020 Dmitriy Yefremov
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
Author: Dmitriy Yefremov
|
||||
|
||||
-->
|
||||
<interface domain="demon-editor">
|
||||
<requires lib="gtk+" version="3.16"/>
|
||||
<!-- interface-license-type mit -->
|
||||
<!-- interface-name DemonEditor -->
|
||||
<!-- interface-copyright 2018-2020 Dmitriy Yefremov -->
|
||||
<!-- interface-authors Dmitriy Yefremov -->
|
||||
<object class="GtkTextTagTable" id="tag_table">
|
||||
<child type="tag">
|
||||
<object class="GtkTextTag" id="end_tag">
|
||||
<property name="font">Normal</property>
|
||||
<property name="editable">False</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<object class="GtkTextBuffer" id="text_buffer">
|
||||
<property name="tag_table">tag_table</property>
|
||||
</object>
|
||||
<object class="GtkWindow" id="dialog_window">
|
||||
<property name="can_focus">False</property>
|
||||
<property name="title" translatable="yes">DemonEditor [Telnet client]</property>
|
||||
<property name="destroy_with_parent">True</property>
|
||||
<property name="icon_name">terminal</property>
|
||||
<property name="skip_taskbar_hint">True</property>
|
||||
<property name="skip_pager_hint">True</property>
|
||||
<signal name="delete-event" handler="on_close" swapped="no"/>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkBox" id="main_box">
|
||||
<property name="width_request">560</property>
|
||||
<property name="height_request">320</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<property name="spacing">2</property>
|
||||
<child>
|
||||
<object class="GtkScrolledWindow" id="telnet_scrolled_window">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="shadow_type">in</property>
|
||||
<child>
|
||||
<object class="GtkTextView" id="text_view">
|
||||
<property name="name">textview-large</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="wrap_mode">char</property>
|
||||
<property name="left_margin">5</property>
|
||||
<property name="right_margin">5</property>
|
||||
<property name="buffer">text_buffer</property>
|
||||
<property name="overwrite">True</property>
|
||||
<property name="input_hints">GTK_INPUT_HINT_WORD_COMPLETION | GTK_INPUT_HINT_NONE</property>
|
||||
<property name="monospace">True</property>
|
||||
<signal name="key-press-event" handler="on_view_key_press" swapped="no"/>
|
||||
<signal name="realize" handler="on_text_view_realize" swapped="no"/>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkBox" id="commands_entry">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_left">5</property>
|
||||
<property name="margin_right">5</property>
|
||||
<property name="margin_bottom">2</property>
|
||||
<property name="spacing">2</property>
|
||||
<child>
|
||||
<object class="GtkComboBoxText" id="profile_combo_box">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="active">0</property>
|
||||
<property name="has_frame">False</property>
|
||||
<signal name="changed" handler="on_profile_changed" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="connect_button">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</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">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="disconnect_button">
|
||||
<property name="can_focus">True</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">4</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child type="center">
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="clear_button">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="tooltip_text" translatable="yes">Clear</property>
|
||||
<property name="halign">center</property>
|
||||
<property name="valign">center</property>
|
||||
<signal name="clicked" handler="on_clear" swapped="no"/>
|
||||
<child>
|
||||
<object class="GtkImage" id="clear_button_image">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="stock">gtk-clear</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="pack_type">end</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkInfoBar" id="info_bar">
|
||||
<property name="app_paintable">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="spacing">2</property>
|
||||
<property name="show_close_button">True</property>
|
||||
<signal name="response" handler="on_info_bar_close" swapped="no"/>
|
||||
<child internal-child="action_area">
|
||||
<object class="GtkButtonBox">
|
||||
<property name="can_focus">False</property>
|
||||
<property name="layout_style">end</property>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child internal-child="content_area">
|
||||
<object class="GtkBox">
|
||||
<property name="can_focus">False</property>
|
||||
<child>
|
||||
<object class="GtkLabel" id="info_bar_message_label">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label">Info</property>
|
||||
<property name="justify">center</property>
|
||||
<property name="wrap">True</property>
|
||||
<property name="wrap_mode">word-char</property>
|
||||
<property name="lines">2</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</interface>
|
||||
256
app/ui/telnet.py
Executable file
256
app/ui/telnet.py
Executable file
@@ -0,0 +1,256 @@
|
||||
import re
|
||||
import selectors
|
||||
import socket
|
||||
from collections import deque
|
||||
from telnetlib import Telnet
|
||||
|
||||
from gi.repository import GLib
|
||||
|
||||
from app.commons import run_task, run_idle, log
|
||||
from app.settings import Settings
|
||||
from app.ui.uicommons import Gtk, Gdk, UI_RESOURCES_PATH, KeyboardKey, MOD_MASK
|
||||
|
||||
|
||||
class ExtTelnet(Telnet):
|
||||
|
||||
def __init__(self, output_callback, **kwargs):
|
||||
super().__init__(**kwargs)
|
||||
self._output_callback = output_callback
|
||||
|
||||
def interact(self):
|
||||
"""Interaction function, emulates a very dumb telnet client."""
|
||||
with selectors.DefaultSelector() as selector:
|
||||
selector.register(self, selectors.EVENT_READ)
|
||||
|
||||
while True:
|
||||
for key, events in selector.select():
|
||||
if key.fileobj is self:
|
||||
try:
|
||||
text = self.read_very_eager()
|
||||
except EOFError as e:
|
||||
msg = "\n*** Connection closed by remote host ***\n"
|
||||
self._output_callback(msg)
|
||||
log(msg)
|
||||
raise e
|
||||
else:
|
||||
if text:
|
||||
self._output_callback(text)
|
||||
|
||||
|
||||
class TelnetDialog:
|
||||
""" Dialog of very simple telnet client. """
|
||||
_COLOR_PATTERN = re.compile("\x1b.*?m") # Color info
|
||||
_ERASING_PATTERN = re.compile("\x1b.*?K") # Erase to right
|
||||
_APP_MODE_PATTERN = re.compile("\x1b.*?(1h)|(1l)") # h - on, l - off
|
||||
_ALL_PATTERN = re.compile(r'(\x1b\[|\x9b)[0-?]*[@-~]')
|
||||
_NOT_SUPPORTED = {"mc", "mcedit", "vi", "nano"}
|
||||
|
||||
def __init__(self, transient, settings):
|
||||
self._handlers = {"on_profile_changed": self.on_profile_changed,
|
||||
"on_clear": self.on_clear,
|
||||
"on_text_view_realize": self.on_text_view_realize,
|
||||
"on_view_key_press": self.on_view_key_press,
|
||||
"on_info_bar_close": self.on_info_bar_close,
|
||||
"on_connect": self.on_connect,
|
||||
"on_disconnect": self.on_disconnect,
|
||||
"on_close": self.on_close}
|
||||
|
||||
builder = Gtk.Builder()
|
||||
builder.add_from_file(UI_RESOURCES_PATH + "telnet.glade")
|
||||
builder.connect_signals(self._handlers)
|
||||
self._dialog_window = builder.get_object("dialog_window")
|
||||
self._dialog_window.set_transient_for(transient)
|
||||
self._profile_combo_box = builder.get_object("profile_combo_box")
|
||||
self._info_bar = builder.get_object("info_bar")
|
||||
self._info_message_label = builder.get_object("info_bar_message_label")
|
||||
self._text_view = builder.get_object("text_view")
|
||||
self._buf = builder.get_object("text_buffer")
|
||||
self._end_tag = builder.get_object("end_tag")
|
||||
self._connect_button = builder.get_object("connect_button")
|
||||
self._connect_button.bind_property("visible", builder.get_object("disconnect_button"), "visible", 4)
|
||||
provider = Gtk.CssProvider()
|
||||
provider.load_from_path(UI_RESOURCES_PATH + "style.css")
|
||||
builder.get_object("main_box").get_style_context().add_provider(provider, Gtk.STYLE_PROVIDER_PRIORITY_USER)
|
||||
|
||||
window_size = settings.get("telnet_dialog_window_size")
|
||||
if window_size:
|
||||
self._dialog_window.resize(*window_size)
|
||||
|
||||
self._ext_settings = settings
|
||||
self._settings = Settings(settings.settings)
|
||||
self._tn = None
|
||||
self._app_mode = False
|
||||
self._commands = deque(maxlen=10)
|
||||
|
||||
def show(self):
|
||||
self._dialog_window.show()
|
||||
|
||||
def on_close(self, window, event):
|
||||
""" Performs shutdown tasks """
|
||||
self._ext_settings.add("telnet_dialog_window_size", window.get_size())
|
||||
self.on_disconnect()
|
||||
|
||||
def on_info_bar_close(self, bar=None, resp=None):
|
||||
self._info_bar.set_visible(False)
|
||||
|
||||
@run_idle
|
||||
def show_info_message(self, text, message_type):
|
||||
self._info_bar.set_visible(True)
|
||||
self._info_bar.set_message_type(message_type)
|
||||
self._info_message_label.set_text(text)
|
||||
|
||||
def on_text_view_realize(self, view):
|
||||
self.init_profiles()
|
||||
self.on_connect()
|
||||
|
||||
@run_idle
|
||||
def init_profiles(self):
|
||||
for p in self._settings.profiles:
|
||||
self._profile_combo_box.append(p, p)
|
||||
self._profile_combo_box.set_active_id(self._settings.current_profile)
|
||||
|
||||
@run_task
|
||||
def on_connect(self, item=None):
|
||||
try:
|
||||
GLib.idle_add(self._connect_button.set_visible, False)
|
||||
GLib.idle_add(self.on_info_bar_close)
|
||||
user, password = self._settings.user, self._settings.password
|
||||
timeout = self._settings.telnet_timeout
|
||||
|
||||
self._tn = ExtTelnet(self.append_output,
|
||||
host=self._settings.host,
|
||||
port=self._settings.telnet_port,
|
||||
timeout=timeout)
|
||||
|
||||
if user != "":
|
||||
self._tn.read_until(b"login: ")
|
||||
self._tn.write(user.encode("utf-8") + b"\n")
|
||||
if password != "":
|
||||
self._tn.read_until(b"Password: ")
|
||||
self._tn.write(password.encode("utf-8") + b"\n")
|
||||
|
||||
self._tn.interact()
|
||||
except (OSError, EOFError, socket.timeout, ConnectionRefusedError) as e:
|
||||
log("{}: {}".format(self.__class__.__name__, e))
|
||||
self.show_info_message(str(e), Gtk.MessageType.ERROR)
|
||||
finally:
|
||||
GLib.idle_add(self._connect_button.set_visible, True)
|
||||
|
||||
@run_task
|
||||
def on_disconnect(self, item=None):
|
||||
if self._tn:
|
||||
GLib.idle_add(self._connect_button.set_visible, True)
|
||||
self._tn.close()
|
||||
|
||||
def on_profile_changed(self, button):
|
||||
self._settings.current_profile = button.get_active_id()
|
||||
|
||||
def on_command_done(self, entry):
|
||||
command = entry.get_text()
|
||||
entry.set_text("")
|
||||
if command and self._tn:
|
||||
self._tn.write(command.encode("ascii") + b"\r")
|
||||
|
||||
def on_clear(self, item=None):
|
||||
self._buf.delete(self._buf.get_start_iter(), self._buf.get_end_iter())
|
||||
|
||||
def on_view_key_press(self, view, event):
|
||||
""" Handling keystrokes on press """
|
||||
if event.keyval == Gdk.KEY_Return:
|
||||
self.do_command()
|
||||
return True
|
||||
|
||||
key_code = event.hardware_keycode
|
||||
if not KeyboardKey.value_exist(key_code):
|
||||
return
|
||||
|
||||
key = KeyboardKey(key_code)
|
||||
ctrl = event.state & MOD_MASK
|
||||
if ctrl and key is KeyboardKey.C:
|
||||
if self._tn and self._tn.sock:
|
||||
self._tn.write(b"\x03") # interrupt
|
||||
|
||||
# last commands navigation
|
||||
if key is KeyboardKey.UP:
|
||||
self.delete_last_command()
|
||||
if self._commands:
|
||||
cmd = self._commands.pop()
|
||||
self._commands.appendleft(cmd)
|
||||
self._buf.insert_at_cursor(cmd, -1)
|
||||
return True
|
||||
elif key is KeyboardKey.DOWN:
|
||||
self.delete_last_command()
|
||||
if self._commands:
|
||||
cmd = self._commands.popleft()
|
||||
self._commands.append(cmd)
|
||||
self._buf.insert_at_cursor(cmd, -1)
|
||||
return True
|
||||
|
||||
def delete_last_command(self):
|
||||
end = self._buf.get_end_iter()
|
||||
if end.ends_tag(self._end_tag):
|
||||
return
|
||||
|
||||
if end.backward_to_tag_toggle(self._end_tag):
|
||||
self._buf.delete(self._buf.get_end_iter(), end)
|
||||
|
||||
def do_command(self):
|
||||
count = self._buf.get_line_count()
|
||||
begin = self._buf.get_iter_at_line(count)
|
||||
end = self._buf.get_end_iter()
|
||||
command = []
|
||||
|
||||
while end.backward_to_tag_toggle(self._end_tag):
|
||||
command.append(self._buf.get_text(end, begin, False))
|
||||
break
|
||||
else: # if buf is empty
|
||||
command.append(self._buf.get_text(begin, end, False))
|
||||
|
||||
# to preventing duplication of the command in the buf
|
||||
self._buf.delete(end, begin)
|
||||
|
||||
if command and self._tn.sock:
|
||||
cmd = command[0]
|
||||
if cmd in self._NOT_SUPPORTED:
|
||||
self.show_info_message("'{}' is not supported by this client.".format(cmd), Gtk.MessageType.ERROR)
|
||||
else:
|
||||
self._tn.write(cmd.encode("ascii") + b"\r")
|
||||
self._commands.append(cmd)
|
||||
|
||||
@run_idle
|
||||
def append_output(self, txt):
|
||||
t = txt.decode("ascii", errors="ignore")
|
||||
|
||||
ap = re.search(self._APP_MODE_PATTERN, t)
|
||||
if ap:
|
||||
on, of = ap.group(1), ap.group(2)
|
||||
if on:
|
||||
self._app_mode = True
|
||||
elif of:
|
||||
self._app_mode = False
|
||||
self.on_clear()
|
||||
|
||||
t = re.sub(self._ALL_PATTERN, "", t) # removing [replacing] ascii escape sequences
|
||||
|
||||
if self._app_mode:
|
||||
start, end = self._buf.get_start_iter(), self._buf.get_end_iter()
|
||||
count = self._buf.get_line_count()
|
||||
new_lines = t.split("\r\n")
|
||||
ext_lines = self._buf.get_text(start, end, True).split("\r\n")
|
||||
if count < len(new_lines):
|
||||
self._buf.set_text(re.sub(self._ERASING_PATTERN, "", t))
|
||||
else:
|
||||
for i, line in enumerate(new_lines):
|
||||
if line:
|
||||
ext_lines[i] = re.sub(self._ERASING_PATTERN, "", line)
|
||||
self._buf.set_text("\r\n".join(ext_lines))
|
||||
else:
|
||||
self._buf.insert_at_cursor(t, -1)
|
||||
|
||||
insert = self._buf.get_insert()
|
||||
self._text_view.scroll_to_mark(insert, 0.0, True, 0.0, 1.0)
|
||||
self._buf.apply_tag(self._end_tag, self._buf.get_start_iter(), self._buf.get_end_iter())
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
pass
|
||||
@@ -5,15 +5,15 @@ 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.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
|
||||
|
||||
|
||||
class LinksTransmitter:
|
||||
""" The main class for the "send to" function.
|
||||
""" The main media bar class for the "send to" function..
|
||||
|
||||
It used for direct playback of media links by the enigma2 media player.
|
||||
"""
|
||||
@@ -35,7 +35,9 @@ class LinksTransmitter:
|
||||
self._app_window = app_window
|
||||
self._is_status_icon = True
|
||||
|
||||
builder = get_builder(UI_RESOURCES_PATH + "transmitter.glade", handlers)
|
||||
builder = Gtk.Builder()
|
||||
builder.add_from_file(UI_RESOURCES_PATH + "transmitter.glade")
|
||||
builder.connect_signals(handlers)
|
||||
|
||||
self._main_window = builder.get_object("main_window")
|
||||
self._url_entry = builder.get_object("url_entry")
|
||||
@@ -46,16 +48,19 @@ class LinksTransmitter:
|
||||
self._status_passive = None
|
||||
self._yt = YouTube.get_instance(settings)
|
||||
|
||||
try:
|
||||
gi.require_version("AppIndicator3", "0.1")
|
||||
from gi.repository import AppIndicator3
|
||||
except (ImportError, ValueError) as e:
|
||||
log("{}: Load library error: {}".format(__class__.__name__, e))
|
||||
if IS_DARWIN:
|
||||
self._tray = builder.get_object("status_icon")
|
||||
else:
|
||||
self._is_status_icon = False
|
||||
self._status_active = AppIndicator3.IndicatorStatus.ACTIVE
|
||||
self._status_passive = AppIndicator3.IndicatorStatus.PASSIVE
|
||||
try:
|
||||
gi.require_version("AppIndicator3", "0.1")
|
||||
from gi.repository import AppIndicator3
|
||||
except (ImportError, ValueError) as e:
|
||||
log("{}: Load library error: {}".format(__class__.__name__, e))
|
||||
self._tray = builder.get_object("status_icon")
|
||||
else:
|
||||
self._is_status_icon = False
|
||||
self._status_active = AppIndicator3.IndicatorStatus.ACTIVE
|
||||
self._status_passive = AppIndicator3.IndicatorStatus.PASSIVE
|
||||
|
||||
category = AppIndicator3.IndicatorCategory.APPLICATION_STATUS
|
||||
path = Path(UI_RESOURCES_PATH + "/icons/hicolor/scalable/apps/demon-editor.svg")
|
||||
|
||||
@@ -1,23 +1,21 @@
|
||||
import locale
|
||||
import os
|
||||
from enum import Enum, IntEnum
|
||||
from functools import lru_cache
|
||||
from app.settings import Settings, SettingsException, IS_DARWIN
|
||||
|
||||
import gi
|
||||
|
||||
gi.require_version("Gtk", "3.0")
|
||||
gi.require_version("Gdk", "3.0")
|
||||
gi.require_version("Notify", "0.7")
|
||||
from gi.repository import Gtk, Gdk, Notify
|
||||
from gi.repository import Gtk, Gdk, GLib
|
||||
|
||||
from app.settings import Settings, SettingsException, IS_DARWIN
|
||||
|
||||
# Init notify
|
||||
Notify.init("DemonEditor")
|
||||
# Setting mod mask for the keyboard depending on the platform.
|
||||
# Setting mod mask for keyboard depending on platform
|
||||
MOD_MASK = Gdk.ModifierType.MOD2_MASK if IS_DARWIN else Gdk.ModifierType.CONTROL_MASK
|
||||
# Path to *.glade files.
|
||||
UI_RESOURCES_PATH = "app/ui/" if os.path.exists("app/ui/") else "/usr/share/demoneditor/app/ui/"
|
||||
# Path to *.glade files
|
||||
UI_RESOURCES_PATH = "app/ui/" if os.path.exists("app/ui/") else "ui/"
|
||||
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"
|
||||
@@ -29,31 +27,61 @@ except SettingsException:
|
||||
pass
|
||||
else:
|
||||
os.environ["LANGUAGE"] = settings.language
|
||||
if UI_RESOURCES_PATH == "app/ui/":
|
||||
locale.bindtextdomain(TEXT_DOMAIN, UI_RESOURCES_PATH + "lang")
|
||||
|
||||
st = Gtk.Settings().get_default()
|
||||
APP_FONT = st.get_property("gtk-font-name")
|
||||
if not settings.list_font:
|
||||
settings.list_font = APP_FONT
|
||||
st.set_property("gtk-application-prefer-dark-theme", settings.dark_mode)
|
||||
|
||||
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(UI_RESOURCES_PATH + "icons")
|
||||
theme.append_search_path(GTK_PATH + "/share/icons" if GTK_PATH else UI_RESOURCES_PATH + "icons")
|
||||
|
||||
_IMAGE_MISSING = theme.load_icon("image-missing", 16, 0) if theme.lookup_icon("image-missing", 16, 0) else None
|
||||
CODED_ICON = theme.load_icon("emblem-readonly", 16, 0) if theme.lookup_icon(
|
||||
"emblem-readonly", 16, 0) else _IMAGE_MISSING
|
||||
LOCKED_ICON = theme.load_icon("changes-prevent-symbolic", 16, 0) if theme.lookup_icon(
|
||||
"system-lock-screen", 16, 0) else _IMAGE_MISSING
|
||||
HIDE_ICON = theme.load_icon("go-jump", 16, 0) if theme.lookup_icon("go-jump", 16, 0) else _IMAGE_MISSING
|
||||
TV_ICON = theme.load_icon("tv-symbolic", 16, 0) if theme.lookup_icon("tv-symbolic", 16, 0) else _IMAGE_MISSING
|
||||
IPTV_ICON = theme.load_icon("emblem-shared", 16, 0) if theme.lookup_icon("emblem-shared", 16, 0) else None
|
||||
EPG_ICON = theme.load_icon("gtk-index", 16, 0) if theme.lookup_icon("gtk-index", 16, 0) else None
|
||||
DEFAULT_ICON = theme.load_icon("emblem-default", 16, 0) if theme.lookup_icon("emblem-default", 16, 0) else None
|
||||
|
||||
def get_theme_icon(icon_theme, name, size):
|
||||
try:
|
||||
return icon_theme.load_icon(name, size, 0)
|
||||
except GLib.Error:
|
||||
pass
|
||||
|
||||
|
||||
_IMAGE_MISSING = get_theme_icon(theme, "image-missing", 16)
|
||||
CODED_ICON = get_theme_icon(theme, "emblem-readonly", 16) or _IMAGE_MISSING
|
||||
LOCKED_ICON = get_theme_icon(theme, "changes-prevent-symbolic", 16) or _IMAGE_MISSING
|
||||
HIDE_ICON = get_theme_icon(theme, "go-jump", 16) or _IMAGE_MISSING
|
||||
TV_ICON = get_theme_icon(theme, "tv-symbolic", 16) or _IMAGE_MISSING
|
||||
IPTV_ICON = get_theme_icon(theme, "emblem-shared", 16)
|
||||
EPG_ICON = get_theme_icon(theme, "gtk-index", 16)
|
||||
DEFAULT_ICON = get_theme_icon(theme, "emblem-default", 16)
|
||||
|
||||
|
||||
@lru_cache(maxsize=1)
|
||||
@@ -70,11 +98,12 @@ def get_yt_icon(icon_name, size=24):
|
||||
import glob
|
||||
|
||||
for theme_name in map(os.path.basename, filter(os.path.isdir, glob.glob("/usr/share/icons/*"))):
|
||||
n_theme.set_custom_theme(theme_name)
|
||||
theme.set_custom_theme(theme_name)
|
||||
if n_theme.has_icon(icon_name):
|
||||
return n_theme.load_icon(icon_name, size, 0)
|
||||
|
||||
return default_theme.load_icon("info", size, 0)
|
||||
if default_theme.lookup_icon(Gtk.STOCK_APPLY, size, 0):
|
||||
return default_theme.load_icon(Gtk.STOCK_APPLY, size, 0)
|
||||
|
||||
|
||||
def show_notification(message, timeout=10000, urgency=1):
|
||||
@@ -84,41 +113,47 @@ def show_notification(message, timeout=10000, urgency=1):
|
||||
@param timeout: milliseconds
|
||||
@param urgency: 0 - low, 1 - normal, 2 - critical
|
||||
"""
|
||||
notify = Notify.Notification.new("DemonEditor", message, "demon-editor")
|
||||
notify.set_urgency(urgency)
|
||||
notify.set_timeout(timeout)
|
||||
notify.show()
|
||||
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()
|
||||
|
||||
|
||||
class KeyboardKey(Enum):
|
||||
""" The raw(hardware) codes of the keyboard keys. """
|
||||
E = 26
|
||||
R = 27
|
||||
T = 28
|
||||
P = 33
|
||||
S = 39
|
||||
F = 41
|
||||
X = 53
|
||||
C = 54
|
||||
V = 55
|
||||
W = 25
|
||||
Z = 52
|
||||
INSERT = 118
|
||||
HOME = 110
|
||||
END = 115
|
||||
UP = 111
|
||||
DOWN = 116
|
||||
PAGE_UP = 112
|
||||
PAGE_DOWN = 117
|
||||
LEFT = 113
|
||||
RIGHT = 114
|
||||
F2 = 68
|
||||
F7 = 73
|
||||
SPACE = 65
|
||||
DELETE = 119
|
||||
BACK_SPACE = 22
|
||||
CTRL_L = 37
|
||||
CTRL_R = 105
|
||||
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
|
||||
# Laptop codes
|
||||
HOME_KP = 79
|
||||
END_KP = 87
|
||||
|
||||
18
build-deb.sh
18
build-deb.sh
@@ -1,18 +0,0 @@
|
||||
#!/bin/bash
|
||||
VER="1.0.9_Beta"
|
||||
B_PATH="dist/DemonEditor"
|
||||
DEB_PATH="$B_PATH/usr/share/demoneditor"
|
||||
|
||||
mkdir -p $B_PATH
|
||||
cp -TRv deb $B_PATH
|
||||
rsync --exclude=app/ui/lang --exclude=app/ui/icons -arv app $DEB_PATH
|
||||
|
||||
cd dist
|
||||
fakeroot dpkg-deb --build DemonEditor
|
||||
mv DemonEditor.deb DemonEditor_$VER.deb
|
||||
|
||||
rm -R DemonEditor
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,62 +0,0 @@
|
||||
demon-editor for Debian
|
||||
----------------------
|
||||
DemonEditor
|
||||
Enigma2 channel and satellites list editor for GNU/Linux.
|
||||
|
||||
Experimental support of Neutrino-MP or others on the same basis (BPanther, etc).
|
||||
Focused on the convenience of working in lists from the keyboard. The mouse is also fully supported (Drag and Drop etc).
|
||||
|
||||
Main features of the program:
|
||||
Editing bouquets, channels, satellites.
|
||||
Import function.
|
||||
Backup function.
|
||||
Support of picons.
|
||||
Importing services, downloading picons and updating satellites from the Web.
|
||||
Extended support of IPTV.
|
||||
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).
|
||||
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:
|
||||
Ctrl + Insert - copies the selected channels from the main list to the the bouquet beginning or inserts (creates) a new bouquet.
|
||||
Ctrl + BackSpace - copies the selected channels from the main list to the bouquet end.
|
||||
Ctrl + X - only in bouquet list. Ctrl + C - only in services list.
|
||||
Clipboard is "rubber". There is an accumulation before the insertion!
|
||||
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 + F - show/hide search bar.
|
||||
Ctrl + Shift + F - show/hide filter bar.
|
||||
|
||||
For multiple selection with the mouse, press and hold the Ctrl key!
|
||||
|
||||
Minimum requirements:
|
||||
Python >= 3.5.2, GTK+ >= 3.22, python3-gi, python3-gi-cairo, python3-requests.
|
||||
|
||||
Important:
|
||||
Terrestrial(DVB-T/T2) and cable(DVB-C) channels are only supported for Enigma2!
|
||||
Main supported *lamedb* format is version **4**. Versions **3** and **5** has only **experimental** support!
|
||||
For version **3** is only read mode available. When saving, version **4** format is used instead!
|
||||
|
||||
When using the multiple import feature, from *lamedb* will be taken data **only for channels that are in the
|
||||
selected bouquets!** 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.
|
||||
|
||||
For streams playback, this app supports VLC, MPV and GStreamer.
|
||||
Depending on your distro, you may need to install additional packages and libraries.
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
Package: demon-editor
|
||||
Version: 1.0.9-Beta
|
||||
Section: utils
|
||||
Priority: optional
|
||||
Architecture: all
|
||||
Essential: no
|
||||
Depends: python3 (>= 3.5),
|
||||
python3-requests,
|
||||
python3-gi,
|
||||
python3-gi-cairo
|
||||
Recommends: libmpv1,
|
||||
python3-chardet
|
||||
Maintainer: Dmitriy Yefremov <dmitry.v.yefremov@gmail.com>
|
||||
Description: Enigma2 channel and satellite list editor
|
||||
@@ -1,26 +0,0 @@
|
||||
Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
|
||||
Upstream-Contact: Dmitriy Yefremov <dmitry.v.yefremov@gmail.com>
|
||||
Source: https://github.com/DYefremov/DemonEditor
|
||||
|
||||
Files: *
|
||||
MIT License
|
||||
|
||||
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.
|
||||
@@ -1 +0,0 @@
|
||||
README.source
|
||||
@@ -1,2 +0,0 @@
|
||||
#!/bin/bash
|
||||
python3 /usr/share/demoneditor/start.py $1
|
||||
@@ -1,13 +0,0 @@
|
||||
[Desktop Entry]
|
||||
Version=1.0
|
||||
Name=DemonEditor
|
||||
Comment=Channel and satellite list editor for Enigma2
|
||||
Comment[ru]=Редактор списка каналов и спутников для Enigma2
|
||||
Comment[be]=Рэдактар спіса каналаў і спадарожнікаў для Enigma2
|
||||
Comment[de]=Programm- und Satellitenlisten-Editor für Enigma2
|
||||
Icon=demon-editor
|
||||
Exec=/usr/bin/demon-editor
|
||||
Terminal=false
|
||||
Type=Application
|
||||
Categories=Utility;Application;
|
||||
StartupNotify=false
|
||||
@@ -1,4 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
from app.ui.main_app_window import start_app
|
||||
|
||||
start_app()
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 4.2 KiB |
@@ -1,634 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
id="svg1541"
|
||||
version="1.1"
|
||||
viewBox="0 0 16.743339 16.72816"
|
||||
height="63.224556"
|
||||
width="63.281971"
|
||||
sodipodi:docname="demon-editor.svg"
|
||||
inkscape:version="0.92.4 (unknown)">
|
||||
<sodipodi:namedview
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="1366"
|
||||
inkscape:window-height="716"
|
||||
id="namedview127"
|
||||
showgrid="true"
|
||||
fit-margin-left="0"
|
||||
inkscape:zoom="6.1714295"
|
||||
inkscape:cx="40.088627"
|
||||
inkscape:cy="31.742631"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg1541"
|
||||
fit-margin-top="0"
|
||||
fit-margin-right="0"
|
||||
fit-margin-bottom="0">
|
||||
<inkscape:grid
|
||||
type="xygrid"
|
||||
id="grid128"
|
||||
originx="-10.604603"
|
||||
originy="-1.1727724" />
|
||||
</sodipodi:namedview>
|
||||
<title
|
||||
id="title1088">DeamonEditor Icons</title>
|
||||
<defs
|
||||
id="defs1535">
|
||||
<linearGradient
|
||||
id="linearGradient2198">
|
||||
<stop
|
||||
id="stop2194"
|
||||
style="stop-color:#ffb320;stop-opacity:1"
|
||||
offset="0" />
|
||||
<stop
|
||||
id="stop2196"
|
||||
style="stop-color:#e7ff00;stop-opacity:1"
|
||||
offset="1" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient2192">
|
||||
<stop
|
||||
offset="0"
|
||||
style="stop-color:#ffb320;stop-opacity:1"
|
||||
id="stop2188" />
|
||||
<stop
|
||||
offset="1"
|
||||
style="stop-color:#b3c54c;stop-opacity:1"
|
||||
id="stop2190" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient3700-8">
|
||||
<stop
|
||||
offset="0"
|
||||
style="stop-color:#2e4f84;stop-opacity:1"
|
||||
id="stop2183" />
|
||||
<stop
|
||||
offset="1"
|
||||
style="stop-color:#4c77c5;stop-opacity:1"
|
||||
id="stop2185" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
x1="0"
|
||||
y1="0"
|
||||
x2="1"
|
||||
y2="0"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(13.526835,20.525875,20.525875,-13.526835,19.18986,1021.0543)"
|
||||
spreadMethod="pad"
|
||||
id="linearGradient1844">
|
||||
<stop
|
||||
style="stop-opacity:1;stop-color:#29282b"
|
||||
offset="0"
|
||||
id="stop1826" />
|
||||
<stop
|
||||
id="stop1828"
|
||||
offset="0.13293758"
|
||||
style="stop-color:#b5bdcf;stop-opacity:1" />
|
||||
<stop
|
||||
style="stop-opacity:1;stop-color:#92979f"
|
||||
offset="0.21261224"
|
||||
id="stop1832" />
|
||||
<stop
|
||||
style="stop-opacity:1;stop-color:#737881"
|
||||
offset="0.29780689"
|
||||
id="stop1834" />
|
||||
<stop
|
||||
style="stop-opacity:1;stop-color:#70757e"
|
||||
offset="0.29780689"
|
||||
id="stop1836" />
|
||||
<stop
|
||||
style="stop-opacity:1;stop-color:#e4e6e8"
|
||||
offset="0.45395693"
|
||||
id="stop1838" />
|
||||
<stop
|
||||
style="stop-opacity:1;stop-color:#696c76"
|
||||
offset="0.71871042"
|
||||
id="stop1840" />
|
||||
<stop
|
||||
style="stop-opacity:1;stop-color:#29282b"
|
||||
offset="1"
|
||||
id="stop1842" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient1754"
|
||||
spreadMethod="pad"
|
||||
gradientTransform="matrix(13.526835,20.525875,20.525875,-13.526835,19.18986,1021.0543)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
y2="0"
|
||||
x2="1"
|
||||
y1="0"
|
||||
x1="0">
|
||||
<stop
|
||||
id="stop1736"
|
||||
offset="0"
|
||||
style="stop-opacity:1;stop-color:#29282b" />
|
||||
<stop
|
||||
style="stop-color:#b5bdcf;stop-opacity:1"
|
||||
offset="0.02582242"
|
||||
id="stop1738" />
|
||||
<stop
|
||||
id="stop1740"
|
||||
offset="0.14669065"
|
||||
style="stop-opacity:1;stop-color:#868c95" />
|
||||
<stop
|
||||
id="stop1742"
|
||||
offset="0.21261224"
|
||||
style="stop-opacity:1;stop-color:#92979f" />
|
||||
<stop
|
||||
id="stop1744"
|
||||
offset="0.29780689"
|
||||
style="stop-opacity:1;stop-color:#737881" />
|
||||
<stop
|
||||
id="stop1746"
|
||||
offset="0.29780689"
|
||||
style="stop-opacity:1;stop-color:#70757e" />
|
||||
<stop
|
||||
id="stop1748"
|
||||
offset="0.45395693"
|
||||
style="stop-opacity:1;stop-color:#e4e6e8" />
|
||||
<stop
|
||||
id="stop1750"
|
||||
offset="0.71871042"
|
||||
style="stop-opacity:1;stop-color:#ffffff" />
|
||||
<stop
|
||||
id="stop1752"
|
||||
offset="1"
|
||||
style="stop-opacity:1;stop-color:#1d191a" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
x1="0"
|
||||
y1="0"
|
||||
x2="1"
|
||||
y2="0"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(13.526835,20.525875,20.525875,-13.526835,19.18986,1021.0543)"
|
||||
spreadMethod="pad"
|
||||
id="linearGradient1606">
|
||||
<stop
|
||||
style="stop-opacity:1;stop-color:#29282b"
|
||||
offset="0"
|
||||
id="stop1590" />
|
||||
<stop
|
||||
id="stop1608"
|
||||
offset="0.03065561"
|
||||
style="stop-color:#b5bdcf;stop-opacity:1" />
|
||||
<stop
|
||||
style="stop-opacity:1;stop-color:#868c95"
|
||||
offset="0.1125849"
|
||||
id="stop1592" />
|
||||
<stop
|
||||
style="stop-opacity:1;stop-color:#92979f"
|
||||
offset="0.13955873"
|
||||
id="stop1594" />
|
||||
<stop
|
||||
style="stop-opacity:1;stop-color:#737881"
|
||||
offset="0.1915853"
|
||||
id="stop1596" />
|
||||
<stop
|
||||
style="stop-opacity:1;stop-color:#70757e"
|
||||
offset="0.25400829"
|
||||
id="stop1598" />
|
||||
<stop
|
||||
style="stop-opacity:1;stop-color:#e4e6e8"
|
||||
offset="0.45395693"
|
||||
id="stop1600" />
|
||||
<stop
|
||||
style="stop-opacity:1;stop-color:#ffffff"
|
||||
offset="0.71871042"
|
||||
id="stop1602" />
|
||||
<stop
|
||||
style="stop-opacity:1;stop-color:#1d191a"
|
||||
offset="1"
|
||||
id="stop1604" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient886-0">
|
||||
<stop
|
||||
style="stop-color:#535353;stop-opacity:1"
|
||||
offset="0"
|
||||
id="stop888" />
|
||||
<stop
|
||||
style="stop-color:#f0f0f0;stop-opacity:1"
|
||||
offset="1"
|
||||
id="stop890" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient1473">
|
||||
<stop
|
||||
id="stop1469"
|
||||
offset="0"
|
||||
style="stop-color:#ffffff;stop-opacity:0" />
|
||||
<stop
|
||||
id="stop1471"
|
||||
offset="1"
|
||||
style="stop-color:#ffffff;stop-opacity:0.96088022" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient2447-35">
|
||||
<stop
|
||||
id="stop2449"
|
||||
offset="0"
|
||||
style="stop-color:#00c62e;stop-opacity:1" />
|
||||
<stop
|
||||
id="stop2451"
|
||||
offset="1"
|
||||
style="stop-color:#136100;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient3795-1">
|
||||
<stop
|
||||
id="stop3797-1"
|
||||
offset="0"
|
||||
style="stop-color:#803400;stop-opacity:1" />
|
||||
<stop
|
||||
id="stop3799-3"
|
||||
offset="1"
|
||||
style="stop-color:#c87137;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient2447-3">
|
||||
<stop
|
||||
style="stop-color:#c60300;stop-opacity:1"
|
||||
offset="0"
|
||||
id="stop2443" />
|
||||
<stop
|
||||
style="stop-color:#c40e00;stop-opacity:1"
|
||||
offset="1"
|
||||
id="stop2445" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient2458">
|
||||
<stop
|
||||
id="stop2454"
|
||||
offset="0"
|
||||
style="stop-color:#c60300;stop-opacity:1" />
|
||||
<stop
|
||||
id="stop2456"
|
||||
offset="1"
|
||||
style="stop-color:#ee6000;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient3519">
|
||||
<stop
|
||||
id="stop3521"
|
||||
offset="0"
|
||||
style="stop-color:#1d2120;stop-opacity:1" />
|
||||
<stop
|
||||
id="stop3523"
|
||||
offset="1"
|
||||
style="stop-color:#545d5d;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient1446">
|
||||
<stop
|
||||
id="stop1442"
|
||||
offset="0"
|
||||
style="stop-color:#ffffff;stop-opacity:1;" />
|
||||
<stop
|
||||
id="stop1444"
|
||||
offset="1"
|
||||
style="stop-color:#ffffff;stop-opacity:0;" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient1547"
|
||||
spreadMethod="pad"
|
||||
gradientTransform="matrix(13.526835,20.525875,20.525875,-13.526835,19.18986,1021.0543)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
y2="0"
|
||||
x2="1"
|
||||
y1="0"
|
||||
x1="0">
|
||||
<stop
|
||||
id="stop1529"
|
||||
offset="0"
|
||||
style="stop-opacity:1;stop-color:#ffffff" />
|
||||
<stop
|
||||
id="stop1531"
|
||||
offset="0.0263736"
|
||||
style="stop-opacity:1;stop-color:#29282b" />
|
||||
<stop
|
||||
id="stop1533"
|
||||
offset="0.263736"
|
||||
style="stop-opacity:1;stop-color:#868c95" />
|
||||
<stop
|
||||
id="stop1535"
|
||||
offset="0.395604"
|
||||
style="stop-opacity:1;stop-color:#92979f" />
|
||||
<stop
|
||||
id="stop1537"
|
||||
offset="0.39560401"
|
||||
style="stop-opacity:1;stop-color:#737881" />
|
||||
<stop
|
||||
id="stop1539"
|
||||
offset="0.42333773"
|
||||
style="stop-opacity:1;stop-color:#70757e" />
|
||||
<stop
|
||||
id="stop1541"
|
||||
offset="0.56268591"
|
||||
style="stop-opacity:1;stop-color:#e4e6e8" />
|
||||
<stop
|
||||
id="stop1543"
|
||||
offset="0.62400264"
|
||||
style="stop-opacity:1;stop-color:#ffffff" />
|
||||
<stop
|
||||
id="stop1545"
|
||||
offset="1"
|
||||
style="stop-opacity:1;stop-color:#1d191a" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient1527"
|
||||
spreadMethod="pad"
|
||||
gradientTransform="matrix(13.526835,20.525875,20.525875,-13.526835,19.18986,1021.0543)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
y2="0"
|
||||
x2="1"
|
||||
y1="0"
|
||||
x1="0">
|
||||
<stop
|
||||
id="stop1509"
|
||||
offset="0"
|
||||
style="stop-opacity:1;stop-color:#ffffff" />
|
||||
<stop
|
||||
id="stop1511"
|
||||
offset="0.0527472"
|
||||
style="stop-opacity:1;stop-color:#29282b" />
|
||||
<stop
|
||||
id="stop1513"
|
||||
offset="0.142147"
|
||||
style="stop-opacity:1;stop-color:#868c95" />
|
||||
<stop
|
||||
id="stop1515"
|
||||
offset="0.19700864"
|
||||
style="stop-opacity:1;stop-color:#92979f" />
|
||||
<stop
|
||||
id="stop1517"
|
||||
offset="0.25031137"
|
||||
style="stop-opacity:1;stop-color:#737881" />
|
||||
<stop
|
||||
id="stop1519"
|
||||
offset="0.3710371"
|
||||
style="stop-opacity:1;stop-color:#70757e" />
|
||||
<stop
|
||||
id="stop1521"
|
||||
offset="0.53961843"
|
||||
style="stop-opacity:1;stop-color:#e4e6e8" />
|
||||
<stop
|
||||
id="stop1523"
|
||||
offset="0.76283824"
|
||||
style="stop-opacity:1;stop-color:#ffffff" />
|
||||
<stop
|
||||
id="stop1525"
|
||||
offset="1"
|
||||
style="stop-opacity:1;stop-color:#1d191a" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
gradientTransform="matrix(0.26458333,0,0,0.26458333,-5.8254634,-78.732754)"
|
||||
y2="1179.7145"
|
||||
x2="66.791626"
|
||||
y1="1188.7661"
|
||||
x1="99.044022"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="linearGradient1485"
|
||||
xlink:href="#linearGradient1473" />
|
||||
<linearGradient
|
||||
gradientTransform="matrix(0.26458333,0,0,0.26458333,-2.2733744,-70.526334)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
y2="1155.8046"
|
||||
x2="67.311417"
|
||||
y1="1161.6112"
|
||||
x1="77.19442"
|
||||
id="linearGradient1448"
|
||||
xlink:href="#linearGradient1446" />
|
||||
<linearGradient
|
||||
gradientTransform="matrix(0.26458333,0,0,0.26458333,-18.198747,-79.859424)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
y2="1186.8096"
|
||||
x2="146.16808"
|
||||
y1="1186.8096"
|
||||
x1="131.86871"
|
||||
id="linearGradient2180"
|
||||
xlink:href="#linearGradient886-0" />
|
||||
<linearGradient
|
||||
gradientTransform="matrix(0.20863982,0,0,0.20863982,-0.63935937,-13.031239)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
y2="1184.73"
|
||||
x2="101.19952"
|
||||
y1="1184.73"
|
||||
x1="83.066002"
|
||||
id="linearGradient2160"
|
||||
xlink:href="#linearGradient886-0" />
|
||||
<linearGradient
|
||||
gradientTransform="matrix(0.26458333,0,0,0.26458333,-6.2370714,-102.12414)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
y2="1267.1335"
|
||||
x2="117.99127"
|
||||
y1="1267.1335"
|
||||
x1="75.853806"
|
||||
id="linearGradient2131"
|
||||
xlink:href="#linearGradient886-0" />
|
||||
<linearGradient
|
||||
id="linearGradient3700-8-3">
|
||||
<stop
|
||||
id="stop3702-1"
|
||||
style="stop-color:#2e4f84;stop-opacity:1"
|
||||
offset="0" />
|
||||
<stop
|
||||
id="stop3704-8"
|
||||
style="stop-color:#4c77c5;stop-opacity:1"
|
||||
offset="1" />
|
||||
</linearGradient>
|
||||
<defs
|
||||
id="defs4922">
|
||||
<filter
|
||||
style="color-interpolation-filters:sRGB"
|
||||
id="Adobe_OpacityMaskFilter"
|
||||
filterUnits="userSpaceOnUse"
|
||||
x="3.7850001"
|
||||
y="4.6750002"
|
||||
width="5.8829999"
|
||||
height="73.013">
|
||||
<feColorMatrix
|
||||
type="matrix"
|
||||
values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 1 0"
|
||||
id="feColorMatrix4925" />
|
||||
</filter>
|
||||
</defs>
|
||||
<mask
|
||||
maskUnits="userSpaceOnUse"
|
||||
x="3.785"
|
||||
y="4.675"
|
||||
width="5.883"
|
||||
height="73.013"
|
||||
id="SVGID_2_">
|
||||
<g
|
||||
style="filter:url(#Adobe_OpacityMaskFilter)"
|
||||
id="g4928">
|
||||
<linearGradient
|
||||
id="SVGID_3_"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="3.7852001"
|
||||
y1="41.181198"
|
||||
x2="9.6680002"
|
||||
y2="41.181198">
|
||||
<stop
|
||||
offset="0"
|
||||
style="stop-color:#FFFFFF"
|
||||
id="stop4931" />
|
||||
<stop
|
||||
offset="0.0029"
|
||||
style="stop-color:#FAFBFB"
|
||||
id="stop4933" />
|
||||
<stop
|
||||
offset="0.0756"
|
||||
style="stop-color:#BBBDBF"
|
||||
id="stop4935" />
|
||||
<stop
|
||||
offset="0.1438"
|
||||
style="stop-color:#898B8E"
|
||||
id="stop4937" />
|
||||
<stop
|
||||
offset="0.2053"
|
||||
style="stop-color:#646567"
|
||||
id="stop4939" />
|
||||
<stop
|
||||
offset="0.259"
|
||||
style="stop-color:#444446"
|
||||
id="stop4941" />
|
||||
<stop
|
||||
offset="0.3028"
|
||||
style="stop-color:#1D1C1D"
|
||||
id="stop4943" />
|
||||
<stop
|
||||
offset="0.3313"
|
||||
style="stop-color:#000000"
|
||||
id="stop4945" />
|
||||
</linearGradient>
|
||||
<rect
|
||||
style="fill:url(#SVGID_3_)"
|
||||
x="3.7850001"
|
||||
y="4.6750002"
|
||||
width="5.8829999"
|
||||
height="73.013"
|
||||
id="rect4947" />
|
||||
</g>
|
||||
</mask>
|
||||
<filter
|
||||
style="color-interpolation-filters:sRGB"
|
||||
id="filter1268"
|
||||
filterUnits="userSpaceOnUse"
|
||||
x="3.7850001"
|
||||
y="4.6750002"
|
||||
width="5.8829999"
|
||||
height="73.013">
|
||||
<feColorMatrix
|
||||
type="matrix"
|
||||
values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 1 0"
|
||||
id="feColorMatrix1266" />
|
||||
</filter>
|
||||
<linearGradient
|
||||
gradientUnits="userSpaceOnUse"
|
||||
y2="203.22046"
|
||||
x2="23.551136"
|
||||
y1="203.22046"
|
||||
x1="13.487289"
|
||||
id="linearGradient1160"
|
||||
xlink:href="#linearGradient3519" />
|
||||
<clipPath
|
||||
id="clipPath1890"
|
||||
clipPathUnits="userSpaceOnUse">
|
||||
<circle
|
||||
style="opacity:1;fill:#2d2d2d;fill-opacity:1;stroke:#434242;stroke-width:0.0575568;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="circle1892"
|
||||
cx="78.548424"
|
||||
cy="-31.019459"
|
||||
r="6.4721422"
|
||||
transform="scale(1,-1)" />
|
||||
</clipPath>
|
||||
<linearGradient
|
||||
gradientTransform="translate(-0.24389927,14.877856)"
|
||||
y2="27.314217"
|
||||
x2="84.864914"
|
||||
y1="27.314217"
|
||||
x1="72.164909"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="linearGradient2134"
|
||||
xlink:href="#linearGradient3700-8-3" />
|
||||
</defs>
|
||||
<metadata
|
||||
id="metadata1538">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title>DeamonEditor Icons</dc:title>
|
||||
<dc:publisher>
|
||||
<cc:Agent>
|
||||
<dc:title>mfgeg</dc:title>
|
||||
</cc:Agent>
|
||||
</dc:publisher>
|
||||
<dc:date>7.1.2020</dc:date>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
transform="matrix(1.1690805,0,0,1.1690805,-14.929261,-167.45253)"
|
||||
id="layer1">
|
||||
<g
|
||||
transform="translate(0.86526724,-82.691658)"
|
||||
id="g1351">
|
||||
<path
|
||||
d="m 13.715358,227.65981 h 2.97616 v 12.57332 h -2.97616 z m 5.79826,-1.73376 -0.327076,0.42227 c -0.179879,0.23226 -0.586303,0.67932 -0.903223,0.99412 -0.480677,0.47726 -0.640897,0.57235 -0.967777,0.57235 -0.223557,0 -0.380895,-0.0584 -0.624024,-0.25067 v 3.18307 c 0.172884,-0.0214 0.359647,-0.0333 0.575071,-0.0352 0.68403,-0.006 0.91815,0.0416 1.436333,0.29581 1.14586,0.56274 1.762492,1.5589 1.768251,2.8566 0.0094,2.10836 -1.870896,3.55161 -3.779655,3.16529 v 3.10345 c 4.345352,0.0758 4.093104,-2.37537 6.573781,-2.25837 1.486139,0.0748 1.333421,0.16555 1.796225,-1.064 l 0.259834,-0.6913 -0.803704,-0.66493 c -0.960855,-0.79478 -1.303983,-1.29079 -1.205013,-1.74132 0.06631,-0.30189 1.20818,-1.50093 1.779011,-1.86778 0.240181,-0.1543 0.244255,-0.17878 0.09575,-0.59661 -0.08522,-0.23979 -0.259522,-0.65746 -0.386789,-0.92744 l -0.23132,-0.49115 h -1.412666 c -1.868582,0 -1.802386,0.068 -1.805368,-1.82472 l -0.0021,-1.43582 -0.917745,-0.37171 z"
|
||||
style="opacity:1;fill:#000000;fill-opacity:0.5372549;stroke:none;stroke-width:0.18460207;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.5012225"
|
||||
id="path2133"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
id="path2106"
|
||||
style="opacity:1;fill:url(#linearGradient2131);fill-opacity:1;stroke:none;stroke-width:0.17733108;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.5012225"
|
||||
d="m 13.832581,227.93117 h 2.858936 v 12.07807 h -2.858936 z m 5.569881,-1.6655 -0.314194,0.40566 c -0.172793,0.22309 -0.563209,0.65257 -0.867647,0.95496 -0.46174,0.45847 -0.615654,0.5498 -0.929658,0.5498 -0.214752,0 -0.365893,-0.056 -0.599446,-0.24079 v 3.05768 c 0.166077,-0.0206 0.345483,-0.0319 0.55242,-0.0338 0.657089,-0.006 0.881987,0.0399 1.379764,0.28416 1.100724,0.54057 1.693073,1.49747 1.698606,2.74408 0.009,2.02535 -1.797211,3.41174 -3.63079,3.04061 v 2.98122 c 4.174205,0.0728 3.931888,-2.28179 6.314859,-2.1694 1.427604,0.0718 1.2809,0.15902 1.725477,-1.02213 l 0.249597,-0.66408 -0.772046,-0.63871 c -0.923009,-0.76345 -1.252622,-1.23996 -1.157552,-1.67277 0.0637,-0.29001 1.160592,-1.44177 1.708939,-1.79419 0.230721,-0.14825 0.234635,-0.17174 0.09198,-0.57312 -0.08187,-0.23032 -0.249296,-0.63156 -0.371555,-0.89088 l -0.222207,-0.4718 h -1.357022 c -1.794983,0 -1.731396,0.0653 -1.734262,-1.75284 l -0.0021,-1.3793 -0.881603,-0.35705 z"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
id="path2151"
|
||||
d="m 17.266983,230.95755 c -0.215637,0.003 -0.402408,0.014 -0.575465,0.0354 v 6.28854 c 1.910653,0.38671 3.792716,-1.05815 3.783336,-3.16865 -0.0058,-1.29897 -0.623064,-2.29597 -1.770059,-2.85927 -0.518699,-0.25451 -0.753103,-0.30234 -1.437812,-0.29607 z m 0.482341,1.17002 c 0.433747,-0.004 0.582202,0.0265 0.910784,0.18761 0.726595,0.35682 1.117604,0.98848 1.121256,1.81134 0.0059,1.33694 -1.186343,2.25211 -2.396695,2.00715 v -3.98359 c 0.109628,-0.0135 0.228054,-0.0214 0.364655,-0.0225 z"
|
||||
style="opacity:1;fill:url(#linearGradient2160);fill-opacity:1;stroke:none;stroke-width:0.18478528;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.5012225"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
style="opacity:1;fill:none;fill-opacity:1;stroke:url(#linearGradient2180);stroke-width:0.18478528;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.5012225"
|
||||
d="m 17.266981,230.95753 c -0.215636,0.003 -0.402408,0.014 -0.575464,0.0354 v 6.28853 c 1.910652,0.38672 3.792715,-1.05815 3.783336,-3.16865 -0.0057,-1.29897 -0.623065,-2.29597 -1.77006,-2.85927 -0.518697,-0.2545 -0.753103,-0.30234 -1.437812,-0.29607 z m 0.482343,1.17001 c 0.433747,-0.004 0.5822,0.0265 0.910783,0.18762 0.726594,0.35681 1.117605,0.98848 1.121257,1.81133 0.006,1.33694 -1.186344,2.25211 -2.396697,2.00716 v -3.98359 c 0.109628,-0.0135 0.228055,-0.0214 0.364657,-0.0225 z"
|
||||
id="path2170"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
id="path1422"
|
||||
d="m 17.266983,230.95755 c -0.215637,0.003 -0.402408,0.014 -0.575464,0.0354 v 6.28854 c 1.910652,0.38671 3.792715,-1.05815 3.783335,-3.16865 -0.0057,-1.29897 -0.623064,-2.29597 -1.770059,-2.85927 -0.518699,-0.25451 -0.753103,-0.30234 -1.437812,-0.29607 z m 0.482342,1.17002 c 0.433748,-0.004 0.582201,0.0265 0.910784,0.18761 0.726594,0.35682 1.117605,0.98848 1.121257,1.81134 0.006,1.33694 -1.186344,2.25211 -2.396697,2.00715 v -3.98359 c 0.109628,-0.0135 0.228054,-0.0214 0.364656,-0.0225 z"
|
||||
style="opacity:1;fill:none;fill-opacity:1;stroke:url(#linearGradient1448);stroke-width:0.1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
id="path1454"
|
||||
d="m 19.402462,226.26149 -0.314194,0.40566 c -0.172793,0.22309 -0.563209,0.65259 -0.867644,0.95499 -0.461741,0.45847 -0.615657,0.54983 -0.929661,0.54983 -0.214752,0 -0.365893,-0.056 -0.599446,-0.2408 v -0.004 h -2.858741 v 12.0783 h 2.858741 c 4.174205,0.0728 3.931888,-2.28177 6.314861,-2.16937 1.427604,0.0718 1.280898,0.15899 1.725475,-1.02217 l 0.249597,-0.66402 -0.772046,-0.63873 c -0.923009,-0.76346 -1.252622,-1.23997 -1.157552,-1.67278 0.0637,-0.29001 1.160595,-1.44176 1.708939,-1.79419 0.230721,-0.14825 0.234635,-0.17171 0.09198,-0.57309 -0.08187,-0.23032 -0.249296,-0.63158 -0.371555,-0.8909 l -0.222207,-0.47181 h -1.357025 c -1.794983,0 -1.731393,0.0653 -1.734259,-1.75286 l -0.0021,-1.37925 -0.8816,-0.35708 z"
|
||||
style="opacity:1;fill:none;fill-opacity:1;stroke:url(#linearGradient1485);stroke-width:0.1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
inkscape:connector-curvature="0" />
|
||||
</g>
|
||||
<g
|
||||
transform="translate(63.49728,-55.136805)"
|
||||
id="g1174" />
|
||||
<g
|
||||
transform="translate(50.48327,11.624037)"
|
||||
id="g1812" />
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 22 KiB |
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1228,15 +1228,3 @@ msgstr "Памер пiконаў у спісах:"
|
||||
|
||||
msgid "Logo size in tooltips:"
|
||||
msgstr "Памер лагатыпа ва ўсплыўных падказках:"
|
||||
|
||||
msgid "Save as"
|
||||
msgstr "Захаваць як"
|
||||
|
||||
msgid "Mark duplicates"
|
||||
msgstr "Адзначыць дублікаты"
|
||||
|
||||
msgid "Load only for selected bouquet"
|
||||
msgstr "Загрузіць толькі для абранага букета"
|
||||
|
||||
msgid "The task is canceled!"
|
||||
msgstr "Заданне скасавана!"
|
||||
|
||||
@@ -3,10 +3,9 @@
|
||||
#
|
||||
# Charly, 2019.
|
||||
# Dmitriy Yefremov, 2020-2021.
|
||||
# Thomas Schmidt, 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"
|
||||
@@ -1242,15 +1241,3 @@ msgstr "Picons Größe in den Listen:"
|
||||
|
||||
msgid "Logo size in tooltips:"
|
||||
msgstr "Logo-Größe in Tooltips:"
|
||||
|
||||
msgid "Save as"
|
||||
msgstr "Speichern als"
|
||||
|
||||
msgid "Mark duplicates"
|
||||
msgstr "Duplikate markieren"
|
||||
|
||||
msgid "Load only for selected bouquet"
|
||||
msgstr "Nur für ausgewähltes Bouquet laden"
|
||||
|
||||
msgid "The task is canceled!"
|
||||
msgstr "Der Task wird abgebrochen!"
|
||||
|
||||
@@ -1225,15 +1225,3 @@ msgstr "Размер пиконов в списках:"
|
||||
|
||||
msgid "Logo size in tooltips:"
|
||||
msgstr "Размер логотипа во всплывающих подсказках:"
|
||||
|
||||
msgid "Save as"
|
||||
msgstr "Сохранить как"
|
||||
|
||||
msgid "Mark duplicates"
|
||||
msgstr "Отметить дубликаты"
|
||||
|
||||
msgid "Load only for selected bouquet"
|
||||
msgstr "Загрузить только для выбранного букета"
|
||||
|
||||
msgid "The task is canceled!"
|
||||
msgstr "Задание отменено!"
|
||||
|
||||
@@ -3,7 +3,7 @@ msgstr ""
|
||||
"Project-Id-Version: DemonEditor\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2020-04-16 15:59+0300\n"
|
||||
"PO-Revision-Date: 2021-06-13 14:54+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"
|
||||
@@ -1023,7 +1023,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Kaydedilmemiş değişiklikler var.\n"
|
||||
"\n"
|
||||
"\t Şimdi kaydedilsin mi?"
|
||||
"\tŞimdi kaydedilsin mi?"
|
||||
|
||||
msgid ""
|
||||
"Are you sure you want to change the order\n"
|
||||
@@ -1245,27 +1245,3 @@ msgstr "Picon'lar indirin"
|
||||
|
||||
msgid "Errors:"
|
||||
msgstr "Hatalar:"
|
||||
|
||||
msgid "Use to play streams:"
|
||||
msgstr "Akışları oynatmak için kullanın:"
|
||||
|
||||
msgid "Font in the lists:"
|
||||
msgstr "Listelerdeki yazı tipi:"
|
||||
|
||||
msgid "Picons size in the lists:"
|
||||
msgstr "Listelerdeki Piconların boyutu:"
|
||||
|
||||
msgid "Logo size in tooltips:"
|
||||
msgstr "Araç ipuçlarındaki logo boyutu:"
|
||||
|
||||
msgid "Save as"
|
||||
msgstr "Farklı kaydet"
|
||||
|
||||
msgid "Mark duplicates"
|
||||
msgstr "Yinelenenleri işaretle"
|
||||
|
||||
msgid "Load only for selected bouquet"
|
||||
msgstr "Yalnızca seçilen buket için yükle"
|
||||
|
||||
msgid "The task is canceled!"
|
||||
msgstr "Görev iptal edildi!"
|
||||
|
||||
34
start.py
34
start.py
@@ -1,29 +1,19 @@
|
||||
#!/usr/bin/env python3
|
||||
import os
|
||||
|
||||
|
||||
def update_icon():
|
||||
need_update = False
|
||||
icon_name = "DemonEditor.desktop"
|
||||
|
||||
with open(icon_name, "r") as f:
|
||||
lines = f.readlines()
|
||||
for i, line in enumerate(lines):
|
||||
if line.startswith("Icon="):
|
||||
icon_path = line.lstrip("Icon=")
|
||||
current_path = "{}/app/ui/icons/hicolor/96x96/apps/demon-editor.png".format(os.getcwd())
|
||||
if icon_path != current_path:
|
||||
need_update = True
|
||||
lines[i] = "Icon={}\n".format(current_path)
|
||||
break
|
||||
|
||||
if need_update:
|
||||
with open(icon_name, "w") as f:
|
||||
f.writelines(lines)
|
||||
|
||||
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 app.ui.main_app_window import start_app
|
||||
|
||||
update_icon()
|
||||
set_start_method("fork") # For compatibility [Python > 3.7]
|
||||
start_app()
|
||||
|
||||
Reference in New Issue
Block a user