diff --git a/.gitignore b/.gitignore index 96b9c30..80a1a6b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,12 @@ /.idea /.vscode /downloaded +/src/cache/* +/src/vendor +*.log +.env *.m3u *.m3u.* *.m3u8 *.m3u8.* +!/**/.gitkeep diff --git a/README.md b/README.md index a618a43..3ba4085 100644 --- a/README.md +++ b/README.md @@ -1,97 +1,200 @@ -# Список самообновляемых плейлистов для IPTV +# Автообновляемые IPTV-плейлисты -- [Список самообновляемых плейлистов для IPTV](#список-самообновляемых-плейлистов-для-iptv) - - [Как использовать этот список?](#как-использовать-этот-список) - - [Как добавить плейлист в этот список?](#как-добавить-плейлист-в-этот-список) - - [API](#api) - - [Формат `playlists.ini`](#формат-playlistsini) - - [Дополнительные инструменты (`./tools`)](#дополнительные-инструменты-tools) - - [Как создать свой собственный плейлист](#как-создать-свой-собственный-плейлист) - - [Лицензия](#лицензия) +> **Web-версия**: [https://iptv.axenov.dev/](https://iptv.axenov.dev/) +> **FAQ**: [https://iptv.axenov.dev/faq](https://iptv.axenov.dev/faq) +> **Зеркало репозитория**: https://git.axenov.dev/anthony/iptv + +Проект, содержащий в себе инструменты для работы с IPTV-плейлистами: + +* список автообновляемых плейлистов, которые найдены в открытых источниках; +* скрипты для поиска каналов в этом списке, создания своего плейлиста; +* веб-сервис, предоставляющий короткие ссылки на эти плейлисты и отображающий список каналов. + +Плейлисты подбираются преимущественно для РФ и любых стран бывшего СНГ, но этими странами список не ограничивается. + +Поддержкой этих плейлистов занимаются сервисы и ресурсы, указанные как источник. +Вопросы работоспособности плейлистов адресуйте тем, кто несёт за них ответственность. + +Они бесплатны для использования. +Список проверяется и обновляется мной вручную. +Гарантию работоспособности никто не даёт. + +## English description + +> **Mirrored repo**: https://git.axenov.dev/anthony/iptv + +This repo contains IPTV-playlists free to use with your media-player. + +Most of them are in russian or CIS languages but you can find something interesting here for yourself. + +Also there are some handy tools to make your own playlist or find channels you want in playlists listed here. + +You can use this repo according to [LICENSE](LICENSE) conditions. + +I'm too lazy to translate and support the whole project in ru and en, sorry, guys. --- -> **[Перейти на актуальную сраницу](https://iptv.axenov.dev/)** +## Содержание -Здесь собраны ссылки на IPTV-плейлисты, которые находятся в открытом доступе. +- [Как использовать этот список?](#howtouse) +- [Формат playlists.ini](#iniformat) +- [API](#api) +- [Развёртывание проекта](#deploy) +- [Дополнительные инструменты](#tools) +- [Как создать свой собственный плейлист](#howtomake) +- [Использованный стек](#stack) +- [Лицензия](#license) -Они бесплатны для использования. Список проверяется и обновляется мной вручную. +--- -Поддержкой этих плейлистов занимаются сервисы и ресурсы, указанные как источник. - -Вопросы работоспособности плейлистов адресуйте тем, кто несёт за них ответственность. + ## Как использовать этот список? -Чтобы подключить плейлист, нужно в настройках IPTV-плеера указать ссылку в следующем формате: +Чтобы подключить плейлист, нужно в настройках медиаплеера указать ссылку в следующем формате: ``` -iptv.axenov.dev?ID +iptv.axenov.dev/ +iptv.axenov.dev? (устаревший формат) +iptv.axenov.dev/? (устаревший формат) ``` -где `ID` - один из идентификаторов, указанных в [`playlists.ini`](playlists.ini) в квадратных скобках. - -## Как добавить плейлист в этот список? - -1) Склонировать себе репозиторий, создать ветку -2) Внести изменения в файл [`playlists.ini`](playlists.ini) как описано ниже -3) Сделать коммит, отправить изменения в свой репозиторий и создать merge-request +где `` - один из идентификаторов, указанных в [`playlists.ini`](playlists.ini) в квадратных скобках. Либо провернуть всё то же самое через браузер. + + +## Формат `playlists.ini` + +```ini +# В квадратных скобках - ID плейлиста в рамках этого +# конфига (обязателен). Для удобства ввода с пульта, +# для ID рекомендуется число или короткая строка без +# пробелов и др. спецсимволов. +[1] + +# Название плейлиста (необязательно) +name = 'Рабочий и актуальный IPTV плейлист M3U' + +# Краткое описание из источника или от себя (необязательно) +desc = 'В этом IPTV плейлисте вы найдете очень много каналов в HD качестве' + +# Прямая ссылка на m3u/m3u8 плейлист (обязательно) +pls = 'https://example.com/pls.m3u' + +# Ссылка на источник, откуда взят плейлист (необязательно) +src = 'https://example.com/super-duper-playlist' + +[2] + +# ID другого плейлиста в этом списке, на который +# произойдёт редирект. Нужен для мягкой смены ID. +redirect = 1 +``` + +В описании плейлиста обязательны: + +* любой желаемый ID в квадратных скобках; +* либо `pls`, либо `redirect` (если указаны оба, то `redirect` приоритетен). + +Плейлистов с редиректами может быть сколько угодно, но они не должны быть цикличными. + + + ## API Можно получать состояние плейлистов из этого сборника при помощи метода: ``` -GET https://iptv.axenov.dev/?getinfo= +GET https://iptv.axenov.dev//json ``` -где `ID` - один из идентификаторов, указанных в [`playlists.ini`](playlists.ini) в квадратных скобках. +где `ID` -- один из идентификаторов, указанных в [`playlists.ini`](playlists.ini) в квадратных скобках. -Ответом может быть JSON следующего содержания: +В случае успеха вернётся JSON следующего содержания: ```json { - "is_online": true, - "count": 123, - "channels": [ ... ] + "id": "p1", + "url": "localhost:8080/p1", + "name": "Каналы в SD и HD качестве (smarttvnews.ru)", + "desc": "Рабочий и актуальный IPTV плейлист M3U — на июнь 2022 года", + "pls": "https://smarttvnews.ru/apps/iptvchannels.m3u", + "src": "https://smarttvnews.ru/rabochiy-i-aktualnyiy-iptv-pleylist-m3u-kanalyi-v-sd-i-hd-kachestve/", + "status": "online", + "encoding": { + "name": "UTF-8", + "alert": false + }, + "channels": [ + "Channel1", + "Channel2", + "ChannelX" + ], + "count": 3 } ``` где: -* `is_online` - `bool`, доступность плейлиста -* `count` - `uint|char[1]`, количество каналов >=0 либо `'-'` при `is_online === false` -* `channels` - `string[]`, массив строк с названиями каналов, может быть пустым. -Также ответ может быть пустым (вообще пустым, даже не `null`). -Такое я встречал с одним конкретном плейлисте с поехавшей кодировкой. -Лень разбираться, пофиг. +* `id` -- название плейлиста +* `name` -- краткое описание из источника или от себя +* `url` -- короткая ссылка, которую можно использовать для добавления плейлиста в плеер +* `desc` -- подробное описание плейлиста +* `pls` -- прямая ссылка на m3u/m3u8 плейлист +* `src` -- ссылка на источник, откуда взят плейлист +* `status` -- признак доступности плейлиста (`online`, `timeout`, `offline`, `unknown`) +* `encoding` -- данные о кодировке файла плейлиста + * `name` -- название кодировки (на данный момент определяются только `UTF-8` или `Windows-1251`) + * `alert` -- признак отличия кодировки от `UTF-8`, названия каналов сконвертированы в `UTF-8` +* `channels` -- массив названий каналов +* `count` -- количество каналов >= 0 -## Формат `playlists.ini` +> Название кодировки `encoding.name` может определяться неточно! -```ini -; В квадратных скобках - ID плейлиста в рамках этого -; конфига (обязателен). Для удобства ввода с пульта, -; для ID рекомендуется число или короткая строка без -; пробелов и др. спецсимволов. -[p1] -; Название плейлиста (необязательно) -name='webarmen.com 18+' -; Краткое описание из источника или от себя (необязательно) -desc='' -; Прямая ссылка на m3u/m3u8 плейлист (обязательно) -pls='https://webarmen.com/my/iptv/auto.xxx.m3u' -; Ссылка на источник, откуда взят плейлист (необязательно) -src='https://webarmen.com/my/iptv/xxx.php' +В случае ошибки вернётся JSON в следующем формате: -[p2] -; ID другого плейлиста в этом списке, на который -; произойдёт редирект (нужно для мягкой смены ID). -; Необязателен, но если указан, то приоритетнее, чем pls. -redirect=p1 +```json +{ + "id": "p1", + "url": "localhost:8080/p1", + "name": "Каналы в SD и HD качестве (smarttvnews.ru)", + "desc": "Рабочий и актуальный IPTV плейлист M3U — на июнь 2022 года", + "pls": "https://smarttvnews.ru/apps/iptvchannels.m3u", + "src": "https://smarttvnews.ru/rabochiy-i-aktualnyiy-iptv-pleylist-m3u-kanalyi-v-sd-i-hd-kachestve/", + "status": "offline", + "error": { + "code": 22, + "message": "The requested URL returned error: 404 Not Found" + } +} ``` +где: + +* `id` -- название плейлиста +* `name` -- краткое описание из источника или от себя +* `url` -- короткая ссылка, которую можно использовать для добавления плейлиста в плеер +* `desc` -- подробное описание плейлиста +* `pls` -- прямая ссылка на m3u/m3u8 плейлист +* `src` -- ссылка на источник, откуда взят плейлист +* `status` -- признак доступности плейлиста (`online`, `timeout`, `offline`, `error`) +* `error` -- данные о кодировке файла плейлиста + * `code` -- [код ошибки curl](https://curl.se/libcurl/c/libcurl-errors.html) + * `message` -- текст ошибки curl + + + +## Развёртывание проекта + +1. Выполнить `cp .env.example .env`, установить необходимые параметры +2. Выполнить `docker compose up -d --build` +3. Открыть `https://:8080` + + + ## Дополнительные инструменты (`./tools`) ### `download-all.sh` @@ -102,7 +205,7 @@ redirect=p1 Проверяет каждый канал в плейлисте на доступность и выводит результат проверки. -Поддерживаются \*.m3u и \*.m3u8; как локальные файлы, так по прямым ссылкам. +Поддерживаются `*.m3u` и `*.m3u8`; как локальные файлы, так по прямым ссылкам. Коды ошибок доступны [здесь](https://everything.curl.dev/usingcurl/returns). @@ -139,9 +242,9 @@ Check stats ### `find-in-pls.sh` -Находит каналы по заданному регулярному выражению в указанном плейлисте. +Находит каналы по заданному регулярному выражению в одном указанном плейлисте. -Поддерживаются \*.m3u и \*.m3u8; как локальные файлы, так по прямым ссылкам. +Поддерживаются `*.m3u` и `*.m3u8`; как локальные файлы, так по прямым ссылкам. Пример: @@ -165,7 +268,7 @@ Found: 2 ### `find-in-all.sh` -Находит каналы по заданному регулярному выражению в плейлистах, скачанных через `download-all.sh`. +Находит каналы по заданному регулярному выражению во всех плейлистах, скачанных через `download-all.sh`. Пример: @@ -199,9 +302,10 @@ Nothing found ### `make-pls.sh` -Находит каналы по заданному регулярному выражению в плейлистах, скачанных через `download-all.sh`. +Находит каналы по заданному регулярному выражению во всех плейлистах, скачанных через `download-all.sh`. -Отличается от `find-in-all.sh` тем, что тот выводит результат в человекочитаемом формате, а этот -- в готовом m3u формате для сохранения в файл. +Отличается от `find-in-all.sh` тем, что тот выводит результат в человекочитаемом формате, а этот -- в готовом m3u +формате для сохранения в файл. Пример: @@ -222,6 +326,7 @@ http://live02-cdn.tv.ti.ru:80/dtv/id376_NBN_SG--Fox_HD/04/plst.m3u8 ... ``` + ## Как создать свой собственный плейлист? 1. Скачать все плейлисты, указанные в [`playlists.ini`](playlists.ini): @@ -232,7 +337,8 @@ http://live02-cdn.tv.ti.ru:80/dtv/id376_NBN_SG--Fox_HD/04/plst.m3u8 ``` $ ./tools/make-pls.sh "(fox|disney)" > my.m3u8 ``` - Так в плейлисте `./my.m3u8` окажутся все каналы из скачанных плейлистов, в названиях которых встрелись `fox` или `disney`. + Так в плейлисте `./my.m3u8` окажутся все каналы из скачанных плейлистов, в названиях которых встретились `fox` + или `disney`. 3. Проверить доступность каналов в полученном плейлисте: ``` $ ./tools/check-pls.sh my.m3u8 @@ -242,6 +348,19 @@ http://live02-cdn.tv.ti.ru:80/dtv/id376_NBN_SG--Fox_HD/04/plst.m3u8 4. Вручную: удалить нерабочие, мусорные и продублировавшиеся (по ссылкам) каналы. 5. Вручную: добавить плейлист в IPTV-плеер и перепроверить результат. + + +## Использованный стек + +* [docker compose](https://docs.docker.com/compose/) +* [php8.1-fpm](https://www.php.net/releases/8.1/en.php) +* [FlightPHP](https://flightphp.com/learn) +* [Bootstrap 5](https://getbootstrap.com/docs/5.0/getting-started/introduction/) +* [nginx](https://nginx.org/ru/) +* bash + + + ## Лицензия [The MIT License](LICENSE) diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..7c1dad5 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,39 @@ +version: '3' + +networks: + iptv: + driver: bridge + +services: + + php: + container_name: iptv-php + build: 'docker/php' + restart: unless-stopped + networks: + - iptv + volumes: + - /etc/localtime:/etc/localtime:ro + - ./docker/php/www.conf:/usr/local/etc/php-fpm.d/www.conf:ro + - ./docker/php/php.ini:/usr/local/etc/php/conf.d/php.ini:ro + - ./log/php:/var/log/php:rw + - ./src:/var/www:rw + - ./playlists.ini:/var/www/config/playlists.ini:ro + + nginx: + container_name: iptv-nginx + image: nginx:latest + restart: unless-stopped + networks: + - iptv + volumes: + - /etc/localtime:/etc/localtime:ro + - ./docker/nginx/vhost.conf:/etc/nginx/conf.d/default.conf:ro + - ./log/nginx:/var/log/nginx:rw + - ./src:/var/www:ro + ports: + - 8080:80 + links: + - php + depends_on: + - php diff --git a/docker/nginx/vhost.conf b/docker/nginx/vhost.conf new file mode 100644 index 0000000..c974310 --- /dev/null +++ b/docker/nginx/vhost.conf @@ -0,0 +1,30 @@ +server { + server_name iptv.local; + listen 80; + root /var/www/public; + index index.php; + gzip on; + gzip_vary on; + gzip_proxied any; + gzip_comp_level 6; + gzip_buffers 16 8k; + gzip_http_version 1.1; + gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript; + location ~* \.(jpg|jpeg|gif|css|png|js|ico|html)$ { + access_log off; + expires max; + log_not_found off; + } + location / { + try_files $uri $uri/ /index.php$is_args$args; + } + location ~ \.php$ { + try_files $uri /index.php =404; + fastcgi_pass php:9000; + fastcgi_split_path_info ^(.+\.php)(/.+)$; + fastcgi_index index.php; + fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; + fastcgi_param PATH_INFO $fastcgi_path_info; + include fastcgi_params; + } +} diff --git a/docker/php/dockerfile b/docker/php/dockerfile new file mode 100644 index 0000000..180a248 --- /dev/null +++ b/docker/php/dockerfile @@ -0,0 +1,16 @@ +FROM php:8.1-fpm + +RUN apt update && \ + apt upgrade -y && \ + apt install -y git && \ + curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer + +# https://pecl.php.net/package/xdebug +RUN pecl channel-update pecl.php.net && \ + pecl install xdebug-3.1.5 && \ + mkdir -p /var/log/php + +EXPOSE 9000 +WORKDIR /var/www +CMD composer install +CMD php-fpm diff --git a/docker/php/php.ini b/docker/php/php.ini new file mode 100644 index 0000000..f87b81b --- /dev/null +++ b/docker/php/php.ini @@ -0,0 +1,27 @@ +[PHP] +file_uploads = Off +; upload_max_filesize=256M +; post_max_size=256M +error_reporting = E_ALL +;& ~E_DEPRECATED & ~E_STRICT & ~E_NOTICE & ~E_WARNING + +[opcache] +opcache.enable = 1 +opcache.enable_cli = 1 +opcache.memory_consumption = 128 +opcache.max_accelerated_files = 30000 +opcache.revalidate_freq = 0 +opcache.jit_buffer_size = 64M +opcache.jit = tracing + +[xdebug] +; https://xdebug.org/docs/all_settings +zend_extension = xdebug.so +xdebug.mode = develop,debug +xdebug.REQUEST = * +xdebug.SESSION = * +xdebug.SERVER = * +xdebug.client_host = 172.17.0.1 +;xdebug.start_with_request=yes +xdebug.start_with_request = trigger +xdebug.trigger_value = go diff --git a/docker/php/www.conf b/docker/php/www.conf new file mode 100644 index 0000000..27a90ee --- /dev/null +++ b/docker/php/www.conf @@ -0,0 +1,21 @@ +[www] +user = www-data +group = www-data +listen = 127.0.0.1:9000 +pm = dynamic +pm.max_children = 5 +pm.start_servers = 2 +pm.min_spare_servers = 1 +pm.max_spare_servers = 3 +pm.max_requests = 50 +pm.status_path = /status +ping.path = /ping +ping.response = pong +access.log = /var/log/php/$pool.access.log +;access.format = "%R - %u %t \"%m %r%Q%q\" %s %f %{milli}d %{kilo}M %C%%" +; chroot = /var/www +; chdir = /var/www +php_flag[display_errors] = on +php_admin_value[error_log] = /var/log/php/www.error.log +php_admin_flag[log_errors] = on +php_admin_value[memory_limit] = 32M diff --git a/index.php b/index.php deleted file mode 100644 index 01429fb..0000000 --- a/index.php +++ /dev/null @@ -1,345 +0,0 @@ - false, - 'count' => 0, - 'channels' => [], - ]); - } - - $curl = curl_init(); - curl_setopt($curl, CURLOPT_URL, $pls['pls']); - curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1); - curl_setopt($curl, CURLOPT_TIMEOUT, 5); - curl_setopt($curl, CURLOPT_HEADER, 0); - $response = curl_exec($curl); - $code = curl_getinfo($curl, CURLINFO_RESPONSE_CODE); - curl_close($curl); - unset($curl); - if ($response === false) { // timed out - response([ - 'is_online' => false, - 'count' => '-', - 'channels' => [], - ]); - } - - $matches = []; - preg_match_all("/^#EXTINF:-?\d.*,\s*(.*)/m", $response, $matches); - $channels = array_map('trim', $matches[1]); - unset($response, $matches); - response([ - 'is_online' => $is_online = $code < 400, - 'count' => $is_online ? count($channels) : '-', - 'channels' => $channels, - ]); -} - -// redirect to playlist -if (array_intersect(array_keys($_GET), array_keys($ini))) { - $id = array_keys($_GET)[0]; - if (!empty($ini[$id]['redirect'])) { - header('Location: ' . $ini[$ini[$id]['redirect']]['pls']); - die; - } elseif (!empty($ini[$id]['pls'])) { - header('Location: ' . $ini[$id]['pls']); - die; - } -} -?> - - - - - - IPTV Playlists - - - - - -
-
- -

Самообновляемые плейлисты IPTV

-
-

- GitHub | axenov.dev
- Обновлено: МСК
- Плейлистов в списке:  -

-
- -
- -
-
- - - - - - - - - - - $element) { - if (empty($element['pls'])) { - continue; - } - ?> - - - - - - - - -
IDИнформация о плейлистеКаналовСсылка
- - - - ? -
- M3U - - | Источник - - -

- -
-
-
- загрузка... -
-
- - - -
-
-
-

Что здесь происходит?

-

- На этой странице собраны ссылки на IPTV-плейлисты, которые находятся в открытом доступе. - Они отбираются мной вручную и проверяются здесь автоматически. -

-

- Ресурс не занимается трансляцией видео- и аудиопотоков, - администрированием конечных плейлистов и программ телепередач или хранением всего указанного. - Подобными вопросами занимаются администраторы ресурсов, указанные как источник, и те, с чьих ресурсов - ведётся трансляция. -

-

- Ресурс предоставляет только информацию об активности плейлистов, найденных - в открытом доступе, и короткие ссылки на них для удобства ввода с пульта на телевизоре. - Вопросы работоспособности плейлистов и каналов адресуйте тем, кто несёт за них ответственность. -

- -

Что означают статусы плейлистов?

-
    -
  • - ? Загрузка данных, нужно немного подождать. -
  • -
  • - online Плейлист, возможно, активен. -
  • -
  • - unknown Состояние неизвестно. - Возможно, плейлист активен, но корректно его проверить не удалось. -
  • -
  • - timeout Не удалось вовремя проверить плейлист. -
  • -
  • - offline Плейлист неактивен. -
  • -
  • - error Ошибка при проверке плейлиста. -
  • -
- -

Почему нельзя доверять результатам проверки?

-

- Я не гарантирую корректность и актуальность информации, которую ты увидишь здесь. - Хотя я и стараюсь улучшать качество проверок, но всё же рекомендую проверять желаемые - плейлисты самостоятельно вручную, ибо нет никаких гарантий: -

-
    -
  • - что это вообще плейлисты, а не чьи-то архивы с мокрыми кисками; -
  • -
  • - что плейлисты по разным ссылкам не дублируют друг друга и отличаются каналами хотя бы на четверть; -
  • -
  • - что плейлист работоспособен (каналы работают, корректно названы, имеют аудио, etc.); -
  • -
  • - что подгрузится корректное количество каналов и их список (хотя на это я ещё могу влиять и - стараюсь как-то улучшить). -
  • -
- -

Эти плейлисты и каналы в них -- бесплатны?

-

Возможно. По крайней мере, так утверждают источники.

- -

Как подключить плейлист?

-

- - Добавь в свой IPTV-плеер ссылку из последней колонки. -

- -

Какова гарантия, что я добавлю себе плейлист отсюда и он работать хоть сколько-нибудь долго?

-

- Никакова. - Мёртвые плейлисты я периодически вычищаю, реже -- добавляю новые. - ID плейлистов могут меняться, поэтому вполне может произойти внезапная подмена одного другим, однако намеренно я так не делаю. - Если один плейлист переезжает на новый адрес, то я ставлю временное перенаправление со старого ID на новый. - Плюс читай выше про доверие результатам проверки (проблема может быть не на этой стороне). -

- -

Где взять программу передач (EPG)?

-
    -
  • https://iptvx.one/viewtopic.php?f=12&t=4
  • -
  • https://iptvmaster.ru/epg-for-iptv
  • -
  • https://google.com
  • -
- -

Как часто обновляется этот список?

-

- Время от времени. - Иногда я захожу сюда и проверяю всё ли на месте, иногда занимаюсь какими-то доработками. -

-

- Если есть кандидаты на добавление, то читай ниже. -

- -

Как часто обновляются сами плейлисты (каналы)?

-

- Зависит от источника. Я этим не занимаюсь. -

- -

Как пополнить этот список?

-

- Сделать pull-request в репозиторий. - Я проверю плейлист и добавлю его в общий список, если всё ок. -

-
-
-
- - -
- - - diff --git a/log/nginx/.gitkeep b/log/nginx/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/log/php/.gitkeep b/log/php/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/playlists.ini b/playlists.ini deleted file mode 100644 index 6197c19..0000000 --- a/playlists.ini +++ /dev/null @@ -1,452 +0,0 @@ -[1] -name='Рабочий и актуальный IPTV плейлист M3U (smarttvapp.ru)' -desc='В этом IPTV плейлисте формата m3u вы найдете очень много каналов в HD качестве. Познавательные: Discovery HD, Discovery Science, Nat Geo, Nat Geo WILD, TLC HD. Детские: Nickelodeon HD. Спортивные, много каналов с фильмами: Дом Кино Премиум HD, Кинопремьера HD. Плейлист актуален на: 3.02.22' -pls='https://smarttvapp.ru/app/iptvfull.m3u' -src='https://smarttvapp.ru/aktualnyiy-i-rabochiy-iptv-pleylist-m3u/' - -[2] -name='Самообновляемый IPTV плейлист — июнь 2022 (prodigtv.ru)' -desc='Возможно, доублирует какой-то от smarttvnews' -pls='https://prodigtv.ru/play/iptv.m3u' -src='https://prodigtv.ru/iptv/playlist/samoobnovlyaemyj' - -[3] -name='IPTV каналы плейлист m3u без тормозов (poiskpmr)' -desc='Самые популярные и актуальные жанры iptv каналов m3u в 2022 году' -pls='https://iptvmaster.ru/december.m3u' -src='https://poiskpmr.ru/blog/ip-kanaly-plejlist-m3u-bez-tormozov-b256' - -[4] -name='Самообновляемый IPTV плейлист 2022 на июнь (iptv-russia.ru)' -desc='' -pls='https://iptv-russia.ru/list/iptv-playlist.m3u' -src='https://iptv-russia.ru/playlists/iptv-playlist/' - -[5] -name='IPTV плейлист с миксом ТВ каналов 2022 на июнь (iptv-russia.ru)' -desc='' -pls='https://iptv-russia.ru/list/mix.m3u' -src='https://iptv-russia.ru/playlists/mix/' - -[p1] -name='Каналы в SD и HD качестве (smarttvnews.ru)' -desc='Рабочий и актуальный IPTV плейлист M3U — на июнь 2022 года' -pls='https://smarttvnews.ru/apps/iptvchannels.m3u' -src='https://smarttvnews.ru/rabochiy-i-aktualnyiy-iptv-pleylist-m3u-kanalyi-v-sd-i-hd-kachestve/' - -[p2] -name='Самообновляемый iptv плейлист 2022 июнь (smarttvnews.ru)' -desc='Лучший самообновляемый IPTV плейлист в 2022 году' -pls='https://smarttvnews.ru/apps/freeiptv.m3u' -src='https://smarttvnews.ru/samoobnovlyaemyj-iptv-plejlist/' - -[p4] -name='IPTV плейлист на июль 2020 (iptvm3u.ru)' -desc='Плейлист содержит 1200+ ТВ каналов всех категорий (музыка, спорт, детские, образовательные, взрослые). Так же в файле есть каналы Украины, Белоруссии, Молдовы. Для удобства каналы других стран расположены в низу списка.' -pls='https://iptvm3u.ru/0720.m3u' -src='https://iptvm3u.ru/iptv-plejlist-na-ijul-2/' - -[p5] -name='Плейлист 2020 от iptv-playlisty.ru' -desc='Трансляции для детей и подростков. Сериалы и Премьеры кино. Каналы для женщин и мужских развлечений. Документалистика и исторические лента о событиях прошлого.' -pls='https://iptv-playlisty.ru/wp-content/uploads/m3u/2020.m3u' -src='https://iptv-playlisty.ru/collection/samyj-svezheobnovlennyj-plejlist-iptv-na-2020-god/' - -[p6] -redirect='p1' - -[kid1] -name='Детский IPTV «Kids»' -desc='' -pls='https://webhalpme.ru/kids.m3u' -src='https://webhalpme.ru/samoobnovljaemye-plejlisty-iptv-2019/' - -[kid2] -name='Плейлист детских каналов iptvmaster.ru' -desc='02.08.2020 Среди детских каналов есть и отечественные, и зарубежные, большинство из них в HD.' -pls='https://iptvmaster.ru/kids-all.m3u' -src='https://iptvmaster.ru/detskie-kanaly-playlist/' - -[np] -name='Плейлист newplay (iptv-playlisty.ru)' -desc='Общефедеральные. Каналы фильмов. Все на русском. Имеются с зарубежными лентами. Спортивные. Как трансляции, так и кино данной тематики. Детские. Мультфильмы и передачи.' -pls='https://iptv-playlisty.ru/wp-content/uploads/m3u/newplay.m3u' -src='https://iptv-playlisty.ru/collection/besplatnyj-iptv-plejlist-formata-m3u/' - -[his] -name='IPTV плейлист телеканала History (iptv-playlisty.ru)' -desc= -pls='https://iptv-playlisty.ru/wp-content/uploads/m3u/history.m3u' -src='https://iptv-playlisty.ru/iptv-kanaly/iptv-plejlist-telekanala-history/' - -[dis] -name='IPTV плейлист телеканала Discovery (iptv-playlisty.ru)' -desc= -pls='https://iptv-playlisty.ru/wp-content/uploads/m3u/discovery.m3u' -src='https://iptv-playlisty.ru/iptv-kanaly/iptv-plejlist-telekanala-discovery/' - -[ngeo] -name='IPTV плейлист канала national geographic (iptv-playlisty.ru)' -desc= -pls='https://iptv-playlisty.ru/wp-content/uploads/m3u/ngeografik.m3u' -src='https://iptv-playlisty.ru/iptv-kanaly/iptv-plejlist-kanala-national-geographic/' - -[news] -name='Новости' -desc= -pls='https://iptvmaster.ru/news.m3u' -src='https://iptvsensei.ru/novye-samoobnovlyaemye-plejlisty' - -[mus] -name='Музыкальные 1' -desc= -pls='https://iptvmaster.ru/music.m3u' -src='https://iptvsensei.ru/novye-samoobnovlyaemye-plejlisty' - -[mus1] -name='Музыкальные 2 (smarttvnews.ru)' -desc='IPTV плейлист музыкальных каналов 2022' -pls='https://smarttvnews.ru/apps/music.m3u' -src='https://iptvsensei.ru/novye-samoobnovlyaemye-plejlisty' - -[mus2] -name='IPTV плейлист с музыкальными каналами (iptv-russia.ru)' -desc='' -pls='https://iptv-russia.ru/list/music.m3u' -src='https://iptv-russia.ru/playlists/music/' - -[ser] -name='Сериалы' -desc= -pls='http://bluecrabstv.do.am/serial.m3u' -src='https://iptvsensei.ru/samoobnovlyayemyye-pleylisty-iptv' - -[kino1] -name='Фильмы 1' -desc= -pls='https://smarttvnews.ru/apps/Films.m3u' -src='https://iptvsensei.ru/novye-samoobnovlyaemye-plejlisty' - -[kino2] -name='Фильмы 2' -desc= -pls='http://iptvm3u.ru/500newFilms.m3u' -src='https://iptvsensei.ru/novye-samoobnovlyaemye-plejlisty' - -[kino3] -name='Фильмы 3' -desc= -pls='http://iptvm3u.ru/film1.m3u' -src='https://iptvsensei.ru/novye-samoobnovlyaemye-plejlisty' - -[kino4] -name='Фильмы 4' -desc= -pls='http://iptvm3u.ru/film4.m3u' -src='https://iptvsensei.ru/novye-samoobnovlyaemye-plejlisty' - -[kino5] -name='Фильмы 5' -desc= -pls='https://pastebin.com/raw/jLaRge54' -src='https://iptvsensei.ru/novye-samoobnovlyaemye-plejlisty' - -[kino6] -name='IPTV плейлист с кино, сериалами и мультфильмами 2022 на июнь (iptv-russia.ru)' -desc='' -pls='https://iptv-russia.ru/list/cinematic.m3u' -src='https://iptv-russia.ru/playlists/cinematic/' - -[ru1] -name='Русские 1' -desc= -pls='https://webhalpme.ru/RussiaIPTV.m3u' -src='https://iptvsensei.ru/novye-samoobnovlyaemye-plejlisty' - -[ru3] -name='Русские 3' -desc= -pls='https://getsapp.ru/IPTV/Auto_IPTV.m3u' -src='https://iptvsensei.ru/novye-samoobnovlyaemye-plejlisty' - -[ru4] -name='Русские 4' -desc= -pls='https://iptvm3u.ru/list2511.m3u8' -src='https://iptvsensei.ru/novye-samoobnovlyaemye-plejlisty' - -[ru5] -name='Русские 5' -desc= -pls='https://avdmono.do.am/film/natgeo.m3u' -src='https://iptvsensei.ru/novye-samoobnovlyaemye-plejlisty' - -[ru6] -name='Русские 6' -desc= -pls='http://iptv.ktkru.ru/playlist.m3u' -src='https://iptvsensei.ru/novye-samoobnovlyaemye-plejlisty' - -[ru7] -name='IPTV плейлист с ТВ каналами России 2022 на июнь (iptv-russia.ru)' -desc='' -pls='https://iptv-russia.ru/list/ru-all.m3u' -src='https://iptv-russia.ru/playlists/ru-all/' - -[reg] -name='IPTV Плейлист — Региональные каналы России (iptv-russia.ru)' -desc='' -pls='https://iptv-russia.ru/list/ru-regional.m3u' -src='https://iptv-russia.ru/playlists/ru-regional/' - -[ua1] -name='Украинские IPTV каналы (smarttvnews.ru)' -desc='' -pls='https://smarttvnews.ru/apps/ukraine.m3u' -src='https://smarttvnews.ru/iptv-plejlist-ukrainskih-kanalov/' - -[ua2] -name='Украинские 2' -desc='' -pls='https://iptvmaster.ru/ukraine.m3u' -src='https://iptvsensei.ru/novye-samoobnovlyaemye-plejlisty' - -[ua3] -name='IPTV плейлист с ТВ каналами Украины 2022 (iptv-russia.ru)' -desc='' -pls='https://iptv-russia.ru/list/ua-all.m3u' -src='https://iptv-russia.ru/playlists/ua-all/' - -[ua4] -name='IPTV m3u плейлист Украина самообновляемый 2022 (tva.org.ua)' -desc='IPTV плейлист m3u бесплатных украинских каналов на 29 мая 2022 року' -pls='https://tva.org.ua/ip/u/iptv_ukr.m3u' -src='https://tva.org.ua/iptv-m3u-plejlist-ukraina-samoobnovlyaemyj.html' - -[by] -name='IPTV плейлист с ТВ каналами Беларуси 2022 (iptv-russia.ru)' -desc='' -pls='https://iptv-russia.ru/list/by-all.m3u' -src='https://iptv-russia.ru/playlists/by-all/' - -[arm] -name='IPTV плейлист с ТВ каналами Армении 2022 (iptv-russia.ru)' -desc='' -pls='https://iptv-russia.ru/list/arm-all.m3u' -src='https://iptv-russia.ru/playlists/all-arm/' - -[uz] -name='IPTV плейлист с ТВ каналами Узбекистана 2022 (iptv-russia.ru)' -desc='' -pls='https://iptv-russia.ru/list/uz-all.m3u' -src='https://iptv-russia.ru/playlists/uz-all/' - -[uz] -name='IPTV плейлист с ТВ каналами Казахстана 2022 (iptv-russia.ru)' -desc='' -pls='https://iptv-russia.ru/list/kz-all.m3u' -src='https://iptv-russia.ru/playlists/kz-all/' - -[tr] -name='IPTV плейлист с ТВ каналами Турции и Азербайджана 2022 (iptv-russia.ru)' -desc='' -pls='https://iptv-russia.ru/list/tr-all.m3u' -src='https://iptv-russia.ru/playlists/tr-all/' - -[usa] -name='IPTV плейлист с ТВ каналами США 2022 (iptv-russia.ru)' -desc='' -pls='https://iptv-russia.ru/list/usa-all.m3u' -src='https://iptv-russia.ru/playlists/usa-all/' - -[ita] -name='IPTV плейлист с ТВ каналами Италии 2022 (iptv-russia.ru)' -desc='' -pls='https://iptv-russia.ru/list/ita-all.m3u' -src='https://iptv-russia.ru/playlists/ita-all/' - -[m2] -name='Мультфильмы 2' -desc= -pls='https://iptvmaster.ru/kids.m3u' -src='https://iptvsensei.ru/novye-samoobnovlyaemye-plejlisty' - -[m3] -name='Мультфильмы 3' -desc= -pls='https://iptvmaster.ru/multfilm.m3u' -src='https://iptvsensei.ru/novye-samoobnovlyaemye-plejlisty' - -[m4] -name='Мультфильмы 4' -desc= -pls='https://iptvmaster.ru/kids-all.m3u' -src='https://iptvsensei.ru/novye-samoobnovlyaemye-plejlisty' - -[m5] -name='Мультфильмы 5' -desc= -pls='https://smarttvnews.ru/apps/Films.m3u' -src='https://iptvsensei.ru/samoobnovlyayemyye-pleylisty-iptv' - -[m6] -name='Мультфильмы 6' -desc= -pls='http://iptvm3u.ru/film4.m3u' -src='https://iptvsensei.ru/samoobnovlyayemyye-pleylisty-iptv' - -[m7] -name='Мультфильмы 7' -desc= -pls='http://iptvm3u.ru/film2.m3u' -src='https://iptvsensei.ru/samoobnovlyayemyye-pleylisty-iptv' - -[m8] -name='Мультфильмы 8' -desc= -pls='http://iptvm3u.ru/film1.m3u' -src='https://iptvsensei.ru/samoobnovlyayemyye-pleylisty-iptv' - -[m9] -name='Мультфильмы 9' -desc= -pls='http://iptvm3u.ru/500newFilms.m3u' -src='https://iptvsensei.ru/samoobnovlyayemyye-pleylisty-iptv' - -[m10] -name='Детский Iptv плейлист с каналами и мультфильмами (smarttvnews.ru)' -desc='' -pls='https://smarttvnews.ru/apps/mult.m3u' -src='https://smarttvnews.ru/samoobnovlyaemyie-iptv-pleylistyi/' - -[sci] -name='Познавательные' -desc='' -pls='https://iptvmaster.ru/poznavatelnoe.m3u' -src='https://iptvsensei.ru/novye-samoobnovlyaemye-plejlisty' - -[sci2] -name='IPTV плейлист с познавательными ТВ каналами 2022 на июнь (iptv-russia.ru)' -desc='' -pls='https://iptv-russia.ru/list/sci-all.m3u' -src='https://iptv-russia.ru/playlists/sci-all/' - -[sp] -name='IPTV плейлист со спортивными каналами 2022 на июнь (iptv-russia.ru)' -desc='' -pls='https://iptv-russia.ru/list/sport-all.m3u' -src='https://iptv-russia.ru/playlists/sports-all/' - -[cam] -name='IPTV плейлист с вебкамерами России и мира 2022 на июнь (iptv-russia.ru)' -desc='' -pls='https://iptv-russia.ru/list/webcams.m3u' -src='https://iptv-russia.ru/playlists/webcams/' - -[cam2] -name='Веб камеры онлайн всего мира m3u (tva.org.ua)' -desc='Веб камеры со всего мира онлайн в формате m3u плейлиста iptv.' -pls='https://tva.org.ua/ip/web/web-kam-14.12.2021.m3u' -src='https://tva.org.ua/veb-kamery-onlayn-vsego-mira-m3u.html' - -[r1] -name='Радио каналы 1' -desc= -pls='http://lradio.c1.biz/ltradio.m3u' -src='https://iptvsensei.ru/novye-samoobnovlyaemye-plejlisty' - -[r3] -name='Радио каналы 3' -desc= -pls='https://iptvmaster.ru/radio.m3u' -src='https://iptvsensei.ru/novye-samoobnovlyaemye-plejlisty' - -[sng1] -name='Каналы СНГ 1' -desc= -pls='https://iptvm3u.ru/iptv1218.m3u' -src='https://iptvsensei.ru/novye-samoobnovlyaemye-plejlisty' - -[sng2] -name='Каналы СНГ 2' -desc= -pls='https://iptvm3u.ru/0119.m3u' -src='https://iptvsensei.ru/novye-samoobnovlyaemye-plejlisty' - -[sng3] -name='Каналы СНГ 3' -desc= -pls='https://iptvm3u.ru/0219.m3u' -src='https://iptvsensei.ru/novye-samoobnovlyaemye-plejlisty' - -[sng4] -name='Каналы СНГ 4' -desc= -pls='http://iptvm3u.ru/iptv082018.m3u' -src='https://iptvsensei.ru/novye-samoobnovlyaemye-plejlisty' - -[sng5] -name='Каналы СНГ 5' -desc= -pls='https://iptvm3u.ru/0919.m3u' -src='https://iptvsensei.ru/novye-samoobnovlyaemye-plejlisty' - -[sng6] -name='Каналы СНГ 6' -desc= -pls='https://iptvm3u.ru/0819.m3u' -src='https://iptvsensei.ru/novye-samoobnovlyaemye-plejlisty' - -[sng7] -name='Каналы СНГ 7' -desc= -pls='https://iptvm3u.ru/1019.m3u' -src='https://iptvsensei.ru/novye-samoobnovlyaemye-plejlisty' - -[sng8] -name='Каналы СНГ 8' -desc= -pls='https://iptvm3u.ru/1119.m3u' -src='https://iptvsensei.ru/novye-samoobnovlyaemye-plejlisty' - -[sng10] -name='Каналы СНГ 10' -desc= -pls='https://webhalpme.ru/donwhm.m3u' -src='https://iptvsensei.ru/novye-samoobnovlyaemye-plejlisty' - -[sng11] -name='Каналы СНГ 11' -desc= -pls='https://iptvmaster.ru/hd.m3u' -src='https://iptvsensei.ru/novye-samoobnovlyaemye-plejlisty' - -[sng12] -name='Каналы СНГ 12' -desc= -pls='https://iptvmaster.ru/armenia.m3u' -src='https://iptvsensei.ru/novye-samoobnovlyaemye-plejlisty' - -[sng13] -name='Каналы СНГ 13' -desc= -pls='https://dl.dropboxusercontent.com/s/iw9v57cln6dfkpu/Vinnitsa.m3u' -src='https://iptvsensei.ru/samoobnovlyayemyye-pleylisty-iptv' - -[sng14] -name='Каналы СНГ 14' -desc= -pls='http://gorod.tv/iptv.m3u' -src='https://iptvsensei.ru/samoobnovlyayemyye-pleylisty-iptv' - -[x] -name='IPTV плейлист для взрослых 2022 (smarttvnews.ru)' -desc='Рабочий IPTV плейлист с каналами и фильмами для взрослых' -pls='https://smarttvnews.ru/apps/xxx.m3u' -src='https://smarttvnews.ru/iptv-plejlist-dlya-vzroslyh/' - -[x2] -name='IPTV плейлист для взрослых 2022 (iptv-russia.ru)' -desc='' -pls='https://iptv-russia.ru/list/xxx.m3u' -src='https://iptv-russia.ru/playlists/xxx/' diff --git a/playlists.ini b/playlists.ini new file mode 120000 index 0000000..28db6c0 --- /dev/null +++ b/playlists.ini @@ -0,0 +1 @@ +src/config/playlists.ini \ No newline at end of file diff --git a/src/.env.example b/src/.env.example new file mode 100644 index 0000000..33ed7cc --- /dev/null +++ b/src/.env.example @@ -0,0 +1,7 @@ +APP_TITLE= +APP_URL= +TWIG_CACHE=1 +TWIG_DEBUG=0 +FLIGHT_CASE_SENSITIVE=0 +FLIGHT_HANDLE_ERRORS=1 +FLIGHT_LOG_ERRORS=1 diff --git a/src/app/Controllers/Controller.php b/src/app/Controllers/Controller.php new file mode 100644 index 0000000..3939f8a --- /dev/null +++ b/src/app/Controllers/Controller.php @@ -0,0 +1,9 @@ +ini = new PlaylistProcessor(); + } + + /** + * @throws Exception + */ + public function index() + { + if (Flight::request()->query->count() > 0) { + $id = Flight::request()->query->keys()[0]; + Flight::redirect(base_url("$id")); + die; + } + view('list', [ + 'updated_at' => $this->ini->updatedAt(), + 'count' => $this->ini->playlists->count(), + 'playlists' => $this->ini->playlists->where('redirect_id', null)->toArray(), + ]); + } + + /** + * @throws Exception + */ + public function faq() + { + view('faq'); + } + + /** + * @throws Exception + */ + public function details(string $id): void + { + $playlist = $this->ini->playlist($id); + if ($playlist instanceof RedirectedPlaylist) { + Flight::redirect(base_url($playlist->redirect_id . '/info')); + } + view('details', [ + 'id' => $id, + 'playlist' => $playlist->toArray(), + 'info' => $this->ini->parse($id), + ]); + } + + /** + * @throws Exception + */ + public function ajax(string $id): void + { + $playlist = $this->ini->playlist($id); + if ($playlist instanceof RedirectedPlaylist) { + Flight::redirect(base_url($playlist->redirect_id . '/getInfo')); + } + Flight::json([ + 'playlist' => $playlist->toArray(), + 'info' => $this->ini->parse($id), + ]); + } +} diff --git a/src/app/Controllers/PlaylistController.php b/src/app/Controllers/PlaylistController.php new file mode 100644 index 0000000..81ff069 --- /dev/null +++ b/src/app/Controllers/PlaylistController.php @@ -0,0 +1,65 @@ +ini = new PlaylistProcessor(); + } + + /** + * @throws Exception + */ + public function download($id) + { + $playlist = $this->ini->playlist($id); + if ($playlist instanceof RedirectedPlaylist) { + Flight::redirect(base_url($playlist->redirect_id)); + die; + } + Flight::redirect($playlist->pls); + } + + /** + * @throws Exception + */ + public function details(string $id): void + { + $playlist = $this->ini->playlist($id); + if ($playlist instanceof RedirectedPlaylist) { + Flight::redirect(base_url($playlist->redirect_id . '/details')); + die; + } + view('details', [ + ...$playlist->toArray(), + ...$this->ini->parse($id), + ]); + } + + /** + * @throws Exception + */ + public function json(string $id): void + { + $playlist = $this->ini->playlist($id); + if ($playlist instanceof RedirectedPlaylist) { + Flight::redirect(base_url($playlist->redirect_id . '/json')); + die; + } + Flight::json([ + ...$playlist->toArray(), + ...$this->ini->parse($id), + ]); + } +} diff --git a/src/app/Core/BasicPlaylist.php b/src/app/Core/BasicPlaylist.php new file mode 100644 index 0000000..ecd30c3 --- /dev/null +++ b/src/app/Core/BasicPlaylist.php @@ -0,0 +1,17 @@ +id); + } +} diff --git a/src/app/Core/Bootstrapper.php b/src/app/Core/Bootstrapper.php new file mode 100644 index 0000000..5e2db9c --- /dev/null +++ b/src/app/Core/Bootstrapper.php @@ -0,0 +1,77 @@ +loadEnv(root_path() . '/.env'); + } + + public static function bootSettings(): void + { + $settings = Arr::dot(require_once config_path('app.php')); + Arr::map($settings, function ($value, $key) { + Flight::set("flight.$key", $value); + }); + Flight::set('config', $settings); + } + + public static function bootTwig(): void + { + $filesystemLoader = new FilesystemLoader(config('views.path')); + Flight::register( + 'view', + Environment::class, + [$filesystemLoader, config('twig')], + function ($twig) { + /** @var Environment $twig */ + Flight::set('twig', $twig); + $twig->addExtension(new TwigFunctions()); + $twig->addExtension(new DebugExtension()); + } + ); + } + + public static function bootRoutes(): void + { + Flight::route( + 'GET /', + fn() => (new HomeController())->index() + ); + Flight::route( + 'GET /faq', + fn() => (new HomeController())->faq() + ); + Flight::route( + 'GET /@id:[a-zA-Z0-9_-]+', + fn($id) => (new PlaylistController())->download($id) + ); + Flight::route( + 'GET /?[a-zA-Z0-9_-]+', + fn($id) => (new PlaylistController())->download($id) + ); + Flight::route( + 'GET /@id:[a-zA-Z0-9_-]+/details', + fn($id) => (new PlaylistController())->details($id) + ); + Flight::route( + 'GET /@id:[a-zA-Z0-9_-]+/json', + fn($id) => (new PlaylistController())->json($id) + ); + } +} diff --git a/src/app/Core/Playlist.php b/src/app/Core/Playlist.php new file mode 100644 index 0000000..df91ffe --- /dev/null +++ b/src/app/Core/Playlist.php @@ -0,0 +1,45 @@ +url = str_replace(['http://', 'https://'], '', base_url($id)); + $this->name = $params['name'] ?? "Плейлист #$id"; + $this->desc = $params['desc'] ?? null; + $this->pls = $params['pls']; + $this->src = $params['src'] ?? null; + } + + public function toArray(): array + { + return [ + 'id' => $this->id, + 'url' => $this->url, + 'name' => $this->name, + 'desc' => $this->desc, + 'pls' => $this->pls, + 'src' => $this->src, + ]; + } +} diff --git a/src/app/Core/PlaylistProcessor.php b/src/app/Core/PlaylistProcessor.php new file mode 100644 index 0000000..c8ed6d8 --- /dev/null +++ b/src/app/Core/PlaylistProcessor.php @@ -0,0 +1,119 @@ +updated_at = date('d.m.Y h:i', filemtime($filepath)); + $this->playlists = collect(parse_ini_file($filepath, true)) + ->transform(function ($playlist, $id) { + return empty($playlist['redirect']) + ? new Playlist((string)$id, $playlist) + : new RedirectedPlaylist((string)$id, $playlist['redirect']); + }); + } + + public function hasId(string $id): bool + { + return in_array($id, $this->playlists->keys()->toArray()); + } + + public function playlist(string $id): Playlist|RedirectedPlaylist + { + !$this->hasId($id) && throw new \InvalidArgumentException("Плейлист с ID=$id не найден"); + return $this->playlists[$id]; + } + + public function check(string $id): bool + { + $curl = curl_init(); + curl_setopt($curl, CURLOPT_URL, $this->playlist($id)['pls']); + curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1); + curl_setopt($curl, CURLOPT_TIMEOUT, 5); + curl_setopt($curl, CURLOPT_HEADER, 0); + curl_setopt($curl, CURLOPT_NOBODY, 1); + curl_exec($curl); + $code = curl_getinfo($curl, CURLINFO_RESPONSE_CODE); + curl_close($curl); + return $code < 400; + } + + protected function fetch(string $id) + { + $curl = curl_init(); + curl_setopt_array($curl, [ + CURLOPT_URL => $this->playlist($id)->pls, + CURLOPT_RETURNTRANSFER => true, + CURLOPT_TIMEOUT => 5, + CURLOPT_HEADER => false, + CURLOPT_FAILONERROR => true, + ]); + $content = curl_exec($curl); + $http_code = curl_getinfo($curl, CURLINFO_RESPONSE_CODE); + $err_code = curl_errno($curl); + $err_text = curl_error($curl); + curl_close($curl); + return [ + 'content' => $content, + 'http_code' => $http_code, + 'err_code' => $err_code, + 'err_text' => $err_text, + ]; + } + + protected function guessStatus(int $curl_err_code): string + { + return match ($curl_err_code) { + 0 => 'online', + 28 => 'timeout', + 5, 6, 7, 22, 35 => 'offline', + default => 'error', + }; + } + + public function parse(string $id): array + { + $fetched = $this->fetch($id); + if ($fetched['err_code'] > 0) { + return [ + 'status' => $this->guessStatus($fetched['err_code']), + 'error' => [ + 'code' => $fetched['err_code'], + 'message' => $fetched['err_text'], + ], + ]; + } + $result['status'] = $this->guessStatus($fetched['err_code']); + $result['encoding']['name'] = 'UTF-8'; + $result['encoding']['alert'] = false; + if (($enc = mb_detect_encoding($fetched['content'], config('app.pls_encodings'))) !== 'UTF-8') { + $fetched['content'] = mb_convert_encoding($fetched['content'], 'UTF-8', $enc); + $result['encoding']['name'] = $enc; + $result['encoding']['alert'] = true; + } + $matches = []; + preg_match_all("/^#EXTINF:-?\d.*,\s*(.*)/m", $fetched['content'], $matches); + $result['channels'] = array_map('trim', $matches[1]); + $result['count'] = $fetched['http_code'] < 400 ? count($result['channels']) : 0; + return $result; + } + + /** + * @return string + */ + public function updatedAt(): string + { + return $this->updated_at; + } +} diff --git a/src/app/Core/RedirectedPlaylist.php b/src/app/Core/RedirectedPlaylist.php new file mode 100644 index 0000000..d5d4b65 --- /dev/null +++ b/src/app/Core/RedirectedPlaylist.php @@ -0,0 +1,25 @@ + $this->id, + 'redirect_id' => $this->redirect_id, + ]; + } +} diff --git a/src/app/Extensions/TwigFunctions.php b/src/app/Extensions/TwigFunctions.php new file mode 100644 index 0000000..03e727d --- /dev/null +++ b/src/app/Extensions/TwigFunctions.php @@ -0,0 +1,29 @@ +=7.4.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Container\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common Container Interface (PHP FIG PSR-11)", + "homepage": "https://github.com/php-fig/container", + "keywords": [ + "PSR-11", + "container", + "container-interface", + "container-interop", + "psr" + ], + "support": { + "issues": "https://github.com/php-fig/container/issues", + "source": "https://github.com/php-fig/container/tree/2.0.2" + }, + "time": "2021-11-05T16:47:00+00:00" + }, + { + "name": "psr/simple-cache", + "version": "3.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/simple-cache.git", + "reference": "764e0b3939f5ca87cb904f570ef9be2d78a07865" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/simple-cache/zipball/764e0b3939f5ca87cb904f570ef9be2d78a07865", + "reference": "764e0b3939f5ca87cb904f570ef9be2d78a07865", + "shasum": "" + }, + "require": { + "php": ">=8.0.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\SimpleCache\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interfaces for simple caching", + "keywords": [ + "cache", + "caching", + "psr", + "psr-16", + "simple-cache" + ], + "support": { + "source": "https://github.com/php-fig/simple-cache/tree/3.0.0" + }, + "time": "2021-10-29T13:26:27+00:00" + }, + { + "name": "symfony/dotenv", + "version": "v6.1.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/dotenv.git", + "reference": "568c11bcedf419e7e61f663912c3547b54de51df" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/dotenv/zipball/568c11bcedf419e7e61f663912c3547b54de51df", + "reference": "568c11bcedf419e7e61f663912c3547b54de51df", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "conflict": { + "symfony/console": "<5.4" + }, + "require-dev": { + "symfony/console": "^5.4|^6.0", + "symfony/process": "^5.4|^6.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Dotenv\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Registers environment variables from a .env file", + "homepage": "https://symfony.com", + "keywords": [ + "dotenv", + "env", + "environment" + ], + "support": { + "source": "https://github.com/symfony/dotenv/tree/v6.1.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-04-01T07:15:35+00:00" + }, + { + "name": "symfony/polyfill-ctype", + "version": "v1.26.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-ctype.git", + "reference": "6fd1b9a79f6e3cf65f9e679b23af304cd9e010d4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/6fd1b9a79f6e3cf65f9e679b23af304cd9e010d4", + "reference": "6fd1b9a79f6e3cf65f9e679b23af304cd9e010d4", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "provide": { + "ext-ctype": "*" + }, + "suggest": { + "ext-ctype": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.26-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Ctype\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Gert de Pagter", + "email": "BackEndTea@gmail.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for ctype functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "ctype", + "polyfill", + "portable" + ], + "support": { + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.26.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-05-24T11:49:31+00:00" + }, + { + "name": "symfony/polyfill-mbstring", + "version": "v1.26.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "9344f9cb97f3b19424af1a21a3b0e75b0a7d8d7e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/9344f9cb97f3b19424af1a21a3b0e75b0a7d8d7e", + "reference": "9344f9cb97f3b19424af1a21a3b0e75b0a7d8d7e", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "provide": { + "ext-mbstring": "*" + }, + "suggest": { + "ext-mbstring": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.26-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Mbstring\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for the Mbstring extension", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "mbstring", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.26.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-05-24T11:49:31+00:00" + }, + { + "name": "twig/twig", + "version": "v3.4.2", + "source": { + "type": "git", + "url": "https://github.com/twigphp/Twig.git", + "reference": "e07cdd3d430cd7e453c31b36eb5ad6c0c5e43077" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/twigphp/Twig/zipball/e07cdd3d430cd7e453c31b36eb5ad6c0c5e43077", + "reference": "e07cdd3d430cd7e453c31b36eb5ad6c0c5e43077", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "symfony/polyfill-ctype": "^1.8", + "symfony/polyfill-mbstring": "^1.3" + }, + "require-dev": { + "psr/container": "^1.0", + "symfony/phpunit-bridge": "^4.4.9|^5.0.9|^6.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.4-dev" + } + }, + "autoload": { + "psr-4": { + "Twig\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com", + "homepage": "http://fabien.potencier.org", + "role": "Lead Developer" + }, + { + "name": "Twig Team", + "role": "Contributors" + }, + { + "name": "Armin Ronacher", + "email": "armin.ronacher@active-4.com", + "role": "Project Founder" + } + ], + "description": "Twig, the flexible, fast, and secure template language for PHP", + "homepage": "https://twig.symfony.com", + "keywords": [ + "templating" + ], + "support": { + "issues": "https://github.com/twigphp/Twig/issues", + "source": "https://github.com/twigphp/Twig/tree/v3.4.2" + }, + "funding": [ + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/twig/twig", + "type": "tidelift" + } + ], + "time": "2022-08-12T06:47:24+00:00" + } + ], + "packages-dev": [], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": [], + "prefer-stable": false, + "prefer-lowest": false, + "platform": [], + "platform-dev": [], + "plugin-api-version": "2.3.0" +} diff --git a/src/config/app.php b/src/config/app.php new file mode 100644 index 0000000..00cec23 --- /dev/null +++ b/src/config/app.php @@ -0,0 +1,30 @@ + [ + // https://flightphp.com/learn#configuration + 'base_url' => env('APP_URL', 'http://localhost:8080'), + 'case_sensitive' => bool(env('FLIGHT_CASE_SENSITIVE', false)), + 'handle_errors' => bool(env('FLIGHT_HANDLE_ERRORS', true)), + 'log_errors' => bool(env('FLIGHT_LOG_ERRORS', true)), + 'views' => [ + 'path' => views_path(), + 'extension' => '.twig', + ], + ], + 'twig' => [ + 'cache' => bool(env('TWIG_CACHE', true)) ? cache_path() . '/views' : false, + 'debug' => bool(env('TWIG_DEBUG', false)), + ], + 'app' => [ + 'title' => env('APP_TITLE', 'IPTV Playlists'), + 'pls_encodings' => [ + 'UTF-8', + 'CP1251', + // 'CP866', + // 'ISO-8859-5', + ], + ], +]; diff --git a/src/config/playlists.ini b/src/config/playlists.ini new file mode 100644 index 0000000..54d1e7a --- /dev/null +++ b/src/config/playlists.ini @@ -0,0 +1,452 @@ +[1] +name = 'Рабочий и актуальный IPTV плейлист M3U (smarttvapp.ru)' +desc = 'В этом IPTV плейлисте формата m3u вы найдете очень много каналов в HD качестве. Познавательные: Discovery HD, Discovery Science, Nat Geo, Nat Geo WILD, TLC HD. Детские: Nickelodeon HD. Спортивные, много каналов с фильмами: Дом Кино Премиум HD, Кинопремьера HD. Плейлист актуален на: 3.02.22' +pls = 'https://smarttvapp.ru/app/iptvfull.m3u' +src = 'https://smarttvapp.ru/aktualnyiy-i-rabochiy-iptv-pleylist-m3u/' + +[2] +name = 'Самообновляемый IPTV плейлист — июнь 2022 (prodigtv.ru)' +desc = 'Возможно, дублирует какой-то от smarttvnews' +pls = 'https://prodigtv.ru/play/iptv.m3u' +src = 'https://prodigtv.ru/iptv/playlist/samoobnovlyaemyj' + +[3] +name = 'IPTV каналы плейлист m3u без тормозов (poiskpmr)' +desc = 'Самые популярные и актуальные жанры iptv каналов m3u в 2022 году' +pls = 'https://iptvmaster.ru/december.m3u' +src = 'https://poiskpmr.ru/blog/ip-kanaly-plejlist-m3u-bez-tormozov-b256' + +[4] +name = 'Самообновляемый IPTV плейлист 2022 на июнь (iptv-russia.ru)' +desc = '' +pls = 'https://iptv-russia.ru/list/iptv-playlist.m3u' +src = 'https://iptv-russia.ru/playlists/iptv-playlist/' + +[5] +name = 'IPTV плейлист с миксом ТВ каналов 2022 на июнь (iptv-russia.ru)' +desc = '' +pls = 'https://iptv-russia.ru/list/mix.m3u' +src = 'https://iptv-russia.ru/playlists/mix/' + +[p1] +name = 'Каналы в SD и HD качестве (smarttvnews.ru)' +desc = 'Рабочий и актуальный IPTV плейлист M3U — на июнь 2022 года' +pls = 'https://smarttvnews.ru/apps/iptvchannels.m3u' +src = 'https://smarttvnews.ru/rabochiy-i-aktualnyiy-iptv-pleylist-m3u-kanalyi-v-sd-i-hd-kachestve/' + +[p2] +name = 'Самообновляемый iptv плейлист 2022 июнь (smarttvnews.ru)' +desc = 'Лучший самообновляемый IPTV плейлист в 2022 году' +pls = 'https://smarttvnews.ru/apps/freeiptv.m3u' +src = 'https://smarttvnews.ru/samoobnovlyaemyj-iptv-plejlist/' + +[p4] +name = 'IPTV плейлист на июль 2020 (iptvm3u.ru)' +desc = 'Плейлист содержит 1200+ ТВ каналов всех категорий (музыка, спорт, детские, образовательные, взрослые). Так же в файле есть каналы Украины, Белоруссии, Молдовы. Для удобства каналы других стран расположены в низу списка.' +pls = 'https://iptvm3u.ru/0720.m3u' +src = 'https://iptvm3u.ru/iptv-plejlist-na-ijul-2/' + +[p5] +name = 'Плейлист 2020 от iptv-playlisty.ru' +desc = 'Трансляции для детей и подростков. Сериалы и Премьеры кино. Каналы для женщин и мужских развлечений. Документалистика и исторические лента о событиях прошлого.' +pls = 'https://iptv-playlisty.ru/wp-content/uploads/m3u/2020.m3u' +src = 'https://iptv-playlisty.ru/collection/samyj-svezheobnovlennyj-plejlist-iptv-na-2020-god/' + +[p6] +redirect = 'p1' + +[kid1] +name = 'Детский IPTV «Kids»' +desc = '' +pls = 'https://webhalpme.ru/kids.m3u' +src = 'https://webhalpme.ru/samoobnovljaemye-plejlisty-iptv-2019/' + +[kid2] +name = 'Плейлист детских каналов iptvmaster.ru' +desc = '02.08.2020 Среди детских каналов есть и отечественные, и зарубежные, большинство из них в HD.' +pls = 'https://iptvmaster.ru/kids-all.m3u' +src = 'https://iptvmaster.ru/detskie-kanaly-playlist/' + +[np] +name = 'Плейлист newplay (iptv-playlisty.ru)' +desc = 'Общефедеральные. Каналы фильмов. Все на русском. Имеются с зарубежными лентами. Спортивные. Как трансляции, так и кино данной тематики. Детские. Мультфильмы и передачи.' +pls = 'https://iptv-playlisty.ru/wp-content/uploads/m3u/newplay.m3u' +src = 'https://iptv-playlisty.ru/collection/besplatnyj-iptv-plejlist-formata-m3u/' + +[his] +name = 'IPTV плейлист телеканала History (iptv-playlisty.ru)' +desc = +pls = 'https://iptv-playlisty.ru/wp-content/uploads/m3u/history.m3u' +src = 'https://iptv-playlisty.ru/iptv-kanaly/iptv-plejlist-telekanala-history/' + +[dis] +name = 'IPTV плейлист телеканала Discovery (iptv-playlisty.ru)' +desc = +pls = 'https://iptv-playlisty.ru/wp-content/uploads/m3u/discovery.m3u' +src = 'https://iptv-playlisty.ru/iptv-kanaly/iptv-plejlist-telekanala-discovery/' + +[ngeo] +name = 'IPTV плейлист канала national geographic (iptv-playlisty.ru)' +desc = +pls = 'https://iptv-playlisty.ru/wp-content/uploads/m3u/ngeografik.m3u' +src = 'https://iptv-playlisty.ru/iptv-kanaly/iptv-plejlist-kanala-national-geographic/' + +[news] +name = 'Новости' +desc = +pls = 'https://iptvmaster.ru/news.m3u' +src = 'https://iptvsensei.ru/novye-samoobnovlyaemye-plejlisty' + +[mus] +name = 'Музыкальные 1' +desc = +pls = 'https://iptvmaster.ru/music.m3u' +src = 'https://iptvsensei.ru/novye-samoobnovlyaemye-plejlisty' + +[mus1] +name = 'Музыкальные 2 (smarttvnews.ru)' +desc = 'IPTV плейлист музыкальных каналов 2022' +pls = 'https://smarttvnews.ru/apps/music.m3u' +src = 'https://iptvsensei.ru/novye-samoobnovlyaemye-plejlisty' + +[mus2] +name = 'IPTV плейлист с музыкальными каналами (iptv-russia.ru)' +desc = '' +pls = 'https://iptv-russia.ru/list/music.m3u' +src = 'https://iptv-russia.ru/playlists/music/' + +[ser] +name = 'Сериалы' +desc = +pls = 'http://bluecrabstv.do.am/serial.m3u' +src = 'https://iptvsensei.ru/samoobnovlyayemyye-pleylisty-iptv' + +[kino1] +name = 'Фильмы 1' +desc = +pls = 'https://smarttvnews.ru/apps/Films.m3u' +src = 'https://iptvsensei.ru/novye-samoobnovlyaemye-plejlisty' + +[kino2] +name = 'Фильмы 2' +desc = +pls = 'http://iptvm3u.ru/500newFilms.m3u' +src = 'https://iptvsensei.ru/novye-samoobnovlyaemye-plejlisty' + +[kino3] +name = 'Фильмы 3' +desc = +pls = 'http://iptvm3u.ru/film1.m3u' +src = 'https://iptvsensei.ru/novye-samoobnovlyaemye-plejlisty' + +[kino4] +name = 'Фильмы 4' +desc = +pls = 'http://iptvm3u.ru/film4.m3u' +src = 'https://iptvsensei.ru/novye-samoobnovlyaemye-plejlisty' + +[kino5] +name = 'Фильмы 5' +desc = +pls = 'https://pastebin.com/raw/jLaRge54' +src = 'https://iptvsensei.ru/novye-samoobnovlyaemye-plejlisty' + +[kino6] +name = 'IPTV плейлист с кино, сериалами и мультфильмами 2022 на июнь (iptv-russia.ru)' +desc = '' +pls = 'https://iptv-russia.ru/list/cinematic.m3u' +src = 'https://iptv-russia.ru/playlists/cinematic/' + +[ru1] +name = 'Русские 1' +desc = +pls = 'https://webhalpme.ru/RussiaIPTV.m3u' +src = 'https://iptvsensei.ru/novye-samoobnovlyaemye-plejlisty' + +[ru3] +name = 'Русские 3' +desc = +pls = 'https://getsapp.ru/IPTV/Auto_IPTV.m3u' +src = 'https://iptvsensei.ru/novye-samoobnovlyaemye-plejlisty' + +[ru4] +name = 'Русские 4' +desc = +pls = 'https://iptvm3u.ru/list2511.m3u8' +src = 'https://iptvsensei.ru/novye-samoobnovlyaemye-plejlisty' + +[ru5] +name = 'Русские 5' +desc = +pls = 'https://avdmono.do.am/film/natgeo.m3u' +src = 'https://iptvsensei.ru/novye-samoobnovlyaemye-plejlisty' + +[ru6] +name = 'Русские 6' +desc = +pls = 'http://iptv.ktkru.ru/playlist.m3u' +src = 'https://iptvsensei.ru/novye-samoobnovlyaemye-plejlisty' + +[ru7] +name = 'IPTV плейлист с ТВ каналами России 2022 на июнь (iptv-russia.ru)' +desc = '' +pls = 'https://iptv-russia.ru/list/ru-all.m3u' +src = 'https://iptv-russia.ru/playlists/ru-all/' + +[reg] +name = 'IPTV Плейлист — Региональные каналы России (iptv-russia.ru)' +desc = '' +pls = 'https://iptv-russia.ru/list/ru-regional.m3u' +src = 'https://iptv-russia.ru/playlists/ru-regional/' + +[ua1] +name = 'Украинские IPTV каналы (smarttvnews.ru)' +desc = '' +pls = 'https://smarttvnews.ru/apps/ukraine.m3u' +src = 'https://smarttvnews.ru/iptv-plejlist-ukrainskih-kanalov/' + +[ua2] +name = 'Украинские 2' +desc = '' +pls = 'https://iptvmaster.ru/ukraine.m3u' +src = 'https://iptvsensei.ru/novye-samoobnovlyaemye-plejlisty' + +[ua3] +name = 'IPTV плейлист с ТВ каналами Украины 2022 (iptv-russia.ru)' +desc = '' +pls = 'https://iptv-russia.ru/list/ua-all.m3u' +src = 'https://iptv-russia.ru/playlists/ua-all/' + +[ua4] +name = 'IPTV m3u плейлист Украина самообновляемый 2022 (tva.org.ua)' +desc = 'IPTV плейлист m3u бесплатных украинских каналов на 29 мая 2022 року' +pls = 'https://tva.org.ua/ip/u/iptv_ukr.m3u' +src = 'https://tva.org.ua/iptv-m3u-plejlist-ukraina-samoobnovlyaemyj.html' + +[by] +name = 'IPTV плейлист с ТВ каналами Беларуси 2022 (iptv-russia.ru)' +desc = '' +pls = 'https://iptv-russia.ru/list/by-all.m3u' +src = 'https://iptv-russia.ru/playlists/by-all/' + +[arm] +name = 'IPTV плейлист с ТВ каналами Армении 2022 (iptv-russia.ru)' +desc = '' +pls = 'https://iptv-russia.ru/list/arm-all.m3u' +src = 'https://iptv-russia.ru/playlists/all-arm/' + +[uz] +name = 'IPTV плейлист с ТВ каналами Узбекистана 2022 (iptv-russia.ru)' +desc = '' +pls = 'https://iptv-russia.ru/list/uz-all.m3u' +src = 'https://iptv-russia.ru/playlists/uz-all/' + +[uz] +name = 'IPTV плейлист с ТВ каналами Казахстана 2022 (iptv-russia.ru)' +desc = '' +pls = 'https://iptv-russia.ru/list/kz-all.m3u' +src = 'https://iptv-russia.ru/playlists/kz-all/' + +[tr] +name = 'IPTV плейлист с ТВ каналами Турции и Азербайджана 2022 (iptv-russia.ru)' +desc = '' +pls = 'https://iptv-russia.ru/list/tr-all.m3u' +src = 'https://iptv-russia.ru/playlists/tr-all/' + +[usa] +name = 'IPTV плейлист с ТВ каналами США 2022 (iptv-russia.ru)' +desc = '' +pls = 'https://iptv-russia.ru/list/usa-all.m3u' +src = 'https://iptv-russia.ru/playlists/usa-all/' + +[ita] +name = 'IPTV плейлист с ТВ каналами Италии 2022 (iptv-russia.ru)' +desc = '' +pls = 'https://iptv-russia.ru/list/ita-all.m3u' +src = 'https://iptv-russia.ru/playlists/ita-all/' + +[m2] +name = 'Мультфильмы 2' +desc = +pls = 'https://iptvmaster.ru/kids.m3u' +src = 'https://iptvsensei.ru/novye-samoobnovlyaemye-plejlisty' + +[m3] +name = 'Мультфильмы 3' +desc = +pls = 'https://iptvmaster.ru/multfilm.m3u' +src = 'https://iptvsensei.ru/novye-samoobnovlyaemye-plejlisty' + +[m4] +name = 'Мультфильмы 4' +desc = +pls = 'https://iptvmaster.ru/kids-all.m3u' +src = 'https://iptvsensei.ru/novye-samoobnovlyaemye-plejlisty' + +[m5] +name = 'Мультфильмы 5' +desc = +pls = 'https://smarttvnews.ru/apps/Films.m3u' +src = 'https://iptvsensei.ru/samoobnovlyayemyye-pleylisty-iptv' + +[m6] +name = 'Мультфильмы 6' +desc = +pls = 'http://iptvm3u.ru/film4.m3u' +src = 'https://iptvsensei.ru/samoobnovlyayemyye-pleylisty-iptv' + +[m7] +name = 'Мультфильмы 7' +desc = +pls = 'http://iptvm3u.ru/film2.m3u' +src = 'https://iptvsensei.ru/samoobnovlyayemyye-pleylisty-iptv' + +[m8] +name = 'Мультфильмы 8' +desc = +pls = 'http://iptvm3u.ru/film1.m3u' +src = 'https://iptvsensei.ru/samoobnovlyayemyye-pleylisty-iptv' + +[m9] +name = 'Мультфильмы 9' +desc = +pls = 'http://iptvm3u.ru/500newFilms.m3u' +src = 'https://iptvsensei.ru/samoobnovlyayemyye-pleylisty-iptv' + +[m10] +name = 'Детский Iptv плейлист с каналами и мультфильмами (smarttvnews.ru)' +desc = '' +pls = 'https://smarttvnews.ru/apps/mult.m3u' +src = 'https://smarttvnews.ru/samoobnovlyaemyie-iptv-pleylistyi/' + +[sci] +name = 'Познавательные' +desc = '' +pls = 'https://iptvmaster.ru/poznavatelnoe.m3u' +src = 'https://iptvsensei.ru/novye-samoobnovlyaemye-plejlisty' + +[sci2] +name = 'IPTV плейлист с познавательными ТВ каналами 2022 на июнь (iptv-russia.ru)' +desc = '' +pls = 'https://iptv-russia.ru/list/sci-all.m3u' +src = 'https://iptv-russia.ru/playlists/sci-all/' + +[sp] +name = 'IPTV плейлист со спортивными каналами 2022 на июнь (iptv-russia.ru)' +desc = '' +pls = 'https://iptv-russia.ru/list/sport-all.m3u' +src = 'https://iptv-russia.ru/playlists/sports-all/' + +[cam] +name = 'IPTV плейлист с вебкамерами России и мира 2022 на июнь (iptv-russia.ru)' +desc = '' +pls = 'https://iptv-russia.ru/list/webcams.m3u' +src = 'https://iptv-russia.ru/playlists/webcams/' + +[cam2] +name = 'Веб камеры онлайн всего мира m3u (tva.org.ua)' +desc = 'Веб камеры со всего мира онлайн в формате m3u плейлиста iptv.' +pls = 'https://tva.org.ua/ip/web/web-kam-14.12.2021.m3u' +src = 'https://tva.org.ua/veb-kamery-onlayn-vsego-mira-m3u.html' + +[r1] +name = 'Радио каналы 1' +desc = +pls = 'http://lradio.c1.biz/ltradio.m3u' +src = 'https://iptvsensei.ru/novye-samoobnovlyaemye-plejlisty' + +[r3] +name = 'Радио каналы 3' +desc = +pls = 'https://iptvmaster.ru/radio.m3u' +src = 'https://iptvsensei.ru/novye-samoobnovlyaemye-plejlisty' + +[sng1] +name = 'Каналы СНГ 1' +desc = +pls = 'https://iptvm3u.ru/iptv1218.m3u' +src = 'https://iptvsensei.ru/novye-samoobnovlyaemye-plejlisty' + +[sng2] +name = 'Каналы СНГ 2' +desc = +pls = 'https://iptvm3u.ru/0119.m3u' +src = 'https://iptvsensei.ru/novye-samoobnovlyaemye-plejlisty' + +[sng3] +name = 'Каналы СНГ 3' +desc = +pls = 'https://iptvm3u.ru/0219.m3u' +src = 'https://iptvsensei.ru/novye-samoobnovlyaemye-plejlisty' + +[sng4] +name = 'Каналы СНГ 4' +desc = +pls = 'http://iptvm3u.ru/iptv082018.m3u' +src = 'https://iptvsensei.ru/novye-samoobnovlyaemye-plejlisty' + +[sng5] +name = 'Каналы СНГ 5' +desc = +pls = 'https://iptvm3u.ru/0919.m3u' +src = 'https://iptvsensei.ru/novye-samoobnovlyaemye-plejlisty' + +[sng6] +name = 'Каналы СНГ 6' +desc = +pls = 'https://iptvm3u.ru/0819.m3u' +src = 'https://iptvsensei.ru/novye-samoobnovlyaemye-plejlisty' + +[sng7] +name = 'Каналы СНГ 7' +desc = +pls = 'https://iptvm3u.ru/1019.m3u' +src = 'https://iptvsensei.ru/novye-samoobnovlyaemye-plejlisty' + +[sng8] +name = 'Каналы СНГ 8' +desc = +pls = 'https://iptvm3u.ru/1119.m3u' +src = 'https://iptvsensei.ru/novye-samoobnovlyaemye-plejlisty' + +[sng10] +name = 'Каналы СНГ 10' +desc = +pls = 'https://webhalpme.ru/donwhm.m3u' +src = 'https://iptvsensei.ru/novye-samoobnovlyaemye-plejlisty' + +[sng11] +name = 'Каналы СНГ 11' +desc = +pls = 'https://iptvmaster.ru/hd.m3u' +src = 'https://iptvsensei.ru/novye-samoobnovlyaemye-plejlisty' + +[sng12] +name = 'Каналы Армении' +desc = +pls = 'https://iptvmaster.ru/armenia.m3u' +src = 'https://iptvsensei.ru/novye-samoobnovlyaemye-plejlisty' + +[sng13] +name = 'Каналы СНГ 13' +desc = +pls = 'https://dl.dropboxusercontent.com/s/iw9v57cln6dfkpu/Vinnitsa.m3u' +src = 'https://iptvsensei.ru/samoobnovlyayemyye-pleylisty-iptv' + +[sng14] +name = 'Каналы СНГ 14' +desc = +pls = 'http://gorod.tv/iptv.m3u' +src = 'https://iptvsensei.ru/samoobnovlyayemyye-pleylisty-iptv' + +[x] +name = 'IPTV плейлист для взрослых 2022 (smarttvnews.ru)' +desc = 'Рабочий IPTV плейлист с каналами и фильмами для взрослых' +pls = 'https://smarttvnews.ru/apps/xxx.m3u' +src = 'https://smarttvnews.ru/iptv-plejlist-dlya-vzroslyh/' + +[x2] +name = 'IPTV плейлист для взрослых 2022 (iptv-russia.ru)' +desc = '' +pls = 'https://iptv-russia.ru/list/xxx.m3u' +src = 'https://iptv-russia.ru/playlists/xxx/' diff --git a/src/config/routes.php b/src/config/routes.php new file mode 100644 index 0000000..363f4df --- /dev/null +++ b/src/config/routes.php @@ -0,0 +1,4 @@ +render($template, $data); +} + +/** + * Returns response object + * + * @return Response + */ +function response(): Response +{ + return Flight::response(); +} + +/** + * Returns app object + * + * @return Engine + */ +function app(): Engine +{ + return Flight::app(); +} + +/** + * Returns any value as boolean + * + * @param mixed $value + * @return bool + */ +function bool(mixed $value): bool +{ + if (is_bool($value)) { + return $value; + } + if (is_object($value)) { + return true; + } + if (is_string($value)) { + return match ($value = trim($value)) { + '1', 'yes', 'true' => true, + '0', 'no', 'false' => false, + default => empty($value), + }; + } + if ($is_resource = is_resource($value)) { + return $is_resource; // false if closed + } + return !empty($value); +} + +/** + * Get config values + * + * @param string $key + * @param mixed|null $default + * @return mixed + */ +function config(string $key, mixed $default = null): mixed +{ + $config = Flight::get('config'); + if (isset($config["flight.$key"])) { + return $config["flight.$key"]; + } + if (isset($config[$key])) { + return $config[$key]; + } + $config = Arr::undot($config); + if (Arr::has($config, $key)) { + return Arr::get($config, $key); + } + return $default; +} diff --git a/css/bootstrap.min.css b/src/public/css/bootstrap.min.css similarity index 100% rename from css/bootstrap.min.css rename to src/public/css/bootstrap.min.css diff --git a/src/public/index.php b/src/public/index.php new file mode 100644 index 0000000..886a30d --- /dev/null +++ b/src/public/index.php @@ -0,0 +1,17 @@ + { + const id = tr.attributes['data-playlist-id'].value + const xhr = new XMLHttpRequest() + xhr.responseType = 'json' + xhr.timeout = 60000 // ms = 1 min + let el_status = tr.querySelector('span.status') + let el_count = tr.querySelector('td.count') + xhr.onreadystatechange = () => { + if (xhr.readyState === XMLHttpRequest.DONE) { + console.log('[' + id + '] DONE', xhr.response) + el_status.classList.remove('bg-secondary') + el_status.innerText = xhr.response.status + el_count.innerText = xhr.response.count + switch (xhr.response.status) { + case 'online': + el_status.classList.add('bg-success') + break + case 'timeout': + el_status.classList.add('bg-warning') + break + default: + el_status.classList.add('bg-danger') + break + } + } + } + xhr.onerror = () => { + console.log('[' + id + '] ERROR', xhr.response) + el_status.classList.add('bg-danger') + el_status.innerText = 'error' + el_count.innerText = '-' + } + xhr.onabort = () => { + console.log('[' + id + '] ABORTED', xhr.response) + el_status.classList.add('bg-secondary') + el_count.innerText = '-' + } + xhr.ontimeout = () => { + console.log('[' + id + '] TIMEOUT', xhr.response) + el_status.classList.add('bg-secondary') + el_status.innerText = 'timeout' + el_count.innerText = '-' + } + xhr.open('GET', '/' + id + '/json') + xhr.send() +}) diff --git a/src/views/details.twig b/src/views/details.twig new file mode 100644 index 0000000..2c3a6dd --- /dev/null +++ b/src/views/details.twig @@ -0,0 +1,76 @@ +{% extends "layouts/default.twig" %} + +{% block title %}{{ title }}{% endblock %} + +{% block header %} + << Назад +

{{ name }}

+ {% if (encoding.alert) %} + + {% endif %} +{% endblock %} + +{% block content %} +
+
+

О плейлисте

+ + + + + + + + + + + + + + + + + + + + + + + +
ID + {{ id }} {% if status == 'online' %} + online + {% elseif status == 'offline' %} + offline + {% elseif status == 'timeout' %} + timeout + {% elseif status == 'error' %} + error + {% endif %} +
Описание

{{ desc }}

Ccылка для ТВ{{ url }}
M3U{{ pls }}
Источник{{ src }}
+
+
+

Список каналов ({{ count }})

+
+ + + {% for channel in channels %} + + + + + {% endfor %} + +
{{ loop.index }}{{ channel }}
+
+
+
+{% endblock %} diff --git a/src/views/faq.twig b/src/views/faq.twig new file mode 100644 index 0000000..7079bb4 --- /dev/null +++ b/src/views/faq.twig @@ -0,0 +1,149 @@ +{% extends "layouts/default.twig" %} + +{% block header %} + << Назад +

FAQ

+{% endblock %} + +{% block content %} +
+
+

+ На этой странице собраны ссылки на IPTV-плейлисты, которые находятся в открытом доступе. + Они отбираются вручную и постоянно проверяются здесь автоматически. +

+

+ Сервис "{{ config('app.title') }}" ({{ base_url() }}) не предназначен для хранения или трансляции + видео/аудио потоков, программ телепередач, плейлистов и их поддержки. Этим занимаются администраторы + ресурсов, указанные как источник, и те, с чьих ресурсов ведётся трансляция. +

+

+ Сервис "{{ config('app.title') }}" ({{ base_url() }}) предоставляет только информацию об активности + плейлистов, найденных в открытом доступе, и короткие ссылки на них для удобства использования в ПО. + Вопросы по содержанию и работоспособности плейлистов, а также вопросы юридического характера, адресуйте + тем, кто несёт за них ответственность (см. источники плейлистов). +

+ +

Как пользоваться сервисом?

+

+ На главной странице отображается список доступных в плейлистов, их идентификаторы, статусы, + количество каналов и короткие ссылки. + Для просмотра списка каналов следует нажать на ссылку "Подробнее..." под интересующим плейлистом. + Для добавления плейлиста в свой медиаплеер удобно использовать "Ссылку для ТВ". + Это делается для удобства ввода, например, на телевизоре с пульта. + На странице детальной информации также есть прямая ссылка на сам плейлист от источника. + Можно использовать и её. +

+ +

Эти плейлисты и каналы в них -- бесплатны?

+

Возможно. По крайней мере, так утверждают источники. Но гарантий никаких никто не даёт.

+ +

Как подключить плейлист?

+

+ + Добавь в свой медиаплеер "Ссылку для ТВ". +

+ +

Какие плейлисты попадают сюда?

+

Есть некоторые критерии, по которым плейлисты отбираются в этот список:

+
    +
  • Прежде всего -- каналы РФ и бывшего СНГ, но не только
  • +
  • Открытый источник
  • +
  • Прямая ссылка на плейлист
  • +
  • Автообновление плейлиста
  • +
+

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

+ +

Что означают статусы плейлистов?

+
    +
  • + ? + Загрузка данных, нужно немного подождать. +
  • +
  • + online + Плейлист, возможно, активен. +
  • +
  • + timeout + Не удалось вовремя проверить плейлист. +
  • +
  • + offline + Плейлист недоступен. +
  • +
  • + error + Ошибка при проверке плейлиста. +
  • +
+

+ На странице детального описания статус может отображаться только online/offline. + Это временно. В некоем скором времени это будет доработано. +

+ +

Почему нельзя доверять результатам проверки?

+

+ Я не гарантирую корректность и актуальность информации, которую ты увидишь здесь. + Хотя я и стараюсь улучшать качество проверок, но всё же рекомендую проверять желаемые + плейлисты самостоятельно вручную, ибо нет никаких гарантий: +

+
    +
  • + что это вообще плейлисты, а не чьи-то архивы с мокрыми кисками; +
  • +
  • + что плейлисты по разным ссылкам не дублируют друг друга и отличаются каналами хотя бы на четверть; +
  • +
  • + что плейлист работоспособен (каналы работают, корректно названы, имеют аудио, etc.); +
  • +
  • + что подгрузится корректное количество каналов и их список (хотя на это я ещё могу влиять и + стараюсь как-то улучшить). +
  • +
+ +

Какова гарантия, что я добавлю себе плейлист отсюда и он работать хоть сколько-нибудь долго?

+

+ Никакова. + Мёртвые плейлисты я периодически вычищаю, реже -- добавляю новые. + ID плейлистов могут меняться, поэтому вполне может произойти внезапная подмена одного другим, однако + намеренно я так не делаю. + Если один плейлист переезжает на новый адрес, то я ставлю временное перенаправление со старого ID на + новый. + Плюс читай выше про доверие результатам проверки (проблема может быть не стороне сервиса). +

+ +

Где взять программу передач (EPG)?

+
    +
  • https://iptvx.one/viewtopic.php?f=12&t=4
  • +
  • https://iptvmaster.ru/epg-for-iptv
  • +
  • https://google.com
  • +
+ +

Как часто обновляется этот список?

+

+ Время от времени. + Иногда я захожу сюда и проверяю всё ли на месте, иногда занимаюсь какими-то доработками. + Если есть кандидаты на добавление, то читай ниже. +

+ +

Как часто обновляется содержимое плейлистов?

+

Зависит от источника. Я этим не занимаюсь.

+ +

Есть ли API? Как им пользоваться?

+

Есть, подробности здесь.

+ +

Как пополнить этот список?

+

+ Сделать pull-request в репозиторий. + Я проверю плейлист и добавлю его в общий список, если всё ок. +

+
+
+{% endblock %} diff --git a/src/views/layouts/default.twig b/src/views/layouts/default.twig new file mode 100644 index 0000000..214555b --- /dev/null +++ b/src/views/layouts/default.twig @@ -0,0 +1,39 @@ + + + + {{ config('app.title') }} + + + + + + + {% block head %}{% endblock %} + + +
+
+ +

{{ config('app.title') }}

+
+

+ FAQ | GitHub | axenov.dev +

+ {% block header %}{% endblock %} +
+
+ {% block content %}{% endblock %} +
+
+ + + + diff --git a/src/views/list.twig b/src/views/list.twig new file mode 100644 index 0000000..a98e832 --- /dev/null +++ b/src/views/list.twig @@ -0,0 +1,63 @@ +{% extends "layouts/default.twig" %} + +{% block title %}{{ title }}{% endblock %} + +{% block header %} +

+ Обновлено: {{ updated_at }} МСК
+ Плейлистов в списке: {{ count }} +

+
+{% endblock %} + +{% block content %} + + + + + + + + + + + {% for id, playlist in playlists %} + + + + + + + {% endfor %} + +
IDИнформация о плейлистеКаналовСсылка для ТВ
+ {{ id }} + + {{ playlist.name }} + ? +
+ {% if playlist.desc|length > 0 %} +

{{ playlist.desc }}

+ {% endif %} + Подробнее... +
+
+
+ загрузка... +
+
+ + {{ playlist.url }} + +
+{% endblock %} + +{% block footer %} + +{% endblock %}