mirror of
https://github.com/DYefremov/DemonEditor.git
synced 2026-05-09 12:56:19 +02:00
Compare commits
43 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cf3c05f324 | ||
|
|
030b7c4957 | ||
|
|
d7ed3e20a4 | ||
|
|
c69b0ac9e1 | ||
|
|
c5c88a8958 | ||
|
|
24c064b450 | ||
|
|
240d724b59 | ||
|
|
5b410241a9 | ||
|
|
a6ffe4999a | ||
|
|
f67a79e869 | ||
|
|
d37c088112 | ||
|
|
adf117c88d | ||
|
|
98da7acd96 | ||
|
|
c82763081a | ||
|
|
1a39557964 | ||
|
|
c274c9e91d | ||
|
|
dd1ec89592 | ||
|
|
8ce9823a0c | ||
|
|
cc08fa8096 | ||
|
|
dfdf0f9d3a | ||
|
|
a3cf34ba2a | ||
|
|
0f02055c0c | ||
|
|
347dd15233 | ||
|
|
6dccdc258a | ||
|
|
c8c9a0bbf0 | ||
|
|
4dfa126795 | ||
|
|
9a0aa1e28f | ||
|
|
0fb708ca9b | ||
|
|
74d4c9e038 | ||
|
|
f229169d29 | ||
|
|
8263f39591 | ||
|
|
cf25057658 | ||
|
|
300fedf684 | ||
|
|
5e954c7ec9 | ||
|
|
d723ecd7f7 | ||
|
|
37d4cbe1f4 | ||
|
|
beabac5c2c | ||
|
|
4399664bd4 | ||
|
|
de497d1adf | ||
|
|
3077a1c536 | ||
|
|
f793666c88 | ||
|
|
5aade90d96 | ||
|
|
3f226e0090 |
@@ -1,8 +1,8 @@
|
|||||||
[Desktop Entry]
|
[Desktop Entry]
|
||||||
Version=1.0
|
Version=1.0
|
||||||
Name=DemonEditor
|
Name=DemonEditor
|
||||||
Comment=Channels and satellites editor for Enigma2
|
Comment=Channels and satellites list editor for Enigma2
|
||||||
Comment[ru]=Редактор каналов и спутников для Enigma2
|
Comment[ru]=Редактор списка каналов и спутников для Enigma2
|
||||||
Icon=accessories-text-editor
|
Icon=accessories-text-editor
|
||||||
Exec=bash -c 'cd $(dirname %k) && ./start.py'
|
Exec=bash -c 'cd $(dirname %k) && ./start.py'
|
||||||
Terminal=false
|
Terminal=false
|
||||||
|
|||||||
2
LICENSE
2
LICENSE
@@ -1,6 +1,6 @@
|
|||||||
MIT License
|
MIT License
|
||||||
|
|
||||||
Copyright (c) 2017 Dmitriy Yefremov
|
Copyright (c) 2018 Dmitriy Yefremov
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|||||||
36
README.md
36
README.md
@@ -1,23 +1,29 @@
|
|||||||
# DemonEditor
|
# DemonEditor
|
||||||
Enigma2 channel and satellites list editor for GNU/Linux.
|
|
||||||
Focused on the convenience of working in lists from the keyboard.
|
|
||||||
The mouse is also fully supported (Drag and Drop etc)
|
|
||||||
|
|
||||||
Keyboard shortcuts:
|
Enigma2 channel and satellites list editor for GNU/Linux.
|
||||||
Ctrl + X, C, V, Up, Down, PageUp, PageDown, S, T, E, L, H, Space; Insert, Delete, F2.
|
Experimental support of Neutrino-MP or others on the same basis (BPanther, etc).
|
||||||
Insert - copies the selected channels from the main list to the bouquet or inserts (creates) a new bouquet.
|
Focused on the convenience of working in lists from the keyboard. The mouse is also fully supported (Drag and Drop etc)
|
||||||
Ctrl + X - only in bouquet list.
|
|
||||||
Ctrl + C - only in services list. Clipboard is "rubber". There is an accumulation before the insertion!
|
|
||||||
F2 - rename the bouquet.
|
|
||||||
Ctrl + S, T, E in Satellites edit tool for create and edit satellite or transponder.
|
|
||||||
Ctrl + L - parental lock.
|
|
||||||
Ctrl + H - hide/skip.
|
|
||||||
|
|
||||||
Ability to import IPTV into bouquet from m3u files!
|
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, F2 - edit/rename.
|
||||||
|
Ctrl + S, T, E in Satellites edit tool for create and edit satellite or transponder.
|
||||||
|
Ctrl + L - parental lock.
|
||||||
|
Ctrl + H - hide/skip.
|
||||||
|
|
||||||
Tests only on OpenPLi based image with GM 990 Spark Reloaded receiver
|
Extra:
|
||||||
|
Ability to import IPTV into bouquet from m3u files(Enigma2 only)!
|
||||||
|
Tool for downloading picons from lyngsat.com.
|
||||||
|
|
||||||
|
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)!
|
in my preferred linux distro (Last Linux Mint 18.* - MATE 64-bit)!
|
||||||
|
|
||||||
Minimum requirements: Python >= 3.5.2 and GTK+ 3 with PyGObject bindings.
|
Minimum requirements: Python >= 3.5.2 and GTK+ 3 with PyGObject bindings.
|
||||||
|
|
||||||
Terrestrial and cable channels at the moment are not supported!
|
Terrestrial and cable channels at the moment are not supported!
|
||||||
|
|
||||||
|
Note. To create a simple debian package, you can use the build-deb.sh
|
||||||
|
|
||||||
|
|||||||
@@ -1,39 +0,0 @@
|
|||||||
""" This module only for common constants """
|
|
||||||
from enum import Enum
|
|
||||||
|
|
||||||
|
|
||||||
class Type(Enum):
|
|
||||||
""" Types of DVB transponders """
|
|
||||||
Satellite = "s"
|
|
||||||
Terestrial = "t"
|
|
||||||
Cable = "c"
|
|
||||||
|
|
||||||
|
|
||||||
class FLAG(Enum):
|
|
||||||
""" Service flags """
|
|
||||||
HIDE = "f:0002"
|
|
||||||
LOCK = "f:0008"
|
|
||||||
NEW = "f:0040"
|
|
||||||
|
|
||||||
|
|
||||||
POLARIZATION = {"0": "H", "1": "V", "2": "L", "3": "R"}
|
|
||||||
|
|
||||||
PLS_MODE = {"0": "Root", "1": "Gold", "2": "Combo"}
|
|
||||||
|
|
||||||
FEC = {"0": "Auto", "1": "1/2", "2": "2/3",
|
|
||||||
"3": "3/4", "4": "5/6", "5": "7/8",
|
|
||||||
"6": "8/9", "7": "3/5", "8": "4/5",
|
|
||||||
"9": "9/10", "15": None}
|
|
||||||
|
|
||||||
SYSTEM = {"0": "DVB-S", "1": "DVB-S2"}
|
|
||||||
|
|
||||||
MODULATION = {"0": "Auto", "1": "QPSK", "2": "8PSK", "3": "16APSK", "5": "32APSK"}
|
|
||||||
|
|
||||||
SERVICE_TYPE = {"-2": "Unknown", "1": "TV", "2": "Radio", "3": "Data",
|
|
||||||
"10": "Radio", "12": "Data", "22": "TV", "25": "TV (HD)",
|
|
||||||
"136": "Data", "139": "Data"}
|
|
||||||
|
|
||||||
CAS = {"C:2600": "BISS", "C:0b00": "Conax", "C:0b01": "Conax", "C:0b02": "Conax", "C:0baa": "Conax", "C:0602": "Irdeto",
|
|
||||||
"C:0604": "Irdeto", "C:0606": "Irdeto", "C:0608": "Irdeto", "C:0622": "Irdeto", "C:0626": "Irdeto",
|
|
||||||
"C:0664": "Irdeto", "C:0614": "Irdeto", "C:0692": "Irdeto", "C:1801": "Nagravision", "C:0500": "Viaccess",
|
|
||||||
"C:0E00": "PowerVu", "C:4ae0": "DRE-Crypt", "C:4ae1": "DRE-Crypt", "C:7be1": "DRE-Crypt"}
|
|
||||||
@@ -1,10 +1,44 @@
|
|||||||
from .lamedb import get_channels, write_channels, Channel
|
from app.commons import run_task
|
||||||
from .bouquets import get_bouquets, write_bouquets, to_bouquet_id, Bouquet, Bouquets
|
from app.properties import Profile
|
||||||
from .satxml import get_satellites, write_satellites, Satellite, Transponder
|
from .ecommons import Service, Satellite, Transponder, Bouquet, Bouquets
|
||||||
from .blacklist import get_blacklist, write_blacklist
|
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
|
||||||
from .iptv import parse_m3u
|
from .iptv import parse_m3u
|
||||||
|
from .neutrino.bouquets import get_bouquets as get_neutrino_bouquets, write_bouquets as write_neutrino_bouquets
|
||||||
|
from .neutrino.services import get_services as get_neutrino_services, write_services as write_neutrino_services
|
||||||
|
from .satxml import get_satellites, write_satellites
|
||||||
|
|
||||||
|
|
||||||
|
def get_services(data_path, profile):
|
||||||
|
if profile is Profile.ENIGMA_2:
|
||||||
|
return get_enigma_services(data_path)
|
||||||
|
elif profile is Profile.NEUTRINO_MP:
|
||||||
|
return get_neutrino_services(data_path)
|
||||||
|
|
||||||
|
|
||||||
|
@run_task
|
||||||
|
def write_services(path, channels, profile):
|
||||||
|
if profile is Profile.ENIGMA_2:
|
||||||
|
write_enigma_services(path, channels)
|
||||||
|
elif profile is Profile.NEUTRINO_MP:
|
||||||
|
write_neutrino_services(path, channels)
|
||||||
|
|
||||||
|
|
||||||
|
def get_bouquets(path, profile):
|
||||||
|
if profile is Profile.ENIGMA_2:
|
||||||
|
return get_enigma_bouquets(path)
|
||||||
|
elif profile is Profile.NEUTRINO_MP:
|
||||||
|
return get_neutrino_bouquets(path)
|
||||||
|
|
||||||
|
|
||||||
|
@run_task
|
||||||
|
def write_bouquets(path, bouquets, profile):
|
||||||
|
if profile is Profile.ENIGMA_2:
|
||||||
|
write_enigma_bouquets(path, bouquets)
|
||||||
|
elif profile is Profile.NEUTRINO_MP:
|
||||||
|
write_neutrino_bouquets(path, bouquets)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|||||||
73
app/eparser/ecommons.py
Normal file
73
app/eparser/ecommons.py
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
""" Common elements module """
|
||||||
|
from collections import namedtuple
|
||||||
|
from enum import Enum
|
||||||
|
|
||||||
|
Service = namedtuple("Service", ["flags_cas", "transponder_type", "coded", "service", "locked", "hide",
|
||||||
|
"package", "service_type", "ssid", "freq", "rate", "pol", "fec",
|
||||||
|
"system", "pos", "data_id", "fav_id", "transponder"])
|
||||||
|
|
||||||
|
|
||||||
|
# ***************** Bouquets *******************#
|
||||||
|
|
||||||
|
class BqServiceType(Enum):
|
||||||
|
DEFAULT = "DEFAULT"
|
||||||
|
IPTV = "IPTV"
|
||||||
|
MARKER = "MARKER" # 64
|
||||||
|
|
||||||
|
|
||||||
|
Bouquet = namedtuple("Bouquet", ["name", "type", "services", "locked", "hidden"])
|
||||||
|
Bouquets = namedtuple("Bouquets", ["name", "type", "bouquets"])
|
||||||
|
BouquetService = namedtuple("BouquetService", ["name", "type", "data", "num"])
|
||||||
|
|
||||||
|
# ***************** Satellites *******************#
|
||||||
|
|
||||||
|
Satellite = namedtuple("Satellite", ["name", "flags", "position", "transponders"])
|
||||||
|
|
||||||
|
Transponder = namedtuple("Transponder", ["frequency", "symbol_rate", "polarization", "fec_inner",
|
||||||
|
"system", "modulation", "pls_mode", "pls_code", "is_id"])
|
||||||
|
|
||||||
|
|
||||||
|
class Type(Enum):
|
||||||
|
""" Types of DVB transponders """
|
||||||
|
Satellite = "s"
|
||||||
|
Terestrial = "t"
|
||||||
|
Cable = "c"
|
||||||
|
|
||||||
|
|
||||||
|
class FLAG(Enum):
|
||||||
|
""" Service flags """
|
||||||
|
KEEP = 1 # Do not automatically update the services parameters.
|
||||||
|
HIDE = 2
|
||||||
|
PIDS = 4 # Always use the cached instead of current pids.
|
||||||
|
LOCK = 8
|
||||||
|
NEW = 40 # Marked as new at the last scan
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def hide_values():
|
||||||
|
return 2, 3, 6, 7, 10, 42, 43, 46, 47
|
||||||
|
|
||||||
|
|
||||||
|
POLARIZATION = {"0": "H", "1": "V", "2": "L", "3": "R"}
|
||||||
|
|
||||||
|
PLS_MODE = {"0": "Root", "1": "Gold", "2": "Combo"}
|
||||||
|
|
||||||
|
FEC = {"0": "Auto", "1": "1/2", "2": "2/3", "3": "3/4", "4": "5/6", "5": "7/8", "6": "8/9", "7": "3/5", "8": "4/5",
|
||||||
|
"9": "9/10", "10": "1/2", "11": "2/3", "12": "3/4", "13": "5/6", "14": "7/8", "15": "8/9", "16": "3/5",
|
||||||
|
"17": "4/5", "18": "9/10", "19": "1/2", "20": "2/3", "21": "3/4", "22": "5/6", "23": "7/8", "24": "8/9",
|
||||||
|
"25": "3/5", "26": "4/5", "27": "9/10", "28": "Auto"}
|
||||||
|
|
||||||
|
SYSTEM = {"0": "DVB-S", "1": "DVB-S2"}
|
||||||
|
|
||||||
|
MODULATION = {"0": "Auto", "1": "QPSK", "2": "8PSK", "3": "16APSK", "5": "32APSK"}
|
||||||
|
|
||||||
|
SERVICE_TYPE = {"-2": "Unknown", "1": "TV", "2": "Radio", "3": "Data",
|
||||||
|
"10": "Radio", "12": "Data", "22": "TV", "25": "TV (HD)",
|
||||||
|
"136": "Data", "139": "Data"}
|
||||||
|
|
||||||
|
CAS = {"C:2600": "BISS", "C:0b00": "Conax", "C:0b01": "Conax", "C:0b02": "Conax", "C:0baa": "Conax", "C:0602": "Irdeto",
|
||||||
|
"C:0604": "Irdeto", "C:0606": "Irdeto", "C:0608": "Irdeto", "C:0622": "Irdeto", "C:0626": "Irdeto",
|
||||||
|
"C:0664": "Irdeto", "C:0614": "Irdeto", "C:0692": "Irdeto", "C:1801": "Nagravision", "C:0500": "Viaccess",
|
||||||
|
"C:0E00": "PowerVu", "C:4ae0": "DRE-Crypt", "C:4ae1": "DRE-Crypt", "C:7be1": "DRE-Crypt"}
|
||||||
|
|
||||||
|
# 'on' attribute 0070(hex) = 112(int) = ONID(ONID-TID on www.lyngsat.com)
|
||||||
|
PROVIDER = {112: "HTB+", 253: "Tricolor TV"}
|
||||||
0
app/eparser/enigma/__init__.py
Normal file
0
app/eparser/enigma/__init__.py
Normal file
@@ -1,28 +1,16 @@
|
|||||||
""" Module for parsing bouquets """
|
""" Module for parsing bouquets """
|
||||||
from collections import namedtuple
|
from app.eparser.ecommons import BqServiceType, BouquetService, Bouquets, Bouquet
|
||||||
from enum import Enum
|
|
||||||
|
|
||||||
_BOUQUETS_PATH = "../data/"
|
_BOUQUETS_PATH = "../data/"
|
||||||
_TV_ROOT_FILE_NAME = "bouquets.tv"
|
_TV_ROOT_FILE_NAME = "bouquets.tv"
|
||||||
_RADIO_ROOT_FILE_NAME = "bouquets.radio"
|
_RADIO_ROOT_FILE_NAME = "bouquets.radio"
|
||||||
|
|
||||||
|
|
||||||
class BqServiceType(Enum):
|
|
||||||
DEFAULT = "DEFAULT"
|
|
||||||
IPTV = "IPTV"
|
|
||||||
MARKER = "MARKER" # 64
|
|
||||||
|
|
||||||
|
|
||||||
Bouquet = namedtuple("Bouquet", ["name", "type", "services"])
|
|
||||||
Bouquets = namedtuple("Bouquets", ["name", "type", "bouquets"])
|
|
||||||
BouquetService = namedtuple("BouquetService", ["name", "type", "data", "num"])
|
|
||||||
|
|
||||||
|
|
||||||
def get_bouquets(path):
|
def get_bouquets(path):
|
||||||
return parse_bouquets(path, "bouquets.tv", "tv"), parse_bouquets(path, "bouquets.radio", "radio")
|
return parse_bouquets(path, "bouquets.tv", "tv"), parse_bouquets(path, "bouquets.radio", "radio")
|
||||||
|
|
||||||
|
|
||||||
def write_bouquets(path, bouquets, bouquets_services):
|
def write_bouquets(path, bouquets):
|
||||||
srv_line = '#SERVICE 1:7:1:0:0:0:0:0:0:0:FROM BOUQUET "userbouquet.{}.{}" ORDER BY bouquet\n'
|
srv_line = '#SERVICE 1:7:1:0:0:0:0:0:0:0:FROM BOUQUET "userbouquet.{}.{}" ORDER BY bouquet\n'
|
||||||
line = []
|
line = []
|
||||||
|
|
||||||
@@ -98,7 +86,11 @@ def parse_bouquets(path, bq_name, bq_type):
|
|||||||
bouquets = Bouquets(name.strip(), bq_type, [])
|
bouquets = Bouquets(name.strip(), bq_type, [])
|
||||||
if bouquets and "#SERVICE" in line:
|
if bouquets and "#SERVICE" in line:
|
||||||
name = line.split(".")[1]
|
name = line.split(".")[1]
|
||||||
bouquets[2].append(Bouquet(name=name, type=bq_type, services=get_bouquet(path, name, bq_type)))
|
bouquets[2].append(Bouquet(name=name,
|
||||||
|
type=bq_type,
|
||||||
|
services=get_bouquet(path, name, bq_type),
|
||||||
|
locked=None,
|
||||||
|
hidden=None))
|
||||||
|
|
||||||
return bouquets
|
return bouquets
|
||||||
|
|
||||||
@@ -3,42 +3,36 @@
|
|||||||
Currently implemented only for satellite channels!!!
|
Currently implemented only for satellite channels!!!
|
||||||
Description of format taken from here: http://www.satsupreme.com/showthread.php/194074-Lamedb-format-explained
|
Description of format taken from here: http://www.satsupreme.com/showthread.php/194074-Lamedb-format-explained
|
||||||
"""
|
"""
|
||||||
from collections import namedtuple
|
|
||||||
|
|
||||||
from app.commons import log
|
from app.commons import log
|
||||||
from app.eparser.__constants import POLARIZATION, SYSTEM, FEC, SERVICE_TYPE
|
|
||||||
from app.ui import CODED_ICON, LOCKED_ICON, HIDE_ICON
|
from app.ui import CODED_ICON, LOCKED_ICON, HIDE_ICON
|
||||||
from .blacklist import get_blacklist
|
from .blacklist import get_blacklist
|
||||||
|
from ..ecommons import Service, POLARIZATION, SYSTEM, FEC, SERVICE_TYPE, FLAG
|
||||||
|
|
||||||
_HEADER = "eDVB services /4/"
|
_HEADER = "eDVB services /4/"
|
||||||
_FILE_PATH = "../data/lamedb"
|
_FILE_PATH = "../data/lamedb"
|
||||||
_SEP = ":" # separator
|
_SEP = ":" # separator
|
||||||
_FILE_NAME = "lamedb"
|
_FILE_NAME = "lamedb"
|
||||||
|
|
||||||
Channel = namedtuple("Channel", ["flags_cas", "transponder_type", "coded", "service", "locked", "hide",
|
|
||||||
"package", "service_type", "ssid", "freq", "rate", "pol", "fec",
|
|
||||||
"system", "pos", "data_id", "fav_id", "transponder"])
|
|
||||||
|
|
||||||
|
def get_services(path):
|
||||||
def get_channels(path):
|
|
||||||
return parse(path)
|
return parse(path)
|
||||||
|
|
||||||
|
|
||||||
def write_channels(path, channels):
|
def write_services(path, services):
|
||||||
lines = [_HEADER, "\ntransponders\n"]
|
lines = [_HEADER, "\ntransponders\n"]
|
||||||
tr_lines = []
|
tr_lines = []
|
||||||
services_lines = ["end\nservices\n"]
|
services_lines = ["end\nservices\n"]
|
||||||
tr_set = set()
|
tr_set = set()
|
||||||
|
|
||||||
for ch in channels:
|
for srv in services:
|
||||||
data_id = str(ch.data_id).split(_SEP)
|
data_id = str(srv.data_id).split(_SEP)
|
||||||
tr_id = "{}:{}:{}".format(data_id[1], data_id[2], data_id[3])
|
tr_id = "{}:{}:{}".format(data_id[1], data_id[2], data_id[3])
|
||||||
if tr_id not in tr_set:
|
if tr_id not in tr_set:
|
||||||
transponder = "{}\n\t{}\n/\n".format(tr_id, ch.transponder)
|
transponder = "{}\n\t{}\n/\n".format(tr_id, srv.transponder)
|
||||||
tr_lines.append(transponder)
|
tr_lines.append(transponder)
|
||||||
tr_set.add(tr_id)
|
tr_set.add(tr_id)
|
||||||
# Services
|
# Services
|
||||||
services_lines.append("{}\n{}\n{}\n".format(ch.data_id, ch.service, ch.flags_cas))
|
services_lines.append("{}\n{}\n{}\n".format(srv.data_id, srv.service, srv.flags_cas))
|
||||||
|
|
||||||
tr_lines.sort()
|
tr_lines.sort()
|
||||||
lines.extend(tr_lines)
|
lines.extend(tr_lines)
|
||||||
@@ -65,7 +59,7 @@ def parse(path):
|
|||||||
transponders, sep, services = services.partition("services") # 2 step
|
transponders, sep, services = services.partition("services") # 2 step
|
||||||
services, sep, _ = services.partition("end") # 3 step
|
services, sep, _ = services.partition("end") # 3 step
|
||||||
|
|
||||||
return parse_channels(services.split("\n"), transponders.split("/"), path)
|
return parse_services(services.split("\n"), transponders.split("/"), path)
|
||||||
|
|
||||||
|
|
||||||
def parse_transponders(arg):
|
def parse_transponders(arg):
|
||||||
@@ -79,7 +73,7 @@ def parse_transponders(arg):
|
|||||||
return transponders
|
return transponders
|
||||||
|
|
||||||
|
|
||||||
def parse_channels(services, transponders, path):
|
def parse_services(services, transponders, path):
|
||||||
""" Parsing channels """
|
""" Parsing channels """
|
||||||
channels = []
|
channels = []
|
||||||
transponders = parse_transponders(transponders)
|
transponders = parse_transponders(transponders)
|
||||||
@@ -98,7 +92,7 @@ def parse_channels(services, transponders, path):
|
|||||||
all_flags = ch[2].split(",")
|
all_flags = ch[2].split(",")
|
||||||
coded = CODED_ICON if list(filter(lambda x: x.startswith("C:"), all_flags)) else None
|
coded = CODED_ICON if list(filter(lambda x: x.startswith("C:"), all_flags)) else None
|
||||||
flags = list(filter(lambda x: x.startswith("f:"), all_flags))
|
flags = list(filter(lambda x: x.startswith("f:"), all_flags))
|
||||||
hide = HIDE_ICON if flags and int(flags[0][2:]) == 2 else None
|
hide = HIDE_ICON if flags and int(flags[0][2:]) in FLAG.hide_values() else None
|
||||||
locked = LOCKED_ICON if fav_id in blacklist else None
|
locked = LOCKED_ICON if fav_id in blacklist else None
|
||||||
|
|
||||||
package = list(filter(lambda x: x.startswith("p:"), all_flags))
|
package = list(filter(lambda x: x.startswith("p:"), all_flags))
|
||||||
@@ -111,7 +105,7 @@ def parse_channels(services, transponders, path):
|
|||||||
tr_type, sp, tr = str(transponder).partition(" ")
|
tr_type, sp, tr = str(transponder).partition(" ")
|
||||||
tr = tr.split(_SEP)
|
tr = tr.split(_SEP)
|
||||||
service_type = SERVICE_TYPE.get(data[4], SERVICE_TYPE["-2"])
|
service_type = SERVICE_TYPE.get(data[4], SERVICE_TYPE["-2"])
|
||||||
channels.append(Channel(flags_cas=ch[2],
|
channels.append(Service(flags_cas=ch[2],
|
||||||
transponder_type=tr_type,
|
transponder_type=tr_type,
|
||||||
coded=coded,
|
coded=coded,
|
||||||
service=ch[1],
|
service=ch[1],
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
from app.eparser.bouquets import BqServiceType
|
from .ecommons import BqServiceType, Service
|
||||||
from . import Channel
|
|
||||||
|
|
||||||
|
|
||||||
def parse_m3u(path):
|
def parse_m3u(path):
|
||||||
@@ -16,7 +15,7 @@ def parse_m3u(path):
|
|||||||
count = 0
|
count = 0
|
||||||
fav_id = " 1:0:1:0:0:0:0:0:0:0:{}:{}\n#DESCRIPTION: {}\n".format(
|
fav_id = " 1:0:1:0:0:0:0:0:0:0:{}:{}\n#DESCRIPTION: {}\n".format(
|
||||||
line.strip().replace(":", "%3a"), name, name, None)
|
line.strip().replace(":", "%3a"), name, name, None)
|
||||||
channels.append(Channel(*aggr[0:3], name, *aggr[0:3], BqServiceType.IPTV.name, *aggr, fav_id, None))
|
channels.append(Service(*aggr[0:3], name, *aggr[0:3], BqServiceType.IPTV.name, *aggr, fav_id, None))
|
||||||
|
|
||||||
return channels
|
return channels
|
||||||
|
|
||||||
|
|||||||
0
app/eparser/neutrino/__init__.py
Normal file
0
app/eparser/neutrino/__init__.py
Normal file
106
app/eparser/neutrino/bouquets.py
Normal file
106
app/eparser/neutrino/bouquets.py
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
import os
|
||||||
|
from contextlib import suppress
|
||||||
|
from enum import Enum
|
||||||
|
from xml.dom.minidom import parse, Document
|
||||||
|
|
||||||
|
from app.ui import LOCKED_ICON, HIDE_ICON
|
||||||
|
from ..ecommons import Bouquets, Bouquet, BouquetService, BqServiceType, PROVIDER
|
||||||
|
|
||||||
|
_FILE = "bouquets.xml"
|
||||||
|
_U_FILE = "ubouquets.xml"
|
||||||
|
|
||||||
|
|
||||||
|
class BqType(Enum):
|
||||||
|
BOUQUET = "bouquet"
|
||||||
|
TV = "tv"
|
||||||
|
|
||||||
|
|
||||||
|
def get_bouquets(path):
|
||||||
|
return (parse_bouquets(path + _FILE, "Providers", BqType.BOUQUET.value),
|
||||||
|
parse_bouquets(path + _U_FILE, "FAV", BqType.TV.value))
|
||||||
|
|
||||||
|
|
||||||
|
def parse_bouquets(file, name, bq_type):
|
||||||
|
bouquets = Bouquets(name=name, type=bq_type, bouquets=[])
|
||||||
|
if not os.path.exists(file):
|
||||||
|
return bouquets
|
||||||
|
|
||||||
|
dom = parse(file)
|
||||||
|
|
||||||
|
for elem in dom.getElementsByTagName("Bouquet"):
|
||||||
|
if elem.hasAttributes():
|
||||||
|
bq_name = elem.attributes["name"].value
|
||||||
|
hidden = elem.attributes.get("hidden")
|
||||||
|
hidden = hidden.value if hidden else hidden
|
||||||
|
locked = elem.attributes.get("locked")
|
||||||
|
locked = locked.value if locked else locked
|
||||||
|
# epg = elem.attributes["epg"].value
|
||||||
|
services = []
|
||||||
|
for srv_elem in elem.getElementsByTagName("S"):
|
||||||
|
if srv_elem.hasAttributes():
|
||||||
|
ssid = srv_elem.attributes["i"].value
|
||||||
|
on = srv_elem.attributes["on"].value
|
||||||
|
tr_id = srv_elem.attributes["t"].value
|
||||||
|
fav_id = "{}:{}:{}".format(tr_id, on, ssid)
|
||||||
|
services.append(BouquetService(None, BqServiceType.DEFAULT, fav_id, 0))
|
||||||
|
bouquets[2].append(Bouquet(name=bq_name,
|
||||||
|
type=bq_type,
|
||||||
|
services=services,
|
||||||
|
locked=LOCKED_ICON if locked == "1" else None,
|
||||||
|
hidden=HIDE_ICON if hidden == "1" else None))
|
||||||
|
|
||||||
|
if BqType(bq_type) is BqType.BOUQUET:
|
||||||
|
for bq in bouquets.bouquets:
|
||||||
|
if bq.services:
|
||||||
|
name = bq.name
|
||||||
|
name = name[name.index("]") + 1:]
|
||||||
|
key = int(bq.services[0].data.split(":")[1], 16)
|
||||||
|
if key not in PROVIDER:
|
||||||
|
PROVIDER[key] = name
|
||||||
|
|
||||||
|
return bouquets
|
||||||
|
|
||||||
|
|
||||||
|
def write_bouquets(path, bouquets):
|
||||||
|
if len(bouquets) < 2:
|
||||||
|
for f in path + _FILE, path + _U_FILE:
|
||||||
|
with suppress(FileNotFoundError):
|
||||||
|
os.remove(f)
|
||||||
|
|
||||||
|
for bq in bouquets:
|
||||||
|
bq_type = BqType(bq.type)
|
||||||
|
write_bouquet(path + (_FILE if bq_type is BqType.BOUQUET else _U_FILE), bq)
|
||||||
|
|
||||||
|
|
||||||
|
def write_bouquet(file, bouquet):
|
||||||
|
doc = Document()
|
||||||
|
root = doc.createElement("zapit")
|
||||||
|
doc.appendChild(root)
|
||||||
|
comment = doc.createComment(" File was created in DemonEditor. Enjoy watching! ")
|
||||||
|
doc.appendChild(comment)
|
||||||
|
|
||||||
|
for bq in bouquet.bouquets:
|
||||||
|
bq_elem = doc.createElement("Bouquet")
|
||||||
|
bq_elem.setAttribute("name", bq.name)
|
||||||
|
bq_elem.setAttribute("hidden", "1" if bq.hidden else "0")
|
||||||
|
bq_elem.setAttribute("locked", "1" if bq.locked else "0")
|
||||||
|
bq_elem.setAttribute("epg", "0")
|
||||||
|
root.appendChild(bq_elem)
|
||||||
|
|
||||||
|
for srv in bq.services:
|
||||||
|
tr_id, on, ssid = srv.fav_id.split(":")
|
||||||
|
srv_elem = doc.createElement("S")
|
||||||
|
srv_elem.setAttribute("i", ssid)
|
||||||
|
srv_elem.setAttribute("n", srv.service)
|
||||||
|
srv_elem.setAttribute("t", tr_id)
|
||||||
|
srv_elem.setAttribute("on", on)
|
||||||
|
srv_elem.setAttribute("s", srv.pos.replace(".", ""))
|
||||||
|
srv_elem.setAttribute("frq", srv.freq[:-3])
|
||||||
|
srv_elem.setAttribute("l", "0") # temporary !!!
|
||||||
|
bq_elem.appendChild(srv_elem)
|
||||||
|
|
||||||
|
doc.writexml(open(file, "w"), addindent=" ", newl="\n", encoding="UTF-8")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
pass
|
||||||
168
app/eparser/neutrino/services.py
Normal file
168
app/eparser/neutrino/services.py
Normal file
@@ -0,0 +1,168 @@
|
|||||||
|
from xml.dom.minidom import parse, Document
|
||||||
|
|
||||||
|
from ..ecommons import Service, POLARIZATION, FEC, SYSTEM, SERVICE_TYPE, PROVIDER
|
||||||
|
|
||||||
|
_FILE = "services.xml"
|
||||||
|
_TR_ATTR_NAMES = ("id", "on", "frq", "inv", "sr", "fec", "pol", "mod", "sys") # transponder attributes
|
||||||
|
_SRV_ATTR_NAMES = ("t", "s", "num", "f", "v", "a", "p", "pmt", "tx", "vt") # service attributes
|
||||||
|
|
||||||
|
|
||||||
|
def write_services(path, services):
|
||||||
|
doc = Document()
|
||||||
|
root = doc.createElement("zapit")
|
||||||
|
root.setAttribute("api", "4")
|
||||||
|
doc.appendChild(root)
|
||||||
|
comment = doc.createComment(" File was created in DemonEditor. Enjoy watching! ")
|
||||||
|
doc.appendChild(comment)
|
||||||
|
|
||||||
|
sats = {}
|
||||||
|
for srv in services:
|
||||||
|
flag = srv[0]
|
||||||
|
if flag in sats:
|
||||||
|
sats.get(flag).append(srv)
|
||||||
|
else:
|
||||||
|
srv_list = [srv]
|
||||||
|
sats[flag] = srv_list
|
||||||
|
|
||||||
|
for sat in sats:
|
||||||
|
tr_atr = sat.split(":")
|
||||||
|
sat_elem = doc.createElement("sat")
|
||||||
|
sat_elem.setAttribute("name", tr_atr[0])
|
||||||
|
sat_elem.setAttribute("position", tr_atr[1].replace(".", ""))
|
||||||
|
sat_elem.setAttribute("diseqc", tr_atr[2])
|
||||||
|
sat_elem.setAttribute("uncommited", tr_atr[3])
|
||||||
|
root.appendChild(sat_elem)
|
||||||
|
|
||||||
|
transponers = {}
|
||||||
|
for srv in sats.get(sat):
|
||||||
|
flag = srv[-1]
|
||||||
|
if flag in transponers:
|
||||||
|
transponers.get(flag).append(srv)
|
||||||
|
else:
|
||||||
|
srv_list = [srv]
|
||||||
|
transponers[flag] = srv_list
|
||||||
|
|
||||||
|
for tr in transponers:
|
||||||
|
tr_elem = doc.createElement("TS")
|
||||||
|
tr_atr = tr.split(":")
|
||||||
|
for i, value in enumerate(tr_atr):
|
||||||
|
if value == "None":
|
||||||
|
continue
|
||||||
|
tr_elem.setAttribute(_TR_ATTR_NAMES[i], value)
|
||||||
|
sat_elem.appendChild(tr_elem)
|
||||||
|
|
||||||
|
for srv in transponers.get(tr):
|
||||||
|
srv_elem = doc.createElement("S")
|
||||||
|
srv_elem.setAttribute("i", srv[8])
|
||||||
|
srv_elem.setAttribute("n", srv[3])
|
||||||
|
|
||||||
|
srv_attrs = srv.data_id.split(":")
|
||||||
|
api = srv_attrs.pop(0)
|
||||||
|
|
||||||
|
if api == "3":
|
||||||
|
root.setAttribute("api", "3") # !!!
|
||||||
|
for i, value in enumerate(srv_attrs):
|
||||||
|
if value == "None":
|
||||||
|
continue
|
||||||
|
srv_elem.setAttribute(_SRV_ATTR_NAMES[i], value)
|
||||||
|
|
||||||
|
tr_elem.appendChild(srv_elem)
|
||||||
|
|
||||||
|
doc.writexml(open(path + _FILE, "w"), addindent=" ", newl="\n", encoding="UTF-8")
|
||||||
|
doc.unlink()
|
||||||
|
|
||||||
|
|
||||||
|
def get_services(path):
|
||||||
|
return parse_services(path)
|
||||||
|
|
||||||
|
|
||||||
|
def parse_services(path):
|
||||||
|
""" Parsing services from xml"""
|
||||||
|
dom = parse(path + _FILE)
|
||||||
|
services = []
|
||||||
|
|
||||||
|
for root in dom.getElementsByTagName("zapit"):
|
||||||
|
api = root.attributes["api"].value
|
||||||
|
|
||||||
|
for elem in root.getElementsByTagName("sat"):
|
||||||
|
if elem.hasAttributes():
|
||||||
|
sat_name = elem.attributes["name"].value
|
||||||
|
sat_pos = elem.attributes["position"].value
|
||||||
|
sat_pos = "{}.{}".format(sat_pos[:-1], sat_pos[-1:])
|
||||||
|
diseqc = elem.attributes.get("diseqc")
|
||||||
|
diseqc = diseqc.value if diseqc else diseqc
|
||||||
|
uncommited = elem.attributes.get("uncommited")
|
||||||
|
uncommited = uncommited.value if uncommited else uncommited
|
||||||
|
sat = "{}:{}:{}:{}".format(sat_name, sat_pos, diseqc, uncommited)
|
||||||
|
|
||||||
|
for tr_elem in elem.getElementsByTagName("TS"):
|
||||||
|
if tr_elem.hasAttributes():
|
||||||
|
parse_transponder(api, sat, sat_pos, services, tr_elem)
|
||||||
|
|
||||||
|
return services
|
||||||
|
|
||||||
|
|
||||||
|
def parse_transponder(api, sat, sat_pos, services, tr_elem):
|
||||||
|
tr_id = tr_elem.attributes["id"].value
|
||||||
|
on = tr_elem.attributes["on"].value
|
||||||
|
freq = tr_elem.attributes["frq"].value
|
||||||
|
rate = tr_elem.attributes["sr"].value
|
||||||
|
inv = tr_elem.attributes["inv"].value
|
||||||
|
fec = tr_elem.attributes["fec"].value
|
||||||
|
pol = tr_elem.attributes["pol"].value
|
||||||
|
mod = tr_elem.attributes.get("mod")
|
||||||
|
mod = mod.value if mod else mod
|
||||||
|
sys = tr_elem.attributes.get("sys")
|
||||||
|
sys = sys.value if sys else sys
|
||||||
|
|
||||||
|
tr = "{}:{}:{}:{}:{}:{}:{}:{}:{}".format(tr_id, on, freq, inv, rate, fec, pol, mod, sys)
|
||||||
|
|
||||||
|
tr_id = tr_id.lstrip("0")
|
||||||
|
on = on.lstrip("0")
|
||||||
|
|
||||||
|
for srv_elem in tr_elem.getElementsByTagName("S"):
|
||||||
|
if srv_elem.hasAttributes():
|
||||||
|
ssid = srv_elem.attributes["i"].value
|
||||||
|
name = srv_elem.attributes["n"].value
|
||||||
|
srv_type = srv_elem.attributes["t"].value
|
||||||
|
sys = srv_elem.attributes["s"].value
|
||||||
|
num = srv_elem.attributes.get("num")
|
||||||
|
num = num.value if num else num
|
||||||
|
f = srv_elem.attributes.get("f")
|
||||||
|
f = f.value if f else f
|
||||||
|
v, a, p, pmt, tx, vt = [None] * 6
|
||||||
|
# For v3 is possible so: '<S i="0001" n="name" t="1" s="0" num="770" f="4"/>' (equals v4 api)
|
||||||
|
if api == "3" and len(srv_elem.attributes) > 6:
|
||||||
|
v = srv_elem.attributes["v"].value
|
||||||
|
a = srv_elem.attributes["a"].value
|
||||||
|
p = srv_elem.attributes["p"].value
|
||||||
|
pmt = srv_elem.attributes["pmt"].value
|
||||||
|
tx = srv_elem.attributes["tx"].value
|
||||||
|
vt = srv_elem.attributes["vt"].value
|
||||||
|
|
||||||
|
data_id = "{}:{}:{}:{}:{}:{}:{}:{}:{}:{}:{}".format(api, srv_type, sys, num, f, v, a, p, pmt, tx, vt)
|
||||||
|
fav_id = "{}:{}:{}".format(tr_id, on, ssid.lstrip("0"))
|
||||||
|
|
||||||
|
srv = Service(flags_cas=sat,
|
||||||
|
transponder_type=None,
|
||||||
|
coded=None,
|
||||||
|
service=name,
|
||||||
|
locked=None,
|
||||||
|
hide=None,
|
||||||
|
package=PROVIDER.get(int(on, 16)),
|
||||||
|
service_type=SERVICE_TYPE.get(str(int(srv_type, 16))),
|
||||||
|
ssid=ssid,
|
||||||
|
freq=freq,
|
||||||
|
rate=rate,
|
||||||
|
pol=POLARIZATION.get(pol),
|
||||||
|
fec=FEC.get(fec),
|
||||||
|
system=SYSTEM.get(sys),
|
||||||
|
pos=sat_pos,
|
||||||
|
data_id=data_id,
|
||||||
|
fav_id=fav_id,
|
||||||
|
transponder=tr)
|
||||||
|
services.append(srv)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
pass
|
||||||
@@ -2,18 +2,12 @@
|
|||||||
|
|
||||||
For more info see __COMMENT
|
For more info see __COMMENT
|
||||||
"""
|
"""
|
||||||
from collections import namedtuple
|
|
||||||
from xml.dom.minidom import parse, Document
|
from xml.dom.minidom import parse, Document
|
||||||
|
|
||||||
from app.eparser.__constants import POLARIZATION, FEC, SYSTEM, MODULATION, PLS_MODE
|
from .ecommons import POLARIZATION, FEC, SYSTEM, MODULATION, PLS_MODE, Transponder, Satellite
|
||||||
|
|
||||||
Satellite = namedtuple("Satellite", ["name", "flags", "position", "transponders"])
|
|
||||||
|
|
||||||
Transponder = namedtuple("Transponder", ["frequency", "symbol_rate", "polarization", "fec_inner",
|
|
||||||
"system", "modulation", "pls_mode", "pls_code", "is_id"])
|
|
||||||
|
|
||||||
__COMMENT = (" File was created in DemonEditor\n\n"
|
__COMMENT = (" File was created in DemonEditor\n\n"
|
||||||
"useable flags are\n"
|
"usable flags are\n"
|
||||||
" 1: Network Scan\n"
|
" 1: Network Scan\n"
|
||||||
" 2: use BAT\n"
|
" 2: use BAT\n"
|
||||||
" 4: use ONIT\n"
|
" 4: use ONIT\n"
|
||||||
|
|||||||
69
app/ftp.py
69
app/ftp.py
@@ -2,22 +2,28 @@ import os
|
|||||||
import socket
|
import socket
|
||||||
import time
|
import time
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
from ftplib import FTP
|
from ftplib import FTP, error_perm
|
||||||
from telnetlib import Telnet
|
from telnetlib import Telnet
|
||||||
|
|
||||||
__DATA_FILES_LIST = ("tv", "radio", "lamedb", "blacklist", "whitelist")
|
from app.commons import log
|
||||||
|
from app.properties import Profile
|
||||||
|
|
||||||
|
__DATA_FILES_LIST = ("tv", "radio", "lamedb", "blacklist", "whitelist", # enigma 2
|
||||||
|
"services.xml", "myservices.xml", "bouquets.xml", "ubouquets.xml") # neutrino
|
||||||
|
|
||||||
|
|
||||||
class DownloadDataType(Enum):
|
class DownloadDataType(Enum):
|
||||||
ALL = 0
|
ALL = 0
|
||||||
BOUQUETS = 1
|
BOUQUETS = 1
|
||||||
SATELLITES = 2
|
SATELLITES = 2
|
||||||
|
PICONS = 3
|
||||||
|
|
||||||
|
|
||||||
def download_data(*, properties, download_type=DownloadDataType.ALL):
|
def download_data(*, properties, download_type=DownloadDataType.ALL, callback=None):
|
||||||
with FTP(host=properties["host"]) as ftp:
|
with FTP(host=properties["host"]) as ftp:
|
||||||
ftp.login(user=properties["user"], passwd=properties["password"])
|
ftp.login(user=properties["user"], passwd=properties["password"])
|
||||||
save_path = properties["data_dir_path"]
|
save_path = properties["data_dir_path"]
|
||||||
|
os.makedirs(os.path.dirname(save_path), exist_ok=True)
|
||||||
files = []
|
files = []
|
||||||
# bouquets section
|
# bouquets section
|
||||||
if download_type is DownloadDataType.ALL or download_type is DownloadDataType.BOUQUETS:
|
if download_type is DownloadDataType.ALL or download_type is DownloadDataType.BOUQUETS:
|
||||||
@@ -43,14 +49,19 @@ def download_data(*, properties, download_type=DownloadDataType.ALL):
|
|||||||
with open(save_path + xml_file, 'wb') as f:
|
with open(save_path + xml_file, 'wb') as f:
|
||||||
ftp.retrbinary("RETR " + xml_file, f.write)
|
ftp.retrbinary("RETR " + xml_file, f.write)
|
||||||
|
|
||||||
|
if callback is not None:
|
||||||
|
callback()
|
||||||
|
|
||||||
def upload_data(*, properties, download_type=DownloadDataType.ALL, remove_unused=False):
|
|
||||||
|
def upload_data(*, properties, download_type=DownloadDataType.ALL, remove_unused=False, profile=Profile.ENIGMA_2,
|
||||||
|
callback=None):
|
||||||
data_path = properties["data_dir_path"]
|
data_path = properties["data_dir_path"]
|
||||||
host = properties["host"]
|
host = properties["host"]
|
||||||
# telnet
|
# telnet
|
||||||
tn = telnet(host=host)
|
tn = telnet(host=host, user=properties.get("telnet_user", "root"), password=properties.get("telnet_password", ""),
|
||||||
|
timeout=properties.get("telnet_timeout", 5))
|
||||||
next(tn)
|
next(tn)
|
||||||
# terminate enigma
|
# terminate enigma or enigma
|
||||||
tn.send("init 4")
|
tn.send("init 4")
|
||||||
|
|
||||||
with FTP(host=host) as ftp:
|
with FTP(host=host) as ftp:
|
||||||
@@ -77,9 +88,37 @@ def upload_data(*, properties, download_type=DownloadDataType.ALL, remove_unused
|
|||||||
for file_name in os.listdir(data_path):
|
for file_name in os.listdir(data_path):
|
||||||
if file_name == "satellites.xml":
|
if file_name == "satellites.xml":
|
||||||
continue
|
continue
|
||||||
file_name, send_file(file_name, data_path, ftp)
|
if file_name.endswith(__DATA_FILES_LIST):
|
||||||
# resume enigma
|
send_file(file_name, data_path, ftp)
|
||||||
tn.send("init 3")
|
|
||||||
|
if download_type is DownloadDataType.PICONS:
|
||||||
|
picons_dir_path = properties.get("picons_dir_path")
|
||||||
|
picons_path = properties.get("picons_path")
|
||||||
|
try:
|
||||||
|
ftp.cwd(picons_path)
|
||||||
|
except error_perm as e:
|
||||||
|
if str(e).startswith("550"):
|
||||||
|
ftp.mkd(picons_path) # if not exist
|
||||||
|
ftp.cwd(picons_path)
|
||||||
|
|
||||||
|
files = []
|
||||||
|
ftp.dir(files.append)
|
||||||
|
picons_suf = (".jpg", ".png")
|
||||||
|
|
||||||
|
for file in files:
|
||||||
|
name = str(file).strip()
|
||||||
|
if name.endswith(picons_suf):
|
||||||
|
name = name.split()[-1]
|
||||||
|
ftp.delete(name)
|
||||||
|
for file_name in os.listdir(picons_dir_path):
|
||||||
|
if file_name.endswith(picons_suf):
|
||||||
|
send_file(file_name, picons_dir_path, ftp)
|
||||||
|
|
||||||
|
# resume enigma or restart neutrino
|
||||||
|
tn.send("init 3" if profile is Profile.ENIGMA_2 else "init 6")
|
||||||
|
|
||||||
|
if callback is not None:
|
||||||
|
callback()
|
||||||
|
|
||||||
|
|
||||||
def send_file(file_name, path, ftp):
|
def send_file(file_name, path, ftp):
|
||||||
@@ -88,14 +127,22 @@ def send_file(file_name, path, ftp):
|
|||||||
return ftp.storbinary("STOR " + file_name, f)
|
return ftp.storbinary("STOR " + file_name, f)
|
||||||
|
|
||||||
|
|
||||||
def telnet(host, port=23, user="root", password="root", timeout=5):
|
def telnet(host, port=23, user="", password="", timeout=5):
|
||||||
try:
|
try:
|
||||||
tn = Telnet(host=host, port=port, timeout=timeout)
|
tn = Telnet(host=host, port=port, timeout=timeout)
|
||||||
except socket.timeout:
|
except socket.timeout:
|
||||||
print("socket timeout")
|
log("telnet error: socket timeout")
|
||||||
else:
|
else:
|
||||||
time.sleep(1)
|
time.sleep(1)
|
||||||
command = yield
|
command = yield
|
||||||
|
if user != "":
|
||||||
|
tn.read_until(b"login: ")
|
||||||
|
tn.write(user.encode("utf-8") + b"\n")
|
||||||
|
time.sleep(timeout)
|
||||||
|
if password != "":
|
||||||
|
tn.read_until(b"Password: ")
|
||||||
|
tn.write(password.encode("utf-8") + b"\n")
|
||||||
|
time.sleep(timeout)
|
||||||
tn.write("{}\r\n".format(command).encode("utf-8"))
|
tn.write("{}\r\n".format(command).encode("utf-8"))
|
||||||
time.sleep(timeout)
|
time.sleep(timeout)
|
||||||
command = yield
|
command = yield
|
||||||
|
|||||||
0
app/picons/__init__.py
Normal file
0
app/picons/__init__.py
Normal file
169
app/picons/picons.py
Normal file
169
app/picons/picons.py
Normal file
@@ -0,0 +1,169 @@
|
|||||||
|
import os
|
||||||
|
import shutil
|
||||||
|
from collections import namedtuple
|
||||||
|
from html.parser import HTMLParser
|
||||||
|
|
||||||
|
from app.commons import log
|
||||||
|
from app.properties import Profile
|
||||||
|
|
||||||
|
Provider = namedtuple("Provider", ["logo", "name", "url", "on_id", "selected"])
|
||||||
|
Picon = namedtuple("Picon", ["ref", "ssid", "v_pid"])
|
||||||
|
|
||||||
|
|
||||||
|
class PiconsParser(HTMLParser):
|
||||||
|
""" Parser for package html page. (https://www.lyngsat.com/packages/*provider-name*.html) """
|
||||||
|
|
||||||
|
def __init__(self, entities=False, separator=' '):
|
||||||
|
|
||||||
|
HTMLParser.__init__(self)
|
||||||
|
|
||||||
|
self._parse_html_entities = entities
|
||||||
|
self._separator = separator
|
||||||
|
self._is_td = False
|
||||||
|
self._is_th = False
|
||||||
|
self._current_row = []
|
||||||
|
self._current_cell = []
|
||||||
|
self.picons = []
|
||||||
|
|
||||||
|
def handle_starttag(self, tag, attrs):
|
||||||
|
if tag == 'td':
|
||||||
|
self._is_td = True
|
||||||
|
if tag == 'th':
|
||||||
|
self._is_th = True
|
||||||
|
if tag == "img":
|
||||||
|
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 == 'th':
|
||||||
|
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
|
||||||
|
ln = len(row)
|
||||||
|
if 9 < ln < 13:
|
||||||
|
url = None
|
||||||
|
if row[0].startswith("../logo/"):
|
||||||
|
url = row[0]
|
||||||
|
elif row[1].startswith("../logo/"):
|
||||||
|
url = row[1]
|
||||||
|
|
||||||
|
ssid = row[-4]
|
||||||
|
if url and len(ssid) > 2:
|
||||||
|
self.picons.append(Picon(url, ssid, row[-3]))
|
||||||
|
|
||||||
|
self._current_row = []
|
||||||
|
|
||||||
|
def error(self, message):
|
||||||
|
pass
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def parse(open_path, picons_path, tmp_path, on_id, profile=Profile.ENIGMA_2):
|
||||||
|
with open(open_path, encoding="utf-8", errors="replace") as f:
|
||||||
|
parser = PiconsParser()
|
||||||
|
parser.reset()
|
||||||
|
parser.feed(f.read())
|
||||||
|
picons = parser.picons
|
||||||
|
if picons:
|
||||||
|
os.makedirs(picons_path, exist_ok=True)
|
||||||
|
for p in picons:
|
||||||
|
try:
|
||||||
|
picon_file_name = picons_path + PiconsParser.format(p.ssid, on_id, p.v_pid, profile)
|
||||||
|
shutil.copyfile(tmp_path + "www.lyngsat.com/" + p.ref.lstrip("."), picon_file_name)
|
||||||
|
except (TypeError, ValueError) as e:
|
||||||
|
log("Picons format parse error: {} {} {}".format(p.ref, p.ssid, p.v_pid) + "\n" + str(e))
|
||||||
|
print(e)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def format(ssid, on_id, v_pid, profile: Profile):
|
||||||
|
tr_id = int(ssid[:-2] if len(ssid) < 4 else ssid[:2])
|
||||||
|
if profile is Profile.ENIGMA_2:
|
||||||
|
return "1_0_{}_{:X}_{:X}_{:X}_1680000_0_0_0.png".format(1 if v_pid else 2, int(ssid), tr_id, int(on_id))
|
||||||
|
elif profile is Profile.NEUTRINO_MP:
|
||||||
|
return "{:x}{:04x}{:04x}.png".format(tr_id, int(on_id), int(ssid))
|
||||||
|
else:
|
||||||
|
return "{}.png".format(ssid)
|
||||||
|
|
||||||
|
|
||||||
|
class ProviderParser(HTMLParser):
|
||||||
|
""" Parser for satellite html page. (https://www.lyngsat.com/*sat-name*.html) """
|
||||||
|
|
||||||
|
def __init__(self, entities=False, separator=' '):
|
||||||
|
|
||||||
|
HTMLParser.__init__(self)
|
||||||
|
|
||||||
|
self._ON_ID_BLACK_LIST = ("65535", "?", "0", "1")
|
||||||
|
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._ids = set()
|
||||||
|
|
||||||
|
def handle_starttag(self, tag, attrs):
|
||||||
|
if tag == 'td':
|
||||||
|
self._is_td = True
|
||||||
|
if tag == 'tr':
|
||||||
|
self._is_th = True
|
||||||
|
if tag == "img":
|
||||||
|
if attrs[0][1].startswith("logo/"):
|
||||||
|
self._current_row.append(attrs[0][1])
|
||||||
|
if tag == "a":
|
||||||
|
if "https://www.lyngsat.com/packages/" in attrs[0][1]:
|
||||||
|
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
|
||||||
|
if len(row) == 12:
|
||||||
|
on_id, sep, tid = str(row[-2]).partition("-")
|
||||||
|
if tid and on_id not in self._ON_ID_BLACK_LIST and on_id not in self._ids:
|
||||||
|
row[-2] = on_id
|
||||||
|
self.rows.append(row)
|
||||||
|
self._ids.add(on_id)
|
||||||
|
self._current_row = []
|
||||||
|
|
||||||
|
def error(self, message):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def parse_providers(open_path):
|
||||||
|
with open(open_path, encoding="utf-8", errors="replace") as f:
|
||||||
|
parser = ProviderParser()
|
||||||
|
parser.reset()
|
||||||
|
parser.feed(f.read())
|
||||||
|
rows = parser.rows
|
||||||
|
|
||||||
|
if rows:
|
||||||
|
return [Provider(logo=r[2], name=r[5], url=r[6], on_id=r[-2], selected=True) for r in rows]
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
pass
|
||||||
@@ -1,25 +1,35 @@
|
|||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
|
from enum import Enum
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
CONFIG_PATH = str(Path.home()) + "/.config/demon-editor/"
|
CONFIG_PATH = str(Path.home()) + "/.config/demon-editor/"
|
||||||
CONFIG_FILE = CONFIG_PATH + "config.json"
|
CONFIG_FILE = CONFIG_PATH + "config.json"
|
||||||
DATA_PATH = "data/"
|
DATA_PATH = "data/"
|
||||||
|
|
||||||
|
|
||||||
|
class Profile(Enum):
|
||||||
|
""" Profiles for settings """
|
||||||
|
ENIGMA_2 = "0"
|
||||||
|
NEUTRINO_MP = "1"
|
||||||
|
|
||||||
|
|
||||||
def get_config():
|
def get_config():
|
||||||
os.makedirs(os.path.dirname(CONFIG_PATH), exist_ok=True) # create dir if not exist
|
os.makedirs(os.path.dirname(CONFIG_PATH), exist_ok=True) # create dir if not exist
|
||||||
os.makedirs(os.path.dirname(DATA_PATH), exist_ok=True)
|
os.makedirs(os.path.dirname(DATA_PATH), exist_ok=True)
|
||||||
|
|
||||||
if not os.path.isfile(CONFIG_FILE) or os.stat(CONFIG_FILE).st_size == 0:
|
if not os.path.isfile(CONFIG_FILE) or os.stat(CONFIG_FILE).st_size == 0:
|
||||||
with open(CONFIG_FILE, "w") as default_config_file:
|
reset_config()
|
||||||
json.dump(get_default_settings(), default_config_file)
|
|
||||||
|
|
||||||
with open(CONFIG_FILE, "r") as config_file:
|
with open(CONFIG_FILE, "r") as config_file:
|
||||||
return json.load(config_file)
|
return json.load(config_file)
|
||||||
|
|
||||||
|
|
||||||
|
def reset_config():
|
||||||
|
with open(CONFIG_FILE, "w") as default_config_file:
|
||||||
|
json.dump(get_default_settings(), default_config_file)
|
||||||
|
|
||||||
|
|
||||||
def write_config(config):
|
def write_config(config):
|
||||||
assert isinstance(config, dict)
|
assert isinstance(config, dict)
|
||||||
with open(CONFIG_FILE, "w") as config_file:
|
with open(CONFIG_FILE, "w") as config_file:
|
||||||
@@ -27,12 +37,30 @@ def write_config(config):
|
|||||||
|
|
||||||
|
|
||||||
def get_default_settings():
|
def get_default_settings():
|
||||||
return {"host": "127.0.0.1", "port": "21",
|
return {
|
||||||
|
Profile.ENIGMA_2.value: {
|
||||||
|
"host": "127.0.0.1", "port": "21",
|
||||||
"user": "root", "password": "root",
|
"user": "root", "password": "root",
|
||||||
|
"telnet_user": "", "telnet_password": "",
|
||||||
|
"telnet_port": "21", "telnet_timeout": 5,
|
||||||
"services_path": "/etc/enigma2/",
|
"services_path": "/etc/enigma2/",
|
||||||
"user_bouquet_path": "/etc/enigma2/",
|
"user_bouquet_path": "/etc/enigma2/",
|
||||||
"satellites_xml_path": "/etc/tuxbox/",
|
"satellites_xml_path": "/etc/tuxbox/",
|
||||||
"data_dir_path": DATA_PATH}
|
"picons_path": "/usr/share/enigma2/picon",
|
||||||
|
"data_dir_path": DATA_PATH + "enigma2/",
|
||||||
|
"picons_dir_path": DATA_PATH + "enigma2/picons/"},
|
||||||
|
Profile.NEUTRINO_MP.value: {
|
||||||
|
"host": "127.0.0.1", "port": "21",
|
||||||
|
"user": "root", "password": "root",
|
||||||
|
"telnet_user": "root", "telnet_password": "",
|
||||||
|
"telnet_port": "21", "telnet_timeout": 1,
|
||||||
|
"services_path": "/var/tuxbox/config/zapit/",
|
||||||
|
"user_bouquet_path": "/var/tuxbox/config/zapit/",
|
||||||
|
"satellites_xml_path": "/var/tuxbox/config/",
|
||||||
|
"picons_path": "/usr/share/tuxbox/neutrino/icons/logo/",
|
||||||
|
"data_dir_path": DATA_PATH + "neutrino/",
|
||||||
|
"picons_dir_path": DATA_PATH + "neutrino/picons/"},
|
||||||
|
"profile": Profile.ENIGMA_2.value}
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|||||||
@@ -1,8 +1,12 @@
|
|||||||
import gi
|
import gi
|
||||||
|
import os
|
||||||
|
|
||||||
gi.require_version('Gtk', '3.0')
|
gi.require_version('Gtk', '3.0')
|
||||||
from gi.repository import Gtk, Gdk
|
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/"
|
||||||
|
|
||||||
theme = Gtk.IconTheme.get_default()
|
theme = Gtk.IconTheme.get_default()
|
||||||
_IMAGE_MISSING = theme.load_icon("image-missing", 16, 0) if theme.lookup_icon("image-missing", 16, 0) else None
|
_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(
|
CODED_ICON = theme.load_icon("emblem-readonly", 16, 0) if theme.lookup_icon(
|
||||||
|
|||||||
@@ -9,14 +9,14 @@
|
|||||||
<property name="icon_name">system-help</property>
|
<property name="icon_name">system-help</property>
|
||||||
<property name="type_hint">normal</property>
|
<property name="type_hint">normal</property>
|
||||||
<property name="program_name">DemonEditor</property>
|
<property name="program_name">DemonEditor</property>
|
||||||
<property name="version">0.1.1 Pre-alpha</property>
|
<property name="version">0.2.1 Pre-alpha</property>
|
||||||
<property name="copyright" translatable="yes">2017 Dmitriy Yefremov
|
<property name="copyright" translatable="yes">2018 Dmitriy Yefremov
|
||||||
dmitry.v.yefremov@gmail.com
|
dmitry.v.yefremov@gmail.com
|
||||||
</property>
|
</property>
|
||||||
<property name="comments" translatable="yes">Enigma2 channel and satellites list editor for GNU/Linux</property>
|
<property name="comments" translatable="yes">Enigma2 channel and satellites list editor for GNU/Linux</property>
|
||||||
<property name="license" translatable="yes">Это приложение распространяется без каких-либо гарантий.
|
<property name="license" translatable="yes">Это приложение распространяется без каких-либо гарантий.
|
||||||
Подробнее в <a href="http://opensource.org/licenses/mit-license.php">The MIT License (MIT)</a>.</property>
|
Подробнее в <a href="http://opensource.org/licenses/mit-license.php">The MIT License (MIT)</a>.</property>
|
||||||
<property name="authors">Dmitriy Yefremov
|
<property name="authors">Dmitriy Yefremov
|
||||||
</property>
|
</property>
|
||||||
<property name="logo_icon_name">accessories-text-editor</property>
|
<property name="logo_icon_name">accessories-text-editor</property>
|
||||||
<property name="wrap_license">True</property>
|
<property name="wrap_license">True</property>
|
||||||
@@ -116,6 +116,7 @@ dmitry.v.yefremov@gmail.com
|
|||||||
<property name="max_width_chars">10</property>
|
<property name="max_width_chars">10</property>
|
||||||
<property name="text" translatable="yes">127.0.0.1</property>
|
<property name="text" translatable="yes">127.0.0.1</property>
|
||||||
<property name="caps_lock_warning">False</property>
|
<property name="caps_lock_warning">False</property>
|
||||||
|
<property name="primary_icon_name">network-transmit-receive-symbolic</property>
|
||||||
</object>
|
</object>
|
||||||
<packing>
|
<packing>
|
||||||
<property name="left_attach">0</property>
|
<property name="left_attach">0</property>
|
||||||
@@ -196,7 +197,6 @@ dmitry.v.yefremov@gmail.com
|
|||||||
<property name="can_focus">True</property>
|
<property name="can_focus">True</property>
|
||||||
<property name="receives_default">False</property>
|
<property name="receives_default">False</property>
|
||||||
<property name="xalign">0</property>
|
<property name="xalign">0</property>
|
||||||
<property name="active">True</property>
|
|
||||||
<property name="draw_indicator">True</property>
|
<property name="draw_indicator">True</property>
|
||||||
<property name="group">satellites_radio_button</property>
|
<property name="group">satellites_radio_button</property>
|
||||||
</object>
|
</object>
|
||||||
@@ -213,7 +213,6 @@ dmitry.v.yefremov@gmail.com
|
|||||||
<property name="can_focus">True</property>
|
<property name="can_focus">True</property>
|
||||||
<property name="receives_default">False</property>
|
<property name="receives_default">False</property>
|
||||||
<property name="xalign">0</property>
|
<property name="xalign">0</property>
|
||||||
<property name="active">True</property>
|
|
||||||
<property name="draw_indicator">True</property>
|
<property name="draw_indicator">True</property>
|
||||||
<property name="group">all_radio_button</property>
|
<property name="group">all_radio_button</property>
|
||||||
</object>
|
</object>
|
||||||
@@ -223,6 +222,9 @@ dmitry.v.yefremov@gmail.com
|
|||||||
<property name="position">3</property>
|
<property name="position">3</property>
|
||||||
</packing>
|
</packing>
|
||||||
</child>
|
</child>
|
||||||
|
<child>
|
||||||
|
<placeholder/>
|
||||||
|
</child>
|
||||||
</object>
|
</object>
|
||||||
<packing>
|
<packing>
|
||||||
<property name="expand">False</property>
|
<property name="expand">False</property>
|
||||||
@@ -603,6 +605,14 @@ dmitry.v.yefremov@gmail.com
|
|||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
</object>
|
</object>
|
||||||
|
<object class="GtkAdjustment" id="telnet_timeout_adjustment">
|
||||||
|
<property name="lower">1</property>
|
||||||
|
<property name="upper">11</property>
|
||||||
|
<property name="value">1</property>
|
||||||
|
<property name="step_increment">1</property>
|
||||||
|
<property name="page_increment">10</property>
|
||||||
|
<property name="page_size">1</property>
|
||||||
|
</object>
|
||||||
<object class="GtkDialog" id="settings_dialog">
|
<object class="GtkDialog" id="settings_dialog">
|
||||||
<property name="can_focus">False</property>
|
<property name="can_focus">False</property>
|
||||||
<property name="title" translatable="yes">Options</property>
|
<property name="title" translatable="yes">Options</property>
|
||||||
@@ -613,7 +623,10 @@ dmitry.v.yefremov@gmail.com
|
|||||||
<child internal-child="vbox">
|
<child internal-child="vbox">
|
||||||
<object class="GtkBox" id="dialog-vbox3">
|
<object class="GtkBox" id="dialog-vbox3">
|
||||||
<property name="can_focus">False</property>
|
<property name="can_focus">False</property>
|
||||||
|
<property name="margin_left">2</property>
|
||||||
|
<property name="margin_right">2</property>
|
||||||
<property name="margin_top">5</property>
|
<property name="margin_top">5</property>
|
||||||
|
<property name="margin_bottom">2</property>
|
||||||
<property name="orientation">vertical</property>
|
<property name="orientation">vertical</property>
|
||||||
<property name="spacing">2</property>
|
<property name="spacing">2</property>
|
||||||
<child internal-child="action_area">
|
<child internal-child="action_area">
|
||||||
@@ -623,11 +636,29 @@ dmitry.v.yefremov@gmail.com
|
|||||||
<property name="layout_style">end</property>
|
<property name="layout_style">end</property>
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkButton" id="cancel_button">
|
<object class="GtkButton" id="cancel_button">
|
||||||
<property name="label">gtk-undo</property>
|
<property name="label">gtk-cancel</property>
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="receives_default">True</property>
|
||||||
|
<property name="margin_right">10</property>
|
||||||
|
<property name="use_stock">True</property>
|
||||||
|
<property name="always_show_image">True</property>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">True</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="position">0</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkButton" id="button5">
|
||||||
|
<property name="label">gtk-apply</property>
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
<property name="can_focus">True</property>
|
<property name="can_focus">True</property>
|
||||||
<property name="receives_default">True</property>
|
<property name="receives_default">True</property>
|
||||||
<property name="use_stock">True</property>
|
<property name="use_stock">True</property>
|
||||||
|
<property name="always_show_image">True</property>
|
||||||
|
<signal name="clicked" handler="apply_settings" swapped="no"/>
|
||||||
</object>
|
</object>
|
||||||
<packing>
|
<packing>
|
||||||
<property name="expand">True</property>
|
<property name="expand">True</property>
|
||||||
@@ -642,6 +673,7 @@ dmitry.v.yefremov@gmail.com
|
|||||||
<property name="can_focus">True</property>
|
<property name="can_focus">True</property>
|
||||||
<property name="receives_default">True</property>
|
<property name="receives_default">True</property>
|
||||||
<property name="use_stock">True</property>
|
<property name="use_stock">True</property>
|
||||||
|
<property name="always_show_image">True</property>
|
||||||
</object>
|
</object>
|
||||||
<packing>
|
<packing>
|
||||||
<property name="expand">True</property>
|
<property name="expand">True</property>
|
||||||
@@ -653,109 +685,249 @@ dmitry.v.yefremov@gmail.com
|
|||||||
<packing>
|
<packing>
|
||||||
<property name="expand">False</property>
|
<property name="expand">False</property>
|
||||||
<property name="fill">False</property>
|
<property name="fill">False</property>
|
||||||
<property name="position">0</property>
|
<property name="position">6</property>
|
||||||
</packing>
|
</packing>
|
||||||
</child>
|
</child>
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkGrid" id="grid1">
|
<object class="GtkNotebook" id="notebook">
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
<property name="can_focus">False</property>
|
<property name="can_focus">True</property>
|
||||||
<property name="column_spacing">1</property>
|
|
||||||
<property name="column_homogeneous">True</property>
|
|
||||||
<child>
|
<child>
|
||||||
|
<object class="GtkGrid" id="ftp_settings_grid">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="column_spacing">2</property>
|
||||||
|
<property name="column_homogeneous">True</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkLabel" id="label4">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="label" translatable="yes">Host:</property>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="left_attach">0</property>
|
||||||
|
<property name="top_attach">0</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkEntry" id="host_field">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="text" translatable="yes">127.0.0.1</property>
|
||||||
|
<property name="primary_icon_name">network-transmit-receive-symbolic</property>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="left_attach">0</property>
|
||||||
|
<property name="top_attach">1</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkLabel" id="label13">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="label" translatable="yes">Login:</property>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="left_attach">0</property>
|
||||||
|
<property name="top_attach">2</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkLabel" id="label14">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="label" translatable="yes">Password:</property>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="left_attach">1</property>
|
||||||
|
<property name="top_attach">2</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkEntry" id="port_field">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="text" translatable="yes">21</property>
|
||||||
|
<property name="primary_icon_name">network-workgroup-symbolic</property>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="left_attach">1</property>
|
||||||
|
<property name="top_attach">1</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkLabel" id="label15">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="label" translatable="yes">Port:</property>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="left_attach">1</property>
|
||||||
|
<property name="top_attach">0</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkEntry" id="login_field">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="text" translatable="yes">root</property>
|
||||||
|
<property name="primary_icon_name">avatar-default-symbolic</property>
|
||||||
|
<property name="primary_icon_activatable">False</property>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="left_attach">0</property>
|
||||||
|
<property name="top_attach">3</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkEntry" id="password_field">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="visibility">False</property>
|
||||||
|
<property name="invisible_char">●</property>
|
||||||
|
<property name="text" translatable="yes">root</property>
|
||||||
|
<property name="primary_icon_name">emblem-readonly</property>
|
||||||
|
<property name="primary_icon_activatable">False</property>
|
||||||
|
<property name="input_purpose">password</property>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="left_attach">1</property>
|
||||||
|
<property name="top_attach">3</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
<child type="tab">
|
||||||
<object class="GtkLabel" id="label1">
|
<object class="GtkLabel" id="label1">
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
<property name="can_focus">False</property>
|
<property name="can_focus">False</property>
|
||||||
<property name="label" translatable="yes">Host:</property>
|
<property name="label" translatable="yes">FTP</property>
|
||||||
</object>
|
</object>
|
||||||
<packing>
|
<packing>
|
||||||
<property name="left_attach">0</property>
|
<property name="tab_fill">False</property>
|
||||||
<property name="top_attach">0</property>
|
|
||||||
</packing>
|
</packing>
|
||||||
</child>
|
</child>
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkEntry" id="host_field">
|
<object class="GtkGrid" id="telnet_settings_grid">
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">True</property>
|
|
||||||
<property name="text" translatable="yes">127.0.0.1</property>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="left_attach">0</property>
|
|
||||||
<property name="top_attach">1</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
<child>
|
|
||||||
<object class="GtkLabel" id="label3">
|
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
<property name="can_focus">False</property>
|
<property name="can_focus">False</property>
|
||||||
<property name="label" translatable="yes">Login:</property>
|
<property name="column_spacing">2</property>
|
||||||
|
<property name="column_homogeneous">True</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkLabel" id="label3">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="label" translatable="yes">Port:</property>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="left_attach">0</property>
|
||||||
|
<property name="top_attach">0</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkEntry" id="telnet_password_field">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="primary_icon_name">emblem-readonly</property>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="left_attach">1</property>
|
||||||
|
<property name="top_attach">3</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkEntry" id="telnet_login_field">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="primary_icon_name">avatar-default-symbolic</property>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="left_attach">0</property>
|
||||||
|
<property name="top_attach">3</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkLabel" id="label16">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="label" translatable="yes">Loggin:</property>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="left_attach">0</property>
|
||||||
|
<property name="top_attach">2</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkLabel" id="label17">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="label" translatable="yes">Password:</property>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="left_attach">1</property>
|
||||||
|
<property name="top_attach">2</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkEntry" id="telnet_port_field">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="text" translatable="yes">23</property>
|
||||||
|
<property name="primary_icon_name">network-workgroup-symbolic</property>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="left_attach">0</property>
|
||||||
|
<property name="top_attach">1</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkLabel" id="label19">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="label" translatable="yes">Timeout:</property>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="left_attach">1</property>
|
||||||
|
<property name="top_attach">0</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkSpinButton" id="telnet_timeout_spin_button">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="tooltip_text" translatable="yes">Timeout between commands in seconds</property>
|
||||||
|
<property name="max_length">2</property>
|
||||||
|
<property name="primary_icon_name">alarm-symbolic</property>
|
||||||
|
<property name="input_purpose">number</property>
|
||||||
|
<property name="adjustment">telnet_timeout_adjustment</property>
|
||||||
|
<property name="numeric">True</property>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="left_attach">1</property>
|
||||||
|
<property name="top_attach">1</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
</object>
|
</object>
|
||||||
<packing>
|
<packing>
|
||||||
<property name="left_attach">0</property>
|
<property name="position">1</property>
|
||||||
<property name="top_attach">2</property>
|
|
||||||
</packing>
|
</packing>
|
||||||
</child>
|
</child>
|
||||||
<child>
|
<child type="tab">
|
||||||
<object class="GtkLabel" id="label4">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">False</property>
|
|
||||||
<property name="label" translatable="yes">Password:</property>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="left_attach">1</property>
|
|
||||||
<property name="top_attach">2</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
<child>
|
|
||||||
<object class="GtkEntry" id="port_field">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">True</property>
|
|
||||||
<property name="text" translatable="yes">21</property>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="left_attach">1</property>
|
|
||||||
<property name="top_attach">1</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
<child>
|
|
||||||
<object class="GtkLabel" id="label2">
|
<object class="GtkLabel" id="label2">
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
<property name="can_focus">False</property>
|
<property name="can_focus">False</property>
|
||||||
<property name="label" translatable="yes">Port:</property>
|
<property name="label" translatable="yes">Telnet</property>
|
||||||
</object>
|
</object>
|
||||||
<packing>
|
<packing>
|
||||||
<property name="left_attach">1</property>
|
<property name="position">1</property>
|
||||||
<property name="top_attach">0</property>
|
<property name="tab_fill">False</property>
|
||||||
</packing>
|
</packing>
|
||||||
</child>
|
</child>
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkEntry" id="login_field">
|
<placeholder/>
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">True</property>
|
|
||||||
<property name="text" translatable="yes">root</property>
|
|
||||||
<property name="primary_icon_name">emblem-personal</property>
|
|
||||||
<property name="primary_icon_activatable">False</property>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="left_attach">0</property>
|
|
||||||
<property name="top_attach">3</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
</child>
|
||||||
<child>
|
<child type="tab">
|
||||||
<object class="GtkEntry" id="password_field">
|
<placeholder/>
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">True</property>
|
|
||||||
<property name="visibility">False</property>
|
|
||||||
<property name="invisible_char">●</property>
|
|
||||||
<property name="text" translatable="yes">root</property>
|
|
||||||
<property name="primary_icon_name">emblem-nowrite</property>
|
|
||||||
<property name="primary_icon_activatable">False</property>
|
|
||||||
<property name="input_purpose">password</property>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="left_attach">1</property>
|
|
||||||
<property name="top_attach">3</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
</child>
|
||||||
</object>
|
</object>
|
||||||
<packing>
|
<packing>
|
||||||
@@ -778,74 +950,203 @@ dmitry.v.yefremov@gmail.com
|
|||||||
</packing>
|
</packing>
|
||||||
</child>
|
</child>
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkGrid" id="grid2">
|
<object class="GtkBox" id="box2">
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
<property name="can_focus">False</property>
|
<property name="can_focus">False</property>
|
||||||
<property name="column_homogeneous">True</property>
|
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkLabel" id="label5">
|
<object class="GtkGrid" id="grid2">
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
<property name="can_focus">False</property>
|
<property name="can_focus">False</property>
|
||||||
<property name="label" translatable="yes">Services and Bouquets files:</property>
|
<property name="column_homogeneous">True</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkLabel" id="label5">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="label" translatable="yes">Services and Bouquets files:</property>
|
||||||
|
<property name="xalign">0</property>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="left_attach">0</property>
|
||||||
|
<property name="top_attach">0</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkEntry" id="services_field">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="text" translatable="yes">/etc/enigma2/</property>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="left_attach">0</property>
|
||||||
|
<property name="top_attach">1</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkLabel" id="label6">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="label" translatable="yes">User bouquet files:</property>
|
||||||
|
<property name="xalign">2.2351741291171123e-10</property>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="left_attach">0</property>
|
||||||
|
<property name="top_attach">2</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkEntry" id="user_bouquet_field">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="text" translatable="yes">/etc/enigma2/</property>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="left_attach">0</property>
|
||||||
|
<property name="top_attach">3</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkLabel" id="label7">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="label" translatable="yes">Satellites.xml file:</property>
|
||||||
|
<property name="xalign">0.019999999552965164</property>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="left_attach">0</property>
|
||||||
|
<property name="top_attach">4</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkEntry" id="satellites_xml_field">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="text" translatable="yes">/etc/tuxbox/</property>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="left_attach">0</property>
|
||||||
|
<property name="top_attach">5</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkLabel" id="label20">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="label" translatable="yes">Picons:</property>
|
||||||
|
<property name="xalign">2.2351741291171123e-10</property>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="left_attach">0</property>
|
||||||
|
<property name="top_attach">6</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkEntry" id="picons_field">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="text" translatable="yes">/usr/share/enigma2/picon</property>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="left_attach">0</property>
|
||||||
|
<property name="top_attach">7</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
</object>
|
</object>
|
||||||
<packing>
|
<packing>
|
||||||
<property name="left_attach">0</property>
|
<property name="expand">True</property>
|
||||||
<property name="top_attach">0</property>
|
<property name="fill">True</property>
|
||||||
|
<property name="position">0</property>
|
||||||
</packing>
|
</packing>
|
||||||
</child>
|
</child>
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkEntry" id="services_field">
|
<object class="GtkSeparator" id="separator5">
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">True</property>
|
|
||||||
<property name="text" translatable="yes">/etc/enigma2/</property>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="left_attach">0</property>
|
|
||||||
<property name="top_attach">1</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
<child>
|
|
||||||
<object class="GtkLabel" id="label6">
|
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
<property name="can_focus">False</property>
|
<property name="can_focus">False</property>
|
||||||
<property name="label" translatable="yes">User bouquet files:</property>
|
<property name="margin_left">5</property>
|
||||||
|
<property name="margin_right">5</property>
|
||||||
|
<property name="orientation">vertical</property>
|
||||||
</object>
|
</object>
|
||||||
<packing>
|
<packing>
|
||||||
<property name="left_attach">0</property>
|
<property name="expand">False</property>
|
||||||
<property name="top_attach">2</property>
|
<property name="fill">True</property>
|
||||||
|
<property name="position">1</property>
|
||||||
</packing>
|
</packing>
|
||||||
</child>
|
</child>
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkEntry" id="user_bouquet_field">
|
<object class="GtkBox" id="box3">
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">True</property>
|
|
||||||
<property name="text" translatable="yes">/etc/enigma2/</property>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="left_attach">0</property>
|
|
||||||
<property name="top_attach">3</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
<child>
|
|
||||||
<object class="GtkLabel" id="label7">
|
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
<property name="can_focus">False</property>
|
<property name="can_focus">False</property>
|
||||||
<property name="label" translatable="yes">Satellites.xml file:</property>
|
<property name="margin_right">5</property>
|
||||||
|
<property name="orientation">vertical</property>
|
||||||
|
<child>
|
||||||
|
<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</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" translatable="yes">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" translatable="yes">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>
|
</object>
|
||||||
<packing>
|
<packing>
|
||||||
<property name="left_attach">0</property>
|
<property name="expand">False</property>
|
||||||
<property name="top_attach">4</property>
|
<property name="fill">True</property>
|
||||||
</packing>
|
<property name="position">2</property>
|
||||||
</child>
|
|
||||||
<child>
|
|
||||||
<object class="GtkEntry" id="satellites_xml_field">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">True</property>
|
|
||||||
<property name="text" translatable="yes">/etc/tuxbox/</property>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="left_attach">0</property>
|
|
||||||
<property name="top_attach">5</property>
|
|
||||||
</packing>
|
</packing>
|
||||||
</child>
|
</child>
|
||||||
</object>
|
</object>
|
||||||
@@ -867,29 +1168,31 @@ dmitry.v.yefremov@gmail.com
|
|||||||
<property name="position">3</property>
|
<property name="position">3</property>
|
||||||
</packing>
|
</packing>
|
||||||
</child>
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkLabel" id="label8">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="label" translatable="yes">Data dir:</property>
|
||||||
|
<property name="lines">0</property>
|
||||||
|
<property name="xalign">0.019999999552965164</property>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="position">4</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkGrid" id="grid3">
|
<object class="GtkGrid" id="grid3">
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
<property name="can_focus">False</property>
|
<property name="can_focus">False</property>
|
||||||
<property name="column_homogeneous">True</property>
|
<property name="column_homogeneous">True</property>
|
||||||
<child>
|
|
||||||
<object class="GtkLabel" id="label8">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">False</property>
|
|
||||||
<property name="label" translatable="yes">Data directory:</property>
|
|
||||||
<property name="lines">0</property>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="left_attach">0</property>
|
|
||||||
<property name="top_attach">0</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkEntry" id="data_dir_field">
|
<object class="GtkEntry" id="data_dir_field">
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
<property name="can_focus">True</property>
|
<property name="can_focus">True</property>
|
||||||
<property name="text" translatable="yes">/data</property>
|
<property name="text" translatable="yes">/data</property>
|
||||||
<property name="secondary_icon_stock">gtk-open</property>
|
<property name="secondary_icon_name">folder-open-symbolic</property>
|
||||||
<property name="primary_icon_activatable">False</property>
|
<property name="primary_icon_activatable">False</property>
|
||||||
<property name="secondary_icon_tooltip_text" translatable="yes">Select</property>
|
<property name="secondary_icon_tooltip_text" translatable="yes">Select</property>
|
||||||
<property name="secondary_icon_tooltip_markup" translatable="yes">Select</property>
|
<property name="secondary_icon_tooltip_markup" translatable="yes">Select</property>
|
||||||
@@ -897,7 +1200,7 @@ dmitry.v.yefremov@gmail.com
|
|||||||
</object>
|
</object>
|
||||||
<packing>
|
<packing>
|
||||||
<property name="left_attach">0</property>
|
<property name="left_attach">0</property>
|
||||||
<property name="top_attach">1</property>
|
<property name="top_attach">0</property>
|
||||||
</packing>
|
</packing>
|
||||||
</child>
|
</child>
|
||||||
</object>
|
</object>
|
||||||
@@ -919,6 +1222,33 @@ dmitry.v.yefremov@gmail.com
|
|||||||
<property name="position">6</property>
|
<property name="position">6</property>
|
||||||
</packing>
|
</packing>
|
||||||
</child>
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkLabel" id="label18">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="label" translatable="yes">Picons dir:</property>
|
||||||
|
<property name="xalign">0.019999999552965164</property>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="position">8</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkEntry" id="picons_dir_field">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="text" translatable="yes">/data/picons</property>
|
||||||
|
<property name="secondary_icon_name">folder-open-symbolic</property>
|
||||||
|
<signal name="icon-press" handler="on_picons_dir_field_icon_press" swapped="no"/>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="position">9</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
<action-widgets>
|
<action-widgets>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
""" Common module for showing dialogs """
|
""" Common module for showing dialogs """
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
|
|
||||||
from . import Gtk
|
from . import Gtk, UI_RESOURCES_PATH
|
||||||
|
|
||||||
|
|
||||||
class DialogType(Enum):
|
class DialogType(Enum):
|
||||||
@@ -16,7 +16,7 @@ class DialogType(Enum):
|
|||||||
def show_dialog(dialog_type: DialogType, transient, text=None, options=None, action_type=None, file_filter=None):
|
def show_dialog(dialog_type: DialogType, transient, text=None, options=None, action_type=None, file_filter=None):
|
||||||
""" Shows dialogs by name """
|
""" Shows dialogs by name """
|
||||||
builder = Gtk.Builder()
|
builder = Gtk.Builder()
|
||||||
builder.add_from_file("app/ui/dialogs.glade")
|
builder.add_from_file(UI_RESOURCES_PATH + "dialogs.glade")
|
||||||
dialog = builder.get_object(dialog_type.value)
|
dialog = builder.get_object(dialog_type.value)
|
||||||
dialog.set_transient_for(transient)
|
dialog.set_transient_for(transient)
|
||||||
|
|
||||||
@@ -25,11 +25,12 @@ def show_dialog(dialog_type: DialogType, transient, text=None, options=None, act
|
|||||||
dialog.set_action(action_type)
|
dialog.set_action(action_type)
|
||||||
if file_filter is not None:
|
if file_filter is not None:
|
||||||
dialog.add_filter(file_filter)
|
dialog.add_filter(file_filter)
|
||||||
dialog.set_current_folder(options["data_dir_path"])
|
|
||||||
|
path = options.get("data_dir_path")
|
||||||
|
dialog.set_current_folder(path)
|
||||||
|
|
||||||
response = dialog.run()
|
response = dialog.run()
|
||||||
if response == -12: # -12 for fix assertion 'gtk_widget_get_can_default (widget)' failed
|
if response == -12: # -12 for fix assertion 'gtk_widget_get_can_default (widget)' failed
|
||||||
path = options["data_dir_path"]
|
|
||||||
if dialog.get_filename():
|
if dialog.get_filename():
|
||||||
path = dialog.get_filename()
|
path = dialog.get_filename()
|
||||||
if action_type is not Gtk.FileChooserAction.OPEN:
|
if action_type is not Gtk.FileChooserAction.OPEN:
|
||||||
|
|||||||
@@ -1,26 +1,28 @@
|
|||||||
from app.commons import run_idle, run_task
|
from app.commons import run_idle, run_task
|
||||||
from app.ftp import download_data, DownloadDataType, upload_data
|
from app.ftp import download_data, DownloadDataType, upload_data
|
||||||
from . import Gtk
|
from app.properties import Profile
|
||||||
|
from . import Gtk, UI_RESOURCES_PATH
|
||||||
from .dialogs import show_dialog, DialogType
|
from .dialogs import show_dialog, DialogType
|
||||||
|
|
||||||
|
|
||||||
def show_download_dialog(transient, options, open_data):
|
def show_download_dialog(transient, options, open_data, profile=Profile.ENIGMA_2):
|
||||||
dialog = DownloadDialog(transient, options, open_data)
|
dialog = DownloadDialog(transient, options, open_data, profile)
|
||||||
dialog.run()
|
dialog.run()
|
||||||
dialog.destroy()
|
dialog.destroy()
|
||||||
|
|
||||||
|
|
||||||
class DownloadDialog:
|
class DownloadDialog:
|
||||||
def __init__(self, transient, properties, open_data):
|
def __init__(self, transient, properties, open_data, profile):
|
||||||
self._properties = properties
|
self._properties = properties
|
||||||
self._open_data = open_data
|
self._open_data = open_data
|
||||||
|
self._profile = profile
|
||||||
|
|
||||||
handlers = {"on_receive": self.on_receive,
|
handlers = {"on_receive": self.on_receive,
|
||||||
"on_send": self.on_send,
|
"on_send": self.on_send,
|
||||||
"on_info_bar_close": self.on_info_bar_close}
|
"on_info_bar_close": self.on_info_bar_close}
|
||||||
|
|
||||||
builder = Gtk.Builder()
|
builder = Gtk.Builder()
|
||||||
builder.add_objects_from_file("app/ui/dialogs.glade", ("download_dialog",))
|
builder.add_objects_from_file(UI_RESOURCES_PATH + "dialogs.glade", ("download_dialog",))
|
||||||
builder.connect_signals(handlers)
|
builder.connect_signals(handlers)
|
||||||
|
|
||||||
self._dialog = builder.get_object("download_dialog")
|
self._dialog = builder.get_object("download_dialog")
|
||||||
@@ -58,7 +60,7 @@ class DownloadDialog:
|
|||||||
def destroy(self):
|
def destroy(self):
|
||||||
self._dialog.destroy()
|
self._dialog.destroy()
|
||||||
|
|
||||||
def on_info_bar_close(self, *args):
|
def on_info_bar_close(self, bar=None, resp=None):
|
||||||
self._info_bar.set_visible(False)
|
self._info_bar.set_visible(False)
|
||||||
|
|
||||||
@run_idle
|
@run_idle
|
||||||
@@ -72,12 +74,13 @@ class DownloadDialog:
|
|||||||
self.show_info_message("Please, wait...", Gtk.MessageType.INFO)
|
self.show_info_message("Please, wait...", Gtk.MessageType.INFO)
|
||||||
upload_data(properties=self._properties,
|
upload_data(properties=self._properties,
|
||||||
download_type=d_type,
|
download_type=d_type,
|
||||||
remove_unused=self._remove_unused_check_button.get_active())
|
remove_unused=self._remove_unused_check_button.get_active(),
|
||||||
|
profile=self._profile,
|
||||||
|
callback=lambda: self.show_info_message("Done!", Gtk.MessageType.INFO))
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
message = str(getattr(e, "message", str(e)))
|
message = str(getattr(e, "message", str(e)))
|
||||||
self.show_info_message(message, Gtk.MessageType.ERROR)
|
self.show_info_message(message, Gtk.MessageType.ERROR)
|
||||||
else:
|
else:
|
||||||
self.show_info_message("Done!", Gtk.MessageType.INFO)
|
|
||||||
if download and d_type is not DownloadDataType.SATELLITES:
|
if download and d_type is not DownloadDataType.SATELLITES:
|
||||||
self._open_data()
|
self._open_data()
|
||||||
|
|
||||||
|
|||||||
@@ -2,16 +2,18 @@ import os
|
|||||||
from contextlib import suppress
|
from contextlib import suppress
|
||||||
from functools import lru_cache
|
from functools import lru_cache
|
||||||
|
|
||||||
from app.commons import run_idle
|
from app.commons import run_idle, log
|
||||||
from app.eparser import get_blacklist, write_blacklist, to_bouquet_id, parse_m3u
|
from app.eparser import get_blacklist, write_blacklist, parse_m3u
|
||||||
from app.eparser import get_channels, get_bouquets, write_bouquets, write_channels, Bouquets, Bouquet, Channel
|
from app.eparser import get_services, get_bouquets, write_bouquets, write_services, Bouquets, Bouquet, Service
|
||||||
from app.eparser.__constants import CAS, FLAG
|
from app.eparser.ecommons import CAS, FLAG
|
||||||
from app.eparser.bouquets import BqServiceType
|
from app.eparser.enigma.bouquets import BqServiceType
|
||||||
from app.properties import get_config, write_config
|
from app.properties import get_config, write_config, Profile
|
||||||
from .main_helper import edit_marker, insert_marker
|
from .picons_dialog import PiconsDialog
|
||||||
from . import Gtk, Gdk, LOCKED_ICON, HIDE_ICON
|
from . import Gtk, Gdk, UI_RESOURCES_PATH, LOCKED_ICON, HIDE_ICON
|
||||||
from .dialogs import show_dialog, DialogType
|
from .dialogs import show_dialog, DialogType
|
||||||
from .download_dialog import show_download_dialog
|
from .download_dialog import show_download_dialog
|
||||||
|
from .main_helper import edit_marker, insert_marker, move_items, edit, ViewTarget, set_flags, locate_in_services, \
|
||||||
|
scroll_to
|
||||||
from .satellites_dialog import show_satellites_dialog
|
from .satellites_dialog import show_satellites_dialog
|
||||||
from .settings_dialog import show_settings_dialog
|
from .settings_dialog import show_settings_dialog
|
||||||
|
|
||||||
@@ -21,22 +23,35 @@ class MainAppWindow:
|
|||||||
_FAV_LIST_NAME = "fav_list_store"
|
_FAV_LIST_NAME = "fav_list_store"
|
||||||
_BOUQUETS_LIST_NAME = "bouquets_tree_store"
|
_BOUQUETS_LIST_NAME = "bouquets_tree_store"
|
||||||
# dynamically active elements depending on the selected view
|
# dynamically active elements depending on the selected view
|
||||||
_SERVICE_ELEMENTS = ("copy_tool_button", "to_fav_tool_button", "copy_menu_item", "services_to_fav_move_popup_item")
|
_SERVICE_ELEMENTS = ("copy_tool_button", "to_fav_tool_button", "copy_menu_item", "services_to_fav_move_popup_item",
|
||||||
_BOUQUET_ELEMENTS = ("edit_tool_button", "new_tool_button", "bouquets_new_popup_item", "bouguets_edit_popup_item")
|
"services_edit_popup_item", "services_copy_popup_item")
|
||||||
_REMOVE_ELEMENTS = ("remove_tool_button", "delete_menu_item", "services_remove_popup_item",
|
|
||||||
"bouquets_remove_popup_item", "fav_remove_popup_item")
|
_BOUQUET_ELEMENTS = ("edit_tool_button", "new_tool_button",
|
||||||
_FAV_ELEMENTS = ("up_tool_button", "down_tool_button", "cut_tool_button", "paste_tool_button", "cut_menu_item",
|
"bouquets_new_popup_item", "bouquets_edit_popup_item")
|
||||||
|
|
||||||
|
_COMMONS_ELEMENTS = ("edit_tool_button", "remove_tool_button", "delete_menu_item", "services_remove_popup_item",
|
||||||
|
"bouquets_remove_popup_item", "fav_remove_popup_item", "up_tool_button", "down_tool_button")
|
||||||
|
|
||||||
|
_FAV_ELEMENTS = ("cut_tool_button", "paste_tool_button", "cut_menu_item",
|
||||||
"paste_menu_item", "fav_cut_popup_item", "fav_paste_popup_item", "import_m3u_tool_button",
|
"paste_menu_item", "fav_cut_popup_item", "fav_paste_popup_item", "import_m3u_tool_button",
|
||||||
"fav_import_m3u_popup_item", "fav_insert_marker_popup_item")
|
"fav_import_m3u_popup_item", "fav_insert_marker_popup_item", "fav_edit_popup_item",
|
||||||
|
"fav_locate_popup_item")
|
||||||
|
|
||||||
|
_FAV_ONLY_ELEMENTS = ("import_m3u_tool_button", "fav_import_m3u_popup_item", "fav_insert_marker_popup_item",
|
||||||
|
"fav_edit_marker_popup_item")
|
||||||
|
|
||||||
_LOCK_HIDE_ELEMENTS = ("locked_tool_button", "hide_tool_button")
|
_LOCK_HIDE_ELEMENTS = ("locked_tool_button", "hide_tool_button")
|
||||||
|
|
||||||
__DYNAMIC_ELEMENTS = ("up_tool_button", "down_tool_button", "cut_tool_button", "copy_tool_button",
|
__DYNAMIC_ELEMENTS = ("up_tool_button", "down_tool_button", "cut_tool_button", "copy_tool_button",
|
||||||
"paste_tool_button", "to_fav_tool_button", "new_tool_button", "remove_tool_button",
|
"paste_tool_button", "to_fav_tool_button", "new_tool_button", "remove_tool_button",
|
||||||
"cut_menu_item", "copy_menu_item", "paste_menu_item", "delete_menu_item", "edit_tool_button",
|
"cut_menu_item", "copy_menu_item", "paste_menu_item", "delete_menu_item", "edit_tool_button",
|
||||||
"services_to_fav_move_popup_item", "services_remove_popup_item", "fav_cut_popup_item",
|
"services_to_fav_move_popup_item", "services_edit_popup_item", "locked_tool_button",
|
||||||
"fav_paste_popup_item", "bouquets_new_popup_item", "bouguets_edit_popup_item",
|
"services_remove_popup_item", "fav_cut_popup_item", "fav_paste_popup_item",
|
||||||
"services_remove_popup_item", "bouquets_remove_popup_item", "fav_remove_popup_item",
|
"bouquets_new_popup_item", "bouquets_edit_popup_item", "services_remove_popup_item",
|
||||||
"locked_tool_button", "hide_tool_button", "import_m3u_tool_button",
|
"bouquets_remove_popup_item", "fav_remove_popup_item", "hide_tool_button",
|
||||||
"fav_import_m3u_popup_item", "fav_insert_marker_popup_item", "fav_edit_marker_popup_item")
|
"import_m3u_tool_button", "fav_import_m3u_popup_item", "fav_insert_marker_popup_item",
|
||||||
|
"fav_edit_marker_popup_item", "fav_edit_popup_item", "fav_locate_popup_item",
|
||||||
|
"services_copy_popup_item")
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
handlers = {"on_close_main_window": self.on_quit,
|
handlers = {"on_close_main_window": self.on_quit,
|
||||||
@@ -56,9 +71,11 @@ class MainAppWindow:
|
|||||||
"on_cut": self.on_cut,
|
"on_cut": self.on_cut,
|
||||||
"on_copy": self.on_copy,
|
"on_copy": self.on_copy,
|
||||||
"on_paste": self.on_paste,
|
"on_paste": self.on_paste,
|
||||||
|
"on_edit": self.on_edit,
|
||||||
"on_delete": self.on_delete,
|
"on_delete": self.on_delete,
|
||||||
"on_new_bouquet": self.on_new_bouquet,
|
"on_new_bouquet": self.on_new_bouquet,
|
||||||
"on_bouquets_edit": self.on_bouquets_edit,
|
"on_bouquets_edit": self.on_bouquets_edit,
|
||||||
|
"on_tool_edit": self.on_tool_edit,
|
||||||
"on_to_fav_move": self.on_to_fav_move,
|
"on_to_fav_move": self.on_to_fav_move,
|
||||||
"on_services_tree_view_drag_data_get": self.on_services_tree_view_drag_data_get,
|
"on_services_tree_view_drag_data_get": self.on_services_tree_view_drag_data_get,
|
||||||
"on_fav_tree_view_drag_data_get": self.on_fav_tree_view_drag_data_get,
|
"on_fav_tree_view_drag_data_get": self.on_fav_tree_view_drag_data_get,
|
||||||
@@ -71,19 +88,23 @@ class MainAppWindow:
|
|||||||
"on_import_m3u": self.on_import_m3u,
|
"on_import_m3u": self.on_import_m3u,
|
||||||
"on_insert_marker": self.on_insert_marker,
|
"on_insert_marker": self.on_insert_marker,
|
||||||
"on_edit_marker": self.on_edit_marker,
|
"on_edit_marker": self.on_edit_marker,
|
||||||
"on_fav_popup": self.on_fav_popup}
|
"on_fav_popup": self.on_fav_popup,
|
||||||
|
"on_locate_in_services": self.on_locate_in_services,
|
||||||
|
"on_picons_loader_show": self.on_picons_loader_show}
|
||||||
|
|
||||||
self.__options = get_config()
|
self.__options = get_config()
|
||||||
|
self.__profile = self.__options.get("profile")
|
||||||
# Used for copy/paste. When adding the previous data will not be deleted.
|
# Used for copy/paste. When adding the previous data will not be deleted.
|
||||||
# Clearing only after the insertion!
|
# Clearing only after the insertion!
|
||||||
self.__rows_buffer = []
|
self.__rows_buffer = []
|
||||||
self.__channels = {}
|
self.__services = {}
|
||||||
self.__bouquets = {}
|
self.__bouquets = {}
|
||||||
self.__bouquets_to_del = []
|
self.__bouquets_to_del = []
|
||||||
self.__blacklist = set()
|
self.__blacklist = set()
|
||||||
|
|
||||||
builder = Gtk.Builder()
|
builder = Gtk.Builder()
|
||||||
builder.add_from_file("app/ui/main_window.glade")
|
builder.add_from_file(UI_RESOURCES_PATH + "main_window.glade")
|
||||||
|
builder.connect_signals(handlers)
|
||||||
self.__main_window = builder.get_object("main_window")
|
self.__main_window = builder.get_object("main_window")
|
||||||
main_window_size = self.__options.get("window_size", None)
|
main_window_size = self.__options.get("window_size", None)
|
||||||
# Setting the last size of the window if it was saved
|
# Setting the last size of the window if it was saved
|
||||||
@@ -96,7 +117,9 @@ class MainAppWindow:
|
|||||||
self.__services_model = builder.get_object("services_list_store")
|
self.__services_model = builder.get_object("services_list_store")
|
||||||
self.__bouquets_model = builder.get_object("bouquets_tree_store")
|
self.__bouquets_model = builder.get_object("bouquets_tree_store")
|
||||||
self.__status_bar = builder.get_object("status_bar")
|
self.__status_bar = builder.get_object("status_bar")
|
||||||
self.__status_bar.push(0, "Current IP: " + self.__options["host"])
|
self.__profile_label = builder.get_object("profile_label")
|
||||||
|
self.__status_bar.push(0, "Current IP: " + self.__options.get(self.__profile).get("host"))
|
||||||
|
self.__profile_label.set_text("Enigma2 v.4" if Profile(self.__profile) is Profile.ENIGMA_2 else "Neutrino-MP")
|
||||||
# dynamically active elements depending on the selected view
|
# dynamically active elements depending on the selected view
|
||||||
self.__tool_elements = {k: builder.get_object(k) for k in self.__DYNAMIC_ELEMENTS}
|
self.__tool_elements = {k: builder.get_object(k) for k in self.__DYNAMIC_ELEMENTS}
|
||||||
self.__cas_label = builder.get_object("cas_label")
|
self.__cas_label = builder.get_object("cas_label")
|
||||||
@@ -106,7 +129,6 @@ class MainAppWindow:
|
|||||||
self.__radio_count_label = builder.get_object("radio_count_label")
|
self.__radio_count_label = builder.get_object("radio_count_label")
|
||||||
self.__data_count_label = builder.get_object("data_count_label")
|
self.__data_count_label = builder.get_object("data_count_label")
|
||||||
self.__fav_edit_marker_popup_item = builder.get_object("fav_edit_marker_popup_item")
|
self.__fav_edit_marker_popup_item = builder.get_object("fav_edit_marker_popup_item")
|
||||||
builder.connect_signals(handlers)
|
|
||||||
self.init_drag_and_drop() # drag and drop
|
self.init_drag_and_drop() # drag and drop
|
||||||
self.__main_window.show()
|
self.__main_window.show()
|
||||||
|
|
||||||
@@ -143,33 +165,13 @@ class MainAppWindow:
|
|||||||
show_dialog(DialogType.ABOUT, self.__main_window)
|
show_dialog(DialogType.ABOUT, self.__main_window)
|
||||||
|
|
||||||
def move_items(self, key):
|
def move_items(self, key):
|
||||||
""" Move items in fav tree view """
|
""" Move items in fav or bouquets tree view """
|
||||||
selection = self.__fav_view.get_selection()
|
if self.__services_view.is_focus():
|
||||||
model, paths = selection.get_selected_rows()
|
return
|
||||||
|
elif self.__fav_view.is_focus():
|
||||||
if paths:
|
move_items(key, self.__fav_view)
|
||||||
# for correct down move!
|
elif self.__bouquets_view and key not in (Gdk.KEY_Page_Up, Gdk.KEY_Page_Down):
|
||||||
if key in (Gdk.KEY_Down, Gdk.KEY_Page_Down, Gdk.KEY_KP_Page_Down):
|
move_items(key, self.__bouquets_view)
|
||||||
paths = reversed(paths)
|
|
||||||
|
|
||||||
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(self.__fav_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(self.__fav_view.get_cursor()[0])
|
|
||||||
if down_itr:
|
|
||||||
model.move_after(itr, down_itr)
|
|
||||||
|
|
||||||
def on_cut(self, view):
|
def on_cut(self, view):
|
||||||
for row in tuple(self.on_delete(view)):
|
for row in tuple(self.on_delete(view)):
|
||||||
@@ -207,6 +209,16 @@ class MainAppWindow:
|
|||||||
self.__rows_buffer.clear()
|
self.__rows_buffer.clear()
|
||||||
self.on_view_focus(view, None)
|
self.on_view_focus(view, None)
|
||||||
|
|
||||||
|
def on_edit(self, view):
|
||||||
|
name = view.get_model().get_name()
|
||||||
|
if name == self._BOUQUETS_LIST_NAME:
|
||||||
|
self.on_bouquets_edit(view)
|
||||||
|
# edit(view, self.__main_window, ViewTarget.BOUQUET)
|
||||||
|
elif name == self._FAV_LIST_NAME:
|
||||||
|
edit(view, self.__main_window, ViewTarget.FAV, service_view=self.__services_view, channels=self.__services)
|
||||||
|
elif name == self._SERVICE_LIST_NAME:
|
||||||
|
edit(view, self.__main_window, ViewTarget.SERVICES, fav_view=self.__fav_view, channels=self.__services)
|
||||||
|
|
||||||
def on_delete(self, item):
|
def on_delete(self, item):
|
||||||
""" Delete selected items from views
|
""" Delete selected items from views
|
||||||
|
|
||||||
@@ -255,7 +267,7 @@ class MainAppWindow:
|
|||||||
if services:
|
if services:
|
||||||
with suppress(ValueError):
|
with suppress(ValueError):
|
||||||
services.remove(fav_id)
|
services.remove(fav_id)
|
||||||
self.__channels.pop(fav_id, None)
|
self.__services.pop(fav_id, None)
|
||||||
self.__fav_model.clear()
|
self.__fav_model.clear()
|
||||||
|
|
||||||
if bq_selected:
|
if bq_selected:
|
||||||
@@ -265,8 +277,14 @@ class MainAppWindow:
|
|||||||
""" Deleting bouquet """
|
""" Deleting bouquet """
|
||||||
self.__bouquets.pop(bouquet)
|
self.__bouquets.pop(bouquet)
|
||||||
self.__fav_model.clear()
|
self.__fav_model.clear()
|
||||||
bouquet_file_name = "{}userbouquet.{}.{}".format(self.__options["data_dir_path"], *bouquet.split(":"))
|
profile = Profile(self.__profile)
|
||||||
self.__bouquets_to_del.append(bouquet_file_name)
|
if profile is Profile.ENIGMA_2:
|
||||||
|
self.__bouquets_to_del.append(self.get_bouquet_file_name(bouquet))
|
||||||
|
|
||||||
|
def get_bouquet_file_name(self, bouquet):
|
||||||
|
bouquet_file_name = "{}userbouquet.{}.{}".format(self.__options.get(self.__profile).get("data_dir_path"),
|
||||||
|
*bouquet.split(":"))
|
||||||
|
return bouquet_file_name
|
||||||
|
|
||||||
def on_new_bouquet(self, view):
|
def on_new_bouquet(self, view):
|
||||||
""" Creates a new item in the bouquets tree """
|
""" Creates a new item in the bouquets tree """
|
||||||
@@ -288,29 +306,31 @@ class MainAppWindow:
|
|||||||
if response == Gtk.ResponseType.CANCEL:
|
if response == Gtk.ResponseType.CANCEL:
|
||||||
return
|
return
|
||||||
|
|
||||||
bq = response, bq_type
|
bq = response, None, None, bq_type
|
||||||
key = "{}:{}".format(response, bq_type)
|
key = "{}:{}".format(response, bq_type)
|
||||||
|
|
||||||
if model.iter_n_children(itr): # parent
|
if model.iter_n_children(itr): # parent
|
||||||
ch_itr = model.insert(itr, 0, bq)
|
ch_itr = model.insert(itr, 0, bq)
|
||||||
self.scroll_to(model.get_path(ch_itr), paths, view)
|
scroll_to(model.get_path(ch_itr), view, paths)
|
||||||
else:
|
else:
|
||||||
p_itr = model.iter_parent(itr)
|
p_itr = model.iter_parent(itr)
|
||||||
it = model.insert(p_itr, int(model.get_path(itr)[1]) + 1, bq) if p_itr else model.append(itr, bq)
|
it = model.insert(p_itr, int(model.get_path(itr)[1]) + 1, bq) if p_itr else model.append(itr, bq)
|
||||||
self.scroll_to(model.get_path(it), paths, view)
|
scroll_to(model.get_path(it), view, paths)
|
||||||
self.__bouquets[key] = []
|
self.__bouquets[key] = []
|
||||||
|
|
||||||
def scroll_to(self, path, paths, view):
|
def on_tool_edit(self, item):
|
||||||
""" Scrolling to and selecting given path """
|
""" Edit tool bar button """
|
||||||
view.expand_row(paths[0], 0)
|
if self.__services_view.is_focus():
|
||||||
selection = view.get_selection()
|
self.on_edit(self.__services_view)
|
||||||
selection.unselect_all()
|
elif self.__fav_view.is_focus():
|
||||||
view.scroll_to_cell(path, None)
|
self.on_edit(self.__fav_view)
|
||||||
selection.select_path(path)
|
elif self.__bouquets_view.is_focus():
|
||||||
|
self.on_edit(self.__bouquets_view)
|
||||||
|
|
||||||
def on_bouquets_edit(self, view):
|
def on_bouquets_edit(self, view):
|
||||||
""" Rename bouquets """
|
""" Rename bouquets """
|
||||||
if not self.is_bouquet_selected():
|
bq_selected = self.is_bouquet_selected()
|
||||||
|
if not bq_selected:
|
||||||
show_dialog(DialogType.ERROR, self.__main_window, "This item is not allowed to edit!")
|
show_dialog(DialogType.ERROR, self.__main_window, "This item is not allowed to edit!")
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -318,7 +338,7 @@ class MainAppWindow:
|
|||||||
|
|
||||||
if paths:
|
if paths:
|
||||||
itr = model.get_iter(paths[0])
|
itr = model.get_iter(paths[0])
|
||||||
bq_name, bq_type = model.get(itr, 0, 1)
|
bq_name, bq_type = model.get(itr, 0, 3)
|
||||||
response = show_dialog(DialogType.INPUT, self.__main_window, bq_name)
|
response = show_dialog(DialogType.INPUT, self.__main_window, bq_name)
|
||||||
|
|
||||||
if response == Gtk.ResponseType.CANCEL:
|
if response == Gtk.ResponseType.CANCEL:
|
||||||
@@ -326,6 +346,8 @@ class MainAppWindow:
|
|||||||
|
|
||||||
model.set_value(itr, 0, response)
|
model.set_value(itr, 0, response)
|
||||||
self.__bouquets["{}:{}".format(response, bq_type)] = self.__bouquets.pop("{}:{}".format(bq_name, bq_type))
|
self.__bouquets["{}:{}".format(response, bq_type)] = self.__bouquets.pop("{}:{}".format(bq_name, bq_type))
|
||||||
|
if Profile(self.__profile) is Profile.ENIGMA_2:
|
||||||
|
self.__bouquets_to_del.append(self.get_bouquet_file_name(bq_selected))
|
||||||
|
|
||||||
def on_to_fav_move(self, view):
|
def on_to_fav_move(self, view):
|
||||||
""" Move items from app to fav list """
|
""" Move items from app to fav list """
|
||||||
@@ -374,7 +396,7 @@ class MainAppWindow:
|
|||||||
for ext_row in ext_rows:
|
for ext_row in ext_rows:
|
||||||
dest_index += 1
|
dest_index += 1
|
||||||
fav_id = ext_row[-2]
|
fav_id = ext_row[-2]
|
||||||
channel = self.__channels[fav_id]
|
channel = self.__services[fav_id]
|
||||||
model.insert(dest_index, (0, channel.coded, channel.service, channel.locked, channel.hide,
|
model.insert(dest_index, (0, channel.coded, channel.service, channel.locked, channel.hide,
|
||||||
channel.service_type, channel.pos, channel.fav_id))
|
channel.service_type, channel.pos, channel.fav_id))
|
||||||
fav_bouquet.insert(dest_index, channel.fav_id)
|
fav_bouquet.insert(dest_index, channel.fav_id)
|
||||||
@@ -421,29 +443,27 @@ class MainAppWindow:
|
|||||||
if event.get_event_type() == Gdk.EventType.BUTTON_PRESS and event.button == Gdk.BUTTON_SECONDARY:
|
if event.get_event_type() == Gdk.EventType.BUTTON_PRESS and event.button == Gdk.BUTTON_SECONDARY:
|
||||||
menu.popup(None, None, None, None, event.button, event.time)
|
menu.popup(None, None, None, None, event.button, event.time)
|
||||||
|
|
||||||
|
@run_idle
|
||||||
def on_satellite_editor_show(self, model):
|
def on_satellite_editor_show(self, model):
|
||||||
""" Shows satellites editor dialog """
|
""" Shows satellites editor dialog """
|
||||||
show_satellites_dialog(self.__main_window, self.__options)
|
show_satellites_dialog(self.__main_window, self.__options.get(self.__profile))
|
||||||
|
|
||||||
def on_data_open(self, model):
|
def on_data_open(self, model):
|
||||||
response = show_dialog(DialogType.CHOOSER, self.__main_window, options=self.__options)
|
response = show_dialog(DialogType.CHOOSER, self.__main_window, options=self.__options.get(self.__profile))
|
||||||
if response == Gtk.ResponseType.CANCEL:
|
if response in (Gtk.ResponseType.CANCEL, Gtk.ResponseType.DELETE_EVENT):
|
||||||
return
|
return
|
||||||
self.open_data(response)
|
self.open_data(response)
|
||||||
|
|
||||||
@run_idle
|
@run_idle
|
||||||
def open_data(self, data_path=None):
|
def open_data(self, data_path=None):
|
||||||
""" Opening data and fill views. """
|
""" Opening data and fill views. """
|
||||||
self.__bouquets_model.clear()
|
self.clear_current_data()
|
||||||
self.__fav_model.clear()
|
|
||||||
self.__services_model.clear()
|
|
||||||
self.__blacklist.clear()
|
|
||||||
|
|
||||||
data_path = self.__options["data_dir_path"] if data_path is None else data_path
|
data_path = self.__options.get(self.__profile).get("data_dir_path") if data_path is None else data_path
|
||||||
try:
|
try:
|
||||||
self.append_blacklist(data_path)
|
self.append_blacklist(data_path)
|
||||||
self.append_services(data_path)
|
|
||||||
self.append_bouquets(data_path)
|
self.append_bouquets(data_path)
|
||||||
|
self.append_services(data_path)
|
||||||
self.update_services_counts(len(self.__services_model))
|
self.update_services_counts(len(self.__services_model))
|
||||||
except FileNotFoundError as e:
|
except FileNotFoundError as e:
|
||||||
show_dialog(DialogType.ERROR, self.__main_window, getattr(e, "message", str(e)) +
|
show_dialog(DialogType.ERROR, self.__main_window, getattr(e, "message", str(e)) +
|
||||||
@@ -457,48 +477,55 @@ class MainAppWindow:
|
|||||||
self.__blacklist.update(black_list)
|
self.__blacklist.update(black_list)
|
||||||
|
|
||||||
def append_bouquets(self, data_path):
|
def append_bouquets(self, data_path):
|
||||||
for bouquet in get_bouquets(data_path):
|
for bouquet in get_bouquets(data_path, Profile(self.__profile)):
|
||||||
parent = self.__bouquets_model.append(None, [bouquet.name, bouquet.type])
|
parent = self.__bouquets_model.append(None, [bouquet.name, None, None, bouquet.type])
|
||||||
for bt in bouquet.bouquets:
|
for bt in bouquet.bouquets:
|
||||||
name, bt_type = bt.name, bt.type
|
name, bt_type, locked, hidden = bt.name, bt.type, bt.locked, bt.hidden
|
||||||
self.__bouquets_model.append(parent, [name, bt_type])
|
self.__bouquets_model.append(parent, [name, locked, hidden, bt_type])
|
||||||
services = []
|
services = []
|
||||||
agr = [None] * 7
|
agr = [None] * 7
|
||||||
for srv in bt.services:
|
for srv in bt.services:
|
||||||
fav_id = srv.data
|
fav_id = srv.data
|
||||||
# IPTV and MARKER services
|
# IPTV and MARKER services
|
||||||
s_type = srv.type
|
s_type = srv.type
|
||||||
if s_type is BqServiceType.MARKER:
|
if s_type is BqServiceType.MARKER or s_type is BqServiceType.IPTV:
|
||||||
self.__channels[fav_id] = Channel(*agr[0:3], srv.name, *agr[0:3],
|
self.__services[fav_id] = Service(*agr[0:3], srv.name, *agr[0:3],
|
||||||
s_type.name, *agr, srv.num, fav_id, None)
|
s_type.name, *agr, srv.num, fav_id, None)
|
||||||
elif s_type is BqServiceType.IPTV:
|
|
||||||
self.__channels[fav_id] = Channel(*agr[0:3], srv.name, *agr[0:3],
|
|
||||||
srv.type.name, *agr, srv.num, fav_id, None)
|
|
||||||
services.append(fav_id)
|
services.append(fav_id)
|
||||||
self.__bouquets["{}:{}".format(name, bt_type)] = services
|
self.__bouquets["{}:{}".format(name, bt_type)] = services
|
||||||
|
|
||||||
def append_services(self, data_path):
|
def append_services(self, data_path):
|
||||||
channels = get_channels(data_path)
|
try:
|
||||||
if channels:
|
services = get_services(data_path, Profile(self.__profile))
|
||||||
for ch in channels:
|
except Exception as e:
|
||||||
# adding channels to dict with fav_id as keys
|
print(e)
|
||||||
self.__channels[ch.fav_id] = ch
|
log("Append services error: " + str(e))
|
||||||
self.__services_model.append(ch)
|
|
||||||
else:
|
|
||||||
show_dialog(DialogType.ERROR, self.__main_window, "Error opening data!")
|
show_dialog(DialogType.ERROR, self.__main_window, "Error opening data!")
|
||||||
|
else:
|
||||||
|
if services:
|
||||||
|
for srv in services:
|
||||||
|
# adding channels to dict with fav_id as keys
|
||||||
|
self.__services[srv.fav_id] = srv
|
||||||
|
self.__services_model.append(srv)
|
||||||
|
|
||||||
|
def clear_current_data(self):
|
||||||
|
""" Clearing current data from lists """
|
||||||
|
self.__bouquets_model.clear()
|
||||||
|
self.__fav_model.clear()
|
||||||
|
self.__services_model.clear()
|
||||||
|
self.__blacklist.clear()
|
||||||
|
self.__services.clear()
|
||||||
|
self.__rows_buffer.clear()
|
||||||
|
self.__bouquets.clear()
|
||||||
|
self.__bouquets_to_del.clear()
|
||||||
|
|
||||||
def on_data_save(self, *args):
|
def on_data_save(self, *args):
|
||||||
if show_dialog(DialogType.QUESTION, self.__main_window) == Gtk.ResponseType.CANCEL:
|
if show_dialog(DialogType.QUESTION, self.__main_window) == Gtk.ResponseType.CANCEL:
|
||||||
return
|
return
|
||||||
|
|
||||||
path = self.__options["data_dir_path"]
|
path = self.__options.get(self.__profile).get("data_dir_path")
|
||||||
bouquets = []
|
bouquets = []
|
||||||
services_model = self.__services_view.get_model()
|
services_model = self.__services_view.get_model()
|
||||||
# removing bouquet files
|
|
||||||
for bqf in self.__bouquets_to_del:
|
|
||||||
with suppress(FileNotFoundError):
|
|
||||||
os.remove(bqf)
|
|
||||||
self.__bouquets_to_del.clear()
|
|
||||||
|
|
||||||
def parse_bouquets(model, b_path, itr):
|
def parse_bouquets(model, b_path, itr):
|
||||||
if model.iter_has_child(itr):
|
if model.iter_has_child(itr):
|
||||||
@@ -507,21 +534,28 @@ class MainAppWindow:
|
|||||||
|
|
||||||
for num in range(num_of_children):
|
for num in range(num_of_children):
|
||||||
bq_itr = model.iter_nth_child(itr, num)
|
bq_itr = model.iter_nth_child(itr, num)
|
||||||
bq_name, bq_type = model.get(bq_itr, 0, 1)
|
bq_name, locked, hidden, bq_type = model.get(bq_itr, 0, 1, 2, 3)
|
||||||
favs = self.__bouquets["{}:{}".format(bq_name, bq_type)]
|
favs = self.__bouquets["{}:{}".format(bq_name, bq_type)]
|
||||||
bq = Bouquet(bq_name, bq_type, [self.__channels.get(f_id, None) for f_id in favs])
|
bq = Bouquet(bq_name, bq_type, [self.__services.get(f_id, None) for f_id in favs], locked, hidden)
|
||||||
bqs.append(bq)
|
bqs.append(bq)
|
||||||
bqs = Bouquets(*model.get(itr, 0, 1), bqs)
|
bqs = Bouquets(*model.get(itr, 0, 3), bqs)
|
||||||
bouquets.append(bqs)
|
bouquets.append(bqs)
|
||||||
|
|
||||||
|
profile = Profile(self.__profile)
|
||||||
# Getting bouquets
|
# Getting bouquets
|
||||||
self.__bouquets_view.get_model().foreach(parse_bouquets)
|
self.__bouquets_view.get_model().foreach(parse_bouquets)
|
||||||
write_bouquets(path, bouquets, self.__bouquets)
|
write_bouquets(path, bouquets, profile)
|
||||||
# Getting services
|
# Getting services
|
||||||
services = [Channel(*row[:]) for row in services_model]
|
services = [Service(*row[:]) for row in services_model]
|
||||||
write_channels(path, services)
|
write_services(path, services, profile)
|
||||||
# blacklist
|
# removing bouquet files
|
||||||
write_blacklist(path, self.__blacklist)
|
if profile is profile.ENIGMA_2:
|
||||||
|
for bqf in self.__bouquets_to_del:
|
||||||
|
with suppress(FileNotFoundError):
|
||||||
|
os.remove(bqf)
|
||||||
|
self.__bouquets_to_del.clear()
|
||||||
|
# blacklist
|
||||||
|
write_blacklist(path, self.__blacklist)
|
||||||
|
|
||||||
def on_services_selection(self, model, path, column):
|
def on_services_selection(self, model, path, column):
|
||||||
self.delete_selection(self.__fav_view)
|
self.delete_selection(self.__fav_view)
|
||||||
@@ -556,11 +590,11 @@ class MainAppWindow:
|
|||||||
if path:
|
if path:
|
||||||
tree_iter = model.get_iter(path)
|
tree_iter = model.get_iter(path)
|
||||||
|
|
||||||
key = bq_key if bq_key else "{}:{}".format(*model.get(tree_iter, 0, 1))
|
key = bq_key if bq_key else "{}:{}".format(*model.get(tree_iter, 0, 3))
|
||||||
services = self.__bouquets[key]
|
services = self.__bouquets[key]
|
||||||
|
|
||||||
for num, ch_id in enumerate(services):
|
for num, ch_id in enumerate(services):
|
||||||
channel = self.__channels.get(ch_id, None)
|
channel = self.__services.get(ch_id, None)
|
||||||
if channel:
|
if channel:
|
||||||
self.__fav_model.append((num + 1, channel.coded, channel.service, channel.locked,
|
self.__fav_model.append((num + 1, channel.coded, channel.service, channel.locked,
|
||||||
channel.hide, channel.service_type, channel.pos, channel.fav_id))
|
channel.hide, channel.service_type, channel.pos, channel.fav_id))
|
||||||
@@ -575,7 +609,7 @@ class MainAppWindow:
|
|||||||
if not path or len(path[0]) < 2:
|
if not path or len(path[0]) < 2:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
return "{}:{}".format(*model.get(model.get_iter(path), 0, 1))
|
return "{}:{}".format(*model.get(model.get_iter(path), 0, 3))
|
||||||
|
|
||||||
@run_idle
|
@run_idle
|
||||||
def delete_selection(self, view, *args):
|
def delete_selection(self, view, *args):
|
||||||
@@ -583,9 +617,17 @@ class MainAppWindow:
|
|||||||
for v in [view, *args]:
|
for v in [view, *args]:
|
||||||
v.get_selection().unselect_all()
|
v.get_selection().unselect_all()
|
||||||
|
|
||||||
|
@run_idle
|
||||||
def on_preferences(self, item):
|
def on_preferences(self, item):
|
||||||
show_settings_dialog(self.__main_window, self.__options)
|
response = show_settings_dialog(self.__main_window, self.__options)
|
||||||
self.__status_bar.push(0, "Current IP: " + self.__options["host"])
|
if response != Gtk.ResponseType.CANCEL:
|
||||||
|
profile = self.__options.get("profile")
|
||||||
|
if profile != self.__profile:
|
||||||
|
self.__status_bar.push(0, "Current IP: " + self.__options.get(profile).get("host"))
|
||||||
|
self.__profile_label.set_text("Enigma 2 v.4" if Profile(profile) is Profile.ENIGMA_2 else "Neutrino-MP")
|
||||||
|
self.__profile = profile
|
||||||
|
self.clear_current_data()
|
||||||
|
self.update_services_counts()
|
||||||
|
|
||||||
def on_tree_view_key_release(self, view, event):
|
def on_tree_view_key_release(self, view, event):
|
||||||
""" Handling keystrokes """
|
""" Handling keystrokes """
|
||||||
@@ -609,8 +651,6 @@ class MainAppWindow:
|
|||||||
self.on_to_fav_move(view)
|
self.on_to_fav_move(view)
|
||||||
elif model_name == self._BOUQUETS_LIST_NAME:
|
elif model_name == self._BOUQUETS_LIST_NAME:
|
||||||
self.on_new_bouquet(view)
|
self.on_new_bouquet(view)
|
||||||
elif key == Gdk.KEY_F2 and model_name == self._BOUQUETS_LIST_NAME:
|
|
||||||
self.on_bouquets_edit(view)
|
|
||||||
elif ctrl and (key == Gdk.KEY_c or key == Gdk.KEY_C) and model_name == self._SERVICE_LIST_NAME:
|
elif ctrl and (key == Gdk.KEY_c or key == Gdk.KEY_C) and model_name == self._SERVICE_LIST_NAME:
|
||||||
self.on_copy(view)
|
self.on_copy(view)
|
||||||
elif ctrl and key == Gdk.KEY_x or key == Gdk.KEY_X:
|
elif ctrl and key == Gdk.KEY_x or key == Gdk.KEY_X:
|
||||||
@@ -624,14 +664,20 @@ class MainAppWindow:
|
|||||||
self.on_locked(None)
|
self.on_locked(None)
|
||||||
elif ctrl and key == Gdk.KEY_h or key == Gdk.KEY_H:
|
elif ctrl and key == Gdk.KEY_h or key == Gdk.KEY_H:
|
||||||
self.on_hide(None)
|
self.on_hide(None)
|
||||||
|
elif ctrl and key == Gdk.KEY_E or key == Gdk.KEY_e or key == Gdk.KEY_F2:
|
||||||
|
self.on_edit(view)
|
||||||
elif key == Gdk.KEY_space and model_name == self._FAV_LIST_NAME:
|
elif key == Gdk.KEY_space and model_name == self._FAV_LIST_NAME:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def on_download(self, item):
|
def on_download(self, item):
|
||||||
show_download_dialog(self.__main_window, self.__options, self.open_data)
|
show_download_dialog(transient=self.__main_window,
|
||||||
|
options=self.__options.get(self.__profile),
|
||||||
|
open_data=self.open_data,
|
||||||
|
profile=Profile(self.__profile))
|
||||||
|
|
||||||
@run_idle
|
@run_idle
|
||||||
def on_view_focus(self, view, focus_event):
|
def on_view_focus(self, view, focus_event):
|
||||||
|
profile = Profile(self.__profile)
|
||||||
model = view.get_model()
|
model = view.get_model()
|
||||||
model_name = model.get_name()
|
model_name = model.get_name()
|
||||||
not_empty = len(model) > 0 # if > 0 model has items
|
not_empty = len(model) > 0 # if > 0 model has items
|
||||||
@@ -641,13 +687,17 @@ class MainAppWindow:
|
|||||||
self.__tool_elements[elem].set_sensitive(False)
|
self.__tool_elements[elem].set_sensitive(False)
|
||||||
for elem in self._BOUQUET_ELEMENTS:
|
for elem in self._BOUQUET_ELEMENTS:
|
||||||
self.__tool_elements[elem].set_sensitive(not_empty)
|
self.__tool_elements[elem].set_sensitive(not_empty)
|
||||||
|
if profile is Profile.NEUTRINO_MP:
|
||||||
|
for elem in self._LOCK_HIDE_ELEMENTS:
|
||||||
|
self.__tool_elements[elem].set_sensitive(not_empty)
|
||||||
else:
|
else:
|
||||||
is_service = model_name == self._SERVICE_LIST_NAME
|
is_service = model_name == self._SERVICE_LIST_NAME
|
||||||
for elem in self._FAV_ELEMENTS:
|
for elem in self._FAV_ELEMENTS:
|
||||||
if elem in ("paste_tool_button", "paste_menu_item", "fav_paste_popup_item"):
|
if elem in ("paste_tool_button", "paste_menu_item", "fav_paste_popup_item"):
|
||||||
self.__tool_elements[elem].set_sensitive(not is_service and self.__rows_buffer)
|
self.__tool_elements[elem].set_sensitive(not is_service and self.__rows_buffer)
|
||||||
elif elem in ("import_m3u_tool_button", "fav_import_m3u_popup_item"):
|
elif elem in self._FAV_ONLY_ELEMENTS:
|
||||||
self.__tool_elements[elem].set_sensitive(self.is_bouquet_selected() and not is_service)
|
if profile is Profile.ENIGMA_2:
|
||||||
|
self.__tool_elements[elem].set_sensitive(self.is_bouquet_selected() and not is_service)
|
||||||
else:
|
else:
|
||||||
self.__tool_elements[elem].set_sensitive(not_empty and not is_service)
|
self.__tool_elements[elem].set_sensitive(not_empty and not is_service)
|
||||||
for elem in self._SERVICE_ELEMENTS:
|
for elem in self._SERVICE_ELEMENTS:
|
||||||
@@ -655,9 +705,9 @@ class MainAppWindow:
|
|||||||
for elem in self._BOUQUET_ELEMENTS:
|
for elem in self._BOUQUET_ELEMENTS:
|
||||||
self.__tool_elements[elem].set_sensitive(False)
|
self.__tool_elements[elem].set_sensitive(False)
|
||||||
for elem in self._LOCK_HIDE_ELEMENTS:
|
for elem in self._LOCK_HIDE_ELEMENTS:
|
||||||
self.__tool_elements[elem].set_sensitive(not_empty and is_service)
|
self.__tool_elements[elem].set_sensitive(not_empty and profile is Profile.ENIGMA_2)
|
||||||
|
|
||||||
for elem in self._REMOVE_ELEMENTS:
|
for elem in self._COMMONS_ELEMENTS:
|
||||||
self.__tool_elements[elem].set_sensitive(not_empty)
|
self.__tool_elements[elem].set_sensitive(not_empty)
|
||||||
|
|
||||||
def on_hide(self, item):
|
def on_hide(self, item):
|
||||||
@@ -667,45 +717,19 @@ class MainAppWindow:
|
|||||||
self.set_service_flags(FLAG.LOCK)
|
self.set_service_flags(FLAG.LOCK)
|
||||||
|
|
||||||
def set_service_flags(self, flag):
|
def set_service_flags(self, flag):
|
||||||
model, paths = self.__services_view.get_selection().get_selected_rows()
|
profile = Profile(self.__profile)
|
||||||
if not paths:
|
|
||||||
return
|
|
||||||
|
|
||||||
if flag is FLAG.HIDE:
|
|
||||||
col_num = 5
|
|
||||||
hide = self.has_locked_hide(model, paths, col_num)
|
|
||||||
for path in paths:
|
|
||||||
itr = model.get_iter(path)
|
|
||||||
model.set_value(itr, col_num, None) if hide else model.set_value(itr, col_num, HIDE_ICON)
|
|
||||||
flags = {*model.get_value(itr, 0).split(",")}
|
|
||||||
flags.discard(FLAG.HIDE.value) if hide else flags.add(FLAG.HIDE.value)
|
|
||||||
model.set_value(itr, 0, (",".join(reversed(sorted(flags)))))
|
|
||||||
fav_id = model.get_value(itr, 16)
|
|
||||||
channel = self.__channels.get(fav_id, None)
|
|
||||||
if channel:
|
|
||||||
self.__channels[fav_id] = Channel(*channel[:5], None if hide else HIDE_ICON, *channel[6:])
|
|
||||||
elif flag is FLAG.LOCK:
|
|
||||||
col_num = 4
|
|
||||||
locked = self.has_locked_hide(model, paths, col_num)
|
|
||||||
for path in paths:
|
|
||||||
itr = model.get_iter(path)
|
|
||||||
fav_id = model.get_value(itr, 16)
|
|
||||||
channel = self.__channels.get(fav_id, None)
|
|
||||||
if channel:
|
|
||||||
bq_id = to_bouquet_id(channel)
|
|
||||||
self.__blacklist.discard(bq_id) if locked else self.__blacklist.add(bq_id)
|
|
||||||
model.set_value(itr, col_num, None) if locked else model.set_value(itr, col_num, LOCKED_ICON)
|
|
||||||
self.__channels[fav_id] = Channel(*channel[:4], None if locked else LOCKED_ICON, *channel[5:])
|
|
||||||
bq_selected = self.is_bouquet_selected()
|
bq_selected = self.is_bouquet_selected()
|
||||||
if bq_selected:
|
if profile is Profile.ENIGMA_2:
|
||||||
self.__fav_model.clear()
|
if set_flags(flag, self.__services_view, self.__fav_view, self.__services, self.__blacklist):
|
||||||
self.update_bouquet_channels(self.__fav_model, None, bq_selected)
|
if bq_selected:
|
||||||
|
self.__fav_model.clear()
|
||||||
def has_locked_hide(self, model, paths, col_num):
|
self.update_bouquet_channels(self.__fav_model, None, bq_selected)
|
||||||
for path in paths:
|
elif profile is Profile.NEUTRINO_MP:
|
||||||
if model.get_value(model.get_iter(path), col_num):
|
if bq_selected:
|
||||||
return True
|
model, path = self.__bouquets_view.get_selection().get_selected()
|
||||||
return False
|
value = model.get_value(path, 1 if flag is FLAG.LOCK else 2)
|
||||||
|
value = None if value else LOCKED_ICON if flag is FLAG.LOCK else HIDE_ICON
|
||||||
|
model.set_value(path, 1 if flag is FLAG.LOCK else 2, value)
|
||||||
|
|
||||||
@run_idle
|
@run_idle
|
||||||
def on_model_changed(self, model, path, itr=None):
|
def on_model_changed(self, model, path, itr=None):
|
||||||
@@ -725,7 +749,7 @@ class MainAppWindow:
|
|||||||
radio_count = 0
|
radio_count = 0
|
||||||
data_count = 0
|
data_count = 0
|
||||||
|
|
||||||
for ch in self.__channels.values():
|
for ch in self.__services.values():
|
||||||
ch_type = ch.service_type
|
ch_type = ch.service_type
|
||||||
if ch_type in ("TV", "TV (HD)"):
|
if ch_type in ("TV", "TV (HD)"):
|
||||||
tv_count += 1
|
tv_count += 1
|
||||||
@@ -745,7 +769,7 @@ class MainAppWindow:
|
|||||||
file_filter.set_name("m3u files")
|
file_filter.set_name("m3u files")
|
||||||
response = show_dialog(dialog_type=DialogType.CHOOSER,
|
response = show_dialog(dialog_type=DialogType.CHOOSER,
|
||||||
transient=self.__main_window,
|
transient=self.__main_window,
|
||||||
options=self.__options,
|
options=self.__options.get(self.__profile),
|
||||||
action_type=Gtk.FileChooserAction.OPEN,
|
action_type=Gtk.FileChooserAction.OPEN,
|
||||||
file_filter=file_filter)
|
file_filter=file_filter)
|
||||||
if response == Gtk.ResponseType.CANCEL:
|
if response == Gtk.ResponseType.CANCEL:
|
||||||
@@ -761,17 +785,17 @@ class MainAppWindow:
|
|||||||
bq_services = self.__bouquets.get(bq_selected)
|
bq_services = self.__bouquets.get(bq_selected)
|
||||||
self.__fav_model.clear()
|
self.__fav_model.clear()
|
||||||
for ch in channels:
|
for ch in channels:
|
||||||
self.__channels[ch.fav_id] = ch
|
self.__services[ch.fav_id] = ch
|
||||||
bq_services.append(ch.fav_id)
|
bq_services.append(ch.fav_id)
|
||||||
self.update_bouquet_channels(self.__fav_model, None, bq_selected)
|
self.update_bouquet_channels(self.__fav_model, None, bq_selected)
|
||||||
|
|
||||||
def on_insert_marker(self, view):
|
def on_insert_marker(self, view):
|
||||||
""" Inserts marker into bouquet services list. """
|
""" Inserts marker into bouquet services list. """
|
||||||
insert_marker(view, self.__bouquets, self.is_bouquet_selected(), self.__channels, self.__main_window)
|
insert_marker(view, self.__bouquets, self.is_bouquet_selected(), self.__services, self.__main_window)
|
||||||
self.update_fav_num_column(self.__fav_model)
|
self.update_fav_num_column(self.__fav_model)
|
||||||
|
|
||||||
def on_edit_marker(self, view):
|
def on_edit_marker(self, view):
|
||||||
edit_marker(view, self.__bouquets, self.is_bouquet_selected(), self.__channels, self.__main_window)
|
edit_marker(view, self.__bouquets, self.is_bouquet_selected(), self.__services, self.__main_window)
|
||||||
|
|
||||||
@run_idle
|
@run_idle
|
||||||
def on_fav_popup(self, view, event):
|
def on_fav_popup(self, view, event):
|
||||||
@@ -779,6 +803,13 @@ class MainAppWindow:
|
|||||||
self.__fav_edit_marker_popup_item.set_sensitive(
|
self.__fav_edit_marker_popup_item.set_sensitive(
|
||||||
len(paths) == 1 and model.get_value(model.get_iter(paths[0]), 5) == BqServiceType.MARKER.name)
|
len(paths) == 1 and model.get_value(model.get_iter(paths[0]), 5) == BqServiceType.MARKER.name)
|
||||||
|
|
||||||
|
def on_locate_in_services(self, view):
|
||||||
|
locate_in_services(view, self.__services_view, self.__main_window)
|
||||||
|
|
||||||
|
def on_picons_loader_show(self, item):
|
||||||
|
dialog = PiconsDialog(self.__main_window, self.__options.get(self.__profile), Profile(self.__profile))
|
||||||
|
dialog.show()
|
||||||
|
|
||||||
|
|
||||||
def start_app():
|
def start_app():
|
||||||
MainAppWindow()
|
MainAppWindow()
|
||||||
|
|||||||
@@ -1,9 +1,21 @@
|
|||||||
""" This is helper module for main_app_window """
|
""" This is helper module for ui """
|
||||||
from app.eparser import Channel
|
from enum import Enum
|
||||||
from app.eparser.bouquets import BqServiceType
|
|
||||||
from .dialogs import show_dialog, DialogType
|
|
||||||
from . import Gtk
|
|
||||||
|
|
||||||
|
from app.eparser import Service
|
||||||
|
from app.eparser.ecommons import FLAG
|
||||||
|
from app.eparser.enigma.bouquets import BqServiceType, to_bouquet_id
|
||||||
|
from . import Gtk, Gdk, HIDE_ICON, LOCKED_ICON
|
||||||
|
from .dialogs import show_dialog, DialogType
|
||||||
|
|
||||||
|
|
||||||
|
class ViewTarget(Enum):
|
||||||
|
""" Used for set target view """
|
||||||
|
BOUQUET = 0
|
||||||
|
FAV = 1
|
||||||
|
SERVICES = 2
|
||||||
|
|
||||||
|
|
||||||
|
# ***************** Markers *******************#
|
||||||
|
|
||||||
def insert_marker(view, bouquets, selected_bouquet, channels, parent_window):
|
def insert_marker(view, bouquets, selected_bouquet, channels, parent_window):
|
||||||
"""" Inserts marker into bouquet services list. """
|
"""" Inserts marker into bouquet services list. """
|
||||||
@@ -23,11 +35,11 @@ def insert_marker(view, bouquets, selected_bouquet, channels, parent_window):
|
|||||||
s_type = BqServiceType.MARKER.name
|
s_type = BqServiceType.MARKER.name
|
||||||
model, paths = view.get_selection().get_selected_rows()
|
model, paths = view.get_selection().get_selected_rows()
|
||||||
itr = model.insert_before(model.get_iter(paths[0]), (None, None, response, None, None, s_type, None, fav_id))
|
itr = model.insert_before(model.get_iter(paths[0]), (None, None, response, None, None, s_type, None, fav_id))
|
||||||
channels[fav_id] = Channel(None, None, None, response, None, None, None, s_type, *[None] * 7, max_num, fav_id, None)
|
channels[fav_id] = Service(None, None, None, response, None, None, None, s_type, *[None] * 7, max_num, fav_id, None)
|
||||||
bouquets[selected_bouquet].insert(model.get_path(itr)[0], fav_id)
|
bouquets[selected_bouquet].insert(model.get_path(itr)[0], fav_id)
|
||||||
|
|
||||||
|
|
||||||
def edit_marker(view, bouquets, selected_bouquet, channels, parent_window):
|
def edit_marker(view, bouquets, selected_bouquet, channels, parent_window):
|
||||||
""" Edits marker text """
|
""" Edits marker text """
|
||||||
model, paths = view.get_selection().get_selected_rows()
|
model, paths = view.get_selection().get_selected_rows()
|
||||||
itr = model.get_iter(paths[0])
|
itr = model.get_iter(paths[0])
|
||||||
@@ -41,10 +53,235 @@ def edit_marker(view, bouquets, selected_bouquet, channels, parent_window):
|
|||||||
old_ch = channels.pop(fav_id, None)
|
old_ch = channels.pop(fav_id, None)
|
||||||
new_fav_id = "{}::{}\n#DESCRIPTION {}\n".format(fav_id.split("::")[0], response, response)
|
new_fav_id = "{}::{}\n#DESCRIPTION {}\n".format(fav_id.split("::")[0], response, response)
|
||||||
model.set(itr, {2: response, 7: new_fav_id})
|
model.set(itr, {2: response, 7: new_fav_id})
|
||||||
channels[new_fav_id] = Channel(*old_ch[0:3], response, *old_ch[4:15], old_ch.data_id, new_fav_id, None)
|
channels[new_fav_id] = Service(*old_ch[0:3], response, *old_ch[4:15], old_ch.data_id, new_fav_id, None)
|
||||||
bq_services.pop(index)
|
bq_services.pop(index)
|
||||||
bq_services.insert(index, new_fav_id)
|
bq_services.insert(index, new_fav_id)
|
||||||
|
|
||||||
|
|
||||||
|
# ***************** Movement *******************#
|
||||||
|
|
||||||
|
def move_items(key, view):
|
||||||
|
""" Move items in 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)
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
|
||||||
|
# ***************** Edit *******************#
|
||||||
|
|
||||||
|
def edit(view, parent_window, target, fav_view=None, service_view=None, channels=None):
|
||||||
|
model, paths = view.get_selection().get_selected_rows()
|
||||||
|
|
||||||
|
if not paths:
|
||||||
|
return
|
||||||
|
elif len(paths) > 1:
|
||||||
|
show_dialog(DialogType.ERROR, parent_window, "Please, select only one item!")
|
||||||
|
return
|
||||||
|
|
||||||
|
itr = model.get_iter(paths)
|
||||||
|
f_id = None
|
||||||
|
channel_name = None
|
||||||
|
|
||||||
|
if target is ViewTarget.SERVICES:
|
||||||
|
name, fav_id = model.get(itr, 3, 16)
|
||||||
|
f_id = fav_id
|
||||||
|
response = show_dialog(DialogType.INPUT, parent_window, name)
|
||||||
|
if response == Gtk.ResponseType.CANCEL:
|
||||||
|
return
|
||||||
|
channel_name = response
|
||||||
|
model.set_value(itr, 3, response)
|
||||||
|
if fav_view is not None:
|
||||||
|
for row in fav_view.get_model():
|
||||||
|
if row[7] == fav_id:
|
||||||
|
row[2] = response
|
||||||
|
break
|
||||||
|
elif target is ViewTarget.FAV:
|
||||||
|
name, fav_id = model.get(itr, 2, 7)
|
||||||
|
f_id = fav_id
|
||||||
|
response = show_dialog(DialogType.INPUT, parent_window, name)
|
||||||
|
if response == Gtk.ResponseType.CANCEL:
|
||||||
|
return
|
||||||
|
|
||||||
|
channel_name = response
|
||||||
|
model.set_value(itr, 2, response)
|
||||||
|
|
||||||
|
if service_view is not None:
|
||||||
|
for row in service_view.get_model():
|
||||||
|
if row[16] == fav_id:
|
||||||
|
row[3] = response
|
||||||
|
break
|
||||||
|
|
||||||
|
old_ch = channels.get(f_id, None)
|
||||||
|
if old_ch:
|
||||||
|
channels[f_id] = Service(*old_ch[0:3], channel_name, *old_ch[4:])
|
||||||
|
|
||||||
|
|
||||||
|
# ***************** Flags *******************#
|
||||||
|
|
||||||
|
def set_flags(flag, services_view, fav_view, channels, blacklist):
|
||||||
|
""" Updates flags for services. Returns True if any was changed. """
|
||||||
|
target = ViewTarget.SERVICES if services_view.is_focus() else ViewTarget.FAV if fav_view.is_focus() else None
|
||||||
|
if not target:
|
||||||
|
return
|
||||||
|
|
||||||
|
model, paths = None, None
|
||||||
|
|
||||||
|
if target is ViewTarget.SERVICES:
|
||||||
|
model, paths = services_view.get_selection().get_selected_rows()
|
||||||
|
elif target is ViewTarget.FAV:
|
||||||
|
model, paths = fav_view.get_selection().get_selected_rows()
|
||||||
|
|
||||||
|
if not paths:
|
||||||
|
return
|
||||||
|
|
||||||
|
if flag is FLAG.HIDE:
|
||||||
|
if target is ViewTarget.SERVICES:
|
||||||
|
set_hide(channels, model, paths)
|
||||||
|
else:
|
||||||
|
fav_ids = [model.get_value(model.get_iter(path), 7) for path in paths]
|
||||||
|
srv_model = services_view.get_model()
|
||||||
|
srv_paths = [row.path for row in srv_model if row[16] in fav_ids]
|
||||||
|
set_hide(channels, srv_model, srv_paths)
|
||||||
|
elif flag is FLAG.LOCK:
|
||||||
|
set_lock(blacklist, channels, model, paths, target, services_model=services_view.get_model())
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def set_lock(blacklist, channels, model, paths, target, services_model):
|
||||||
|
col_num = 4 if target is ViewTarget.SERVICES else 3
|
||||||
|
locked = has_locked_hide(model, paths, col_num)
|
||||||
|
|
||||||
|
ids = []
|
||||||
|
|
||||||
|
for path in paths:
|
||||||
|
itr = model.get_iter(path)
|
||||||
|
fav_id = model.get_value(itr, 16 if target is ViewTarget.SERVICES else 7)
|
||||||
|
channel = channels.get(fav_id, None)
|
||||||
|
if channel:
|
||||||
|
bq_id = to_bouquet_id(channel)
|
||||||
|
blacklist.discard(bq_id) if locked else blacklist.add(bq_id)
|
||||||
|
model.set_value(itr, col_num, None if locked else LOCKED_ICON)
|
||||||
|
channels[fav_id] = Service(*channel[:4], None if locked else LOCKED_ICON, *channel[5:])
|
||||||
|
ids.append(fav_id)
|
||||||
|
|
||||||
|
if target is ViewTarget.FAV and ids:
|
||||||
|
for ch in services_model:
|
||||||
|
if ch[16] in ids:
|
||||||
|
ch[4] = None if locked else LOCKED_ICON
|
||||||
|
|
||||||
|
|
||||||
|
def set_hide(channels, model, paths):
|
||||||
|
col_num = 5
|
||||||
|
hide = has_locked_hide(model, paths, col_num)
|
||||||
|
|
||||||
|
for path in paths:
|
||||||
|
itr = model.get_iter(path)
|
||||||
|
model.set_value(itr, col_num, None if hide else HIDE_ICON)
|
||||||
|
flags = [*model.get_value(itr, 0).split(",")]
|
||||||
|
index, flag = None, None
|
||||||
|
for i, fl in enumerate(flags):
|
||||||
|
if fl.startswith("f:"):
|
||||||
|
index = i
|
||||||
|
flag = fl
|
||||||
|
break
|
||||||
|
|
||||||
|
value = int(flag[2:]) if flag else 0
|
||||||
|
|
||||||
|
if not hide:
|
||||||
|
if value in FLAG.hide_values():
|
||||||
|
continue # skip if already hidden
|
||||||
|
value += FLAG.HIDE.value
|
||||||
|
else:
|
||||||
|
if value not in FLAG.hide_values():
|
||||||
|
continue # skip if already allowed to show
|
||||||
|
value -= FLAG.HIDE.value
|
||||||
|
|
||||||
|
if value == 0 and index is not None:
|
||||||
|
del flags[index]
|
||||||
|
else:
|
||||||
|
value = "f:{}".format(value) if value > 10 else "f:0{}".format(value)
|
||||||
|
if index is not None:
|
||||||
|
flags[index] = value
|
||||||
|
else:
|
||||||
|
flags.append(value)
|
||||||
|
|
||||||
|
model.set_value(itr, 0, (",".join(reversed(sorted(flags)))))
|
||||||
|
fav_id = model.get_value(itr, 16)
|
||||||
|
channel = channels.get(fav_id, None)
|
||||||
|
if channel:
|
||||||
|
channels[fav_id] = Service(*channel[:5], None if hide else HIDE_ICON, *channel[6:])
|
||||||
|
|
||||||
|
|
||||||
|
def has_locked_hide(model, paths, col_num):
|
||||||
|
for path in paths:
|
||||||
|
if model.get_value(model.get_iter(path), col_num):
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
# ***************** Location *******************#
|
||||||
|
|
||||||
|
def locate_in_services(fav_view, services_view, parent_window):
|
||||||
|
""" Locating and scrolling to the service """
|
||||||
|
model, paths = fav_view.get_selection().get_selected_rows()
|
||||||
|
|
||||||
|
if not paths:
|
||||||
|
return
|
||||||
|
elif len(paths) > 1:
|
||||||
|
show_dialog(DialogType.ERROR, parent_window, "Please, select only one item!")
|
||||||
|
return
|
||||||
|
|
||||||
|
fav_id = model.get_value(model.get_iter(paths[0]), 7)
|
||||||
|
for index, row in enumerate(services_view.get_model()):
|
||||||
|
if row[16] == fav_id:
|
||||||
|
scroll_to(index, services_view)
|
||||||
|
break
|
||||||
|
|
||||||
|
|
||||||
|
def scroll_to(index, view, paths=None):
|
||||||
|
""" Scrolling to and selecting given index(path) """
|
||||||
|
if paths is not None:
|
||||||
|
view.expand_row(paths[0], 0)
|
||||||
|
view.scroll_to_cell(index, None)
|
||||||
|
selection = view.get_selection()
|
||||||
|
selection.unselect_all()
|
||||||
|
selection.select_path(index)
|
||||||
|
|
||||||
|
|
||||||
|
# ***************** Others *********************#
|
||||||
|
|
||||||
|
def update_entry_data(entry, dialog, options):
|
||||||
|
""" Updates value in text entry from chooser dialog """
|
||||||
|
response = show_dialog(dialog_type=DialogType.CHOOSER, transient=dialog, options=options)
|
||||||
|
if response not in (Gtk.ResponseType.CANCEL, Gtk.ResponseType.DELETE_EVENT):
|
||||||
|
entry.set_text(response)
|
||||||
|
return response
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
pass
|
pass
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkImageMenuItem" id="bouguets_edit_popup_item">
|
<object class="GtkImageMenuItem" id="bouquets_edit_popup_item">
|
||||||
<property name="label">gtk-edit</property>
|
<property name="label">gtk-edit</property>
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
<property name="sensitive">False</property>
|
<property name="sensitive">False</property>
|
||||||
@@ -43,6 +43,10 @@
|
|||||||
<columns>
|
<columns>
|
||||||
<!-- column-name bouquet -->
|
<!-- column-name bouquet -->
|
||||||
<column type="gchararray"/>
|
<column type="gchararray"/>
|
||||||
|
<!-- column-name locked -->
|
||||||
|
<column type="GdkPixbuf"/>
|
||||||
|
<!-- column-name hidden -->
|
||||||
|
<column type="GdkPixbuf"/>
|
||||||
<!-- column-name type -->
|
<!-- column-name type -->
|
||||||
<column type="gchararray"/>
|
<column type="gchararray"/>
|
||||||
</columns>
|
</columns>
|
||||||
@@ -79,7 +83,7 @@
|
|||||||
<object class="GtkImage" id="image4">
|
<object class="GtkImage" id="image4">
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
<property name="can_focus">False</property>
|
<property name="can_focus">False</property>
|
||||||
<property name="icon_name">applications-utilities</property>
|
<property name="icon_name">edit-select-all</property>
|
||||||
</object>
|
</object>
|
||||||
<object class="GtkImage" id="image5">
|
<object class="GtkImage" id="image5">
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
@@ -91,6 +95,11 @@
|
|||||||
<property name="can_focus">False</property>
|
<property name="can_focus">False</property>
|
||||||
<property name="stock">gtk-edit</property>
|
<property name="stock">gtk-edit</property>
|
||||||
</object>
|
</object>
|
||||||
|
<object class="GtkImage" id="image7">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="stock">gtk-find</property>
|
||||||
|
</object>
|
||||||
<object class="GtkMenu" id="fav_popup_menu">
|
<object class="GtkMenu" id="fav_popup_menu">
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
<property name="can_focus">False</property>
|
<property name="can_focus">False</property>
|
||||||
@@ -116,6 +125,34 @@
|
|||||||
<signal name="activate" handler="on_paste" object="fav_tree_view" swapped="no"/>
|
<signal name="activate" handler="on_paste" object="fav_tree_view" swapped="no"/>
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkImageMenuItem" id="fav_edit_popup_item">
|
||||||
|
<property name="label">gtk-edit</property>
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="sensitive">False</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="use_underline">True</property>
|
||||||
|
<property name="use_stock">True</property>
|
||||||
|
<signal name="activate" handler="on_edit" object="fav_tree_view" swapped="no"/>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkSeparatorMenuItem" id="separatormenuitem3">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkImageMenuItem" id="fav_locate_popup_item">
|
||||||
|
<property name="label" translatable="yes">Locate in services</property>
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="sensitive">False</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="image">image7</property>
|
||||||
|
<property name="use_stock">False</property>
|
||||||
|
<signal name="activate" handler="on_locate_in_services" object="fav_tree_view" swapped="no"/>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkSeparatorMenuItem" id="fav_pupup_separator_1">
|
<object class="GtkSeparatorMenuItem" id="fav_pupup_separator_1">
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
@@ -182,6 +219,11 @@
|
|||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
</object>
|
</object>
|
||||||
|
<object class="GtkImage" id="image8">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="icon_name">insert-image</property>
|
||||||
|
</object>
|
||||||
<object class="GtkImage" id="send_recive_image">
|
<object class="GtkImage" id="send_recive_image">
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
<property name="can_focus">False</property>
|
<property name="can_focus">False</property>
|
||||||
@@ -374,6 +416,17 @@
|
|||||||
<signal name="activate" handler="on_satellite_editor_show" swapped="no"/>
|
<signal name="activate" handler="on_satellite_editor_show" swapped="no"/>
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkImageMenuItem" id="Picons loader">
|
||||||
|
<property name="label" translatable="yes">Picons loader</property>
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="use_underline">True</property>
|
||||||
|
<property name="image">image8</property>
|
||||||
|
<property name="use_stock">False</property>
|
||||||
|
<signal name="activate" handler="on_picons_loader_show" swapped="no"/>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkSeparatorMenuItem" id="menuitem5">
|
<object class="GtkSeparatorMenuItem" id="menuitem5">
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
@@ -534,6 +587,7 @@
|
|||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
<property name="sensitive">False</property>
|
<property name="sensitive">False</property>
|
||||||
<property name="can_focus">False</property>
|
<property name="can_focus">False</property>
|
||||||
|
<property name="tooltip_text" translatable="yes">Up</property>
|
||||||
<property name="label" translatable="yes">Up</property>
|
<property name="label" translatable="yes">Up</property>
|
||||||
<property name="use_underline">True</property>
|
<property name="use_underline">True</property>
|
||||||
<property name="stock_id">gtk-go-up</property>
|
<property name="stock_id">gtk-go-up</property>
|
||||||
@@ -549,6 +603,7 @@
|
|||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
<property name="sensitive">False</property>
|
<property name="sensitive">False</property>
|
||||||
<property name="can_focus">False</property>
|
<property name="can_focus">False</property>
|
||||||
|
<property name="tooltip_text" translatable="yes">Down</property>
|
||||||
<property name="label" translatable="yes">Down</property>
|
<property name="label" translatable="yes">Down</property>
|
||||||
<property name="use_underline">True</property>
|
<property name="use_underline">True</property>
|
||||||
<property name="stock_id">gtk-go-down</property>
|
<property name="stock_id">gtk-go-down</property>
|
||||||
@@ -687,54 +742,17 @@
|
|||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
<property name="sensitive">False</property>
|
<property name="sensitive">False</property>
|
||||||
<property name="can_focus">False</property>
|
<property name="can_focus">False</property>
|
||||||
<property name="tooltip_text" translatable="yes">Rename bouquet</property>
|
<property name="tooltip_text" translatable="yes">Edit</property>
|
||||||
<property name="label" translatable="yes">Edit </property>
|
<property name="label" translatable="yes">Edit </property>
|
||||||
<property name="use_underline">True</property>
|
<property name="use_underline">True</property>
|
||||||
<property name="stock_id">gtk-edit</property>
|
<property name="stock_id">gtk-edit</property>
|
||||||
<signal name="clicked" handler="on_bouquets_edit" object="bouquets_tree_view" swapped="no"/>
|
<signal name="clicked" handler="on_tool_edit" swapped="no"/>
|
||||||
</object>
|
</object>
|
||||||
<packing>
|
<packing>
|
||||||
<property name="expand">False</property>
|
<property name="expand">False</property>
|
||||||
<property name="homogeneous">True</property>
|
<property name="homogeneous">True</property>
|
||||||
</packing>
|
</packing>
|
||||||
</child>
|
</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="import_m3u_tool_button">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="sensitive">False</property>
|
|
||||||
<property name="can_focus">False</property>
|
|
||||||
<property name="tooltip_text" translatable="yes">Import m3u file</property>
|
|
||||||
<property name="opacity">0.93999999999999995</property>
|
|
||||||
<property name="label" translatable="yes">IPTV</property>
|
|
||||||
<property name="use_underline">True</property>
|
|
||||||
<property name="icon_name">emblem-downloads</property>
|
|
||||||
<signal name="clicked" handler="on_import_m3u" swapped="no"/>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="expand">False</property>
|
|
||||||
<property name="homogeneous">True</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
<child>
|
|
||||||
<object class="GtkSeparatorToolItem" id="separatortoolitem7">
|
|
||||||
<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>
|
<child>
|
||||||
<object class="GtkToolButton" id="remove_tool_button">
|
<object class="GtkToolButton" id="remove_tool_button">
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
@@ -751,19 +769,20 @@
|
|||||||
</packing>
|
</packing>
|
||||||
</child>
|
</child>
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkSeparatorToolItem" id="toolbutton9">
|
<object class="GtkSeparatorToolItem" id="separatortoolitem4">
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
<property name="can_focus">False</property>
|
<property name="can_focus">False</property>
|
||||||
</object>
|
</object>
|
||||||
<packing>
|
<packing>
|
||||||
<property name="expand">False</property>
|
<property name="expand">False</property>
|
||||||
<property name="homogeneous">True</property>
|
<property name="homogeneous">False</property>
|
||||||
</packing>
|
</packing>
|
||||||
</child>
|
</child>
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkToolButton" id="preferences_tool_button">
|
<object class="GtkToolButton" id="preferences_tool_button">
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
<property name="can_focus">False</property>
|
<property name="can_focus">False</property>
|
||||||
|
<property name="tooltip_text" translatable="yes">Settings</property>
|
||||||
<property name="label" translatable="yes">Preferences</property>
|
<property name="label" translatable="yes">Preferences</property>
|
||||||
<property name="use_underline">True</property>
|
<property name="use_underline">True</property>
|
||||||
<property name="stock_id">gtk-preferences</property>
|
<property name="stock_id">gtk-preferences</property>
|
||||||
@@ -775,13 +794,23 @@
|
|||||||
</packing>
|
</packing>
|
||||||
</child>
|
</child>
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkToolButton" id="tools_button">
|
<object class="GtkSeparatorToolItem" id="separatortoolitem7">
|
||||||
|
<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="satellites_editor_tool_button">
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
<property name="can_focus">False</property>
|
<property name="can_focus">False</property>
|
||||||
<property name="tooltip_text" translatable="yes">Satellites editor</property>
|
<property name="tooltip_text" translatable="yes">Satellites editor</property>
|
||||||
<property name="label" translatable="yes">Tools</property>
|
<property name="label" translatable="yes">Satellites editor</property>
|
||||||
<property name="use_underline">True</property>
|
<property name="use_underline">True</property>
|
||||||
<property name="icon_name">applications-utilities</property>
|
<property name="icon_name">edit-select-all</property>
|
||||||
<signal name="clicked" handler="on_satellite_editor_show" swapped="no"/>
|
<signal name="clicked" handler="on_satellite_editor_show" swapped="no"/>
|
||||||
</object>
|
</object>
|
||||||
<packing>
|
<packing>
|
||||||
@@ -789,6 +818,38 @@
|
|||||||
<property name="homogeneous">True</property>
|
<property name="homogeneous">True</property>
|
||||||
</packing>
|
</packing>
|
||||||
</child>
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkToolButton" id="picons_download_tool_button">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="tooltip_text" translatable="yes">Picons</property>
|
||||||
|
<property name="label" translatable="yes">Picons loader</property>
|
||||||
|
<property name="use_underline">True</property>
|
||||||
|
<property name="icon_name">insert-image</property>
|
||||||
|
<signal name="clicked" handler="on_picons_loader_show" swapped="no"/>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="homogeneous">True</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkToolButton" id="import_m3u_tool_button">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="sensitive">False</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="tooltip_text" translatable="yes">Import m3u file</property>
|
||||||
|
<property name="opacity">0.93999999999999995</property>
|
||||||
|
<property name="label" translatable="yes">IPTV</property>
|
||||||
|
<property name="use_underline">True</property>
|
||||||
|
<property name="icon_name">emblem-downloads</property>
|
||||||
|
<signal name="clicked" handler="on_import_m3u" swapped="no"/>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="homogeneous">True</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkSeparatorToolItem" id="toolbutton11">
|
<object class="GtkSeparatorToolItem" id="toolbutton11">
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
@@ -1495,7 +1556,7 @@
|
|||||||
<property name="can_focus">True</property>
|
<property name="can_focus">True</property>
|
||||||
<property name="model">bouquets_tree_store</property>
|
<property name="model">bouquets_tree_store</property>
|
||||||
<property name="headers_clickable">False</property>
|
<property name="headers_clickable">False</property>
|
||||||
<property name="enable_search">False</property>
|
<property name="search_column">0</property>
|
||||||
<property name="activate_on_single_click">True</property>
|
<property name="activate_on_single_click">True</property>
|
||||||
<signal name="button-press-event" handler="on_view_popup_menu" object="bouquets_popup_menu" swapped="no"/>
|
<signal name="button-press-event" handler="on_view_popup_menu" object="bouquets_popup_menu" swapped="no"/>
|
||||||
<signal name="focus-in-event" handler="on_view_focus" swapped="no"/>
|
<signal name="focus-in-event" handler="on_view_focus" swapped="no"/>
|
||||||
@@ -1507,6 +1568,7 @@
|
|||||||
<child>
|
<child>
|
||||||
<object class="GtkTreeViewColumn" id="bouquets_column">
|
<object class="GtkTreeViewColumn" id="bouquets_column">
|
||||||
<property name="resizable">True</property>
|
<property name="resizable">True</property>
|
||||||
|
<property name="spacing">2</property>
|
||||||
<property name="sizing">autosize</property>
|
<property name="sizing">autosize</property>
|
||||||
<property name="title" translatable="yes">Bouquets</property>
|
<property name="title" translatable="yes">Bouquets</property>
|
||||||
<property name="expand">True</property>
|
<property name="expand">True</property>
|
||||||
@@ -1516,6 +1578,20 @@
|
|||||||
<attribute name="text">0</attribute>
|
<attribute name="text">0</attribute>
|
||||||
</attributes>
|
</attributes>
|
||||||
</child>
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkCellRendererPixbuf" id="boiquets_cellrenderer_locked"/>
|
||||||
|
<attributes>
|
||||||
|
<attribute name="pixbuf">1</attribute>
|
||||||
|
</attributes>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkCellRendererPixbuf" id="boiquets_cellrenderer_hidden">
|
||||||
|
<property name="xalign">0</property>
|
||||||
|
</object>
|
||||||
|
<attributes>
|
||||||
|
<attribute name="pixbuf">2</attribute>
|
||||||
|
</attributes>
|
||||||
|
</child>
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
<child>
|
<child>
|
||||||
@@ -1527,7 +1603,7 @@
|
|||||||
<child>
|
<child>
|
||||||
<object class="GtkCellRendererText" id="bouquet_type_cellrenderertext"/>
|
<object class="GtkCellRendererText" id="bouquet_type_cellrenderertext"/>
|
||||||
<attributes>
|
<attributes>
|
||||||
<attribute name="text">1</attribute>
|
<attribute name="text">3</attribute>
|
||||||
</attributes>
|
</attributes>
|
||||||
</child>
|
</child>
|
||||||
</object>
|
</object>
|
||||||
@@ -1627,13 +1703,46 @@
|
|||||||
<property name="spacing">2</property>
|
<property name="spacing">2</property>
|
||||||
<property name="homogeneous">True</property>
|
<property name="homogeneous">True</property>
|
||||||
<child>
|
<child>
|
||||||
<placeholder/>
|
<object class="GtkBox" id="box1">
|
||||||
</child>
|
|
||||||
<child>
|
|
||||||
<object class="GtkLabel" id="label2">
|
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
<property name="can_focus">False</property>
|
<property name="can_focus">False</property>
|
||||||
<property name="label" translatable="yes">Ver. 0.1.1 Pre-alpha</property>
|
<property name="spacing">2</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkLabel" id="label1">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="label" translatable="yes">Profile:</property>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="position">0</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkLabel" id="profile_label">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="label" translatable="yes">Enigma 2 v.4</property>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="position">1</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">False</property>
|
||||||
|
<property name="position">1</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkLabel" id="ver_label">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="label" translatable="yes">Ver. 0.2.1 Pre-alpha</property>
|
||||||
<property name="xalign">0.94999998807907104</property>
|
<property name="xalign">0.94999998807907104</property>
|
||||||
</object>
|
</object>
|
||||||
<packing>
|
<packing>
|
||||||
@@ -1666,6 +1775,41 @@
|
|||||||
<signal name="activate" handler="on_to_fav_move" object="services_tree_view" swapped="no"/>
|
<signal name="activate" handler="on_to_fav_move" object="services_tree_view" swapped="no"/>
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkSeparatorMenuItem" id="separatormenuitem4">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkImageMenuItem" id="services_copy_popup_item">
|
||||||
|
<property name="label">gtk-copy</property>
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="sensitive">False</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="use_underline">True</property>
|
||||||
|
<property name="use_stock">True</property>
|
||||||
|
<signal name="activate" handler="on_copy" object="services_tree_view" swapped="no"/>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkImageMenuItem" id="services_edit_popup_item">
|
||||||
|
<property name="label">gtk-edit</property>
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="sensitive">False</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="resize_mode">immediate</property>
|
||||||
|
<property name="use_underline">True</property>
|
||||||
|
<property name="use_stock">True</property>
|
||||||
|
<signal name="activate" handler="on_edit" object="services_tree_view" swapped="no"/>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkSeparatorMenuItem" id="separatormenuitem2">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkImageMenuItem" id="services_remove_popup_item">
|
<object class="GtkImageMenuItem" id="services_remove_popup_item">
|
||||||
<property name="label">gtk-remove</property>
|
<property name="label">gtk-remove</property>
|
||||||
|
|||||||
572
app/ui/picons_dialog.glade
Normal file
572
app/ui/picons_dialog.glade
Normal file
@@ -0,0 +1,572 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!-- Generated with glade 3.18.3 -->
|
||||||
|
<interface>
|
||||||
|
<requires lib="gtk+" version="3.12"/>
|
||||||
|
<!-- interface-css-provider-path style.css -->
|
||||||
|
<object class="GtkListStore" id="providers_list_store">
|
||||||
|
<columns>
|
||||||
|
<!-- column-name logo -->
|
||||||
|
<column type="GdkPixbuf"/>
|
||||||
|
<!-- column-name name -->
|
||||||
|
<column type="gchararray"/>
|
||||||
|
<!-- column-name url -->
|
||||||
|
<column type="gchararray"/>
|
||||||
|
<!-- column-name on_id -->
|
||||||
|
<column type="gchararray"/>
|
||||||
|
<!-- column-name selected -->
|
||||||
|
<column type="gboolean"/>
|
||||||
|
</columns>
|
||||||
|
</object>
|
||||||
|
<object class="GtkDialog" id="picons_dialog">
|
||||||
|
<property name="width_request">480</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="title" translatable="yes">Picons download tool</property>
|
||||||
|
<property name="resizable">False</property>
|
||||||
|
<property name="destroy_with_parent">True</property>
|
||||||
|
<property name="icon_name">emblem-photos</property>
|
||||||
|
<property name="type_hint">dialog</property>
|
||||||
|
<child internal-child="vbox">
|
||||||
|
<object class="GtkBox" id="picons_dialog_vbox">
|
||||||
|
<property name="width_request">320</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="margin_left">2</property>
|
||||||
|
<property name="margin_right">2</property>
|
||||||
|
<property name="orientation">vertical</property>
|
||||||
|
<property name="spacing">2</property>
|
||||||
|
<child internal-child="action_area">
|
||||||
|
<object class="GtkButtonBox" id="dialog_action_area">
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="margin_left">2</property>
|
||||||
|
<property name="margin_right">2</property>
|
||||||
|
<property name="homogeneous">True</property>
|
||||||
|
<property name="layout_style">spread</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkButton" id="cancel_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" swapped="no"/>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">True</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="position">0</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">True</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="position">0</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkBox" id="main_box">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="orientation">vertical</property>
|
||||||
|
<property name="spacing">2</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkGrid" id="grid">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="column_spacing">2</property>
|
||||||
|
<property name="column_homogeneous">True</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkEntry" id="ip_entry">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="primary_icon_name">network-transmit-receive-symbolic</property>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="left_attach">0</property>
|
||||||
|
<property name="top_attach">1</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkEntry" id="picons_entry">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="left_attach">1</property>
|
||||||
|
<property name="top_attach">1</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkLabel" id="ip_label">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="label" translatable="yes">Receiver IP:</property>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="left_attach">0</property>
|
||||||
|
<property name="top_attach">0</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkLabel" id="res_picons_label">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="label" translatable="yes">Receiver picons path:</property>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="left_attach">1</property>
|
||||||
|
<property name="top_attach">0</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="position">0</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkLabel" id="picons_dir_label">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="label" translatable="yes">Current picons path:</property>
|
||||||
|
<property name="xalign">0.019999999552965164</property>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="position">1</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkEntry" id="picons_dir_entry">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="secondary_icon_name">folder-open-symbolic</property>
|
||||||
|
<property name="primary_icon_activatable">False</property>
|
||||||
|
<signal name="icon-press" handler="on_picons_dir_open" swapped="no"/>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="position">2</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkBox" id="format_box">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="spacing">5</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkLabel" id="label1">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="margin_left">5</property>
|
||||||
|
<property name="label" translatable="yes">Picons name format:</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="enigma2_radio_button">
|
||||||
|
<property name="label" translatable="yes">Enigma2 (default)</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_mp_radio_button</property>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="position">1</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkRadioButton" id="neutrino_mp_radio_button">
|
||||||
|
<property name="label" translatable="yes">Neutrino-MP</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">enigma2_radio_button</property>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="position">2</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<style>
|
||||||
|
<class name="primary-toolbar"/>
|
||||||
|
</style>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="position">3</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkSeparator" id="separator1">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="position">4</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkLabel" id="url_label">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="label" translatable="yes">Satellite url (www.lyngsat.com):</property>
|
||||||
|
<property name="xalign">0.019999999552965164</property>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="position">5</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkEntry" id="url_entry">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="primary_icon_name">network-workgroup-symbolic</property>
|
||||||
|
<property name="primary_icon_activatable">False</property>
|
||||||
|
<property name="placeholder_text" translatable="yes">https://www.lyngsat.com/*satellite*.html</property>
|
||||||
|
<property name="input_purpose">url</property>
|
||||||
|
<signal name="changed" handler="on_url_changed" swapped="no"/>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="position">6</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkScrolledWindow" id="providers_scrolled_window">
|
||||||
|
<property name="height_request">150</property>
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="shadow_type">out</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkTreeView" id="providers_tree_view">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="has_focus">True</property>
|
||||||
|
<property name="model">providers_list_store</property>
|
||||||
|
<child internal-child="selection">
|
||||||
|
<object class="GtkTreeSelection" id="treeview_selection"/>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkTreeViewColumn" id="provider_column">
|
||||||
|
<property name="spacing">15</property>
|
||||||
|
<property name="title" translatable="yes">Providers</property>
|
||||||
|
<property name="expand">True</property>
|
||||||
|
<property name="alignment">0.5</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkCellRendererPixbuf" id="logo_cellrendererpixbuf"/>
|
||||||
|
<attributes>
|
||||||
|
<attribute name="pixbuf">0</attribute>
|
||||||
|
</attributes>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkCellRendererText" id="name_cellrenderertext"/>
|
||||||
|
<attributes>
|
||||||
|
<attribute name="text">1</attribute>
|
||||||
|
</attributes>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkTreeViewColumn" id="url_column">
|
||||||
|
<property name="visible">False</property>
|
||||||
|
<property name="title" translatable="yes">Url</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkCellRendererText" id="cellrenderertext1"/>
|
||||||
|
<attributes>
|
||||||
|
<attribute name="text">2</attribute>
|
||||||
|
</attributes>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkTreeViewColumn" id="on_id_column">
|
||||||
|
<property name="visible">False</property>
|
||||||
|
<property name="title" translatable="yes">ONID</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkCellRendererText" id="cellrenderertext2"/>
|
||||||
|
<attributes>
|
||||||
|
<attribute name="text">3</attribute>
|
||||||
|
</attributes>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkTreeViewColumn" id="selected_column">
|
||||||
|
<property name="title" translatable="yes">Selected</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkCellRendererToggle" id="cellrenderer_toggle">
|
||||||
|
<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">7</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">10</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkToolbar" id="toolbar">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="show_arrow">False</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkToolButton" id="cancel_tool_button">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</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" swapped="no"/>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">True</property>
|
||||||
|
<property name="homogeneous">True</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkSeparatorToolItem" id="separatortoolitem2">
|
||||||
|
<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="receive_tool_button">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="sensitive">False</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="tooltip_text" translatable="yes">Receive picons for providers</property>
|
||||||
|
<property name="is_important">True</property>
|
||||||
|
<property name="label" translatable="yes">Receive picons</property>
|
||||||
|
<property name="use_underline">True</property>
|
||||||
|
<property name="icon_name">go-bottom</property>
|
||||||
|
<signal name="clicked" handler="on_receive" swapped="no"/>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">True</property>
|
||||||
|
<property name="homogeneous">True</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkToolButton" id="load_providers_tool_button">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="sensitive">False</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="tooltip_text" translatable="yes">Load satellite providers.</property>
|
||||||
|
<property name="is_important">True</property>
|
||||||
|
<property name="label" translatable="yes">Load providers</property>
|
||||||
|
<property name="use_underline">True</property>
|
||||||
|
<property name="icon_name">network-server-symbolic</property>
|
||||||
|
<signal name="clicked" handler="on_load_providers" swapped="no"/>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="homogeneous">True</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkSeparatorToolItem" id="separatortoolitem1">
|
||||||
|
<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="send_tool_button">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="tooltip_text" translatable="yes">Transfer to receiver</property>
|
||||||
|
<property name="label" translatable="yes">Send</property>
|
||||||
|
<property name="use_underline">True</property>
|
||||||
|
<property name="icon_name">go-top</property>
|
||||||
|
<signal name="clicked" handler="on_send" 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">11</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkExpander" id="expander">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="resize_toplevel">True</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkScrolledWindow" id="scrolled_window">
|
||||||
|
<property name="height_request">150</property>
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="shadow_type">in</property>
|
||||||
|
<property name="min_content_width">240</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkTextView" id="text_view">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="editable">False</property>
|
||||||
|
<property name="wrap_mode">word-char</property>
|
||||||
|
<property name="overwrite">True</property>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
<child type="label">
|
||||||
|
<object class="GtkLabel" id="expander_label">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="label" translatable="yes">Info</property>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="position">12</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkInfoBar" id="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_area1">
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="spacing">6</property>
|
||||||
|
<property name="layout_style">end</property>
|
||||||
|
<child>
|
||||||
|
<placeholder/>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<placeholder/>
|
||||||
|
</child>
|
||||||
|
<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_area1">
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="spacing">16</property>
|
||||||
|
<child>
|
||||||
|
<placeholder/>
|
||||||
|
</child>
|
||||||
|
<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">1</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<placeholder/>
|
||||||
|
</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">13</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkSeparator" id="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="position">14</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">True</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="position">1</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
</interface>
|
||||||
225
app/ui/picons_dialog.py
Normal file
225
app/ui/picons_dialog.py
Normal file
@@ -0,0 +1,225 @@
|
|||||||
|
import re
|
||||||
|
import subprocess
|
||||||
|
import tempfile
|
||||||
|
import time
|
||||||
|
|
||||||
|
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
|
||||||
|
from app.properties import Profile
|
||||||
|
from . import Gtk, Gdk, UI_RESOURCES_PATH
|
||||||
|
from .dialogs import show_dialog, DialogType
|
||||||
|
from .main_helper import update_entry_data
|
||||||
|
|
||||||
|
|
||||||
|
class PiconsDialog:
|
||||||
|
def __init__(self, transient, options, profile=Profile.ENIGMA_2):
|
||||||
|
self._TMP_DIR = tempfile.gettempdir() + "/"
|
||||||
|
self._BASE_URL = "www.lyngsat.com/packages/"
|
||||||
|
self._PATTERN = re.compile("^https://www\.lyngsat\.com/[\w-]+\.html$")
|
||||||
|
self._current_process = None
|
||||||
|
self._picons_path = options.get("picons_dir_path", "")
|
||||||
|
self._terminate = False
|
||||||
|
|
||||||
|
handlers = {"on_receive": self.on_receive,
|
||||||
|
"on_load_providers": self.on_load_providers,
|
||||||
|
"on_cancel": self.on_cancel,
|
||||||
|
"on_close": self.on_close,
|
||||||
|
"on_send": self.on_send,
|
||||||
|
"on_info_bar_close": self.on_info_bar_close,
|
||||||
|
"on_picons_dir_open": self.on_picons_dir_open,
|
||||||
|
"on_selected_toggled": self.on_selected_toggled,
|
||||||
|
"on_url_changed": self.on_url_changed}
|
||||||
|
|
||||||
|
builder = Gtk.Builder()
|
||||||
|
builder.add_objects_from_file(UI_RESOURCES_PATH + "picons_dialog.glade",
|
||||||
|
("picons_dialog", "receive_image", "providers_list_store"))
|
||||||
|
builder.connect_signals(handlers)
|
||||||
|
self._dialog = builder.get_object("picons_dialog")
|
||||||
|
self._dialog.set_transient_for(transient)
|
||||||
|
self._providers_tree_view = builder.get_object("providers_tree_view")
|
||||||
|
self._expander = builder.get_object("expander")
|
||||||
|
self._text_view = builder.get_object("text_view")
|
||||||
|
self._info_bar = builder.get_object("info_bar")
|
||||||
|
self._ip_entry = builder.get_object("ip_entry")
|
||||||
|
self._picons_entry = builder.get_object("picons_entry")
|
||||||
|
self._url_entry = builder.get_object("url_entry")
|
||||||
|
self._picons_dir_entry = builder.get_object("picons_dir_entry")
|
||||||
|
self._info_bar = builder.get_object("info_bar")
|
||||||
|
self._info_bar = builder.get_object("info_bar")
|
||||||
|
self._message_label = builder.get_object("info_bar_message_label")
|
||||||
|
self._load_providers_tool_button = builder.get_object("load_providers_tool_button")
|
||||||
|
self._receive_tool_button = builder.get_object("receive_tool_button")
|
||||||
|
self._enigma2_radio_button = builder.get_object("enigma2_radio_button")
|
||||||
|
self._neutrino_mp_radio_button = builder.get_object("neutrino_mp_radio_button")
|
||||||
|
# style
|
||||||
|
self._style_provider = Gtk.CssProvider()
|
||||||
|
self._style_provider.load_from_path(UI_RESOURCES_PATH + "style.css")
|
||||||
|
self._url_entry.get_style_context().add_provider_for_screen(Gdk.Screen.get_default(), self._style_provider,
|
||||||
|
Gtk.STYLE_PROVIDER_PRIORITY_USER)
|
||||||
|
|
||||||
|
self._properties = options
|
||||||
|
self._profile = profile
|
||||||
|
self._ip_entry.set_text(options.get("host", ""))
|
||||||
|
self._picons_entry.set_text(options.get("picons_path", ""))
|
||||||
|
self._picons_path = options.get("picons_dir_path", "")
|
||||||
|
self._picons_dir_entry.set_text(self._picons_path)
|
||||||
|
|
||||||
|
def show(self):
|
||||||
|
self._dialog.run()
|
||||||
|
self._dialog.destroy()
|
||||||
|
|
||||||
|
@run_idle
|
||||||
|
def on_load_providers(self, item):
|
||||||
|
self._expander.set_expanded(True)
|
||||||
|
url = self._url_entry.get_text()
|
||||||
|
self._current_process = subprocess.Popen(["wget", "-pkP", self._TMP_DIR, url],
|
||||||
|
stdout=subprocess.PIPE,
|
||||||
|
stderr=subprocess.PIPE,
|
||||||
|
universal_newlines=True)
|
||||||
|
GLib.io_add_watch(self._current_process.stderr, GLib.IO_IN, self.write_to_buffer)
|
||||||
|
self.append_providers(url)
|
||||||
|
|
||||||
|
@run_task
|
||||||
|
def append_providers(self, url):
|
||||||
|
model = self._providers_tree_view.get_model()
|
||||||
|
model.clear()
|
||||||
|
self._current_process.wait()
|
||||||
|
providers = parse_providers(self._TMP_DIR + url[url.find("w"):])
|
||||||
|
if providers:
|
||||||
|
for p in providers:
|
||||||
|
logo = self.get_pixbuf(p[0])
|
||||||
|
model.append((logo, p.name, p.url, p.on_id, p.selected))
|
||||||
|
self.update_receive_button_state()
|
||||||
|
|
||||||
|
def get_pixbuf(self, img_url):
|
||||||
|
return GdkPixbuf.Pixbuf.new_from_file_at_scale(filename=self._TMP_DIR + "www.lyngsat.com/" + img_url,
|
||||||
|
width=48, height=48, preserve_aspect_ratio=True)
|
||||||
|
|
||||||
|
@run_idle
|
||||||
|
def on_receive(self, item):
|
||||||
|
self.start_download()
|
||||||
|
|
||||||
|
@run_task
|
||||||
|
def start_download(self):
|
||||||
|
if self._current_process.poll() is None:
|
||||||
|
self.show_dialog("The task is already running!", DialogType.ERROR)
|
||||||
|
return
|
||||||
|
|
||||||
|
self._terminate = False
|
||||||
|
self._expander.set_expanded(True)
|
||||||
|
|
||||||
|
for prv in self.get_selected_providers():
|
||||||
|
if self._terminate:
|
||||||
|
break
|
||||||
|
self.process_provider(Provider(*prv))
|
||||||
|
|
||||||
|
def process_provider(self, provider):
|
||||||
|
url = provider.url
|
||||||
|
self.show_info_message("Please, wait...", Gtk.MessageType.INFO)
|
||||||
|
self._current_process = subprocess.Popen(["wget", "-pkP", self._TMP_DIR, url],
|
||||||
|
stdout=subprocess.PIPE,
|
||||||
|
stderr=subprocess.PIPE,
|
||||||
|
universal_newlines=True)
|
||||||
|
GLib.io_add_watch(self._current_process.stderr, GLib.IO_IN, self.write_to_buffer)
|
||||||
|
self._current_process.wait()
|
||||||
|
path = self._TMP_DIR + self._BASE_URL + url[url.rfind("/") + 1:]
|
||||||
|
PiconsParser.parse(path, self._picons_path, self._TMP_DIR, provider.on_id, self.get_picons_format())
|
||||||
|
self.show_info_message("Done", Gtk.MessageType.INFO)
|
||||||
|
|
||||||
|
def write_to_buffer(self, fd, condition):
|
||||||
|
if condition == GLib.IO_IN:
|
||||||
|
char = fd.read(1)
|
||||||
|
self.append_output(char)
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
@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)
|
||||||
|
|
||||||
|
@run_task
|
||||||
|
def on_cancel(self, item):
|
||||||
|
if self._current_process:
|
||||||
|
self._terminate = True
|
||||||
|
self._current_process.terminate()
|
||||||
|
time.sleep(1)
|
||||||
|
|
||||||
|
@run_idle
|
||||||
|
def on_close(self, item):
|
||||||
|
self.on_cancel(item)
|
||||||
|
self._dialog.destroy()
|
||||||
|
|
||||||
|
def on_send(self, item):
|
||||||
|
if show_dialog(DialogType.QUESTION, self._dialog) == Gtk.ResponseType.CANCEL:
|
||||||
|
return
|
||||||
|
|
||||||
|
self.show_info_message("Please, wait...", Gtk.MessageType.INFO)
|
||||||
|
self.upload_picons()
|
||||||
|
|
||||||
|
@run_task
|
||||||
|
def upload_picons(self):
|
||||||
|
if self._current_process is not None and self._current_process.poll() is None:
|
||||||
|
self.show_dialog("The task is already running!", DialogType.ERROR)
|
||||||
|
return
|
||||||
|
|
||||||
|
upload_data(properties=self._properties,
|
||||||
|
download_type=DownloadDataType.PICONS,
|
||||||
|
profile=self._profile,
|
||||||
|
callback=lambda: self.show_info_message("Done!", Gtk.MessageType.INFO))
|
||||||
|
|
||||||
|
def on_info_bar_close(self, bar=None, resp=None):
|
||||||
|
self._info_bar.set_visible(False)
|
||||||
|
|
||||||
|
@run_idle
|
||||||
|
def show_info_message(self, text, message_type):
|
||||||
|
self._info_bar.set_visible(True)
|
||||||
|
self._info_bar.set_message_type(message_type)
|
||||||
|
self._message_label.set_text(text)
|
||||||
|
|
||||||
|
def on_picons_dir_open(self, entry, icon, event_button):
|
||||||
|
update_entry_data(entry, self._dialog, options={"data_dir_path": self._picons_path})
|
||||||
|
|
||||||
|
@run_idle
|
||||||
|
def on_selected_toggled(self, toggle, path):
|
||||||
|
model = self._providers_tree_view.get_model()
|
||||||
|
model.set_value(model.get_iter(path), 4, not toggle.get_active())
|
||||||
|
self.update_receive_button_state()
|
||||||
|
|
||||||
|
def on_url_changed(self, entry):
|
||||||
|
suit = self._PATTERN.search(entry.get_text())
|
||||||
|
entry.set_name("GtkEntry" if suit else "digit-entry")
|
||||||
|
self._load_providers_tool_button.set_sensitive(suit if suit else False)
|
||||||
|
|
||||||
|
@run_idle
|
||||||
|
def update_receive_button_state(self):
|
||||||
|
self._receive_tool_button.set_sensitive(len(self.get_selected_providers()) > 0)
|
||||||
|
|
||||||
|
def get_selected_providers(self):
|
||||||
|
""" returns selected providers """
|
||||||
|
return [r for r in self._providers_tree_view.get_model() if r[4]]
|
||||||
|
|
||||||
|
@run_idle
|
||||||
|
def show_dialog(self, message, dialog_type):
|
||||||
|
show_dialog(dialog_type, self._dialog, message)
|
||||||
|
|
||||||
|
def get_picons_format(self):
|
||||||
|
picon_format = Profile.ENIGMA_2
|
||||||
|
|
||||||
|
if self._neutrino_mp_radio_button.get_active():
|
||||||
|
picon_format = Profile.NEUTRINO_MP
|
||||||
|
|
||||||
|
return picon_format
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
pass
|
||||||
@@ -446,6 +446,44 @@
|
|||||||
<property name="homogeneous">False</property>
|
<property name="homogeneous">False</property>
|
||||||
</packing>
|
</packing>
|
||||||
</child>
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkToolButton" id="up_tool_button">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="label" translatable="yes">Up</property>
|
||||||
|
<property name="use_underline">True</property>
|
||||||
|
<property name="stock_id">gtk-go-up</property>
|
||||||
|
<signal name="clicked" handler="on_up" swapped="no"/>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="homogeneous">True</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkToolButton" id="down_tool_button">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="label" translatable="yes">Down</property>
|
||||||
|
<property name="use_underline">True</property>
|
||||||
|
<property name="stock_id">gtk-go-down</property>
|
||||||
|
<signal name="clicked" handler="on_down" swapped="no"/>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="homogeneous">True</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkSeparatorToolItem" id="separatortoolitem1">
|
||||||
|
<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>
|
<child>
|
||||||
<object class="GtkToolItem" id="toolitem1">
|
<object class="GtkToolItem" id="toolitem1">
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
|
|||||||
@@ -3,8 +3,9 @@ from math import fabs
|
|||||||
|
|
||||||
from app.commons import run_idle
|
from app.commons import run_idle
|
||||||
from app.eparser import get_satellites, write_satellites, Satellite, Transponder
|
from app.eparser import get_satellites, write_satellites, Satellite, Transponder
|
||||||
from . import Gtk, Gdk
|
from . import Gtk, Gdk, UI_RESOURCES_PATH
|
||||||
from .dialogs import show_dialog, DialogType
|
from .dialogs import show_dialog, DialogType
|
||||||
|
from .main_helper import move_items, scroll_to
|
||||||
|
|
||||||
|
|
||||||
def show_satellites_dialog(transient, options):
|
def show_satellites_dialog(transient, options):
|
||||||
@@ -19,12 +20,14 @@ class SatellitesDialog:
|
|||||||
_aggr = [None for x in range(9)] # aggregate
|
_aggr = [None for x in range(9)] # aggregate
|
||||||
|
|
||||||
def __init__(self, transient, options):
|
def __init__(self, transient, options):
|
||||||
self._data_path = options["data_dir_path"] + "satellites.xml"
|
self._data_path = options.get("data_dir_path") + "satellites.xml"
|
||||||
self._options = options
|
self._options = options
|
||||||
|
|
||||||
handlers = {"on_open": self.on_open,
|
handlers = {"on_open": self.on_open,
|
||||||
"on_remove": self.on_remove,
|
"on_remove": self.on_remove,
|
||||||
"on_save": self.on_save,
|
"on_save": self.on_save,
|
||||||
|
"on_up": self.on_up,
|
||||||
|
"on_down": self.on_down,
|
||||||
"on_popup_menu": self.on_popup_menu,
|
"on_popup_menu": self.on_popup_menu,
|
||||||
"on_satellite_add": self.on_satellite_add,
|
"on_satellite_add": self.on_satellite_add,
|
||||||
"on_transponder_add": self.on_transponder_add,
|
"on_transponder_add": self.on_transponder_add,
|
||||||
@@ -35,7 +38,7 @@ class SatellitesDialog:
|
|||||||
"on_quit": self.on_quit}
|
"on_quit": self.on_quit}
|
||||||
|
|
||||||
builder = Gtk.Builder()
|
builder = Gtk.Builder()
|
||||||
builder.add_objects_from_file("app/ui/satellites_dialog.glade",
|
builder.add_objects_from_file(UI_RESOURCES_PATH + "satellites_dialog.glade",
|
||||||
("satellites_editor_dialog", "satellites_tree_store",
|
("satellites_editor_dialog", "satellites_tree_store",
|
||||||
"popup_menu", "add_popup_menu", "add_menu_icon"))
|
"popup_menu", "add_popup_menu", "add_menu_icon"))
|
||||||
builder.connect_signals(handlers)
|
builder.connect_signals(handlers)
|
||||||
@@ -97,6 +100,12 @@ class SatellitesDialog:
|
|||||||
else:
|
else:
|
||||||
view.expand_row(path, column)
|
view.expand_row(path, column)
|
||||||
|
|
||||||
|
def on_up(self, item):
|
||||||
|
move_items(Gdk.KEY_Up, self._sat_view)
|
||||||
|
|
||||||
|
def on_down(self, item):
|
||||||
|
move_items(Gdk.KEY_Down, self._sat_view)
|
||||||
|
|
||||||
def on_key_release(self, view, event):
|
def on_key_release(self, view, event):
|
||||||
""" Handling keystrokes """
|
""" Handling keystrokes """
|
||||||
key = event.keyval
|
key = event.keyval
|
||||||
@@ -106,7 +115,6 @@ class SatellitesDialog:
|
|||||||
self.on_remove(view)
|
self.on_remove(view)
|
||||||
elif key == Gdk.KEY_Insert:
|
elif key == Gdk.KEY_Insert:
|
||||||
pass
|
pass
|
||||||
# self.on_add(view)
|
|
||||||
elif ctrl and key == Gdk.KEY_E or key == Gdk.KEY_e:
|
elif ctrl and key == Gdk.KEY_E or key == Gdk.KEY_e:
|
||||||
self.on_edit(view)
|
self.on_edit(view)
|
||||||
elif ctrl and key == Gdk.KEY_s or key == Gdk.KEY_S:
|
elif ctrl and key == Gdk.KEY_s or key == Gdk.KEY_S:
|
||||||
@@ -115,6 +123,10 @@ class SatellitesDialog:
|
|||||||
self.on_transponder()
|
self.on_transponder()
|
||||||
elif key == Gdk.KEY_space:
|
elif key == Gdk.KEY_space:
|
||||||
pass
|
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):
|
||||||
|
move_items(key, self._sat_view)
|
||||||
|
|
||||||
@run_idle
|
@run_idle
|
||||||
def on_satellites_list_load(self, model):
|
def on_satellites_list_load(self, model):
|
||||||
@@ -174,7 +186,7 @@ class SatellitesDialog:
|
|||||||
else:
|
else:
|
||||||
index = self.get_sat_position_index(sat.position, model)
|
index = self.get_sat_position_index(sat.position, model)
|
||||||
model.insert(None, index, [sat.name, *self._aggr, sat.flags, sat.position])
|
model.insert(None, index, [sat.name, *self._aggr, sat.flags, sat.position])
|
||||||
self.scroll_to(index, view)
|
scroll_to(index, view)
|
||||||
|
|
||||||
def on_transponder(self, transponder=None, edited_itr=None):
|
def on_transponder(self, transponder=None, edited_itr=None):
|
||||||
""" Create or edit transponder """
|
""" Create or edit transponder """
|
||||||
@@ -215,20 +227,13 @@ class SatellitesDialog:
|
|||||||
path = model.get_path(tr_itr)
|
path = model.get_path(tr_itr)
|
||||||
index = path.get_indices()[1]
|
index = path.get_indices()[1]
|
||||||
model.insert(model.iter_parent(tr_itr), index, row)
|
model.insert(model.iter_parent(tr_itr), index, row)
|
||||||
self.scroll_to(path, view)
|
scroll_to(path, view)
|
||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
tr_itr = model.iter_next(tr_itr)
|
tr_itr = model.iter_next(tr_itr)
|
||||||
else:
|
else:
|
||||||
itr = model.append(itr, row)
|
itr = model.append(itr, row)
|
||||||
self.scroll_to(model.get_path(itr), view)
|
scroll_to(model.get_path(itr), view)
|
||||||
|
|
||||||
def scroll_to(self, index, view):
|
|
||||||
""" Scrolling to and selecting given index(path) """
|
|
||||||
view.scroll_to_cell(index, None)
|
|
||||||
selection = view.get_selection()
|
|
||||||
selection.unselect_all()
|
|
||||||
selection.select_path(index)
|
|
||||||
|
|
||||||
def get_sat_position_index(self, pos, model):
|
def get_sat_position_index(self, pos, model):
|
||||||
""" Search and returns index after given position """
|
""" Search and returns index after given position """
|
||||||
@@ -299,7 +304,7 @@ class TransponderDialog:
|
|||||||
handlers = {"on_entry_changed": self.on_entry_changed}
|
handlers = {"on_entry_changed": self.on_entry_changed}
|
||||||
|
|
||||||
builder = Gtk.Builder()
|
builder = Gtk.Builder()
|
||||||
builder.add_objects_from_file("app/ui/satellites_dialog.glade",
|
builder.add_objects_from_file(UI_RESOURCES_PATH + "satellites_dialog.glade",
|
||||||
("transponder_dialog",
|
("transponder_dialog",
|
||||||
"pol_store", "fec_store",
|
"pol_store", "fec_store",
|
||||||
"mod_store", "system_store",
|
"mod_store", "system_store",
|
||||||
@@ -321,7 +326,7 @@ class TransponderDialog:
|
|||||||
self._pattern = re.compile("\D")
|
self._pattern = re.compile("\D")
|
||||||
# style
|
# style
|
||||||
self._style_provider = Gtk.CssProvider()
|
self._style_provider = Gtk.CssProvider()
|
||||||
self._style_provider.load_from_path("app/ui/style.css")
|
self._style_provider.load_from_path(UI_RESOURCES_PATH + "style.css")
|
||||||
self._freq_entry.get_style_context().add_provider_for_screen(Gdk.Screen.get_default(), self._style_provider,
|
self._freq_entry.get_style_context().add_provider_for_screen(Gdk.Screen.get_default(), self._style_provider,
|
||||||
Gtk.STYLE_PROVIDER_PRIORITY_USER)
|
Gtk.STYLE_PROVIDER_PRIORITY_USER)
|
||||||
self._rate_entry.get_style_context().add_provider_for_screen(Gdk.Screen.get_default(), self._style_provider,
|
self._rate_entry.get_style_context().add_provider_for_screen(Gdk.Screen.get_default(), self._style_provider,
|
||||||
@@ -382,7 +387,7 @@ class SatelliteDialog:
|
|||||||
|
|
||||||
def __init__(self, transient, satellite: Satellite = None):
|
def __init__(self, transient, satellite: Satellite = None):
|
||||||
builder = Gtk.Builder()
|
builder = Gtk.Builder()
|
||||||
builder.add_objects_from_file("app/ui/satellites_dialog.glade",
|
builder.add_objects_from_file(UI_RESOURCES_PATH + "satellites_dialog.glade",
|
||||||
("satellite_dialog", "side_store", "pos_adjustment"))
|
("satellite_dialog", "side_store", "pos_adjustment"))
|
||||||
|
|
||||||
self._dialog = builder.get_object("satellite_dialog")
|
self._dialog = builder.get_object("satellite_dialog")
|
||||||
|
|||||||
@@ -1,54 +1,118 @@
|
|||||||
from app.properties import write_config
|
from app.properties import write_config, Profile, get_default_settings
|
||||||
from app.ui.dialogs import show_dialog, DialogType
|
from . import Gtk, UI_RESOURCES_PATH
|
||||||
from . import Gtk
|
from .main_helper import update_entry_data
|
||||||
|
|
||||||
|
|
||||||
def show_settings_dialog(transient, options):
|
def show_settings_dialog(transient, options):
|
||||||
SettingsDialog(transient, options)
|
return SettingsDialog(transient, options).show()
|
||||||
|
|
||||||
|
|
||||||
class SettingsDialog:
|
class SettingsDialog:
|
||||||
def __init__(self, transient, options):
|
def __init__(self, transient, options):
|
||||||
handlers = {"on_data_dir_field_icon_press": self.on_data_dir_field_icon_press}
|
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,
|
||||||
|
"on_profile_changed": self.on_profile_changed,
|
||||||
|
"on_reset": self.on_reset,
|
||||||
|
"apply_settings": self.apply_settings}
|
||||||
|
|
||||||
builder = Gtk.Builder()
|
builder = Gtk.Builder()
|
||||||
builder.add_objects_from_file("app/ui/dialogs.glade", ("settings_dialog", ))
|
builder.add_objects_from_file(UI_RESOURCES_PATH + "dialogs.glade",
|
||||||
|
("settings_dialog", "telnet_timeout_adjustment"))
|
||||||
builder.connect_signals(handlers)
|
builder.connect_signals(handlers)
|
||||||
self._options = options
|
|
||||||
self._dialog = builder.get_object("settings_dialog")
|
self._dialog = builder.get_object("settings_dialog")
|
||||||
self._dialog.set_transient_for(transient)
|
self._dialog.set_transient_for(transient)
|
||||||
self._host_field = builder.get_object("host_field")
|
self._host_field = builder.get_object("host_field")
|
||||||
self._host_field.set_text(options["host"])
|
|
||||||
self._port_field = builder.get_object("port_field")
|
self._port_field = builder.get_object("port_field")
|
||||||
self._port_field.set_text(options["port"])
|
|
||||||
self._login_field = builder.get_object("login_field")
|
self._login_field = builder.get_object("login_field")
|
||||||
self._login_field.set_text(options["user"])
|
|
||||||
self._password_field = builder.get_object("password_field")
|
self._password_field = builder.get_object("password_field")
|
||||||
self._password_field.set_text(options["password"])
|
self._telnet_login_field = builder.get_object("telnet_login_field")
|
||||||
|
self._telnet_password_field = builder.get_object("telnet_password_field")
|
||||||
|
self._telnet_port_field = builder.get_object("telnet_port_field")
|
||||||
|
self._telnet_timeout_spin_button = builder.get_object("telnet_timeout_spin_button")
|
||||||
self._services_field = builder.get_object("services_field")
|
self._services_field = builder.get_object("services_field")
|
||||||
self._services_field.set_text(options["services_path"])
|
|
||||||
self._user_bouquet_field = builder.get_object("user_bouquet_field")
|
self._user_bouquet_field = builder.get_object("user_bouquet_field")
|
||||||
self._user_bouquet_field.set_text(options["user_bouquet_path"])
|
|
||||||
self._satellites_xml_field = builder.get_object("satellites_xml_field")
|
self._satellites_xml_field = builder.get_object("satellites_xml_field")
|
||||||
self._satellites_xml_field.set_text(options["satellites_xml_path"])
|
|
||||||
self._data_dir_field = builder.get_object("data_dir_field")
|
self._data_dir_field = builder.get_object("data_dir_field")
|
||||||
self._data_dir_field.set_text(options["data_dir_path"])
|
self._picons_field = builder.get_object("picons_field")
|
||||||
|
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")
|
||||||
|
|
||||||
if self._dialog.run() == Gtk.ResponseType.OK:
|
self._options = options
|
||||||
options["host"] = self._host_field.get_text()
|
self._active_profile = options.get("profile")
|
||||||
options["port"] = self._port_field.get_text()
|
self.set_settings()
|
||||||
options["user"] = self._login_field.get_text()
|
self._neutrino_radio_button.set_active(Profile(self._active_profile) is Profile.NEUTRINO_MP)
|
||||||
options["password"] = self._password_field.get_text()
|
|
||||||
options["services_path"] = self._services_field.get_text()
|
def show(self):
|
||||||
options["user_bouquet_path"] = self._user_bouquet_field.get_text()
|
response = self._dialog.run()
|
||||||
options["satellites_xml_path"] = self._satellites_xml_field.get_text()
|
if response == Gtk.ResponseType.OK:
|
||||||
options["data_dir_path"] = self._data_dir_field.get_text()
|
self.apply_settings()
|
||||||
write_config(options)
|
write_config(self._options)
|
||||||
self._dialog.destroy()
|
self._dialog.destroy()
|
||||||
|
|
||||||
|
return response
|
||||||
|
|
||||||
def on_data_dir_field_icon_press(self, entry, icon, event_button):
|
def on_data_dir_field_icon_press(self, entry, icon, event_button):
|
||||||
response = show_dialog(dialog_type=DialogType.CHOOSER, transient=self._dialog, options=self._options)
|
update_entry_data(entry, self._dialog, self._options.get(self._options.get("profile")))
|
||||||
if response != Gtk.ResponseType.CANCEL:
|
|
||||||
entry.set_text(response)
|
def on_picons_dir_field_icon_press(self, entry, icon, event_button):
|
||||||
|
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)
|
||||||
|
|
||||||
|
def set_profile(self, profile):
|
||||||
|
self._active_profile = profile.value
|
||||||
|
self.set_settings()
|
||||||
|
|
||||||
|
def on_reset(self, item):
|
||||||
|
def_settings = get_default_settings()
|
||||||
|
for key in def_settings:
|
||||||
|
current = self._options.get(key)
|
||||||
|
if type(current) is str:
|
||||||
|
continue
|
||||||
|
default = def_settings.get(key)
|
||||||
|
for k in default:
|
||||||
|
current[k] = default.get(k)
|
||||||
|
self.set_settings()
|
||||||
|
|
||||||
|
def set_settings(self):
|
||||||
|
options = self._options.get(self._active_profile)
|
||||||
|
self._host_field.set_text(options.get("host", ""))
|
||||||
|
self._port_field.set_text(options.get("port", ""))
|
||||||
|
self._login_field.set_text(options.get("user", ""))
|
||||||
|
self._password_field.set_text(options.get("password", ""))
|
||||||
|
self._telnet_login_field.set_text(options.get("telnet_user", ""))
|
||||||
|
self._telnet_password_field.set_text(options.get("telnet_password", ""))
|
||||||
|
self._telnet_port_field.set_text(options.get("telnet_port", ""))
|
||||||
|
self._telnet_timeout_spin_button.set_value(options.get("telnet_timeout", 5))
|
||||||
|
self._services_field.set_text(options.get("services_path", ""))
|
||||||
|
self._user_bouquet_field.set_text(options.get("user_bouquet_path", ""))
|
||||||
|
self._satellites_xml_field.set_text(options.get("satellites_xml_path", ""))
|
||||||
|
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", ""))
|
||||||
|
|
||||||
|
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
|
||||||
|
options = self._options.get(self._active_profile)
|
||||||
|
options["host"] = self._host_field.get_text()
|
||||||
|
options["port"] = self._port_field.get_text()
|
||||||
|
options["user"] = self._login_field.get_text()
|
||||||
|
options["password"] = self._password_field.get_text()
|
||||||
|
options["telnet_user"] = self._telnet_login_field.get_text()
|
||||||
|
options["telnet_password"] = self._telnet_password_field.get_text()
|
||||||
|
options["telnet_port"] = self._telnet_port_field.get_text()
|
||||||
|
options["telnet_timeout"] = int(self._telnet_timeout_spin_button.get_value())
|
||||||
|
options["services_path"] = self._services_field.get_text()
|
||||||
|
options["user_bouquet_path"] = self._user_bouquet_field.get_text()
|
||||||
|
options["satellites_xml_path"] = self._satellites_xml_field.get_text()
|
||||||
|
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 __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|||||||
19
build-deb.sh
Executable file
19
build-deb.sh
Executable file
@@ -0,0 +1,19 @@
|
|||||||
|
#!/bin/env bash
|
||||||
|
VER="0.2.1_Pre-alpha"
|
||||||
|
B_PATH="dist/DemonEditor"
|
||||||
|
DEB_PATH="$B_PATH/usr/share/demoneditor"
|
||||||
|
|
||||||
|
mkdir -p $B_PATH
|
||||||
|
cp -TRv deb $B_PATH
|
||||||
|
cp -Rv app $DEB_PATH
|
||||||
|
cp -Rv start.py $DEB_PATH
|
||||||
|
|
||||||
|
cd dist
|
||||||
|
fakeroot dpkg-deb --build DemonEditor
|
||||||
|
mv DemonEditor.deb DemonEditor_$VER.deb
|
||||||
|
|
||||||
|
rm -R DemonEditor
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
Package: DemonEditor
|
Package: DemonEditor
|
||||||
Version: 0.1.1-Pre-alpha
|
Version: 0.2.1-Pre-alpha
|
||||||
Section: utils
|
Section: utils
|
||||||
Priority: optional
|
Priority: optional
|
||||||
Architecture: all
|
Architecture: all
|
||||||
Essential: no
|
Essential: no
|
||||||
Depends: python3 (>= 3.5)
|
Depends: python3 (>= 3.5)
|
||||||
Maintainer: Dmitriy Yefremov <dmitry.v.yefremov@gmail.com>
|
Maintainer: Dmitriy Yefremov <dmitry.v.yefremov@gmail.com>
|
||||||
Description: Enigma2 channels and satellites editor
|
Description: Enigma2 channels and satellites list editor
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ Source: https://github.com/DYefremov/DemonEditor
|
|||||||
Files: *
|
Files: *
|
||||||
MIT License
|
MIT License
|
||||||
|
|
||||||
Copyright (c) 2017 Dmitriy Yefremov
|
Copyright (c) 2018 Dmitriy Yefremov
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
[Desktop Entry]
|
[Desktop Entry]
|
||||||
Version=1.0
|
Version=1.0
|
||||||
Name=DemonEditor
|
Name=DemonEditor
|
||||||
Comment=Channels and satellites editor for Enigma2
|
Comment=Channels and satellites list editor for Enigma2
|
||||||
Comment[ru]=Редактор каналов и спутников для Enigma2
|
Comment[ru]=Редактор списка каналов и спутников для Enigma2
|
||||||
Icon=accessories-text-editor
|
Icon=accessories-text-editor
|
||||||
Exec=/usr/bin/demoneditor.sh
|
Exec=/usr/bin/demoneditor.sh
|
||||||
Terminal=false
|
Terminal=false
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
MIT License
|
MIT License
|
||||||
|
|
||||||
Copyright (c) 2017 Dmitriy Yefremov
|
Copyright (c) 2018 Dmitriy Yefremov
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|||||||
@@ -1,23 +1,25 @@
|
|||||||
# DemonEditor
|
# DemonEditor
|
||||||
Enigma2 channel and satellites list editor for GNU/Linux.
|
|
||||||
Focused on the convenience of working in lists from the keyboard.
|
|
||||||
The mouse is also fully supported (Drag and Drop etc)
|
|
||||||
|
|
||||||
Keyboard shortcuts:
|
Enigma2 channel and satellites list editor for GNU/Linux.
|
||||||
Ctrl + X, C, V, Up, Down, PageUp, PageDown, S, T, E, L, H, Space; Insert, Delete, F2.
|
Experimental support of Neutrino-MP or others on the same basis (BPanther, etc).
|
||||||
Insert - copies the selected channels from the main list to the bouquet or inserts (creates) a new bouquet.
|
Focused on the convenience of working in lists from the keyboard. The mouse is also fully supported (Drag and Drop etc)
|
||||||
Ctrl + X - only in bouquet list.
|
|
||||||
Ctrl + C - only in services list. Clipboard is "rubber". There is an accumulation before the insertion!
|
|
||||||
F2 - rename the bouquet.
|
|
||||||
Ctrl + S, T, E in Satellites edit tool for create and edit satellite or transponder.
|
|
||||||
Ctrl + L - parental lock.
|
|
||||||
Ctrl + H - hide/skip.
|
|
||||||
|
|
||||||
Ability to import IPTV into bouquet from m3u files!
|
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, F2 - edit/rename.
|
||||||
|
Ctrl + S, T, E in Satellites edit tool for create and edit satellite or transponder.
|
||||||
|
Ctrl + L - parental lock.
|
||||||
|
Ctrl + H - hide/skip.
|
||||||
|
|
||||||
Tests only on OpenPLi based image with GM 990 Spark Reloaded receiver
|
Ability to import IPTV into bouquet from m3u files!
|
||||||
|
|
||||||
|
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)!
|
in my preferred linux distro (Last Linux Mint 18.* - MATE 64-bit)!
|
||||||
|
|
||||||
Minimum requirements: Python >= 3.5.2 and GTK+ 3 with PyGObject bindings.
|
Minimum requirements: Python >= 3.5.2 and GTK+ 3 with PyGObject bindings.
|
||||||
|
|
||||||
Terrestrial and cable channels at the moment are not supported!
|
Terrestrial and cable channels at the moment are not supported!
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user