From 7edb03836a33f1609b2c6fc042c4cc89f5a5996b Mon Sep 17 00:00:00 2001 From: DYefremov Date: Sat, 23 Oct 2021 00:18:51 +0300 Subject: [PATCH] added support for setting id for iptv --- app/eparser/enigma/bouquets.py | 1 + app/eparser/iptv.py | 39 +- app/ui/iptv.glade | 668 ++++++++++++++++++--------------- app/ui/iptv.py | 69 ++-- 4 files changed, 440 insertions(+), 337 deletions(-) diff --git a/app/eparser/enigma/bouquets.py b/app/eparser/enigma/bouquets.py index e56a71f1..deb0bb9a 100644 --- a/app/eparser/enigma/bouquets.py +++ b/app/eparser/enigma/bouquets.py @@ -141,6 +141,7 @@ class ServiceType(Enum): MARKER = "64" SPACE = "832" # Hidden marker. ALT = "134" # Alternatives. + UDP = "256" @classmethod def _missing_(cls, value): diff --git a/app/eparser/iptv.py b/app/eparser/iptv.py index 41b8dffb..651d1fb5 100644 --- a/app/eparser/iptv.py +++ b/app/eparser/iptv.py @@ -1,3 +1,31 @@ +# -*- coding: utf-8 -*- +# +# The MIT License (MIT) +# +# Copyright (c) 2018-2021 Dmitriy Yefremov +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +# +# Author: Dmitriy Yefremov +# + + """ Module for IPTV and streams support """ import re from enum import Enum @@ -10,7 +38,7 @@ from app.ui.uicommons import IPTV_ICON # url, description, urlkey, account, usrname, psw, s_type, iconsrc, iconsrc_b, group NEUTRINO_FAV_ID_FORMAT = "{}::{}::{}::{}::{}::{}::{}::{}::{}::{}" -ENIGMA2_FAV_ID_FORMAT = " {}:0:{}:{:X}:{:X}:{:X}:{:X}:0:0:0:{}:{}\n#DESCRIPTION: {}\n" +ENIGMA2_FAV_ID_FORMAT = " {}:{}:{}:{:X}:{:X}:{:X}:{:X}:0:0:0:{}:{}\n#DESCRIPTION: {}\n" MARKER_FORMAT = " 1:64:{}:0:0:0:0:0:0:0::{}\n#DESCRIPTION {}\n" @@ -51,9 +79,6 @@ def parse_m3u(path, s_type, detect_encoding=True, params=None): for line in str(data, encoding=encoding, errors="ignore").splitlines(): if line.startswith("#EXTINF"): - inf, sep, line = line.partition(" ") - if not line: - line = inf line, sep, name = line.rpartition(",") data = re.split('"', line) @@ -115,12 +140,12 @@ def export_to_m3u(path, bouquet, s_type): file.writelines(lines) -def get_fav_id(url, service_name, settings_type, params=None, stream_type=None, s_type=1): +def get_fav_id(url, name, settings_type, params=None, st_type=None, s_id=0, srv_type=1): """ Returns fav id depending on the profile. """ if settings_type is SettingsType.ENIGMA_2: - stream_type = stream_type or StreamType.NONE_TS.value + st_type = st_type or StreamType.NONE_TS.value params = params or (0, 0, 0, 0) - return ENIGMA2_FAV_ID_FORMAT.format(stream_type, s_type, *params, quote(url), service_name, service_name, None) + return ENIGMA2_FAV_ID_FORMAT.format(st_type, s_id, srv_type, *params, quote(url), name, name, None) elif settings_type is SettingsType.NEUTRINO_MP: return NEUTRINO_FAV_ID_FORMAT.format(url, "", 0, None, None, None, None, "", "", 1) diff --git a/app/ui/iptv.glade b/app/ui/iptv.glade index 34b77c1c..6e663d3e 100644 --- a/app/ui/iptv.glade +++ b/app/ui/iptv.glade @@ -432,223 +432,8 @@ Author: Dmitriy Yefremov 5 5 2 - 2 + 5 True - - - Default - True - True - False - center - True - True - - - - 5 - 0 - - - - - True - False - Namespace - - - 5 - 1 - - - - - True - False - True - 5 - 5 - 0 - document-edit-symbolic - - - - 5 - 2 - - - - - Default - True - True - False - center - True - True - - - - 4 - 0 - - - - - True - False - NID - - - 4 - 1 - - - - - True - False - True - 5 - 5 - 0 - document-edit-symbolic - - - - 4 - 2 - - - - - Default - True - True - False - center - True - True - - - - 3 - 0 - - - - - True - False - TID - - - 3 - 1 - - - - - True - False - True - 5 - 5 - 0 - document-edit-symbolic - - - - 3 - 2 - - - - - Auto - True - True - False - center - True - True - - - - 2 - 0 - - - - - True - False - SID - - - 2 - 1 - - - - - True - False - True - 5 - 5 - 0 - document-edit-symbolic - - - - 2 - 2 - - - - - Default - True - True - False - center - True - True - - - - 1 - 0 - - - - - True - False - Type - - - 1 - 1 - - - - - True - False - True - 5 - 5 - 1 - document-edit-symbolic - - - - 1 - 2 - - True @@ -696,6 +481,264 @@ Author: Dmitriy Yefremov 0 + + + Default + True + True + False + center + True + True + + + + 6 + 0 + + + + + True + False + Namespace + + + 6 + 1 + + + + + True + False + True + 5 + 5 + 0 + document-edit-symbolic + + + + 6 + 2 + + + + + Default + True + True + False + center + True + True + + + + 5 + 0 + + + + + True + False + NID + + + 5 + 1 + + + + + True + False + True + 5 + 5 + 0 + document-edit-symbolic + + + + 5 + 2 + + + + + Default + True + True + False + center + True + True + + + + 4 + 0 + + + + + True + False + TID + + + 4 + 1 + + + + + True + False + True + 5 + 5 + 0 + document-edit-symbolic + + + + 4 + 2 + + + + + Auto + True + True + False + center + True + True + + + + 3 + 0 + + + + + True + False + SID + + + 3 + 1 + + + + + True + False + True + 5 + 5 + 0 + document-edit-symbolic + + + + 3 + 2 + + + + + Default + True + True + False + center + True + True + + + + 1 + 0 + + + + + True + False + ID + + + 1 + 1 + + + + + True + False + True + 5 + 5 + 0 + document-edit-symbolic + + + + 1 + 2 + + + + + Default + True + True + False + center + True + True + + + + 2 + 0 + + + + + True + False + Type + + + 2 + 1 + + + + + True + False + True + 5 + 5 + 1 + document-edit-symbolic + + + + 2 + 2 + + @@ -1118,39 +1161,32 @@ Author: Dmitriy Yefremov 5 5 2 - 2 + 5 True - + True False - Type + Namespace - 0 + 5 0 - + True - False - SID + True + 10 + 5 + 0 + document-edit-symbolic + - 1 - 0 - - - - - True - False - TID - - - 2 - 0 + 5 + 1 @@ -1159,19 +1195,111 @@ Author: Dmitriy Yefremov False NID + + 4 + 0 + + + + + True + True + 5 + 5 + 0 + document-edit-symbolic + + + + 4 + 1 + + + + + True + False + TID + 3 0 - + True - False - Namespace + True + 5 + 5 + 0 + document-edit-symbolic + - 4 + 3 + 1 + + + + + True + False + SID + + + 2 + 0 + + + + + True + True + 5 + 5 + 0 + document-edit-symbolic + + + + 2 + 1 + + + + + True + False + ID + + + 0 + 0 + + + + + True + True + 5 + 4 + document-edit-symbolic + + + + 0 + 1 + + + + + True + False + Type + + + 1 0 @@ -1185,71 +1313,11 @@ Author: Dmitriy Yefremov document-edit-symbolic - - 0 - 1 - - - - - True - True - 5 - 5 - 0 - document-edit-symbolic - - 1 1 - - - True - True - 5 - 5 - 0 - document-edit-symbolic - - - - 2 - 1 - - - - - True - True - 5 - 5 - 0 - document-edit-symbolic - - - - 3 - 1 - - - - - True - True - 5 - 5 - 0 - document-edit-symbolic - - - - 4 - 1 - - diff --git a/app/ui/iptv.py b/app/ui/iptv.py index fa9296aa..34c1d3d1 100644 --- a/app/ui/iptv.py +++ b/app/ui/iptv.py @@ -48,7 +48,7 @@ from app.ui.main_helper import get_base_model, get_iptv_url, on_popup_menu, get_ from app.ui.uicommons import (Gtk, Gdk, UI_RESOURCES_PATH, IPTV_ICON, Column, KeyboardKey, get_yt_icon) _DIGIT_ENTRY_NAME = "digit-entry" -_ENIGMA2_REFERENCE = "{}:0:{}:{:X}:{:X}:{:X}:{:X}:0:0:0" +_ENIGMA2_REFERENCE = "{}:{}:{}:{:X}:{:X}:{:X}:{:X}:0:0:0" _PATTERN = re.compile("(?:^[\\s]*$|\\D)") _UI_PATH = UI_RESOURCES_PATH + "iptv.glade" @@ -104,6 +104,7 @@ class IptvDialog: self._url_entry = builder.get_object("url_entry") self._reference_entry = builder.get_object("reference_entry") self._srv_type_entry = builder.get_object("srv_type_entry") + self._srv_id_entry = builder.get_object("srv_id_entry") self._sid_entry = builder.get_object("sid_entry") self._tr_id_entry = builder.get_object("tr_id_entry") self._net_id_entry = builder.get_object("net_id_entry") @@ -119,8 +120,8 @@ class IptvDialog: # style self._style_provider = Gtk.CssProvider() self._style_provider.load_from_path(UI_RESOURCES_PATH + "style.css") - self._digit_elems = (self._srv_type_entry, self._sid_entry, self._tr_id_entry, self._net_id_entry, - self._namespace_entry) + self._digit_elems = (self._srv_id_entry, self._srv_type_entry, self._sid_entry, self._tr_id_entry, + self._net_id_entry, self._namespace_entry) for el in self._digit_elems: el.get_style_context().add_provider_for_screen(Gdk.Screen.get_default(), self._style_provider, Gtk.STYLE_PROVIDER_PRIORITY_USER) @@ -195,6 +196,7 @@ class IptvDialog: except ValueError: self.show_info_message("Unknown stream type {}".format(s_type), Gtk.MessageType.ERROR) + self._srv_id_entry.set_text(data[1]) self._srv_type_entry.set_text(data[2]) self._sid_entry.set_text(str(int(data[3], 16))) self._tr_id_entry.set_text(str(int(data[4], 16))) @@ -212,6 +214,7 @@ class IptvDialog: if self._s_type is SettingsType.ENIGMA_2 and is_data_correct(self._digit_elems): self.on_url_changed(self._url_entry) self._reference_entry.set_text(_ENIGMA2_REFERENCE.format(self.get_type(), + self._srv_id_entry.get_text(), self._srv_type_entry.get_text(), int(self._sid_entry.get_text()), int(self._tr_id_entry.get_text()), @@ -296,6 +299,7 @@ class IptvDialog: def save_enigma2_data(self): name = self._name_entry.get_text().strip() fav_id = ENIGMA2_FAV_ID_FORMAT.format(self.get_type(), + self._srv_id_entry.get_text(), self._srv_type_entry.get_text(), int(self._sid_entry.get_text()), int(self._tr_id_entry.get_text()), @@ -432,6 +436,7 @@ class IptvListDialog: "on_response": self.on_response, "on_stream_type_default_togged": self.on_stream_type_default_togged, "on_stream_type_changed": self.on_stream_type_changed, + "on_default_id_toggled": self.on_default_id_toggled, "on_default_type_toggled": self.on_default_type_toggled, "on_auto_sid_toggled": self.on_auto_sid_toggled, "on_default_tid_toggled": self.on_default_tid_toggled, @@ -453,12 +458,14 @@ class IptvListDialog: self._info_bar = builder.get_object("list_configuration_info_bar") self._reference_label = builder.get_object("reference_label") self._stream_type_check_button = builder.get_object("stream_type_default_check_button") + self._id_default_check_button = builder.get_object("id_default_check_button") self._type_check_button = builder.get_object("type_default_check_button") self._sid_auto_check_button = builder.get_object("sid_auto_check_button") self._tid_check_button = builder.get_object("tid_default_check_button") self._nid_check_button = builder.get_object("nid_default_check_button") self._namespace_check_button = builder.get_object("namespace_default_check_button") self._stream_type_combobox = builder.get_object("stream_type_list_combobox") + self._list_srv_id_entry = builder.get_object("list_srv_id_entry") self._list_srv_type_entry = builder.get_object("list_srv_type_entry") self._list_sid_entry = builder.get_object("list_sid_entry") self._list_tid_entry = builder.get_object("list_tid_entry") @@ -470,10 +477,11 @@ class IptvListDialog: # Style style_provider = Gtk.CssProvider() style_provider.load_from_path(UI_RESOURCES_PATH + "style.css") - self._default_elems = (self._stream_type_check_button, self._type_check_button, self._sid_auto_check_button, - self._tid_check_button, self._nid_check_button, self._namespace_check_button) - self._digit_elems = (self._list_srv_type_entry, self._list_sid_entry, self._list_tid_entry, - self._list_nid_entry, self._list_namespace_entry) + self._default_elems = (self._stream_type_check_button, self._id_default_check_button, self._type_check_button, + self._sid_auto_check_button, self._tid_check_button, self._nid_check_button, + self._namespace_check_button) + self._digit_elems = (self._list_srv_id_entry, self._list_srv_type_entry, self._list_sid_entry, + self._list_tid_entry, self._list_nid_entry, self._list_namespace_entry) for el in self._digit_elems: el.get_style_context().add_provider_for_screen(Gdk.Screen.get_default(), style_provider, Gtk.STYLE_PROVIDER_PRIORITY_USER) @@ -495,30 +503,28 @@ class IptvListDialog: self._stream_type_combobox.set_active(1) self._stream_type_combobox.set_sensitive(not button.get_active()) + def on_default_id_toggled(self, button): + self.set_default(button, self._list_srv_id_entry, "0") + def on_default_type_toggled(self, button): - if button.get_active(): - self._list_srv_type_entry.set_text("1") - self._list_srv_type_entry.set_sensitive(not button.get_active()) + self.set_default(button, self._list_srv_type_entry, "1") def on_auto_sid_toggled(self, button): - if button.get_active(): - self._list_sid_entry.set_text("0") - self._list_sid_entry.set_sensitive(not button.get_active()) + self.set_default(button, self._list_sid_entry, "0") def on_default_tid_toggled(self, button): - if button.get_active(): - self._list_tid_entry.set_text("0") - self._list_tid_entry.set_sensitive(not button.get_active()) + self.set_default(button, self._list_tid_entry, "0") def on_default_nid_toggled(self, button): - if button.get_active(): - self._list_nid_entry.set_text("0") - self._list_nid_entry.set_sensitive(not button.get_active()) + self.set_default(button, self._list_nid_entry, "0") def on_default_namespace_toggled(self, button): + self.set_default(button, self._list_namespace_entry, "0") + + def set_default(self, button, entry, value): if button.get_active(): - self._list_namespace_entry.set_text("0") - self._list_namespace_entry.set_sensitive(not button.get_active()) + entry.set_text(value) + entry.set_sensitive(not button.get_active()) @run_idle def on_reset_to_default(self, item): @@ -550,7 +556,7 @@ class IptvListDialog: self.update_reference() def is_default_values(self): - return any(el.get_text() == "0" for el in self._digit_elems[2:]) + return any(el.get_text() == "0" for el in self._digit_elems[3:]) def is_all_data_default(self): return all(el.get_active() for el in self._default_elems) @@ -573,13 +579,15 @@ class IptvListConfigurationDialog(IptvListDialog): return if self._s_type is SettingsType.ENIGMA_2: + id_default = self._id_default_check_button.get_active() type_default = self._type_check_button.get_active() tid_default = self._tid_check_button.get_active() sid_auto = self._sid_auto_check_button.get_active() nid_default = self._nid_check_button.get_active() namespace_default = self._namespace_check_button.get_active() - stream_type = get_stream_type(self._stream_type_combobox) + st_type = get_stream_type(self._stream_type_combobox) + s_id = 0 if id_default else int(self._list_srv_id_entry.get_text()) srv_type = "1" if type_default else self._list_srv_type_entry.get_text() tid = "0" if tid_default else "{:X}".format(int(self._list_tid_entry.get_text())) nid = "0" if nid_default else "{:X}".format(int(self._list_nid_entry.get_text())) @@ -591,9 +599,9 @@ class IptvListConfigurationDialog(IptvListDialog): data = data.split(":") if self.is_all_data_default(): - data[2], data[3], data[4], data[5], data[6] = "10000" + data[1], data[2], data[3], data[4], data[5], data[6] = "010000" else: - data[0], data[2], data[4], data[5], data[6] = stream_type, srv_type, tid, nid, namespace + data[0], data[1], data[2], data[4], data[5], data[6] = st_type, s_id, srv_type, tid, nid, namespace data[3] = "{:X}".format(index) if sid_auto else "0" data = ":".join(data) @@ -685,9 +693,10 @@ class M3uImportDialog(IptvListDialog): if not self.is_all_data_default(): services = [] params = [int(el.get_text()) for el in self._digit_elems] - s_type = params[0] - params = params[1:] - stream_type = get_stream_type(self._stream_type_combobox) + s_id = params[0] + s_type = params[1] + params = params[2:] + st_type = get_stream_type(self._stream_type_combobox) for i, s in enumerate(self._services, start=params[0]): # Skipping markers. @@ -696,8 +705,8 @@ class M3uImportDialog(IptvListDialog): continue params[0] = i - picon_id = "{}_0_{:X}_{:X}_{:X}_{:X}_{:X}_0_0_0.png".format(stream_type, s_type, *params) - fav_id = get_fav_id(s.data_id, s.service, self._s_type, params, stream_type, s_type) + picon_id = "{}_{}_{:X}_{:X}_{:X}_{:X}_{:X}_0_0_0.png".format(st_type, s_id, s_type, *params) + fav_id = get_fav_id(s.data_id, s.service, self._s_type, params, st_type, s_id, s_type) if s.picon: picons[s.picon] = picon_id