mirror of
https://github.com/DYefremov/DemonEditor.git
synced 2026-05-06 19:36:38 +02:00
added displaying sub bouquets support
This commit is contained in:
@@ -15,6 +15,7 @@ class BqServiceType(Enum):
|
||||
MARKER = "MARKER" # 64
|
||||
SPACE = "SPACE" # 832 [hidden marker]
|
||||
ALT = "ALT" # Service with alternatives
|
||||
BOUQUET = "BOUQUET" # Sub bouquet.
|
||||
|
||||
|
||||
Bouquet = namedtuple("Bouquet", ["name", "type", "services", "locked", "hidden", "file"])
|
||||
|
||||
@@ -1,3 +1,31 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# The MIT License (MIT)
|
||||
#
|
||||
# Copyright (c) 2018-2021 Dmitriy Yefremov
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
# THE SOFTWARE.
|
||||
#
|
||||
# Author: Dmitriy Yefremov
|
||||
#
|
||||
|
||||
|
||||
""" This module used for parsing blacklist file
|
||||
|
||||
Parent Lock/Unlock
|
||||
@@ -9,14 +37,14 @@ __FILE_NAME = "blacklist"
|
||||
|
||||
def get_blacklist(path):
|
||||
with suppress(FileNotFoundError):
|
||||
with open(path + __FILE_NAME, "r") as file:
|
||||
with open(path + __FILE_NAME, "r", encoding="utf-8") as file:
|
||||
# filter empty values and "\n"
|
||||
return {*list(filter(None, (x.strip() for x in file.readlines())))}
|
||||
return {}
|
||||
|
||||
|
||||
def write_blacklist(path, channels):
|
||||
with open(path + __FILE_NAME, "w") as file:
|
||||
with open(path + __FILE_NAME, "w", encoding="utf-8") as file:
|
||||
if channels:
|
||||
file.writelines("\n".join(channels))
|
||||
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
""" Module for working with Enigma2 bouquets. """
|
||||
import re
|
||||
from collections import Counter
|
||||
from enum import Enum
|
||||
from pathlib import Path
|
||||
|
||||
from app.commons import log
|
||||
@@ -127,10 +128,24 @@ class BouquetsWriter:
|
||||
file.writelines(bouquet)
|
||||
|
||||
|
||||
class ServiceType(Enum):
|
||||
SERVICE = "0"
|
||||
BOUQUET = "7" # Sub bouquet.
|
||||
MARKER = "64"
|
||||
SPACE = "832" # Hidden marker.
|
||||
ALT = "134" # Alternatives.
|
||||
|
||||
@classmethod
|
||||
def _missing_(cls, value):
|
||||
log("Error. No matching service type [{} {}] was found.".format(cls.__name__, value))
|
||||
return cls.SERVICE
|
||||
|
||||
|
||||
class BouquetsReader:
|
||||
""" Class for reading and parsing bouquets. """
|
||||
_ALT_PAT = re.compile(".*alternatives\\.+(.*)\\.([tv|radio]+).*")
|
||||
_BQ_PAT = re.compile(".*userbouquet\\.+(.*)\\.+[tv|radio].*")
|
||||
_SUB_BQ_PAT = re.compile(".*subbouquet\\.+(.*)\\.([tv|radio]+).*")
|
||||
_STREAM_TYPES = {"4097", "5001", "5002", "8193", "8739"}
|
||||
|
||||
__slots__ = ["_path"]
|
||||
@@ -199,19 +214,26 @@ class BouquetsReader:
|
||||
log("The bouquet [{}] service [{}] has the wrong data format: [{}]".format(bq_name, num, srv))
|
||||
continue
|
||||
|
||||
s_type = srv_data[1]
|
||||
if s_type == "64":
|
||||
s_type = ServiceType(srv_data[1])
|
||||
if s_type is ServiceType.MARKER:
|
||||
m_data, sep, desc = srv.partition("#DESCRIPTION")
|
||||
services.append(BouquetService(desc.strip() if desc else "", BqServiceType.MARKER, srv, num))
|
||||
elif s_type == "832":
|
||||
elif s_type is ServiceType.SPACE:
|
||||
m_data, sep, desc = srv.partition("#DESCRIPTION")
|
||||
services.append(BouquetService(desc.strip() if desc else "", BqServiceType.SPACE, srv, num))
|
||||
elif s_type == "134":
|
||||
elif s_type is ServiceType.ALT:
|
||||
alt = re.match(BouquetsReader._ALT_PAT, srv)
|
||||
if alt:
|
||||
alt_name, alt_type = alt.group(1), alt.group(2)
|
||||
alt_bq_name, alt_srvs = BouquetsReader.get_bouquet(path, alt_name, alt_type, "alternatives")
|
||||
services.append(BouquetService(alt_bq_name, BqServiceType.ALT, alt_name, tuple(alt_srvs)))
|
||||
elif s_type is ServiceType.BOUQUET:
|
||||
sub = re.match(BouquetsReader._SUB_BQ_PAT, srv)
|
||||
if sub:
|
||||
sub_name, sub_type = sub.group(1), sub.group(2)
|
||||
sub_bq_name, sub_srvs = BouquetsReader.get_bouquet(path, sub_name, sub_type, "subbouquet")
|
||||
bq = Bouquet(sub_bq_name, sub_type, tuple(sub_srvs), None, None, sub_name)
|
||||
services.append(BouquetService(sub_bq_name, BqServiceType.BOUQUET, bq, num))
|
||||
elif srv_data[0].strip() in BouquetsReader._STREAM_TYPES or srv_data[10].startswith(("http", "rtsp")):
|
||||
stream_data, sep, desc = srv.partition("#DESCRIPTION")
|
||||
desc = desc.lstrip(":").strip() if desc else srv_data[-1].strip()
|
||||
|
||||
@@ -1,3 +1,31 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# The MIT License (MIT)
|
||||
#
|
||||
# Copyright (c) 2018-2021 Dmitriy Yefremov
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
# THE SOFTWARE.
|
||||
#
|
||||
# Author: Dmitriy Yefremov
|
||||
#
|
||||
|
||||
|
||||
""" This module used for parsing and write lamedb file """
|
||||
import re
|
||||
|
||||
@@ -283,7 +311,7 @@ class LameDbWriter:
|
||||
def write(self):
|
||||
if self._fmt == 4:
|
||||
# Writing lamedb file ver.4
|
||||
with open(self._path + _FILE_NAME, "w") as file:
|
||||
with open(self._path + _FILE_NAME, "w", encoding="utf-8") as file:
|
||||
file.writelines(LameDbReader.get_services_lines(self._services))
|
||||
elif self._fmt == 5:
|
||||
self.write_to_lamedb5()
|
||||
@@ -308,7 +336,7 @@ class LameDbWriter:
|
||||
lines.extend(services_lines)
|
||||
lines.append(_END_LINE)
|
||||
|
||||
with open(self._path + "lamedb5", "w") as file:
|
||||
with open(self._path + "lamedb5", "w", encoding="utf-8") as file:
|
||||
file.writelines(lines)
|
||||
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- Generated with glade 3.22.1
|
||||
<!-- Generated with glade 3.22.2
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
@@ -1291,7 +1291,7 @@ Author: Dmitriy Yefremov
|
||||
</packing>
|
||||
</child>
|
||||
<child type="title">
|
||||
<object class="GtkStackSwitcher">
|
||||
<object class="GtkStackSwitcher" id="stack_switcher">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="stack">stack</property>
|
||||
@@ -1309,10 +1309,6 @@ Author: Dmitriy Yefremov
|
||||
<object class="GtkPaned" id="main_paned">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="margin_left">5</property>
|
||||
<property name="margin_right">5</property>
|
||||
<property name="margin_top">5</property>
|
||||
<property name="margin_bottom">5</property>
|
||||
<property name="wide_handle">True</property>
|
||||
<child>
|
||||
<object class="GtkStack" id="stack">
|
||||
@@ -1382,6 +1378,10 @@ Author: Dmitriy Yefremov
|
||||
<object class="GtkFrame" id="services_frame">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_left">5</property>
|
||||
<property name="margin_right">5</property>
|
||||
<property name="margin_top">5</property>
|
||||
<property name="margin_bottom">5</property>
|
||||
<property name="label_xalign">0.49000000953674316</property>
|
||||
<property name="shadow_type">in</property>
|
||||
<child>
|
||||
@@ -2220,6 +2220,10 @@ Author: Dmitriy Yefremov
|
||||
<object class="GtkBox" id="satellite_box">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_left">5</property>
|
||||
<property name="margin_right">5</property>
|
||||
<property name="margin_top">5</property>
|
||||
<property name="margin_bottom">5</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<signal name="realize" handler="on_satellites_realize" swapped="no"/>
|
||||
<child>
|
||||
@@ -2236,6 +2240,10 @@ Author: Dmitriy Yefremov
|
||||
<object class="GtkBox" id="picon_box">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_left">5</property>
|
||||
<property name="margin_right">5</property>
|
||||
<property name="margin_top">5</property>
|
||||
<property name="margin_bottom">5</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<signal name="realize" handler="on_picons_realize" swapped="no"/>
|
||||
<child>
|
||||
@@ -2439,6 +2447,10 @@ Author: Dmitriy Yefremov
|
||||
<object class="GtkBox" id="ftp_box">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_left">5</property>
|
||||
<property name="margin_right">5</property>
|
||||
<property name="margin_top">5</property>
|
||||
<property name="margin_bottom">5</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<signal name="realize" handler="on_ftp_realize" swapped="no"/>
|
||||
<child>
|
||||
@@ -2455,6 +2467,10 @@ Author: Dmitriy Yefremov
|
||||
<object class="GtkBox" id="control_box">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_left">5</property>
|
||||
<property name="margin_right">5</property>
|
||||
<property name="margin_top">5</property>
|
||||
<property name="margin_bottom">5</property>
|
||||
<signal name="realize" handler="on_control_realize" swapped="no"/>
|
||||
<child>
|
||||
<placeholder/>
|
||||
@@ -2475,6 +2491,10 @@ Author: Dmitriy Yefremov
|
||||
<child>
|
||||
<object class="GtkPaned" id="fav_paned">
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_left">5</property>
|
||||
<property name="margin_right">5</property>
|
||||
<property name="margin_top">5</property>
|
||||
<property name="margin_bottom">5</property>
|
||||
<property name="wide_handle">True</property>
|
||||
<child>
|
||||
<object class="GtkFrame" id="fav_frame">
|
||||
@@ -3301,8 +3321,9 @@ Author: Dmitriy Yefremov
|
||||
<property name="height_request">28</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_top">1</property>
|
||||
<property name="margin_bottom">1</property>
|
||||
<property name="margin_left">10</property>
|
||||
<property name="margin_right">10</property>
|
||||
<property name="margin_bottom">5</property>
|
||||
<child>
|
||||
<object class="GtkBox" id="receiver_info_box">
|
||||
<property name="can_focus">False</property>
|
||||
|
||||
@@ -264,6 +264,7 @@ class Application(Gtk.Application):
|
||||
if main_window_size:
|
||||
self._main_window.resize(*main_window_size)
|
||||
|
||||
self._stack = builder.get_object("stack")
|
||||
self._fav_paned = builder.get_object("fav_paned")
|
||||
self._services_view = builder.get_object("services_tree_view")
|
||||
self._fav_view = builder.get_object("fav_tree_view")
|
||||
@@ -271,7 +272,6 @@ class Application(Gtk.Application):
|
||||
self._fav_model = builder.get_object("fav_list_store")
|
||||
self._services_model = builder.get_object("services_list_store")
|
||||
self._bouquets_model = builder.get_object("bouquets_tree_store")
|
||||
self._main_data_box = builder.get_object("main_data_box")
|
||||
self._status_bar_box = builder.get_object("status_bar_box")
|
||||
self._services_main_box = builder.get_object("services_main_box")
|
||||
self._bouquets_main_box = builder.get_object("bouquets_main_box")
|
||||
@@ -341,7 +341,6 @@ class Application(Gtk.Application):
|
||||
self._player_prev_button = builder.get_object("player_prev_button")
|
||||
self._player_next_button = builder.get_object("player_next_button")
|
||||
self._player_play_button = builder.get_object("player_play_button")
|
||||
self._fav_bouquets_paned = builder.get_object("fav_bouquets_paned")
|
||||
self._player_box.bind_property("visible", self._profile_combo_box, "visible", 4)
|
||||
self._player_box.bind_property("visible", self._player_event_box, "visible")
|
||||
self._fav_view.bind_property("sensitive", self._player_prev_button, "sensitive")
|
||||
@@ -1613,7 +1612,7 @@ class Application(Gtk.Application):
|
||||
|
||||
def append_bouquet(self, bq, parent):
|
||||
name, bq_type, locked, hidden = bq.name, bq.type, bq.locked, bq.hidden
|
||||
self._bouquets_model.append(parent, [name, locked, hidden, bq_type])
|
||||
bouquet = self._bouquets_model.append(parent, [name, locked, hidden, bq_type])
|
||||
bq_id = "{}:{}".format(name, bq_type)
|
||||
services = []
|
||||
extra_services = {} # for services with different names in bouquet and main list
|
||||
@@ -1643,6 +1642,11 @@ class Application(Gtk.Application):
|
||||
srv = Service(None, None, None, srv.name, locked, None, None, s_type.name,
|
||||
None, None, *agr, srv.data, fav_id, srv.num)
|
||||
self._services[fav_id] = srv
|
||||
elif s_type is BqServiceType.BOUQUET:
|
||||
# Sub bouquets!
|
||||
msg = "Detected sub-bouquets! This feature is not fully supported. Saving may cause bouquet data loss!"
|
||||
self.show_info_message(msg, Gtk.MessageType.WARNING)
|
||||
self.append_bouquet(srv.data, bouquet)
|
||||
elif srv.name:
|
||||
extra_services[fav_id] = srv.name
|
||||
services.append(fav_id)
|
||||
@@ -2518,6 +2522,7 @@ class Application(Gtk.Application):
|
||||
GLib.idle_add(self._player.play, url)
|
||||
|
||||
self._player_box.set_visible(True)
|
||||
self._stack.set_visible_child(self._player_box)
|
||||
|
||||
def on_player_stop(self, item=None):
|
||||
if self._player:
|
||||
@@ -2616,7 +2621,7 @@ class Application(Gtk.Application):
|
||||
|
||||
def on_player_box_visibility(self, box):
|
||||
visible = box.get_visible()
|
||||
self._fav_bouquets_paned.set_orientation(Gtk.Orientation.VERTICAL if visible else Gtk.Orientation.HORIZONTAL)
|
||||
self._fav_paned.set_orientation(Gtk.Orientation.VERTICAL if visible else Gtk.Orientation.HORIZONTAL)
|
||||
|
||||
@run_idle
|
||||
def set_player_area_size(self, widget):
|
||||
@@ -2644,10 +2649,8 @@ class Application(Gtk.Application):
|
||||
self._playback_window.fullscreen() if self._full_screen else self._playback_window.unfullscreen()
|
||||
|
||||
def update_state_on_full_screen(self, visible):
|
||||
self._main_data_box.set_visible(visible)
|
||||
self._player_tool_bar.set_visible(visible)
|
||||
if self._control_box:
|
||||
self._control_box.set_visible(visible)
|
||||
self._fav_paned.set_visible(visible)
|
||||
self._status_bar_box.set_visible(visible and not self._app_info_box.get_visible())
|
||||
|
||||
def on_main_window_state(self, window, event):
|
||||
|
||||
Reference in New Issue
Block a user