mirror of
https://github.com/anthonyaxenov/iptv.git
synced 2024-12-21 13:55:37 +00:00
Большое обновление
- проект переписан на flight + twig, laravel-like хелперы - docker-окружение - новая страница с подробностями о плейлисте - улучшен json о плейлисте - нормальный роутинг - нормальная статусная система - попытка перекодировки при не utf-8 + предупреждение об этом - дополнены FAQ + README
This commit is contained in:
parent
649ab85d79
commit
c43439b9cc
5
.gitignore
vendored
5
.gitignore
vendored
@ -1,7 +1,12 @@
|
||||
/.idea
|
||||
/.vscode
|
||||
/downloaded
|
||||
/src/cache/*
|
||||
/src/vendor
|
||||
*.log
|
||||
.env
|
||||
*.m3u
|
||||
*.m3u.*
|
||||
*.m3u8
|
||||
*.m3u8.*
|
||||
!/**/.gitkeep
|
||||
|
245
README.md
245
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)
|
||||
|
||||
Они бесплатны для использования. Список проверяется и обновляется мной вручную.
|
||||
---
|
||||
|
||||
Поддержкой этих плейлистов занимаются сервисы и ресурсы, указанные как источник.
|
||||
|
||||
Вопросы работоспособности плейлистов адресуйте тем, кто несёт за них ответственность.
|
||||
<a id="howtouse"></a>
|
||||
|
||||
## Как использовать этот список?
|
||||
|
||||
Чтобы подключить плейлист, нужно в настройках IPTV-плеера указать ссылку в следующем формате:
|
||||
Чтобы подключить плейлист, нужно в настройках медиаплеера указать ссылку в следующем формате:
|
||||
|
||||
```
|
||||
iptv.axenov.dev?ID
|
||||
iptv.axenov.dev/<ID>
|
||||
iptv.axenov.dev?<ID> (устаревший формат)
|
||||
iptv.axenov.dev/?<ID> (устаревший формат)
|
||||
```
|
||||
|
||||
где `ID` - один из идентификаторов, указанных в [`playlists.ini`](playlists.ini) в квадратных скобках.
|
||||
|
||||
## Как добавить плейлист в этот список?
|
||||
|
||||
1) Склонировать себе репозиторий, создать ветку
|
||||
2) Внести изменения в файл [`playlists.ini`](playlists.ini) как описано ниже
|
||||
3) Сделать коммит, отправить изменения в свой репозиторий и создать merge-request
|
||||
где `<ID>` - один из идентификаторов, указанных в [`playlists.ini`](playlists.ini) в квадратных скобках.
|
||||
|
||||
Либо провернуть всё то же самое через браузер.
|
||||
|
||||
<a id="iniformat"></a>
|
||||
|
||||
## Формат `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` приоритетен).
|
||||
|
||||
Плейлистов с редиректами может быть сколько угодно, но они не должны быть цикличными.
|
||||
|
||||
<a id="api"></a>
|
||||
|
||||
## API
|
||||
|
||||
Можно получать состояние плейлистов из этого сборника при помощи метода:
|
||||
|
||||
```
|
||||
GET https://iptv.axenov.dev/?getinfo=<ID>
|
||||
GET https://iptv.axenov.dev/<ID>/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
|
||||
|
||||
<a id="deploy"></a>
|
||||
|
||||
## Развёртывание проекта
|
||||
|
||||
1. Выполнить `cp .env.example .env`, установить необходимые параметры
|
||||
2. Выполнить `docker compose up -d --build`
|
||||
3. Открыть `https://<APP_URL>:8080`
|
||||
|
||||
<a id="tools"></a>
|
||||
|
||||
## Дополнительные инструменты (`./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
|
||||
...
|
||||
```
|
||||
|
||||
<a id="howtomake"></a>
|
||||
## Как создать свой собственный плейлист?
|
||||
|
||||
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-плеер и перепроверить результат.
|
||||
|
||||
<a id="stack"></a>
|
||||
|
||||
## Использованный стек
|
||||
|
||||
* [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
|
||||
|
||||
<a id="license"></a>
|
||||
|
||||
## Лицензия
|
||||
|
||||
[The MIT License](LICENSE)
|
||||
|
39
docker-compose.yml
Normal file
39
docker-compose.yml
Normal file
@ -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
|
30
docker/nginx/vhost.conf
Normal file
30
docker/nginx/vhost.conf
Normal file
@ -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;
|
||||
}
|
||||
}
|
16
docker/php/dockerfile
Normal file
16
docker/php/dockerfile
Normal file
@ -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
|
27
docker/php/php.ini
Normal file
27
docker/php/php.ini
Normal file
@ -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
|
21
docker/php/www.conf
Normal file
21
docker/php/www.conf
Normal file
@ -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
|
345
index.php
345
index.php
@ -1,345 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Anthony Axenov (c) 2022
|
||||
* The MIT License:
|
||||
* https://github.com/anthonyaxenov/iptv/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
//ini_set('display_errors', 1);
|
||||
//ini_set('display_startup_errors', 1);
|
||||
//error_reporting(E_ALL);
|
||||
|
||||
function response(array $content): void
|
||||
{
|
||||
header("Content-Type: application/json; charset=utf-8");
|
||||
die(json_encode($content));
|
||||
}
|
||||
|
||||
$updated_at = date('d.m.Y h:i', filemtime('playlists.ini'));
|
||||
$my_url = $_SERVER['SERVER_NAME'] . '?';
|
||||
$ini = parse_ini_file('playlists.ini', true);
|
||||
|
||||
// get playlist info (ajax)
|
||||
if (!empty($_GET['getinfo'])) {
|
||||
$pls = $ini[$_GET['getinfo']];
|
||||
if (!empty($pls['redirect'])) {
|
||||
$pls = $ini[$pls['redirect']];
|
||||
}
|
||||
unset($ini);
|
||||
if (empty($pls)) { // no playlist in ini
|
||||
response([
|
||||
'is_online' => 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;
|
||||
}
|
||||
}
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="ru">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>IPTV Playlists</title>
|
||||
<link href="css/bootstrap.min.css" rel="stylesheet">
|
||||
<style>.cursor-pointer {cursor: pointer} </style>
|
||||
<script src="js/bootstrap.bundle.min.js"></script>
|
||||
</head>
|
||||
<body class="bg-dark text-light">
|
||||
<div class="col-lg-8 mx-auto p-3 py-md-5">
|
||||
<header class="pb-3 mb-3">
|
||||
<a href="/" class="text-light text-decoration-none">
|
||||
<h1>Самообновляемые плейлисты IPTV</h1>
|
||||
</a>
|
||||
<p class="small text-muted">
|
||||
<a href="https://github.com/anthonyaxenov/iptv">GitHub</a> | <a href="https://axenov.dev">axenov.dev</a><br/>
|
||||
Обновлено: <?=$updated_at?>МСК<br/>
|
||||
Плейлистов в списке: <strong><?=count($ini)?></strong>
|
||||
</p>
|
||||
</header>
|
||||
|
||||
<main>
|
||||
<ul class="nav nav-tabs" id="myTab" role="tablist">
|
||||
<li class="nav-item" role="presentation">
|
||||
<button class="nav-link active" id="list-tab" data-bs-toggle="tab" data-bs-target="#list" type="button"
|
||||
role="tab" aria-controls="list" aria-selected="true">Список</button>
|
||||
</li>
|
||||
<li class="nav-item" role="presentation">
|
||||
<button class="nav-link" id="faq-tab" data-bs-toggle="tab" data-bs-target="#faq" type="button"
|
||||
role="tab" aria-controls="faq" aria-selected="false">FAQ</button>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="tab-content" id="myTabContent">
|
||||
<div class="tab-pane fade show active pt-5" id="list" role="tabpanel" aria-labelledby="list-tab">
|
||||
<table class="table table-dark table-hover small">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
<th>Информация о плейлисте</th>
|
||||
<th>Каналов</th>
|
||||
<th title="Нажми на ссылку, чтобы скопировать её в буфер обмена">Ссылка</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php
|
||||
foreach ($ini as $id => $element) {
|
||||
if (empty($element['pls'])) {
|
||||
continue;
|
||||
}
|
||||
?>
|
||||
<tr class="pls" data-playlist-id="<?=$id?>">
|
||||
<td class="text-center id">
|
||||
<strong><?=$id?></strong>
|
||||
</td>
|
||||
<td class="info">
|
||||
<strong><?=$element['name'] ?: "Плейлист #" . $id?></strong>
|
||||
<span class="badge small bg-warning text-dark status">?</span>
|
||||
<div class="small">
|
||||
<a href="<?=$element['pls']?>"
|
||||
target="_blank"
|
||||
rel="noopener nofollow">M3U</a>
|
||||
<?php
|
||||
if (!empty($element['src'])) { ?>
|
||||
| <a href="<?=$element['src']?>"
|
||||
target="_blank"
|
||||
rel="noopener nofollow">Источник</a>
|
||||
<?php
|
||||
} ?>
|
||||
<?php
|
||||
if (!empty($element['desc'])) { ?>
|
||||
<br/><p class="my-1"><?=$element['desc']?></p>
|
||||
<?php
|
||||
} ?>
|
||||
</div>
|
||||
</td>
|
||||
<td class="text-center count">
|
||||
<div class="spinner-border text-success" role="status">
|
||||
<span class="visually-hidden">загрузка...</span>
|
||||
</div>
|
||||
</td>
|
||||
<td class="col-3">
|
||||
<span onclick="prompt('Скопируй адрес плейлиста', '<?=$my_url?><?=$id?>')"
|
||||
data-bs-toggle="tooltip"
|
||||
data-bs-placement="top"
|
||||
title="Нажми на ссылку, чтобы скопировать её в буфер обмена"
|
||||
class="font-monospace cursor-pointer">
|
||||
<?=$my_url?><?=$id?>
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
<?php
|
||||
} ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="tab-pane fade p-3 pt-5" id="faq" role="tabpanel" aria-labelledby="profile-tab">
|
||||
<h2>Что здесь происходит?</h2>
|
||||
<p>
|
||||
На этой странице собраны ссылки на IPTV-плейлисты, которые находятся в открытом доступе.
|
||||
Они отбираются мной вручную и проверяются здесь автоматически.
|
||||
</p>
|
||||
<p>
|
||||
Ресурс <?=$_SERVER['SERVER_NAME']?> не занимается трансляцией видео- и аудиопотоков,
|
||||
администрированием конечных плейлистов и программ телепередач или хранением всего указанного.
|
||||
Подобными вопросами занимаются администраторы ресурсов, указанные как источник, и те, с чьих ресурсов
|
||||
ведётся трансляция.
|
||||
</p>
|
||||
<p class="mb-5">
|
||||
Ресурс <?=$_SERVER['SERVER_NAME']?> предоставляет только информацию об активности плейлистов, найденных
|
||||
в открытом доступе, и короткие ссылки на них для удобства ввода с пульта на телевизоре.
|
||||
Вопросы работоспособности плейлистов и каналов адресуйте тем, кто несёт за них ответственность.
|
||||
</p>
|
||||
|
||||
<h2>Что означают статусы плейлистов?</h2>
|
||||
<ul class="mb-5">
|
||||
<li>
|
||||
<span class="badge small bg-warning text-dark">?</span> Загрузка данных, нужно немного подождать.
|
||||
</li>
|
||||
<li>
|
||||
<span class="badge small text-dark bg-success">online</span> Плейлист, возможно, активен.
|
||||
</li>
|
||||
<li>
|
||||
<span class="badge small text-dark bg-secondary">unknown</span> Состояние неизвестно.
|
||||
Возможно, плейлист активен, но корректно его проверить не удалось.
|
||||
</li>
|
||||
<li>
|
||||
<span class="badge small text-dark bg-secondary">timeout</span> Не удалось вовремя проверить плейлист.
|
||||
</li>
|
||||
<li>
|
||||
<span class="badge small text-dark bg-danger">offline</span> Плейлист неактивен.
|
||||
</li>
|
||||
<li>
|
||||
<span class="badge small text-dark bg-danger">error</span> Ошибка при проверке плейлиста.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h2>Почему нельзя доверять результатам проверки?</h2>
|
||||
<p>
|
||||
Я не гарантирую корректность и актуальность информации, которую ты увидишь здесь.
|
||||
Хотя я и стараюсь улучшать качество проверок, но всё же рекомендую проверять желаемые
|
||||
плейлисты самостоятельно вручную, ибо нет никаких гарантий:
|
||||
</p>
|
||||
<ul class="mb-5">
|
||||
<li>
|
||||
что это вообще плейлисты, а не чьи-то архивы с мокрыми кисками;
|
||||
</li>
|
||||
<li>
|
||||
что плейлисты по разным ссылкам не дублируют друг друга и отличаются каналами хотя бы на четверть;
|
||||
</li>
|
||||
<li>
|
||||
что плейлист работоспособен (каналы работают, корректно названы, имеют аудио, etc.);
|
||||
</li>
|
||||
<li>
|
||||
что подгрузится корректное количество каналов и их список (хотя на это я ещё могу влиять и
|
||||
стараюсь как-то улучшить).
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h2>Эти плейлисты и каналы в них -- бесплатны?</h2>
|
||||
<p class="mb-5">Возможно. По крайней мере, так утверждают источники.</p>
|
||||
|
||||
<h2>Как подключить плейлист?</h2>
|
||||
<p class="mb-5">
|
||||
<a href="https://www.google.com/search?q=%D0%BA%D0%B0%D0%BA%20%D0%BF%D0%BE%D0%B4%D0%BA%D0%BB%D1%8E%D1%87%D0%B8%D1%82%D1%8C%20iptv%20%D0%BF%D0%BB%D0%B5%D0%B9%D0%BB%D0%B8%D1%81%D1%82%20%D0%BF%D0%BE%20%D1%81%D1%81%D1%8B%D0%BB%D0%BA%D0%B5">
|
||||
Добавь в свой IPTV-плеер</a> ссылку из последней колонки.
|
||||
</p>
|
||||
|
||||
<h2>Какова гарантия, что я добавлю себе плейлист отсюда и он работать хоть сколько-нибудь долго?</h2>
|
||||
<p class="mb-5">
|
||||
Никакова.
|
||||
Мёртвые плейлисты я периодически вычищаю, реже -- добавляю новые.
|
||||
ID плейлистов могут меняться, поэтому вполне может произойти внезапная подмена одного другим, однако намеренно я так не делаю.
|
||||
Если один плейлист переезжает на новый адрес, то я ставлю временное перенаправление со старого ID на новый.
|
||||
Плюс читай выше про доверие результатам проверки (проблема может быть не на этой стороне).
|
||||
</p>
|
||||
|
||||
<h2>Где взять программу передач (EPG)?</h2>
|
||||
<ul class="mb-5">
|
||||
<li><b>https://iptvx.one/viewtopic.php?f=12&t=4</b></li>
|
||||
<li>https://iptvmaster.ru/epg-for-iptv</li>
|
||||
<li>https://google.com</li>
|
||||
</ul>
|
||||
|
||||
<h2>Как часто обновляется этот список?</h2>
|
||||
<p>
|
||||
Время от времени.
|
||||
Иногда я захожу сюда и проверяю всё ли на месте, иногда занимаюсь какими-то доработками.
|
||||
</p>
|
||||
<p class="mb-5">
|
||||
Если есть кандидаты на добавление, то читай ниже.
|
||||
</p>
|
||||
|
||||
<h2>Как часто обновляются сами плейлисты (каналы)?</h2>
|
||||
<p class="mb-5">
|
||||
Зависит от источника. Я этим не занимаюсь.
|
||||
</p>
|
||||
|
||||
<h2>Как пополнить этот список?</h2>
|
||||
<p class="mb-5">
|
||||
Сделать pull-request в <a href="https://github.com/anthonyaxenov/iptv">репозиторий</a>.
|
||||
Я проверю плейлист и добавлю его в общий список, если всё ок.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<footer class="py-4 text-center">
|
||||
<a href="https://github.com/anthonyaxenov/iptv">GitHub</a> | <a href="https://axenov.dev">axenov.dev</a>
|
||||
</footer>
|
||||
</div>
|
||||
<script>
|
||||
document.querySelectorAll('tr.pls').forEach((tr) => {
|
||||
const id = tr.attributes['data-playlist-id'].value
|
||||
const xhr = new XMLHttpRequest()
|
||||
xhr.responseType = 'json'
|
||||
xhr.timeout = 60000 // ms = 1 min
|
||||
let st_el = tr.querySelector('span.status')
|
||||
xhr.onreadystatechange = () => {
|
||||
if (xhr.readyState === XMLHttpRequest.DONE) {
|
||||
console.log('[' + id + '] DONE', xhr.response)
|
||||
st_el.classList.remove('bg-warning')
|
||||
if (xhr.response) {
|
||||
tr.querySelector('td.count').innerHTML = xhr.response.count
|
||||
if (xhr.response.is_online === true) {
|
||||
st_el.innerHTML = 'online'
|
||||
st_el.classList.add('bg-success')
|
||||
if (xhr.response.channels.length > 0) {
|
||||
tr.querySelector('td.info').innerHTML += '<a class="small cursor-pointer" ' +
|
||||
'data-bs-toggle="collapse" data-bs-target="#channels-' + id + '" aria-expanded="false" ' +
|
||||
'aria-controls="channels-' + id + '">Список каналов</a><div class="collapse" id="channels-' + id +
|
||||
'"><p class="card card-body bg-dark small" style="max-height:250px;overflow-y:auto;">' +
|
||||
xhr.response.channels.join('<br />') + '</p></div>'
|
||||
}
|
||||
} else {
|
||||
st_el.innerHTML = 'offline'
|
||||
st_el.classList.add('bg-danger')
|
||||
}
|
||||
} else {
|
||||
tr.querySelector('td.count').innerHTML = '-'
|
||||
st_el.classList.add('bg-secondary')
|
||||
st_el.innerHTML = 'unknown'
|
||||
}
|
||||
}
|
||||
}
|
||||
xhr.onerror = () => {
|
||||
console.log('[' + id + '] ERROR', xhr.response)
|
||||
st_el.classList.add('bg-danger')
|
||||
st_el.innerHTML = 'error'
|
||||
tr.querySelector('td.count').innerHTML = '-'
|
||||
}
|
||||
xhr.onabort = () => {
|
||||
console.log('[' + id + '] ABORTED', xhr.response)
|
||||
st_el.classList.add('bg-secondary')
|
||||
tr.querySelector('td.count').innerHTML = '-'
|
||||
}
|
||||
xhr.ontimeout = () => {
|
||||
console.log('[' + id + '] TIMEOUT', xhr.response)
|
||||
st_el.classList.add('bg-secondary')
|
||||
st_el.innerHTML = 'timeout'
|
||||
tr.querySelector('td.count').innerHTML = '-'
|
||||
}
|
||||
xhr.open('GET', '/?getinfo=' + id)
|
||||
xhr.send()
|
||||
})
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
0
log/nginx/.gitkeep
Normal file
0
log/nginx/.gitkeep
Normal file
0
log/php/.gitkeep
Normal file
0
log/php/.gitkeep
Normal file
452
playlists.ini
452
playlists.ini
@ -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/'
|
1
playlists.ini
Symbolic link
1
playlists.ini
Symbolic link
@ -0,0 +1 @@
|
||||
src/config/playlists.ini
|
7
src/.env.example
Normal file
7
src/.env.example
Normal file
@ -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
|
9
src/app/Controllers/Controller.php
Normal file
9
src/app/Controllers/Controller.php
Normal file
@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types = 1);
|
||||
|
||||
namespace App\Controllers;
|
||||
|
||||
abstract class Controller
|
||||
{
|
||||
}
|
76
src/app/Controllers/HomeController.php
Normal file
76
src/app/Controllers/HomeController.php
Normal file
@ -0,0 +1,76 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types = 1);
|
||||
|
||||
namespace App\Controllers;
|
||||
|
||||
use App\Core\PlaylistProcessor;
|
||||
use App\Core\RedirectedPlaylist;
|
||||
use Exception;
|
||||
use Flight;
|
||||
|
||||
class HomeController extends Controller
|
||||
{
|
||||
protected PlaylistProcessor $ini;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->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),
|
||||
]);
|
||||
}
|
||||
}
|
65
src/app/Controllers/PlaylistController.php
Normal file
65
src/app/Controllers/PlaylistController.php
Normal file
@ -0,0 +1,65 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types = 1);
|
||||
|
||||
namespace App\Controllers;
|
||||
|
||||
use App\Core\PlaylistProcessor;
|
||||
use App\Core\RedirectedPlaylist;
|
||||
use Exception;
|
||||
use Flight;
|
||||
|
||||
class PlaylistController extends Controller
|
||||
{
|
||||
protected PlaylistProcessor $ini;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->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),
|
||||
]);
|
||||
}
|
||||
}
|
17
src/app/Core/BasicPlaylist.php
Normal file
17
src/app/Core/BasicPlaylist.php
Normal file
@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types = 1);
|
||||
|
||||
namespace App\Core;
|
||||
|
||||
use Illuminate\Contracts\Support\Arrayable;
|
||||
|
||||
abstract class BasicPlaylist implements Arrayable
|
||||
{
|
||||
public string $id;
|
||||
|
||||
public function url(): string
|
||||
{
|
||||
return sprintf('%s/%s', base_url(), $this->id);
|
||||
}
|
||||
}
|
77
src/app/Core/Bootstrapper.php
Normal file
77
src/app/Core/Bootstrapper.php
Normal file
@ -0,0 +1,77 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types = 1);
|
||||
|
||||
namespace App\Core;
|
||||
|
||||
use App\Controllers\AjaxController;
|
||||
use App\Controllers\HomeController;
|
||||
use App\Controllers\PlaylistController;
|
||||
use App\Extensions\TwigFunctions;
|
||||
use Flight;
|
||||
use Illuminate\Support\Arr;
|
||||
use Symfony\Component\Dotenv\Dotenv;
|
||||
use Twig\Environment;
|
||||
use Twig\Extension\DebugExtension;
|
||||
use Twig\Loader\FilesystemLoader;
|
||||
|
||||
final class Bootstrapper
|
||||
{
|
||||
public static function bootEnv(): void
|
||||
{
|
||||
(new Dotenv())->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)
|
||||
);
|
||||
}
|
||||
}
|
45
src/app/Core/Playlist.php
Normal file
45
src/app/Core/Playlist.php
Normal file
@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types = 1);
|
||||
|
||||
namespace App\Core;
|
||||
|
||||
class Playlist extends BasicPlaylist
|
||||
{
|
||||
public ?string $name;
|
||||
|
||||
public ?string $desc;
|
||||
|
||||
public string $pls;
|
||||
|
||||
public ?string $src;
|
||||
|
||||
public string $url;
|
||||
|
||||
/**
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function __construct(public string $id, array $params)
|
||||
{
|
||||
empty($params['pls']) && throw new \Exception(
|
||||
"Плейлист с ID=$id обязан иметь параметр pls или redirect"
|
||||
);
|
||||
$this->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,
|
||||
];
|
||||
}
|
||||
}
|
119
src/app/Core/PlaylistProcessor.php
Normal file
119
src/app/Core/PlaylistProcessor.php
Normal file
@ -0,0 +1,119 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types = 1);
|
||||
|
||||
namespace App\Core;
|
||||
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
final class PlaylistProcessor
|
||||
{
|
||||
public Collection $playlists;
|
||||
|
||||
protected string $updated_at;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$filepath = config_path('playlists.ini');
|
||||
$this->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;
|
||||
}
|
||||
}
|
25
src/app/Core/RedirectedPlaylist.php
Normal file
25
src/app/Core/RedirectedPlaylist.php
Normal file
@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types = 1);
|
||||
|
||||
namespace App\Core;
|
||||
|
||||
class RedirectedPlaylist extends BasicPlaylist
|
||||
{
|
||||
/**
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function __construct(
|
||||
public string $id,
|
||||
public string $redirect_id,
|
||||
) {
|
||||
}
|
||||
|
||||
public function toArray(): array
|
||||
{
|
||||
return [
|
||||
'id' => $this->id,
|
||||
'redirect_id' => $this->redirect_id,
|
||||
];
|
||||
}
|
||||
}
|
29
src/app/Extensions/TwigFunctions.php
Normal file
29
src/app/Extensions/TwigFunctions.php
Normal file
@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types = 1);
|
||||
|
||||
namespace App\Extensions;
|
||||
|
||||
use Twig\Extension\AbstractExtension;
|
||||
use Twig\TwigFunction;
|
||||
|
||||
class TwigFunctions extends AbstractExtension
|
||||
{
|
||||
public function getFunctions(): array
|
||||
{
|
||||
return [
|
||||
new TwigFunction('config', [$this, 'config']),
|
||||
new TwigFunction('base_url', [$this, 'base_url']),
|
||||
];
|
||||
}
|
||||
|
||||
public function config(string $key, mixed $default = null): mixed
|
||||
{
|
||||
return config($key, $default);
|
||||
}
|
||||
|
||||
public function base_url(string $path = ''): string
|
||||
{
|
||||
return base_url($path);
|
||||
}
|
||||
}
|
20
src/bootstrap.php
Normal file
20
src/bootstrap.php
Normal file
@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types = 1);
|
||||
|
||||
use App\Core\Bootstrapper;
|
||||
|
||||
// autoload composer packages
|
||||
require 'vendor/autoload.php';
|
||||
|
||||
// load .env parameters
|
||||
Bootstrapper::bootEnv();
|
||||
|
||||
// set up framework according to its config
|
||||
Bootstrapper::bootSettings();
|
||||
|
||||
// set up Twig template engine
|
||||
Bootstrapper::bootTwig();
|
||||
|
||||
// set up routes defined in config file
|
||||
Bootstrapper::bootRoutes();
|
0
src/cache/.gitkeep
vendored
Normal file
0
src/cache/.gitkeep
vendored
Normal file
20
src/composer.json
Normal file
20
src/composer.json
Normal file
@ -0,0 +1,20 @@
|
||||
{
|
||||
"require": {
|
||||
"illuminate/collections": "^9.26",
|
||||
"mikecao/flight": "^2.0",
|
||||
"symfony/dotenv": "^6.1",
|
||||
"twig/twig": "^3.4"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"App\\": "app/"
|
||||
},
|
||||
"files": [
|
||||
"helpers.php"
|
||||
]
|
||||
},
|
||||
"config": {
|
||||
"optimize-autoloader": true,
|
||||
"sort-packages": true
|
||||
}
|
||||
}
|
680
src/composer.lock
generated
Normal file
680
src/composer.lock
generated
Normal file
@ -0,0 +1,680 @@
|
||||
{
|
||||
"_readme": [
|
||||
"This file locks the dependencies of your project to a known state",
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "d8a1bc42a20f2a843ee133cd33f44fd4",
|
||||
"packages": [
|
||||
{
|
||||
"name": "illuminate/collections",
|
||||
"version": "v9.26.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/illuminate/collections.git",
|
||||
"reference": "3bda212d2c245b3261cd9af690dfd47d9878cebf"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/illuminate/collections/zipball/3bda212d2c245b3261cd9af690dfd47d9878cebf",
|
||||
"reference": "3bda212d2c245b3261cd9af690dfd47d9878cebf",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"illuminate/conditionable": "^9.0",
|
||||
"illuminate/contracts": "^9.0",
|
||||
"illuminate/macroable": "^9.0",
|
||||
"php": "^8.0.2"
|
||||
},
|
||||
"suggest": {
|
||||
"symfony/var-dumper": "Required to use the dump method (^6.0)."
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "9.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"files": [
|
||||
"helpers.php"
|
||||
],
|
||||
"psr-4": {
|
||||
"Illuminate\\Support\\": ""
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Taylor Otwell",
|
||||
"email": "taylor@laravel.com"
|
||||
}
|
||||
],
|
||||
"description": "The Illuminate Collections package.",
|
||||
"homepage": "https://laravel.com",
|
||||
"support": {
|
||||
"issues": "https://github.com/laravel/framework/issues",
|
||||
"source": "https://github.com/laravel/framework"
|
||||
},
|
||||
"time": "2022-08-22T14:29:59+00:00"
|
||||
},
|
||||
{
|
||||
"name": "illuminate/conditionable",
|
||||
"version": "v9.26.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/illuminate/conditionable.git",
|
||||
"reference": "5b40f51ccb07e0e7b1ec5559d8db9e0e2dc51883"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/illuminate/conditionable/zipball/5b40f51ccb07e0e7b1ec5559d8db9e0e2dc51883",
|
||||
"reference": "5b40f51ccb07e0e7b1ec5559d8db9e0e2dc51883",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^8.0.2"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "9.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Illuminate\\Support\\": ""
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Taylor Otwell",
|
||||
"email": "taylor@laravel.com"
|
||||
}
|
||||
],
|
||||
"description": "The Illuminate Conditionable package.",
|
||||
"homepage": "https://laravel.com",
|
||||
"support": {
|
||||
"issues": "https://github.com/laravel/framework/issues",
|
||||
"source": "https://github.com/laravel/framework"
|
||||
},
|
||||
"time": "2022-07-29T19:44:19+00:00"
|
||||
},
|
||||
{
|
||||
"name": "illuminate/contracts",
|
||||
"version": "v9.26.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/illuminate/contracts.git",
|
||||
"reference": "0d1dd1a7e947072319f2e641cc50081219606502"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/illuminate/contracts/zipball/0d1dd1a7e947072319f2e641cc50081219606502",
|
||||
"reference": "0d1dd1a7e947072319f2e641cc50081219606502",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^8.0.2",
|
||||
"psr/container": "^1.1.1|^2.0.1",
|
||||
"psr/simple-cache": "^1.0|^2.0|^3.0"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "9.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Illuminate\\Contracts\\": ""
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Taylor Otwell",
|
||||
"email": "taylor@laravel.com"
|
||||
}
|
||||
],
|
||||
"description": "The Illuminate Contracts package.",
|
||||
"homepage": "https://laravel.com",
|
||||
"support": {
|
||||
"issues": "https://github.com/laravel/framework/issues",
|
||||
"source": "https://github.com/laravel/framework"
|
||||
},
|
||||
"time": "2022-08-18T14:18:13+00:00"
|
||||
},
|
||||
{
|
||||
"name": "illuminate/macroable",
|
||||
"version": "v9.26.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/illuminate/macroable.git",
|
||||
"reference": "e3bfaf6401742a9c6abca61b9b10e998e5b6449a"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/illuminate/macroable/zipball/e3bfaf6401742a9c6abca61b9b10e998e5b6449a",
|
||||
"reference": "e3bfaf6401742a9c6abca61b9b10e998e5b6449a",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^8.0.2"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "9.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Illuminate\\Support\\": ""
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Taylor Otwell",
|
||||
"email": "taylor@laravel.com"
|
||||
}
|
||||
],
|
||||
"description": "The Illuminate Macroable package.",
|
||||
"homepage": "https://laravel.com",
|
||||
"support": {
|
||||
"issues": "https://github.com/laravel/framework/issues",
|
||||
"source": "https://github.com/laravel/framework"
|
||||
},
|
||||
"time": "2022-08-09T13:29:29+00:00"
|
||||
},
|
||||
{
|
||||
"name": "mikecao/flight",
|
||||
"version": "v2.0.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/mikecao/flight.git",
|
||||
"reference": "a130231646e6c7a9e2504a9025f851e9a3bf1975"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/mikecao/flight/zipball/a130231646e6c7a9e2504a9025f851e9a3bf1975",
|
||||
"reference": "a130231646e6c7a9e2504a9025f851e9a3bf1975",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-json": "*",
|
||||
"php": "^7.4|^8.0|^8.1"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^9.5"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"files": [
|
||||
"flight/autoload.php",
|
||||
"flight/Flight.php"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Mike Cao",
|
||||
"email": "mike@mikecao.com",
|
||||
"homepage": "http://www.mikecao.com/",
|
||||
"role": "Original Developer"
|
||||
}
|
||||
],
|
||||
"description": "Flight is a fast, simple, extensible framework for PHP. Flight enables you to quickly and easily build RESTful web applications.",
|
||||
"homepage": "http://flightphp.com",
|
||||
"support": {
|
||||
"issues": "https://github.com/mikecao/flight/issues",
|
||||
"source": "https://github.com/mikecao/flight/tree/v2.0.1"
|
||||
},
|
||||
"time": "2021-12-19T03:03:01+00:00"
|
||||
},
|
||||
{
|
||||
"name": "psr/container",
|
||||
"version": "2.0.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/php-fig/container.git",
|
||||
"reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/php-fig/container/zipball/c71ecc56dfe541dbd90c5360474fbc405f8d5963",
|
||||
"reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=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"
|
||||
}
|
30
src/config/app.php
Normal file
30
src/config/app.php
Normal file
@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types = 1);
|
||||
|
||||
return [
|
||||
'flight' => [
|
||||
// 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',
|
||||
],
|
||||
],
|
||||
];
|
452
src/config/playlists.ini
Normal file
452
src/config/playlists.ini
Normal file
@ -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/'
|
4
src/config/routes.php
Normal file
4
src/config/routes.php
Normal file
@ -0,0 +1,4 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types = 1);
|
||||
|
180
src/helpers.php
Normal file
180
src/helpers.php
Normal file
@ -0,0 +1,180 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types = 1);
|
||||
|
||||
use flight\Engine;
|
||||
use flight\net\Response;
|
||||
use Illuminate\Support\Arr;
|
||||
|
||||
/**
|
||||
* Returns path to root application directory
|
||||
*
|
||||
* @param string $path
|
||||
* @return string
|
||||
*/
|
||||
function root_path(string $path = ''): string
|
||||
{
|
||||
return rtrim(sprintf('%s/%s', dirname($_SERVER['DOCUMENT_ROOT']), $path), '/');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns path to app
|
||||
*
|
||||
* @param string $path
|
||||
* @return string
|
||||
*/
|
||||
function app_path(string $path = ''): string
|
||||
{
|
||||
return root_path("app/$path");
|
||||
}
|
||||
|
||||
/**
|
||||
* Return path to application configuration directory
|
||||
*
|
||||
* @param string $path
|
||||
* @return string
|
||||
*/
|
||||
function config_path(string $path = ''): string
|
||||
{
|
||||
return root_path("config/$path");
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns path to app cache
|
||||
*
|
||||
* @param string $path
|
||||
* @return string
|
||||
*/
|
||||
function cache_path(string $path = ''): string
|
||||
{
|
||||
return root_path("cache/$path");
|
||||
}
|
||||
|
||||
/**
|
||||
* Return path to public part of application
|
||||
*
|
||||
* @param string $path
|
||||
* @return string
|
||||
*/
|
||||
function public_path(string $path = ''): string
|
||||
{
|
||||
return root_path("public/$path");
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns path to app views
|
||||
*
|
||||
* @param string $path
|
||||
* @return string
|
||||
*/
|
||||
function views_path(string $path = ''): string
|
||||
{
|
||||
return root_path("views/$path");
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns base URL
|
||||
*
|
||||
* @param string $route
|
||||
* @return string
|
||||
*/
|
||||
function base_url(string $route = ''): string
|
||||
{
|
||||
return rtrim(sprintf('%s/%s', config('flight.base_url'), $route), '/');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns value of environment var
|
||||
*
|
||||
* @param string $key
|
||||
* @param mixed|null $default
|
||||
* @return mixed
|
||||
*/
|
||||
function env(string $key, mixed $default = null): mixed
|
||||
{
|
||||
return $_ENV[$key] ?? $default;
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders template
|
||||
*
|
||||
* @param mixed $template
|
||||
* @param array $data
|
||||
* @return void
|
||||
* @throws Exception
|
||||
*/
|
||||
function view(mixed $template, array $data = []): void
|
||||
{
|
||||
$template = str_contains($template, '.twig') ? $template : "$template.twig";
|
||||
echo Flight::view()->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;
|
||||
}
|
17
src/public/index.php
Normal file
17
src/public/index.php
Normal file
@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types = 1);
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Bootstrap all classes, settings, etc.
|
||||
|--------------------------------------------------------------------------
|
||||
*/
|
||||
require '../bootstrap.php';
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Start application
|
||||
|--------------------------------------------------------------------------
|
||||
*/
|
||||
Flight::start();
|
46
src/public/js/checker.js
Normal file
46
src/public/js/checker.js
Normal file
@ -0,0 +1,46 @@
|
||||
document.querySelectorAll('tr.pls').forEach((tr) => {
|
||||
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()
|
||||
})
|
76
src/views/details.twig
Normal file
76
src/views/details.twig
Normal file
@ -0,0 +1,76 @@
|
||||
{% extends "layouts/default.twig" %}
|
||||
|
||||
{% block title %}{{ title }}{% endblock %}
|
||||
|
||||
{% block header %}
|
||||
<a href="{{ base_url() }}" class="btn btn-outline-light mb-3"><< Назад</a>
|
||||
<h2>{{ name }}</h2>
|
||||
{% if (encoding.alert) %}
|
||||
<div class="alert alert-warning small" role="alert">
|
||||
Кодировка исходного плейлиста отличается от UTF-8.
|
||||
Он был автоматически с конвертирован из {{ encoding.name }}, чтобы отобразить здесь список каналов.
|
||||
Однако названия каналов могут отображаться некорректно, причём не только здесь, но и в плеере.
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row">
|
||||
<div class="col-md-8">
|
||||
<h4>О плейлисте</h4>
|
||||
<table class="table table-dark table-hover small">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="w-25">ID</td>
|
||||
<td>
|
||||
{{ id }} {% if status == 'online' %}
|
||||
<span class="badge small text-dark bg-success">online</span>
|
||||
{% elseif status == 'offline' %}
|
||||
<span class="badge small text-dark bg-danger">offline</span>
|
||||
{% elseif status == 'timeout' %}
|
||||
<span class="badge small text-dark bg-warning">timeout</span>
|
||||
{% elseif status == 'error' %}
|
||||
<span class="badge small text-dark bg-danger">error</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Описание</td>
|
||||
<td><p>{{ desc }}</p></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td title="Нажми на ссылку, чтобы скопировать её в буфер обмена"><b>Ccылка для ТВ</b></td>
|
||||
<td><b onclick="prompt('Скопируй адрес плейлиста', '{{ url }}')"
|
||||
data-bs-toggle="tooltip"
|
||||
data-bs-placement="top"
|
||||
title="Нажми на ссылку, чтобы скопировать её в буфер обмена"
|
||||
class="font-monospace cursor-pointer">{{ url }}</b></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>M3U</td>
|
||||
<td>{{ pls }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Источник</td>
|
||||
<td>{{ src }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<h4>Список каналов ({{ count }})</h4>
|
||||
<div class="overflow-auto" style="max-height: 350px;">
|
||||
<table class="table table-dark table-hover small">
|
||||
<tbody>
|
||||
{% for channel in channels %}
|
||||
<tr>
|
||||
<td>{{ loop.index }}</td>
|
||||
<td>{{ channel }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
149
src/views/faq.twig
Normal file
149
src/views/faq.twig
Normal file
@ -0,0 +1,149 @@
|
||||
{% extends "layouts/default.twig" %}
|
||||
|
||||
{% block header %}
|
||||
<a href="{{ base_url() }}" class="btn btn-outline-light mb-3"><< Назад</a>
|
||||
<h2>FAQ</h2>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<p>
|
||||
На этой странице собраны ссылки на IPTV-плейлисты, которые находятся в открытом доступе.
|
||||
Они отбираются вручную и постоянно проверяются здесь автоматически.
|
||||
</p>
|
||||
<p>
|
||||
Сервис "{{ config('app.title') }}" ({{ base_url() }}) не предназначен для хранения или трансляции
|
||||
видео/аудио потоков, программ телепередач, плейлистов и их поддержки. Этим занимаются администраторы
|
||||
ресурсов, указанные как источник, и те, с чьих ресурсов ведётся трансляция.
|
||||
</p>
|
||||
<p class="mb-5">
|
||||
Сервис "{{ config('app.title') }}" ({{ base_url() }}) предоставляет только информацию об активности
|
||||
плейлистов, найденных в открытом доступе, и короткие ссылки на них для удобства использования в ПО.
|
||||
Вопросы по содержанию и работоспособности плейлистов, а также вопросы юридического характера, адресуйте
|
||||
тем, кто несёт за них ответственность (см. источники плейлистов).
|
||||
</p>
|
||||
|
||||
<h3>Как пользоваться сервисом?</h3>
|
||||
<p class="mb-5">
|
||||
На главной странице отображается список доступных в плейлистов, их идентификаторы, статусы,
|
||||
количество каналов и короткие ссылки.
|
||||
Для просмотра списка каналов следует нажать на ссылку <b>"Подробнее..."</b> под интересующим плейлистом.
|
||||
Для добавления плейлиста в свой медиаплеер удобно использовать <b>"Ссылку для ТВ"</b>.
|
||||
Это делается для удобства ввода, например, на телевизоре с пульта.
|
||||
На странице детальной информации также есть прямая ссылка на сам плейлист от источника.
|
||||
Можно использовать и её.
|
||||
</p>
|
||||
|
||||
<h3>Эти плейлисты и каналы в них -- бесплатны?</h3>
|
||||
<p class="mb-5">Возможно. По крайней мере, так утверждают источники. Но гарантий никаких никто не даёт.</p>
|
||||
|
||||
<h3>Как подключить плейлист?</h3>
|
||||
<p class="mb-5">
|
||||
<a href="https://www.google.com/search?q=%D0%BA%D0%B0%D0%BA%20%D0%BF%D0%BE%D0%B4%D0%BA%D0%BB%D1%8E%D1%87%D0%B8%D1%82%D1%8C%20iptv%20%D0%BF%D0%BB%D0%B5%D0%B9%D0%BB%D0%B8%D1%81%D1%82%20%D0%BF%D0%BE%20%D1%81%D1%81%D1%8B%D0%BB%D0%BA%D0%B5">
|
||||
Добавь в свой медиаплеер</a> "Ссылку для ТВ".
|
||||
</p>
|
||||
|
||||
<h3>Какие плейлисты попадают сюда?</h3>
|
||||
<p>Есть некоторые критерии, по которым плейлисты отбираются в этот список:</p>
|
||||
<ul>
|
||||
<li>Прежде всего -- каналы РФ и бывшего СНГ, но не только</li>
|
||||
<li>Открытый источник</li>
|
||||
<li>Прямая ссылка на плейлист</li>
|
||||
<li>Автообновление плейлиста</li>
|
||||
</ul>
|
||||
<p>
|
||||
В основном, в плейлистах именно трансляции телеканалов, но могут быть просто список каких-то
|
||||
(мульт)фильмов и передач, находящихся на чужих дисках (как если бы вы сами составили плейлист с музыкой,
|
||||
например).
|
||||
</p>
|
||||
|
||||
<h3>Что означают статусы плейлистов?</h3>
|
||||
<ul>
|
||||
<li>
|
||||
<span class="badge small text-dark bg-secondary">?</span>
|
||||
Загрузка данных, нужно немного подождать.
|
||||
</li>
|
||||
<li>
|
||||
<span class="badge small text-dark bg-success">online</span>
|
||||
Плейлист, возможно, активен.
|
||||
</li>
|
||||
<li>
|
||||
<span class="badge small text-dark bg-warning">timeout</span>
|
||||
Не удалось вовремя проверить плейлист.
|
||||
</li>
|
||||
<li>
|
||||
<span class="badge small text-dark bg-danger">offline</span>
|
||||
Плейлист недоступен.
|
||||
</li>
|
||||
<li>
|
||||
<span class="badge small text-dark bg-danger">error</span>
|
||||
Ошибка при проверке плейлиста.
|
||||
</li>
|
||||
</ul>
|
||||
<p class="mb-5">
|
||||
На странице детального описания статус может отображаться только online/offline.
|
||||
Это временно. В некоем скором времени это будет доработано.
|
||||
</p>
|
||||
|
||||
<h3>Почему нельзя доверять результатам проверки?</h3>
|
||||
<p>
|
||||
Я не гарантирую корректность и актуальность информации, которую ты увидишь здесь.
|
||||
Хотя я и стараюсь улучшать качество проверок, но всё же рекомендую проверять желаемые
|
||||
плейлисты самостоятельно вручную, ибо нет никаких гарантий:
|
||||
</p>
|
||||
<ul class="mb-5">
|
||||
<li>
|
||||
что это вообще плейлисты, а не чьи-то архивы с мокрыми кисками;
|
||||
</li>
|
||||
<li>
|
||||
что плейлисты по разным ссылкам не дублируют друг друга и отличаются каналами хотя бы на четверть;
|
||||
</li>
|
||||
<li>
|
||||
что плейлист работоспособен (каналы работают, корректно названы, имеют аудио, etc.);
|
||||
</li>
|
||||
<li>
|
||||
что подгрузится корректное количество каналов и их список (хотя на это я ещё могу влиять и
|
||||
стараюсь как-то улучшить).
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h3>Какова гарантия, что я добавлю себе плейлист отсюда и он работать хоть сколько-нибудь долго?</h3>
|
||||
<p class="mb-5">
|
||||
Никакова.
|
||||
Мёртвые плейлисты я периодически вычищаю, реже -- добавляю новые.
|
||||
ID плейлистов могут меняться, поэтому вполне может произойти внезапная подмена одного другим, однако
|
||||
намеренно я так не делаю.
|
||||
Если один плейлист переезжает на новый адрес, то я ставлю временное перенаправление со старого ID на
|
||||
новый.
|
||||
Плюс читай выше про доверие результатам проверки (проблема может быть не стороне сервиса).
|
||||
</p>
|
||||
|
||||
<h3>Где взять программу передач (EPG)?</h3>
|
||||
<ul class="mb-5">
|
||||
<li><b>https://iptvx.one/viewtopic.php?f=12&t=4</b></li>
|
||||
<li>https://iptvmaster.ru/epg-for-iptv</li>
|
||||
<li>https://google.com</li>
|
||||
</ul>
|
||||
|
||||
<h3>Как часто обновляется этот список?</h3>
|
||||
<p class="mb-5">
|
||||
Время от времени.
|
||||
Иногда я захожу сюда и проверяю всё ли на месте, иногда занимаюсь какими-то доработками.
|
||||
Если есть кандидаты на добавление, то читай ниже.
|
||||
</p>
|
||||
|
||||
<h3>Как часто обновляется содержимое плейлистов?</h3>
|
||||
<p class="mb-5">Зависит от источника. Я этим не занимаюсь.</p>
|
||||
|
||||
<h3>Есть ли API? Как им пользоваться?</h3>
|
||||
<p class="mb-5">Есть, подробности <a href="https://github.com/anthonyaxenov/iptv2#api">здесь</a>.</p>
|
||||
|
||||
<h3>Как пополнить этот список?</h3>
|
||||
<p class="mb-5">
|
||||
Сделать pull-request в <a href="https://github.com/anthonyaxenov/iptv">репозиторий</a>.
|
||||
Я проверю плейлист и добавлю его в общий список, если всё ок.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
39
src/views/layouts/default.twig
Normal file
39
src/views/layouts/default.twig
Normal file
@ -0,0 +1,39 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="ru">
|
||||
<head>
|
||||
<title>{{ config('app.title') }}</title>
|
||||
<meta charset="utf-8">
|
||||
<meta name="description" content="">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta http-equiv="x-ua-compatible" content="ie=edge">
|
||||
<link href="{{ base_url('css/bootstrap.min.css') }}" rel="stylesheet">
|
||||
<style>.cursor-pointer {
|
||||
cursor: pointer
|
||||
}</style>
|
||||
{% block head %}{% endblock %}
|
||||
</head>
|
||||
<body class="bg-dark text-light">
|
||||
<div class="col-lg-8 mx-auto p-3 pt-md-5 pb-0">
|
||||
<header class="pb-3 mb-3">
|
||||
<a href="/" class="text-light text-decoration-none">
|
||||
<h1>{{ config('app.title') }}</h1>
|
||||
</a>
|
||||
<p class="small text-muted">
|
||||
<a class="small" href="{{ base_url('faq') }}">FAQ</a> | <a
|
||||
class="small" href="https://github.com/anthonyaxenov/iptv">GitHub</a> | <a
|
||||
class="small" href="https://axenov.dev">axenov.dev</a>
|
||||
</p>
|
||||
{% block header %}{% endblock %}
|
||||
</header>
|
||||
<div class="container">
|
||||
{% block content %}{% endblock %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<footer class="py-4 text-center">
|
||||
<a href="https://github.com/anthonyaxenov/iptv">GitHub</a> | <a href="https://axenov.dev">axenov.dev</a>
|
||||
<script src="{{ base_url('js/bootstrap.bundle.min.js') }}"></script>
|
||||
{% block footer %}{% endblock %}
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
63
src/views/list.twig
Normal file
63
src/views/list.twig
Normal file
@ -0,0 +1,63 @@
|
||||
{% extends "layouts/default.twig" %}
|
||||
|
||||
{% block title %}{{ title }}{% endblock %}
|
||||
|
||||
{% block header %}
|
||||
<p class="text-muted small">
|
||||
Обновлено: {{ updated_at }} МСК<br/>
|
||||
Плейлистов в списке: <strong>{{ count }}</strong>
|
||||
</p>
|
||||
<hr/>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<table class="table table-dark table-hover small">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
<th>Информация о плейлисте</th>
|
||||
<th>Каналов</th>
|
||||
<th title="Нажми на ссылку, чтобы скопировать её в буфер обмена">Ссылка для ТВ</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for id, playlist in playlists %}
|
||||
<tr class="pls" data-playlist-id="{{ id }}">
|
||||
<td class="text-center id">
|
||||
<strong>{{ id }}</strong>
|
||||
</td>
|
||||
<td class="info">
|
||||
<strong>{{ playlist.name }}</strong>
|
||||
<span class="badge small bg-secondary text-dark status">?</span>
|
||||
<div class="small">
|
||||
{% if playlist.desc|length > 0 %}
|
||||
<p class="my-1">{{ playlist.desc }}</p>
|
||||
{% endif %}
|
||||
<a href="{{ base_url(id ~ '/details') }}"
|
||||
target="_blank"
|
||||
rel="noopener nofollow">Подробнее...</a>
|
||||
</div>
|
||||
</td>
|
||||
<td class="text-center count">
|
||||
<div class="spinner-border text-success" role="status">
|
||||
<span class="visually-hidden">загрузка...</span>
|
||||
</div>
|
||||
</td>
|
||||
<td class="col-3">
|
||||
<span onclick="prompt('Скопируй адрес плейлиста', '{{ playlist.url }}')"
|
||||
data-bs-toggle="tooltip"
|
||||
data-bs-placement="top"
|
||||
title="Нажми на ссылку, чтобы скопировать её в буфер обмена"
|
||||
class="font-monospace cursor-pointer">
|
||||
{{ playlist.url }}
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% endblock %}
|
||||
|
||||
{% block footer %}
|
||||
<script src="{{ base_url('js/checker.js') }}"></script>
|
||||
{% endblock %}
|
Loading…
Reference in New Issue
Block a user