From 6c27b5434e842ab76ed9074d730125ba673193de Mon Sep 17 00:00:00 2001 From: SebMourlhou Date: Tue, 7 Nov 2023 00:00:01 +0100 Subject: [PATCH 01/15] Add support for SFR TV --- .../resource.language.en_gb/strings.po | 9 + .../resource.language.fr_fr/strings.po | 10 +- .../resource.language.he_il/strings.po | 8 + .../resource.language.nl_nl/strings.po | 9 + resources/lib/channels/fr/sfrtv.py | 542 +++ resources/lib/iptvmanager.py | 27 +- resources/lib/skeletons/fr_live.py | 8 + resources/lib/skeletons/fr_replay.py | 7 + resources/lib/skeletons/sfrtv_live.py | 4180 +++++++++++++++++ resources/lib/xmltv.py | 1 + resources/settings.xml | 3 + 11 files changed, 4796 insertions(+), 8 deletions(-) create mode 100644 resources/lib/channels/fr/sfrtv.py create mode 100644 resources/lib/skeletons/sfrtv_live.py diff --git a/resources/language/resource.language.en_gb/strings.po b/resources/language/resource.language.en_gb/strings.po index c8b1584ed..92e1a7804 100644 --- a/resources/language/resource.language.en_gb/strings.po +++ b/resources/language/resource.language.en_gb/strings.po @@ -566,6 +566,15 @@ msgctxt "#30261" msgid "MyTF1 Password" msgstr "" +msgctxt "#30262" +msgid "SFR TV login" +msgstr "" + +msgctxt "#30263" +msgid "SFR TV password" +msgstr "" + + # TV integration (from 30270 to 30289) msgctxt "#30270" diff --git a/resources/language/resource.language.fr_fr/strings.po b/resources/language/resource.language.fr_fr/strings.po index 97281aa45..0a68c81d0 100644 --- a/resources/language/resource.language.fr_fr/strings.po +++ b/resources/language/resource.language.fr_fr/strings.po @@ -566,6 +566,14 @@ msgctxt "#30261" msgid "Mot de passe MyTF1" msgstr "" +msgctxt "#30262" +msgid "SFR TV login" +msgstr "Login SFR TV" + +msgctxt "#30263" +msgid "SFR TV password" +msgstr "Mot de passe SFR TV" + # TV integration (from 30270 to 30289) @@ -824,7 +832,7 @@ msgstr "Ce contenu est protégé par DRM. Le mode téléchargement ne fonctionne msgctxt "#30604" msgid "Access to this content requires an account %s. You can provide your credentials for it in the settings of this add-on. If you don't have an account, you can create one at this url %s." -msgstr "Accèss à ce contenu nécessite un compte %s. Vous pouvez configurer votre identifiant et votre mot de passe dans la configuration de cette extension. Si vous n'avez pas de compte, vous pouvez le créer à cette URL %s." +msgstr "Accéder à ce contenu nécessite un compte %s. Vous pouvez configurer votre identifiant et votre mot de passe dans la configuration de cette extension. Si vous n'avez pas de compte, vous pouvez le créer à cette URL %s." msgctxt "#30605" msgid "You can now use Kodi's TV feature to watch live TV channels from Catch-up TV & More, follow the tutorial at https://catch-up-tv-and-more.github.io/live_tv_installation/." diff --git a/resources/language/resource.language.he_il/strings.po b/resources/language/resource.language.he_il/strings.po index 25bb18cf8..fb8712caa 100644 --- a/resources/language/resource.language.he_il/strings.po +++ b/resources/language/resource.language.he_il/strings.po @@ -566,6 +566,14 @@ msgctxt "#30261" msgid "MyTF1 Password" msgstr "" +msgctxt "#30262" +msgid "SFR TV login" +msgstr "" + +msgctxt "#30263" +msgid "SFR TV password" +msgstr "" + # TV integration (from 30270 to 30289) diff --git a/resources/language/resource.language.nl_nl/strings.po b/resources/language/resource.language.nl_nl/strings.po index a8e89c07a..c7f267f79 100644 --- a/resources/language/resource.language.nl_nl/strings.po +++ b/resources/language/resource.language.nl_nl/strings.po @@ -566,6 +566,15 @@ msgctxt "#30261" msgid "MyTF1 Password" msgstr "" +msgctxt "#30262" +msgid "SFR TV login" +msgstr "" + +msgctxt "#30263" +msgid "SFR TV password" +msgstr "" + + # TV integration (from 30270 to 30289) msgctxt "#30270" diff --git a/resources/lib/channels/fr/sfrtv.py b/resources/lib/channels/fr/sfrtv.py new file mode 100644 index 000000000..db1d23569 --- /dev/null +++ b/resources/lib/channels/fr/sfrtv.py @@ -0,0 +1,542 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +import base64 +from datetime import datetime +import importlib +import json +import urlquick +import os +from codequick import Listitem, Resolver, Route, Script +from codequick.storage import Cache +from kodi_six import xbmcgui +from resources.lib import web_utils, resolver_proxy +from resources.lib.menu_utils import item_post_treatment +from resources.lib.main import tv_guide_menu + +CACHE_FILE = os.path.join(Route.get_info("profile"), u".sfrtv_cache.sqlite") +USER_AGENT = web_utils.get_random_ua() +TOKEN_MAX_AGE = 840 # 14 minutes to be under the 15 mn token validity limit +CONFIG_URL = 'https://tv.sfr.fr/configs/config.json' +LOGIN_URL = 'https://www.sfr.fr/cas/login' +ACCESS_TOKEN_URL = 'https://www.sfr.fr/cas/oidc/authorize' +USER_PROFILE_URL = 'https://ws-backendtv.sfr.fr/heimdall-core/public/api/v2/userProfiles' +SERVICE_URL = 'https://ws-backendtv.sfr.fr/sekai-service-plan/public/v2/service-list' +LICENSE_URL = 'https://ws-backendtv.sfr.fr/asgard-drm-widevine/public/licence' +STRUCT_MENU_URL = 'https://ws-cdn.tv.sfr.net/gaia-core/rest/api/web/v2/menu/RefMenuItem::gen8-replay-v2/structure' +MENU_URL = 'https://ws-backendtv.sfr.fr/gaia-core/rest/api/web/v2/spot/{}/content' +CATEGORIES_URL = 'https://ws-cdn.tv.sfr.net/gaia-core/rest/api/web/v1/stores/{}/categories' +PRODUCTS_URL = 'https://ws-cdn.tv.sfr.net/gaia-core/rest/api/web/v2/categories/{}/contents' +PRODUCT_DETAILS_URL = 'https://ws-cdn.tv.sfr.net/gaia-core/rest/api/web/v1/content/{}/detail' +PRODUCT_OPTIONS_URL = 'https://ws-backendtv.sfr.fr/gaia-core/rest/api/web/v3/content/{}/options' +CUSTOMDATA_REPLAY = ('description={}&deviceId=byPassARTHIUS&deviceName=Firefox-96.0----Windows&deviceType=PC' + '&osName=Windows&osVersion=10&persistent=false&resolution=1600x900&tokenType=castoken' + '&tokenSSO={}&type=REPLAY') +CUSTOMDATA_LIVE = ('description={}&deviceId=byPassARTHIUS&deviceName=Firefox-96.0----Windows&deviceType=PC' + '&osName=Windows&osVersion=10&persistent=false&resolution=1600x900&tokenType=castoken' + '&tokenSSO={}&type=LIVEOTT&accountId={}') +MAX_PRODUCTS = 20 + + +def get_sfrtv_config(plugin): + return urlquick.get(CONFIG_URL, + headers={ + 'User-Agent': USER_AGENT + }).json() + + +def get_sfrtv_user_profile(plugin, token): + return urlquick.get(USER_PROFILE_URL, + params={ + 'token': token + }, + headers={ + 'Accept': 'application/json, text/plain, */*', + 'Referer': 'https://tv.sfr.fr/', + 'User-Agent': USER_AGENT + }).json() + + +@Route.register +def get_token(plugin, with_dialog=True): + username = plugin.setting.get_string('sfrtv.login') + password = plugin.setting.get_string('sfrtv.password') + if not username or not password: + if with_dialog: + xbmcgui.Dialog().ok(plugin.localize(30600), + plugin.localize(30604) % ('SFR TV', 'https://tv.sfr.fr')) + return None + + # Unable to use urlquick cache due to authentication redirects + cache = Cache(CACHE_FILE, TOKEN_MAX_AGE) + + if 'token' in cache: + return cache['token'] + + sfrtv_config = get_sfrtv_config(plugin) + sfrtv_client_id = sfrtv_config['auth']['OIDC_CLIENT_ID'] + + session = urlquick.Session() + + resp = session.get(ACCESS_TOKEN_URL, + params={ + 'client_id': sfrtv_client_id, + 'scope': 'openid', + 'response_type': 'token', + 'redirect_uri': 'https://tv.sfr.fr/' + }, + headers={ + 'user-agent': USER_AGENT, + 'authority': 'www.sfr.fr', + 'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', + 'accept-language': 'fr-FR,fr;q=0.9,en-US;q=0.8,en;q=0.7', + 'referer': 'https://tv.sfr.fr/', + }, + max_age=-1) + + root = resp.parse() + form_elt = root.find(".//form[@name='loginForm']") + lt = form_elt.find(".//input[@name='lt']").get('value') + lrt = form_elt.find(".//input[@name='lrt']").get('value') + execution = form_elt.find(".//input[@name='execution']").get('value') + event_id = form_elt.find(".//input[@name='_eventId']").get('value') + + session.post( + LOGIN_URL, + params={ + 'domain': 'mire-sfr', + 'service': 'https://www.sfr.fr/cas/oidc/callbackAuthorize' + }, + headers={ + 'user-agent': USER_AGENT, + 'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', + 'authority': 'www.sfr.fr', + 'accept-language': 'fr-FR,fr;q=0.9,en-US;q=0.8,en;q=0.7', + 'content-type': 'application/x-www-form-urlencoded', + 'origin': 'https://www.sfr.fr', + 'referer': resp.url, + }, + data={ + 'lt': lt, + 'execution': execution, + 'lrt': lrt, + '_eventId': event_id, + 'username': username, + 'password': password, + 'remember-me': 'on', + 'identifier': '' + }, + max_age=-1 + ) + + resp = session.get(ACCESS_TOKEN_URL, + params={ + 'client_id': sfrtv_client_id, + 'scope': 'openid', + 'response_type': 'token', + 'redirect_uri': 'https://tv.sfr.fr/', + 'token': 'true', + 'gateway': 'true' + }, + headers={ + 'user-agent': USER_AGENT, + 'authority': 'www.sfr.fr', + 'accept': 'application/json, text/plain, */*', + 'accept-language': 'fr-FR,fr;q=0.9,en-US;q=0.8,en;q=0.7', + 'origin': 'https://tv.sfr.fr', + 'referer': 'https://tv.sfr.fr/', + }, + max_age=-1) + access_token_b64_bytes = resp.content + access_token_b64 = access_token_b64_bytes.decode('ascii') + if not access_token_b64: + if with_dialog: + xbmcgui.Dialog().ok(plugin.localize(30600), + plugin.localize(30711)) + return None + access_token_b64_second_part = access_token_b64.split('.')[1] + access_token_b64_second_part_bytes = access_token_b64_second_part.encode('ascii') + missing_padding = len(access_token_b64_second_part_bytes) % 4 + if missing_padding: + access_token_b64_second_part_bytes += b'=' * (4 - missing_padding) + access_token_second_part_bytes = base64.b64decode(access_token_b64_second_part_bytes) + access_token_second_part_json = access_token_second_part_bytes.decode('utf-8') + access_token_second_part = json.loads(access_token_second_part_json) + token = access_token_second_part['tu'] + + cache['token'] = token + return token + + +def get_stores(plugin, token): + struct_menu = urlquick.get(STRUCT_MENU_URL, + params={ + 'app': 'gen8', + 'device': 'browser' + }, + headers={ + 'Accept': 'application/json', + 'Referer': 'https://tv.sfr.fr/', + 'User-Agent': USER_AGENT + }).json() + spot_id = struct_menu['spots'][0]['id'] + menu = urlquick.get(MENU_URL.format(spot_id), + params={ + 'app': 'gen8', + 'device': 'browser', + 'token': token + }, + headers={ + 'Accept': 'application/json', + 'Referer': 'https://tv.sfr.fr/', + 'User-Agent': USER_AGENT + }, + max_age=TOKEN_MAX_AGE).json() + return list(map(lambda t: {'id': t['action']['actionIds']['storeId'], + 'title': t['title'], + 'images': t['images']}, + menu['tiles'])) + + +@Route.register(autosort=False) +def list_stores(plugin, **kwargs): + token = get_token(plugin) + if not token: + yield False + return + + stores = get_stores(plugin, token) + + for store in stores: + item = Listitem() + item.label = store['title'] + + for image in store['images']: + if image['format'] == 'logo': + item.art['thumb'] = image['url'] + + item.set_callback(list_categories, + store_id=store['id']) + item_post_treatment(item) + yield item + + +def get_categories(plugin, store_id): + categories_infos = urlquick.get(CATEGORIES_URL.format(store_id), + params={ + 'app': 'gen8', + 'device': 'browser' + }, + headers={ + 'Accept': 'application/json', + 'Referer': 'https://tv.sfr.fr/', + 'User-Agent': USER_AGENT + }).json() + return categories_infos['categories'] + + +@Route.register(autosort=False) +def list_categories(plugin, store_id): + categories = get_categories(plugin, store_id) + + for category in categories: + item = Listitem() + item.label = category['name'] + item.set_callback(list_products, + category_id=category['id'], + page=0) + item_post_treatment(item) + yield item + + +def get_products(plugin, category_id, page): + resp = urlquick.get(PRODUCTS_URL.format(category_id), + params={ + 'app': 'gen8', + 'device': 'browser', + 'page': page, + 'size': MAX_PRODUCTS + }, + headers={ + 'Accept': 'application/json', + 'Referer': 'https://tv.sfr.fr/', + 'User-Agent': USER_AGENT + }, + max_age=900) + products = resp.json() + return products + + +@Route.register(autosort=False) +def list_products(plugin, category_id, page): + # Pagination seems to be blocked at 20 videos (the "page" parameter doesn't change anything), + # so let's paginate at 100 videos + n_loop = 5 + for x in range(n_loop): + products = get_products(plugin, category_id, page) + + for product in products: + yield build_product_item(plugin, product) + + if len(products) == MAX_PRODUCTS: + if x < (n_loop - 1): + page += 1 + else: + yield Listitem.next_page(category_id=category_id, + callback=list_products, + page=page + 1) + else: + break + + +def get_product_details(plugin, product_id): + return urlquick.get(PRODUCT_DETAILS_URL.format(product_id), + params={ + 'accountTypes': 'LAND', + 'universe': 'PROVIDER' + }, + headers={ + 'Accept': 'application/json', + 'Referer': 'https://tv.sfr.fr/', + 'User-Agent': USER_AGENT + }).json() + + +@Route.register(autosort=False) +def list_product_details(plugin, product_id): + token = get_token(plugin) + if not token: + yield False + return + + product_details = get_product_details(plugin, product_id) + + if 'seasons' in product_details: + for season in product_details['seasons']: + season_details = get_product_details(plugin, season['id']) + yield build_product_item(plugin, season_details) + elif 'episodes' in product_details: + for episode in product_details['episodes']: + episode_details = get_product_details(plugin, episode['id']) + yield build_product_item(plugin, episode_details) + else: + yield build_product_item(plugin, product_details) + + +def build_product_item(plugin, product): + item = Listitem() + + item.label = product['title'] + + if 'description' in product: + item.info['plot'] = product['description'] + + if 'duration' in product: + item.info['duration'] = product['duration'] + + if 'seasonNumber' in product and product['seasonNumber']: + item.info['season'] = product['seasonNumber'] + + if 'episodeNumber' in product and product['episodeNumber']: + item.info['mediatype'] = 'episode' + item.info['episode'] = product['episodeNumber'] + + if 'diffusionDate' in product: + dt = datetime.fromtimestamp(int(product['diffusionDate'] / 1000)) + dt_format = '%d/%m/%Y' + item.info.date(dt.strftime(dt_format), dt_format) + + for image in product.get('images', []): + if image['format'] == '2/3' and not item.art.get('thumb', None): + item.art['thumb'] = image['url'] + elif image['format'] == '16/9': + item.art['thumb'] = image['url'] + + item.set_callback(list_product_details if product.get('type', '') in ['Serie', 'Season'] else get_replay_stream, + product_id=product['id']) + + item_post_treatment(item) + + return item + + +def get_replay_url(plugin, product_id, token): + product_options = urlquick.get(PRODUCT_OPTIONS_URL.format(product_id), + params={ + 'app': 'gen8', + 'device': 'browser', + 'token': token, + 'accountTypes': 'LAND', + 'universe': 'PROVIDER' + }, + headers={ + 'Accept': 'application/json', + 'Referer': 'https://tv.sfr.fr/', + 'User-Agent': USER_AGENT + }).json() + for stream in product_options[0]['offers'][0]['streams']: + if stream['drm'] == 'WIDEVINE': + return stream['url'] + return None + + +@Resolver.register +def get_replay_stream(plugin, product_id): + token = get_token(plugin) + if not token: + return False + + replay_url = get_replay_url(plugin, product_id, token) + if not replay_url: + return False + + headers = { + 'User-Agent': USER_AGENT, + 'customdata': CUSTOMDATA_REPLAY.format(USER_AGENT, token), + 'Referer': 'https://tv.sfr.fr/', + 'content-type': 'application/octet-stream' + } + + return resolver_proxy.get_stream_with_quality(plugin, + video_url=replay_url, + license_url=LICENSE_URL, + manifest_type='mpd', + headers=headers) + + +def get_active_services(plugin, token): + services = urlquick.get(SERVICE_URL, + params={ + 'app': 'gen8', + 'device': 'browser', + 'token': token + }, + headers={ + 'User-Agent': USER_AGENT, + 'Accept': 'application/json', + 'Accept-language': 'fr-FR,fr;q=0.9,en-US;q=0.8,en;q=0.7', + 'Origin': 'https://tv.sfr.fr', + 'Referer': 'https://tv.sfr.fr/', + 'Connection': 'keep-alive' + }, + max_age=TOKEN_MAX_AGE).json() + active_services = list(filter(lambda c: c['access'], services)) + return active_services + + +def list_live_channels(plugin=Script): + + """ + Called by iptvmanager to retrieve a dynamic list of SFR TV channels to activate for IPTV Manager. + Only the channels activated for the current account are yielded. + For each channel, the information is retrieved from the "sfrtv_live" skeleton but if the channel + is not found in it, the information is manually built. + + :param plugin: plugin (codequick.script.Script) + + :returns: A generator of the channels infos + :rtype: :class:`types.GeneratorType` + """ + + token = get_token(plugin, + with_dialog=False) + if not token: + return + + active_services = get_active_services(plugin, token) + channels_dict = importlib.import_module('resources.lib.skeletons.sfrtv_live').menu + + for serv in active_services: + channel_infos = channels_dict.get(serv['serviceId'], None) + + if not channel_infos: + channel_infos = { + 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + 'label': serv['name'], + 'enabled': True, + 'order': serv['zappingId'] + } + for image in serv.get('images', []): + if image['type'] == 'color': + channel_infos['thumb'] = image['url'] + + channel_infos['id'] = serv['serviceId'] + yield channel_infos + + +@Route.register +def list_lives(plugin, item_id, **kwargs): + token = get_token(plugin) + if not token: + yield False + return + + active_services = get_active_services(plugin, token) + tv_guide_items = list(tv_guide_menu(plugin, 'sfrtv_live')) + + for serv in sorted(active_services, key=lambda s: s['zappingId']): + + # Try to find the TV Guide Listitem from sfrtv live menu + item = next( + (tvg for tvg in tv_guide_items if tvg.params.item_id == serv['serviceId']), + None + ) + + # If not found, construct manually a Listitem without TV guide + if not item: + item = Listitem() + item.label = serv['name'] + + for image in serv.get('images', []): + if image.get('type', '') == 'color': + item.art['thumb'] = item.art['landscape'] = image.get('url', None) + + # Playcount is useless for live streams + item.info['playcount'] = 0 + + item.set_callback(get_live_stream, + item_id=serv['serviceId']) + + yield item + + +def get_live_url(plugin, service_id, token): + active_services = get_active_services(plugin, token) + + for serv in active_services: + if serv['serviceId'] == service_id: + for stream in serv['streams']: + if stream['drm'] == 'WIDEVINE': + # Workaround for IA bug : https://github.com/xbmc/inputstream.adaptive/issues/804 + response = urlquick.get(stream['url'], headers={'User-Agent': USER_AGENT}, max_age=-1) + live_url = response.xml().find('{urn:mpeg:dash:schema:mpd:2011}Location').text + return live_url + return None + + +@Resolver.register +def get_live_stream(plugin, item_id, **kwargs): + token = get_token(plugin) + if not token: + return False + + live_url = get_live_url(plugin, item_id, token) + if not live_url: + return False + + sfrtv_user_profile = get_sfrtv_user_profile(plugin, token) + account_id = sfrtv_user_profile['siebelId'] + + headers = { + 'user-agent': USER_AGENT, + 'customdata': CUSTOMDATA_LIVE.format(USER_AGENT, token, account_id), + 'origin': 'https://tv.sfr.fr', + 'referer': 'https://tv.sfr.fr/', + 'content-type': 'application/octet-stream' + } + + return resolver_proxy.get_stream_with_quality(plugin, + video_url=live_url, + license_url=LICENSE_URL, + manifest_type='mpd', + headers=headers) diff --git a/resources/lib/iptvmanager.py b/resources/lib/iptvmanager.py index 313163633..57ab17877 100644 --- a/resources/lib/iptvmanager.py +++ b/resources/lib/iptvmanager.py @@ -79,16 +79,26 @@ def get_all_live_tv_channels(): # If this channel is disabled --> ignore this channel if not channel_infos.get('enabled', False): continue - # If this channel is a folder (e.g. multi live) --> ignore this channel + # If this channel is a folder (e.g. multi live) if 'resolver' not in channel_infos: + # If a function is defined to retrieve a dynamic list of channels for that folder + if 'list_channels_function' in channel_infos: + module_name, function_name = channel_infos['list_channels_function'].split(':') + module = importlib.import_module(module_name) + for ch_infos in getattr(module, function_name)(): + # With a prefixed id to avoid conflicts with the country's "classic" channels + folder_id = channel_id + ch_id = folder_id + '.' + ch_infos['id'] + ch_label = get_item_label(ch_infos['id'], ch_infos) + channels.append((ch_infos['order'], ch_id, ch_label, ch_infos, None, folder_id)) continue # Check if this channel has multiple language if 'available_languages' in channel_infos: for lang in channel_infos['available_languages']: label = '{} ({})'.format(get_item_label(channel_id, channel_infos, append_selected_lang=False), lang) - channels.append((channel_infos['order'], channel_id, label, channel_infos, lang)) + channels.append((channel_infos['order'], channel_id, label, channel_infos, lang, None)) else: - channels.append((channel_infos['order'], channel_id, get_item_label(channel_id, channel_infos), channel_infos, None)) + channels.append((channel_infos['order'], channel_id, get_item_label(channel_id, channel_infos), channel_infos, None, None)) channels = sorted(channels, key=lambda x: x[0]) country_channels.append((country_infos['order'], country_id, get_item_label(country_id, country_infos), country_infos, channels)) return sorted(country_channels, key=lambda x: x[2]) @@ -117,12 +127,12 @@ def select_channels(plugin): if country_id not in tv_integration_settings['enabled_channels']: tv_integration_settings['enabled_channels'][country_id] = {} - for (channel_order, channel_id, channel_label, channel_infos, lang) in channels: + for (channel_order, channel_id, channel_label, channel_infos, lang, folder_id) in channels: channel_key = channel_id if not lang else channel_id + ' ' + lang if channel_key not in tv_integration_settings['enabled_channels'][country_id]: tv_integration_settings['enabled_channels'][country_id][channel_key] = {'enabled': False} - label = country_label + ' - ' + channel_label + label = country_label + ' - ' + (folder_id + ' - ' if folder_id else '') + channel_label options.append(label) selected_channels_map.append((country_id, channel_key)) if tv_integration_settings['enabled_channels'][country_id][channel_key]['enabled']: @@ -182,7 +192,7 @@ def send_channels(self): tv_integration_settings = get_tv_integration_settings() for (country_order, country_id, country_label, country_infos, channels) in country_channels: - for (channel_order, channel_id, channel_label, channel_infos, lang) in channels: + for (channel_order, channel_id, channel_label, channel_infos, lang, folder_id) in channels: channel_key = channel_id if not lang else channel_id + ' ' + lang if not tv_integration_settings['enabled_channels'].get(country_id, {}).get(channel_key, {}).get('enabled', False): continue @@ -190,6 +200,9 @@ def send_channels(self): json_stream = {} json_stream['name'] = channel_label resolver = channel_infos['resolver'].replace(':', '/') + if folder_id: + # clean-up the extra-prefix + channel_id = channel_id.replace(folder_id + '.', '', 1) params = { 'item_id': channel_id } @@ -231,7 +244,7 @@ def send_epg(self): # Ierate over each country and enabled channels to grab needed programmes for (country_order, country_id, country_label, country_infos, channels) in country_channels: - for (channel_order, channel_id, channel_label, channel_infos, lang) in channels: + for (channel_order, channel_id, channel_label, channel_infos, lang, folder_id) in channels: channel_key = channel_id if not lang else channel_id + ' ' + lang if not tv_integration_settings['enabled_channels'].get(country_id, {}).get(channel_key, {}).get('enabled', False): continue diff --git a/resources/lib/skeletons/fr_live.py b/resources/lib/skeletons/fr_live.py index 173371f46..20b572ced 100644 --- a/resources/lib/skeletons/fr_live.py +++ b/resources/lib/skeletons/fr_live.py @@ -857,5 +857,13 @@ }, 'enabled': True, 'order': 81 + }, + 'sfrtv': { + 'route': '/resources/lib/channels/fr/sfrtv:list_lives', + 'list_channels_function': 'resources.lib.channels.fr.sfrtv:list_live_channels', + 'label': 'SFR TV', + 'thumb': 'channels/fr/sfrtv.png', + 'enabled': True, + 'order': 82 } } diff --git a/resources/lib/skeletons/fr_replay.py b/resources/lib/skeletons/fr_replay.py index 9aaff0158..c080bf451 100644 --- a/resources/lib/skeletons/fr_replay.py +++ b/resources/lib/skeletons/fr_replay.py @@ -382,5 +382,12 @@ "fanart": "channels/fr/jone_fanart.jpg", "enabled": True, "order": 45, + }, + "sfrtv": { + "route": "/resources/lib/channels/fr/sfrtv:list_stores", + "label": "SFR TV", + "thumb": "channels/fr/sfrtv.png", + "enabled": True, + "order": 46, } } diff --git a/resources/lib/skeletons/sfrtv_live.py b/resources/lib/skeletons/sfrtv_live.py new file mode 100644 index 000000000..996e8cf0b --- /dev/null +++ b/resources/lib/skeletons/sfrtv_live.py @@ -0,0 +1,4180 @@ +# -*- coding: utf-8 -*- + +from __future__ import unicode_literals + +# This dictionary is not really needed to list the live channels because the list is dynamically generated to keep +# only the active channels for the account. +# It is necessary to have the EPG by setting the xmltv id and also to have a higher-quality thumb. +# That's why a lot of code is commented out, this information is precisely what's missing for the commented channels. + +menu = { + 'NEUF_TF1': { + 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + 'label': 'TF1', + 'thumb': 'channels/fr/tf1.png', + 'fanart': 'channels/fr/tf1_fanart.jpg', + 'xmltv_id': 'C192.api.telerama.fr', + 'm3u_group': 'TNT', + 'm3u_order': 1, + 'enabled': True, + 'order': 1 + }, + 'NEUF_FRANCE2': { + 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + 'label': 'France 2', + 'thumb': 'channels/fr/france2.png', + 'fanart': 'channels/fr/france2_fanart.jpg', + 'xmltv_id': 'C4.api.telerama.fr', + 'm3u_group': 'TNT', + 'm3u_order': 2, + 'enabled': True, + 'order': 2 + }, + 'NEUF_FRANCE3': { + 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + 'label': 'France 3', + 'thumb': 'channels/fr/france3.png', + 'fanart': 'channels/fr/france3_fanart.jpg', + 'xmltv_id': 'C80.api.telerama.fr', + 'm3u_group': 'TNT', + 'm3u_order': 3, + 'enabled': True, + 'order': 3 + }, + 'NEUF_FRANCE5': { + 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + 'label': 'France 5', + 'thumb': 'channels/fr/france5.png', + 'fanart': 'channels/fr/france5_fanart.jpg', + 'xmltv_id': 'C47.api.telerama.fr', + 'm3u_group': 'TNT', + 'm3u_order': 5, + 'enabled': True, + 'order': 5 + }, + 'NEUF_M6': { + 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + 'label': 'M6', + 'thumb': 'channels/fr/m6.png', + 'fanart': 'channels/fr/m6_fanart.jpg', + 'xmltv_id': 'C118.api.telerama.fr', + 'm3u_group': 'TNT', + 'm3u_order': 6, + 'enabled': True, + 'order': 6 + }, + 'NEUF_ARTE': { + 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + 'label': 'Arte', + 'thumb': 'channels/fr/arte.png', + 'fanart': 'channels/fr/arte_fanart.jpg', + 'xmltv_id': 'C111.api.telerama.fr', + 'm3u_group': 'TNT', + 'm3u_order': 7, + 'enabled': True, + 'order': 7 + }, + 'NEUF_DIRECT8': { + 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + 'label': 'C8', + 'thumb': 'channels/fr/c8.png', + 'fanart': 'channels/fr/c8_fanart.jpg', + 'xmltv_id': 'C445.api.telerama.fr', + 'm3u_group': 'TNT', + 'm3u_order': 8, + 'enabled': True, + 'order': 8 + }, + 'NEUF_W9': { + 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + 'label': 'W9', + 'thumb': 'channels/fr/w9.png', + 'fanart': 'channels/fr/w9_fanart.jpg', + 'xmltv_id': 'C119.api.telerama.fr', + 'm3u_group': 'TNT', + 'm3u_order': 9, + 'enabled': True, + 'order': 9 + }, + 'NEUF_TMC': { + 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + 'label': 'TMC', + 'thumb': 'channels/fr/tmc.png', + 'fanart': 'channels/fr/tmc_fanart.jpg', + 'xmltv_id': 'C195.api.telerama.fr', + 'm3u_group': 'TNT', + 'm3u_order': 10, + 'enabled': True, + 'order': 10 + }, + 'NEUF_NT1': { + 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + 'label': 'TFX', + 'thumb': 'channels/fr/tfx.png', + 'fanart': 'channels/fr/tfx_fanart.jpg', + 'xmltv_id': 'C446.api.telerama.fr', + 'm3u_group': 'TNT', + 'm3u_order': 11, + 'enabled': True, + 'order': 11 + }, + 'NEUF_NRJ12': { + 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + 'label': 'NRJ12', + 'thumb': 'channels/fr/nrj12.png', + 'fanart': 'channels/fr/nrj12_fanart.jpg', + 'xmltv_id': 'C444.api.telerama.fr', + 'm3u_group': 'TNT', + 'm3u_order': 12, + 'enabled': True, + 'order': 12 + }, + 'NEUF_LCP': { + 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + 'label': 'LCP', + 'thumb': 'channels/fr/lcp.png', + 'fanart': 'channels/fr/lcp_fanart.jpg', + 'xmltv_id': 'C234.api.telerama.fr', + 'm3u_group': 'TNT', + 'm3u_order': 13, + 'enabled': True, + 'order': 13 + }, + 'NEUF_FRANCE4': { + 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + 'label': 'France 4', + 'thumb': 'channels/fr/france4.png', + 'fanart': 'channels/fr/france4_fanart.jpg', + 'xmltv_id': 'C78.api.telerama.fr', + 'm3u_group': 'TNT', + 'm3u_order': 14, + 'enabled': True, + 'order': 14 + }, + 'NEUF_BFMTV': { + 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + 'label': 'BFM TV', + 'thumb': 'channels/fr/bfmtv.png', + 'fanart': 'channels/fr/bfmtv_fanart.jpg', + 'xmltv_id': 'C481.api.telerama.fr', + 'm3u_group': 'TNT', + 'm3u_order': 15, + 'enabled': True, + 'order': 15 + }, + # 'NEUF_ITELE': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'CNews', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 16, + # 'enabled': True, + # 'order': 16 + # }, + # 'NEUF_VIRGIN17': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'CStar', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 17, + # 'enabled': True, + # 'order': 17 + # }, + # 'NEUF_GULLI': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'Gulli', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 18, + # 'enabled': True, + # 'order': 18 + # }, + # 'NEUF_HD1': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'TF1 Séries-Films', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 20, + # 'enabled': True, + # 'order': 20 + # }, + # 'NEUF_LEQUIPETV': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'La chaine l’Équipe', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 21, + # 'enabled': True, + # 'order': 21 + # }, + # 'NEUF_6TER': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': '6ter', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 22, + # 'enabled': True, + # 'order': 22 + # }, + # 'NEUF_NUM23': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'RMC STORY', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 23, + # 'enabled': True, + # 'order': 23 + # }, + # 'NEUF_RMCDEC': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'RMC Découverte', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 24, + # 'enabled': True, + # 'order': 24 + # }, + # 'NEUF_CHERIE25': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'Chérie 25', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 25, + # 'enabled': True, + # 'order': 25 + # }, + # 'NEUF_LCI': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'LCI', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 26, + # 'enabled': True, + # 'order': 26 + # }, + # 'NEUF_FRANCEINFO': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'franceinfo:', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 27, + # 'enabled': True, + # 'order': 27 + # }, + # 'NEUF_GUYSEN': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'i24 news', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 28, + # 'enabled': True, + # 'order': 28 + # }, + # 'NEUF_BFMBUSINESS': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'BFM Business', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 31, + # 'enabled': True, + # 'order': 31 + # }, + # 'NEUF_01NET': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'TECH & CO', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 32, + # 'enabled': True, + # 'order': 32 + # }, + # 'NEUF_RMCSPORT1': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'RMC Sport 1', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 33, + # 'enabled': True, + # 'order': 33 + # }, + # 'NEUF_RMCSPORT2': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'RMC Sport 2', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 34, + # 'enabled': True, + # 'order': 34 + # }, + # 'NEUF_BFMPARIS': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'BFM PARIS ILE-DE-FRANCE', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 39, + # 'enabled': True, + # 'order': 39 + # }, + # 'NEUF_DISCOVERYCHNL': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'Discovery Channel', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 40, + # 'enabled': True, + # 'order': 40 + # }, + # 'NEUF_DISCOVERYSCN': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'Discovery Science', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 41, + # 'enabled': True, + # 'order': 41 + # }, + # 'NEUF_DISCOVERYINVST': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'Discovery Investigation', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 43, + # 'enabled': True, + # 'order': 43 + # }, + # 'NEUF_FRANCE24': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'France 24', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 47, + # 'enabled': True, + # 'order': 47 + # }, + # 'NEUF_EURONEWS_FR': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'Euronews', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 48, + # 'enabled': True, + # 'order': 48 + # }, + # 'NEUF_13EMERUE': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': '13ème rue', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 50, + # 'enabled': True, + # 'order': 50 + # }, + # 'NEUF_SYFY': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'Syfy', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 51, + # 'enabled': True, + # 'order': 51 + # }, + # 'NEUF_EENTERTAINMENT': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'E! Entertainment', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 52, + # 'enabled': True, + # 'order': 52 + # }, + # 'NEUF_MCSMAISON': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'My Cuisine', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 55, + # 'enabled': True, + # 'order': 55 + # }, + # 'NEUF_MTV': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'MTV', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 56, + # 'enabled': True, + # 'order': 56 + # }, + # 'NEUF_MCM': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'MCM', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 57, + # 'enabled': True, + # 'order': 57 + # }, + # 'NEUF_AB1': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'AB1', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 58, + # 'enabled': True, + # 'order': 58 + # }, + # 'NEUF_SERIECLUB': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'SERIE CLUB', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 59, + # 'enabled': True, + # 'order': 59 + # }, + # 'NEUF_GAMEONE': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'Game One', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 60, + # 'enabled': True, + # 'order': 60 + # }, + # 'NEUF_GAMEONE1': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'Game One +1', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 61, + # 'enabled': True, + # 'order': 61 + # }, + # 'NEUF_WARNER_TV_NEXT': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'Warner TV Next', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 62, + # 'enabled': True, + # 'order': 62 + # }, + # 'NEUF_JONE': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'J-One', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 63, + # 'enabled': True, + # 'order': 63 + # }, + # 'NEUF_BET': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'BET', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 64, + # 'enabled': True, + # 'order': 64 + # }, + # 'NEUF_COMEDYCENTRAL': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'Comedy Central', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 65, + # 'enabled': True, + # 'order': 65 + # }, + # 'NEUF_PARPREM': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'Paris Première', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 70, + # 'enabled': True, + # 'order': 70 + # }, + # 'NEUF_TEVA': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'Téva', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 71, + # 'enabled': True, + # 'order': 71 + # }, + # 'NEUF_RTL9': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'RTL9', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 72, + # 'enabled': True, + # 'order': 72 + # }, + # 'NEUF_TVBREIZH': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'TV Breizh', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 73, + # 'enabled': True, + # 'order': 73 + # }, + # 'NEUF_TV5MONDE': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'TV5 Monde', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 74, + # 'enabled': True, + # 'order': 74 + # }, + # 'NEUF_TF1PLUSONE': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'TF1 + 1', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 75, + # 'enabled': True, + # 'order': 75 + # }, + # 'NEUF_TMCPLUSONE': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'TMC + 1', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 76, + # 'enabled': True, + # 'order': 76 + # }, + # 'NEUF_CPLUS_BOX_OFFICE': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'CANAL+ BOX OFFICE', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 83, + # 'enabled': True, + # 'order': 83 + # }, + # 'NEUF_LCPAN': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'LCP-AN 24/24', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 103, + # 'enabled': True, + # 'order': 103 + # }, + # 'NEUF_PUBSENAT': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'Public Sénat', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 104, + # 'enabled': True, + # 'order': 104 + # }, + # 'NEUF_SFRSPORT1': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'RMC Sport Access', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 106, + # 'enabled': True, + # 'order': 106 + # }, + # 'NEUF_CPLUS_LIGUE1': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'CANAL+ Ligue 1 Uber Eats', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 114, + # 'enabled': True, + # 'order': 114 + # }, + # 'NEUF_BEINSPORT1': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'beIN SPORTS 1', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 115, + # 'enabled': True, + # 'order': 115 + # }, + # 'NEUF_BEINSPORT2': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'beIN SPORTS 2', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 116, + # 'enabled': True, + # 'order': 116 + # }, + # 'NEUF_BEINMULTI1': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'beIN SPORTS 3', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 117, + # 'enabled': True, + # 'order': 117 + # }, + # 'NEUF_DAZN1': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'DAZN 1', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 118, + # 'enabled': True, + # 'order': 118 + # }, + # 'NEUF_EQUIDIA': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'Equidia', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 119, + # 'enabled': True, + # 'order': 119 + # }, + # 'NEUF_MGG_TV': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'MGG TV', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 121, + # 'enabled': True, + # 'order': 121 + # }, + # 'NEUF_ABMOTEURS': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'Automoto la chaine', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 125, + # 'enabled': True, + # 'order': 125 + # }, + # 'NEUF_GOLFCHANNEL': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'Golf Channel', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 126, + # 'enabled': True, + # 'order': 126 + # }, + # 'NEUF_SPORT_EN_FRANCE': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'Sport en France', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 129, + # 'enabled': True, + # 'order': 129 + # }, + # 'NEUF_OLPLAY': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'OLPLAY', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 130, + # 'enabled': True, + # 'order': 130 + # }, + # 'NEUF_OCINEMAX': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'OCS Max', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 141, + # 'enabled': True, + # 'order': 141 + # }, + # 'NEUF_OCINEPULP': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'OCS Pulp', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 143, + # 'enabled': True, + # 'order': 143 + # }, + # 'NEUF_OCINEGEANTS': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'OCS Geants', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 144, + # 'enabled': True, + # 'order': 144 + # }, + # 'NEUF_CINE+PREMIER': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'Ciné+ Premier', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 146, + # 'enabled': True, + # 'order': 146 + # }, + # 'NEUF_CINE+FRISSON': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'Ciné+ Frisson', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 147, + # 'enabled': True, + # 'order': 147 + # }, + # 'NEUF_CINE+EMOTION': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'Ciné+ Emotion', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 148, + # 'enabled': True, + # 'order': 148 + # }, + # 'NEUF_CCINEMAFAMIZ': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'Ciné+ Famiz', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 149, + # 'enabled': True, + # 'order': 149 + # }, + # 'NEUF_CCINEMACLUB': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'Ciné+ Club', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 150, + # 'enabled': True, + # 'order': 150 + # }, + # 'NEUF_CCINEMACLASSIC': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'Ciné+ Classic', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 151, + # 'enabled': True, + # 'order': 151 + # }, + # 'NEUF_PARAMOUNT': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'Paramount Channel', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 160, + # 'enabled': True, + # 'order': 160 + # }, + # 'NEUF_PARAMOUNT+1': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'Paramount Channel Décalé', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 161, + # 'enabled': True, + # 'order': 161 + # }, + # 'NEUF_TCM': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'TCM Cinéma', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 162, + # 'enabled': True, + # 'order': 162 + # }, + # 'NEUF_ACTION': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'Action', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 163, + # 'enabled': True, + # 'order': 163 + # }, + # 'NEUF_USHUAIA': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'Ushuaia TV', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 173, + # 'enabled': True, + # 'order': 173 + # }, + # 'NEUF_ESCALES': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'TREK', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 174, + # 'enabled': True, + # 'order': 174 + # }, + # 'NEUF_CRIMEDISCTRICT': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'Crime District', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 175, + # 'enabled': True, + # 'order': 175 + # }, + # 'NEUF_HISTOIRE': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'Histoire', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 177, + # 'enabled': True, + # 'order': 177 + # }, + # 'NEUF_TTELHISTOIRE': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'Toute l\'Histoire', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 178, + # 'enabled': True, + # 'order': 178 + # }, + # 'NEUF_KTO': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'KTO', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 179, + # 'enabled': True, + # 'order': 179 + # }, + # 'NEUF_ANIMAUX': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'Animaux', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 180, + # 'enabled': True, + # 'order': 180 + # }, + # 'NEUF_CHASSEPECHE': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'Chasse et Pêche', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 181, + # 'enabled': True, + # 'order': 181 + # }, + # 'NEUF_ENCYCLOPEDIA': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'Science et Vie TV', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 182, + # 'enabled': True, + # 'order': 182 + # }, + # 'NEUF_LUXETV': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'Luxe TV', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 183, + # 'enabled': True, + # 'order': 183 + # }, + # 'NEUF_FASHIONTV': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'Fashion TV', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 184, + # 'enabled': True, + # 'order': 184 + # }, + # 'NEUF_MENSUP': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'Men\'s Up TV', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 185, + # 'enabled': True, + # 'order': 185 + # }, + # 'NEUF_ASTRO': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'Astrocenter TV', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 186, + # 'enabled': True, + # 'order': 186 + # }, + # 'NEUF_MYZENTV': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'My Zen TV', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 187, + # 'enabled': True, + # 'order': 187 + # }, + # 'NEUF_MUSEUM_TV': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'MUSEUM TV', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 191, + # 'enabled': True, + # 'order': 191 + # }, + # 'NEUF_NICKELODEONJUNIOR': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'Nickelodeon Junior', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 200, + # 'enabled': True, + # 'order': 200 + # }, + # 'NEUF_BOOMERANG': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'Boomerang', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 201, + # 'enabled': True, + # 'order': 201 + # }, + # 'NEUF_BOOMERANG+1': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'Boomerang +1', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 202, + # 'enabled': True, + # 'order': 202 + # }, + # 'NEUF_TIJI': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'TIJI', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 203, + # 'enabled': True, + # 'order': 203 + # }, + # 'NEUF_DREAMWORKS': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'DREAMWORKS', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 204, + # 'enabled': True, + # 'order': 204 + # }, + # 'NEUF_NICKELODEON': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'Nickelodeon', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 205, + # 'enabled': True, + # 'order': 205 + # }, + # 'NEUF_NICKELODEONPLUSONE': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'Nickelodeon+1', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 206, + # 'enabled': True, + # 'order': 206 + # }, + # 'NEUF_CANALJ': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'CANAL J', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 210, + # 'enabled': True, + # 'order': 210 + # }, + # 'NEUF_CARTOONNETWORK': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'Cartoon Network', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 211, + # 'enabled': True, + # 'order': 211 + # }, + # 'NEUF_NICKELODEONTEEN': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'Nickelodeon Teen', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 212, + # 'enabled': True, + # 'order': 212 + # }, + # 'NEUF_CARTOONITO': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'Cartoonito', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 213, + # 'enabled': True, + # 'order': 213 + # }, + # 'NEUF_MANGAS': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'Mangas', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 231, + # 'enabled': True, + # 'order': 231 + # }, + # 'NEUF_LUCKYJACK': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'Lucky Jack', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 234, + # 'enabled': True, + # 'order': 234 + # }, + # 'NEUF_MTVHITS': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'MTV Hits', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 250, + # 'enabled': True, + # 'order': 250 + # }, + # 'NEUF_M6MUSICHITS': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'M6 Music', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 254, + # 'enabled': True, + # 'order': 254 + # }, + # 'NEUF_MCMPOP': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'RFM TV', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 255, + # 'enabled': True, + # 'order': 255 + # }, + # 'NEUF_NRJHITS': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'NRJ Hits', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 256, + # 'enabled': True, + # 'order': 256 + # }, + # 'NEUF_MEZZO': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'Mezzo', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 260, + # 'enabled': True, + # 'order': 260 + # }, + # 'NEUF_MEZZOHD': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'Mezzo Live HD', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 261, + # 'enabled': True, + # 'order': 261 + # }, + # 'NEUF_TELEMELODY': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'MELODY TV', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 262, + # 'enabled': True, + # 'order': 262 + # }, + # 'NEUF_TRACE': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'Trace Urban', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 263, + # 'enabled': True, + # 'order': 263 + # }, + # 'NEUF_TRACETOCA': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'Trace Toca', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 264, + # 'enabled': True, + # 'order': 264 + # }, + # 'NEUF_TRACETROP': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'TRACE CARIBBEAN', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 265, + # 'enabled': True, + # 'order': 265 + # }, + # 'NEUF_TRACEGOSPEL': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'Trace Gospel', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 266, + # 'enabled': True, + # 'order': 266 + # }, + # 'NEUF_BEBLACKTV': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'BBLACK Classik', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 267, + # 'enabled': True, + # 'order': 267 + # }, + # 'NEUF_BBLACKCARIBBEAN': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'BBLACK Caribbean', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 268, + # 'enabled': True, + # 'order': 268 + # }, + # 'NEUF_BBLACKAFRICA': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'BBLACK Africa', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 269, + # 'enabled': True, + # 'order': 269 + # }, + # 'NEUF_MELODYAFRIQUE': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'Melody d\'Afrique', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 272, + # 'enabled': True, + # 'order': 272 + # }, + # 'NEUF_BFMLYON': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'BFM Lyon', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 281, + # 'enabled': True, + # 'order': 281 + # }, + # 'NEUF_BFMGRANDLILLE': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'BFM Grand Lille', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 282, + # 'enabled': True, + # 'order': 282 + # }, + # 'NEUF_BFMGRANDLITTORAL': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'BFM Grand Littoral', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 283, + # 'enabled': True, + # 'order': 283 + # }, + # 'NEUF_BFM_MARSEILLEPROV': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'BFM MARSEILLE PROVENCE', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 284, + # 'enabled': True, + # 'order': 284 + # }, + # 'NEUF_BFM_NICECOTEDAZUR': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'BFM NICE COTE D\'AZUR', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 285, + # 'enabled': True, + # 'order': 285 + # }, + # 'NEUF_BFM_TOULONVAR': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'BFM TOULON VAR', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 286, + # 'enabled': True, + # 'order': 286 + # }, + # 'NEUF_BFM_DICI_ALPESDUSUD': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'BFM DICI ALPES DU SUD', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 287, + # 'enabled': True, + # 'order': 287 + # }, + # 'NEUF_BFM_DICI_HAUTEPROVENCE': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'BFM DICI HAUTE-PROVENCE', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 288, + # 'enabled': True, + # 'order': 288 + # }, + # 'NEUF_BFM_ALSACE': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'BFM ALSACE', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 289, + # 'enabled': True, + # 'order': 289 + # }, + # 'NEUF_BFM_NORMANDIE': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'BFM NORMANDIE', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 290, + # 'enabled': True, + # 'order': 290 + # }, + # 'NEUF_TVSUDCAMCEV': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'vià30', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 295, + # 'enabled': True, + # 'order': 295 + # }, + # 'NEUF_VIA31': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'vià31', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 296, + # 'enabled': True, + # 'order': 296 + # }, + # 'NEUF_TVSUDMONTP': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'vià34', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 297, + # 'enabled': True, + # 'order': 297 + # }, + # 'NEUF_VIA66': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'vià66', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 298, + # 'enabled': True, + # 'order': 298 + # }, + # 'NEUF_BEINMULTI2': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'beIN SPORTS MAX 4', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 300, + # 'enabled': True, + # 'order': 300 + # }, + # 'NEUF_BEINMULTI3': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'beIN SPORTS MAX 5', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 301, + # 'enabled': True, + # 'order': 301 + # }, + # 'NEUF_BEINMULTI4': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'beIN SPORTS MAX 6', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 302, + # 'enabled': True, + # 'order': 302 + # }, + # 'NEUF_BEINMULTI5': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'beIN SPORTS MAX 7', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 303, + # 'enabled': True, + # 'order': 303 + # }, + # 'NEUF_BEINMULTI6': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'beIN SPORTS MAX 8', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 304, + # 'enabled': True, + # 'order': 304 + # }, + # 'NEUF_BEINMULTI7': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'beIN SPORTS MAX 9', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 305, + # 'enabled': True, + # 'order': 305 + # }, + # 'NEUF_BEINMULTI8': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'beIN SPORTS MAX 10', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 306, + # 'enabled': True, + # 'order': 306 + # }, + # 'NEUF_RMCSPORTLIVE3': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'RMC Sport Live 3', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 316, + # 'enabled': True, + # 'order': 316 + # }, + # 'NEUF_RMCSPORTLIVE4': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'RMC Sport Live 4', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 317, + # 'enabled': True, + # 'order': 317 + # }, + # 'NEUF_RMCSPORTLIVE5': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'RMC Sport Live 5', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 318, + # 'enabled': True, + # 'order': 318 + # }, + # 'NEUF_RMCSPORTLIVE6': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'RMC Sport Live 6', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 319, + # 'enabled': True, + # 'order': 319 + # }, + # 'NEUF_RMCSPORTLIVE7': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'RMC Sport Live 7', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 320, + # 'enabled': True, + # 'order': 320 + # }, + # 'NEUF_RMCSPORTLIVE8': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'RMC Sport Live 8', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 321, + # 'enabled': True, + # 'order': 321 + # }, + # 'NEUF_RMCSPORTLIVE9': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'RMC Sport Live 9', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 322, + # 'enabled': True, + # 'order': 322 + # }, + # 'NEUF_RMCSPORTLIVE10': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'RMC Sport Live 10', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 323, + # 'enabled': True, + # 'order': 323 + # }, + # 'NEUF_RMCSPORTLIVE11': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'RMC Sport Live 11', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 324, + # 'enabled': True, + # 'order': 324 + # }, + # 'NEUF_RMCSPORTLIVE12': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'RMC Sport Live 12', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 325, + # 'enabled': True, + # 'order': 325 + # }, + # 'NEUF_RMCSPORTLIVE13': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'RMC Sport Live 13', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 326, + # 'enabled': True, + # 'order': 326 + # }, + # 'NEUF_RMCSPORTLIVE14': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'RMC Sport Live 14', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 327, + # 'enabled': True, + # 'order': 327 + # }, + # 'NEUF_RMCSPORTLIVE15': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'RMC Sport Live 15', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 328, + # 'enabled': True, + # 'order': 328 + # }, + # 'NEUF_RMCSPORTLIVE16': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'RMC Sport Live 16', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 329, + # 'enabled': True, + # 'order': 329 + # }, + # 'NEUF_RMCSPORTLIVE17': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'RMC Sport Live 17', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 330, + # 'enabled': True, + # 'order': 330 + # }, + # 'NEUF_RMCSPORTLIVE18': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'RMC Sport Live 18', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 331, + # 'enabled': True, + # 'order': 331 + # }, + # 'NEUF_FRANCE3_ALP': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'France 3 - Alpes', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 431, + # 'enabled': True, + # 'order': 431 + # }, + # 'NEUF_FRANCE3_ALS': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'France 3 - Alsace', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 432, + # 'enabled': True, + # 'order': 432 + # }, + # 'NEUF_FRANCE3_AQU': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'France 3 - Aquitaine', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 433, + # 'enabled': True, + # 'order': 433 + # }, + # 'NEUF_FRANCE3_AUV': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'France 3 - Auvergne', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 434, + # 'enabled': True, + # 'order': 434 + # }, + # 'NEUF_FRANCE3_BAN': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'France 3 - Basse-Normandie', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 435, + # 'enabled': True, + # 'order': 435 + # }, + # 'NEUF_FRANCE3_BOU': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'France 3 - Bourgogne', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 436, + # 'enabled': True, + # 'order': 436 + # }, + # 'NEUF_FRANCE3_BRE': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'France 3 - Bretagne', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 437, + # 'enabled': True, + # 'order': 437 + # }, + # 'NEUF_FRANCE3_CEN': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'France 3 - Centre', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 438, + # 'enabled': True, + # 'order': 438 + # }, + # 'NEUF_FRANCE3_CHA': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'France 3 - Champagne-Ardenne', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 439, + # 'enabled': True, + # 'order': 439 + # }, + # 'NEUF_FRANCE3_COR': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'France 3 - Corse', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 440, + # 'enabled': True, + # 'order': 440 + # }, + # 'NEUF_FRANCE3_CAZ': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'France 3 - Côte d\'Azur', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 441, + # 'enabled': True, + # 'order': 441 + # }, + # 'NEUF_FRANCE3_FRC': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'France 3 - Franche-Comté', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 442, + # 'enabled': True, + # 'order': 442 + # }, + # 'NEUF_FRANCE3_HTN': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'France 3 - Haute-Normandie', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 443, + # 'enabled': True, + # 'order': 443 + # }, + # 'NEUF_FRANCE3_LAN': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'France 3 - Languedoc', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 444, + # 'enabled': True, + # 'order': 444 + # }, + # 'NEUF_FRANCE3_LIM': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'France 3 - Limousin', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 445, + # 'enabled': True, + # 'order': 445 + # }, + # 'NEUF_FRANCE3_LOR': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'France 3 - Lorraine', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 446, + # 'enabled': True, + # 'order': 446 + # }, + # 'NEUF_FRANCE3_MDP': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'France 3 - Midi-Pyrénées', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 447, + # 'enabled': True, + # 'order': 447 + # }, + # 'NEUF_FRANCE3_NOR': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'France 3 - Nord Pas-de-Calais', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 448, + # 'enabled': True, + # 'order': 448 + # }, + # 'NEUF_FRANCE3_PAR': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'France 3 - Paris Ile-de-France', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 449, + # 'enabled': True, + # 'order': 449 + # }, + # 'NEUF_FRANCE3_PLO': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'France 3 - Pays de Loire', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 450, + # 'enabled': True, + # 'order': 450 + # }, + # 'NEUF_FRANCE3_PIC': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'France 3 - Picardie', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 451, + # 'enabled': True, + # 'order': 451 + # }, + # 'NEUF_FRANCE3_POC': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'France 3 - Poitou-Charentes', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 452, + # 'enabled': True, + # 'order': 452 + # }, + # 'NEUF_FRANCE3_PRA': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'France 3 - Provence Alpes', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 453, + # 'enabled': True, + # 'order': 453 + # }, + # 'NEUF_FRANCE3_RHA': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'France 3 - Rhône-Alpes', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 454, + # 'enabled': True, + # 'order': 454 + # }, + # 'NEUF_FRANCE3NAQTN': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'France 3 Nouvelle Aquitaine', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 455, + # 'enabled': True, + # 'order': 455 + # }, + # 'NEUF_CANAL21': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'Canal 21', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 457, + # 'enabled': True, + # 'order': 457 + # }, + # 'NEUF_20MINUTES_TV': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': '20 Minutes TV', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 461, + # 'enabled': True, + # 'order': 461 + # }, + # 'NEUF_TELEBOCAL': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'Télé Bocal', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 464, + # 'enabled': True, + # 'order': 464 + # }, + # 'NEUF_TVMONTREUIL': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'TELE-VISION MONTREUIL', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 467, + # 'enabled': True, + # 'order': 467 + # }, + # 'NEUF_FIGARO_TV_IDF': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'Figaro TV IDF', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 468, + # 'enabled': True, + # 'order': 468 + # }, + # 'NEUF_TVFIL78': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'TV78', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 469, + # 'enabled': True, + # 'order': 469 + # }, + # 'NEUF_LYON_CAP_TV': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'Lyon Capitale TV', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 476, + # 'enabled': True, + # 'order': 476 + # }, + # 'NEUF_TELEGRENOBLE': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'Télé Grenoble', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 477, + # 'enabled': True, + # 'order': 477 + # }, + # 'NEUF_TL7': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'TL7', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 478, + # 'enabled': True, + # 'order': 478 + # }, + # 'NEUF_TV8MB': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': '8 Mont Blanc', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 480, + # 'enabled': True, + # 'order': 480 + # }, + # 'NEUF_ILTV': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'IL TV', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 481, + # 'enabled': True, + # 'order': 481 + # }, + # 'NEUF_ASTV': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'ASTV', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 482, + # 'enabled': True, + # 'order': 482 + # }, + # 'NEUF_TELEGOHELLE': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'TELEGOHELLE', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 485, + # 'enabled': True, + # 'order': 485 + # }, + # 'NEUF_WEO_PICARDIE': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'Wéo Picardie', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 486, + # 'enabled': True, + # 'order': 486 + # }, + # 'NEUF_WEO_TV_LVDN': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'Wéo TV, La voix du nord', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 487, + # 'enabled': True, + # 'order': 487 + # }, + # 'NEUF_VIA_MATELE': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'Vià MATÉLÉ', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 489, + # 'enabled': True, + # 'order': 489 + # }, + # 'NEUF_7ALIMOGES': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': '7 A LIMOGES', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 490, + # 'enabled': True, + # 'order': 490 + # }, + # 'NEUF_TV7BORDEAUX': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'TV7 Bordeaux', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 491, + # 'enabled': True, + # 'order': 491 + # }, + # 'NEUF_TVPI': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'TVPI', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 492, + # 'enabled': True, + # 'order': 492 + # }, + # 'NEUF_KANALDUDE': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'KANALDUDE', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 496, + # 'enabled': True, + # 'order': 496 + # }, + # 'NEUF_TV2COM': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'TV2COM', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 501, + # 'enabled': True, + # 'order': 501 + # }, + # 'NEUF_NA_TV': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'NA TV', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 504, + # 'enabled': True, + # 'order': 504 + # }, + # 'NEUF_CANAL32': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'Canal 32', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 505, + # 'enabled': True, + # 'order': 505 + # }, + # 'NEUF_MIRABELTV': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'VIA Mirabelle', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 506, + # 'enabled': True, + # 'order': 506 + # }, + # 'NEUF_TVMOSAIK': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'Mosaïk Cristal', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 512, + # 'enabled': True, + # 'order': 512 + # }, + # 'NEUF_TV8MOSELLE': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'TV8 Moselle-Est', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 513, + # 'enabled': True, + # 'order': 513 + # }, + # 'NEUF_VOSGESTV': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'VIA Vosges', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 514, + # 'enabled': True, + # 'order': 514 + # }, + # 'NEUF_MARITIMA': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'Maritima TV', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 520, + # 'enabled': True, + # 'order': 520 + # }, + # 'NEUF_MAURIENNE_TV': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'MAURIENNE TV', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 522, + # 'enabled': True, + # 'order': 522 + # }, + # 'NEUF_ANGERSTV': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'Angers télé', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 523, + # 'enabled': True, + # 'order': 523 + # }, + # 'NEUF_TLNANTES': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'Télénantes', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 524, + # 'enabled': True, + # 'order': 524 + # }, + # 'NEUF_TVVENDEE': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'TV Vendée', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 525, + # 'enabled': True, + # 'order': 525 + # }, + # 'NEUF_LM_TV_SARTHE': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'LMtv Sarthe', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 526, + # 'enabled': True, + # 'order': 526 + # }, + # 'NEUF_TEBEO': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'TEBEO', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 529, + # 'enabled': True, + # 'order': 529 + # }, + # 'NEUF_TVRENNES': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'TV RENNES 35', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 530, + # 'enabled': True, + # 'order': 530 + # }, + # 'NEUF_TVTOURS': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'TV Tours', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 533, + # 'enabled': True, + # 'order': 533 + # }, + # 'NEUF_ZOUKTV': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'ZOUK TV', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 536, + # 'enabled': True, + # 'order': 536 + # }, + # 'NEUF_PAESETV': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'Télé Paese', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 537, + # 'enabled': True, + # 'order': 537 + # }, + # 'NEUF_CNNEUROPE': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'CNN International', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 541, + # 'enabled': True, + # 'order': 541 + # }, + # 'NEUF_BBCNEWS': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'BBC NEWS', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 542, + # 'enabled': True, + # 'order': 542 + # }, + # 'NEUF_FRANCE24ENG': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'France 24 Anglais', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 543, + # 'enabled': True, + # 'order': 543 + # }, + # 'NEUF_CNBCEUR': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'CNBC Europe', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 544, + # 'enabled': True, + # 'order': 544 + # }, + # 'NEUF_BLOOMBERG_PAN': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'Bloomberg PAN-European', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 545, + # 'enabled': True, + # 'order': 545 + # }, + # 'NEUF_ALJAZEERA_EN': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'Al Jazeera English', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 546, + # 'enabled': True, + # 'order': 546 + # }, + # 'NEUF_I24NEWSANGLAIS': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'i24 News Anglais', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 547, + # 'enabled': True, + # 'order': 547 + # }, + # 'NEUF_NHKWORLD': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'NHK WORLD-JAPAN', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 548, + # 'enabled': True, + # 'order': 548 + # }, + # 'NEUF_SKYNEWS': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'Sky News', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 549, + # 'enabled': True, + # 'order': 549 + # }, + # 'NEUF_TGCOM24': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'TGCOM24', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 550, + # 'enabled': True, + # 'order': 550 + # }, + # 'NEUF_I24NEWSARABE': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'i24 News Arabe', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 555, + # 'enabled': True, + # 'order': 555 + # }, + # 'NEUF_FRANCE24ARA': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'France 24 Arabe', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 556, + # 'enabled': True, + # 'order': 556 + # }, + # 'NEUF_MEDI1SAT': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'Medi1TV', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 558, + # 'enabled': True, + # 'order': 558 + # }, + # 'NEUF_ALARABIYA': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'AL ARABIYA', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 559, + # 'enabled': True, + # 'order': 559 + # }, + # 'NEUF_ENNAHAR': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'Ennahar TV', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 561, + # 'enabled': True, + # 'order': 561 + # }, + # 'NEUF_CANAL_11': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'Canal 11', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 568, + # 'enabled': True, + # 'order': 568 + # }, + # 'NEUF_24HTVE': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': '24h', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 574, + # 'enabled': True, + # 'order': 574 + # }, + # 'NEUF_TVEINTL': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'TVE', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 575, + # 'enabled': True, + # 'order': 575 + # }, + # 'NEUF_DWTV': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'DW-TV', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 578, + # 'enabled': True, + # 'order': 578 + # }, + # 'NEUF_N24': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'WELT', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 579, + # 'enabled': True, + # 'order': 579 + # }, + # 'NEUF_RECNEWS': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'Record News', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 586, + # 'enabled': True, + # 'order': 586 + # }, + # 'NEUF_AFRICA24': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'Africa 24', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 595, + # 'enabled': True, + # 'order': 595 + # }, + # 'NEUF_CANAL2': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'Canal2', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 597, + # 'enabled': True, + # 'order': 597 + # }, + # 'NEUF_TVIFICCAO': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'TVI ficcao', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 602, + # 'enabled': True, + # 'order': 602 + # }, + # 'NEUF_PORTOCANAL': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'Porto Canal', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 604, + # 'enabled': True, + # 'order': 604 + # }, + # 'NEUF_LOCALVISAO': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'Local Visao', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 605, + # 'enabled': True, + # 'order': 605 + # }, + # 'NEUF_BENFICATV': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'Benfica TV', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 608, + # 'enabled': True, + # 'order': 608 + # }, + # 'NEUF_ABOLATV': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'A BOLA TV', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 609, + # 'enabled': True, + # 'order': 609 + # }, + # 'NEUF_CANALQ': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'Canal Q', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 622, + # 'enabled': True, + # 'order': 622 + # }, + # 'NEUF_RTPI': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'RTPI', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 624, + # 'enabled': True, + # 'order': 624 + # }, + # 'NEUF_RAI1': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'Rai Uno', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 626, + # 'enabled': True, + # 'order': 626 + # }, + # 'NEUF_RAI_SCUOLA': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'RAI SCUOLA', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 629, + # 'enabled': True, + # 'order': 629 + # }, + # 'NEUF_RAI_STORIA': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'RAI STORIA', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 630, + # 'enabled': True, + # 'order': 630 + # }, + # 'NEUF_REAL_MADRID_TV': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'REAL MADRID TV', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 645, + # 'enabled': True, + # 'order': 645 + # }, + # 'NEUF_STAR_TVE': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'STAR TVE', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 646, + # 'enabled': True, + # 'order': 646 + # }, + # 'NEUF_FLAMENCO': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'ALL FLAMENCO_', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 650, + # 'enabled': True, + # 'order': 650 + # }, + # 'NEUF_GALICIATV': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'TVG EUROPA', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 651, + # 'enabled': True, + # 'order': 651 + # }, + # 'NEUF_ETB_BASQUE': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'etb basque', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 655, + # 'enabled': True, + # 'order': 655 + # }, + # 'NEUF_TELEMADRID': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'TELE MADRID', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 657, + # 'enabled': True, + # 'order': 657 + # }, + # 'NEUF_ANDALUCIA_TV': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'ANDALUCIA TV', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 658, + # 'enabled': True, + # 'order': 658 + # }, + # 'NEUF_BBCENTERTAINMENT': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'BBC Entertainment', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 667, + # 'enabled': True, + # 'order': 667 + # }, + # 'NEUF_PROSIEB': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'ProSieben', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 673, + # 'enabled': True, + # 'order': 673 + # }, + # 'NEUF_NTV': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'N-TV', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 674, + # 'enabled': True, + # 'order': 674 + # }, + # 'NEUF_RTLTV': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'RTL Television', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 675, + # 'enabled': True, + # 'order': 675 + # }, + # 'NEUF_RTL2': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'RTL2', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 676, + # 'enabled': True, + # 'order': 676 + # }, + # 'NEUF_SUPERRTL': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'Super RTL', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 678, + # 'enabled': True, + # 'order': 678 + # }, + # 'NEUF_VOX': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'VOX', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 680, + # 'enabled': True, + # 'order': 680 + # }, + # 'NEUF_KABELEINS': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'KABEL EINS', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 682, + # 'enabled': True, + # 'order': 682 + # }, + # 'NEUF_RTLNITROTV': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'RTL NITRO TV', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 685, + # 'enabled': True, + # 'order': 685 + # }, + # 'NEUF_ARTEALLD': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'Arte Allemand', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 686, + # 'enabled': True, + # 'order': 686 + # }, + # 'NEUF_TVPPOLONIA': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'TVP Polonia', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 700, + # 'enabled': True, + # 'order': 700 + # }, + # 'NEUF_ARMENIA1': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'Armenia 1', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 713, + # 'enabled': True, + # 'order': 713 + # }, + # 'NEUF_CHANNEL1': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'Channel One Russia', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 718, + # 'enabled': True, + # 'order': 718 + # }, + # 'NEUF_RTRPLANETA': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'RTR Planeta', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 731, + # 'enabled': True, + # 'order': 731 + # }, + # 'NEUF_ART_CINEMA': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'ART CINEMA', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 733, + # 'enabled': True, + # 'order': 733 + # }, + # 'NEUF_ART_AFLAM1': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'ART AFLAM 1', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 734, + # 'enabled': True, + # 'order': 734 + # }, + # 'NEUF_ART_AFLAM2': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'ART AFLAM 2', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 735, + # 'enabled': True, + # 'order': 735 + # }, + # 'NEUF_AL_HEKAYAT1': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'AL HEKAYAT 1', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 736, + # 'enabled': True, + # 'order': 736 + # }, + # 'NEUF_AL_HEKAYAT2': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'AL HEKAYAT 2', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 737, + # 'enabled': True, + # 'order': 737 + # }, + # 'NEUF_TVROMANIA': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'TV Romania International', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 738, + # 'enabled': True, + # 'order': 738 + # }, + # 'NEUF_BAHIA_TV': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'Bahia TV', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 739, + # 'enabled': True, + # 'order': 739 + # }, + # 'NEUF_TELE_MAROC': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'Télé Maroc', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 740, + # 'enabled': True, + # 'order': 740 + # }, + # 'NEUF_CANALALGERIE': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'Canal Algérie', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 742, + # 'enabled': True, + # 'order': 742 + # }, + # 'NEUF_BEURTV': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'Beur FM TV', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 746, + # 'enabled': True, + # 'order': 746 + # }, + # 'NEUF_2MMAROC': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': '2M Maroc', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 749, + # 'enabled': True, + # 'order': 749 + # }, + # 'NEUF_ALAOULA': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'Al Aoula', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 750, + # 'enabled': True, + # 'order': 750 + # }, + # 'NEUF_ARRYADIA': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'Arryadia', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 751, + # 'enabled': True, + # 'order': 751 + # }, + # 'NEUF_ASSADISSA': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'Assadissa', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 752, + # 'enabled': True, + # 'order': 752 + # }, + # 'NEUF_WATANIA2': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'Watania 2', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 758, + # 'enabled': True, + # 'order': 758 + # }, + # 'NEUF_TNTUNISIA': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'Tunisia 1', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 759, + # 'enabled': True, + # 'order': 759 + # }, + # 'NEUF_ASHARQ_NEWS': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'Asharq News', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 760, + # 'enabled': True, + # 'order': 760 + # }, + # 'NEUF_BRBRTV': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'Berbère TV', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 765, + # 'enabled': True, + # 'order': 765 + # }, + # 'NEUF_SKYNEWSARABIA': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'Sky News Arabia', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 767, + # 'enabled': True, + # 'order': 767 + # }, + # 'NEUF_CHADA_TV': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'CHADA TV', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 768, + # 'enabled': True, + # 'order': 768 + # }, + # 'NEUF_MBC_PLUS_DRAMA': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'MBC PLUS DRAMA', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 769, + # 'enabled': True, + # 'order': 769 + # }, + # 'NEUF_HELWA_TV': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'HELWA TV', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 770, + # 'enabled': True, + # 'order': 770 + # }, + # 'NEUF_MBC_DRAMA': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'MBC Drama', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 773, + # 'enabled': True, + # 'order': 773 + # }, + # 'NEUF_ECHOROUKNEWS': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'Echorouk News', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 774, + # 'enabled': True, + # 'order': 774 + # }, + # 'NEUF_ELBILADTV': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'El Bilad TV', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 775, + # 'enabled': True, + # 'order': 775 + # }, + # 'NEUF_DIZI': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'Dizi', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 776, + # 'enabled': True, + # 'order': 776 + # }, + # 'NEUF_ALARABY2': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'Alaraby 2', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 777, + # 'enabled': True, + # 'order': 777 + # }, + # 'NEUF_MBC': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'MBC', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 778, + # 'enabled': True, + # 'order': 778 + # }, + # 'NEUF_ROTANACINEPLUS': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'Rotana Cinéma+ FR', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 779, + # 'enabled': True, + # 'order': 779 + # }, + # 'NEUF_MBC5': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'MBC 5', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 780, + # 'enabled': True, + # 'order': 780 + # }, + # 'NEUF_MBC_MASR': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'MBC Masr', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 782, + # 'enabled': True, + # 'order': 782 + # }, + # 'NEUF_ROTANACINE': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'Rotana Cinema', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 783, + # 'enabled': True, + # 'order': 783 + # }, + # 'NEUF_ROTANA_CLASSIC': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'Rotana Classic', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 784, + # 'enabled': True, + # 'order': 784 + # }, + # 'NEUF_NESMA': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'Nessma', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 785, + # 'enabled': True, + # 'order': 785 + # }, + # 'NEUF_DMC': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'DMC', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 786, + # 'enabled': True, + # 'order': 786 + # }, + # 'NEUF_FIX_AND_FOXI': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'Fix and Foxi', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 787, + # 'enabled': True, + # 'order': 787 + # }, + # 'NEUF_CARTHAGE_PLUS': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'Carthage+', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 788, + # 'enabled': True, + # 'order': 788 + # }, + # 'NEUF_DMC_DRAMA': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'DMC Drama', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 789, + # 'enabled': True, + # 'order': 789 + # }, + # 'NEUF_IQRAAINTER': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'Iqraa International', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 790, + # 'enabled': True, + # 'order': 790 + # }, + # 'NEUF_IQRAA': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'Iqraa TV', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 791, + # 'enabled': True, + # 'order': 791 + # }, + # 'NEUF_HOLYQURAN': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'Al Majd Holy Quran', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 792, + # 'enabled': True, + # 'order': 792 + # }, + # 'NEUF_ALMAGH': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'Al Maghribia', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 798, + # 'enabled': True, + # 'order': 798 + # }, + # 'NEUF_ARRABIA': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'Arrabiâ', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 799, + # 'enabled': True, + # 'order': 799 + # }, + # 'NEUF_LBC_SAT': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'LBC Sat', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 801, + # 'enabled': True, + # 'order': 801 + # }, + # 'NEUF_MURRTV': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'Murr TV', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 805, + # 'enabled': True, + # 'order': 805 + # }, + # 'NEUF_ROTANA_M_PLUS': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'Rotana M+', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 806, + # 'enabled': True, + # 'order': 806 + # }, + # 'NEUF_DUBAITV': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'Dubaï TV', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 807, + # 'enabled': True, + # 'order': 807 + # }, + # 'NEUF_ON_TV': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'ON TV', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 809, + # 'enabled': True, + # 'order': 809 + # }, + # 'NEUF_HANNIBAL': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'HANNIBAL TV', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 810, + # 'enabled': True, + # 'order': 810 + # }, + # 'NEUF_ALMASRIYA': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'Al Masriya', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 813, + # 'enabled': True, + # 'order': 813 + # }, + # 'NEUF_ISRAELI': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'The Israeli Network', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 814, + # 'enabled': True, + # 'order': 814 + # }, + # 'NEUF_JORDANSAT': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'Jordan Satellite Channel', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 815, + # 'enabled': True, + # 'order': 815 + # }, + # 'NEUF_EURO_STAR': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'Euro Star', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 818, + # 'enabled': True, + # 'order': 818 + # }, + # 'NEUF_EURO_D': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'Euro D', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 819, + # 'enabled': True, + # 'order': 819 + # }, + # 'NEUF_BEIN_MOVIES': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'BEIN MOVIES', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 821, + # 'enabled': True, + # 'order': 821 + # }, + # 'NEUF_SHOW_MAX': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'SHOW MAX', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 822, + # 'enabled': True, + # 'order': 822 + # }, + # 'NEUF_ATV_AVRUPA': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'ATV Avrupa', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 824, + # 'enabled': True, + # 'order': 824 + # }, + # 'NEUF_KANAL7_AVRUPA': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'KANAL 7 AVRUPA', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 825, + # 'enabled': True, + # 'order': 825 + # }, + # 'NEUF_APLUS': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'A+', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 838, + # 'enabled': True, + # 'order': 838 + # }, + # 'NEUF_ORTB': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'ORTB', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 839, + # 'enabled': True, + # 'order': 839 + # }, + # 'NEUF_NOVELAS': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'NOVELAS', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 840, + # 'enabled': True, + # 'order': 840 + # }, + # 'NEUF_CRTV': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'CRTV', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 841, + # 'enabled': True, + # 'order': 841 + # }, + # 'NEUF_CHERIFLA_TV': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'CHERIFLA TV', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 844, + # 'enabled': True, + # 'order': 844 + # }, + # 'NEUF_ORTC': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'ORTC', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 845, + # 'enabled': True, + # 'order': 845 + # }, + # 'NEUF_TELECONGO': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'TV Congo', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 846, + # 'enabled': True, + # 'order': 846 + # }, + # 'NEUF_MABOKE_TV': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'Maboke TV', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 847, + # 'enabled': True, + # 'order': 847 + # }, + # 'NEUF_RTNC': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'RTNC', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 848, + # 'enabled': True, + # 'order': 848 + # }, + # 'NEUF_NCI': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'NCI', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 849, + # 'enabled': True, + # 'order': 849 + # }, + # 'NEUF_TCI': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'RTI 1', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 851, + # 'enabled': True, + # 'order': 851 + # }, + # 'NEUF_CDIRECT': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'CDIRECT', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 852, + # 'enabled': True, + # 'order': 852 + # }, + # 'NEUF_GABON_1ERE': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'Gabon 1ère', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 853, + # 'enabled': True, + # 'order': 853 + # }, + # 'NEUF_RTG': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'RTG', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 854, + # 'enabled': True, + # 'order': 854 + # }, + # 'NEUF_TVM': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'TVM', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 855, + # 'enabled': True, + # 'order': 855 + # }, + # 'NEUF_ORTM': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'ORTM', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 856, + # 'enabled': True, + # 'order': 856 + # }, + # 'NEUF_NOLLYWOOD': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'Nollywood TV', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 858, + # 'enabled': True, + # 'order': 858 + # }, + # 'NEUF_AFRICABLE': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'Africâble', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 859, + # 'enabled': True, + # 'order': 859 + # }, + # 'NEUF_TRACEAFRICA': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'Trace Africa', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 861, + # 'enabled': True, + # 'order': 861 + # }, + # 'NEUF_VOXAFRICA': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'Vox Africa', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 862, + # 'enabled': True, + # 'order': 862 + # }, + # 'NEUF_2STV': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': '2STV', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 863, + # 'enabled': True, + # 'order': 863 + # }, + # 'NEUF_RTS1': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'RTS 1', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 864, + # 'enabled': True, + # 'order': 864 + # }, + # 'NEUF_TFM': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'TFM', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 866, + # 'enabled': True, + # 'order': 866 + # }, + # 'NEUF_SUNU_YEUF': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'SUNU YEUF', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 867, + # 'enabled': True, + # 'order': 867 + # }, + # 'NEUF_BEIJINGTV': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'Beijing TV', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 878, + # 'enabled': True, + # 'order': 878 + # }, + # 'NEUF_CCTVYULE': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'CCTV YuLe', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 879, + # 'enabled': True, + # 'order': 879 + # }, + # 'NEUF_CCTV4': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'CCTV-4', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 880, + # 'enabled': True, + # 'order': 880 + # }, + # 'NEUF_CMC': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'CMC', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 881, + # 'enabled': True, + # 'order': 881 + # }, + # 'NEUF_HUNANTV': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'Hunan TV', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 882, + # 'enabled': True, + # 'order': 882 + # }, + # 'NEUF_JSBCINT': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'JSBC International', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 883, + # 'enabled': True, + # 'order': 883 + # }, + # 'NEUF_PHOENIXCNE': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'Phoenix CNE', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 884, + # 'enabled': True, + # 'order': 884 + # }, + # 'NEUF_DRAGONTV': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'Dragon TV', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 886, + # 'enabled': True, + # 'order': 886 + # }, + # 'NEUF_XIAMENSTAR': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'Great Wall Elite', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 887, + # 'enabled': True, + # 'order': 887 + # }, + # 'NEUF_ZTVWORLD': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'Zhejiang Star TV', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 888, + # 'enabled': True, + # 'order': 888 + # }, + # 'NEUF_GRT_GBA': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'GRT GBA Satellite TV', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 916, + # 'enabled': True, + # 'order': 916 + # }, + # 'NEUF_CCTVDOC': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'CGTN-Documentary', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 918, + # 'enabled': True, + # 'order': 918 + # }, + # 'NEUF_CCTVF': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'CGTN-Français', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 920, + # 'enabled': True, + # 'order': 920 + # }, + # 'NEUF_NTD': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'NTD', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 921, + # 'enabled': True, + # 'order': 921 + # }, + # 'NEUF_NHK_WP': { + # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'label': 'NHK World Premium', + # 'thumb': 'channels/fr/???.png', + # 'fanart': 'channels/fr/???_fanart.jpg', + # 'xmltv_id': 'C???.api.telerama.fr', + # 'm3u_group': '???', + # 'm3u_order': 938, + # 'enabled': True, + # 'order': 938 + # } +} diff --git a/resources/lib/xmltv.py b/resources/lib/xmltv.py index 2e811eb2c..2fe6a66b5 100644 --- a/resources/lib/xmltv.py +++ b/resources/lib/xmltv.py @@ -484,6 +484,7 @@ def programme_post_treatment_iptvmanager(programme): 'keyword': 'tv_guide_ca_' } } +xmltv_infos['sfrtv_live'] = xmltv_infos['fr_live'] def get_xmltv_url(country_id, date): diff --git a/resources/settings.xml b/resources/settings.xml index ad424cfdf..6f80d8353 100644 --- a/resources/settings.xml +++ b/resources/settings.xml @@ -126,6 +126,9 @@ + + + From d6ec61c636d1fe9fb512e0e8a2fb1bffa630eb95 Mon Sep 17 00:00:00 2001 From: SebMourlhou Date: Sun, 12 Nov 2023 00:16:30 +0100 Subject: [PATCH 02/15] [sfrtv] Add some channels configuration --- resources/lib/skeletons/sfrtv_live.py | 307 +++++++++++++------------- 1 file changed, 153 insertions(+), 154 deletions(-) diff --git a/resources/lib/skeletons/sfrtv_live.py b/resources/lib/skeletons/sfrtv_live.py index 996e8cf0b..8e2affad3 100644 --- a/resources/lib/skeletons/sfrtv_live.py +++ b/resources/lib/skeletons/sfrtv_live.py @@ -162,160 +162,159 @@ 'enabled': True, 'order': 15 }, - # 'NEUF_ITELE': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', - # 'label': 'CNews', - # 'thumb': 'channels/fr/???.png', - # 'fanart': 'channels/fr/???_fanart.jpg', - # 'xmltv_id': 'C???.api.telerama.fr', - # 'm3u_group': '???', - # 'm3u_order': 16, - # 'enabled': True, - # 'order': 16 - # }, - # 'NEUF_VIRGIN17': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', - # 'label': 'CStar', - # 'thumb': 'channels/fr/???.png', - # 'fanart': 'channels/fr/???_fanart.jpg', - # 'xmltv_id': 'C???.api.telerama.fr', - # 'm3u_group': '???', - # 'm3u_order': 17, - # 'enabled': True, - # 'order': 17 - # }, - # 'NEUF_GULLI': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', - # 'label': 'Gulli', - # 'thumb': 'channels/fr/???.png', - # 'fanart': 'channels/fr/???_fanart.jpg', - # 'xmltv_id': 'C???.api.telerama.fr', - # 'm3u_group': '???', - # 'm3u_order': 18, - # 'enabled': True, - # 'order': 18 - # }, - # 'NEUF_HD1': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', - # 'label': 'TF1 Séries-Films', - # 'thumb': 'channels/fr/???.png', - # 'fanart': 'channels/fr/???_fanart.jpg', - # 'xmltv_id': 'C???.api.telerama.fr', - # 'm3u_group': '???', - # 'm3u_order': 20, - # 'enabled': True, - # 'order': 20 - # }, - # 'NEUF_LEQUIPETV': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', - # 'label': 'La chaine l’Équipe', - # 'thumb': 'channels/fr/???.png', - # 'fanart': 'channels/fr/???_fanart.jpg', - # 'xmltv_id': 'C???.api.telerama.fr', - # 'm3u_group': '???', - # 'm3u_order': 21, - # 'enabled': True, - # 'order': 21 - # }, - # 'NEUF_6TER': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', - # 'label': '6ter', - # 'thumb': 'channels/fr/???.png', - # 'fanart': 'channels/fr/???_fanart.jpg', - # 'xmltv_id': 'C???.api.telerama.fr', - # 'm3u_group': '???', - # 'm3u_order': 22, - # 'enabled': True, - # 'order': 22 - # }, - # 'NEUF_NUM23': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', - # 'label': 'RMC STORY', - # 'thumb': 'channels/fr/???.png', - # 'fanart': 'channels/fr/???_fanart.jpg', - # 'xmltv_id': 'C???.api.telerama.fr', - # 'm3u_group': '???', - # 'm3u_order': 23, - # 'enabled': True, - # 'order': 23 - # }, - # 'NEUF_RMCDEC': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', - # 'label': 'RMC Découverte', - # 'thumb': 'channels/fr/???.png', - # 'fanart': 'channels/fr/???_fanart.jpg', - # 'xmltv_id': 'C???.api.telerama.fr', - # 'm3u_group': '???', - # 'm3u_order': 24, - # 'enabled': True, - # 'order': 24 - # }, - # 'NEUF_CHERIE25': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', - # 'label': 'Chérie 25', - # 'thumb': 'channels/fr/???.png', - # 'fanart': 'channels/fr/???_fanart.jpg', - # 'xmltv_id': 'C???.api.telerama.fr', - # 'm3u_group': '???', - # 'm3u_order': 25, - # 'enabled': True, - # 'order': 25 - # }, - # 'NEUF_LCI': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', - # 'label': 'LCI', - # 'thumb': 'channels/fr/???.png', - # 'fanart': 'channels/fr/???_fanart.jpg', - # 'xmltv_id': 'C???.api.telerama.fr', - # 'm3u_group': '???', - # 'm3u_order': 26, - # 'enabled': True, - # 'order': 26 - # }, - # 'NEUF_FRANCEINFO': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', - # 'label': 'franceinfo:', - # 'thumb': 'channels/fr/???.png', - # 'fanart': 'channels/fr/???_fanart.jpg', - # 'xmltv_id': 'C???.api.telerama.fr', - # 'm3u_group': '???', - # 'm3u_order': 27, - # 'enabled': True, - # 'order': 27 - # }, - # 'NEUF_GUYSEN': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', - # 'label': 'i24 news', - # 'thumb': 'channels/fr/???.png', - # 'fanart': 'channels/fr/???_fanart.jpg', - # 'xmltv_id': 'C???.api.telerama.fr', - # 'm3u_group': '???', - # 'm3u_order': 28, - # 'enabled': True, - # 'order': 28 - # }, - # 'NEUF_BFMBUSINESS': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', - # 'label': 'BFM Business', - # 'thumb': 'channels/fr/???.png', - # 'fanart': 'channels/fr/???_fanart.jpg', - # 'xmltv_id': 'C???.api.telerama.fr', - # 'm3u_group': '???', - # 'm3u_order': 31, - # 'enabled': True, - # 'order': 31 - # }, - # 'NEUF_01NET': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', - # 'label': 'TECH & CO', - # 'thumb': 'channels/fr/???.png', - # 'fanart': 'channels/fr/???_fanart.jpg', - # 'xmltv_id': 'C???.api.telerama.fr', - # 'm3u_group': '???', - # 'm3u_order': 32, - # 'enabled': True, - # 'order': 32 - # }, + 'NEUF_ITELE': { + 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + 'label': 'CNews', + 'thumb': 'channels/fr/cnews.png', + 'fanart': 'channels/fr/cnews_fanart.jpg', + 'xmltv_id': 'C226.api.telerama.fr', + 'm3u_group': 'TNT', + 'm3u_order': 16, + 'enabled': True, + 'order': 16 + }, + 'NEUF_VIRGIN17': { + 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + 'label': 'CStar', + 'thumb': 'channels/fr/cstar.png', + 'fanart': 'channels/fr/cstar_fanart.jpg', + 'xmltv_id': 'C458.api.telerama.fr', + 'm3u_group': 'TNT', + 'm3u_order': 17, + 'enabled': True, + 'order': 17 + }, + 'NEUF_GULLI': { + 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + 'label': 'Gulli', + 'thumb': 'channels/fr/gulli.png', + 'fanart': 'channels/fr/gulli_fanart.jpg', + 'xmltv_id': 'C482.api.telerama.fr', + 'm3u_group': 'TNT', + 'm3u_order': 18, + 'enabled': True, + 'order': 18 + }, + 'NEUF_HD1': { + 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + 'label': 'TF1 Séries-Films', + 'thumb': 'channels/fr/tf1seriesfilms.png', + 'fanart': 'channels/fr/tf1seriesfilms_fanart.jpg', + 'xmltv_id': 'C1404.api.telerama.fr', + 'm3u_group': 'TNT', + 'm3u_order': 20, + 'enabled': True, + 'order': 20 + }, + 'NEUF_LEQUIPETV': { + 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + 'label': 'La chaine l’Équipe', + 'thumb': 'channels/fr/lequipe.png', + 'fanart': 'channels/fr/lequipe_fanart.jpg', + 'xmltv_id': 'C1401.api.telerama.fr', + 'm3u_group': 'TNT', + 'm3u_order': 21, + 'enabled': True, + 'order': 21 + }, + 'NEUF_6TER': { + 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + 'label': '6ter', + 'thumb': 'channels/fr/6ter.png', + 'fanart': 'channels/fr/6ter_fanart.jpg', + 'xmltv_id': 'C1403.api.telerama.fr', + 'm3u_group': 'TNT', + 'm3u_order': 22, + 'enabled': True, + 'order': 22 + }, + 'NEUF_NUM23': { + 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + 'label': 'RMC STORY', + 'thumb': 'channels/fr/rmcstory.png', + 'fanart': 'channels/fr/rmcstory_fanart.jpg', + 'xmltv_id': 'C1402.api.telerama.fr', + 'm3u_group': 'TNT', + 'm3u_order': 23, + 'enabled': True, + 'order': 23 + }, + 'NEUF_RMCDEC': { + 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + 'label': 'RMC Découverte', + 'thumb': 'channels/fr/rmcdecouverte.png', + 'fanart': 'channels/fr/rmcdecouverte_fanart.jpg', + 'xmltv_id': 'C1400.api.telerama.fr', + 'm3u_group': 'TNT', + 'm3u_order': 24, + 'enabled': True, + 'order': 24 + }, + 'NEUF_CHERIE25': { + 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + 'label': 'Chérie 25', + 'thumb': 'channels/fr/cherie25.png', + 'fanart': 'channels/fr/cherie25_fanart.jpg', + 'xmltv_id': 'C1399.api.telerama.fr', + 'm3u_group': 'TNT', + 'm3u_order': 25, + 'enabled': True, + 'order': 25 + }, + 'NEUF_LCI': { + 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + 'label': 'LCI', + 'thumb': 'channels/fr/lci.png', + 'fanart': 'channels/fr/lci_fanart.jpg', + 'xmltv_id': 'C112.api.telerama.fr', + 'm3u_group': 'TNT', + 'm3u_order': 26, + 'enabled': True, + 'order': 26 + }, + 'NEUF_FRANCEINFO': { + 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + 'label': 'franceinfo:', + 'thumb': 'channels/fr/franceinfo.png', + 'fanart': 'channels/fr/franceinfo_fanart.jpg', + 'xmltv_id': 'C2111.api.telerama.fr', + 'm3u_group': 'TNT', + 'm3u_order': 27, + 'enabled': True, + 'order': 27 + }, + 'NEUF_GUYSEN': { + 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + 'label': 'i24 news', + 'thumb': 'channels/fr/i24news.png', + 'fanart': 'channels/fr/i24news_fanart.jpg', + 'xmltv_id': 'C781.api.telerama.fr', + 'm3u_group': 'Satellite/FAI', + 'm3u_order': 28, + 'enabled': True, + 'order': 28 + }, + 'NEUF_BFMBUSINESS': { + 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + 'label': 'BFM Business', + 'thumb': 'channels/fr/bfmbusiness.png', + 'fanart': 'channels/fr/bfmbusiness_fanart.jpg', + 'xmltv_id': 'C1073.api.telerama.fr', + 'm3u_group': 'Satellite/FAI', + 'm3u_order': 31, + 'enabled': True, + 'order': 31 + }, + 'NEUF_01NET': { + 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + 'label': 'TECH & CO', + 'thumb': 'channels/fr/01net.png', + 'fanart': 'channels/fr/01net_fanart.jpg', + 'm3u_group': 'Satellite/FAI', + 'm3u_order': 32, + 'enabled': True, + 'order': 32 + }, # 'NEUF_RMCSPORT1': { # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', # 'label': 'RMC Sport 1', From 0133e4abd1a06bb151ebde771987e0666840b2d6 Mon Sep 17 00:00:00 2001 From: SebMourlhou Date: Sun, 12 Nov 2023 09:58:59 +0100 Subject: [PATCH 03/15] [sfrtv] Need kwargs for calls from favorites --- resources/lib/channels/fr/sfrtv.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/resources/lib/channels/fr/sfrtv.py b/resources/lib/channels/fr/sfrtv.py index db1d23569..58e278f7d 100644 --- a/resources/lib/channels/fr/sfrtv.py +++ b/resources/lib/channels/fr/sfrtv.py @@ -57,7 +57,6 @@ def get_sfrtv_user_profile(plugin, token): }).json() -@Route.register def get_token(plugin, with_dialog=True): username = plugin.setting.get_string('sfrtv.login') password = plugin.setting.get_string('sfrtv.password') @@ -236,7 +235,7 @@ def get_categories(plugin, store_id): @Route.register(autosort=False) -def list_categories(plugin, store_id): +def list_categories(plugin, store_id, **kwargs): categories = get_categories(plugin, store_id) for category in categories: @@ -268,7 +267,7 @@ def get_products(plugin, category_id, page): @Route.register(autosort=False) -def list_products(plugin, category_id, page): +def list_products(plugin, category_id, page, **kwargs): # Pagination seems to be blocked at 20 videos (the "page" parameter doesn't change anything), # so let's paginate at 100 videos n_loop = 5 @@ -303,7 +302,7 @@ def get_product_details(plugin, product_id): @Route.register(autosort=False) -def list_product_details(plugin, product_id): +def list_product_details(plugin, product_id, **kwargs): token = get_token(plugin) if not token: yield False @@ -381,7 +380,7 @@ def get_replay_url(plugin, product_id, token): @Resolver.register -def get_replay_stream(plugin, product_id): +def get_replay_stream(plugin, product_id, **kwargs): token = get_token(plugin) if not token: return False From 574bf7886cc137b8f95f1f2c5c6aa4e00be14a35 Mon Sep 17 00:00:00 2001 From: SebMourlhou Date: Mon, 13 Nov 2023 23:25:41 +0100 Subject: [PATCH 04/15] [sfrtv] iptvmanager : simplified folder prefixing --- resources/lib/iptvmanager.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/resources/lib/iptvmanager.py b/resources/lib/iptvmanager.py index 57ab17877..34515646e 100644 --- a/resources/lib/iptvmanager.py +++ b/resources/lib/iptvmanager.py @@ -68,7 +68,7 @@ def get_all_live_tv_channels(): Returns: list: Format: (coutry_order, country_id, country_label, country_infos, [channels]), - Channel format: (channel_order, channel_id, channel_label, channel_infos, lang) + Channel format: (channel_order, channel_id, channel_label, channel_infos, lang, folder_id) """ country_channels = [] live_tv_dict = importlib.import_module('resources.lib.skeletons.live_tv').menu @@ -86,11 +86,8 @@ def get_all_live_tv_channels(): module_name, function_name = channel_infos['list_channels_function'].split(':') module = importlib.import_module(module_name) for ch_infos in getattr(module, function_name)(): - # With a prefixed id to avoid conflicts with the country's "classic" channels folder_id = channel_id - ch_id = folder_id + '.' + ch_infos['id'] - ch_label = get_item_label(ch_infos['id'], ch_infos) - channels.append((ch_infos['order'], ch_id, ch_label, ch_infos, None, folder_id)) + channels.append((ch_infos['order'], ch_infos['id'], ch_infos['label'], ch_infos, None, folder_id)) continue # Check if this channel has multiple language if 'available_languages' in channel_infos: @@ -129,6 +126,8 @@ def select_channels(plugin): for (channel_order, channel_id, channel_label, channel_infos, lang, folder_id) in channels: channel_key = channel_id if not lang else channel_id + ' ' + lang + if folder_id: + channel_key = folder_id + '.' + channel_key if channel_key not in tv_integration_settings['enabled_channels'][country_id]: tv_integration_settings['enabled_channels'][country_id][channel_key] = {'enabled': False} @@ -194,15 +193,14 @@ def send_channels(self): for (country_order, country_id, country_label, country_infos, channels) in country_channels: for (channel_order, channel_id, channel_label, channel_infos, lang, folder_id) in channels: channel_key = channel_id if not lang else channel_id + ' ' + lang + if folder_id: + channel_key = folder_id + '.' + channel_key if not tv_integration_settings['enabled_channels'].get(country_id, {}).get(channel_key, {}).get('enabled', False): continue json_stream = {} json_stream['name'] = channel_label resolver = channel_infos['resolver'].replace(':', '/') - if folder_id: - # clean-up the extra-prefix - channel_id = channel_id.replace(folder_id + '.', '', 1) params = { 'item_id': channel_id } @@ -246,6 +244,8 @@ def send_epg(self): for (country_order, country_id, country_label, country_infos, channels) in country_channels: for (channel_order, channel_id, channel_label, channel_infos, lang, folder_id) in channels: channel_key = channel_id if not lang else channel_id + ' ' + lang + if folder_id: + channel_key = folder_id + '.' + channel_key if not tv_integration_settings['enabled_channels'].get(country_id, {}).get(channel_key, {}).get('enabled', False): continue From d7c7072b384c0f6ad9356d991997cf886540403d Mon Sep 17 00:00:00 2001 From: SebMourlhou Date: Sun, 19 Nov 2023 22:17:49 +0100 Subject: [PATCH 05/15] [sfrtv] Add search feature --- resources/lib/channels/fr/sfrtv.py | 85 +++++++++++++++++++++------- resources/lib/skeletons/fr_replay.py | 2 +- 2 files changed, 64 insertions(+), 23 deletions(-) diff --git a/resources/lib/channels/fr/sfrtv.py b/resources/lib/channels/fr/sfrtv.py index 58e278f7d..42f7a74b7 100644 --- a/resources/lib/channels/fr/sfrtv.py +++ b/resources/lib/channels/fr/sfrtv.py @@ -29,6 +29,7 @@ PRODUCTS_URL = 'https://ws-cdn.tv.sfr.net/gaia-core/rest/api/web/v2/categories/{}/contents' PRODUCT_DETAILS_URL = 'https://ws-cdn.tv.sfr.net/gaia-core/rest/api/web/v1/content/{}/detail' PRODUCT_OPTIONS_URL = 'https://ws-backendtv.sfr.fr/gaia-core/rest/api/web/v3/content/{}/options' +SEARCH_TEXT_URL = 'https://ws-backendtv.sfr.fr/gaia-core/rest/api/web/v2/search/text' CUSTOMDATA_REPLAY = ('description={}&deviceId=byPassARTHIUS&deviceName=Firefox-96.0----Windows&deviceType=PC' '&osName=Windows&osVersion=10&persistent=false&resolution=1600x900&tokenType=castoken' '&tokenSSO={}&type=REPLAY') @@ -198,15 +199,19 @@ def get_stores(plugin, token): @Route.register(autosort=False) -def list_stores(plugin, **kwargs): +def list_programs(plugin, **kwargs): token = get_token(plugin) if not token: yield False return - stores = get_stores(plugin, token) + # Search feature + item = Listitem.search(search) + item_post_treatment(item) + yield item - for store in stores: + # Stores + for store in get_stores(plugin, token): item = Listitem() item.label = store['title'] @@ -249,7 +254,7 @@ def list_categories(plugin, store_id, **kwargs): def get_products(plugin, category_id, page): - resp = urlquick.get(PRODUCTS_URL.format(category_id), + return urlquick.get(PRODUCTS_URL.format(category_id), params={ 'app': 'gen8', 'device': 'browser', @@ -260,10 +265,7 @@ def get_products(plugin, category_id, page): 'Accept': 'application/json', 'Referer': 'https://tv.sfr.fr/', 'User-Agent': USER_AGENT - }, - max_age=900) - products = resp.json() - return products + }).json() @Route.register(autosort=False) @@ -288,11 +290,11 @@ def list_products(plugin, category_id, page, **kwargs): break -def get_product_details(plugin, product_id): +def get_product_details(plugin, product_id, universe='PROVIDER', **kwargs): return urlquick.get(PRODUCT_DETAILS_URL.format(product_id), params={ 'accountTypes': 'LAND', - 'universe': 'PROVIDER' + 'universe': universe }, headers={ 'Accept': 'application/json', @@ -308,7 +310,7 @@ def list_product_details(plugin, product_id, **kwargs): yield False return - product_details = get_product_details(plugin, product_id) + product_details = get_product_details(plugin, product_id, **kwargs) if 'seasons' in product_details: for season in product_details['seasons']: @@ -327,10 +329,12 @@ def build_product_item(plugin, product): item.label = product['title'] - if 'description' in product: + if 'description' in product and product['description']: item.info['plot'] = product['description'] + elif 'shortDescription' in product and product['shortDescription']: + item.info['plot'] = product['shortDescription'] - if 'duration' in product: + if 'duration' in product and product['duration']: item.info['duration'] = product['duration'] if 'seasonNumber' in product and product['seasonNumber']: @@ -340,19 +344,20 @@ def build_product_item(plugin, product): item.info['mediatype'] = 'episode' item.info['episode'] = product['episodeNumber'] - if 'diffusionDate' in product: + if 'diffusionDate' in product and product['diffusionDate']: dt = datetime.fromtimestamp(int(product['diffusionDate'] / 1000)) dt_format = '%d/%m/%Y' item.info.date(dt.strftime(dt_format), dt_format) for image in product.get('images', []): - if image['format'] == '2/3' and not item.art.get('thumb', None): + if image['format'] == '2/3' and not item.art.get('thumb'): item.art['thumb'] = image['url'] elif image['format'] == '16/9': item.art['thumb'] = image['url'] - item.set_callback(list_product_details if product.get('type', '') in ['Serie', 'Season'] else get_replay_stream, - product_id=product['id']) + item.set_callback(list_product_details if product.get('type', '') in ['Serie', 'Season', 'CONTENT'] else get_replay_stream, + product_id=product['id'], + universe=product['universe'] if 'universe' in product else 'PROVIDER') item_post_treatment(item) @@ -373,9 +378,11 @@ def get_replay_url(plugin, product_id, token): 'Referer': 'https://tv.sfr.fr/', 'User-Agent': USER_AGENT }).json() - for stream in product_options[0]['offers'][0]['streams']: - if stream['drm'] == 'WIDEVINE': - return stream['url'] + for option in product_options: + for offer in option['offers']: + for stream in offer['streams']: + if stream['drm'] == 'WIDEVINE': + return stream['url'] return None @@ -403,6 +410,40 @@ def get_replay_stream(plugin, product_id, **kwargs): headers=headers) +def get_products_to_search(plugin, keyword, page=0, size=25, **kwargs): + + search_result = urlquick.get(SEARCH_TEXT_URL, + params={ + 'app': 'gen8', + 'device': 'browser', + 'keyword': keyword, + 'page': page, + 'size': size + }, + headers={ + 'Accept': 'application/json', + 'Referer': 'https://tv.sfr.fr/', + 'User-Agent': USER_AGENT + }).json() + + has_next_page = (search_result['count'] - ((page + 1) * size)) > 0 + + return search_result['content'], page, has_next_page + + +@Route.register(autosort=False) +def search(plugin, search_query, **kwargs): + products, page, has_next_page = get_products_to_search(plugin, search_query, **kwargs) + + for product in products: + yield build_product_item(plugin, product) + + if has_next_page: + yield Listitem.next_page(search_query=search_query, + callback=search, + page=page + 1) + + def get_active_services(plugin, token): services = urlquick.get(SERVICE_URL, params={ @@ -446,7 +487,7 @@ def list_live_channels(plugin=Script): channels_dict = importlib.import_module('resources.lib.skeletons.sfrtv_live').menu for serv in active_services: - channel_infos = channels_dict.get(serv['serviceId'], None) + channel_infos = channels_dict.get(serv['serviceId']) if not channel_infos: channel_infos = { @@ -488,7 +529,7 @@ def list_lives(plugin, item_id, **kwargs): for image in serv.get('images', []): if image.get('type', '') == 'color': - item.art['thumb'] = item.art['landscape'] = image.get('url', None) + item.art['thumb'] = item.art['landscape'] = image.get('url') # Playcount is useless for live streams item.info['playcount'] = 0 diff --git a/resources/lib/skeletons/fr_replay.py b/resources/lib/skeletons/fr_replay.py index c080bf451..a62b17bc5 100644 --- a/resources/lib/skeletons/fr_replay.py +++ b/resources/lib/skeletons/fr_replay.py @@ -384,7 +384,7 @@ "order": 45, }, "sfrtv": { - "route": "/resources/lib/channels/fr/sfrtv:list_stores", + "route": "/resources/lib/channels/fr/sfrtv:list_programs", "label": "SFR TV", "thumb": "channels/fr/sfrtv.png", "enabled": True, From 5027ef117902cf1fd47a316df76010eb8f4f0d1c Mon Sep 17 00:00:00 2001 From: SebMourlhou Date: Mon, 20 Nov 2023 22:34:26 +0100 Subject: [PATCH 06/15] [sfrtv] Avoid random errors (HTTP 500) --- resources/lib/channels/fr/sfrtv.py | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/resources/lib/channels/fr/sfrtv.py b/resources/lib/channels/fr/sfrtv.py index 42f7a74b7..0a438c52a 100644 --- a/resources/lib/channels/fr/sfrtv.py +++ b/resources/lib/channels/fr/sfrtv.py @@ -171,6 +171,10 @@ def get_token(plugin, with_dialog=True): def get_stores(plugin, token): struct_menu = urlquick.get(STRUCT_MENU_URL, params={ + 'accountTypes': 'LAND', + 'infrastructures': 'FTTH', + 'operators': 'sfr', + 'noTracking': 'false', 'app': 'gen8', 'device': 'browser' }, @@ -184,7 +188,11 @@ def get_stores(plugin, token): params={ 'app': 'gen8', 'device': 'browser', - 'token': token + 'token': token, + 'operators': 'sfr', + 'infrastructures': 'FTTH', + 'accountTypes': 'LAND', + 'noTracking': 'false', }, headers={ 'Accept': 'application/json', @@ -229,7 +237,11 @@ def get_categories(plugin, store_id): categories_infos = urlquick.get(CATEGORIES_URL.format(store_id), params={ 'app': 'gen8', - 'device': 'browser' + 'device': 'browser', + 'accountTypes': 'LAND', + 'infrastructures': 'FTTH', + 'operators': 'sfr', + 'noTracking': 'false' }, headers={ 'Accept': 'application/json', @@ -258,6 +270,10 @@ def get_products(plugin, category_id, page): params={ 'app': 'gen8', 'device': 'browser', + 'accountTypes': 'LAND', + 'infrastructures': 'FTTH', + 'operators': 'sfr', + 'noTracking': 'false', 'page': page, 'size': MAX_PRODUCTS }, @@ -294,6 +310,9 @@ def get_product_details(plugin, product_id, universe='PROVIDER', **kwargs): return urlquick.get(PRODUCT_DETAILS_URL.format(product_id), params={ 'accountTypes': 'LAND', + 'infrastructures': 'FTTH', + 'operators': 'sfr', + 'noTracking': 'false', 'universe': universe }, headers={ @@ -371,6 +390,9 @@ def get_replay_url(plugin, product_id, token): 'device': 'browser', 'token': token, 'accountTypes': 'LAND', + 'infrastructures': 'FTTH', + 'operators': 'sfr', + 'noTracking': 'false', 'universe': 'PROVIDER' }, headers={ From 988d7c31a75a2da5592875c29bf655ce08b80e94 Mon Sep 17 00:00:00 2001 From: SebMourlhou Date: Tue, 21 Nov 2023 00:20:55 +0100 Subject: [PATCH 07/15] [sfrtv] Fixed user-agent as the list of connected devices is limited --- resources/lib/channels/fr/sfrtv.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/resources/lib/channels/fr/sfrtv.py b/resources/lib/channels/fr/sfrtv.py index 0a438c52a..6ca4a5f4a 100644 --- a/resources/lib/channels/fr/sfrtv.py +++ b/resources/lib/channels/fr/sfrtv.py @@ -10,12 +10,13 @@ from codequick import Listitem, Resolver, Route, Script from codequick.storage import Cache from kodi_six import xbmcgui -from resources.lib import web_utils, resolver_proxy +from resources.lib import resolver_proxy from resources.lib.menu_utils import item_post_treatment from resources.lib.main import tv_guide_menu CACHE_FILE = os.path.join(Route.get_info("profile"), u".sfrtv_cache.sqlite") -USER_AGENT = web_utils.get_random_ua() +USER_AGENT = ('Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 ' + 'Safari/537.36') # fixed as the list of connected devices is limited TOKEN_MAX_AGE = 840 # 14 minutes to be under the 15 mn token validity limit CONFIG_URL = 'https://tv.sfr.fr/configs/config.json' LOGIN_URL = 'https://www.sfr.fr/cas/login' From 51496f561b04a5d911fd424c91a86f6a4840a360 Mon Sep 17 00:00:00 2001 From: SebMourlhou Date: Tue, 21 Nov 2023 00:22:20 +0100 Subject: [PATCH 08/15] [sfrtv] search : next page at bottom --- resources/lib/channels/fr/sfrtv.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/resources/lib/channels/fr/sfrtv.py b/resources/lib/channels/fr/sfrtv.py index 6ca4a5f4a..7c6a72b88 100644 --- a/resources/lib/channels/fr/sfrtv.py +++ b/resources/lib/channels/fr/sfrtv.py @@ -462,9 +462,11 @@ def search(plugin, search_query, **kwargs): yield build_product_item(plugin, product) if has_next_page: - yield Listitem.next_page(search_query=search_query, - callback=search, - page=page + 1) + item = Listitem.next_page(search_query=search_query, + callback=search, + page=page + 1) + item.property['SpecialSort'] = 'bottom' + yield item def get_active_services(plugin, token): From 07028d681b086456fc558f9345dae0defcfd2dd2 Mon Sep 17 00:00:00 2001 From: SebMourlhou Date: Tue, 21 Nov 2023 22:13:47 +0100 Subject: [PATCH 09/15] [sfrtv] add comments --- resources/lib/channels/fr/sfrtv.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/resources/lib/channels/fr/sfrtv.py b/resources/lib/channels/fr/sfrtv.py index 7c6a72b88..8783b3923 100644 --- a/resources/lib/channels/fr/sfrtv.py +++ b/resources/lib/channels/fr/sfrtv.py @@ -287,7 +287,7 @@ def get_products(plugin, category_id, page): @Route.register(autosort=False) def list_products(plugin, category_id, page, **kwargs): - # Pagination seems to be blocked at 20 videos (the "page" parameter doesn't change anything), + # Pagination seems to be blocked at 20 videos (the "size" parameter doesn't change anything), # so let's paginate at 100 videos n_loop = 5 for x in range(n_loop): @@ -296,7 +296,7 @@ def list_products(plugin, category_id, page, **kwargs): for product in products: yield build_product_item(plugin, product) - if len(products) == MAX_PRODUCTS: + if len(products) == MAX_PRODUCTS: # didn't find "count" or "total" information if x < (n_loop - 1): page += 1 else: From cf18622bb6fd383ec31ab4dcbade01644d1f57cf Mon Sep 17 00:00:00 2001 From: SebMourlhou Date: Sat, 25 Nov 2023 22:53:51 +0100 Subject: [PATCH 10/15] [SFR TV] Fix Flake8 & Pylint warnings --- resources/lib/channels/fr/sfrtv.py | 398 ++++++++++++++++------------- 1 file changed, 219 insertions(+), 179 deletions(-) diff --git a/resources/lib/channels/fr/sfrtv.py b/resources/lib/channels/fr/sfrtv.py index 8783b3923..1506c3d0e 100644 --- a/resources/lib/channels/fr/sfrtv.py +++ b/resources/lib/channels/fr/sfrtv.py @@ -5,8 +5,8 @@ from datetime import datetime import importlib import json -import urlquick import os +import urlquick from codequick import Listitem, Resolver, Route, Script from codequick.storage import Cache from kodi_six import xbmcgui @@ -38,25 +38,29 @@ '&osName=Windows&osVersion=10&persistent=false&resolution=1600x900&tokenType=castoken' '&tokenSSO={}&type=LIVEOTT&accountId={}') MAX_PRODUCTS = 20 +REQUEST_TIMEOUT = 30 +PRODUCT_DETAILS_TYPES = ['Serie', 'Season', 'CONTENT'] def get_sfrtv_config(plugin): return urlquick.get(CONFIG_URL, - headers={ - 'User-Agent': USER_AGENT - }).json() + headers={'User-Agent': USER_AGENT}, + timeout=REQUEST_TIMEOUT).json() def get_sfrtv_user_profile(plugin, token): + params = { + 'token': token + } + headers = { + 'Accept': 'application/json, text/plain, */*', + 'Referer': 'https://tv.sfr.fr/', + 'User-Agent': USER_AGENT + } return urlquick.get(USER_PROFILE_URL, - params={ - 'token': token - }, - headers={ - 'Accept': 'application/json, text/plain, */*', - 'Referer': 'https://tv.sfr.fr/', - 'User-Agent': USER_AGENT - }).json() + params=params, + headers=headers, + timeout=REQUEST_TIMEOUT).json() def get_token(plugin, with_dialog=True): @@ -79,21 +83,24 @@ def get_token(plugin, with_dialog=True): session = urlquick.Session() + params = { + 'client_id': sfrtv_client_id, + 'scope': 'openid', + 'response_type': 'token', + 'redirect_uri': 'https://tv.sfr.fr/' + } + headers = { + 'user-agent': USER_AGENT, + 'authority': 'www.sfr.fr', + 'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', + 'accept-language': 'fr-FR,fr;q=0.9,en-US;q=0.8,en;q=0.7', + 'referer': 'https://tv.sfr.fr/', + } resp = session.get(ACCESS_TOKEN_URL, - params={ - 'client_id': sfrtv_client_id, - 'scope': 'openid', - 'response_type': 'token', - 'redirect_uri': 'https://tv.sfr.fr/' - }, - headers={ - 'user-agent': USER_AGENT, - 'authority': 'www.sfr.fr', - 'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', - 'accept-language': 'fr-FR,fr;q=0.9,en-US;q=0.8,en;q=0.7', - 'referer': 'https://tv.sfr.fr/', - }, - max_age=-1) + params=params, + headers=headers, + max_age=-1, + timeout=REQUEST_TIMEOUT) root = resp.parse() form_elt = root.find(".//form[@name='loginForm']") @@ -102,52 +109,59 @@ def get_token(plugin, with_dialog=True): execution = form_elt.find(".//input[@name='execution']").get('value') event_id = form_elt.find(".//input[@name='_eventId']").get('value') + params = { + 'domain': 'mire-sfr', + 'service': 'https://www.sfr.fr/cas/oidc/callbackAuthorize' + } + headers = { + 'user-agent': USER_AGENT, + 'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', + 'authority': 'www.sfr.fr', + 'accept-language': 'fr-FR,fr;q=0.9,en-US;q=0.8,en;q=0.7', + 'content-type': 'application/x-www-form-urlencoded', + 'origin': 'https://www.sfr.fr', + 'referer': resp.url, + } + data = { + 'lt': lt, + 'execution': execution, + 'lrt': lrt, + '_eventId': event_id, + 'username': username, + 'password': password, + 'remember-me': 'on', + 'identifier': '' + } session.post( LOGIN_URL, - params={ - 'domain': 'mire-sfr', - 'service': 'https://www.sfr.fr/cas/oidc/callbackAuthorize' - }, - headers={ - 'user-agent': USER_AGENT, - 'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', - 'authority': 'www.sfr.fr', - 'accept-language': 'fr-FR,fr;q=0.9,en-US;q=0.8,en;q=0.7', - 'content-type': 'application/x-www-form-urlencoded', - 'origin': 'https://www.sfr.fr', - 'referer': resp.url, - }, - data={ - 'lt': lt, - 'execution': execution, - 'lrt': lrt, - '_eventId': event_id, - 'username': username, - 'password': password, - 'remember-me': 'on', - 'identifier': '' - }, - max_age=-1 + params=params, + headers=headers, + data=data, + max_age=-1, + timeout=REQUEST_TIMEOUT ) + params = { + 'client_id': sfrtv_client_id, + 'scope': 'openid', + 'response_type': 'token', + 'redirect_uri': 'https://tv.sfr.fr/', + 'token': 'true', + 'gateway': 'true' + } + headers = { + 'user-agent': USER_AGENT, + 'authority': 'www.sfr.fr', + 'accept': 'application/json, text/plain, */*', + 'accept-language': 'fr-FR,fr;q=0.9,en-US;q=0.8,en;q=0.7', + 'origin': 'https://tv.sfr.fr', + 'referer': 'https://tv.sfr.fr/', + } resp = session.get(ACCESS_TOKEN_URL, - params={ - 'client_id': sfrtv_client_id, - 'scope': 'openid', - 'response_type': 'token', - 'redirect_uri': 'https://tv.sfr.fr/', - 'token': 'true', - 'gateway': 'true' - }, - headers={ - 'user-agent': USER_AGENT, - 'authority': 'www.sfr.fr', - 'accept': 'application/json, text/plain, */*', - 'accept-language': 'fr-FR,fr;q=0.9,en-US;q=0.8,en;q=0.7', - 'origin': 'https://tv.sfr.fr', - 'referer': 'https://tv.sfr.fr/', - }, - max_age=-1) + params=params, + headers=headers, + max_age=-1, + timeout=REQUEST_TIMEOUT) access_token_b64_bytes = resp.content access_token_b64 = access_token_b64_bytes.decode('ascii') if not access_token_b64: @@ -170,37 +184,43 @@ def get_token(plugin, with_dialog=True): def get_stores(plugin, token): + params = { + 'accountTypes': 'LAND', + 'infrastructures': 'FTTH', + 'operators': 'sfr', + 'noTracking': 'false', + 'app': 'gen8', + 'device': 'browser' + } + headers = { + 'Accept': 'application/json', + 'Referer': 'https://tv.sfr.fr/', + 'User-Agent': USER_AGENT + } struct_menu = urlquick.get(STRUCT_MENU_URL, - params={ - 'accountTypes': 'LAND', - 'infrastructures': 'FTTH', - 'operators': 'sfr', - 'noTracking': 'false', - 'app': 'gen8', - 'device': 'browser' - }, - headers={ - 'Accept': 'application/json', - 'Referer': 'https://tv.sfr.fr/', - 'User-Agent': USER_AGENT - }).json() + params=params, + headers=headers, + timeout=REQUEST_TIMEOUT).json() spot_id = struct_menu['spots'][0]['id'] + params = { + 'app': 'gen8', + 'device': 'browser', + 'token': token, + 'operators': 'sfr', + 'infrastructures': 'FTTH', + 'accountTypes': 'LAND', + 'noTracking': 'false', + } + headers = { + 'Accept': 'application/json', + 'Referer': 'https://tv.sfr.fr/', + 'User-Agent': USER_AGENT + } menu = urlquick.get(MENU_URL.format(spot_id), - params={ - 'app': 'gen8', - 'device': 'browser', - 'token': token, - 'operators': 'sfr', - 'infrastructures': 'FTTH', - 'accountTypes': 'LAND', - 'noTracking': 'false', - }, - headers={ - 'Accept': 'application/json', - 'Referer': 'https://tv.sfr.fr/', - 'User-Agent': USER_AGENT - }, - max_age=TOKEN_MAX_AGE).json() + params=params, + headers=headers, + max_age=TOKEN_MAX_AGE, + timeout=REQUEST_TIMEOUT).json() return list(map(lambda t: {'id': t['action']['actionIds']['storeId'], 'title': t['title'], 'images': t['images']}, @@ -235,20 +255,23 @@ def list_programs(plugin, **kwargs): def get_categories(plugin, store_id): + params = { + 'app': 'gen8', + 'device': 'browser', + 'accountTypes': 'LAND', + 'infrastructures': 'FTTH', + 'operators': 'sfr', + 'noTracking': 'false' + } + headers = { + 'Accept': 'application/json', + 'Referer': 'https://tv.sfr.fr/', + 'User-Agent': USER_AGENT + } categories_infos = urlquick.get(CATEGORIES_URL.format(store_id), - params={ - 'app': 'gen8', - 'device': 'browser', - 'accountTypes': 'LAND', - 'infrastructures': 'FTTH', - 'operators': 'sfr', - 'noTracking': 'false' - }, - headers={ - 'Accept': 'application/json', - 'Referer': 'https://tv.sfr.fr/', - 'User-Agent': USER_AGENT - }).json() + params=params, + headers=headers, + timeout=REQUEST_TIMEOUT).json() return categories_infos['categories'] @@ -267,22 +290,25 @@ def list_categories(plugin, store_id, **kwargs): def get_products(plugin, category_id, page): + params = { + 'app': 'gen8', + 'device': 'browser', + 'accountTypes': 'LAND', + 'infrastructures': 'FTTH', + 'operators': 'sfr', + 'noTracking': 'false', + 'page': page, + 'size': MAX_PRODUCTS + } + headers = { + 'Accept': 'application/json', + 'Referer': 'https://tv.sfr.fr/', + 'User-Agent': USER_AGENT + } return urlquick.get(PRODUCTS_URL.format(category_id), - params={ - 'app': 'gen8', - 'device': 'browser', - 'accountTypes': 'LAND', - 'infrastructures': 'FTTH', - 'operators': 'sfr', - 'noTracking': 'false', - 'page': page, - 'size': MAX_PRODUCTS - }, - headers={ - 'Accept': 'application/json', - 'Referer': 'https://tv.sfr.fr/', - 'User-Agent': USER_AGENT - }).json() + params=params, + headers=headers, + timeout=REQUEST_TIMEOUT).json() @Route.register(autosort=False) @@ -308,19 +334,22 @@ def list_products(plugin, category_id, page, **kwargs): def get_product_details(plugin, product_id, universe='PROVIDER', **kwargs): + params = { + 'accountTypes': 'LAND', + 'infrastructures': 'FTTH', + 'operators': 'sfr', + 'noTracking': 'false', + 'universe': universe + } + headers = { + 'Accept': 'application/json', + 'Referer': 'https://tv.sfr.fr/', + 'User-Agent': USER_AGENT + } return urlquick.get(PRODUCT_DETAILS_URL.format(product_id), - params={ - 'accountTypes': 'LAND', - 'infrastructures': 'FTTH', - 'operators': 'sfr', - 'noTracking': 'false', - 'universe': universe - }, - headers={ - 'Accept': 'application/json', - 'Referer': 'https://tv.sfr.fr/', - 'User-Agent': USER_AGENT - }).json() + params=params, + headers=headers, + timeout=REQUEST_TIMEOUT).json() @Route.register(autosort=False) @@ -375,7 +404,7 @@ def build_product_item(plugin, product): elif image['format'] == '16/9': item.art['thumb'] = image['url'] - item.set_callback(list_product_details if product.get('type', '') in ['Serie', 'Season', 'CONTENT'] else get_replay_stream, + item.set_callback(list_product_details if product.get('type', '') in PRODUCT_DETAILS_TYPES else get_replay_stream, product_id=product['id'], universe=product['universe'] if 'universe' in product else 'PROVIDER') @@ -385,22 +414,25 @@ def build_product_item(plugin, product): def get_replay_url(plugin, product_id, token): + params = { + 'app': 'gen8', + 'device': 'browser', + 'token': token, + 'accountTypes': 'LAND', + 'infrastructures': 'FTTH', + 'operators': 'sfr', + 'noTracking': 'false', + 'universe': 'PROVIDER' + } + headers = { + 'Accept': 'application/json', + 'Referer': 'https://tv.sfr.fr/', + 'User-Agent': USER_AGENT + } product_options = urlquick.get(PRODUCT_OPTIONS_URL.format(product_id), - params={ - 'app': 'gen8', - 'device': 'browser', - 'token': token, - 'accountTypes': 'LAND', - 'infrastructures': 'FTTH', - 'operators': 'sfr', - 'noTracking': 'false', - 'universe': 'PROVIDER' - }, - headers={ - 'Accept': 'application/json', - 'Referer': 'https://tv.sfr.fr/', - 'User-Agent': USER_AGENT - }).json() + params=params, + headers=headers, + timeout=REQUEST_TIMEOUT).json() for option in product_options: for offer in option['offers']: for stream in offer['streams']: @@ -434,20 +466,22 @@ def get_replay_stream(plugin, product_id, **kwargs): def get_products_to_search(plugin, keyword, page=0, size=25, **kwargs): - + params = { + 'app': 'gen8', + 'device': 'browser', + 'keyword': keyword, + 'page': page, + 'size': size + } + headers = { + 'Accept': 'application/json', + 'Referer': 'https://tv.sfr.fr/', + 'User-Agent': USER_AGENT + } search_result = urlquick.get(SEARCH_TEXT_URL, - params={ - 'app': 'gen8', - 'device': 'browser', - 'keyword': keyword, - 'page': page, - 'size': size - }, - headers={ - 'Accept': 'application/json', - 'Referer': 'https://tv.sfr.fr/', - 'User-Agent': USER_AGENT - }).json() + params=params, + headers=headers, + timeout=REQUEST_TIMEOUT).json() has_next_page = (search_result['count'] - ((page + 1) * size)) > 0 @@ -470,21 +504,24 @@ def search(plugin, search_query, **kwargs): def get_active_services(plugin, token): + params = { + 'app': 'gen8', + 'device': 'browser', + 'token': token + } + headers = { + 'User-Agent': USER_AGENT, + 'Accept': 'application/json', + 'Accept-language': 'fr-FR,fr;q=0.9,en-US;q=0.8,en;q=0.7', + 'Origin': 'https://tv.sfr.fr', + 'Referer': 'https://tv.sfr.fr/', + 'Connection': 'keep-alive' + } services = urlquick.get(SERVICE_URL, - params={ - 'app': 'gen8', - 'device': 'browser', - 'token': token - }, - headers={ - 'User-Agent': USER_AGENT, - 'Accept': 'application/json', - 'Accept-language': 'fr-FR,fr;q=0.9,en-US;q=0.8,en;q=0.7', - 'Origin': 'https://tv.sfr.fr', - 'Referer': 'https://tv.sfr.fr/', - 'Connection': 'keep-alive' - }, - max_age=TOKEN_MAX_AGE).json() + params=params, + headers=headers, + max_age=TOKEN_MAX_AGE, + timeout=REQUEST_TIMEOUT).json() active_services = list(filter(lambda c: c['access'], services)) return active_services @@ -573,7 +610,10 @@ def get_live_url(plugin, service_id, token): for stream in serv['streams']: if stream['drm'] == 'WIDEVINE': # Workaround for IA bug : https://github.com/xbmc/inputstream.adaptive/issues/804 - response = urlquick.get(stream['url'], headers={'User-Agent': USER_AGENT}, max_age=-1) + response = urlquick.get(stream['url'], + headers={'User-Agent': USER_AGENT}, + max_age=-1, + timeout=REQUEST_TIMEOUT) live_url = response.xml().find('{urn:mpeg:dash:schema:mpd:2011}Location').text return live_url return None From a6a4198c58f08046e02ce60588b9c2b858df089c Mon Sep 17 00:00:00 2001 From: SebMourlhou Date: Fri, 1 Dec 2023 22:23:14 +0100 Subject: [PATCH 11/15] [SFR TV] Move services to a new menu "Internet service providers" --- .../resource.language.en_gb/strings.po | 4 + .../resource.language.fr_fr/strings.po | 4 + .../resource.language.nl_nl/strings.po | 4 + resources/lib/iptvmanager.py | 113 +-- resources/lib/providers/__init__.py | 0 .../lib/{channels/fr => providers}/sfrtv.py | 53 +- resources/lib/skeletons/fr_live.py | 8 - resources/lib/skeletons/fr_replay.py | 7 - resources/lib/skeletons/providers.py | 21 + resources/lib/skeletons/root.py | 9 +- resources/lib/skeletons/sfrtv_live.py | 758 +++++++++--------- resources/settings.xml | 8 +- 12 files changed, 528 insertions(+), 461 deletions(-) create mode 100644 resources/lib/providers/__init__.py rename resources/lib/{channels/fr => providers}/sfrtv.py (95%) create mode 100644 resources/lib/skeletons/providers.py diff --git a/resources/language/resource.language.en_gb/strings.po b/resources/language/resource.language.en_gb/strings.po index 92e1a7804..d1b474206 100644 --- a/resources/language/resource.language.en_gb/strings.po +++ b/resources/language/resource.language.en_gb/strings.po @@ -118,6 +118,10 @@ msgctxt "#30033" msgid "Favourites" msgstr "" +msgctxt "#30034" +msgid "Internet service providers" +msgstr "" + # Countries (from 30050 to 30079) diff --git a/resources/language/resource.language.fr_fr/strings.po b/resources/language/resource.language.fr_fr/strings.po index 0a68c81d0..55279babf 100644 --- a/resources/language/resource.language.fr_fr/strings.po +++ b/resources/language/resource.language.fr_fr/strings.po @@ -118,6 +118,10 @@ msgctxt "#30033" msgid "Favourites" msgstr "Favoris" +msgctxt "#30034" +msgid "Internet service providers" +msgstr "Fournisseurs d'accès internet" + # Countries (from 30050 to 30079) diff --git a/resources/language/resource.language.nl_nl/strings.po b/resources/language/resource.language.nl_nl/strings.po index c7f267f79..4f5349b27 100644 --- a/resources/language/resource.language.nl_nl/strings.po +++ b/resources/language/resource.language.nl_nl/strings.po @@ -118,6 +118,10 @@ msgctxt "#30033" msgid "Favourites" msgstr "Favorieten" +msgctxt "#30034" +msgid "Internet service providers" +msgstr "Internetproviders" + # Countries (from 30050 to 30079) diff --git a/resources/lib/iptvmanager.py b/resources/lib/iptvmanager.py index 34515646e..983de0d43 100644 --- a/resources/lib/iptvmanager.py +++ b/resources/lib/iptvmanager.py @@ -64,12 +64,15 @@ def save_tv_integration_settings(j): # Settings callback functions def get_all_live_tv_channels(): - """Explore each live_tv skeleton files to retrieve all sorted live tv channels + """Explore each live_tv skeleton files to retrieve all sorted live tv channels. + The live channels of each internet provider are then added to the list. Returns: - list: Format: (coutry_order, country_id, country_label, country_infos, [channels]), - Channel format: (channel_order, channel_id, channel_label, channel_infos, lang, folder_id) + list: Format: (group_id, group_label, group_infos, [channels]), + Channel format: (channel_order, channel_id, channel_label, channel_infos, lang) """ + + # Live channels of each country country_channels = [] live_tv_dict = importlib.import_module('resources.lib.skeletons.live_tv').menu for country_id, country_infos in live_tv_dict.items(): @@ -79,26 +82,36 @@ def get_all_live_tv_channels(): # If this channel is disabled --> ignore this channel if not channel_infos.get('enabled', False): continue - # If this channel is a folder (e.g. multi live) + # If this channel is a folder (e.g. multi live) --> ignore this channel if 'resolver' not in channel_infos: - # If a function is defined to retrieve a dynamic list of channels for that folder - if 'list_channels_function' in channel_infos: - module_name, function_name = channel_infos['list_channels_function'].split(':') - module = importlib.import_module(module_name) - for ch_infos in getattr(module, function_name)(): - folder_id = channel_id - channels.append((ch_infos['order'], ch_infos['id'], ch_infos['label'], ch_infos, None, folder_id)) continue # Check if this channel has multiple language if 'available_languages' in channel_infos: for lang in channel_infos['available_languages']: label = '{} ({})'.format(get_item_label(channel_id, channel_infos, append_selected_lang=False), lang) - channels.append((channel_infos['order'], channel_id, label, channel_infos, lang, None)) + channels.append((channel_infos['order'], channel_id, label, channel_infos, lang)) else: - channels.append((channel_infos['order'], channel_id, get_item_label(channel_id, channel_infos), channel_infos, None, None)) + channels.append((channel_infos['order'], channel_id, get_item_label(channel_id, channel_infos), channel_infos, None)) channels = sorted(channels, key=lambda x: x[0]) - country_channels.append((country_infos['order'], country_id, get_item_label(country_id, country_infos), country_infos, channels)) - return sorted(country_channels, key=lambda x: x[2]) + country_channels.append((country_id, get_item_label(country_id, country_infos), country_infos, channels)) + group_channels = sorted(country_channels, key=lambda x: x[1]) + + # Live channels of each internet provider + provider_channels = [] + providers_dict = importlib.import_module('resources.lib.skeletons.providers').menu + for provider_id, provider_infos in providers_dict.items(): + channels = [] + # If a function is defined to retrieve a dynamic list of channels for that folder + if 'list_channels_function' in provider_infos: + module_name, function_name = provider_infos['list_channels_function'].split(':') + module = importlib.import_module(module_name) + for ch_infos in getattr(module, function_name)(): + channels.append((ch_infos['order'], ch_infos['id'], ch_infos['label'], ch_infos, None)) + channels = sorted(channels, key=lambda x: x[0]) + provider_channels.append((provider_id + '_live', get_item_label(provider_id, provider_infos), provider_infos, channels)) + group_channels.extend(sorted(provider_channels, key=lambda x: x[1])) + + return group_channels @Script.register @@ -110,7 +123,7 @@ def select_channels(plugin): """ # Grab all live TV channels - country_channels = get_all_live_tv_channels() + group_channels = get_all_live_tv_channels() # Grab current user settings tv_integration_settings = get_tv_integration_settings() @@ -120,25 +133,23 @@ def select_channels(plugin): preselect = [] selected_channels_map = [] cnt = 0 - for (country_order, country_id, country_label, country_infos, channels) in country_channels: - if country_id not in tv_integration_settings['enabled_channels']: - tv_integration_settings['enabled_channels'][country_id] = {} + for (group_id, group_label, group_infos, channels) in group_channels: + if group_id not in tv_integration_settings['enabled_channels']: + tv_integration_settings['enabled_channels'][group_id] = {} - for (channel_order, channel_id, channel_label, channel_infos, lang, folder_id) in channels: + for (channel_order, channel_id, channel_label, channel_infos, lang) in channels: channel_key = channel_id if not lang else channel_id + ' ' + lang - if folder_id: - channel_key = folder_id + '.' + channel_key - if channel_key not in tv_integration_settings['enabled_channels'][country_id]: - tv_integration_settings['enabled_channels'][country_id][channel_key] = {'enabled': False} + if channel_key not in tv_integration_settings['enabled_channels'][group_id]: + tv_integration_settings['enabled_channels'][group_id][channel_key] = {'enabled': False} - label = country_label + ' - ' + (folder_id + ' - ' if folder_id else '') + channel_label + label = group_label + ' - ' + channel_label options.append(label) - selected_channels_map.append((country_id, channel_key)) - if tv_integration_settings['enabled_channels'][country_id][channel_key]['enabled']: + selected_channels_map.append((group_id, channel_key)) + if tv_integration_settings['enabled_channels'][group_id][channel_key]['enabled']: preselect.append(cnt) cnt += 1 - # Show mulit-select dialog + # Show multi-select dialog dialog = xbmcgui.Dialog() selected_channels = dialog.multiselect(Script.localize(30277), options, preselect=preselect) @@ -146,14 +157,14 @@ def select_channels(plugin): return # By default, disable all channels in the setting file - for country_id in tv_integration_settings['enabled_channels'].keys(): - for channel_key in tv_integration_settings['enabled_channels'][country_id].keys(): - tv_integration_settings['enabled_channels'][country_id][channel_key]['enabled'] = False + for group_id in tv_integration_settings['enabled_channels'].keys(): + for channel_key in tv_integration_settings['enabled_channels'][group_id].keys(): + tv_integration_settings['enabled_channels'][group_id][channel_key]['enabled'] = False # Apply user selection and save settings for selected_channel in selected_channels: - (country_id, channel_key) = selected_channels_map[selected_channel] - tv_integration_settings['enabled_channels'][country_id][channel_key]['enabled'] = True + (group_id, channel_key) = selected_channels_map[selected_channel] + tv_integration_settings['enabled_channels'][group_id][channel_key]['enabled'] = True save_tv_integration_settings(tv_integration_settings) @@ -185,17 +196,15 @@ def send_channels(self): channels_list = [] # Grab all live TV channels - country_channels = get_all_live_tv_channels() + group_channels = get_all_live_tv_channels() # Grab current user settings tv_integration_settings = get_tv_integration_settings() - for (country_order, country_id, country_label, country_infos, channels) in country_channels: - for (channel_order, channel_id, channel_label, channel_infos, lang, folder_id) in channels: + for (group_id, group_label, group_infos, channels) in group_channels: + for (channel_order, channel_id, channel_label, channel_infos, lang) in channels: channel_key = channel_id if not lang else channel_id + ' ' + lang - if folder_id: - channel_key = folder_id + '.' + channel_key - if not tv_integration_settings['enabled_channels'].get(country_id, {}).get(channel_key, {}).get('enabled', False): + if not tv_integration_settings['enabled_channels'].get(group_id, {}).get(channel_key, {}).get('enabled', False): continue json_stream = {} @@ -231,30 +240,28 @@ def send_epg(self): epg_channels = {} # Grab all live TV channels - country_channels = get_all_live_tv_channels() + group_channels = get_all_live_tv_channels() # Grab current user settings tv_integration_settings = get_tv_integration_settings() - country_tv_guides = {} + group_tv_guides = {} xmltv_ids_to_keep = [] - # Ierate over each country and enabled channels to grab needed programmes - for (country_order, country_id, country_label, country_infos, channels) in country_channels: - for (channel_order, channel_id, channel_label, channel_infos, lang, folder_id) in channels: + # Iterate over each group and enabled channels to grab needed programmes + for (group_id, group_label, group_infos, channels) in group_channels: + for (channel_order, channel_id, channel_label, channel_infos, lang) in channels: channel_key = channel_id if not lang else channel_id + ' ' + lang - if folder_id: - channel_key = folder_id + '.' + channel_key - if not tv_integration_settings['enabled_channels'].get(country_id, {}).get(channel_key, {}).get('enabled', False): + if not tv_integration_settings['enabled_channels'].get(group_id, {}).get(channel_key, {}).get('enabled', False): continue - # Check if we have programmes for this country - if country_id not in country_tv_guides: + # Check if we have programmes for this group + if group_id not in group_tv_guides: programmes = [] for day_delta in range(0, 4): - programmes.extend(grab_programmes(country_id, day_delta)) - country_tv_guides[country_id] = programmes + programmes.extend(grab_programmes(group_id, day_delta)) + group_tv_guides[group_id] = programmes # Get the correct xmltv id if lang: @@ -264,8 +271,8 @@ def send_epg(self): if xmltv_id: xmltv_ids_to_keep.append(xmltv_id) - # Send all programmes of enables channels - for country_id, programmes in country_tv_guides.items(): + # Send all programmes of enabled channels + for group_id, programmes in group_tv_guides.items(): for p in programmes: if p.get('channel') not in xmltv_ids_to_keep: continue diff --git a/resources/lib/providers/__init__.py b/resources/lib/providers/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/resources/lib/channels/fr/sfrtv.py b/resources/lib/providers/sfrtv.py similarity index 95% rename from resources/lib/channels/fr/sfrtv.py rename to resources/lib/providers/sfrtv.py index 1506c3d0e..236b3735a 100644 --- a/resources/lib/channels/fr/sfrtv.py +++ b/resources/lib/providers/sfrtv.py @@ -11,6 +11,7 @@ from codequick.storage import Cache from kodi_six import xbmcgui from resources.lib import resolver_proxy +from resources.lib.addon_utils import get_item_media_path from resources.lib.menu_utils import item_post_treatment from resources.lib.main import tv_guide_menu @@ -63,13 +64,22 @@ def get_sfrtv_user_profile(plugin, token): timeout=REQUEST_TIMEOUT).json() -def get_token(plugin, with_dialog=True): +def get_credentials_from_config(plugin, with_dialog): username = plugin.setting.get_string('sfrtv.login') password = plugin.setting.get_string('sfrtv.password') + if not username or not password: if with_dialog: xbmcgui.Dialog().ok(plugin.localize(30600), plugin.localize(30604) % ('SFR TV', 'https://tv.sfr.fr')) + return None, None + + return username, password + + +def get_token(plugin, with_dialog=True): + username, password = get_credentials_from_config(plugin, with_dialog) + if not username or not password: return None # Unable to use urlquick cache due to authentication redirects @@ -183,6 +193,35 @@ def get_token(plugin, with_dialog=True): return token +@Route.register(autosort=False) +def provider_root(plugin, **kwargs): + username, password = get_credentials_from_config(plugin, True) + if not username or not password: + yield False + return + + # Live TV + item = Listitem() + item.label = plugin.localize(30030) + item.art['thumb'] = get_item_media_path('live_tv.png') + item.set_callback(list_lives) + item_post_treatment(item) + yield item + + # Replay + item = Listitem() + item.label = 'Replay' + item.art['thumb'] = get_item_media_path('replay.png') + item.set_callback(list_stores) + item_post_treatment(item) + yield item + + # Search feature + item = Listitem.search(search) + item_post_treatment(item) + yield item + + def get_stores(plugin, token): params = { 'accountTypes': 'LAND', @@ -228,18 +267,12 @@ def get_stores(plugin, token): @Route.register(autosort=False) -def list_programs(plugin, **kwargs): +def list_stores(plugin, **kwargs): token = get_token(plugin) if not token: yield False return - # Search feature - item = Listitem.search(search) - item_post_treatment(item) - yield item - - # Stores for store in get_stores(plugin, token): item = Listitem() item.label = store['title'] @@ -553,7 +586,7 @@ def list_live_channels(plugin=Script): if not channel_infos: channel_infos = { - 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', 'label': serv['name'], 'enabled': True, 'order': serv['zappingId'] @@ -567,7 +600,7 @@ def list_live_channels(plugin=Script): @Route.register -def list_lives(plugin, item_id, **kwargs): +def list_lives(plugin, **kwargs): token = get_token(plugin) if not token: yield False diff --git a/resources/lib/skeletons/fr_live.py b/resources/lib/skeletons/fr_live.py index 20b572ced..173371f46 100644 --- a/resources/lib/skeletons/fr_live.py +++ b/resources/lib/skeletons/fr_live.py @@ -857,13 +857,5 @@ }, 'enabled': True, 'order': 81 - }, - 'sfrtv': { - 'route': '/resources/lib/channels/fr/sfrtv:list_lives', - 'list_channels_function': 'resources.lib.channels.fr.sfrtv:list_live_channels', - 'label': 'SFR TV', - 'thumb': 'channels/fr/sfrtv.png', - 'enabled': True, - 'order': 82 } } diff --git a/resources/lib/skeletons/fr_replay.py b/resources/lib/skeletons/fr_replay.py index a62b17bc5..9aaff0158 100644 --- a/resources/lib/skeletons/fr_replay.py +++ b/resources/lib/skeletons/fr_replay.py @@ -382,12 +382,5 @@ "fanart": "channels/fr/jone_fanart.jpg", "enabled": True, "order": 45, - }, - "sfrtv": { - "route": "/resources/lib/channels/fr/sfrtv:list_programs", - "label": "SFR TV", - "thumb": "channels/fr/sfrtv.png", - "enabled": True, - "order": 46, } } diff --git a/resources/lib/skeletons/providers.py b/resources/lib/skeletons/providers.py new file mode 100644 index 000000000..35fec555c --- /dev/null +++ b/resources/lib/skeletons/providers.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- +# Copyright: (c) 2016, SylvainCecchetto +# GNU General Public License v2.0+ (see LICENSE.txt or https://www.gnu.org/licenses/gpl-2.0.txt) + +# This file is part of Catch-up TV & More + +from __future__ import unicode_literals + +root = 'root' + +menu = { + 'sfrtv': { + 'route': '/resources/lib/providers/sfrtv:provider_root', + 'list_channels_function': 'resources.lib.providers.sfrtv:list_live_channels', + 'label': 'SFR TV', + 'thumb': 'providers/sfrtv.png', + 'fanart': 'providers/sfrtv_fanart.jpg', + 'enabled': True, + 'order': 1 + } +} diff --git a/resources/lib/skeletons/root.py b/resources/lib/skeletons/root.py index a3f2fde3f..401f5d800 100644 --- a/resources/lib/skeletons/root.py +++ b/resources/lib/skeletons/root.py @@ -38,11 +38,18 @@ 'enabled': True, 'order': 3 }, + 'providers': { + 'route': '/resources/lib/main:generic_menu', + 'label': 30034, + 'thumb': 'providers.png', + 'enabled': True, + 'order': 4 + }, 'favourites': { 'route': '/resources/lib/main:favourites', 'label': 30033, 'thumb': 'favourites.png', 'enabled': True, - 'order': 4 + 'order': 5 } } diff --git a/resources/lib/skeletons/sfrtv_live.py b/resources/lib/skeletons/sfrtv_live.py index 8e2affad3..7e646a8b4 100644 --- a/resources/lib/skeletons/sfrtv_live.py +++ b/resources/lib/skeletons/sfrtv_live.py @@ -9,7 +9,7 @@ menu = { 'NEUF_TF1': { - 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', 'label': 'TF1', 'thumb': 'channels/fr/tf1.png', 'fanart': 'channels/fr/tf1_fanart.jpg', @@ -20,7 +20,7 @@ 'order': 1 }, 'NEUF_FRANCE2': { - 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', 'label': 'France 2', 'thumb': 'channels/fr/france2.png', 'fanart': 'channels/fr/france2_fanart.jpg', @@ -31,7 +31,7 @@ 'order': 2 }, 'NEUF_FRANCE3': { - 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', 'label': 'France 3', 'thumb': 'channels/fr/france3.png', 'fanart': 'channels/fr/france3_fanart.jpg', @@ -42,7 +42,7 @@ 'order': 3 }, 'NEUF_FRANCE5': { - 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', 'label': 'France 5', 'thumb': 'channels/fr/france5.png', 'fanart': 'channels/fr/france5_fanart.jpg', @@ -53,7 +53,7 @@ 'order': 5 }, 'NEUF_M6': { - 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', 'label': 'M6', 'thumb': 'channels/fr/m6.png', 'fanart': 'channels/fr/m6_fanart.jpg', @@ -64,7 +64,7 @@ 'order': 6 }, 'NEUF_ARTE': { - 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', 'label': 'Arte', 'thumb': 'channels/fr/arte.png', 'fanart': 'channels/fr/arte_fanart.jpg', @@ -75,7 +75,7 @@ 'order': 7 }, 'NEUF_DIRECT8': { - 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', 'label': 'C8', 'thumb': 'channels/fr/c8.png', 'fanart': 'channels/fr/c8_fanart.jpg', @@ -86,7 +86,7 @@ 'order': 8 }, 'NEUF_W9': { - 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', 'label': 'W9', 'thumb': 'channels/fr/w9.png', 'fanart': 'channels/fr/w9_fanart.jpg', @@ -97,7 +97,7 @@ 'order': 9 }, 'NEUF_TMC': { - 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', 'label': 'TMC', 'thumb': 'channels/fr/tmc.png', 'fanart': 'channels/fr/tmc_fanart.jpg', @@ -108,7 +108,7 @@ 'order': 10 }, 'NEUF_NT1': { - 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', 'label': 'TFX', 'thumb': 'channels/fr/tfx.png', 'fanart': 'channels/fr/tfx_fanart.jpg', @@ -119,7 +119,7 @@ 'order': 11 }, 'NEUF_NRJ12': { - 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', 'label': 'NRJ12', 'thumb': 'channels/fr/nrj12.png', 'fanart': 'channels/fr/nrj12_fanart.jpg', @@ -130,7 +130,7 @@ 'order': 12 }, 'NEUF_LCP': { - 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', 'label': 'LCP', 'thumb': 'channels/fr/lcp.png', 'fanart': 'channels/fr/lcp_fanart.jpg', @@ -141,7 +141,7 @@ 'order': 13 }, 'NEUF_FRANCE4': { - 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', 'label': 'France 4', 'thumb': 'channels/fr/france4.png', 'fanart': 'channels/fr/france4_fanart.jpg', @@ -152,7 +152,7 @@ 'order': 14 }, 'NEUF_BFMTV': { - 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', 'label': 'BFM TV', 'thumb': 'channels/fr/bfmtv.png', 'fanart': 'channels/fr/bfmtv_fanart.jpg', @@ -163,7 +163,7 @@ 'order': 15 }, 'NEUF_ITELE': { - 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', 'label': 'CNews', 'thumb': 'channels/fr/cnews.png', 'fanart': 'channels/fr/cnews_fanart.jpg', @@ -174,7 +174,7 @@ 'order': 16 }, 'NEUF_VIRGIN17': { - 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', 'label': 'CStar', 'thumb': 'channels/fr/cstar.png', 'fanart': 'channels/fr/cstar_fanart.jpg', @@ -185,7 +185,7 @@ 'order': 17 }, 'NEUF_GULLI': { - 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', 'label': 'Gulli', 'thumb': 'channels/fr/gulli.png', 'fanart': 'channels/fr/gulli_fanart.jpg', @@ -196,7 +196,7 @@ 'order': 18 }, 'NEUF_HD1': { - 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', 'label': 'TF1 Séries-Films', 'thumb': 'channels/fr/tf1seriesfilms.png', 'fanart': 'channels/fr/tf1seriesfilms_fanart.jpg', @@ -207,7 +207,7 @@ 'order': 20 }, 'NEUF_LEQUIPETV': { - 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', 'label': 'La chaine l’Équipe', 'thumb': 'channels/fr/lequipe.png', 'fanart': 'channels/fr/lequipe_fanart.jpg', @@ -218,7 +218,7 @@ 'order': 21 }, 'NEUF_6TER': { - 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', 'label': '6ter', 'thumb': 'channels/fr/6ter.png', 'fanart': 'channels/fr/6ter_fanart.jpg', @@ -229,7 +229,7 @@ 'order': 22 }, 'NEUF_NUM23': { - 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', 'label': 'RMC STORY', 'thumb': 'channels/fr/rmcstory.png', 'fanart': 'channels/fr/rmcstory_fanart.jpg', @@ -240,7 +240,7 @@ 'order': 23 }, 'NEUF_RMCDEC': { - 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', 'label': 'RMC Découverte', 'thumb': 'channels/fr/rmcdecouverte.png', 'fanart': 'channels/fr/rmcdecouverte_fanart.jpg', @@ -251,7 +251,7 @@ 'order': 24 }, 'NEUF_CHERIE25': { - 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', 'label': 'Chérie 25', 'thumb': 'channels/fr/cherie25.png', 'fanart': 'channels/fr/cherie25_fanart.jpg', @@ -262,7 +262,7 @@ 'order': 25 }, 'NEUF_LCI': { - 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', 'label': 'LCI', 'thumb': 'channels/fr/lci.png', 'fanart': 'channels/fr/lci_fanart.jpg', @@ -273,7 +273,7 @@ 'order': 26 }, 'NEUF_FRANCEINFO': { - 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', 'label': 'franceinfo:', 'thumb': 'channels/fr/franceinfo.png', 'fanart': 'channels/fr/franceinfo_fanart.jpg', @@ -284,7 +284,7 @@ 'order': 27 }, 'NEUF_GUYSEN': { - 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', 'label': 'i24 news', 'thumb': 'channels/fr/i24news.png', 'fanart': 'channels/fr/i24news_fanart.jpg', @@ -295,7 +295,7 @@ 'order': 28 }, 'NEUF_BFMBUSINESS': { - 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', 'label': 'BFM Business', 'thumb': 'channels/fr/bfmbusiness.png', 'fanart': 'channels/fr/bfmbusiness_fanart.jpg', @@ -306,7 +306,7 @@ 'order': 31 }, 'NEUF_01NET': { - 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', 'label': 'TECH & CO', 'thumb': 'channels/fr/01net.png', 'fanart': 'channels/fr/01net_fanart.jpg', @@ -316,7 +316,7 @@ 'order': 32 }, # 'NEUF_RMCSPORT1': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'RMC Sport 1', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -327,7 +327,7 @@ # 'order': 33 # }, # 'NEUF_RMCSPORT2': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'RMC Sport 2', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -338,7 +338,7 @@ # 'order': 34 # }, # 'NEUF_BFMPARIS': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'BFM PARIS ILE-DE-FRANCE', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -349,7 +349,7 @@ # 'order': 39 # }, # 'NEUF_DISCOVERYCHNL': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'Discovery Channel', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -360,7 +360,7 @@ # 'order': 40 # }, # 'NEUF_DISCOVERYSCN': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'Discovery Science', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -371,7 +371,7 @@ # 'order': 41 # }, # 'NEUF_DISCOVERYINVST': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'Discovery Investigation', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -382,7 +382,7 @@ # 'order': 43 # }, # 'NEUF_FRANCE24': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'France 24', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -393,7 +393,7 @@ # 'order': 47 # }, # 'NEUF_EURONEWS_FR': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'Euronews', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -404,7 +404,7 @@ # 'order': 48 # }, # 'NEUF_13EMERUE': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': '13ème rue', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -415,7 +415,7 @@ # 'order': 50 # }, # 'NEUF_SYFY': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'Syfy', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -426,7 +426,7 @@ # 'order': 51 # }, # 'NEUF_EENTERTAINMENT': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'E! Entertainment', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -437,7 +437,7 @@ # 'order': 52 # }, # 'NEUF_MCSMAISON': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'My Cuisine', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -448,7 +448,7 @@ # 'order': 55 # }, # 'NEUF_MTV': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'MTV', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -459,7 +459,7 @@ # 'order': 56 # }, # 'NEUF_MCM': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'MCM', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -470,7 +470,7 @@ # 'order': 57 # }, # 'NEUF_AB1': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'AB1', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -481,7 +481,7 @@ # 'order': 58 # }, # 'NEUF_SERIECLUB': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'SERIE CLUB', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -492,7 +492,7 @@ # 'order': 59 # }, # 'NEUF_GAMEONE': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'Game One', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -503,7 +503,7 @@ # 'order': 60 # }, # 'NEUF_GAMEONE1': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'Game One +1', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -514,7 +514,7 @@ # 'order': 61 # }, # 'NEUF_WARNER_TV_NEXT': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'Warner TV Next', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -525,7 +525,7 @@ # 'order': 62 # }, # 'NEUF_JONE': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'J-One', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -536,7 +536,7 @@ # 'order': 63 # }, # 'NEUF_BET': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'BET', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -547,7 +547,7 @@ # 'order': 64 # }, # 'NEUF_COMEDYCENTRAL': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'Comedy Central', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -558,7 +558,7 @@ # 'order': 65 # }, # 'NEUF_PARPREM': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'Paris Première', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -569,7 +569,7 @@ # 'order': 70 # }, # 'NEUF_TEVA': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'Téva', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -580,7 +580,7 @@ # 'order': 71 # }, # 'NEUF_RTL9': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'RTL9', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -591,7 +591,7 @@ # 'order': 72 # }, # 'NEUF_TVBREIZH': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'TV Breizh', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -602,7 +602,7 @@ # 'order': 73 # }, # 'NEUF_TV5MONDE': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'TV5 Monde', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -613,7 +613,7 @@ # 'order': 74 # }, # 'NEUF_TF1PLUSONE': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'TF1 + 1', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -624,7 +624,7 @@ # 'order': 75 # }, # 'NEUF_TMCPLUSONE': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'TMC + 1', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -635,7 +635,7 @@ # 'order': 76 # }, # 'NEUF_CPLUS_BOX_OFFICE': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'CANAL+ BOX OFFICE', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -646,7 +646,7 @@ # 'order': 83 # }, # 'NEUF_LCPAN': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'LCP-AN 24/24', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -657,7 +657,7 @@ # 'order': 103 # }, # 'NEUF_PUBSENAT': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'Public Sénat', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -668,7 +668,7 @@ # 'order': 104 # }, # 'NEUF_SFRSPORT1': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'RMC Sport Access', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -679,7 +679,7 @@ # 'order': 106 # }, # 'NEUF_CPLUS_LIGUE1': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'CANAL+ Ligue 1 Uber Eats', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -690,7 +690,7 @@ # 'order': 114 # }, # 'NEUF_BEINSPORT1': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'beIN SPORTS 1', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -701,7 +701,7 @@ # 'order': 115 # }, # 'NEUF_BEINSPORT2': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'beIN SPORTS 2', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -712,7 +712,7 @@ # 'order': 116 # }, # 'NEUF_BEINMULTI1': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'beIN SPORTS 3', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -723,7 +723,7 @@ # 'order': 117 # }, # 'NEUF_DAZN1': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'DAZN 1', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -734,7 +734,7 @@ # 'order': 118 # }, # 'NEUF_EQUIDIA': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'Equidia', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -745,7 +745,7 @@ # 'order': 119 # }, # 'NEUF_MGG_TV': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'MGG TV', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -756,7 +756,7 @@ # 'order': 121 # }, # 'NEUF_ABMOTEURS': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'Automoto la chaine', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -767,7 +767,7 @@ # 'order': 125 # }, # 'NEUF_GOLFCHANNEL': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'Golf Channel', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -778,7 +778,7 @@ # 'order': 126 # }, # 'NEUF_SPORT_EN_FRANCE': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'Sport en France', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -789,7 +789,7 @@ # 'order': 129 # }, # 'NEUF_OLPLAY': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'OLPLAY', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -800,7 +800,7 @@ # 'order': 130 # }, # 'NEUF_OCINEMAX': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'OCS Max', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -811,7 +811,7 @@ # 'order': 141 # }, # 'NEUF_OCINEPULP': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'OCS Pulp', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -822,7 +822,7 @@ # 'order': 143 # }, # 'NEUF_OCINEGEANTS': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'OCS Geants', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -833,7 +833,7 @@ # 'order': 144 # }, # 'NEUF_CINE+PREMIER': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'Ciné+ Premier', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -844,7 +844,7 @@ # 'order': 146 # }, # 'NEUF_CINE+FRISSON': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'Ciné+ Frisson', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -855,7 +855,7 @@ # 'order': 147 # }, # 'NEUF_CINE+EMOTION': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'Ciné+ Emotion', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -866,7 +866,7 @@ # 'order': 148 # }, # 'NEUF_CCINEMAFAMIZ': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'Ciné+ Famiz', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -877,7 +877,7 @@ # 'order': 149 # }, # 'NEUF_CCINEMACLUB': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'Ciné+ Club', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -888,7 +888,7 @@ # 'order': 150 # }, # 'NEUF_CCINEMACLASSIC': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'Ciné+ Classic', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -899,7 +899,7 @@ # 'order': 151 # }, # 'NEUF_PARAMOUNT': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'Paramount Channel', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -910,7 +910,7 @@ # 'order': 160 # }, # 'NEUF_PARAMOUNT+1': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'Paramount Channel Décalé', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -921,7 +921,7 @@ # 'order': 161 # }, # 'NEUF_TCM': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'TCM Cinéma', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -932,7 +932,7 @@ # 'order': 162 # }, # 'NEUF_ACTION': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'Action', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -943,7 +943,7 @@ # 'order': 163 # }, # 'NEUF_USHUAIA': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'Ushuaia TV', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -954,7 +954,7 @@ # 'order': 173 # }, # 'NEUF_ESCALES': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'TREK', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -965,7 +965,7 @@ # 'order': 174 # }, # 'NEUF_CRIMEDISCTRICT': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'Crime District', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -976,7 +976,7 @@ # 'order': 175 # }, # 'NEUF_HISTOIRE': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'Histoire', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -987,7 +987,7 @@ # 'order': 177 # }, # 'NEUF_TTELHISTOIRE': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'Toute l\'Histoire', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -998,7 +998,7 @@ # 'order': 178 # }, # 'NEUF_KTO': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'KTO', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -1009,7 +1009,7 @@ # 'order': 179 # }, # 'NEUF_ANIMAUX': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'Animaux', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -1020,7 +1020,7 @@ # 'order': 180 # }, # 'NEUF_CHASSEPECHE': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'Chasse et Pêche', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -1031,7 +1031,7 @@ # 'order': 181 # }, # 'NEUF_ENCYCLOPEDIA': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'Science et Vie TV', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -1042,7 +1042,7 @@ # 'order': 182 # }, # 'NEUF_LUXETV': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'Luxe TV', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -1053,7 +1053,7 @@ # 'order': 183 # }, # 'NEUF_FASHIONTV': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'Fashion TV', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -1064,7 +1064,7 @@ # 'order': 184 # }, # 'NEUF_MENSUP': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'Men\'s Up TV', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -1075,7 +1075,7 @@ # 'order': 185 # }, # 'NEUF_ASTRO': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'Astrocenter TV', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -1086,7 +1086,7 @@ # 'order': 186 # }, # 'NEUF_MYZENTV': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'My Zen TV', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -1097,7 +1097,7 @@ # 'order': 187 # }, # 'NEUF_MUSEUM_TV': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'MUSEUM TV', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -1108,7 +1108,7 @@ # 'order': 191 # }, # 'NEUF_NICKELODEONJUNIOR': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'Nickelodeon Junior', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -1119,7 +1119,7 @@ # 'order': 200 # }, # 'NEUF_BOOMERANG': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'Boomerang', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -1130,7 +1130,7 @@ # 'order': 201 # }, # 'NEUF_BOOMERANG+1': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'Boomerang +1', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -1141,7 +1141,7 @@ # 'order': 202 # }, # 'NEUF_TIJI': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'TIJI', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -1152,7 +1152,7 @@ # 'order': 203 # }, # 'NEUF_DREAMWORKS': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'DREAMWORKS', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -1163,7 +1163,7 @@ # 'order': 204 # }, # 'NEUF_NICKELODEON': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'Nickelodeon', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -1174,7 +1174,7 @@ # 'order': 205 # }, # 'NEUF_NICKELODEONPLUSONE': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'Nickelodeon+1', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -1185,7 +1185,7 @@ # 'order': 206 # }, # 'NEUF_CANALJ': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'CANAL J', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -1196,7 +1196,7 @@ # 'order': 210 # }, # 'NEUF_CARTOONNETWORK': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'Cartoon Network', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -1207,7 +1207,7 @@ # 'order': 211 # }, # 'NEUF_NICKELODEONTEEN': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'Nickelodeon Teen', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -1218,7 +1218,7 @@ # 'order': 212 # }, # 'NEUF_CARTOONITO': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'Cartoonito', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -1229,7 +1229,7 @@ # 'order': 213 # }, # 'NEUF_MANGAS': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'Mangas', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -1240,7 +1240,7 @@ # 'order': 231 # }, # 'NEUF_LUCKYJACK': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'Lucky Jack', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -1251,7 +1251,7 @@ # 'order': 234 # }, # 'NEUF_MTVHITS': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'MTV Hits', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -1262,7 +1262,7 @@ # 'order': 250 # }, # 'NEUF_M6MUSICHITS': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'M6 Music', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -1273,7 +1273,7 @@ # 'order': 254 # }, # 'NEUF_MCMPOP': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'RFM TV', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -1284,7 +1284,7 @@ # 'order': 255 # }, # 'NEUF_NRJHITS': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'NRJ Hits', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -1295,7 +1295,7 @@ # 'order': 256 # }, # 'NEUF_MEZZO': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'Mezzo', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -1306,7 +1306,7 @@ # 'order': 260 # }, # 'NEUF_MEZZOHD': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'Mezzo Live HD', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -1317,7 +1317,7 @@ # 'order': 261 # }, # 'NEUF_TELEMELODY': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'MELODY TV', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -1328,7 +1328,7 @@ # 'order': 262 # }, # 'NEUF_TRACE': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'Trace Urban', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -1339,7 +1339,7 @@ # 'order': 263 # }, # 'NEUF_TRACETOCA': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'Trace Toca', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -1350,7 +1350,7 @@ # 'order': 264 # }, # 'NEUF_TRACETROP': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'TRACE CARIBBEAN', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -1361,7 +1361,7 @@ # 'order': 265 # }, # 'NEUF_TRACEGOSPEL': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'Trace Gospel', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -1372,7 +1372,7 @@ # 'order': 266 # }, # 'NEUF_BEBLACKTV': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'BBLACK Classik', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -1383,7 +1383,7 @@ # 'order': 267 # }, # 'NEUF_BBLACKCARIBBEAN': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'BBLACK Caribbean', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -1394,7 +1394,7 @@ # 'order': 268 # }, # 'NEUF_BBLACKAFRICA': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'BBLACK Africa', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -1405,7 +1405,7 @@ # 'order': 269 # }, # 'NEUF_MELODYAFRIQUE': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'Melody d\'Afrique', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -1416,7 +1416,7 @@ # 'order': 272 # }, # 'NEUF_BFMLYON': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'BFM Lyon', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -1427,7 +1427,7 @@ # 'order': 281 # }, # 'NEUF_BFMGRANDLILLE': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'BFM Grand Lille', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -1438,7 +1438,7 @@ # 'order': 282 # }, # 'NEUF_BFMGRANDLITTORAL': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'BFM Grand Littoral', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -1449,7 +1449,7 @@ # 'order': 283 # }, # 'NEUF_BFM_MARSEILLEPROV': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'BFM MARSEILLE PROVENCE', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -1460,7 +1460,7 @@ # 'order': 284 # }, # 'NEUF_BFM_NICECOTEDAZUR': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'BFM NICE COTE D\'AZUR', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -1471,7 +1471,7 @@ # 'order': 285 # }, # 'NEUF_BFM_TOULONVAR': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'BFM TOULON VAR', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -1482,7 +1482,7 @@ # 'order': 286 # }, # 'NEUF_BFM_DICI_ALPESDUSUD': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'BFM DICI ALPES DU SUD', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -1493,7 +1493,7 @@ # 'order': 287 # }, # 'NEUF_BFM_DICI_HAUTEPROVENCE': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'BFM DICI HAUTE-PROVENCE', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -1504,7 +1504,7 @@ # 'order': 288 # }, # 'NEUF_BFM_ALSACE': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'BFM ALSACE', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -1515,7 +1515,7 @@ # 'order': 289 # }, # 'NEUF_BFM_NORMANDIE': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'BFM NORMANDIE', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -1526,7 +1526,7 @@ # 'order': 290 # }, # 'NEUF_TVSUDCAMCEV': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'vià30', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -1537,7 +1537,7 @@ # 'order': 295 # }, # 'NEUF_VIA31': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'vià31', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -1548,7 +1548,7 @@ # 'order': 296 # }, # 'NEUF_TVSUDMONTP': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'vià34', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -1559,7 +1559,7 @@ # 'order': 297 # }, # 'NEUF_VIA66': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'vià66', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -1570,7 +1570,7 @@ # 'order': 298 # }, # 'NEUF_BEINMULTI2': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'beIN SPORTS MAX 4', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -1581,7 +1581,7 @@ # 'order': 300 # }, # 'NEUF_BEINMULTI3': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'beIN SPORTS MAX 5', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -1592,7 +1592,7 @@ # 'order': 301 # }, # 'NEUF_BEINMULTI4': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'beIN SPORTS MAX 6', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -1603,7 +1603,7 @@ # 'order': 302 # }, # 'NEUF_BEINMULTI5': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'beIN SPORTS MAX 7', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -1614,7 +1614,7 @@ # 'order': 303 # }, # 'NEUF_BEINMULTI6': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'beIN SPORTS MAX 8', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -1625,7 +1625,7 @@ # 'order': 304 # }, # 'NEUF_BEINMULTI7': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'beIN SPORTS MAX 9', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -1636,7 +1636,7 @@ # 'order': 305 # }, # 'NEUF_BEINMULTI8': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'beIN SPORTS MAX 10', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -1647,7 +1647,7 @@ # 'order': 306 # }, # 'NEUF_RMCSPORTLIVE3': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'RMC Sport Live 3', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -1658,7 +1658,7 @@ # 'order': 316 # }, # 'NEUF_RMCSPORTLIVE4': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'RMC Sport Live 4', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -1669,7 +1669,7 @@ # 'order': 317 # }, # 'NEUF_RMCSPORTLIVE5': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'RMC Sport Live 5', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -1680,7 +1680,7 @@ # 'order': 318 # }, # 'NEUF_RMCSPORTLIVE6': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'RMC Sport Live 6', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -1691,7 +1691,7 @@ # 'order': 319 # }, # 'NEUF_RMCSPORTLIVE7': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'RMC Sport Live 7', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -1702,7 +1702,7 @@ # 'order': 320 # }, # 'NEUF_RMCSPORTLIVE8': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'RMC Sport Live 8', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -1713,7 +1713,7 @@ # 'order': 321 # }, # 'NEUF_RMCSPORTLIVE9': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'RMC Sport Live 9', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -1724,7 +1724,7 @@ # 'order': 322 # }, # 'NEUF_RMCSPORTLIVE10': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'RMC Sport Live 10', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -1735,7 +1735,7 @@ # 'order': 323 # }, # 'NEUF_RMCSPORTLIVE11': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'RMC Sport Live 11', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -1746,7 +1746,7 @@ # 'order': 324 # }, # 'NEUF_RMCSPORTLIVE12': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'RMC Sport Live 12', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -1757,7 +1757,7 @@ # 'order': 325 # }, # 'NEUF_RMCSPORTLIVE13': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'RMC Sport Live 13', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -1768,7 +1768,7 @@ # 'order': 326 # }, # 'NEUF_RMCSPORTLIVE14': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'RMC Sport Live 14', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -1779,7 +1779,7 @@ # 'order': 327 # }, # 'NEUF_RMCSPORTLIVE15': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'RMC Sport Live 15', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -1790,7 +1790,7 @@ # 'order': 328 # }, # 'NEUF_RMCSPORTLIVE16': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'RMC Sport Live 16', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -1801,7 +1801,7 @@ # 'order': 329 # }, # 'NEUF_RMCSPORTLIVE17': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'RMC Sport Live 17', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -1812,7 +1812,7 @@ # 'order': 330 # }, # 'NEUF_RMCSPORTLIVE18': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'RMC Sport Live 18', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -1823,7 +1823,7 @@ # 'order': 331 # }, # 'NEUF_FRANCE3_ALP': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'France 3 - Alpes', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -1834,7 +1834,7 @@ # 'order': 431 # }, # 'NEUF_FRANCE3_ALS': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'France 3 - Alsace', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -1845,7 +1845,7 @@ # 'order': 432 # }, # 'NEUF_FRANCE3_AQU': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'France 3 - Aquitaine', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -1856,7 +1856,7 @@ # 'order': 433 # }, # 'NEUF_FRANCE3_AUV': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'France 3 - Auvergne', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -1867,7 +1867,7 @@ # 'order': 434 # }, # 'NEUF_FRANCE3_BAN': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'France 3 - Basse-Normandie', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -1878,7 +1878,7 @@ # 'order': 435 # }, # 'NEUF_FRANCE3_BOU': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'France 3 - Bourgogne', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -1889,7 +1889,7 @@ # 'order': 436 # }, # 'NEUF_FRANCE3_BRE': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'France 3 - Bretagne', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -1900,7 +1900,7 @@ # 'order': 437 # }, # 'NEUF_FRANCE3_CEN': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'France 3 - Centre', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -1911,7 +1911,7 @@ # 'order': 438 # }, # 'NEUF_FRANCE3_CHA': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'France 3 - Champagne-Ardenne', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -1922,7 +1922,7 @@ # 'order': 439 # }, # 'NEUF_FRANCE3_COR': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'France 3 - Corse', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -1933,7 +1933,7 @@ # 'order': 440 # }, # 'NEUF_FRANCE3_CAZ': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'France 3 - Côte d\'Azur', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -1944,7 +1944,7 @@ # 'order': 441 # }, # 'NEUF_FRANCE3_FRC': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'France 3 - Franche-Comté', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -1955,7 +1955,7 @@ # 'order': 442 # }, # 'NEUF_FRANCE3_HTN': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'France 3 - Haute-Normandie', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -1966,7 +1966,7 @@ # 'order': 443 # }, # 'NEUF_FRANCE3_LAN': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'France 3 - Languedoc', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -1977,7 +1977,7 @@ # 'order': 444 # }, # 'NEUF_FRANCE3_LIM': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'France 3 - Limousin', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -1988,7 +1988,7 @@ # 'order': 445 # }, # 'NEUF_FRANCE3_LOR': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'France 3 - Lorraine', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -1999,7 +1999,7 @@ # 'order': 446 # }, # 'NEUF_FRANCE3_MDP': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'France 3 - Midi-Pyrénées', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -2010,7 +2010,7 @@ # 'order': 447 # }, # 'NEUF_FRANCE3_NOR': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'France 3 - Nord Pas-de-Calais', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -2021,7 +2021,7 @@ # 'order': 448 # }, # 'NEUF_FRANCE3_PAR': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'France 3 - Paris Ile-de-France', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -2032,7 +2032,7 @@ # 'order': 449 # }, # 'NEUF_FRANCE3_PLO': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'France 3 - Pays de Loire', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -2043,7 +2043,7 @@ # 'order': 450 # }, # 'NEUF_FRANCE3_PIC': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'France 3 - Picardie', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -2054,7 +2054,7 @@ # 'order': 451 # }, # 'NEUF_FRANCE3_POC': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'France 3 - Poitou-Charentes', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -2065,7 +2065,7 @@ # 'order': 452 # }, # 'NEUF_FRANCE3_PRA': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'France 3 - Provence Alpes', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -2076,7 +2076,7 @@ # 'order': 453 # }, # 'NEUF_FRANCE3_RHA': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'France 3 - Rhône-Alpes', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -2087,7 +2087,7 @@ # 'order': 454 # }, # 'NEUF_FRANCE3NAQTN': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'France 3 Nouvelle Aquitaine', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -2098,7 +2098,7 @@ # 'order': 455 # }, # 'NEUF_CANAL21': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'Canal 21', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -2109,7 +2109,7 @@ # 'order': 457 # }, # 'NEUF_20MINUTES_TV': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': '20 Minutes TV', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -2120,7 +2120,7 @@ # 'order': 461 # }, # 'NEUF_TELEBOCAL': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'Télé Bocal', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -2131,7 +2131,7 @@ # 'order': 464 # }, # 'NEUF_TVMONTREUIL': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'TELE-VISION MONTREUIL', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -2142,7 +2142,7 @@ # 'order': 467 # }, # 'NEUF_FIGARO_TV_IDF': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'Figaro TV IDF', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -2153,7 +2153,7 @@ # 'order': 468 # }, # 'NEUF_TVFIL78': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'TV78', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -2164,7 +2164,7 @@ # 'order': 469 # }, # 'NEUF_LYON_CAP_TV': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'Lyon Capitale TV', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -2175,7 +2175,7 @@ # 'order': 476 # }, # 'NEUF_TELEGRENOBLE': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'Télé Grenoble', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -2186,7 +2186,7 @@ # 'order': 477 # }, # 'NEUF_TL7': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'TL7', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -2197,7 +2197,7 @@ # 'order': 478 # }, # 'NEUF_TV8MB': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': '8 Mont Blanc', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -2208,7 +2208,7 @@ # 'order': 480 # }, # 'NEUF_ILTV': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'IL TV', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -2219,7 +2219,7 @@ # 'order': 481 # }, # 'NEUF_ASTV': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'ASTV', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -2230,7 +2230,7 @@ # 'order': 482 # }, # 'NEUF_TELEGOHELLE': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'TELEGOHELLE', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -2241,7 +2241,7 @@ # 'order': 485 # }, # 'NEUF_WEO_PICARDIE': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'Wéo Picardie', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -2252,7 +2252,7 @@ # 'order': 486 # }, # 'NEUF_WEO_TV_LVDN': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'Wéo TV, La voix du nord', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -2263,7 +2263,7 @@ # 'order': 487 # }, # 'NEUF_VIA_MATELE': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'Vià MATÉLÉ', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -2274,7 +2274,7 @@ # 'order': 489 # }, # 'NEUF_7ALIMOGES': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': '7 A LIMOGES', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -2285,7 +2285,7 @@ # 'order': 490 # }, # 'NEUF_TV7BORDEAUX': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'TV7 Bordeaux', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -2296,7 +2296,7 @@ # 'order': 491 # }, # 'NEUF_TVPI': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'TVPI', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -2307,7 +2307,7 @@ # 'order': 492 # }, # 'NEUF_KANALDUDE': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'KANALDUDE', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -2318,7 +2318,7 @@ # 'order': 496 # }, # 'NEUF_TV2COM': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'TV2COM', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -2329,7 +2329,7 @@ # 'order': 501 # }, # 'NEUF_NA_TV': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'NA TV', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -2340,7 +2340,7 @@ # 'order': 504 # }, # 'NEUF_CANAL32': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'Canal 32', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -2351,7 +2351,7 @@ # 'order': 505 # }, # 'NEUF_MIRABELTV': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'VIA Mirabelle', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -2362,7 +2362,7 @@ # 'order': 506 # }, # 'NEUF_TVMOSAIK': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'Mosaïk Cristal', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -2373,7 +2373,7 @@ # 'order': 512 # }, # 'NEUF_TV8MOSELLE': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'TV8 Moselle-Est', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -2384,7 +2384,7 @@ # 'order': 513 # }, # 'NEUF_VOSGESTV': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'VIA Vosges', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -2395,7 +2395,7 @@ # 'order': 514 # }, # 'NEUF_MARITIMA': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'Maritima TV', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -2406,7 +2406,7 @@ # 'order': 520 # }, # 'NEUF_MAURIENNE_TV': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'MAURIENNE TV', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -2417,7 +2417,7 @@ # 'order': 522 # }, # 'NEUF_ANGERSTV': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'Angers télé', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -2428,7 +2428,7 @@ # 'order': 523 # }, # 'NEUF_TLNANTES': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'Télénantes', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -2439,7 +2439,7 @@ # 'order': 524 # }, # 'NEUF_TVVENDEE': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'TV Vendée', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -2450,7 +2450,7 @@ # 'order': 525 # }, # 'NEUF_LM_TV_SARTHE': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'LMtv Sarthe', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -2461,7 +2461,7 @@ # 'order': 526 # }, # 'NEUF_TEBEO': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'TEBEO', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -2472,7 +2472,7 @@ # 'order': 529 # }, # 'NEUF_TVRENNES': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'TV RENNES 35', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -2483,7 +2483,7 @@ # 'order': 530 # }, # 'NEUF_TVTOURS': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'TV Tours', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -2494,7 +2494,7 @@ # 'order': 533 # }, # 'NEUF_ZOUKTV': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'ZOUK TV', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -2505,7 +2505,7 @@ # 'order': 536 # }, # 'NEUF_PAESETV': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'Télé Paese', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -2516,7 +2516,7 @@ # 'order': 537 # }, # 'NEUF_CNNEUROPE': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'CNN International', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -2527,7 +2527,7 @@ # 'order': 541 # }, # 'NEUF_BBCNEWS': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'BBC NEWS', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -2538,7 +2538,7 @@ # 'order': 542 # }, # 'NEUF_FRANCE24ENG': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'France 24 Anglais', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -2549,7 +2549,7 @@ # 'order': 543 # }, # 'NEUF_CNBCEUR': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'CNBC Europe', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -2560,7 +2560,7 @@ # 'order': 544 # }, # 'NEUF_BLOOMBERG_PAN': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'Bloomberg PAN-European', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -2571,7 +2571,7 @@ # 'order': 545 # }, # 'NEUF_ALJAZEERA_EN': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'Al Jazeera English', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -2582,7 +2582,7 @@ # 'order': 546 # }, # 'NEUF_I24NEWSANGLAIS': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'i24 News Anglais', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -2593,7 +2593,7 @@ # 'order': 547 # }, # 'NEUF_NHKWORLD': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'NHK WORLD-JAPAN', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -2604,7 +2604,7 @@ # 'order': 548 # }, # 'NEUF_SKYNEWS': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'Sky News', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -2615,7 +2615,7 @@ # 'order': 549 # }, # 'NEUF_TGCOM24': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'TGCOM24', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -2626,7 +2626,7 @@ # 'order': 550 # }, # 'NEUF_I24NEWSARABE': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'i24 News Arabe', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -2637,7 +2637,7 @@ # 'order': 555 # }, # 'NEUF_FRANCE24ARA': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'France 24 Arabe', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -2648,7 +2648,7 @@ # 'order': 556 # }, # 'NEUF_MEDI1SAT': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'Medi1TV', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -2659,7 +2659,7 @@ # 'order': 558 # }, # 'NEUF_ALARABIYA': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'AL ARABIYA', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -2670,7 +2670,7 @@ # 'order': 559 # }, # 'NEUF_ENNAHAR': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'Ennahar TV', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -2681,7 +2681,7 @@ # 'order': 561 # }, # 'NEUF_CANAL_11': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'Canal 11', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -2692,7 +2692,7 @@ # 'order': 568 # }, # 'NEUF_24HTVE': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': '24h', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -2703,7 +2703,7 @@ # 'order': 574 # }, # 'NEUF_TVEINTL': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'TVE', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -2714,7 +2714,7 @@ # 'order': 575 # }, # 'NEUF_DWTV': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'DW-TV', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -2725,7 +2725,7 @@ # 'order': 578 # }, # 'NEUF_N24': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'WELT', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -2736,7 +2736,7 @@ # 'order': 579 # }, # 'NEUF_RECNEWS': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'Record News', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -2747,7 +2747,7 @@ # 'order': 586 # }, # 'NEUF_AFRICA24': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'Africa 24', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -2758,7 +2758,7 @@ # 'order': 595 # }, # 'NEUF_CANAL2': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'Canal2', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -2769,7 +2769,7 @@ # 'order': 597 # }, # 'NEUF_TVIFICCAO': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'TVI ficcao', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -2780,7 +2780,7 @@ # 'order': 602 # }, # 'NEUF_PORTOCANAL': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'Porto Canal', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -2791,7 +2791,7 @@ # 'order': 604 # }, # 'NEUF_LOCALVISAO': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'Local Visao', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -2802,7 +2802,7 @@ # 'order': 605 # }, # 'NEUF_BENFICATV': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'Benfica TV', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -2813,7 +2813,7 @@ # 'order': 608 # }, # 'NEUF_ABOLATV': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'A BOLA TV', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -2824,7 +2824,7 @@ # 'order': 609 # }, # 'NEUF_CANALQ': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'Canal Q', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -2835,7 +2835,7 @@ # 'order': 622 # }, # 'NEUF_RTPI': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'RTPI', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -2846,7 +2846,7 @@ # 'order': 624 # }, # 'NEUF_RAI1': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'Rai Uno', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -2857,7 +2857,7 @@ # 'order': 626 # }, # 'NEUF_RAI_SCUOLA': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'RAI SCUOLA', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -2868,7 +2868,7 @@ # 'order': 629 # }, # 'NEUF_RAI_STORIA': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'RAI STORIA', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -2879,7 +2879,7 @@ # 'order': 630 # }, # 'NEUF_REAL_MADRID_TV': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'REAL MADRID TV', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -2890,7 +2890,7 @@ # 'order': 645 # }, # 'NEUF_STAR_TVE': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'STAR TVE', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -2901,7 +2901,7 @@ # 'order': 646 # }, # 'NEUF_FLAMENCO': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'ALL FLAMENCO_', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -2912,7 +2912,7 @@ # 'order': 650 # }, # 'NEUF_GALICIATV': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'TVG EUROPA', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -2923,7 +2923,7 @@ # 'order': 651 # }, # 'NEUF_ETB_BASQUE': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'etb basque', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -2934,7 +2934,7 @@ # 'order': 655 # }, # 'NEUF_TELEMADRID': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'TELE MADRID', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -2945,7 +2945,7 @@ # 'order': 657 # }, # 'NEUF_ANDALUCIA_TV': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'ANDALUCIA TV', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -2956,7 +2956,7 @@ # 'order': 658 # }, # 'NEUF_BBCENTERTAINMENT': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'BBC Entertainment', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -2967,7 +2967,7 @@ # 'order': 667 # }, # 'NEUF_PROSIEB': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'ProSieben', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -2978,7 +2978,7 @@ # 'order': 673 # }, # 'NEUF_NTV': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'N-TV', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -2989,7 +2989,7 @@ # 'order': 674 # }, # 'NEUF_RTLTV': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'RTL Television', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -3000,7 +3000,7 @@ # 'order': 675 # }, # 'NEUF_RTL2': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'RTL2', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -3011,7 +3011,7 @@ # 'order': 676 # }, # 'NEUF_SUPERRTL': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'Super RTL', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -3022,7 +3022,7 @@ # 'order': 678 # }, # 'NEUF_VOX': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'VOX', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -3033,7 +3033,7 @@ # 'order': 680 # }, # 'NEUF_KABELEINS': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'KABEL EINS', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -3044,7 +3044,7 @@ # 'order': 682 # }, # 'NEUF_RTLNITROTV': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'RTL NITRO TV', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -3055,7 +3055,7 @@ # 'order': 685 # }, # 'NEUF_ARTEALLD': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'Arte Allemand', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -3066,7 +3066,7 @@ # 'order': 686 # }, # 'NEUF_TVPPOLONIA': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'TVP Polonia', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -3077,7 +3077,7 @@ # 'order': 700 # }, # 'NEUF_ARMENIA1': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'Armenia 1', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -3088,7 +3088,7 @@ # 'order': 713 # }, # 'NEUF_CHANNEL1': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'Channel One Russia', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -3099,7 +3099,7 @@ # 'order': 718 # }, # 'NEUF_RTRPLANETA': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'RTR Planeta', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -3110,7 +3110,7 @@ # 'order': 731 # }, # 'NEUF_ART_CINEMA': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'ART CINEMA', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -3121,7 +3121,7 @@ # 'order': 733 # }, # 'NEUF_ART_AFLAM1': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'ART AFLAM 1', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -3132,7 +3132,7 @@ # 'order': 734 # }, # 'NEUF_ART_AFLAM2': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'ART AFLAM 2', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -3143,7 +3143,7 @@ # 'order': 735 # }, # 'NEUF_AL_HEKAYAT1': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'AL HEKAYAT 1', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -3154,7 +3154,7 @@ # 'order': 736 # }, # 'NEUF_AL_HEKAYAT2': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'AL HEKAYAT 2', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -3165,7 +3165,7 @@ # 'order': 737 # }, # 'NEUF_TVROMANIA': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'TV Romania International', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -3176,7 +3176,7 @@ # 'order': 738 # }, # 'NEUF_BAHIA_TV': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'Bahia TV', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -3187,7 +3187,7 @@ # 'order': 739 # }, # 'NEUF_TELE_MAROC': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'Télé Maroc', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -3198,7 +3198,7 @@ # 'order': 740 # }, # 'NEUF_CANALALGERIE': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'Canal Algérie', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -3209,7 +3209,7 @@ # 'order': 742 # }, # 'NEUF_BEURTV': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'Beur FM TV', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -3220,7 +3220,7 @@ # 'order': 746 # }, # 'NEUF_2MMAROC': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': '2M Maroc', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -3231,7 +3231,7 @@ # 'order': 749 # }, # 'NEUF_ALAOULA': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'Al Aoula', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -3242,7 +3242,7 @@ # 'order': 750 # }, # 'NEUF_ARRYADIA': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'Arryadia', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -3253,7 +3253,7 @@ # 'order': 751 # }, # 'NEUF_ASSADISSA': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'Assadissa', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -3264,7 +3264,7 @@ # 'order': 752 # }, # 'NEUF_WATANIA2': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'Watania 2', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -3275,7 +3275,7 @@ # 'order': 758 # }, # 'NEUF_TNTUNISIA': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'Tunisia 1', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -3286,7 +3286,7 @@ # 'order': 759 # }, # 'NEUF_ASHARQ_NEWS': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'Asharq News', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -3297,7 +3297,7 @@ # 'order': 760 # }, # 'NEUF_BRBRTV': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'Berbère TV', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -3308,7 +3308,7 @@ # 'order': 765 # }, # 'NEUF_SKYNEWSARABIA': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'Sky News Arabia', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -3319,7 +3319,7 @@ # 'order': 767 # }, # 'NEUF_CHADA_TV': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'CHADA TV', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -3330,7 +3330,7 @@ # 'order': 768 # }, # 'NEUF_MBC_PLUS_DRAMA': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'MBC PLUS DRAMA', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -3341,7 +3341,7 @@ # 'order': 769 # }, # 'NEUF_HELWA_TV': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'HELWA TV', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -3352,7 +3352,7 @@ # 'order': 770 # }, # 'NEUF_MBC_DRAMA': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'MBC Drama', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -3363,7 +3363,7 @@ # 'order': 773 # }, # 'NEUF_ECHOROUKNEWS': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'Echorouk News', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -3374,7 +3374,7 @@ # 'order': 774 # }, # 'NEUF_ELBILADTV': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'El Bilad TV', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -3385,7 +3385,7 @@ # 'order': 775 # }, # 'NEUF_DIZI': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'Dizi', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -3396,7 +3396,7 @@ # 'order': 776 # }, # 'NEUF_ALARABY2': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'Alaraby 2', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -3407,7 +3407,7 @@ # 'order': 777 # }, # 'NEUF_MBC': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'MBC', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -3418,7 +3418,7 @@ # 'order': 778 # }, # 'NEUF_ROTANACINEPLUS': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'Rotana Cinéma+ FR', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -3429,7 +3429,7 @@ # 'order': 779 # }, # 'NEUF_MBC5': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'MBC 5', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -3440,7 +3440,7 @@ # 'order': 780 # }, # 'NEUF_MBC_MASR': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'MBC Masr', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -3451,7 +3451,7 @@ # 'order': 782 # }, # 'NEUF_ROTANACINE': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'Rotana Cinema', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -3462,7 +3462,7 @@ # 'order': 783 # }, # 'NEUF_ROTANA_CLASSIC': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'Rotana Classic', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -3473,7 +3473,7 @@ # 'order': 784 # }, # 'NEUF_NESMA': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'Nessma', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -3484,7 +3484,7 @@ # 'order': 785 # }, # 'NEUF_DMC': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'DMC', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -3495,7 +3495,7 @@ # 'order': 786 # }, # 'NEUF_FIX_AND_FOXI': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'Fix and Foxi', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -3506,7 +3506,7 @@ # 'order': 787 # }, # 'NEUF_CARTHAGE_PLUS': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'Carthage+', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -3517,7 +3517,7 @@ # 'order': 788 # }, # 'NEUF_DMC_DRAMA': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'DMC Drama', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -3528,7 +3528,7 @@ # 'order': 789 # }, # 'NEUF_IQRAAINTER': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'Iqraa International', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -3539,7 +3539,7 @@ # 'order': 790 # }, # 'NEUF_IQRAA': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'Iqraa TV', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -3550,7 +3550,7 @@ # 'order': 791 # }, # 'NEUF_HOLYQURAN': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'Al Majd Holy Quran', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -3561,7 +3561,7 @@ # 'order': 792 # }, # 'NEUF_ALMAGH': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'Al Maghribia', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -3572,7 +3572,7 @@ # 'order': 798 # }, # 'NEUF_ARRABIA': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'Arrabiâ', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -3583,7 +3583,7 @@ # 'order': 799 # }, # 'NEUF_LBC_SAT': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'LBC Sat', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -3594,7 +3594,7 @@ # 'order': 801 # }, # 'NEUF_MURRTV': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'Murr TV', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -3605,7 +3605,7 @@ # 'order': 805 # }, # 'NEUF_ROTANA_M_PLUS': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'Rotana M+', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -3616,7 +3616,7 @@ # 'order': 806 # }, # 'NEUF_DUBAITV': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'Dubaï TV', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -3627,7 +3627,7 @@ # 'order': 807 # }, # 'NEUF_ON_TV': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'ON TV', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -3638,7 +3638,7 @@ # 'order': 809 # }, # 'NEUF_HANNIBAL': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'HANNIBAL TV', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -3649,7 +3649,7 @@ # 'order': 810 # }, # 'NEUF_ALMASRIYA': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'Al Masriya', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -3660,7 +3660,7 @@ # 'order': 813 # }, # 'NEUF_ISRAELI': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'The Israeli Network', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -3671,7 +3671,7 @@ # 'order': 814 # }, # 'NEUF_JORDANSAT': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'Jordan Satellite Channel', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -3682,7 +3682,7 @@ # 'order': 815 # }, # 'NEUF_EURO_STAR': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'Euro Star', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -3693,7 +3693,7 @@ # 'order': 818 # }, # 'NEUF_EURO_D': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'Euro D', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -3704,7 +3704,7 @@ # 'order': 819 # }, # 'NEUF_BEIN_MOVIES': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'BEIN MOVIES', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -3715,7 +3715,7 @@ # 'order': 821 # }, # 'NEUF_SHOW_MAX': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'SHOW MAX', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -3726,7 +3726,7 @@ # 'order': 822 # }, # 'NEUF_ATV_AVRUPA': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'ATV Avrupa', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -3737,7 +3737,7 @@ # 'order': 824 # }, # 'NEUF_KANAL7_AVRUPA': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'KANAL 7 AVRUPA', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -3748,7 +3748,7 @@ # 'order': 825 # }, # 'NEUF_APLUS': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'A+', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -3759,7 +3759,7 @@ # 'order': 838 # }, # 'NEUF_ORTB': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'ORTB', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -3770,7 +3770,7 @@ # 'order': 839 # }, # 'NEUF_NOVELAS': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'NOVELAS', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -3781,7 +3781,7 @@ # 'order': 840 # }, # 'NEUF_CRTV': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'CRTV', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -3792,7 +3792,7 @@ # 'order': 841 # }, # 'NEUF_CHERIFLA_TV': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'CHERIFLA TV', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -3803,7 +3803,7 @@ # 'order': 844 # }, # 'NEUF_ORTC': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'ORTC', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -3814,7 +3814,7 @@ # 'order': 845 # }, # 'NEUF_TELECONGO': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'TV Congo', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -3825,7 +3825,7 @@ # 'order': 846 # }, # 'NEUF_MABOKE_TV': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'Maboke TV', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -3836,7 +3836,7 @@ # 'order': 847 # }, # 'NEUF_RTNC': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'RTNC', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -3847,7 +3847,7 @@ # 'order': 848 # }, # 'NEUF_NCI': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'NCI', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -3858,7 +3858,7 @@ # 'order': 849 # }, # 'NEUF_TCI': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'RTI 1', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -3869,7 +3869,7 @@ # 'order': 851 # }, # 'NEUF_CDIRECT': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'CDIRECT', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -3880,7 +3880,7 @@ # 'order': 852 # }, # 'NEUF_GABON_1ERE': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'Gabon 1ère', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -3891,7 +3891,7 @@ # 'order': 853 # }, # 'NEUF_RTG': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'RTG', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -3902,7 +3902,7 @@ # 'order': 854 # }, # 'NEUF_TVM': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'TVM', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -3913,7 +3913,7 @@ # 'order': 855 # }, # 'NEUF_ORTM': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'ORTM', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -3924,7 +3924,7 @@ # 'order': 856 # }, # 'NEUF_NOLLYWOOD': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'Nollywood TV', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -3935,7 +3935,7 @@ # 'order': 858 # }, # 'NEUF_AFRICABLE': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'Africâble', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -3946,7 +3946,7 @@ # 'order': 859 # }, # 'NEUF_TRACEAFRICA': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'Trace Africa', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -3957,7 +3957,7 @@ # 'order': 861 # }, # 'NEUF_VOXAFRICA': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'Vox Africa', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -3968,7 +3968,7 @@ # 'order': 862 # }, # 'NEUF_2STV': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': '2STV', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -3979,7 +3979,7 @@ # 'order': 863 # }, # 'NEUF_RTS1': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'RTS 1', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -3990,7 +3990,7 @@ # 'order': 864 # }, # 'NEUF_TFM': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'TFM', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -4001,7 +4001,7 @@ # 'order': 866 # }, # 'NEUF_SUNU_YEUF': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'SUNU YEUF', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -4012,7 +4012,7 @@ # 'order': 867 # }, # 'NEUF_BEIJINGTV': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'Beijing TV', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -4023,7 +4023,7 @@ # 'order': 878 # }, # 'NEUF_CCTVYULE': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'CCTV YuLe', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -4034,7 +4034,7 @@ # 'order': 879 # }, # 'NEUF_CCTV4': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'CCTV-4', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -4045,7 +4045,7 @@ # 'order': 880 # }, # 'NEUF_CMC': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'CMC', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -4056,7 +4056,7 @@ # 'order': 881 # }, # 'NEUF_HUNANTV': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'Hunan TV', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -4067,7 +4067,7 @@ # 'order': 882 # }, # 'NEUF_JSBCINT': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'JSBC International', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -4078,7 +4078,7 @@ # 'order': 883 # }, # 'NEUF_PHOENIXCNE': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'Phoenix CNE', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -4089,7 +4089,7 @@ # 'order': 884 # }, # 'NEUF_DRAGONTV': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'Dragon TV', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -4100,7 +4100,7 @@ # 'order': 886 # }, # 'NEUF_XIAMENSTAR': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'Great Wall Elite', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -4111,7 +4111,7 @@ # 'order': 887 # }, # 'NEUF_ZTVWORLD': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'Zhejiang Star TV', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -4122,7 +4122,7 @@ # 'order': 888 # }, # 'NEUF_GRT_GBA': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'GRT GBA Satellite TV', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -4133,7 +4133,7 @@ # 'order': 916 # }, # 'NEUF_CCTVDOC': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'CGTN-Documentary', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -4144,7 +4144,7 @@ # 'order': 918 # }, # 'NEUF_CCTVF': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'CGTN-Français', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -4155,7 +4155,7 @@ # 'order': 920 # }, # 'NEUF_NTD': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'NTD', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', @@ -4166,7 +4166,7 @@ # 'order': 921 # }, # 'NEUF_NHK_WP': { - # 'resolver': '/resources/lib/channels/fr/sfrtv:get_live_stream', + # 'resolver': '/resources/lib/providers/sfrtv:get_live_stream', # 'label': 'NHK World Premium', # 'thumb': 'channels/fr/???.png', # 'fanart': 'channels/fr/???_fanart.jpg', diff --git a/resources/settings.xml b/resources/settings.xml index 6f80d8353..099a5d413 100644 --- a/resources/settings.xml +++ b/resources/settings.xml @@ -126,9 +126,6 @@ - - - @@ -147,6 +144,11 @@ + + + + + From f2a9824ee08b12b57df1c167e5be3d8dff1cbe8e Mon Sep 17 00:00:00 2001 From: SebMourlhou Date: Fri, 1 Dec 2023 23:21:30 +0100 Subject: [PATCH 12/15] [SFR TV] "universe" parameter must be a variable --- resources/lib/providers/sfrtv.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/resources/lib/providers/sfrtv.py b/resources/lib/providers/sfrtv.py index 236b3735a..943b31b81 100644 --- a/resources/lib/providers/sfrtv.py +++ b/resources/lib/providers/sfrtv.py @@ -396,7 +396,7 @@ def list_product_details(plugin, product_id, **kwargs): if 'seasons' in product_details: for season in product_details['seasons']: - season_details = get_product_details(plugin, season['id']) + season_details = get_product_details(plugin, season['id'], **kwargs) yield build_product_item(plugin, season_details) elif 'episodes' in product_details: for episode in product_details['episodes']: @@ -438,7 +438,7 @@ def build_product_item(plugin, product): item.art['thumb'] = image['url'] item.set_callback(list_product_details if product.get('type', '') in PRODUCT_DETAILS_TYPES else get_replay_stream, - product_id=product['id'], + product['id'], universe=product['universe'] if 'universe' in product else 'PROVIDER') item_post_treatment(item) @@ -446,7 +446,7 @@ def build_product_item(plugin, product): return item -def get_replay_url(plugin, product_id, token): +def get_replay_url(plugin, product_id, token, universe='PROVIDER', **kwargs): params = { 'app': 'gen8', 'device': 'browser', @@ -455,7 +455,7 @@ def get_replay_url(plugin, product_id, token): 'infrastructures': 'FTTH', 'operators': 'sfr', 'noTracking': 'false', - 'universe': 'PROVIDER' + 'universe': universe } headers = { 'Accept': 'application/json', @@ -480,7 +480,7 @@ def get_replay_stream(plugin, product_id, **kwargs): if not token: return False - replay_url = get_replay_url(plugin, product_id, token) + replay_url = get_replay_url(plugin, product_id, token, **kwargs) if not replay_url: return False From 48b54b348538d4d4aaa46f9c13b5450136c17ca5 Mon Sep 17 00:00:00 2001 From: SebMourlhou Date: Fri, 1 Dec 2023 23:38:09 +0100 Subject: [PATCH 13/15] [SFR TV] get the list of episodes from a specific URL --- resources/lib/providers/sfrtv.py | 53 ++++++++++++++++++++++++++++++-- 1 file changed, 50 insertions(+), 3 deletions(-) diff --git a/resources/lib/providers/sfrtv.py b/resources/lib/providers/sfrtv.py index 943b31b81..a2ce46e16 100644 --- a/resources/lib/providers/sfrtv.py +++ b/resources/lib/providers/sfrtv.py @@ -30,6 +30,7 @@ CATEGORIES_URL = 'https://ws-cdn.tv.sfr.net/gaia-core/rest/api/web/v1/stores/{}/categories' PRODUCTS_URL = 'https://ws-cdn.tv.sfr.net/gaia-core/rest/api/web/v2/categories/{}/contents' PRODUCT_DETAILS_URL = 'https://ws-cdn.tv.sfr.net/gaia-core/rest/api/web/v1/content/{}/detail' +EPISODES_URL = 'https://ws-cdn.tv.sfr.net/gaia-core/rest/api/web/v1/content/{}/episodes' PRODUCT_OPTIONS_URL = 'https://ws-backendtv.sfr.fr/gaia-core/rest/api/web/v3/content/{}/options' SEARCH_TEXT_URL = 'https://ws-backendtv.sfr.fr/gaia-core/rest/api/web/v2/search/text' CUSTOMDATA_REPLAY = ('description={}&deviceId=byPassARTHIUS&deviceName=Firefox-96.0----Windows&deviceType=PC' @@ -399,13 +400,59 @@ def list_product_details(plugin, product_id, **kwargs): season_details = get_product_details(plugin, season['id'], **kwargs) yield build_product_item(plugin, season_details) elif 'episodes' in product_details: - for episode in product_details['episodes']: - episode_details = get_product_details(plugin, episode['id']) - yield build_product_item(plugin, episode_details) + for episode in list_episodes(plugin, product_id, **kwargs): + yield episode else: yield build_product_item(plugin, product_details) +def get_episodes(plugin, season_id, universe='PROVIDER', page=0, size=10, **kwargs): + params = { + 'app': 'gen8', + 'device': 'browser', + 'accountTypes': 'LAND', + 'infrastructures': 'FTTH', + 'operators': 'sfr', + 'noTracking': 'false', + 'universe': universe, + 'page': page, + 'size': size, + 'sortBy': 'DIFFUSIONDATE', + 'sorting': 'DESC' + } + headers = { + 'User-Agent': USER_AGENT, + 'Accept': 'application/json', + 'Referer': 'https://tv.sfr.fr/', + 'Origin': 'https://tv.sfr.fr', + 'Connection': 'keep-alive' + } + search_result = urlquick.get(EPISODES_URL.format(season_id), + params=params, + headers=headers, + timeout=REQUEST_TIMEOUT).json() + + has_next_page = (search_result['count'] - ((page + 1) * size)) > 0 + + return search_result['content'], page, has_next_page + + +@Route.register(autosort=False) +def list_episodes(plugin, season_id, **kwargs): + episodes, page, has_next_page = get_episodes(plugin, season_id, **kwargs) + + for episode in episodes: + yield build_product_item(plugin, episode) + + if has_next_page: + item = Listitem.next_page(callback=list_episodes, + season_id=season_id, + page=page + 1, + **kwargs) + item.property['SpecialSort'] = 'bottom' + yield item + + def build_product_item(plugin, product): item = Listitem() From a112b7a9e26f776b59de3ec31971986ab9b343d1 Mon Sep 17 00:00:00 2001 From: SebMourlhou Date: Mon, 11 Dec 2023 23:21:46 +0100 Subject: [PATCH 14/15] [SFR TV] Improve listitem information --- resources/lib/providers/sfrtv.py | 38 ++++++++++++++++++++++---------- 1 file changed, 26 insertions(+), 12 deletions(-) diff --git a/resources/lib/providers/sfrtv.py b/resources/lib/providers/sfrtv.py index a2ce46e16..4d59a6818 100644 --- a/resources/lib/providers/sfrtv.py +++ b/resources/lib/providers/sfrtv.py @@ -453,36 +453,50 @@ def list_episodes(plugin, season_id, **kwargs): yield item -def build_product_item(plugin, product): +def build_product_item(plugin, product, default_preferred_image_ratio='16/9'): item = Listitem() item.label = product['title'] - if 'description' in product and product['description']: + if product.get('subTitle'): + item.label += ' - ' + product['subTitle'] + + if product.get('universe') == 'aggregate': + if product.get('type') == 'Season' and product.get('seasonNumber'): + item.label += ' - Saison ' + str(product['seasonNumber']) + + if product.get('universe') == 'aggregate' or (product.get('context') and 'VOD' in product['context'].upper()): + if product.get('genres') and len(product['genres']) > 0: + item.info['genre'] = product['genres'][0] + + if product.get('description'): item.info['plot'] = product['description'] - elif 'shortDescription' in product and product['shortDescription']: + elif product.get('shortDescription'): item.info['plot'] = product['shortDescription'] - if 'duration' in product and product['duration']: + if product.get('duration'): item.info['duration'] = product['duration'] - if 'seasonNumber' in product and product['seasonNumber']: + if product.get('seasonNumber'): + item.info['mediatype'] = 'season' item.info['season'] = product['seasonNumber'] - if 'episodeNumber' in product and product['episodeNumber']: + if product.get('episodeNumber'): item.info['mediatype'] = 'episode' item.info['episode'] = product['episodeNumber'] - if 'diffusionDate' in product and product['diffusionDate']: + if product.get('diffusionDate'): dt = datetime.fromtimestamp(int(product['diffusionDate'] / 1000)) dt_format = '%d/%m/%Y' item.info.date(dt.strftime(dt_format), dt_format) - for image in product.get('images', []): - if image['format'] == '2/3' and not item.art.get('thumb'): - item.art['thumb'] = image['url'] - elif image['format'] == '16/9': - item.art['thumb'] = image['url'] + if product.get('releaseDate'): + item.info['year'] = product['releaseDate'] + + if len(product.get('images', [])) > 0: + pref_ratio = product.get('preferredImageRatio') or default_preferred_image_ratio + item.art['thumb'] = (next((image['url'] for image in product['images'] if image['format'] == pref_ratio), None) + or product['images'][0]['url']) item.set_callback(list_product_details if product.get('type', '') in PRODUCT_DETAILS_TYPES else get_replay_stream, product['id'], From 584c45f7c37615461609ef62fd8fbba2e42a3b64 Mon Sep 17 00:00:00 2001 From: SebMourlhou Date: Mon, 11 Dec 2023 23:30:30 +0100 Subject: [PATCH 15/15] [SFR TV] Add VOD service + refactoring --- .../resource.language.en_gb/strings.po | 5 + .../resource.language.fr_fr/strings.po | 5 + .../resource.language.he_il/strings.po | 5 + .../resource.language.nl_nl/strings.po | 5 + resources/lib/providers/sfrtv.py | 429 ++++++++++++++---- 5 files changed, 352 insertions(+), 97 deletions(-) diff --git a/resources/language/resource.language.en_gb/strings.po b/resources/language/resource.language.en_gb/strings.po index d1b474206..b3020c7f9 100644 --- a/resources/language/resource.language.en_gb/strings.po +++ b/resources/language/resource.language.en_gb/strings.po @@ -969,6 +969,11 @@ msgctxt "#30731" msgid "Homepage" msgstr "" +msgctxt "#30732" +msgid "This content requires a payment. To buy or rent it, go to %s website at this URL : %s" +msgstr "" + + # Favourites (from 30800 to 30859) msgctxt "#30800" diff --git a/resources/language/resource.language.fr_fr/strings.po b/resources/language/resource.language.fr_fr/strings.po index 55279babf..7df16a0e5 100644 --- a/resources/language/resource.language.fr_fr/strings.po +++ b/resources/language/resource.language.fr_fr/strings.po @@ -969,6 +969,11 @@ msgctxt "#30731" msgid "Homepage" msgstr "Accueil" +msgctxt "#30732" +msgid "This content requires a payment. To buy or rent it, go to %s website at this URL : %s" +msgstr "Ce contenu est payant. Pour l'acheter ou le louer, rendez-vous sur le site %s à cette URL : %s" + + # Favourites (from 30800 to 30859) msgctxt "#30800" diff --git a/resources/language/resource.language.he_il/strings.po b/resources/language/resource.language.he_il/strings.po index fb8712caa..24a073874 100644 --- a/resources/language/resource.language.he_il/strings.po +++ b/resources/language/resource.language.he_il/strings.po @@ -965,6 +965,11 @@ msgctxt "#30731" msgid "Homepage" msgstr "" +msgctxt "#30732" +msgid "This content requires a payment. To buy or rent it, go to %s website at this URL : %s" +msgstr "" + + # Favourites (from 30800 to 30859) msgctxt "#30800" diff --git a/resources/language/resource.language.nl_nl/strings.po b/resources/language/resource.language.nl_nl/strings.po index 4f5349b27..a8da699d7 100644 --- a/resources/language/resource.language.nl_nl/strings.po +++ b/resources/language/resource.language.nl_nl/strings.po @@ -969,6 +969,11 @@ msgctxt "#30731" msgid "Homepage" msgstr "" +msgctxt "#30732" +msgid "This content requires a payment. To buy or rent it, go to %s website at this URL : %s" +msgstr "" + + # Favourites (from 30800 to 30859) msgctxt "#30800" diff --git a/resources/lib/providers/sfrtv.py b/resources/lib/providers/sfrtv.py index 4d59a6818..bea32f07a 100644 --- a/resources/lib/providers/sfrtv.py +++ b/resources/lib/providers/sfrtv.py @@ -25,23 +25,31 @@ USER_PROFILE_URL = 'https://ws-backendtv.sfr.fr/heimdall-core/public/api/v2/userProfiles' SERVICE_URL = 'https://ws-backendtv.sfr.fr/sekai-service-plan/public/v2/service-list' LICENSE_URL = 'https://ws-backendtv.sfr.fr/asgard-drm-widevine/public/licence' -STRUCT_MENU_URL = 'https://ws-cdn.tv.sfr.net/gaia-core/rest/api/web/v2/menu/RefMenuItem::gen8-replay-v2/structure' -MENU_URL = 'https://ws-backendtv.sfr.fr/gaia-core/rest/api/web/v2/spot/{}/content' -CATEGORIES_URL = 'https://ws-cdn.tv.sfr.net/gaia-core/rest/api/web/v1/stores/{}/categories' -PRODUCTS_URL = 'https://ws-cdn.tv.sfr.net/gaia-core/rest/api/web/v2/categories/{}/contents' -PRODUCT_DETAILS_URL = 'https://ws-cdn.tv.sfr.net/gaia-core/rest/api/web/v1/content/{}/detail' -EPISODES_URL = 'https://ws-cdn.tv.sfr.net/gaia-core/rest/api/web/v1/content/{}/episodes' -PRODUCT_OPTIONS_URL = 'https://ws-backendtv.sfr.fr/gaia-core/rest/api/web/v3/content/{}/options' +MENU_STRUCTURE_URL = 'https://ws-cdn.tv.sfr.net/gaia-core/rest/api/web/v2/menu/{}/structure' +SPOT_CONTENT_URL = 'https://ws-backendtv.sfr.fr/gaia-core/rest/api/web/v2/spot/{}/content' +SPOT_MORE_URL = 'https://ws-backendtv.sfr.fr/gaia-core/rest/api/web/v2/spot/{}/more' +TILE_CONTENT_URL = 'https://ws-backendtv.sfr.fr/gaia-core/rest/api/web/v2/tile/{}/content' +STORE_CATEGORIES_URL = 'https://ws-cdn.tv.sfr.net/gaia-core/rest/api/web/v1/stores/{}/categories' +CATEGORY_CONTENTS_URL = 'https://ws-cdn.tv.sfr.net/gaia-core/rest/api/web/v2/categories/{}/contents' +CONTENT_DETAILS_URL = 'https://ws-cdn.tv.sfr.net/gaia-core/rest/api/web/v1/content/{}/detail' +CONTENT_EPISODES_URL = 'https://ws-cdn.tv.sfr.net/gaia-core/rest/api/web/v1/content/{}/episodes' +CONTENT_OPTIONS_URL = 'https://ws-backendtv.sfr.fr/gaia-core/rest/api/web/v3/content/{}/options' SEARCH_TEXT_URL = 'https://ws-backendtv.sfr.fr/gaia-core/rest/api/web/v2/search/text' -CUSTOMDATA_REPLAY = ('description={}&deviceId=byPassARTHIUS&deviceName=Firefox-96.0----Windows&deviceType=PC' - '&osName=Windows&osVersion=10&persistent=false&resolution=1600x900&tokenType=castoken' - '&tokenSSO={}&type=REPLAY') -CUSTOMDATA_LIVE = ('description={}&deviceId=byPassARTHIUS&deviceName=Firefox-96.0----Windows&deviceType=PC' +VOD_PLAY_URL = 'https://ws-backendtv.sfr.fr/gaia-core/rest/api/web/v1/vod/play' +CUSTOMDATA_LIVE = ('description={}&deviceId=byPassARTHIUS&deviceName=Chrome-119.0.0.0----Windows&deviceType=PC' '&osName=Windows&osVersion=10&persistent=false&resolution=1600x900&tokenType=castoken' '&tokenSSO={}&type=LIVEOTT&accountId={}') -MAX_PRODUCTS = 20 +CUSTOMDATA_REPLAY = ('description={}&deviceId=byPassARTHIUS&deviceName=Chrome-119.0.0.0----Windows&deviceType=PC' + '&osName=Windows&osVersion=10&persistent=false&resolution=1600x900&tokenType=castoken' + '&tokenSSO={}&type={}') +CUSTOMDATA_VOD = ('description={}&deviceId=byPassARTHIUS&deviceName=Chrome-119.0.0.0----Windows&deviceType=PC' + '&osName=Windows&osVersion=10&persistent=false&resolution=1600x900&tokenType=castoken' + '&tokenSSO={}&entitlementId={}&type={}') +MAX_CONTENTS = 20 REQUEST_TIMEOUT = 30 -PRODUCT_DETAILS_TYPES = ['Serie', 'Season', 'CONTENT'] +REPLAY_MENU_ID = 'RefMenuItem::gen8-replay-v2' +VOD_MENU_ID = 'RefMenuItem::gen8-vod-v2' +PAID_CONTENT_INDICATORS = ['inPackRented', 'inRent', 'inSVodSubscribed'] def get_sfrtv_config(plugin): @@ -213,7 +221,15 @@ def provider_root(plugin, **kwargs): item = Listitem() item.label = 'Replay' item.art['thumb'] = get_item_media_path('replay.png') - item.set_callback(list_stores) + item.set_callback(list_replay_stores) + item_post_treatment(item) + yield item + + # VOD + item = Listitem() + item.label = 'VOD' + item.art['thumb'] = get_item_media_path('vod.png') + item.set_callback(list_vod_spots) item_post_treatment(item) yield item @@ -223,7 +239,7 @@ def provider_root(plugin, **kwargs): yield item -def get_stores(plugin, token): +def get_menu_structure(plugin, menu_id): params = { 'accountTypes': 'LAND', 'infrastructures': 'FTTH', @@ -237,58 +253,64 @@ def get_stores(plugin, token): 'Referer': 'https://tv.sfr.fr/', 'User-Agent': USER_AGENT } - struct_menu = urlquick.get(STRUCT_MENU_URL, - params=params, - headers=headers, - timeout=REQUEST_TIMEOUT).json() - spot_id = struct_menu['spots'][0]['id'] - params = { - 'app': 'gen8', - 'device': 'browser', - 'token': token, - 'operators': 'sfr', - 'infrastructures': 'FTTH', - 'accountTypes': 'LAND', - 'noTracking': 'false', - } + return urlquick.get(MENU_STRUCTURE_URL.format(menu_id), + params=params, + headers=headers, + timeout=REQUEST_TIMEOUT).json() + + +def get_spot_content(plugin, spot_id, token=None): + if token: + params = { + 'app': 'gen8', + 'device': 'browser', + 'token': token, + 'operators': 'sfr', + 'infrastructures': 'FTTH', + 'accountTypes': 'LAND', + 'noTracking': 'false', + } + else: + params = { + 'app': 'gen8', + 'device': 'browser', + 'noTracking': 'false', + } headers = { 'Accept': 'application/json', 'Referer': 'https://tv.sfr.fr/', 'User-Agent': USER_AGENT } - menu = urlquick.get(MENU_URL.format(spot_id), + return urlquick.get(SPOT_CONTENT_URL.format(spot_id), params=params, headers=headers, - max_age=TOKEN_MAX_AGE, + max_age=(TOKEN_MAX_AGE if token else -1), timeout=REQUEST_TIMEOUT).json() - return list(map(lambda t: {'id': t['action']['actionIds']['storeId'], - 'title': t['title'], - 'images': t['images']}, - menu['tiles'])) + + +def get_replay_stores(plugin, token): + menu_structure = get_menu_structure(plugin, REPLAY_MENU_ID) + + # only one spot for Replay + spot_id = menu_structure['spots'][0]['id'] + + spot_content = get_spot_content(plugin, spot_id, token) + + return spot_content['tiles'] @Route.register(autosort=False) -def list_stores(plugin, **kwargs): +def list_replay_stores(plugin, **kwargs): token = get_token(plugin) if not token: yield False return - for store in get_stores(plugin, token): - item = Listitem() - item.label = store['title'] + for store in get_replay_stores(plugin, token): + yield build_product_item(plugin, store) - for image in store['images']: - if image['format'] == 'logo': - item.art['thumb'] = image['url'] - item.set_callback(list_categories, - store_id=store['id']) - item_post_treatment(item) - yield item - - -def get_categories(plugin, store_id): +def get_store_categories(plugin, store_id): params = { 'app': 'gen8', 'device': 'browser', @@ -302,7 +324,7 @@ def get_categories(plugin, store_id): 'Referer': 'https://tv.sfr.fr/', 'User-Agent': USER_AGENT } - categories_infos = urlquick.get(CATEGORIES_URL.format(store_id), + categories_infos = urlquick.get(STORE_CATEGORIES_URL.format(store_id), params=params, headers=headers, timeout=REQUEST_TIMEOUT).json() @@ -310,20 +332,19 @@ def get_categories(plugin, store_id): @Route.register(autosort=False) -def list_categories(plugin, store_id, **kwargs): - categories = get_categories(plugin, store_id) +def list_store_categories(plugin, store_id, **kwargs): + categories = get_store_categories(plugin, store_id) for category in categories: item = Listitem() item.label = category['name'] - item.set_callback(list_products, - category_id=category['id'], - page=0) + item.set_callback(list_category_contents, + category_id=category['id']) item_post_treatment(item) yield item -def get_products(plugin, category_id, page): +def get_category_contents(plugin, category_id, page=0): params = { 'app': 'gen8', 'device': 'browser', @@ -332,42 +353,42 @@ def get_products(plugin, category_id, page): 'operators': 'sfr', 'noTracking': 'false', 'page': page, - 'size': MAX_PRODUCTS + 'size': MAX_CONTENTS # this parameter doesn't change anything (max 20 contents) } headers = { 'Accept': 'application/json', 'Referer': 'https://tv.sfr.fr/', 'User-Agent': USER_AGENT } - return urlquick.get(PRODUCTS_URL.format(category_id), + return urlquick.get(CATEGORY_CONTENTS_URL.format(category_id), params=params, headers=headers, timeout=REQUEST_TIMEOUT).json() @Route.register(autosort=False) -def list_products(plugin, category_id, page, **kwargs): - # Pagination seems to be blocked at 20 videos (the "size" parameter doesn't change anything), - # so let's paginate at 100 videos +def list_category_contents(plugin, category_id, page=0, **kwargs): + # Pagination seems to be blocked at 20 contents (the "size" parameter doesn't change anything), + # so let's paginate at 100 contents n_loop = 5 for x in range(n_loop): - products = get_products(plugin, category_id, page) + contents = get_category_contents(plugin, category_id, page) - for product in products: - yield build_product_item(plugin, product) + for content in contents: + yield build_product_item(plugin, content) - if len(products) == MAX_PRODUCTS: # didn't find "count" or "total" information + if len(contents) == MAX_CONTENTS: # no "count" or "total" information if x < (n_loop - 1): page += 1 else: yield Listitem.next_page(category_id=category_id, - callback=list_products, + callback=list_category_contents, page=page + 1) else: break -def get_product_details(plugin, product_id, universe='PROVIDER', **kwargs): +def get_content_details(plugin, content_id, universe='PROVIDER', **kwargs): params = { 'accountTypes': 'LAND', 'infrastructures': 'FTTH', @@ -380,33 +401,38 @@ def get_product_details(plugin, product_id, universe='PROVIDER', **kwargs): 'Referer': 'https://tv.sfr.fr/', 'User-Agent': USER_AGENT } - return urlquick.get(PRODUCT_DETAILS_URL.format(product_id), + return urlquick.get(CONTENT_DETAILS_URL.format(content_id), params=params, headers=headers, timeout=REQUEST_TIMEOUT).json() @Route.register(autosort=False) -def list_product_details(plugin, product_id, **kwargs): +def list_content_details(plugin, content_id, action_type='', **kwargs): token = get_token(plugin) if not token: yield False return - product_details = get_product_details(plugin, product_id, **kwargs) + content_details = get_content_details(plugin, content_id, **kwargs) - if 'seasons' in product_details: - for season in product_details['seasons']: - season_details = get_product_details(plugin, season['id'], **kwargs) + if 'seasons' in content_details: + for season in content_details['seasons']: + season_details = get_content_details(plugin, season['id'], **kwargs) yield build_product_item(plugin, season_details) - elif 'episodes' in product_details: - for episode in list_episodes(plugin, product_id, **kwargs): + elif action_type == 'displayFip' and content_details['type'] == 'Season' and content_details.get('seriesId'): + series_details = get_content_details(plugin, content_details['seriesId'], **kwargs) + for season in series_details['seasons']: + season_details = get_content_details(plugin, season['id'], **kwargs) + yield build_product_item(plugin, season_details) + elif 'episodes' in content_details: + for episode in list_content_episodes(plugin, content_id, **kwargs): yield episode else: - yield build_product_item(plugin, product_details) + yield build_product_item(plugin, content_details) -def get_episodes(plugin, season_id, universe='PROVIDER', page=0, size=10, **kwargs): +def get_content_episodes(plugin, season_id, universe='PROVIDER', page=0, size=10, **kwargs): params = { 'app': 'gen8', 'device': 'browser', @@ -427,7 +453,7 @@ def get_episodes(plugin, season_id, universe='PROVIDER', page=0, size=10, **kwar 'Origin': 'https://tv.sfr.fr', 'Connection': 'keep-alive' } - search_result = urlquick.get(EPISODES_URL.format(season_id), + search_result = urlquick.get(CONTENT_EPISODES_URL.format(season_id), params=params, headers=headers, timeout=REQUEST_TIMEOUT).json() @@ -438,14 +464,14 @@ def get_episodes(plugin, season_id, universe='PROVIDER', page=0, size=10, **kwar @Route.register(autosort=False) -def list_episodes(plugin, season_id, **kwargs): - episodes, page, has_next_page = get_episodes(plugin, season_id, **kwargs) +def list_content_episodes(plugin, season_id, **kwargs): + episodes, page, has_next_page = get_content_episodes(plugin, season_id, **kwargs) for episode in episodes: yield build_product_item(plugin, episode) if has_next_page: - item = Listitem.next_page(callback=list_episodes, + item = Listitem.next_page(callback=list_content_episodes, season_id=season_id, page=page + 1, **kwargs) @@ -498,16 +524,46 @@ def build_product_item(plugin, product, default_preferred_image_ratio='16/9'): item.art['thumb'] = (next((image['url'] for image in product['images'] if image['format'] == pref_ratio), None) or product['images'][0]['url']) - item.set_callback(list_product_details if product.get('type', '') in PRODUCT_DETAILS_TYPES else get_replay_stream, - product['id'], - universe=product['universe'] if 'universe' in product else 'PROVIDER') + product_type = product.get('type') + if product_type in ['Serie', 'Season', 'CONTENT']: + callback = list_content_details + elif product_type == 'BASE': + if 'categoryId' in product['action']['actionIds']: + callback = list_category_contents + elif 'spotId' in product['action']['actionIds']: + callback = list_spot_more_contents + elif 'tileId' in product['action']['actionIds']: + callback = list_tile_contents + else: + callback = list_store_categories + else: + callback = get_stream + + if product_type == 'CONTENT': + product_id = product['action']['actionIds']['contentId'] + elif product_type == 'BASE': + if 'categoryId' in product['action']['actionIds']: + product_id = product['action']['actionIds']['categoryId'] + elif 'spotId' in product['action']['actionIds']: + product_id = product['action']['actionIds']['spotId'] + elif 'tileId' in product['action']['actionIds']: + product_id = product['action']['actionIds']['tileId'] + else: + product_id = product['action']['actionIds']['storeId'] + else: + product_id = product['id'] + + item.set_callback(callback, + product_id, + universe=product['universe'] if 'universe' in product else 'PROVIDER', + action_type=product['action']['actionType'] if 'action' in product else '') item_post_treatment(item) return item -def get_replay_url(plugin, product_id, token, universe='PROVIDER', **kwargs): +def get_stream_url(plugin, product_id, token, universe='PROVIDER', **kwargs): params = { 'app': 'gen8', 'device': 'browser', @@ -523,42 +579,221 @@ def get_replay_url(plugin, product_id, token, universe='PROVIDER', **kwargs): 'Referer': 'https://tv.sfr.fr/', 'User-Agent': USER_AGENT } - product_options = urlquick.get(PRODUCT_OPTIONS_URL.format(product_id), + content_options = urlquick.get(CONTENT_OPTIONS_URL.format(product_id), params=params, headers=headers, timeout=REQUEST_TIMEOUT).json() - for option in product_options: - for offer in option['offers']: - for stream in offer['streams']: + context = '' + offer_id = '' + for option in content_options: + context = option['context'] + for offer in option.get('offers', []): + if offer.get('buyable') and not any(map(lambda x: offer[x], PAID_CONTENT_INDICATORS)): + continue + if offer.get('offerType'): + context = offer['offerType'] + if offer.get('offerId'): + offer_id = offer['offerId'] + for stream in offer.get('streams', []): if stream['drm'] == 'WIDEVINE': - return stream['url'] - return None + return stream['url'], context, offer_id + return None, context, offer_id @Resolver.register -def get_replay_stream(plugin, product_id, **kwargs): +def get_stream(plugin, product_id, **kwargs): token = get_token(plugin) if not token: return False - replay_url = get_replay_url(plugin, product_id, token, **kwargs) - if not replay_url: + video_url, context, offer_id = get_stream_url(plugin, product_id, token, **kwargs) + stream_type = (context or '').upper() + + if not video_url: + if 'VOD' in stream_type: + xbmcgui.Dialog().ok(plugin.localize(30600), + plugin.localize(30732) % ('SFR TV', 'https://tv.sfr.fr')) + return False + if 'VOD' in stream_type: + vod_play_info = get_vod_play_info(plugin, offer_id, token) + entitlement_id = vod_play_info['entitlementId'] + custom_data = CUSTOMDATA_VOD.format(USER_AGENT, token, entitlement_id, stream_type) + else: + custom_data = CUSTOMDATA_REPLAY.format(USER_AGENT, token, stream_type) + headers = { 'User-Agent': USER_AGENT, - 'customdata': CUSTOMDATA_REPLAY.format(USER_AGENT, token), + 'customdata': custom_data, 'Referer': 'https://tv.sfr.fr/', 'content-type': 'application/octet-stream' } return resolver_proxy.get_stream_with_quality(plugin, - video_url=replay_url, + video_url=video_url, license_url=LICENSE_URL, manifest_type='mpd', headers=headers) +def get_vod_play_info(plugin, offer_id, token): + params = { + 'app': 'gen8', + 'device': 'browser', + 'token': token, + 'operators': 'sfr', + 'infrastructures': 'FTTH', + 'accountTypes': 'LAND', + 'noTracking': 'false' + } + headers = { + 'Accept': 'application/json', + 'Content-Type': 'application/json', + 'Referer': 'https://tv.sfr.fr/', + 'User-Agent': USER_AGENT + } + data = { + 'offerId': offer_id, + 'macAddress': 'PC', + 'app': 'gen8', + 'device': 'browser', + 'token': token + } + return urlquick.post(VOD_PLAY_URL, + params=params, + headers=headers, + json=data, + max_age=-1, + timeout=REQUEST_TIMEOUT).json() + + +@Route.register(autosort=False) +def list_vod_spots(plugin, **kwargs): + + menu_structure = get_menu_structure(plugin, VOD_MENU_ID) + + for spot in menu_structure['spots']: + + if spot.get('layout') == 'poster': + spot_content = get_spot_content(plugin, spot['id']) + content = spot_content['tiles'][0] + yield build_product_item(plugin, content) + continue + + if spot.get('title'): + label = spot['title'] + else: + spot_content = get_spot_content(plugin, spot['id']) + label = spot_content['title'] + + item = Listitem() + item.label = label + item.set_callback(list_spot_contents, spot['id']) + item_post_treatment(item) + yield item + + +@Route.register(autosort=False) +def list_spot_contents(plugin, spot_id, **kwargs): + token = get_token(plugin) + if not token: + yield False + return + + spot_content = get_spot_content(plugin, spot_id, token) + + for content in spot_content['tiles']: + yield build_product_item(plugin, content) + + +def get_spot_more_content(plugin, spot_id, token, page=0, size=MAX_CONTENTS): + params = { + 'app': 'gen8', + 'device': 'browser', + 'token': token, + 'page': page, + 'size': size, + 'operators': 'sfr', + 'infrastructures': 'FTTH', + 'accountTypes': 'LAND', + 'noTracking': 'false', + } + headers = { + 'Accept': 'application/json', + 'Referer': 'https://tv.sfr.fr/', + 'User-Agent': USER_AGENT + } + return urlquick.get(SPOT_MORE_URL.format(spot_id), + params=params, + headers=headers, + max_age=TOKEN_MAX_AGE, + timeout=REQUEST_TIMEOUT).json() + + +@Route.register(autosort=False) +def list_spot_more_contents(plugin, spot_id, page=0, **kwargs): + token = get_token(plugin) + if not token: + yield False + return + + spot_content = get_spot_more_content(plugin, spot_id, token, page) + contents = spot_content['tiles'] + + for content in contents: + yield build_product_item(plugin, content) + + if len(contents) == MAX_CONTENTS: # no "count" or "total" information + yield Listitem.next_page(spot_id=spot_id, + callback=list_spot_more_contents, + page=page + 1) + + +def get_tile_content(plugin, tile_id, token, page=0, size=MAX_CONTENTS): + params = { + 'app': 'gen8', + 'device': 'browser', + 'token': token, + 'page': page, + 'size': size, + 'operators': 'sfr', + 'infrastructures': 'FTTH', + 'accountTypes': 'LAND', + 'noTracking': 'false', + } + headers = { + 'Accept': 'application/json', + 'Referer': 'https://tv.sfr.fr/', + 'User-Agent': USER_AGENT + } + return urlquick.get(TILE_CONTENT_URL.format(tile_id), + params=params, + headers=headers, + max_age=TOKEN_MAX_AGE, + timeout=REQUEST_TIMEOUT).json() + + +@Route.register(autosort=False) +def list_tile_contents(plugin, tile_id, page=0, **kwargs): + token = get_token(plugin) + if not token: + yield False + return + + tile_content = get_tile_content(plugin, tile_id, token, page) + contents = tile_content['items'] + preferred_image_ratio = tile_content.get('preferredImageRatio') + + for content in contents: + yield build_product_item(plugin, content, preferred_image_ratio) + + if len(contents) == MAX_CONTENTS: # no "count" or "total" information + yield Listitem.next_page(tile_id=tile_id, + callback=list_tile_contents, + page=page + 1) + + def get_products_to_search(plugin, keyword, page=0, size=25, **kwargs): params = { 'app': 'gen8', @@ -684,7 +919,7 @@ def list_lives(plugin, **kwargs): item.label = serv['name'] for image in serv.get('images', []): - if image.get('type', '') == 'color': + if image.get('type') == 'color': item.art['thumb'] = item.art['landscape'] = image.get('url') # Playcount is useless for live streams