mirror of
https://github.com/DYefremov/DemonEditor.git
synced 2026-03-06 20:41:41 +01:00
skeleton implementation of service details show
This commit is contained in:
@@ -9,7 +9,8 @@ Ctrl + X, C, V, Up, Down, PageUp, PageDown, S, T, E, L, H, Space; Insert, Delete
|
||||
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 + E, F2 - edit.
|
||||
Ctrl + R - rename.
|
||||
Ctrl + S, T, E in Satellites edit tool for create and edit satellite or transponder.
|
||||
Ctrl + L - parental lock.
|
||||
Ctrl + H - hide/skip.
|
||||
|
||||
@@ -34,7 +34,7 @@ class Type(Enum):
|
||||
Cable = "c"
|
||||
|
||||
|
||||
class FLAG(Enum):
|
||||
class Flag(Enum):
|
||||
""" Service flags """
|
||||
KEEP = 1 # Do not automatically update the services parameters.
|
||||
HIDE = 2
|
||||
@@ -47,6 +47,20 @@ class FLAG(Enum):
|
||||
return 2, 3, 6, 7, 10, 42, 43, 46, 47
|
||||
|
||||
|
||||
class Inversion(Enum):
|
||||
Off = "0"
|
||||
On = "1"
|
||||
Auto = "2"
|
||||
|
||||
|
||||
class Pilot(Enum):
|
||||
Off = "0"
|
||||
On = "1"
|
||||
Auto = "2"
|
||||
|
||||
|
||||
ROLL_OFF = {"0": "35%", "1": "25%", "2": "20%", "3": "Auto"}
|
||||
|
||||
POLARIZATION = {"0": "H", "1": "V", "2": "L", "3": "R"}
|
||||
|
||||
PLS_MODE = {"0": "Root", "1": "Gold", "2": "Combo"}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
from app.commons import log
|
||||
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
|
||||
from ..ecommons import Service, POLARIZATION, SYSTEM, FEC, SERVICE_TYPE, Flag
|
||||
|
||||
_HEADER = "eDVB services /4/"
|
||||
_SEP = ":" # separator
|
||||
@@ -101,7 +101,7 @@ def parse_services(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:]) in FLAG.hide_values() 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))
|
||||
@@ -128,7 +128,7 @@ def parse_services(services, transponders, path):
|
||||
rate=tr[1],
|
||||
pol=POLARIZATION[tr[2]],
|
||||
fec=FEC[tr[3]],
|
||||
system=SYSTEM[tr[6]],
|
||||
system="DVB-S2" if len(tr) > 7 else "DVB-S",
|
||||
pos="{}.{}".format(tr[4][:-1], tr[4][-1:]),
|
||||
data_id=ch[0],
|
||||
fav_id=fav_id,
|
||||
|
||||
@@ -1,21 +1,31 @@
|
||||
""" Module for m3u import """
|
||||
from app.properties import Profile
|
||||
from app.ui import IPTV_ICON
|
||||
from .ecommons import BqServiceType, Service
|
||||
|
||||
# url, description, urlkey, account, usrname, psw, s_type, iconsrc, iconsrc_b, group
|
||||
NEUTRINO_FAV_ID_FORMAT = "{}::{}::{}::{}::{}::{}::{}::{}::{}::{}"
|
||||
ENIGMA2_FAV_ID_FORMAT = " 1:0:1:0:0:0:0:0:0:0:{}:{}\n#DESCRIPTION: {}\n"
|
||||
|
||||
def parse_m3u(path):
|
||||
|
||||
def parse_m3u(path, profile):
|
||||
with open(path) as file:
|
||||
aggr = [None] * 10
|
||||
channels = []
|
||||
count = 0
|
||||
name = None
|
||||
fav_id = None
|
||||
for line in file.readlines():
|
||||
if line.startswith("#EXTINF"):
|
||||
name = line[1 + line.index(","):].strip()
|
||||
count += 1
|
||||
elif count == 1:
|
||||
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)
|
||||
srv = Service(*aggr[0:3], name, *aggr[0:3], BqServiceType.IPTV.name, *aggr, fav_id, None)
|
||||
if profile is Profile.ENIGMA_2:
|
||||
fav_id = ENIGMA2_FAV_ID_FORMAT.format(line.strip().replace(":", "%3a"), name, name, None)
|
||||
elif profile is Profile.NEUTRINO_MP:
|
||||
fav_id = NEUTRINO_FAV_ID_FORMAT.format(line.strip(), "", 0, None, None, None, None, "", "", 1)
|
||||
srv = Service(None, None, IPTV_ICON, name, *aggr[0:3], BqServiceType.IPTV.name, *aggr, fav_id, None)
|
||||
channels.append(srv)
|
||||
|
||||
return channels
|
||||
|
||||
@@ -1,23 +1,28 @@
|
||||
import os
|
||||
from contextlib import suppress
|
||||
from enum import Enum
|
||||
from xml.dom.minidom import parse, Document
|
||||
|
||||
from app.eparser.iptv import NEUTRINO_FAV_ID_FORMAT
|
||||
from app.ui import LOCKED_ICON, HIDE_ICON
|
||||
from ..ecommons import Bouquets, Bouquet, BouquetService, BqServiceType, PROVIDER
|
||||
|
||||
_FILE = "bouquets.xml"
|
||||
_U_FILE = "ubouquets.xml"
|
||||
_W_FILE = "webtv.xml"
|
||||
|
||||
_COMMENT = " File was created in DemonEditor. Enjoy watching! "
|
||||
|
||||
|
||||
class BqType(Enum):
|
||||
BOUQUET = "bouquet"
|
||||
TV = "tv"
|
||||
WEBTV = "webtv"
|
||||
|
||||
|
||||
def get_bouquets(path):
|
||||
return (parse_bouquets(path + _FILE, "Providers", BqType.BOUQUET.value),
|
||||
parse_bouquets(path + _U_FILE, "FAV", BqType.TV.value))
|
||||
parse_bouquets(path + _U_FILE, "FAV", BqType.TV.value),
|
||||
parse_webtv(path + _W_FILE, "WEBTV", BqType.WEBTV.value))
|
||||
|
||||
|
||||
def parse_bouquets(file, name, bq_type):
|
||||
@@ -61,22 +66,62 @@ def parse_bouquets(file, name, bq_type):
|
||||
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)
|
||||
def parse_webtv(path, name, bq_type):
|
||||
bouquets = Bouquets(name=name, type=bq_type, bouquets=[])
|
||||
if not os.path.exists(path):
|
||||
return bouquets
|
||||
|
||||
dom = parse(path)
|
||||
services = []
|
||||
for elem in dom.getElementsByTagName("webtv"):
|
||||
if elem.hasAttributes():
|
||||
title = elem.attributes["title"].value
|
||||
url = elem.attributes["url"].value
|
||||
description = elem.attributes.get("description")
|
||||
description = description.value if description else description
|
||||
urlkey = elem.attributes.get("urlkey", None)
|
||||
urlkey = urlkey.value if urlkey else urlkey
|
||||
account = elem.attributes.get("account", None)
|
||||
account = account.value if account else account
|
||||
usrname = elem.attributes.get("usrname", None)
|
||||
usrname = usrname.value if usrname else usrname
|
||||
psw = elem.attributes.get("psw", None)
|
||||
psw = psw.value if psw else psw
|
||||
s_type = elem.attributes.get("type", None)
|
||||
s_type = s_type.value if s_type else s_type
|
||||
iconsrc = elem.attributes.get("iconsrc", None)
|
||||
iconsrc = iconsrc.value if iconsrc else iconsrc
|
||||
iconsrc_b = elem.attributes.get("iconsrc_b", None)
|
||||
iconsrc_b = iconsrc_b.value if iconsrc_b else iconsrc_b
|
||||
group = elem.attributes.get("group", None)
|
||||
group = group.value if group else group
|
||||
fav_id = NEUTRINO_FAV_ID_FORMAT.format(url, description, urlkey, account, usrname, psw, s_type, iconsrc,
|
||||
iconsrc_b, group)
|
||||
srv = BouquetService(name=title,
|
||||
type=BqServiceType.IPTV,
|
||||
data=fav_id,
|
||||
num=0)
|
||||
services.append(srv)
|
||||
bouquet = Bouquet(name="default", type=bq_type, services=services, locked=None, hidden=None)
|
||||
bouquets[2].append(bouquet)
|
||||
|
||||
return bouquets
|
||||
|
||||
|
||||
def write_bouquets(path, bouquets):
|
||||
for bq in bouquets:
|
||||
bq_type = BqType(bq.type)
|
||||
write_bouquet(path + (_FILE if bq_type is BqType.BOUQUET else _U_FILE), bq)
|
||||
if bq_type is BqType.WEBTV:
|
||||
write_webtv(path + _W_FILE, bq)
|
||||
else:
|
||||
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! ")
|
||||
comment = doc.createComment(_COMMENT)
|
||||
doc.appendChild(comment)
|
||||
|
||||
for bq in bouquet.bouquets:
|
||||
@@ -102,5 +147,43 @@ def write_bouquet(file, bouquet):
|
||||
doc.writexml(open(file, "w"), addindent=" ", newl="\n", encoding="UTF-8")
|
||||
|
||||
|
||||
def write_webtv(file, bouquet):
|
||||
doc = Document()
|
||||
root = doc.createElement("webtvs")
|
||||
doc.appendChild(root)
|
||||
comment = doc.createComment(_COMMENT)
|
||||
doc.appendChild(comment)
|
||||
|
||||
for bq in bouquet.bouquets:
|
||||
for srv in bq.services:
|
||||
url, description, urlkey, account, usrname, psw, s_type, iconsrc, iconsrc_b, group = srv.fav_id.split("::")
|
||||
srv_elem = doc.createElement("webtv")
|
||||
srv_elem.setAttribute("title", srv.service)
|
||||
srv_elem.setAttribute("url", url)
|
||||
|
||||
if description != "None":
|
||||
srv_elem.setAttribute("description", description)
|
||||
if urlkey != "None":
|
||||
srv_elem.setAttribute("urlkey", urlkey)
|
||||
if account != "None":
|
||||
srv_elem.setAttribute("account", account)
|
||||
if usrname != "None":
|
||||
srv_elem.setAttribute("usrname", usrname)
|
||||
if psw != "None":
|
||||
srv_elem.setAttribute("psw", psw)
|
||||
if s_type != "None":
|
||||
srv_elem.setAttribute("type", s_type)
|
||||
if iconsrc != "None":
|
||||
srv_elem.setAttribute("iconsrc", iconsrc)
|
||||
if iconsrc_b != "None":
|
||||
srv_elem.setAttribute("iconsrc_b", iconsrc_b)
|
||||
if group != "None":
|
||||
srv_elem.setAttribute("group", group)
|
||||
|
||||
root.appendChild(srv_elem)
|
||||
|
||||
doc.writexml(open(file, "w"), addindent=" ", newl="\n", encoding="UTF-8")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
pass
|
||||
|
||||
45
app/ftp.py
45
app/ftp.py
@@ -11,12 +11,16 @@ from app.properties import Profile
|
||||
__DATA_FILES_LIST = ("tv", "radio", "lamedb", "blacklist", "whitelist", # enigma 2
|
||||
"services.xml", "myservices.xml", "bouquets.xml", "ubouquets.xml") # neutrino
|
||||
|
||||
_SATELLITES_XML_FILE = "satellites.xml"
|
||||
_WEBTV_XML_FILE = "webtv.xml"
|
||||
|
||||
|
||||
class DownloadDataType(Enum):
|
||||
ALL = 0
|
||||
BOUQUETS = 1
|
||||
SATELLITES = 2
|
||||
PICONS = 3
|
||||
WEBTV = 4
|
||||
|
||||
|
||||
def download_data(*, properties, download_type=DownloadDataType.ALL, callback=None):
|
||||
@@ -35,20 +39,21 @@ def download_data(*, properties, download_type=DownloadDataType.ALL, callback=No
|
||||
name = str(file).strip()
|
||||
if name.endswith(__DATA_FILES_LIST):
|
||||
name = name.split()[-1]
|
||||
with open(save_path + name, "wb") as f:
|
||||
ftp.retrbinary("RETR " + name, f.write)
|
||||
# satellites.xml section
|
||||
if download_type is DownloadDataType.ALL or download_type is DownloadDataType.SATELLITES:
|
||||
download_file(ftp, name, save_path)
|
||||
# satellites.xml and webtv section
|
||||
if download_type in (DownloadDataType.ALL, DownloadDataType.SATELLITES, DownloadDataType.WEBTV):
|
||||
ftp.cwd(properties["satellites_xml_path"])
|
||||
files.clear()
|
||||
ftp.dir(files.append)
|
||||
|
||||
for file in files:
|
||||
name = str(file).strip()
|
||||
xml_file = "satellites.xml"
|
||||
if name.endswith(xml_file):
|
||||
with open(save_path + xml_file, 'wb') as f:
|
||||
ftp.retrbinary("RETR " + xml_file, f.write)
|
||||
if download_type in (DownloadDataType.ALL, DownloadDataType.SATELLITES):
|
||||
if name.endswith(_SATELLITES_XML_FILE):
|
||||
download_file(ftp, _SATELLITES_XML_FILE, save_path)
|
||||
elif download_type in (DownloadDataType.ALL, DownloadDataType.WEBTV):
|
||||
if name.endswith(_WEBTV_XML_FILE):
|
||||
download_file(ftp, _WEBTV_XML_FILE, save_path)
|
||||
|
||||
if callback is not None:
|
||||
callback()
|
||||
@@ -71,9 +76,20 @@ def upload_data(*, properties, download_type=DownloadDataType.ALL, remove_unused
|
||||
|
||||
if download_type is DownloadDataType.ALL or download_type is DownloadDataType.SATELLITES:
|
||||
ftp.cwd(properties["satellites_xml_path"])
|
||||
file_name = "satellites.xml"
|
||||
send = send_file(file_name, data_path, ftp)
|
||||
if download_type == DownloadDataType.SATELLITES:
|
||||
send = send_file(_SATELLITES_XML_FILE, data_path, ftp)
|
||||
if download_type is DownloadDataType.SATELLITES:
|
||||
tn.send("init 3" if profile is Profile.ENIGMA_2 else "init 6")
|
||||
if callback is not None:
|
||||
callback()
|
||||
return send
|
||||
|
||||
if profile is Profile.NEUTRINO_MP and download_type in (DownloadDataType.ALL, DownloadDataType.WEBTV):
|
||||
ftp.cwd(properties["satellites_xml_path"])
|
||||
send = send_file(_WEBTV_XML_FILE, data_path, ftp)
|
||||
if download_type is DownloadDataType.WEBTV:
|
||||
tn.send("init 6")
|
||||
if callback is not None:
|
||||
callback()
|
||||
return send
|
||||
|
||||
if download_type is DownloadDataType.ALL or download_type is DownloadDataType.BOUQUETS:
|
||||
@@ -88,7 +104,7 @@ def upload_data(*, properties, download_type=DownloadDataType.ALL, remove_unused
|
||||
ftp.delete(name)
|
||||
|
||||
for file_name in os.listdir(data_path):
|
||||
if file_name == "satellites.xml":
|
||||
if file_name == _SATELLITES_XML_FILE or file_name == _WEBTV_XML_FILE:
|
||||
continue
|
||||
if file_name.endswith(__DATA_FILES_LIST):
|
||||
send_file(file_name, data_path, ftp)
|
||||
@@ -123,6 +139,11 @@ def upload_data(*, properties, download_type=DownloadDataType.ALL, remove_unused
|
||||
callback()
|
||||
|
||||
|
||||
def download_file(ftp, name, save_path):
|
||||
with open(save_path + name, "wb") as f:
|
||||
ftp.retrbinary("RETR " + name, f.write)
|
||||
|
||||
|
||||
def send_file(file_name, path, ftp):
|
||||
""" Opens the file in binary mode and transfers into receiver """
|
||||
with open(path + file_name, "rb") as f:
|
||||
|
||||
@@ -15,6 +15,7 @@ LOCKED_ICON = theme.load_icon("system-lock-screen", 16, 0) if theme.lookup_icon(
|
||||
"system-lock-screen", 16, 0) else _IMAGE_MISSING
|
||||
HIDE_ICON = theme.load_icon("go-jump", 16, 0) if theme.lookup_icon("go-jump", 16, 0) else _IMAGE_MISSING
|
||||
TV_ICON = theme.load_icon("tv-symbolic", 16, 0) if theme.lookup_icon("tv-symbolic", 16, 0) else _IMAGE_MISSING
|
||||
IPTV_ICON = theme.load_icon("emblem-shared", 16, 0) if theme.load_icon("emblem-shared", 16, 0) else None
|
||||
|
||||
if __name__ == "__main__":
|
||||
pass
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
<property name="icon_name">system-help</property>
|
||||
<property name="type_hint">normal</property>
|
||||
<property name="program_name">DemonEditor</property>
|
||||
<property name="version">0.2.3 Pre-alpha</property>
|
||||
<property name="version">0.2.4 Pre-alpha</property>
|
||||
<property name="copyright" translatable="yes">2018 Dmitriy Yefremov
|
||||
dmitry.v.yefremov@gmail.com
|
||||
</property>
|
||||
@@ -220,7 +220,21 @@ dmitry.v.yefremov@gmail.com
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
<object class="GtkRadioButton" id="webtv_radio_button">
|
||||
<property name="label" translatable="yes">WebTV</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">False</property>
|
||||
<property name="xalign">0</property>
|
||||
<property name="yalign">0.52999997138977051</property>
|
||||
<property name="active">True</property>
|
||||
<property name="draw_indicator">True</property>
|
||||
<property name="group">all_radio_button</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">4</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
|
||||
@@ -35,16 +35,19 @@ class DownloadDialog:
|
||||
self._all_radio_button = builder.get_object("all_radio_button")
|
||||
self._bouquets_radio_button = builder.get_object("bouquets_radio_button")
|
||||
self._satellites_radio_button = builder.get_object("satellites_radio_button")
|
||||
self._webtv_radio_button = builder.get_object("webtv_radio_button")
|
||||
if profile is Profile.NEUTRINO_MP:
|
||||
self._webtv_radio_button.set_visible(True)
|
||||
# self._dialog.get_content_area().set_border_width(0)
|
||||
|
||||
@run_idle
|
||||
def on_receive(self, item):
|
||||
self.download(True, d_type=self.get_download_type())
|
||||
self.download(True, self.get_download_type())
|
||||
|
||||
@run_idle
|
||||
def on_send(self, item):
|
||||
if show_dialog(DialogType.QUESTION, self._dialog) != Gtk.ResponseType.CANCEL:
|
||||
self.download(d_type=self.get_download_type())
|
||||
self.download(False, self.get_download_type())
|
||||
|
||||
def get_download_type(self):
|
||||
download_type = DownloadDataType.ALL
|
||||
@@ -52,6 +55,8 @@ class DownloadDialog:
|
||||
download_type = DownloadDataType.BOUQUETS
|
||||
elif self._satellites_radio_button.get_active():
|
||||
download_type = DownloadDataType.SATELLITES
|
||||
elif self._webtv_radio_button.get_active():
|
||||
download_type = DownloadDataType.WEBTV
|
||||
return download_type
|
||||
|
||||
def run(self):
|
||||
@@ -65,7 +70,7 @@ class DownloadDialog:
|
||||
|
||||
@run_idle
|
||||
@run_task
|
||||
def download(self, download=False, d_type=DownloadDataType.ALL):
|
||||
def download(self, download, d_type):
|
||||
""" Download/upload data from/to receiver """
|
||||
try:
|
||||
if download:
|
||||
|
||||
@@ -7,10 +7,11 @@ import shutil
|
||||
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.ecommons import CAS, Flag
|
||||
from app.eparser.enigma.bouquets import BqServiceType
|
||||
from app.eparser.neutrino.bouquets import BqType
|
||||
from app.properties import get_config, write_config, Profile
|
||||
from . import Gtk, Gdk, UI_RESOURCES_PATH, LOCKED_ICON, HIDE_ICON
|
||||
from . import Gtk, Gdk, UI_RESOURCES_PATH, LOCKED_ICON, HIDE_ICON, IPTV_ICON
|
||||
from .dialogs import show_dialog, DialogType, get_chooser_dialog
|
||||
from .download_dialog import show_download_dialog
|
||||
from .main_helper import edit_marker, insert_marker, move_items, edit, ViewTarget, set_flags, locate_in_services, \
|
||||
@@ -18,6 +19,7 @@ from .main_helper import edit_marker, insert_marker, move_items, edit, ViewTarge
|
||||
from .picons_dialog import PiconsDialog
|
||||
from .satellites_dialog import show_satellites_dialog
|
||||
from .settings_dialog import show_settings_dialog
|
||||
from .service_details_dialog import ServiceDetailsDialog
|
||||
|
||||
|
||||
class MainAppWindow:
|
||||
@@ -39,8 +41,9 @@ class MainAppWindow:
|
||||
"fav_import_m3u_popup_item", "fav_insert_marker_popup_item", "fav_edit_popup_item",
|
||||
"fav_locate_popup_item", "fav_picon_popup_item")
|
||||
|
||||
_FAV_ONLY_ELEMENTS = ("import_m3u_tool_button", "fav_import_m3u_popup_item", "fav_insert_marker_popup_item",
|
||||
"fav_edit_marker_popup_item")
|
||||
_FAV_ENIGMA_ELEMENTS = ("fav_insert_marker_popup_item", "fav_edit_marker_popup_item")
|
||||
|
||||
_FAV_M3U_ELEMENTS = ("import_m3u_tool_button", "fav_import_m3u_popup_item")
|
||||
|
||||
_LOCK_HIDE_ELEMENTS = ("locked_tool_button", "hide_tool_button")
|
||||
|
||||
@@ -99,7 +102,8 @@ class MainAppWindow:
|
||||
"on_reference_picon": self.on_reference_picon,
|
||||
"on_filter_toggled": self.on_filter_toggled,
|
||||
"on_search_toggled": self.on_search_toggled,
|
||||
"on_search": self.on_search}
|
||||
"on_search": self.on_search,
|
||||
"on_services_data_edit": self.on_services_data_edit}
|
||||
|
||||
self.__options = get_config()
|
||||
self.__profile = self.__options.get("profile")
|
||||
@@ -524,7 +528,8 @@ class MainAppWindow:
|
||||
# IPTV and MARKER services
|
||||
s_type = srv.type
|
||||
if s_type is BqServiceType.MARKER or s_type is BqServiceType.IPTV:
|
||||
srv = Service(*agr[0:3], srv.name, *agr[0:3], s_type.name, *agr, srv.num, fav_id, None)
|
||||
icon = IPTV_ICON if s_type is BqServiceType.IPTV else None
|
||||
srv = Service(*agr[0:2], icon, srv.name, *agr[0:3], s_type.name, *agr, srv.num, fav_id, None)
|
||||
self.__services[fav_id] = srv
|
||||
services.append(fav_id)
|
||||
self.__bouquets["{}:{}".format(name, bt_type)] = services
|
||||
@@ -703,8 +708,15 @@ 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:
|
||||
elif ctrl and key == Gdk.KEY_R or key == Gdk.KEY_r:
|
||||
self.on_edit(view)
|
||||
elif ctrl and key == Gdk.KEY_E or key == Gdk.KEY_e or key == Gdk.KEY_F2:
|
||||
if model_name == self._BOUQUETS_LIST_NAME:
|
||||
self.on_edit(view)
|
||||
return
|
||||
elif model_name == self._FAV_LIST_NAME:
|
||||
self.on_locate_in_services(view)
|
||||
self.on_services_data_edit(view)
|
||||
elif key == Gdk.KEY_Left or key == Gdk.KEY_Right:
|
||||
view.do_unselect_all(view)
|
||||
|
||||
@@ -714,7 +726,6 @@ class MainAppWindow:
|
||||
open_data=self.open_data,
|
||||
profile=Profile(self.__profile))
|
||||
|
||||
@run_idle
|
||||
def on_view_focus(self, view, focus_event):
|
||||
profile = Profile(self.__profile)
|
||||
model = get_base_model(view.get_model())
|
||||
@@ -731,12 +742,21 @@ class MainAppWindow:
|
||||
self.__tool_elements[elem].set_sensitive(not_empty)
|
||||
else:
|
||||
is_service = model_name == self._SERVICE_LIST_NAME
|
||||
bq_selected = False
|
||||
if model_name == self._FAV_LIST_NAME:
|
||||
bq_selected = self.is_bouquet_selected()
|
||||
if profile is Profile.NEUTRINO_MP and bq_selected:
|
||||
name, bq_type = bq_selected.split(":")
|
||||
bq_selected = BqType(bq_type) is BqType.WEBTV
|
||||
|
||||
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 self._FAV_ONLY_ELEMENTS:
|
||||
elif elem in self._FAV_ENIGMA_ELEMENTS:
|
||||
if profile is Profile.ENIGMA_2:
|
||||
self.__tool_elements[elem].set_sensitive(self.is_bouquet_selected() and not is_service)
|
||||
self.__tool_elements[elem].set_sensitive(bq_selected and not is_service)
|
||||
elif elem in self._FAV_M3U_ELEMENTS:
|
||||
self.__tool_elements[elem].set_sensitive(bq_selected and not is_service)
|
||||
else:
|
||||
self.__tool_elements[elem].set_sensitive(not_empty and not is_service)
|
||||
for elem in self._SERVICE_ELEMENTS:
|
||||
@@ -750,10 +770,10 @@ class MainAppWindow:
|
||||
self.__tool_elements[elem].set_sensitive(not_empty)
|
||||
|
||||
def on_hide(self, item):
|
||||
self.set_service_flags(FLAG.HIDE)
|
||||
self.set_service_flags(Flag.HIDE)
|
||||
|
||||
def on_locked(self, item):
|
||||
self.set_service_flags(FLAG.LOCK)
|
||||
self.set_service_flags(Flag.LOCK)
|
||||
|
||||
def set_service_flags(self, flag):
|
||||
profile = Profile(self.__profile)
|
||||
@@ -766,9 +786,9 @@ class MainAppWindow:
|
||||
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)
|
||||
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):
|
||||
@@ -811,7 +831,7 @@ class MainAppWindow:
|
||||
show_dialog(DialogType.ERROR, self.__main_window, text="No m3u file is selected!")
|
||||
return
|
||||
|
||||
channels = parse_m3u(response)
|
||||
channels = parse_m3u(response, Profile(self.__profile))
|
||||
bq_selected = self.is_bouquet_selected()
|
||||
if channels and bq_selected:
|
||||
bq_services = self.__bouquets.get(bq_selected)
|
||||
@@ -876,6 +896,11 @@ class MainAppWindow:
|
||||
self.__services,
|
||||
self.__bouquets)
|
||||
|
||||
@run_idle
|
||||
def on_services_data_edit(self, item):
|
||||
dialog = ServiceDetailsDialog(self.__main_window, self.__options, self.__services_view)
|
||||
dialog.show()
|
||||
|
||||
@run_idle
|
||||
def update_picons(self):
|
||||
update_picons(self.__options.get(self.__profile).get("picons_dir_path"), self.__picons, self.__services_model)
|
||||
|
||||
@@ -7,7 +7,7 @@ import shutil
|
||||
from gi.repository import GdkPixbuf
|
||||
|
||||
from app.eparser import Service
|
||||
from app.eparser.ecommons import FLAG
|
||||
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, get_chooser_dialog
|
||||
@@ -166,7 +166,7 @@ def set_flags(flag, services_view, fav_view, channels, blacklist):
|
||||
|
||||
model = get_base_model(model)
|
||||
|
||||
if flag is FLAG.HIDE:
|
||||
if flag is Flag.HIDE:
|
||||
if target is ViewTarget.SERVICES:
|
||||
set_hide(channels, model, paths)
|
||||
else:
|
||||
@@ -174,7 +174,7 @@ def set_flags(flag, services_view, fav_view, channels, blacklist):
|
||||
srv_model = get_base_model(services_view.get_model())
|
||||
srv_paths = [row.path for row in srv_model if row[18] in fav_ids]
|
||||
set_hide(channels, srv_model, srv_paths)
|
||||
elif flag is FLAG.LOCK:
|
||||
elif flag is Flag.LOCK:
|
||||
set_lock(blacklist, channels, model, paths, target, services_model=get_base_model(services_view.get_model()))
|
||||
|
||||
return True
|
||||
@@ -223,13 +223,13 @@ def set_hide(channels, model, paths):
|
||||
value = int(flag[2:]) if flag else 0
|
||||
|
||||
if not hide:
|
||||
if value in FLAG.hide_values():
|
||||
if value in Flag.hide_values():
|
||||
continue # skip if already hidden
|
||||
value += FLAG.HIDE.value
|
||||
value += Flag.HIDE.value
|
||||
else:
|
||||
if value not in FLAG.hide_values():
|
||||
if value not in Flag.hide_values():
|
||||
continue # skip if already allowed to show
|
||||
value -= FLAG.HIDE.value
|
||||
value -= Flag.HIDE.value
|
||||
|
||||
if value == 0 and index is not None:
|
||||
del flags[index]
|
||||
|
||||
@@ -117,6 +117,11 @@
|
||||
<property name="can_focus">False</property>
|
||||
<property name="stock">gtk-copy</property>
|
||||
</object>
|
||||
<object class="GtkImage" id="image17">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="stock">gtk-properties</property>
|
||||
</object>
|
||||
<object class="GtkImage" id="image4">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
@@ -367,6 +372,22 @@
|
||||
<signal name="activate" handler="on_edit" object="services_tree_view" swapped="no"/>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkSeparatorMenuItem" id="separatormenuitem7">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkImageMenuItem" id="services_data_edit_popup_item">
|
||||
<property name="label" translatable="yes">Show details/edit</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="image">image17</property>
|
||||
<property name="use_stock">False</property>
|
||||
<signal name="activate" handler="on_services_data_edit" object="services_tree_view" swapped="no"/>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkSeparatorMenuItem" id="separatormenuitem2">
|
||||
<property name="visible">True</property>
|
||||
@@ -1158,35 +1179,29 @@
|
||||
<object class="GtkInfoBar" id="search_info_bar">
|
||||
<property name="app_paintable">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="homogeneous">True</property>
|
||||
<child internal-child="action_area">
|
||||
<object class="GtkButtonBox" id="search_infobar_action_area">
|
||||
<property name="can_focus">False</property>
|
||||
<property name="spacing">6</property>
|
||||
<property name="spacing">5</property>
|
||||
<property name="homogeneous">True</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>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child internal-child="content_area">
|
||||
<object class="GtkBox" id="search_infobar_content_area">
|
||||
<property name="can_focus">False</property>
|
||||
<property name="spacing">16</property>
|
||||
<property name="spacing">2</property>
|
||||
<property name="homogeneous">True</property>
|
||||
<child>
|
||||
<object class="GtkAlignment" id="alignment1">
|
||||
<object class="GtkAlignment" id="alignment2">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<child>
|
||||
@@ -1216,7 +1231,7 @@
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkAlignment" id="alignment2">
|
||||
<object class="GtkAlignment" id="alignment1">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<child>
|
||||
@@ -1233,12 +1248,9 @@
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
<property name="position">0</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
@@ -2232,7 +2244,7 @@
|
||||
<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.3 Pre-alpha</property>
|
||||
<property name="label" translatable="yes">Ver. 0.2.4 Pre-alpha</property>
|
||||
<property name="xalign">0.94999998807907104</property>
|
||||
</object>
|
||||
<packing>
|
||||
|
||||
1357
app/ui/service_details_dialog.glade
Normal file
1357
app/ui/service_details_dialog.glade
Normal file
File diff suppressed because it is too large
Load Diff
190
app/ui/service_details_dialog.py
Normal file
190
app/ui/service_details_dialog.py
Normal file
@@ -0,0 +1,190 @@
|
||||
from enum import Enum
|
||||
from functools import lru_cache
|
||||
|
||||
from app.commons import run_idle
|
||||
from app.eparser import Service, get_satellites
|
||||
from app.eparser.ecommons import MODULATION, Inversion, ROLL_OFF, Pilot
|
||||
from app.properties import Profile
|
||||
from . import Gtk, UI_RESOURCES_PATH
|
||||
from .main_helper import is_only_one_item_selected
|
||||
|
||||
|
||||
class Pids(Enum):
|
||||
VIDEO = "c:00"
|
||||
AUDIO = "c:01"
|
||||
TELETEXT = "c:02"
|
||||
PCR = "c:03"
|
||||
AC3 = "c:04"
|
||||
VIDEO_TYPE = "c:05"
|
||||
AUDIO_CHANNEL = "c:06"
|
||||
BIT_STREAM_DELAY = "c:07" # in ms
|
||||
PCM_DELAY = "c:08" # in ms
|
||||
SUBTITLE = "c:09"
|
||||
|
||||
|
||||
@lru_cache(maxsize=1)
|
||||
def get_sat_positions(path):
|
||||
return ["{:.1f}".format(float(x.position) / 10) for x in get_satellites(path)]
|
||||
|
||||
|
||||
class ServiceDetailsDialog:
|
||||
def __init__(self, transient, options, view):
|
||||
handlers = {"on_system_changed": self.on_system_changed}
|
||||
|
||||
builder = Gtk.Builder()
|
||||
builder.add_from_file(UI_RESOURCES_PATH + "service_details_dialog.glade")
|
||||
builder.connect_signals(handlers)
|
||||
|
||||
self._dialog = builder.get_object("service_details_dialog")
|
||||
self._dialog.set_transient_for(transient)
|
||||
self._profile = Profile(options["profile"])
|
||||
self._satellites_xml_path = options.get(self._profile.value)["data_dir_path"] + "satellites.xml"
|
||||
self._services_view = view
|
||||
# Service elements
|
||||
self._name_entry = builder.get_object("name_entry")
|
||||
self._package_entry = builder.get_object("package_entry")
|
||||
self._id_entry = builder.get_object("id_entry")
|
||||
self._service_type_combo_box = builder.get_object("service_type_combo_box")
|
||||
self._cas_entry = builder.get_object("cas_entry")
|
||||
self._bitstream_entry = builder.get_object("bitstream_entry")
|
||||
self._pcm_entry = builder.get_object("pcm_entry")
|
||||
self._reference_entry = builder.get_object("reference_entry")
|
||||
self._video_pid_entry = builder.get_object("video_pid_entry")
|
||||
self._pcr_pid_entry = builder.get_object("pcr_pid_entry")
|
||||
self._audio_pid_entry = builder.get_object("audio_pid_entry")
|
||||
self._ac3_pid_entry = builder.get_object("ac3_pid_entry")
|
||||
self._ac3plus_pid_entry = builder.get_object("ac3plus_pid_entry")
|
||||
self._acc_pid_entry = builder.get_object("acc_pid_entry")
|
||||
self._he_acc_pid_entry = builder.get_object("he_acc_pid_entry")
|
||||
self._teletext_pid_entry = builder.get_object("teletext_pid_entry")
|
||||
self._keep_check_button = builder.get_object("keep_check_button")
|
||||
self._hide_check_button = builder.get_object("hide_check_button")
|
||||
self._use_pids_check_button = builder.get_object("use_pids_check_button")
|
||||
self._new_check_button = builder.get_object("new_check_button")
|
||||
# Transponder elements
|
||||
self._sat_pos_combo_box = builder.get_object("sat_pos_combo_box")
|
||||
self._transponder_id_entry = builder.get_object("transponder_id_entry")
|
||||
self._network_id_entry = builder.get_object("network_id_entry")
|
||||
self._freq_entry = builder.get_object("freq_entry")
|
||||
self._rate_entry = builder.get_object("rate_entry")
|
||||
self._pol_combo_box = builder.get_object("pol_combo_box")
|
||||
self._fec_combo_box = builder.get_object("fec_combo_box")
|
||||
self._sys_combo_box = builder.get_object("sys_combo_box")
|
||||
self._mod_combo_box = builder.get_object("mod_combo_box")
|
||||
self._invertion_combo_box = builder.get_object("invertion_combo_box")
|
||||
self._rolloff_combo_box = builder.get_object("rolloff_combo_box")
|
||||
self._pilot_combo_box = builder.get_object("pilot_combo_box")
|
||||
self._pls_mode_combo_box = builder.get_object("pls_mode_combo_box")
|
||||
self._pls_code_entry = builder.get_object("pls_code_entry")
|
||||
self._stream_id_entry = builder.get_object("stream_id_entry")
|
||||
self._flags_entry = builder.get_object("flags_entry")
|
||||
self._namespace_entry = builder.get_object("namespace_entry")
|
||||
self._DVB_S2_ELEMENTS = (self._mod_combo_box, self._rolloff_combo_box, self._pilot_combo_box,
|
||||
self._pls_mode_combo_box, self._pls_code_entry, self._stream_id_entry)
|
||||
self.update_data_elements()
|
||||
|
||||
@run_idle
|
||||
def update_data_elements(self):
|
||||
model, paths = self._services_view.get_selection().get_selected_rows()
|
||||
if is_only_one_item_selected(paths, self._dialog):
|
||||
srv = Service(*model[paths][:])
|
||||
# Service
|
||||
self._name_entry.set_text(srv.service)
|
||||
self._package_entry.set_text(srv.package)
|
||||
self.select_active_text(self._service_type_combo_box, srv.service_type)
|
||||
self._id_entry.set_text(str(int(srv.ssid, 16)))
|
||||
# Transponder
|
||||
self._freq_entry.set_text(srv.freq)
|
||||
self._rate_entry.set_text(srv.rate)
|
||||
self.select_active_text(self._pol_combo_box, srv.pol)
|
||||
self.select_active_text(self._fec_combo_box, srv.fec)
|
||||
self.select_active_text(self._sys_combo_box, srv.system)
|
||||
self.set_sat_positions(srv.pos)
|
||||
|
||||
if self._profile is Profile.ENIGMA_2:
|
||||
self.init_enigma2_service_data(srv)
|
||||
self.init_enigma2_transponder_data(srv)
|
||||
|
||||
@run_idle
|
||||
def init_enigma2_service_data(self, srv):
|
||||
""" Service data initialisation """
|
||||
flags = srv.flags_cas.split(",")
|
||||
|
||||
cas = list(filter(lambda x: x.startswith("C:"), flags))
|
||||
if cas:
|
||||
self._cas_entry.set_text(",".join(cas))
|
||||
|
||||
pids = list(filter(lambda x: x.startswith("c:"), flags))
|
||||
if pids:
|
||||
for pid in pids:
|
||||
if pid.startswith(Pids.VIDEO.value):
|
||||
self._video_pid_entry.set_text(str(int(pid[4:], 16)))
|
||||
elif pid.startswith(Pids.AUDIO.value):
|
||||
self._audio_pid_entry.set_text(str(int(pid[4:], 16)))
|
||||
elif pid.startswith(Pids.TELETEXT.value):
|
||||
self._teletext_pid_entry.set_text(str(int(pid[4:], 16)))
|
||||
elif pid.startswith(Pids.PCR.value):
|
||||
self._pcr_pid_entry.set_text(str(int(pid[4:], 16)))
|
||||
elif pid.startswith(Pids.AC3.value):
|
||||
self._ac3_pid_entry.set_text(str(int(pid[4:], 16)))
|
||||
elif pid.startswith(Pids.VIDEO_TYPE.value):
|
||||
pass
|
||||
elif pid.startswith(Pids.AUDIO_CHANNEL.value):
|
||||
pass
|
||||
elif pid.startswith(Pids.BIT_STREAM_DELAY.value):
|
||||
self._bitstream_entry.set_text(str(int(pid[4:], 16)))
|
||||
elif pid.startswith(Pids.PCM_DELAY.value):
|
||||
self._pcm_entry.set_text(str(int(pid[4:], 16)))
|
||||
elif pid.startswith(Pids.SUBTITLE.value):
|
||||
pass
|
||||
|
||||
self._reference_entry.set_text(srv.picon_id.replace("_", ":").rstrip(".png"))
|
||||
|
||||
@run_idle
|
||||
def init_enigma2_transponder_data(self, srv):
|
||||
""" Transponder data initialisation """
|
||||
data = srv.data_id.split(":")
|
||||
tr_data = srv.transponder.split(":")
|
||||
|
||||
if srv.system == "DVB-S2":
|
||||
self.select_active_text(self._mod_combo_box, MODULATION.get(tr_data[8]))
|
||||
self.select_active_text(self._rolloff_combo_box, ROLL_OFF.get(tr_data[9]))
|
||||
self.select_active_text(self._pilot_combo_box, Pilot(tr_data[10]).name)
|
||||
|
||||
self._namespace_entry.set_text(str(int(data[1], 16)))
|
||||
self._transponder_id_entry.set_text(str(int(data[2], 16)))
|
||||
self._network_id_entry.set_text(str(int(data[3], 16)))
|
||||
self.select_active_text(self._invertion_combo_box, Inversion(tr_data[5]).name)
|
||||
self._flags_entry.set_text(tr_data[6])
|
||||
|
||||
def select_active_text(self, box: Gtk.ComboBox, text):
|
||||
model = box.get_model()
|
||||
for index, row in enumerate(model):
|
||||
if row[0] == text:
|
||||
box.set_active(index)
|
||||
break
|
||||
|
||||
@run_idle
|
||||
def set_sat_positions(self, sat_pos):
|
||||
model = self._sat_pos_combo_box.get_model()
|
||||
positions = get_sat_positions(self._satellites_xml_path)
|
||||
for pos in positions:
|
||||
model.append((pos,))
|
||||
self.select_active_text(self._sat_pos_combo_box, sat_pos)
|
||||
|
||||
def on_system_changed(self, box):
|
||||
for elem in self._DVB_S2_ELEMENTS:
|
||||
elem.set_sensitive(box.get_active())
|
||||
|
||||
def show(self):
|
||||
response = self._dialog.run()
|
||||
if response == Gtk.ResponseType.OK:
|
||||
pass
|
||||
self._dialog.destroy()
|
||||
|
||||
return response
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
dialog = ServiceDetailsDialog()
|
||||
dialog.show()
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/bin/env bash
|
||||
VER="0.2.3_Pre-alpha"
|
||||
VER="0.2.4_Pre-alpha"
|
||||
B_PATH="dist/DemonEditor"
|
||||
DEB_PATH="$B_PATH/usr/share/demoneditor"
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
Package: DemonEditor
|
||||
Version: 0.2.3-Pre-alpha
|
||||
Version: 0.2.4-Pre-alpha
|
||||
Section: utils
|
||||
Priority: optional
|
||||
Architecture: all
|
||||
|
||||
Reference in New Issue
Block a user