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 @@
-
+
180
0.10
@@ -324,6 +392,53 @@ Author: Dmitriy Yefremov
0
+
+
+
+ False
+ True
+ 1
+
+
False