2017-11-13 00:21:52 +03:00
|
|
|
import os
|
2018-12-01 13:34:26 +03:00
|
|
|
import sys
|
2018-04-16 18:50:48 +03:00
|
|
|
|
2017-11-14 19:20:16 +03:00
|
|
|
from contextlib import suppress
|
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
|
2017-11-13 00:21:52 +03:00
|
|
|
|
2019-05-12 16:26:58 +03:00
|
|
|
from gi.repository import GLib, Gio
|
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
|
2019-03-18 23:03:42 +03:00
|
|
|
from app.connections import http_request, HttpRequestType, download_data, DownloadType, upload_data, test_http, \
|
|
|
|
|
TestException
|
2017-12-24 16:43:05 +03:00
|
|
|
from app.eparser import get_blacklist, write_blacklist, parse_m3u
|
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
|
2017-12-30 21:51:57 +03:00
|
|
|
from app.properties import get_config, write_config, Profile
|
2018-04-29 15:36:35 +03:00
|
|
|
from app.tools.media import Player
|
2019-04-18 23:05:19 +03:00
|
|
|
from app.ui.epg_dialog import EpgDialog
|
2019-02-05 16:58:54 +03:00
|
|
|
from .backup import BackupDialog, backup_data, clear_data_path
|
2019-03-14 12:37:48 +03:00
|
|
|
from .imports import ImportDialog, import_bouquet
|
2018-11-06 21:21:47 +03:00
|
|
|
from .download_dialog import DownloadDialog
|
2018-08-18 17:35:30 +03:00
|
|
|
from .iptv import IptvDialog, SearchUnavailableDialog, IptvListConfigurationDialog
|
2018-03-06 19:06:16 +03:00
|
|
|
from .search import SearchProvider
|
2018-12-23 16:15:48 +03:00
|
|
|
from .uicommons import Gtk, Gdk, UI_RESOURCES_PATH, LOCKED_ICON, HIDE_ICON, IPTV_ICON, MOVE_KEYS, KeyboardKey, Column, \
|
2019-03-14 12:37:48 +03:00
|
|
|
EXTRA_COLOR, NEW_COLOR, FavClickMode
|
2018-03-06 19:06:16 +03:00
|
|
|
from .dialogs import show_dialog, DialogType, get_chooser_dialog, WaitDialog, get_message
|
2018-10-25 16:00:25 +03:00
|
|
|
from .main_helper import insert_marker, move_items, rename, ViewTarget, set_flags, locate_in_services, \
|
2018-08-18 10:21:40 +03:00
|
|
|
scroll_to, get_base_model, update_picons_data, copy_picon_reference, assign_picon, remove_picon, \
|
2019-03-03 12:50:40 +03:00
|
|
|
is_only_one_item_selected, gen_bouquets, BqGenType, get_iptv_url, append_picons, get_selection, get_model_data, \
|
|
|
|
|
remove_all_unused_picons
|
2018-04-25 17:26:29 +03:00
|
|
|
from .picons_downloader import PiconsDialog
|
2017-11-09 19:01:09 +03:00
|
|
|
from .satellites_dialog import show_satellites_dialog
|
|
|
|
|
from .settings_dialog import show_settings_dialog
|
2018-03-10 17:49:53 +03:00
|
|
|
from .service_details_dialog import ServiceDetailsDialog, Action
|
2017-11-09 19:01:09 +03:00
|
|
|
|
|
|
|
|
|
2018-12-01 13:34:26 +03:00
|
|
|
class Application(Gtk.Application):
|
2018-03-03 20:55:08 +03:00
|
|
|
_TV_TYPES = ("TV", "TV (HD)", "TV (UHD)", "TV (H264)")
|
|
|
|
|
|
2018-01-24 13:39:11 +03:00
|
|
|
_SERVICE_LIST_NAME = "services_list_store"
|
|
|
|
|
_FAV_LIST_NAME = "fav_list_store"
|
|
|
|
|
_BOUQUETS_LIST_NAME = "bouquets_tree_store"
|
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",
|
2018-09-16 16:59:34 +03:00
|
|
|
"fav_insert_marker_popup_item", "fav_edit_sub_menu_popup_item", "fav_edit_popup_item",
|
2019-05-29 14:31:44 +03:00
|
|
|
"fav_picon_popup_item", "fav_copy_popup_item", "fav_epg_configuration_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
|
|
|
|
2019-05-29 14:31:44 +03:00
|
|
|
_FAV_ENIGMA_ELEMENTS = ("fav_insert_marker_popup_item", "fav_epg_configuration_popup_item",
|
|
|
|
|
"epg_configuration_header_button")
|
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",
|
|
|
|
|
"epg_configuration_header_button")
|
2019-02-23 13:54:00 +03:00
|
|
|
|
2017-11-26 20:40:22 +03:00
|
|
|
_LOCK_HIDE_ELEMENTS = ("locked_tool_button", "hide_tool_button")
|
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)
|
2018-12-01 13:34:26 +03:00
|
|
|
|
2018-07-08 00:09:26 +03:00
|
|
|
handlers = {"on_close_app": self.on_close_app,
|
2017-11-09 19:01:09 +03:00
|
|
|
"on_resize": self.on_resize,
|
|
|
|
|
"on_about_app": self.on_about_app,
|
|
|
|
|
"on_preferences": self.on_preferences,
|
|
|
|
|
"on_download": self.on_download,
|
|
|
|
|
"on_data_open": self.on_data_open,
|
|
|
|
|
"on_data_save": self.on_data_save,
|
2018-12-01 00:13:19 +03:00
|
|
|
"on_new_configuration": self.on_new_configuration,
|
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,
|
|
|
|
|
"on_satellite_editor_show": self.on_satellite_editor_show,
|
|
|
|
|
"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-02-18 11:23:31 +03:00
|
|
|
"on_edit": self.on_rename,
|
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_service_edit": self.on_service_edit,
|
|
|
|
|
"on_services_add_new": self.on_services_add_new,
|
2017-11-09 19:01:09 +03:00
|
|
|
"on_delete": self.on_delete,
|
2018-12-17 18:31:57 +03:00
|
|
|
"on_tool_edit": self.on_header_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,
|
2018-09-22 21:08:28 +03:00
|
|
|
"on_view_drag_begin": self.on_view_drag_begin,
|
2018-09-19 11:46:41 +03:00
|
|
|
"on_view_drag_data_get": self.on_view_drag_data_get,
|
|
|
|
|
"on_view_drag_data_received": self.on_view_drag_data_received,
|
|
|
|
|
"on_bq_view_drag_data_received": self.on_bq_view_drag_data_received,
|
2018-09-22 19:14:47 +03:00
|
|
|
"on_view_press": self.on_view_press,
|
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,
|
|
|
|
|
"on_hide": self.on_hide,
|
2017-11-29 00:26:12 +03:00
|
|
|
"on_locked": self.on_locked,
|
2017-12-08 18:32:28 +03:00
|
|
|
"on_model_changed": self.on_model_changed,
|
2017-12-19 22:57:04 +03:00
|
|
|
"on_import_m3u": self.on_import_m3u,
|
2019-04-18 21:43:35 +03:00
|
|
|
"on_export_to_m3u": self.on_export_to_m3u,
|
2019-02-23 13:54:00 +03:00
|
|
|
"on_import_bouquet": self.on_import_bouquet,
|
2019-02-05 16:58:54 +03:00
|
|
|
"on_import_bouquets": self.on_import_bouquets,
|
2018-12-20 18:14:19 +03:00
|
|
|
"on_backup_tool_show": self.on_backup_tool_show,
|
2017-12-19 22:57:04 +03:00
|
|
|
"on_insert_marker": self.on_insert_marker,
|
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,
|
2018-01-23 16:18:28 +03:00
|
|
|
"on_picons_loader_show": self.on_picons_loader_show,
|
2018-01-29 18:07:47 +03:00
|
|
|
"on_filter_changed": self.on_filter_changed,
|
|
|
|
|
"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-01-31 16:02:26 +03:00
|
|
|
"on_filter_toggled": self.on_filter_toggled,
|
2018-02-02 12:45:58 +03:00
|
|
|
"on_search_toggled": self.on_search_toggled,
|
2018-03-05 22:45:21 +03:00
|
|
|
"on_search_down": self.on_search_down,
|
|
|
|
|
"on_search_up": self.on_search_up,
|
2018-02-14 00:00:52 +03:00
|
|
|
"on_search": self.on_search,
|
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,
|
2018-08-25 15:30:12 +03:00
|
|
|
"on_player_play": self.on_player_play,
|
|
|
|
|
"on_player_stop": self.on_player_stop,
|
2018-10-05 15:12:13 +03:00
|
|
|
"on_player_previous": self.on_player_previous,
|
|
|
|
|
"on_player_next": self.on_player_next,
|
2018-08-25 15:30:12 +03:00
|
|
|
"on_player_close": self.on_player_close,
|
2018-08-31 17:26:36 +03:00
|
|
|
"on_player_press": self.on_player_press,
|
|
|
|
|
"on_full_screen": self.on_full_screen,
|
2018-08-25 15:30:12 +03:00
|
|
|
"on_drawing_area_realize": self.on_drawing_area_realize,
|
2018-09-30 23:16:30 +03:00
|
|
|
"on_player_drawing_area_draw": self.on_player_drawing_area_draw,
|
2018-08-31 17:26:36 +03:00
|
|
|
"on_main_window_state": self.on_main_window_state,
|
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_bouquets_edit": self.on_bouquets_edit,
|
|
|
|
|
"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,
|
|
|
|
|
"on_create_bouquet_for_each_type": self.on_create_bouquet_for_each_type}
|
2017-11-09 19:01:09 +03:00
|
|
|
|
2018-04-06 16:02:16 +03:00
|
|
|
self._options = get_config()
|
|
|
|
|
self._profile = self._options.get("profile")
|
|
|
|
|
os.makedirs(os.path.dirname(self._options.get(self._profile).get("data_dir_path")), exist_ok=True)
|
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 = []
|
2018-04-06 16:02:16 +03:00
|
|
|
self._services = {}
|
|
|
|
|
self._bouquets = {}
|
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
|
2018-09-27 22:02:35 +03:00
|
|
|
# Current satellite positions in the services list
|
|
|
|
|
self._sat_positions = []
|
2018-04-29 15:36:35 +03:00
|
|
|
# Player
|
|
|
|
|
self._player = None
|
2018-05-19 16:24:20 +03:00
|
|
|
self._full_screen = False
|
2018-08-31 17:26:36 +03:00
|
|
|
self._drawing_area_xid = 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
|
2018-11-17 23:19:17 +03:00
|
|
|
self._monitor_signal = False
|
2018-12-23 16:15:48 +03:00
|
|
|
# Colors
|
|
|
|
|
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
|
2017-11-09 19:01:09 +03:00
|
|
|
|
|
|
|
|
builder = Gtk.Builder()
|
2018-02-27 14:55:03 +03:00
|
|
|
builder.set_translation_domain("demon-editor")
|
2017-12-25 19:50:35 +03:00
|
|
|
builder.add_from_file(UI_RESOURCES_PATH + "main_window.glade")
|
2018-01-08 22:00:48 +03:00
|
|
|
builder.connect_signals(handlers)
|
2018-04-06 16:02:16 +03:00
|
|
|
self._main_window = builder.get_object("main_window")
|
|
|
|
|
main_window_size = self._options.get("window_size", None)
|
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)
|
|
|
|
|
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-09-30 00:12:15 +03:00
|
|
|
self._main_data_box = builder.get_object("main_data_box")
|
2018-08-25 15:30:12 +03:00
|
|
|
self._player_drawing_area = builder.get_object("player_drawing_area")
|
2018-09-30 23:16:30 +03:00
|
|
|
self._player_box = builder.get_object("player_box")
|
2018-09-30 00:12:15 +03:00
|
|
|
self._player_tool_bar = builder.get_object("player_tool_bar")
|
2018-10-06 18:28:59 +03:00
|
|
|
self._player_prev_button = builder.get_object("player_prev_button")
|
|
|
|
|
self._player_next_button = builder.get_object("player_next_button")
|
2018-09-30 00:12:15 +03:00
|
|
|
self._status_bar_box = builder.get_object("status_bar_box")
|
|
|
|
|
self._services_main_box = builder.get_object("services_main_box")
|
|
|
|
|
self._bouquets_main_box = builder.get_object("bouquets_main_box")
|
2018-09-20 16:36:03 +03:00
|
|
|
# Enabling events for the drawing area
|
2018-08-25 15:30:12 +03:00
|
|
|
self._player_drawing_area.set_events(Gdk.ModifierType.BUTTON1_MASK)
|
2018-08-31 17:26:36 +03:00
|
|
|
self._player_frame = builder.get_object("player_frame")
|
2018-08-15 10:51:19 +03:00
|
|
|
self._header_bar = builder.get_object("header_bar")
|
2018-07-09 19:00:05 +03:00
|
|
|
self._bq_name_label = builder.get_object("bq_name_label")
|
2018-11-17 23:19:17 +03:00
|
|
|
# Status bar
|
2018-04-06 16:02:16 +03:00
|
|
|
self._ip_label = builder.get_object("ip_label")
|
|
|
|
|
self._ip_label.set_text(self._options.get(self._profile).get("host"))
|
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")
|
|
|
|
|
self._signal_box = builder.get_object("signal_box")
|
|
|
|
|
self._service_name_label = builder.get_object("service_name_label")
|
|
|
|
|
self._signal_level_bar = builder.get_object("signal_level_bar")
|
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")
|
2018-01-22 14:51:34 +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)
|
|
|
|
|
self._filter_entry = builder.get_object("filter_entry")
|
2018-07-08 00:09:26 +03:00
|
|
|
self._filter_bar = builder.get_object("filter_bar")
|
2018-09-12 14:05:28 +03:00
|
|
|
self._filter_types_box = builder.get_object("filter_types_box")
|
|
|
|
|
self._filter_sat_positions_box = builder.get_object("filter_sat_positions_box")
|
|
|
|
|
self._filter_types_model = builder.get_object("filter_types_list_store")
|
|
|
|
|
self._filter_sat_positions_model = builder.get_object("filter_sat_positions_list_store")
|
2018-09-12 17:26:22 +03:00
|
|
|
self._filter_only_free_button = builder.get_object("filter_only_free_button")
|
2018-03-06 19:06:16 +03:00
|
|
|
# Search
|
2018-07-08 00:09:26 +03:00
|
|
|
self._search_bar = builder.get_object("search_bar")
|
2018-05-07 18:19:00 +03:00
|
|
|
self._search_provider = SearchProvider((self._services_view, self._fav_view, self._bouquets_view),
|
2018-04-06 16:02:16 +03:00
|
|
|
builder.get_object("search_down_button"),
|
|
|
|
|
builder.get_object("search_up_button"))
|
2019-05-13 14:42:23 +03:00
|
|
|
# Dynamically active elements depending on the selected view
|
|
|
|
|
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))}
|
2018-12-01 13:34:26 +03:00
|
|
|
|
|
|
|
|
def do_startup(self):
|
|
|
|
|
Gtk.Application.do_startup(self)
|
2018-12-23 16:15:48 +03:00
|
|
|
self.update_profile_label()
|
|
|
|
|
self.init_drag_and_drop()
|
|
|
|
|
self.init_colors()
|
2018-12-01 13:34:26 +03:00
|
|
|
self.init_http_api()
|
2019-05-29 14:31:44 +03:00
|
|
|
self._services_view.grab_focus()
|
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 """
|
|
|
|
|
write_config(self._options) # storing current config
|
|
|
|
|
if self._player:
|
|
|
|
|
self._player.release()
|
|
|
|
|
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()
|
|
|
|
|
if "log" in options:
|
|
|
|
|
init_logger()
|
|
|
|
|
|
|
|
|
|
self.activate()
|
|
|
|
|
return 0
|
|
|
|
|
|
2017-11-09 19:01:09 +03:00
|
|
|
def init_drag_and_drop(self):
|
2018-09-19 11:46:41 +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 = []
|
2018-04-06 16:02:16 +03:00
|
|
|
self._services_view.enable_model_drag_source(Gdk.ModifierType.BUTTON1_MASK, target, Gdk.DragAction.COPY)
|
|
|
|
|
self._fav_view.enable_model_drag_source(Gdk.ModifierType.BUTTON1_MASK, target,
|
|
|
|
|
Gdk.DragAction.DEFAULT | Gdk.DragAction.MOVE)
|
|
|
|
|
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)
|
2018-04-06 16:02:16 +03:00
|
|
|
self._fav_view.drag_dest_set_target_list(None)
|
|
|
|
|
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()
|
|
|
|
|
self._services_view.drag_source_set_target_list(None)
|
|
|
|
|
self._services_view.drag_source_add_text_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
|
|
|
|
2018-12-23 16:15:48 +03:00
|
|
|
def init_colors(self, update=False):
|
|
|
|
|
""" Initialisation of background colors for the services.
|
|
|
|
|
|
|
|
|
|
If update=False - first call on program start, else - after options changes!
|
|
|
|
|
"""
|
|
|
|
|
profile = Profile(self._profile)
|
|
|
|
|
if profile is Profile.ENIGMA_2:
|
|
|
|
|
opts = self._options.get(self._profile)
|
|
|
|
|
self._use_colors = opts.get("use_colors", False)
|
|
|
|
|
if self._use_colors:
|
|
|
|
|
new_rgb = Gdk.RGBA()
|
|
|
|
|
extra_rgb = Gdk.RGBA()
|
|
|
|
|
new_rgb = new_rgb if new_rgb.parse(opts.get("new_color", NEW_COLOR)) else None
|
|
|
|
|
extra_rgb = extra_rgb if extra_rgb.parse(opts.get("extra_color", EXTRA_COLOR)) else None
|
|
|
|
|
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
|
|
|
|
|
|
|
|
|
|
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 """
|
|
|
|
|
event.state |= Gdk.ModifierType.CONTROL_MASK
|
|
|
|
|
|
2018-04-29 15:36:35 +03:00
|
|
|
@run_idle
|
2018-07-08 00:09:26 +03:00
|
|
|
def on_close_app(self, *args):
|
2018-12-01 13:34:26 +03:00
|
|
|
self.quit()
|
2017-11-09 19:01:09 +03:00
|
|
|
|
|
|
|
|
def on_resize(self, window):
|
|
|
|
|
""" Stores new size properties for app window after resize """
|
2018-04-06 16:02:16 +03:00
|
|
|
self._options["window_size"] = window.get_size()
|
2017-11-09 19:01:09 +03:00
|
|
|
|
2018-07-12 11:57:02 +03:00
|
|
|
@run_idle
|
2017-11-09 19:01:09 +03:00
|
|
|
def on_about_app(self, item):
|
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
|
|
|
|
2018-09-20 16:36:03 +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
|
|
|
|
2018-01-24 13:39:11 +03:00
|
|
|
if model.get_name() == self._FAV_LIST_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:
|
2019-03-19 21:44:05 +03:00
|
|
|
self.show_error_dialog("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!
|
|
|
|
|
"""
|
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]
|
|
|
|
|
|
|
|
|
|
if model_name == self._FAV_LIST_NAME:
|
2018-10-13 10:48:39 +03:00
|
|
|
next(self.remove_favs(itrs, model), False)
|
2018-10-09 13:16:49 +03:00
|
|
|
elif model_name == self._BOUQUETS_LIST_NAME:
|
|
|
|
|
self.delete_bouquets(itrs, model)
|
|
|
|
|
elif model_name == self._SERVICE_LIST_NAME:
|
2018-10-13 10:48:39 +03:00
|
|
|
next(self.delete_services(itrs, model, rows), False)
|
2018-10-09 13:16:49 +03:00
|
|
|
|
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:
|
|
|
|
|
for itr in itrs:
|
|
|
|
|
del fav_bouquet[int(model.get_path(itr)[0])]
|
|
|
|
|
self._fav_model.remove(itr)
|
|
|
|
|
self.update_fav_num_column(model)
|
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 """
|
2018-04-06 16:02:16 +03:00
|
|
|
srv_itrs = [self._services_model_filter.convert_iter_to_child_iter(
|
2018-01-26 15:15:39 +03:00
|
|
|
model.convert_iter_to_child_iter(itr)) for itr in itrs]
|
|
|
|
|
for s_itr in srv_itrs:
|
2018-04-06 16:02:16 +03:00
|
|
|
self._services_model.remove(s_itr)
|
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)
|
|
|
|
|
self.update_fav_num_column(self._fav_model)
|
2018-09-27 22:02:35 +03:00
|
|
|
self.update_sat_positions()
|
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:
|
2019-03-19 21:44:05 +03:00
|
|
|
self.show_error_dialog("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()
|
|
|
|
|
self._bouquets_model.remove(itr)
|
2018-01-05 14:32:14 +03:00
|
|
|
|
2018-09-20 16:36:03 +03:00
|
|
|
# ***************** ####### *********************#
|
|
|
|
|
|
2018-01-05 14:32:14 +03:00
|
|
|
def get_bouquet_file_name(self, bouquet):
|
2018-04-06 16:02:16 +03:00
|
|
|
bouquet_file_name = "{}userbouquet.{}.{}".format(self._options.get(self._profile).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)
|
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
|
|
|
|
2018-12-17 18:31:57 +03:00
|
|
|
def on_header_edit(self, item):
|
|
|
|
|
""" 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):
|
|
|
|
|
for index, row in enumerate(model):
|
|
|
|
|
row[0] = index + 1
|
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
|
|
|
|
|
|
|
|
# ***************** Drag-and-drop *********************#
|
|
|
|
|
|
2018-09-22 21:08:28 +03:00
|
|
|
def on_view_drag_begin(self, view, context):
|
|
|
|
|
""" Selects a row under the cursor in the view at the dragging beginning. """
|
2018-09-22 21:14:56 +03:00
|
|
|
selection = view.get_selection()
|
|
|
|
|
if selection.count_selected_rows() > 1:
|
2018-09-22 21:08:28 +03:00
|
|
|
view.do_toggle_cursor_row(view)
|
|
|
|
|
|
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
|
|
|
|
|
|
|
|
def on_view_drag_data_received(self, view, drag_context, x, y, data, info, time):
|
|
|
|
|
self.receive_selection(view=view, drop_info=view.get_dest_row_at_pos(x, y), data=data.get_text())
|
2018-09-22 19:14:47 +03:00
|
|
|
return False
|
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
|
2018-09-19 11:46:41 +03:00
|
|
|
itr_str, sep, source = data.partition("::::")
|
|
|
|
|
if source != self._BOUQUETS_LIST_NAME:
|
|
|
|
|
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:
|
|
|
|
|
top_iter = model.move_before(itr, top_iter)
|
|
|
|
|
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:
|
2018-10-13 22:27:32 +03:00
|
|
|
itr_str, sep, source = data.partition("::::")
|
|
|
|
|
if source == self._BOUQUETS_LIST_NAME:
|
|
|
|
|
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())
|
|
|
|
|
dest_index = 0
|
|
|
|
|
if drop_info:
|
|
|
|
|
path, position = drop_info
|
|
|
|
|
dest_iter = model.get_iter(path)
|
|
|
|
|
if dest_iter:
|
|
|
|
|
dest_index = model.get_value(dest_iter, 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
|
|
|
|
2018-01-24 13:39:11 +03:00
|
|
|
if source == self._SERVICE_LIST_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]
|
2017-11-09 19:01:09 +03:00
|
|
|
dest_index -= 1
|
|
|
|
|
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)
|
2018-01-24 13:39:11 +03:00
|
|
|
elif source == self._FAV_LIST_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])
|
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:
|
2019-03-19 21:44:05 +03:00
|
|
|
self.show_error_dialog(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):
|
|
|
|
|
if event.get_event_type() == Gdk.EventType.BUTTON_PRESS and event.button == Gdk.BUTTON_PRIMARY:
|
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)
|
|
|
|
|
|
|
|
|
|
def delete_views_selection(self, name):
|
|
|
|
|
if name == self._SERVICE_LIST_NAME:
|
|
|
|
|
self.delete_selection(self._fav_view)
|
|
|
|
|
elif name == self._FAV_LIST_NAME:
|
|
|
|
|
self.delete_selection(self._services_view)
|
|
|
|
|
elif name == self._BOUQUETS_LIST_NAME:
|
|
|
|
|
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
|
|
|
|
2018-01-05 14:32:14 +03:00
|
|
|
@run_idle
|
2017-11-09 19:01:09 +03:00
|
|
|
def on_satellite_editor_show(self, model):
|
|
|
|
|
""" Shows satellites editor dialog """
|
2018-04-06 16:02:16 +03:00
|
|
|
show_satellites_dialog(self._main_window, self._options.get(self._profile))
|
2017-11-09 19:01:09 +03:00
|
|
|
|
2019-03-18 23:03:42 +03:00
|
|
|
def on_download(self, item):
|
|
|
|
|
DownloadDialog(transient=self._main_window,
|
|
|
|
|
properties=self._options,
|
|
|
|
|
open_data_callback=self.open_data,
|
2019-05-14 22:12:36 +03:00
|
|
|
update_settings_callback=self.update_options,
|
2019-03-18 23:03:42 +03:00
|
|
|
profile=Profile(self._profile)).show()
|
|
|
|
|
|
|
|
|
|
@run_task
|
|
|
|
|
def on_download_data(self):
|
|
|
|
|
try:
|
|
|
|
|
download_data(properties=self._options.get(self._profile),
|
|
|
|
|
download_type=DownloadType.ALL,
|
|
|
|
|
callback=lambda x: print(x, end=""))
|
|
|
|
|
except Exception as e:
|
2019-03-19 00:12:33 +03:00
|
|
|
self.show_error_dialog(str(e))
|
2019-03-18 23:03:42 +03:00
|
|
|
else:
|
|
|
|
|
GLib.idle_add(self.open_data)
|
|
|
|
|
|
|
|
|
|
@run_task
|
|
|
|
|
def on_upload_data(self, download_type):
|
|
|
|
|
try:
|
|
|
|
|
profile = Profile(self._profile)
|
|
|
|
|
opts = self._options.get(self._profile)
|
|
|
|
|
use_http = profile is Profile.ENIGMA_2
|
|
|
|
|
|
|
|
|
|
if profile is Profile.ENIGMA_2:
|
|
|
|
|
host, port = opts.get("host", "127.0.0.1"), opts.get("http_port")
|
|
|
|
|
user, password = opts.get("http_user", "root"), opts.get("http_password", "")
|
|
|
|
|
try:
|
|
|
|
|
test_http(host, port, user, password, skip_message=True)
|
|
|
|
|
except TestException:
|
|
|
|
|
use_http = False
|
|
|
|
|
|
|
|
|
|
upload_data(properties=opts,
|
|
|
|
|
download_type=download_type,
|
|
|
|
|
remove_unused=True,
|
|
|
|
|
profile=profile,
|
|
|
|
|
callback=lambda x: print(x, end=""),
|
|
|
|
|
use_http=use_http)
|
|
|
|
|
except Exception as e:
|
2019-03-19 00:12:33 +03:00
|
|
|
self.show_error_dialog(str(e))
|
|
|
|
|
|
2018-07-12 11:57:02 +03:00
|
|
|
@run_idle
|
2017-11-09 19:01:09 +03:00
|
|
|
def on_data_open(self, model):
|
2018-04-06 16:02:16 +03:00
|
|
|
response = show_dialog(DialogType.CHOOSER, self._main_window, options=self._options.get(self._profile))
|
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
|
|
|
|
2017-12-12 09:16:58 +03:00
|
|
|
def open_data(self, data_path=None):
|
2017-11-09 19:01:09 +03:00
|
|
|
""" Opening data and fill views. """
|
2018-04-06 16:02:16 +03:00
|
|
|
self._wait_dialog.show()
|
2018-01-04 01:23:22 +03:00
|
|
|
self.clear_current_data()
|
2018-08-18 11:35:44 +03:00
|
|
|
GLib.idle_add(self.append_data, data_path, priority=GLib.PRIORITY_LOW)
|
2017-12-12 09:16:58 +03:00
|
|
|
|
2018-08-18 10:21:40 +03:00
|
|
|
def append_data(self, data_path):
|
2018-08-18 11:35:44 +03:00
|
|
|
profile = Profile(self._profile)
|
2018-04-06 16:02:16 +03:00
|
|
|
data_path = self._options.get(self._profile).get("data_dir_path") if data_path is None else data_path
|
2018-08-18 11:35:44 +03:00
|
|
|
|
2017-11-09 19:01:09 +03:00
|
|
|
try:
|
2018-08-18 11:35:44 +03:00
|
|
|
black_list = get_blacklist(data_path)
|
|
|
|
|
bouquets = get_bouquets(data_path, Profile(self._profile))
|
|
|
|
|
services = get_services(data_path, profile, self.get_format_version() if profile is Profile.ENIGMA_2 else 0)
|
2018-08-18 12:31:12 +03:00
|
|
|
update_picons_data(self._options.get(self._profile).get("picons_dir_path"), self._picons)
|
2017-11-09 19:01:09 +03:00
|
|
|
except FileNotFoundError as e:
|
2018-08-18 10:21:40 +03:00
|
|
|
self._wait_dialog.hide()
|
2019-03-19 21:44:05 +03:00
|
|
|
msg = get_message("Please, download files from receiver or setup your path for read data!")
|
|
|
|
|
self.show_error_dialog(getattr(e, "message", str(e)) + "\n\n" + msg)
|
2017-12-20 16:46:15 +03:00
|
|
|
except SyntaxError as e:
|
2018-04-06 16:02:16 +03:00
|
|
|
self._wait_dialog.hide()
|
2019-03-19 21:44:05 +03:00
|
|
|
self.show_error_dialog(str(e))
|
2018-08-18 11:35:44 +03:00
|
|
|
except Exception as e:
|
|
|
|
|
self._wait_dialog.hide()
|
|
|
|
|
log("Append services error: " + str(e))
|
2019-03-19 21:44:05 +03:00
|
|
|
self.show_error_dialog(get_message("Reading data error!") + "\n" + str(e))
|
2018-08-18 11:35:44 +03:00
|
|
|
else:
|
|
|
|
|
self.append_blacklist(black_list)
|
|
|
|
|
self.append_bouquets(bouquets)
|
|
|
|
|
self.append_services(services)
|
2018-09-27 22:02:35 +03:00
|
|
|
self.update_sat_positions()
|
2017-11-09 19:01:09 +03:00
|
|
|
|
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:
|
|
|
|
|
for bouquet in bqs:
|
|
|
|
|
parent = self._bouquets_model.append(None, [bouquet.name, None, None, bouquet.type])
|
|
|
|
|
for bq in bouquet.bouquets:
|
|
|
|
|
self.append_bouquet(bq, parent)
|
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):
|
|
|
|
|
name, bt_type, locked, hidden = bq.name, bq.type, bq.locked, bq.hidden
|
2018-04-06 16:02:16 +03:00
|
|
|
self._bouquets_model.append(parent, [name, locked, hidden, bt_type])
|
2018-09-09 23:38:00 +03:00
|
|
|
bq_id = "{}:{}".format(name, bt_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
|
|
|
|
|
if s_type is BqServiceType.MARKER or s_type is BqServiceType.IPTV:
|
2018-09-01 00:49:11 +03:00
|
|
|
icon = None
|
|
|
|
|
picon_id = None
|
|
|
|
|
if s_type is BqServiceType.IPTV:
|
|
|
|
|
icon = IPTV_ICON
|
|
|
|
|
id_data = fav_id.lstrip().split(":")
|
|
|
|
|
picon_id = "{}_{}_{}_{}_{}_{}_{}_{}_{}_{}.png".format(*id_data[0:10])
|
|
|
|
|
srv = Service(*agr[0:2], icon, srv.name, *agr[0:3], s_type.name, self._picons.get(picon_id, None),
|
|
|
|
|
picon_id, *agr, srv.num, fav_id, None)
|
2018-04-06 16:02:16 +03:00
|
|
|
self._services[fav_id] = srv
|
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
|
|
|
|
|
if extra_services:
|
|
|
|
|
self._extra_bouquets[bq_id] = extra_services
|
2017-11-14 19:20:16 +03:00
|
|
|
|
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:
|
|
|
|
|
# adding channels to dict with fav_id as keys
|
|
|
|
|
self._services[srv.fav_id] = srv
|
2019-02-09 12:46:06 +03:00
|
|
|
self.update_services_counts(len(self._services.values()))
|
2018-09-21 10:16:30 +03:00
|
|
|
gen = self.append_services_data(services)
|
|
|
|
|
GLib.idle_add(lambda: next(gen, False), priority=GLib.PRIORITY_LOW)
|
2018-08-18 10:21:40 +03:00
|
|
|
|
|
|
|
|
def append_services_data(self, services):
|
|
|
|
|
for srv in 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
|
|
|
|
|
|
|
|
s = srv + (tooltip, background)
|
|
|
|
|
itr = self._services_model.append(s)
|
2019-02-09 12:46:06 +03:00
|
|
|
self._services_model.set_value(itr, Column.SRV_PICON, self._picons.get(srv.picon_id, None))
|
2019-02-09 10:25:49 +03:00
|
|
|
yield True
|
2018-08-18 10:21:40 +03:00
|
|
|
self._wait_dialog.hide()
|
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 """
|
2018-04-06 16:02:16 +03:00
|
|
|
self._bouquets_model.clear()
|
|
|
|
|
self._fav_model.clear()
|
|
|
|
|
self._services_model.clear()
|
|
|
|
|
self._blacklist.clear()
|
|
|
|
|
self._services.clear()
|
|
|
|
|
self._rows_buffer.clear()
|
|
|
|
|
self._bouquets.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()
|
2018-01-04 01:23:22 +03:00
|
|
|
|
2018-07-12 11:57:02 +03:00
|
|
|
@run_idle
|
2017-11-09 19:01:09 +03:00
|
|
|
def on_data_save(self, *args):
|
2019-01-05 22:53:51 +03:00
|
|
|
if len(self._bouquets_model) == 0:
|
2019-03-19 21:44:05 +03:00
|
|
|
self.show_error_dialog("No data to save!")
|
2019-01-05 22:53:51 +03:00
|
|
|
return
|
|
|
|
|
|
2018-04-06 16:02:16 +03:00
|
|
|
if show_dialog(DialogType.QUESTION, self._main_window) == Gtk.ResponseType.CANCEL:
|
2017-11-09 19:01:09 +03:00
|
|
|
return
|
|
|
|
|
|
2018-10-15 13:37:40 +03:00
|
|
|
profile = Profile(self._profile)
|
2019-01-03 23:32:28 +03:00
|
|
|
options = self._options.get(self._profile)
|
|
|
|
|
path = options.get("data_dir_path")
|
2019-01-12 19:17:20 +03:00
|
|
|
backup_path = options.get("backup_dir_path", path + "backup/")
|
2019-01-03 23:32:28 +03:00
|
|
|
# Backup data or clearing data path
|
2019-01-12 19:17:20 +03:00
|
|
|
backup_data(path, backup_path) if options.get("backup_before_save", True) else clear_data_path(path)
|
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):
|
|
|
|
|
bq_itr = model.iter_nth_child(itr, num)
|
2019-02-09 12:46:06 +03:00
|
|
|
bq_name, locked, hidden, bq_type = model.get(bq_itr, Column.BQ_NAME, Column.BQ_LOCKED,
|
|
|
|
|
Column.BQ_HIDDEN, Column.BQ_TYPE)
|
2018-09-11 15:21:05 +03:00
|
|
|
bq_id = "{}:{}".format(bq_name, bq_type)
|
|
|
|
|
favs = self._bouquets[bq_id]
|
2018-10-15 13:37:40 +03:00
|
|
|
ex_s = self._extra_bouquets.get(bq_id)
|
|
|
|
|
bq_s = list(filter(None, [self._services.get(f_id, None) for f_id in favs]))
|
|
|
|
|
if profile is Profile.ENIGMA_2:
|
|
|
|
|
bq_s = list(map(lambda s: s._replace(service=ex_s.get(s.fav_id, None) if ex_s else None), bq_s))
|
|
|
|
|
bq = Bouquet(bq_name, bq_type, bq_s, locked, hidden)
|
2017-11-09 19:01:09 +03:00
|
|
|
bqs.append(bq)
|
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
|
|
|
|
2018-04-06 16:02:16 +03:00
|
|
|
profile = Profile(self._profile)
|
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)
|
2018-01-04 20:58:22 +03:00
|
|
|
write_bouquets(path, bouquets, profile)
|
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]
|
2018-06-01 17:45:26 +03:00
|
|
|
write_services(path, services, profile, self.get_format_version() if profile is Profile.ENIGMA_2 else 0)
|
2018-01-04 20:58:22 +03:00
|
|
|
# removing bouquet files
|
2018-02-10 15:49:44 +03:00
|
|
|
if profile is Profile.ENIGMA_2:
|
2018-01-04 20:58:22 +03:00
|
|
|
# blacklist
|
2018-04-06 16:02:16 +03:00
|
|
|
write_blacklist(path, self._blacklist)
|
2017-11-09 19:01:09 +03:00
|
|
|
|
2018-12-01 00:13:19 +03:00
|
|
|
def on_new_configuration(self, item):
|
|
|
|
|
""" Creates new empty configuration """
|
|
|
|
|
if show_dialog(DialogType.QUESTION, self._main_window) == Gtk.ResponseType.CANCEL:
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
self.clear_current_data()
|
2018-12-01 00:17:21 +03:00
|
|
|
|
2018-12-01 00:13:19 +03:00
|
|
|
profile = Profile(self._profile)
|
|
|
|
|
if profile is Profile.ENIGMA_2:
|
2018-12-01 13:34:26 +03:00
|
|
|
parent = self._bouquets_model.append(None, ["Favourites (TV)", None, None, BqType.TV.value])
|
|
|
|
|
self.append_bouquet(Bouquet("Favourites (TV)", BqType.TV.value, [], None, None), parent)
|
|
|
|
|
parent = self._bouquets_model.append(None, ["Favourites (Radio)", None, None, BqType.RADIO.value])
|
|
|
|
|
self.append_bouquet(Bouquet("Favourites (Radio)", BqType.RADIO.value, [], None, None), parent)
|
2018-12-01 00:13:19 +03:00
|
|
|
elif profile is Profile.NEUTRINO_MP:
|
|
|
|
|
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])
|
|
|
|
|
|
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
|
|
|
|
|
cas_values = list(filter(lambda val: val.startswith("C:"), cas.split(",")))
|
2018-04-06 16:02:16 +03:00
|
|
|
self._cas_label.set_text(",".join(map(str, sorted(set(CAS.get(val, def_val) for val in cas_values)))))
|
2017-11-09 19:01:09 +03:00
|
|
|
|
|
|
|
|
def on_bouquets_selection(self, model, path, column):
|
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 "")
|
2018-04-06 16:02:16 +03:00
|
|
|
self._fav_model.clear()
|
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:
|
2018-10-13 10:48:39 +03:00
|
|
|
next(self.update_bouquet_services(model, path), False)
|
2017-11-09 19:01:09 +03:00
|
|
|
|
2019-05-29 14:31:44 +03:00
|
|
|
self.on_view_focus(self._bouquets_view)
|
|
|
|
|
|
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))
|
2018-04-06 16:02:16 +03:00
|
|
|
services = self._bouquets.get(key, None)
|
2018-09-09 23:38:00 +03:00
|
|
|
ex_services = self._extra_bouquets.get(key, None)
|
2018-04-06 16:02:16 +03:00
|
|
|
if not services:
|
|
|
|
|
return
|
2017-11-09 19:01:09 +03:00
|
|
|
|
2018-04-06 16:02:16 +03:00
|
|
|
for num, srv_id in enumerate(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:
|
2018-12-23 16:15:48 +03:00
|
|
|
tooltip, background = None, self._EXTRA_COLOR if self._use_colors and ex_srv_name else None
|
2018-09-09 23:38:00 +03:00
|
|
|
self._fav_model.append((num + 1, srv.coded, ex_srv_name if ex_srv_name else srv.service, srv.locked,
|
|
|
|
|
srv.hide, srv.service_type, srv.pos, srv.fav_id,
|
2018-12-16 22:44:45 +03:00
|
|
|
self._picons.get(srv.picon_id, None), tooltip, background))
|
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):
|
|
|
|
|
""" checks and returns bouquet if selected """
|
2018-12-11 19:09:55 +03:00
|
|
|
if not self._bq_selected:
|
2019-03-19 21:44:05 +03:00
|
|
|
self.show_error_dialog("Error. No bouquet is selected!")
|
2018-04-10 11:15:50 +03:00
|
|
|
return
|
|
|
|
|
|
2018-12-11 19:09:55 +03:00
|
|
|
if Profile(self._profile) is Profile.NEUTRINO_MP and self._bq_selected.endswith(BqType.WEBTV.value):
|
2019-03-19 21:44:05 +03:00
|
|
|
self.show_error_dialog("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()
|
|
|
|
|
|
2018-01-18 00:57:58 +03:00
|
|
|
@run_idle
|
2017-11-09 19:01:09 +03:00
|
|
|
def on_preferences(self, item):
|
2018-04-06 16:02:16 +03:00
|
|
|
response = show_settings_dialog(self._main_window, self._options)
|
2017-12-30 21:51:57 +03:00
|
|
|
if response != Gtk.ResponseType.CANCEL:
|
2019-05-14 22:12:36 +03:00
|
|
|
self.update_options()
|
|
|
|
|
|
|
|
|
|
def update_options(self):
|
|
|
|
|
profile = self._options.get("profile")
|
|
|
|
|
self._ip_label.set_text(self._options.get(profile).get("host"))
|
|
|
|
|
if profile != self._profile:
|
|
|
|
|
self._profile = profile
|
|
|
|
|
self.clear_current_data()
|
|
|
|
|
self.update_services_counts()
|
|
|
|
|
self.update_profile_label()
|
|
|
|
|
self.init_colors(True)
|
|
|
|
|
self.init_http_api()
|
2018-06-01 11:16:30 +03:00
|
|
|
|
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)
|
2018-09-18 14:40:24 +03:00
|
|
|
ctrl = event.state & Gdk.ModifierType.CONTROL_MASK
|
2018-11-02 23:13:31 +03:00
|
|
|
model_name, model = get_model_data(view)
|
2018-09-18 14:40:24 +03:00
|
|
|
|
2019-03-10 18:11:38 +03:00
|
|
|
if ctrl and key is KeyboardKey.O:
|
|
|
|
|
self.open_data()
|
|
|
|
|
elif ctrl and key is KeyboardKey.Q:
|
|
|
|
|
self.quit()
|
|
|
|
|
elif 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:
|
2018-09-18 14:40:24 +03:00
|
|
|
if model_name == self._SERVICE_LIST_NAME:
|
|
|
|
|
self.on_copy(view, ViewTarget.FAV)
|
|
|
|
|
elif model_name == self._FAV_LIST_NAME:
|
|
|
|
|
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:
|
2018-09-18 14:40:24 +03:00
|
|
|
if model_name == self._FAV_LIST_NAME:
|
2018-09-20 16:36:03 +03:00
|
|
|
self.on_cut(view, ViewTarget.FAV)
|
|
|
|
|
elif model_name == self._BOUQUETS_LIST_NAME:
|
|
|
|
|
self.on_cut(view, ViewTarget.BOUQUET)
|
2018-11-05 00:31:44 +03:00
|
|
|
elif ctrl and key is KeyboardKey.V:
|
2018-09-18 14:40:24 +03:00
|
|
|
if model_name == self._FAV_LIST_NAME:
|
2018-09-20 16:36:03 +03:00
|
|
|
self.on_paste(view, ViewTarget.FAV)
|
|
|
|
|
elif model_name == self._BOUQUETS_LIST_NAME:
|
|
|
|
|
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)
|
2017-11-09 19:01:09 +03:00
|
|
|
ctrl = event.state & Gdk.ModifierType.CONTROL_MASK
|
2018-11-02 23:13:31 +03:00
|
|
|
model_name, model = get_model_data(view)
|
2017-11-09 19:01:09 +03:00
|
|
|
|
2019-03-18 23:03:42 +03:00
|
|
|
if ctrl and key is KeyboardKey.D:
|
|
|
|
|
self.on_download_data()
|
|
|
|
|
elif ctrl and key is KeyboardKey.U:
|
|
|
|
|
self.on_upload_data(DownloadType.ALL)
|
|
|
|
|
elif ctrl and key is KeyboardKey.B:
|
|
|
|
|
self.on_upload_data(DownloadType.BOUQUETS)
|
|
|
|
|
elif ctrl and key is KeyboardKey.INSERT:
|
2017-11-09 19:01:09 +03:00
|
|
|
# Move items from app to fav list
|
2018-01-24 13:39:11 +03:00
|
|
|
if model_name == self._SERVICE_LIST_NAME:
|
2018-10-13 22:27:32 +03:00
|
|
|
self.on_to_fav_copy(view)
|
2018-01-24 13:39:11 +03:00
|
|
|
elif model_name == self._BOUQUETS_LIST_NAME:
|
2017-11-09 19:01:09 +03:00
|
|
|
self.on_new_bouquet(view)
|
2018-11-05 00:31:44 +03:00
|
|
|
elif ctrl and key is KeyboardKey.BACK_SPACE and model_name == self._SERVICE_LIST_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.S:
|
2017-11-09 19:01:09 +03:00
|
|
|
self.on_data_save()
|
2018-11-05 00:31:44 +03:00
|
|
|
elif ctrl and key is KeyboardKey.L:
|
2017-11-27 13:58:33 +03:00
|
|
|
self.on_locked(None)
|
2018-11-05 00:31:44 +03:00
|
|
|
elif ctrl and key is KeyboardKey.H:
|
2017-11-27 13:58:33 +03:00
|
|
|
self.on_hide(None)
|
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 ctrl and key is KeyboardKey.E:
|
2018-02-17 16:23:41 +03:00
|
|
|
if model_name == self._BOUQUETS_LIST_NAME:
|
2018-02-18 11:23:31 +03:00
|
|
|
self.on_rename(view)
|
2018-02-17 16:23:41 +03:00
|
|
|
return
|
2018-02-18 11:23:31 +03:00
|
|
|
self.on_service_edit(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)
|
2018-11-23 15:06:36 +03:00
|
|
|
elif ctrl and model_name == self._FAV_LIST_NAME:
|
|
|
|
|
if key is KeyboardKey.P:
|
|
|
|
|
self.on_play_stream()
|
2018-12-02 00:45:55 +03:00
|
|
|
if key is KeyboardKey.W:
|
|
|
|
|
self.on_zap(self.on_watch)
|
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-04-06 16:02:16 +03:00
|
|
|
profile = Profile(self._profile)
|
2018-11-02 23:13:31 +03:00
|
|
|
model_name, model = get_model_data(view)
|
2017-11-16 01:24:16 +03:00
|
|
|
not_empty = len(model) > 0 # if > 0 model has items
|
2019-05-29 14:31:44 +03:00
|
|
|
is_service = model_name == self._SERVICE_LIST_NAME
|
2017-11-09 19:01:09 +03:00
|
|
|
|
2018-01-24 13:39:11 +03:00
|
|
|
if model_name == self._BOUQUETS_LIST_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)
|
2018-01-05 14:32:14 +03:00
|
|
|
if profile is Profile.NEUTRINO_MP:
|
|
|
|
|
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:
|
2018-04-06 16:02:16 +03:00
|
|
|
self._tool_elements[elem].set_sensitive(not_empty and profile is Profile.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:
|
|
|
|
|
self._tool_elements[elem].set_sensitive(self._bq_selected and not is_service)
|
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-05-29 14:31:44 +03:00
|
|
|
if profile is not Profile.ENIGMA_2:
|
|
|
|
|
for elem in self._FAV_ENIGMA_ELEMENTS:
|
|
|
|
|
self._tool_elements[elem].set_sensitive(False)
|
|
|
|
|
|
2017-11-23 16:59:21 +03:00
|
|
|
def on_hide(self, item):
|
2018-02-17 16:23:41 +03:00
|
|
|
self.set_service_flags(Flag.HIDE)
|
2017-11-23 16:59:21 +03:00
|
|
|
|
|
|
|
|
def on_locked(self, item):
|
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):
|
2018-04-06 16:02:16 +03:00
|
|
|
profile = Profile(self._profile)
|
2018-12-11 19:09:55 +03:00
|
|
|
|
2018-01-05 14:32:14 +03:00
|
|
|
if profile is Profile.ENIGMA_2:
|
2018-09-29 21:57:17 +03:00
|
|
|
set_flags(flag, self._services_view, self._fav_view, self._services, self._blacklist)
|
2018-12-11 19:09:55 +03:00
|
|
|
elif profile is Profile.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
|
|
|
|
2017-11-29 00:26:12 +03:00
|
|
|
@run_idle
|
|
|
|
|
def on_model_changed(self, model, path, itr=None):
|
|
|
|
|
model_name = model.get_name()
|
|
|
|
|
|
2018-01-24 13:39:11 +03:00
|
|
|
if model_name == self._FAV_LIST_NAME:
|
2018-04-06 16:02:16 +03:00
|
|
|
self._fav_count_label.set_text(str(len(model)))
|
2018-01-24 13:39:11 +03:00
|
|
|
elif model_name == self._SERVICE_LIST_NAME:
|
2017-11-30 00:45:52 +03:00
|
|
|
self.update_services_counts(len(model))
|
2018-01-24 13:39:11 +03:00
|
|
|
elif model_name == self._BOUQUETS_LIST_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
|
|
|
|
2017-12-19 22:57:04 +03:00
|
|
|
def on_insert_marker(self, view):
|
2017-12-20 16:46:15 +03:00
|
|
|
""" Inserts marker into bouquet services list. """
|
2018-12-11 19:09:55 +03:00
|
|
|
insert_marker(view, self._bouquets, self._bq_selected, self._services, self._main_window)
|
2018-04-06 16:02:16 +03:00
|
|
|
self.update_fav_num_column(self._fav_model)
|
2017-12-19 22:57:04 +03:00
|
|
|
|
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
|
|
|
|
|
elif self._fav_click_mode is FavClickMode.STREAM:
|
|
|
|
|
self.on_play_stream()
|
|
|
|
|
elif self._fav_click_mode is FavClickMode.PLAY:
|
|
|
|
|
self.on_zap(self.on_watch)
|
|
|
|
|
elif self._fav_click_mode is FavClickMode.ZAP:
|
|
|
|
|
self.on_zap()
|
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 *********************#
|
|
|
|
|
|
|
|
|
|
def on_iptv(self, item):
|
|
|
|
|
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),
|
2018-09-01 00:49:11 +03:00
|
|
|
Profile(self._profile),
|
|
|
|
|
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
|
2018-08-18 17:35:30 +03:00
|
|
|
def on_iptv_list_configuration(self, item):
|
2018-08-21 17:19:44 +03:00
|
|
|
profile = Profile(self._profile)
|
2018-09-23 19:19:34 +03:00
|
|
|
if profile is Profile.NEUTRINO_MP:
|
2019-03-19 21:44:05 +03:00
|
|
|
self.show_error_dialog("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:
|
2019-03-19 21:44:05 +03:00
|
|
|
self.show_error_dialog("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, [])
|
|
|
|
|
IptvListConfigurationDialog(self._main_window, self._services, iptv_rows, bq, self._fav_model, profile).show()
|
2018-08-18 17:35:30 +03:00
|
|
|
|
2018-06-29 22:43:04 +03:00
|
|
|
@run_idle
|
|
|
|
|
def on_remove_all_unavailable(self, item):
|
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:
|
2019-03-19 21:44:05 +03:00
|
|
|
self.show_error_dialog("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)
|
2018-07-03 19:30:45 +03:00
|
|
|
prf = Profile(self._profile)
|
|
|
|
|
response = SearchUnavailableDialog(self._main_window, self._fav_model, fav_bqt, iptv_rows, prf).show()
|
|
|
|
|
if response:
|
2018-10-15 13:37:40 +03:00
|
|
|
next(self.remove_favs(response, self._fav_model), False)
|
2018-06-29 22:43:04 +03:00
|
|
|
|
2019-04-18 23:05:19 +03:00
|
|
|
# ****************** EPG **********************#
|
|
|
|
|
|
2019-04-21 01:18:54 +03:00
|
|
|
@run_idle
|
2019-04-18 23:05:19 +03:00
|
|
|
def on_epg_list_configuration(self, item):
|
|
|
|
|
if Profile(self._profile) is not Profile.ENIGMA_2:
|
|
|
|
|
self.show_error_dialog("Only Enigma2 is supported!")
|
|
|
|
|
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):
|
|
|
|
|
self.show_error_dialog("This list does not contains IPTV streams!")
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
bq = self._bouquets.get(self._bq_selected)
|
2019-06-04 13:31:54 +03:00
|
|
|
profile = self._options.get(self._profile)
|
|
|
|
|
EpgDialog(self._main_window, profile, self._services, bq, self._fav_model, self._current_bq_name).show()
|
2019-04-18 23:05:19 +03:00
|
|
|
|
2019-02-05 16:58:54 +03:00
|
|
|
# ***************** Import ********************#
|
|
|
|
|
|
2018-09-01 00:49:11 +03:00
|
|
|
def on_import_m3u(self, item):
|
|
|
|
|
""" Imports iptv from m3u files. """
|
|
|
|
|
response = get_chooser_dialog(self._main_window, self._options.get(self._profile), "*.m3u", "m3u files")
|
|
|
|
|
if response == Gtk.ResponseType.CANCEL:
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
if not str(response).endswith("m3u"):
|
2019-03-19 21:44:05 +03:00
|
|
|
self.show_error_dialog("No m3u file is selected!")
|
2018-09-01 00:49:11 +03:00
|
|
|
return
|
|
|
|
|
|
|
|
|
|
channels = parse_m3u(response, Profile(self._profile))
|
2018-12-11 19:09:55 +03:00
|
|
|
|
|
|
|
|
if channels and self._bq_selected:
|
|
|
|
|
bq_services = self._bouquets.get(self._bq_selected)
|
2018-09-01 00:49:11 +03:00
|
|
|
self._fav_model.clear()
|
|
|
|
|
for ch in channels:
|
|
|
|
|
self._services[ch.fav_id] = ch
|
|
|
|
|
bq_services.append(ch.fav_id)
|
2018-12-11 19:09:55 +03:00
|
|
|
next(self.update_bouquet_services(self._fav_model, None, self._bq_selected), False)
|
2018-09-01 00:49:11 +03:00
|
|
|
|
2019-04-18 21:43:35 +03:00
|
|
|
@run_idle
|
|
|
|
|
def on_export_to_m3u(self, item):
|
|
|
|
|
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):
|
|
|
|
|
self.show_error_dialog("This list does not contains IPTV streams!")
|
|
|
|
|
return
|
|
|
|
|
|
2019-05-01 17:21:51 +03:00
|
|
|
response = show_dialog(DialogType.CHOOSER, self._main_window, options=self._options.get(self._profile))
|
|
|
|
|
if response in (Gtk.ResponseType.CANCEL, Gtk.ResponseType.DELETE_EVENT):
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
try:
|
2019-05-30 15:56:04 +03:00
|
|
|
bq = Bouquet(self._current_bq_name, None, bq_services, None, None)
|
|
|
|
|
export_to_m3u(response, bq, Profile(self._profile))
|
2019-05-01 17:21:51 +03:00
|
|
|
except Exception as e:
|
|
|
|
|
self.show_error_dialog(str(e))
|
|
|
|
|
else:
|
|
|
|
|
show_dialog(DialogType.INFO, self._main_window, "Done!")
|
2019-04-18 21:43:35 +03:00
|
|
|
|
2019-02-23 13:54:00 +03:00
|
|
|
def on_import_bouquet(self, item):
|
|
|
|
|
profile = Profile(self._profile)
|
|
|
|
|
model, paths = self._bouquets_view.get_selection().get_selected_rows()
|
|
|
|
|
if not paths:
|
2019-03-19 21:44:05 +03:00
|
|
|
self.show_error_dialog("No selected item!")
|
2019-02-23 13:54:00 +03:00
|
|
|
return
|
|
|
|
|
|
2019-03-14 12:37:48 +03:00
|
|
|
opts = self._options.get(self._profile)
|
|
|
|
|
appender = self.append_bouquet if profile is Profile.ENIGMA_2 else self.append_bouquets
|
|
|
|
|
import_bouquet(self._main_window, profile, model, paths[0], opts, self._services, appender)
|
2019-02-23 13:54:00 +03:00
|
|
|
|
2019-02-05 16:58:54 +03:00
|
|
|
def on_import_bouquets(self, item):
|
2019-02-23 13:54:00 +03:00
|
|
|
response = show_dialog(DialogType.CHOOSER, self._main_window, options=self._options.get(self._profile))
|
|
|
|
|
if response in (Gtk.ResponseType.CANCEL, Gtk.ResponseType.DELETE_EVENT):
|
|
|
|
|
return
|
|
|
|
|
|
2019-02-27 20:12:41 +03:00
|
|
|
ImportDialog(self._main_window, response, Profile(self._profile), self._services.keys(),
|
|
|
|
|
lambda b, s: (self._wait_dialog.show(), self.append_bouquets(b),
|
|
|
|
|
self.append_services(s), self.update_sat_positions())).show()
|
2019-02-05 16:58:54 +03:00
|
|
|
|
2019-01-03 23:32:28 +03:00
|
|
|
# ***************** Backup ********************#
|
2018-12-20 18:14:19 +03:00
|
|
|
|
|
|
|
|
def on_backup_tool_show(self, item):
|
|
|
|
|
""" Shows backup tool dialog """
|
2018-12-21 00:48:45 +03:00
|
|
|
BackupDialog(self._main_window,
|
2019-01-12 18:10:04 +03:00
|
|
|
self._options,
|
2018-12-22 15:26:47 +03:00
|
|
|
Profile(self._profile),
|
2018-12-21 00:48:45 +03:00
|
|
|
self.open_data).show()
|
2018-12-20 18:14:19 +03:00
|
|
|
|
2018-08-25 15:30:12 +03:00
|
|
|
# ***************** Player *********************#
|
2018-08-31 17:26:36 +03:00
|
|
|
|
2018-08-25 15:30:12 +03:00
|
|
|
def on_play_stream(self, item=None):
|
|
|
|
|
self.on_player_play()
|
|
|
|
|
|
2018-08-31 17:26:36 +03:00
|
|
|
@run_idle
|
2018-08-25 15:30:12 +03:00
|
|
|
def on_player_play(self, item=None):
|
2018-10-05 15:12:13 +03:00
|
|
|
url = self.get_stream_url()
|
2018-10-06 18:28:59 +03:00
|
|
|
self.update_player_buttons()
|
2018-10-05 15:12:13 +03:00
|
|
|
if not url:
|
|
|
|
|
return
|
|
|
|
|
self.play(url)
|
|
|
|
|
|
|
|
|
|
def play(self, url):
|
|
|
|
|
if not self._player:
|
|
|
|
|
try:
|
|
|
|
|
self._player = Player()
|
|
|
|
|
except (NameError, AttributeError):
|
2019-03-19 21:44:05 +03:00
|
|
|
self.show_error_dialog("No VLC is found. Check that it is installed!")
|
2018-10-05 15:12:13 +03:00
|
|
|
return
|
|
|
|
|
else:
|
|
|
|
|
if self._drawing_area_xid:
|
|
|
|
|
self._player.set_xwindow(self._drawing_area_xid)
|
|
|
|
|
self._services_main_box.set_visible(False)
|
|
|
|
|
self._bouquets_main_box.set_visible(False)
|
|
|
|
|
w, h = self._main_window.get_size()
|
|
|
|
|
self._player_box.set_size_request(w * 0.6, -1)
|
|
|
|
|
|
|
|
|
|
self._player_box.set_visible(True)
|
|
|
|
|
GLib.idle_add(self._player.play, url, priority=GLib.PRIORITY_LOW)
|
|
|
|
|
|
|
|
|
|
def get_stream_url(self):
|
2018-08-25 15:30:12 +03:00
|
|
|
path, column = self._fav_view.get_cursor()
|
|
|
|
|
if path:
|
|
|
|
|
row = self._fav_model[path][:]
|
2018-09-18 07:13:32 +03:00
|
|
|
if row[5] == BqServiceType.IPTV.name:
|
2018-10-05 15:12:13 +03:00
|
|
|
return get_iptv_url(row, Profile(self._profile))
|
2018-08-25 15:30:12 +03:00
|
|
|
|
|
|
|
|
def on_player_stop(self, item=None):
|
|
|
|
|
if self._player:
|
2018-09-18 07:13:32 +03:00
|
|
|
self._player.stop()
|
2018-08-25 15:30:12 +03:00
|
|
|
|
2018-10-05 15:12:13 +03:00
|
|
|
def on_player_previous(self, item):
|
|
|
|
|
if self._fav_view.do_move_cursor(self._fav_view, Gtk.MovementStep.DISPLAY_LINES, -1):
|
|
|
|
|
self.on_play_stream()
|
|
|
|
|
|
|
|
|
|
def on_player_next(self, item):
|
|
|
|
|
if self._fav_view.do_move_cursor(self._fav_view, Gtk.MovementStep.DISPLAY_LINES, 1):
|
|
|
|
|
self.on_play_stream()
|
|
|
|
|
|
2018-10-06 18:28:59 +03:00
|
|
|
def update_player_buttons(self):
|
|
|
|
|
if self._player:
|
|
|
|
|
path, column = self._fav_view.get_cursor()
|
|
|
|
|
current_index = path[0]
|
|
|
|
|
self._player_prev_button.set_sensitive(current_index != 0)
|
|
|
|
|
self._player_next_button.set_sensitive(len(self._fav_model) != current_index + 1)
|
|
|
|
|
|
2018-08-25 15:30:12 +03:00
|
|
|
def on_player_close(self, item=None):
|
2018-04-29 15:36:35 +03:00
|
|
|
if self._player:
|
2018-08-25 15:30:12 +03:00
|
|
|
self._player.release()
|
2018-08-25 23:53:53 +03:00
|
|
|
self._player = None
|
2018-09-30 23:16:30 +03:00
|
|
|
GLib.idle_add(self._player_box.set_visible, False, priority=GLib.PRIORITY_LOW)
|
2018-09-30 00:12:15 +03:00
|
|
|
GLib.idle_add(self._services_main_box.set_visible, True, priority=GLib.PRIORITY_LOW)
|
|
|
|
|
GLib.idle_add(self._bouquets_main_box.set_visible, True, priority=GLib.PRIORITY_LOW)
|
2018-08-25 15:30:12 +03:00
|
|
|
|
|
|
|
|
def on_drawing_area_realize(self, widget):
|
|
|
|
|
self._drawing_area_xid = widget.get_window().get_xid()
|
2018-09-18 07:13:32 +03:00
|
|
|
self._player.set_xwindow(self._drawing_area_xid)
|
2018-08-31 17:26:36 +03:00
|
|
|
|
2018-09-30 23:16:30 +03:00
|
|
|
def on_player_drawing_area_draw(self, widget, cr):
|
|
|
|
|
""" Used for black background drawing in the player drawing area.
|
|
|
|
|
|
|
|
|
|
Required for Gtk >= 3.20.
|
|
|
|
|
More info: https://developer.gnome.org/gtk3/stable/ch32s10.html,
|
|
|
|
|
https://developer.gnome.org/gtk3/stable/GtkStyleContext.html#gtk-render-background
|
|
|
|
|
"""
|
|
|
|
|
context = widget.get_style_context()
|
|
|
|
|
width = widget.get_allocated_width()
|
|
|
|
|
height = widget.get_allocated_height()
|
|
|
|
|
Gtk.render_background(context, cr, 0, 0, width, height)
|
|
|
|
|
r, g, b, a = 0, 0, 0, 1 # black color
|
|
|
|
|
cr.set_source_rgba(r, g, b, a)
|
|
|
|
|
cr.rectangle(0, 0, width, height)
|
|
|
|
|
cr.fill()
|
|
|
|
|
|
2018-08-31 17:26:36 +03:00
|
|
|
def on_player_press(self, area, event):
|
|
|
|
|
if event.button == Gdk.BUTTON_PRIMARY:
|
|
|
|
|
if event.type == Gdk.EventType.DOUBLE_BUTTON_PRESS:
|
|
|
|
|
self.on_full_screen()
|
|
|
|
|
|
|
|
|
|
def on_full_screen(self, item=None):
|
|
|
|
|
self._full_screen = not self._full_screen
|
|
|
|
|
self._main_window.fullscreen() if self._full_screen else self._main_window.unfullscreen()
|
|
|
|
|
|
|
|
|
|
def on_main_window_state(self, window, event):
|
2018-09-30 00:12:15 +03:00
|
|
|
full = not event.new_window_state & Gdk.WindowState.FULLSCREEN
|
|
|
|
|
self._main_data_box.set_visible(full)
|
|
|
|
|
self._status_bar_box.set_visible(full)
|
2018-09-30 23:16:30 +03:00
|
|
|
self._player_tool_bar.set_visible(full)
|
2018-08-31 17:26:36 +03:00
|
|
|
|
2018-11-17 23:19:17 +03:00
|
|
|
# ************************ HTTP API ****************************#
|
|
|
|
|
@run_task
|
|
|
|
|
def init_http_api(self):
|
|
|
|
|
if self._http_api:
|
|
|
|
|
self._http_api.close()
|
|
|
|
|
self._http_api = None
|
|
|
|
|
|
|
|
|
|
prp = self._options.get(self._profile)
|
2019-03-14 12:37:48 +03:00
|
|
|
self._fav_click_mode = FavClickMode(prp.get("fav_click_mode", FavClickMode.DISABLED))
|
|
|
|
|
|
2018-11-19 17:53:33 +03:00
|
|
|
if prp is Profile.NEUTRINO_MP or not prp.get("http_api_support", False):
|
2018-11-17 23:19:17 +03:00
|
|
|
self.update_info_boxes_visible(False)
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
self._http_api = http_request(prp.get("host", "127.0.0.1"), prp.get("http_port", "80"),
|
|
|
|
|
prp.get("http_user", ""), prp.get("http_password", ""))
|
|
|
|
|
next(self._http_api)
|
|
|
|
|
GLib.timeout_add_seconds(1, self.update_receiver_info)
|
|
|
|
|
|
2018-12-02 00:45:55 +03:00
|
|
|
def on_watch(self):
|
|
|
|
|
""" Switch to the channel and watch in the player """
|
|
|
|
|
m3u = self._http_api.send((HttpRequestType.STREAM, None))
|
|
|
|
|
next(self._http_api)
|
|
|
|
|
if m3u:
|
|
|
|
|
url = [s for s in m3u.split("\n") if not s.startswith("#")]
|
|
|
|
|
if url:
|
|
|
|
|
GLib.timeout_add_seconds(1, self.play, url[0])
|
|
|
|
|
|
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
|
|
|
|
|
|
2018-12-02 00:45:55 +03:00
|
|
|
if self._player and self._player.is_playing():
|
|
|
|
|
self._player.stop()
|
|
|
|
|
|
2018-11-17 23:19:17 +03:00
|
|
|
row = self._fav_model[path][:]
|
2019-02-03 23:44:53 +03:00
|
|
|
srv = self._services.get(row[Column.FAV_ID], None)
|
2018-11-17 23:19:17 +03:00
|
|
|
if srv and srv.transponder:
|
|
|
|
|
ref = srv.picon_id.rstrip(".png").replace("_", ":")
|
2018-12-02 00:45:55 +03:00
|
|
|
|
2018-11-17 23:19:17 +03:00
|
|
|
req = self._http_api.send((HttpRequestType.ZAP, ref))
|
|
|
|
|
next(self._http_api)
|
|
|
|
|
if req and req.get("result", False):
|
|
|
|
|
GLib.timeout_add_seconds(2, self.update_service_info)
|
2018-12-02 00:45:55 +03:00
|
|
|
GLib.idle_add(scroll_to, path, self._fav_view)
|
|
|
|
|
if callback is not None:
|
|
|
|
|
callback()
|
2018-11-17 23:19:17 +03:00
|
|
|
|
|
|
|
|
@run_task
|
|
|
|
|
def update_receiver_info(self):
|
|
|
|
|
info = self._http_api.send((HttpRequestType.INFO, None))
|
|
|
|
|
next(self._http_api)
|
|
|
|
|
if not info:
|
|
|
|
|
self._http_api.close()
|
|
|
|
|
self._http_api = None
|
|
|
|
|
GLib.idle_add(self.update_info_boxes_visible, False)
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
service_info = info.get("service", None)
|
|
|
|
|
res_info = info.get("info", None)
|
|
|
|
|
if res_info:
|
|
|
|
|
image = res_info.get("friendlyimagedistro", "")
|
|
|
|
|
image_ver = res_info.get("imagever", "")
|
|
|
|
|
brand = res_info.get("brand", "")
|
|
|
|
|
model = res_info.get("model", "")
|
|
|
|
|
info_text = "{} {} Image: {} {}".format(brand, model, image, image_ver)
|
|
|
|
|
GLib.idle_add(self._receiver_info_label.set_text, info_text)
|
|
|
|
|
GLib.idle_add(self._receiver_info_box.set_visible, res_info)
|
|
|
|
|
|
|
|
|
|
if service_info:
|
|
|
|
|
GLib.idle_add(self._service_name_label.set_text, service_info.get("name", ""))
|
|
|
|
|
GLib.timeout_add_seconds(2, self.update_signal)
|
|
|
|
|
GLib.idle_add(self._signal_box.set_visible, service_info)
|
|
|
|
|
|
|
|
|
|
def update_signal(self):
|
|
|
|
|
sig = self._http_api.send((HttpRequestType.SIGNAL, None))
|
|
|
|
|
next(self._http_api)
|
2018-11-19 13:37:10 +03:00
|
|
|
val = sig.get("snr", 0)
|
|
|
|
|
self._signal_level_bar.set_value(val if val else 0)
|
|
|
|
|
self._signal_level_bar.set_visible(val)
|
|
|
|
|
|
2018-11-17 23:19:17 +03:00
|
|
|
return self._monitor_signal
|
|
|
|
|
|
|
|
|
|
def update_service_info(self):
|
|
|
|
|
info = self._http_api.send((HttpRequestType.INFO, None))
|
|
|
|
|
next(self._http_api)
|
|
|
|
|
if info:
|
|
|
|
|
service_info = info.get("service", None)
|
|
|
|
|
if service_info:
|
|
|
|
|
GLib.idle_add(self._service_name_label.set_text, service_info.get("name", ""))
|
|
|
|
|
GLib.timeout_add_seconds(1, self.update_signal)
|
|
|
|
|
|
2018-09-01 00:49:11 +03:00
|
|
|
# ***************** Filter and search *********************#
|
2018-01-08 22:00:48 +03:00
|
|
|
|
2018-01-31 00:13:42 +03:00
|
|
|
def on_filter_toggled(self, toggle_button: Gtk.ToggleToolButton):
|
2018-07-08 00:09:26 +03:00
|
|
|
active = toggle_button.get_active()
|
2018-09-12 14:05:28 +03:00
|
|
|
if active:
|
|
|
|
|
self.update_filter_sat_positions()
|
|
|
|
|
|
2018-07-08 00:09:26 +03:00
|
|
|
self._filter_bar.set_search_mode(active)
|
2018-09-12 17:49:28 +03:00
|
|
|
self._filter_bar.set_visible(active)
|
2018-01-31 00:13:42 +03:00
|
|
|
|
2018-09-27 22:02:35 +03:00
|
|
|
def init_sat_positions(self):
|
|
|
|
|
self._sat_positions.clear()
|
|
|
|
|
first = (self._filter_sat_positions_model[0][0],)
|
2018-09-12 14:05:28 +03:00
|
|
|
self._filter_sat_positions_model.clear()
|
2018-09-27 22:02:35 +03:00
|
|
|
self._filter_sat_positions_model.append(first)
|
2018-09-12 14:05:28 +03:00
|
|
|
self._filter_sat_positions_box.set_active(0)
|
2018-09-27 22:02:35 +03:00
|
|
|
|
|
|
|
|
def update_sat_positions(self):
|
2018-10-15 11:39:33 +03:00
|
|
|
""" Updates positions values for the filtering function """
|
2018-09-27 22:02:35 +03:00
|
|
|
self._sat_positions.clear()
|
2018-10-15 11:39:33 +03:00
|
|
|
sat_positions = set()
|
|
|
|
|
terrestrial = False
|
2018-11-25 19:34:28 +03:00
|
|
|
cable = False
|
|
|
|
|
|
2018-10-15 11:39:33 +03:00
|
|
|
for srv in self._services.values():
|
2018-11-25 19:34:28 +03:00
|
|
|
tr_type = srv.transponder_type
|
|
|
|
|
if tr_type == "s" and srv.pos:
|
2018-10-15 11:39:33 +03:00
|
|
|
sat_positions.add(float(srv.pos))
|
2018-11-25 19:34:28 +03:00
|
|
|
elif tr_type == "t":
|
2018-10-15 11:39:33 +03:00
|
|
|
terrestrial = True
|
2018-11-25 19:34:28 +03:00
|
|
|
elif tr_type == "c":
|
|
|
|
|
cable = True
|
|
|
|
|
|
2018-10-15 11:39:33 +03:00
|
|
|
if terrestrial:
|
|
|
|
|
self._sat_positions.append("T")
|
2018-11-25 19:34:28 +03:00
|
|
|
if cable:
|
|
|
|
|
self._sat_positions.append("C")
|
|
|
|
|
|
2018-10-15 11:39:33 +03:00
|
|
|
self._sat_positions.extend(map(str, sorted(sat_positions)))
|
2018-09-27 22:02:35 +03:00
|
|
|
if self._filter_bar.is_visible():
|
|
|
|
|
self.update_filter_sat_positions()
|
|
|
|
|
|
|
|
|
|
@run_idle
|
|
|
|
|
def update_filter_sat_positions(self):
|
|
|
|
|
model = self._filter_sat_positions_model
|
|
|
|
|
if len(model) < 2:
|
|
|
|
|
list(map(self._filter_sat_positions_model.append, map(lambda x: (str(x),), self._sat_positions)))
|
|
|
|
|
else:
|
|
|
|
|
selected = self._filter_sat_positions_box.get_active_id()
|
|
|
|
|
active = self._filter_sat_positions_box.get_active()
|
|
|
|
|
itrs = list(filter(lambda it: model[it][0] not in self._sat_positions, [row.iter for row in model][1:]))
|
|
|
|
|
list(map(model.remove, itrs))
|
|
|
|
|
|
|
|
|
|
if active != 0 and selected not in self._sat_positions:
|
|
|
|
|
self._filter_sat_positions_box.set_active(0)
|
2018-09-12 14:05:28 +03:00
|
|
|
|
2018-08-24 12:03:51 +03:00
|
|
|
@run_with_delay(1)
|
2018-09-12 14:05:28 +03:00
|
|
|
def on_filter_changed(self, item):
|
2018-09-12 17:26:22 +03:00
|
|
|
GLib.idle_add(self._services_model_filter.refilter, priority=GLib.PRIORITY_LOW)
|
2018-01-25 16:11:52 +03:00
|
|
|
|
2018-12-19 14:43:43 +03:00
|
|
|
def services_filter_function(self, model, itr, data):
|
2018-04-06 16:02:16 +03:00
|
|
|
if self._services_model_filter is None or self._services_model_filter == "None":
|
2018-01-25 16:11:52 +03:00
|
|
|
return True
|
|
|
|
|
else:
|
2018-12-19 14:43:43 +03:00
|
|
|
txt = self._filter_entry.get_text() in str(model.get(itr, Column.SRV_SERVICE, Column.SRV_PACKAGE,
|
|
|
|
|
Column.SRV_TYPE, Column.SRV_SSID, Column.SRV_FREQ,
|
|
|
|
|
Column.SRV_RATE, Column.SRV_POL, Column.SRV_FEC,
|
|
|
|
|
Column.SRV_SYSTEM, Column.SRV_POS))
|
2018-09-12 14:05:28 +03:00
|
|
|
type_active = self._filter_types_box.get_active() > 0
|
|
|
|
|
pos_active = self._filter_sat_positions_box.get_active() > 0
|
2018-12-19 14:43:43 +03:00
|
|
|
free = not model.get(itr, Column.SRV_CODED)[0] if self._filter_only_free_button.get_active() else True
|
2018-09-12 14:05:28 +03:00
|
|
|
|
|
|
|
|
if type_active and pos_active:
|
2018-12-19 14:43:43 +03:00
|
|
|
active_id = self._filter_types_box.get_active_id() == model.get(itr, Column.SRV_TYPE)[0]
|
|
|
|
|
pos = self._filter_sat_positions_box.get_active_id() == model.get(itr, Column.SRV_POS)[0]
|
|
|
|
|
return active_id and pos and txt and free
|
2018-09-12 14:05:28 +03:00
|
|
|
elif type_active:
|
2018-12-19 14:43:43 +03:00
|
|
|
return self._filter_types_box.get_active_id() == model.get(itr, Column.SRV_TYPE)[0] and txt and free
|
2018-09-12 14:05:28 +03:00
|
|
|
elif pos_active:
|
2018-12-19 14:43:43 +03:00
|
|
|
pos = self._filter_sat_positions_box.get_active_id() == model.get(itr, Column.SRV_POS)[0]
|
|
|
|
|
return pos and txt and free
|
2018-09-12 14:05:28 +03:00
|
|
|
|
2018-09-12 17:26:22 +03:00
|
|
|
return txt and free
|
2018-01-28 23:10:54 +03:00
|
|
|
|
2018-01-31 16:02:26 +03:00
|
|
|
def on_search_toggled(self, toggle_button: Gtk.ToggleToolButton):
|
2018-07-08 00:09:26 +03:00
|
|
|
self._search_bar.set_search_mode(toggle_button.get_active())
|
2018-01-31 16:02:26 +03:00
|
|
|
|
2018-03-05 22:45:21 +03:00
|
|
|
def on_search_down(self, item):
|
2018-04-06 16:02:16 +03:00
|
|
|
self._search_provider.on_search_down()
|
2018-03-05 22:45:21 +03:00
|
|
|
|
|
|
|
|
def on_search_up(self, item):
|
2018-04-06 16:02:16 +03:00
|
|
|
self._search_provider.on_search_up()
|
2018-03-05 22:45:21 +03:00
|
|
|
|
2018-08-24 12:03:51 +03:00
|
|
|
@run_with_delay(1)
|
2018-03-06 19:06:16 +03:00
|
|
|
def on_search(self, entry):
|
2018-04-06 16:02:16 +03:00
|
|
|
self._search_provider.search(entry.get_text())
|
2018-02-02 12:45:58 +03:00
|
|
|
|
2018-09-01 00:49:11 +03:00
|
|
|
# ***************** Editing *********************#
|
|
|
|
|
|
2018-02-15 15:16:34 +03:00
|
|
|
@run_idle
|
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()
|
|
|
|
|
if model_name == self._FAV_LIST_NAME:
|
2018-12-19 14:43:43 +03:00
|
|
|
srv_type = model.get_value(model.get_iter(paths), Column.FAV_TYPE)
|
2018-03-11 21:52:10 +03:00
|
|
|
if srv_type == BqServiceType.MARKER.name:
|
|
|
|
|
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),
|
2018-04-06 16:02:16 +03:00
|
|
|
Profile(self._profile),
|
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,
|
|
|
|
|
self._options,
|
|
|
|
|
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,
|
|
|
|
|
self._options,
|
|
|
|
|
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):
|
|
|
|
|
""" Rename bouquets """
|
2018-12-11 19:09:55 +03:00
|
|
|
if not self._bq_selected:
|
2019-03-19 21:44:05 +03:00
|
|
|
self.show_error_dialog("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
|
|
|
|
|
|
|
|
|
|
model.set_value(itr, 0, response)
|
|
|
|
|
self._bouquets["{}:{}".format(response, bq_type)] = self._bouquets.pop("{}:{}".format(bq_name, bq_type))
|
2019-05-09 23:51:47 +03:00
|
|
|
self._current_bq_name = response
|
|
|
|
|
self._bq_name_label.set_text(self._current_bq_name)
|
|
|
|
|
self._bq_selected = "{}:{}".format(response, bq_type)
|
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)
|
2018-09-11 15:21:05 +03:00
|
|
|
if name == self._BOUQUETS_LIST_NAME:
|
|
|
|
|
self.on_bouquets_edit(view)
|
|
|
|
|
elif name == self._FAV_LIST_NAME:
|
|
|
|
|
rename(view, self._main_window, ViewTarget.FAV, service_view=self._services_view,
|
2018-10-25 16:00:25 +03:00
|
|
|
services=self._services)
|
2018-09-11 15:21:05 +03:00
|
|
|
elif name == self._SERVICE_LIST_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:
|
2019-03-19 21:44:05 +03:00
|
|
|
self.show_error_dialog("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:
|
2019-03-19 21:44:05 +03:00
|
|
|
self.show_error_dialog("No changes required!")
|
2018-09-11 16:25:12 +03:00
|
|
|
return
|
|
|
|
|
else:
|
|
|
|
|
if not ex_bq.pop(fav_id, None):
|
2019-03-19 21:44:05 +03:00
|
|
|
self.show_error_dialog("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)
|
|
|
|
|
|
|
|
|
|
# ***************** Picons *********************#
|
|
|
|
|
|
|
|
|
|
@run_idle
|
|
|
|
|
def on_picons_loader_show(self, item):
|
|
|
|
|
ids = {}
|
|
|
|
|
if Profile(self._profile) is Profile.ENIGMA_2:
|
|
|
|
|
for r in self._services_model:
|
2018-12-19 14:43:43 +03:00
|
|
|
data = r[Column.SRV_PICON_ID].split("_")
|
|
|
|
|
ids["{}:{}:{}".format(data[3], data[5], data[6])] = r[Column.SRV_PICON_ID]
|
2018-09-01 00:49:11 +03:00
|
|
|
|
2018-10-21 11:12:57 +03:00
|
|
|
dialog = PiconsDialog(self._main_window, self._options, ids, self._sat_positions, Profile(self._profile))
|
2018-09-01 00:49:11 +03:00
|
|
|
dialog.show()
|
|
|
|
|
self.update_picons()
|
|
|
|
|
|
2018-08-18 10:21:40 +03:00
|
|
|
@run_task
|
2018-01-28 23:10:54 +03:00
|
|
|
def update_picons(self):
|
2018-08-18 10:21:40 +03:00
|
|
|
update_picons_data(self._options.get(self._profile).get("picons_dir_path"), self._picons)
|
|
|
|
|
append_picons(self._picons, self._services_model)
|
2018-01-23 16:18:28 +03:00
|
|
|
|
2018-01-30 12:37:04 +03:00
|
|
|
def on_assign_picon(self, view):
|
2018-02-01 21:43:44 +03:00
|
|
|
assign_picon(self.get_target_view(view),
|
2018-04-06 16:02:16 +03:00
|
|
|
self._services_view,
|
|
|
|
|
self._fav_view,
|
|
|
|
|
self._main_window,
|
|
|
|
|
self._picons,
|
|
|
|
|
self._options.get(self._profile),
|
|
|
|
|
self._services)
|
2018-01-29 18:07:47 +03:00
|
|
|
|
2018-01-30 12:37:04 +03:00
|
|
|
def on_remove_picon(self, view):
|
2018-02-01 13:10:06 +03:00
|
|
|
remove_picon(self.get_target_view(view),
|
2018-04-06 16:02:16 +03:00
|
|
|
self._services_view,
|
|
|
|
|
self._fav_view, self._picons,
|
|
|
|
|
self._options.get(self._profile))
|
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
|
|
|
|
|
|
|
|
|
|
remove_all_unused_picons(self._options.get(self._profile), self._picons, self._services.values())
|
|
|
|
|
|
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
|
|
|
|
2018-09-01 00:49:11 +03:00
|
|
|
# ***************** Bouquets *********************#
|
|
|
|
|
|
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):
|
2018-04-06 16:02:16 +03:00
|
|
|
gen_bouquets(self._services_view, self._bouquets_view, self._main_window, g_type, self._TV_TYPES,
|
|
|
|
|
Profile(self._profile), self.append_bouquet)
|
2018-04-02 23:55:41 +03:00
|
|
|
|
2018-09-01 00:49:11 +03:00
|
|
|
# ***************** Profile label *********************#
|
|
|
|
|
|
2018-06-01 11:16:30 +03:00
|
|
|
def update_profile_label(self):
|
|
|
|
|
profile = Profile(self._profile)
|
|
|
|
|
if profile is Profile.ENIGMA_2:
|
2018-08-15 10:51:19 +03:00
|
|
|
self._header_bar.set_subtitle("{} Enigma2 v.{}".format(get_message("Profile:"), self.get_format_version()))
|
2018-06-01 11:16:30 +03:00
|
|
|
elif profile is Profile.NEUTRINO_MP:
|
2018-08-15 10:51:19 +03:00
|
|
|
self._header_bar.set_subtitle("{} Neutrino-MP".format(get_message("Profile:")))
|
2018-06-01 11:16:30 +03:00
|
|
|
|
|
|
|
|
def get_format_version(self):
|
|
|
|
|
return 5 if self._options.get(self._profile).get("v5_support", False) else 4
|
|
|
|
|
|
2018-11-17 23:19:17 +03:00
|
|
|
@run_idle
|
|
|
|
|
def update_info_boxes_visible(self, visible):
|
|
|
|
|
self._signal_box.set_visible(visible)
|
|
|
|
|
self._receiver_info_box.set_visible(visible)
|
|
|
|
|
|
2019-03-19 21:44:05 +03:00
|
|
|
@run_idle
|
|
|
|
|
def show_error_dialog(self, message):
|
|
|
|
|
show_dialog(DialogType.ERROR, self._main_window, message)
|
|
|
|
|
|
2017-11-09 19:01:09 +03:00
|
|
|
|
|
|
|
|
def start_app():
|
2018-12-01 13:34:26 +03:00
|
|
|
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
|