2021-05-11 00:18:27 +03:00
|
|
|
# -*- 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
|
|
|
|
|
#
|
|
|
|
|
|
|
|
|
|
|
2017-11-13 00:21:52 +03:00
|
|
|
import os
|
2018-12-01 13:34:26 +03:00
|
|
|
import sys
|
2017-11-14 19:20:16 +03:00
|
|
|
from contextlib import suppress
|
2020-01-08 21:33:24 +03:00
|
|
|
from datetime import datetime
|
2017-11-30 00:45:52 +03:00
|
|
|
from functools import lru_cache
|
2019-05-13 14:42:23 +03:00
|
|
|
from itertools import chain
|
2020-09-19 12:32:08 +03:00
|
|
|
from urllib.parse import urlparse, unquote
|
2017-11-13 00:21:52 +03:00
|
|
|
|
2021-08-31 14:16:14 +03:00
|
|
|
from gi.repository import GLib, Gio, GObject
|
2018-08-18 10:21:40 +03:00
|
|
|
|
2019-05-12 16:26:58 +03:00
|
|
|
from app.commons import run_idle, log, run_task, run_with_delay, init_logger
|
2020-11-11 15:34:12 +03:00
|
|
|
from app.connections import (HttpAPI, download_data, DownloadType, upload_data, test_http, TestException,
|
|
|
|
|
HttpApiException, STC_XML_FILE)
|
2021-05-12 14:59:55 +03:00
|
|
|
from app.eparser import get_blacklist, write_blacklist, write_bouquet
|
2018-01-01 23:42:40 +03:00
|
|
|
from app.eparser import get_services, get_bouquets, write_bouquets, write_services, Bouquets, Bouquet, Service
|
2019-04-18 21:43:35 +03:00
|
|
|
from app.eparser.ecommons import CAS, Flag, BouquetService
|
2019-03-14 12:37:48 +03:00
|
|
|
from app.eparser.enigma.bouquets import BqServiceType
|
2019-04-18 21:43:35 +03:00
|
|
|
from app.eparser.iptv import export_to_m3u
|
2018-02-11 23:14:22 +03:00
|
|
|
from app.eparser.neutrino.bouquets import BqType
|
2021-09-13 16:52:19 +03:00
|
|
|
from app.settings import (SettingsType, Settings, SettingsException, SettingsReadException,
|
|
|
|
|
IS_DARWIN, PlayStreamsMode)
|
|
|
|
|
from app.tools.media import Recorder
|
2021-09-02 12:20:29 +03:00
|
|
|
from app.ui.control import ControlBox, EpgBox, TimersBox, RecordingsBox
|
2021-09-15 13:12:25 +03:00
|
|
|
from app.ui.epg import EpgDialog
|
2021-08-15 14:37:21 +03:00
|
|
|
from app.ui.ftp import FtpClientBox
|
2021-09-13 16:52:19 +03:00
|
|
|
from app.ui.playback import PlayerBox
|
2019-11-05 23:04:21 +03:00
|
|
|
from app.ui.transmitter import LinksTransmitter
|
2019-02-05 16:58:54 +03:00
|
|
|
from .backup import BackupDialog, backup_data, clear_data_path
|
2021-04-28 14:12:59 +03:00
|
|
|
from .dialogs import show_dialog, DialogType, get_chooser_dialog, WaitDialog, get_message, get_builder
|
2018-11-06 21:21:47 +03:00
|
|
|
from .download_dialog import DownloadDialog
|
2019-12-27 23:05:37 +03:00
|
|
|
from .imports import ImportDialog, import_bouquet
|
2021-01-31 16:27:35 +03:00
|
|
|
from .iptv import IptvDialog, SearchUnavailableDialog, IptvListConfigurationDialog, YtListImportDialog, M3uImportDialog
|
2020-03-28 17:56:39 +03:00
|
|
|
from .main_helper import (insert_marker, move_items, rename, ViewTarget, set_flags, locate_in_services,
|
2020-06-06 09:36:11 +03:00
|
|
|
scroll_to, get_base_model, update_picons_data, copy_picon_reference, assign_picons,
|
2021-09-13 16:52:19 +03:00
|
|
|
remove_picon, is_only_one_item_selected, gen_bouquets, BqGenType, append_picons,
|
|
|
|
|
get_selection, get_model_data, remove_all_unused_picons, get_picon_pixbuf, get_base_itrs,
|
|
|
|
|
get_iptv_url)
|
2021-08-15 15:42:27 +03:00
|
|
|
from .picons import PiconManager
|
2021-08-15 14:37:21 +03:00
|
|
|
from .satellites import SatellitesTool, ServicesUpdateDialog
|
2019-12-27 23:05:37 +03:00
|
|
|
from .search import SearchProvider
|
2018-03-10 17:49:53 +03:00
|
|
|
from .service_details_dialog import ServiceDetailsDialog, Action
|
2019-12-27 23:05:37 +03:00
|
|
|
from .settings_dialog import show_settings_dialog
|
2020-03-28 17:56:39 +03:00
|
|
|
from .uicommons import (Gtk, Gdk, UI_RESOURCES_PATH, LOCKED_ICON, HIDE_ICON, IPTV_ICON, MOVE_KEYS, KeyboardKey, Column,
|
2021-08-20 17:24:48 +03:00
|
|
|
FavClickMode, MOD_MASK, APP_FONT, Page, IS_GNOME_SESSION)
|
2017-11-09 19:01:09 +03:00
|
|
|
|
|
|
|
|
|
2018-12-01 13:34:26 +03:00
|
|
|
class Application(Gtk.Application):
|
2020-04-19 13:23:18 +03:00
|
|
|
SERVICE_MODEL_NAME = "services_list_store"
|
|
|
|
|
FAV_MODEL_NAME = "fav_list_store"
|
|
|
|
|
BQ_MODEL_NAME = "bouquets_tree_store"
|
2021-01-08 23:01:16 +03:00
|
|
|
ALT_MODEL_NAME = "alt_list_store"
|
2020-11-19 18:05:40 +03:00
|
|
|
DRAG_SEP = "::::"
|
2020-04-19 13:23:18 +03:00
|
|
|
|
2021-07-25 22:33:00 +03:00
|
|
|
DEL_FACTOR = 100 # Batch size to delete in one pass.
|
|
|
|
|
FAV_FACTOR = DEL_FACTOR * 5
|
2020-04-28 11:23:22 +03:00
|
|
|
|
2018-03-03 20:55:08 +03:00
|
|
|
_TV_TYPES = ("TV", "TV (HD)", "TV (UHD)", "TV (H264)")
|
|
|
|
|
|
2018-12-11 14:10:44 +03:00
|
|
|
# Dynamically active elements depending on the selected view
|
2019-05-13 14:42:23 +03:00
|
|
|
_SERVICE_ELEMENTS = ("services_to_fav_end_move_popup_item", "services_to_fav_move_popup_item",
|
|
|
|
|
"services_create_bouquet_popup_item", "services_copy_popup_item", "services_edit_popup_item",
|
|
|
|
|
"services_add_new_popup_item", "services_picon_popup_item", "services_remove_popup_item")
|
2019-02-23 13:54:00 +03:00
|
|
|
|
2018-07-09 19:00:05 +03:00
|
|
|
_FAV_ELEMENTS = ("fav_cut_popup_item", "fav_paste_popup_item", "fav_locate_popup_item", "fav_iptv_popup_item",
|
2020-06-12 22:37:29 +03:00
|
|
|
"fav_insert_marker_popup_item", "fav_insert_space_popup_item", "fav_edit_sub_menu_popup_item",
|
2021-04-19 13:15:36 +03:00
|
|
|
"fav_edit_popup_item", "fav_picon_popup_item", "fav_copy_popup_item", "fav_add_alt_popup_item",
|
|
|
|
|
"fav_epg_configuration_popup_item", "fav_mark_dup_popup_item")
|
2019-02-23 13:54:00 +03:00
|
|
|
|
2018-12-17 18:31:57 +03:00
|
|
|
_BOUQUET_ELEMENTS = ("bouquets_new_popup_item", "bouquets_edit_popup_item", "bouquets_cut_popup_item",
|
2019-05-29 14:31:44 +03:00
|
|
|
"bouquets_copy_popup_item", "bouquets_paste_popup_item", "new_header_button",
|
|
|
|
|
"bouquet_import_popup_item")
|
2019-02-23 13:54:00 +03:00
|
|
|
|
2019-05-29 14:31:44 +03:00
|
|
|
_COMMONS_ELEMENTS = ("bouquets_remove_popup_item", "fav_remove_popup_item", "import_bq_menu_button")
|
2019-02-23 13:54:00 +03:00
|
|
|
|
2021-09-03 18:09:26 +03:00
|
|
|
_FAV_ENIGMA_ELEMENTS = ("fav_insert_marker_popup_item", "fav_epg_configuration_popup_item")
|
2019-02-23 13:54:00 +03:00
|
|
|
|
2019-05-29 14:31:44 +03:00
|
|
|
_FAV_IPTV_ELEMENTS = ("fav_iptv_popup_item", "import_m3u_header_button", "export_to_m3u_header_button",
|
2021-09-03 18:09:26 +03:00
|
|
|
"iptv_menu_button")
|
2019-02-23 13:54:00 +03:00
|
|
|
|
2021-09-04 14:46:11 +03:00
|
|
|
_LOCK_HIDE_ELEMENTS = ("enigma_lock_hide_box", "bouquet_lock_hide_box")
|
2019-02-23 13:54:00 +03:00
|
|
|
|
2018-12-01 13:34:26 +03:00
|
|
|
def __init__(self, **kwargs):
|
2019-05-12 16:26:58 +03:00
|
|
|
super().__init__(flags=Gio.ApplicationFlags.HANDLES_COMMAND_LINE, **kwargs)
|
|
|
|
|
# Adding command line options
|
|
|
|
|
self.add_main_option("log", ord("l"), GLib.OptionFlags.NONE, GLib.OptionArg.NONE, "", None)
|
2020-03-07 18:33:51 +03:00
|
|
|
self.add_main_option("record", ord("r"), GLib.OptionFlags.NONE, GLib.OptionArg.NONE, "", None)
|
2020-07-11 12:58:03 +03:00
|
|
|
self.add_main_option("debug", ord("d"), GLib.OptionFlags.NONE, GLib.OptionArg.STRING, "", None)
|
2018-12-01 13:34:26 +03:00
|
|
|
|
2018-07-08 00:09:26 +03:00
|
|
|
handlers = {"on_close_app": self.on_close_app,
|
2021-08-15 17:24:30 +03:00
|
|
|
"on_info_bar_close": self.on_info_bar_close,
|
2019-12-27 23:05:37 +03:00
|
|
|
"on_profile_changed": self.on_profile_changed,
|
2018-09-18 14:40:24 +03:00
|
|
|
"on_tree_view_key_press": self.on_tree_view_key_press,
|
2017-11-09 19:01:09 +03:00
|
|
|
"on_tree_view_key_release": self.on_tree_view_key_release,
|
|
|
|
|
"on_bouquets_selection": self.on_bouquets_selection,
|
2020-11-07 18:38:40 +03:00
|
|
|
"on_fav_selection": self.on_fav_selection,
|
2021-01-21 16:51:25 +03:00
|
|
|
"on_alt_selection": self.on_alt_selection,
|
2017-11-09 19:01:09 +03:00
|
|
|
"on_services_selection": self.on_services_selection,
|
2018-09-20 16:36:03 +03:00
|
|
|
"on_fav_cut": self.on_fav_cut,
|
|
|
|
|
"on_bouquets_cut": self.on_bouquets_cut,
|
2018-09-18 10:35:10 +03:00
|
|
|
"on_services_copy": self.on_services_copy,
|
|
|
|
|
"on_fav_copy": self.on_fav_copy,
|
2018-09-20 16:36:03 +03:00
|
|
|
"on_bouquets_copy": self.on_bouquets_copy,
|
|
|
|
|
"on_fav_paste": self.on_fav_paste,
|
|
|
|
|
"on_bouquets_paste": self.on_bouquets_paste,
|
2018-09-11 15:21:05 +03:00
|
|
|
"on_rename_for_bouquet": self.on_rename_for_bouquet,
|
|
|
|
|
"on_set_default_name_for_bouquet": self.on_set_default_name_for_bouquet,
|
|
|
|
|
"on_services_add_new": self.on_services_add_new,
|
2017-11-09 19:01:09 +03:00
|
|
|
"on_delete": self.on_delete,
|
2020-02-11 13:18:14 +03:00
|
|
|
"on_edit": self.on_edit,
|
2018-10-13 22:27:32 +03:00
|
|
|
"on_to_fav_copy": self.on_to_fav_copy,
|
|
|
|
|
"on_to_fav_end_copy": self.on_to_fav_end_copy,
|
2020-07-09 22:29:33 +03:00
|
|
|
"on_fav_sort": self.on_fav_sort,
|
2020-04-06 16:50:11 +03:00
|
|
|
"on_fav_view_query_tooltip": self.on_fav_view_query_tooltip,
|
2020-04-19 17:20:51 +03:00
|
|
|
"on_services_view_query_tooltip": self.on_services_view_query_tooltip,
|
2018-09-22 21:08:28 +03:00
|
|
|
"on_view_drag_begin": self.on_view_drag_begin,
|
2020-09-24 23:17:15 +03:00
|
|
|
"on_view_drag_end": self.on_view_drag_end,
|
2018-09-19 11:46:41 +03:00
|
|
|
"on_view_drag_data_get": self.on_view_drag_data_get,
|
2020-02-28 20:59:53 +03:00
|
|
|
"on_services_view_drag_drop": self.on_services_view_drag_drop,
|
|
|
|
|
"on_services_view_drag_data_received": self.on_services_view_drag_data_received,
|
2018-09-19 11:46:41 +03:00
|
|
|
"on_view_drag_data_received": self.on_view_drag_data_received,
|
|
|
|
|
"on_bq_view_drag_data_received": self.on_bq_view_drag_data_received,
|
2021-01-08 23:01:16 +03:00
|
|
|
"on_alt_view_drag_data_received": self.on_alt_view_drag_data_received,
|
2018-09-22 19:14:47 +03:00
|
|
|
"on_view_press": self.on_view_press,
|
2020-09-24 23:17:15 +03:00
|
|
|
"on_view_release": self.on_view_release,
|
2017-11-09 19:01:09 +03:00
|
|
|
"on_view_popup_menu": self.on_view_popup_menu,
|
2017-11-23 16:59:21 +03:00
|
|
|
"on_view_focus": self.on_view_focus,
|
2017-12-08 18:32:28 +03:00
|
|
|
"on_model_changed": self.on_model_changed,
|
2019-06-24 00:36:54 +03:00
|
|
|
"on_import_yt_list": self.on_import_yt_list,
|
2017-12-19 22:57:04 +03:00
|
|
|
"on_import_m3u": self.on_import_m3u,
|
2021-05-12 14:59:55 +03:00
|
|
|
"on_bouquet_export": self.on_bouquet_export,
|
2019-04-18 21:43:35 +03:00
|
|
|
"on_export_to_m3u": self.on_export_to_m3u,
|
2017-12-19 22:57:04 +03:00
|
|
|
"on_insert_marker": self.on_insert_marker,
|
2020-05-23 15:16:31 +03:00
|
|
|
"on_insert_space": self.on_insert_space,
|
2018-04-29 01:44:28 +03:00
|
|
|
"on_fav_press": self.on_fav_press,
|
2018-01-08 22:00:48 +03:00
|
|
|
"on_locate_in_services": self.on_locate_in_services,
|
2021-04-19 13:15:36 +03:00
|
|
|
"on_mark_duplicates": self.on_mark_duplicates,
|
2018-01-29 18:07:47 +03:00
|
|
|
"on_filter_changed": self.on_filter_changed,
|
2021-03-26 11:46:43 +03:00
|
|
|
"on_filter_type_toggled": self.on_filter_type_toggled,
|
|
|
|
|
"on_filter_satellite_toggled": self.on_filter_satellite_toggled,
|
2018-01-29 18:07:47 +03:00
|
|
|
"on_assign_picon": self.on_assign_picon,
|
|
|
|
|
"on_remove_picon": self.on_remove_picon,
|
2018-01-31 00:13:42 +03:00
|
|
|
"on_reference_picon": self.on_reference_picon,
|
2019-03-03 12:50:40 +03:00
|
|
|
"on_remove_unused_picons": self.on_remove_unused_picons,
|
2018-04-02 23:55:41 +03:00
|
|
|
"on_iptv": self.on_iptv,
|
2019-04-18 23:05:19 +03:00
|
|
|
"on_epg_list_configuration": self.on_epg_list_configuration,
|
2018-08-18 17:35:30 +03:00
|
|
|
"on_iptv_list_configuration": self.on_iptv_list_configuration,
|
2018-08-01 11:05:29 +03:00
|
|
|
"on_play_stream": self.on_play_stream,
|
2021-09-13 16:52:19 +03:00
|
|
|
"on_play_current": self.on_play_current,
|
2021-04-12 17:08:43 +03:00
|
|
|
"on_main_window_state": self.on_main_window_state,
|
2020-03-07 18:33:51 +03:00
|
|
|
"on_record": self.on_record,
|
2018-06-29 22:43:04 +03:00
|
|
|
"on_remove_all_unavailable": self.on_remove_all_unavailable,
|
2018-04-02 23:55:41 +03:00
|
|
|
"on_new_bouquet": self.on_new_bouquet,
|
|
|
|
|
"on_create_bouquet_for_current_satellite": self.on_create_bouquet_for_current_satellite,
|
|
|
|
|
"on_create_bouquet_for_each_satellite": self.on_create_bouquet_for_each_satellite,
|
|
|
|
|
"on_create_bouquet_for_current_package": self.on_create_bouquet_for_current_package,
|
2018-04-07 23:49:36 +03:00
|
|
|
"on_create_bouquet_for_each_package": self.on_create_bouquet_for_each_package,
|
|
|
|
|
"on_create_bouquet_for_current_type": self.on_create_bouquet_for_current_type,
|
2021-01-08 23:01:16 +03:00
|
|
|
"on_create_bouquet_for_each_type": self.on_create_bouquet_for_each_type,
|
2021-08-15 14:37:21 +03:00
|
|
|
"on_add_alternatives": self.on_add_alternatives,
|
|
|
|
|
"on_satellites_realize": self.on_satellites_realize,
|
2021-08-15 15:42:27 +03:00
|
|
|
"on_picons_realize": self.on_picons_realize,
|
2021-09-01 00:05:23 +03:00
|
|
|
"on_epg_realize": self.on_epg_realize,
|
2021-09-01 14:15:39 +03:00
|
|
|
"on_timers_realize": self.on_timers_realize,
|
2021-09-02 12:20:29 +03:00
|
|
|
"on_recordings_realize": self.on_recordings_realize,
|
2021-08-15 14:37:21 +03:00
|
|
|
"on_control_realize": self.on_control_realize,
|
|
|
|
|
"on_ftp_realize": self.on_ftp_realize,
|
2021-09-19 00:23:23 +03:00
|
|
|
"on_visible_page": self.on_visible_page,
|
|
|
|
|
"on_data_paned_realize": self.init_main_paned_position}
|
2017-11-09 19:01:09 +03:00
|
|
|
|
2019-12-13 13:31:07 +03:00
|
|
|
self._settings = Settings.get_instance()
|
2019-12-22 20:42:29 +03:00
|
|
|
self._s_type = self._settings.setting_type
|
2021-09-04 14:46:11 +03:00
|
|
|
self._is_enigma = self._s_type is SettingsType.ENIGMA_2
|
2017-11-09 19:01:09 +03:00
|
|
|
# Used for copy/paste. When adding the previous data will not be deleted.
|
|
|
|
|
# Clearing only after the insertion!
|
2018-04-06 16:02:16 +03:00
|
|
|
self._rows_buffer = []
|
2018-09-20 16:36:03 +03:00
|
|
|
self._bouquets_buffer = []
|
2020-06-06 09:36:11 +03:00
|
|
|
self._picons_buffer = []
|
2018-04-06 16:02:16 +03:00
|
|
|
self._services = {}
|
|
|
|
|
self._bouquets = {}
|
2021-01-27 16:46:37 +03:00
|
|
|
self._bq_file = {}
|
|
|
|
|
self._alt_file = set()
|
|
|
|
|
self._alt_counter = 1
|
2020-06-04 11:32:53 +03:00
|
|
|
self._data_hash = 0
|
2021-08-04 08:37:52 +03:00
|
|
|
self._filter_cache = {}
|
2018-09-20 16:36:03 +03:00
|
|
|
# For bouquets with different names of services in bouquet and main list
|
|
|
|
|
self._extra_bouquets = {}
|
2018-04-06 16:02:16 +03:00
|
|
|
self._picons = {}
|
|
|
|
|
self._blacklist = set()
|
|
|
|
|
self._current_bq_name = None
|
2018-12-11 19:09:55 +03:00
|
|
|
self._bq_selected = "" # Current selected bouquet
|
2020-09-24 23:17:15 +03:00
|
|
|
self._select_enabled = True # Multiple selection
|
2018-09-27 22:02:35 +03:00
|
|
|
# Current satellite positions in the services list
|
2021-03-26 11:46:43 +03:00
|
|
|
self._sat_positions = set()
|
|
|
|
|
self._service_types = set()
|
2021-03-23 22:13:33 +03:00
|
|
|
self._marker_types = {BqServiceType.MARKER.name, BqServiceType.SPACE.name, BqServiceType.ALT.name}
|
2021-08-15 14:37:21 +03:00
|
|
|
# Tools
|
|
|
|
|
self._links_transmitter = None
|
|
|
|
|
self._satellite_tool = None
|
2021-08-15 15:42:27 +03:00
|
|
|
self._picon_manager = None
|
2021-09-01 00:05:23 +03:00
|
|
|
self._epg_box = None
|
2021-09-01 14:15:39 +03:00
|
|
|
self._timers_box = None
|
2021-09-02 12:20:29 +03:00
|
|
|
self._recordings_box = None
|
2021-08-15 14:37:21 +03:00
|
|
|
self._control_box = None
|
|
|
|
|
self._ftp_client = None
|
2020-03-07 18:33:51 +03:00
|
|
|
# Record
|
|
|
|
|
self._recorder = None
|
2018-11-17 23:19:17 +03:00
|
|
|
# http api
|
|
|
|
|
self._http_api = None
|
2019-03-14 12:37:48 +03:00
|
|
|
self._fav_click_mode = None
|
2021-03-12 10:24:09 +03:00
|
|
|
# Appearance
|
|
|
|
|
self._current_font = APP_FONT
|
|
|
|
|
self._picons_size = self._settings.list_picon_size
|
2018-12-23 16:15:48 +03:00
|
|
|
self._use_colors = False
|
|
|
|
|
self._NEW_COLOR = None # Color for new services in the main list
|
|
|
|
|
self._EXTRA_COLOR = None # Color for services with a extra name for the bouquet
|
2021-08-31 14:16:14 +03:00
|
|
|
# Current page.
|
|
|
|
|
self._page = Page.INFO
|
2021-09-13 16:52:19 +03:00
|
|
|
self._fav_pages = {Page.SERVICES, Page.PICONS, Page.EPG, Page.TIMERS}
|
2021-08-31 14:16:14 +03:00
|
|
|
# Signals.
|
|
|
|
|
GObject.signal_new("profile-changed", self, GObject.SIGNAL_RUN_LAST,
|
|
|
|
|
GObject.TYPE_PYOBJECT, (GObject.TYPE_PYOBJECT,))
|
2021-09-01 00:05:23 +03:00
|
|
|
GObject.signal_new("fav-changed", self, GObject.SIGNAL_RUN_LAST,
|
|
|
|
|
GObject.TYPE_PYOBJECT, (GObject.TYPE_PYOBJECT,))
|
2021-09-13 16:52:19 +03:00
|
|
|
GObject.signal_new("fav-clicked", self, GObject.SIGNAL_RUN_LAST,
|
|
|
|
|
GObject.TYPE_PYOBJECT, (GObject.TYPE_PYOBJECT,))
|
2021-09-01 14:15:39 +03:00
|
|
|
GObject.signal_new("page-changed", self, GObject.SIGNAL_RUN_LAST,
|
|
|
|
|
GObject.TYPE_PYOBJECT, (GObject.TYPE_PYOBJECT,))
|
2021-09-13 16:52:19 +03:00
|
|
|
GObject.signal_new("play-recording", self, GObject.SIGNAL_RUN_LAST,
|
|
|
|
|
GObject.TYPE_PYOBJECT, (GObject.TYPE_PYOBJECT,))
|
|
|
|
|
GObject.signal_new("play-current", self, GObject.SIGNAL_RUN_LAST,
|
|
|
|
|
GObject.TYPE_PYOBJECT, (GObject.TYPE_PYOBJECT,))
|
2017-11-09 19:01:09 +03:00
|
|
|
|
2021-08-15 14:37:21 +03:00
|
|
|
builder = get_builder(UI_RESOURCES_PATH + "main.glade", handlers)
|
2018-04-06 16:02:16 +03:00
|
|
|
self._main_window = builder.get_object("main_window")
|
2019-12-13 13:31:07 +03:00
|
|
|
main_window_size = self._settings.get("window_size")
|
2017-11-09 19:01:09 +03:00
|
|
|
# Setting the last size of the window if it was saved
|
|
|
|
|
if main_window_size:
|
2018-04-06 16:02:16 +03:00
|
|
|
self._main_window.resize(*main_window_size)
|
2021-08-15 14:37:21 +03:00
|
|
|
|
2021-08-18 19:46:47 +03:00
|
|
|
self._stack = builder.get_object("stack")
|
2021-08-15 14:37:21 +03:00
|
|
|
self._fav_paned = builder.get_object("fav_paned")
|
2018-04-06 16:02:16 +03:00
|
|
|
self._services_view = builder.get_object("services_tree_view")
|
|
|
|
|
self._fav_view = builder.get_object("fav_tree_view")
|
|
|
|
|
self._bouquets_view = builder.get_object("bouquets_tree_view")
|
|
|
|
|
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")
|
2018-07-09 19:00:05 +03:00
|
|
|
self._bq_name_label = builder.get_object("bq_name_label")
|
2021-09-03 18:09:26 +03:00
|
|
|
self._iptv_menu_button = builder.get_object("iptv_menu_button")
|
2021-02-13 15:04:54 +03:00
|
|
|
# Setting custom sort function for position column.
|
|
|
|
|
self._services_view.get_model().set_sort_func(Column.SRV_POS, self.position_sort_func, Column.SRV_POS)
|
2019-10-04 21:31:41 +03:00
|
|
|
# App info
|
|
|
|
|
self._app_info_box = builder.get_object("app_info_box")
|
2021-09-13 16:52:19 +03:00
|
|
|
self._app_info_box.bind_property("visible", builder.get_object("data_paned"), "visible", 4)
|
2021-08-15 17:24:30 +03:00
|
|
|
# Info bar.
|
|
|
|
|
self._info_bar = builder.get_object("info_bar")
|
|
|
|
|
self._info_label = builder.get_object("info_label")
|
2021-09-03 18:09:26 +03:00
|
|
|
# Status bar.
|
|
|
|
|
self._status_bar_box = builder.get_object("status_bar_box")
|
2019-12-22 20:42:29 +03:00
|
|
|
self._profile_combo_box = builder.get_object("profile_combo_box")
|
2018-11-17 23:19:17 +03:00
|
|
|
self._receiver_info_box = builder.get_object("receiver_info_box")
|
|
|
|
|
self._receiver_info_label = builder.get_object("receiver_info_label")
|
2020-02-13 01:09:40 +03:00
|
|
|
self._current_ip_label = builder.get_object("current_ip_label")
|
2018-11-17 23:19:17 +03:00
|
|
|
self._signal_box = builder.get_object("signal_box")
|
|
|
|
|
self._service_name_label = builder.get_object("service_name_label")
|
2019-10-28 00:45:47 +03:00
|
|
|
self._service_epg_label = builder.get_object("service_epg_label")
|
2018-11-17 23:19:17 +03:00
|
|
|
self._signal_level_bar = builder.get_object("signal_level_bar")
|
2019-11-21 23:13:06 +03:00
|
|
|
self._http_status_image = builder.get_object("http_status_image")
|
2018-04-06 16:02:16 +03:00
|
|
|
self._cas_label = builder.get_object("cas_label")
|
|
|
|
|
self._fav_count_label = builder.get_object("fav_count_label")
|
|
|
|
|
self._bouquets_count_label = builder.get_object("bouquets_count_label")
|
|
|
|
|
self._tv_count_label = builder.get_object("tv_count_label")
|
|
|
|
|
self._radio_count_label = builder.get_object("radio_count_label")
|
|
|
|
|
self._data_count_label = builder.get_object("data_count_label")
|
2021-07-11 23:29:19 +03:00
|
|
|
self._services_load_spinner = builder.get_object("services_load_spinner")
|
2021-08-20 17:24:48 +03:00
|
|
|
self._save_tool_button = builder.get_object("save_tool_button")
|
2019-10-28 00:45:47 +03:00
|
|
|
self._signal_level_bar.bind_property("visible", builder.get_object("play_current_service_button"), "visible")
|
2020-03-07 18:33:51 +03:00
|
|
|
self._signal_level_bar.bind_property("visible", builder.get_object("record_button"), "visible")
|
2019-11-21 23:13:06 +03:00
|
|
|
self._receiver_info_box.bind_property("visible", self._http_status_image, "visible", 4)
|
2020-02-29 21:15:24 +03:00
|
|
|
self._receiver_info_box.bind_property("visible", self._signal_box, "visible")
|
2021-01-05 23:07:15 +03:00
|
|
|
# Alternatives
|
2021-01-08 23:01:16 +03:00
|
|
|
self._alt_view = builder.get_object("alt_tree_view")
|
2021-01-05 23:07:15 +03:00
|
|
|
self._alt_model = builder.get_object("alt_list_store")
|
|
|
|
|
self._alt_revealer = builder.get_object("alt_revealer")
|
|
|
|
|
self._alt_revealer.bind_property("visible", self._alt_revealer, "reveal-child")
|
2020-12-16 23:28:00 +03:00
|
|
|
# Force Ctrl press event for view. Multiple selections in lists only with Space key(as in file managers)!!!
|
2018-04-06 16:02:16 +03:00
|
|
|
self._services_view.connect("key-press-event", self.force_ctrl)
|
|
|
|
|
self._fav_view.connect("key-press-event", self.force_ctrl)
|
2018-01-30 12:37:04 +03:00
|
|
|
# Clipboard
|
2018-04-06 16:02:16 +03:00
|
|
|
self._clipboard = Gtk.Clipboard.get(Gdk.SELECTION_CLIPBOARD)
|
2018-02-18 17:14:02 +03:00
|
|
|
# Wait dialog
|
2018-04-06 16:02:16 +03:00
|
|
|
self._wait_dialog = WaitDialog(self._main_window)
|
2018-03-06 19:06:16 +03:00
|
|
|
# Filter
|
2018-04-06 16:02:16 +03:00
|
|
|
self._services_model_filter = builder.get_object("services_model_filter")
|
|
|
|
|
self._services_model_filter.set_visible_func(self.services_filter_function)
|
2021-08-04 08:37:52 +03:00
|
|
|
self._filter_header_button = builder.get_object("filter_header_button")
|
2018-04-06 16:02:16 +03:00
|
|
|
self._filter_entry = builder.get_object("filter_entry")
|
2021-04-09 20:00:37 +03:00
|
|
|
self._filter_box = builder.get_object("filter_box")
|
2018-09-12 14:05:28 +03:00
|
|
|
self._filter_types_model = builder.get_object("filter_types_list_store")
|
2021-03-26 11:46:43 +03:00
|
|
|
self._filter_sat_pos_model = builder.get_object("filter_sat_pos_list_store")
|
2018-09-12 17:26:22 +03:00
|
|
|
self._filter_only_free_button = builder.get_object("filter_only_free_button")
|
2021-08-04 08:37:52 +03:00
|
|
|
self._services_load_spinner.bind_property("active", self._filter_header_button, "sensitive", 4)
|
|
|
|
|
self._services_load_spinner.bind_property("active", self._filter_box, "sensitive", 4)
|
2021-09-19 00:23:23 +03:00
|
|
|
# Search.
|
|
|
|
|
services_search_provider = SearchProvider(self._services_view,
|
|
|
|
|
builder.get_object("services_search_entry"),
|
|
|
|
|
builder.get_object("srv_search_down_button"),
|
|
|
|
|
builder.get_object("srv_search_up_button"),
|
|
|
|
|
(Column.SRV_SERVICE, Column.SRV_PACKAGE))
|
|
|
|
|
self._srv_search_button = builder.get_object("srv_search_button")
|
|
|
|
|
self._srv_search_button.bind_property("active", builder.get_object("srv_search_box"), "visible")
|
|
|
|
|
self._srv_search_button.connect("toggled", services_search_provider.on_search_toggled)
|
|
|
|
|
fav_search_provider = SearchProvider(self._fav_view,
|
|
|
|
|
builder.get_object("fav_search_entry"),
|
|
|
|
|
builder.get_object("fav_search_down_button"),
|
|
|
|
|
builder.get_object("fav_search_up_button"),
|
|
|
|
|
(Column.FAV_SERVICE, Column.FAV_TYPE, Column.FAV_POS))
|
|
|
|
|
self._fav_search_button = builder.get_object("fav_search_button")
|
|
|
|
|
self._fav_search_button.bind_property("active", builder.get_object("fav_search_box"), "visible")
|
|
|
|
|
self._fav_search_button.connect("toggled", fav_search_provider.on_search_toggled)
|
2021-09-13 16:52:19 +03:00
|
|
|
# Playback.
|
|
|
|
|
self._player_box = PlayerBox(self)
|
|
|
|
|
paned = builder.get_object("main_paned")
|
|
|
|
|
data_paned = paned.get_child1()
|
|
|
|
|
paned.remove(data_paned)
|
2020-02-13 01:09:40 +03:00
|
|
|
self._player_box.bind_property("visible", self._profile_combo_box, "visible", 4)
|
2021-09-19 00:23:23 +03:00
|
|
|
paned.pack1(self._player_box, True, False)
|
2021-09-13 16:52:19 +03:00
|
|
|
paned.pack2(data_paned, True, False)
|
|
|
|
|
self._player_box.connect("show", self.on_playback_show)
|
|
|
|
|
self._player_box.connect("playback-close", self.on_playback_close)
|
|
|
|
|
self._player_box.connect("playback-full-screen", self.on_playback_full_screen)
|
|
|
|
|
self._data_paned = builder.get_object("data_paned")
|
|
|
|
|
self._data_paned.bind_property("visible", self._status_bar_box, "visible")
|
2021-09-19 00:23:23 +03:00
|
|
|
# Record.
|
2020-03-07 18:33:51 +03:00
|
|
|
self._record_image = builder.get_object("record_button_image")
|
2021-09-19 00:23:23 +03:00
|
|
|
# Dynamically active elements depending on the selected view.
|
2019-05-13 14:42:23 +03:00
|
|
|
d_elements = (self._SERVICE_ELEMENTS, self._BOUQUET_ELEMENTS, self._COMMONS_ELEMENTS, self._FAV_ELEMENTS,
|
|
|
|
|
self._FAV_ENIGMA_ELEMENTS, self._FAV_IPTV_ELEMENTS, self._LOCK_HIDE_ELEMENTS)
|
|
|
|
|
self._tool_elements = {k: builder.get_object(k) for k in set(chain.from_iterable(d_elements))}
|
2021-09-04 14:46:11 +03:00
|
|
|
# Lock, Hide.
|
|
|
|
|
self.bind_property("is-enigma", self._tool_elements.get(self._LOCK_HIDE_ELEMENTS[0]), "visible")
|
|
|
|
|
self.bind_property("is-enigma", self._tool_elements.get(self._LOCK_HIDE_ELEMENTS[1]), "visible", 4)
|
2021-08-21 14:50:05 +03:00
|
|
|
# Stack page widgets.
|
|
|
|
|
self._stack_services_frame = builder.get_object("services_frame")
|
|
|
|
|
self._stack_satellite_box = builder.get_object("satellite_box")
|
|
|
|
|
self._stack_picon_box = builder.get_object("picon_box")
|
2021-09-01 00:05:23 +03:00
|
|
|
self._stack_epg_box = builder.get_object("epg_box")
|
|
|
|
|
self._stack_timers_box = builder.get_object("timers_box")
|
|
|
|
|
self._stack_recordings_box = builder.get_object("recordings_box")
|
2021-08-21 14:50:05 +03:00
|
|
|
self._stack_ftp_box = builder.get_object("ftp_box")
|
|
|
|
|
self._stack_control_box = builder.get_object("control_box")
|
2021-08-20 17:24:48 +03:00
|
|
|
# Header bar.
|
2021-09-13 16:52:19 +03:00
|
|
|
profile_box = builder.get_object("profile_combo_box")
|
|
|
|
|
toolbar_box = builder.get_object("toolbar_main_box")
|
2021-08-20 17:24:48 +03:00
|
|
|
if IS_GNOME_SESSION:
|
|
|
|
|
header_bar = Gtk.HeaderBar(visible=True, show_close_button=True)
|
|
|
|
|
header_bar.pack_start(builder.get_object("file_header_button"))
|
|
|
|
|
header_bar.pack_start(Gtk.Separator(visible=True))
|
2021-09-13 16:52:19 +03:00
|
|
|
header_bar.pack_start(profile_box)
|
|
|
|
|
header_bar.pack_start(toolbar_box)
|
2021-08-20 17:24:48 +03:00
|
|
|
header_bar.set_custom_title(builder.get_object("stack_switcher"))
|
2021-09-13 16:52:19 +03:00
|
|
|
self._player_box.bind_property("visible", builder.get_object("main_popover_menu_box"), "visible", 4)
|
|
|
|
|
self._player_box.bind_property("visible", builder.get_object("close_player_menu_button"), "visible")
|
2021-08-20 17:24:48 +03:00
|
|
|
self._main_window.set_titlebar(header_bar)
|
|
|
|
|
else:
|
|
|
|
|
tool_bar = Gtk.Box(visible=True, spacing=6, margin=6, valign=Gtk.Align.CENTER)
|
2021-09-13 16:52:19 +03:00
|
|
|
tool_bar.add(profile_box)
|
|
|
|
|
tool_bar.add(toolbar_box)
|
2021-08-20 17:24:48 +03:00
|
|
|
tool_bar.set_center_widget(builder.get_object("stack_switcher"))
|
|
|
|
|
main_header_box = Gtk.Box(visible=True, spacing=6)
|
|
|
|
|
main_header_box.get_style_context().add_class(Gtk.STYLE_CLASS_PRIMARY_TOOLBAR)
|
|
|
|
|
main_header_box.pack_start(tool_bar, True, True, 0)
|
|
|
|
|
main_box = builder.get_object("main_window_box")
|
|
|
|
|
main_box.add(main_header_box)
|
|
|
|
|
main_box.reorder_child(main_header_box, 0)
|
2021-09-13 16:52:19 +03:00
|
|
|
self._data_paned.bind_property("visible", main_header_box, "visible")
|
|
|
|
|
self._player_box.bind_property("visible", profile_box, "visible", 4)
|
|
|
|
|
self._player_box.bind_property("visible", toolbar_box, "visible", 4)
|
2019-10-29 13:39:11 +03:00
|
|
|
# Style
|
2020-12-16 23:28:00 +03:00
|
|
|
style_provider = Gtk.CssProvider()
|
|
|
|
|
style_provider.load_from_path(UI_RESOURCES_PATH + "style.css")
|
|
|
|
|
self._status_bar_box.get_style_context().add_provider_for_screen(Gdk.Screen.get_default(), style_provider,
|
2019-10-29 13:39:11 +03:00
|
|
|
Gtk.STYLE_PROVIDER_PRIORITY_USER)
|
2018-12-01 13:34:26 +03:00
|
|
|
|
|
|
|
|
def do_startup(self):
|
|
|
|
|
Gtk.Application.do_startup(self)
|
2021-08-17 16:19:42 +03:00
|
|
|
# App menu.
|
2021-08-21 14:50:05 +03:00
|
|
|
builder = get_builder(UI_RESOURCES_PATH + "app_menu.ui")
|
2021-08-20 17:24:48 +03:00
|
|
|
if not IS_GNOME_SESSION:
|
|
|
|
|
if IS_DARWIN:
|
|
|
|
|
self.set_app_menu(builder.get_object("mac_app_menu"))
|
|
|
|
|
self.set_menubar(builder.get_object("mac_menu_bar"))
|
|
|
|
|
else:
|
|
|
|
|
self.set_menubar(builder.get_object("menu_bar"))
|
2021-08-21 14:50:05 +03:00
|
|
|
else:
|
|
|
|
|
view_menu = builder.get_object("view_menu")
|
|
|
|
|
view_button = Gtk.MenuButton(visible=True, menu_model=view_menu, direction=Gtk.ArrowType.NONE)
|
|
|
|
|
view_button.set_tooltip_text(get_message("View"))
|
|
|
|
|
self._main_window.get_titlebar().pack_end(view_button)
|
2021-09-03 18:09:26 +03:00
|
|
|
# IPTV menu.
|
|
|
|
|
self._iptv_menu_button.set_menu_model(builder.get_object("iptv_menu"))
|
|
|
|
|
iptv_elem = self._tool_elements.get("fav_iptv_popup_item")
|
|
|
|
|
for h in (self.on_iptv, self.on_import_yt_list, self.on_import_m3u, self.on_export_to_m3u,
|
|
|
|
|
self.on_epg_list_configuration, self.on_iptv_list_configuration, self.on_remove_all_unavailable):
|
|
|
|
|
iptv_elem.bind_property("sensitive", self.set_action(h.__name__, h, False), "enabled")
|
|
|
|
|
|
2021-08-17 16:19:42 +03:00
|
|
|
self.init_actions()
|
2020-02-11 13:18:14 +03:00
|
|
|
self.set_accels()
|
|
|
|
|
|
2018-12-23 16:15:48 +03:00
|
|
|
self.init_drag_and_drop()
|
2021-03-12 10:24:09 +03:00
|
|
|
self.init_appearance()
|
2021-03-26 11:46:43 +03:00
|
|
|
self.filter_set_default()
|
2020-04-19 13:23:18 +03:00
|
|
|
|
2020-01-09 13:14:49 +03:00
|
|
|
if self._settings.load_last_config:
|
|
|
|
|
config = self._settings.get("last_config") or {}
|
|
|
|
|
self.init_profiles(config.get("last_profile", None))
|
|
|
|
|
last_bouquet = config.get("last_bouquet", None)
|
|
|
|
|
self.open_data(callback=lambda: self.open_bouquet(last_bouquet))
|
|
|
|
|
else:
|
|
|
|
|
self.init_profiles()
|
2019-12-27 23:05:37 +03:00
|
|
|
gen = self.init_http_api()
|
|
|
|
|
GLib.idle_add(lambda: next(gen, False), priority=GLib.PRIORITY_LOW)
|
2018-12-01 13:34:26 +03:00
|
|
|
|
2021-08-17 16:19:42 +03:00
|
|
|
def init_actions(self):
|
|
|
|
|
self.set_action("on_import_bouquet", self.on_import_bouquet)
|
|
|
|
|
self.set_action("on_import_bouquets", self.on_import_bouquets)
|
|
|
|
|
self.set_action("on_new_configuration", self.on_new_configuration)
|
2020-11-03 20:36:21 +03:00
|
|
|
self.set_action("on_import_from_web", self.on_import_from_web)
|
2021-08-17 16:19:42 +03:00
|
|
|
self.set_action("on_settings", self.on_settings)
|
2021-08-20 17:24:48 +03:00
|
|
|
self.set_action("on_backup_tool_show", self.on_backup_tool_show)
|
|
|
|
|
self.set_action("on_about_app", self.on_about_app)
|
2021-08-17 16:19:42 +03:00
|
|
|
self.set_action("on_close_app", self.on_close_app)
|
2021-09-19 00:23:23 +03:00
|
|
|
# Filter.
|
2020-02-11 13:18:14 +03:00
|
|
|
filter_action = Gio.SimpleAction.new_stateful("filter", None, GLib.Variant.new_boolean(False))
|
|
|
|
|
filter_action.connect("change-state", self.on_filter_toggled)
|
2021-09-19 00:23:23 +03:00
|
|
|
self._main_window.add_action(filter_action) # For "win.*" actions!
|
2021-08-17 16:19:42 +03:00
|
|
|
# Lock, Hide.
|
2020-11-03 20:36:21 +03:00
|
|
|
self.set_action("on_hide", self.on_hide)
|
|
|
|
|
self.set_action("on_locked", self.on_locked)
|
2021-08-17 16:19:42 +03:00
|
|
|
# Open and download/upload data.
|
2020-11-03 20:36:21 +03:00
|
|
|
self.set_action("open_data", lambda a, v: self.open_data())
|
|
|
|
|
self.set_action("on_download_data", self.on_download_data)
|
|
|
|
|
self.set_action("upload_all", lambda a, v: self.on_upload_data(DownloadType.ALL))
|
|
|
|
|
self.set_action("upload_bouquets", lambda a, v: self.on_upload_data(DownloadType.BOUQUETS))
|
2021-08-17 16:19:42 +03:00
|
|
|
self.set_action("on_data_save", self.on_data_save)
|
|
|
|
|
self.set_action("on_data_save_as", self.on_data_save_as)
|
|
|
|
|
self.set_action("on_download", self.on_download)
|
|
|
|
|
self.set_action("on_data_open", self.on_data_open)
|
|
|
|
|
self.set_action("on_archive_open", self.on_archive_open)
|
|
|
|
|
# Edit.
|
2020-11-03 20:36:21 +03:00
|
|
|
self.set_action("on_edit", self.on_edit)
|
2021-08-21 14:50:05 +03:00
|
|
|
# View actions.
|
|
|
|
|
sa = self.set_state_action("show_bouquets", self.on_page_show, self._settings.get("show_bouquets", True))
|
|
|
|
|
sa.connect("change-state", lambda a, v: self._stack_services_frame.set_visible(v))
|
|
|
|
|
sa = self.set_state_action("show_satellites", self.on_page_show, self._settings.get("show_satellites", True))
|
|
|
|
|
sa.connect("change-state", lambda a, v: self._stack_satellite_box.set_visible(v))
|
|
|
|
|
sa = self.set_state_action("show_picons", self.on_page_show, self._settings.get("show_picons", True))
|
|
|
|
|
sa.connect("change-state", lambda a, v: self._stack_picon_box.set_visible(v))
|
2021-09-01 00:05:23 +03:00
|
|
|
sa = self.set_state_action("show_epg", self.on_page_show, self._settings.get("show_epg", True))
|
|
|
|
|
sa.connect("change-state", lambda a, v: self._stack_epg_box.set_visible(v))
|
2021-09-04 14:46:11 +03:00
|
|
|
self.bind_property("is-enigma", sa, "enabled")
|
2021-08-21 14:50:05 +03:00
|
|
|
sa = self.set_state_action("show_timers", self.on_page_show, self._settings.get("show_timers", True))
|
2021-09-01 00:05:23 +03:00
|
|
|
sa.connect("change-state", lambda a, v: self._stack_timers_box.set_visible(v))
|
2021-09-04 14:46:11 +03:00
|
|
|
self.bind_property("is-enigma", sa, "enabled")
|
2021-08-21 14:50:05 +03:00
|
|
|
sa = self.set_state_action("show_recordings", self.on_page_show, self._settings.get("show_recordings", True))
|
2021-09-01 00:05:23 +03:00
|
|
|
sa.connect("change-state", lambda a, v: self._stack_recordings_box.set_visible(v))
|
2021-09-04 14:46:11 +03:00
|
|
|
self.bind_property("is-enigma", sa, "enabled")
|
2021-08-21 14:50:05 +03:00
|
|
|
sa = self.set_state_action("show_ftp", self.on_page_show, self._settings.get("show_ftp", True))
|
|
|
|
|
sa.connect("change-state", lambda a, v: self._stack_ftp_box.set_visible(v))
|
|
|
|
|
sa = self.set_state_action("show_control", self.on_page_show, self._settings.get("show_control", True))
|
|
|
|
|
sa.connect("change-state", lambda a, v: self._stack_control_box.set_visible(v))
|
2021-09-04 14:46:11 +03:00
|
|
|
self.bind_property("is-enigma", sa, "enabled")
|
2021-09-13 16:52:19 +03:00
|
|
|
# Menu bar and playback.
|
|
|
|
|
self.set_action("on_playback_close", self._player_box.on_close)
|
2021-08-20 17:24:48 +03:00
|
|
|
if not IS_GNOME_SESSION:
|
2021-08-17 16:19:42 +03:00
|
|
|
# We are working with the "hidden-when" submenu attribute. See 'app_menu_.ui' file.
|
|
|
|
|
hide_bar_action = Gio.SimpleAction.new("hide_menu_bar", None)
|
2021-09-13 16:52:19 +03:00
|
|
|
self._player_box.bind_property("visible", hide_bar_action, "enabled", 4)
|
2021-08-17 16:19:42 +03:00
|
|
|
self.add_action(hide_bar_action)
|
2021-09-13 16:52:19 +03:00
|
|
|
hide_media_bar = Gio.SimpleAction.new("hide_media_bar", None)
|
|
|
|
|
hide_media_bar.set_enabled(False)
|
|
|
|
|
self._player_box.bind_property("visible", hide_media_bar, "enabled")
|
|
|
|
|
self.add_action(hide_media_bar)
|
2020-11-03 20:36:21 +03:00
|
|
|
|
|
|
|
|
def set_action(self, name, fun, enabled=True):
|
|
|
|
|
ac = Gio.SimpleAction.new(name, None)
|
|
|
|
|
ac.connect("activate", fun)
|
|
|
|
|
ac.set_enabled(enabled)
|
|
|
|
|
self.add_action(ac)
|
2021-08-21 14:50:05 +03:00
|
|
|
|
2020-11-03 20:36:21 +03:00
|
|
|
return ac
|
2020-02-11 13:18:14 +03:00
|
|
|
|
2021-08-21 14:50:05 +03:00
|
|
|
def set_state_action(self, name, fun, enabled=True):
|
|
|
|
|
action = Gio.SimpleAction.new_stateful(name, None, GLib.Variant.new_boolean(enabled))
|
|
|
|
|
action.connect("change-state", fun)
|
|
|
|
|
self.add_action(action)
|
|
|
|
|
|
|
|
|
|
return action
|
|
|
|
|
|
2020-02-11 13:18:14 +03:00
|
|
|
def set_accels(self):
|
|
|
|
|
""" Setting accelerators for the actions. """
|
|
|
|
|
self.set_accels_for_action("app.on_data_save", ["<primary>s"])
|
|
|
|
|
self.set_accels_for_action("app.on_download_data", ["<primary>d"])
|
|
|
|
|
self.set_accels_for_action("app.upload_all", ["<primary>u"])
|
|
|
|
|
self.set_accels_for_action("app.upload_bouquets", ["<primary>b"])
|
|
|
|
|
self.set_accels_for_action("app.open_data", ["<primary>o"])
|
|
|
|
|
self.set_accels_for_action("app.on_hide", ["<primary>h"])
|
|
|
|
|
self.set_accels_for_action("app.on_locked", ["<primary>l"])
|
|
|
|
|
self.set_accels_for_action("app.on_close_app", ["<primary>q"])
|
|
|
|
|
self.set_accels_for_action("app.on_edit", ["<primary>e"])
|
|
|
|
|
self.set_accels_for_action("win.filter", ["<shift><primary>f"])
|
|
|
|
|
|
2018-12-01 13:34:26 +03:00
|
|
|
def do_activate(self):
|
|
|
|
|
self._main_window.set_application(self)
|
2018-12-16 22:44:45 +03:00
|
|
|
self._main_window.set_wmclass("DemonEditor", "DemonEditor")
|
2018-12-01 13:34:26 +03:00
|
|
|
self._main_window.present()
|
2017-11-09 19:01:09 +03:00
|
|
|
|
2018-12-09 21:09:51 +03:00
|
|
|
def do_shutdown(self):
|
|
|
|
|
""" Performs shutdown tasks """
|
2020-01-09 13:14:49 +03:00
|
|
|
if self._settings.load_last_config:
|
|
|
|
|
self._settings.add("last_config", {"last_profile": self._settings.current_profile,
|
|
|
|
|
"last_bouquet": self._current_bq_name})
|
|
|
|
|
self._settings.save() # storing current settings
|
|
|
|
|
|
2020-01-17 00:34:18 +03:00
|
|
|
if self._http_api:
|
|
|
|
|
self._http_api.close()
|
|
|
|
|
|
2018-12-09 21:09:51 +03:00
|
|
|
Gtk.Application.do_shutdown(self)
|
|
|
|
|
|
2019-05-12 16:26:58 +03:00
|
|
|
def do_command_line(self, command_line):
|
|
|
|
|
""" Processing command line parameters. """
|
|
|
|
|
options = command_line.get_options_dict()
|
|
|
|
|
options = options.end().unpack()
|
2020-03-07 18:33:51 +03:00
|
|
|
|
2019-05-12 16:26:58 +03:00
|
|
|
if "log" in options:
|
|
|
|
|
init_logger()
|
|
|
|
|
|
2020-03-07 18:33:51 +03:00
|
|
|
if "record" in options:
|
|
|
|
|
log("Starting record of current stream...")
|
|
|
|
|
log("Not implemented yet!")
|
|
|
|
|
|
2020-07-11 12:58:03 +03:00
|
|
|
if "debug" in options:
|
|
|
|
|
d_op = options.get("debug", "off")
|
|
|
|
|
if d_op == "on":
|
|
|
|
|
self._settings.debug_mode = True
|
|
|
|
|
elif d_op == "off":
|
|
|
|
|
self._settings.debug_mode = False
|
|
|
|
|
else:
|
|
|
|
|
log("No valid [on, off] arguments for -d found!")
|
|
|
|
|
return 1
|
|
|
|
|
log("Debug mode is {}.".format(d_op))
|
|
|
|
|
self._settings.save()
|
|
|
|
|
|
2019-05-12 16:26:58 +03:00
|
|
|
self.activate()
|
|
|
|
|
return 0
|
|
|
|
|
|
2020-01-09 13:14:49 +03:00
|
|
|
def init_profiles(self, profile=None):
|
2019-12-27 23:05:37 +03:00
|
|
|
self.update_profiles()
|
2020-02-13 20:15:18 +03:00
|
|
|
self._profile_combo_box.set_active_id(profile if profile else self._settings.current_profile)
|
2020-01-09 13:14:49 +03:00
|
|
|
if profile:
|
|
|
|
|
self.set_profile(profile)
|
2019-12-27 23:05:37 +03:00
|
|
|
|
2017-11-09 19:01:09 +03:00
|
|
|
def init_drag_and_drop(self):
|
2020-09-19 12:32:08 +03:00
|
|
|
""" Enable drag-and-drop. """
|
2017-11-09 19:01:09 +03:00
|
|
|
target = []
|
2018-09-19 11:46:41 +03:00
|
|
|
bq_target = []
|
2020-02-28 20:59:53 +03:00
|
|
|
|
2018-04-06 16:02:16 +03:00
|
|
|
self._services_view.enable_model_drag_source(Gdk.ModifierType.BUTTON1_MASK, target, Gdk.DragAction.COPY)
|
2020-02-28 20:59:53 +03:00
|
|
|
self._services_view.enable_model_drag_dest([], Gdk.DragAction.DEFAULT | Gdk.DragAction.MOVE)
|
2018-04-06 16:02:16 +03:00
|
|
|
self._fav_view.enable_model_drag_source(Gdk.ModifierType.BUTTON1_MASK, target,
|
2020-11-19 18:05:40 +03:00
|
|
|
Gdk.DragAction.DEFAULT | Gdk.DragAction.MOVE | Gdk.DragAction.COPY)
|
2018-04-06 16:02:16 +03:00
|
|
|
self._fav_view.enable_model_drag_dest(target, Gdk.DragAction.DEFAULT | Gdk.DragAction.MOVE)
|
2018-09-19 11:46:41 +03:00
|
|
|
self._bouquets_view.enable_model_drag_source(Gdk.ModifierType.BUTTON1_MASK, bq_target,
|
|
|
|
|
Gdk.DragAction.DEFAULT | Gdk.DragAction.MOVE)
|
|
|
|
|
self._bouquets_view.enable_model_drag_dest(bq_target, Gdk.DragAction.DEFAULT | Gdk.DragAction.MOVE)
|
2021-01-08 23:01:16 +03:00
|
|
|
self._alt_view.enable_model_drag_dest(bq_target, Gdk.DragAction.DEFAULT | Gdk.DragAction.COPY)
|
2021-01-10 14:05:01 +03:00
|
|
|
self._alt_view.enable_model_drag_source(Gdk.ModifierType.BUTTON1_MASK, [], Gdk.DragAction.MOVE)
|
2020-02-28 20:59:53 +03:00
|
|
|
|
2018-04-06 16:02:16 +03:00
|
|
|
self._fav_view.drag_source_set_target_list(None)
|
|
|
|
|
self._fav_view.drag_dest_add_text_targets()
|
|
|
|
|
self._fav_view.drag_source_add_text_targets()
|
2020-02-28 20:59:53 +03:00
|
|
|
self._fav_view.drag_dest_add_uri_targets()
|
|
|
|
|
|
2018-04-06 16:02:16 +03:00
|
|
|
self._services_view.drag_source_set_target_list(None)
|
|
|
|
|
self._services_view.drag_source_add_text_targets()
|
2020-09-19 12:32:08 +03:00
|
|
|
self._services_view.drag_dest_add_text_targets()
|
2020-02-28 20:59:53 +03:00
|
|
|
self._services_view.drag_dest_add_uri_targets()
|
|
|
|
|
|
2018-09-19 11:46:41 +03:00
|
|
|
self._bouquets_view.drag_dest_set_target_list(None)
|
|
|
|
|
self._bouquets_view.drag_source_set_target_list(None)
|
|
|
|
|
self._bouquets_view.drag_dest_add_text_targets()
|
|
|
|
|
self._bouquets_view.drag_source_add_text_targets()
|
2017-11-09 19:01:09 +03:00
|
|
|
|
2021-01-10 14:05:01 +03:00
|
|
|
self._alt_view.drag_source_set_target_list(None)
|
|
|
|
|
self._alt_view.drag_source_add_text_targets()
|
2021-01-08 23:01:16 +03:00
|
|
|
self._alt_view.drag_dest_add_text_targets()
|
2021-01-10 14:05:01 +03:00
|
|
|
|
2020-09-19 12:32:08 +03:00
|
|
|
self._app_info_box.drag_dest_set(Gtk.DestDefaults.ALL, [], Gdk.DragAction.COPY)
|
|
|
|
|
self._app_info_box.drag_dest_add_text_targets()
|
2020-09-24 23:17:15 +03:00
|
|
|
# For multiple selection.
|
|
|
|
|
self._services_view.get_selection().set_select_function(lambda *args: self._select_enabled)
|
|
|
|
|
self._fav_view.get_selection().set_select_function(lambda *args: self._select_enabled)
|
|
|
|
|
self._bouquets_view.get_selection().set_select_function(lambda *args: self._select_enabled)
|
2020-09-19 12:32:08 +03:00
|
|
|
|
2021-03-12 10:24:09 +03:00
|
|
|
def init_appearance(self, update=False):
|
|
|
|
|
""" Appearance initialisation.
|
2018-12-23 16:15:48 +03:00
|
|
|
|
|
|
|
|
If update=False - first call on program start, else - after options changes!
|
|
|
|
|
"""
|
2021-03-12 10:24:09 +03:00
|
|
|
if self._current_font != self._settings.list_font:
|
|
|
|
|
from gi.repository import Pango
|
|
|
|
|
|
|
|
|
|
font_desc = Pango.FontDescription.from_string(self._settings.list_font)
|
|
|
|
|
list(map(lambda v: v.modify_font(font_desc), (self._services_view, self._fav_view, self._bouquets_view)))
|
|
|
|
|
self._current_font = self._settings.list_font
|
|
|
|
|
|
|
|
|
|
if self._picons_size != self._settings.list_picon_size:
|
|
|
|
|
self.update_picons_size()
|
|
|
|
|
|
2019-12-22 20:42:29 +03:00
|
|
|
if self._s_type is SettingsType.ENIGMA_2:
|
2020-05-04 22:32:02 +03:00
|
|
|
self._use_colors = self._settings.use_colors
|
|
|
|
|
|
|
|
|
|
if self._use_colors:
|
2018-12-23 16:15:48 +03:00
|
|
|
new_rgb = Gdk.RGBA()
|
|
|
|
|
extra_rgb = Gdk.RGBA()
|
2019-12-13 13:31:07 +03:00
|
|
|
new_rgb = new_rgb if new_rgb.parse(self._settings.new_color) else None
|
|
|
|
|
extra_rgb = extra_rgb if extra_rgb.parse(self._settings.extra_color) else None
|
2018-12-23 16:15:48 +03:00
|
|
|
if update:
|
|
|
|
|
gen = self.update_background_colors(new_rgb, extra_rgb)
|
|
|
|
|
GLib.idle_add(lambda: next(gen, False), priority=GLib.PRIORITY_LOW)
|
|
|
|
|
else:
|
|
|
|
|
self._NEW_COLOR = new_rgb
|
|
|
|
|
self._EXTRA_COLOR = extra_rgb
|
|
|
|
|
|
2021-09-19 00:23:23 +03:00
|
|
|
def init_main_paned_position(self, paned):
|
|
|
|
|
""" Initializes starting positions of main paned widgets. """
|
|
|
|
|
width = paned.get_allocated_width()
|
|
|
|
|
paned.set_position(width * 0.5)
|
|
|
|
|
self._fav_paned.set_position(width * 0.27)
|
|
|
|
|
|
2021-03-12 10:24:09 +03:00
|
|
|
@run_idle
|
|
|
|
|
def update_picons_size(self):
|
|
|
|
|
self._picons_size = self._settings.list_picon_size
|
2021-08-30 15:04:15 +03:00
|
|
|
update_picons_data(self._settings.profile_picons_path, self._picons, self._picons_size)
|
2021-03-12 10:24:09 +03:00
|
|
|
self._fav_model.foreach(lambda m, p, itr: m.set_value(itr, Column.FAV_PICON, self._picons.get(
|
|
|
|
|
self._services.get(m.get_value(itr, Column.FAV_ID)).picon_id, None)))
|
|
|
|
|
self._services_model.foreach(lambda m, p, itr: m.set_value(itr, Column.SRV_PICON, self._picons.get(
|
|
|
|
|
m.get_value(itr, Column.SRV_PICON_ID), None)))
|
|
|
|
|
|
2018-12-23 16:15:48 +03:00
|
|
|
def update_background_colors(self, new_color, extra_color):
|
|
|
|
|
if extra_color != self._EXTRA_COLOR:
|
|
|
|
|
for row in self._fav_model:
|
|
|
|
|
if row[Column.FAV_BACKGROUND]:
|
|
|
|
|
row[Column.FAV_BACKGROUND] = extra_color
|
|
|
|
|
yield True
|
|
|
|
|
|
|
|
|
|
if new_color != self._NEW_COLOR:
|
|
|
|
|
for row in self._services_model:
|
|
|
|
|
if row[Column.SRV_BACKGROUND]:
|
|
|
|
|
row[Column.SRV_BACKGROUND] = new_color
|
|
|
|
|
yield True
|
|
|
|
|
|
|
|
|
|
self._NEW_COLOR = new_color
|
|
|
|
|
self._EXTRA_COLOR = extra_color
|
|
|
|
|
yield True
|
|
|
|
|
|
2018-01-22 14:51:34 +03:00
|
|
|
def force_ctrl(self, view, event):
|
|
|
|
|
""" Function for force ctrl press event for view """
|
2020-04-19 13:23:18 +03:00
|
|
|
event.state |= MOD_MASK
|
2018-01-22 14:51:34 +03:00
|
|
|
|
2018-07-08 00:09:26 +03:00
|
|
|
def on_close_app(self, *args):
|
2020-12-19 12:36:42 +03:00
|
|
|
""" Performing operations before closing the application. """
|
|
|
|
|
# Saving the current size of the application window.
|
2021-04-12 17:08:43 +03:00
|
|
|
if not self._main_window.is_maximized():
|
|
|
|
|
self._settings.add("window_size", self._main_window.get_size())
|
2020-12-19 12:36:42 +03:00
|
|
|
|
2021-07-11 23:29:19 +03:00
|
|
|
if self._services_load_spinner.get_property("active"):
|
|
|
|
|
msg = "{}\n\n\t{}".format(get_message("Data loading in progress!"), get_message("Are you sure?"))
|
|
|
|
|
if show_dialog(DialogType.QUESTION, self._main_window, msg) == Gtk.ResponseType.CANCEL:
|
|
|
|
|
return True
|
|
|
|
|
|
2020-03-07 18:33:51 +03:00
|
|
|
if self._recorder:
|
|
|
|
|
if self._recorder.is_record():
|
|
|
|
|
msg = "{}\n\n\t{}".format(get_message("Recording in progress!"), get_message("Are you sure?"))
|
|
|
|
|
if show_dialog(DialogType.QUESTION, self._main_window, msg) == Gtk.ResponseType.CANCEL:
|
|
|
|
|
return True
|
|
|
|
|
self._recorder.release()
|
|
|
|
|
|
2020-06-04 11:32:53 +03:00
|
|
|
if not self.is_data_saved():
|
|
|
|
|
gen = self.save_data(lambda: GLib.idle_add(self.quit))
|
|
|
|
|
GLib.idle_add(lambda: next(gen, False), priority=GLib.PRIORITY_LOW)
|
|
|
|
|
return True
|
|
|
|
|
else:
|
|
|
|
|
GLib.idle_add(self.quit)
|
2017-11-09 19:01:09 +03:00
|
|
|
|
2021-09-13 16:52:19 +03:00
|
|
|
def on_main_window_state(self, window, event):
|
|
|
|
|
if event.new_window_state & Gdk.WindowState.FULLSCREEN or event.new_window_state & Gdk.WindowState.MAXIMIZED:
|
|
|
|
|
# Saving the current size of the application window.
|
|
|
|
|
self._settings.add("window_size", self._main_window.get_size())
|
|
|
|
|
|
2018-07-12 11:57:02 +03:00
|
|
|
@run_idle
|
2020-04-19 13:23:18 +03:00
|
|
|
def on_about_app(self, action, value=None):
|
2018-04-06 16:02:16 +03:00
|
|
|
show_dialog(DialogType.ABOUT, self._main_window)
|
2017-11-09 19:01:09 +03:00
|
|
|
|
2019-01-30 14:39:58 +03:00
|
|
|
@run_idle
|
2017-11-09 19:01:09 +03:00
|
|
|
def move_items(self, key):
|
2017-12-25 19:50:35 +03:00
|
|
|
""" Move items in fav or bouquets tree view """
|
2018-04-06 16:02:16 +03:00
|
|
|
if self._services_view.is_focus():
|
2017-12-25 19:50:35 +03:00
|
|
|
return
|
2018-04-16 18:50:48 +03:00
|
|
|
move_items(key, self._fav_view if self._fav_view.is_focus() else self._bouquets_view)
|
2017-11-09 19:01:09 +03:00
|
|
|
|
2021-08-15 15:42:27 +03:00
|
|
|
# ************** Pages initialization *************** #
|
|
|
|
|
|
|
|
|
|
def on_satellites_realize(self, box):
|
|
|
|
|
self._satellite_tool = SatellitesTool(self, self._settings)
|
|
|
|
|
box.pack_start(self._satellite_tool, True, True, 0)
|
|
|
|
|
|
|
|
|
|
def on_picons_realize(self, box):
|
|
|
|
|
ids = {}
|
|
|
|
|
if self._s_type is SettingsType.ENIGMA_2:
|
|
|
|
|
for r in self._services_model:
|
|
|
|
|
data = r[Column.SRV_PICON_ID].split("_")
|
|
|
|
|
ids["{}:{}:{}".format(data[3], data[5], data[6])] = r[Column.SRV_PICON_ID]
|
|
|
|
|
|
|
|
|
|
self._picon_manager = PiconManager(self, self._settings, ids, self._sat_positions)
|
|
|
|
|
box.pack_start(self._picon_manager, True, True, 0)
|
|
|
|
|
|
2021-09-01 00:05:23 +03:00
|
|
|
def on_epg_realize(self, box):
|
|
|
|
|
self._epg_box = EpgBox(self, self._http_api)
|
|
|
|
|
box.pack_start(self._epg_box, True, True, 0)
|
|
|
|
|
|
2021-09-01 14:15:39 +03:00
|
|
|
def on_timers_realize(self, box):
|
|
|
|
|
self._epg_box = TimersBox(self, self._http_api)
|
|
|
|
|
box.pack_start(self._epg_box, True, True, 0)
|
|
|
|
|
|
2021-09-02 12:20:29 +03:00
|
|
|
def on_recordings_realize(self, box):
|
|
|
|
|
self._recordings_box = RecordingsBox(self, self._http_api, self._settings)
|
|
|
|
|
box.pack_start(self._recordings_box, True, True, 0)
|
2021-09-13 16:52:19 +03:00
|
|
|
self._player_box.connect("play", self._recordings_box.on_playback)
|
|
|
|
|
self._player_box.connect("playback-close", self._recordings_box.on_playback_close)
|
2021-09-02 12:20:29 +03:00
|
|
|
|
2021-08-15 15:42:27 +03:00
|
|
|
def on_ftp_realize(self, box):
|
|
|
|
|
self._ftp_client = FtpClientBox(self, self._settings)
|
|
|
|
|
box.pack_start(self._ftp_client, True, True, 0)
|
|
|
|
|
|
|
|
|
|
def on_control_realize(self, box: Gtk.HBox):
|
|
|
|
|
self._control_box = ControlBox(self, self._http_api, self._settings)
|
|
|
|
|
box.pack_start(self._control_box, True, True, 0)
|
|
|
|
|
|
|
|
|
|
def on_visible_page(self, stack, param):
|
2021-08-31 14:16:14 +03:00
|
|
|
self._page = Page(stack.get_visible_child_name())
|
2021-09-01 14:15:39 +03:00
|
|
|
self._fav_paned.set_visible(self._page in self._fav_pages)
|
2021-08-31 14:16:14 +03:00
|
|
|
self._save_tool_button.set_visible(self._page in (Page.SERVICES, Page.SATELLITE))
|
2021-09-01 14:15:39 +03:00
|
|
|
self.emit("page-changed", self._page)
|
2021-08-15 15:42:27 +03:00
|
|
|
|
2021-08-21 14:50:05 +03:00
|
|
|
def on_page_show(self, action, value):
|
|
|
|
|
action.set_state(value)
|
|
|
|
|
self._settings.add(action.get_name(), bool(value))
|
|
|
|
|
|
2021-08-15 15:42:27 +03:00
|
|
|
# ***************** Copy - Cut - Paste ********************* #
|
2019-03-19 21:44:05 +03:00
|
|
|
|
2018-09-18 10:35:10 +03:00
|
|
|
def on_services_copy(self, view):
|
|
|
|
|
self.on_copy(view, target=ViewTarget.FAV)
|
|
|
|
|
|
|
|
|
|
def on_fav_copy(self, view):
|
|
|
|
|
self.on_copy(view, target=ViewTarget.SERVICES)
|
|
|
|
|
|
2018-09-20 16:36:03 +03:00
|
|
|
def on_bouquets_copy(self, view):
|
|
|
|
|
self.on_copy(view, target=ViewTarget.BOUQUET)
|
|
|
|
|
|
|
|
|
|
def on_copy(self, view, target):
|
2017-11-09 19:01:09 +03:00
|
|
|
model, paths = view.get_selection().get_selected_rows()
|
2018-09-20 16:36:03 +03:00
|
|
|
|
2018-09-18 10:35:10 +03:00
|
|
|
if target is ViewTarget.FAV:
|
2018-12-17 18:31:57 +03:00
|
|
|
self._rows_buffer.extend((0, *model.get(model.get_iter(path), Column.SRV_CODED, Column.SRV_SERVICE,
|
|
|
|
|
Column.SRV_LOCKED, Column.SRV_HIDE, Column.SRV_TYPE, Column.SRV_POS,
|
|
|
|
|
Column.SRV_FAV_ID, Column.SRV_PICON), None, None) for path in paths)
|
2018-09-18 10:35:10 +03:00
|
|
|
elif target is ViewTarget.SERVICES:
|
2018-10-09 13:16:49 +03:00
|
|
|
self._rows_buffer.extend(model[path][:] for path in paths)
|
2018-09-18 14:40:24 +03:00
|
|
|
elif target is ViewTarget.BOUQUET:
|
2018-09-20 16:36:03 +03:00
|
|
|
to_copy = list(map(model.get_iter, filter(lambda p: p.get_depth() == 2, paths)))
|
|
|
|
|
if to_copy:
|
|
|
|
|
self._bouquets_buffer.extend([model[i][:] for i in to_copy])
|
2017-11-09 19:01:09 +03:00
|
|
|
|
2018-09-20 16:36:03 +03:00
|
|
|
def on_fav_cut(self, view):
|
|
|
|
|
self.on_cut(view, ViewTarget.FAV)
|
|
|
|
|
|
|
|
|
|
def on_bouquets_cut(self, view):
|
|
|
|
|
self.on_cut(view, ViewTarget.BOUQUET)
|
|
|
|
|
|
|
|
|
|
def on_cut(self, view, target=None):
|
|
|
|
|
if target is ViewTarget.FAV:
|
|
|
|
|
for row in tuple(self.on_delete(view)):
|
|
|
|
|
self._rows_buffer.append(row)
|
|
|
|
|
elif target is ViewTarget.BOUQUET:
|
|
|
|
|
model, paths = view.get_selection().get_selected_rows()
|
|
|
|
|
to_cut = list(map(model.get_iter, filter(lambda p: p.get_depth() == 2, paths)))
|
|
|
|
|
if to_cut:
|
|
|
|
|
self._bouquets_buffer.extend([model[i][:] for i in to_cut])
|
|
|
|
|
list(map(model.remove, to_cut))
|
|
|
|
|
|
|
|
|
|
def on_fav_paste(self, view):
|
|
|
|
|
self.on_paste(view, ViewTarget.FAV)
|
|
|
|
|
|
|
|
|
|
def on_bouquets_paste(self, view):
|
|
|
|
|
self.on_paste(view, ViewTarget.BOUQUET)
|
|
|
|
|
|
|
|
|
|
def on_paste(self, view, target):
|
2017-11-09 19:01:09 +03:00
|
|
|
selection = view.get_selection()
|
2018-09-20 16:36:03 +03:00
|
|
|
|
|
|
|
|
if target is ViewTarget.FAV:
|
|
|
|
|
self.fav_paste(selection)
|
|
|
|
|
elif target is ViewTarget.BOUQUET:
|
|
|
|
|
self.bouquet_paste(selection)
|
2019-05-29 14:31:44 +03:00
|
|
|
self.on_view_focus(view)
|
2018-09-20 16:36:03 +03:00
|
|
|
|
|
|
|
|
def fav_paste(self, selection):
|
2017-11-09 19:01:09 +03:00
|
|
|
dest_index = 0
|
2018-04-10 11:15:50 +03:00
|
|
|
bq_selected = self.check_bouquet_selection()
|
2017-11-09 19:01:09 +03:00
|
|
|
if not bq_selected:
|
|
|
|
|
return
|
|
|
|
|
|
2018-04-06 16:02:16 +03:00
|
|
|
fav_bouquet = self._bouquets[bq_selected]
|
2017-11-09 19:01:09 +03:00
|
|
|
model, paths = selection.get_selected_rows()
|
|
|
|
|
|
|
|
|
|
if paths:
|
|
|
|
|
dest_index = int(paths[0][0])
|
|
|
|
|
|
2018-04-06 16:02:16 +03:00
|
|
|
for row in self._rows_buffer:
|
2017-11-09 19:01:09 +03:00
|
|
|
dest_index += 1
|
|
|
|
|
model.insert(dest_index, row)
|
2019-02-09 12:46:06 +03:00
|
|
|
fav_bouquet.insert(dest_index, row[Column.FAV_ID])
|
2017-11-09 19:01:09 +03:00
|
|
|
|
2020-04-19 13:23:18 +03:00
|
|
|
if model.get_name() == self.FAV_MODEL_NAME:
|
2017-11-09 19:01:09 +03:00
|
|
|
self.update_fav_num_column(model)
|
|
|
|
|
|
2018-04-06 16:02:16 +03:00
|
|
|
self._rows_buffer.clear()
|
2018-09-20 16:36:03 +03:00
|
|
|
|
|
|
|
|
def bouquet_paste(self, selection):
|
|
|
|
|
model, paths = selection.get_selected_rows()
|
|
|
|
|
if len(paths) > 1:
|
2021-08-15 17:24:30 +03:00
|
|
|
self.show_error_message("Please, select only one item!")
|
2018-09-20 16:36:03 +03:00
|
|
|
return
|
|
|
|
|
|
|
|
|
|
path = paths[0]
|
|
|
|
|
dest_iter = model.get_iter(path)
|
|
|
|
|
|
|
|
|
|
if path.get_depth() == 1:
|
|
|
|
|
list(map(lambda r: model.append(dest_iter, r), self._bouquets_buffer))
|
|
|
|
|
self._bouquets_view.expand_all()
|
|
|
|
|
else:
|
|
|
|
|
p_iter = model.iter_parent(dest_iter)
|
|
|
|
|
dest_index = path.get_indices()[1] + 1
|
|
|
|
|
for index, row in enumerate(self._bouquets_buffer):
|
|
|
|
|
model.insert(p_iter, dest_index + index, row)
|
|
|
|
|
self._bouquets_buffer.clear()
|
2018-09-20 18:37:47 +03:00
|
|
|
self.update_bouquets_type()
|
2018-09-20 16:36:03 +03:00
|
|
|
|
|
|
|
|
# ***************** Deletion *********************#
|
2017-11-09 19:01:09 +03:00
|
|
|
|
2018-10-09 13:16:49 +03:00
|
|
|
def on_delete(self, view):
|
|
|
|
|
""" Delete selected items from view
|
2017-11-09 19:01:09 +03:00
|
|
|
|
|
|
|
|
returns deleted rows list!
|
|
|
|
|
"""
|
2021-07-26 09:45:05 +03:00
|
|
|
if self._services_load_spinner.get_property("active"):
|
|
|
|
|
show_dialog(DialogType.ERROR, self._main_window, get_message("Data loading in progress!"))
|
|
|
|
|
return
|
|
|
|
|
|
2018-10-09 13:16:49 +03:00
|
|
|
selection = view.get_selection()
|
|
|
|
|
model, paths = selection.get_selected_rows()
|
|
|
|
|
model_name = get_base_model(model).get_name()
|
|
|
|
|
itrs = [model.get_iter(path) for path in paths]
|
|
|
|
|
rows = [model[in_itr][:] for in_itr in itrs]
|
|
|
|
|
|
2020-04-28 11:23:22 +03:00
|
|
|
if len(itrs) > self.DEL_FACTOR:
|
|
|
|
|
self._wait_dialog.show("Deleting data...")
|
|
|
|
|
|
2020-05-10 21:17:24 +03:00
|
|
|
priority = GLib.PRIORITY_LOW
|
|
|
|
|
|
2020-04-19 13:23:18 +03:00
|
|
|
if model_name == self.FAV_MODEL_NAME:
|
2020-04-28 11:23:22 +03:00
|
|
|
gen = self.remove_favs(itrs, model)
|
2020-04-19 13:23:18 +03:00
|
|
|
elif model_name == self.BQ_MODEL_NAME:
|
2020-04-28 11:23:22 +03:00
|
|
|
gen = self.delete_bouquets(itrs, model)
|
2020-05-10 21:17:24 +03:00
|
|
|
priority = GLib.PRIORITY_DEFAULT
|
2020-04-19 13:23:18 +03:00
|
|
|
elif model_name == self.SERVICE_MODEL_NAME:
|
2020-04-28 11:23:22 +03:00
|
|
|
gen = self.delete_services(itrs, model, rows)
|
2021-01-08 23:01:16 +03:00
|
|
|
elif model_name == self.ALT_MODEL_NAME:
|
|
|
|
|
gen = self.delete_alts(itrs, model, rows)
|
2018-10-09 13:16:49 +03:00
|
|
|
|
2020-05-10 21:17:24 +03:00
|
|
|
GLib.idle_add(lambda: next(gen, False), priority=priority)
|
2019-05-29 14:31:44 +03:00
|
|
|
self.on_view_focus(view)
|
2018-10-09 13:16:49 +03:00
|
|
|
|
|
|
|
|
return rows
|
2017-11-09 19:01:09 +03:00
|
|
|
|
2018-09-27 22:02:35 +03:00
|
|
|
def remove_favs(self, itrs, model):
|
2018-01-26 15:15:39 +03:00
|
|
|
""" Deleting bouquet services """
|
2018-12-11 19:09:55 +03:00
|
|
|
if self._bq_selected:
|
|
|
|
|
fav_bouquet = self._bouquets.get(self._bq_selected, None)
|
2018-09-27 22:02:35 +03:00
|
|
|
if fav_bouquet:
|
2020-04-28 11:23:22 +03:00
|
|
|
for index, itr in enumerate(itrs):
|
2018-09-27 22:02:35 +03:00
|
|
|
del fav_bouquet[int(model.get_path(itr)[0])]
|
|
|
|
|
self._fav_model.remove(itr)
|
2020-04-28 11:23:22 +03:00
|
|
|
if index % self.DEL_FACTOR == 0:
|
|
|
|
|
yield True
|
2018-09-27 22:02:35 +03:00
|
|
|
self.update_fav_num_column(model)
|
2020-04-28 11:23:22 +03:00
|
|
|
|
2021-07-25 22:33:00 +03:00
|
|
|
self.on_model_changed(self._fav_model)
|
2020-04-28 11:23:22 +03:00
|
|
|
self._wait_dialog.hide()
|
2018-10-13 10:48:39 +03:00
|
|
|
yield True
|
2018-01-26 15:15:39 +03:00
|
|
|
|
2018-09-27 22:02:35 +03:00
|
|
|
def delete_services(self, itrs, model, rows):
|
2017-11-16 01:24:16 +03:00
|
|
|
""" Deleting services """
|
2020-07-09 22:29:33 +03:00
|
|
|
for index, s_itr in enumerate(get_base_itrs(itrs, model)):
|
2018-04-06 16:02:16 +03:00
|
|
|
self._services_model.remove(s_itr)
|
2020-04-28 11:23:22 +03:00
|
|
|
if index % self.DEL_FACTOR == 0:
|
|
|
|
|
yield True
|
2018-01-26 15:15:39 +03:00
|
|
|
|
2018-04-06 17:57:04 +03:00
|
|
|
srv_ids_to_delete = set()
|
2017-11-16 01:24:16 +03:00
|
|
|
for row in rows:
|
|
|
|
|
# There are channels with the same parameters except for the name.
|
|
|
|
|
# None because it can have duplicates! Need fix
|
2018-12-18 19:23:44 +03:00
|
|
|
fav_id = row[Column.SRV_FAV_ID]
|
2018-04-06 16:02:16 +03:00
|
|
|
for bq in self._bouquets:
|
|
|
|
|
services = self._bouquets[bq]
|
2017-11-26 14:55:57 +03:00
|
|
|
if services:
|
|
|
|
|
with suppress(ValueError):
|
|
|
|
|
services.remove(fav_id)
|
2018-04-06 17:57:04 +03:00
|
|
|
srv_ids_to_delete.add(fav_id)
|
2018-04-06 16:02:16 +03:00
|
|
|
self._services.pop(fav_id, None)
|
2017-11-16 01:24:16 +03:00
|
|
|
|
2018-12-18 19:23:44 +03:00
|
|
|
for f_itr in filter(lambda r: r[Column.FAV_ID] in srv_ids_to_delete, self._fav_model):
|
2018-04-06 17:57:04 +03:00
|
|
|
self._fav_model.remove(f_itr.iter)
|
2020-04-28 11:23:22 +03:00
|
|
|
|
2021-07-25 22:33:00 +03:00
|
|
|
self.on_model_changed(self._services_model)
|
2018-04-06 17:57:04 +03:00
|
|
|
self.update_fav_num_column(self._fav_model)
|
2018-09-27 22:02:35 +03:00
|
|
|
self.update_sat_positions()
|
2020-04-28 11:23:22 +03:00
|
|
|
self._wait_dialog.hide()
|
2018-10-13 10:48:39 +03:00
|
|
|
yield True
|
2017-11-16 01:24:16 +03:00
|
|
|
|
2018-09-21 11:15:20 +03:00
|
|
|
def delete_bouquets(self, itrs, model):
|
2018-01-26 15:15:39 +03:00
|
|
|
""" Deleting bouquets """
|
2018-04-06 16:02:16 +03:00
|
|
|
if len(itrs) == 1 and len(model.get_path(itrs[0])) < 2:
|
2021-08-15 17:24:30 +03:00
|
|
|
self.show_error_message("This item is not allowed to be removed!")
|
2018-04-06 16:02:16 +03:00
|
|
|
return
|
|
|
|
|
|
2018-01-26 15:15:39 +03:00
|
|
|
for itr in itrs:
|
|
|
|
|
if len(model.get_path(itr)) < 2:
|
2018-04-06 16:02:16 +03:00
|
|
|
continue
|
2018-09-21 11:09:40 +03:00
|
|
|
|
2018-04-06 16:02:16 +03:00
|
|
|
self._fav_model.clear()
|
2020-05-10 21:17:24 +03:00
|
|
|
yield True
|
2020-02-10 19:24:48 +03:00
|
|
|
b_row = self._bouquets_model[itr][:]
|
|
|
|
|
self._bouquets.pop("{}:{}".format(b_row[Column.BQ_NAME], b_row[Column.BQ_TYPE]), None)
|
2018-04-06 16:02:16 +03:00
|
|
|
self._bouquets_model.remove(itr)
|
2018-01-05 14:32:14 +03:00
|
|
|
|
2020-09-24 23:17:15 +03:00
|
|
|
self._bq_selected = ""
|
|
|
|
|
self._bq_name_label.set_text(self._bq_selected)
|
2021-07-25 22:33:00 +03:00
|
|
|
self.on_model_changed(model)
|
2020-04-28 11:23:22 +03:00
|
|
|
self._wait_dialog.hide()
|
|
|
|
|
yield True
|
|
|
|
|
|
2018-09-20 16:36:03 +03:00
|
|
|
# ***************** ####### *********************#
|
|
|
|
|
|
2018-01-05 14:32:14 +03:00
|
|
|
def get_bouquet_file_name(self, bouquet):
|
2019-12-22 20:42:29 +03:00
|
|
|
bouquet_file_name = "{}userbouquet.{}.{}".format(self._settings.get(self._s_type).get("data_dir_path"),
|
2018-01-05 14:32:14 +03:00
|
|
|
*bouquet.split(":"))
|
|
|
|
|
return bouquet_file_name
|
2017-11-14 19:20:16 +03:00
|
|
|
|
2017-11-09 19:01:09 +03:00
|
|
|
def on_new_bouquet(self, view):
|
|
|
|
|
""" Creates a new item in the bouquets tree """
|
|
|
|
|
model, paths = view.get_selection().get_selected_rows()
|
|
|
|
|
|
|
|
|
|
if paths:
|
|
|
|
|
itr = model.get_iter(paths[0])
|
2018-01-25 16:11:52 +03:00
|
|
|
bq_type = model.get_value(itr, 3)
|
2017-11-09 19:01:09 +03:00
|
|
|
bq_name = "bouquet"
|
|
|
|
|
count = 0
|
|
|
|
|
key = "{}:{}".format(bq_name, bq_type)
|
|
|
|
|
# Generating name of new bouquet
|
2018-04-06 16:02:16 +03:00
|
|
|
while key in self._bouquets:
|
2017-11-09 19:01:09 +03:00
|
|
|
count += 1
|
|
|
|
|
bq_name = "bouquet{}".format(count)
|
|
|
|
|
key = "{}:{}".format(bq_name, bq_type)
|
|
|
|
|
|
2018-04-06 16:02:16 +03:00
|
|
|
response = show_dialog(DialogType.INPUT, self._main_window, bq_name)
|
2017-11-09 19:01:09 +03:00
|
|
|
if response == Gtk.ResponseType.CANCEL:
|
|
|
|
|
return
|
|
|
|
|
|
2018-01-05 14:53:53 +03:00
|
|
|
bq = response, None, None, bq_type
|
2017-12-02 20:59:25 +03:00
|
|
|
key = "{}:{}".format(response, bq_type)
|
2020-01-29 14:50:02 +03:00
|
|
|
|
|
|
|
|
while key in self._bouquets:
|
2021-08-15 17:24:30 +03:00
|
|
|
self.show_error_message(get_message("A bouquet with that name exists!"))
|
2020-01-29 14:50:02 +03:00
|
|
|
response = show_dialog(DialogType.INPUT, self._main_window, bq_name)
|
|
|
|
|
if response == Gtk.ResponseType.CANCEL:
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
key = "{}:{}".format(response, bq_type)
|
|
|
|
|
bq = response, None, None, bq_type
|
|
|
|
|
|
2018-04-06 16:02:16 +03:00
|
|
|
self._current_bq_name = response
|
2017-11-09 19:01:09 +03:00
|
|
|
|
|
|
|
|
if model.iter_n_children(itr): # parent
|
2017-11-18 20:49:53 +03:00
|
|
|
ch_itr = model.insert(itr, 0, bq)
|
2017-12-25 19:50:35 +03:00
|
|
|
scroll_to(model.get_path(ch_itr), view, paths)
|
2017-11-09 19:01:09 +03:00
|
|
|
else:
|
2017-11-18 20:49:53 +03:00
|
|
|
p_itr = model.iter_parent(itr)
|
|
|
|
|
it = model.insert(p_itr, int(model.get_path(itr)[1]) + 1, bq) if p_itr else model.append(itr, bq)
|
2017-12-25 19:50:35 +03:00
|
|
|
scroll_to(model.get_path(it), view, paths)
|
2018-04-06 16:02:16 +03:00
|
|
|
self._bouquets[key] = []
|
2017-11-09 19:01:09 +03:00
|
|
|
|
2020-02-11 13:18:14 +03:00
|
|
|
def on_edit(self, *args):
|
2018-12-17 18:31:57 +03:00
|
|
|
""" Edit header bar button """
|
2018-04-06 16:02:16 +03:00
|
|
|
if self._services_view.is_focus():
|
|
|
|
|
self.on_service_edit(self._services_view)
|
|
|
|
|
elif self._fav_view.is_focus():
|
|
|
|
|
self.on_service_edit(self._fav_view)
|
|
|
|
|
elif self._bouquets_view.is_focus():
|
|
|
|
|
self.on_rename(self._bouquets_view)
|
2017-11-18 20:49:53 +03:00
|
|
|
|
2018-10-13 22:27:32 +03:00
|
|
|
def on_to_fav_copy(self, view):
|
|
|
|
|
""" Copy items from main to beginning of fav list """
|
2017-11-09 19:01:09 +03:00
|
|
|
selection = self.get_selection(view)
|
|
|
|
|
if selection:
|
2018-04-06 16:02:16 +03:00
|
|
|
self.receive_selection(view=self._fav_view, drop_info=None, data=selection)
|
2018-11-05 14:23:33 +03:00
|
|
|
scroll_to(0, self._fav_view)
|
2017-11-09 19:01:09 +03:00
|
|
|
|
2018-10-13 22:27:32 +03:00
|
|
|
def on_to_fav_end_copy(self, view):
|
|
|
|
|
""" Copy items from main to end of fav list """
|
|
|
|
|
selection = self.get_selection(view)
|
|
|
|
|
if selection:
|
|
|
|
|
pos = Gtk.TreeViewDropPosition.AFTER
|
|
|
|
|
path = Gtk.TreePath.new()
|
|
|
|
|
mod_len = len(self._fav_model)
|
2018-10-16 14:12:07 +03:00
|
|
|
info = None
|
|
|
|
|
if mod_len > 0:
|
|
|
|
|
path.append_index(mod_len - 1)
|
|
|
|
|
info = (path, pos)
|
|
|
|
|
self.receive_selection(view=self._fav_view, drop_info=info, data=selection)
|
2018-10-13 22:27:32 +03:00
|
|
|
if mod_len > 0:
|
|
|
|
|
scroll_to(mod_len, self._fav_view)
|
|
|
|
|
|
2018-10-13 10:48:39 +03:00
|
|
|
@run_with_delay(1)
|
2018-09-19 11:46:41 +03:00
|
|
|
def update_fav_num_column(self, model):
|
|
|
|
|
""" Iterate through model and updates values for Num column """
|
2018-09-22 19:14:47 +03:00
|
|
|
gen = self.update_num_column(model)
|
|
|
|
|
GLib.idle_add(lambda: next(gen, False), priority=GLib.PRIORITY_LOW)
|
|
|
|
|
|
|
|
|
|
def update_num_column(self, model):
|
2020-06-22 11:07:44 +03:00
|
|
|
num = 0
|
|
|
|
|
for row in model:
|
|
|
|
|
is_marker = row[Column.FAV_TYPE] in self._marker_types
|
|
|
|
|
if not is_marker:
|
|
|
|
|
num += 1
|
|
|
|
|
row[Column.FAV_NUM] = 0 if is_marker else num
|
2021-07-26 09:45:05 +03:00
|
|
|
|
|
|
|
|
self.on_model_changed(model)
|
2018-10-13 10:48:39 +03:00
|
|
|
yield True
|
2018-09-19 11:46:41 +03:00
|
|
|
|
|
|
|
|
def update_bouquet_list(self):
|
|
|
|
|
""" Update bouquet after move items """
|
2018-12-11 19:09:55 +03:00
|
|
|
if self._bq_selected:
|
|
|
|
|
fav_bouquet = self._bouquets[self._bq_selected]
|
2018-09-19 11:46:41 +03:00
|
|
|
fav_bouquet.clear()
|
|
|
|
|
for row in self._fav_model:
|
2018-12-19 14:43:43 +03:00
|
|
|
fav_bouquet.append(row[Column.FAV_ID])
|
2018-09-19 11:46:41 +03:00
|
|
|
|
2020-07-09 22:29:33 +03:00
|
|
|
# ** Bouquet details sort [sorting model not used!] ** #
|
|
|
|
|
|
|
|
|
|
def on_fav_sort(self, column):
|
|
|
|
|
""" Bouquet details (FAV) list sorting by clicking on column header. """
|
|
|
|
|
if not len(self._fav_model):
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
bq = self._bouquets.get(self._bq_selected, None)
|
|
|
|
|
if not bq:
|
|
|
|
|
return
|
|
|
|
|
|
2020-07-17 09:51:45 +03:00
|
|
|
msg = "Are you sure you want to change the order\n\t of services in this bouquet?"
|
2020-07-09 22:29:33 +03:00
|
|
|
if show_dialog(DialogType.QUESTION, self._main_window, msg) != Gtk.ResponseType.OK:
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
c_num = Column.FAV_NUM
|
|
|
|
|
c_name = column.get_name()
|
|
|
|
|
|
|
|
|
|
if c_name == "fav_service_column":
|
|
|
|
|
c_num = Column.FAV_SERVICE
|
|
|
|
|
elif c_name == "fav_type_column":
|
|
|
|
|
c_num = Column.FAV_TYPE
|
|
|
|
|
elif c_name == "fav_pos_column":
|
|
|
|
|
c_num = Column.FAV_POS
|
|
|
|
|
|
|
|
|
|
order = column.get_sort_order()
|
|
|
|
|
if not column.get_sort_indicator():
|
|
|
|
|
self.reset_view_sort_indication(self._fav_view)
|
|
|
|
|
column.set_sort_indicator(True)
|
|
|
|
|
else:
|
|
|
|
|
order = not order
|
|
|
|
|
column.set_sort_order(not column.get_sort_order())
|
|
|
|
|
|
|
|
|
|
model, paths = self._fav_view.get_selection().get_selected_rows()
|
|
|
|
|
|
|
|
|
|
if len(paths) < 2 and len(bq) > self.FAV_FACTOR or len(paths) > self.FAV_FACTOR:
|
|
|
|
|
self._wait_dialog.show(get_message("Sorting data..."))
|
|
|
|
|
GLib.idle_add(self.sort_fav, c_num, bq, paths, order, 0 if c_num == Column.FAV_NUM else "")
|
|
|
|
|
|
|
|
|
|
def sort_fav(self, c_num, bq, paths, rev=False, nv=""):
|
|
|
|
|
""" Sorting function for the bouquet details list.
|
|
|
|
|
|
|
|
|
|
@param c_num: column number
|
|
|
|
|
@param bq: current bouquet
|
|
|
|
|
@param paths: selected paths
|
|
|
|
|
@param rev: sort reverse.
|
|
|
|
|
@param nv: default value for the None items.
|
|
|
|
|
If the number of selected items is more than one, then only these items will be sorted!
|
|
|
|
|
"""
|
|
|
|
|
rows = self._fav_model if len(paths) < 2 else [self._fav_model[p] for p in paths]
|
|
|
|
|
index = int(str(rows[0].path))
|
2021-01-06 22:46:12 +03:00
|
|
|
columns = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
|
2020-07-09 22:29:33 +03:00
|
|
|
|
2021-02-13 15:04:54 +03:00
|
|
|
for s_row, row in zip(sorted(map(
|
|
|
|
|
lambda r: r[:], rows),
|
|
|
|
|
key=lambda r: r[c_num] or nv if c_num != Column.FAV_POS else self.get_pos_num(r[c_num]),
|
|
|
|
|
reverse=rev), rows):
|
2021-01-06 22:46:12 +03:00
|
|
|
self._fav_model.set(row.iter, columns, s_row)
|
2020-07-09 22:29:33 +03:00
|
|
|
bq[index] = s_row[Column.FAV_ID]
|
|
|
|
|
index += 1
|
|
|
|
|
|
|
|
|
|
self._wait_dialog.hide()
|
|
|
|
|
self._fav_view.grab_focus()
|
|
|
|
|
|
|
|
|
|
def reset_view_sort_indication(self, view):
|
|
|
|
|
for column in view.get_columns():
|
|
|
|
|
column.set_sort_indicator(False)
|
|
|
|
|
column.set_sort_order(Gtk.SortType.ASCENDING)
|
|
|
|
|
|
2021-02-13 15:04:54 +03:00
|
|
|
def position_sort_func(self, model, iter1, iter2, column):
|
|
|
|
|
""" Custom sort function for position column. """
|
|
|
|
|
return self.get_pos_num(model.get_value(iter1, column)) - self.get_pos_num(model.get_value(iter2, column))
|
|
|
|
|
|
|
|
|
|
def get_pos_num(self, pos):
|
|
|
|
|
""" Returns num [float] representation of satellite position. """
|
|
|
|
|
if not pos:
|
|
|
|
|
return -183.0
|
|
|
|
|
|
|
|
|
|
if len(pos) > 1:
|
|
|
|
|
m = -1 if pos[-1] == "W" else 1
|
|
|
|
|
return float(pos[:-1]) * m
|
|
|
|
|
|
|
|
|
|
return -181.0 if pos == "T" else -182.0
|
|
|
|
|
|
2020-04-06 16:50:11 +03:00
|
|
|
# ********************* Hints *************************#
|
|
|
|
|
|
2020-04-19 17:20:51 +03:00
|
|
|
def on_fav_view_query_tooltip(self, view, x, y, keyboard_mode, tooltip):
|
|
|
|
|
""" Sets detailed info about service in the tooltip [fav view]. """
|
2020-04-06 16:50:11 +03:00
|
|
|
result = view.get_dest_row_at_pos(x, y)
|
|
|
|
|
if not result or not self._settings.show_bq_hints:
|
|
|
|
|
return False
|
|
|
|
|
|
2020-04-19 17:20:51 +03:00
|
|
|
return self.get_tooltip(view, result, tooltip)
|
|
|
|
|
|
|
|
|
|
def on_services_view_query_tooltip(self, view, x, y, keyboard_mode, tooltip):
|
|
|
|
|
""" Sets short info about service in the tooltip [main services view]. """
|
|
|
|
|
result = view.get_dest_row_at_pos(x, y)
|
|
|
|
|
if not result or not self._settings.show_srv_hints:
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
return self.get_tooltip(view, result, tooltip, target=ViewTarget.SERVICES)
|
|
|
|
|
|
|
|
|
|
def get_tooltip(self, view, dest_row, tooltip, target=ViewTarget.FAV):
|
|
|
|
|
path, pos = dest_row
|
2020-04-06 16:50:11 +03:00
|
|
|
model = view.get_model()
|
|
|
|
|
|
2020-04-19 17:20:51 +03:00
|
|
|
target_column = Column.FAV_ID if target is ViewTarget.FAV else Column.SRV_FAV_ID
|
|
|
|
|
srv = self._services.get(model[path][target_column], None)
|
2020-04-06 16:50:11 +03:00
|
|
|
if srv and srv.picon_id:
|
2021-08-30 15:04:15 +03:00
|
|
|
tooltip.set_icon(get_picon_pixbuf(self._settings.profile_picons_path + srv.picon_id,
|
2021-03-12 10:24:09 +03:00
|
|
|
size=self._settings.tooltip_logo_size))
|
2020-04-19 17:20:51 +03:00
|
|
|
tooltip.set_text(
|
|
|
|
|
self.get_hint_for_bq_list(srv) if target is ViewTarget.FAV else self.get_hint_for_srv_list(srv))
|
2020-04-06 16:50:11 +03:00
|
|
|
view.set_tooltip_row(tooltip, path)
|
|
|
|
|
return True
|
|
|
|
|
return False
|
|
|
|
|
|
2020-04-19 17:20:51 +03:00
|
|
|
def get_hint_for_bq_list(self, srv):
|
2020-04-06 16:50:11 +03:00
|
|
|
""" Returns detailed info about service as formatted string for using as hint. """
|
2020-04-19 17:20:51 +03:00
|
|
|
header, ref = self.get_hint_header_info(srv)
|
2020-04-06 16:50:11 +03:00
|
|
|
|
|
|
|
|
if srv.service_type == "IPTV":
|
|
|
|
|
return "{}{}".format(header, ref)
|
|
|
|
|
|
|
|
|
|
pol = ", {}: {},".format(get_message("Pol"), srv.pol) if srv.pol else ","
|
|
|
|
|
fec = "{}: {}".format("FEC", srv.fec) if srv.fec else ","
|
2020-09-12 11:33:17 +03:00
|
|
|
ht = "{}{}: {}\n{}: {}\n{}: {}\n{}: {}{} {}, {}\n{}"
|
2020-04-06 16:50:11 +03:00
|
|
|
return ht.format(header,
|
|
|
|
|
get_message("Package"), srv.package,
|
|
|
|
|
get_message("System"), srv.system,
|
|
|
|
|
get_message("Freq"), srv.freq,
|
2020-09-12 11:33:17 +03:00
|
|
|
get_message("Rate"), srv.rate, pol, fec, self.get_ssid_info(srv),
|
2020-04-06 16:50:11 +03:00
|
|
|
ref)
|
|
|
|
|
|
2020-04-19 17:20:51 +03:00
|
|
|
def get_hint_for_srv_list(self, srv):
|
|
|
|
|
""" Returns short info about service as formatted string for using as hint. """
|
2020-09-12 11:33:17 +03:00
|
|
|
header, ref = self.get_hint_header_info(srv)
|
|
|
|
|
return "{}{}\n{}".format(header, self.get_ssid_info(srv), ref)
|
2020-04-19 17:20:51 +03:00
|
|
|
|
|
|
|
|
def get_hint_header_info(self, srv):
|
|
|
|
|
header = "{}: {}\n{}: {}\n".format(get_message("Name"), srv.service, get_message("Type"), srv.service_type)
|
|
|
|
|
ref = "{}: {}".format(get_message("Service reference"), srv.picon_id.rstrip(".png"))
|
|
|
|
|
return header, ref
|
|
|
|
|
|
2020-09-12 11:33:17 +03:00
|
|
|
def get_ssid_info(self, srv):
|
|
|
|
|
""" Returns SID representation in hex and dec formats. """
|
2020-09-24 23:17:15 +03:00
|
|
|
sid = srv.ssid or "0"
|
2020-09-12 11:33:17 +03:00
|
|
|
try:
|
2020-09-24 23:17:15 +03:00
|
|
|
dec = "{0:04d}".format(int(sid, 16))
|
2020-09-12 11:33:17 +03:00
|
|
|
except ValueError as e:
|
|
|
|
|
log("SID value conversion error: {}".format(e))
|
|
|
|
|
else:
|
2020-09-24 23:17:15 +03:00
|
|
|
return "SID: 0x{} ({})".format(sid.upper(), dec)
|
2020-09-12 11:33:17 +03:00
|
|
|
|
2020-09-24 23:17:15 +03:00
|
|
|
return "SID: 0x{}".format(sid.upper())
|
2020-09-12 11:33:17 +03:00
|
|
|
|
2018-09-19 11:46:41 +03:00
|
|
|
# ***************** Drag-and-drop *********************#
|
|
|
|
|
|
2018-09-22 21:08:28 +03:00
|
|
|
def on_view_drag_begin(self, view, context):
|
2020-09-24 23:17:15 +03:00
|
|
|
""" Sets its own icon for dragging.
|
|
|
|
|
|
|
|
|
|
We have to use "connect_after" (after="yes" in xml) to override what the default handler did.
|
|
|
|
|
https://lazka.github.io/pgi-docs/Gtk-3.0/classes/Widget.html#Gtk.Widget.signals.drag_begin
|
|
|
|
|
"""
|
2021-01-14 23:05:31 +03:00
|
|
|
top_model, paths = view.get_selection().get_selected_rows()
|
2020-09-24 23:17:15 +03:00
|
|
|
if len(paths) < 1:
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
name, model = get_model_data(view)
|
|
|
|
|
name_column, type_column = Column.SRV_SERVICE, Column.SRV_TYPE
|
|
|
|
|
if name == self.FAV_MODEL_NAME:
|
|
|
|
|
name_column, type_column = Column.FAV_SERVICE, Column.FAV_TYPE
|
|
|
|
|
elif name == self.BQ_MODEL_NAME:
|
|
|
|
|
name_column, type_column = Column.BQ_NAME, Column.BQ_TYPE
|
2021-01-10 14:05:01 +03:00
|
|
|
elif name == self.ALT_MODEL_NAME:
|
|
|
|
|
name_column, type_column = Column.ALT_SERVICE, Column.ALT_TYPE
|
2020-09-24 23:17:15 +03:00
|
|
|
# https://stackoverflow.com/a/52248549
|
2021-01-14 23:05:31 +03:00
|
|
|
Gtk.drag_set_icon_pixbuf(context, self.get_drag_icon_pixbuf(top_model, paths, name_column, type_column), 0, 0)
|
2020-09-30 20:54:03 +03:00
|
|
|
return True
|
2020-09-24 23:17:15 +03:00
|
|
|
|
2020-09-30 20:54:03 +03:00
|
|
|
def on_view_drag_end(self, view, context):
|
2020-09-24 23:17:15 +03:00
|
|
|
self._select_enabled = True
|
|
|
|
|
view.get_selection().unselect_all()
|
|
|
|
|
|
2020-09-30 20:54:03 +03:00
|
|
|
def get_drag_icon_pixbuf(self, model, paths, text_column, type_column):
|
|
|
|
|
""" Creates and returns Pixbuf for a dragging icon. """
|
|
|
|
|
import cairo
|
|
|
|
|
|
|
|
|
|
window = Gtk.OffscreenWindow()
|
|
|
|
|
window.get_style_context().add_class(Gtk.STYLE_CLASS_DND)
|
2020-09-24 23:17:15 +03:00
|
|
|
frame = Gtk.Frame()
|
|
|
|
|
list_box = Gtk.ListBox()
|
2020-09-30 20:54:03 +03:00
|
|
|
list_box.set_selection_mode(Gtk.SelectionMode.NONE)
|
2020-09-24 23:17:15 +03:00
|
|
|
padding = 10
|
2020-09-30 20:54:03 +03:00
|
|
|
|
2020-09-24 23:17:15 +03:00
|
|
|
for index, row in enumerate([model[p] for p in paths]):
|
|
|
|
|
if index == 25:
|
|
|
|
|
list_box.add(Gtk.Arrow(Gtk.ArrowType.DOWN))
|
|
|
|
|
break
|
|
|
|
|
|
|
|
|
|
h_box = Gtk.HBox()
|
|
|
|
|
h_box.set_spacing(10)
|
2020-09-30 20:54:03 +03:00
|
|
|
h_box.get_style_context().add_class(Gtk.STYLE_CLASS_LIST_ROW)
|
2020-09-24 23:17:15 +03:00
|
|
|
label = Gtk.Label(row[text_column])
|
|
|
|
|
label.set_alignment(0, 0)
|
|
|
|
|
label.set_padding(padding, 2)
|
|
|
|
|
h_box.add(label)
|
|
|
|
|
label = Gtk.Label(row[type_column])
|
|
|
|
|
label.set_halign(Gtk.Align.END)
|
|
|
|
|
label.set_padding(padding, 2)
|
|
|
|
|
h_box.add(label)
|
|
|
|
|
list_box.add(h_box)
|
|
|
|
|
|
|
|
|
|
if len(paths) > 1:
|
|
|
|
|
list_box.add(Gtk.Separator())
|
|
|
|
|
h_box = Gtk.HBox()
|
|
|
|
|
h_box.set_spacing(2)
|
|
|
|
|
img = Gtk.Image.new_from_icon_name("document-properties", 0)
|
|
|
|
|
h_box.add(img)
|
|
|
|
|
h_box.add(Gtk.Label(len(paths)))
|
|
|
|
|
h_box.set_halign(Gtk.Align.START)
|
|
|
|
|
h_box.set_margin_left(10)
|
|
|
|
|
h_box.set_margin_bottom(5)
|
|
|
|
|
h_box.set_margin_top(2)
|
|
|
|
|
list_box.add(h_box)
|
|
|
|
|
|
|
|
|
|
frame.add(list_box)
|
|
|
|
|
frame.show_all()
|
2020-09-30 20:54:03 +03:00
|
|
|
window.add(frame)
|
|
|
|
|
window.show()
|
|
|
|
|
alloc = frame.get_allocation()
|
|
|
|
|
w, h = alloc.width, alloc.height
|
|
|
|
|
surf = cairo.ImageSurface(cairo.FORMAT_ARGB32, w, h)
|
|
|
|
|
frame.draw(cairo.Context(surf))
|
|
|
|
|
pix = Gdk.pixbuf_get_from_surface(surf, 0, 0, w, h)
|
|
|
|
|
window.destroy()
|
2020-09-24 23:17:15 +03:00
|
|
|
|
2020-09-30 20:54:03 +03:00
|
|
|
return pix
|
2018-09-22 21:08:28 +03:00
|
|
|
|
2018-09-19 11:46:41 +03:00
|
|
|
def on_view_drag_data_get(self, view, drag_context, data, info, time):
|
2018-09-20 11:06:33 +03:00
|
|
|
selection = self.get_selection(view)
|
|
|
|
|
if selection:
|
|
|
|
|
data.set_text(selection, -1)
|
2018-09-19 11:46:41 +03:00
|
|
|
|
2020-02-28 20:59:53 +03:00
|
|
|
def on_services_view_drag_drop(self, view, drag_context, x, y, time):
|
|
|
|
|
view.stop_emission_by_name("drag_drop")
|
|
|
|
|
# https://stackoverflow.com/q/7661016 [Some data was dropped, get the data!]
|
2020-04-26 19:26:36 +03:00
|
|
|
targets = drag_context.list_targets()
|
|
|
|
|
view.drag_get_data(drag_context, targets[-1] if targets else Gdk.atom_intern("text/plain", False), time)
|
2020-02-28 20:59:53 +03:00
|
|
|
|
|
|
|
|
def on_services_view_drag_data_received(self, view, drag_context, x, y, data, info, time):
|
|
|
|
|
# Needs for the GtkTreeView when using models [filter, sort]
|
|
|
|
|
# that don't support the GtkTreeDragDest interface.
|
|
|
|
|
view.stop_emission_by_name("drag_data_received")
|
|
|
|
|
self.on_view_drag_data_received(view, drag_context, x, y, data, info, time)
|
|
|
|
|
|
2018-09-19 11:46:41 +03:00
|
|
|
def on_view_drag_data_received(self, view, drag_context, x, y, data, info, time):
|
2020-02-28 20:59:53 +03:00
|
|
|
txt = data.get_text()
|
|
|
|
|
uris = data.get_uris()
|
|
|
|
|
if txt:
|
2020-09-24 23:17:15 +03:00
|
|
|
name, model = get_model_data(view)
|
|
|
|
|
if txt.startswith("file://") and name == self.SERVICE_MODEL_NAME:
|
2020-09-19 12:32:08 +03:00
|
|
|
self.on_import_data(urlparse(unquote(txt)).path.strip())
|
2020-09-24 23:17:15 +03:00
|
|
|
elif name == self.FAV_MODEL_NAME:
|
2020-09-19 12:32:08 +03:00
|
|
|
self.receive_selection(view=view, drop_info=view.get_dest_row_at_pos(x, y), data=txt)
|
2020-06-01 21:36:56 +03:00
|
|
|
elif len(uris) == 2:
|
2020-06-06 09:36:11 +03:00
|
|
|
self.picons_buffer = self.on_assign_picon(view, urlparse(unquote(uris[0])).path,
|
2020-09-19 12:32:08 +03:00
|
|
|
urlparse(unquote(uris[1])).path + os.sep)
|
2020-09-30 20:54:03 +03:00
|
|
|
drag_context.finish(True, False, time)
|
2018-09-19 11:46:41 +03:00
|
|
|
|
|
|
|
|
def on_bq_view_drag_data_received(self, view, drag_context, x, y, data, info, time):
|
2018-11-02 23:13:31 +03:00
|
|
|
model_name, model = get_model_data(view)
|
2018-09-19 11:46:41 +03:00
|
|
|
drop_info = view.get_dest_row_at_pos(x, y)
|
|
|
|
|
data = data.get_text()
|
2018-09-20 11:06:33 +03:00
|
|
|
if not data:
|
|
|
|
|
return
|
2020-07-12 11:11:32 +03:00
|
|
|
|
|
|
|
|
if data.startswith("file://"):
|
|
|
|
|
self.on_import_bouquet(None, file_path=urlparse(unquote(data)).path.strip())
|
|
|
|
|
return
|
|
|
|
|
|
2020-11-19 18:05:40 +03:00
|
|
|
itr_str, sep, source = data.partition(self.DRAG_SEP)
|
2020-04-19 13:23:18 +03:00
|
|
|
if source != self.BQ_MODEL_NAME:
|
2018-09-19 11:46:41 +03:00
|
|
|
return
|
|
|
|
|
|
|
|
|
|
if drop_info:
|
|
|
|
|
path, position = drop_info
|
|
|
|
|
itrs = [model.get_iter_from_string(itr) for itr in itr_str.split(",")]
|
|
|
|
|
top_iter = model.get_iter(path)
|
2018-09-20 11:06:33 +03:00
|
|
|
parent_itr = model.iter_parent(top_iter) # parent
|
|
|
|
|
to_del = []
|
|
|
|
|
if parent_itr:
|
|
|
|
|
p_path = model.get_path(parent_itr)[0]
|
|
|
|
|
for itr in itrs:
|
|
|
|
|
p_itr = model.iter_parent(itr)
|
|
|
|
|
if not p_itr:
|
|
|
|
|
break
|
|
|
|
|
if p_itr and model.get_path(p_itr)[0] == p_path:
|
2020-09-24 23:17:15 +03:00
|
|
|
model.move_after(itr, top_iter)
|
|
|
|
|
top_iter = itr
|
2018-09-20 11:06:33 +03:00
|
|
|
else:
|
|
|
|
|
model.insert(parent_itr, model.get_path(top_iter)[1], model[itr][:])
|
|
|
|
|
to_del.append(itr)
|
|
|
|
|
elif not model.iter_has_child(top_iter):
|
|
|
|
|
for itr in itrs:
|
|
|
|
|
model.append(top_iter, model[itr][:])
|
|
|
|
|
to_del.append(itr)
|
|
|
|
|
view.expand_all()
|
|
|
|
|
|
|
|
|
|
list(map(model.remove, to_del))
|
2018-09-20 18:37:47 +03:00
|
|
|
self.update_bouquets_type()
|
2018-09-19 11:46:41 +03:00
|
|
|
|
2017-11-09 19:01:09 +03:00
|
|
|
def get_selection(self, view):
|
|
|
|
|
""" Creates a string from the iterators of the selected rows """
|
2017-11-23 16:59:21 +03:00
|
|
|
model, paths = view.get_selection().get_selected_rows()
|
2018-02-06 14:20:59 +03:00
|
|
|
model = get_base_model(model)
|
2017-11-09 19:01:09 +03:00
|
|
|
|
|
|
|
|
if len(paths) > 0:
|
|
|
|
|
itrs = [model.get_iter(path) for path in paths]
|
2018-09-19 11:46:41 +03:00
|
|
|
return "{}::::{}".format(",".join([model.get_string_from_iter(itr) for itr in itrs]), model.get_name())
|
2017-11-09 19:01:09 +03:00
|
|
|
|
|
|
|
|
def receive_selection(self, *, view, drop_info, data):
|
|
|
|
|
""" Update fav view after data received """
|
|
|
|
|
try:
|
2020-11-19 18:05:40 +03:00
|
|
|
itr_str, sep, source = data.partition(self.DRAG_SEP)
|
2020-04-19 13:23:18 +03:00
|
|
|
if source == self.BQ_MODEL_NAME:
|
2018-10-13 22:27:32 +03:00
|
|
|
return
|
|
|
|
|
|
2018-09-19 11:46:41 +03:00
|
|
|
bq_selected = self.check_bouquet_selection()
|
|
|
|
|
if not bq_selected:
|
|
|
|
|
return
|
|
|
|
|
|
2018-10-13 22:27:32 +03:00
|
|
|
model = get_base_model(view.get_model())
|
2020-06-22 19:45:30 +03:00
|
|
|
dest_index = -1
|
2020-05-17 13:08:02 +03:00
|
|
|
|
2018-10-13 22:27:32 +03:00
|
|
|
if drop_info:
|
|
|
|
|
path, position = drop_info
|
2020-05-17 13:08:02 +03:00
|
|
|
dest_index = path.get_indices()[0]
|
|
|
|
|
|
2018-04-06 16:02:16 +03:00
|
|
|
fav_bouquet = self._bouquets[bq_selected]
|
2018-09-19 11:46:41 +03:00
|
|
|
itrs = itr_str.split(",")
|
2017-11-09 19:01:09 +03:00
|
|
|
|
2020-04-19 13:23:18 +03:00
|
|
|
if source == self.SERVICE_MODEL_NAME:
|
2018-04-06 16:02:16 +03:00
|
|
|
ext_model = self._services_view.get_model()
|
2017-11-09 19:01:09 +03:00
|
|
|
ext_itrs = [ext_model.get_iter_from_string(itr) for itr in itrs]
|
2018-01-24 13:39:11 +03:00
|
|
|
ext_rows = [ext_model[ext_itr][:] for ext_itr in ext_itrs]
|
2020-07-09 22:29:33 +03:00
|
|
|
|
2017-11-09 19:01:09 +03:00
|
|
|
for ext_row in ext_rows:
|
|
|
|
|
dest_index += 1
|
2018-12-16 22:44:45 +03:00
|
|
|
fav_id = ext_row[Column.SRV_FAV_ID]
|
2018-04-06 16:02:16 +03:00
|
|
|
ch = self._services[fav_id]
|
2018-12-16 22:44:45 +03:00
|
|
|
model.insert(dest_index, (0, ch.coded, ch.service, ch.locked, ch.hide, ch.service_type, ch.pos,
|
|
|
|
|
ch.fav_id, self._picons.get(ch.picon_id, None), None, None))
|
2018-02-06 14:20:59 +03:00
|
|
|
fav_bouquet.insert(dest_index, ch.fav_id)
|
2020-04-19 13:23:18 +03:00
|
|
|
elif source == self.FAV_MODEL_NAME:
|
2017-11-09 19:01:09 +03:00
|
|
|
in_itrs = [model.get_iter_from_string(itr) for itr in itrs]
|
2018-01-25 16:11:52 +03:00
|
|
|
in_rows = [model[in_itr][:] for in_itr in in_itrs]
|
2017-11-09 19:01:09 +03:00
|
|
|
for row in in_rows:
|
|
|
|
|
model.insert(dest_index, row)
|
2018-12-16 22:44:45 +03:00
|
|
|
fav_bouquet.insert(dest_index, row[Column.FAV_ID])
|
2020-05-17 13:08:02 +03:00
|
|
|
dest_index += 1
|
2017-11-09 19:01:09 +03:00
|
|
|
for in_itr in in_itrs:
|
|
|
|
|
del fav_bouquet[int(model.get_path(in_itr)[0])]
|
|
|
|
|
model.remove(in_itr)
|
|
|
|
|
self.update_fav_num_column(model)
|
|
|
|
|
except ValueError as e:
|
2021-08-15 17:24:30 +03:00
|
|
|
self.show_error_message(str(e))
|
2017-11-09 19:01:09 +03:00
|
|
|
|
2018-09-22 19:14:47 +03:00
|
|
|
def on_view_press(self, view, event):
|
2020-09-24 23:17:15 +03:00
|
|
|
""" Handles a mouse click (press) to view. """
|
2018-09-22 19:14:47 +03:00
|
|
|
if event.get_event_type() == Gdk.EventType.BUTTON_PRESS and event.button == Gdk.BUTTON_PRIMARY:
|
2020-09-24 23:17:15 +03:00
|
|
|
target = view.get_path_at_pos(event.x, event.y)
|
|
|
|
|
# Idea taken from here: https://kevinmehall.net/2010/pygtk_multi_select_drag_drop
|
|
|
|
|
mask = not (event.state & (Gdk.ModifierType.CONTROL_MASK | Gdk.ModifierType.SHIFT_MASK))
|
|
|
|
|
if target and mask and view.get_selection().path_is_selected(target[0]):
|
|
|
|
|
self._select_enabled = False
|
|
|
|
|
|
2018-11-02 23:13:31 +03:00
|
|
|
name, model = get_model_data(view)
|
2018-09-22 19:14:47 +03:00
|
|
|
self.delete_views_selection(name)
|
|
|
|
|
|
2020-09-24 23:17:15 +03:00
|
|
|
def on_view_release(self, view, event):
|
|
|
|
|
""" Handles a mouse click (release) to view. """
|
|
|
|
|
# Enable selection.
|
|
|
|
|
self._select_enabled = True
|
|
|
|
|
|
2018-09-22 19:14:47 +03:00
|
|
|
def delete_views_selection(self, name):
|
2020-04-19 13:23:18 +03:00
|
|
|
if name == self.SERVICE_MODEL_NAME:
|
2018-09-22 19:14:47 +03:00
|
|
|
self.delete_selection(self._fav_view)
|
2020-04-19 13:23:18 +03:00
|
|
|
elif name == self.FAV_MODEL_NAME:
|
2018-09-22 19:14:47 +03:00
|
|
|
self.delete_selection(self._services_view)
|
2020-04-19 13:23:18 +03:00
|
|
|
elif name == self.BQ_MODEL_NAME:
|
2018-09-22 19:14:47 +03:00
|
|
|
self.delete_selection(self._services_view, self._fav_view)
|
|
|
|
|
|
2017-11-09 19:01:09 +03:00
|
|
|
def on_view_popup_menu(self, menu, event):
|
|
|
|
|
""" Shows popup menu for any view """
|
|
|
|
|
if event.get_event_type() == Gdk.EventType.BUTTON_PRESS and event.button == Gdk.BUTTON_SECONDARY:
|
2018-07-09 11:38:36 +03:00
|
|
|
name = Gtk.Buildable.get_name(menu)
|
|
|
|
|
if name == "services_popup_menu":
|
|
|
|
|
self.delete_selection(self._fav_view, self._bouquets_view)
|
2019-05-29 14:31:44 +03:00
|
|
|
self.on_view_focus(self._services_view)
|
2018-07-09 11:38:36 +03:00
|
|
|
elif name == "fav_popup_menu":
|
|
|
|
|
self.delete_selection(self._services_view, self._bouquets_view)
|
2019-05-29 14:31:44 +03:00
|
|
|
self.on_view_focus(self._fav_view)
|
2018-07-09 11:38:36 +03:00
|
|
|
elif name == "bouquets_popup_menu":
|
|
|
|
|
self.delete_selection(self._services_view, self._fav_view)
|
2019-05-29 14:31:44 +03:00
|
|
|
self.on_view_focus(self._bouquets_view)
|
2018-07-09 11:38:36 +03:00
|
|
|
|
2017-11-09 19:01:09 +03:00
|
|
|
menu.popup(None, None, None, None, event.button, event.time)
|
2018-09-22 19:14:47 +03:00
|
|
|
return True
|
2017-11-09 19:01:09 +03:00
|
|
|
|
2020-02-11 13:18:14 +03:00
|
|
|
def on_download(self, action=None, value=None):
|
2020-06-04 11:32:53 +03:00
|
|
|
dialog = DownloadDialog(self._main_window, self._settings, self.open_data, self.update_settings)
|
|
|
|
|
|
|
|
|
|
if not self.is_data_saved():
|
|
|
|
|
gen = self.save_data(dialog.show)
|
|
|
|
|
GLib.idle_add(lambda: next(gen, False), priority=GLib.PRIORITY_LOW)
|
|
|
|
|
else:
|
|
|
|
|
dialog.show()
|
2019-03-18 23:03:42 +03:00
|
|
|
|
|
|
|
|
@run_task
|
2020-02-11 13:18:14 +03:00
|
|
|
def on_download_data(self, *args):
|
2019-03-18 23:03:42 +03:00
|
|
|
try:
|
2019-12-13 13:31:07 +03:00
|
|
|
download_data(settings=self._settings,
|
2019-03-18 23:03:42 +03:00
|
|
|
download_type=DownloadType.ALL,
|
|
|
|
|
callback=lambda x: print(x, end=""))
|
|
|
|
|
except Exception as e:
|
2020-07-11 12:58:03 +03:00
|
|
|
msg = "Downloading data error: {}"
|
|
|
|
|
log(msg.format(e), debug=self._settings.debug_mode, fmt_message=msg)
|
2021-08-15 17:24:30 +03:00
|
|
|
self.show_error_message(str(e))
|
2019-03-18 23:03:42 +03:00
|
|
|
else:
|
|
|
|
|
GLib.idle_add(self.open_data)
|
|
|
|
|
|
|
|
|
|
def on_upload_data(self, download_type):
|
2020-07-11 12:58:03 +03:00
|
|
|
if not self.is_data_saved():
|
|
|
|
|
gen = self.save_data(lambda: self.on_upload_data(download_type))
|
|
|
|
|
GLib.idle_add(lambda: next(gen, False), priority=GLib.PRIORITY_LOW)
|
|
|
|
|
else:
|
|
|
|
|
self.upload_data(download_type)
|
|
|
|
|
|
|
|
|
|
@run_task
|
|
|
|
|
def upload_data(self, download_type):
|
2019-03-18 23:03:42 +03:00
|
|
|
try:
|
2019-12-22 20:42:29 +03:00
|
|
|
profile = self._s_type
|
2019-12-13 13:31:07 +03:00
|
|
|
opts = self._settings
|
2019-12-22 20:42:29 +03:00
|
|
|
use_http = profile is SettingsType.ENIGMA_2
|
2019-03-18 23:03:42 +03:00
|
|
|
|
2019-12-22 20:42:29 +03:00
|
|
|
if profile is SettingsType.ENIGMA_2:
|
2020-12-29 22:41:28 +03:00
|
|
|
host, port, user, password = opts.host, opts.http_port, opts.user, opts.password
|
2019-03-18 23:03:42 +03:00
|
|
|
try:
|
2020-01-03 23:26:55 +03:00
|
|
|
test_http(host, port, user, password, use_ssl=opts.http_use_ssl, skip_message=True)
|
2020-01-06 13:17:56 +03:00
|
|
|
except (TestException, HttpApiException):
|
2019-03-18 23:03:42 +03:00
|
|
|
use_http = False
|
|
|
|
|
|
2019-12-13 13:31:07 +03:00
|
|
|
upload_data(settings=opts,
|
2019-03-18 23:03:42 +03:00
|
|
|
download_type=download_type,
|
|
|
|
|
remove_unused=True,
|
|
|
|
|
callback=lambda x: print(x, end=""),
|
|
|
|
|
use_http=use_http)
|
|
|
|
|
except Exception as e:
|
2020-07-11 12:58:03 +03:00
|
|
|
msg = "Uploading data error: {}"
|
|
|
|
|
log(msg.format(e), debug=self._settings.debug_mode, fmt_message=msg)
|
2021-08-15 17:24:30 +03:00
|
|
|
self.show_error_message(str(e))
|
2019-03-19 00:12:33 +03:00
|
|
|
|
2020-02-11 13:18:14 +03:00
|
|
|
def on_data_open(self, action=None, value=None):
|
2020-09-17 17:16:00 +03:00
|
|
|
""" Opening data via "File/Open". """
|
2021-09-05 13:58:26 +03:00
|
|
|
if self._page is Page.SERVICES or self._page is Page.INFO:
|
2021-08-31 14:16:14 +03:00
|
|
|
response = show_dialog(DialogType.CHOOSER, self._main_window, settings=self._settings, title="Open folder")
|
|
|
|
|
if response in (Gtk.ResponseType.CANCEL, Gtk.ResponseType.DELETE_EVENT):
|
|
|
|
|
return
|
|
|
|
|
self.open_data(response)
|
|
|
|
|
elif self._page is Page.SATELLITE:
|
|
|
|
|
self._satellite_tool.on_open()
|
2021-09-05 13:58:26 +03:00
|
|
|
elif self._page is Page.PICONS:
|
|
|
|
|
self._picon_manager.on_open()
|
2020-09-17 17:16:00 +03:00
|
|
|
|
|
|
|
|
def on_archive_open(self, action=None, value=None):
|
|
|
|
|
""" Opening the data archive via "File/Open archive". """
|
|
|
|
|
file_filter = Gtk.FileFilter()
|
|
|
|
|
file_filter.set_name("*.zip, *.gz")
|
|
|
|
|
file_filter.add_mime_type("application/zip")
|
|
|
|
|
file_filter.add_mime_type("application/gzip")
|
|
|
|
|
|
|
|
|
|
response = show_dialog(DialogType.CHOOSER, self._main_window,
|
|
|
|
|
action_type=Gtk.FileChooserAction.OPEN,
|
|
|
|
|
file_filter=file_filter,
|
|
|
|
|
settings=self._settings,
|
|
|
|
|
title="Open archive")
|
2018-01-11 17:59:59 +03:00
|
|
|
if response in (Gtk.ResponseType.CANCEL, Gtk.ResponseType.DELETE_EVENT):
|
2017-11-09 19:01:09 +03:00
|
|
|
return
|
2017-12-12 09:16:58 +03:00
|
|
|
self.open_data(response)
|
2017-11-09 19:01:09 +03:00
|
|
|
|
2020-01-09 13:14:49 +03:00
|
|
|
def open_data(self, data_path=None, callback=None):
|
2017-11-09 19:01:09 +03:00
|
|
|
""" Opening data and fill views. """
|
2020-09-17 17:16:00 +03:00
|
|
|
if data_path and os.path.isfile(data_path):
|
|
|
|
|
self.open_compressed_data(data_path)
|
|
|
|
|
else:
|
|
|
|
|
gen = self.update_data(data_path, callback)
|
|
|
|
|
GLib.idle_add(lambda: next(gen, False), priority=GLib.PRIORITY_LOW)
|
|
|
|
|
|
|
|
|
|
def open_compressed_data(self, data_path):
|
|
|
|
|
""" Opening archived data. """
|
2020-09-19 12:32:08 +03:00
|
|
|
arch_path = self.get_archive_path(data_path)
|
|
|
|
|
if arch_path:
|
|
|
|
|
gen = self.update_data("{}{}".format(arch_path.name, os.sep), callback=arch_path.cleanup)
|
|
|
|
|
GLib.idle_add(lambda: next(gen, False), priority=GLib.PRIORITY_LOW)
|
|
|
|
|
|
|
|
|
|
def get_archive_path(self, data_path):
|
|
|
|
|
""" Returns the temp dir path for the extracted data, or None if the archive format is not supported. """
|
2020-09-17 17:16:00 +03:00
|
|
|
import zipfile
|
|
|
|
|
import tarfile
|
|
|
|
|
import tempfile
|
|
|
|
|
|
|
|
|
|
tmp_path = tempfile.TemporaryDirectory()
|
2020-09-19 12:32:08 +03:00
|
|
|
tmp_path_name = tmp_path.name
|
2020-09-17 17:16:00 +03:00
|
|
|
|
|
|
|
|
if zipfile.is_zipfile(data_path):
|
|
|
|
|
with zipfile.ZipFile(data_path) as zip_file:
|
|
|
|
|
for zip_info in zip_file.infolist():
|
2020-09-19 12:32:08 +03:00
|
|
|
if not zip_info.filename.endswith(os.sep):
|
2020-09-17 17:16:00 +03:00
|
|
|
zip_info.filename = os.path.basename(zip_info.filename)
|
|
|
|
|
zip_file.extract(zip_info, path=tmp_path_name)
|
|
|
|
|
elif tarfile.is_tarfile(data_path):
|
|
|
|
|
with tarfile.open(data_path) as tar:
|
|
|
|
|
for mb in tar.getmembers():
|
|
|
|
|
if mb.isfile():
|
|
|
|
|
mb.name = os.path.basename(mb.name)
|
|
|
|
|
tar.extract(mb, path=tmp_path_name)
|
|
|
|
|
else:
|
|
|
|
|
tmp_path.cleanup()
|
2020-09-19 12:32:08 +03:00
|
|
|
log("Error getting the path for the archive. Unsupported file format: {}".format(data_path))
|
2021-08-15 17:24:30 +03:00
|
|
|
self.show_error_message("Unsupported format!")
|
2020-09-17 17:16:00 +03:00
|
|
|
return
|
|
|
|
|
|
2020-09-19 12:32:08 +03:00
|
|
|
return tmp_path
|
2017-12-12 09:16:58 +03:00
|
|
|
|
2020-01-09 13:14:49 +03:00
|
|
|
def update_data(self, data_path, callback=None):
|
2020-01-12 00:33:33 +03:00
|
|
|
self._profile_combo_box.set_sensitive(False)
|
2021-01-05 23:07:15 +03:00
|
|
|
self._alt_revealer.set_visible(False)
|
2021-08-04 08:37:52 +03:00
|
|
|
self._filter_header_button.set_active(False)
|
2019-08-01 01:05:30 +03:00
|
|
|
self._wait_dialog.show()
|
|
|
|
|
|
2019-08-08 21:15:43 +03:00
|
|
|
yield from self.clear_current_data()
|
2020-11-25 16:27:48 +03:00
|
|
|
# Reset of sorting
|
|
|
|
|
self._services_view.get_model().reset_default_sort_func()
|
|
|
|
|
self.reset_view_sort_indication(self._services_view)
|
|
|
|
|
self.reset_view_sort_indication(self._fav_view)
|
2019-08-01 01:05:30 +03:00
|
|
|
|
2017-11-09 19:01:09 +03:00
|
|
|
try:
|
2020-02-10 17:00:46 +03:00
|
|
|
current_profile = self._profile_combo_box.get_active_text()
|
2020-05-03 00:22:16 +03:00
|
|
|
if not current_profile:
|
2021-08-15 17:24:30 +03:00
|
|
|
self.show_error_message("No profile selected!")
|
2020-05-03 00:22:16 +03:00
|
|
|
return
|
|
|
|
|
|
2020-02-10 17:00:46 +03:00
|
|
|
if current_profile != self._settings.current_profile:
|
|
|
|
|
self.init_profiles(self._settings.current_profile)
|
|
|
|
|
|
2021-08-30 15:04:15 +03:00
|
|
|
data_path = self._settings.profile_data_path if data_path is None else data_path
|
|
|
|
|
local_path = self._settings.profile_data_path
|
2020-05-03 00:22:16 +03:00
|
|
|
os.makedirs(os.path.dirname(local_path), exist_ok=True)
|
|
|
|
|
|
|
|
|
|
if data_path != local_path:
|
2020-04-28 14:49:10 +03:00
|
|
|
from shutil import copyfile
|
|
|
|
|
|
|
|
|
|
for f in STC_XML_FILE:
|
|
|
|
|
xml_src = data_path + f
|
|
|
|
|
if os.path.isfile(xml_src):
|
2020-05-03 00:22:16 +03:00
|
|
|
copyfile(xml_src, local_path + f)
|
2020-04-16 11:55:48 +03:00
|
|
|
|
2019-12-22 20:42:29 +03:00
|
|
|
prf = self._s_type
|
2018-08-18 11:35:44 +03:00
|
|
|
black_list = get_blacklist(data_path)
|
2019-12-13 13:31:07 +03:00
|
|
|
bouquets = get_bouquets(data_path, prf)
|
2019-08-01 01:05:30 +03:00
|
|
|
yield True
|
2019-12-22 20:42:29 +03:00
|
|
|
services = get_services(data_path, prf, self.get_format_version() if prf is SettingsType.ENIGMA_2 else 0)
|
2019-08-01 01:05:30 +03:00
|
|
|
yield True
|
2021-08-30 15:04:15 +03:00
|
|
|
update_picons_data(self._settings.profile_picons_path, self._picons, self._picons_size)
|
2019-08-01 01:05:30 +03:00
|
|
|
yield True
|
2017-11-09 19:01:09 +03:00
|
|
|
except FileNotFoundError as e:
|
2019-03-19 21:44:05 +03:00
|
|
|
msg = get_message("Please, download files from receiver or setup your path for read data!")
|
2021-08-15 17:24:30 +03:00
|
|
|
self.show_error_message(getattr(e, "message", str(e)) + "\n\n" + msg)
|
2019-08-01 01:05:30 +03:00
|
|
|
return
|
2017-12-20 16:46:15 +03:00
|
|
|
except SyntaxError as e:
|
2021-08-15 17:24:30 +03:00
|
|
|
self.show_error_message(str(e))
|
2019-08-01 01:05:30 +03:00
|
|
|
return
|
2018-08-18 11:35:44 +03:00
|
|
|
except Exception as e:
|
2020-07-11 12:58:03 +03:00
|
|
|
msg = "Reading data error: {}"
|
|
|
|
|
log(msg.format(e), debug=self._settings.debug_mode, fmt_message=msg)
|
2021-08-15 17:24:30 +03:00
|
|
|
self.show_error_message("{}\n{}".format(get_message("Reading data error!"), e))
|
2019-08-01 01:05:30 +03:00
|
|
|
return
|
2018-08-18 11:35:44 +03:00
|
|
|
else:
|
|
|
|
|
self.append_blacklist(black_list)
|
2019-08-08 21:15:43 +03:00
|
|
|
yield from self.append_data(bouquets, services)
|
2020-01-12 00:33:33 +03:00
|
|
|
self._profile_combo_box.set_sensitive(True)
|
2020-01-09 13:14:49 +03:00
|
|
|
if callback:
|
|
|
|
|
callback()
|
2019-08-01 01:05:30 +03:00
|
|
|
yield True
|
2020-02-11 13:18:14 +03:00
|
|
|
self.on_view_focus(self._services_view)
|
|
|
|
|
yield True
|
2020-06-04 11:32:53 +03:00
|
|
|
self._data_hash = self.get_data_hash()
|
|
|
|
|
yield True
|
2021-04-09 20:00:37 +03:00
|
|
|
if self._filter_box.get_visible():
|
2021-03-26 11:46:43 +03:00
|
|
|
self.on_filter_changed()
|
|
|
|
|
yield True
|
2021-07-11 23:29:19 +03:00
|
|
|
finally:
|
|
|
|
|
self._wait_dialog.hide()
|
2017-11-09 19:01:09 +03:00
|
|
|
|
2019-08-08 21:15:43 +03:00
|
|
|
def append_data(self, bouquets, services):
|
2019-10-04 21:31:41 +03:00
|
|
|
if self._app_info_box.get_visible():
|
|
|
|
|
yield from self.show_app_info(False)
|
2019-08-08 21:15:43 +03:00
|
|
|
self.append_bouquets(bouquets)
|
|
|
|
|
yield from self.append_services(services)
|
|
|
|
|
self.update_sat_positions()
|
|
|
|
|
yield True
|
|
|
|
|
|
2019-10-04 21:31:41 +03:00
|
|
|
def show_app_info(self, visible):
|
|
|
|
|
self._app_info_box.set_visible(visible)
|
|
|
|
|
self._app_info_box.grab_focus() if visible else self._services_view.grab_focus()
|
|
|
|
|
yield True
|
|
|
|
|
|
2018-08-18 11:35:44 +03:00
|
|
|
def append_blacklist(self, black_list):
|
2017-11-25 15:55:24 +03:00
|
|
|
if black_list:
|
2018-04-06 16:02:16 +03:00
|
|
|
self._blacklist.update(black_list)
|
2017-11-25 15:55:24 +03:00
|
|
|
|
2018-08-18 11:35:44 +03:00
|
|
|
def append_bouquets(self, bqs):
|
2019-02-09 09:55:35 +03:00
|
|
|
if len(self._bouquets_model):
|
|
|
|
|
self.add_to_bouquets(bqs)
|
|
|
|
|
else:
|
2021-09-14 16:30:27 +03:00
|
|
|
allow_markers = self.bq_has_markers(bqs)
|
|
|
|
|
|
2019-02-09 09:55:35 +03:00
|
|
|
for bouquet in bqs:
|
|
|
|
|
parent = self._bouquets_model.append(None, [bouquet.name, None, None, bouquet.type])
|
|
|
|
|
for bq in bouquet.bouquets:
|
2021-09-14 16:30:27 +03:00
|
|
|
# Markers!
|
|
|
|
|
if bq.type == BqType.MARKER.value and allow_markers:
|
|
|
|
|
self.append_bouquet(bq, parent)
|
|
|
|
|
else:
|
|
|
|
|
self.append_bouquet(bq, parent)
|
|
|
|
|
|
|
|
|
|
def bq_has_markers(self, bqs):
|
|
|
|
|
"""" Checks if there are markers in the list of bouquets. """
|
|
|
|
|
msg = "Detected markers in the bouquet list!\nThis feature is not fully supported.\n\n\t Add them to the list?"
|
|
|
|
|
for bq in bqs:
|
|
|
|
|
for b in bq.bouquets:
|
|
|
|
|
if b.type == BqType.MARKER.value:
|
|
|
|
|
return show_dialog(DialogType.QUESTION, self._main_window, msg) == Gtk.ResponseType.OK
|
|
|
|
|
return True
|
2018-04-02 23:55:41 +03:00
|
|
|
|
2019-02-08 19:11:30 +03:00
|
|
|
def add_to_bouquets(self, bqs):
|
|
|
|
|
for bouquets in bqs:
|
|
|
|
|
for row in self._bouquets_model:
|
2019-02-09 12:46:06 +03:00
|
|
|
if row[Column.BQ_TYPE] == bouquets.type:
|
2019-02-08 19:11:30 +03:00
|
|
|
for bq in bouquets.bouquets:
|
|
|
|
|
self.append_bouquet(bq, row.iter)
|
|
|
|
|
|
2018-04-02 23:55:41 +03:00
|
|
|
def append_bouquet(self, bq, parent):
|
2021-01-27 16:46:37 +03:00
|
|
|
name, bq_type, locked, hidden = bq.name, bq.type, bq.locked, bq.hidden
|
2021-08-18 19:46:47 +03:00
|
|
|
bouquet = self._bouquets_model.append(parent, [name, locked, hidden, bq_type])
|
2021-01-27 16:46:37 +03:00
|
|
|
bq_id = "{}:{}".format(name, bq_type)
|
2018-04-02 23:55:41 +03:00
|
|
|
services = []
|
2018-09-09 23:38:00 +03:00
|
|
|
extra_services = {} # for services with different names in bouquet and main list
|
2018-09-01 00:49:11 +03:00
|
|
|
agr = [None] * 7
|
2018-04-02 23:55:41 +03:00
|
|
|
for srv in bq.services:
|
|
|
|
|
fav_id = srv.data
|
|
|
|
|
# IPTV and MARKER services
|
|
|
|
|
s_type = srv.type
|
2020-05-23 15:16:31 +03:00
|
|
|
if s_type in (BqServiceType.MARKER, BqServiceType.IPTV, BqServiceType.SPACE):
|
2018-09-01 00:49:11 +03:00
|
|
|
icon = None
|
|
|
|
|
picon_id = None
|
2020-07-18 20:55:15 +03:00
|
|
|
data_id = srv.num
|
|
|
|
|
locked = None
|
|
|
|
|
|
2018-09-01 00:49:11 +03:00
|
|
|
if s_type is BqServiceType.IPTV:
|
|
|
|
|
icon = IPTV_ICON
|
2020-07-18 20:55:15 +03:00
|
|
|
fav_id_data = fav_id.lstrip().split(":")
|
|
|
|
|
if len(fav_id_data) > 10:
|
|
|
|
|
data_id = ":".join(fav_id_data[:11])
|
|
|
|
|
picon_id = "{}_{}_{}_{}_{}_{}_{}_{}_{}_{}.png".format(*fav_id_data[:10])
|
|
|
|
|
locked = LOCKED_ICON if data_id in self._blacklist else None
|
|
|
|
|
srv = Service(None, None, icon, srv.name, locked, None, None, s_type.name,
|
|
|
|
|
self._picons.get(picon_id, None), picon_id, *agr, data_id, fav_id, None)
|
2018-04-06 16:02:16 +03:00
|
|
|
self._services[fav_id] = srv
|
2021-01-05 23:07:15 +03:00
|
|
|
elif s_type is BqServiceType.ALT:
|
2021-01-27 16:46:37 +03:00
|
|
|
self._alt_file.add("{}:{}".format(srv.data, bq_type))
|
2021-01-05 23:07:15 +03:00
|
|
|
srv = Service(None, None, None, srv.name, locked, None, None, s_type.name,
|
2021-01-27 16:46:37 +03:00
|
|
|
None, None, *agr, srv.data, fav_id, srv.num)
|
2021-01-05 23:07:15 +03:00
|
|
|
self._services[fav_id] = srv
|
2021-08-18 19:46:47 +03:00
|
|
|
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)
|
2018-09-09 23:38:00 +03:00
|
|
|
elif srv.name:
|
|
|
|
|
extra_services[fav_id] = srv.name
|
2018-04-02 23:55:41 +03:00
|
|
|
services.append(fav_id)
|
2018-09-09 23:38:00 +03:00
|
|
|
|
|
|
|
|
self._bouquets[bq_id] = services
|
2021-01-27 16:46:37 +03:00
|
|
|
self._bq_file[bq_id] = bq.file
|
2018-09-09 23:38:00 +03:00
|
|
|
if extra_services:
|
|
|
|
|
self._extra_bouquets[bq_id] = extra_services
|
2017-11-14 19:20:16 +03:00
|
|
|
|
2020-01-09 13:14:49 +03:00
|
|
|
@run_idle
|
|
|
|
|
def open_bouquet(self, name):
|
|
|
|
|
""" Find and open bouquet by name """
|
|
|
|
|
for r in self._bouquets_model:
|
|
|
|
|
for i in r.iterchildren():
|
|
|
|
|
if i[Column.BQ_NAME] == name:
|
|
|
|
|
self._bouquets_view.expand_row(self._bouquets_model.get_path(r.iter), Column.BQ_NAME)
|
|
|
|
|
self._bouquets_view.set_cursor(i.path)
|
|
|
|
|
self._bouquets_view.row_activated(i.path, self._bouquets_view.get_column(Column.BQ_NAME))
|
|
|
|
|
break
|
|
|
|
|
|
2018-08-18 11:35:44 +03:00
|
|
|
def append_services(self, services):
|
2018-09-21 10:16:30 +03:00
|
|
|
for srv in services:
|
2020-04-28 11:23:22 +03:00
|
|
|
# Adding channels to dict with fav_id as keys.
|
2018-09-21 10:16:30 +03:00
|
|
|
self._services[srv.fav_id] = srv
|
2019-02-09 12:46:06 +03:00
|
|
|
self.update_services_counts(len(self._services.values()))
|
2021-07-11 23:29:19 +03:00
|
|
|
self._wait_dialog.hide()
|
|
|
|
|
self._services_load_spinner.start()
|
|
|
|
|
factor = self.DEL_FACTOR
|
2018-08-18 10:21:40 +03:00
|
|
|
|
2020-04-28 11:23:22 +03:00
|
|
|
for index, srv in enumerate(services):
|
2018-12-16 17:28:07 +03:00
|
|
|
tooltip, background = None, None
|
2018-12-23 16:15:48 +03:00
|
|
|
if self._use_colors:
|
2018-12-17 18:31:57 +03:00
|
|
|
flags = srv.flags_cas
|
|
|
|
|
if flags:
|
|
|
|
|
f_flags = list(filter(lambda x: x.startswith("f:"), flags.split(",")))
|
|
|
|
|
if f_flags and Flag.is_new(int(f_flags[0][2:])):
|
2018-12-23 16:15:48 +03:00
|
|
|
background = self._NEW_COLOR
|
2018-12-16 17:28:07 +03:00
|
|
|
|
2020-04-28 11:23:22 +03:00
|
|
|
s = srv._replace(picon=self._picons.get(srv.picon_id, None)) + (tooltip, background)
|
|
|
|
|
self._services_model.append(s)
|
|
|
|
|
if index % factor == 0:
|
|
|
|
|
yield True
|
2021-07-11 23:29:19 +03:00
|
|
|
|
|
|
|
|
self._services_load_spinner.stop()
|
2020-04-28 11:23:22 +03:00
|
|
|
yield True
|
2017-11-14 19:20:16 +03:00
|
|
|
|
2018-01-04 01:23:22 +03:00
|
|
|
def clear_current_data(self):
|
|
|
|
|
""" Clearing current data from lists """
|
2021-07-26 09:45:05 +03:00
|
|
|
if len(self._services_model) > self.DEL_FACTOR * 50:
|
2020-04-28 11:23:22 +03:00
|
|
|
self._wait_dialog.set_text("Deleting data...")
|
|
|
|
|
|
2018-04-06 16:02:16 +03:00
|
|
|
self._bouquets_model.clear()
|
2019-08-01 01:05:30 +03:00
|
|
|
yield True
|
2018-04-06 16:02:16 +03:00
|
|
|
self._fav_model.clear()
|
2019-08-01 01:05:30 +03:00
|
|
|
yield True
|
|
|
|
|
for index, itr in enumerate([row.iter for row in self._services_model]):
|
|
|
|
|
self._services_model.remove(itr)
|
2020-04-28 11:23:22 +03:00
|
|
|
if index % self.DEL_FACTOR == 0:
|
2019-08-01 01:05:30 +03:00
|
|
|
yield True
|
|
|
|
|
yield True
|
2018-04-06 16:02:16 +03:00
|
|
|
self._blacklist.clear()
|
|
|
|
|
self._services.clear()
|
|
|
|
|
self._rows_buffer.clear()
|
2020-01-12 00:33:33 +03:00
|
|
|
self._picons.clear()
|
2021-01-27 16:46:37 +03:00
|
|
|
self._alt_file.clear()
|
|
|
|
|
self._alt_counter = 1
|
2018-04-06 16:02:16 +03:00
|
|
|
self._bouquets.clear()
|
2021-01-27 16:46:37 +03:00
|
|
|
self._bq_file.clear()
|
2018-09-09 23:38:00 +03:00
|
|
|
self._extra_bouquets.clear()
|
2018-07-12 11:57:02 +03:00
|
|
|
self._current_bq_name = None
|
|
|
|
|
self._bq_name_label.set_text("")
|
2018-09-27 22:02:35 +03:00
|
|
|
self.init_sat_positions()
|
2019-10-14 00:17:06 +03:00
|
|
|
self.update_services_counts()
|
2020-04-28 11:23:22 +03:00
|
|
|
self._wait_dialog.set_text(None)
|
2019-08-01 01:05:30 +03:00
|
|
|
yield True
|
2018-01-04 01:23:22 +03:00
|
|
|
|
2017-11-09 19:01:09 +03:00
|
|
|
def on_data_save(self, *args):
|
2021-08-31 14:16:14 +03:00
|
|
|
if self._page is Page.SERVICES:
|
|
|
|
|
self.on_services_save()
|
|
|
|
|
elif self._page is Page.SATELLITE:
|
|
|
|
|
self._satellite_tool.on_save()
|
|
|
|
|
|
|
|
|
|
def on_data_save_as(self, action=None, value=None):
|
|
|
|
|
if self._page is Page.SERVICES:
|
|
|
|
|
self.on_services_save_as()
|
|
|
|
|
elif self._page is Page.SATELLITE:
|
|
|
|
|
self._satellite_tool.on_save_as()
|
|
|
|
|
|
|
|
|
|
def on_services_save(self):
|
2020-02-13 01:09:40 +03:00
|
|
|
if self._app_info_box.get_visible():
|
|
|
|
|
return
|
|
|
|
|
|
2019-01-05 22:53:51 +03:00
|
|
|
if len(self._bouquets_model) == 0:
|
2021-08-15 17:24:30 +03:00
|
|
|
self.show_error_message("No data to save!")
|
2019-01-05 22:53:51 +03:00
|
|
|
return
|
|
|
|
|
|
2020-06-04 11:32:53 +03:00
|
|
|
if show_dialog(DialogType.QUESTION, self._main_window) != Gtk.ResponseType.OK:
|
2017-11-09 19:01:09 +03:00
|
|
|
return
|
|
|
|
|
|
2019-08-01 01:05:30 +03:00
|
|
|
gen = self.save_data()
|
|
|
|
|
GLib.idle_add(lambda: next(gen, False), priority=GLib.PRIORITY_LOW)
|
|
|
|
|
|
2021-08-31 14:16:14 +03:00
|
|
|
def on_services_save_as(self):
|
2021-07-26 11:11:42 +03:00
|
|
|
if len(self._bouquets_model) == 0:
|
2021-08-15 17:24:30 +03:00
|
|
|
self.show_error_message("No data to save!")
|
2021-07-26 11:11:42 +03:00
|
|
|
return
|
|
|
|
|
|
|
|
|
|
response = show_dialog(DialogType.CHOOSER, self._main_window, settings=self._settings,
|
|
|
|
|
buttons=(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, Gtk.STOCK_SAVE, Gtk.ResponseType.OK),
|
|
|
|
|
create_dir=True)
|
|
|
|
|
if response in (Gtk.ResponseType.CANCEL, Gtk.ResponseType.DELETE_EVENT):
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
if os.listdir(response):
|
|
|
|
|
msg = "{}\n\n\t\t{}".format(get_message("The selected folder already contains files!"),
|
|
|
|
|
get_message("Are you sure?"))
|
|
|
|
|
if show_dialog(DialogType.QUESTION, self._main_window, msg) != Gtk.ResponseType.OK:
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
gen = self.save_data(lambda: show_dialog(DialogType.INFO, self._main_window, "Done!"), response)
|
|
|
|
|
GLib.idle_add(lambda: next(gen, False), priority=GLib.PRIORITY_LOW)
|
|
|
|
|
|
|
|
|
|
def save_data(self, callback=None, ext_path=None):
|
2021-08-20 17:24:48 +03:00
|
|
|
self._save_tool_button.set_sensitive(False)
|
2019-12-22 20:42:29 +03:00
|
|
|
profile = self._s_type
|
2021-08-30 15:04:15 +03:00
|
|
|
path = ext_path or self._settings.profile_data_path
|
|
|
|
|
backup_path = self._settings.profile_backup_path
|
2019-01-03 23:32:28 +03:00
|
|
|
# Backup data or clearing data path
|
2021-07-26 11:11:42 +03:00
|
|
|
backup_data(path, backup_path) if not ext_path and self._settings.backup_before_save else clear_data_path(path)
|
2019-08-01 01:05:30 +03:00
|
|
|
yield True
|
2018-01-25 16:11:52 +03:00
|
|
|
|
2017-11-09 19:01:09 +03:00
|
|
|
bouquets = []
|
|
|
|
|
|
|
|
|
|
def parse_bouquets(model, b_path, itr):
|
2018-01-25 16:11:52 +03:00
|
|
|
bqs = None
|
2017-11-09 19:01:09 +03:00
|
|
|
if model.iter_has_child(itr):
|
|
|
|
|
bqs = []
|
2018-01-25 16:11:52 +03:00
|
|
|
num_of_children = model.iter_n_children(itr)
|
2017-11-09 19:01:09 +03:00
|
|
|
for num in range(num_of_children):
|
2021-05-12 14:59:55 +03:00
|
|
|
bqs.append(self.get_bouquet(model.iter_nth_child(itr, num), model))
|
2018-01-25 16:11:52 +03:00
|
|
|
if len(b_path) == 1:
|
2019-02-09 12:46:06 +03:00
|
|
|
bouquets.append(Bouquets(*model.get(itr, Column.BQ_NAME, Column.BQ_TYPE), bqs if bqs else []))
|
2017-11-26 20:40:22 +03:00
|
|
|
|
2017-11-09 19:01:09 +03:00
|
|
|
# Getting bouquets
|
2018-04-06 16:02:16 +03:00
|
|
|
self._bouquets_view.get_model().foreach(parse_bouquets)
|
2020-04-16 11:55:48 +03:00
|
|
|
write_bouquets(path, bouquets, profile, self._settings.force_bq_names)
|
2019-08-01 01:05:30 +03:00
|
|
|
yield True
|
2017-11-09 19:01:09 +03:00
|
|
|
# Getting services
|
2018-09-20 18:37:47 +03:00
|
|
|
services_model = get_base_model(self._services_view.get_model())
|
2018-12-16 22:44:45 +03:00
|
|
|
services = [Service(*row[: Column.SRV_TOOLTIP]) for row in services_model]
|
2019-12-22 20:42:29 +03:00
|
|
|
write_services(path, services, profile, self.get_format_version() if profile is SettingsType.ENIGMA_2 else 0)
|
2019-08-01 01:05:30 +03:00
|
|
|
yield True
|
2021-01-05 23:07:15 +03:00
|
|
|
|
2019-12-22 20:42:29 +03:00
|
|
|
if profile is SettingsType.ENIGMA_2:
|
2021-09-14 16:30:27 +03:00
|
|
|
# Blacklist.
|
2018-04-06 16:02:16 +03:00
|
|
|
write_blacklist(path, self._blacklist)
|
2017-11-09 19:01:09 +03:00
|
|
|
|
2021-08-20 17:24:48 +03:00
|
|
|
self._save_tool_button.set_sensitive(True)
|
2019-08-01 01:05:30 +03:00
|
|
|
yield True
|
2020-06-04 11:32:53 +03:00
|
|
|
self._data_hash = self.get_data_hash()
|
|
|
|
|
yield True
|
|
|
|
|
if callback:
|
|
|
|
|
callback()
|
2019-08-01 01:05:30 +03:00
|
|
|
|
2021-05-12 14:59:55 +03:00
|
|
|
def get_bouquet(self, itr, model):
|
|
|
|
|
""" Constructs and returns Bouquet class instance. """
|
|
|
|
|
bq_name, locked, hidden, bq_type = model.get(itr, Column.BQ_NAME, Column.BQ_LOCKED, Column.BQ_HIDDEN,
|
|
|
|
|
Column.BQ_TYPE)
|
|
|
|
|
bq_id = "{}:{}".format(bq_name, bq_type)
|
2021-09-14 16:30:27 +03:00
|
|
|
favs = self._bouquets.get(bq_id, [])
|
2021-05-12 14:59:55 +03:00
|
|
|
ex_s = self._extra_bouquets.get(bq_id, None)
|
|
|
|
|
bq_s = list(filter(None, [self._services.get(f_id, None) for f_id in favs]))
|
|
|
|
|
|
|
|
|
|
if self._s_type is SettingsType.ENIGMA_2:
|
|
|
|
|
bq_s = self.get_enigma_bq_services(bq_s, ex_s)
|
|
|
|
|
|
|
|
|
|
return Bouquet(bq_name, bq_type, bq_s, locked, hidden, self._bq_file.get(bq_id, None))
|
|
|
|
|
|
2021-01-05 23:07:15 +03:00
|
|
|
def get_enigma_bq_services(self, services, ext_services):
|
|
|
|
|
""" Preparing a list of services for the Enigma2 bouquet. """
|
|
|
|
|
s_list = []
|
|
|
|
|
for srv in services:
|
|
|
|
|
if srv.service_type == BqServiceType.ALT.name:
|
|
|
|
|
# Alternatives to service in a bouquet.
|
|
|
|
|
alts = list(map(lambda s: s._replace(service=None),
|
|
|
|
|
filter(None, [self._services.get(s.data, None) for s in srv.transponder or []])))
|
|
|
|
|
s_list.append(srv._replace(transponder=alts))
|
|
|
|
|
else:
|
|
|
|
|
# Extra names for service in bouquet.
|
|
|
|
|
s_list.append(srv._replace(service=ext_services.get(srv.fav_id, None) if ext_services else None))
|
|
|
|
|
return s_list
|
|
|
|
|
|
2020-04-19 13:23:18 +03:00
|
|
|
def on_new_configuration(self, action, value=None):
|
2018-12-01 00:13:19 +03:00
|
|
|
""" Creates new empty configuration """
|
|
|
|
|
if show_dialog(DialogType.QUESTION, self._main_window) == Gtk.ResponseType.CANCEL:
|
|
|
|
|
return
|
|
|
|
|
|
2019-12-22 20:42:29 +03:00
|
|
|
gen = self.create_new_configuration(self._s_type)
|
2019-08-01 01:05:30 +03:00
|
|
|
GLib.idle_add(lambda: next(gen, False), priority=GLib.PRIORITY_LOW)
|
|
|
|
|
|
|
|
|
|
def create_new_configuration(self, profile):
|
2019-10-10 12:55:58 +03:00
|
|
|
if self._app_info_box.get_visible():
|
|
|
|
|
yield from self.show_app_info(False)
|
|
|
|
|
|
2019-08-01 01:05:30 +03:00
|
|
|
c_gen = self.clear_current_data()
|
|
|
|
|
yield from c_gen
|
2018-12-01 00:17:21 +03:00
|
|
|
|
2019-12-22 20:42:29 +03:00
|
|
|
if profile is SettingsType.ENIGMA_2:
|
2021-01-18 14:24:27 +03:00
|
|
|
parent = self._bouquets_model.append(None, ["Bouquets (TV)", None, None, BqType.TV.value])
|
2021-01-27 16:46:37 +03:00
|
|
|
self.append_bouquet(Bouquet("Favourites (TV)", BqType.TV.value, [], None, None, "favourites"), parent)
|
2021-01-18 14:24:27 +03:00
|
|
|
parent = self._bouquets_model.append(None, ["Bouquets (Radio)", None, None, BqType.RADIO.value])
|
2021-01-27 16:46:37 +03:00
|
|
|
self.append_bouquet(Bouquet("Favourites (Radio)", BqType.RADIO.value, [], None, None, "favourites"), parent)
|
2019-12-22 20:42:29 +03:00
|
|
|
elif profile is SettingsType.NEUTRINO_MP:
|
2018-12-01 00:13:19 +03:00
|
|
|
self._bouquets_model.append(None, ["Providers", None, None, BqType.BOUQUET.value])
|
|
|
|
|
self._bouquets_model.append(None, ["FAV", None, None, BqType.TV.value])
|
|
|
|
|
self._bouquets_model.append(None, ["WEBTV", None, None, BqType.WEBTV.value])
|
2020-11-02 21:55:34 +03:00
|
|
|
|
|
|
|
|
self._data_hash = self.get_data_hash()
|
2019-08-01 01:05:30 +03:00
|
|
|
yield True
|
2018-12-01 00:13:19 +03:00
|
|
|
|
2020-11-07 18:38:40 +03:00
|
|
|
def on_fav_selection(self, model, path, column):
|
2021-01-05 23:07:15 +03:00
|
|
|
row = model[path][:]
|
|
|
|
|
if row[Column.FAV_TYPE] == BqServiceType.ALT.name:
|
|
|
|
|
self._alt_model.clear()
|
2021-01-08 23:01:16 +03:00
|
|
|
a_id = row[Column.FAV_ID]
|
|
|
|
|
srv = self._services.get(a_id, None)
|
2021-01-05 23:07:15 +03:00
|
|
|
if srv:
|
2021-01-08 23:01:16 +03:00
|
|
|
for i, s in enumerate(srv[-1] or [], start=1):
|
2021-01-05 23:07:15 +03:00
|
|
|
srv = self._services.get(s.data, None)
|
|
|
|
|
if srv:
|
2021-01-08 23:01:16 +03:00
|
|
|
pic = self._picons.get(srv.picon_id, None)
|
|
|
|
|
itr = model.get_string_from_iter(model.get_iter(path))
|
|
|
|
|
self._alt_model.append((i, pic, srv.service, srv.service_type, srv.pos, srv.fav_id, a_id, itr))
|
2021-01-05 23:07:15 +03:00
|
|
|
self._alt_revealer.set_visible(True)
|
|
|
|
|
else:
|
|
|
|
|
self._alt_revealer.set_visible(False)
|
|
|
|
|
|
2021-09-01 00:05:23 +03:00
|
|
|
if self._page is Page.EPG:
|
2021-01-05 23:07:15 +03:00
|
|
|
ref = self.get_service_ref(path)
|
|
|
|
|
if not ref:
|
|
|
|
|
return
|
2021-09-01 00:05:23 +03:00
|
|
|
|
|
|
|
|
self.emit("fav-changed", ref)
|
2020-11-07 18:38:40 +03:00
|
|
|
|
2017-11-09 19:01:09 +03:00
|
|
|
def on_services_selection(self, model, path, column):
|
2017-11-23 16:59:21 +03:00
|
|
|
self.update_service_bar(model, path)
|
|
|
|
|
|
|
|
|
|
def update_service_bar(self, model, path):
|
|
|
|
|
def_val = "Unknown"
|
2019-02-09 12:46:06 +03:00
|
|
|
cas = model.get_value(model.get_iter(path), Column.SRV_CAS_FLAGS)
|
2017-12-08 18:32:28 +03:00
|
|
|
if not cas:
|
|
|
|
|
return
|
2020-05-23 15:17:45 +03:00
|
|
|
cvs = list(filter(lambda val: val.startswith("C:") and len(val) > 3, cas.split(",")))
|
2020-07-19 14:59:37 +03:00
|
|
|
self._cas_label.set_text(", ".join(map(str, sorted(set(CAS.get(v[:4].upper(), def_val) for v in cvs)))))
|
2017-11-09 19:01:09 +03:00
|
|
|
|
|
|
|
|
def on_bouquets_selection(self, model, path, column):
|
2020-07-09 22:29:33 +03:00
|
|
|
self.reset_view_sort_indication(self._fav_view)
|
2021-02-07 19:27:38 +03:00
|
|
|
self._alt_revealer.set_visible(False)
|
2018-04-06 16:02:16 +03:00
|
|
|
self._current_bq_name = model[path][0] if len(path) > 1 else None
|
2018-07-09 19:00:05 +03:00
|
|
|
self._bq_name_label.set_text(self._current_bq_name if self._current_bq_name else "")
|
2017-11-09 19:01:09 +03:00
|
|
|
|
2018-12-11 19:09:55 +03:00
|
|
|
if self._current_bq_name:
|
|
|
|
|
ch_row = model[model.get_iter(path)][:]
|
2019-02-09 12:46:06 +03:00
|
|
|
self._bq_selected = "{}:{}".format(ch_row[Column.BQ_NAME], ch_row[Column.BQ_TYPE])
|
2018-12-11 19:09:55 +03:00
|
|
|
else:
|
|
|
|
|
self._bq_selected = ""
|
|
|
|
|
|
2018-04-06 16:02:16 +03:00
|
|
|
if self._bouquets_view.row_expanded(path):
|
|
|
|
|
self._bouquets_view.collapse_row(path)
|
2017-11-09 19:01:09 +03:00
|
|
|
else:
|
2018-04-06 16:02:16 +03:00
|
|
|
self._bouquets_view.expand_row(path, column)
|
2017-11-09 19:01:09 +03:00
|
|
|
|
|
|
|
|
if len(path) > 1:
|
2020-04-28 11:23:22 +03:00
|
|
|
gen = self.update_bouquet_services(model, path)
|
2020-08-24 22:06:30 +03:00
|
|
|
GLib.idle_add(lambda: next(gen, False))
|
2019-05-29 14:31:44 +03:00
|
|
|
|
2018-04-06 16:02:16 +03:00
|
|
|
def update_bouquet_services(self, model, path, bq_key=None):
|
|
|
|
|
""" Updates list of bouquet services """
|
2017-11-09 19:01:09 +03:00
|
|
|
tree_iter = None
|
|
|
|
|
if path:
|
|
|
|
|
tree_iter = model.get_iter(path)
|
|
|
|
|
|
2019-02-09 12:46:06 +03:00
|
|
|
key = bq_key if bq_key else "{}:{}".format(*model.get(tree_iter, Column.BQ_NAME, Column.BQ_TYPE))
|
2020-04-28 11:23:22 +03:00
|
|
|
services = self._bouquets.get(key, [])
|
2018-09-09 23:38:00 +03:00
|
|
|
ex_services = self._extra_bouquets.get(key, None)
|
2020-04-28 11:23:22 +03:00
|
|
|
|
2020-08-24 22:06:30 +03:00
|
|
|
if len(services) > self.FAV_FACTOR * 20:
|
2020-07-09 22:29:33 +03:00
|
|
|
self._bouquets_view.set_sensitive(False)
|
2020-08-24 22:06:30 +03:00
|
|
|
yield True
|
2020-04-28 11:23:22 +03:00
|
|
|
|
2020-08-24 22:06:30 +03:00
|
|
|
self._fav_view.set_model(None)
|
2020-04-28 11:23:22 +03:00
|
|
|
self._fav_model.clear()
|
2017-11-09 19:01:09 +03:00
|
|
|
|
2020-06-22 11:07:44 +03:00
|
|
|
num = 0
|
|
|
|
|
for srv_id in services:
|
2018-09-09 23:38:00 +03:00
|
|
|
srv = self._services.get(srv_id, None)
|
|
|
|
|
ex_srv_name = None
|
|
|
|
|
if ex_services:
|
|
|
|
|
ex_srv_name = ex_services.get(srv_id)
|
|
|
|
|
if srv:
|
2020-04-06 16:50:11 +03:00
|
|
|
background = self._EXTRA_COLOR if self._use_colors and ex_srv_name else None
|
2020-06-22 11:07:44 +03:00
|
|
|
|
|
|
|
|
srv_type = srv.service_type
|
|
|
|
|
is_marker = srv_type in self._marker_types
|
|
|
|
|
if not is_marker:
|
|
|
|
|
num += 1
|
|
|
|
|
|
2021-01-06 22:46:12 +03:00
|
|
|
picon = self._picons.get(srv.picon_id, None)
|
|
|
|
|
# Alternatives
|
|
|
|
|
if srv.service_type == BqServiceType.ALT.name:
|
|
|
|
|
alt_servs = srv.transponder
|
|
|
|
|
if alt_servs:
|
|
|
|
|
alt_srv = self._services.get(alt_servs[0].data, None)
|
2021-05-11 00:18:27 +03:00
|
|
|
if alt_srv:
|
|
|
|
|
picon = self._picons.get(alt_srv.picon_id, None) if srv else None
|
2021-01-06 22:46:12 +03:00
|
|
|
|
2020-06-22 11:07:44 +03:00
|
|
|
self._fav_model.append((0 if is_marker else num, srv.coded, ex_srv_name if ex_srv_name else srv.service,
|
|
|
|
|
srv.locked, srv.hide, srv_type, srv.pos, srv.fav_id,
|
2021-01-06 22:46:12 +03:00
|
|
|
picon, None, background))
|
2020-04-28 11:23:22 +03:00
|
|
|
|
2020-08-24 22:06:30 +03:00
|
|
|
yield True
|
|
|
|
|
self._fav_view.set_model(self._fav_model)
|
2021-07-26 09:45:05 +03:00
|
|
|
self.on_model_changed(self._fav_model)
|
2020-08-24 22:06:30 +03:00
|
|
|
self._bouquets_view.set_sensitive(True)
|
|
|
|
|
self._bouquets_view.grab_focus()
|
2018-10-13 10:48:39 +03:00
|
|
|
yield True
|
2017-11-09 19:01:09 +03:00
|
|
|
|
2018-04-10 11:15:50 +03:00
|
|
|
def check_bouquet_selection(self):
|
2020-04-06 16:50:11 +03:00
|
|
|
""" Checks and returns bouquet if selected """
|
2018-12-11 19:09:55 +03:00
|
|
|
if not self._bq_selected:
|
2021-08-15 17:24:30 +03:00
|
|
|
self.show_error_message("Error. No bouquet is selected!")
|
2018-04-10 11:15:50 +03:00
|
|
|
return
|
|
|
|
|
|
2019-12-22 20:42:29 +03:00
|
|
|
if self._s_type is SettingsType.NEUTRINO_MP and self._bq_selected.endswith(BqType.WEBTV.value):
|
2021-08-15 17:24:30 +03:00
|
|
|
self.show_error_message("Operation not allowed in this context!")
|
2018-04-10 11:15:50 +03:00
|
|
|
return
|
|
|
|
|
|
2018-12-11 19:09:55 +03:00
|
|
|
return self._bq_selected
|
2017-11-09 19:01:09 +03:00
|
|
|
|
2018-09-20 18:37:47 +03:00
|
|
|
@run_idle
|
|
|
|
|
def update_bouquets_type(self):
|
|
|
|
|
""" Update bouquets type in the model and dict """
|
|
|
|
|
for row in get_base_model(self._bouquets_view.get_model()):
|
|
|
|
|
bqs_rows = row.iterchildren()
|
|
|
|
|
if bqs_rows:
|
|
|
|
|
bq_type = row[-1]
|
|
|
|
|
for b_row in bqs_rows:
|
2019-02-09 12:46:06 +03:00
|
|
|
bq_id = "{}:{}".format(b_row[Column.BQ_NAME], b_row[Column.BQ_TYPE])
|
2018-09-20 18:37:47 +03:00
|
|
|
bq = self._bouquets.get(bq_id, None)
|
|
|
|
|
if bq:
|
2019-02-09 12:46:06 +03:00
|
|
|
b_row[Column.BQ_TYPE] = bq_type
|
|
|
|
|
self._bouquets["{}:{}".format(b_row[Column.BQ_NAME], b_row[Column.BQ_TYPE])] = bq
|
2018-09-20 18:37:47 +03:00
|
|
|
|
2017-11-09 19:01:09 +03:00
|
|
|
def delete_selection(self, view, *args):
|
|
|
|
|
""" Used for clear selection on given view(s) """
|
|
|
|
|
for v in [view, *args]:
|
|
|
|
|
v.get_selection().unselect_all()
|
|
|
|
|
|
2020-04-19 13:23:18 +03:00
|
|
|
def on_settings(self, action, value=None):
|
2019-12-13 13:31:07 +03:00
|
|
|
response = show_settings_dialog(self._main_window, self._settings)
|
2017-12-30 21:51:57 +03:00
|
|
|
if response != Gtk.ResponseType.CANCEL:
|
2019-12-27 23:05:37 +03:00
|
|
|
gen = self.update_settings()
|
2019-08-01 01:05:30 +03:00
|
|
|
GLib.idle_add(lambda: next(gen, False), priority=GLib.PRIORITY_LOW)
|
2019-05-14 22:12:36 +03:00
|
|
|
|
2019-12-27 23:05:37 +03:00
|
|
|
def update_settings(self):
|
|
|
|
|
s_type = self._settings.setting_type
|
2019-12-22 20:42:29 +03:00
|
|
|
|
2019-12-27 23:05:37 +03:00
|
|
|
if s_type != self._s_type:
|
2019-10-04 21:31:41 +03:00
|
|
|
yield from self.show_app_info(True)
|
2019-12-27 23:05:37 +03:00
|
|
|
self._s_type = s_type
|
2019-08-01 01:05:30 +03:00
|
|
|
c_gen = self.clear_current_data()
|
|
|
|
|
yield from c_gen
|
2019-12-27 23:05:37 +03:00
|
|
|
|
2021-03-12 10:24:09 +03:00
|
|
|
self.init_appearance(True)
|
2019-12-27 23:05:37 +03:00
|
|
|
self.init_profiles()
|
2019-08-01 01:05:30 +03:00
|
|
|
yield True
|
2019-12-27 23:05:37 +03:00
|
|
|
gen = self.init_http_api()
|
|
|
|
|
yield from gen
|
|
|
|
|
|
2020-01-07 12:36:29 +03:00
|
|
|
def on_profile_changed(self, entry):
|
2020-02-13 01:09:40 +03:00
|
|
|
active = self._profile_combo_box.get_active_text()
|
|
|
|
|
if not active:
|
2019-12-27 23:05:37 +03:00
|
|
|
return
|
|
|
|
|
|
2020-02-13 01:09:40 +03:00
|
|
|
changed = self._settings.current_profile != active
|
|
|
|
|
|
2019-12-27 23:05:37 +03:00
|
|
|
if active in self._settings.profiles:
|
2020-01-09 13:14:49 +03:00
|
|
|
self.set_profile(active)
|
2020-02-13 01:09:40 +03:00
|
|
|
|
2020-12-16 23:28:00 +03:00
|
|
|
gen = self.init_http_api()
|
|
|
|
|
GLib.idle_add(lambda: next(gen, False), priority=GLib.PRIORITY_LOW)
|
|
|
|
|
|
2021-08-15 14:37:21 +03:00
|
|
|
if self._ftp_client:
|
2020-12-16 23:28:00 +03:00
|
|
|
self._ftp_client.init_ftp()
|
|
|
|
|
|
2020-02-13 01:09:40 +03:00
|
|
|
if changed:
|
|
|
|
|
self.open_data()
|
2021-08-31 14:16:14 +03:00
|
|
|
self.emit("profile-changed", None)
|
2020-01-07 12:36:29 +03:00
|
|
|
|
2020-01-09 13:14:49 +03:00
|
|
|
def set_profile(self, active):
|
|
|
|
|
self._settings.current_profile = active
|
|
|
|
|
self._s_type = self._settings.setting_type
|
|
|
|
|
self.update_profile_label()
|
2021-09-04 14:46:11 +03:00
|
|
|
is_enigma = self._s_type is SettingsType.ENIGMA_2
|
|
|
|
|
self.set_property("is-enigma", is_enigma)
|
|
|
|
|
self.update_stack_elements_visibility(is_enigma)
|
2020-01-09 13:14:49 +03:00
|
|
|
|
2019-12-27 23:05:37 +03:00
|
|
|
def update_profiles(self):
|
|
|
|
|
self._profile_combo_box.remove_all()
|
|
|
|
|
for p in self._settings.profiles:
|
|
|
|
|
self._profile_combo_box.append(p, p)
|
2018-06-01 11:16:30 +03:00
|
|
|
|
2021-09-04 14:46:11 +03:00
|
|
|
@run_idle
|
|
|
|
|
def update_stack_elements_visibility(self, is_enigma=False):
|
|
|
|
|
self._stack_services_frame.set_visible(self._settings.get("show_bouquets", True))
|
|
|
|
|
self._stack_satellite_box.set_visible(self._settings.get("show_satellites", True))
|
|
|
|
|
self._stack_picon_box.set_visible(self._settings.get("show_picons", True))
|
|
|
|
|
self._stack_ftp_box.set_visible(self._settings.get("show_ftp", True))
|
|
|
|
|
self._stack_epg_box.set_visible(is_enigma and self._settings.get("show_epg", True))
|
|
|
|
|
self._stack_timers_box.set_visible(is_enigma and self._settings.get("show_timers", True))
|
|
|
|
|
self._stack_recordings_box.set_visible(is_enigma and self._settings.get("show_recordings", True))
|
|
|
|
|
self._stack_control_box.set_visible(is_enigma and self._settings.get("show_control", True))
|
|
|
|
|
|
2018-09-18 14:40:24 +03:00
|
|
|
def on_tree_view_key_press(self, view, event):
|
|
|
|
|
""" Handling keystrokes on press """
|
2018-11-09 14:14:24 +03:00
|
|
|
key_code = event.hardware_keycode
|
|
|
|
|
if not KeyboardKey.value_exist(key_code):
|
2018-11-05 00:31:44 +03:00
|
|
|
return
|
2019-01-30 09:10:43 +03:00
|
|
|
|
2018-11-09 14:14:24 +03:00
|
|
|
key = KeyboardKey(key_code)
|
2021-09-19 00:23:23 +03:00
|
|
|
ctrl = event.state & MOD_MASK
|
2020-02-11 13:18:14 +03:00
|
|
|
if key is KeyboardKey.F:
|
2021-09-19 00:23:23 +03:00
|
|
|
if ctrl:
|
|
|
|
|
self.activate_search_state(view)
|
2020-02-11 13:18:14 +03:00
|
|
|
return True
|
|
|
|
|
|
2020-04-19 13:23:18 +03:00
|
|
|
ctrl = event.state & MOD_MASK
|
2018-11-02 23:13:31 +03:00
|
|
|
model_name, model = get_model_data(view)
|
2018-09-18 14:40:24 +03:00
|
|
|
|
2020-02-11 13:18:14 +03:00
|
|
|
if ctrl and key in MOVE_KEYS:
|
2019-01-30 09:10:43 +03:00
|
|
|
self.move_items(key)
|
|
|
|
|
elif ctrl and key is KeyboardKey.C:
|
2020-04-19 13:23:18 +03:00
|
|
|
if model_name == self.SERVICE_MODEL_NAME:
|
2018-09-18 14:40:24 +03:00
|
|
|
self.on_copy(view, ViewTarget.FAV)
|
2020-04-19 13:23:18 +03:00
|
|
|
elif model_name == self.FAV_MODEL_NAME:
|
2018-09-18 14:40:24 +03:00
|
|
|
self.on_copy(view, ViewTarget.SERVICES)
|
|
|
|
|
else:
|
|
|
|
|
self.on_copy(view, ViewTarget.BOUQUET)
|
2018-11-05 00:31:44 +03:00
|
|
|
elif ctrl and key is KeyboardKey.X:
|
2020-04-19 13:23:18 +03:00
|
|
|
if model_name == self.FAV_MODEL_NAME:
|
2018-09-20 16:36:03 +03:00
|
|
|
self.on_cut(view, ViewTarget.FAV)
|
2020-04-19 13:23:18 +03:00
|
|
|
elif model_name == self.BQ_MODEL_NAME:
|
2018-09-20 16:36:03 +03:00
|
|
|
self.on_cut(view, ViewTarget.BOUQUET)
|
2018-11-05 00:31:44 +03:00
|
|
|
elif ctrl and key is KeyboardKey.V:
|
2020-04-19 13:23:18 +03:00
|
|
|
if model_name == self.FAV_MODEL_NAME:
|
2018-09-20 16:36:03 +03:00
|
|
|
self.on_paste(view, ViewTarget.FAV)
|
2020-04-19 13:23:18 +03:00
|
|
|
elif model_name == self.BQ_MODEL_NAME:
|
2018-09-20 16:36:03 +03:00
|
|
|
self.on_paste(view, ViewTarget.BOUQUET)
|
2018-11-05 00:31:44 +03:00
|
|
|
elif key is KeyboardKey.DELETE:
|
2018-09-18 14:40:24 +03:00
|
|
|
self.on_delete(view)
|
|
|
|
|
|
2017-11-09 19:01:09 +03:00
|
|
|
def on_tree_view_key_release(self, view, event):
|
2018-09-18 14:40:24 +03:00
|
|
|
""" Handling keystrokes on release """
|
2018-11-09 14:14:24 +03:00
|
|
|
key_code = event.hardware_keycode
|
|
|
|
|
if not KeyboardKey.value_exist(key_code):
|
2018-11-05 00:31:44 +03:00
|
|
|
return
|
2019-01-30 09:10:43 +03:00
|
|
|
|
2018-11-09 14:14:24 +03:00
|
|
|
key = KeyboardKey(key_code)
|
2020-04-19 13:23:18 +03:00
|
|
|
ctrl = event.state & MOD_MASK
|
2018-11-02 23:13:31 +03:00
|
|
|
model_name, model = get_model_data(view)
|
2017-11-09 19:01:09 +03:00
|
|
|
|
2020-02-11 13:18:14 +03:00
|
|
|
if ctrl and key is KeyboardKey.INSERT:
|
2017-11-09 19:01:09 +03:00
|
|
|
# Move items from app to fav list
|
2020-04-19 13:23:18 +03:00
|
|
|
if model_name == self.SERVICE_MODEL_NAME:
|
2018-10-13 22:27:32 +03:00
|
|
|
self.on_to_fav_copy(view)
|
2020-04-19 13:23:18 +03:00
|
|
|
elif model_name == self.BQ_MODEL_NAME:
|
2017-11-09 19:01:09 +03:00
|
|
|
self.on_new_bouquet(view)
|
2020-04-19 13:23:18 +03:00
|
|
|
elif ctrl and key is KeyboardKey.BACK_SPACE and model_name == self.SERVICE_MODEL_NAME:
|
2018-10-14 11:37:53 +03:00
|
|
|
self.on_to_fav_end_copy(view)
|
2018-11-05 00:31:44 +03:00
|
|
|
elif ctrl and key is KeyboardKey.R or key is KeyboardKey.F2:
|
2018-02-18 11:23:31 +03:00
|
|
|
self.on_rename(view)
|
2018-11-05 00:31:44 +03:00
|
|
|
elif key is KeyboardKey.LEFT or key is KeyboardKey.RIGHT:
|
2018-01-25 21:43:48 +03:00
|
|
|
view.do_unselect_all(view)
|
2020-04-19 13:23:18 +03:00
|
|
|
elif ctrl and model_name == self.FAV_MODEL_NAME:
|
2018-11-23 15:06:36 +03:00
|
|
|
if key is KeyboardKey.P:
|
2021-09-13 16:52:19 +03:00
|
|
|
self.emit("fav-clicked", FavClickMode.STREAM)
|
2018-12-02 00:45:55 +03:00
|
|
|
if key is KeyboardKey.W:
|
2021-09-13 16:52:19 +03:00
|
|
|
self.emit("fav-clicked", FavClickMode.ZAP_PLAY)
|
2018-11-23 15:06:36 +03:00
|
|
|
if key is KeyboardKey.Z:
|
|
|
|
|
self.on_zap()
|
|
|
|
|
elif key is KeyboardKey.CTRL_L or key is KeyboardKey.CTRL_R:
|
|
|
|
|
self.update_fav_num_column(model)
|
|
|
|
|
self.update_bouquet_list()
|
2017-11-09 19:01:09 +03:00
|
|
|
|
2019-05-29 14:31:44 +03:00
|
|
|
def on_view_focus(self, view, focus_event=None):
|
2018-11-02 23:13:31 +03:00
|
|
|
model_name, model = get_model_data(view)
|
2021-07-11 23:29:19 +03:00
|
|
|
not_empty = len(model) > 0 if model else False
|
2020-04-19 13:23:18 +03:00
|
|
|
is_service = model_name == self.SERVICE_MODEL_NAME
|
2017-11-09 19:01:09 +03:00
|
|
|
|
2020-04-19 13:23:18 +03:00
|
|
|
if model_name == self.BQ_MODEL_NAME:
|
2018-04-06 16:02:16 +03:00
|
|
|
for elem in self._tool_elements:
|
|
|
|
|
self._tool_elements[elem].set_sensitive(False)
|
2017-11-09 19:01:09 +03:00
|
|
|
for elem in self._BOUQUET_ELEMENTS:
|
2018-04-06 16:02:16 +03:00
|
|
|
self._tool_elements[elem].set_sensitive(not_empty)
|
2018-09-20 16:36:03 +03:00
|
|
|
if elem == "bouquets_paste_popup_item":
|
|
|
|
|
self._tool_elements[elem].set_sensitive(not_empty and self._bouquets_buffer)
|
2019-12-22 20:42:29 +03:00
|
|
|
if self._s_type is SettingsType.NEUTRINO_MP:
|
2018-01-05 14:32:14 +03:00
|
|
|
for elem in self._LOCK_HIDE_ELEMENTS:
|
2018-04-06 16:02:16 +03:00
|
|
|
self._tool_elements[elem].set_sensitive(not_empty)
|
2017-11-09 19:01:09 +03:00
|
|
|
else:
|
|
|
|
|
for elem in self._FAV_ELEMENTS:
|
2018-07-08 00:09:26 +03:00
|
|
|
if elem in ("paste_tool_button", "fav_paste_popup_item"):
|
2018-04-06 16:02:16 +03:00
|
|
|
self._tool_elements[elem].set_sensitive(not is_service and self._rows_buffer)
|
2018-02-11 23:14:22 +03:00
|
|
|
elif elem in self._FAV_ENIGMA_ELEMENTS:
|
2018-12-11 19:09:55 +03:00
|
|
|
self._tool_elements[elem].set_sensitive(self._bq_selected and not is_service)
|
2017-11-16 01:24:16 +03:00
|
|
|
else:
|
2018-04-06 16:02:16 +03:00
|
|
|
self._tool_elements[elem].set_sensitive(not_empty and not is_service)
|
2017-11-09 19:01:09 +03:00
|
|
|
for elem in self._SERVICE_ELEMENTS:
|
2018-04-06 16:02:16 +03:00
|
|
|
self._tool_elements[elem].set_sensitive(not_empty and is_service)
|
2017-11-09 19:01:09 +03:00
|
|
|
for elem in self._BOUQUET_ELEMENTS:
|
2018-04-06 16:02:16 +03:00
|
|
|
self._tool_elements[elem].set_sensitive(False)
|
2017-11-26 20:40:22 +03:00
|
|
|
for elem in self._LOCK_HIDE_ELEMENTS:
|
2019-12-22 20:42:29 +03:00
|
|
|
self._tool_elements[elem].set_sensitive(not_empty and self._s_type is SettingsType.ENIGMA_2)
|
2017-11-09 19:01:09 +03:00
|
|
|
|
2019-05-29 14:31:44 +03:00
|
|
|
for elem in self._FAV_IPTV_ELEMENTS:
|
2019-09-16 17:44:33 +03:00
|
|
|
is_iptv = self._bq_selected and not is_service
|
2019-12-22 20:42:29 +03:00
|
|
|
if self._s_type is SettingsType.NEUTRINO_MP:
|
2019-09-16 17:44:33 +03:00
|
|
|
is_iptv = is_iptv and BqType(self._bq_selected.split(":")[1]) is BqType.WEBTV
|
|
|
|
|
self._tool_elements[elem].set_sensitive(is_iptv)
|
2017-12-25 19:50:35 +03:00
|
|
|
for elem in self._COMMONS_ELEMENTS:
|
2018-04-06 16:02:16 +03:00
|
|
|
self._tool_elements[elem].set_sensitive(not_empty)
|
2017-11-09 19:01:09 +03:00
|
|
|
|
2019-12-22 20:42:29 +03:00
|
|
|
if self._s_type is not SettingsType.ENIGMA_2:
|
2019-05-29 14:31:44 +03:00
|
|
|
for elem in self._FAV_ENIGMA_ELEMENTS:
|
|
|
|
|
self._tool_elements[elem].set_sensitive(False)
|
|
|
|
|
|
2020-02-11 13:18:14 +03:00
|
|
|
def on_hide(self, action=None, value=None):
|
2018-02-17 16:23:41 +03:00
|
|
|
self.set_service_flags(Flag.HIDE)
|
2017-11-23 16:59:21 +03:00
|
|
|
|
2020-02-11 13:18:14 +03:00
|
|
|
def on_locked(self, action=None, value=None):
|
2018-02-17 16:23:41 +03:00
|
|
|
self.set_service_flags(Flag.LOCK)
|
2017-11-23 16:59:21 +03:00
|
|
|
|
|
|
|
|
def set_service_flags(self, flag):
|
2019-12-22 20:42:29 +03:00
|
|
|
if self._s_type is SettingsType.ENIGMA_2:
|
2018-09-29 21:57:17 +03:00
|
|
|
set_flags(flag, self._services_view, self._fav_view, self._services, self._blacklist)
|
2019-12-22 20:42:29 +03:00
|
|
|
elif self._s_type is SettingsType.NEUTRINO_MP and self._bq_selected:
|
2018-07-08 14:58:41 +03:00
|
|
|
model, paths = self._bouquets_view.get_selection().get_selected_rows()
|
|
|
|
|
itr = model.get_iter(paths[0])
|
|
|
|
|
value = model.get_value(itr, 1 if flag is Flag.LOCK else 2)
|
2018-07-08 00:09:26 +03:00
|
|
|
value = None if value else LOCKED_ICON if flag is Flag.LOCK else HIDE_ICON
|
2018-07-08 14:58:41 +03:00
|
|
|
model.set_value(itr, 1 if flag is Flag.LOCK else 2, value)
|
2017-11-23 16:59:21 +03:00
|
|
|
|
2021-07-25 22:33:00 +03:00
|
|
|
def on_model_changed(self, model, path=None, itr=None):
|
2017-11-29 00:26:12 +03:00
|
|
|
model_name = model.get_name()
|
|
|
|
|
|
2020-04-19 13:23:18 +03:00
|
|
|
if model_name == self.FAV_MODEL_NAME:
|
2018-04-06 16:02:16 +03:00
|
|
|
self._fav_count_label.set_text(str(len(model)))
|
2020-04-19 13:23:18 +03:00
|
|
|
elif model_name == self.SERVICE_MODEL_NAME:
|
2017-11-30 00:45:52 +03:00
|
|
|
self.update_services_counts(len(model))
|
2020-04-19 13:23:18 +03:00
|
|
|
elif model_name == self.BQ_MODEL_NAME:
|
2018-04-06 16:02:16 +03:00
|
|
|
self._bouquets_count_label.set_text(str(len(self._bouquets.keys())))
|
2017-11-29 00:26:12 +03:00
|
|
|
|
2017-11-30 00:45:52 +03:00
|
|
|
@lru_cache(maxsize=1)
|
|
|
|
|
def update_services_counts(self, size=0):
|
|
|
|
|
""" Updates counters for services. May be temporary! """
|
2017-11-29 00:26:12 +03:00
|
|
|
tv_count = 0
|
|
|
|
|
radio_count = 0
|
|
|
|
|
data_count = 0
|
|
|
|
|
|
2018-04-06 16:02:16 +03:00
|
|
|
for ch in self._services.values():
|
2017-11-29 00:26:12 +03:00
|
|
|
ch_type = ch.service_type
|
2018-03-03 20:55:08 +03:00
|
|
|
if ch_type in self._TV_TYPES:
|
2017-11-29 00:26:12 +03:00
|
|
|
tv_count += 1
|
|
|
|
|
elif ch_type == "Radio":
|
|
|
|
|
radio_count += 1
|
|
|
|
|
elif ch_type == "Data":
|
|
|
|
|
data_count += 1
|
|
|
|
|
|
2018-04-06 16:02:16 +03:00
|
|
|
self._tv_count_label.set_text(str(tv_count))
|
|
|
|
|
self._radio_count_label.set_text(str(radio_count))
|
|
|
|
|
self._data_count_label.set_text(str(data_count))
|
2017-11-29 00:26:12 +03:00
|
|
|
|
2020-05-23 15:16:31 +03:00
|
|
|
def on_insert_marker(self, view, m_type=BqServiceType.MARKER):
|
2017-12-20 16:46:15 +03:00
|
|
|
""" Inserts marker into bouquet services list. """
|
2020-05-23 15:16:31 +03:00
|
|
|
insert_marker(view, self._bouquets, self._bq_selected, self._services, self._main_window, m_type)
|
2018-04-06 16:02:16 +03:00
|
|
|
self.update_fav_num_column(self._fav_model)
|
2017-12-19 22:57:04 +03:00
|
|
|
|
2020-05-23 15:16:31 +03:00
|
|
|
def on_insert_space(self, view):
|
|
|
|
|
self.on_insert_marker(view, BqServiceType.SPACE)
|
|
|
|
|
|
2018-04-29 01:44:28 +03:00
|
|
|
def on_fav_press(self, menu, event):
|
2018-08-01 11:05:29 +03:00
|
|
|
if event.get_event_type() == Gdk.EventType.DOUBLE_BUTTON_PRESS:
|
2019-03-14 12:37:48 +03:00
|
|
|
if self._fav_click_mode is FavClickMode.DISABLED:
|
|
|
|
|
return
|
2020-03-28 17:56:39 +03:00
|
|
|
|
2021-09-13 16:52:19 +03:00
|
|
|
if self._fav_click_mode is FavClickMode.ZAP:
|
2019-03-14 12:37:48 +03:00
|
|
|
self.on_zap()
|
2021-09-13 16:52:19 +03:00
|
|
|
else:
|
|
|
|
|
self.emit("fav-clicked", self._fav_click_mode)
|
2018-09-22 19:14:47 +03:00
|
|
|
else:
|
|
|
|
|
return self.on_view_popup_menu(menu, event)
|
2018-04-29 15:36:35 +03:00
|
|
|
|
2018-09-01 00:49:11 +03:00
|
|
|
# ***************** IPTV *********************#
|
|
|
|
|
|
2020-04-19 13:23:18 +03:00
|
|
|
def on_iptv(self, action, value=None):
|
2018-09-01 00:49:11 +03:00
|
|
|
response = IptvDialog(self._main_window,
|
|
|
|
|
self._fav_view,
|
|
|
|
|
self._services,
|
2018-12-11 19:09:55 +03:00
|
|
|
self._bouquets.get(self._bq_selected, None),
|
2020-06-10 11:10:41 +03:00
|
|
|
self._settings,
|
2018-09-01 00:49:11 +03:00
|
|
|
Action.ADD).show()
|
|
|
|
|
if response != Gtk.ResponseType.CANCEL:
|
|
|
|
|
self.update_fav_num_column(self._fav_model)
|
|
|
|
|
|
2018-08-19 23:27:13 +03:00
|
|
|
@run_idle
|
2020-04-19 13:23:18 +03:00
|
|
|
def on_iptv_list_configuration(self, action, value=None):
|
2019-12-22 20:42:29 +03:00
|
|
|
if self._s_type is SettingsType.NEUTRINO_MP:
|
2021-08-15 17:24:30 +03:00
|
|
|
self.show_error_message("Neutrino at the moment not supported!")
|
2018-08-21 17:19:44 +03:00
|
|
|
return
|
|
|
|
|
|
2018-12-19 14:43:43 +03:00
|
|
|
iptv_rows = list(filter(lambda r: r[Column.FAV_TYPE] == BqServiceType.IPTV.value, self._fav_model))
|
2018-08-19 23:27:13 +03:00
|
|
|
if not iptv_rows:
|
2021-08-15 17:24:30 +03:00
|
|
|
self.show_error_message("This list does not contains IPTV streams!")
|
2018-08-19 23:27:13 +03:00
|
|
|
return
|
|
|
|
|
|
2018-12-11 19:09:55 +03:00
|
|
|
if not self._bq_selected:
|
2018-08-19 23:27:13 +03:00
|
|
|
return
|
|
|
|
|
|
2019-04-14 20:24:57 +03:00
|
|
|
bq = self._bouquets.get(self._bq_selected, [])
|
2019-12-13 13:31:07 +03:00
|
|
|
IptvListConfigurationDialog(self._main_window, self._services, iptv_rows, bq,
|
2019-12-22 20:42:29 +03:00
|
|
|
self._fav_model, self._s_type).show()
|
2018-08-18 17:35:30 +03:00
|
|
|
|
2018-06-29 22:43:04 +03:00
|
|
|
@run_idle
|
2020-04-19 13:23:18 +03:00
|
|
|
def on_remove_all_unavailable(self, action, value=None):
|
2019-03-14 12:37:48 +03:00
|
|
|
iptv_rows = list(filter(lambda r: r[Column.FAV_TYPE] == BqServiceType.IPTV.value, self._fav_model))
|
2018-07-03 19:30:45 +03:00
|
|
|
if not iptv_rows:
|
2021-08-15 17:24:30 +03:00
|
|
|
self.show_error_message("This list does not contains IPTV streams!")
|
2018-07-03 19:30:45 +03:00
|
|
|
return
|
2018-06-29 22:43:04 +03:00
|
|
|
|
2018-12-11 19:09:55 +03:00
|
|
|
if not self._bq_selected:
|
2018-07-03 19:30:45 +03:00
|
|
|
return
|
|
|
|
|
|
|
|
|
|
if show_dialog(DialogType.QUESTION, self._main_window) == Gtk.ResponseType.CANCEL:
|
|
|
|
|
return
|
|
|
|
|
|
2018-12-11 19:09:55 +03:00
|
|
|
fav_bqt = self._bouquets.get(self._bq_selected, None)
|
2019-12-22 20:42:29 +03:00
|
|
|
response = SearchUnavailableDialog(self._main_window, self._fav_model, fav_bqt, iptv_rows, self._s_type).show()
|
2018-07-03 19:30:45 +03:00
|
|
|
if response:
|
2020-06-03 11:25:53 +03:00
|
|
|
gen = self.remove_favs(response, self._fav_model)
|
|
|
|
|
GLib.idle_add(lambda: next(gen, False), priority=GLib.PRIORITY_LOW)
|
2018-06-29 22:43:04 +03:00
|
|
|
|
2019-04-18 23:05:19 +03:00
|
|
|
# ****************** EPG **********************#
|
|
|
|
|
|
2020-04-19 13:23:18 +03:00
|
|
|
def on_epg_list_configuration(self, action, value=None):
|
2019-12-22 20:42:29 +03:00
|
|
|
if self._s_type is not SettingsType.ENIGMA_2:
|
2021-08-15 17:24:30 +03:00
|
|
|
self.show_error_message("Only Enigma2 is supported!")
|
2019-04-18 23:05:19 +03:00
|
|
|
return
|
2019-04-21 01:18:54 +03:00
|
|
|
|
|
|
|
|
if not any(r[Column.FAV_TYPE] == BqServiceType.IPTV.value for r in self._fav_model):
|
2021-08-15 17:24:30 +03:00
|
|
|
self.show_error_message("This list does not contains IPTV streams!")
|
2019-04-21 01:18:54 +03:00
|
|
|
return
|
|
|
|
|
|
|
|
|
|
bq = self._bouquets.get(self._bq_selected)
|
2019-12-13 13:31:07 +03:00
|
|
|
EpgDialog(self._main_window, self._settings, self._services, bq, self._fav_model, self._current_bq_name).show()
|
2019-04-18 23:05:19 +03:00
|
|
|
|
2021-05-12 14:59:55 +03:00
|
|
|
# ***************** Import ******************** #
|
2019-02-05 16:58:54 +03:00
|
|
|
|
2020-04-19 13:23:18 +03:00
|
|
|
def on_import_yt_list(self, action, value=None):
|
2019-06-24 00:36:54 +03:00
|
|
|
""" Import playlist from YouTube """
|
|
|
|
|
if not self._bq_selected:
|
|
|
|
|
return
|
|
|
|
|
|
2020-06-10 11:10:41 +03:00
|
|
|
YtListImportDialog(self._main_window, self._settings, self.append_imported_services).show()
|
2019-06-24 00:36:54 +03:00
|
|
|
|
2020-04-19 13:23:18 +03:00
|
|
|
def on_import_m3u(self, action, value=None):
|
2018-09-01 00:49:11 +03:00
|
|
|
""" Imports iptv from m3u files. """
|
2020-04-21 14:43:57 +03:00
|
|
|
response = get_chooser_dialog(self._main_window, self._settings, "*.m3u* files", ("*.m3u", "*.m3u8"))
|
2018-09-01 00:49:11 +03:00
|
|
|
if response == Gtk.ResponseType.CANCEL:
|
|
|
|
|
return
|
|
|
|
|
|
2020-04-21 14:43:57 +03:00
|
|
|
if not str(response).endswith(("m3u", "m3u8")):
|
2021-08-15 17:24:30 +03:00
|
|
|
self.show_error_message("No m3u file is selected!")
|
2018-09-01 00:49:11 +03:00
|
|
|
return
|
|
|
|
|
|
2021-01-31 16:27:35 +03:00
|
|
|
if self._bq_selected:
|
2021-02-05 01:12:44 +03:00
|
|
|
M3uImportDialog(self._main_window, self._s_type, response, self).show()
|
2019-06-26 15:57:22 +03:00
|
|
|
|
2019-08-13 19:22:08 +03:00
|
|
|
def append_imported_services(self, services):
|
2019-06-26 15:57:22 +03:00
|
|
|
bq_services = self._bouquets.get(self._bq_selected)
|
|
|
|
|
self._fav_model.clear()
|
|
|
|
|
for srv in services:
|
|
|
|
|
self._services[srv.fav_id] = srv
|
|
|
|
|
bq_services.append(srv.fav_id)
|
2020-04-28 11:23:22 +03:00
|
|
|
|
|
|
|
|
gen = self.update_bouquet_services(self._fav_model, None, self._bq_selected)
|
|
|
|
|
GLib.idle_add(lambda: next(gen, False), priority=GLib.PRIORITY_LOW)
|
2018-09-01 00:49:11 +03:00
|
|
|
|
2020-09-19 12:32:08 +03:00
|
|
|
def on_import_data(self, path):
|
|
|
|
|
msg = "Combine with the current data?"
|
|
|
|
|
if len(self._services_model) > 0 and show_dialog(DialogType.QUESTION, self._main_window,
|
|
|
|
|
msg) == Gtk.ResponseType.OK:
|
|
|
|
|
self.import_data(path, force=True)
|
|
|
|
|
else:
|
|
|
|
|
if os.path.isdir(path) and not path.endswith(os.sep):
|
|
|
|
|
path += os.sep
|
|
|
|
|
self.open_data(path)
|
|
|
|
|
|
2020-07-12 11:11:32 +03:00
|
|
|
def on_import_bouquet(self, action, value=None, file_path=None):
|
2019-02-23 13:54:00 +03:00
|
|
|
model, paths = self._bouquets_view.get_selection().get_selected_rows()
|
|
|
|
|
if not paths:
|
2021-08-15 17:24:30 +03:00
|
|
|
self.show_error_message("No selected item!")
|
2019-02-23 13:54:00 +03:00
|
|
|
return
|
|
|
|
|
|
2019-12-22 20:42:29 +03:00
|
|
|
appender = self.append_bouquet if self._s_type is SettingsType.ENIGMA_2 else self.append_bouquets
|
2020-07-12 11:11:32 +03:00
|
|
|
import_bouquet(self._main_window, model, paths[0], self._settings, self._services, appender, file_path)
|
2019-02-23 13:54:00 +03:00
|
|
|
|
2020-04-19 13:23:18 +03:00
|
|
|
def on_import_bouquets(self, action, value=None):
|
2019-12-13 13:31:07 +03:00
|
|
|
response = show_dialog(DialogType.CHOOSER, self._main_window, settings=self._settings)
|
2019-02-23 13:54:00 +03:00
|
|
|
if response in (Gtk.ResponseType.CANCEL, Gtk.ResponseType.DELETE_EVENT):
|
|
|
|
|
return
|
|
|
|
|
|
2020-09-19 12:32:08 +03:00
|
|
|
self.import_data(response)
|
|
|
|
|
|
|
|
|
|
def import_data(self, path, force=None, callback=None):
|
|
|
|
|
if os.path.isdir(path) and not path.endswith(os.sep):
|
|
|
|
|
path += os.sep
|
|
|
|
|
elif os.path.isfile(path):
|
|
|
|
|
arch_path = self.get_archive_path(path)
|
|
|
|
|
if not arch_path:
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
path = arch_path.name + os.sep
|
|
|
|
|
callback = arch_path.cleanup
|
|
|
|
|
|
2019-08-08 21:15:43 +03:00
|
|
|
def append(b, s):
|
2020-09-19 12:32:08 +03:00
|
|
|
gen = self.append_imported_data(b, s, callback)
|
2019-08-08 21:15:43 +03:00
|
|
|
GLib.idle_add(lambda: next(gen, False))
|
|
|
|
|
|
2020-09-19 12:32:08 +03:00
|
|
|
dialog = ImportDialog(self._main_window, path, self._settings, self._services.keys(), append)
|
|
|
|
|
dialog.import_data() if force else dialog.show()
|
2021-05-11 00:18:27 +03:00
|
|
|
self.update_picons()
|
2019-08-08 21:15:43 +03:00
|
|
|
|
2020-09-19 12:32:08 +03:00
|
|
|
def append_imported_data(self, bouquets, services, callback=None):
|
2019-08-08 21:15:43 +03:00
|
|
|
try:
|
|
|
|
|
self._wait_dialog.show()
|
|
|
|
|
yield from self.append_data(bouquets, services)
|
|
|
|
|
finally:
|
2020-09-19 12:32:08 +03:00
|
|
|
log("Importing data done!")
|
|
|
|
|
if callback:
|
|
|
|
|
callback()
|
2019-08-08 21:15:43 +03:00
|
|
|
self._wait_dialog.hide()
|
2019-02-05 16:58:54 +03:00
|
|
|
|
2020-11-02 21:55:34 +03:00
|
|
|
def on_import_from_web(self, action, value=None):
|
|
|
|
|
if self._s_type is not SettingsType.ENIGMA_2:
|
2021-08-15 17:24:30 +03:00
|
|
|
self.show_error_message("Not allowed in this context!")
|
2020-11-02 21:55:34 +03:00
|
|
|
return
|
|
|
|
|
ServicesUpdateDialog(self._main_window, self._settings, self.on_import_data_from_web).show()
|
|
|
|
|
|
|
|
|
|
@run_idle
|
|
|
|
|
def on_import_data_from_web(self, services):
|
|
|
|
|
msg = "Combine with the current data?"
|
|
|
|
|
if len(self._services_model) > 0 and show_dialog(DialogType.QUESTION, self._main_window,
|
|
|
|
|
msg) == Gtk.ResponseType.OK:
|
|
|
|
|
gen = self.append_imported_data([], services)
|
|
|
|
|
GLib.idle_add(lambda: next(gen, False), priority=GLib.PRIORITY_LOW)
|
|
|
|
|
else:
|
|
|
|
|
gen = self.import_data_from_web(services)
|
|
|
|
|
GLib.idle_add(lambda: next(gen, False), priority=GLib.PRIORITY_LOW)
|
|
|
|
|
|
|
|
|
|
def import_data_from_web(self, services):
|
|
|
|
|
self._wait_dialog.show()
|
|
|
|
|
if self._app_info_box.get_visible():
|
|
|
|
|
yield from self.create_new_configuration(self._s_type)
|
|
|
|
|
yield from self.append_services(services)
|
2020-11-23 12:58:40 +03:00
|
|
|
self.update_sat_positions()
|
|
|
|
|
yield True
|
2020-11-02 21:55:34 +03:00
|
|
|
self._wait_dialog.hide()
|
|
|
|
|
|
2021-05-12 14:59:55 +03:00
|
|
|
# ***************** Export ******************** #
|
|
|
|
|
|
|
|
|
|
def on_bouquet_export(self, item=None):
|
|
|
|
|
""" Exports single bouquet to file. """
|
|
|
|
|
bq_selected = self.check_bouquet_selection()
|
|
|
|
|
if not bq_selected:
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
model, paths = self._bouquets_view.get_selection().get_selected_rows()
|
|
|
|
|
if len(paths) > 1:
|
2021-08-15 17:24:30 +03:00
|
|
|
self.show_error_message("Please, select only one bouquet!")
|
2021-05-12 14:59:55 +03:00
|
|
|
return
|
|
|
|
|
|
|
|
|
|
response = show_dialog(DialogType.CHOOSER, self._main_window, settings=self._settings,
|
|
|
|
|
buttons=(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, Gtk.STOCK_SAVE, Gtk.ResponseType.OK))
|
|
|
|
|
if response in (Gtk.ResponseType.CANCEL, Gtk.ResponseType.DELETE_EVENT):
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
itr = model.get_iter(paths)
|
|
|
|
|
bq = self.get_bouquet(itr, model)
|
|
|
|
|
if self._s_type is SettingsType.NEUTRINO_MP:
|
|
|
|
|
bq = Bouquets(*model.get(itr, Column.BQ_NAME, Column.BQ_TYPE), [bq])
|
|
|
|
|
response += bq.name
|
|
|
|
|
write_bouquet(response, bq, self._s_type)
|
|
|
|
|
except OSError as e:
|
2021-08-15 17:24:30 +03:00
|
|
|
self.show_error_message(str(e))
|
2021-05-12 14:59:55 +03:00
|
|
|
else:
|
|
|
|
|
show_dialog(DialogType.INFO, self._main_window, "Done!")
|
|
|
|
|
|
|
|
|
|
@run_idle
|
|
|
|
|
def on_export_to_m3u(self, action, value=None):
|
|
|
|
|
i_types = (BqServiceType.IPTV.value, BqServiceType.MARKER.value)
|
|
|
|
|
bq_services = [BouquetService(r[Column.FAV_SERVICE],
|
|
|
|
|
BqServiceType(r[Column.FAV_TYPE]),
|
|
|
|
|
r[Column.FAV_ID],
|
|
|
|
|
r[Column.FAV_NUM]) for r in self._fav_model if r[Column.FAV_TYPE] in i_types]
|
|
|
|
|
|
|
|
|
|
if not any(s.type is BqServiceType.IPTV for s in bq_services):
|
2021-08-15 17:24:30 +03:00
|
|
|
self.show_error_message("This list does not contains IPTV streams!")
|
2021-05-12 14:59:55 +03:00
|
|
|
return
|
|
|
|
|
|
|
|
|
|
response = show_dialog(DialogType.CHOOSER, self._main_window, settings=self._settings,
|
|
|
|
|
buttons=(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, Gtk.STOCK_SAVE, Gtk.ResponseType.OK))
|
|
|
|
|
if response in (Gtk.ResponseType.CANCEL, Gtk.ResponseType.DELETE_EVENT):
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
bq = Bouquet(self._current_bq_name, None, bq_services, None, None)
|
|
|
|
|
export_to_m3u(response, bq, self._s_type)
|
|
|
|
|
except Exception as e:
|
2021-08-15 17:24:30 +03:00
|
|
|
self.show_error_message(str(e))
|
2021-05-12 14:59:55 +03:00
|
|
|
else:
|
|
|
|
|
show_dialog(DialogType.INFO, self._main_window, "Done!")
|
|
|
|
|
|
2021-03-09 18:14:15 +03:00
|
|
|
# ***************** Backup ******************** #
|
2018-12-20 18:14:19 +03:00
|
|
|
|
2020-04-19 13:23:18 +03:00
|
|
|
def on_backup_tool_show(self, action, value=None):
|
2018-12-20 18:14:19 +03:00
|
|
|
""" Shows backup tool dialog """
|
2019-12-13 13:31:07 +03:00
|
|
|
BackupDialog(self._main_window, self._settings, self.open_data).show()
|
2018-12-20 18:14:19 +03:00
|
|
|
|
2021-09-13 16:52:19 +03:00
|
|
|
# ************************* Streams ***************************** #
|
2018-08-31 17:26:36 +03:00
|
|
|
|
2018-08-25 15:30:12 +03:00
|
|
|
def on_play_stream(self, item=None):
|
2021-09-13 16:52:19 +03:00
|
|
|
self.emit("fav-clicked", FavClickMode.STREAM)
|
2020-03-28 17:56:39 +03:00
|
|
|
|
2021-09-13 16:52:19 +03:00
|
|
|
def on_play_current(self, item=None):
|
|
|
|
|
""" starts playback of the current channel. """
|
|
|
|
|
self.emit("play-current", None)
|
2020-01-16 14:08:34 +03:00
|
|
|
|
2021-09-13 16:52:19 +03:00
|
|
|
def on_playback_full_screen(self, box, state):
|
|
|
|
|
self._data_paned.set_visible(state)
|
|
|
|
|
self._main_window.unfullscreen() if state else self._main_window.fullscreen()
|
2021-08-21 14:50:05 +03:00
|
|
|
if not IS_GNOME_SESSION:
|
2021-09-13 16:52:19 +03:00
|
|
|
self._main_window.set_show_menubar(state)
|
2018-08-31 17:26:36 +03:00
|
|
|
|
2021-09-13 16:52:19 +03:00
|
|
|
def on_playback_show(self, box):
|
|
|
|
|
if self._page is not Page.RECORDINGS and self._settings.play_streams_mode is PlayStreamsMode.BUILT_IN:
|
|
|
|
|
self._stack.set_visible(False)
|
|
|
|
|
self._fav_paned.set_orientation(Gtk.Orientation.VERTICAL)
|
2021-04-12 17:08:43 +03:00
|
|
|
|
2021-02-07 19:27:38 +03:00
|
|
|
@run_idle
|
2021-09-13 16:52:19 +03:00
|
|
|
def on_playback_close(self, box, state):
|
|
|
|
|
self._fav_view.set_sensitive(True)
|
|
|
|
|
self._stack.set_visible(True)
|
|
|
|
|
self._fav_paned.set_orientation(Gtk.Orientation.HORIZONTAL)
|
2020-03-07 18:33:51 +03:00
|
|
|
|
|
|
|
|
def on_record(self, button):
|
|
|
|
|
if show_dialog(DialogType.QUESTION, self._main_window) == Gtk.ResponseType.CANCEL:
|
|
|
|
|
return True
|
|
|
|
|
|
|
|
|
|
if not self._recorder:
|
|
|
|
|
try:
|
2020-03-09 14:53:03 +03:00
|
|
|
self._recorder = Recorder.get_instance(self._settings)
|
2020-03-07 18:33:51 +03:00
|
|
|
except (ImportError, NameError, AttributeError):
|
2021-08-15 17:24:30 +03:00
|
|
|
self.show_error_message("No VLC is found. Check that it is installed!")
|
2020-03-07 18:33:51 +03:00
|
|
|
return
|
|
|
|
|
|
|
|
|
|
is_record = self._recorder.is_record()
|
|
|
|
|
|
|
|
|
|
if is_record:
|
|
|
|
|
self._recorder.stop()
|
|
|
|
|
else:
|
2021-09-13 16:52:19 +03:00
|
|
|
self._http_api.send(HttpAPI.Request.STREAM_CURRENT, "", self.record)
|
2020-03-07 18:33:51 +03:00
|
|
|
|
2020-03-22 23:26:01 +03:00
|
|
|
def record(self, data):
|
|
|
|
|
url = self.get_url_from_m3u(data)
|
|
|
|
|
if url:
|
|
|
|
|
self._recorder.record(url, self._service_name_label.get_text())
|
|
|
|
|
GLib.timeout_add_seconds(1, self.update_record_button, priority=GLib.PRIORITY_LOW)
|
2020-03-07 18:33:51 +03:00
|
|
|
|
|
|
|
|
def update_record_button(self):
|
|
|
|
|
is_rec = self._recorder.is_record()
|
|
|
|
|
if not is_rec:
|
2020-03-13 14:36:16 +03:00
|
|
|
self._record_image.set_opacity(1.0)
|
2020-03-07 18:33:51 +03:00
|
|
|
else:
|
2020-03-13 14:36:16 +03:00
|
|
|
self._record_image.set_opacity(0 if self._record_image.get_opacity() else 1.0)
|
2020-03-07 18:33:51 +03:00
|
|
|
return is_rec
|
|
|
|
|
|
2020-01-08 21:33:24 +03:00
|
|
|
# ************************ HTTP API **************************** #
|
2019-12-27 23:05:37 +03:00
|
|
|
|
2018-11-17 23:19:17 +03:00
|
|
|
def init_http_api(self):
|
2019-12-13 13:31:07 +03:00
|
|
|
self._fav_click_mode = FavClickMode(self._settings.fav_click_mode)
|
|
|
|
|
http_api_enable = self._settings.http_api_support
|
2020-01-08 21:33:24 +03:00
|
|
|
st = all((http_api_enable, self._s_type is SettingsType.ENIGMA_2, not self._receiver_info_box.get_visible()))
|
|
|
|
|
GLib.idle_add(self._http_status_image.set_visible, st)
|
2019-03-14 12:37:48 +03:00
|
|
|
|
2019-12-22 20:42:29 +03:00
|
|
|
if self._s_type is SettingsType.NEUTRINO_MP or not http_api_enable:
|
2020-01-08 21:33:24 +03:00
|
|
|
GLib.idle_add(self._receiver_info_box.set_visible, False)
|
2019-09-28 21:57:41 +03:00
|
|
|
if self._http_api:
|
|
|
|
|
self._http_api.close()
|
2019-12-27 23:05:37 +03:00
|
|
|
yield True
|
2019-09-28 21:57:41 +03:00
|
|
|
self._http_api = None
|
2019-11-05 23:04:21 +03:00
|
|
|
self.init_send_to(False)
|
2018-11-17 23:19:17 +03:00
|
|
|
return
|
|
|
|
|
|
2020-01-17 00:34:18 +03:00
|
|
|
current_profile = self._profile_combo_box.get_active_text()
|
|
|
|
|
if current_profile in self._settings.profiles:
|
|
|
|
|
self._settings.current_profile = current_profile
|
|
|
|
|
|
2019-09-28 21:57:41 +03:00
|
|
|
if not self._http_api:
|
2019-12-27 23:05:37 +03:00
|
|
|
self._http_api = HttpAPI(self._settings)
|
2019-11-21 23:13:06 +03:00
|
|
|
GLib.timeout_add_seconds(3, self.update_info, priority=GLib.PRIORITY_LOW)
|
2020-01-08 21:33:24 +03:00
|
|
|
else:
|
|
|
|
|
self._http_api.init()
|
2018-11-17 23:19:17 +03:00
|
|
|
|
2019-12-13 13:31:07 +03:00
|
|
|
self.init_send_to(http_api_enable and self._settings.enable_send_to)
|
2019-12-27 23:05:37 +03:00
|
|
|
yield True
|
2019-11-05 23:04:21 +03:00
|
|
|
|
|
|
|
|
@run_idle
|
|
|
|
|
def init_send_to(self, enable):
|
|
|
|
|
if enable and not self._links_transmitter:
|
2020-06-13 20:57:37 +03:00
|
|
|
self._links_transmitter = LinksTransmitter(self._http_api, self._main_window, self._settings)
|
2019-11-05 23:04:21 +03:00
|
|
|
elif self._links_transmitter:
|
|
|
|
|
self._links_transmitter.show(enable)
|
|
|
|
|
|
2020-03-22 23:26:01 +03:00
|
|
|
def get_url_from_m3u(self, data):
|
|
|
|
|
error_code = data.get("error_code", 0)
|
|
|
|
|
if error_code or self._http_status_image.get_visible():
|
2021-08-15 17:24:30 +03:00
|
|
|
self.show_error_message("No connection to the receiver!")
|
2020-03-22 23:26:01 +03:00
|
|
|
return
|
|
|
|
|
|
|
|
|
|
m3u = data.get("m3u", None)
|
2018-12-02 00:45:55 +03:00
|
|
|
if m3u:
|
2020-03-22 23:26:01 +03:00
|
|
|
return [s for s in m3u.split("\n") if not s.startswith("#")][0]
|
2018-12-02 00:45:55 +03:00
|
|
|
|
2020-03-28 17:56:39 +03:00
|
|
|
def save_stream_to_m3u(self, url):
|
2021-09-13 16:52:19 +03:00
|
|
|
if self._page not in self._fav_pages:
|
|
|
|
|
self.show_error_message("Not allowed in this context!")
|
|
|
|
|
return
|
|
|
|
|
|
2020-03-28 17:56:39 +03:00
|
|
|
path, column = self._fav_view.get_cursor()
|
|
|
|
|
s_name = self._fav_model.get_value(self._fav_model.get_iter(path), Column.FAV_SERVICE) if path else "stream"
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
response = show_dialog(DialogType.CHOOSER, self._main_window, settings=self._settings)
|
|
|
|
|
if response in (Gtk.ResponseType.CANCEL, Gtk.ResponseType.DELETE_EVENT):
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
with open("{}{}.m3u".format(response, s_name), "w", encoding="utf-8") as file:
|
|
|
|
|
file.writelines("#EXTM3U\n#EXTVLCOPT--http-reconnect=true\n#EXTINF:-1,{}\n{}\n".format(s_name, url))
|
|
|
|
|
except IOError as e:
|
2021-08-15 17:24:30 +03:00
|
|
|
self.show_error_message(str(e))
|
2020-03-28 17:56:39 +03:00
|
|
|
else:
|
|
|
|
|
show_dialog(DialogType.INFO, self._main_window, "Done!")
|
|
|
|
|
finally:
|
|
|
|
|
GLib.idle_add(self._fav_view.set_sensitive, True)
|
|
|
|
|
|
2019-02-03 23:44:53 +03:00
|
|
|
@run_idle
|
2018-12-02 00:45:55 +03:00
|
|
|
def on_zap(self, callback=None):
|
|
|
|
|
""" Switch(zap) the channel """
|
2018-11-17 23:19:17 +03:00
|
|
|
path, column = self._fav_view.get_cursor()
|
|
|
|
|
if not path or not self._http_api:
|
|
|
|
|
return
|
|
|
|
|
|
2020-01-14 18:26:05 +03:00
|
|
|
ref = self.get_service_ref(path)
|
|
|
|
|
if not ref:
|
|
|
|
|
return
|
|
|
|
|
|
2021-09-13 16:52:19 +03:00
|
|
|
self._player_box.on_stop()
|
2018-12-02 00:45:55 +03:00
|
|
|
|
2021-02-07 19:27:38 +03:00
|
|
|
# IPTV type checking
|
|
|
|
|
row = self._fav_model[path][:]
|
|
|
|
|
if row[Column.FAV_TYPE] == BqServiceType.IPTV.name and callback:
|
2021-09-13 16:52:19 +03:00
|
|
|
callback = self._player_box.play(get_iptv_url(row, self._s_type))
|
2021-02-07 19:27:38 +03:00
|
|
|
|
2020-01-14 18:26:05 +03:00
|
|
|
def zap(rq):
|
|
|
|
|
if rq and rq.get("e2state", False):
|
|
|
|
|
GLib.idle_add(scroll_to, path, self._fav_view)
|
|
|
|
|
if callback:
|
|
|
|
|
callback()
|
2020-03-28 17:56:39 +03:00
|
|
|
else:
|
2021-08-15 17:24:30 +03:00
|
|
|
self.show_error_message("No connection to the receiver!")
|
2020-01-14 18:26:05 +03:00
|
|
|
|
2020-11-11 15:34:12 +03:00
|
|
|
self._http_api.send(HttpAPI.Request.ZAP, ref, zap)
|
2020-01-14 18:26:05 +03:00
|
|
|
|
|
|
|
|
def get_service_ref(self, path):
|
2018-11-17 23:19:17 +03:00
|
|
|
row = self._fav_model[path][:]
|
2020-01-14 18:26:05 +03:00
|
|
|
srv_type, fav_id = row[Column.FAV_TYPE], row[Column.FAV_ID]
|
2018-12-02 00:45:55 +03:00
|
|
|
|
2021-01-21 16:51:25 +03:00
|
|
|
if srv_type in self._marker_types:
|
2021-08-15 17:24:30 +03:00
|
|
|
self.show_error_message("Not allowed in this context!")
|
2020-01-14 18:26:05 +03:00
|
|
|
return
|
2019-11-21 16:59:43 +03:00
|
|
|
|
2020-01-14 18:26:05 +03:00
|
|
|
srv = self._services.get(fav_id, None)
|
2021-02-07 19:27:38 +03:00
|
|
|
if srv:
|
|
|
|
|
if srv_type == BqServiceType.IPTV.name:
|
|
|
|
|
return srv.fav_id.strip()
|
|
|
|
|
elif srv.picon_id:
|
|
|
|
|
return srv.picon_id.rstrip(".png").replace("_", ":")
|
2018-11-17 23:19:17 +03:00
|
|
|
|
2019-11-21 23:13:06 +03:00
|
|
|
def update_info(self):
|
|
|
|
|
""" Updating current info over HTTP API """
|
2020-01-08 21:33:24 +03:00
|
|
|
if not self._http_api or self._s_type is SettingsType.NEUTRINO_MP:
|
2019-11-21 23:13:06 +03:00
|
|
|
GLib.idle_add(self._http_status_image.set_visible, False)
|
2020-01-08 21:33:24 +03:00
|
|
|
GLib.idle_add(self._receiver_info_box.set_visible, False)
|
2019-11-21 23:13:06 +03:00
|
|
|
return False
|
|
|
|
|
|
2020-11-11 15:34:12 +03:00
|
|
|
self._http_api.send(HttpAPI.Request.INFO, None, self.update_receiver_info)
|
2019-11-21 23:13:06 +03:00
|
|
|
return True
|
2018-11-17 23:19:17 +03:00
|
|
|
|
2019-11-21 23:13:06 +03:00
|
|
|
def update_receiver_info(self, info):
|
2020-01-17 00:34:18 +03:00
|
|
|
error_code = info.get("error_code", 0) if info else 0
|
|
|
|
|
GLib.idle_add(self._receiver_info_box.set_visible, error_code == 0, priority=GLib.PRIORITY_LOW)
|
|
|
|
|
if error_code < 0:
|
|
|
|
|
return
|
|
|
|
|
elif error_code == 412:
|
|
|
|
|
self._http_api.init()
|
|
|
|
|
return
|
|
|
|
|
|
2020-02-29 21:15:24 +03:00
|
|
|
srv_name = info.get("e2servicename", None) if info else None
|
|
|
|
|
if srv_name:
|
2020-01-08 21:33:24 +03:00
|
|
|
image = info.get("e2distroversion", "")
|
|
|
|
|
image_ver = info.get("e2imageversion", "")
|
|
|
|
|
model = info.get("e2model", "")
|
|
|
|
|
info_text = "{} Image: {} {}".format(model, image, image_ver)
|
2020-01-14 18:26:05 +03:00
|
|
|
GLib.idle_add(self._receiver_info_label.set_text, info_text, priority=GLib.PRIORITY_LOW)
|
2020-02-29 21:15:24 +03:00
|
|
|
service_name = srv_name or ""
|
2020-01-14 18:26:05 +03:00
|
|
|
GLib.idle_add(self._service_name_label.set_text, service_name, priority=GLib.PRIORITY_LOW)
|
|
|
|
|
if service_name:
|
2020-01-17 00:34:18 +03:00
|
|
|
self.update_service_info()
|
2019-11-21 16:59:43 +03:00
|
|
|
|
2020-02-29 21:15:24 +03:00
|
|
|
GLib.idle_add(self._signal_box.set_visible, bool(srv_name), priority=GLib.PRIORITY_LOW)
|
|
|
|
|
|
2020-01-17 00:34:18 +03:00
|
|
|
def update_service_info(self):
|
|
|
|
|
if self._http_api:
|
2020-11-11 15:34:12 +03:00
|
|
|
self._http_api.send(HttpAPI.Request.SIGNAL, None, self.update_signal)
|
|
|
|
|
self._http_api.send(HttpAPI.Request.CURRENT, None, self.update_status)
|
2019-11-21 16:59:43 +03:00
|
|
|
|
|
|
|
|
def update_signal(self, sig):
|
2020-11-03 20:36:21 +03:00
|
|
|
if self._control_box:
|
|
|
|
|
self._control_box.update_signal(sig)
|
|
|
|
|
|
2020-01-08 21:33:24 +03:00
|
|
|
self.set_signal(sig.get("e2snr", "0 %") if sig else "0 %")
|
2019-11-24 21:58:32 +03:00
|
|
|
|
|
|
|
|
@lru_cache(maxsize=2)
|
|
|
|
|
def set_signal(self, val):
|
2020-01-17 00:34:18 +03:00
|
|
|
val = val.strip().rstrip("%") or 0
|
2020-02-29 21:15:24 +03:00
|
|
|
try:
|
|
|
|
|
val = int(val)
|
|
|
|
|
self._signal_level_bar.set_value(val)
|
|
|
|
|
except ValueError:
|
|
|
|
|
pass # NOP
|
|
|
|
|
finally:
|
|
|
|
|
GLib.idle_add(self._signal_level_bar.set_visible, val != 0 and val != "N/A")
|
2020-01-08 21:33:24 +03:00
|
|
|
|
2020-01-17 00:34:18 +03:00
|
|
|
@run_idle
|
2020-01-08 21:33:24 +03:00
|
|
|
def update_status(self, evn):
|
|
|
|
|
if evn:
|
2020-02-29 21:15:24 +03:00
|
|
|
s_duration = int(evn.get("e2eventstart", 0) or 0)
|
|
|
|
|
self._service_epg_label.set_visible(s_duration > 0)
|
2020-01-11 20:40:16 +03:00
|
|
|
if not s_duration:
|
|
|
|
|
return
|
2020-02-29 21:15:24 +03:00
|
|
|
|
2020-01-08 21:33:24 +03:00
|
|
|
s_time = datetime.fromtimestamp(s_duration)
|
2020-01-11 20:40:16 +03:00
|
|
|
end_time = datetime.fromtimestamp(s_duration + int(evn.get("e2eventduration", "0") or "0"))
|
|
|
|
|
title = evn.get("e2eventtitle", "")
|
|
|
|
|
dsc = "{} {}:{} - {}:{}".format(title, s_time.hour, s_time.minute, end_time.hour, end_time.minute)
|
2019-10-28 11:03:09 +03:00
|
|
|
self._service_epg_label.set_text(dsc)
|
2020-01-08 21:33:24 +03:00
|
|
|
self._service_epg_label.set_tooltip_text(evn.get("e2eventdescription", ""))
|
2019-10-28 00:45:47 +03:00
|
|
|
|
2020-10-25 12:54:08 +03:00
|
|
|
# ***************** Filter and search ********************* #
|
2018-01-08 22:00:48 +03:00
|
|
|
|
2020-02-11 13:18:14 +03:00
|
|
|
def on_filter_toggled(self, action, value):
|
2021-08-04 08:37:52 +03:00
|
|
|
if self._app_info_box.get_visible() or self._services_load_spinner.get_property("active"):
|
2020-02-11 13:18:14 +03:00
|
|
|
return True
|
2020-02-13 01:09:40 +03:00
|
|
|
|
2020-02-11 13:18:14 +03:00
|
|
|
action.set_state(value)
|
2018-09-12 14:05:28 +03:00
|
|
|
|
2021-03-26 11:46:43 +03:00
|
|
|
self._filter_entry.grab_focus() if value else self.on_filter_changed()
|
|
|
|
|
self.filter_set_default()
|
2021-04-09 20:00:37 +03:00
|
|
|
self._filter_box.set_visible(value)
|
2018-01-31 00:13:42 +03:00
|
|
|
|
2021-03-26 11:46:43 +03:00
|
|
|
@run_idle
|
2020-04-23 10:32:18 +03:00
|
|
|
def filter_set_default(self):
|
|
|
|
|
""" Setting defaults for filter elements. """
|
|
|
|
|
self._filter_entry.set_text("")
|
|
|
|
|
self._filter_only_free_button.set_active(False)
|
2021-03-26 11:46:43 +03:00
|
|
|
self._filter_types_model.foreach(lambda m, p, i: m.set_value(i, 1, True))
|
|
|
|
|
self._service_types.update({r[0] for r in self._filter_types_model})
|
|
|
|
|
self.update_sat_positions()
|
2020-04-23 10:32:18 +03:00
|
|
|
|
2018-09-27 22:02:35 +03:00
|
|
|
def init_sat_positions(self):
|
|
|
|
|
self._sat_positions.clear()
|
2021-03-26 11:46:43 +03:00
|
|
|
first = self._filter_sat_pos_model[0][:]
|
|
|
|
|
self._filter_sat_pos_model.clear()
|
|
|
|
|
self._filter_sat_pos_model.append(first)
|
2018-09-27 22:02:35 +03:00
|
|
|
|
|
|
|
|
def update_sat_positions(self):
|
2021-03-26 11:46:43 +03:00
|
|
|
""" Updates positions values for the filtering function. """
|
2018-09-27 22:02:35 +03:00
|
|
|
self._sat_positions.clear()
|
2019-12-13 13:31:07 +03:00
|
|
|
|
2019-12-22 20:42:29 +03:00
|
|
|
if self._s_type is SettingsType.ENIGMA_2:
|
2019-12-13 13:31:07 +03:00
|
|
|
terrestrial = False
|
|
|
|
|
cable = False
|
|
|
|
|
|
|
|
|
|
for srv in self._services.values():
|
|
|
|
|
tr_type = srv.transponder_type
|
|
|
|
|
if tr_type == "s" and srv.pos:
|
2021-03-26 11:46:43 +03:00
|
|
|
self._sat_positions.add(srv.pos)
|
2021-04-01 09:01:08 +03:00
|
|
|
elif tr_type == "t" or tr_type == "a":
|
2019-12-13 13:31:07 +03:00
|
|
|
terrestrial = True
|
|
|
|
|
elif tr_type == "c":
|
|
|
|
|
cable = True
|
|
|
|
|
|
|
|
|
|
if terrestrial:
|
2021-03-26 11:46:43 +03:00
|
|
|
self._sat_positions.add("T")
|
2019-12-13 13:31:07 +03:00
|
|
|
if cable:
|
2021-03-26 11:46:43 +03:00
|
|
|
self._sat_positions.add("C")
|
2019-12-22 20:42:29 +03:00
|
|
|
elif self._s_type is SettingsType.NEUTRINO_MP:
|
2021-03-26 11:46:43 +03:00
|
|
|
list(map(lambda s: self._sat_positions.add(s.pos), filter(lambda s: s.pos, self._services.values())))
|
2018-11-25 19:34:28 +03:00
|
|
|
|
2021-03-26 11:46:43 +03:00
|
|
|
self.update_filter_sat_positions()
|
2018-09-27 22:02:35 +03:00
|
|
|
|
|
|
|
|
def update_filter_sat_positions(self):
|
2021-03-26 11:46:43 +03:00
|
|
|
""" Updates the values for the satellite positions button model. """
|
|
|
|
|
first = self._filter_sat_pos_model[self._filter_sat_pos_model.get_iter_first()][:]
|
|
|
|
|
self._filter_sat_pos_model.clear()
|
|
|
|
|
self._filter_sat_pos_model.append((first[0], True))
|
|
|
|
|
self._sat_positions.discard(first[0])
|
|
|
|
|
list(map(lambda pos: self._filter_sat_pos_model.append((pos, True)),
|
|
|
|
|
sorted(self._sat_positions, key=self.get_pos_num, reverse=True)))
|
2018-09-27 22:02:35 +03:00
|
|
|
|
2021-03-26 11:46:43 +03:00
|
|
|
@run_with_delay(2)
|
|
|
|
|
def on_filter_changed(self, item=None):
|
2021-08-04 08:37:52 +03:00
|
|
|
self._services_load_spinner.start()
|
2021-03-26 11:46:43 +03:00
|
|
|
model = self._services_view.get_model()
|
|
|
|
|
self._services_view.set_model(None)
|
2021-08-04 08:37:52 +03:00
|
|
|
self.update_filter_cache()
|
|
|
|
|
self.update_filter_state(model)
|
|
|
|
|
|
|
|
|
|
@run_idle
|
|
|
|
|
def update_filter_state(self, model):
|
2021-03-26 11:46:43 +03:00
|
|
|
self._services_model_filter.refilter()
|
|
|
|
|
self._services_view.set_model(model)
|
2021-08-04 08:37:52 +03:00
|
|
|
GLib.idle_add(self._services_load_spinner.stop)
|
2018-01-25 16:11:52 +03:00
|
|
|
|
2021-08-04 08:37:52 +03:00
|
|
|
def update_filter_cache(self):
|
|
|
|
|
self._filter_cache.clear()
|
2021-04-09 20:00:37 +03:00
|
|
|
if not self._filter_box.is_visible():
|
2021-08-04 08:37:52 +03:00
|
|
|
return
|
|
|
|
|
|
|
|
|
|
txt = self._filter_entry.get_text().upper()
|
|
|
|
|
for r in self._services_model:
|
|
|
|
|
free = not r[Column.SRV_CODED] if self._filter_only_free_button.get_active() else True
|
|
|
|
|
self._filter_cache[r[Column.SRV_FAV_ID]] = all((r[Column.SRV_TYPE] in self._service_types,
|
|
|
|
|
r[Column.SRV_POS] in self._sat_positions, free,
|
|
|
|
|
txt in "".join((r[Column.SRV_SERVICE],
|
|
|
|
|
r[Column.SRV_PACKAGE],
|
|
|
|
|
r[Column.SRV_TYPE],
|
|
|
|
|
r[Column.SRV_SSID],
|
|
|
|
|
r[Column.SRV_POS])).upper()))
|
|
|
|
|
|
|
|
|
|
def services_filter_function(self, model, itr, data):
|
|
|
|
|
return self._filter_cache.get(model.get_value(itr, Column.SRV_FAV_ID), True)
|
2021-03-26 11:46:43 +03:00
|
|
|
|
|
|
|
|
def on_filter_type_toggled(self, toggle, path):
|
|
|
|
|
self.update_filter_toogle_model(self._filter_types_model, toggle, path, self._service_types)
|
|
|
|
|
|
|
|
|
|
def on_filter_satellite_toggled(self, toggle, path):
|
|
|
|
|
self.update_filter_toogle_model(self._filter_sat_pos_model, toggle, path, self._sat_positions)
|
|
|
|
|
|
|
|
|
|
def update_filter_toogle_model(self, model, toggle, path, values_set):
|
|
|
|
|
active = not toggle.get_active()
|
|
|
|
|
if path == "0":
|
|
|
|
|
model.foreach(lambda m, p, i: m.set_value(i, 1, active))
|
|
|
|
|
else:
|
|
|
|
|
model.set_value(model.get_iter(path), 1, active)
|
|
|
|
|
if active:
|
|
|
|
|
model.set_value(model.get_iter_first(), 1, len({r[0] for r in model if r[1]}) == len(model) - 1)
|
|
|
|
|
else:
|
|
|
|
|
model.set_value(model.get_iter_first(), 1, False)
|
2018-09-12 14:05:28 +03:00
|
|
|
|
2021-03-26 11:46:43 +03:00
|
|
|
values_set.clear()
|
|
|
|
|
values_set.update({r[0] for r in model if r[1]})
|
|
|
|
|
self.on_filter_changed()
|
2018-01-28 23:10:54 +03:00
|
|
|
|
2021-09-19 00:23:23 +03:00
|
|
|
def activate_search_state(self, view):
|
|
|
|
|
if view is self._services_view:
|
|
|
|
|
self._srv_search_button.set_active(True)
|
|
|
|
|
elif view is self._fav_view:
|
|
|
|
|
self._fav_search_button.set_active(True)
|
2018-02-02 12:45:58 +03:00
|
|
|
|
2018-09-01 00:49:11 +03:00
|
|
|
# ***************** Editing *********************#
|
|
|
|
|
|
2018-02-18 11:23:31 +03:00
|
|
|
def on_service_edit(self, view):
|
2018-02-20 00:20:32 +03:00
|
|
|
model, paths = view.get_selection().get_selected_rows()
|
2018-04-06 16:02:16 +03:00
|
|
|
if is_only_one_item_selected(paths, self._main_window):
|
2018-02-20 00:20:32 +03:00
|
|
|
model_name = get_base_model(model).get_name()
|
2020-04-19 13:23:18 +03:00
|
|
|
if model_name == self.FAV_MODEL_NAME:
|
2018-12-19 14:43:43 +03:00
|
|
|
srv_type = model.get_value(model.get_iter(paths), Column.FAV_TYPE)
|
2021-01-27 16:46:37 +03:00
|
|
|
if srv_type == BqServiceType.ALT.name:
|
2021-08-15 17:24:30 +03:00
|
|
|
return self.show_error_message("Operation not allowed in this context!")
|
2021-01-27 16:46:37 +03:00
|
|
|
|
2020-06-22 11:07:44 +03:00
|
|
|
if srv_type in self._marker_types:
|
2018-03-11 21:52:10 +03:00
|
|
|
return self.on_rename(view)
|
|
|
|
|
elif srv_type == BqServiceType.IPTV.name:
|
2018-04-06 16:02:16 +03:00
|
|
|
return IptvDialog(self._main_window,
|
|
|
|
|
self._fav_view,
|
|
|
|
|
self._services,
|
2018-12-11 19:09:55 +03:00
|
|
|
self._bouquets.get(self._bq_selected, None),
|
2020-06-10 11:10:41 +03:00
|
|
|
self._settings,
|
2018-03-11 21:52:10 +03:00
|
|
|
Action.EDIT).show()
|
2018-02-20 00:20:32 +03:00
|
|
|
self.on_locate_in_services(view)
|
2018-02-20 23:34:07 +03:00
|
|
|
|
2018-04-06 16:02:16 +03:00
|
|
|
dialog = ServiceDetailsDialog(self._main_window,
|
2019-12-13 13:31:07 +03:00
|
|
|
self._settings,
|
2018-04-06 16:02:16 +03:00
|
|
|
self._services_view,
|
2018-04-10 13:04:21 +03:00
|
|
|
self._fav_view,
|
2018-04-06 16:02:16 +03:00
|
|
|
self._services,
|
2018-12-23 16:15:48 +03:00
|
|
|
self._bouquets,
|
|
|
|
|
self._NEW_COLOR)
|
2018-02-20 00:20:32 +03:00
|
|
|
dialog.show()
|
2018-02-14 00:00:52 +03:00
|
|
|
|
2018-03-10 17:49:53 +03:00
|
|
|
def on_services_add_new(self, item):
|
2018-04-06 16:02:16 +03:00
|
|
|
dialog = ServiceDetailsDialog(self._main_window,
|
2019-12-13 13:31:07 +03:00
|
|
|
self._settings,
|
2018-04-06 16:02:16 +03:00
|
|
|
self._services_view,
|
2018-04-10 13:04:21 +03:00
|
|
|
self._fav_view,
|
2018-04-06 16:02:16 +03:00
|
|
|
self._services,
|
|
|
|
|
self._bouquets,
|
2018-03-10 17:49:53 +03:00
|
|
|
action=Action.ADD)
|
|
|
|
|
dialog.show()
|
|
|
|
|
|
2018-09-11 15:21:05 +03:00
|
|
|
def on_bouquets_edit(self, view):
|
2020-09-08 12:21:10 +03:00
|
|
|
""" Renaming bouquets. """
|
2018-12-11 19:09:55 +03:00
|
|
|
if not self._bq_selected:
|
2021-08-15 17:24:30 +03:00
|
|
|
self.show_error_message("This item is not allowed to edit!")
|
2018-09-11 15:21:05 +03:00
|
|
|
return
|
|
|
|
|
|
|
|
|
|
model, paths = view.get_selection().get_selected_rows()
|
|
|
|
|
|
|
|
|
|
if paths:
|
|
|
|
|
itr = model.get_iter(paths[0])
|
|
|
|
|
bq_name, bq_type = model.get(itr, 0, 3)
|
|
|
|
|
response = show_dialog(DialogType.INPUT, self._main_window, bq_name)
|
|
|
|
|
if response == Gtk.ResponseType.CANCEL:
|
|
|
|
|
return
|
|
|
|
|
|
2020-01-29 14:50:02 +03:00
|
|
|
bq = "{}:{}".format(response, bq_type)
|
|
|
|
|
if bq in self._bouquets:
|
2021-08-15 17:24:30 +03:00
|
|
|
self.show_error_message(get_message("A bouquet with that name exists!"))
|
2020-01-29 14:50:02 +03:00
|
|
|
return
|
|
|
|
|
|
2018-09-11 15:21:05 +03:00
|
|
|
model.set_value(itr, 0, response)
|
2020-09-08 12:21:10 +03:00
|
|
|
old_bq_name = "{}:{}".format(bq_name, bq_type)
|
|
|
|
|
self._bouquets[bq] = self._bouquets.pop(old_bq_name)
|
2021-01-27 16:46:37 +03:00
|
|
|
self._bq_file[bq] = self._bq_file.pop(old_bq_name, None)
|
2019-05-09 23:51:47 +03:00
|
|
|
self._current_bq_name = response
|
|
|
|
|
self._bq_name_label.set_text(self._current_bq_name)
|
2020-09-08 12:21:10 +03:00
|
|
|
self._bq_selected = bq
|
|
|
|
|
# services with extra names for the bouquet
|
|
|
|
|
ext_bq = self._extra_bouquets.get(old_bq_name, None)
|
|
|
|
|
if ext_bq:
|
|
|
|
|
self._extra_bouquets[bq] = ext_bq
|
2018-09-11 15:21:05 +03:00
|
|
|
|
|
|
|
|
def on_rename(self, view):
|
2018-11-02 23:13:31 +03:00
|
|
|
name, model = get_model_data(view)
|
2020-04-19 13:23:18 +03:00
|
|
|
if name == self.BQ_MODEL_NAME:
|
2018-09-11 15:21:05 +03:00
|
|
|
self.on_bouquets_edit(view)
|
2020-04-19 13:23:18 +03:00
|
|
|
elif name == self.FAV_MODEL_NAME:
|
2018-09-11 15:21:05 +03:00
|
|
|
rename(view, self._main_window, ViewTarget.FAV, service_view=self._services_view,
|
2018-10-25 16:00:25 +03:00
|
|
|
services=self._services)
|
2020-04-19 13:23:18 +03:00
|
|
|
elif name == self.SERVICE_MODEL_NAME:
|
2018-10-25 16:00:25 +03:00
|
|
|
rename(view, self._main_window, ViewTarget.SERVICES, fav_view=self._fav_view, services=self._services)
|
2018-09-11 15:21:05 +03:00
|
|
|
|
|
|
|
|
def on_rename_for_bouquet(self, item):
|
|
|
|
|
selection = get_selection(self._fav_view, self._main_window)
|
|
|
|
|
if not selection:
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
model, paths = selection
|
|
|
|
|
data = model[paths][:]
|
2018-12-19 14:43:43 +03:00
|
|
|
cur_name, srv_type, fav_id = data[Column.FAV_SERVICE], data[Column.FAV_TYPE], data[Column.FAV_ID]
|
2018-09-16 23:39:31 +03:00
|
|
|
|
2018-10-26 19:28:40 +03:00
|
|
|
if srv_type == BqServiceType.IPTV.name or srv_type == BqServiceType.MARKER.name:
|
2021-08-15 17:24:30 +03:00
|
|
|
self.show_error_message("Not allowed in this context!")
|
2018-09-16 23:40:02 +03:00
|
|
|
return
|
2018-09-16 23:39:31 +03:00
|
|
|
|
2018-09-11 15:21:05 +03:00
|
|
|
response = show_dialog(DialogType.INPUT, self._main_window, cur_name)
|
|
|
|
|
if response == Gtk.ResponseType.CANCEL:
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
srv = self._services.get(fav_id, None)
|
2018-12-11 19:09:55 +03:00
|
|
|
ex_bq = self._extra_bouquets.get(self._bq_selected, None)
|
2018-09-11 15:21:05 +03:00
|
|
|
|
|
|
|
|
if srv.service == response and ex_bq:
|
|
|
|
|
ex_bq.pop(fav_id, None)
|
|
|
|
|
if not ex_bq:
|
2018-12-11 19:09:55 +03:00
|
|
|
self._extra_bouquets.pop(self._bq_selected, None)
|
2018-09-11 15:21:05 +03:00
|
|
|
else:
|
|
|
|
|
if ex_bq:
|
|
|
|
|
ex_bq[fav_id] = response
|
|
|
|
|
else:
|
2018-12-11 19:09:55 +03:00
|
|
|
self._extra_bouquets[self._bq_selected] = {fav_id: response}
|
2018-09-11 15:21:05 +03:00
|
|
|
|
2018-12-17 18:31:57 +03:00
|
|
|
model.set(model.get_iter(paths), {Column.FAV_SERVICE: response, Column.FAV_TOOLTIP: None,
|
2018-12-23 16:15:48 +03:00
|
|
|
Column.FAV_BACKGROUND: self._EXTRA_COLOR})
|
2018-09-11 15:21:05 +03:00
|
|
|
|
|
|
|
|
def on_set_default_name_for_bouquet(self, item):
|
2018-09-11 16:25:12 +03:00
|
|
|
selection = get_selection(self._fav_view, self._main_window)
|
|
|
|
|
if not selection:
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
model, paths = selection
|
2018-12-17 18:31:57 +03:00
|
|
|
fav_id = model[paths][Column.FAV_ID]
|
2018-09-11 16:25:12 +03:00
|
|
|
srv = self._services.get(fav_id, None)
|
2018-12-11 19:09:55 +03:00
|
|
|
ex_bq = self._extra_bouquets.get(self._bq_selected, None)
|
2018-09-11 16:25:12 +03:00
|
|
|
|
|
|
|
|
if not ex_bq:
|
2021-08-15 17:24:30 +03:00
|
|
|
self.show_error_message("No changes required!")
|
2018-09-11 16:25:12 +03:00
|
|
|
return
|
|
|
|
|
else:
|
|
|
|
|
if not ex_bq.pop(fav_id, None):
|
2021-08-15 17:24:30 +03:00
|
|
|
self.show_error_message("No changes required!")
|
2018-09-11 16:25:12 +03:00
|
|
|
return
|
|
|
|
|
if not ex_bq:
|
2018-12-11 19:09:55 +03:00
|
|
|
self._extra_bouquets.pop(self._bq_selected, None)
|
2018-09-11 16:25:12 +03:00
|
|
|
|
2018-12-17 18:31:57 +03:00
|
|
|
model.set(model.get_iter(paths), {Column.FAV_SERVICE: srv.service, Column.FAV_TOOLTIP: None,
|
|
|
|
|
Column.FAV_BACKGROUND: None})
|
2018-09-11 15:21:05 +03:00
|
|
|
|
2018-09-01 00:49:11 +03:00
|
|
|
def on_locate_in_services(self, view):
|
|
|
|
|
locate_in_services(view, self._services_view, self._main_window)
|
|
|
|
|
|
2021-04-19 13:15:36 +03:00
|
|
|
def on_mark_duplicates(self, item):
|
|
|
|
|
""" Marks services with duplicate [names] in the fav list. """
|
|
|
|
|
from collections import Counter
|
|
|
|
|
|
|
|
|
|
dup = Counter(r[Column.FAV_SERVICE] for r in self._fav_model if r[Column.FAV_TYPE] not in self._marker_types)
|
|
|
|
|
dup = {k for k, v in dup.items() if v > 1}
|
|
|
|
|
|
|
|
|
|
for r in self._fav_model:
|
|
|
|
|
if r[Column.FAV_SERVICE] in dup:
|
|
|
|
|
r[Column.FAV_BACKGROUND] = self._NEW_COLOR
|
|
|
|
|
|
2018-09-01 00:49:11 +03:00
|
|
|
# ***************** Picons *********************#
|
|
|
|
|
|
2018-08-18 10:21:40 +03:00
|
|
|
@run_task
|
2018-01-28 23:10:54 +03:00
|
|
|
def update_picons(self):
|
2021-08-30 15:04:15 +03:00
|
|
|
update_picons_data(self._settings.profile_picons_path, self._picons, self._picons_size)
|
2018-08-18 10:21:40 +03:00
|
|
|
append_picons(self._picons, self._services_model)
|
2018-01-23 16:18:28 +03:00
|
|
|
|
2020-06-01 21:36:56 +03:00
|
|
|
def on_assign_picon(self, view, src_path=None, dst_path=None):
|
2020-06-06 09:36:11 +03:00
|
|
|
return assign_picons(self.get_target_view(view), self._services_view, self._fav_view, self._main_window,
|
|
|
|
|
self._picons, self._settings, self._services, src_path, dst_path)
|
2018-01-29 18:07:47 +03:00
|
|
|
|
2018-01-30 12:37:04 +03:00
|
|
|
def on_remove_picon(self, view):
|
2020-06-01 21:36:56 +03:00
|
|
|
remove_picon(self.get_target_view(view), self._services_view, self._fav_view, self._picons, self._settings)
|
2018-01-29 18:07:47 +03:00
|
|
|
|
2018-01-30 12:37:04 +03:00
|
|
|
def on_reference_picon(self, view):
|
|
|
|
|
""" Copying picon id to clipboard """
|
2018-04-06 16:02:16 +03:00
|
|
|
copy_picon_reference(self.get_target_view(view), view, self._services, self._clipboard, self._main_window)
|
2018-02-01 13:10:06 +03:00
|
|
|
|
2019-03-03 12:50:40 +03:00
|
|
|
def on_remove_unused_picons(self, item):
|
|
|
|
|
if show_dialog(DialogType.QUESTION, self._main_window) == Gtk.ResponseType.CANCEL:
|
|
|
|
|
return
|
|
|
|
|
|
2019-12-15 19:01:27 +03:00
|
|
|
remove_all_unused_picons(self._settings, self._picons, self._services.values())
|
2019-03-03 12:50:40 +03:00
|
|
|
|
2018-02-01 13:10:06 +03:00
|
|
|
def get_target_view(self, view):
|
|
|
|
|
return ViewTarget.SERVICES if Gtk.Buildable.get_name(view) == "services_tree_view" else ViewTarget.FAV
|
2018-01-29 18:07:47 +03:00
|
|
|
|
2021-01-08 23:01:16 +03:00
|
|
|
# ***************** Bouquets ********************* #
|
2018-09-01 00:49:11 +03:00
|
|
|
|
2018-04-02 23:55:41 +03:00
|
|
|
def on_create_bouquet_for_current_satellite(self, item):
|
2018-04-03 22:47:29 +03:00
|
|
|
self.create_bouquets(BqGenType.SAT)
|
2018-04-02 23:55:41 +03:00
|
|
|
|
|
|
|
|
def on_create_bouquet_for_each_satellite(self, item):
|
2018-04-03 22:47:29 +03:00
|
|
|
self.create_bouquets(BqGenType.EACH_SAT)
|
2018-04-02 23:55:41 +03:00
|
|
|
|
|
|
|
|
def on_create_bouquet_for_current_package(self, item):
|
2018-04-03 22:47:29 +03:00
|
|
|
self.create_bouquets(BqGenType.PACKAGE)
|
2018-04-02 23:55:41 +03:00
|
|
|
|
|
|
|
|
def on_create_bouquet_for_each_package(self, item):
|
2018-04-03 22:47:29 +03:00
|
|
|
self.create_bouquets(BqGenType.EACH_PACKAGE)
|
|
|
|
|
|
2018-04-07 23:49:36 +03:00
|
|
|
def on_create_bouquet_for_current_type(self, item):
|
|
|
|
|
self.create_bouquets(BqGenType.TYPE)
|
|
|
|
|
|
|
|
|
|
def on_create_bouquet_for_each_type(self, item):
|
|
|
|
|
self.create_bouquets(BqGenType.EACH_TYPE)
|
|
|
|
|
|
2018-04-03 22:47:29 +03:00
|
|
|
def create_bouquets(self, g_type):
|
2021-08-24 16:19:39 +03:00
|
|
|
if self._services_load_spinner.get_property("active"):
|
|
|
|
|
self.show_error_message("Data loading in progress!")
|
|
|
|
|
return
|
|
|
|
|
|
2021-04-03 20:51:14 +03:00
|
|
|
gen_bouquets(self._services_view, self._bouquets_view, self._main_window, g_type, self._s_type,
|
|
|
|
|
self.append_bouquet)
|
2018-04-02 23:55:41 +03:00
|
|
|
|
2021-01-08 23:01:16 +03:00
|
|
|
# ***************** Alternatives ********************* #
|
|
|
|
|
|
|
|
|
|
def on_add_alternatives(self, item):
|
2021-01-10 14:05:01 +03:00
|
|
|
""" Adding alternatives. """
|
2021-01-08 23:01:16 +03:00
|
|
|
model, paths = self._fav_view.get_selection().get_selected_rows()
|
|
|
|
|
if not paths:
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
if len(paths) > 1:
|
2021-08-15 17:24:30 +03:00
|
|
|
self.show_error_message("Please, select only one item!")
|
2021-01-08 23:01:16 +03:00
|
|
|
return
|
|
|
|
|
|
|
|
|
|
row = model[paths][:]
|
2021-01-27 16:46:37 +03:00
|
|
|
s_types = {BqServiceType.MARKER.name, BqServiceType.SPACE.name, BqServiceType.ALT.name, BqServiceType.IPTV.name}
|
|
|
|
|
if row[Column.FAV_TYPE] in s_types:
|
2021-08-15 17:24:30 +03:00
|
|
|
self.show_error_message("Operation not allowed in this context!")
|
2021-01-08 23:01:16 +03:00
|
|
|
return
|
|
|
|
|
|
|
|
|
|
srv = self._services.get(row[Column.FAV_ID], None)
|
|
|
|
|
bq = self._bouquets.get(self._bq_selected, None)
|
|
|
|
|
if not srv or not bq:
|
|
|
|
|
return
|
|
|
|
|
|
2021-01-27 16:46:37 +03:00
|
|
|
bq_name, sep, bq_type = self._bq_selected.partition(":")
|
2021-01-08 23:01:16 +03:00
|
|
|
fav_id = srv.fav_id
|
2021-01-27 16:46:37 +03:00
|
|
|
|
|
|
|
|
key = "de{:02d}:{}".format(self._alt_counter, bq_type)
|
|
|
|
|
# Generating file name for alternative
|
|
|
|
|
while key in self._alt_file:
|
|
|
|
|
self._alt_counter += 1
|
|
|
|
|
key = "de{:02d}:{}".format(self._alt_counter, bq_type)
|
|
|
|
|
|
|
|
|
|
alt_name = "de{:02d}".format(self._alt_counter)
|
2021-01-08 23:01:16 +03:00
|
|
|
alt_id = "alternatives_{}_{}".format(self._bq_selected, fav_id)
|
|
|
|
|
if alt_id in bq:
|
2021-08-15 17:24:30 +03:00
|
|
|
self.show_error_message("A similar service is already in this list!")
|
2021-01-08 23:01:16 +03:00
|
|
|
return
|
|
|
|
|
|
|
|
|
|
dt, it = BqServiceType.DEFAULT, BqServiceType.IPTV
|
|
|
|
|
bq_srv = BouquetService(None, dt if srv.service_type != it.name else it, fav_id, 0)
|
|
|
|
|
s_type = BqServiceType.ALT.name
|
2021-01-27 16:46:37 +03:00
|
|
|
a_srv = srv._replace(service_type=s_type, pos=None, data_id=alt_name, fav_id=alt_id, transponder=(bq_srv,))
|
2021-01-08 23:01:16 +03:00
|
|
|
try:
|
|
|
|
|
index = bq.index(fav_id)
|
|
|
|
|
except ValueError as e:
|
|
|
|
|
log("[on_add_alternatives] error: {}".format(e))
|
|
|
|
|
else:
|
|
|
|
|
bq[index] = alt_id
|
|
|
|
|
self._services[alt_id] = a_srv
|
2021-01-27 16:46:37 +03:00
|
|
|
self._alt_file.add(key)
|
2021-01-08 23:01:16 +03:00
|
|
|
data = {Column.FAV_CODED: srv.coded, Column.FAV_SERVICE: srv.service, Column.FAV_LOCKED: srv.locked,
|
|
|
|
|
Column.FAV_HIDE: srv.hide, Column.FAV_TYPE: s_type, Column.FAV_POS: None,
|
|
|
|
|
Column.FAV_ID: alt_id, Column.FAV_PICON: self._picons.get(srv.picon_id, None)}
|
|
|
|
|
model.set(model.get_iter(paths), data)
|
|
|
|
|
self._fav_view.row_activated(paths[0], self._fav_view.get_column(Column.FAV_NUM))
|
|
|
|
|
|
|
|
|
|
def delete_alts(self, itrs, model, rows):
|
2021-01-10 14:05:01 +03:00
|
|
|
""" Deleting alternatives. """
|
2021-01-08 23:01:16 +03:00
|
|
|
list(map(model.remove, itrs))
|
|
|
|
|
row = rows[0]
|
|
|
|
|
alt_id = row[Column.ALT_ID]
|
|
|
|
|
|
|
|
|
|
if not len(model):
|
|
|
|
|
bq = self._bouquets.get(self._bq_selected, None)
|
|
|
|
|
if not bq:
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
fav_id, itr = row[Column.ALT_FAV_ID], row[Column.ALT_ITER]
|
|
|
|
|
bq[bq.index(alt_id)] = fav_id
|
|
|
|
|
self._services.pop(alt_id, None)
|
|
|
|
|
srv = self._services.get(fav_id, None)
|
|
|
|
|
if srv:
|
|
|
|
|
itr = self._fav_model.get_iter_from_string(itr)
|
|
|
|
|
data = {Column.FAV_CODED: srv.coded, Column.FAV_SERVICE: srv.service, Column.FAV_LOCKED: srv.locked,
|
|
|
|
|
Column.FAV_HIDE: srv.hide, Column.FAV_TYPE: srv.service_type, Column.FAV_POS: srv.pos,
|
|
|
|
|
Column.FAV_ID: srv.fav_id, Column.FAV_PICON: self._picons.get(srv.picon_id, None)}
|
|
|
|
|
self._fav_model.set(itr, data)
|
|
|
|
|
self._alt_revealer.set_visible(False)
|
|
|
|
|
else:
|
|
|
|
|
srv = self._services.get(alt_id, None)
|
|
|
|
|
if srv:
|
|
|
|
|
alt_services = srv.transponder or ()
|
|
|
|
|
alt_services = tuple(s for s in alt_services if s.data not in {row[Column.ALT_FAV_ID] for row in rows})
|
|
|
|
|
self._services[alt_id] = srv._replace(transponder=alt_services)
|
|
|
|
|
|
|
|
|
|
yield True
|
|
|
|
|
|
|
|
|
|
def on_alt_view_drag_data_received(self, view, drag_context, x, y, data, info, time):
|
2021-01-10 14:05:01 +03:00
|
|
|
srv = self._services.get(self._alt_model.get_value(self._alt_model.get_iter_first(), Column.ALT_ID), None)
|
|
|
|
|
if not srv:
|
|
|
|
|
return True
|
|
|
|
|
|
2021-01-08 23:01:16 +03:00
|
|
|
txt = data.get_text()
|
|
|
|
|
if txt:
|
|
|
|
|
itr_str, sep, source = txt.partition(self.DRAG_SEP)
|
|
|
|
|
if source == self.SERVICE_MODEL_NAME:
|
|
|
|
|
model, id_col, t_col = self._services_view.get_model(), Column.SRV_FAV_ID, Column.SRV_TYPE
|
|
|
|
|
elif source == self.FAV_MODEL_NAME:
|
|
|
|
|
model, id_col, t_col = self._fav_view.get_model(), Column.FAV_ID, Column.FAV_TYPE
|
2021-01-10 14:05:01 +03:00
|
|
|
elif source == self.ALT_MODEL_NAME:
|
|
|
|
|
return self.on_alt_move(itr_str, view.get_dest_row_at_pos(x, y), srv)
|
2021-01-08 23:01:16 +03:00
|
|
|
else:
|
|
|
|
|
return True
|
|
|
|
|
|
2021-01-10 14:05:01 +03:00
|
|
|
return self.on_alt_received(itr_str, model, id_col, t_col, srv)
|
|
|
|
|
|
|
|
|
|
def on_alt_received(self, itr_str, model, id_col, t_col, srv):
|
|
|
|
|
itrs = tuple(model.get_iter_from_string(itr) for itr in itr_str.split(","))
|
|
|
|
|
types = {BqServiceType.MARKER.name, BqServiceType.SPACE.name, BqServiceType.ALT.name}
|
|
|
|
|
ids = tuple(model.get_value(itr, id_col) for itr in itrs if model.get_value(itr, t_col) not in types)
|
|
|
|
|
srvs = tuple(self._services.get(f_id, None) for f_id in ids)
|
|
|
|
|
dt, it = BqServiceType.DEFAULT, BqServiceType.IPTV
|
|
|
|
|
a_srvs = tuple(BouquetService(None, dt if s.service_type != it.name else it, s.fav_id, 0) for s in srvs)
|
|
|
|
|
alt_services = srv.transponder + a_srvs
|
|
|
|
|
self._services[srv.fav_id] = srv._replace(transponder=alt_services)
|
|
|
|
|
|
|
|
|
|
a_row = self._alt_model[self._alt_model.get_iter_first()][:]
|
|
|
|
|
alt_id, a_itr = a_row[Column.ALT_ID], a_row[Column.ALT_ITER]
|
|
|
|
|
|
|
|
|
|
for i, srv in enumerate(srvs, start=len(self._alt_model) + 1):
|
|
|
|
|
pic = self._picons.get(srv.picon_id, None)
|
|
|
|
|
self._alt_model.append((i, pic, srv.service, srv.service_type, srv.pos, srv.fav_id, alt_id, a_itr))
|
|
|
|
|
|
|
|
|
|
return True
|
2021-01-08 23:01:16 +03:00
|
|
|
|
2021-01-10 14:05:01 +03:00
|
|
|
def on_alt_move(self, s_iters, info, srv):
|
|
|
|
|
""" Move alternatives in the list. """
|
|
|
|
|
di = -1
|
|
|
|
|
if info:
|
|
|
|
|
path, position = info
|
|
|
|
|
di = path.get_indices()[0]
|
|
|
|
|
|
|
|
|
|
itrs = tuple(self._alt_model.get_iter_from_string(itr) for itr in s_iters.split(","))
|
|
|
|
|
[self._alt_model.insert(i, r) for i, r in enumerate((self._alt_model[in_itr][:] for in_itr in itrs), start=di)]
|
|
|
|
|
list(map(self._alt_model.remove, itrs))
|
|
|
|
|
|
|
|
|
|
d_type, i_type = BqServiceType.DEFAULT, BqServiceType.IPTV
|
|
|
|
|
alt_srvs = []
|
|
|
|
|
for i, r in enumerate(self._alt_model, start=1):
|
|
|
|
|
r[Column.ALT_NUM] = i
|
|
|
|
|
s_type = d_type if r[Column.ALT_TYPE] != i_type.name else i_type
|
|
|
|
|
alt_srvs.append(BouquetService(None, s_type, r[Column.ALT_FAV_ID], i))
|
|
|
|
|
|
|
|
|
|
self._services[srv.fav_id] = srv._replace(transponder=tuple(alt_srvs))
|
|
|
|
|
a_iter = self._alt_model.get_iter_first()
|
|
|
|
|
srv = self._services.get(self._alt_model.get_value(a_iter, Column.ALT_FAV_ID), None)
|
|
|
|
|
if srv:
|
|
|
|
|
fav_iter = self._fav_model.get_iter_from_string(self._alt_model.get_value(a_iter, Column.ALT_ITER))
|
|
|
|
|
self._fav_model.set_value(fav_iter, Column.FAV_PICON, self._picons.get(srv.picon_id, None))
|
2021-01-08 23:01:16 +03:00
|
|
|
|
|
|
|
|
return True
|
|
|
|
|
|
2021-01-21 16:51:25 +03:00
|
|
|
def on_alt_selection(self, model, path, column):
|
|
|
|
|
if self._control_box and self._control_box.update_epg:
|
|
|
|
|
row = model[path][:]
|
|
|
|
|
srv = self._services.get(row[Column.ALT_FAV_ID], None)
|
|
|
|
|
if srv and srv.transponder or row[Column.ALT_TYPE] == BqServiceType.IPTV.name:
|
|
|
|
|
self._control_box.on_service_changed(srv.picon_id.rstrip(".png").replace("_", ":"))
|
|
|
|
|
|
2021-01-08 23:01:16 +03:00
|
|
|
# ***************** Profile label ********************* #
|
2018-09-01 00:49:11 +03:00
|
|
|
|
2019-12-27 23:05:37 +03:00
|
|
|
@run_idle
|
2018-06-01 11:16:30 +03:00
|
|
|
def update_profile_label(self):
|
2020-02-13 01:09:40 +03:00
|
|
|
label, sep, ip = self._current_ip_label.get_text().partition(":")
|
|
|
|
|
self._current_ip_label.set_text("{}: {}".format(label, self._settings.host))
|
|
|
|
|
|
2019-12-22 20:42:29 +03:00
|
|
|
profile_name = self._profile_combo_box.get_active_text()
|
|
|
|
|
msg = get_message("Profile:")
|
|
|
|
|
|
|
|
|
|
if self._s_type is SettingsType.ENIGMA_2:
|
2021-08-20 17:24:48 +03:00
|
|
|
title = "DemonEditor [{} {} - Enigma2 v.{}]".format(msg, profile_name, self.get_format_version())
|
|
|
|
|
self._main_window.set_title(title)
|
2019-12-22 20:42:29 +03:00
|
|
|
elif self._s_type is SettingsType.NEUTRINO_MP:
|
2021-08-20 17:24:48 +03:00
|
|
|
self._main_window.set_title("DemonEditor [{} {} - Neutrino-MP]".format(msg, profile_name))
|
2018-06-01 11:16:30 +03:00
|
|
|
|
|
|
|
|
def get_format_version(self):
|
2019-12-13 13:31:07 +03:00
|
|
|
return 5 if self._settings.v5_support else 4
|
2018-06-01 11:16:30 +03:00
|
|
|
|
2021-08-15 17:24:30 +03:00
|
|
|
def show_error_message(self, message):
|
2021-08-18 00:24:51 +03:00
|
|
|
self.show_info_message(message, Gtk.MessageType.ERROR)
|
2019-03-19 21:44:05 +03:00
|
|
|
|
2021-08-15 17:24:30 +03:00
|
|
|
@run_idle
|
|
|
|
|
def show_info_message(self, text, message_type):
|
|
|
|
|
self._info_bar.set_visible(False)
|
|
|
|
|
self._info_label.set_text(get_message(text))
|
|
|
|
|
self._info_bar.set_message_type(message_type)
|
|
|
|
|
self._info_bar.set_visible(True)
|
|
|
|
|
|
|
|
|
|
def on_info_bar_close(self, bar=None, resp=None):
|
|
|
|
|
self._info_bar.set_visible(False)
|
|
|
|
|
|
2020-06-04 11:32:53 +03:00
|
|
|
def is_data_saved(self):
|
|
|
|
|
if self._data_hash != 0 and self._data_hash != self.get_data_hash():
|
2020-07-17 09:51:45 +03:00
|
|
|
msg = "There are unsaved changes.\n\n\t Save them now?"
|
2020-06-04 11:32:53 +03:00
|
|
|
resp = show_dialog(DialogType.QUESTION, self._main_window, msg, action_type=Gtk.ButtonsType.YES_NO)
|
|
|
|
|
return resp != Gtk.ResponseType.YES
|
|
|
|
|
return True
|
|
|
|
|
|
|
|
|
|
def get_data_hash(self):
|
|
|
|
|
""" Returns the sum of all data hash. """
|
|
|
|
|
return sum(map(hash, map(frozenset, (self._services.items(),
|
|
|
|
|
self._bouquets.keys(),
|
|
|
|
|
map(tuple, self._bouquets.values())))))
|
|
|
|
|
|
2020-04-19 13:23:18 +03:00
|
|
|
# ******************* Properties ***********************#
|
|
|
|
|
|
|
|
|
|
@property
|
|
|
|
|
def fav_view(self):
|
|
|
|
|
return self._fav_view
|
|
|
|
|
|
|
|
|
|
@property
|
|
|
|
|
def services_view(self):
|
|
|
|
|
return self._services_view
|
|
|
|
|
|
|
|
|
|
@property
|
|
|
|
|
def bouquets_view(self):
|
|
|
|
|
return self._bouquets_view
|
|
|
|
|
|
2020-05-25 18:25:36 +03:00
|
|
|
@property
|
|
|
|
|
def filter_entry(self):
|
|
|
|
|
return self._filter_entry
|
|
|
|
|
|
2020-04-19 13:23:18 +03:00
|
|
|
@property
|
|
|
|
|
def current_services(self):
|
|
|
|
|
return self._services
|
|
|
|
|
|
2021-05-11 12:46:26 +03:00
|
|
|
@property
|
|
|
|
|
def current_bouquets(self):
|
|
|
|
|
return self._bouquets
|
|
|
|
|
|
2020-06-06 09:36:11 +03:00
|
|
|
@property
|
|
|
|
|
def picons_buffer(self):
|
|
|
|
|
""" Returns a copy and clears the current buffer. """
|
|
|
|
|
buf = list(self._picons_buffer)
|
|
|
|
|
self._picons_buffer.clear()
|
|
|
|
|
return buf
|
|
|
|
|
|
|
|
|
|
@picons_buffer.setter
|
|
|
|
|
def picons_buffer(self, value):
|
|
|
|
|
self._picons_buffer.extend(value)
|
|
|
|
|
|
2021-08-31 14:16:14 +03:00
|
|
|
@property
|
|
|
|
|
def app_window(self):
|
|
|
|
|
return self._main_window
|
|
|
|
|
|
2021-09-04 14:46:11 +03:00
|
|
|
@property
|
|
|
|
|
def app_settings(self):
|
|
|
|
|
return self._settings
|
|
|
|
|
|
2021-09-13 16:52:19 +03:00
|
|
|
@property
|
|
|
|
|
def http_api(self):
|
|
|
|
|
return self._http_api
|
|
|
|
|
|
2021-09-04 14:46:11 +03:00
|
|
|
@GObject.Property(type=bool, default=True)
|
|
|
|
|
def is_enigma(self):
|
|
|
|
|
return self._is_enigma
|
|
|
|
|
|
|
|
|
|
@is_enigma.setter
|
|
|
|
|
def is_enigma(self, value):
|
|
|
|
|
self._is_enigma = value
|
|
|
|
|
|
2017-11-09 19:01:09 +03:00
|
|
|
|
|
|
|
|
def start_app():
|
2019-12-22 20:42:29 +03:00
|
|
|
try:
|
|
|
|
|
Settings.get_instance()
|
2020-05-29 13:24:55 +03:00
|
|
|
except SettingsReadException as e:
|
|
|
|
|
msg = "{}\n {}".format(get_message("Error reading or writing program settings!"), e)
|
|
|
|
|
show_dialog(DialogType.INFO, transient=Gtk.Dialog(), text=msg)
|
2019-12-22 20:42:29 +03:00
|
|
|
except SettingsException as e:
|
2020-04-19 13:23:18 +03:00
|
|
|
msg = "{} \n{}".format(e, get_message("All setting were reset. Restart the program!"))
|
2019-12-22 20:42:29 +03:00
|
|
|
show_dialog(DialogType.INFO, transient=Gtk.Dialog(), text=msg)
|
2020-01-02 15:47:48 +03:00
|
|
|
Settings.reset_to_default()
|
2019-12-22 20:42:29 +03:00
|
|
|
else:
|
|
|
|
|
app = Application()
|
|
|
|
|
app.run(sys.argv)
|
2017-11-09 19:01:09 +03:00
|
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
2017-11-10 13:38:03 +03:00
|
|
|
pass
|