mirror of
https://github.com/DYefremov/DemonEditor.git
synced 2026-05-08 10:37:35 +02:00
Compare commits
55 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d9390aa7be | ||
|
|
e12cc86e5f | ||
|
|
f1ef9fe4aa | ||
|
|
728bfd0b20 | ||
|
|
1d6022b6db | ||
|
|
8609d30ac9 | ||
|
|
fde06dca89 | ||
|
|
e41bf5f58f | ||
|
|
b1488df9ce | ||
|
|
c6e4b3624b | ||
|
|
26b843921b | ||
|
|
e73638d006 | ||
|
|
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]
|
||||
Version=1.0
|
||||
Name=DemonEditor
|
||||
Comment=Channels and satellites editor for Enigma2
|
||||
Comment[ru]=Редактор каналов и спутников для Enigma2
|
||||
Comment=Channels and satellites list editor for Enigma2
|
||||
Comment[ru]=Редактор списка каналов и спутников для Enigma2
|
||||
Icon=accessories-text-editor
|
||||
Exec=bash -c 'cd $(dirname %k) && ./start.py'
|
||||
Terminal=false
|
||||
|
||||
2
LICENSE
2
LICENSE
@@ -1,6 +1,6 @@
|
||||
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
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
39
README.md
39
README.md
@@ -1,23 +1,32 @@
|
||||
# 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:
|
||||
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!
|
||||
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.
|
||||
Enigma2 channel and satellites list editor for GNU/Linux.
|
||||
Experimental support of Neutrino-MP or others on the same basis (BPanther, etc).
|
||||
Focused on the convenience of working in lists from the keyboard. The mouse is also fully supported (Drag and Drop etc)
|
||||
|
||||
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.
|
||||
Left/Right - remove selection.
|
||||
|
||||
Tests only on OpenPLi based image with GM 990 Spark Reloaded receiver
|
||||
Multiple selections in lists only with Space key (as in file managers)!
|
||||
|
||||
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)!
|
||||
|
||||
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!
|
||||
|
||||
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 .bouquets import get_bouquets, write_bouquets, to_bouquet_id, Bouquet, Bouquets
|
||||
from .satxml import get_satellites, write_satellites, Satellite, Transponder
|
||||
from .blacklist import get_blacklist, write_blacklist
|
||||
from app.commons import run_task
|
||||
from app.properties import Profile
|
||||
from .ecommons import Service, Satellite, Transponder, Bouquet, Bouquets
|
||||
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 .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__":
|
||||
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,15 @@
|
||||
""" Module for parsing bouquets """
|
||||
from collections import namedtuple
|
||||
from enum import Enum
|
||||
from app.eparser.ecommons import BqServiceType, BouquetService, Bouquets, Bouquet
|
||||
|
||||
_BOUQUETS_PATH = "../data/"
|
||||
_TV_ROOT_FILE_NAME = "bouquets.tv"
|
||||
_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):
|
||||
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'
|
||||
line = []
|
||||
|
||||
@@ -98,7 +85,11 @@ def parse_bouquets(path, bq_name, bq_type):
|
||||
bouquets = Bouquets(name.strip(), bq_type, [])
|
||||
if bouquets and "#SERVICE" in line:
|
||||
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
|
||||
|
||||
@@ -3,42 +3,35 @@
|
||||
Currently implemented only for satellite channels!!!
|
||||
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.eparser.__constants import POLARIZATION, SYSTEM, FEC, SERVICE_TYPE
|
||||
from app.ui import CODED_ICON, LOCKED_ICON, HIDE_ICON
|
||||
from .blacklist import get_blacklist
|
||||
from ..ecommons import Service, POLARIZATION, SYSTEM, FEC, SERVICE_TYPE, FLAG
|
||||
|
||||
_HEADER = "eDVB services /4/"
|
||||
_FILE_PATH = "../data/lamedb"
|
||||
_SEP = ":" # separator
|
||||
_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_channels(path):
|
||||
def get_services(path):
|
||||
return parse(path)
|
||||
|
||||
|
||||
def write_channels(path, channels):
|
||||
def write_services(path, services):
|
||||
lines = [_HEADER, "\ntransponders\n"]
|
||||
tr_lines = []
|
||||
services_lines = ["end\nservices\n"]
|
||||
tr_set = set()
|
||||
|
||||
for ch in channels:
|
||||
data_id = str(ch.data_id).split(_SEP)
|
||||
for srv in services:
|
||||
data_id = str(srv.data_id).split(_SEP)
|
||||
tr_id = "{}:{}:{}".format(data_id[1], data_id[2], data_id[3])
|
||||
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_set.add(tr_id)
|
||||
# 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()
|
||||
lines.extend(tr_lines)
|
||||
@@ -65,7 +58,7 @@ def parse(path):
|
||||
transponders, sep, services = services.partition("services") # 2 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):
|
||||
@@ -79,7 +72,7 @@ def parse_transponders(arg):
|
||||
return transponders
|
||||
|
||||
|
||||
def parse_channels(services, transponders, path):
|
||||
def parse_services(services, transponders, path):
|
||||
""" Parsing channels """
|
||||
channels = []
|
||||
transponders = parse_transponders(transponders)
|
||||
@@ -98,7 +91,7 @@ def parse_channels(services, transponders, path):
|
||||
all_flags = ch[2].split(",")
|
||||
coded = CODED_ICON if list(filter(lambda x: x.startswith("C:"), all_flags)) else None
|
||||
flags = list(filter(lambda x: x.startswith("f:"), all_flags))
|
||||
hide = HIDE_ICON if flags and 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
|
||||
|
||||
package = list(filter(lambda x: x.startswith("p:"), all_flags))
|
||||
@@ -111,7 +104,7 @@ def parse_channels(services, transponders, path):
|
||||
tr_type, sp, tr = str(transponder).partition(" ")
|
||||
tr = tr.split(_SEP)
|
||||
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,
|
||||
coded=coded,
|
||||
service=ch[1],
|
||||
@@ -1,5 +1,4 @@
|
||||
from app.eparser.bouquets import BqServiceType
|
||||
from . import Channel
|
||||
from .ecommons import BqServiceType, Service
|
||||
|
||||
|
||||
def parse_m3u(path):
|
||||
@@ -16,7 +15,7 @@ def parse_m3u(path):
|
||||
count = 0
|
||||
fav_id = " 1:0:1:0:0:0:0:0:0:0:{}:{}\n#DESCRIPTION: {}\n".format(
|
||||
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
|
||||
|
||||
|
||||
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
|
||||
"""
|
||||
from collections import namedtuple
|
||||
from xml.dom.minidom import parse, Document
|
||||
|
||||
from app.eparser.__constants import POLARIZATION, FEC, SYSTEM, MODULATION, PLS_MODE
|
||||
|
||||
Satellite = namedtuple("Satellite", ["name", "flags", "position", "transponders"])
|
||||
|
||||
Transponder = namedtuple("Transponder", ["frequency", "symbol_rate", "polarization", "fec_inner",
|
||||
"system", "modulation", "pls_mode", "pls_code", "is_id"])
|
||||
from .ecommons import POLARIZATION, FEC, SYSTEM, MODULATION, PLS_MODE, Transponder, Satellite
|
||||
|
||||
__COMMENT = (" File was created in DemonEditor\n\n"
|
||||
"useable flags are\n"
|
||||
"usable flags are\n"
|
||||
" 1: Network Scan\n"
|
||||
" 2: use BAT\n"
|
||||
" 4: use ONIT\n"
|
||||
|
||||
69
app/ftp.py
69
app/ftp.py
@@ -2,22 +2,28 @@ import os
|
||||
import socket
|
||||
import time
|
||||
from enum import Enum
|
||||
from ftplib import FTP
|
||||
from ftplib import FTP, error_perm
|
||||
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):
|
||||
ALL = 0
|
||||
BOUQUETS = 1
|
||||
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:
|
||||
ftp.login(user=properties["user"], passwd=properties["password"])
|
||||
save_path = properties["data_dir_path"]
|
||||
os.makedirs(os.path.dirname(save_path), exist_ok=True)
|
||||
files = []
|
||||
# bouquets section
|
||||
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:
|
||||
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"]
|
||||
host = properties["host"]
|
||||
# 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)
|
||||
# terminate enigma
|
||||
# terminate enigma or enigma
|
||||
tn.send("init 4")
|
||||
|
||||
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):
|
||||
if file_name == "satellites.xml":
|
||||
continue
|
||||
file_name, send_file(file_name, data_path, ftp)
|
||||
# resume enigma
|
||||
tn.send("init 3")
|
||||
if file_name.endswith(__DATA_FILES_LIST):
|
||||
send_file(file_name, data_path, ftp)
|
||||
|
||||
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):
|
||||
@@ -88,14 +127,22 @@ def send_file(file_name, path, ftp):
|
||||
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:
|
||||
tn = Telnet(host=host, port=port, timeout=timeout)
|
||||
except socket.timeout:
|
||||
print("socket timeout")
|
||||
log("telnet error: socket timeout")
|
||||
else:
|
||||
time.sleep(1)
|
||||
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"))
|
||||
time.sleep(timeout)
|
||||
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 os
|
||||
from enum import Enum
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
CONFIG_PATH = str(Path.home()) + "/.config/demon-editor/"
|
||||
CONFIG_FILE = CONFIG_PATH + "config.json"
|
||||
DATA_PATH = "data/"
|
||||
|
||||
|
||||
class Profile(Enum):
|
||||
""" Profiles for settings """
|
||||
ENIGMA_2 = "0"
|
||||
NEUTRINO_MP = "1"
|
||||
|
||||
|
||||
def get_config():
|
||||
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)
|
||||
|
||||
if not os.path.isfile(CONFIG_FILE) or os.stat(CONFIG_FILE).st_size == 0:
|
||||
with open(CONFIG_FILE, "w") as default_config_file:
|
||||
json.dump(get_default_settings(), default_config_file)
|
||||
reset_config()
|
||||
|
||||
with open(CONFIG_FILE, "r") as 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):
|
||||
assert isinstance(config, dict)
|
||||
with open(CONFIG_FILE, "w") as config_file:
|
||||
@@ -27,12 +37,30 @@ def write_config(config):
|
||||
|
||||
|
||||
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",
|
||||
"telnet_user": "", "telnet_password": "",
|
||||
"telnet_port": "21", "telnet_timeout": 5,
|
||||
"services_path": "/etc/enigma2/",
|
||||
"user_bouquet_path": "/etc/enigma2/",
|
||||
"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__":
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
import gi
|
||||
import os
|
||||
|
||||
gi.require_version('Gtk', '3.0')
|
||||
from gi.repository import Gtk, Gdk
|
||||
|
||||
# path to *.glade files
|
||||
UI_RESOURCES_PATH = "app/ui/" if os.path.exists("app/ui/") else "/usr/share/demoneditor/app/ui/"
|
||||
|
||||
theme = Gtk.IconTheme.get_default()
|
||||
_IMAGE_MISSING = theme.load_icon("image-missing", 16, 0) if theme.lookup_icon("image-missing", 16, 0) else None
|
||||
CODED_ICON = theme.load_icon("emblem-readonly", 16, 0) if theme.lookup_icon(
|
||||
|
||||
@@ -9,14 +9,14 @@
|
||||
<property name="icon_name">system-help</property>
|
||||
<property name="type_hint">normal</property>
|
||||
<property name="program_name">DemonEditor</property>
|
||||
<property name="version">0.1.1 Pre-alpha</property>
|
||||
<property name="copyright" translatable="yes">2017 Dmitriy Yefremov
|
||||
<property name="version">0.2.2 Pre-alpha</property>
|
||||
<property name="copyright" translatable="yes">2018 Dmitriy Yefremov
|
||||
dmitry.v.yefremov@gmail.com
|
||||
</property>
|
||||
<property name="comments" translatable="yes">Enigma2 channel and satellites list editor for GNU/Linux</property>
|
||||
<property name="license" translatable="yes">Это приложение распространяется без каких-либо гарантий.
|
||||
Подробнее в <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 name="logo_icon_name">accessories-text-editor</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="text" translatable="yes">127.0.0.1</property>
|
||||
<property name="caps_lock_warning">False</property>
|
||||
<property name="primary_icon_name">network-transmit-receive-symbolic</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
@@ -196,7 +197,6 @@ dmitry.v.yefremov@gmail.com
|
||||
<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">satellites_radio_button</property>
|
||||
</object>
|
||||
@@ -213,7 +213,6 @@ dmitry.v.yefremov@gmail.com
|
||||
<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">all_radio_button</property>
|
||||
</object>
|
||||
@@ -223,6 +222,9 @@ dmitry.v.yefremov@gmail.com
|
||||
<property name="position">3</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
@@ -603,6 +605,13 @@ dmitry.v.yefremov@gmail.com
|
||||
</object>
|
||||
</child>
|
||||
</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>
|
||||
</object>
|
||||
<object class="GtkDialog" id="settings_dialog">
|
||||
<property name="can_focus">False</property>
|
||||
<property name="title" translatable="yes">Options</property>
|
||||
@@ -613,7 +622,10 @@ dmitry.v.yefremov@gmail.com
|
||||
<child internal-child="vbox">
|
||||
<object class="GtkBox" id="dialog-vbox3">
|
||||
<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_bottom">2</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<property name="spacing">2</property>
|
||||
<child internal-child="action_area">
|
||||
@@ -623,11 +635,29 @@ dmitry.v.yefremov@gmail.com
|
||||
<property name="layout_style">end</property>
|
||||
<child>
|
||||
<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="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="apply_settings" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
@@ -642,6 +672,7 @@ dmitry.v.yefremov@gmail.com
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="use_stock">True</property>
|
||||
<property name="always_show_image">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
@@ -653,109 +684,249 @@ dmitry.v.yefremov@gmail.com
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
<property name="position">0</property>
|
||||
<property name="position">6</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkGrid" id="grid1">
|
||||
<object class="GtkNotebook" id="notebook">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="column_spacing">1</property>
|
||||
<property name="column_homogeneous">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<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">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">Host:</property>
|
||||
<property name="label" translatable="yes">FTP</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">0</property>
|
||||
<property name="tab_fill">False</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>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label3">
|
||||
<object class="GtkGrid" id="telnet_settings_grid">
|
||||
<property name="visible">True</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>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">2</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<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>
|
||||
<child type="tab">
|
||||
<object class="GtkLabel" id="label2">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">Port:</property>
|
||||
<property name="label" translatable="yes">Telnet</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">0</property>
|
||||
<property name="position">1</property>
|
||||
<property name="tab_fill">False</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">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>
|
||||
<placeholder/>
|
||||
</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-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 type="tab">
|
||||
<placeholder/>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
@@ -778,74 +949,203 @@ dmitry.v.yefremov@gmail.com
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkGrid" id="grid2">
|
||||
<object class="GtkBox" id="box2">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="column_homogeneous">True</property>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label5">
|
||||
<object class="GtkGrid" id="grid2">
|
||||
<property name="visible">True</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>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">0</property>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">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">
|
||||
<object class="GtkSeparator" id="separator5">
|
||||
<property name="visible">True</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>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">2</property>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</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">
|
||||
<object class="GtkBox" id="box3">
|
||||
<property name="visible">True</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>
|
||||
<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>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
@@ -867,29 +1167,31 @@ dmitry.v.yefremov@gmail.com
|
||||
<property name="position">3</property>
|
||||
</packing>
|
||||
</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>
|
||||
<object class="GtkGrid" id="grid3">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</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>
|
||||
<object class="GtkEntry" id="data_dir_field">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</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="secondary_icon_tooltip_text" translatable="yes">Select</property>
|
||||
<property name="secondary_icon_tooltip_markup" translatable="yes">Select</property>
|
||||
@@ -897,7 +1199,7 @@ dmitry.v.yefremov@gmail.com
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">1</property>
|
||||
<property name="top_attach">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
@@ -919,6 +1221,33 @@ dmitry.v.yefremov@gmail.com
|
||||
<property name="position">6</property>
|
||||
</packing>
|
||||
</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>
|
||||
</child>
|
||||
<action-widgets>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
""" Common module for showing dialogs """
|
||||
from enum import Enum
|
||||
|
||||
from . import Gtk
|
||||
from . import Gtk, UI_RESOURCES_PATH
|
||||
|
||||
|
||||
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):
|
||||
""" Shows dialogs by name """
|
||||
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.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)
|
||||
if file_filter is not None:
|
||||
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()
|
||||
if response == -12: # -12 for fix assertion 'gtk_widget_get_can_default (widget)' failed
|
||||
path = options["data_dir_path"]
|
||||
if dialog.get_filename():
|
||||
path = dialog.get_filename()
|
||||
if action_type is not Gtk.FileChooserAction.OPEN:
|
||||
|
||||
@@ -1,26 +1,28 @@
|
||||
from app.commons import run_idle, run_task
|
||||
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
|
||||
|
||||
|
||||
def show_download_dialog(transient, options, open_data):
|
||||
dialog = DownloadDialog(transient, options, open_data)
|
||||
def show_download_dialog(transient, options, open_data, profile=Profile.ENIGMA_2):
|
||||
dialog = DownloadDialog(transient, options, open_data, profile)
|
||||
dialog.run()
|
||||
dialog.destroy()
|
||||
|
||||
|
||||
class DownloadDialog:
|
||||
def __init__(self, transient, properties, open_data):
|
||||
def __init__(self, transient, properties, open_data, profile):
|
||||
self._properties = properties
|
||||
self._open_data = open_data
|
||||
self._profile = profile
|
||||
|
||||
handlers = {"on_receive": self.on_receive,
|
||||
"on_send": self.on_send,
|
||||
"on_info_bar_close": self.on_info_bar_close}
|
||||
|
||||
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)
|
||||
|
||||
self._dialog = builder.get_object("download_dialog")
|
||||
@@ -58,7 +60,7 @@ class DownloadDialog:
|
||||
def destroy(self):
|
||||
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)
|
||||
|
||||
@run_idle
|
||||
@@ -72,12 +74,13 @@ class DownloadDialog:
|
||||
self.show_info_message("Please, wait...", Gtk.MessageType.INFO)
|
||||
upload_data(properties=self._properties,
|
||||
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:
|
||||
message = str(getattr(e, "message", str(e)))
|
||||
self.show_info_message(message, Gtk.MessageType.ERROR)
|
||||
else:
|
||||
self.show_info_message("Done!", Gtk.MessageType.INFO)
|
||||
if download and d_type is not DownloadDataType.SATELLITES:
|
||||
self._open_data()
|
||||
|
||||
|
||||
@@ -2,16 +2,18 @@ import os
|
||||
from contextlib import suppress
|
||||
from functools import lru_cache
|
||||
|
||||
from app.commons import run_idle
|
||||
from app.eparser import get_blacklist, write_blacklist, to_bouquet_id, parse_m3u
|
||||
from app.eparser import get_channels, get_bouquets, write_bouquets, write_channels, Bouquets, Bouquet, Channel
|
||||
from app.eparser.__constants import CAS, FLAG
|
||||
from app.eparser.bouquets import BqServiceType
|
||||
from app.properties import get_config, write_config
|
||||
from .main_helper import edit_marker, insert_marker
|
||||
from . import Gtk, Gdk, LOCKED_ICON, HIDE_ICON
|
||||
from app.commons import run_idle, log
|
||||
from app.eparser import get_blacklist, write_blacklist, parse_m3u
|
||||
from app.eparser import get_services, get_bouquets, write_bouquets, write_services, Bouquets, Bouquet, Service
|
||||
from app.eparser.ecommons import CAS, FLAG
|
||||
from app.eparser.enigma.bouquets import BqServiceType
|
||||
from app.properties import get_config, write_config, Profile
|
||||
from . import Gtk, Gdk, UI_RESOURCES_PATH, LOCKED_ICON, HIDE_ICON
|
||||
from .dialogs import show_dialog, DialogType
|
||||
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, get_base_model
|
||||
from .picons_dialog import PiconsDialog
|
||||
from .satellites_dialog import show_satellites_dialog
|
||||
from .settings_dialog import show_settings_dialog
|
||||
|
||||
@@ -21,22 +23,35 @@ class MainAppWindow:
|
||||
_FAV_LIST_NAME = "fav_list_store"
|
||||
_BOUQUETS_LIST_NAME = "bouquets_tree_store"
|
||||
# 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")
|
||||
_BOUQUET_ELEMENTS = ("edit_tool_button", "new_tool_button", "bouquets_new_popup_item", "bouguets_edit_popup_item")
|
||||
_REMOVE_ELEMENTS = ("remove_tool_button", "delete_menu_item", "services_remove_popup_item",
|
||||
"bouquets_remove_popup_item", "fav_remove_popup_item")
|
||||
_FAV_ELEMENTS = ("up_tool_button", "down_tool_button", "cut_tool_button", "paste_tool_button", "cut_menu_item",
|
||||
_SERVICE_ELEMENTS = ("copy_tool_button", "to_fav_tool_button", "copy_menu_item", "services_to_fav_move_popup_item",
|
||||
"services_edit_popup_item", "services_copy_popup_item", "filter_entry")
|
||||
|
||||
_BOUQUET_ELEMENTS = ("edit_tool_button", "new_tool_button",
|
||||
"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",
|
||||
"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")
|
||||
|
||||
__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",
|
||||
"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",
|
||||
"fav_paste_popup_item", "bouquets_new_popup_item", "bouguets_edit_popup_item",
|
||||
"services_remove_popup_item", "bouquets_remove_popup_item", "fav_remove_popup_item",
|
||||
"locked_tool_button", "hide_tool_button", "import_m3u_tool_button",
|
||||
"fav_import_m3u_popup_item", "fav_insert_marker_popup_item", "fav_edit_marker_popup_item")
|
||||
"services_to_fav_move_popup_item", "services_edit_popup_item", "locked_tool_button",
|
||||
"services_remove_popup_item", "fav_cut_popup_item", "fav_paste_popup_item",
|
||||
"bouquets_new_popup_item", "bouquets_edit_popup_item", "services_remove_popup_item",
|
||||
"bouquets_remove_popup_item", "fav_remove_popup_item", "hide_tool_button",
|
||||
"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", "filter_entry")
|
||||
|
||||
def __init__(self):
|
||||
handlers = {"on_close_main_window": self.on_quit,
|
||||
@@ -56,9 +71,11 @@ class MainAppWindow:
|
||||
"on_cut": self.on_cut,
|
||||
"on_copy": self.on_copy,
|
||||
"on_paste": self.on_paste,
|
||||
"on_edit": self.on_edit,
|
||||
"on_delete": self.on_delete,
|
||||
"on_new_bouquet": self.on_new_bouquet,
|
||||
"on_bouquets_edit": self.on_bouquets_edit,
|
||||
"on_tool_edit": self.on_tool_edit,
|
||||
"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_fav_tree_view_drag_data_get": self.on_fav_tree_view_drag_data_get,
|
||||
@@ -71,19 +88,24 @@ class MainAppWindow:
|
||||
"on_import_m3u": self.on_import_m3u,
|
||||
"on_insert_marker": self.on_insert_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,
|
||||
"on_filter_changed": self.on_filter_changed}
|
||||
|
||||
self.__options = get_config()
|
||||
self.__profile = self.__options.get("profile")
|
||||
os.makedirs(os.path.dirname(self.__options.get(self.__profile).get("data_dir_path")), exist_ok=True)
|
||||
# Used for copy/paste. When adding the previous data will not be deleted.
|
||||
# Clearing only after the insertion!
|
||||
self.__rows_buffer = []
|
||||
self.__channels = {}
|
||||
self.__services = {}
|
||||
self.__bouquets = {}
|
||||
self.__bouquets_to_del = []
|
||||
self.__blacklist = set()
|
||||
|
||||
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")
|
||||
main_window_size = self.__options.get("window_size", None)
|
||||
# Setting the last size of the window if it was saved
|
||||
@@ -96,7 +118,9 @@ class MainAppWindow:
|
||||
self.__services_model = builder.get_object("services_list_store")
|
||||
self.__bouquets_model = builder.get_object("bouquets_tree_store")
|
||||
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
|
||||
self.__tool_elements = {k: builder.get_object(k) for k in self.__DYNAMIC_ELEMENTS}
|
||||
self.__cas_label = builder.get_object("cas_label")
|
||||
@@ -106,8 +130,14 @@ class MainAppWindow:
|
||||
self.__radio_count_label = builder.get_object("radio_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")
|
||||
builder.connect_signals(handlers)
|
||||
# Filter
|
||||
self.__services_model_filter = builder.get_object("services_model_filter")
|
||||
self.__services_model_filter.set_visible_func(self.services_filter_function)
|
||||
self.__filter_entry = builder.get_object("filter_entry")
|
||||
self.init_drag_and_drop() # drag and drop
|
||||
# Force ctrl press event for view. Multiple selections in lists only with Space key(as in file managers)!!!
|
||||
self.__services_view.connect("key-press-event", self.force_ctrl)
|
||||
self.__fav_view.connect("key-press-event", self.force_ctrl)
|
||||
self.__main_window.show()
|
||||
|
||||
def init_drag_and_drop(self):
|
||||
@@ -124,6 +154,10 @@ class MainAppWindow:
|
||||
self.__services_view.drag_source_set_target_list(None)
|
||||
self.__services_view.drag_source_add_text_targets()
|
||||
|
||||
def force_ctrl(self, view, event):
|
||||
""" Function for force ctrl press event for view """
|
||||
event.state |= Gdk.ModifierType.CONTROL_MASK
|
||||
|
||||
def on_quit(self, *args):
|
||||
""" Called before app quit """
|
||||
write_config(self.__options) # storing current config
|
||||
@@ -143,33 +177,13 @@ class MainAppWindow:
|
||||
show_dialog(DialogType.ABOUT, self.__main_window)
|
||||
|
||||
def move_items(self, key):
|
||||
""" Move items in fav tree view """
|
||||
selection = self.__fav_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(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)
|
||||
""" Move items in fav or bouquets tree view """
|
||||
if self.__services_view.is_focus():
|
||||
return
|
||||
elif self.__fav_view.is_focus():
|
||||
move_items(key, self.__fav_view)
|
||||
elif self.__bouquets_view and key not in (Gdk.KEY_Page_Up, Gdk.KEY_Page_Down):
|
||||
move_items(key, self.__bouquets_view)
|
||||
|
||||
def on_cut(self, view):
|
||||
for row in tuple(self.on_delete(view)):
|
||||
@@ -207,6 +221,17 @@ class MainAppWindow:
|
||||
self.__rows_buffer.clear()
|
||||
self.on_view_focus(view, None)
|
||||
|
||||
def on_edit(self, view):
|
||||
model = get_base_model(view.get_model())
|
||||
name = 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):
|
||||
""" Delete selected items from views
|
||||
|
||||
@@ -216,36 +241,41 @@ class MainAppWindow:
|
||||
if view.is_focus():
|
||||
selection = view.get_selection()
|
||||
model, paths = selection.get_selected_rows()
|
||||
model_name = model.get_name()
|
||||
model_name = get_base_model(model).get_name()
|
||||
itrs = [model.get_iter(path) for path in paths]
|
||||
rows = [model.get(in_itr, *[x for x in range(model.get_n_columns())]) for in_itr in itrs]
|
||||
rows = [model[in_itr][:] for in_itr in itrs]
|
||||
bq_selected = self.is_bouquet_selected()
|
||||
fav_bouquet = None
|
||||
|
||||
if bq_selected:
|
||||
fav_bouquet = self.__bouquets.get(bq_selected, None)
|
||||
|
||||
for itr in itrs:
|
||||
if fav_bouquet and model_name == self._FAV_LIST_NAME:
|
||||
del fav_bouquet[int(model.get_path(itr)[0])]
|
||||
if model_name == self._BOUQUETS_LIST_NAME:
|
||||
if len(model.get_path(itr)) < 2:
|
||||
show_dialog(DialogType.ERROR, self.__main_window, "This item is not allowed to be removed!")
|
||||
return
|
||||
else:
|
||||
self.delete_bouquet(bq_selected)
|
||||
model.remove(itr)
|
||||
if model_name == self._FAV_LIST_NAME:
|
||||
self.update_fav_num_column(model)
|
||||
self.remove_favs(fav_bouquet, itrs, model)
|
||||
elif model_name == self._BOUQUETS_LIST_NAME:
|
||||
self.delete_bouquets(itrs, model, bq_selected)
|
||||
elif model_name == self._SERVICE_LIST_NAME:
|
||||
self.delete_services(bq_selected, rows)
|
||||
self.delete_services(bq_selected, itrs, model, rows)
|
||||
|
||||
self.on_view_focus(view, None)
|
||||
|
||||
return rows
|
||||
|
||||
def delete_services(self, bq_selected, rows):
|
||||
def remove_favs(self, fav_bouquet, itrs, model):
|
||||
""" Deleting bouquet services """
|
||||
if fav_bouquet:
|
||||
for itr in itrs:
|
||||
del fav_bouquet[int(model.get_path(itr)[0])]
|
||||
self.__fav_model.remove(itr)
|
||||
self.update_fav_num_column(model)
|
||||
|
||||
def delete_services(self, bq_selected, itrs, model, rows):
|
||||
""" Deleting services """
|
||||
srv_itrs = [self.__services_model_filter.convert_iter_to_child_iter(
|
||||
model.convert_iter_to_child_iter(itr)) for itr in itrs]
|
||||
for s_itr in srv_itrs:
|
||||
self.__services_model.remove(s_itr)
|
||||
|
||||
for row in rows:
|
||||
# There are channels with the same parameters except for the name.
|
||||
# None because it can have duplicates! Need fix
|
||||
@@ -255,18 +285,27 @@ class MainAppWindow:
|
||||
if services:
|
||||
with suppress(ValueError):
|
||||
services.remove(fav_id)
|
||||
self.__channels.pop(fav_id, None)
|
||||
self.__services.pop(fav_id, None)
|
||||
self.__fav_model.clear()
|
||||
|
||||
if bq_selected:
|
||||
self.update_bouquet_channels(self.__fav_model, None, bq_selected)
|
||||
|
||||
def delete_bouquet(self, bouquet):
|
||||
""" Deleting bouquet """
|
||||
self.__bouquets.pop(bouquet)
|
||||
self.__fav_model.clear()
|
||||
bouquet_file_name = "{}userbouquet.{}.{}".format(self.__options["data_dir_path"], *bouquet.split(":"))
|
||||
self.__bouquets_to_del.append(bouquet_file_name)
|
||||
def delete_bouquets(self, itrs, model, bouquet):
|
||||
""" Deleting bouquets """
|
||||
for itr in itrs:
|
||||
if len(model.get_path(itr)) < 2:
|
||||
show_dialog(DialogType.ERROR, self.__main_window, "This item is not allowed to be removed!")
|
||||
return
|
||||
else:
|
||||
self.__bouquets.pop(bouquet)
|
||||
self.__fav_model.clear()
|
||||
self.__bouquets_model.remove(itr)
|
||||
|
||||
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):
|
||||
""" Creates a new item in the bouquets tree """
|
||||
@@ -274,7 +313,8 @@ class MainAppWindow:
|
||||
|
||||
if paths:
|
||||
itr = model.get_iter(paths[0])
|
||||
bq_type = model.get_value(itr, 1)
|
||||
bq_type = model.get_value(itr, 3)
|
||||
|
||||
bq_name = "bouquet"
|
||||
count = 0
|
||||
key = "{}:{}".format(bq_name, bq_type)
|
||||
@@ -288,29 +328,31 @@ class MainAppWindow:
|
||||
if response == Gtk.ResponseType.CANCEL:
|
||||
return
|
||||
|
||||
bq = response, bq_type
|
||||
bq = response, None, None, bq_type
|
||||
key = "{}:{}".format(response, bq_type)
|
||||
|
||||
if model.iter_n_children(itr): # parent
|
||||
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:
|
||||
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)
|
||||
self.scroll_to(model.get_path(it), paths, view)
|
||||
scroll_to(model.get_path(it), view, paths)
|
||||
self.__bouquets[key] = []
|
||||
|
||||
def scroll_to(self, path, paths, view):
|
||||
""" Scrolling to and selecting given path """
|
||||
view.expand_row(paths[0], 0)
|
||||
selection = view.get_selection()
|
||||
selection.unselect_all()
|
||||
view.scroll_to_cell(path, None)
|
||||
selection.select_path(path)
|
||||
def on_tool_edit(self, item):
|
||||
""" Edit tool bar button """
|
||||
if self.__services_view.is_focus():
|
||||
self.on_edit(self.__services_view)
|
||||
elif self.__fav_view.is_focus():
|
||||
self.on_edit(self.__fav_view)
|
||||
elif self.__bouquets_view.is_focus():
|
||||
self.on_edit(self.__bouquets_view)
|
||||
|
||||
def on_bouquets_edit(self, view):
|
||||
""" 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!")
|
||||
return
|
||||
|
||||
@@ -318,9 +360,8 @@ class MainAppWindow:
|
||||
|
||||
if paths:
|
||||
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)
|
||||
|
||||
if response == Gtk.ResponseType.CANCEL:
|
||||
return
|
||||
|
||||
@@ -337,6 +378,8 @@ class MainAppWindow:
|
||||
def get_selection(self, view):
|
||||
""" Creates a string from the iterators of the selected rows """
|
||||
model, paths = view.get_selection().get_selected_rows()
|
||||
if model.get_model(): # needs think about it !
|
||||
model = model.get_model().get_model()
|
||||
|
||||
if len(paths) > 0:
|
||||
itrs = [model.get_iter(path) for path in paths]
|
||||
@@ -350,7 +393,7 @@ class MainAppWindow:
|
||||
show_dialog(DialogType.ERROR, self.__main_window, "Error. No bouquet is selected!")
|
||||
return
|
||||
|
||||
model = view.get_model()
|
||||
model = get_base_model(view.get_model())
|
||||
dest_index = 0
|
||||
|
||||
if drop_info:
|
||||
@@ -368,19 +411,18 @@ class MainAppWindow:
|
||||
if source == self._SERVICE_LIST_NAME:
|
||||
ext_model = self.__services_view.get_model()
|
||||
ext_itrs = [ext_model.get_iter_from_string(itr) for itr in itrs]
|
||||
ext_rows = [ext_model.get(ext_itr, *[x for x in range(ext_model.get_n_columns())]) for
|
||||
ext_itr in ext_itrs]
|
||||
ext_rows = [ext_model[ext_itr][:] for ext_itr in ext_itrs]
|
||||
dest_index -= 1
|
||||
for ext_row in ext_rows:
|
||||
dest_index += 1
|
||||
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,
|
||||
channel.service_type, channel.pos, channel.fav_id))
|
||||
fav_bouquet.insert(dest_index, channel.fav_id)
|
||||
elif source == self._FAV_LIST_NAME:
|
||||
in_itrs = [model.get_iter_from_string(itr) for itr in itrs]
|
||||
in_rows = [model.get(in_itr, *[x for x in range(model.get_n_columns())]) for in_itr in in_itrs]
|
||||
in_rows = [model[in_itr][:] for in_itr in in_itrs]
|
||||
for row in in_rows:
|
||||
model.insert(dest_index, row)
|
||||
fav_bouquet.insert(dest_index, row[4])
|
||||
@@ -421,29 +463,27 @@ class MainAppWindow:
|
||||
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)
|
||||
|
||||
@run_idle
|
||||
def on_satellite_editor_show(self, model):
|
||||
""" 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):
|
||||
response = show_dialog(DialogType.CHOOSER, self.__main_window, options=self.__options)
|
||||
if response == Gtk.ResponseType.CANCEL:
|
||||
response = show_dialog(DialogType.CHOOSER, self.__main_window, options=self.__options.get(self.__profile))
|
||||
if response in (Gtk.ResponseType.CANCEL, Gtk.ResponseType.DELETE_EVENT):
|
||||
return
|
||||
self.open_data(response)
|
||||
|
||||
@run_idle
|
||||
def open_data(self, data_path=None):
|
||||
""" Opening data and fill views. """
|
||||
self.__bouquets_model.clear()
|
||||
self.__fav_model.clear()
|
||||
self.__services_model.clear()
|
||||
self.__blacklist.clear()
|
||||
self.clear_current_data()
|
||||
|
||||
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:
|
||||
self.append_blacklist(data_path)
|
||||
self.append_services(data_path)
|
||||
self.append_bouquets(data_path)
|
||||
self.append_services(data_path)
|
||||
self.update_services_counts(len(self.__services_model))
|
||||
except FileNotFoundError as e:
|
||||
show_dialog(DialogType.ERROR, self.__main_window, getattr(e, "message", str(e)) +
|
||||
@@ -457,71 +497,84 @@ class MainAppWindow:
|
||||
self.__blacklist.update(black_list)
|
||||
|
||||
def append_bouquets(self, data_path):
|
||||
for bouquet in get_bouquets(data_path):
|
||||
parent = self.__bouquets_model.append(None, [bouquet.name, bouquet.type])
|
||||
for bouquet in get_bouquets(data_path, Profile(self.__profile)):
|
||||
parent = self.__bouquets_model.append(None, [bouquet.name, None, None, bouquet.type])
|
||||
for bt in bouquet.bouquets:
|
||||
name, bt_type = bt.name, bt.type
|
||||
self.__bouquets_model.append(parent, [name, bt_type])
|
||||
name, bt_type, locked, hidden = bt.name, bt.type, bt.locked, bt.hidden
|
||||
self.__bouquets_model.append(parent, [name, locked, hidden, bt_type])
|
||||
services = []
|
||||
agr = [None] * 7
|
||||
for srv in bt.services:
|
||||
fav_id = srv.data
|
||||
# IPTV and MARKER services
|
||||
s_type = srv.type
|
||||
if s_type is BqServiceType.MARKER:
|
||||
self.__channels[fav_id] = Channel(*agr[0:3], srv.name, *agr[0:3],
|
||||
if s_type is BqServiceType.MARKER or s_type is BqServiceType.IPTV:
|
||||
self.__services[fav_id] = Service(*agr[0:3], srv.name, *agr[0:3],
|
||||
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)
|
||||
self.__bouquets["{}:{}".format(name, bt_type)] = services
|
||||
|
||||
def append_services(self, data_path):
|
||||
channels = get_channels(data_path)
|
||||
if channels:
|
||||
for ch in channels:
|
||||
# adding channels to dict with fav_id as keys
|
||||
self.__channels[ch.fav_id] = ch
|
||||
self.__services_model.append(ch)
|
||||
else:
|
||||
try:
|
||||
services = get_services(data_path, Profile(self.__profile))
|
||||
except Exception as e:
|
||||
print(e)
|
||||
log("Append services error: " + str(e))
|
||||
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()
|
||||
|
||||
def on_data_save(self, *args):
|
||||
if show_dialog(DialogType.QUESTION, self.__main_window) == Gtk.ResponseType.CANCEL:
|
||||
return
|
||||
|
||||
path = self.__options["data_dir_path"]
|
||||
path = self.__options.get(self.__profile).get("data_dir_path")
|
||||
# deleting files in data dir(skipping dirs) :)
|
||||
list(map(os.unlink, (os.path.join(path, f) for f in filter(
|
||||
lambda f: f != "satellites.xml" and os.path.isfile(os.path.join(path, f)), os.listdir(path)))))
|
||||
|
||||
bouquets = []
|
||||
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):
|
||||
bqs = None
|
||||
if model.iter_has_child(itr):
|
||||
num_of_children = model.iter_n_children(itr)
|
||||
bqs = []
|
||||
|
||||
num_of_children = model.iter_n_children(itr)
|
||||
for num in range(num_of_children):
|
||||
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)]
|
||||
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 = Bouquets(*model.get(itr, 0, 1), bqs)
|
||||
bouquets.append(bqs)
|
||||
if len(b_path) == 1:
|
||||
bouquets.append(Bouquets(*model.get(itr, 0, 3), bqs if bqs else []))
|
||||
|
||||
profile = Profile(self.__profile)
|
||||
# Getting bouquets
|
||||
self.__bouquets_view.get_model().foreach(parse_bouquets)
|
||||
write_bouquets(path, bouquets, self.__bouquets)
|
||||
write_bouquets(path, bouquets, profile)
|
||||
# Getting services
|
||||
services = [Channel(*row[:]) for row in services_model]
|
||||
write_channels(path, services)
|
||||
# blacklist
|
||||
write_blacklist(path, self.__blacklist)
|
||||
services = [Service(*row[:]) for row in services_model]
|
||||
write_services(path, services, profile)
|
||||
# removing bouquet files
|
||||
if profile is profile.ENIGMA_2:
|
||||
# blacklist
|
||||
write_blacklist(path, self.__blacklist)
|
||||
|
||||
def on_services_selection(self, model, path, column):
|
||||
self.delete_selection(self.__fav_view)
|
||||
@@ -556,11 +609,11 @@ class MainAppWindow:
|
||||
if 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]
|
||||
|
||||
for num, ch_id in enumerate(services):
|
||||
channel = self.__channels.get(ch_id, None)
|
||||
channel = self.__services.get(ch_id, None)
|
||||
if channel:
|
||||
self.__fav_model.append((num + 1, channel.coded, channel.service, channel.locked,
|
||||
channel.hide, channel.service_type, channel.pos, channel.fav_id))
|
||||
@@ -575,7 +628,7 @@ class MainAppWindow:
|
||||
if not path or len(path[0]) < 2:
|
||||
return False
|
||||
|
||||
return "{}:{}".format(*model.get(model.get_iter(path), 0, 1))
|
||||
return "{}:{}".format(*model.get(model.get_iter(path), 0, 3))
|
||||
|
||||
@run_idle
|
||||
def delete_selection(self, view, *args):
|
||||
@@ -583,16 +636,25 @@ class MainAppWindow:
|
||||
for v in [view, *args]:
|
||||
v.get_selection().unselect_all()
|
||||
|
||||
@run_idle
|
||||
def on_preferences(self, item):
|
||||
show_settings_dialog(self.__main_window, self.__options)
|
||||
self.__status_bar.push(0, "Current IP: " + self.__options["host"])
|
||||
response = show_settings_dialog(self.__main_window, self.__options)
|
||||
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):
|
||||
""" Handling keystrokes """
|
||||
key = event.keyval
|
||||
ctrl = event.state & Gdk.ModifierType.CONTROL_MASK
|
||||
alt = event.state & Gdk.ModifierType.MOD1_MASK
|
||||
model_name = view.get_model().get_name()
|
||||
model = get_base_model(view.get_model())
|
||||
model_name = model.get_name()
|
||||
|
||||
if key == Gdk.KEY_Delete:
|
||||
self.on_delete(view)
|
||||
@@ -601,7 +663,7 @@ class MainAppWindow:
|
||||
elif ctrl and key in (Gdk.KEY_Down, Gdk.KEY_Page_Down, Gdk.KEY_KP_Page_Down):
|
||||
self.move_items(key)
|
||||
elif model_name == self._FAV_LIST_NAME and key == Gdk.KEY_Control_L or key == Gdk.KEY_Control_R:
|
||||
self.update_fav_num_column(view.get_model())
|
||||
self.update_fav_num_column(model)
|
||||
self.update_bouquet_list()
|
||||
elif key == Gdk.KEY_Insert:
|
||||
# Move items from app to fav list
|
||||
@@ -609,8 +671,6 @@ class MainAppWindow:
|
||||
self.on_to_fav_move(view)
|
||||
elif model_name == self._BOUQUETS_LIST_NAME:
|
||||
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:
|
||||
self.on_copy(view)
|
||||
elif ctrl and key == Gdk.KEY_x or key == Gdk.KEY_X:
|
||||
@@ -624,15 +684,23 @@ class MainAppWindow:
|
||||
self.on_locked(None)
|
||||
elif ctrl and key == Gdk.KEY_h or key == Gdk.KEY_H:
|
||||
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:
|
||||
pass
|
||||
elif key == Gdk.KEY_Left or key == Gdk.KEY_Right:
|
||||
view.do_unselect_all(view)
|
||||
|
||||
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
|
||||
def on_view_focus(self, view, focus_event):
|
||||
model = view.get_model()
|
||||
profile = Profile(self.__profile)
|
||||
model = get_base_model(view.get_model())
|
||||
model_name = model.get_name()
|
||||
not_empty = len(model) > 0 # if > 0 model has items
|
||||
|
||||
@@ -641,13 +709,17 @@ class MainAppWindow:
|
||||
self.__tool_elements[elem].set_sensitive(False)
|
||||
for elem in self._BOUQUET_ELEMENTS:
|
||||
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:
|
||||
is_service = model_name == self._SERVICE_LIST_NAME
|
||||
for elem in self._FAV_ELEMENTS:
|
||||
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)
|
||||
elif elem in ("import_m3u_tool_button", "fav_import_m3u_popup_item"):
|
||||
self.__tool_elements[elem].set_sensitive(self.is_bouquet_selected() and not is_service)
|
||||
elif elem in self._FAV_ONLY_ELEMENTS:
|
||||
if profile is Profile.ENIGMA_2:
|
||||
self.__tool_elements[elem].set_sensitive(self.is_bouquet_selected() and not is_service)
|
||||
else:
|
||||
self.__tool_elements[elem].set_sensitive(not_empty and not is_service)
|
||||
for elem in self._SERVICE_ELEMENTS:
|
||||
@@ -655,9 +727,9 @@ class MainAppWindow:
|
||||
for elem in self._BOUQUET_ELEMENTS:
|
||||
self.__tool_elements[elem].set_sensitive(False)
|
||||
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)
|
||||
|
||||
def on_hide(self, item):
|
||||
@@ -667,45 +739,19 @@ class MainAppWindow:
|
||||
self.set_service_flags(FLAG.LOCK)
|
||||
|
||||
def set_service_flags(self, flag):
|
||||
model, paths = self.__services_view.get_selection().get_selected_rows()
|
||||
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:])
|
||||
profile = Profile(self.__profile)
|
||||
bq_selected = self.is_bouquet_selected()
|
||||
if bq_selected:
|
||||
self.__fav_model.clear()
|
||||
self.update_bouquet_channels(self.__fav_model, None, bq_selected)
|
||||
|
||||
def has_locked_hide(self, model, paths, col_num):
|
||||
for path in paths:
|
||||
if model.get_value(model.get_iter(path), col_num):
|
||||
return True
|
||||
return False
|
||||
if profile is Profile.ENIGMA_2:
|
||||
if set_flags(flag, self.__services_view, self.__fav_view, self.__services, self.__blacklist):
|
||||
if bq_selected:
|
||||
self.__fav_model.clear()
|
||||
self.update_bouquet_channels(self.__fav_model, None, bq_selected)
|
||||
elif profile is Profile.NEUTRINO_MP:
|
||||
if bq_selected:
|
||||
model, path = self.__bouquets_view.get_selection().get_selected()
|
||||
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
|
||||
def on_model_changed(self, model, path, itr=None):
|
||||
@@ -725,7 +771,7 @@ class MainAppWindow:
|
||||
radio_count = 0
|
||||
data_count = 0
|
||||
|
||||
for ch in self.__channels.values():
|
||||
for ch in self.__services.values():
|
||||
ch_type = ch.service_type
|
||||
if ch_type in ("TV", "TV (HD)"):
|
||||
tv_count += 1
|
||||
@@ -745,7 +791,7 @@ class MainAppWindow:
|
||||
file_filter.set_name("m3u files")
|
||||
response = show_dialog(dialog_type=DialogType.CHOOSER,
|
||||
transient=self.__main_window,
|
||||
options=self.__options,
|
||||
options=self.__options.get(self.__profile),
|
||||
action_type=Gtk.FileChooserAction.OPEN,
|
||||
file_filter=file_filter)
|
||||
if response == Gtk.ResponseType.CANCEL:
|
||||
@@ -761,17 +807,17 @@ class MainAppWindow:
|
||||
bq_services = self.__bouquets.get(bq_selected)
|
||||
self.__fav_model.clear()
|
||||
for ch in channels:
|
||||
self.__channels[ch.fav_id] = ch
|
||||
self.__services[ch.fav_id] = ch
|
||||
bq_services.append(ch.fav_id)
|
||||
self.update_bouquet_channels(self.__fav_model, None, bq_selected)
|
||||
|
||||
def on_insert_marker(self, view):
|
||||
""" 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)
|
||||
|
||||
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
|
||||
def on_fav_popup(self, view, event):
|
||||
@@ -779,6 +825,23 @@ class MainAppWindow:
|
||||
self.__fav_edit_marker_popup_item.set_sensitive(
|
||||
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()
|
||||
|
||||
@run_idle
|
||||
def on_filter_changed(self, entry):
|
||||
self.__services_model_filter.refilter()
|
||||
|
||||
def services_filter_function(self, model, iter, data):
|
||||
if self.__services_model_filter is None or self.__services_model_filter == "None":
|
||||
return True
|
||||
else:
|
||||
return self.__filter_entry.get_text() in str(model.get(iter, 3, 6, 7, 8, 9, 10, 11, 12, 13, 14))
|
||||
|
||||
|
||||
def start_app():
|
||||
MainAppWindow()
|
||||
|
||||
@@ -1,9 +1,21 @@
|
||||
""" This is helper module for main_app_window """
|
||||
from app.eparser import Channel
|
||||
from app.eparser.bouquets import BqServiceType
|
||||
from .dialogs import show_dialog, DialogType
|
||||
from . import Gtk
|
||||
""" This is helper module for ui """
|
||||
from enum import Enum
|
||||
|
||||
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):
|
||||
"""" 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
|
||||
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))
|
||||
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)
|
||||
|
||||
|
||||
def edit_marker(view, bouquets, selected_bouquet, channels, parent_window):
|
||||
def edit_marker(view, bouquets, selected_bouquet, channels, parent_window):
|
||||
""" Edits marker text """
|
||||
model, paths = view.get_selection().get_selected_rows()
|
||||
itr = model.get_iter(paths[0])
|
||||
@@ -41,10 +53,245 @@ def edit_marker(view, bouquets, selected_bouquet, channels, parent_window):
|
||||
old_ch = channels.pop(fav_id, None)
|
||||
new_fav_id = "{}::{}\n#DESCRIPTION {}\n".format(fav_id.split("::")[0], response, response)
|
||||
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.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()
|
||||
model = get_base_model(model)
|
||||
|
||||
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
|
||||
|
||||
model = get_base_model(model)
|
||||
|
||||
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 = get_base_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=get_base_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
|
||||
|
||||
|
||||
def get_base_model(model):
|
||||
""" Returns base tree model if has wrappers ("TreeModelSort" and "TreeModelFilter") """
|
||||
if type(model) is Gtk.TreeModelSort:
|
||||
return model.get_model().get_model()
|
||||
return model
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
pass
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
</object>
|
||||
</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="visible">True</property>
|
||||
<property name="sensitive">False</property>
|
||||
@@ -43,6 +43,10 @@
|
||||
<columns>
|
||||
<!-- column-name bouquet -->
|
||||
<column type="gchararray"/>
|
||||
<!-- column-name locked -->
|
||||
<column type="GdkPixbuf"/>
|
||||
<!-- column-name hidden -->
|
||||
<column type="GdkPixbuf"/>
|
||||
<!-- column-name type -->
|
||||
<column type="gchararray"/>
|
||||
</columns>
|
||||
@@ -79,7 +83,7 @@
|
||||
<object class="GtkImage" id="image4">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="icon_name">applications-utilities</property>
|
||||
<property name="icon_name">edit-select-all</property>
|
||||
</object>
|
||||
<object class="GtkImage" id="image5">
|
||||
<property name="visible">True</property>
|
||||
@@ -91,6 +95,11 @@
|
||||
<property name="can_focus">False</property>
|
||||
<property name="stock">gtk-edit</property>
|
||||
</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">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
@@ -116,6 +125,34 @@
|
||||
<signal name="activate" handler="on_paste" object="fav_tree_view" swapped="no"/>
|
||||
</object>
|
||||
</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>
|
||||
<object class="GtkSeparatorMenuItem" id="fav_pupup_separator_1">
|
||||
<property name="visible">True</property>
|
||||
@@ -182,6 +219,11 @@
|
||||
</object>
|
||||
</child>
|
||||
</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">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
@@ -228,6 +270,12 @@
|
||||
</columns>
|
||||
<signal name="row-deleted" handler="on_model_changed" swapped="no"/>
|
||||
</object>
|
||||
<object class="GtkTreeModelFilter" id="services_model_filter">
|
||||
<property name="child_model">services_list_store</property>
|
||||
</object>
|
||||
<object class="GtkTreeModelSort" id="services_model_tree_model_sort">
|
||||
<property name="model">services_model_filter</property>
|
||||
</object>
|
||||
<object class="GtkApplicationWindow" id="main_window">
|
||||
<property name="width_request">640</property>
|
||||
<property name="can_focus">False</property>
|
||||
@@ -374,6 +422,17 @@
|
||||
<signal name="activate" handler="on_satellite_editor_show" swapped="no"/>
|
||||
</object>
|
||||
</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>
|
||||
<object class="GtkSeparatorMenuItem" id="menuitem5">
|
||||
<property name="visible">True</property>
|
||||
@@ -494,6 +553,38 @@
|
||||
<property name="homogeneous">True</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkSeparatorToolItem" id="separatortoolitem8">
|
||||
<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="GtkToolItem" id="filter_tool_item">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<child>
|
||||
<object class="GtkEntry" id="filter_entry">
|
||||
<property name="visible">True</property>
|
||||
<property name="sensitive">False</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="tooltip_text" translatable="yes">Services filter</property>
|
||||
<property name="primary_icon_name">edit-select-all-symbolic</property>
|
||||
<property name="primary_icon_activatable">False</property>
|
||||
<property name="primary_icon_tooltip_text" translatable="yes">Services filter</property>
|
||||
<signal name="changed" handler="on_filter_changed" swapped="no"/>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="homogeneous">False</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkSeparatorToolItem" id="toolbutton4">
|
||||
<property name="visible">True</property>
|
||||
@@ -534,6 +625,7 @@
|
||||
<property name="visible">True</property>
|
||||
<property name="sensitive">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="use_underline">True</property>
|
||||
<property name="stock_id">gtk-go-up</property>
|
||||
@@ -549,6 +641,7 @@
|
||||
<property name="visible">True</property>
|
||||
<property name="sensitive">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="use_underline">True</property>
|
||||
<property name="stock_id">gtk-go-down</property>
|
||||
@@ -687,54 +780,17 @@
|
||||
<property name="visible">True</property>
|
||||
<property name="sensitive">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="use_underline">True</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>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="homogeneous">True</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkSeparatorToolItem" id="separatortoolitem4">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="homogeneous">False</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkToolButton" id="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>
|
||||
<object class="GtkToolButton" id="remove_tool_button">
|
||||
<property name="visible">True</property>
|
||||
@@ -751,19 +807,20 @@
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkSeparatorToolItem" id="toolbutton9">
|
||||
<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">True</property>
|
||||
<property name="homogeneous">False</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkToolButton" id="preferences_tool_button">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="tooltip_text" translatable="yes">Settings</property>
|
||||
<property name="label" translatable="yes">Preferences</property>
|
||||
<property name="use_underline">True</property>
|
||||
<property name="stock_id">gtk-preferences</property>
|
||||
@@ -775,13 +832,23 @@
|
||||
</packing>
|
||||
</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="can_focus">False</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="icon_name">applications-utilities</property>
|
||||
<property name="icon_name">edit-select-all</property>
|
||||
<signal name="clicked" handler="on_satellite_editor_show" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
@@ -789,6 +856,38 @@
|
||||
<property name="homogeneous">True</property>
|
||||
</packing>
|
||||
</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>
|
||||
<object class="GtkSeparatorToolItem" id="toolbutton11">
|
||||
<property name="visible">True</property>
|
||||
@@ -832,7 +931,7 @@
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="padding">2</property>
|
||||
<property name="position">2</property>
|
||||
<property name="position">3</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
@@ -840,8 +939,8 @@
|
||||
<property name="height_request">250</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="margin_left">2</property>
|
||||
<property name="margin_right">2</property>
|
||||
<property name="margin_left">1</property>
|
||||
<property name="margin_right">1</property>
|
||||
<property name="wide_handle">True</property>
|
||||
<child>
|
||||
<object class="GtkBox" id="box4">
|
||||
@@ -864,12 +963,13 @@
|
||||
<object class="GtkScrolledWindow" id="services_scrolled_window">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="margin_bottom">2</property>
|
||||
<property name="shadow_type">in</property>
|
||||
<child>
|
||||
<object class="GtkTreeView" id="services_tree_view">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="model">services_list_store</property>
|
||||
<property name="model">services_model_tree_model_sort</property>
|
||||
<property name="search_column">3</property>
|
||||
<property name="rubber_banding">True</property>
|
||||
<property name="enable_grid_lines">both</property>
|
||||
@@ -912,6 +1012,7 @@
|
||||
<object class="GtkTreeViewColumn" id="service_column">
|
||||
<property name="resizable">True</property>
|
||||
<property name="spacing">2</property>
|
||||
<property name="min_width">50</property>
|
||||
<property name="title" translatable="yes">Service</property>
|
||||
<property name="expand">True</property>
|
||||
<property name="reorderable">True</property>
|
||||
@@ -945,7 +1046,7 @@
|
||||
<child>
|
||||
<object class="GtkTreeViewColumn" id="package_column">
|
||||
<property name="resizable">True</property>
|
||||
<property name="sizing">autosize</property>
|
||||
<property name="min_width">50</property>
|
||||
<property name="title" translatable="yes">Package</property>
|
||||
<property name="expand">True</property>
|
||||
<property name="reorderable">True</property>
|
||||
@@ -961,6 +1062,7 @@
|
||||
<child>
|
||||
<object class="GtkTreeViewColumn" id="service_type_column">
|
||||
<property name="resizable">True</property>
|
||||
<property name="min_width">25</property>
|
||||
<property name="title" translatable="yes">Type</property>
|
||||
<property name="expand">True</property>
|
||||
<property name="reorderable">True</property>
|
||||
@@ -978,6 +1080,7 @@
|
||||
<child>
|
||||
<object class="GtkTreeViewColumn" id="ssid_column">
|
||||
<property name="resizable">True</property>
|
||||
<property name="min_width">25</property>
|
||||
<property name="title" translatable="yes">Ssid</property>
|
||||
<property name="expand">True</property>
|
||||
<property name="reorderable">True</property>
|
||||
@@ -995,6 +1098,7 @@
|
||||
<child>
|
||||
<object class="GtkTreeViewColumn" id="freq_column">
|
||||
<property name="resizable">True</property>
|
||||
<property name="min_width">25</property>
|
||||
<property name="title" translatable="yes">Freq</property>
|
||||
<property name="expand">True</property>
|
||||
<property name="reorderable">True</property>
|
||||
@@ -1012,6 +1116,7 @@
|
||||
<child>
|
||||
<object class="GtkTreeViewColumn" id="rate_column">
|
||||
<property name="resizable">True</property>
|
||||
<property name="min_width">25</property>
|
||||
<property name="title" translatable="yes">Rate</property>
|
||||
<property name="expand">True</property>
|
||||
<property name="reorderable">True</property>
|
||||
@@ -1029,6 +1134,7 @@
|
||||
<child>
|
||||
<object class="GtkTreeViewColumn" id="pol_column">
|
||||
<property name="resizable">True</property>
|
||||
<property name="min_width">25</property>
|
||||
<property name="title" translatable="yes">Pol</property>
|
||||
<property name="expand">True</property>
|
||||
<property name="reorderable">True</property>
|
||||
@@ -1046,6 +1152,7 @@
|
||||
<child>
|
||||
<object class="GtkTreeViewColumn" id="fec_column">
|
||||
<property name="resizable">True</property>
|
||||
<property name="min_width">25</property>
|
||||
<property name="title" translatable="yes">FEC</property>
|
||||
<property name="expand">True</property>
|
||||
<property name="reorderable">True</property>
|
||||
@@ -1063,6 +1170,7 @@
|
||||
<child>
|
||||
<object class="GtkTreeViewColumn" id="system_column">
|
||||
<property name="resizable">True</property>
|
||||
<property name="min_width">25</property>
|
||||
<property name="title" translatable="yes">System</property>
|
||||
<property name="expand">True</property>
|
||||
<property name="reorderable">True</property>
|
||||
@@ -1080,6 +1188,7 @@
|
||||
<child>
|
||||
<object class="GtkTreeViewColumn" id="pos_column">
|
||||
<property name="resizable">True</property>
|
||||
<property name="min_width">25</property>
|
||||
<property name="title" translatable="yes">Pos</property>
|
||||
<property name="expand">True</property>
|
||||
<property name="sort_column_id">14</property>
|
||||
@@ -1140,7 +1249,7 @@
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkBox" id="services_bar_box">
|
||||
<property name="height_request">25</property>
|
||||
<property name="height_request">20</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="resize_mode">queue</property>
|
||||
@@ -1163,12 +1272,14 @@
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="tooltip_text" translatable="yes">CAS</property>
|
||||
<property name="width_chars">10</property>
|
||||
<property name="width_chars">20</property>
|
||||
<property name="max_width_chars">20</property>
|
||||
<property name="xalign">0</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="padding">2</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
@@ -1262,6 +1373,19 @@
|
||||
<property name="position">8</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkSeparator" id="separator2">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="orientation">vertical</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="padding">5</property>
|
||||
<property name="position">9</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
@@ -1301,6 +1425,7 @@
|
||||
<object class="GtkScrolledWindow" id="scrolledwindow2">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="margin_bottom">2</property>
|
||||
<property name="shadow_type">in</property>
|
||||
<child>
|
||||
<object class="GtkTreeView" id="fav_tree_view">
|
||||
@@ -1421,7 +1546,7 @@
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkBox" id="fav_bar_box">
|
||||
<property name="height_request">25</property>
|
||||
<property name="height_request">20</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="spacing">2</property>
|
||||
@@ -1442,7 +1567,8 @@
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">0</property>
|
||||
<property name="width_chars">10</property>
|
||||
<property name="width_chars">15</property>
|
||||
<property name="max_width_chars">15</property>
|
||||
<property name="xalign">0</property>
|
||||
</object>
|
||||
<packing>
|
||||
@@ -1488,6 +1614,7 @@
|
||||
<object class="GtkScrolledWindow" id="scrolledwindow3">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="margin_bottom">2</property>
|
||||
<property name="shadow_type">in</property>
|
||||
<child>
|
||||
<object class="GtkTreeView" id="bouquets_tree_view">
|
||||
@@ -1495,7 +1622,7 @@
|
||||
<property name="can_focus">True</property>
|
||||
<property name="model">bouquets_tree_store</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>
|
||||
<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"/>
|
||||
@@ -1507,6 +1634,7 @@
|
||||
<child>
|
||||
<object class="GtkTreeViewColumn" id="bouquets_column">
|
||||
<property name="resizable">True</property>
|
||||
<property name="spacing">2</property>
|
||||
<property name="sizing">autosize</property>
|
||||
<property name="title" translatable="yes">Bouquets</property>
|
||||
<property name="expand">True</property>
|
||||
@@ -1516,6 +1644,20 @@
|
||||
<attribute name="text">0</attribute>
|
||||
</attributes>
|
||||
</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>
|
||||
</child>
|
||||
<child>
|
||||
@@ -1527,7 +1669,7 @@
|
||||
<child>
|
||||
<object class="GtkCellRendererText" id="bouquet_type_cellrenderertext"/>
|
||||
<attributes>
|
||||
<attribute name="text">1</attribute>
|
||||
<attribute name="text">3</attribute>
|
||||
</attributes>
|
||||
</child>
|
||||
</object>
|
||||
@@ -1543,7 +1685,7 @@
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkBox" id="bouquet_bar_box">
|
||||
<property name="height_request">25</property>
|
||||
<property name="height_request">20</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="spacing">2</property>
|
||||
@@ -1564,7 +1706,8 @@
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">0</property>
|
||||
<property name="width_chars">5</property>
|
||||
<property name="width_chars">10</property>
|
||||
<property name="max_width_chars">10</property>
|
||||
<property name="xalign">0</property>
|
||||
</object>
|
||||
<packing>
|
||||
@@ -1600,7 +1743,7 @@
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="padding">1</property>
|
||||
<property name="position">3</property>
|
||||
<property name="position">4</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
@@ -1613,7 +1756,7 @@
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="padding">2</property>
|
||||
<property name="position">4</property>
|
||||
<property name="position">5</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
@@ -1622,18 +1765,49 @@
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_start">10</property>
|
||||
<property name="margin_end">10</property>
|
||||
<property name="margin_top">6</property>
|
||||
<property name="margin_bottom">6</property>
|
||||
<property name="spacing">2</property>
|
||||
<property name="homogeneous">True</property>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label2">
|
||||
<object class="GtkBox" id="box1">
|
||||
<property name="visible">True</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.2 Pre-alpha</property>
|
||||
<property name="xalign">0.94999998807907104</property>
|
||||
</object>
|
||||
<packing>
|
||||
@@ -1646,7 +1820,7 @@
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">5</property>
|
||||
<property name="position">7</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
@@ -1666,6 +1840,41 @@
|
||||
<signal name="activate" handler="on_to_fav_move" object="services_tree_view" swapped="no"/>
|
||||
</object>
|
||||
</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>
|
||||
<object class="GtkImageMenuItem" id="services_remove_popup_item">
|
||||
<property name="label">gtk-remove</property>
|
||||
|
||||
692
app/ui/picons_dialog.glade
Normal file
692
app/ui/picons_dialog.glade
Normal file
@@ -0,0 +1,692 @@
|
||||
<?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="GtkGrid" id="format_box">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="column_spacing">2</property>
|
||||
<child>
|
||||
<object class="GtkBox" id="name_format_box">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label1">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</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="GtkBox" id="box2">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_right">5</property>
|
||||
<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">0</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">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkBox" id="resize_format_box">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label2">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">Resize: </property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkBox" id="resize_box">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<child>
|
||||
<object class="GtkRadioButton" id="resize_no_radio_button">
|
||||
<property name="label" translatable="yes">No(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">resize_100_60_radio_button</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="resize_220_132_radio_button">
|
||||
<property name="label" translatable="yes">220x132</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">resize_100_60_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="resize_100_60_radio_button">
|
||||
<property name="label" translatable="yes">100x60</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">resize_no_radio_button</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">2</property>
|
||||
<property name="top_attach">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkSeparator" id="separator3">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_left">5</property>
|
||||
<property name="margin_right">5</property>
|
||||
<property name="orientation">vertical</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<style>
|
||||
<class name="primary-toolbar"/>
|
||||
</style>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</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>
|
||||
239
app/ui/picons_dialog.py
Normal file
239
app/ui/picons_dialog.py
Normal file
@@ -0,0 +1,239 @@
|
||||
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")
|
||||
self._resize_no_radio_button = builder.get_object("resize_no_radio_button")
|
||||
self._resize_220_132_radio_button = builder.get_object("resize_220_132_radio_button")
|
||||
self._resize_100_60_radio_button = builder.get_object("resize_100_60_radio_button")
|
||||
# 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.resize(self._picons_path)
|
||||
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)
|
||||
|
||||
def resize(self, path):
|
||||
if self._resize_no_radio_button.get_active():
|
||||
return
|
||||
|
||||
self.show_info_message("Resizing...", Gtk.MessageType.INFO)
|
||||
command = "mogrify -resize {}! *.png".format(
|
||||
"320x240" if self._resize_220_132_radio_button.get_active() else "100x60").split()
|
||||
self._current_process = subprocess.Popen(command, universal_newlines=True, cwd=path)
|
||||
self._current_process.wait()
|
||||
|
||||
@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>
|
||||
</packing>
|
||||
</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>
|
||||
<object class="GtkToolItem" id="toolitem1">
|
||||
<property name="visible">True</property>
|
||||
|
||||
@@ -3,8 +3,9 @@ from math import fabs
|
||||
|
||||
from app.commons import run_idle
|
||||
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 .main_helper import move_items, scroll_to
|
||||
|
||||
|
||||
def show_satellites_dialog(transient, options):
|
||||
@@ -19,12 +20,14 @@ class SatellitesDialog:
|
||||
_aggr = [None for x in range(9)] # aggregate
|
||||
|
||||
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
|
||||
|
||||
handlers = {"on_open": self.on_open,
|
||||
"on_remove": self.on_remove,
|
||||
"on_save": self.on_save,
|
||||
"on_up": self.on_up,
|
||||
"on_down": self.on_down,
|
||||
"on_popup_menu": self.on_popup_menu,
|
||||
"on_satellite_add": self.on_satellite_add,
|
||||
"on_transponder_add": self.on_transponder_add,
|
||||
@@ -35,7 +38,7 @@ class SatellitesDialog:
|
||||
"on_quit": self.on_quit}
|
||||
|
||||
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",
|
||||
"popup_menu", "add_popup_menu", "add_menu_icon"))
|
||||
builder.connect_signals(handlers)
|
||||
@@ -97,6 +100,12 @@ class SatellitesDialog:
|
||||
else:
|
||||
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):
|
||||
""" Handling keystrokes """
|
||||
key = event.keyval
|
||||
@@ -106,7 +115,6 @@ class SatellitesDialog:
|
||||
self.on_remove(view)
|
||||
elif key == Gdk.KEY_Insert:
|
||||
pass
|
||||
# self.on_add(view)
|
||||
elif ctrl and key == Gdk.KEY_E or key == Gdk.KEY_e:
|
||||
self.on_edit(view)
|
||||
elif ctrl and key == Gdk.KEY_s or key == Gdk.KEY_S:
|
||||
@@ -115,6 +123,10 @@ class SatellitesDialog:
|
||||
self.on_transponder()
|
||||
elif key == Gdk.KEY_space:
|
||||
pass
|
||||
elif ctrl and key in (Gdk.KEY_Up, Gdk.KEY_Page_Up, Gdk.KEY_KP_Page_Up): # KEY_KP_Page_Up for laptop!
|
||||
move_items(key, self._sat_view)
|
||||
elif ctrl and key in (Gdk.KEY_Down, Gdk.KEY_Page_Down, Gdk.KEY_KP_Page_Down):
|
||||
move_items(key, self._sat_view)
|
||||
|
||||
@run_idle
|
||||
def on_satellites_list_load(self, model):
|
||||
@@ -174,7 +186,7 @@ class SatellitesDialog:
|
||||
else:
|
||||
index = self.get_sat_position_index(sat.position, model)
|
||||
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):
|
||||
""" Create or edit transponder """
|
||||
@@ -215,20 +227,13 @@ class SatellitesDialog:
|
||||
path = model.get_path(tr_itr)
|
||||
index = path.get_indices()[1]
|
||||
model.insert(model.iter_parent(tr_itr), index, row)
|
||||
self.scroll_to(path, view)
|
||||
scroll_to(path, view)
|
||||
break
|
||||
else:
|
||||
tr_itr = model.iter_next(tr_itr)
|
||||
else:
|
||||
itr = model.append(itr, row)
|
||||
self.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)
|
||||
scroll_to(model.get_path(itr), view)
|
||||
|
||||
def get_sat_position_index(self, pos, model):
|
||||
""" Search and returns index after given position """
|
||||
@@ -299,7 +304,7 @@ class TransponderDialog:
|
||||
handlers = {"on_entry_changed": self.on_entry_changed}
|
||||
|
||||
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",
|
||||
"pol_store", "fec_store",
|
||||
"mod_store", "system_store",
|
||||
@@ -321,7 +326,7 @@ class TransponderDialog:
|
||||
self._pattern = re.compile("\D")
|
||||
# style
|
||||
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,
|
||||
Gtk.STYLE_PROVIDER_PRIORITY_USER)
|
||||
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):
|
||||
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"))
|
||||
|
||||
self._dialog = builder.get_object("satellite_dialog")
|
||||
|
||||
@@ -1,54 +1,118 @@
|
||||
from app.properties import write_config
|
||||
from app.ui.dialogs import show_dialog, DialogType
|
||||
from . import Gtk
|
||||
from app.properties import write_config, Profile, get_default_settings
|
||||
from . import Gtk, UI_RESOURCES_PATH
|
||||
from .main_helper import update_entry_data
|
||||
|
||||
|
||||
def show_settings_dialog(transient, options):
|
||||
SettingsDialog(transient, options)
|
||||
return SettingsDialog(transient, options).show()
|
||||
|
||||
|
||||
class SettingsDialog:
|
||||
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.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)
|
||||
self._options = options
|
||||
|
||||
self._dialog = builder.get_object("settings_dialog")
|
||||
self._dialog.set_transient_for(transient)
|
||||
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.set_text(options["port"])
|
||||
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.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.set_text(options["services_path"])
|
||||
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.set_text(options["satellites_xml_path"])
|
||||
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:
|
||||
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["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["data_dir_path"] = self._data_dir_field.get_text()
|
||||
write_config(options)
|
||||
self._options = options
|
||||
self._active_profile = options.get("profile")
|
||||
self.set_settings()
|
||||
self._neutrino_radio_button.set_active(Profile(self._active_profile) is Profile.NEUTRINO_MP)
|
||||
|
||||
def show(self):
|
||||
response = self._dialog.run()
|
||||
if response == Gtk.ResponseType.OK:
|
||||
self.apply_settings()
|
||||
write_config(self._options)
|
||||
self._dialog.destroy()
|
||||
|
||||
return response
|
||||
|
||||
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)
|
||||
if response != Gtk.ResponseType.CANCEL:
|
||||
entry.set_text(response)
|
||||
update_entry_data(entry, self._dialog, self._options.get(self._options.get("profile")))
|
||||
|
||||
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__":
|
||||
|
||||
19
build-deb.sh
Executable file
19
build-deb.sh
Executable file
@@ -0,0 +1,19 @@
|
||||
#!/bin/env bash
|
||||
VER="0.2.2_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
|
||||
Version: 0.1.1-Pre-alpha
|
||||
Version: 0.2.2-Pre-alpha
|
||||
Section: utils
|
||||
Priority: optional
|
||||
Architecture: all
|
||||
Essential: no
|
||||
Depends: python3 (>= 3.5)
|
||||
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: *
|
||||
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
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
[Desktop Entry]
|
||||
Version=1.0
|
||||
Name=DemonEditor
|
||||
Comment=Channels and satellites editor for Enigma2
|
||||
Comment[ru]=Редактор каналов и спутников для Enigma2
|
||||
Comment=Channels and satellites list editor for Enigma2
|
||||
Comment[ru]=Редактор списка каналов и спутников для Enigma2
|
||||
Icon=accessories-text-editor
|
||||
Exec=/usr/bin/demoneditor.sh
|
||||
Terminal=false
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
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
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
@@ -1,23 +1,25 @@
|
||||
# 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:
|
||||
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!
|
||||
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.
|
||||
Enigma2 channel and satellites list editor for GNU/Linux.
|
||||
Experimental support of Neutrino-MP or others on the same basis (BPanther, etc).
|
||||
Focused on the convenience of working in lists from the keyboard. The mouse is also fully supported (Drag and Drop etc)
|
||||
|
||||
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)!
|
||||
|
||||
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!
|
||||
|
||||
|
||||
Reference in New Issue
Block a user