mirror of
https://github.com/DYefremov/DemonEditor.git
synced 2025-12-22 00:19:40 +01:00
bouquets gen improvement
This commit is contained in:
@@ -3248,7 +3248,7 @@ class Application(Gtk.Application):
|
|||||||
if self._s_type is not SettingsType.ENIGMA_2:
|
if self._s_type is not SettingsType.ENIGMA_2:
|
||||||
self.show_error_message("Not allowed in this context!")
|
self.show_error_message("Not allowed in this context!")
|
||||||
return
|
return
|
||||||
ServicesUpdateDialog(self._main_window, self._settings, self.on_import_data_from_web).show()
|
ServicesUpdateDialog(self).show()
|
||||||
|
|
||||||
@run_idle
|
@run_idle
|
||||||
def on_import_data_from_web(self, services, bouquets=None):
|
def on_import_data_from_web(self, services, bouquets=None):
|
||||||
@@ -3890,7 +3890,7 @@ class Application(Gtk.Application):
|
|||||||
return self.show_error_message("Data loading in progress!")
|
return self.show_error_message("Data loading in progress!")
|
||||||
|
|
||||||
model, paths = view.get_selection().get_selected_rows()
|
model, paths = view.get_selection().get_selected_rows()
|
||||||
if is_only_one_item_selected(paths, self._main_window):
|
if is_only_one_item_selected(paths, self):
|
||||||
model_name = get_base_model(model).get_name()
|
model_name = get_base_model(model).get_name()
|
||||||
if model_name == self.FAV_MODEL:
|
if model_name == self.FAV_MODEL:
|
||||||
srv_type = model.get_value(model.get_iter(paths), Column.FAV_TYPE)
|
srv_type = model.get_value(model.get_iter(paths), Column.FAV_TYPE)
|
||||||
@@ -4229,8 +4229,7 @@ class Application(Gtk.Application):
|
|||||||
self.show_error_message("No bouquets config is loaded. Load or create a new config!")
|
self.show_error_message("No bouquets config is loaded. Load or create a new config!")
|
||||||
return
|
return
|
||||||
|
|
||||||
gen_bouquets(self._services_view, self._bouquets_view, self._main_window, g_type, self._s_type,
|
gen_bouquets(self, g_type)
|
||||||
self.append_bouquet)
|
|
||||||
|
|
||||||
# ***************** Alternatives ********************* #
|
# ***************** Alternatives ********************* #
|
||||||
|
|
||||||
|
|||||||
@@ -39,8 +39,8 @@ import os
|
|||||||
import re
|
import re
|
||||||
import shutil
|
import shutil
|
||||||
import unicodedata
|
import unicodedata
|
||||||
from collections import defaultdict
|
|
||||||
from functools import lru_cache
|
from functools import lru_cache
|
||||||
|
from itertools import groupby
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from urllib.parse import unquote
|
from urllib.parse import unquote
|
||||||
|
|
||||||
@@ -546,13 +546,13 @@ def remove_picons(settings, picon_ids, picons):
|
|||||||
shutil.move(src, backup_path + p_id)
|
shutil.move(src, backup_path + p_id)
|
||||||
|
|
||||||
|
|
||||||
def is_only_one_item_selected(paths, transient):
|
def is_only_one_item_selected(paths, app):
|
||||||
if len(paths) > 1:
|
if len(paths) > 1:
|
||||||
show_dialog(DialogType.ERROR, transient, "Please, select only one item!")
|
app.show_error_message("Please, select only one item!")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
if not paths:
|
if not paths:
|
||||||
show_dialog(DialogType.ERROR, transient, "No selected item!")
|
app.show_error_message("No selected item!")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
return True
|
return True
|
||||||
@@ -574,47 +574,89 @@ def get_picon_file_name(service_name):
|
|||||||
|
|
||||||
# ***************** Bouquets ********************* #
|
# ***************** Bouquets ********************* #
|
||||||
|
|
||||||
def gen_bouquets(view, bq_view, transient, gen_type, s_type, callback):
|
def gen_bouquets(app, gen_type):
|
||||||
""" Auto-generate and append list of bouquets. """
|
""" Auto-generate and append list of bouquets. """
|
||||||
model, paths = view.get_selection().get_selected_rows()
|
model, paths = app.services_view.get_selection().get_selected_rows()
|
||||||
single_types = (BqGenType.SAT, BqGenType.PACKAGE, BqGenType.TYPE)
|
single_types = {BqGenType.SAT, BqGenType.PACKAGE, BqGenType.TYPE}
|
||||||
if gen_type in single_types:
|
if gen_type in single_types and not is_only_one_item_selected(paths, app):
|
||||||
if not is_only_one_item_selected(paths, transient):
|
return
|
||||||
return
|
|
||||||
|
|
||||||
fav_id_index = Column.SRV_FAV_ID
|
|
||||||
index = Column.SRV_TYPE
|
index = Column.SRV_TYPE
|
||||||
if gen_type in (BqGenType.PACKAGE, BqGenType.EACH_PACKAGE):
|
if gen_type in (BqGenType.PACKAGE, BqGenType.EACH_PACKAGE):
|
||||||
index = Column.SRV_PACKAGE
|
index = Column.SRV_PACKAGE
|
||||||
elif gen_type in (BqGenType.SAT, BqGenType.EACH_SAT):
|
elif gen_type in (BqGenType.SAT, BqGenType.EACH_SAT):
|
||||||
index = Column.SRV_POS
|
index = Column.SRV_POS
|
||||||
|
|
||||||
# Splitting services [caching] by column value.
|
ids = {row[Column.SRV_FAV_ID] for row in model}
|
||||||
s_data = defaultdict(list)
|
services = [v for k, v in app.current_services.items() if k in ids]
|
||||||
for row in model:
|
|
||||||
s_data[row[index]].append(BouquetService(None, BqServiceType.DEFAULT, row[fav_id_index], 0))
|
|
||||||
|
|
||||||
bq_type = BqType.BOUQUET.value if s_type is SettingsType.NEUTRINO_MP else BqType.TV.value
|
|
||||||
bq_index = 0 if s_type is SettingsType.ENIGMA_2 else 1
|
|
||||||
bq_root_iter = bq_view.get_model().get_iter(bq_index)
|
|
||||||
srv = Service(*model[paths][:Column.SRV_TOOLTIP])
|
srv = Service(*model[paths][:Column.SRV_TOOLTIP])
|
||||||
cond = srv.package if gen_type is BqGenType.PACKAGE else srv.pos if gen_type is BqGenType.SAT else srv.service_type
|
cond = srv.package if gen_type is BqGenType.PACKAGE else srv.pos if gen_type is BqGenType.SAT else srv.service_type
|
||||||
bq_view.expand_row(Gtk.TreePath(bq_index), 0)
|
|
||||||
|
if gen_type is BqGenType.TYPE and cond == "Data":
|
||||||
|
msg = f"{get_message('Selected type:')} '{cond}'\n\n{get_message('Are you sure?')}"
|
||||||
|
if show_dialog(DialogType.QUESTION, app.app_window, msg) != Gtk.ResponseType.OK:
|
||||||
|
return
|
||||||
|
|
||||||
|
def grouper(s):
|
||||||
|
data = s[index]
|
||||||
|
return data if data else "None"
|
||||||
|
|
||||||
|
services = {k: list(v) for k, v in groupby(sorted(services, key=grouper), key=grouper)}
|
||||||
|
|
||||||
|
bq_view = app.bouquets_view
|
||||||
|
bq_type = BqType.TV.value if app.is_enigma else BqType.BOUQUET.value
|
||||||
|
bq_index = 0 if app.is_enigma else 1
|
||||||
|
bq_root_iter = bq_view.get_model().get_iter(bq_index)
|
||||||
|
|
||||||
bq_names = get_bouquets_names(bq_view.get_model())
|
bq_names = get_bouquets_names(bq_view.get_model())
|
||||||
|
|
||||||
if gen_type in single_types:
|
if gen_type in single_types:
|
||||||
if cond in bq_names:
|
if cond in bq_names:
|
||||||
show_dialog(DialogType.ERROR, transient, "A bouquet with that name exists!")
|
app.show_error_message("A bouquet with that name exists!")
|
||||||
else:
|
return
|
||||||
callback(Bouquet(cond, bq_type, s_data.get(cond)), bq_root_iter)
|
|
||||||
|
bq_services = get_services_type_groups(services.get(cond, []))
|
||||||
|
if app.is_enigma:
|
||||||
|
if srv.service_type == "Radio":
|
||||||
|
bq_index = 1
|
||||||
|
bq_type = BqType.RADIO.value
|
||||||
|
bq_root_iter = bq_view.get_model().get_iter(bq_index)
|
||||||
|
bq_view.expand_row(Gtk.TreePath(bq_index), 1)
|
||||||
|
bq_services = bq_services.get("Radio", [])
|
||||||
|
else:
|
||||||
|
bq_view.expand_row(Gtk.TreePath(bq_index), 0)
|
||||||
|
bq_services = bq_services.get("Data" if srv.service_type == "Data" else "TV", [])
|
||||||
|
app.append_bouquet(Bouquet(cond, bq_type, get_bouquet_services(bq_services)), bq_root_iter)
|
||||||
else:
|
else:
|
||||||
|
bq_view.expand_row(Gtk.TreePath(bq_index), 0)
|
||||||
# We add a bouquet only if the given name is missing [keys - names]!
|
# We add a bouquet only if the given name is missing [keys - names]!
|
||||||
if gen_type is BqGenType.EACH_SAT:
|
if gen_type is BqGenType.EACH_SAT:
|
||||||
bq_names = sorted(s_data.keys() - bq_names, key=get_pos_num, reverse=True)
|
bq_names = sorted(services.keys() - bq_names, key=get_pos_num, reverse=True)
|
||||||
else:
|
else:
|
||||||
bq_names = sorted(s_data.keys() - bq_names)
|
bq_names = sorted(services.keys() - bq_names)
|
||||||
[callback(Bouquet(name, BqType.TV.value, s_data.get(name)), bq_root_iter) for name in bq_names]
|
|
||||||
|
tv_bqs = []
|
||||||
|
radio_bqs = []
|
||||||
|
for n in bq_names:
|
||||||
|
bqs = services.get(n, [])
|
||||||
|
# TV and Radio separation.
|
||||||
|
bq_grp = get_services_type_groups(bqs)
|
||||||
|
tv_bq = bq_grp.get("TV", [])
|
||||||
|
tv_bqs.append(Bouquet(n, BqType.TV.value, get_bouquet_services(tv_bq))) if tv_bq else None
|
||||||
|
radio_bq = bq_grp.get("Radio", [])
|
||||||
|
radio_bqs.append(Bouquet(n, BqType.RADIO.value, get_bouquet_services(radio_bq))) if radio_bq else None
|
||||||
|
|
||||||
|
[app.append_bouquet(b, bq_root_iter) for b in tv_bqs]
|
||||||
|
if app.is_enigma:
|
||||||
|
bq_root_iter = bq_view.get_model().get_iter(bq_index + 1)
|
||||||
|
bq_view.expand_row(Gtk.TreePath(bq_index + 1), 0)
|
||||||
|
[app.append_bouquet(b, bq_root_iter) for b in radio_bqs]
|
||||||
|
|
||||||
|
|
||||||
|
def get_bouquet_services(services):
|
||||||
|
services.sort(key=lambda s: s.service)
|
||||||
|
return [BouquetService(None, BqServiceType.DEFAULT, s.fav_id, 0) for s in services]
|
||||||
|
|
||||||
|
|
||||||
def get_bouquets_names(model):
|
def get_bouquets_names(model):
|
||||||
@@ -630,12 +672,28 @@ def get_bouquets_names(model):
|
|||||||
return bouquets_names
|
return bouquets_names
|
||||||
|
|
||||||
|
|
||||||
|
def get_services_type_groups(services):
|
||||||
|
""" Returns services grouped by main types [TV, Radio, Data]. -> dict """
|
||||||
|
|
||||||
|
def type_grouper(s):
|
||||||
|
s_type = s.service_type
|
||||||
|
|
||||||
|
if s_type == "Data":
|
||||||
|
return s_type
|
||||||
|
elif s_type == "Radio":
|
||||||
|
return s_type
|
||||||
|
else:
|
||||||
|
return "TV"
|
||||||
|
|
||||||
|
return {k: list(v) for k, v in groupby(sorted(services, key=type_grouper), key=type_grouper)}
|
||||||
|
|
||||||
|
|
||||||
# ***************** Others ********************* #
|
# ***************** Others ********************* #
|
||||||
|
|
||||||
def copy_reference(view, app):
|
def copy_reference(view, app):
|
||||||
""" Copying picon id to clipboard. """
|
""" Copying picon id to clipboard. """
|
||||||
model, paths = view.get_selection().get_selected_rows()
|
model, paths = view.get_selection().get_selected_rows()
|
||||||
if not is_only_one_item_selected(paths, app.app_window):
|
if not is_only_one_item_selected(paths, app):
|
||||||
return
|
return
|
||||||
|
|
||||||
target = app.get_target_view(view)
|
target = app.get_target_view(view)
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ from app.eparser.satxml import get_pos_str
|
|||||||
from app.settings import USE_HEADER_BAR, Settings, CONFIG_PATH
|
from app.settings import USE_HEADER_BAR, Settings, CONFIG_PATH
|
||||||
from app.tools.satellites import SatellitesParser, SatelliteSource, ServicesParser
|
from app.tools.satellites import SatellitesParser, SatelliteSource, ServicesParser
|
||||||
from ..dialogs import show_dialog, DialogType, get_message, get_builder
|
from ..dialogs import show_dialog, DialogType, get_message, get_builder
|
||||||
from ..main_helper import append_text_to_tview, get_base_model, on_popup_menu
|
from ..main_helper import append_text_to_tview, get_base_model, on_popup_menu, get_services_type_groups
|
||||||
from ..search import SearchProvider
|
from ..search import SearchProvider
|
||||||
from ..uicommons import Gtk, Gdk, UI_RESOURCES_PATH, HeaderBar
|
from ..uicommons import Gtk, Gdk, UI_RESOURCES_PATH, HeaderBar
|
||||||
|
|
||||||
@@ -814,10 +814,10 @@ class SatellitesUpdateDialog(UpdateDialog):
|
|||||||
class ServicesUpdateDialog(UpdateDialog):
|
class ServicesUpdateDialog(UpdateDialog):
|
||||||
""" Dialog for updating services from the Web. """
|
""" Dialog for updating services from the Web. """
|
||||||
|
|
||||||
def __init__(self, transient, settings, callback):
|
def __init__(self, app):
|
||||||
super().__init__(transient=transient, settings=settings, title="Services update")
|
super().__init__(transient=app.app_window, settings=app.app_settings, title="Services update")
|
||||||
|
|
||||||
self._callback = callback
|
self._callback = app.on_import_data_from_web
|
||||||
self._satellite_paths = {}
|
self._satellite_paths = {}
|
||||||
self._transponders = {}
|
self._transponders = {}
|
||||||
self._services = {}
|
self._services = {}
|
||||||
@@ -956,7 +956,7 @@ class ServicesUpdateDialog(UpdateDialog):
|
|||||||
else:
|
else:
|
||||||
bouquets = None
|
bouquets = None
|
||||||
if self._source_box.get_active_id() == SatelliteSource.KINGOFSAT.name:
|
if self._source_box.get_active_id() == SatelliteSource.KINGOFSAT.name:
|
||||||
bouquets = self.get_bouquets(srvs, services)
|
bouquets = self.get_bouquets([srv._replace(fav_id=srvs[i].fav_id) for i, srv in enumerate(services)])
|
||||||
|
|
||||||
def c_filter(s):
|
def c_filter(s):
|
||||||
try:
|
try:
|
||||||
@@ -968,21 +968,30 @@ class ServicesUpdateDialog(UpdateDialog):
|
|||||||
|
|
||||||
self.is_download = False
|
self.is_download = False
|
||||||
|
|
||||||
def get_bouquets(self, prepared, services):
|
def get_bouquets(self, services):
|
||||||
bouquets = []
|
type_groups = get_services_type_groups(services)
|
||||||
services = [srv._replace(fav_id=prepared[i].fav_id) for i, srv in enumerate(services)]
|
tv_bouquets, radio_bouquets = [], []
|
||||||
|
|
||||||
|
tv_services = sorted(type_groups.get("TV", []), key=lambda s: s.service)
|
||||||
|
rd_services = sorted(type_groups.get("Radio", []), key=lambda s: s.service)
|
||||||
|
no_lb = "No Category"
|
||||||
|
|
||||||
if self._kos_bq_groups_switch.get_active():
|
if self._kos_bq_groups_switch.get_active():
|
||||||
self.gen_bouquet_group(services, bouquets, lambda s: s[4] or "")
|
self.gen_bouquet_group(tv_services, tv_bouquets, lambda s: s[4] or no_lb)
|
||||||
|
self.gen_bouquet_group(rd_services, radio_bouquets, lambda s: s[4] or no_lb, bq_type=BqType.RADIO.value)
|
||||||
|
|
||||||
if self._kos_bq_lang_switch.get_active():
|
if self._kos_bq_lang_switch.get_active():
|
||||||
self.gen_bouquet_group(services, bouquets, lambda s: s[5] or "")
|
lb = "" if no_lb in {b.name for b in tv_bouquets} else "No Region"
|
||||||
|
self.gen_bouquet_group(tv_services, tv_bouquets, lambda s: s[5] or lb)
|
||||||
|
lb = "" if no_lb in {b.name for b in radio_bouquets} else "No Region"
|
||||||
|
self.gen_bouquet_group(rd_services, radio_bouquets, lambda s: s[5] or lb, bq_type=BqType.RADIO.value)
|
||||||
|
|
||||||
return Bouquets("", BqType.TV.value, bouquets),
|
return Bouquets("", BqType.TV.value, tv_bouquets), Bouquets("", BqType.RADIO.value, radio_bouquets)
|
||||||
|
|
||||||
def gen_bouquet_group(self, services, bouquets, grouper):
|
def gen_bouquet_group(self, services, bouquets, grouper, bq_type=BqType.TV.value):
|
||||||
""" Generates bouquets depending on <grouper>. """
|
""" Generates bouquets depending on <grouper>. """
|
||||||
s_type = BqServiceType.DEFAULT
|
s_type = BqServiceType.DEFAULT
|
||||||
[bouquets.append(Bouquet(name=g[0], type=BqType.TV.name,
|
[bouquets.append(Bouquet(name=g[0], type=bq_type,
|
||||||
services=[BouquetService(None, s_type, s.fav_id, 0) for s in g[1]])) for g in
|
services=[BouquetService(None, s_type, s.fav_id, 0) for s in g[1]])) for g in
|
||||||
groupby(sorted(services, key=grouper), key=grouper) if g[0]]
|
groupby(sorted(services, key=grouper), key=grouper) if g[0]]
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user