mirror of
https://github.com/DYefremov/DemonEditor.git
synced 2026-05-08 15:47:08 +02:00
Compare commits
44 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
93fd3cd2c3 | ||
|
|
03c291a61e | ||
|
|
97d9ce8b68 | ||
|
|
1a55df6674 | ||
|
|
6ac10c1380 | ||
|
|
13270b6152 | ||
|
|
b2c0359017 | ||
|
|
8a6dd1da93 | ||
|
|
2e1410ca36 | ||
|
|
3d96181450 | ||
|
|
fe749ca594 | ||
|
|
1b6cd58112 | ||
|
|
8d405d223a | ||
|
|
81ad19043a | ||
|
|
34db58f8e0 | ||
|
|
890163af4a | ||
|
|
c4e8a6646d | ||
|
|
639c8511bf | ||
|
|
5e082fc5d7 | ||
|
|
7f393ff9ba | ||
|
|
b37aac0cd9 | ||
|
|
d857c4b786 | ||
|
|
6ffd1d7926 | ||
|
|
415ad79c80 | ||
|
|
da4fef7f6b | ||
|
|
76c034435d | ||
|
|
f9239f0642 | ||
|
|
7f096df998 | ||
|
|
3f0738d874 | ||
|
|
b310b640b4 | ||
|
|
18caa58336 | ||
|
|
03e5909c23 | ||
|
|
d9071632d2 | ||
|
|
694269113a | ||
|
|
78f347a505 | ||
|
|
eb9be7b190 | ||
|
|
952aeb4d22 | ||
|
|
8a865513b3 | ||
|
|
30e38dde3f | ||
|
|
5f68eb0f1a | ||
|
|
ce92134a00 | ||
|
|
2c80d13170 | ||
|
|
a49d6490c5 | ||
|
|
dc76a7801e |
36
README.md
36
README.md
@@ -3,24 +3,26 @@
|
||||
## 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)
|
||||
|
||||
### Keyboard shortcuts:
|
||||
Ctrl + X, C, V, Up, Down, PageUp, PageDown, S, T, E, L, H, Space; Insert, Delete, F2.
|
||||
Insert - copies the selected channels from the main list to the bouquet or inserts (creates) a new bouquet.
|
||||
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.
|
||||
Space - select/deselect.
|
||||
Left/Right - remove selection.
|
||||
|
||||
**Ctrl + X, C, V, Up, Down, PageUp, PageDown, Home, End, S, T, E, L, H, Space; Insert, Delete, F2, Enter, P.**
|
||||
* **Insert** - copies the selected channels from the main list to the bouquet or inserts (creates) a new bouquet.
|
||||
* **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.
|
||||
* **P** - enable/disable preview mode for IPTV in the bouquet list.
|
||||
* **Enter** - start play IPTV or other stream in the bouquet list.
|
||||
* **Space** - select/deselect.
|
||||
* **Left/Right** - remove selection.
|
||||
* **Ctrl + Up, Down, PageUp, PageDown, Home, End** - move selected items in the list.
|
||||
### Extra:
|
||||
Multiple selections in lists only with Space key (as in file managers).
|
||||
Ability to import IPTV into bouquet (Neutrino WEBTV) from m3u files.
|
||||
Tool for downloading picons from lyngsat.com.
|
||||
* Multiple selections in lists only with Space key (as in file managers).
|
||||
* Ability to import IPTV into bouquet (Neutrino WEBTV) from m3u files.
|
||||
* Ability to download picons and update satellites (transponders) from web.
|
||||
* Preview (playing) IPTV or other streams directly from the bouquet list(should be installed VLC).
|
||||
### Minimum requirements:
|
||||
Python >= 3.5.2 and GTK+ 3 with PyGObject bindings.
|
||||
#### Note.
|
||||
@@ -29,6 +31,6 @@ To create a simple debian package, you can use the build-deb.sh
|
||||
Tests only in image based on OpenPLi or last BPanther(neutrino) images with GM 990 Spark Reloaded receiver
|
||||
in my preferred linux distro (Last Linux Mint 18.* - MATE 64-bit)!
|
||||
|
||||
#### Terrestrial and cable channels at the moment are not supported!
|
||||
**Terrestrial and cable channels at the moment are not supported!**
|
||||
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
from app.commons import run_task
|
||||
from app.properties import Profile
|
||||
from .ecommons import Service, Satellite, Transponder, Bouquet, Bouquets
|
||||
from .ecommons import Service, Satellite, Transponder, Bouquet, Bouquets, is_transponder_valid
|
||||
from .enigma.blacklist import get_blacklist, write_blacklist
|
||||
from .enigma.bouquets import get_bouquets as get_enigma_bouquets, write_bouquets as write_enigma_bouquets, to_bouquet_id
|
||||
from .enigma.lamedb import get_services as get_enigma_services, write_services as write_enigma_services
|
||||
@@ -10,17 +10,17 @@ from .neutrino.services import get_services as get_neutrino_services, write_serv
|
||||
from .satxml import get_satellites, write_satellites
|
||||
|
||||
|
||||
def get_services(data_path, profile):
|
||||
def get_services(data_path, profile, format_version):
|
||||
if profile is Profile.ENIGMA_2:
|
||||
return get_enigma_services(data_path)
|
||||
return get_enigma_services(data_path, format_version)
|
||||
elif profile is Profile.NEUTRINO_MP:
|
||||
return get_neutrino_services(data_path)
|
||||
|
||||
|
||||
@run_task
|
||||
def write_services(path, channels, profile):
|
||||
def write_services(path, channels, profile, format_version):
|
||||
if profile is Profile.ENIGMA_2:
|
||||
write_enigma_services(path, channels)
|
||||
write_enigma_services(path, channels, format_version)
|
||||
elif profile is Profile.NEUTRINO_MP:
|
||||
write_neutrino_services(path, channels)
|
||||
|
||||
|
||||
@@ -27,13 +27,21 @@ Transponder = namedtuple("Transponder", ["frequency", "symbol_rate", "polarizati
|
||||
"system", "modulation", "pls_mode", "pls_code", "is_id"])
|
||||
|
||||
|
||||
class Type(Enum):
|
||||
""" Types of DVB transponders """
|
||||
class TrType(Enum):
|
||||
""" Transponders type """
|
||||
Satellite = "s"
|
||||
Terestrial = "t"
|
||||
Cable = "c"
|
||||
|
||||
|
||||
class BqType(Enum):
|
||||
""" Bouquet type"""
|
||||
BOUQUET = "bouquet"
|
||||
TV = "tv"
|
||||
RADIO = "radio"
|
||||
WEBTV = "webtv"
|
||||
|
||||
|
||||
class Flag(Enum):
|
||||
""" Service flags
|
||||
|
||||
@@ -134,3 +142,26 @@ def get_value_by_name(en, name):
|
||||
for n in en:
|
||||
if n.name == name:
|
||||
return n.value
|
||||
|
||||
|
||||
def is_transponder_valid(tr: Transponder):
|
||||
""" Checks transponder validity """
|
||||
try:
|
||||
int(tr.frequency)
|
||||
int(tr.symbol_rate)
|
||||
tr.pls_mode is None or int(tr.pls_mode)
|
||||
tr.pls_code is None or int(tr.pls_code)
|
||||
tr.is_id is None or int(tr.is_id)
|
||||
except TypeError:
|
||||
return False
|
||||
|
||||
if tr.polarization not in POLARIZATION.values():
|
||||
return False
|
||||
if tr.fec_inner not in FEC.values():
|
||||
return False
|
||||
if tr.system not in SYSTEM.values():
|
||||
return False
|
||||
if tr.modulation not in MODULATION.values():
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
""" Module for parsing bouquets """
|
||||
from app.eparser.ecommons import BqServiceType, BouquetService, Bouquets, Bouquet
|
||||
from app.eparser.ecommons import BqServiceType, BouquetService, Bouquets, Bouquet, BqType
|
||||
|
||||
_TV_ROOT_FILE_NAME = "bouquets.tv"
|
||||
_RADIO_ROOT_FILE_NAME = "bouquets.radio"
|
||||
|
||||
|
||||
def get_bouquets(path):
|
||||
return parse_bouquets(path, "bouquets.tv", "tv"), parse_bouquets(path, "bouquets.radio", "radio")
|
||||
return parse_bouquets(path, "bouquets.tv", BqType.TV.value), parse_bouquets(path, "bouquets.radio",
|
||||
BqType.RADIO.value)
|
||||
|
||||
|
||||
def write_bouquets(path, bouquets):
|
||||
|
||||
@@ -1,24 +1,32 @@
|
||||
""" This module used for parsing lamedb file
|
||||
""" This module used for parsing and write lamedb file
|
||||
|
||||
Currently implemented only for satellite channels!!!
|
||||
Description of format taken from here: http://www.satsupreme.com/showthread.php/194074-Lamedb-format-explained
|
||||
"""
|
||||
from app.commons import log
|
||||
from app.ui import CODED_ICON, LOCKED_ICON, HIDE_ICON
|
||||
from app.ui.uicommons import CODED_ICON, LOCKED_ICON, HIDE_ICON
|
||||
from .blacklist import get_blacklist
|
||||
from ..ecommons import Service, POLARIZATION, SYSTEM, FEC, SERVICE_TYPE, Flag
|
||||
from ..ecommons import Service, POLARIZATION, FEC, SERVICE_TYPE, Flag
|
||||
|
||||
_HEADER = "eDVB services /4/"
|
||||
_HEADER = "eDVB services /{}/"
|
||||
_SEP = ":" # separator
|
||||
_FILE_NAME = "lamedb"
|
||||
_END_LINE = "# File was created in DemonEditor.\n# ....Enjoy watching!....\n"
|
||||
|
||||
|
||||
def get_services(path):
|
||||
return parse(path)
|
||||
def get_services(path, format_version):
|
||||
return parse(path, format_version)
|
||||
|
||||
|
||||
def write_services(path, services):
|
||||
lines = [_HEADER, "\ntransponders\n"]
|
||||
def write_services(path, services, format_version=4):
|
||||
if format_version == 4:
|
||||
write_to_lamedb(path, services)
|
||||
elif format_version == 5:
|
||||
write_to_lamedb5(path, services)
|
||||
|
||||
|
||||
def write_to_lamedb(path, services):
|
||||
""" Writing lamedb file ver.4 """
|
||||
lines = [_HEADER.format(4), "\ntransponders\n"]
|
||||
tr_lines = []
|
||||
services_lines = ["end\nservices\n"]
|
||||
tr_set = set()
|
||||
@@ -36,14 +44,42 @@ def write_services(path, services):
|
||||
tr_lines.sort()
|
||||
lines.extend(tr_lines)
|
||||
lines.extend(services_lines)
|
||||
lines.append("end\nFile was created in DemonEditor.\n....Enjoy watching!....\n")
|
||||
|
||||
lines.append("end\n" + _END_LINE)
|
||||
with open(path + _FILE_NAME, "w") as file:
|
||||
file.writelines(lines)
|
||||
|
||||
|
||||
def parse(path):
|
||||
def write_to_lamedb5(path, services):
|
||||
""" Writing lamedb5 file """
|
||||
lines = [_HEADER.format(5) + "\n"]
|
||||
services_lines = []
|
||||
tr_set = set()
|
||||
|
||||
for srv in services:
|
||||
data_id = str(srv.data_id).split(_SEP)
|
||||
tr_id = "{}:{}:{}".format(data_id[1], data_id[2], data_id[3])
|
||||
tr_set.add("t:{},{}\n".format(tr_id, srv.transponder.replace(" ", ":", 1)))
|
||||
services_lines.append("s:{},\"{}\",{}\n".format(srv.data_id, srv.service, srv.flags_cas))
|
||||
|
||||
lines.extend(sorted(tr_set))
|
||||
lines.extend(services_lines)
|
||||
lines.append(_END_LINE)
|
||||
|
||||
with open(path + "lamedb5", "w") as file:
|
||||
file.writelines(lines)
|
||||
|
||||
|
||||
def parse(path, version=4):
|
||||
""" Parsing lamedb """
|
||||
if version == 4:
|
||||
return parse_v4(path)
|
||||
elif version == 5:
|
||||
return parse_v5(path)
|
||||
raise SyntaxError("Unsupported version of the format.")
|
||||
|
||||
|
||||
def parse_v4(path):
|
||||
""" Parsing version 4 """
|
||||
with open(path + _FILE_NAME, "r", encoding="utf-8", errors="replace") as file:
|
||||
try:
|
||||
data = str(file.read())
|
||||
@@ -58,7 +94,28 @@ def parse(path):
|
||||
transponders, sep, services = services.partition("services") # 2 step
|
||||
services, sep, _ = services.partition("\nend") # 3 step
|
||||
|
||||
return parse_services(services.split("\n"), transponders.split("/"), path)
|
||||
return parse_services(services.split("\n"), parse_transponders(transponders.split("/")), path)
|
||||
|
||||
|
||||
def parse_v5(path):
|
||||
""" Parsing version 5 """
|
||||
with open(path + "lamedb5", "r", encoding="utf-8", errors="replace") as file:
|
||||
lns = file.readlines()
|
||||
|
||||
if lns and not lns[0].endswith("/5/\n"):
|
||||
raise SyntaxError("lamedb v.5 parsing error: unsupported format.")
|
||||
|
||||
trs, srvs = {}, [""]
|
||||
for l in lns:
|
||||
if l.startswith("s:"):
|
||||
srv_data = l.strip("s:").split(",", 2)
|
||||
srv_data[1], srv_data[2] = srv_data[1].strip("\""), srv_data[2].strip()
|
||||
srvs.extend(srv_data)
|
||||
elif l.startswith("t:"):
|
||||
tr, srv = l.split(",")
|
||||
trs[tr.strip("t:")] = srv.strip().replace(":", " ", 1)
|
||||
|
||||
return parse_services(srvs, trs, path)
|
||||
|
||||
|
||||
def parse_transponders(arg):
|
||||
@@ -75,9 +132,7 @@ def parse_transponders(arg):
|
||||
def parse_services(services, transponders, path):
|
||||
""" Parsing channels """
|
||||
channels = []
|
||||
transponders = parse_transponders(transponders)
|
||||
blacklist = str(get_blacklist(path))
|
||||
|
||||
srv = split(services, 3)
|
||||
if srv[0][0] == "": # remove first empty element
|
||||
srv.remove(srv[0])
|
||||
|
||||
@@ -2,12 +2,12 @@
|
||||
from enum import Enum
|
||||
|
||||
from app.properties import Profile
|
||||
from app.ui import IPTV_ICON
|
||||
from app.ui.uicommons import IPTV_ICON
|
||||
from .ecommons import BqServiceType, Service
|
||||
|
||||
# url, description, urlkey, account, usrname, psw, s_type, iconsrc, iconsrc_b, group
|
||||
NEUTRINO_FAV_ID_FORMAT = "{}::{}::{}::{}::{}::{}::{}::{}::{}::{}"
|
||||
ENIGMA2_FAV_ID_FORMAT = " {}:0:{}:{}:{}:{}:{}:0:0:0:{}:{}\n#DESCRIPTION: {}\n"
|
||||
ENIGMA2_FAV_ID_FORMAT = " {}:0:{}:{:X}:{:X}:{:X}:{:X}:0:0:0:{}:{}\n#DESCRIPTION: {}\n"
|
||||
|
||||
|
||||
class StreamType(Enum):
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
import os
|
||||
from enum import Enum
|
||||
from xml.dom.minidom import parse, Document
|
||||
|
||||
from app.eparser.iptv import NEUTRINO_FAV_ID_FORMAT
|
||||
from app.ui import LOCKED_ICON, HIDE_ICON
|
||||
from ..ecommons import Bouquets, Bouquet, BouquetService, BqServiceType, PROVIDER
|
||||
from app.ui.uicommons import LOCKED_ICON, HIDE_ICON
|
||||
from ..ecommons import Bouquets, Bouquet, BouquetService, BqServiceType, PROVIDER, BqType
|
||||
|
||||
_FILE = "bouquets.xml"
|
||||
_U_FILE = "ubouquets.xml"
|
||||
@@ -13,12 +12,6 @@ _W_FILE = "webtv.xml"
|
||||
_COMMENT = " File was created in DemonEditor. Enjoy watching! "
|
||||
|
||||
|
||||
class BqType(Enum):
|
||||
BOUQUET = "bouquet"
|
||||
TV = "tv"
|
||||
WEBTV = "webtv"
|
||||
|
||||
|
||||
def get_bouquets(path):
|
||||
return (parse_bouquets(path + _FILE, "Providers", BqType.BOUQUET.value),
|
||||
parse_bouquets(path + _U_FILE, "FAV", BqType.TV.value),
|
||||
|
||||
@@ -8,7 +8,7 @@ from telnetlib import Telnet
|
||||
from app.commons import log
|
||||
from app.properties import Profile
|
||||
|
||||
__DATA_FILES_LIST = ("tv", "radio", "lamedb", "blacklist", "whitelist", # enigma 2
|
||||
__DATA_FILES_LIST = ("tv", "radio", "lamedb", "lamedb5", "blacklist", "whitelist", # enigma 2
|
||||
"services.xml", "myservices.xml", "bouquets.xml", "ubouquets.xml") # neutrino
|
||||
|
||||
_SATELLITES_XML_FILE = "satellites.xml"
|
||||
@@ -67,7 +67,7 @@ def upload_data(*, properties, download_type=DownloadDataType.ALL, remove_unused
|
||||
tn = telnet(host=host, user=properties.get("telnet_user", "root"), password=properties.get("telnet_password", ""),
|
||||
timeout=properties.get("telnet_timeout", 5))
|
||||
next(tn)
|
||||
# terminate enigma or enigma
|
||||
# terminate enigma or neutrino
|
||||
tn.send("init 4")
|
||||
|
||||
with FTP(host=host) as ftp:
|
||||
|
||||
@@ -48,7 +48,8 @@ def get_default_settings():
|
||||
"satellites_xml_path": "/etc/tuxbox/",
|
||||
"picons_path": "/usr/share/enigma2/picon",
|
||||
"data_dir_path": DATA_PATH + "enigma2/",
|
||||
"picons_dir_path": DATA_PATH + "enigma2/picons/"},
|
||||
"picons_dir_path": DATA_PATH + "enigma2/picons/",
|
||||
"v5_support": False},
|
||||
Profile.NEUTRINO_MP.value: {
|
||||
"host": "127.0.0.1", "port": "21",
|
||||
"user": "root", "password": "root",
|
||||
|
||||
96
app/tools/media.py
Normal file
96
app/tools/media.py
Normal file
@@ -0,0 +1,96 @@
|
||||
from app.commons import run_idle
|
||||
from app.tools import vlc
|
||||
from app.ui.uicommons import Gtk, Gdk
|
||||
|
||||
MRL = "url"
|
||||
|
||||
|
||||
class Player:
|
||||
_VLC_INSTANCE = None
|
||||
|
||||
def __init__(self, url):
|
||||
handlers = {"on_play": self.on_play,
|
||||
"on_stop": self.on_stop,
|
||||
"on_drawing_area_realize": self.on_drawing_area_realize,
|
||||
"on_press": self.on_press,
|
||||
"on_key_release": self.on_key_release,
|
||||
"on_state_changed": self.on_state_changed,
|
||||
"on_close_window": self.on_close_window}
|
||||
|
||||
builder = Gtk.Builder()
|
||||
builder.add_objects_from_file("player.glade", ("player_main_window",))
|
||||
builder.connect_signals(handlers)
|
||||
self._main_window = builder.get_object("player_main_window")
|
||||
self._main_box = builder.get_object("main_box")
|
||||
self._buttonbox = builder.get_object("buttonbox")
|
||||
self._frame = builder.get_object("")
|
||||
self._drawing_area = builder.get_object("drawing_area")
|
||||
self._drawing_area.set_events(Gdk.ModifierType.BUTTON1_MASK)
|
||||
self._player = Player.get_vlc_instance().media_player_new()
|
||||
self._is_played = False
|
||||
self._url = url
|
||||
self._full_screen = False
|
||||
|
||||
@staticmethod
|
||||
def get_vlc_instance():
|
||||
if Player._VLC_INSTANCE:
|
||||
return Player._VLC_INSTANCE
|
||||
_VLC_INSTANCE = vlc.Instance("--no-xlib")
|
||||
return _VLC_INSTANCE
|
||||
|
||||
def on_play(self, item):
|
||||
if not self._is_played:
|
||||
self._player.play()
|
||||
self._is_played = True
|
||||
|
||||
def on_stop(self, item):
|
||||
if self._is_played:
|
||||
self._player.stop()
|
||||
self._is_played = False
|
||||
|
||||
def on_press(self, area, event: Gdk.EventButton):
|
||||
if event.button == Gdk.BUTTON_PRIMARY and event.type == Gdk.EventType.DOUBLE_BUTTON_PRESS:
|
||||
self.change_state()
|
||||
|
||||
def on_state_changed(self, window, event):
|
||||
if event.new_window_state & Gdk.WindowState.FULLSCREEN:
|
||||
if self._main_box in window:
|
||||
window.remove(self._main_box)
|
||||
self._drawing_area.reparent(self._main_window)
|
||||
else:
|
||||
if self._drawing_area in self._main_window:
|
||||
window.remove(self._drawing_area)
|
||||
window.add(self._main_box)
|
||||
self._main_box.pack_start(self._drawing_area, True, True, 0)
|
||||
self._main_box.reorder_child(self._drawing_area, 0)
|
||||
|
||||
def change_state(self):
|
||||
self._full_screen = not self._full_screen
|
||||
self._main_window.fullscreen() if self._full_screen else self._main_window.unfullscreen()
|
||||
|
||||
def on_key_release(self, area, key):
|
||||
if key.keyval in (Gdk.KEY_F, Gdk.KEY_f):
|
||||
self.change_state()
|
||||
|
||||
def on_drawing_area_realize(self, widget):
|
||||
win_id = widget.get_window().get_xid()
|
||||
if self._player:
|
||||
self._is_played = True
|
||||
self._player.set_xwindow(win_id)
|
||||
self._player.set_mrl(self._url)
|
||||
self._player.play()
|
||||
|
||||
@run_idle
|
||||
def on_close_window(self, *args):
|
||||
if self._player:
|
||||
self.on_stop(None)
|
||||
self._player.release()
|
||||
Gtk.main_quit()
|
||||
|
||||
def show(self):
|
||||
self._main_window.show()
|
||||
Gtk.main()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
Player(MRL).show()
|
||||
@@ -1,11 +1,11 @@
|
||||
import glob
|
||||
import os
|
||||
import re
|
||||
import shutil
|
||||
|
||||
from collections import namedtuple
|
||||
from html.parser import HTMLParser
|
||||
|
||||
import re
|
||||
|
||||
from app.commons import log, run_task
|
||||
from app.properties import Profile
|
||||
|
||||
116
app/tools/player.glade
Normal file
116
app/tools/player.glade
Normal file
@@ -0,0 +1,116 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- Generated with glade 3.18.3 -->
|
||||
<interface>
|
||||
<requires lib="gtk+" version="3.12"/>
|
||||
<object class="GtkApplicationWindow" id="player_main_window">
|
||||
<property name="can_focus">False</property>
|
||||
<property name="title" translatable="yes">Player</property>
|
||||
<property name="icon_name">vlc</property>
|
||||
<signal name="delete-event" handler="on_close_window" swapped="no"/>
|
||||
<signal name="window-state-event" handler="on_state_changed" swapped="no"/>
|
||||
<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="GtkDrawingArea" id="drawing_area">
|
||||
<property name="width_request">320</property>
|
||||
<property name="height_request">240</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="has_focus">True</property>
|
||||
<signal name="button-press-event" handler="on_press" swapped="no"/>
|
||||
<signal name="key-release-event" handler="on_key_release" swapped="no"/>
|
||||
<signal name="realize" handler="on_drawing_area_realize" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="padding">2</property>
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkSeparator" id="separator">
|
||||
<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="padding">2</property>
|
||||
<property name="position">3</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButtonBox" id="buttonbox">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_left">3</property>
|
||||
<property name="margin_right">5</property>
|
||||
<property name="spacing">2</property>
|
||||
<property name="homogeneous">True</property>
|
||||
<property name="layout_style">start</property>
|
||||
<child>
|
||||
<object class="GtkButton" id="play_button">
|
||||
<property name="label">gtk-media-play</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>
|
||||
<signal name="clicked" handler="on_play" 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="stop_button">
|
||||
<property name="label">gtk-media-stop</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>
|
||||
<signal name="clicked" handler="on_stop" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="close_button">
|
||||
<property name="label">gtk-close</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>
|
||||
<signal name="clicked" handler="on_close_window" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="pack_type">end</property>
|
||||
<property name="position">2</property>
|
||||
<property name="secondary">True</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="padding">5</property>
|
||||
<property name="position">4</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</interface>
|
||||
186
app/tools/satellites.py
Normal file
186
app/tools/satellites.py
Normal file
@@ -0,0 +1,186 @@
|
||||
""" Module for download satellites from internet ("flysat.com")
|
||||
for replace or update current satellites.xml file.
|
||||
"""
|
||||
import re
|
||||
import requests
|
||||
from enum import Enum
|
||||
from html.parser import HTMLParser
|
||||
|
||||
from app.eparser import Satellite, Transponder, is_transponder_valid
|
||||
|
||||
|
||||
class SatelliteSource(Enum):
|
||||
FLYSAT = ("https://www.flysat.com/satlist.php",)
|
||||
LYNGSAT = ("https://www.lyngsat.com/asia.html", "https://www.lyngsat.com/europe.html",
|
||||
"https://www.lyngsat.com/atlantic.html", "https://www.lyngsat.com/america.html")
|
||||
|
||||
@staticmethod
|
||||
def get_sources(src):
|
||||
return src.value
|
||||
|
||||
|
||||
class SatellitesParser(HTMLParser):
|
||||
""" Parser for satellite html page. """
|
||||
|
||||
_HEADERS = {"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.9; rv:45.0) Gecko/20100101 Firefox/59.02"}
|
||||
|
||||
def __init__(self, source=SatelliteSource.FLYSAT, entities=False, separator=' '):
|
||||
|
||||
HTMLParser.__init__(self)
|
||||
|
||||
self._parse_html_entities = entities
|
||||
self._separator = separator
|
||||
self._is_td = False
|
||||
self._is_th = False
|
||||
self._is_provider = False
|
||||
self._current_row = []
|
||||
self._current_cell = []
|
||||
self._rows = []
|
||||
self._source = source
|
||||
|
||||
def handle_starttag(self, tag, attrs):
|
||||
if tag == 'td':
|
||||
self._is_td = True
|
||||
if tag == 'tr':
|
||||
self._is_th = True
|
||||
if tag == "a":
|
||||
self._current_row.append(attrs[0][1])
|
||||
|
||||
def handle_data(self, data):
|
||||
""" Save content to a cell """
|
||||
if self._is_td or self._is_th:
|
||||
self._current_cell.append(data.strip())
|
||||
|
||||
def handle_endtag(self, tag):
|
||||
if tag == 'td':
|
||||
self._is_td = False
|
||||
elif tag == 'tr':
|
||||
self._is_th = False
|
||||
|
||||
if tag in ('td', 'th'):
|
||||
final_cell = self._separator.join(self._current_cell).strip()
|
||||
self._current_row.append(final_cell)
|
||||
self._current_cell = []
|
||||
elif tag == 'tr':
|
||||
row = self._current_row
|
||||
self._rows.append(row)
|
||||
self._current_row = []
|
||||
|
||||
def error(self, message):
|
||||
pass
|
||||
|
||||
def get_satellites_list(self, source):
|
||||
""" Getting complete list of satellites. """
|
||||
self.reset()
|
||||
self._rows.clear()
|
||||
self._source = source
|
||||
|
||||
for src in SatelliteSource.get_sources(self._source):
|
||||
request = requests.get(url=src, headers=self._HEADERS)
|
||||
reason = request.reason
|
||||
if reason == "OK":
|
||||
self.feed(request.text)
|
||||
else:
|
||||
print(reason)
|
||||
|
||||
if self._rows:
|
||||
if self._source is SatelliteSource.FLYSAT:
|
||||
def get_sat(r):
|
||||
return r[1], self.parse_position(r[2]), r[3], r[0], False
|
||||
|
||||
return list(map(get_sat, filter(lambda x: all(x) and len(x) == 5, self._rows)))
|
||||
elif self._source is SatelliteSource.LYNGSAT:
|
||||
rows = filter(lambda x: len(x) in (5, 7), self._rows)
|
||||
sats = []
|
||||
current_pos = "0"
|
||||
for row in rows:
|
||||
r_len = len(row)
|
||||
if r_len == 7:
|
||||
current_pos = self.parse_position(row[2])
|
||||
sats.append((row[4], current_pos, row[5], row[1], False))
|
||||
elif r_len == 5:
|
||||
sats.append((row[2], current_pos, row[3], row[1], False))
|
||||
return sats
|
||||
|
||||
def get_satellite(self, sat):
|
||||
pos = sat[1]
|
||||
return Satellite(name=sat[0] + " ({})".format(pos),
|
||||
flags="0",
|
||||
position=self.get_position(pos.replace(".", "")),
|
||||
transponders=self.get_transponders(sat[3]))
|
||||
|
||||
@staticmethod
|
||||
def parse_position(pos_str):
|
||||
return "".join(c for c in pos_str if c.isdigit() or c.isalpha() or c == ".")
|
||||
|
||||
@staticmethod
|
||||
def get_position(pos):
|
||||
return "{}{}".format("-" if pos[-1] == "W" else "", pos[:-1])
|
||||
|
||||
def get_transponders(self, sat_url):
|
||||
self._rows.clear()
|
||||
url = "https://www.flysat.com/" + sat_url if self._source is SatelliteSource.FLYSAT else sat_url
|
||||
request = requests.get(url=url, headers=self._HEADERS)
|
||||
reason = request.reason
|
||||
trs = []
|
||||
if reason == "OK":
|
||||
self.feed(request.text)
|
||||
if self._source is SatelliteSource.FLYSAT:
|
||||
self.get_transponders_for_fly_sat(trs)
|
||||
elif self._source is SatelliteSource.LYNGSAT:
|
||||
self.get_transponders_for_lyng_sat(trs)
|
||||
return trs
|
||||
|
||||
def get_transponders_for_fly_sat(self, trs):
|
||||
""" Parsing transponders for FlySat """
|
||||
if self._rows:
|
||||
zeros = "000"
|
||||
for r in self._rows:
|
||||
if len(r) < 3:
|
||||
continue
|
||||
data = r[2].split(" ")
|
||||
if len(data) != 2:
|
||||
continue
|
||||
sr, fec = data
|
||||
data = r[1].split(" ")
|
||||
if len(data) < 3:
|
||||
continue
|
||||
freq, pol, sys = data[0], data[1], data[2]
|
||||
sys = sys.split("/")
|
||||
if len(sys) != 2:
|
||||
continue
|
||||
sys, mod = sys
|
||||
mod = "QPSK" if sys == "DVB-S" else mod
|
||||
|
||||
tr = Transponder(freq + zeros, sr + zeros, pol, fec, sys, mod, None, None, None)
|
||||
if is_transponder_valid(tr):
|
||||
trs.append(tr)
|
||||
|
||||
def get_transponders_for_lyng_sat(self, trs):
|
||||
""" Parsing transponders for LyngSat """
|
||||
frq_pol_pattern = re.compile("(\d{4,5}).*([RLHV])(.*\d$)")
|
||||
sr_fec_pattern = re.compile("^(\d{4,5})-(\d/\d)(.+PSK)?(.*)?$")
|
||||
sys_pattern = re.compile("(DVB-S[2]?)(.*)?")
|
||||
zeros = "000"
|
||||
for r in filter(lambda x: len(x) > 8, self._rows):
|
||||
freq = re.match(frq_pol_pattern, r[2])
|
||||
if not freq:
|
||||
continue
|
||||
frq, pol = freq.group(1), freq.group(2)
|
||||
sr_fec = re.match(sr_fec_pattern, r[-3])
|
||||
if not sr_fec:
|
||||
continue
|
||||
sr, fec, mod = sr_fec.group(1), sr_fec.group(2), sr_fec.group(3)
|
||||
mod = mod.strip() if mod else "Auto"
|
||||
sys = re.match(sys_pattern, r[-4])
|
||||
if not sys:
|
||||
continue
|
||||
sys = sys.group(1)
|
||||
|
||||
tr = Transponder(frq + zeros, sr + zeros, pol, fec, sys, mod, None, None, None)
|
||||
if is_transponder_valid(tr):
|
||||
trs.append(tr)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
pass
|
||||
8749
app/tools/vlc.py
Normal file
8749
app/tools/vlc.py
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,28 +1 @@
|
||||
import locale
|
||||
import gi
|
||||
import os
|
||||
|
||||
gi.require_version('Gtk', '3.0')
|
||||
from gi.repository import Gtk, Gdk
|
||||
|
||||
# path to *.glade files
|
||||
UI_RESOURCES_PATH = "app/ui/" if os.path.exists("app/ui/") else "/usr/share/demoneditor/app/ui/"
|
||||
|
||||
# translation
|
||||
TEXT_DOMAIN = "demon-editor"
|
||||
if UI_RESOURCES_PATH == "app/ui/":
|
||||
LANG_DIR = UI_RESOURCES_PATH + "lang"
|
||||
locale.bindtextdomain(TEXT_DOMAIN, UI_RESOURCES_PATH + "lang")
|
||||
|
||||
theme = Gtk.IconTheme.get_default()
|
||||
_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("system-lock-screen", 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.load_icon("emblem-shared", 16, 0) else None
|
||||
|
||||
if __name__ == "__main__":
|
||||
pass
|
||||
|
||||
@@ -9,11 +9,11 @@
|
||||
<property name="icon_name">system-help</property>
|
||||
<property name="type_hint">normal</property>
|
||||
<property name="program_name">DemonEditor</property>
|
||||
<property name="version">0.3.0 Pre-alpha</property>
|
||||
<property name="version">0.3.2 Pre-alpha</property>
|
||||
<property name="copyright">2018 Dmitriy Yefremov
|
||||
dmitry.v.yefremov@gmail.com
|
||||
</property>
|
||||
<property name="comments" translatable="yes">Enigma2 channel and satellites list editor for GNU/Linux</property>
|
||||
<property name="website">https://github.com/DYefremov/DemonEditor</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
|
||||
@@ -22,12 +22,12 @@ dmitry.v.yefremov@gmail.com
|
||||
<property name="wrap_license">True</property>
|
||||
<property name="license_type">mit-x11</property>
|
||||
<child internal-child="vbox">
|
||||
<object class="GtkBox" id="aboutdialog-vbox3">
|
||||
<object class="GtkBox" id="aboutdialog_vbox">
|
||||
<property name="can_focus">False</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<property name="spacing">2</property>
|
||||
<child internal-child="action_area">
|
||||
<object class="GtkButtonBox" id="aboutdialog-action_area3">
|
||||
<object class="GtkButtonBox" id="aboutdialog_action_area">
|
||||
<property name="can_focus">False</property>
|
||||
<property name="layout_style">end</property>
|
||||
</object>
|
||||
@@ -1425,7 +1425,7 @@ dmitry.v.yefremov@gmail.com
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkSeparator" id="separator1">
|
||||
<object class="GtkSeparator" id="settings_separator1">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_top">2</property>
|
||||
@@ -1550,7 +1550,7 @@ dmitry.v.yefremov@gmail.com
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkSeparator" id="separator5">
|
||||
<object class="GtkSeparator" id="settings_separator">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_left">5</property>
|
||||
@@ -1564,75 +1564,120 @@ dmitry.v.yefremov@gmail.com
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkBox" id="box3">
|
||||
<object class="GtkFrame" id="settings_profile_frame">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_right">5</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<property name="label_xalign">0</property>
|
||||
<property name="shadow_type">in</property>
|
||||
<child>
|
||||
<object class="GtkBox" id="settings_profile_box">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_left">5</property>
|
||||
<property name="margin_right">5</property>
|
||||
<property name="margin_top">5</property>
|
||||
<property name="margin_bottom">5</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<object class="GtkRadioButton" id="enigma_radio_button">
|
||||
<property name="label">Enigma2 </property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">False</property>
|
||||
<property name="xalign">0</property>
|
||||
<property name="active">True</property>
|
||||
<property name="draw_indicator">True</property>
|
||||
<property name="group">neutrino_radio_button</property>
|
||||
<signal name="toggled" 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="GtkCheckButton" id="support_ver5_check_button">
|
||||
<property name="label" translatable="yes">Ver. 5 support
|
||||
(experimental)</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">False</property>
|
||||
<property name="xalign">0</property>
|
||||
<property name="draw_indicator">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkSeparator" id="settings_prof_separator">
|
||||
<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="padding">2</property>
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkRadioButton" id="neutrino_radio_button">
|
||||
<property name="label">Neutrino-MP
|
||||
(experimental)</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">False</property>
|
||||
<property name="xalign">0</property>
|
||||
<property name="active">True</property>
|
||||
<property name="draw_indicator">True</property>
|
||||
<property name="group">enigma_radio_button</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">3</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkSeparator" id="settings_prof_separator2">
|
||||
<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="padding">2</property>
|
||||
<property name="position">4</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="reset_button">
|
||||
<property name="label" translatable="yes">Reset profile</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="margin_top">3</property>
|
||||
<signal name="clicked" handler="on_reset" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="pack_type">end</property>
|
||||
<property name="position">5</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child type="label">
|
||||
<object class="GtkLabel" id="label12">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">Active profile:</property>
|
||||
<property name="xalign">0.20000000298023224</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="enigma_radio_button">
|
||||
<property name="label">Enigma2 </property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">False</property>
|
||||
<property name="xalign">0</property>
|
||||
<property name="active">True</property>
|
||||
<property name="draw_indicator">True</property>
|
||||
<property name="group">neutrino_radio_button</property>
|
||||
<signal name="toggled" 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="GtkRadioButton" id="neutrino_radio_button">
|
||||
<property name="label">Neutrino-MP
|
||||
(experimental)</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">False</property>
|
||||
<property name="xalign">0</property>
|
||||
<property name="active">True</property>
|
||||
<property name="draw_indicator">True</property>
|
||||
<property name="group">enigma_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="GtkButton" id="reset_button">
|
||||
<property name="label" translatable="yes">Reset profile</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="xalign">0.49000000953674316</property>
|
||||
<signal name="clicked" handler="on_reset" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="pack_type">end</property>
|
||||
<property name="position">3</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
@@ -1797,7 +1842,7 @@ dmitry.v.yefremov@gmail.com
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label21">
|
||||
<object class="GtkLabel" id="wait_dialog_label">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">Loading data...</property>
|
||||
|
||||
@@ -3,7 +3,7 @@ import locale
|
||||
from enum import Enum
|
||||
|
||||
from app.commons import run_idle
|
||||
from . import Gtk, UI_RESOURCES_PATH, TEXT_DOMAIN
|
||||
from .uicommons import Gtk, UI_RESOURCES_PATH, TEXT_DOMAIN
|
||||
|
||||
|
||||
class Action(Enum):
|
||||
@@ -21,10 +21,12 @@ class DialogType(Enum):
|
||||
|
||||
|
||||
class WaitDialog:
|
||||
def __init__(self, transient):
|
||||
def __init__(self, transient, text=None):
|
||||
builder, dialog = get_dialog_from_xml(DialogType.WAIT, transient)
|
||||
self._dialog = dialog
|
||||
self._dialog.set_transient_for(transient)
|
||||
if text is not None:
|
||||
builder.get_object("wait_dialog_label").set_text(text)
|
||||
|
||||
def show(self):
|
||||
self._dialog.show()
|
||||
@@ -33,6 +35,10 @@ class WaitDialog:
|
||||
def hide(self):
|
||||
self._dialog.hide()
|
||||
|
||||
@run_idle
|
||||
def destroy(self):
|
||||
self._dialog.destroy()
|
||||
|
||||
|
||||
def show_dialog(dialog_type: DialogType, transient, text=None, options=None, action_type=None, file_filter=None):
|
||||
""" Shows dialogs by name """
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
from app.commons import run_idle, run_task
|
||||
from app.ftp import download_data, DownloadDataType, upload_data
|
||||
from app.properties import Profile
|
||||
from . import Gtk, UI_RESOURCES_PATH, TEXT_DOMAIN
|
||||
from .uicommons import Gtk, UI_RESOURCES_PATH, TEXT_DOMAIN
|
||||
from .dialogs import show_dialog, DialogType, get_message
|
||||
|
||||
|
||||
|
||||
@@ -4,13 +4,14 @@ from urllib.parse import urlparse
|
||||
from app.eparser.ecommons import BqServiceType, Service
|
||||
from app.eparser.iptv import NEUTRINO_FAV_ID_FORMAT, StreamType, ENIGMA2_FAV_ID_FORMAT
|
||||
from app.properties import Profile
|
||||
from . import Gtk, Gdk, TEXT_DOMAIN, UI_RESOURCES_PATH, IPTV_ICON
|
||||
from .uicommons import Gtk, Gdk, TEXT_DOMAIN, UI_RESOURCES_PATH, IPTV_ICON
|
||||
from .dialogs import Action, show_dialog, DialogType
|
||||
from .main_helper import get_base_model
|
||||
|
||||
|
||||
class IptvDialog:
|
||||
_DIGIT_ENTRY_NAME = "digit-entry"
|
||||
_ENIGMA2_REFERENCE = "{}:0:{}:{:X}:{:X}:{:X}:{:X}:0:0:0"
|
||||
|
||||
def __init__(self, transient, view, services, bouquet, profile=Profile.ENIGMA_2, action=Action.ADD):
|
||||
handlers = {"on_entry_changed": self.on_entry_changed,
|
||||
@@ -97,10 +98,10 @@ class IptvDialog:
|
||||
return
|
||||
self._stream_type_combobox.set_active(0 if StreamType(data[0].strip()) is StreamType.DVB_TS else 1)
|
||||
self._srv_type_entry.set_text(data[2])
|
||||
self._sid_entry.set_text(data[3])
|
||||
self._tr_id_entry.set_text(data[4])
|
||||
self._net_id_entry.set_text(data[5])
|
||||
self._namespace_entry.set_text(data[6])
|
||||
self._sid_entry.set_text(str(int(data[3], 16)))
|
||||
self._tr_id_entry.set_text(str(int(data[4], 16)))
|
||||
self._net_id_entry.set_text(str(int(data[5], 16)))
|
||||
self._namespace_entry.set_text(str(int(data[6], 16)))
|
||||
self._url_entry.set_text(data[10].replace("%3a", ":"))
|
||||
self._update_reference_entry()
|
||||
|
||||
@@ -111,12 +112,12 @@ class IptvDialog:
|
||||
|
||||
def _update_reference_entry(self):
|
||||
if self._profile is Profile.ENIGMA_2:
|
||||
self._reference_entry.set_text("{}:0:{}:{}:{}:{}:{}:0:0:0".format(self.get_type(),
|
||||
self._srv_type_entry.get_text(),
|
||||
self._sid_entry.get_text(),
|
||||
self._tr_id_entry.get_text(),
|
||||
self._net_id_entry.get_text(),
|
||||
self._namespace_entry.get_text()))
|
||||
self._reference_entry.set_text(self._ENIGMA2_REFERENCE.format(self.get_type(),
|
||||
self._srv_type_entry.get_text(),
|
||||
int(self._sid_entry.get_text()),
|
||||
int(self._tr_id_entry.get_text()),
|
||||
int(self._net_id_entry.get_text()),
|
||||
int(self._namespace_entry.get_text())))
|
||||
|
||||
def get_type(self):
|
||||
return 1 if self._stream_type_combobox.get_active() == 0 else 4097
|
||||
@@ -139,10 +140,10 @@ class IptvDialog:
|
||||
name = self._name_entry.get_text().strip()
|
||||
fav_id = ENIGMA2_FAV_ID_FORMAT.format(self.get_type(),
|
||||
self._srv_type_entry.get_text(),
|
||||
self._sid_entry.get_text(),
|
||||
self._tr_id_entry.get_text(),
|
||||
self._net_id_entry.get_text(),
|
||||
self._namespace_entry.get_text(),
|
||||
int(self._sid_entry.get_text()),
|
||||
int(self._tr_id_entry.get_text()),
|
||||
int(self._net_id_entry.get_text()),
|
||||
int(self._namespace_entry.get_text()),
|
||||
self._url_entry.get_text().replace(":", "%3a"),
|
||||
name, name)
|
||||
self.update_bouquet_data(name, fav_id)
|
||||
|
||||
Binary file not shown.
File diff suppressed because it is too large
Load Diff
@@ -1,23 +1,15 @@
|
||||
""" This is helper module for ui """
|
||||
from enum import Enum
|
||||
|
||||
import os
|
||||
|
||||
import shutil
|
||||
from gi.repository import GdkPixbuf
|
||||
|
||||
from app.commons import run_task
|
||||
from app.eparser import Service
|
||||
from app.eparser.ecommons import Flag
|
||||
from app.eparser.ecommons import Flag, BouquetService, Bouquet, BqType
|
||||
from app.eparser.enigma.bouquets import BqServiceType, to_bouquet_id
|
||||
from . import Gtk, Gdk, HIDE_ICON, LOCKED_ICON
|
||||
from .dialogs import show_dialog, DialogType, get_chooser_dialog
|
||||
|
||||
|
||||
class ViewTarget(Enum):
|
||||
""" Used for set target view """
|
||||
BOUQUET = 0
|
||||
FAV = 1
|
||||
SERVICES = 2
|
||||
from app.properties import Profile
|
||||
from .uicommons import ViewTarget, BqGenType, Gtk, Gdk, HIDE_ICON, LOCKED_ICON
|
||||
from .dialogs import show_dialog, DialogType, get_chooser_dialog, WaitDialog
|
||||
|
||||
|
||||
# ***************** Markers *******************#
|
||||
@@ -66,34 +58,81 @@ def edit_marker(view, bouquets, selected_bouquet, channels, parent_window):
|
||||
|
||||
# ***************** Movement *******************#
|
||||
|
||||
def move_items(key, view):
|
||||
""" Move items in tree view """
|
||||
def move_items(key, view: Gtk.TreeView):
|
||||
""" Move items in the tree view """
|
||||
selection = view.get_selection()
|
||||
model, paths = selection.get_selected_rows()
|
||||
|
||||
if paths:
|
||||
# for correct down move!
|
||||
if key in (Gdk.KEY_Down, Gdk.KEY_Page_Down, Gdk.KEY_KP_Page_Down):
|
||||
paths = reversed(paths)
|
||||
mod_length = len(model)
|
||||
cursor_path = view.get_cursor()[0]
|
||||
max_path = Gtk.TreePath.new_from_indices((mod_length,))
|
||||
min_path = Gtk.TreePath.new_from_indices((0,))
|
||||
is_tree_store = False
|
||||
|
||||
for path in paths:
|
||||
itr = model.get_iter(path)
|
||||
if key == Gdk.KEY_Down:
|
||||
next_itr = model.iter_next(itr)
|
||||
if next_itr:
|
||||
model.move_after(itr, next_itr)
|
||||
elif key == Gdk.KEY_Up:
|
||||
prev_itr = model.iter_previous(itr)
|
||||
if prev_itr:
|
||||
model.move_before(itr, prev_itr)
|
||||
elif key == Gdk.KEY_Page_Up or key == Gdk.KEY_KP_Page_Up:
|
||||
up_itr = model.get_iter(view.get_cursor()[0])
|
||||
if up_itr:
|
||||
model.move_before(itr, up_itr)
|
||||
elif key == Gdk.KEY_Page_Down or key == Gdk.KEY_KP_Page_Down:
|
||||
down_itr = model.get_iter(view.get_cursor()[0])
|
||||
if down_itr:
|
||||
model.move_after(itr, down_itr)
|
||||
if type(model) is Gtk.TreeStore:
|
||||
parent_paths = list(filter(lambda p: p.get_depth() == 1, paths))
|
||||
if parent_paths:
|
||||
paths = parent_paths
|
||||
min_path = model.get_path(model.get_iter_first())
|
||||
view.collapse_all()
|
||||
if mod_length == len(paths):
|
||||
return
|
||||
else:
|
||||
if not is_some_level(paths):
|
||||
return
|
||||
parent_itr = model.iter_parent(model.get_iter(paths[0]))
|
||||
parent_index = model.get_path(parent_itr)
|
||||
children_num = model.iter_n_children(parent_itr)
|
||||
if key in (Gdk.KEY_Page_Down, Gdk.KEY_KP_Page_Down, Gdk.KEY_End):
|
||||
children_num -= 1
|
||||
min_path = Gtk.TreePath.new_from_string("{}:{}".format(parent_index, 0))
|
||||
max_path = Gtk.TreePath.new_from_string("{}:{}".format(parent_index, children_num))
|
||||
is_tree_store = True
|
||||
|
||||
if key == Gdk.KEY_Up:
|
||||
top_path = Gtk.TreePath(paths[0])
|
||||
top_path.prev()
|
||||
move_up(top_path, model, paths)
|
||||
elif key == Gdk.KEY_Down:
|
||||
down_path = Gtk.TreePath(paths[-1])
|
||||
down_path.next()
|
||||
if down_path < max_path:
|
||||
move_down(down_path, model, paths)
|
||||
else:
|
||||
max_path.prev()
|
||||
move_down(max_path, model, paths)
|
||||
elif key in (Gdk.KEY_Page_Up, Gdk.KEY_KP_Page_Up, Gdk.KEY_Home):
|
||||
move_up(min_path if is_tree_store else cursor_path, model, paths)
|
||||
elif key in (Gdk.KEY_Page_Down, Gdk.KEY_KP_Page_Down, Gdk.KEY_End):
|
||||
move_down(max_path if is_tree_store else cursor_path, model, paths)
|
||||
|
||||
|
||||
def move_up(top_path, model, paths):
|
||||
top_iter = model.get_iter(top_path)
|
||||
for path in paths:
|
||||
itr = model.get_iter(path)
|
||||
model.move_before(itr, top_iter)
|
||||
top_path.next()
|
||||
top_iter = model.get_iter(top_path)
|
||||
|
||||
|
||||
def move_down(down_path, model, paths):
|
||||
top_iter = model.get_iter(down_path)
|
||||
for path in reversed(paths):
|
||||
itr = model.get_iter(path)
|
||||
model.move_after(itr, top_iter)
|
||||
down_path.prev()
|
||||
top_iter = model.get_iter(down_path)
|
||||
|
||||
|
||||
def is_some_level(paths):
|
||||
for i in range(1, len(paths)):
|
||||
prev = paths[i - 1]
|
||||
current = paths[i]
|
||||
if len(prev) != len(current) or (len(prev) == 2 and len(current) == 2 and prev[0] != current[0]):
|
||||
return
|
||||
return True
|
||||
|
||||
|
||||
# ***************** Rename *******************#
|
||||
@@ -411,6 +450,63 @@ def get_picon_pixbuf(path):
|
||||
return GdkPixbuf.Pixbuf.new_from_file_at_scale(filename=path, width=32, height=32, preserve_aspect_ratio=True)
|
||||
|
||||
|
||||
# ***************** Bouquets *********************#
|
||||
|
||||
def gen_bouquets(view, bq_view, transient, gen_type, tv_types, profile, callback):
|
||||
""" Auto-generate and append list of bouquets """
|
||||
fav_id_index = 18
|
||||
index = 6 if gen_type in (BqGenType.PACKAGE, BqGenType.EACH_PACKAGE) else 16 if gen_type in (
|
||||
BqGenType.SAT, BqGenType.EACH_SAT) else 7
|
||||
model, paths = view.get_selection().get_selected_rows()
|
||||
model = get_base_model(model)
|
||||
bq_type = BqType.BOUQUET.value if profile is Profile.NEUTRINO_MP else BqType.TV.value
|
||||
if gen_type in (BqGenType.SAT, BqGenType.PACKAGE, BqGenType.TYPE):
|
||||
if not is_only_one_item_selected(paths, transient):
|
||||
return
|
||||
service = Service(*model[paths][:])
|
||||
if service.service_type not in tv_types:
|
||||
bq_type = BqType.RADIO.value
|
||||
append_bouquets(bq_type, bq_view, callback, fav_id_index, index, model,
|
||||
[service.package if gen_type is BqGenType.PACKAGE else
|
||||
service.pos if gen_type is BqGenType.SAT else service.service_type], profile)
|
||||
else:
|
||||
wait_dialog = WaitDialog(transient)
|
||||
wait_dialog.show()
|
||||
append_bouquets(bq_type, bq_view, callback, fav_id_index, index, model,
|
||||
{row[index] for row in model}, profile, wait_dialog)
|
||||
|
||||
|
||||
@run_task
|
||||
def append_bouquets(bq_type, bq_view, callback, fav_id_index, index, model, names, profile, wait_dialog=None):
|
||||
bq_index = 0 if profile is Profile.ENIGMA_2 else 1
|
||||
bq_view.expand_row(Gtk.TreePath(bq_index), 0)
|
||||
bqs_model = bq_view.get_model()
|
||||
bouquets_names = get_bouquets_names(bqs_model)
|
||||
|
||||
for pos, name in enumerate(sorted(names)):
|
||||
if name not in bouquets_names:
|
||||
services = [BouquetService(None, BqServiceType.DEFAULT, row[fav_id_index], 0)
|
||||
for row in model if row[index] == name]
|
||||
callback(Bouquet(name=name, type=bq_type, services=services, locked=None, hidden=None),
|
||||
bqs_model.get_iter(bq_index))
|
||||
|
||||
if wait_dialog is not None:
|
||||
wait_dialog.destroy()
|
||||
|
||||
|
||||
def get_bouquets_names(model):
|
||||
""" Returns all current bouquets names """
|
||||
bouquets_names = []
|
||||
for row in model:
|
||||
itr = row.iter
|
||||
if model.iter_has_child(itr):
|
||||
num_of_children = model.iter_n_children(itr)
|
||||
for num in range(num_of_children):
|
||||
child_itr = model.iter_nth_child(itr, num)
|
||||
bouquets_names.append(model[child_itr][0])
|
||||
return bouquets_names
|
||||
|
||||
|
||||
# ***************** Others *********************#
|
||||
|
||||
def update_entry_data(entry, dialog, options):
|
||||
@@ -429,5 +525,13 @@ def get_base_model(model):
|
||||
return model
|
||||
|
||||
|
||||
def append_text_to_tview(char, view):
|
||||
""" Appending text and scrolling to a given line in the text view. """
|
||||
buf = view.get_buffer()
|
||||
buf.insert_at_cursor(char)
|
||||
insert = buf.get_insert()
|
||||
view.scroll_to_mark(insert, 0.0, True, 0.0, 1.0)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
pass
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -7,11 +7,11 @@ from gi.repository import GLib, GdkPixbuf
|
||||
|
||||
from app.commons import run_idle, run_task
|
||||
from app.ftp import upload_data, DownloadDataType
|
||||
from app.picons.picons import PiconsParser, parse_providers, Provider, convert_to
|
||||
from app.tools.picons import PiconsParser, parse_providers, Provider, convert_to
|
||||
from app.properties import Profile
|
||||
from . import Gtk, Gdk, UI_RESOURCES_PATH, TEXT_DOMAIN
|
||||
from .uicommons import Gtk, Gdk, UI_RESOURCES_PATH, TEXT_DOMAIN
|
||||
from .dialogs import show_dialog, DialogType, get_message
|
||||
from .main_helper import update_entry_data
|
||||
from .main_helper import update_entry_data, append_text_to_tview
|
||||
|
||||
|
||||
class PiconsDialog:
|
||||
@@ -166,13 +166,7 @@ class PiconsDialog:
|
||||
|
||||
@run_idle
|
||||
def append_output(self, char):
|
||||
buf = self._text_view.get_buffer()
|
||||
buf.insert_at_cursor(char)
|
||||
self.scroll_to_end(buf)
|
||||
|
||||
def scroll_to_end(self, buf):
|
||||
insert = buf.get_insert()
|
||||
self._text_view.scroll_to_mark(insert, 0.0, True, 0.0, 1.0)
|
||||
append_text_to_tview(char, self._text_view)
|
||||
|
||||
def resize(self, path):
|
||||
if self._resize_no_radio_button.get_active():
|
||||
@@ -194,6 +194,11 @@
|
||||
<property name="step_increment">0.10000000000000001</property>
|
||||
<property name="page_increment">10</property>
|
||||
</object>
|
||||
<object class="GtkAdjustment" id="pos_adjustment2">
|
||||
<property name="upper">180</property>
|
||||
<property name="step_increment">0.10000000000000001</property>
|
||||
<property name="page_increment">10</property>
|
||||
</object>
|
||||
<object class="GtkTreeStore" id="satellites_tree_store">
|
||||
<columns>
|
||||
<!-- column-name satelitte -->
|
||||
@@ -451,6 +456,31 @@
|
||||
<property name="homogeneous">False</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkToolButton" id="update_tool_button">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="tooltip_text" translatable="yes">Update from internet</property>
|
||||
<property name="label" translatable="yes">Receive from internet</property>
|
||||
<property name="use_underline">True</property>
|
||||
<property name="stock_id">gtk-refresh</property>
|
||||
<signal name="clicked" handler="on_update" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="homogeneous">True</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkSeparatorToolItem" id="separatortoolitem4">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="homogeneous">False</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkToolButton" id="up_tool_button">
|
||||
<property name="visible">True</property>
|
||||
@@ -739,7 +769,7 @@
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">2</property>
|
||||
<property name="position">4</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
@@ -1388,4 +1418,734 @@
|
||||
<action-widget response="-5">button2</action-widget>
|
||||
</action-widgets>
|
||||
</object>
|
||||
<object class="GtkListStore" id="update_sat_list_store">
|
||||
<columns>
|
||||
<!-- column-name satellite -->
|
||||
<column type="gchararray"/>
|
||||
<!-- column-name position -->
|
||||
<column type="gchararray"/>
|
||||
<!-- column-name type -->
|
||||
<column type="gchararray"/>
|
||||
<!-- column-name url -->
|
||||
<column type="gchararray"/>
|
||||
<!-- column-name selected -->
|
||||
<column type="gboolean"/>
|
||||
</columns>
|
||||
</object>
|
||||
<object class="GtkTreeModelFilter" id="update_sat_list_model_filter">
|
||||
<property name="child_model">update_sat_list_store</property>
|
||||
</object>
|
||||
<object class="GtkTreeModelSort" id="update_sat_list_model_sort">
|
||||
<property name="model">update_sat_list_model_filter</property>
|
||||
</object>
|
||||
<object class="GtkListStore" id="update_source_store">
|
||||
<columns>
|
||||
<!-- column-name source -->
|
||||
<column type="gchararray"/>
|
||||
</columns>
|
||||
<data>
|
||||
<row>
|
||||
<col id="0">FlySat</col>
|
||||
</row>
|
||||
<row>
|
||||
<col id="0">LyngSat</col>
|
||||
</row>
|
||||
</data>
|
||||
</object>
|
||||
<object class="GtkDialog" id="satellites_update_dialog">
|
||||
<property name="can_focus">False</property>
|
||||
<property name="title" translatable="yes">Satellites update</property>
|
||||
<property name="modal">True</property>
|
||||
<property name="default_height">480</property>
|
||||
<property name="destroy_with_parent">True</property>
|
||||
<property name="type_hint">dialog</property>
|
||||
<property name="skip_pager_hint">True</property>
|
||||
<child internal-child="vbox">
|
||||
<object class="GtkBox" id="sat_update_dialog_vbox">
|
||||
<property name="width_request">480</property>
|
||||
<property name="height_request">320</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<property name="spacing">2</property>
|
||||
<child internal-child="action_area">
|
||||
<object class="GtkButtonBox" id="sat_update_dialog_action_area">
|
||||
<property name="can_focus">False</property>
|
||||
<property name="layout_style">center</property>
|
||||
<child>
|
||||
<object class="GtkButton" id="sat_update_close_button">
|
||||
<property name="label">gtk-close</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="use_stock">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</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="GtkBox" id="satellites_update_main_box">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<object class="GtkToolbar" id="sat_update_header_tool_bar">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<child>
|
||||
<object class="GtkToolItem" id="sat_update_source_tool_button">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<child>
|
||||
<object class="GtkLabel" id="source_label">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="halign">start</property>
|
||||
<property name="margin_left">2</property>
|
||||
<property name="label" translatable="yes">Source:</property>
|
||||
<property name="track_visited_links">False</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="homogeneous">True</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkToolItem" id="source_tool_item">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<child>
|
||||
<object class="GtkComboBox" id="source_combo_box">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="model">update_source_store</property>
|
||||
<property name="active">0</property>
|
||||
<child>
|
||||
<object class="GtkCellRendererText" id="source_cellrenderertext"/>
|
||||
<attributes>
|
||||
<attribute name="text">0</attribute>
|
||||
</attributes>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="homogeneous">False</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkToolItem" id="sep_tool_item">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="homogeneous">True</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkToggleToolButton" id="sat_update_filter_button">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="is_important">True</property>
|
||||
<property name="label" translatable="yes">Filter</property>
|
||||
<property name="use_underline">True</property>
|
||||
<property name="stock_id">gtk-select-all</property>
|
||||
<signal name="toggled" handler="on_filter_toggled" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="homogeneous">False</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkToggleToolButton" id="sat_update_find_button">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="is_important">True</property>
|
||||
<property name="label" translatable="yes">Find</property>
|
||||
<property name="use_underline">True</property>
|
||||
<property name="stock_id">gtk-find</property>
|
||||
<signal name="toggled" handler="on_find_toggled" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="homogeneous">False</property>
|
||||
</packing>
|
||||
</child>
|
||||
<style>
|
||||
<class name="primary-toolbar"/>
|
||||
</style>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkInfoBar" id="sat_update_search_info_bar">
|
||||
<property name="app_paintable">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<child internal-child="action_area">
|
||||
<object class="GtkButtonBox" id="infobar-action_area4">
|
||||
<property name="can_focus">False</property>
|
||||
<property name="spacing">6</property>
|
||||
<property name="layout_style">end</property>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child internal-child="content_area">
|
||||
<object class="GtkBox" id="infobar-content_area4">
|
||||
<property name="can_focus">False</property>
|
||||
<property name="homogeneous">True</property>
|
||||
<child>
|
||||
<object class="GtkBox" id="search_bar_box">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<child>
|
||||
<object class="GtkSearchEntry" id="sat_update_search_entry">
|
||||
<property name="width_request">200</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="primary_icon_name">edit-find-symbolic</property>
|
||||
<property name="primary_icon_activatable">False</property>
|
||||
<property name="primary_icon_sensitive">False</property>
|
||||
<signal name="search-changed" handler="on_search" 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="sat_update_search_down_button">
|
||||
<property name="visible">True</property>
|
||||
<property name="sensitive">False</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<signal name="clicked" handler="on_search_down" swapped="no"/>
|
||||
<child>
|
||||
<object class="GtkArrow" id="arrow1">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="arrow_type">down</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="sat_update_search_up_button">
|
||||
<property name="visible">True</property>
|
||||
<property name="sensitive">False</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<signal name="clicked" handler="on_search_up" swapped="no"/>
|
||||
<child>
|
||||
<object class="GtkArrow" id="arrow2">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="arrow_type">up</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkInfoBar" id="sat_update_filter_info_bar">
|
||||
<property name="app_paintable">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<child internal-child="action_area">
|
||||
<object class="GtkButtonBox" id="infobar-action_area3">
|
||||
<property name="can_focus">False</property>
|
||||
<property name="spacing">2</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" id="infobar-content_area3">
|
||||
<property name="can_focus">False</property>
|
||||
<property name="spacing">5</property>
|
||||
<property name="homogeneous">True</property>
|
||||
<child>
|
||||
<object class="GtkGrid" id="source_header_grid">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_bottom">2</property>
|
||||
<property name="column_spacing">2</property>
|
||||
<child>
|
||||
<object class="GtkLabel" id="from_label">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">From:</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkSpinButton" id="from_pos_button">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="input_purpose">number</property>
|
||||
<property name="adjustment">pos_adjustment</property>
|
||||
<property name="digits">1</property>
|
||||
<property name="numeric">True</property>
|
||||
<signal name="changed" handler="on_filter" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkComboBox" id="filter_from_combo_box">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="model">side_store</property>
|
||||
<property name="active">0</property>
|
||||
<signal name="changed" handler="on_filter" swapped="no"/>
|
||||
<child>
|
||||
<object class="GtkCellRendererText" id="from_filter_cellrenderertext"/>
|
||||
<attributes>
|
||||
<attribute name="text">0</attribute>
|
||||
</attributes>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">2</property>
|
||||
<property name="top_attach">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="to_label">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">To:</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">3</property>
|
||||
<property name="top_attach">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkSpinButton" id="to_pos_button">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="input_purpose">number</property>
|
||||
<property name="adjustment">pos_adjustment2</property>
|
||||
<property name="digits">1</property>
|
||||
<property name="numeric">True</property>
|
||||
<signal name="changed" handler="on_filter" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">4</property>
|
||||
<property name="top_attach">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkComboBox" id="filter_to_combo_box">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="model">side_store</property>
|
||||
<property name="active">0</property>
|
||||
<signal name="changed" handler="on_filter" swapped="no"/>
|
||||
<child>
|
||||
<object class="GtkCellRendererText" id="filter_to_cellrenderertext"/>
|
||||
<attributes>
|
||||
<attribute name="text">0</attribute>
|
||||
</attributes>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">5</property>
|
||||
<property name="top_attach">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="filter_apply_button">
|
||||
<property name="label">gtk-apply</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="use_stock">True</property>
|
||||
<signal name="clicked" handler="on_filter" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">6</property>
|
||||
<property name="top_attach">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">3</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkSeparator" id="separator8">
|
||||
<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="padding">2</property>
|
||||
<property name="position">4</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkScrolledWindow" id="sat_update_scrolled_window">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="shadow_type">in</property>
|
||||
<child>
|
||||
<object class="GtkTreeView" id="sat_update_tree_view">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="model">update_sat_list_model_sort</property>
|
||||
<child internal-child="selection">
|
||||
<object class="GtkTreeSelection" id="sat_update_treeview_selection">
|
||||
<property name="mode">multiple</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkTreeViewColumn" id="upd_satellite_column">
|
||||
<property name="title" translatable="yes">Satellite</property>
|
||||
<property name="expand">True</property>
|
||||
<property name="reorderable">True</property>
|
||||
<property name="sort_column_id">0</property>
|
||||
<child>
|
||||
<object class="GtkCellRendererText" id="upd_satellite_cellrenderertext"/>
|
||||
<attributes>
|
||||
<attribute name="text">0</attribute>
|
||||
</attributes>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkTreeViewColumn" id="upd_position_column">
|
||||
<property name="title" translatable="yes">Position</property>
|
||||
<property name="expand">True</property>
|
||||
<property name="reorderable">True</property>
|
||||
<property name="sort_column_id">1</property>
|
||||
<child>
|
||||
<object class="GtkCellRendererText" id="upd_position_cellrenderertext"/>
|
||||
<attributes>
|
||||
<attribute name="text">1</attribute>
|
||||
</attributes>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkTreeViewColumn" id="upd_type_column">
|
||||
<property name="title" translatable="yes">Type</property>
|
||||
<property name="expand">True</property>
|
||||
<property name="reorderable">True</property>
|
||||
<property name="sort_column_id">2</property>
|
||||
<child>
|
||||
<object class="GtkCellRendererText" id="upd_type_cellrenderertext"/>
|
||||
<attributes>
|
||||
<attribute name="text">2</attribute>
|
||||
</attributes>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkTreeViewColumn" id="upd_url_column">
|
||||
<property name="visible">False</property>
|
||||
<property name="title" translatable="yes">Url</property>
|
||||
<child>
|
||||
<object class="GtkCellRendererText" id="upd_url_cellrenderertext"/>
|
||||
<attributes>
|
||||
<attribute name="text">3</attribute>
|
||||
</attributes>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkTreeViewColumn" id="upd_selected_treeviewcolumn">
|
||||
<property name="title" translatable="yes">Selected</property>
|
||||
<property name="reorderable">True</property>
|
||||
<property name="sort_column_id">4</property>
|
||||
<child>
|
||||
<object class="GtkCellRendererToggle" id="upd_selected_cellrenderertoggle">
|
||||
<signal name="toggled" handler="on_selected_toggled" swapped="no"/>
|
||||
</object>
|
||||
<attributes>
|
||||
<attribute name="active">4</attribute>
|
||||
</attributes>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">5</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkSeparator" id="separator7">
|
||||
<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="padding">2</property>
|
||||
<property name="position">6</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkToolbar" id="sat_update_toolbar">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<child>
|
||||
<object class="GtkToolButton" id="cancel_tool_button">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="is_important">True</property>
|
||||
<property name="label" translatable="yes">Cancel</property>
|
||||
<property name="use_underline">True</property>
|
||||
<property name="stock_id">gtk-cancel</property>
|
||||
<signal name="clicked" handler="on_cancel_receive" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="homogeneous">True</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkToolButton" id="update_sat_list_tool_button">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="is_important">True</property>
|
||||
<property name="label" translatable="yes">Update</property>
|
||||
<property name="use_underline">True</property>
|
||||
<property name="stock_id">gtk-refresh</property>
|
||||
<signal name="clicked" handler="on_update_satellites_list" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="homogeneous">True</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkToolButton" id="receive_sat_list_tool_button">
|
||||
<property name="visible">True</property>
|
||||
<property name="sensitive">False</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="is_important">True</property>
|
||||
<property name="label" translatable="yes">Receive</property>
|
||||
<property name="use_underline">True</property>
|
||||
<property name="stock_id">gtk-goto-bottom</property>
|
||||
<signal name="clicked" handler="on_receive_satellites_list" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="homogeneous">True</property>
|
||||
</packing>
|
||||
</child>
|
||||
<style>
|
||||
<class name="primary-toolbar"/>
|
||||
</style>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">7</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkExpander" id="sat_update_expander">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<child>
|
||||
<object class="GtkScrolledWindow" id="text_view_scrolled_window">
|
||||
<property name="height_request">120</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="shadow_type">in</property>
|
||||
<child>
|
||||
<object class="GtkTextView" id="text_view">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="editable">False</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child type="label">
|
||||
<object class="GtkLabel" id="Extra:">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">Extra:</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">8</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkInfoBar" id="sat_update_info_bar">
|
||||
<property name="app_paintable">True</property>
|
||||
<property name="can_focus">False</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" id="infobar-action_area2">
|
||||
<property name="can_focus">False</property>
|
||||
<property name="spacing">6</property>
|
||||
<property name="layout_style">end</property>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child internal-child="content_area">
|
||||
<object class="GtkBox" id="infobar-content_area2">
|
||||
<property name="can_focus">False</property>
|
||||
<property name="spacing">16</property>
|
||||
<child>
|
||||
<object class="GtkLabel" id="info_bar_message_label">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">Info</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</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">9</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkSeparator" id="separator6">
|
||||
<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="padding">2</property>
|
||||
<property name="position">10</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<action-widgets>
|
||||
<action-widget response="-6">sat_update_close_button</action-widget>
|
||||
</action-widgets>
|
||||
</object>
|
||||
</interface>
|
||||
|
||||
@@ -1,22 +1,22 @@
|
||||
import re
|
||||
import time
|
||||
import concurrent.futures
|
||||
from math import fabs
|
||||
|
||||
from app.commons import run_idle
|
||||
from app.commons import run_idle, run_task
|
||||
from app.eparser import get_satellites, write_satellites, Satellite, Transponder
|
||||
from . import Gtk, Gdk, UI_RESOURCES_PATH, TEXT_DOMAIN
|
||||
from app.tools.satellites import SatellitesParser, SatelliteSource
|
||||
from .search import SearchProvider
|
||||
from .uicommons import Gtk, Gdk, UI_RESOURCES_PATH, TEXT_DOMAIN, MOVE_KEYS
|
||||
from .dialogs import show_dialog, DialogType, WaitDialog
|
||||
from .main_helper import move_items, scroll_to
|
||||
from .main_helper import move_items, scroll_to, append_text_to_tview, get_base_model
|
||||
|
||||
|
||||
def show_satellites_dialog(transient, options):
|
||||
dialog = SatellitesDialog(transient, options)
|
||||
dialog.run()
|
||||
dialog.destroy()
|
||||
SatellitesDialog(transient, options).show()
|
||||
|
||||
|
||||
class SatellitesDialog:
|
||||
__slots__ = ["_dialog", "_data_path", "_stores", "_options", "_sat_view", "_wait_dialog"]
|
||||
|
||||
_aggr = [None for x in range(9)] # aggregate
|
||||
|
||||
def __init__(self, transient, options):
|
||||
@@ -26,6 +26,7 @@ class SatellitesDialog:
|
||||
handlers = {"on_open": self.on_open,
|
||||
"on_remove": self.on_remove,
|
||||
"on_save": self.on_save,
|
||||
"on_update": self.on_update,
|
||||
"on_up": self.on_up,
|
||||
"on_down": self.on_down,
|
||||
"on_popup_menu": self.on_popup_menu,
|
||||
@@ -40,8 +41,8 @@ class SatellitesDialog:
|
||||
builder = Gtk.Builder()
|
||||
builder.set_translation_domain(TEXT_DOMAIN)
|
||||
builder.add_objects_from_file(UI_RESOURCES_PATH + "satellites_dialog.glade",
|
||||
("satellites_editor_dialog", "satellites_tree_store",
|
||||
"popup_menu", "add_popup_menu", "add_menu_icon"))
|
||||
("satellites_editor_dialog", "satellites_tree_store", "popup_menu",
|
||||
"add_popup_menu", "add_menu_icon", "receive_menu_icon"))
|
||||
builder.connect_signals(handlers)
|
||||
# Adding custom image for add_menu_tool_button
|
||||
add_menu_tool_button = builder.get_object("add_menu_tool_button")
|
||||
@@ -63,10 +64,9 @@ class SatellitesDialog:
|
||||
6: builder.get_object("mod_store")}
|
||||
self.on_satellites_list_load(self._sat_view.get_model())
|
||||
|
||||
def run(self):
|
||||
@run_idle
|
||||
def show(self):
|
||||
self._dialog.run()
|
||||
|
||||
def destroy(self):
|
||||
self._dialog.destroy()
|
||||
|
||||
def on_resize(self, window):
|
||||
@@ -75,7 +75,7 @@ class SatellitesDialog:
|
||||
self._options["sat_editor_window_size"] = window.get_size()
|
||||
|
||||
def on_quit(self, item):
|
||||
self.destroy()
|
||||
self._dialog.destroy()
|
||||
|
||||
def on_open(self, model):
|
||||
file_filter = Gtk.FileFilter()
|
||||
@@ -125,10 +125,10 @@ class SatellitesDialog:
|
||||
self.on_transponder()
|
||||
elif key == Gdk.KEY_space:
|
||||
pass
|
||||
elif ctrl and key in (Gdk.KEY_Up, Gdk.KEY_Page_Up, Gdk.KEY_KP_Page_Up): # KEY_KP_Page_Up for laptop!
|
||||
move_items(key, self._sat_view)
|
||||
elif ctrl and key in (Gdk.KEY_Down, Gdk.KEY_Page_Down, Gdk.KEY_KP_Page_Down):
|
||||
elif ctrl and key in MOVE_KEYS:
|
||||
move_items(key, self._sat_view)
|
||||
elif key == Gdk.KEY_Left or key == Gdk.KEY_Right:
|
||||
view.do_unselect_all(view)
|
||||
|
||||
@run_idle
|
||||
def on_satellites_list_load(self, model):
|
||||
@@ -147,10 +147,8 @@ class SatellitesDialog:
|
||||
|
||||
@run_idle
|
||||
def append_data(self, model, satellites):
|
||||
for name, flags, pos, transponders in satellites:
|
||||
parent = model.append(None, [name, *self._aggr, flags, pos])
|
||||
for transponder in transponders:
|
||||
model.append(parent, ["Transponder:", *transponder, None, None])
|
||||
for sat in satellites:
|
||||
append_satellite(model, sat)
|
||||
|
||||
def on_add(self, view):
|
||||
""" Common adding """
|
||||
@@ -253,9 +251,7 @@ class SatellitesDialog:
|
||||
returns selected path or None
|
||||
"""
|
||||
model, paths = view.get_selection().get_selected_rows()
|
||||
paths_count = len(paths)
|
||||
|
||||
if paths_count > 1:
|
||||
if len(paths) > 1:
|
||||
show_dialog(DialogType.ERROR, self._dialog, message)
|
||||
return
|
||||
|
||||
@@ -265,9 +261,8 @@ class SatellitesDialog:
|
||||
def on_remove(view):
|
||||
selection = view.get_selection()
|
||||
model, paths = selection.get_selected_rows()
|
||||
itrs = [model.get_iter(path) for path in paths]
|
||||
|
||||
for itr in itrs:
|
||||
for itr in [model.get_iter(path) for path in paths]:
|
||||
model.remove(itr)
|
||||
|
||||
def on_save(self, view):
|
||||
@@ -279,6 +274,11 @@ class SatellitesDialog:
|
||||
model.foreach(self.parse_data, satellites)
|
||||
write_satellites(satellites, self._data_path)
|
||||
|
||||
def on_update(self, item):
|
||||
dialog = SatellitesUpdateDialog(self._dialog, self._sat_view.get_model())
|
||||
dialog.run()
|
||||
dialog.destroy()
|
||||
|
||||
@staticmethod
|
||||
def parse_data(model, path, itr, sats):
|
||||
if model.iter_has_child(itr):
|
||||
@@ -301,6 +301,8 @@ class SatellitesDialog:
|
||||
menu.popup(None, None, None, None, event.button, event.time)
|
||||
|
||||
|
||||
# ***************** Transponder dialog *******************#
|
||||
|
||||
class TransponderDialog:
|
||||
""" Shows dialog for adding or edit transponder """
|
||||
|
||||
@@ -311,9 +313,7 @@ class TransponderDialog:
|
||||
builder = Gtk.Builder()
|
||||
builder.set_translation_domain(TEXT_DOMAIN)
|
||||
builder.add_objects_from_file(UI_RESOURCES_PATH + "satellites_dialog.glade",
|
||||
("transponder_dialog",
|
||||
"pol_store", "fec_store",
|
||||
"mod_store", "system_store",
|
||||
("transponder_dialog", "pol_store", "fec_store", "mod_store", "system_store",
|
||||
"pls_mode_store"))
|
||||
builder.connect_signals(handlers)
|
||||
|
||||
@@ -388,6 +388,8 @@ class TransponderDialog:
|
||||
return True
|
||||
|
||||
|
||||
# ***************** Satellite dialog *******************#
|
||||
|
||||
class SatelliteDialog:
|
||||
""" Shows dialog for adding or edit satellite """
|
||||
|
||||
@@ -429,5 +431,240 @@ class SatelliteDialog:
|
||||
return Satellite(name=name, flags="0", position=pos, transponders=None)
|
||||
|
||||
|
||||
# ***************** Satellite update dialog *******************#
|
||||
|
||||
class SatellitesUpdateDialog:
|
||||
""" Dialog for update satellites over internet """
|
||||
|
||||
def __init__(self, transient, main_model):
|
||||
handlers = {"on_update_satellites_list": self.on_update_satellites_list,
|
||||
"on_receive_satellites_list": self.on_receive_satellites_list,
|
||||
"on_cancel_receive": self.on_cancel_receive,
|
||||
"on_selected_toggled": self.on_selected_toggled,
|
||||
"on_info_bar_close": self.on_info_bar_close,
|
||||
"on_filter_toggled": self.on_filter_toggled,
|
||||
"on_find_toggled": self.on_find_toggled,
|
||||
"on_filter": self.on_filter,
|
||||
"on_search": self.on_search,
|
||||
"on_search_down": self.on_search_down,
|
||||
"on_search_up": self.on_search_up,
|
||||
"on_quit": self.on_quit}
|
||||
|
||||
builder = Gtk.Builder()
|
||||
builder.set_translation_domain(TEXT_DOMAIN)
|
||||
builder.add_objects_from_file(UI_RESOURCES_PATH + "satellites_dialog.glade",
|
||||
("satellites_update_dialog", "update_source_store", "update_sat_list_store",
|
||||
"update_sat_list_model_filter", "update_sat_list_model_sort", "side_store",
|
||||
"pos_adjustment", "pos_adjustment2"))
|
||||
builder.connect_signals(handlers)
|
||||
|
||||
self._dialog = builder.get_object("satellites_update_dialog")
|
||||
self._dialog.set_transient_for(transient)
|
||||
self._main_model = main_model
|
||||
# self._dialog.get_content_area().set_border_width(0)
|
||||
self._sat_view = builder.get_object("sat_update_tree_view")
|
||||
self._source_box = builder.get_object("source_combo_box")
|
||||
self._sat_update_expander = builder.get_object("sat_update_expander")
|
||||
self._text_view = builder.get_object("text_view")
|
||||
self._receive_button = builder.get_object("receive_sat_list_tool_button")
|
||||
self._sat_update_info_bar = builder.get_object("sat_update_info_bar")
|
||||
self._info_bar_message_label = builder.get_object("info_bar_message_label")
|
||||
# Filter
|
||||
self._filter_info_bar = builder.get_object("sat_update_filter_info_bar")
|
||||
self._from_pos_button = builder.get_object("from_pos_button")
|
||||
self._to_pos_button = builder.get_object("to_pos_button")
|
||||
self._filter_from_combo_box = builder.get_object("filter_from_combo_box")
|
||||
self._filter_to_combo_box = builder.get_object("filter_to_combo_box")
|
||||
self._filter_model = builder.get_object("update_sat_list_model_filter")
|
||||
self._filter_model.set_visible_func(self.filter_function)
|
||||
self._filter_positions = (0, 0)
|
||||
# Search
|
||||
self._search_info_bar = builder.get_object("sat_update_search_info_bar")
|
||||
self._search_provider = SearchProvider((self._sat_view,),
|
||||
builder.get_object("sat_update_search_down_button"),
|
||||
builder.get_object("sat_update_search_up_button"))
|
||||
|
||||
self._download_task = False
|
||||
self._parser = None
|
||||
|
||||
def run(self):
|
||||
if self._dialog.run() == Gtk.ResponseType.CANCEL:
|
||||
self._download_task = False
|
||||
return
|
||||
|
||||
def destroy(self):
|
||||
self._dialog.destroy()
|
||||
|
||||
def on_update_satellites_list(self, item):
|
||||
if self._download_task:
|
||||
show_dialog(DialogType.ERROR, self._dialog, "The task is already running!")
|
||||
return
|
||||
|
||||
model = get_base_model(self._sat_view.get_model())
|
||||
model.clear()
|
||||
self._download_task = True
|
||||
src = self._source_box.get_active()
|
||||
if not self._parser:
|
||||
self._parser = SatellitesParser()
|
||||
|
||||
self.get_sat_list(src, self.append_satellites)
|
||||
|
||||
@run_task
|
||||
def get_sat_list(self, src, callback):
|
||||
sats = self._parser.get_satellites_list(SatelliteSource.FLYSAT if src == 0 else SatelliteSource.LYNGSAT)
|
||||
if sats:
|
||||
callback(sats)
|
||||
self._download_task = False
|
||||
|
||||
@run_idle
|
||||
def append_satellites(self, sats):
|
||||
model = get_base_model(self._sat_view.get_model())
|
||||
for sat in sats:
|
||||
model.append(sat)
|
||||
|
||||
@run_task
|
||||
def on_receive_satellites_list(self, item):
|
||||
if self._download_task:
|
||||
show_dialog(DialogType.ERROR, self._dialog, "The task is already running!")
|
||||
return
|
||||
self.receive_satellites()
|
||||
|
||||
@run_task
|
||||
def receive_satellites(self):
|
||||
self._download_task = True
|
||||
self._sat_update_expander.set_expanded(True)
|
||||
self._text_view.get_buffer().set_text("", 0)
|
||||
model = self._sat_view.get_model()
|
||||
start = time.time()
|
||||
|
||||
with concurrent.futures.ProcessPoolExecutor(max_workers=4) as executor:
|
||||
text = "Processing: {}\n"
|
||||
sats = []
|
||||
appender = self.append_output()
|
||||
next(appender)
|
||||
futures = {executor.submit(self._parser.get_satellite, sat[:-1]): sat for sat in [r for r in model if r[4]]}
|
||||
for future in concurrent.futures.as_completed(futures):
|
||||
if not self._download_task:
|
||||
executor.shutdown()
|
||||
appender.send("\nCanceled\n")
|
||||
appender.close()
|
||||
return
|
||||
data = future.result()
|
||||
appender.send(text.format(data[0]))
|
||||
sats.append(data)
|
||||
|
||||
appender.send("-" * 75 + "\n")
|
||||
appender.send("Consumed : {:0.0f}s, {} satellites received.".format(start - time.time(), len(sats)))
|
||||
appender.close()
|
||||
# self.show_info_message(message, Gtk.MessageType.INFO)
|
||||
sats = {s[2]: s for s in sats} # key = position, v = satellite
|
||||
|
||||
for row in self._main_model:
|
||||
pos = row[-1]
|
||||
if pos in sats:
|
||||
sat = sats.pop(pos)
|
||||
itr = row.iter
|
||||
self.update_satellite(itr, row, sat)
|
||||
|
||||
for sat in sats.values():
|
||||
append_satellite(self._main_model, sat)
|
||||
|
||||
self._download_task = False
|
||||
|
||||
@run_idle
|
||||
def update_satellite(self, itr, row, sat):
|
||||
if self._main_model.iter_has_child(itr):
|
||||
children = row.iterchildren()
|
||||
for ch in children:
|
||||
self._main_model.remove(ch.iter)
|
||||
|
||||
for tr in sat[3]:
|
||||
self._main_model.append(itr, ["Transponder:", *tr, None, None])
|
||||
|
||||
def append_output(self):
|
||||
@run_idle
|
||||
def append(t):
|
||||
append_text_to_tview(t, self._text_view)
|
||||
|
||||
while True:
|
||||
text = yield
|
||||
append(text)
|
||||
|
||||
@run_idle
|
||||
def on_cancel_receive(self, item=None):
|
||||
self._download_task = False
|
||||
|
||||
def on_selected_toggled(self, toggle, path):
|
||||
s_model = self._sat_view.get_model()
|
||||
itr = self._filter_model.convert_iter_to_child_iter(s_model.convert_iter_to_child_iter(s_model.get_iter(path)))
|
||||
self._filter_model.get_model().set_value(itr, 4, not toggle.get_active())
|
||||
self.update_receive_button_state(self._filter_model)
|
||||
|
||||
@run_idle
|
||||
def update_receive_button_state(self, model):
|
||||
self._receive_button.set_sensitive((any(r[4] for r in model)))
|
||||
|
||||
@run_idle
|
||||
def show_info_message(self, text, message_type):
|
||||
self._sat_update_info_bar.set_visible(True)
|
||||
self._sat_update_info_bar.set_message_type(message_type)
|
||||
self._info_bar_message_label.set_text(text)
|
||||
|
||||
def on_info_bar_close(self, bar=None, resp=None):
|
||||
self._sat_update_info_bar.set_visible(False)
|
||||
|
||||
def on_find_toggled(self, button: Gtk.ToggleToolButton):
|
||||
self._search_info_bar.set_visible(button.get_active())
|
||||
|
||||
def on_filter_toggled(self, button: Gtk.ToggleToolButton):
|
||||
self._filter_info_bar.set_visible(button.get_active())
|
||||
|
||||
@run_idle
|
||||
def on_filter(self, item):
|
||||
self._filter_positions = self.get_positions()
|
||||
self._filter_model.refilter()
|
||||
|
||||
def filter_function(self, model, iter, data):
|
||||
if self._filter_model is None or self._filter_model == "None":
|
||||
return True
|
||||
|
||||
from_pos, to_pos = self._filter_positions
|
||||
if from_pos == 0 and to_pos == 0:
|
||||
return True
|
||||
|
||||
if from_pos > to_pos:
|
||||
from_pos, to_pos = to_pos, from_pos
|
||||
|
||||
return from_pos <= float(self._parser.get_position(model.get(iter, 1)[0])) <= to_pos
|
||||
|
||||
def get_positions(self):
|
||||
from_pos = round(self._from_pos_button.get_value(), 1) * (-1 if self._filter_from_combo_box.get_active() else 1)
|
||||
to_pos = round(self._to_pos_button.get_value(), 1) * (-1 if self._filter_to_combo_box.get_active() else 1)
|
||||
return from_pos, to_pos
|
||||
|
||||
def on_search(self, entry):
|
||||
self._search_provider.search(entry.get_text())
|
||||
|
||||
def on_search_down(self, item):
|
||||
self._search_provider.on_search_down()
|
||||
|
||||
def on_search_up(self, item):
|
||||
self._search_provider.on_search_up()
|
||||
|
||||
def on_quit(self):
|
||||
self._download_task = False
|
||||
|
||||
|
||||
# ***************** Commons *******************#
|
||||
|
||||
@run_idle
|
||||
def append_satellite(model, sat):
|
||||
""" Common function for append satellite to the model """
|
||||
name, flags, pos, transponders = sat
|
||||
parent = model.append(None, [name, *(None,) * 9, flags, pos])
|
||||
for transponder in transponders:
|
||||
model.append(parent, ["Transponder:", *transponder, None, None])
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
pass
|
||||
|
||||
@@ -2,22 +2,18 @@
|
||||
|
||||
|
||||
class SearchProvider:
|
||||
def __init__(self, srv_view, fav_view, bqs_view, services, bouquets, down_button, up_button):
|
||||
def __init__(self, views, down_button, up_button):
|
||||
self._paths = []
|
||||
self._current_index = -1
|
||||
self._max_indexes = 0
|
||||
self._srv_view = srv_view
|
||||
self._fav_view = fav_view
|
||||
self._bqs_view = bqs_view
|
||||
self._services = services
|
||||
self._bouquets = bouquets
|
||||
self._views = views
|
||||
self._up_button = up_button
|
||||
self._down_button = down_button
|
||||
|
||||
def search(self, text, ):
|
||||
def search(self, text):
|
||||
self._current_index = -1
|
||||
self._paths.clear()
|
||||
for view in self._srv_view, self._fav_view:
|
||||
for view in self._views:
|
||||
model = view.get_model()
|
||||
selection = view.get_selection()
|
||||
selection.unselect_all()
|
||||
|
||||
@@ -6,7 +6,7 @@ from app.eparser import Service
|
||||
from app.eparser.ecommons import MODULATION, Inversion, ROLL_OFF, Pilot, Flag, Pids, POLARIZATION, \
|
||||
get_key_by_value, get_value_by_name, FEC_DEFAULT, PLS_MODE, SERVICE_TYPE
|
||||
from app.properties import Profile
|
||||
from . import Gtk, Gdk, UI_RESOURCES_PATH, HIDE_ICON, TEXT_DOMAIN
|
||||
from .uicommons import Gtk, Gdk, UI_RESOURCES_PATH, HIDE_ICON, TEXT_DOMAIN, CODED_ICON
|
||||
from .dialogs import show_dialog, DialogType, Action
|
||||
from .main_helper import get_base_model
|
||||
|
||||
@@ -31,7 +31,7 @@ class ServiceDetailsDialog:
|
||||
|
||||
_DIGIT_ENTRY_NAME = "digit-entry"
|
||||
|
||||
def __init__(self, transient, options, view, services, bouquets, action=Action.EDIT):
|
||||
def __init__(self, transient, options, srv_view, fav_view, services, bouquets, action=Action.EDIT):
|
||||
handlers = {"on_system_changed": self.on_system_changed,
|
||||
"on_save": self.on_save,
|
||||
"on_create_new": self.on_create_new,
|
||||
@@ -52,7 +52,8 @@ class ServiceDetailsDialog:
|
||||
self._profile = Profile(options["profile"])
|
||||
self._satellites_xml_path = options.get(self._profile.value)["data_dir_path"] + "satellites.xml"
|
||||
self._picons_dir_path = options.get(self._profile.value)["picons_dir_path"]
|
||||
self._services_view = view
|
||||
self._services_view = srv_view
|
||||
self._fav_view = fav_view
|
||||
self._action = action
|
||||
self._old_service = None
|
||||
self._services = services
|
||||
@@ -343,6 +344,7 @@ class ServiceDetailsDialog:
|
||||
if self._old_service.picon_id != service.picon_id:
|
||||
self.update_picon_name(self._old_service.picon_id, service.picon_id)
|
||||
self._current_model.set(self._current_itr, {i: v for i, v in enumerate(service)})
|
||||
self.update_fav_view(self._old_service, service)
|
||||
self._old_service = service
|
||||
|
||||
def update_bouquets(self, fav_id, old_fav_id):
|
||||
@@ -355,6 +357,19 @@ class ServiceDetailsDialog:
|
||||
for i in indexes:
|
||||
bq[i] = fav_id
|
||||
|
||||
@run_idle
|
||||
def update_fav_view(self, old_service, new_service):
|
||||
model = self._fav_view.get_model()
|
||||
for row in filter(lambda r: old_service.fav_id == r[7], model):
|
||||
model.set(row.iter, {1: new_service.coded,
|
||||
2: new_service.service,
|
||||
3: new_service.locked,
|
||||
4: new_service.hide,
|
||||
5: new_service.service_type,
|
||||
6: new_service.pos,
|
||||
7: new_service.fav_id,
|
||||
8: new_service.picon})
|
||||
|
||||
def update_picon_name(self, old_name, new_name):
|
||||
for file_name in os.listdir(self._picons_dir_path):
|
||||
if file_name == old_name:
|
||||
@@ -372,7 +387,7 @@ class ServiceDetailsDialog:
|
||||
freq, rate, pol, fec, system, pos = self.get_transponder_values()
|
||||
return Service(flags_cas=self.get_flags(),
|
||||
transponder_type="s",
|
||||
coded=self._old_service.coded,
|
||||
coded=CODED_ICON if self._cas_entry.get_text() else None,
|
||||
service=self._name_entry.get_text(),
|
||||
locked=self._old_service.locked,
|
||||
hide=HIDE_ICON if self._hide_check_button.get_active() else None,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
from app.properties import write_config, Profile, get_default_settings
|
||||
from . import Gtk, UI_RESOURCES_PATH, TEXT_DOMAIN
|
||||
from .uicommons import Gtk, UI_RESOURCES_PATH, TEXT_DOMAIN
|
||||
from .main_helper import update_entry_data
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ def show_settings_dialog(transient, options):
|
||||
|
||||
|
||||
class SettingsDialog:
|
||||
|
||||
def __init__(self, transient, options):
|
||||
handlers = {"on_data_dir_field_icon_press": self.on_data_dir_field_icon_press,
|
||||
"on_picons_dir_field_icon_press": self.on_picons_dir_field_icon_press,
|
||||
@@ -39,11 +40,14 @@ class SettingsDialog:
|
||||
self._picons_dir_field = builder.get_object("picons_dir_field")
|
||||
self._enigma_radio_button = builder.get_object("enigma_radio_button")
|
||||
self._neutrino_radio_button = builder.get_object("neutrino_radio_button")
|
||||
self._support_ver5_check_button = builder.get_object("support_ver5_check_button")
|
||||
|
||||
self._options = options
|
||||
self._active_profile = options.get("profile")
|
||||
self.set_settings()
|
||||
self._neutrino_radio_button.set_active(Profile(self._active_profile) is Profile.NEUTRINO_MP)
|
||||
profile = Profile(self._active_profile)
|
||||
self._neutrino_radio_button.set_active(profile is Profile.NEUTRINO_MP)
|
||||
self._support_ver5_check_button.set_sensitive(profile is not Profile.NEUTRINO_MP)
|
||||
|
||||
def show(self):
|
||||
response = self._dialog.run()
|
||||
@@ -61,7 +65,9 @@ class SettingsDialog:
|
||||
update_entry_data(entry, self._dialog, self._options.get(self._options.get("profile")))
|
||||
|
||||
def on_profile_changed(self, item):
|
||||
self.set_profile(Profile.ENIGMA_2 if self._enigma_radio_button.get_active() else Profile.NEUTRINO_MP)
|
||||
profile = Profile.ENIGMA_2 if self._enigma_radio_button.get_active() else Profile.NEUTRINO_MP
|
||||
self.set_profile(profile)
|
||||
self._support_ver5_check_button.set_sensitive(profile is Profile.ENIGMA_2)
|
||||
|
||||
def set_profile(self, profile):
|
||||
self._active_profile = profile.value
|
||||
@@ -94,11 +100,13 @@ class SettingsDialog:
|
||||
self._picons_field.set_text(options.get("picons_path", ""))
|
||||
self._data_dir_field.set_text(options.get("data_dir_path", ""))
|
||||
self._picons_dir_field.set_text(options.get("picons_dir_path", ""))
|
||||
if Profile(self._active_profile) is Profile.ENIGMA_2:
|
||||
self._support_ver5_check_button.set_active(options.get("v5_support", False))
|
||||
|
||||
def apply_settings(self, item=None):
|
||||
profile = Profile.ENIGMA_2.value if self._enigma_radio_button.get_active() else Profile.NEUTRINO_MP.value
|
||||
self._active_profile = profile
|
||||
self._options["profile"] = profile
|
||||
profile = Profile.ENIGMA_2 if self._enigma_radio_button.get_active() else Profile.NEUTRINO_MP
|
||||
self._active_profile = profile.value
|
||||
self._options["profile"] = self._active_profile
|
||||
options = self._options.get(self._active_profile)
|
||||
options["host"] = self._host_field.get_text()
|
||||
options["port"] = self._port_field.get_text()
|
||||
@@ -114,6 +122,8 @@ class SettingsDialog:
|
||||
options["picons_path"] = self._picons_field.get_text()
|
||||
options["data_dir_path"] = self._data_dir_field.get_text()
|
||||
options["picons_dir_path"] = self._picons_dir_field.get_text()
|
||||
if profile is Profile.ENIGMA_2:
|
||||
options["v5_support"] = self._support_ver5_check_button.get_active()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
52
app/ui/uicommons.py
Normal file
52
app/ui/uicommons.py
Normal file
@@ -0,0 +1,52 @@
|
||||
import locale
|
||||
import os
|
||||
|
||||
import gi
|
||||
from enum import Enum
|
||||
|
||||
gi.require_version('Gtk', '3.0')
|
||||
from gi.repository import Gtk, Gdk
|
||||
|
||||
# path to *.glade files
|
||||
UI_RESOURCES_PATH = "app/ui/" if os.path.exists("app/ui/") else "/usr/share/demoneditor/app/ui/"
|
||||
|
||||
# translation
|
||||
TEXT_DOMAIN = "demon-editor"
|
||||
if UI_RESOURCES_PATH == "app/ui/":
|
||||
LANG_DIR = UI_RESOURCES_PATH + "lang"
|
||||
locale.bindtextdomain(TEXT_DOMAIN, UI_RESOURCES_PATH + "lang")
|
||||
|
||||
theme = Gtk.IconTheme.get_default()
|
||||
_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("system-lock-screen", 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.load_icon("emblem-shared", 16, 0) else None
|
||||
|
||||
# keys for move in lists
|
||||
MOVE_KEYS = (Gdk.KEY_Up, Gdk.KEY_Page_Up, Gdk.KEY_Down, Gdk.KEY_Page_Down, Gdk.KEY_Home, Gdk.KEY_End,
|
||||
Gdk.KEY_KP_Page_Up, Gdk.KEY_KP_Page_Down) # KEY_KP_Page_Up(Down) for laptop!
|
||||
|
||||
|
||||
class ViewTarget(Enum):
|
||||
""" Used for set target view """
|
||||
BOUQUET = 0
|
||||
FAV = 1
|
||||
SERVICES = 2
|
||||
|
||||
|
||||
class BqGenType(Enum):
|
||||
""" Bouquet generation type """
|
||||
SAT = 0
|
||||
EACH_SAT = 1
|
||||
PACKAGE = 2
|
||||
EACH_PACKAGE = 3
|
||||
TYPE = 4
|
||||
EACH_TYPE = 5
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
pass
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/bin/bash
|
||||
VER="0.3.0_Pre-alpha"
|
||||
VER="0.3.2_Pre-alpha"
|
||||
B_PATH="dist/DemonEditor"
|
||||
DEB_PATH="$B_PATH/usr/share/demoneditor"
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
Package: DemonEditor
|
||||
Version: 0.3.0-Pre-alpha
|
||||
Version: 0.3.2-Pre-alpha
|
||||
Section: utils
|
||||
Priority: optional
|
||||
Architecture: all
|
||||
|
||||
@@ -3,23 +3,26 @@
|
||||
## 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)
|
||||
|
||||
### Keyboard shortcuts:
|
||||
Ctrl + X, C, V, Up, Down, PageUp, PageDown, S, T, E, L, H, Space; Insert, Delete, F2.
|
||||
Insert - copies the selected channels from the main list to the bouquet or inserts (creates) a new bouquet.
|
||||
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.
|
||||
Left/Right - remove selection.
|
||||
|
||||
**Ctrl + X, C, V, Up, Down, PageUp, PageDown, Home, End, S, T, E, L, H, Space; Insert, Delete, F2, Enter, P.**
|
||||
* **Insert** - copies the selected channels from the main list to the bouquet or inserts (creates) a new bouquet.
|
||||
* **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.
|
||||
* **P** - enable/disable preview mode for IPTV in the bouquet list.
|
||||
* **Enter** - start play IPTV or other stream in the bouquet list.
|
||||
* **Space** - select/deselect.
|
||||
* **Left/Right** - remove selection.
|
||||
* **Ctrl + Up, Down, PageUp, PageDown, Home, End** - move selected items in the list.
|
||||
### Extra:
|
||||
Multiple selections in lists only with Space key (as in file managers).
|
||||
Ability to import IPTV into bouquet (Neutrino WEBTV) from m3u files.
|
||||
Tool for downloading picons from lyngsat.com.
|
||||
* Multiple selections in lists only with Space key (as in file managers).
|
||||
* Ability to import IPTV into bouquet (Neutrino WEBTV) from m3u files.
|
||||
* Ability to download picons and update satellites (transponders) from web.
|
||||
* Preview (playing) IPTV or other streams directly from the bouquet list(should be installed VLC).
|
||||
### Minimum requirements:
|
||||
Python >= 3.5.2 and GTK+ 3 with PyGObject bindings.
|
||||
#### Note.
|
||||
@@ -28,6 +31,6 @@ To create a simple debian package, you can use the build-deb.sh
|
||||
Tests only in image based on OpenPLi or last BPanther(neutrino) images with GM 990 Spark Reloaded receiver
|
||||
in my preferred linux distro (Last Linux Mint 18.* - MATE 64-bit)!
|
||||
|
||||
#### Terrestrial and cable channels at the moment are not supported!
|
||||
**Terrestrial and cable channels at the moment are not supported!**
|
||||
|
||||
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -85,9 +85,6 @@ msgstr "Пропустить"
|
||||
msgid "Hide/Skip On/Off Ctrl + H"
|
||||
msgstr "Скрыть/Пропустить Вкл/Выкл Ctrl + H"
|
||||
|
||||
msgid "IPTV"
|
||||
msgstr ""
|
||||
|
||||
msgid "Add IPTV or stream service"
|
||||
msgstr "Добавить IPTV или поток"
|
||||
|
||||
@@ -115,21 +112,42 @@ msgstr "Новый"
|
||||
msgid "New bouquet"
|
||||
msgstr "Новый букет"
|
||||
|
||||
msgid "Create bouquet"
|
||||
msgstr "Создать букет"
|
||||
|
||||
msgid "For current satellite"
|
||||
msgstr "Для текущего спутника"
|
||||
|
||||
msgid "For current package"
|
||||
msgstr "Для текущего пакета"
|
||||
|
||||
msgid "For current type"
|
||||
msgstr "Для текущего типа"
|
||||
|
||||
msgid "For each satellite"
|
||||
msgstr "Для каждого спутника"
|
||||
|
||||
msgid "For each package"
|
||||
msgstr "Для каждого пакета"
|
||||
|
||||
msgid "For each type"
|
||||
msgstr "Для каждого типа"
|
||||
|
||||
msgid "Open"
|
||||
msgstr "Открыть"
|
||||
|
||||
msgid "Parent lock On/Off Ctrl + L"
|
||||
msgstr "Родительский замок Вкл/Выкл Ctrl + L"
|
||||
|
||||
msgid "Paste"
|
||||
msgstr ""
|
||||
|
||||
msgid "Picons"
|
||||
msgstr "Пиконы"
|
||||
|
||||
msgid "Picons loader"
|
||||
msgid "Picons downloader"
|
||||
msgstr "Загрузчик пиконов"
|
||||
|
||||
msgid "Satellites downloader"
|
||||
msgstr "Загрузчик спутников"
|
||||
|
||||
msgid "Preferences"
|
||||
msgstr "Настройки"
|
||||
|
||||
@@ -160,9 +178,6 @@ msgstr "Фильтр сервисов"
|
||||
msgid "Settings"
|
||||
msgstr "Настройки"
|
||||
|
||||
msgid "TV"
|
||||
msgstr ""
|
||||
|
||||
msgid "Up"
|
||||
msgstr "Переместить вверх"
|
||||
|
||||
@@ -238,8 +253,8 @@ msgstr "Спутники"
|
||||
msgid "Satellites.xml file:"
|
||||
msgstr "Файл satellites.xml:"
|
||||
|
||||
msgid "Select"
|
||||
msgstr ""
|
||||
msgid "Selected"
|
||||
msgstr "Выбор"
|
||||
|
||||
msgid "Send"
|
||||
msgstr "Отправить"
|
||||
@@ -272,6 +287,9 @@ msgstr "Дополнительно:"
|
||||
msgid "Load providers"
|
||||
msgstr "Загрузить провайдеров"
|
||||
|
||||
msgid "Providers"
|
||||
msgstr "Провайдеры"
|
||||
|
||||
msgid "Receive picons"
|
||||
msgstr "Загрузить пиконы"
|
||||
|
||||
@@ -382,6 +400,21 @@ msgstr "Поток"
|
||||
msgid "Description"
|
||||
msgstr "Описание"
|
||||
|
||||
msgid "Source:"
|
||||
msgstr "Источник:"
|
||||
|
||||
msgid "Cancel"
|
||||
msgstr "Отмена"
|
||||
|
||||
msgid "Update"
|
||||
msgstr "Обновить"
|
||||
|
||||
msgid "Filter"
|
||||
msgstr "Фильтр"
|
||||
|
||||
msgid "Find"
|
||||
msgstr "Поиск"
|
||||
|
||||
# IPTV dialog
|
||||
msgid "Stream data"
|
||||
msgstr "Данные потока"
|
||||
@@ -453,6 +486,12 @@ msgstr "Не выбран файл satellites.xml!"
|
||||
msgid "Error. Verify the data!"
|
||||
msgstr "Ошибка. Проверьте данные!"
|
||||
|
||||
msgid "Operation not allowed in this context!"
|
||||
msgstr "Недопустимая операция в данном контексте!"
|
||||
|
||||
msgid "No VLC is found. Check that it is installed!"
|
||||
msgstr "VLC не найден. Проверьте, что он установлен!"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user