From 95a1732f01528d34f84c5611ff8ecae482fcd904 Mon Sep 17 00:00:00 2001 From: DYefremov Date: Thu, 6 Apr 2023 00:24:45 +0300 Subject: [PATCH] satellites merge support for web import (#165) --- app/eparser/enigma/lamedb.py | 4 +- app/eparser/satxml.py | 7 +- app/ui/xml/dialogs.py | 101 ++++++--- app/ui/xml/edit.py | 15 +- app/ui/xml/editor.glade | 386 +++++++++++++++++------------------ app/ui/xml/update.glade | 115 +++++++++++ 6 files changed, 399 insertions(+), 229 deletions(-) diff --git a/app/eparser/enigma/lamedb.py b/app/eparser/enigma/lamedb.py index 9ad4b91d..2ba3e283 100644 --- a/app/eparser/enigma/lamedb.py +++ b/app/eparser/enigma/lamedb.py @@ -30,6 +30,7 @@ import re from app.commons import log +from app.eparser.satxml import get_pos_str from app.ui.uicommons import CODED_ICON, LOCKED_ICON, HIDE_ICON from .blacklist import get_blacklist from ..ecommons import Service, POLARIZATION, FEC, SERVICE_TYPE, Flag, T_FEC, TrType, FEC_DEFAULT, T_SYSTEM @@ -220,8 +221,7 @@ class LameDbReader: freq = f"{int(freq) // 1000}" rate = f"{int(rate) // 1000}" if tr_type is TrType.Satellite: - pos = int(pos) - pos = f"{abs(pos / 10):0.1f}{'W' if pos < 0 else 'E'}" + pos = get_pos_str(int(pos)) except ValueError as e: log(f"Parse error [parse_services]: {e}") diff --git a/app/eparser/satxml.py b/app/eparser/satxml.py index 322a9350..e4761ef9 100644 --- a/app/eparser/satxml.py +++ b/app/eparser/satxml.py @@ -2,7 +2,7 @@ # # The MIT License (MIT) # -# Copyright (c) 2018-2022 Dmitriy Yefremov +# Copyright (c) 2018-2023 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 @@ -192,5 +192,10 @@ def indent(elem, parent=None, index=-1, level=0, space=" "): elem.tail = f"\n{space * (level - 1)}" +def get_pos_str(pos: int) -> str: + """ Converts satellite position int value to readable string. """ + return f"{abs(pos / 10):0.1f}{'W' if pos < 0 else 'E'}" + + if __name__ == "__main__": pass diff --git a/app/ui/xml/dialogs.py b/app/ui/xml/dialogs.py index df13ee2b..4c21ac59 100644 --- a/app/ui/xml/dialogs.py +++ b/app/ui/xml/dialogs.py @@ -42,6 +42,7 @@ from app.eparser.ecommons import (PLS_MODE, get_key_by_value, POLARIZATION, FEC, T_SYSTEM, BANDWIDTH, CONSTELLATION, T_FEC, GUARD_INTERVAL, TRANSMISSION_MODE, HIERARCHY, Inversion, C_MODULATION, FEC_DEFAULT, TerTransponder, CableTransponder, Bouquet, BouquetService, BqServiceType, Bouquets, BqType) +from app.eparser.satxml import get_pos_str from app.settings import USE_HEADER_BAR from app.tools.satellites import SatellitesParser, SatelliteSource, ServicesParser from ..dialogs import show_dialog, DialogType, get_message, get_builder @@ -463,6 +464,9 @@ class UpdateDialog: builder.get_object("sat_update_find_button").connect("toggled", search_provider.on_search_toggled) # Satellite lists init on dialog start. self._sat_view.connect("realize", self.on_update_satellites_list) + # Options. + self._general_options_box = builder.get_object("general_options_box") + self._skip_c_band_switch = builder.get_object("skip_c_band_switch") if self._settings.use_header_bar: header_bar = HeaderBar() @@ -644,6 +648,15 @@ class SatellitesUpdateDialog(UpdateDialog): self._main_model = main_model self._source_box.connect("changed", self.on_update_satellites_list) + # Options. + self._merge_sat_switch = Gtk.Switch() + box = Gtk.Box(spacing=5, orientation=Gtk.Orientation.HORIZONTAL) + box.pack_start(Gtk.Label(get_message("Merge satellites by positions")), False, True, 0) + box.pack_end(self._merge_sat_switch, False, True, 0) + self._general_options_box.pack_start(box, True, True, 0) + self._general_options_box.show_all() + + self._skip_c_band_switch.get_parent().set_visible(False) @run_idle def on_receive_data(self, item): @@ -659,6 +672,7 @@ class SatellitesUpdateDialog(UpdateDialog): self.update_log_visibility() model = self._sat_view.get_model() start = time.time() + _len = 75 with concurrent.futures.ProcessPoolExecutor(max_workers=4) as executor: text = "Processing: {}\n" @@ -678,10 +692,39 @@ class SatellitesUpdateDialog(UpdateDialog): appender.send(text.format(data[0])) sats.append(data) - appender.send("-" * 75 + "\n") + appender.send("-" * _len + "\n") sat_count = len(sats) - sats = {s[0]: s for s in sats} # key = name, v = satellite + if self._merge_sat_switch.get_active(): + def grouper(sat): + try: + return int(sat.position) + except ValueError: + pass + return 0 + + sat_groups = groupby(sorted(sats, key=grouper, reverse=True), key=grouper) + sats = {} + for pos, satellites in sat_groups: + satellites = list(satellites) + if len(satellites) > 1: + position = get_pos_str(pos) + appender.send(f"Merging satellites for position: {position}\n") + names = [] + transponders = [] + for s in satellites: + names.append(s.name.lstrip(position).strip().split()) + transponders.extend(s.transponders) + + transponders.sort(key=lambda t: int(t.frequency)) + sat = Satellite(self.get_grouped_satellite_name(names, pos), "0", str(pos), transponders) + sats[sat.name] = sat + else: + sat = satellites.pop() + sats[sat.name] = sat + appender.send("-" * _len + "\n") + else: + sats = {s.name: s for s in sats} # key = name, v = satellite for row in self._main_model: pos = row[0] @@ -694,11 +737,39 @@ class SatellitesUpdateDialog(UpdateDialog): appender.send(f"Adding satellite: {s.name}\n") self.append_satellite(s) - appender.send("-" * 75 + "\n") + appender.send("-" * _len + "\n") appender.send(f"Consumed: {time.time() - start:0.0f}s, {sat_count} satellites received.\n") appender.close() self.is_download = False + def get_grouped_satellite_name(self, sat_names, pos): + """ Forms name for merged satellites. """ + + def name_grouper(nd): + if nd: + return nd[0] + return "" + + name_groups = groupby(sorted(sat_names, key=name_grouper), key=name_grouper) + names = [] + for s, s_names in name_groups: + tk = set() + name = s + for i, n_data in enumerate(s_names): + if i == 0: + name = " ".join(n_data) + tk.update(n_data) + else: + for n in n_data: + if n in tk: + continue + name = f"{name}/{n}" + tk.add(n) + + names.append(name) + + return f"{pos} {' & '.join(names)}" + @run_idle def append_satellite(self, sat): self._main_model.append(sat) @@ -736,8 +807,6 @@ class ServicesUpdateDialog(UpdateDialog): self._source_box.connect("changed", self.on_update_satellites_list) self._source_box.connect("changed", self.on_source_changed) # Options for KingOfSat source. - popover = Gtk.Popover() - main_options_box = Gtk.Box(spacing=5, margin_left=10, margin_right=10, orientation=Gtk.Orientation.VERTICAL) self._kos_bq_groups_switch = Gtk.Switch() self._kos_bq_lang_switch = Gtk.Switch() self._kos_options_box = Gtk.Box(spacing=5, orientation=Gtk.Orientation.VERTICAL) @@ -749,26 +818,8 @@ class ServicesUpdateDialog(UpdateDialog): box.pack_start(Gtk.Label(get_message("Create Regional bouquets")), False, True, 0) box.pack_end(self._kos_bq_lang_switch, False, True, 0) self._kos_options_box.add(box) - main_options_box.add(self._kos_options_box) - # General options. - self._general_options_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) - self._skip_c_band_switch = Gtk.Switch() - box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=5, margin_top=5) - box.pack_start(Gtk.Label(get_message("Skip C-band")), False, True, 0) - box.pack_end(self._skip_c_band_switch, False, True, 0) - self._general_options_box.add(box) - main_options_box.add(self._general_options_box) - main_options_box.add(Gtk.ModelButton(get_message("Close"), margin_bottom=5)) - main_options_box.show_all() - popover.add(main_options_box) - # Options button. - option_button_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=5) - option_button_box.add(Gtk.Image.new_from_icon_name("applications-system-symbolic", Gtk.IconSize.BUTTON)) - option_button_box.add(Gtk.Label(get_message("Options"))) - menu_button = Gtk.MenuButton(popover=popover) - menu_button.add(option_button_box) - menu_button.show_all() - self._right_action_box.pack_end(menu_button, False, False, 0) + self._general_options_box.pack_start(self._kos_options_box, True, True, 0) + self._general_options_box.show_all() @run_idle def on_receive_data(self, item): diff --git a/app/ui/xml/edit.py b/app/ui/xml/edit.py index f4ae6c13..6b732a1b 100644 --- a/app/ui/xml/edit.py +++ b/app/ui/xml/edit.py @@ -2,7 +2,7 @@ # # The MIT License (MIT) # -# Copyright (c) 2018-2022 Dmitriy Yefremov +# Copyright (c) 2018-2023 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 @@ -37,9 +37,9 @@ from app.eparser import get_satellites, write_satellites, Satellite, Transponder from app.eparser.ecommons import (POLARIZATION, FEC, SYSTEM, MODULATION, T_SYSTEM, BANDWIDTH, CONSTELLATION, T_FEC, GUARD_INTERVAL, TRANSMISSION_MODE, HIERARCHY, Inversion, FEC_DEFAULT, C_MODULATION, Terrestrial, Cable, CableTransponder, TerTransponder) -from app.eparser.satxml import get_terrestrial, get_cable, write_terrestrial, write_cable -from .dialogs import SatelliteDialog, SatellitesUpdateDialog, TerrestrialDialog, CableDialog, SatTransponderDialog, \ - CableTransponderDialog, TerTransponderDialog +from app.eparser.satxml import get_terrestrial, get_cable, write_terrestrial, write_cable, get_pos_str +from .dialogs import (SatelliteDialog, SatellitesUpdateDialog, TerrestrialDialog, CableDialog, SatTransponderDialog, + CableTransponderDialog, TerTransponderDialog) from ..dialogs import show_dialog, DialogType, get_chooser_dialog, get_message, get_builder from ..main_helper import move_items, on_popup_menu, scroll_to from ..uicommons import Gtk, Gdk, UI_RESOURCES_PATH, MOVE_KEYS, KeyboardKey, MOD_MASK, Page @@ -56,8 +56,8 @@ class SatellitesTool(Gtk.Box): def __str__(self): return self.value - def __init__(self, app, settings, *args, **kwargs): - super().__init__(*args, **kwargs) + def __init__(self, app, settings, **kwargs): + super().__init__(**kwargs) self._app = app self._app.connect("data-save", self.on_save) @@ -162,8 +162,7 @@ class SatellitesTool(Gtk.Box): def sat_pos_func(self, column, renderer, model, itr, data): """ Converts and sets the satellite position value to a readable format. """ - pos = int(model.get_value(itr, 2)) - renderer.set_property("text", f"{abs(pos / 10):0.1f}{'W' if pos < 0 else 'E'}") + renderer.set_property("text", get_pos_str(int(model.get_value(itr, 2)))) def sat_pol_func(self, column, renderer, model, itr, data): renderer.set_property("text", POLARIZATION.get(model.get_value(itr, 2), None)) diff --git a/app/ui/xml/editor.glade b/app/ui/xml/editor.glade index fc3b10f3..6b5c63f4 100644 --- a/app/ui/xml/editor.glade +++ b/app/ui/xml/editor.glade @@ -1,5 +1,5 @@ - + + False + + + True + False + 10 + 10 + 5 + 5 + vertical + 5 + + + True + True + True + Close + True + + + False + True + end + 0 + + + + + True + False + 5 + + + True + False + Skip C-band + + + False + True + 0 + + + + + True + True + center + + + False + True + end + 1 + + + + + False + True + end + 1 + + + + + 180 0.10 @@ -324,6 +392,53 @@ Author: Dmitriy Yefremov 0 + + + True + True + True + none + options_popover + + + True + False + 2 + + + True + False + Options + applications-system-symbolic + 1 + + + False + True + 0 + + + + + True + False + Options + + + False + True + 1 + + + + + + + False + True + 1 + + False