From 78c07f3934bd0520acc45599742a41fe47e38e5b Mon Sep 17 00:00:00 2001 From: DYefremov Date: Tue, 3 Jul 2018 19:30:45 +0300 Subject: [PATCH] added extra dialog for searching unavailable iptv streams --- app/ui/dialogs.glade | 226 +++++++++++++++++++++++++++++++++++--- app/ui/iptv.py | 79 ++++++++++++- app/ui/main_app_window.py | 46 ++++---- app/ui/main_helper.py | 8 ++ 4 files changed, 319 insertions(+), 40 deletions(-) diff --git a/app/ui/dialogs.glade b/app/ui/dialogs.glade index 5d0e1ca7..e18b4186 100644 --- a/app/ui/dialogs.glade +++ b/app/ui/dialogs.glade @@ -1,7 +1,37 @@ - + - + + + + + False True @@ -21,6 +51,9 @@ accessories-text-editor True mit-x11 + + + False @@ -50,6 +83,9 @@ True mail-send-receive dialog + + + 320 @@ -92,11 +128,14 @@ True False Receive - 1 + 2 + 2 + 2 True False + 5 Receiver IP: 0 @@ -124,6 +163,7 @@ True False + 5 Current data path: 0 @@ -156,6 +196,9 @@ True False + 5 + 2 + 2 5 @@ -176,7 +219,6 @@ True True False - 0 True True satellites_radio_button @@ -193,7 +235,6 @@ True True False - 0 True satellites_radio_button @@ -209,7 +250,6 @@ True True False - 0 True all_radio_button @@ -224,8 +264,6 @@ WebTV True False - 0 - 0.52999997138977051 True True all_radio_button @@ -298,7 +336,7 @@ True True False - 0 + 2 True True @@ -409,6 +447,9 @@ dialog error ok + + + False @@ -437,6 +478,9 @@ True gtk-edit dialog + + + 320 @@ -529,6 +573,9 @@ dialog select-folder True + + + False @@ -559,7 +606,6 @@ True True True - 0.55000001192092896 True @@ -593,6 +639,9 @@ question ok-cancel Are you sure? + + + False @@ -613,6 +662,151 @@ + + 320 + False + + True + center + True + dialog + True + True + center + + + + + + + False + 5 + 5 + 5 + vertical + 2 + + + False + end + + + gtk-cancel + True + True + True + True + + + + True + True + 0 + + + + + False + False + 4 + + + + + True + False + Please wait, streams testing in progress. + + + False + True + 0 + + + + + 10 + True + False + True + + + False + True + 1 + + + + + True + False + center + 10 + 2 + + + True + False + Found + + + False + True + 0 + + + + + True + False + 0 + + + + + + False + True + 1 + + + + + True + False + unavailable streams + + + False + True + 2 + + + + + False + True + 2 + + + + + True + False + + + False + True + 2 + 4 + + + + + + button6 + + @@ -637,6 +831,9 @@ dialog True True + + + False @@ -1102,6 +1299,9 @@ True preferences-desktop dialog + + + False @@ -1584,7 +1784,6 @@ True True False - 0 True True neutrino_radio_button @@ -1603,7 +1802,6 @@ True True False - 0 True @@ -1631,7 +1829,6 @@ True True False - 0 True True enigma_radio_button @@ -1805,6 +2002,9 @@ True True False + + + 118 diff --git a/app/ui/iptv.py b/app/ui/iptv.py index 9063ea51..f5889798 100644 --- a/app/ui/iptv.py +++ b/app/ui/iptv.py @@ -1,12 +1,15 @@ import re from urllib.parse import urlparse +from urllib.request import Request, urlopen + +from app.commons import run_idle, run_task from app.eparser.ecommons import BqServiceType, Service from app.eparser.iptv import NEUTRINO_FAV_ID_FORMAT, StreamType, ENIGMA2_FAV_ID_FORMAT from app.properties import Profile from .uicommons import Gtk, Gdk, TEXT_DOMAIN, UI_RESOURCES_PATH, IPTV_ICON from .dialogs import Action, show_dialog, DialogType -from .main_helper import get_base_model +from .main_helper import get_base_model, get_iptv_url class IptvDialog: @@ -182,5 +185,79 @@ class IptvDialog: return True +class SearchUnavailableDialog: + + def __init__(self, transient, model, fav_bouquet, iptv_rows, profile): + handlers = {"on_search_unavailable_close": self.on_close} + + builder = Gtk.Builder() + builder.set_translation_domain(TEXT_DOMAIN) + builder.add_objects_from_file(UI_RESOURCES_PATH + "dialogs.glade", ("search_unavailable_streams_dialog",)) + builder.connect_signals(handlers) + + self._dialog = builder.get_object("search_unavailable_streams_dialog") + self._dialog.set_transient_for(transient) + self._model = model + self._counter_label = builder.get_object("streams_rows_counter_label") + self._level_bar = builder.get_object("unavailable_streams_level_bar") + self._bouquet = fav_bouquet + self._profile = profile + self._iptv_rows = iptv_rows + self._counter = -1 + self._max_rows = len(self._iptv_rows) + self._level_bar.set_max_value(self._max_rows) + self._download_task = True + self._to_delete = [] + + self.update_process() + self.do_search() + + @run_task + def do_search(self): + import concurrent.futures + with concurrent.futures.ThreadPoolExecutor(max_workers=4) as executor: + futures = {executor.submit(self.get_unavailable, row): row for row in self._iptv_rows} + for future in concurrent.futures.as_completed(futures): + if not self._download_task: + executor.shutdown() + return + future.result() + self._download_task = False + self._dialog.destroy() + + def get_unavailable(self, row): + if not self._download_task: + return + req = Request(get_iptv_url(row, self._profile)) + try: + self.update_bar() + urlopen(req, timeout=2) + except Exception: + self._to_delete.append(self._model.get_iter(row.path)) + self.update_process() + + @run_idle + def update_bar(self): + self._max_rows -= 1 + self._level_bar.set_value(self._max_rows) + + @run_idle + def update_process(self): + self._counter += 1 + self._counter_label.set_text(str(self._counter)) + + def show(self): + response = self._dialog.run() + self._dialog.destroy() + + return self._to_delete if response not in (Gtk.ResponseType.CANCEL, Gtk.ResponseType.DELETE_EVENT) else False + + def on_close(self, item, event=None): + if self._download_task and show_dialog(DialogType.QUESTION, self._dialog) == Gtk.ResponseType.CANCEL: + return + self._download_task = False + self._dialog.destroy() + + if __name__ == "__main__": pass diff --git a/app/ui/main_app_window.py b/app/ui/main_app_window.py index 88438942..8790c6f2 100644 --- a/app/ui/main_app_window.py +++ b/app/ui/main_app_window.py @@ -12,14 +12,14 @@ from app.eparser.enigma.bouquets import BqServiceType from app.eparser.neutrino.bouquets import BqType from app.properties import get_config, write_config, Profile from app.tools.media import Player -from .iptv import IptvDialog +from .iptv import IptvDialog, SearchUnavailableDialog from .search import SearchProvider from .uicommons import Gtk, Gdk, UI_RESOURCES_PATH, LOCKED_ICON, HIDE_ICON, IPTV_ICON, MOVE_KEYS from .dialogs import show_dialog, DialogType, get_chooser_dialog, WaitDialog, get_message from .download_dialog import show_download_dialog from .main_helper import edit_marker, insert_marker, move_items, rename, ViewTarget, set_flags, locate_in_services, \ scroll_to, get_base_model, update_picons, copy_picon_reference, assign_picon, remove_picon, \ - is_only_one_item_selected, gen_bouquets, BqGenType + is_only_one_item_selected, gen_bouquets, BqGenType, get_iptv_url from .picons_downloader import PiconsDialog from .satellites_dialog import show_satellites_dialog from .settings_dialog import show_settings_dialog @@ -324,6 +324,7 @@ class MainAppWindow: return rows + @run_idle def remove_favs(self, fav_bouquet, itrs, model): """ Deleting bouquet services """ if fav_bouquet: @@ -595,7 +596,7 @@ class MainAppWindow: except Exception as e: print(e) log("Append services error: " + str(e)) - show_dialog(DialogType.ERROR, self._main_window, "Reading data error!") + show_dialog(DialogType.ERROR, self._main_window, "Reading data error!\n" + e) else: if services: for srv in services: @@ -954,7 +955,7 @@ class MainAppWindow: if path: row = self._fav_model[path][:] if row[5] == BqServiceType.IPTV.value: - url = self.get_iptv_url(row) + url = get_iptv_url(row, Profile(self._profile)) if not url: return @@ -973,32 +974,25 @@ class MainAppWindow: self._is_played = True self._player.play() - def get_iptv_url(self, row): - profile = Profile(self._profile) - data = row[7].split(":" if profile is Profile.ENIGMA_2 else "::") - url = data[-3 if profile is Profile.ENIGMA_2 else 0] - - return url.replace("%3a", ":") if profile is Profile.ENIGMA_2 else url - @run_idle def on_remove_all_unavailable(self, item): - from urllib.request import Request, urlopen - from urllib.error import URLError - - to_delete = [] - for row in self._fav_model: - if row[5] == BqServiceType.IPTV.value: - url = self.get_iptv_url(row) - req = Request(url) - try: - urlopen(req) - except URLError as e: - to_delete.append(self._fav_model.get_iter(row.path)) + iptv_rows = list(filter(lambda r: r[5] == BqServiceType.IPTV.value, self._fav_model)) + if not iptv_rows: + show_dialog(DialogType.ERROR, self._main_window, "This list does not contains iptv streams!") + return bq_selected = self.get_selected_bouquet() - if bq_selected: - fav_bouquet = self._bouquets.get(bq_selected, None) - self.remove_favs(fav_bouquet, to_delete, self._fav_model) + if not bq_selected: + return + + if show_dialog(DialogType.QUESTION, self._main_window) == Gtk.ResponseType.CANCEL: + return + + fav_bqt = self._bouquets.get(bq_selected, None) + prf = Profile(self._profile) + response = SearchUnavailableDialog(self._main_window, self._fav_model, fav_bqt, iptv_rows, prf).show() + if response: + self.remove_favs(fav_bqt, response, self._fav_model) def on_player_stop(self, item): if self._player: diff --git a/app/ui/main_helper.py b/app/ui/main_helper.py index 337f7b8b..a6096092 100644 --- a/app/ui/main_helper.py +++ b/app/ui/main_helper.py @@ -533,5 +533,13 @@ def append_text_to_tview(char, view): view.scroll_to_mark(insert, 0.0, True, 0.0, 1.0) +def get_iptv_url(row, profile): + """ Returns url from iptv type row """ + data = row[7].split(":" if profile is Profile.ENIGMA_2 else "::") + url = data[-3 if profile is Profile.ENIGMA_2 else 0] + + return url.replace("%3a", ":") if profile is Profile.ENIGMA_2 else url + + if __name__ == "__main__": pass