Настраиваем просмотр IPTV в Plex Media Server
Телевизор в 2022 году это совершенно другая сущность, если сравнивать и смотреть со стороны даже 2010-х годов. Сейчас мало кто использует телевизор в обычном его понимании — приложения Smart TV, всевозможные сервисы, подписки или домашние медиацентры с сетевыми хранилищами. Телевизор сейчас — это всего лишь экран для отображения картинки. А телевидение так и вообще ушло на второй план или вовсе не интересно пользователю.
А если все же идти в ногу со временем и иметь домашний медиацентр на базе Plex Media Server, но в тоже время не прочь пролистать ТВ-каналы, как в старые добрые времена?
Задача легко решаема, если в вашем обиходе есть DVR-тюнер или провайдер, поддерживаемый Plex. И второй вариант — более современный, без проводов и, собирающих пыль, ресиверов — IPTV с недорогим плейлистом и хорошим телегидом. Именно на этом варианте мы и остановимся. Приступаем!
1. Исходные данные и немного теории
Мой кейс основан на операционной системе Kubuntu 20.04 и Plex Media Server версии 1.25.3.5385, m3u-плейлисте с IPTV-каналами от сервиса zedom.net за $1/месяц и EPG/Picons от epg.it999.ru. Поэтому все манипуляции по установке и настройке будут ориентированы именно на Linux, а также конкретного провайдера IPTV. Но данное решение кроссплатформенное и его можно без проблем реализовать на других платформах (FreeBSD, OS X, Windows).
Так как в Plex Media Server нет возможности прямым способом подключить m3u-плейлист, ведь он ожидает подключения физического тюнера или антенны, нам придется немного изловчиться — будем вещать для Plex через программный ресивер xTeVe. Важно учесть, что Plex не может принимать поток в формате HLS.
HLS (m3u8) — расшифровывается как HTTP Live Streaming. В этой структуре сервер вещания делит поток на отдельные отрезки mp4 по 10 секунд. Это очень удобно, когда провайдеру требуется потоковое вещание в нескольких вариантах качества, чтобы зритель мог использовать подходящий битрейт видео с учетом пропускной способности сети. HLS был изобретен и использовался Apple на своих устройствах. Единственным недостатком является то, что HLS имеет задержку от 10 до 30 секунд.
MPEG-TS (ts) — состоит из двух частей: MPEG и TS. MPEG — потоковое видео, TS- транспортный поток. Этот формат может предоставлять аудио, видео и метаданные, такие как субтитры и EPG. MPEG имеет функцию исправления ошибок, чтобы сохранить целостность видео при низком уровне сигнала.
Именно в HLS-формате вещают практически все современные IPTV-провайдеры, без возможности смены этого формата. Мой провайдер не исключение. Поэтому, необходимо будет на входе преобразовывать HLS-эфир и отдавать в Plex уже в нужном формате MPEG-TS. Для этого этапа будем использовать HLS Proxy.

Воспринимается сложно и громоздко, но на практике все предельно просто и не сложно реализуется. С теорией разобрались — приступаем к работе.
2. Установка Plex Media Server
Если сервер Plex у вас уже развернут — пропускаем этот шаг. Для новой установки необходимо добавить ключ от репозитория downloads.plex.tv и сам репозиторий:
echo deb https://downloads.plex.tv/repo/deb public main | sudo tee /etc/apt/sources.list.d/plexmediaserver.list wget -O - https://downloads.plex.tv/plex-keys/PlexSign.key | sudo apt-key add - sudo apt-get update && sudo apt -y install plexmediaserver
По завершении установки убедимся, что сервер корректно установлен и запущен.
systemctl status plexmediaserver

3. Подготовка плейлиста IPTV
С регистрацией и оплатой на сервисе ZeDom, думаю, проблем возникнуть не должно. Описывать подробно это не буду. Первые сутки, кстати, можно попробовать бесплатно.
В личном кабинете в разделе «Плейлист» рекомендую выбрать только нужные вам группы каналов (много зарубежных, не «русскоговорящих»). Помимо выбора групп, также рекомендую пройтись и по самим каналам, отключив не нужные. Сделать это можно в разделе «Конструктор». Так, например, изначально мой плейлист составлял 1800+ каналов. После выключения лишних осталось 480 каналов.
480 каналов — это не случайное число. Именно такое ограничение по количеству каналов в xTeVe. Это обязательно нужно учесть, иначе не сможете добавить ни один канал.
Плейлист можно использовать как локальным m3u-файлом, так и ссылкой — значения не имеет. Я буду использовать файл с плейлистом, ввиду периодических блокировок провайдером.
4. Установка и настройка HLS Proxy
Загружаем с сайта архив с последней стабильной версией для платформы linux x64. На момент написания публикации актуальная версия — 8.0.7.
wget -P /tmp https://www.hls-proxy.com/downloads/8.0.7/hls-proxy-8.0.7.linux-x64.zip
Разместим HLS Proxy в директорию /opt/hls-proxy, предварительно создав этот каталог и назначив права.
sudo mkdir /opt/hls-proxy sudo chmod -R 0777 /opt/hls-proxy unzip /tmp/hls-proxy-8.0.7.linux-x64.zip -d /opt/hls-proxy
Делаем исполняемыми два файла — hls-proxy и install.sh
chmod +x /opt/hls-proxy/hls-proxy install.sh
Устанавливаем HTTP-порт 8080 (либо любой другой, если 8080 уже занят) для HLS Proxy. Либо вообще можно не настраивать порт, по умолчанию используется 80.
/opt/hls-proxy/hls-proxy -port 8080 -save -quit
И устанавливаем сам HLS Proxy с помощью скрипта. Он все сделает автоматически, в том числе добавит юнит в подсистему systemd для автозапуска
/opt/hls-proxy/install.sh
По завершении установки проверяем, запущен ли сервис hls-proxy
sudo systemctl status hls-proxy

Теперь можно перейти к веб интерфейсу HLS Proxy
http://ip_адрес_сервера:порт
Если порт оставили по умолчанию, то просто переходим по IP-адресу или localhost в браузере. Если все успешно, то вы должны увидеть вот такой незамысловатый интерфейс

Переходим к настройке сервера («Открыть конфиг») и в разделе «Плейлисты» указываем путь к плейлисту от zedom.net. Так как я буду использовать локальный файл, указываю тип -«file» и место расположение файла /opt/hls-proxy/zedom.m3u.

В разделе «Телегид» указываем путь к EPG. Я лично использую программу с качественными логотипами каналов от it999.ru.

Остальные настройки можно оставить по умолчанию. Сохраняем и переходим на главную страницу. Кликаем «Перезагрузить плейлист» и «Обновить телегид».

Если вы все сделали правильно, то должен отобразиться список ваших каналов, с соответствующими логотипами и телепрограммой.

На этом подготовка HLS Proxy завершена, для дальнейшей работы с ним нам понадобится две ссылки на него:
- http://адрес_сервера:порт/?output=ts — это наш IPTV-плейлиcт уже в нужном формате
- http://адрес_сервера:порт/epg — телегид
5. Установка и настройка xTeVe
Следующим шагом необходимо установить xTeVe и получить в нем весь наш плейлист из HLS Proxy. Для этого загружаем со страницы проекта в GitHub архив, распаковываем и размещаем в каталоге /opt/ и делаем исполняемым бинарный файл xteve.
wget -P /tmp https://github.com/xteve-project/xTeVe-Downloads/blob/master/xteve_linux_amd64.zip unzip /tmp/xteve_linux_amd64.zip -d /opt/ sudo chmod +x /opt/xteve
Запустить xTeVe можно вручную выполнив /opt/xteve, а для запуска xTeVe при старте системы создаем юнит в подсистеме systemd
sudo mcedit /etc/systemd/system/xteve.service
[Unit] Description=xTeVe Service Wants=network-online.target After=network-online.target [Service] Type=simple ExecStart=/opt/xteve ExecReload=/usr/bin/killall xteve ExecStop=/usr/bin/killall xteve KillMode=process Restart=always RestartSec=15 [Install] WantedBy=multi-user.target
Перечитываем изменения, добавляем символическую ссылку в автозапуск и запускаем сервис
sudo systemctl daemon-reload sudo systemctl enable xteve sudo systemctl start xteve
Убедимся, что юнит создан правильно и сервис запущен
sudo systemctl status xteve

Веб-интерфейс xTeVe будет доступен по адресу
Приступаем к настройке. Количество тюнеров можно выставить максимальное, это позволит просматривать трансляции параллельно, на нескольких устройствах одновременно. Но не стоит забывать об ограничениях IPTV-провайдера. Например, zedom допускает одновременный просмотр только на двух устройствах.

Тип источника EPG — XEPG.

Указываем адрес к плейлисту в формате MPEG-TS на нашем сервере HLS Proxy, обязательно используя параметр ?output=ts.

Указываем путь к EPG на сервере HLS Proxy.

Если вы прежде все делали правильно, то xTeVe должен обработать плейлист и EPG. В сводке сверху страницы будут указаны доступные каналы и пути для подключения клиентами.

Также в разделе «Mapping» можно увидеть все подключенные каналы с их логотипами.

Все подготовительные работы выполнены, в конечном результате мы получили IPTV-плейлист в MPEG-TS формате, доступный по пути:
- http://ip_адрес_сервера:34400 — IP-адрес нашего программного DVR-ресивера
- http://ip_адрес_сервера:34400/xmltv/xteve.xml — телегид
6. Подключение xTeVe в Plex Media Server
Переходим в настройки аккаунта и раздел «Прямой эфир и записи» и добавляем наш ресивер.



Plex без проблем обнаруживает наш тюнер и все доступные каналы. Страну не выбираем, а нажимаем на гиперссылку «Использовать телегид для XMLTV» и указываем адрес к EPG на xTeVe (http://ip_адрес_сервера:34400/xmltv/xteve.xml).




Ожидаем, пока Plex обработает весь список каналов и загрузит EPG. Первое выполнение займет достаточно много времени.



В HLS Proxy можно наблюдать сводную информацию о текущем потоке во время просмотра, а также детальное логирование процесса.

Мобильный клиент Plex на iOS также прекрасно справляется с просмотром IPTV, без торможений и фризов, с детальным телегидом и «ровными» логотипами каналов.


Заключение
Все готово, теперь можно наслаждаться просмотром ТВ с домашним сервером Plex, без громоздких и пыльных ресиверов, проводов и практически даром! Да, реализация немного усложнена, ввиду определенных особенностей, как самого Plex, так и формата вещания IPTV-сервиса.
Помимо трудностей в реализации, такая схема вещания влечет за собой еще и потребность в больших ресурсах сервера. Самым «прожорливым» до оперативной памяти в этой цепочке является xTeVe — использует порядка 850 Мб, что для домашнего медиацентра может оказаться критичным. HLS Proxy менее требовательный и даже во время просмотра канала использует около 180 Мб. Если же сравнивать Plex, например, с Kodi, то он с PVR IPTV Simple Client просмотр IPTV решает гораздо проще и менее ресурсоемко.

Еще одним недостатком просмотра IPTV в Plex из m3u является повышенная задержка при переключении каналов. В среднем, переключение занимает порядка 5 секунд, оно и понятно, ведь путь и преобразования, которые проходит эфир от входа до стрима на конечном устройстве не малый. Не влияет на задержку и качество самого эфира — нет разницы при переключении FHD или HD-каналов. Также на уровне погрешности разница в задержке при переключении каналов на локальном хосте или же при стриминге на конечное устройство по Wi-Fi — то есть, канал связи практически не влияет скорость переключения. Виновница задержки, определенно, «гирлянда» из посредников в лице HLS Proxy и xTeVe.
Запуск IPTV на приставке TVIP S-Box v.605
1. Введите имя пользователя и пароль при помощи «стрелок» и «ОК» или кнопок с цифрами на пульте. Имя пользователя и пароль вам будут присвоены при покупке медиацентра.

2. После входа в систему примите пользовательское соглашение.

Запуск приложения
1. Для запуска IPTV выберите «Телевидение» на главном экране.

2. Выберите один из рекомендованных профилей пользователя. Для добавления нового профиля нажмите “+”. Чтобы удалить профиль, нажмите красную кнопку в правом нижнем углу экрана.

3. Добавьте один или несколько профилей из списка. Теперь при запуске приложения под своим профилем вы сможете видеть историю просмотренных каналов, фильмов и передач, а также добавлять их в «Избранное». На основе вашей истории сервис самостоятельно будет предлагать вам новинки к просмотру.

- В детском профиле отображается только контент с возрастным ограничением 12+;
- В профиле «Инкогнито» вам доступен весь список каналов, при этом история просмотров не сохраняется;
- Если кроме вас никто не использует приложение, создайте профиль с автоматической загрузкой контента. Выберите раздел Настройки — Ограничения — Профиль по умолчанию.
4. Наведите фокус на профиль, который собираетесь использовать и нажмите «ОК». После этого вы попадете на главный экран приложения.

- Первые 3 ряда — история просмотров: последние каналы, передачи и фильмы, которые вы посмотрели или не закончили смотреть, и можете продолжить.
- Далее идут рекомендации на основе ваших предпочтений: самые актуальные фильмы, сериалы и передачи. Для каждого профиля — свои подборки.
- Нажмите на обложку любого фильма или передачи в одной из подборок, чтобы увидеть описание и доступные выпуски/серии.
Меню
Чтобы перейти в меню, нажмите стрелку ВЛЕВО на пульте один или несколько раз. Вы увидите вертикальное меню разделов приложения.

Телеканалы
1. Наведите курсор на «Телеканалы» и нажмите на кнопку Enter (OK), чтобы открыть интерфейс выбора категорий, каналов и программ.
2. Чтобы открыть раздел «Телеканалы» в момент просмотра ТВ-программ, нажмите на пульте стрелку ВВЕРХ.

3. Для выбора категории канала (избранные, все каналы, эфирные и т.д.) нажмите на пульте стрелку ВНИЗ или ВВЕРХ.
4. Чтобы выбрать нужный канал в категории, нажмите стрелку ВПРАВО.
5. После выбора канала выберите понравившуюся программу или фильм с помощью нажатия стрелки ВПРАВО.
6. Чтобы увидеть описание выбранной программы или фильма, нажмите стрелку ВПРАВО.
7. В данном разделе также доступны кнопки «Смотреть» и «Серии»:
- Нажмите «Смотреть», чтобы начать воспроизведение передачи или фильма;
- Нажмите «Серии», чтобы прочитать описание и список доступных в архиве выпусков/серий;
- Чтобы перейти к списку выпусков/серий, нажмите на пульте стрелку ВНИЗ во время просмотра любого контента;
- Чтобы увидеть информацию о текущей передаче/фильму и подсказки по управлению, нажмите «OK».

8. Для добавления или удаления канала в/из категории «Избранное» нажмите синюю кнопку на пульте. Другие функции пульта:
- Короткое нажатие «ОК» — запуск эфира при нажатии на канал или открытие описания при нажатии на название программы или фильм;
- Клавиши P+/P (CH+/CH-) — постранично пролистывают большие списки, например, список каналов или передач;
- Желтым шрифтом выделено название текущего телеканала и передачи;
- Звездочка напротив названия телеканала — канал добавлен в «Избранное».
9. Для перемотки нажмите на пульте стрелку ВЛЕВО или ВПРАВО. Останавитесь на нужном кадре и нажмите «ОК» для воспроизведения с этого момента. Чтобы остановить перемотку и вернуться к просмотру эфира или архива ТВ, нажмите клавишу ВЛЕВО или ВПРАВО. Меню исчезнет в течение нескольких секунд.

Поиск
Выберите раздел «Поиск», чтобы найти нужный фильм или передачу. Поиск осуществляется по всем разделам сервиса (архив и каталоги кинотеатров).

Избранное
Для добавления канала в «Избранное» наведите на него курсор и нажмите синюю кнопку на пульте. Напротив названия канала появится звёздочка, а в категории «Избранное» будут только любимые телеканалы. Обратите внимание, что для каждого профиля — свой набор избранных каналов. Аналогичным образом в «Избранное» добавьте передачи в разделе «Архив» и фильмы в кинотеатрах.
Архив
1. Нажмите стрелку ВЛЕВО, чтобы открыть меню. Выберите раздел «Архив».

2. Выберите интересующую категорию. Внутри неё выберите жанр, а затем фильм/сериал/передачу.

3. Чтобы прочитать описание фильма/передачи и все выпуски, нажмите «ОК».
Одна и та же программа или фильм может быть записана несколько раз, т.к. идет в разное время на разных каналах. Выберите наиболее удобный вариант для просмотра. Например, если фильм шёл несколько раз на канале с рекламой, то выбрав ночную трансляцию, вам, возможно, придётся перематывать меньше. Также при выборе может иметь значение HD или SD версия канала.
4. Чтобы начать воспроизведение, выберите нужную запись. Если часть передачи была просмотрена, нажмите «Продолжить просмотр» или «Смотреть сначала».

Выбор качества изображения
В разделе Настройки / Основные доступен выбор качества изображения.

- При выборе «Авто» на всех каналах будет доступен HLS поток с адаптивным битрейтом, оптимальный для нестабильного канала связи;
- «Наилучшее» — это оригинальный UDP поток, «Хорошее» — он же, сжатый до 1,5–4 Мбит/с. «Наилучшее» качество позволяет максимально приблизить качество картинки к исходному качеству телесигнала.
Режим «Только ТВ»
В разделе Настройки / Ограничения для любого устройства и профиля доступен режим «Только ТВ»:
- Предназначен для пользователей, которым сложно осваивать новые интерфейсы;
- Доступен только просмотр каналов, переключение каналов вперёд-назад, по номерам и переход к списку каналов с категориями (стрелка ВВЕРХ).

Нажмите НАЗАД, чтобы выйти из приложения.
Готово! Вы настроили IPTV и теперь можете наслаждаться просмотром любимых фильмов и передач когда угодно.
- Вход в личный кабинет
- Для дома
- Интернет и ТВ
- Интерактивное телевидение
- Акции
- Новости
- Техработы
- Поддержка
- Трансляция
- Телепрограмма
- Список каналов
- О компании
- Документы
- Социальные проекты
- Вакансии
- Политика конфиденциальности
- Интернет и ТВ
- Интерактивное телевидение
- Акции
- Офисы продаж
- Служба поддержки
- Оценка мастеров
- Написать директору
© Подряд, 1997-2024 Разработка сайта — студия House
- Почему подряд?
- О компании
- Документы
- Социальные проекты
- Вакансии
- Политика конфиденциальности
- Подключение
- Интернет и ТВ
- Интерактивное телевидение
- Акции
- Камеры
- Домофоны
- Кондиционеры
- Связаться
- Офисы продаж
- Служба поддержки
- Оценка мастеров
- Написать директору
Пиратское телевидение: если очень хочется, значит, можно

Помнится, было время, когда образованные люди на полном серьезе обсуждали, не вытеснит ли телевидение театр и не заменит ли оно со временем книги. Сегодня эти дискуссии кажутся немного наивными, хотя сказать, что телевидение в жизни людей играет ту же роль, что и лет тридцать назад, — нельзя. Оно изменилось и уже не значит так много, как раньше. Доверие к телевидению как к источнику информации потеряно.
Цель большинства «доступных» телевизионных каналов — навязать свою точку зрения, преподнести новостные события в определенном ракурсе, с выгодой для той или иной политической силы. Телевидение давно потеряло уважение у думающего населения, которое все меньше и меньше смотрит «зомбоящик», отдавая предпочтение цифровому ТВ — тематическим телевизионным каналам, транслируемым по спутнику или через кабельные сети. В эфире альтернативного ТВ нет круглосуточной политики, терактов, бесконечных «ментов» и «бригад», глупых ток-шоу и мыльных опер. Вместо этого зрителю предлагаются спортивные программы, образовательные передачи, документальные фильмы о научных открытиях, рассказы о жизни в разных уголках планеты и так далее. Процент «мусора» очень низкий, и у зрителя есть возможность выбирать то, что он желает смотреть.
В отличие от каналов региональных и государственных телекомпаний, которые по умолчанию включаются операторами кабельных сетей в «стандартные» и «минимальные» пакеты услуг, все интересности вроде «24 Техно», Discovery или Nat Geo Wild скрыты от пользователей цифрового ТВ за более высокими тарифами. Хочешь смотреть не то, что тебе навязывают, — плати.
В том, что такие каналы стоят дороже, ничего неправильного нет. Любая работа должна быть оплачена, в том числе и труд сотрудников телевизионного канала, которым, по идее, должны идти отчисления. Казалось бы, все просто — покупай и наслаждайся качественным продуктом. Но на практике не все так гладко, как хотелось бы. Мы привыкли покупать ПО и оплачивать услуги мобильной связи. Но телевидение — это продукт совершенно иного рода. Где та мера, которой можно оценить потребление телепередач?
У того, кто много работает или учится, времени на просмотр телевизора просто не остается. И если такой человек приобретает месяц ТВ-эфира, на практике у него получается посмотреть телепередачи всего несколько часов или пару дней. Это наверняка удерживает многих от приобретения дополнительных каналов.
Кроме этого, операторы кабельных сетей привыкли «толкать» пакеты телевизионных каналов. Но какой смысл покупать человеку весь пакет, если из сотни предлагаемых каналов он смотрит только два спортивных? Логика подсказывает, что за возможность просмотра одного канала нужно было бы платить меньше. В общем, это очень спорный момент, который наверняка как-то будет в будущем решен. А пока же. пока можно, не мучаясь угрызениями совести, искать альтернативные способы просмотра интересного телевидения. Тем более что такая возможность существует.
На данный момент актуальны три основных способа просмотра ТВ через Интернет: потоковое вещание с низким битрейтом, IPTV и торрент-телевидение.
⇡#Потоковое вещание с низким битрейтом
Некоторые телевизионные каналы можно смотреть в режиме онлайн. Как правило, такую возможность предоставляют телекомпании, которые присутствуют в упомянутом выше «стандартном» пакете цифрового ТВ. Существует немало программ, объединяющих за общей оболочкой десятки свободно транслируемых каналов (Super Internet TV, Online TV Player и другие). К сожалению, качество изображения и звука при таком вещании весьма посредственное, поэтому реальной альтернативы цифровому телевидению потоковое вещание составить никак не может.
⇡#IPTV: все каналы в «цифре»
Интернет-провайдеры и операторы кабельного телевидения — часто одни и те же компании. Технология IPTV в зависимости от возможностей сети и предлагаемых провайдером услуг может быть реализована с использованием различных протоколов. Например, для трансляции видео могут быть задействованы RTP, UDP, HTTP, RTSP и другие протоколы.
Технология IPTV позволяет не только добиться хорошего качества изображения, но и реализовать массу дополнительных удобств для зрителя. Например, провайдер может представлять услугу «видео по запросу», возможность получения информационного меню с телепрограммой, временного сдвига трансляции и так далее. Качество изображения при использовании данной технологии может быть очень высоким (зависит от скорости передаваемого потока), поддерживается видео высокой четкости и многоканальное звуковое сопровождение.
⇡#Как и чем смотреть IPTV
Приложений для просмотра IPTV существует не так и много. Большинство из них построены на движке популярного медиапроигрывателя VLC.
Для просмотра IPTV-каналов понадобится плей-лист с телевизионными каналами, предоставленный вашим провайдером или (если вас по ночам не будут мучить угрызения совести) альтернативный список каналов от другого провайдера, найденный в Сети.
Если открыть готовый плей-лист в текстовом редакторе, например в «Блокноте», можно увидеть ссылки примерно такого вида — http://XXX.XXX.XXX:1212/udp/YYY.YYY.YYY:1234 или udp://@XXX.XXX.XXX:1234. Если вам известен адрес сервера трансляции, вы можете в проигрывателе VLC выбрать команду «Медиа → Открыть URL», вставить в соответствующее поле одну из таких ссылок, после чего нажать на кнопку «Воспроизвести». Разумеется, также можно просто открыть сам файл со списком воспроизведения.

Некоторые интернет-провайдеры сами предоставляют модифицированный IPTV-клиент с возможностью автоматической загрузки списка каналов и настройкой сети. Для просмотра каналов можно также использовать утилиту IP-TV Player. Это — одна из оболочек для того же медиапрогрывателя VideoLAN VLC.

⇡#Что делать, если IPTV «тормозит»
Добытые в Сети списки IPTV-каналов не всегда удобны для просмотра. Очень часто при просмотре телепередачи можно увидеть распадающееся на квадраты изображение, которое перемежается с цветными артефактами. Обычно это можно наблюдать в сетях, где трансляция происходит по UDP-протоколу. Помните анекдот «Я знаю отличную шутку про UDP, но, боюсь, она к вам не дойдет»?
Проблема в том, что трансляция по UDP-протоколу задействует транспортную модель, которая не гарантирует доставку пакетов к получателю. Как следствие — цифровой поток с телевизионным сигналом может содержать большие потери, вызывающие сбой в приеме ТВ. Немного исправить «тормоза» при воспроизведении можно с помощью настройки кеширования. Добавляя канал в медиапроигрыватель VLC, на вкладке «Сеть» установите флажок «Показать дополнительные параметры» и увеличьте объем кеша.

Также стоит иметь в виду, что для комфортного просмотра IPTV (в особенности каналов, транслируемых в HD-качестве) годятся лишь сети с высокой скоростью передачи данных (не менее нескольких мегабит).
⇡#macChange: я тучка, тучка, тучка, я вовсе не медведь…
Еще один полезный инструмент, который может пригодиться тем, кто экспериментирует с IPTV, — небольшая утилита для смены MAC-адреса. Некоторые провайдеры предоставляют свои сервисы с привязкой к уникальному идентификатору сетевого адаптера. По разным причинам иногда может возникнуть необходимость без роутера подключить сеть к другому ПК. В этом случае вы можете с помощью macChange быстро изменить MAC-адрес, задав нужное значение параметра. Достаточно выбрать в списке нужный сетевой адаптер и написать новое значение MAC-адреса.

⇡#Торрент-ТВ: смотри и делись
Технология обмена данными через протокол Bittorrent знакома многим. Однако не всем известно, что технология p2p-сети имеет продолжение.
Если вы обращали внимание на статистику загрузки больших файлов, то наверняка могли отметить про себя высокую скорость передачи данных. Такая комфортная скорость обмена информацией возможна благодаря распределенной структуре торрент-сети, где нет центрального узла. Чем больше пользователей вовлечено в процесс загрузки и раздачи торрента, тем выше и стабильнее скорость загрузки у каждого из участников. Этот же принцип может использоваться не только для загрузки приложений и фильмов с торрент-трекеров. Тем же пользователям ничего не мешает обмениваться пакетами, поддерживая стабильную трансляцию канала. Именно это легло в основу торрент-телевидения.
Как смотреть торрент-телевидение
Первое, что необходимо сделать для просмотра торрент-телевидения, — установить программное обеспечение ACE Stream или Torrent Stream. Это приложение представляет собой медиаплатформу, которая позволяет принимать торрент-каналы и раздавать их другим участникам p2p-сети. Программа устанавливает дополнения к некоторым браузерам.
После установки у вас есть выбор. Вы можете использовать для просмотра торрент-ТВ один из браузеров, например Chrome или Firefox, или же Ace Player — слегка улучшенный медиапроигрыватель VLC.
Чтобы смотреть эфир вещания каналов прямо в браузере, можно воспользоваться соответствующим сервисом. С его помощью можно быстро выбрать один из нескольких сотен каналов и начать его просмотр. Все каналы отсортированы по категориям, поэтому найти нужную тематику не составит труда. После запуска следует некоторое время подождать, пока проигрыватель соединится с другими пользователями и начнет показывать картинку. Плеер в браузере можно переключить в полноэкранный режим.

Кроме этого, можно использовать проигрыватель Ace Player. Каждый торрент-канал имеет свой идентификатор, который выглядит примерно так: 3dead4c7264b5fb152b6fb63f30650acc4fe3372. Если вам известен этот идентификатор — можно начать просмотр. Для этого следует в Ace Player выбрать команду Media → Open Network Stream, вставить в поле идентификатор и нажать на Play. Этот идентификатор легко можно «выцепить» из любой трансляции на torrent-tv.ru, щелкнув правой кнопкой мыши по изображению и выбрав в контекстном меню команду Share → Copy Content ID. Как несложно догадаться, идентификаторами можно легко делиться с друзьями.

Потратив некоторое время, можно собрать из нужных каналов торрент-телевидения удобный плей-лист.

Еще один вариант просмотра торрент-телевидения — популярная оболочка XBMC, превращающая компьютер в медиасервер (подробнее смотри материал «Бесплатные медиацентры: первый шаг к «умному дому». Если вы не прочь поэкспериментировать с медиаплатформой XBMC, вы можете установить официальное дополнение Torrent-TV XBMC и смотреть торрент-каналы в комфортной оболочке, почти как на «умных» телевизорах.

⇡#Телевидение на ладонях: как смотреть ТВ на планшете и смартфоне
До сих пор мы говорили о возможности просмотра телевидения на компьютере под управлением Windows. А как насчет Android-устройств? Несмотря на функциональность этой операционной системы, возможности для просмотра IPTV и торрент-телевидения тут более скромные. Тем не менее если постараться, можно найти способы смотреть цифровое телевидение на планшете с Android.
Способ первый — все тот же «непотопляемый» медиапроигрыватель VLC. Недавно он был портирован на Android, и его интерфейс практически ничем не отличается от версии под Windows. Пока не все функции этого проигрывателя работают должным образом, и плеер все еще находится на стадии бета-тестирования.

Другой способ заполучить телевидение на планшете — с помощью приложения SPB TV. Эта программа дает возможность быстро и без проблем просмотреть около сотни разных каналов. Утилита работает в мобильных сетях 3G, поддерживает Wi-Fi и WiMAX. Помимо Android, программа работает на мобильных устройствах с Symbian, bada, Maemo, iOS и так далее.

SPB TV показывает программу телепередач, дает возможность определить качество принимаемого изображения. Пользователю не нужно искать списки и адреса каналов, но и качество изображения далеко от идеального.
На планшете можно также смотреть торрент-телевидение, хотя реализация этой технологии на мобильных устройствах пока что, если честно, оставляет желать лучшего. Во-первых, необходимо установить приложение Torrent Stream Controller для платформы Android. Во-вторых, понадобится компьютер с установленным программным обеспечением Torrent Stream или ACE Stream. Далее необходимо настроить приложение на планшете, указав IP-адрес этого компьютера и потоковый порт.

Поскольку практика показала, что пользователям бывает сложно самостоятельно настроить параметры Torrent Stream Controller, разработчик добавил очень полезную функцию автоматической настройки сетевых параметров, понятную «любому красноармейцу». Перед тем как начать автоматическую настройку сети, необходимо на компьютере-сервере запустить программу TSPorts.
Правильно настроенный Torrent Stream Controller порадует вас тем, что самостоятельно искать торрент-каналы не потребуется, плей-лист автоматически подгрузится с запуском приложения.

Заключение
Какой бы способ просмотра телевизионных каналов вы ни выбрали, следует помнить, что самый лучший из них — законный. Вам понравилась просмотренная передача? Тогда подпишитесь на этот телеканал, поддержите создателей качественного ТВ-продукта материально. А если не понравилась. ну тогда так им и надо, удаляйте из плей-листа. Приятного всем просмотра!
Статья Немного об IPTV или проверка m3u с помощью Python. Часть 01
Уже довольно давно существует такая технология, как потоковая передача данных. С ее помощью стала возможной передача медиа-контента для просмотра, к примеру, телеканалов или просто видео на компьютере или смартфоне с помощью специальных плееров, которые и созданы для этого. Для примера, это всем известный медиа-комбайн VLC. Но речь не совсем об этом. Для того, чтобы плеер имел возможность открыть сразу же множество каналов, были придуманы, еще в далеком 1997 году, плейлисты. И с возможностью добавления в них ссылок на потоки они стали очень популярны в среде любителей IPTV, для обмена ссылками на каналы. Ну и так как, сами по себе условно «бесплатные» ссылки особо долго не живут, требуется инструмент для проверки плейлиста. Безусловно, такой инструмент нашелся — это IPTV Checker. Однако, данная программа работает только под Windows. Давайте попробуем разобраться, возможно ли с помощью Python сделать что-то подобное.
Скажу сразу, я не фанат телевизора. Но в данном случае меня больше заинтересовала сама проверка работоспособности. Не будем особо торопиться с созданием основного инструмента и для начала пробежимся по структуре плейлиста, а также создадим несколько инструментов, которые выполняют простейшую их обработку.
Структура плейлиста достаточно проста. В базовом видео он состоит из трех основных элементов. Это:
1. #EXTM3U — заголовок, указывающий на то, что данный текст является плейлистом;
2. #EXTINF — информация о медиафайле плейлиста, то есть, его описание;
3. http:// (https://) — ссылка на медиафайл, которая может быть как локальной, так и глобальной.И если о заголовке и ссылке особо сказать нечего, то на описании медиафайла остановимся чуть подробнее. # в начале означает, что проигрыватель с этого места должен начать считывание данных. Далее, за сокращением EXTINF, что означает Extended information, следует указание продолжительности данных. В случае с потоком в интернете используется -1, так как конечная длина файла в этом случае неизвестна. Затем, через запятую указывается название медиаресурса. Для примера: «Песня». Но, это еще не все. Здесь же, в расширенной информации могут присутствовать такие параметры как: «tvg-name» — где указывается название канала или программы; «tvg-logo» — ссылка на логотип канала или обложку альбома; «audio-track» — информация об аудиодорожке; «group-title» — название группы каналов, для их объединения в плеерах по группам.
В принципе, данной информации уже вполне достаточно, чтобы начать работать с плейлистами. Тем более, что сильно глубоко менять их структуры мы здесь не будем.
Разбиение плейлиста на более мелкие части
Зачастую списки каналов в плейлистах могут содержать не одну тысячу ссылок. Открывать такие плейлисты в плеере не особо удобно, да и не все плееры иногда могут отрыть такое количество ссылок. Можно конечно разбить плейлист вручную простым копипастом, но в нашем распоряжении есть Python, поэтому можно воспользоваться им.
Что потребуется?
По большему счету для работы данного скрипта не требуется установки сторонних модулей. Но, для того, чтобы хоть немного разукрасить вывод информации в терминал, давайте установим библиотеку colorama. Для этого пишем в терминале команду:
pip install colorama
После того, как colorama установиться, импортируем в скрипт нужные модули и библиотеки, а также, сразу же инициализируем colorama.
import sys import time from pathlib import Path from colorama import Fore from colorama import init init()Двигаемся дальше. Для того, чтобы куда-то складывать информацию прочитанную из плейлиста, создадим список, а также счетчик, с помощью которого будем считать итерации и делить плейлист на нужные части.
split_set = [] num = 0Создадим функцию main. В ней мы будем запрашивать данные у пользователя о его действиях, а также запускать функцию разбиения файла. Для начала определим глобальные переменные. Выведем информацию о том, что делает данный скрипт, и запросим у пользователя, что он хочет сделать. Обработать файл или целую директорию с файлами, чтобы разделить из все сразу.
global split_set, num try: print(f"\nРАЗБИВКА ФАЙЛА M3U НА БОЛЕЕ МЕЛКИЕ ЧАСТИ\n" f"\n") user_check = input("Выберите действие:\n[1] Обработка директории\n[2] Обработка файла\n>>> ")Далее следует обработка пользовательского выбора. Если он выбрал разбиение файлов в директории, выполняем проверку, существует ли она и является ли указанный путь путем к директории. Затем запрашиваем у пользователя количество каналов в файле после разбивки. Итерируемся по директории, забираем из нее файлы «.m3u» и передаем в функцию для разбивки. После чего выводим информацию о затраченном времени.
if user_check == "1": path = input("Введите путь к директории: ") if not Path(path).exists() or path == "" or not Path(path).is_dir(): print(Fore.RED + "Введенного пути не существует") sys.exit(0) chunk_count = input("Введите количество каналов в файле: ") st = time.monotonic() for file in Path(path).iterdir(): if Path(file).suffix == ".m3u": split_list(str(file), int(chunk_count)) split_set.clear() num = 0 print(f"Затраченное время: c.")Примерно то же самое происходит, если пользователь выбрал разбивку одного файла. За исключением того, что нам нет необходимости итерироваться по директории.
Полный код функции main
def main(): """ Получение пользовательских данных. Запуск разбивки файла. """ global split_set, num try: print(f"\nРАЗБИВКА ФАЙЛА M3U НА БОЛЕЕ МЕЛКИЕ ЧАСТИ\n" f"\n") user_check = input("Выберите действие:\n[1] Обработка директории\n[2] Обработка файла\n>>> ") if user_check == "1": path = input("Введите путь к директории: ") if not Path(path).exists() or path == "" or not Path(path).is_dir(): print(Fore.RED + "Введенного пути не существует") sys.exit(0) chunk_count = input("Введите количество каналов в файле: ") st = time.monotonic() for file in Path(path).iterdir(): if Path(file).suffix == ".m3u": split_list(str(file), int(chunk_count)) split_set.clear() num = 0 print(f"Затраченное время: c.") elif user_check == "2": path = input(Fore.RESET + "Введите путь к файлу: ") if not Path(path).exists() or path == "" or not Path(path).is_file(): print(Fore.RED + "Введенного пути не существует") sys.exit(0) chunk_count = input("Введите количество каналов в файле: ") st = time.monotonic() split_list(path, int(chunk_count)) print(f"Затраченное время: c.") else: print("Ваш выбор не понятен") raise KeyboardInterrupt except KeyboardInterrupt: print(Fore.YELLOW + "\n\nGood by, my friend! Good by!\n") if __name__ == "__main__": main()Двигаемся дальше. Создадим функцию, в которой и будет происходить разбиение файлов на части. Я назвал ее split_list(path: str, chunk_count: int). Как видно, на входе она принимает путь к файлу для разбивки, а также целое число, указывающее на количество каналов в одном файле.
Для начала выводим информацию о действиях. Затем определяем переменную, которая будет хранить описание канала. В нее я добавил значение по умолчанию, так как сталкивался с тем, что в некоторых плейлистах напрочь отсутствует (почему-то) это самое описание.
global num, split_set num_line = len(f"РАЗБИВАЮ ФАЙЛ НА ЧАСТИ ПО ЭЛ.") print(Fore.YELLOW + f"\nРАЗБИВАЮ ФАЙЛ НА ЧАСТИ ПО ЭЛ.\n") ext_inf = '#EXTINF: -1, Null'Открываем файл на чтение и итерируемся в нем построчно. Делаем проверку. Если начало строки содержит #EXTINF, то добавляем его в переменную ext_inf, а если ссылку на поток, или плейлист, то добавляем полученные данные в созданный нами ранее список.
with open(path, 'r', encoding='utf-8') as file: for line in file.readlines(): if line.startswith("#EXTINF"): ext_inf = line.strip() elif line.startswith("http"): split_set.append(f"\n\n")Теперь проверяем, не равна ли длина списка указанному пользователем количеству каналов. Если да, увеличиваем переменную num на единицу, выводим сообщение пользователю, вызываем функцию сохранения информации из списка и затем очищаем его от всей информации. После чего итерируемся далее.
if len(split_set) == chunk_count: num += 1 print(Fore.YELLOW + f"\rЧасть обработана", end="") save_split(path, str(chunk_count)) split_set.clear()В случае, если у нас осталось некоторое количество каналов, которое не попадает под условие заданное пользователем, то есть, меньше, сохраняем остаточные данные. И выводим сообщение для пользователя.
if 0 < len(split_set) < chunk_count: num += 1 print(Fore.YELLOW + f"\rЧасть обработана", end="") save_split(path, str(chunk_count)) print(f"\r\b", end="") print(Fore.GREEN + f"Файл разбит. Количество частей: ")И еще одна функция, это функция сохранения данных. Я назвал ее save_split(path: str, cnt: str). На вход она принимает путь к исходному файлу, чтобы можно было забрать из него директорию для сохранения, а также исходное имя файла. И номер файла по порядку.
Ну и далее — все просто. Получаем директорию, в которую будем сохранять части файла. Затем, чтобы нумерация была нормальной и файловый менеджер корректно сортировал файлы проверяем число, больше 10 или меньше. В зависимости от этого добавляем или не добавляем 0 к номеру файла. Создаем директорию. Открываем файл на дозапись, тем самым создавая его. И в цикле сохраняем в него данные из списка. Так как мы ранее сохраняли их в список с символом перевода каретки, то и в файл они запишутся корректно. Поэтому, сохраняем как есть. И да, дописываем в самое начало файла заголовок, означающий, что данный файл является плейлистом.
global num dir_split = Path(path).parent / f"split__channel_" s = f'0' if num < 10 else num dir_split.mkdir(exist_ok=True) with open(dir_split / f"_.m3u", "a", encoding="utf-8") as f: f.write("#EXTM3U\n") for sst in split_set: f.write(sst)Полный код скрипта для разбиения плейлиста на части
import sys import time from pathlib import Path from colorama import Fore from colorama import init init() split_set = [] num = 0 def save_split(path: str, cnt: str): """ Сохранение информации с указанным количеством элементов. :param cnt: Кол-во файлов для разбивки. :param path: Путь к исходному файлу. """ global num dir_split = Path(path).parent / f"split__channel_" s = f'0' if num < 10 else num dir_split.mkdir(exist_ok=True) with open(dir_split / f"_.m3u", "a", encoding="utf-8") as f: f.write("#EXTM3U\n") for sst in split_set: f.write(sst) def split_list(path: str, chunk_count: int): """ Разбивка общего количества каналов на указанное в переменной. Сохранение разбитых частей в файлы. :param path: Путь к папке с файлом. :param chunk_count: Количество каналов в файле после разбивки. """ global num, split_set num_line = len(f"РАЗБИВАЮ ФАЙЛ НА ЧАСТИ ПО ЭЛ.") print(Fore.YELLOW + f"\nРАЗБИВАЮ ФАЙЛ НА ЧАСТИ ПО ЭЛ.\n") ext_inf = '#EXTINF: -1, Null' with open(path, 'r', encoding='utf-8') as file: for line in file.readlines(): if line.startswith("#EXTINF"): ext_inf = line.strip() elif line.startswith("http"): split_set.append(f"\n\n") if len(split_set) == chunk_count: num += 1 print(Fore.YELLOW + f"\rЧасть обработана", end="") save_split(path, str(chunk_count)) split_set.clear() if 0 < len(split_set) < chunk_count: num += 1 print(Fore.YELLOW + f"\rЧасть обработана", end="") save_split(path, str(chunk_count)) print(f"\r\b", end="") print(Fore.GREEN + f"Файл разбит. Количество частей: ") def main(): """ Получение пользовательских данных. Запуск разбивки файла. """ global split_set, num try: print(f"\nРАЗБИВКА ФАЙЛА M3U НА БОЛЕЕ МЕЛКИЕ ЧАСТИ\n" f"\n") user_check = input("Выберите действие:\n[1] Обработка директории\n[2] Обработка файла\n>>> ") if user_check == "1": path = input("Введите путь к директории: ") if not Path(path).exists() or path == "" or not Path(path).is_dir(): print(Fore.RED + "Введенного пути не существует") sys.exit(0) chunk_count = input("Введите количество каналов в файле: ") st = time.monotonic() for file in Path(path).iterdir(): if Path(file).suffix == ".m3u": split_list(str(file), int(chunk_count)) split_set.clear() num = 0 print(f"Затраченное время: c.") elif user_check == "2": path = input(Fore.RESET + "Введите путь к файлу: ") if not Path(path).exists() or path == "" or not Path(path).is_file(): print(Fore.RED + "Введенного пути не существует") sys.exit(0) chunk_count = input("Введите количество каналов в файле: ") st = time.monotonic() split_list(path, int(chunk_count)) print(f"Затраченное время: c.") else: print("Ваш выбор не понятен") raise KeyboardInterrupt except KeyboardInterrupt: print(Fore.YELLOW + "\n\nGood by, my friend! Good by!\n") if __name__ == "__main__": main()Итак, первый инструмент мы создали. Пора переходить ко второму.
Объединение плейлистов и удаление одинаковых ссылок
В общем-то, для чего служит данный скрипт, понятно из заголовка раздела. Следует пояснить, что данный скрипт может использоваться, к примеру, в том случае, когда у вас есть несколько небольших плейлистов и вы хотели бы сделать один побольше. Однако, в разных плейлистах иногда содержаться одинаковые ссылки на каналы. Просто у них разные описания. А потому, их желательно удалить, чтобы не дублировать. Ну и в одном плейлисте, также могут содержаться одинаковые ссылки. Плейлисты делают люди. А значит, возможны повторы. Что же, давайте приступим.
Что потребуется?
Также, как и в первом скрипте использование сторонних библиотек не обязательно. Я добавил colorama только для раскрашивания вывода в терминал. Для ее установки пишем команду:
pip install colorama
Импортируем необходимые модули и библиотеки, а также инициализируем colorama.
import shutil import sys import time from pathlib import Path from colorama import Fore from colorama import init init()Теперь создадим глобальные переменные, которые будут использоваться при работе в любой из функций скрипта. count — счетчик уникальных ссылок. links — множество, в которое складываются все ссылки во время работы. Именно с помощью этого множества будет проверятся уникальность ссылок. А для того, чтобы данные не занимали слишком много места в памяти, так как плейлисты могут быть довольно внушительных размеров, используем множество, так как оно не позволяет хранить в себе два одинаковых элемента. Да и по скорости доступа к данным множество быстрее, чем тот же список. merge - список с каналов после проверки. error — список, в котором будут храниться ошибки возникшие в процессе объединения. Дело в том, что когда пользователи создают плейлисты, они не учитывают того, что кодировка на той машине, где он будет использоваться может отличатся. Я говорю о пользователях Windows. Попадаются плейлисты, которые сохранены в этой кодировке. И данную проблему можно было бы обойти, если бы не пристрастие добавления в описании канала всяких смайликов, символов сердечек и прочего, что даже при изменении кодировки на виндовую просто не читается. Помогает только пересохранение данного файла вручную с кодировкой utf-8. Ну и, собственно, получение абракадабры взамен смайликов. Я пытался определить кодировку, но chardet в данном случае не помогает. Поэтому я решил просто складывать ошибочные файлы в отдельную папку и сообщать об этом пользователю.
count = 0 links = set() merge = [] error = []Создадим функцию check_url_inline(url: str, ext_info: str). На вход она принимает ссылку на канал и его описание. Определяем глобальные переменные. Сравниваем полученную ссылку с содержимым множества. Если ссылки нет в множестве, собираем канал и добавляем его в список. А также увеличиваем счетчик.
def check_url_inline(url: str, ext_info: str): """ Проверка дубликатов и добавление в основное множество. :param url: Ссылка для проверки. :param ext_info: Информация о канале. """ global count, links, merge if url.strip() not in links: merge.append(f'\n\n') count += 1 links.add(url.strip())Теперь нам потребуется функция main. Особо ее описывать нет необходимости. Пробежимся лишь вкратце. Выводим информацию для пользователя. Запрашиваем путь к директории с объединяемыми файлами. Запрашиваем имя для объединенного файла. Проверяем, является ли путь директорией и вообще, есть ли он. Вызываем функцию объединения файлов. В нее передаем путь к директории и флаг, который указывает, следует ли очищать описание, оставляя только название канала или нет. Соответственно, 0 — не очищать, 1 — очищать. Здесь, данную информацию я не запрашиваю явно. Если у кого-то есть желание, можете написать запрос данной информации у пользователя. Вызываем функцию сохранения объединенной информации. Выводим данные для пользователя в терминал.
def main(): """ Получение пользовательских данных. Запуск функций проверки на дубликаты и сохранения проверенного. """ global merge, count print(f"\nПРОВЕРКА НА ДУБЛИКАТЫ И ОБЪЕДИНЕНИЕ КАНАЛОВ\n\n") path = input(Fore.RESET + "Введите путь к директории: ") name = input("Введите имя для объединенного файла: ") if not Path(path).exists() or not Path(path).is_dir(): print(Fore.RED + "Директории не существует") sys.exit(0) st = time.monotonic() count_file = merged(path, 0) save_merge(name) print("\r\033[K", end="") print(f"\nОбработано файлов: ") print(f"Затраченное время: c.") if __name__ == "__main__": main()Теперь создадим функцию для сохранения объединенной информации. Я назвал ее save_merge(name: str). На вход она принимает имя для сохраняемого файла, которое мы запрашивали у пользователя.
Открываем файл на запись, дописываем в него заголовок. Запускаем цикл по списку с каналами. Записываем в файл. Проверяем, есть ли информация в файле с ошибками. Если есть, запускаем цикл по списку. Создаем директорию, в которую будем складывать файлы с ошибками. Перемещаем файлы в соответствии со списком в созданную директорию. Выводим информацию о том, что имеются файлы с нарушенной кодировкой. И также записываем текстовый файл, в который помещаем имена файлов. После всего выводим информацию для пользователя о количестве оригинальных ссылок.
def save_merge(name: str): """ Сохранение проверенных данных без дубликатов. :param name: Имя для сохраняемого файла. """ global count, merge with open(Path.cwd() / f".m3u", "w", encoding="utf-8") as ch: ch.write("#EXTM3U\n") for channel in merge: ch.write(channel) if error: for file in error: (Path(file).parent / 'unicode_error').mkdir(exist_ok=True) shutil.move(file, Path(file).parent / 'unicode_error' / Path(file).name) print("\nИмеются файлы требующие исправления кодировки.\nСмотрите файл: 'error.txt'\n") with open(Path.cwd() / "error.txt", "w", encoding="utf-8") as er: er.write("Файлы требующие исправления кодировки: \n\n") for err in error: er.write(f'\n') print(f"\nОригинальных ссылок: ")Создадим функцию для чтения файлов. Я назвал ее merged(path_dir, flag). В нее мы передаем путь к файлу, а также флаг, который указывает, удалять описание и оставлять только название канала или нет. В данном скрипте данный файл указан по умолчанию и не запрашивается у пользователя. Хотя, его обработка в функции имеет место быть. Дальше все просто. Открываем файл на чтение, забираем заголовок в переменную, получаем ссылку и передаем заголовок и ссылку в функцию проверки. Если возникает исключение с кодировкой, добавляем в список с ошибками путь к файлу.
Функция чтения файла
def merged(path_dir, flag): """ Выборка информации о файле и ссылок из плейлиста. :param path_dir: Ссылка на папку с файлами. :param flag: Флаг очистки информации. 0 - не очищает; 1 - очищает, оставляет только название канала. """ count_file = 0 print("") for nn, file in enumerate(Path(path_dir).iterdir()): count_file += 1 print("\r\033[K", end="") print(f'\r | Обработка: ', end="") if file.suffix == ".m3u": ext_info = '' try: with open(file, 'r', encoding="utf-8") as f: for item in f.readlines(): if item.startswith("#EXTINF"): ext_info = f"#EXTINF:-1 ," \ if flag == 1 else item.strip() elif item.startswith("http"): if item.strip().split("/")[-1].split(".")[-1] == "mpd" \ or item.strip().split("/")[-1].split(".")[-1] == "flv": continue check_url_inline(item.strip(), ext_info) except UnicodeDecodeError: error.append(file) continue return count_file Вот в принципе и все. Ниже приведен полный код функции. И да, данная функция не обрабатывает файлы «.m3u8», только «.m3u». Поэтому, если у вас плейлист в первом виде, просто переименуйте расширение.
Полный код функции объединения и удаления дубликатов каналов
import shutil import sys import time from pathlib import Path from colorama import Fore from colorama import init init() count = 0 links = set() merge = [] error = [] def check_url_inline(url: str, ext_info: str): """ Проверка дубликатов и добавление в основное множество. :param url: Ссылка для проверки. :param ext_info: Информация о канале. """ global count, links, merge if url.strip() not in links: merge.append(f'\n\n') count += 1 links.add(url.strip()) def save_merge(name: str): """ Сохранение проверенных данных без дубликатов. :param name: Имя для сохраняемого файла. """ global count, merge with open(Path.cwd() / f".m3u", "w", encoding="utf-8") as ch: ch.write("#EXTM3U\n") for channel in merge: ch.write(channel) if error: for file in error: (Path(file).parent / 'unicode_error').mkdir(exist_ok=True) shutil.move(file, Path(file).parent / 'unicode_error' / Path(file).name) print("\nИмеются файлы требующие исправления кодировки.\nСмотрите файл: 'error.txt'\n") with open(Path.cwd() / "error.txt", "w", encoding="utf-8") as er: er.write("Файлы требующие исправления кодировки: \n\n") for err in error: er.write(f'\n') print(f"\nОригинальных ссылок: ") def merged(path_dir, flag): """ Выборка информации о файле и ссылок из плейлиста. :param path_dir: Ссылка на папку с файлами. :param flag: Флаг очистки информации. 0 - не очищает; 1 - очищает, оставляет только название канала. """ count_file = 0 print("") for nn, file in enumerate(Path(path_dir).iterdir()): count_file += 1 print("\r\033[K", end="") print(f'\r | Обработка: ', end="") if file.suffix == ".m3u": ext_info = '' try: with open(file, 'r', encoding="utf-8") as f: for item in f.readlines(): if item.startswith("#EXTINF"): ext_info = f"#EXTINF:-1 ," \ if flag == 1 else item.strip() elif item.startswith("http"): if item.strip().split("/")[-1].split(".")[-1] == "mpd" \ or item.strip().split("/")[-1].split(".")[-1] == "flv": continue check_url_inline(item.strip(), ext_info) except UnicodeDecodeError: error.append(file) continue return count_file def main(): """ Получение пользовательских данных. Запуск функций проверки на дубликаты и сохранения проверенного. """ global merge, count print(f"\nПРОВЕРКА НА ДУБЛИКАТЫ И ОБЪЕДИНЕНИЕ КАНАЛОВ\n\n") path = input(Fore.RESET + "Введите путь к директории: ") name = input("Введите имя для объединенного файла: ") if not Path(path).exists() or not Path(path).is_dir(): print(Fore.RED + "Директории не существует") sys.exit(0) st = time.monotonic() count_file = merged(path, 0) save_merge(name) print("\r\033[K", end="") print(f"\nОбработано файлов: ") print(f"Затраченное время: c.") if __name__ == "__main__": main() Данная функция является полностью рабочей. Я, в процессе тестирования основного скрипта, набрала очень большое количество каналов. Решил объединить и немного был… хм… поражен ))) Она обработала более 12 млн. записей. Конечно же, это заняло некоторое время. Уж не помню сколько, но, что-то не более двух с копейками минут. Или даже меньше.
Переименование m3u8 в m3u
В этой функции нет ничего особенного, потому, детального описания не будет. Ее я сделал просто, как вспомогательную для того, чтобы не делать это вручную. Так как часто плейлисты скачиваются именно в формате m3u8, а мои скрипты с ним не работают. Просто поясню, что данный формат иногда не содержит в себе ссылок на каналы, а только лишь частичные ссылки на фрагменты потока. Поэтому, обрабатывать данный вид плейлистов не имеет большого смысла. Ну и собственно, сама функция. Итерируемся по директории. Выбираем файлы с нужным расширением и переименовываем с добавлением небольшого суффикса. Так как в директории могут быть файлы с похожим названием, но другим расширением. Переименовываем их.
from pathlib import Path num = 0 print("\nПереименование файлов m3u8".upper()) print("") for file in Path(input("Введите путь к директории для переименования>>> ")).iterdir(): if Path(file).suffix == ".m3u8": if Path(file).exists(): num += 1 name = f'__copy.m3u' else: name = f'.m3u' print(name) Path(file).rename(f'')Сортировка каналов по названиям
Данный скрипт будет полезен в том случае, если у вас есть множество каналов, которые объединены в один файл, но находятся по нему вразброс. То есть, условная «Россия» в одном месте с уникальной ссылкой на поток и в другом месте та же «Россия», но уже с другой ссылкой. По сути, это один и тот же канал, только трансляция ведется с разных серверов. Вот, для того, чтобы собрать одинаковые по названиям каналы в кучку, я и сделал этот скрипт. По большому счету он очень простой и не использует никаких запредельных технологий )).
В данном скрипте не потребуется устанавливать сторонних модулей или библиотек. Так что, будем любоваться стандартным терминалом. Импортируем необходимые для работы скрипта библиотеки и модули. Создадим словарь, в который и будем складывать данные каналов для сортировки.
import json import sys from pathlib import Path sort_ch = dict()Создадим функцию main. В ней мы будем запрашивать путь к файлу для сортировки по именам каналов. Затем проверять существует ли файл и является ли он файлом. Создаем две переменные. В одной будем хранить полное описание канала, во второй — его отображаемое имя. Открываем файл на чтение. Итерируемся по нему построчно в цикле. Забираем описание канала, получаем его имя. Затем, если встречается ссылка, передаем все полученные данные в функцию для добавления в словарь, с помощью которого и будет происходить сортировка. После сохраняем отсортированные данные.
Код функции main
def main(): """ Открытие файла. Перебор ссылок и описаний. Передача в функцию для добавления в словарь. """ print("\nСортировка каналов по имени".upper()) print("") path = input("Введите путь к файлу m3u >>> ") if not Path(path).exists() or not Path(path).is_file(): print("Пути не существует") sys.exit(0) ext, name = "", "" with open(path, 'r', encoding='utf-8') as f: for ln in f.readlines(): if ln.startswith("#EXTINF"): ext = ln.strip() name = "No name" if ext.split(',')[-1].replace('#EXTGRP:', '').strip() is None \ else ext.split(',')[-1].replace('#EXTGRP:', '').strip() continue elif ln.startswith("http"): sort_channel(ext, name, ln.strip()) pass s_name = Path(path).parent / f'_sort.json' save_dict(str(s_name)) if __name__ == "__main__": main() Теперь, собственно, создадим саму функцию сортировки. Я назвал ее sort_channel(ext: str, name: str, url: str). В нее мы передаем описание канала, его отображаемое имя и ссылку на поток. Здесь все просто. Создаем ключ с именем канала. Проверяем, есть ли в данном ключе значение url. Если нет, добавляем. Обратите внимание на порядок. Сначала идет url, а затем описание канала. Дело в том, что описание может быть одинаковым. А значит, данный канал не попадет в словарь. А вот у url шансов на уникальность больше. Особенно после того, как вы пройдетесь по плейлисту с помощью предыдущего скрипта для объединения каналов.
def sort_channel(ext: str, name: str, url: str): """ Сортировка каналов по наименованиям в словаре. :param ext: Описание канала. :param name: Имя канала. :param url: Ссылка на канал. """ if name not in sort_ch: sort_ch[name] = dict() sort_ch[name].update()Таким образом, мы также выполняем частичную сортировку на уникальность. Так как в словаре не может быть двух одинаковых ключей. Тем самым мы отсеем повторяющиеся ссылки.
И еще одна функция, уже для сохранения отсортированного содержимого словаря. Назовем ее save_dict(name: str). На вход она принимает имя, которое является путем к директории для сохранения, с расширением файла, которое в данном случае является «.json». Для начала сохраняем json. В принципе, этого делать не обязательно, но, мало ли что, вдруг вам понадобиться поработать с помощью каких-либо скриптов с данными каналами. Затем определяем имя для «.m3u» файла. Открываем его на запись, дописываем в него заголовок, после чего итерируемся по словарю и сохраняем полученные данные в файл.
def save_dict(name: str): """ Сохранение результатов в файлы. :param name: Путь к файлу для сохранения json. """ with open(name, 'w', encoding='utf-8') as f: json.dump(sort_ch, f, indent=4, ensure_ascii=False) m3u_name = Path(name).parent / f'_sort.m3u' with open(m3u_name, 'w', encoding='utf-8') as file: file.write("#EXTM3U\n") for key in sort_ch: for item in sort_ch.get(key): file.write(f'\n\n') print("\r\033[K", end="") print(f'\rСохранение: | ', end="") Полный код скрипта для сортировки каналов по имени
import json import sys from pathlib import Path sort_ch = dict() def save_dict(name: str): """ Сохранение результатов в файлы. :param name: Путь к файлу для сохранения json. """ with open(name, 'w', encoding='utf-8') as f: json.dump(sort_ch, f, indent=4, ensure_ascii=False) m3u_name = Path(name).parent / f'_sort.m3u' with open(m3u_name, 'w', encoding='utf-8') as file: file.write("#EXTM3U\n") for key in sort_ch: for item in sort_ch.get(key): file.write(f'\n\n') print("\r\033[K", end="") print(f'\rСохранение: | ', end="") def sort_channel(ext: str, name: str, url: str): """ Сортировка каналов по наименованиям в словаре. :param ext: Описание канала. :param name: Имя канала. :param url: Ссылка на канал. """ if name not in sort_ch: sort_ch[name] = dict() sort_ch[name].update() def main(): """ Открытие файла. Перебор ссылок и описаний. Передача в функцию для добавления в словарь. """ print("\nСортировка каналов по имени".upper()) print("") path = input("Введите путь к файлу m3u >>> ") if not Path(path).exists() or not Path(path).is_file(): print("Пути не существует") sys.exit(0) ext, name = "", "" with open(path, 'r', encoding='utf-8') as f: for ln in f.readlines(): if ln.startswith("#EXTINF"): ext = ln.strip() name = "No name" if ext.split(',')[-1].replace('#EXTGRP:', '').strip() is None \ else ext.split(',')[-1].replace('#EXTGRP:', '').strip() continue elif ln.startswith("http"): sort_channel(ext, name, ln.strip()) pass s_name = Path(path).parent / f' _sort.json' save_dict(str(s_name)) if __name__ == "__main__": main() Проверка работоспособности каналов с помощью плеера VLC
Конечно же, мы не будем открывать каждый канал в плеере. А будем использовать скрипт Python.
Что потребуется?
Для начала, вне зависимости от операционной системы, вам потребуется установить сам медиаплеер. Затем, для того, чтобы мы могли работать с ним из python, установим библиотеку python-vlc. Пишем в терминале:
pip install python-vlc
И да, здесь мы также будем использовать colorama, хоть это и не обязательно. Для ее установки пишем:
pip install colorama
После того, как нужные библиотеки будут установлены, импортируем все необходимое в наш скрипт и инициализируем colorama.
import concurrent.futures import subprocess import sys import time from pathlib import Path import vlc from colorama import Fore from colorama import init init()Создадим списки и переменные, в которых будем хранить рабочие и нерабочие каналы, а также подсчитывать количество обработанных ссылок.
status = [] error = [] st_count, err_count, all_ch = 0, 0, 0В этот раз мы начнем с сохранения данных. Создадим функцию save_status_error(path). На вход она получает путь к обрабатываемому файлу, чтобы получить путь к его директории. Здесь нам не требуется сохранять каждый файл отдельно. Нам нужно просто сохранить рабочие и нерабочие каналы в разные файлы. У меня это реализовано именно так потому, что я использовал данный скрипт как вспомогательный, то есть, средство перепроверки нерабочих файлов, которые мне выдал мой скрипт. Потому так. Ну и далее все просто. Открываем файл на дозапись, итерируемся по спискам, если в них что-то есть и сохраняем данные в файл. Здесь я поставил небольшой фильтр, чтобы файлы «Триколора» не попадали в какой-либо из списков. Не знаю, не вдавался в подробности, может быть данные ссылки и можно смотреть на компьютере с помощью какого-то хитрого плеера. Тем более, что на работоспособность они в принципе проверяются неплохо. Но, дело в том, что к примеру, в плеере «Celluloid», который идет из коробки в Мяте, эти ссылки на потоки распадаются в буквальном списке на отдельные сегменты. Из-за этого плеер может даже подвиснуть. Ну, а в VLC они просто не воспроизводятся. Но это так, мое субъективное, личное мнение. Вполне возможно, что кому-то эти ссылки нужны. И тогда фильтр можно просто удалить.
def save_status_error(path): (Path(path).parent / 'checked_vlc').mkdir(exist_ok=True) if len(status) > 0: name = Path(path).parent / 'checked_vlc' / f'good_vlc.m3u' with open(name, "a", encoding='utf-8') as f: for item in status: if "tricolor" in item: continue f.write(f"") if len(error) > 0: name = Path(path).parent / 'checked_vlc' / f'error_vlc.m3u' with open(name, "a", encoding='utf-8') as f: for item in error: f.write(f"")Создадим функцию для проверки ссылок. Назовем ее, к примеру, vlc_player(url, ext, nm). На входе она получает ссылку на поток, описание канала, и порядковый номер, в котором она была извлечена из файла. Он служит больше в психологическом плане, когда показывает номер проверяемой ссылки для пользователя. Так как сам по себе плеер вываливает в терминал столько информации, что в ней легко потеряться. Что происходит в данном скрипте? Для начала создаем экземпляр плеера. Передаем в него ссылку и далее, передаем в плеер для воспроизведения. Запускаем таймер на 0,5 секунды. Так как проверка будет работать в потоках, таймер необходим. Если ставить значение меньше, контент не успевает воспроизвестись, так же, как и в случае, если таймер убрать вообще. Если больше, скрипт соответственно работает довольно долго. При этом, высока вероятность того, что если у вас много рабочих каналов, может просто подвиснуть графическая оболочка. Как в Windows с этим — не проверял. А вот Cinnamone подвисал намертво. Спасала только перезагрузка с кнопки.
Ну и далее читаем коды состояния. Если это коды «Error», «Ended» или «Opening», канал можно считать нерабочим. Более того, два кода vlc.State.Error", "State.Error" явно устарели. Их оставил так, на всякий случай. А два остальных были выявлены опытным путем. Читать документацию было слишком долго )).
Ну и соответственно добавляем в рабочие и нерабочие списки. Также обрабатываем исключение, хотя, за все время работы оно не возникало ни разу. Другие ошибки — да. При проверке слишком больших файлов скорее всего не хватает ресурсов. И скрипт вылетает. Большие — это более 4000.
Полный код функции проверки
def vlc_player(url, ext, nm): player, state = "", "" try: instance = vlc.Instance('--input-repeat=-1', '--no-fullscreen') player = instance.media_player_new() media = instance.media_new(url) player.set_media(media) player.play() time.sleep(0.5) state = str(player.get_state()) if state in ["vlc.State.Error", "State.Error", "State.Ended", "State.Opening"]: player.stop() error.append(f'\n\n') print(f'\n | Stream is working. Current state = \n') return else: print(f'\n | Stream is working. Current state = \n') player.stop() status.append(f'\n\n') return except Exception: print(f'\nStream is dead. Current state = \n') player.stop() error.append(f'\n\n') returnНу и функция main. Думаю особых пояснений она не требует. Здесь мы запрашиваем путь к директории с файлами или файлом. Так как можно обрабатывать сразу же несколько. Открываем файл, итерируемся по нему построчно и запускаем функцию проверки в потоках. Здесь их максимально количество 80. Качество проверки не страдает. VLC — очень мощный товарищ.
Код функции main
def main(): global status, error, st_count, err_count, all_ch path = input("Введите путь к директории: ") if not Path(path).exists() or not Path(path).is_dir(): print("Директории не существует или введенный путь не ведет к директории") sys.exit(0) time_start = time.monotonic() for num, path in enumerate(Path(path).iterdir()): if Path(path).suffix == ".m3u": nm = 0 print(f"Проверка файла: | \n") if not Path(path).exists(): sys.exit(0) ext = "" with concurrent.futures.ThreadPoolExecutor(max_workers=80) as executor: with open(path, 'r', encoding='utf-8') as file: for line in file.readlines(): if line.startswith("#EXTINF"): ext = line.strip() continue if line.startswith("http"): nm += 1 all_ch += 1 executor.submit(vlc_player, url=line.strip(), ext=ext, nm=nm) time.sleep(0.3) save_status_error(path) subprocess.Popen("clear", shell=True) time.sleep(0.3) print(f"\nGood: ") print(f"Error: \n") st_count = st_count + len(status) err_count = err_count + len(error) status.clear() error.clear() Path(path).unlink() print(f"\nAll Channel: ") print(f"\nAll Good: ") print(f"All Error: \n") print(f'All Scan time | ' f' <(int(time.monotonic() - time_start) // 3600) % 24:d>ч. ' f' <(int(time.monotonic() - time_start) // 60) % 60:02d>м. ' f'с.\n') if __name__ == "__main__": main() Полный код функции проверки с помощью VLC
# pip install python-vlc import concurrent.futures import subprocess import sys import time from pathlib import Path import vlc from colorama import Fore from colorama import init init() status = [] error = [] st_count, err_count, all_ch = 0, 0, 0 def save_status_error(path): (Path(path).parent / 'checked_vlc').mkdir(exist_ok=True) if len(status) > 0: name = Path(path).parent / 'checked_vlc' / f'good_vlc.m3u' with open(name, "a", encoding='utf-8') as f: for item in status: if "tricolor" in item: continue f.write(f"") if len(error) > 0: name = Path(path).parent / 'checked_vlc' / f'error_vlc.m3u' with open(name, "a", encoding='utf-8') as f: for item in error: f.write(f"") def vlc_player(url, ext, nm): player, state = "", "" try: instance = vlc.Instance('--input-repeat=-1', '--no-fullscreen') player = instance.media_player_new() media = instance.media_new(url) player.set_media(media) player.play() time.sleep(0.5) state = str(player.get_state()) if state in ["vlc.State.Error", "State.Error", "State.Ended", "State.Opening"]: player.stop() error.append(f'\n\n') print(f'\n | Stream is working. Current state = \n') return else: print(f'\n | Stream is working. Current state = \n') player.stop() status.append(f'\n\n') return except Exception: print(f'\nStream is dead. Current state = \n') player.stop() error.append(f'\n\n') return def main(): global status, error, st_count, err_count, all_ch path = input("Введите путь к директории: ") if not Path(path).exists() or not Path(path).is_dir(): print("Директории не существует или введенный путь не ведет к директории") sys.exit(0) time_start = time.monotonic() for num, path in enumerate(Path(path).iterdir()): if Path(path).suffix == ".m3u": nm = 0 print(f"Проверка файла: | \n") if not Path(path).exists(): sys.exit(0) ext = "" with concurrent.futures.ThreadPoolExecutor(max_workers=80) as executor: with open(path, 'r', encoding='utf-8') as file: for line in file.readlines(): if line.startswith("#EXTINF"): ext = line.strip() continue if line.startswith("http"): nm += 1 all_ch += 1 executor.submit(vlc_player, url=line.strip(), ext=ext, nm=nm) time.sleep(0.3) save_status_error(path) subprocess.Popen("clear", shell=True) time.sleep(0.3) print(f"\nGood: ") print(f"Error: \n") st_count = st_count + len(status) err_count = err_count + len(error) status.clear() error.clear() Path(path).unlink() print(f"\nAll Channel: ") print(f"\nAll Good: ") print(f"All Error: \n") print(f'All Scan time | ' f' <(int(time.monotonic() - time_start) // 3600) % 24:d>ч. ' f' <(int(time.monotonic() - time_start) // 60) % 60:02d>м. ' f'с.\n') if __name__ == "__main__": main() Что же, думаю, что на этом можно данную часть статьи завершить, так как она угрожает слишком затянуться.
Создание основного инструмента, с помощью которого мы будем проверять плейлисты на работоспособность перенесем во вторую часть.Спасибо за внимание. Надеюсь, данная информация будет вам полезна