Приводим в порядок плейлист Торрент-ТВ («Суперпомойка»)

в 14:02, , рубрики: Android TV, iptv, кирпичная кладка, суперпомойка, Торрент-ТВ

Потоки в нелегальном сервисе Торрент-ТВ («Суперпомойка») в подавляющем большинстве являются оригинальными потоками от операторов или со спутника без пережатия. В основном это потоки с чересстрочным видео. Торрент-ТВ обеспечивает максимально возможное качество, но низкую стабильность. Сегодня одни каналы есть, завтра нет. Сегодня одни каналы работают хорошо, завтра плохо. Этот сервис не подходит для постоянного беспроблемного использования — за что заплатили, т.е. не платили, то и получили. Но он отлично подходит, когда нужно посмотреть что-то с высоким качество (если канал в тот момент будет работать стабильно). Ещё одна проблема — это формирование удобного персонального плейлиста. Об этом как раз и пойдёт речь в заметке.

Приводим в порядок плейлист Торрент-ТВ («Суперпомойка») - 1

Базовый плейлист Торрент-ТВ («Суперпомойка») содержит более 1200 каналов. Всевозможные региональные, разных стран, дубликаты SD и HD, разные мусорные каналы и пр. К российским зрителям относятся 200-300 каналов. При этом не всем удобна принятая группировка каналов. Казалось бы, можно взять плейлист от Торрент-ТВ, оформить его так, как требует душа перфекциониста. Проблема в том, что AceStream-ссылки на каналы меняются периодически, и подготовленный плейлист станет бесполезным. Т.е. нужна автоматическая генерации нового актуального плейлиста в удобном для вас виде. Именно это мы и сделаем.

Вам понадобится:

  • Опыт настройки программ для просмотра Торрент-ТВ («Суперпомойка») и знание, что это такое.
  • Маршрутизатор с поддержкой Entware (при необходимости и некоторых навыков вы легко адаптируете инструкцию под OpenWrt или Entware на самих боксах).

В заметке не затрагиваются вопросы:

  • Выбора и настройки IPTV-менеджера.
  • Настройки системы в целом для просмотра Торрент-ТВ.
  • Особенностей конкретных Android-боксов по работе с чересстрочным видео в общем и Торрент-ТВ в частности.

Мы будем использовать программу на маршрутизаторе, которая будет при запросе по ссылке загружать актуальный плейлист Торрент-ТВ, формировать из него новый плейлист и отдавать его:

  • Ваш собственный список каналов на базе подготовленного списка избранных каналов.
  • Вы можете автоматически исключить SD-каналы, если для них есть HD-соответствия.
  • Сортировка групп и их названия по вашему желанию.
  • Отображаемые название каналов по вашему желанию.
  • Сортировка каналов в группе по HD/SD (первично) и названию.
  • Встроенные ссылки на EPG-источники.
  • Источник логотипов и соответствие EPG для каналов по вашему желанию.

Как у вас всё будет работать после настройки?

Добавляете ссылку на плейлист http://192.168.0.1:81/playlist.cgi (замените 192.168.0.1 на внутренний адрес вашего маршрутизатора) в вашем IPTV-менеджере. Готово.

Приводим в порядок плейлист Торрент-ТВ («Суперпомойка») - 2

Установка необходимого ПО на маршрутизаторе

Подключитесь по SSH к маршрутизатору. В Windows для подключения подключения вы можете использовать клиент PuTTY.

Установите необходимое ПО:

opkg update
opkg install wget ca-certificates mc python3 lighttpd-mod-cgi

wget — программа для загрузки файлов. Она понадобится только для первоначальной настройки.

ca-certificates — сертификаты для wget.

mc — файловый менеджер Midnight Commander. Он нужен лишь из-за удобного редактора mcedit. Если вы привыкли пользоваться другим текстовым редактором, то mc можно не устанавливать.

python3 — интерпретатор Python.

lighttpd-mod-cgi — веб-сервер lighttpd.

Загрузка основных файлов

mkdir -p /opt/etc/ttv
wget --no-check-certificate -O /opt/etc/ttv/ttv.py https://raw.githubusercontent.com/Kyrie1965/ttv/master/ttv.py
wget --no-check-certificate -O /opt/share/www/playlist.cgi https://raw.githubusercontent.com/Kyrie1965/ttv/master/playlist.cgi
chmod +x /opt/share/www/playlist.cgi

Об их назначении я расскажу потом, а пока просто покажу содержимое. К коду Python сильно не придирайтесь, до этого момент я на Python никогда ничего не писал.

Содержимое /opt/etc/ttv/ttv.py

PLAYLIST_LOAD_URL = "http://91.92.66.82/trash/ttv-list/ttv.all.tag.player.m3u"
TEMPLATE_SAVE_PATH = "/opt/etc/ttv/template.txt"
FAVORITES_LOAD_PATH = "/opt/etc/ttv/favorites.txt"
PLAYLIST_SAVE_PATH = "/opt/etc/ttv/playlist.m3u"
LOGOS_URL = "https://raw.githubusercontent.com/AlexELEC/channel-logos/master/logos/{}.png"
#LOGOS_URL = "{}.png"
STREAM_URL = "http://127.0.0.1:6878/ace/getstream?id={}&.mp4"
#STREAM_URL = "acestream://{}"
EPG_LINKS = "https://teleguide.info/download/new3/xmltv.xml.gz"
#EPG_LINKS = "https://teleguide.info/download/new3/xmltv.xml.gz,http://programtv.ru/xmltv.xml.gz,http://api.torrent-tv.ru/ttv.xmltv.xml.gz"

import re
import urllib.request
import os
from operator import itemgetter as i
from functools import cmp_to_key
from urllib.parse import urlencode

def cmp(a, b):
	return (a > b) - (a < b) 
	
def multikeysort(items, columns):
	comparers = [
		((i(col[1:].strip()), -1) if col.startswith('-') else (i(col.strip()), 1))
		for col in columns
	]
	def comparer(left, right):
		comparer_iter = (
			cmp(fn(left), fn(right)) * mult
			for fn, mult in comparers
		)
		return next((result for result in comparer_iter if result), 0)
	return sorted(items, key=cmp_to_key(comparer))
	
def loadChannels(content):
	lines = content.splitlines()
	
	returnChannels = {}
	
	pattern = re.compile("group-title="(.*?)"")
	channelName = ""
	channelGroup = ""
	channelStreamID = ""
	waitURI = False
	
	for line in lines:
		if line.startswith("acestream"):
			if waitURI:
				channelStreamID = line[12:]
				HD = False
				if "HD" in channelName:
					HD = True
				tmpDict = {"name": channelName, "group": channelGroup, "stream": channelStreamID, "hd": HD}
				returnChannels[channelName] = tmpDict
				waitURI = False
		elif line.startswith("#EXTINF"):
			x = line.split("",")
			if (len(x) != 2):
				continue
			channelName = x[1]
			match = pattern.search(x[0])
			if match:
				channelGroup = match.group(1)
			else:
				channelGroup = "Общие"
			waitURI = True
	return returnChannels
	
def saveTemplate(content, channels, path):
	lines = content.splitlines()
	pattern = re.compile("group-title="(.*?)"")
	
	waitURI = False
	
	channelName = ""
	channelReplace = ""
	channelNewName = ""
	channelEPG = ""
	channelGroup = ""
	channelStreamID = ""
	groupDict = {}
	currentGroup = 1
	template=""
	for line in lines:
		if line.startswith("acestream"):
			if waitURI:
				channelStreamID = line[12:]
				template += channelName
				template += "/"
				template += channelReplace
				template += "/"
				template += channelNewName
				template += "/"
				template += channelEPG
				template += "/"
				template += channelGroup
				template += "n"
				waitURI = False
		elif line.startswith("#EXTINF"):
			x = line.split("",")
			if (len(x) != 2):
				continue
			channelName = x[1]
			channelNewName = x[1]
			channelEPG = x[1]
			if (channels.get(channelName + " HD") != None):
				channelReplace = channelName + " HD"
			else:
				channelReplace = "-"
			match = pattern.search(x[0])
			if match:
				channelGroup = match.group(1)
				if (groupDict.get(channelGroup)):
					channelGroup = groupDict.get(channelGroup)
				else:
					newGroupName = "{:02d}_{}".format(currentGroup, channelGroup)
					currentGroup += 1
					groupDict[channelGroup] = newGroupName
					channelGroup = newGroupName
			else:
				channelGroup = "00_Unsigned"
			waitURI = True
	file = open(path,'w', encoding='utf-8')
	file.write(template)
	file.close()
	return

def loadFavorites(content):
	returnChannels = {}
	lines = content.splitlines()
	for line in lines:
		parts = line.split('/')
		if len(parts) == 5:
			tmpDict = {"name": parts[0], "replace": parts[1], "newName": parts[2], "EPG": parts[3], "group": parts[4]}
			returnChannels[parts[0]] = tmpDict
	return returnChannels

def savePlaylist(channels, favorites, path):
	returnChannels = []
	currentChannels = set()
	for key, chDict in favorites.items():
		if chDict["replace"] != "-":
			if favorites.get(chDict["replace"]) != None and channels.get(chDict["replace"]) != None:
				currentChannels.add(chDict["replace"])
			elif channels.get(chDict["name"]) != None:
				currentChannels.add(chDict["name"])
		elif channels.get(chDict["name"]) != None:
			currentChannels.add(chDict["name"])
	for ch in currentChannels:
		chFromFavorites = favorites.get(ch)
		chFromChannels = channels.get(ch)
		tmpDict = {"name": chFromFavorites.get("newName"), "oldName": chFromFavorites.get("name"), "EPG": chFromFavorites.get("EPG"), "group": chFromFavorites.get("group"), "stream": chFromChannels.get("stream"), "hd": chFromChannels.get("hd")}
		returnChannels.append(tmpDict)
	result = multikeysort(returnChannels, ['group', '-hd', 'name'])
	template=""
	template += "#EXTM3U url-tvg="
	template += """
	template += EPG_LINKS
	template += """
	template += "n"
	for n in result:
		group = n.get("group")
		if group.find("_", 2, 3) != -1:
			group = group[3:]
		template += "#EXTINF:-1 tvg-name="{}" tvg-logo="{}" group-title="{}" ,{}".format(n.get("EPG"), LOGOS_URL.format(urllib.parse.quote(n.get("oldName"))), group, n.get("name"))
		template += "n"
		template += STREAM_URL.format(n.get("stream"))
		template += "n"
	file = open(path,'w', encoding='utf-8')
	file.write(template)
	file.close()
	return result
	
response = urllib.request.urlopen(PLAYLIST_LOAD_URL)
content = response.read().decode("utf-8")
channels = loadChannels(content)

if channels == None or (len(channels.keys()) == 0):
	exit()

saveTemplate(content, channels, TEMPLATE_SAVE_PATH)

exists = os.path.isfile(FAVORITES_LOAD_PATH)

if exists:
	file = open(FAVORITES_LOAD_PATH,'r', encoding='utf-8')
	content = file.read()
	favorites = loadFavorites(content)
	savePlaylist(channels, favorites, PLAYLIST_SAVE_PATH)

Содержимое /opt/share/www/playlist.cgi

#!/bin/sh
PATH=/opt/sbin:/opt/bin:/opt/usr/sbin:/opt/usr/bin:/usr/sbin:/usr/bin:/sbin:/bin

python3 /opt/etc/ttv/ttv.py
echo "Content-Type: text/plain; charset=UTF-8"
echo ""
echo "$(cat /opt/etc/ttv/playlist.m3u)"

Конфигурация и запуск веб-сервера

Откройте файл /opt/etc/lighttpd/lighttpd.conf:

mcedit /opt/etc/lighttpd/lighttpd.conf

Чтобы вставить из буфера, используйте Shift+Insert, сохранить — F2, выйти — F10.

Измените строку #server.port = 80 на:

server.port = 81

Откройте файл /opt/etc/lighttpd/conf.d/30-cgi.conf:

mcedit /opt/etc/lighttpd/conf.d/30-cgi.conf

Измените ".cgi" => "/opt/bin/perl" на:

".cgi" => "/bin/sh"

Запустите веб-сервер:

/opt/etc/init.d/S80lighttpd start

Создание списка избранных каналов

Запустите программу ttv.py:

python3 /opt/etc/ttv/ttv.py

В папке /opt/etc/ttv будет создан шаблонный файл template.txt. Это простой текстовый файл. Он всегда будет актуальный, т.е. при запуске программы он перезаписывается с актуальными данными.

Каждая строка в этом файле соответствует одному каналу Торрент-ТВ и имеет вид:
НАЗВАНИЕ_КАНАЛА/ЗАМЕНА_КАНАЛА/НОВОЕ_НАЗВАНИЕ_КАНАЛА/НАЗВАНИЕ_КАНАЛА_В_EPG/ГРУППА

Например:

Amedia Premium/Amedia Premium HD/Amedia Premium/Amedia Premium/11_Фильмы
Amedia Premium HD/-/Amedia Premium HD/Amedia Premium HD/11_Фильмы

Сохраните этот файл на компьютере для удобного редактирования (например, с помощью WinSCP). Переименуйте его в favorites.txt.

Отредактируйте файл, оставляя только те каналы, которые вам нужны.

НАЗВАНИЕ_КАНАЛА — название канала в оригинальном плейлисте.

ЗАМЕНА_КАНАЛА — название канала в оригинальном плейлисте для замены. Поставьте "-", если замена не нужна. По умолчанию в шаблоне автоматически подставляются замены, если у канала есть HD-вариант. Например, в оригинальном плейлисте присутствуют Amedia Premium и Amedia Premium HD. В финальном плейлисте будет только Amedia Premium HD.

НОВОЕ_НАЗВАНИЕ_КАНАЛА — отображаемое название в IPTV-менеджере. Оно может быть любым и влияет только на выводимое названия в IPTV-менеджере. Например, оригинальный канал имеет название «Paramount Comedy HD (Россия)», а вы его переименовываете в «Paramount Comedy HD».

НАЗВАНИЕ_КАНАЛА_В_EPG — это нужно для полного соответствия в выбранном источнике EPG. Например, оригинальный канала называется «Матч ТВ HD». А в EPG этот канал называется «Матч!». Меняете этот параметр на «Матч!» и получаете полное соответствие для вашего источника EPG. Таким образом образом для всех каналов вы можете приблизить соответствие EPG к 100%.

ГРУППА — название группы канала. Вы можете использовать индекс, двузначное число, перед названием группы. Этот индекс определяет порядок групп в финальном плейлисте (от меньшего к большему). После сортировки индекс будет автоматически убран из названия группы.

Вот пример отредактированного файла favorites.txt

Amedia Premium/Amedia Premium HD/Amedia Premium/Amedia Premium/11_Фильмы и сериалы
Amedia Premium HD/-/Amedia Premium HD/Amedia Premium HD/11_Фильмы и сериалы
Дождь/Дождь HD/Дождь/Дождь/12_Общие
Дождь HD/-/Дождь HD/Дождь HD/12_Общие
Viasat History/-/Viasat History/Viasat History/01_Познавательные
Discovery Channel/Discovery Channel HD/Discovery Channel/Discovery Channel/01_Познавательные
Discovery Channel HD/-/Discovery Channel HD/Discovery Channel HD/01_Познавательные
Discovery Science/Discovery Science HD/Discovery Science/Discovery Science/01_Познавательные
Discovery Science HD/-/Discovery Science HD/Discovery Science HD/01_Познавательные
Amedia Hit/Amedia Hit HD/Amedia Hit/Amedia Hit/11_Фильмы и сериалы
Amedia Hit HD/-/Amedia Hit HD/Amedia Hit HD/11_Фильмы и сериалы
Матч ТВ/Матч ТВ HD/Матч!/Матч!/03_Спортивные
Матч ТВ HD/-/Матч! HD/Матч!/03_Спортивные
Eurosport 1/Eurosport 1 HD/Eurosport 1/Eurosport 1/03_Спортивные
Eurosport 1 HD/-/Eurosport 1 HD/Eurosport 1 HD/03_Спортивные
Paramount Comedy HD (Россия)/-/Paramount Comedy HD/Paramount Comedy HD (Россия)/11_Фильмы и сериалы
Матч! Футбол 1/Матч! Футбол 1 HD/Матч! Футбол 1/Матч! Футбол 1/03_Спортивные
Матч! Футбол 1 HD/-/Матч! Футбол 1 HD/Матч! Футбол 1 HD/03_Спортивные
Россия 1/Россия HD/Россия 1/Россия 1/13_Зомби-пропаганда
Россия HD/-/Россия HD/Россия HD/13_Зомби-пропаганда

Скопируйте подготовленный файл favorites.txt в папку /opt/etc/ttv на маршрутизаторе.

Всё готово. Вы можете забирать плейлист по ссылке http://192.168.0.1:81/playlist.cgi (замените 192.168.0.1 на внутренний адрес вашего маршрутизатора). Просто указываете ссылку в IPTV-менеджере и у вас всегда будет ваш актуальный плейлист. Скрипт playlist.cgi запускает программу ttv.py, которая на базе актуального плейлиста Торрент-ТВ (загружается в момент обращения) и favorites.txt генерирует новый плейлист, а потом отдаёт этот новый плейлист.

На выходе, если все каналы будут присутствовать в актуальном плейлисте (в противном случае отсутствующие каналы будут проигнорированы), вы получите такой аккуратный плейлист:

Плейлист

#EXTM3U url-tvg="https://teleguide.info/download/new3/xmltv.xml.gz"
#EXTINF:-1 tvg-name="Discovery Channel HD" tvg-logo="https://raw.githubusercontent.com/AlexELEC/channel-logos/master/logos/Discovery%20Channel%20HD.png" group-title="Познавательные" ,Discovery Channel HD
http://127.0.0.1:6878/ace/getstream?id=3fedfe54dd73c4c8790dd196178aeeb9fc2af955&.mp4
#EXTINF:-1 tvg-name="Discovery Science HD" tvg-logo="https://raw.githubusercontent.com/AlexELEC/channel-logos/master/logos/Discovery%20Science%20HD.png" group-title="Познавательные" ,Discovery Science HD
http://127.0.0.1:6878/ace/getstream?id=03fb28986da9168dd56ec6891253bcc496c13eb5&.mp4
#EXTINF:-1 tvg-name="Viasat History" tvg-logo="https://raw.githubusercontent.com/AlexELEC/channel-logos/master/logos/Viasat%20History.png" group-title="Познавательные" ,Viasat History
http://127.0.0.1:6878/ace/getstream?id=59ccac1786b71ed0f8b4a27160653a1fd3cb2807&.mp4
#EXTINF:-1 tvg-name="Eurosport 1 HD" tvg-logo="https://raw.githubusercontent.com/AlexELEC/channel-logos/master/logos/Eurosport%201%20HD.png" group-title="Спортивные" ,Eurosport 1 HD
http://127.0.0.1:6878/ace/getstream?id=d98ac49ba0fe473f78e894eb6500cc2f7ac4e42a&.mp4
#EXTINF:-1 tvg-name="Матч!" tvg-logo="https://raw.githubusercontent.com/AlexELEC/channel-logos/master/logos/%D0%9C%D0%B0%D1%82%D1%87%20%D0%A2%D0%92%20HD.png" group-title="Спортивные" ,Матч ТВ HD
http://127.0.0.1:6878/ace/getstream?id=b64817f8953dbeb35c6c64936e06d0d1ce16fe38&.mp4
#EXTINF:-1 tvg-name="Матч! Футбол 1 HD" tvg-logo="https://raw.githubusercontent.com/AlexELEC/channel-logos/master/logos/%D0%9C%D0%B0%D1%82%D1%87%21%20%D0%A4%D1%83%D1%82%D0%B1%D0%BE%D0%BB%201%20HD.png" group-title="Спортивные" ,Матч! Футбол 1 HD
http://127.0.0.1:6878/ace/getstream?id=65f70b33490d5cbb4d7e7881dc9cf38e4d586c20&.mp4
#EXTINF:-1 tvg-name="Amedia Hit HD" tvg-logo="https://raw.githubusercontent.com/AlexELEC/channel-logos/master/logos/Amedia%20Hit%20HD.png" group-title="Фильмы и сериалы" ,Amedia Hit HD
http://127.0.0.1:6878/ace/getstream?id=84e509d0e80ec7daaa46096b722138a3c3804cd8&.mp4
#EXTINF:-1 tvg-name="Amedia Premium HD" tvg-logo="https://raw.githubusercontent.com/AlexELEC/channel-logos/master/logos/Amedia%20Premium%20HD.png" group-title="Фильмы и сериалы" ,Amedia Premium HD
http://127.0.0.1:6878/ace/getstream?id=7a2ac5852a6b03f294c8a8dfde1cda6e8d844317&.mp4
#EXTINF:-1 tvg-name="Paramount Comedy HD (Россия)" tvg-logo="https://raw.githubusercontent.com/AlexELEC/channel-logos/master/logos/Paramount%20Comedy%20HD%20%28%D0%A0%D0%BE%D1%81%D1%81%D0%B8%D1%8F%29.png" group-title="Фильмы и сериалы" ,Paramount Comedy HD
http://127.0.0.1:6878/ace/getstream?id=431e5fb85b1b47ed1dc6d873b10587a926acdc48&.mp4
#EXTINF:-1 tvg-name="Дождь" tvg-logo="https://raw.githubusercontent.com/AlexELEC/channel-logos/master/logos/%D0%94%D0%BE%D0%B6%D0%B4%D1%8C.png" group-title="Общие" ,Дождь
http://127.0.0.1:6878/ace/getstream?id=56b9f4a6e8fc3269d63aecf650ef5baac4d7b256&.mp4
#EXTINF:-1 tvg-name="Россия HD" tvg-logo="https://raw.githubusercontent.com/AlexELEC/channel-logos/master/logos/%D0%A0%D0%BE%D1%81%D1%81%D0%B8%D1%8F%20HD.png" group-title="Зомби-пропаганда" ,Россия HD
http://127.0.0.1:6878/ace/getstream?id=4c9a8a67fc57ab547db3581a62d2d6d1b2159b11&.mp4

Познавательные:
    Discovery Channel HD
    Discovery Science HD
    Viasat History
Спортивные:
    Eurosport 1 HD
    Матч! HD
    Матч! Футбол 1 HD
Фильмы и сериалы:
    Amedia Hit HD
    Amedia Premium HD
    Paramount Comedy HD
Общие:
   Дождь
Зомби-пропаганда:
   Россия HD

В случае необходимости вы можете изменить некоторые параметры в программе ttv.py:

mcedit /opt/etc/ttv/ttv.py

Параметры, доступные для изменения

PLAYLIST_LOAD_URL = "http://91.92.66.82/trash/ttv-list/ttv.all.tag.player.m3u"
TEMPLATE_SAVE_PATH = "/opt/etc/ttv/template.txt"
FAVORITES_LOAD_PATH = "/opt/etc/ttv/favorites.txt"
PLAYLIST_SAVE_PATH = "/opt/etc/ttv/playlist.m3u"
LOGOS_URL = "https://raw.githubusercontent.com/AlexELEC/channel-logos/master/logos/{}.png"
#LOGOS_URL = "{}.png"
STREAM_URL = "http://127.0.0.1:6878/ace/getstream?id={}&.mp4"
#STREAM_URL = "acestream://{}"
EPG_LINKS = "https://teleguide.info/download/new3/xmltv.xml.gz"
#EPG_LINKS = "https://teleguide.info/download/new3/xmltv.xml.gz,http://programtv.ru/xmltv.xml.gz,http://api.torrent-tv.ru/ttv.xmltv.xml.gz"

PLAYLIST_LOAD_URL — ссылка на загрузку актуального плейлиста Торрент-ТВ.
TEMPLATE_SAVE_PATH — путь для сохранения шаблона.
FAVORITES_LOAD_PATH — путь для загрузки списка избранных каналов.
PLAYLIST_SAVE_PATH — путь для сохранения нового плейлиста.
LOGOS_URL — ссылка с логотипами каналов.
STREAM_URL — вид ссылки на поток в финальном плейлисте. Это может быть прямая ссылка на Ace Stream или ссылка на Ace Stream Proxy. Зависит от того, какой IPTV-менеджер вы используете.
EPG_LINKS — список ссылок EPG. Все IPTV-менеджеры разные. Есть те, которые не умеют брать ссылку на EPG из плейлиста. Есть те, которые поддерживают только один источник EPG из плейлиста. Есть тем, которые не умееют объединять EPG из разных источников.

Вам будут интересны лишь STREAM_URL, LOGOS_URL, EPG_LINKS, т.к. от них зависит вид финального плейлиста. Для них даны альтернативные примеры в файле.

Заключение

В итоге вы получаете актуальный плейлист Торрент-ТВ («Суперпомойка») с собственным списком каналов, с собственными названиями каналов, с собственными группами и их произвольной сортировкой, собственными логотипами, с собственным сопоставлением EPG, с возможностью исключать дубликаты HD/SD, с сортировкой каналов внутри групп по HD/SD и названию. Нужно лишь один раз всё настроить и один раз создать файл избранных каналов. А далее все устройства в вашей домашней сети будут получать нужный плейлист по ссылке, делая «всё красиво» сразу без дополнительных настроек.

Автор: Kyrie1965

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js