From 00d612c0e908d72b979783015ddc53c27c99e058 Mon Sep 17 00:00:00 2001 From: AnthonyAxenov Date: Mon, 3 Mar 2025 13:04:05 +0800 Subject: [PATCH] =?UTF-8?q?=D0=91=D0=BE=D0=BB=D1=8C=D1=88=D0=B0=D1=8F=20?= =?UTF-8?q?=D0=BF=D0=B5=D1=80=D0=B5=D1=80=D0=B0=D0=B1=D0=BE=D1=82=D0=BA?= =?UTF-8?q?=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - миграция с Flight на Slim v4 - кэширование ini-файла - кэширование скачанных плейлистов - прочее --- .gitignore | 2 + compose.yml | 1 + src/.env.example | 23 +- src/app/Controllers/ApiController.php | 36 + src/app/Controllers/BasicController.php | 84 + src/app/Controllers/Controller.php | 85 - src/app/Controllers/HomeController.php | 60 - src/app/Controllers/PlaylistController.php | 89 - src/app/Controllers/WebController.php | 115 ++ src/app/Core/Bootstrapper.php | 75 - src/app/Core/ChannelLogo.php | 16 +- src/app/Core/Core.php | 230 +++ src/app/Core/IniFile.php | 43 +- src/app/Core/Playlist.php | 81 +- .../TwigExtention.php} | 4 +- src/app/Errors/ErrorHandler.php | 92 + .../PlaylistNotFoundException.php | 2 +- src/app/Middleware/RequestId.php | 29 + src/app/helpers.php | 46 +- src/cache/.gitkeep | 0 src/composer.json | 14 +- src/composer.lock | 1628 +++++++++++++++-- src/config/app.php | 18 +- src/config/playlists.ini | 1561 ---------------- src/config/redis.php | 11 + src/config/routes.php | 59 +- src/config/twig.php | 8 + src/public/index.php | 17 +- src/views/list.twig | 6 +- 29 files changed, 2341 insertions(+), 2094 deletions(-) create mode 100644 src/app/Controllers/ApiController.php create mode 100644 src/app/Controllers/BasicController.php delete mode 100644 src/app/Controllers/Controller.php delete mode 100644 src/app/Controllers/HomeController.php delete mode 100644 src/app/Controllers/PlaylistController.php create mode 100644 src/app/Controllers/WebController.php delete mode 100644 src/app/Core/Bootstrapper.php create mode 100644 src/app/Core/Core.php rename src/app/{Extensions/TwigFunctions.php => Core/TwigExtention.php} (92%) create mode 100644 src/app/Errors/ErrorHandler.php rename src/app/{Exceptions => Errors}/PlaylistNotFoundException.php (89%) create mode 100644 src/app/Middleware/RequestId.php mode change 100644 => 100755 src/cache/.gitkeep delete mode 100644 src/config/playlists.ini create mode 100644 src/config/redis.php create mode 100644 src/config/twig.php diff --git a/.gitignore b/.gitignore index bbdc365..c2fc36c 100644 --- a/.gitignore +++ b/.gitignore @@ -4,7 +4,9 @@ downloaded/ /src/commit /src/cache/* /src/vendor +/src/config/playlists.ini /src/views/custom.twig +/tmp *.log .env *.m3u diff --git a/compose.yml b/compose.yml index 813a7d0..8743409 100644 --- a/compose.yml +++ b/compose.yml @@ -40,6 +40,7 @@ services: - ./log/php:/var/log/php:rw - ./src:/var/www:rw - ./playlists.ini:/var/www/config/playlists.ini:ro + - ./.git/refs/heads/master:/var/www/commit:ro depends_on: - keydb diff --git a/src/.env.example b/src/.env.example index de4c437..da185b7 100644 --- a/src/.env.example +++ b/src/.env.example @@ -1,7 +1,18 @@ -APP_TITLE='Плейлисты IPTV' +# config/app.php +APP_DEBUG=false +APP_ENV=prod APP_URL=http://localhost:8080 -TWIG_CACHE=1 -TWIG_DEBUG=0 -FLIGHT_CASE_SENSITIVE=0 -FLIGHT_HANDLE_ERRORS=1 -FLIGHT_LOG_ERRORS=1 +APP_TITLE='IPTV Плейлисты' +USER_AGENT='Mozilla/5.0 (Windows NT 10.0; Win64; x99) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36' +PAGE_SIZE=10 + +# config/redis.php +REDIS_HOST='keydb' +REDIS_PORT=6379 +REDIS_PASSWORD= +REDIS_DB=0 +REDIS_TTL_DAYS=14 + +# config/redis.php +TWIG_USE_CACHE=true +TWIG_DEBUG=false diff --git a/src/app/Controllers/ApiController.php b/src/app/Controllers/ApiController.php new file mode 100644 index 0000000..11ac970 --- /dev/null +++ b/src/app/Controllers/ApiController.php @@ -0,0 +1,36 @@ +getAttributes()['code']; + $playlist = $this->getPlaylist($code, true); + $playlist->fetchContent(); + $playlist->parse(); + + $json = json_encode($playlist->toArray(), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES); + $response->getBody()->write($json); + + return $response + ->withHeader('Content-Type', 'application/json') + ->withHeader('Content-Length', strlen($json)); + } +} diff --git a/src/app/Controllers/BasicController.php b/src/app/Controllers/BasicController.php new file mode 100644 index 0000000..9d96898 --- /dev/null +++ b/src/app/Controllers/BasicController.php @@ -0,0 +1,84 @@ +withStatus(404); + $this->view($request, $response, 'notfound.twig'); + + return $response; + } + + /** + * @param ServerRequestInterface $request + * @param ResponseInterface $response + * @param string $template + * @param array $data + * @return ResponseInterface + * @throws LoaderError + * @throws RuntimeError + * @throws SyntaxError + */ + protected function view( + ServerRequestInterface $request, + ResponseInterface $response, + string $template, + array $data = [], + ): ResponseInterface { + $view = Twig::fromRequest($request); + return $view->render($response, $template, $data); + } + + /** + * Возвращает плейлист по его ID для обработки + * + * @param string $id + * @param bool $asJson + * @return Playlist + * @throws Exception + */ + protected function getPlaylist(string $id, bool $asJson = false): Playlist + { + ini()->load(); + + if (ini()->getRedirection($id)) { + $redirectTo = base_url(ini()->getRedirection($id) . ($asJson ? '/json' : '/details')); + Flight::redirect($redirectTo); + die; + } + + try { + return ini()->getPlaylist($id); + } catch (PlaylistNotFoundException) { + $this->notFound($id, $asJson); + die; + } + } +} diff --git a/src/app/Controllers/Controller.php b/src/app/Controllers/Controller.php deleted file mode 100644 index 9d2a736..0000000 --- a/src/app/Controllers/Controller.php +++ /dev/null @@ -1,85 +0,0 @@ -ini = Flight::get('ini'); - } - - /** - * Возвращает плейлист по его ID для обработки - * - * @param string $id - * @param bool $asJson - * @return Playlist - * @throws Exception - */ - protected function getPlaylist(string $id, bool $asJson = false): Playlist - { - if ($this->ini->getRedirection($id)) { - Flight::redirect(base_url($this->ini->getRedirection($id) . ($asJson ? '/json' : '/details'))); - die; - } - - try { - return $this->ini->getPlaylist($id); - } catch (PlaylistNotFoundException) { - $this->notFound($id, $asJson); - die; - } - } - - /** - * Возвращает обработанный плейлист для ответа - * - * @param string $id ID плейлиста - * @param bool $asJson Обрабатывать как json - * @return array - * @throws RandomException - * @throws Exception - */ - protected function getPlaylistResponse(string $id, bool $asJson = false): array - { - $playlist = $this->getPlaylist($id, $asJson); - $playlist->download(); - $playlist->parse(); - return $playlist->toArray(); - } - - /** - * Перебрасывает на страницу 404 при ненайденном плейлисте - * - * @param string $id ID плейлиста - * @param bool $asJson Обрабатывать как json - * @return void - * @throws Exception - */ - public function notFound(string $id, bool $asJson = false): void - { - Flight::response()->status(404)->sendHeaders(); - $asJson || view('notfound', ['id' => $id]); - } -} diff --git a/src/app/Controllers/HomeController.php b/src/app/Controllers/HomeController.php deleted file mode 100644 index 60b5338..0000000 --- a/src/app/Controllers/HomeController.php +++ /dev/null @@ -1,60 +0,0 @@ -query->count() > 0) { - $id = Flight::request()->query->keys()[0]; - Flight::redirect(base_url($id)); - die; - } - - // иначе формируем и сортируем список при необходимости, рисуем страницу - $perPage = 10; - $playlists = $this->ini->playlists(false); - $count = count($playlists); - $pageCount = ceil($count / $perPage); - $offset = max(0, ($page - 1) * $perPage); - $list = array_slice($playlists, $offset, $perPage, true); - - view('list', [ - 'updated_at' => $this->ini->updatedAt(), - 'count' => $count, - 'pages' => [ - 'count' => $pageCount, - 'current' => $page, - ], - 'playlists' => $list, - ]); - } - - /** - * Отображает страницу FAQ - * - * @return void - * @throws Exception - */ - public function faq(): void - { - view('faq'); - } -} diff --git a/src/app/Controllers/PlaylistController.php b/src/app/Controllers/PlaylistController.php deleted file mode 100644 index 6d86b79..0000000 --- a/src/app/Controllers/PlaylistController.php +++ /dev/null @@ -1,89 +0,0 @@ -ini->getPlaylist($id); - Flight::redirect($playlist->pls); - } catch (PlaylistNotFoundException) { - $this->notFound($id); - } - die; - } - - /** - * Отображает страницу описания плейлиста - * - * @param string $id ID плейлиста - * @return void - * @throws Exception - */ - public function details(string $id): void - { - $result = $this->getPlaylistResponse($id); - view('details', $result); - } - - /** - * Возвращает JSON с описанием плейлиста - * - * @param string $id ID плейлиста - * @return void - * @throws Exception - */ - public function json(string $id): void - { - $result = $this->getPlaylistResponse($id, true); - Flight::json($result); - } - - /** - * Возвращает логотип канала, кэшируя при необходимости - * - * @return void - */ - public function logo(): void - { - $input = Flight::request()->query['url'] ?? null; - - $logo = new ChannelLogo($input); - if (!$logo->readFile()) { - $logo->fetch(); - } - - if ($logo->size() === 0) { - $logo->setDefault(); - } - - $logo->store(); - $body = $logo->raw(); - $size = $logo->size(); - $mime = $logo->mimeType(); - - Flight::response() - ->write($body) - ->header('Content-Type', $mime) - ->header('Content-Length', (string)$size); - } -} diff --git a/src/app/Controllers/WebController.php b/src/app/Controllers/WebController.php new file mode 100644 index 0000000..b11308e --- /dev/null +++ b/src/app/Controllers/WebController.php @@ -0,0 +1,115 @@ +load(); + + $playlists = ini()->playlists(false); + $count = count($playlists); + $page = (int)($request->getAttributes()['page'] ?? $request->getQueryParams()['page'] ?? 1); + $pageSize = config('app.page_size'); + $pageCount = ceil($count / $pageSize); + $offset = max(0, ($page - 1) * $pageSize); + $list = array_slice($playlists, $offset, $pageSize, true); + + return $this->view($request, $response, 'list.twig', [ + 'updated_at' => ini()->updatedAt(), + 'playlists' => $list, + 'count' => $count, + 'pageCount' => $pageCount, + 'pageCurrent' => $page, + ]); + } + + /** + * @param ServerRequestInterface $request + * @param ResponseInterface $response + * @return ResponseInterface + * @throws LoaderError + * @throws RuntimeError + * @throws SyntaxError + */ + public function faq(ServerRequestInterface $request, ResponseInterface $response): ResponseInterface + { + return $this->view($request, $response, 'faq.twig'); + } + + /** + * @param ServerRequestInterface $request + * @param ResponseInterface $response + * @return ResponseInterface + */ + public function redirect(ServerRequestInterface $request, ResponseInterface $response): ResponseInterface + { + $code = $request->getAttributes()['code']; + $playlist = $this->getPlaylist($code); + return $response->withHeader('Location', $playlist->pls); + } + + /** + * @param ServerRequestInterface $request + * @param ResponseInterface $response + * @return ResponseInterface + * @throws \Random\RandomException + * @throws LoaderError + * @throws RuntimeError + * @throws SyntaxError + */ + public function details(ServerRequestInterface $request, ResponseInterface $response): ResponseInterface + { + $code = $request->getAttributes()['code']; + $playlist = $this->getPlaylist($code); + $playlist->fetchContent(); + $playlist->parse(); + + return $this->view($request, $response, 'details.twig', $playlist->toArray()); + } + + /** + * @param ServerRequestInterface $request + * @param ResponseInterface $response + * @return ResponseInterface + */ + public function logo(ServerRequestInterface $request, ResponseInterface $response): ResponseInterface + { + $input = $request->getQueryParams()['url'] ?? null; + + $logo = new ChannelLogo($input); + $logo->readFile() || $logo->fetch(); + $logo->size() === 0 && $logo->setDefault(); + $logo->store(); + $body = $logo->raw(); + $size = $logo->size(); + $mime = $logo->mimeType(); + + $response->getBody()->write($body); + return $response->withHeader('Content-Type', $mime) + ->withHeader('Content-Length', $size); + } +} diff --git a/src/app/Core/Bootstrapper.php b/src/app/Core/Bootstrapper.php deleted file mode 100644 index 4cebf16..0000000 --- a/src/app/Core/Bootstrapper.php +++ /dev/null @@ -1,75 +0,0 @@ - $value) { - Flight::set($key, $value); - } - Flight::set('config', $config); - } - - public static function bootCore(): void - { - $loader = new IniFile(); - $loader->load(); - - Flight::set('ini', $loader); - } - - /** - * Загружает шаблонизатор и его расширения - * - * @return void - */ - public static function bootTwig(): void - { - $twigCfg = [ - 'cache' => config('twig.cache'), - 'debug' => config('twig.debug'), - ]; - - $closure = static function ($twig) { - /** @var Environment $twig */ - Flight::set('twig', $twig); - $twig->addExtension(new TwigFunctions()); - $twig->addExtension(new DebugExtension()); - }; - - $loader = new FilesystemLoader(config('flight.views.path')); - Flight::register('view', Environment::class, [$loader, $twigCfg], $closure); - } - - /** - * Загружает маршруты - * - * @return void - */ - public static function bootRoutes(): void - { - $routes = require_once config_path('routes.php'); - foreach ($routes as $route => $handler) { - Flight::route($route, $handler); - } - } -} diff --git a/src/app/Core/ChannelLogo.php b/src/app/Core/ChannelLogo.php index 36a3de1..5dc9af6 100644 --- a/src/app/Core/ChannelLogo.php +++ b/src/app/Core/ChannelLogo.php @@ -41,7 +41,7 @@ class ChannelLogo implements \Stringable */ public function __construct(string $url) { - $url = $this->prepareUrl($url); + $url = empty($url) ? base_url('public/no-tvg-logo.png') : $this->prepareUrl($url); if (is_string($url)) { $this->url = $url; $this->hash = md5($url); @@ -57,17 +57,15 @@ class ChannelLogo implements \Stringable */ protected function prepareUrl(string $url): false|string { - $url = filter_var(trim($url), FILTER_VALIDATE_URL); - if ($url === false) { + $parts = parse_url(trim($url)); + if (!is_array($parts) || count($parts) < 2) { return false; } - $parts = parse_url($url); - if (!is_array($parts)) { - return false; - } + $result = $parts['scheme'] . '://' . $parts['host']; + $result .= (empty($parts['port']) ? '' : ':' . $parts['port']); - return $parts['scheme'] . '://' . $parts['host'] . $parts['path']; + return $result . $parts['path']; } /** @@ -122,7 +120,7 @@ class ChannelLogo implements \Stringable public function setDefault(): bool { $this->path = root_path('public/no-tvg-logo.png'); - return$this->readFile(); + return $this->readFile(); } /** diff --git a/src/app/Core/Core.php b/src/app/Core/Core.php new file mode 100644 index 0000000..9d2d876 --- /dev/null +++ b/src/app/Core/Core.php @@ -0,0 +1,230 @@ +app = AppFactory::create(); + + $this->bootSettings(); + $this->bootRoutes(); + $this->bootTwig(); + $this->bootRedis(); + $this->bootIni(); + + return $this->app; + } + + /** + * Возвращает значение из конфига + * + * @param string $key Ключ в формате "config.key" + * @param mixed|null $default Значение по умолчанию + * @return mixed + */ + public function config(string $key, mixed $default = null): mixed + { + $parts = explode('.', $key); + return $this->config[$parts[0]][$parts[1]] ?? $default; + } + + /** + * @return Redis + */ + public function redis(): Redis + { + return $this->redis; + } + + /** + * @return IniFile + */ + public function ini(): IniFile + { + return $this->iniFile; + } + + /** + * @return App + */ + public function app(): App + { + return $this->app; + } + + /** + * Загружает файл .env или .env.$env + * + * @param string $env + * @return array + */ + protected function loadDotEnvFile(string $env = ''): array + { + $filename = empty($env) ? '.env' : ".env.$env"; + if (!file_exists(root_path($filename))) { + return []; + } + + $dotenv = Dotenv::createMutable(root_path(), $filename); + return $dotenv->safeLoad(); + } + + /** + * Загружает конфигурационные файлы + * + * @return void + */ + protected function bootSettings(): void + { + $env = $this->loadDotEnvFile(); + + if (!empty($env['APP_ENV'])) { + $this->loadDotEnvFile($env['APP_ENV']); + } + + foreach (glob(config_path() . '/*.php') as $file) { + $key = basename($file, '.php'); + $this->config += [$key => require_once $file]; + } + } + + /** + * Загружает маршруты + * + * @return void + * @see https://www.slimframework.com/docs/v4/objects/routing.html + */ + protected function bootRoutes(): void + { + foreach ($this->config['routes'] as $route) { + if (is_array($route['method'])) { + $definition = $this->app->map($route['method'], $route['path'], $route['handler']); + } else { + $isPossible = in_array($route['method'], ['GET', 'POST', 'OPTIONS', 'PUT', 'PATCH', 'DELETE']); + + $func = match (true) { + $route['method'] === '*' => 'any', + $isPossible => strtolower($route['method']), + default => throw new InvalidArgumentException(sprintf('Неверный HTTP метод %s', $route['method'])) + }; + + $definition = $this->app->$func($route['path'], $route['handler']); + } + + if (!empty($route['name'])) { + $definition->setName($route['name']); + } + } + } + + /** + * Загружает шаблонизатор и его расширения + * + * @return void + * @throws LoaderError + * @see https://www.slimframework.com/docs/v4/features/twig-view.html + */ + protected function bootTwig(): void + { + $twig = Twig::create(root_path('views'), $this->config['twig']); + $twig->addExtension(new IptvTwigExtension()); + $this->app->add(TwigMiddleware::create($this->app, $twig)); + } + + /** + * Инициализирует подключение к Redis + * + * @return void + * @see https://github.com/phpredis/phpredis/?tab=readme-ov-file + */ + protected function bootRedis(): void + { + $options = [ + 'host' => $this->config['redis']['host'], + 'port' => (int)$this->config['redis']['port'], + ]; + + if (!empty($this->config['redis']['password'])) { + $options['auth'] = $this->config['redis']['password']; + } + + $this->redis = new Redis($options); + $this->redis->select((int)$this->config['redis']['db']); + $this->redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_JSON); + } + + /** + * Инициализирует объект ini-файла + * + * @return void + */ + protected function bootIni(): void + { + $this->iniFile = new IniFile(); + } +} diff --git a/src/app/Core/IniFile.php b/src/app/Core/IniFile.php index 42c5983..8c99a3f 100644 --- a/src/app/Core/IniFile.php +++ b/src/app/Core/IniFile.php @@ -4,7 +4,7 @@ declare(strict_types=1); namespace App\Core; -use App\Exceptions\PlaylistNotFoundException; +use App\Errors\PlaylistNotFoundException; use Exception; /** @@ -15,7 +15,7 @@ class IniFile /** * @var array Считанное из файла содержимое ini-файла */ - protected array $rawIni; + protected array $ini; /** * @var Playlist[] Коллекция подгруженных плейлистов @@ -40,13 +40,30 @@ class IniFile */ public function load(): void { - $filepath = config_path('playlists.ini'); - $this->updated_at = date('d.m.Y h:i', filemtime($filepath)); - - $this->rawIni = parse_ini_file($filepath, true); - foreach ($this->rawIni as $id => $data) { - $this->playlists[(string)$id] = $this->makePlaylist($id, $data); + $ini = redis()->hGetAll('_playlists_'); + if (empty($ini)) { + $filepath = config_path('playlists.ini'); + $ini = parse_ini_file($filepath, true); + $this->updated_at = date('d.m.Y h:i', filemtime($filepath)); + $order = array_keys($ini); } + + $order ??= redis()->get('_order_'); + $this->ini ??= $ini; + $this->updated_at ??= redis()->get('_updated_at_'); + $transaction = redis()->multi(); + foreach ($order as $id) { + $data = $this->ini[$id]; + $this->playlists[(string)$id] = $pls = $this->makePlaylist($id, $data); + $transaction->hSet('_playlists_', $id, $pls); + } + + $expireAfter = config('redis.ttl_days'); + $transaction + ->expire('_playlists_', $expireAfter) + ->set('_order_', $order, ['EX' => $expireAfter]) + ->set('_updated_at_', $this->updated_at, ['EX' => $expireAfter]) + ->exec(); } /** @@ -57,11 +74,9 @@ class IniFile */ public function playlists(bool $all = true): array { - if ($all) { - return $this->playlists; - } - - return array_filter($this->playlists, static fn ($playlist) => is_null($playlist->redirectId)); + return $all + ? $this->playlists + : array_filter($this->playlists, static fn ($playlist) => is_null($playlist->redirectId)); } /** @@ -111,7 +126,7 @@ class IniFile $id = (string)$id; if (isset($params['redirect'])) { $this->redirections[$id] = $redirectId = (string)$params['redirect']; - $params = $this->rawIni[$redirectId]; + $params = $this->ini[$redirectId]; return $this->makePlaylist($id, $params, $redirectId); } diff --git a/src/app/Core/Playlist.php b/src/app/Core/Playlist.php index d254d98..e4349df 100644 --- a/src/app/Core/Playlist.php +++ b/src/app/Core/Playlist.php @@ -4,6 +4,7 @@ declare(strict_types=1); namespace App\Core; +use CurlHandle; use Exception; use Random\RandomException; @@ -86,20 +87,19 @@ class Playlist * * @return void */ - public function download(): void + public function fetchContent(): void { - $curl = curl_init(); - curl_setopt_array($curl, [ - CURLOPT_URL => $this->pls, - CURLOPT_RETURNTRANSFER => true, - CURLOPT_TIMEOUT => 30, - CURLOPT_HEADER => false, - CURLOPT_FAILONERROR => true, - CURLOPT_FOLLOWLOCATION => true, - CURLOPT_MAXREDIRS => 5, - CURLOPT_USERAGENT => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36', - ]); + $cached = redis()->get($this->id); + if (is_array($cached)) { + $this->downloadStatus['httpCode'] = $cached['httpCode']; + $this->downloadStatus['errCode'] = $cached['errCode']; + $this->downloadStatus['errText'] = $cached['errText']; + $this->downloadStatus['possibleStatus'] = $cached['possibleStatus']; + $this->rawContent = $cached['content']; + return; + } + $curl = $this->makeCurl(); $content = curl_exec($curl); $this->rawContent = $content === false ? null : $content; $this->downloadStatus['httpCode'] = curl_getinfo($curl, CURLINFO_RESPONSE_CODE); @@ -107,6 +107,16 @@ class Playlist $this->downloadStatus['errText'] = curl_error($curl); $this->downloadStatus['possibleStatus'] = $this->guessStatus($this->downloadStatus['errCode']); curl_close($curl); + + if ($cached === false) { + redis()->set($this->id, [ + 'httpCode' => $this->downloadStatus['httpCode'], + 'errCode' => $this->downloadStatus['errCode'], + 'errText' => $this->downloadStatus['errText'], + 'possibleStatus' => $this->downloadStatus['possibleStatus'], + 'content' => $this->rawContent, + ], ['EX' => config('redis.ttl_days')]); + } } /** @@ -241,6 +251,53 @@ class Playlist return $this->parsedContent = $result; } + public function check(): bool + { + $curl = $this->makeCurl([ + CURLOPT_RETURNTRANSFER => true, + CURLOPT_NOBODY => true, + CURLOPT_HEADER => true, + CURLOPT_CUSTOMREQUEST => 'HEAD', + ]); + + $content = curl_exec($curl); + $this->rawContent = $content === false ? null : $content; + $this->downloadStatus['httpCode'] = curl_getinfo($curl, CURLINFO_RESPONSE_CODE); + $this->downloadStatus['errCode'] = curl_errno($curl); + $this->downloadStatus['errText'] = curl_error($curl); + $this->downloadStatus['possibleStatus'] = $this->guessStatus($this->downloadStatus['errCode']); + curl_close($curl); + + return $this->downloadStatus['httpCode'] < 400; + } + + protected function makeCurl(array $customOptions = []): CurlHandle + { + $options = [ + CURLOPT_URL => $this->pls, + CURLOPT_RETURNTRANSFER => true, + CURLOPT_TIMEOUT => 30, + CURLOPT_HEADER => false, + CURLOPT_FAILONERROR => true, + CURLOPT_FOLLOWLOCATION => true, + CURLOPT_MAXREDIRS => 5, + CURLOPT_USERAGENT => config('app.user_agent'), + ]; + + $curl = curl_init(); + + foreach ($options as $option => $value) { + curl_setopt($curl, $option, $value); + } + + // array_merge($options, $customOptions) loses keys + foreach ($customOptions as $option => $value) { + curl_setopt($curl, $option, $value); + } + + return $curl; + } + /** * Парсит атрибуты строки и возвращает ассоциативный массив * diff --git a/src/app/Extensions/TwigFunctions.php b/src/app/Core/TwigExtention.php similarity index 92% rename from src/app/Extensions/TwigFunctions.php rename to src/app/Core/TwigExtention.php index b906b41..909efa7 100644 --- a/src/app/Extensions/TwigFunctions.php +++ b/src/app/Core/TwigExtention.php @@ -2,12 +2,12 @@ declare(strict_types=1); -namespace App\Extensions; +namespace App\Core; use Twig\Extension\AbstractExtension; use Twig\TwigFunction; -class TwigFunctions extends AbstractExtension +class TwigExtention extends AbstractExtension { public function getFunctions(): array { diff --git a/src/app/Errors/ErrorHandler.php b/src/app/Errors/ErrorHandler.php new file mode 100644 index 0000000..3d9d4e2 --- /dev/null +++ b/src/app/Errors/ErrorHandler.php @@ -0,0 +1,92 @@ +payload($exception, $displayErrorDetails); + + $response = app()->getResponseFactory()->createResponse(); + $response->getBody()->write(json_encode($payload, JSON_UNESCAPED_UNICODE)); + + return $response; + } + + /** + * Возвращает структуру исключения для контекста + * + * @param Throwable $e Исключение + * @param bool $logErrorDetails Признак дополнения деталями + * @return array + */ + protected function context(Throwable $e, bool $logErrorDetails): array + { + $result = ['code' => $e->getCode()]; + + $logErrorDetails && $result += [ + 'class' => $e::class, + 'file' => $e->getFile(), + 'line' => $e->getLine(), + 'trace' => $e->getTrace() + ]; + + return $result; + } + + /** + * Возвращает структуру исключения для передачи в ответе + * + * @param Throwable $e Исключение + * @param bool $displayErrorDetails Признак дополнения деталями + * @return array + */ + protected function payload(Throwable $e, bool $displayErrorDetails): array + { + $result = [ + 'error' => [ + 'code' => $e->getCode(), + 'message' => $e->getMessage(), + ], + ]; + + $displayErrorDetails && $result['error'] += [ + 'class' => $e::class, + 'file' => $e->getFile(), + 'line' => $e->getLine(), + 'trace' => $e->getTrace(), + ]; + + return $result; + } +} diff --git a/src/app/Exceptions/PlaylistNotFoundException.php b/src/app/Errors/PlaylistNotFoundException.php similarity index 89% rename from src/app/Exceptions/PlaylistNotFoundException.php rename to src/app/Errors/PlaylistNotFoundException.php index bae0833..1a24043 100644 --- a/src/app/Exceptions/PlaylistNotFoundException.php +++ b/src/app/Errors/PlaylistNotFoundException.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace App\Exceptions; +namespace App\Errors; use Exception; diff --git a/src/app/Middleware/RequestId.php b/src/app/Middleware/RequestId.php new file mode 100644 index 0000000..6d5e284 --- /dev/null +++ b/src/app/Middleware/RequestId.php @@ -0,0 +1,29 @@ +withHeader('X-Request-ID', uniqid()); + + return $handler->handle($request); + } +} diff --git a/src/app/helpers.php b/src/app/helpers.php index 0f784f8..4329899 100644 --- a/src/app/helpers.php +++ b/src/app/helpers.php @@ -2,9 +2,9 @@ declare(strict_types=1); -use flight\Engine; -use flight\net\Response; -use Illuminate\Support\Arr; +use App\Core\Core; +use App\Core\IniFile; +use Slim\App; /** * Returns path to root application directory @@ -58,7 +58,7 @@ function views_path(string $path = ''): string */ function base_url(string $route = ''): string { - return rtrim(sprintf('%s/%s', config('flight.base_url'), $route), '/'); + return rtrim(sprintf('%s/%s', env('APP_URL'), $route), '/'); } /** @@ -70,7 +70,7 @@ function base_url(string $route = ''): string */ function env(string $key, mixed $default = null): mixed { - return $_ENV[$key] ?? $default; + return $_ENV[$key] ?? $_SERVER[$key] ?? $default; } /** @@ -89,23 +89,23 @@ function view(mixed $template, array $data = []): void } /** - * Returns response object + * Returns core object * - * @return Response + * @return Core */ -function response(): Response +function core(): Core { - return Flight::response(); + return Core::get(); } /** * Returns app object * - * @return Engine + * @return App */ -function app(): Engine +function app(): App { - return Flight::app(); + return Core::get()->app(); } /** @@ -135,5 +135,25 @@ function bool(mixed $value): bool */ function config(string $key, mixed $default = null): mixed { - return Flight::get('config')[$key] ?? $default; + return Core::get()->config($key, $default); +} + +/** + * Get Redis instance + * + * @return Redis + */ +function redis(): Redis +{ + return Core::get()->redis(); +} + +/** + * Get ini-file instance + * + * @return IniFile + */ +function ini(): IniFile +{ + return Core::get()->ini(); } diff --git a/src/cache/.gitkeep b/src/cache/.gitkeep old mode 100644 new mode 100755 diff --git a/src/composer.json b/src/composer.json index f88ffa8..c670d6d 100644 --- a/src/composer.json +++ b/src/composer.json @@ -16,10 +16,11 @@ "ext-curl": "*", "ext-redis": "*", "ext-fileinfo": "*", - "mikecao/flight": "^3.12", - "symfony/dotenv": "^7.1", - "twig/twig": "^3.14" - "ext-redis": "*", + "guzzlehttp/guzzle": "^7.8", + "nyholm/psr7": "^1.6", + "vlucas/phpdotenv": "*", + "slim/slim": "^4.11", + "slim/twig-view": "^3.4" }, "autoload": { "psr-4": { @@ -37,6 +38,9 @@ }, "config": { "optimize-autoloader": true, + "preferred-install": "dist", "sort-packages": true - } + }, + "minimum-stability": "dev", + "prefer-stable": true } diff --git a/src/composer.lock b/src/composer.lock index f5fd5ab..8acacda 100644 --- a/src/composer.lock +++ b/src/composer.lock @@ -4,46 +4,1102 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "1877a9475b0d490e07a01363c0508ba5", + "content-hash": "3dab02381fde206f9cddedb4dbcd7703", "packages": [ { - "name": "mikecao/flight", - "version": "v3.14.0", + "name": "graham-campbell/result-type", + "version": "v1.1.3", "source": { "type": "git", - "url": "https://github.com/flightphp/core.git", - "reference": "2762daf4f4725eae784291ac4ef21b04a6863be6" + "url": "https://github.com/GrahamCampbell/Result-Type.git", + "reference": "3ba905c11371512af9d9bdd27d99b782216b6945" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/flightphp/core/zipball/2762daf4f4725eae784291ac4ef21b04a6863be6", - "reference": "2762daf4f4725eae784291ac4ef21b04a6863be6", + "url": "https://api.github.com/repos/GrahamCampbell/Result-Type/zipball/3ba905c11371512af9d9bdd27d99b782216b6945", + "reference": "3ba905c11371512af9d9bdd27d99b782216b6945", + "shasum": "" + }, + "require": { + "php": "^7.2.5 || ^8.0", + "phpoption/phpoption": "^1.9.3" + }, + "require-dev": { + "phpunit/phpunit": "^8.5.39 || ^9.6.20 || ^10.5.28" + }, + "type": "library", + "autoload": { + "psr-4": { + "GrahamCampbell\\ResultType\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + } + ], + "description": "An Implementation Of The Result Type", + "keywords": [ + "Graham Campbell", + "GrahamCampbell", + "Result Type", + "Result-Type", + "result" + ], + "support": { + "issues": "https://github.com/GrahamCampbell/Result-Type/issues", + "source": "https://github.com/GrahamCampbell/Result-Type/tree/v1.1.3" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/graham-campbell/result-type", + "type": "tidelift" + } + ], + "time": "2024-07-20T21:45:45+00:00" + }, + { + "name": "guzzlehttp/guzzle", + "version": "7.9.2", + "source": { + "type": "git", + "url": "https://github.com/guzzle/guzzle.git", + "reference": "d281ed313b989f213357e3be1a179f02196ac99b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/d281ed313b989f213357e3be1a179f02196ac99b", + "reference": "d281ed313b989f213357e3be1a179f02196ac99b", "shasum": "" }, "require": { "ext-json": "*", - "php": ">=7.4" + "guzzlehttp/promises": "^1.5.3 || ^2.0.3", + "guzzlehttp/psr7": "^2.7.0", + "php": "^7.2.5 || ^8.0", + "psr/http-client": "^1.0", + "symfony/deprecation-contracts": "^2.2 || ^3.0" + }, + "provide": { + "psr/http-client-implementation": "1.0" }, "require-dev": { - "ext-pdo_sqlite": "*", - "flightphp/runway": "^0.2.3 || ^1.0", - "league/container": "^4.2", - "level-2/dice": "^4.0", - "phpstan/extension-installer": "^1.3", - "phpstan/phpstan": "^1.10", - "phpunit/phpunit": "^9.5", - "rregeer/phpunit-coverage-check": "^0.3.1", - "squizlabs/php_codesniffer": "^3.8" + "bamarni/composer-bin-plugin": "^1.8.2", + "ext-curl": "*", + "guzzle/client-integration-tests": "3.0.2", + "php-http/message-factory": "^1.1", + "phpunit/phpunit": "^8.5.39 || ^9.6.20", + "psr/log": "^1.1 || ^2.0 || ^3.0" }, "suggest": { - "latte/latte": "Latte template engine", - "phpstan/phpstan": "PHP Static Analyzer", - "tracy/tracy": "Tracy debugger" + "ext-curl": "Required for CURL handler support", + "ext-intl": "Required for Internationalized Domain Name (IDN) support", + "psr/log": "Required for using the Log middleware" + }, + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + } + }, + "autoload": { + "files": [ + "src/functions_include.php" + ], + "psr-4": { + "GuzzleHttp\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "Jeremy Lindblom", + "email": "jeremeamia@gmail.com", + "homepage": "https://github.com/jeremeamia" + }, + { + "name": "George Mponos", + "email": "gmponos@gmail.com", + "homepage": "https://github.com/gmponos" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com", + "homepage": "https://github.com/sagikazarmark" + }, + { + "name": "Tobias Schultze", + "email": "webmaster@tubo-world.de", + "homepage": "https://github.com/Tobion" + } + ], + "description": "Guzzle is a PHP HTTP client library", + "keywords": [ + "client", + "curl", + "framework", + "http", + "http client", + "psr-18", + "psr-7", + "rest", + "web service" + ], + "support": { + "issues": "https://github.com/guzzle/guzzle/issues", + "source": "https://github.com/guzzle/guzzle/tree/7.9.2" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/guzzle", + "type": "tidelift" + } + ], + "time": "2024-07-24T11:22:20+00:00" + }, + { + "name": "guzzlehttp/promises", + "version": "2.0.4", + "source": { + "type": "git", + "url": "https://github.com/guzzle/promises.git", + "reference": "f9c436286ab2892c7db7be8c8da4ef61ccf7b455" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/promises/zipball/f9c436286ab2892c7db7be8c8da4ef61ccf7b455", + "reference": "f9c436286ab2892c7db7be8c8da4ef61ccf7b455", + "shasum": "" + }, + "require": { + "php": "^7.2.5 || ^8.0" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.8.2", + "phpunit/phpunit": "^8.5.39 || ^9.6.20" + }, + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + } + }, + "autoload": { + "psr-4": { + "GuzzleHttp\\Promise\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + }, + { + "name": "Tobias Schultze", + "email": "webmaster@tubo-world.de", + "homepage": "https://github.com/Tobion" + } + ], + "description": "Guzzle promises library", + "keywords": [ + "promise" + ], + "support": { + "issues": "https://github.com/guzzle/promises/issues", + "source": "https://github.com/guzzle/promises/tree/2.0.4" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/promises", + "type": "tidelift" + } + ], + "time": "2024-10-17T10:06:22+00:00" + }, + { + "name": "guzzlehttp/psr7", + "version": "2.7.0", + "source": { + "type": "git", + "url": "https://github.com/guzzle/psr7.git", + "reference": "a70f5c95fb43bc83f07c9c948baa0dc1829bf201" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/psr7/zipball/a70f5c95fb43bc83f07c9c948baa0dc1829bf201", + "reference": "a70f5c95fb43bc83f07c9c948baa0dc1829bf201", + "shasum": "" + }, + "require": { + "php": "^7.2.5 || ^8.0", + "psr/http-factory": "^1.0", + "psr/http-message": "^1.1 || ^2.0", + "ralouphie/getallheaders": "^3.0" + }, + "provide": { + "psr/http-factory-implementation": "1.0", + "psr/http-message-implementation": "1.0" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.8.2", + "http-interop/http-factory-tests": "0.9.0", + "phpunit/phpunit": "^8.5.39 || ^9.6.20" + }, + "suggest": { + "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses" + }, + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + } + }, + "autoload": { + "psr-4": { + "GuzzleHttp\\Psr7\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "George Mponos", + "email": "gmponos@gmail.com", + "homepage": "https://github.com/gmponos" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com", + "homepage": "https://github.com/sagikazarmark" + }, + { + "name": "Tobias Schultze", + "email": "webmaster@tubo-world.de", + "homepage": "https://github.com/Tobion" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com", + "homepage": "https://sagikazarmark.hu" + } + ], + "description": "PSR-7 message implementation that also provides common utility methods", + "keywords": [ + "http", + "message", + "psr-7", + "request", + "response", + "stream", + "uri", + "url" + ], + "support": { + "issues": "https://github.com/guzzle/psr7/issues", + "source": "https://github.com/guzzle/psr7/tree/2.7.0" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/psr7", + "type": "tidelift" + } + ], + "time": "2024-07-18T11:15:46+00:00" + }, + { + "name": "monolog/monolog", + "version": "3.8.1", + "source": { + "type": "git", + "url": "https://github.com/Seldaek/monolog.git", + "reference": "aef6ee73a77a66e404dd6540934a9ef1b3c855b4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Seldaek/monolog/zipball/aef6ee73a77a66e404dd6540934a9ef1b3c855b4", + "reference": "aef6ee73a77a66e404dd6540934a9ef1b3c855b4", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "psr/log": "^2.0 || ^3.0" + }, + "provide": { + "psr/log-implementation": "3.0.0" + }, + "require-dev": { + "aws/aws-sdk-php": "^3.0", + "doctrine/couchdb": "~1.0@dev", + "elasticsearch/elasticsearch": "^7 || ^8", + "ext-json": "*", + "graylog2/gelf-php": "^1.4.2 || ^2.0", + "guzzlehttp/guzzle": "^7.4.5", + "guzzlehttp/psr7": "^2.2", + "mongodb/mongodb": "^1.8", + "php-amqplib/php-amqplib": "~2.4 || ^3", + "php-console/php-console": "^3.1.8", + "phpstan/phpstan": "^2", + "phpstan/phpstan-deprecation-rules": "^2", + "phpstan/phpstan-strict-rules": "^2", + "phpunit/phpunit": "^10.5.17 || ^11.0.7", + "predis/predis": "^1.1 || ^2", + "rollbar/rollbar": "^4.0", + "ruflin/elastica": "^7 || ^8", + "symfony/mailer": "^5.4 || ^6", + "symfony/mime": "^5.4 || ^6" + }, + "suggest": { + "aws/aws-sdk-php": "Allow sending log messages to AWS services like DynamoDB", + "doctrine/couchdb": "Allow sending log messages to a CouchDB server", + "elasticsearch/elasticsearch": "Allow sending log messages to an Elasticsearch server via official client", + "ext-amqp": "Allow sending log messages to an AMQP server (1.0+ required)", + "ext-curl": "Required to send log messages using the IFTTTHandler, the LogglyHandler, the SendGridHandler, the SlackWebhookHandler or the TelegramBotHandler", + "ext-mbstring": "Allow to work properly with unicode symbols", + "ext-mongodb": "Allow sending log messages to a MongoDB server (via driver)", + "ext-openssl": "Required to send log messages using SSL", + "ext-sockets": "Allow sending log messages to a Syslog server (via UDP driver)", + "graylog2/gelf-php": "Allow sending log messages to a GrayLog2 server", + "mongodb/mongodb": "Allow sending log messages to a MongoDB server (via library)", + "php-amqplib/php-amqplib": "Allow sending log messages to an AMQP server using php-amqplib", + "rollbar/rollbar": "Allow sending log messages to Rollbar", + "ruflin/elastica": "Allow sending log messages to an Elastic Search server" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Monolog\\": "src/Monolog" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "https://seld.be" + } + ], + "description": "Sends your logs to files, sockets, inboxes, databases and various web services", + "homepage": "https://github.com/Seldaek/monolog", + "keywords": [ + "log", + "logging", + "psr-3" + ], + "support": { + "issues": "https://github.com/Seldaek/monolog/issues", + "source": "https://github.com/Seldaek/monolog/tree/3.8.1" + }, + "funding": [ + { + "url": "https://github.com/Seldaek", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/monolog/monolog", + "type": "tidelift" + } + ], + "time": "2024-12-05T17:15:07+00:00" + }, + { + "name": "nikic/fast-route", + "version": "v1.3.0", + "source": { + "type": "git", + "url": "https://github.com/nikic/FastRoute.git", + "reference": "181d480e08d9476e61381e04a71b34dc0432e812" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nikic/FastRoute/zipball/181d480e08d9476e61381e04a71b34dc0432e812", + "reference": "181d480e08d9476e61381e04a71b34dc0432e812", + "shasum": "" + }, + "require": { + "php": ">=5.4.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.8.35|~5.7" }, "type": "library", "autoload": { "files": [ - "flight/autoload.php" + "src/functions.php" + ], + "psr-4": { + "FastRoute\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Nikita Popov", + "email": "nikic@php.net" + } + ], + "description": "Fast request router for PHP", + "keywords": [ + "router", + "routing" + ], + "support": { + "issues": "https://github.com/nikic/FastRoute/issues", + "source": "https://github.com/nikic/FastRoute/tree/master" + }, + "time": "2018-02-13T20:26:39+00:00" + }, + { + "name": "nyholm/psr7", + "version": "1.8.2", + "source": { + "type": "git", + "url": "https://github.com/Nyholm/psr7.git", + "reference": "a71f2b11690f4b24d099d6b16690a90ae14fc6f3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Nyholm/psr7/zipball/a71f2b11690f4b24d099d6b16690a90ae14fc6f3", + "reference": "a71f2b11690f4b24d099d6b16690a90ae14fc6f3", + "shasum": "" + }, + "require": { + "php": ">=7.2", + "psr/http-factory": "^1.0", + "psr/http-message": "^1.1 || ^2.0" + }, + "provide": { + "php-http/message-factory-implementation": "1.0", + "psr/http-factory-implementation": "1.0", + "psr/http-message-implementation": "1.0" + }, + "require-dev": { + "http-interop/http-factory-tests": "^0.9", + "php-http/message-factory": "^1.0", + "php-http/psr7-integration-tests": "^1.0", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.4", + "symfony/error-handler": "^4.4" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.8-dev" + } + }, + "autoload": { + "psr-4": { + "Nyholm\\Psr7\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com" + }, + { + "name": "Martijn van der Ven", + "email": "martijn@vanderven.se" + } + ], + "description": "A fast PHP7 implementation of PSR-7", + "homepage": "https://tnyholm.se", + "keywords": [ + "psr-17", + "psr-7" + ], + "support": { + "issues": "https://github.com/Nyholm/psr7/issues", + "source": "https://github.com/Nyholm/psr7/tree/1.8.2" + }, + "funding": [ + { + "url": "https://github.com/Zegnat", + "type": "github" + }, + { + "url": "https://github.com/nyholm", + "type": "github" + } + ], + "time": "2024-09-09T07:06:30+00:00" + }, + { + "name": "phpoption/phpoption", + "version": "1.9.3", + "source": { + "type": "git", + "url": "https://github.com/schmittjoh/php-option.git", + "reference": "e3fac8b24f56113f7cb96af14958c0dd16330f54" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/schmittjoh/php-option/zipball/e3fac8b24f56113f7cb96af14958c0dd16330f54", + "reference": "e3fac8b24f56113f7cb96af14958c0dd16330f54", + "shasum": "" + }, + "require": { + "php": "^7.2.5 || ^8.0" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.8.2", + "phpunit/phpunit": "^8.5.39 || ^9.6.20 || ^10.5.28" + }, + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + }, + "branch-alias": { + "dev-master": "1.9-dev" + } + }, + "autoload": { + "psr-4": { + "PhpOption\\": "src/PhpOption/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "Johannes M. Schmitt", + "email": "schmittjoh@gmail.com", + "homepage": "https://github.com/schmittjoh" + }, + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + } + ], + "description": "Option Type for PHP", + "keywords": [ + "language", + "option", + "php", + "type" + ], + "support": { + "issues": "https://github.com/schmittjoh/php-option/issues", + "source": "https://github.com/schmittjoh/php-option/tree/1.9.3" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpoption/phpoption", + "type": "tidelift" + } + ], + "time": "2024-07-20T21:41:07+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/http-client", + "version": "1.0.3", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-client.git", + "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-client/zipball/bb5906edc1c324c9a05aa0873d40117941e5fa90", + "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90", + "shasum": "" + }, + "require": { + "php": "^7.0 || ^8.0", + "psr/http-message": "^1.0 || ^2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Client\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP clients", + "homepage": "https://github.com/php-fig/http-client", + "keywords": [ + "http", + "http-client", + "psr", + "psr-18" + ], + "support": { + "source": "https://github.com/php-fig/http-client" + }, + "time": "2023-09-23T14:17:50+00:00" + }, + { + "name": "psr/http-factory", + "version": "1.1.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-factory.git", + "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-factory/zipball/2b4765fddfe3b508ac62f829e852b1501d3f6e8a", + "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a", + "shasum": "" + }, + "require": { + "php": ">=7.1", + "psr/http-message": "^1.0 || ^2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Message\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "PSR-17: Common interfaces for PSR-7 HTTP message factories", + "keywords": [ + "factory", + "http", + "message", + "psr", + "psr-17", + "psr-7", + "request", + "response" + ], + "support": { + "source": "https://github.com/php-fig/http-factory" + }, + "time": "2024-04-15T12:06:14+00:00" + }, + { + "name": "psr/http-message", + "version": "2.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-message.git", + "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-message/zipball/402d35bcb92c70c026d1a6a9883f06b2ead23d71", + "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Message\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP messages", + "homepage": "https://github.com/php-fig/http-message", + "keywords": [ + "http", + "http-message", + "psr", + "psr-7", + "request", + "response" + ], + "support": { + "source": "https://github.com/php-fig/http-message/tree/2.0" + }, + "time": "2023-04-04T09:54:51+00:00" + }, + { + "name": "psr/http-server-handler", + "version": "1.0.2", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-server-handler.git", + "reference": "84c4fb66179be4caaf8e97bd239203245302e7d4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-server-handler/zipball/84c4fb66179be4caaf8e97bd239203245302e7d4", + "reference": "84c4fb66179be4caaf8e97bd239203245302e7d4", + "shasum": "" + }, + "require": { + "php": ">=7.0", + "psr/http-message": "^1.0 || ^2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Server\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP server-side request handler", + "keywords": [ + "handler", + "http", + "http-interop", + "psr", + "psr-15", + "psr-7", + "request", + "response", + "server" + ], + "support": { + "source": "https://github.com/php-fig/http-server-handler/tree/1.0.2" + }, + "time": "2023-04-10T20:06:20+00:00" + }, + { + "name": "psr/http-server-middleware", + "version": "1.0.2", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-server-middleware.git", + "reference": "c1481f747daaa6a0782775cd6a8c26a1bf4a3829" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-server-middleware/zipball/c1481f747daaa6a0782775cd6a8c26a1bf4a3829", + "reference": "c1481f747daaa6a0782775cd6a8c26a1bf4a3829", + "shasum": "" + }, + "require": { + "php": ">=7.0", + "psr/http-message": "^1.0 || ^2.0", + "psr/http-server-handler": "^1.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Server\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP server-side middleware", + "keywords": [ + "http", + "http-interop", + "middleware", + "psr", + "psr-15", + "psr-7", + "request", + "response" + ], + "support": { + "issues": "https://github.com/php-fig/http-server-middleware/issues", + "source": "https://github.com/php-fig/http-server-middleware/tree/1.0.2" + }, + "time": "2023-04-11T06:14:47+00:00" + }, + { + "name": "psr/log", + "version": "3.0.2", + "source": { + "type": "git", + "url": "https://github.com/php-fig/log.git", + "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/log/zipball/f16e1d5863e37f8d8c2a01719f5b34baa2b714d3", + "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3", + "shasum": "" + }, + "require": { + "php": ">=8.0.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Log\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for logging libraries", + "homepage": "https://github.com/php-fig/log", + "keywords": [ + "log", + "psr", + "psr-3" + ], + "support": { + "source": "https://github.com/php-fig/log/tree/3.0.2" + }, + "time": "2024-09-11T13:17:53+00:00" + }, + { + "name": "ralouphie/getallheaders", + "version": "3.0.3", + "source": { + "type": "git", + "url": "https://github.com/ralouphie/getallheaders.git", + "reference": "120b605dfeb996808c31b6477290a714d356e822" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/120b605dfeb996808c31b6477290a714d356e822", + "reference": "120b605dfeb996808c31b6477290a714d356e822", + "shasum": "" + }, + "require": { + "php": ">=5.6" + }, + "require-dev": { + "php-coveralls/php-coveralls": "^2.1", + "phpunit/phpunit": "^5 || ^6.5" + }, + "type": "library", + "autoload": { + "files": [ + "src/getallheaders.php" ] }, "notification-url": "https://packagist.org/downloads/", @@ -52,30 +1108,197 @@ ], "authors": [ { - "name": "Mike Cao", - "email": "mike@mikecao.com", - "homepage": "http://www.mikecao.com/", - "role": "Original Developer" - }, - { - "name": "Franyer Sánchez", - "email": "franyeradriansanchez@gmail.com", - "homepage": "https://faslatam.000webhostapp.com", - "role": "Maintainer" - }, - { - "name": "n0nag0n", - "email": "n0nag0n@sky-9.com", - "role": "Maintainer" + "name": "Ralph Khattar", + "email": "ralph.khattar@gmail.com" } ], - "description": "Flight is a fast, simple, extensible framework for PHP. Flight enables you to quickly and easily build RESTful web applications. This is the maintained fork of mikecao/flight", - "homepage": "http://flightphp.com", + "description": "A polyfill for getallheaders.", "support": { - "issues": "https://github.com/flightphp/core/issues", - "source": "https://github.com/flightphp/core/tree/v3.14.0" + "issues": "https://github.com/ralouphie/getallheaders/issues", + "source": "https://github.com/ralouphie/getallheaders/tree/develop" }, - "time": "2025-02-21T17:42:57+00:00" + "time": "2019-03-08T08:55:37+00:00" + }, + { + "name": "slim/slim", + "version": "4.14.0", + "source": { + "type": "git", + "url": "https://github.com/slimphp/Slim.git", + "reference": "5943393b88716eb9e82c4161caa956af63423913" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/slimphp/Slim/zipball/5943393b88716eb9e82c4161caa956af63423913", + "reference": "5943393b88716eb9e82c4161caa956af63423913", + "shasum": "" + }, + "require": { + "ext-json": "*", + "nikic/fast-route": "^1.3", + "php": "^7.4 || ^8.0", + "psr/container": "^1.0 || ^2.0", + "psr/http-factory": "^1.1", + "psr/http-message": "^1.1 || ^2.0", + "psr/http-server-handler": "^1.0", + "psr/http-server-middleware": "^1.0", + "psr/log": "^1.1 || ^2.0 || ^3.0" + }, + "require-dev": { + "adriansuter/php-autoload-override": "^1.4", + "ext-simplexml": "*", + "guzzlehttp/psr7": "^2.6", + "httpsoft/http-message": "^1.1", + "httpsoft/http-server-request": "^1.1", + "laminas/laminas-diactoros": "^2.17 || ^3", + "nyholm/psr7": "^1.8", + "nyholm/psr7-server": "^1.1", + "phpspec/prophecy": "^1.19", + "phpspec/prophecy-phpunit": "^2.1", + "phpstan/phpstan": "^1.11", + "phpunit/phpunit": "^9.6", + "slim/http": "^1.3", + "slim/psr7": "^1.6", + "squizlabs/php_codesniffer": "^3.10", + "vimeo/psalm": "^5.24" + }, + "suggest": { + "ext-simplexml": "Needed to support XML format in BodyParsingMiddleware", + "ext-xml": "Needed to support XML format in BodyParsingMiddleware", + "php-di/php-di": "PHP-DI is the recommended container library to be used with Slim", + "slim/psr7": "Slim PSR-7 implementation. See https://www.slimframework.com/docs/v4/start/installation.html for more information." + }, + "type": "library", + "autoload": { + "psr-4": { + "Slim\\": "Slim" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Josh Lockhart", + "email": "hello@joshlockhart.com", + "homepage": "https://joshlockhart.com" + }, + { + "name": "Andrew Smith", + "email": "a.smith@silentworks.co.uk", + "homepage": "http://silentworks.co.uk" + }, + { + "name": "Rob Allen", + "email": "rob@akrabat.com", + "homepage": "http://akrabat.com" + }, + { + "name": "Pierre Berube", + "email": "pierre@lgse.com", + "homepage": "http://www.lgse.com" + }, + { + "name": "Gabriel Manricks", + "email": "gmanricks@me.com", + "homepage": "http://gabrielmanricks.com" + } + ], + "description": "Slim is a PHP micro framework that helps you quickly write simple yet powerful web applications and APIs", + "homepage": "https://www.slimframework.com", + "keywords": [ + "api", + "framework", + "micro", + "router" + ], + "support": { + "docs": "https://www.slimframework.com/docs/v4/", + "forum": "https://discourse.slimframework.com/", + "irc": "irc://irc.freenode.net:6667/slimphp", + "issues": "https://github.com/slimphp/Slim/issues", + "rss": "https://www.slimframework.com/blog/feed.rss", + "slack": "https://slimphp.slack.com/", + "source": "https://github.com/slimphp/Slim", + "wiki": "https://github.com/slimphp/Slim/wiki" + }, + "funding": [ + { + "url": "https://opencollective.com/slimphp", + "type": "open_collective" + }, + { + "url": "https://tidelift.com/funding/github/packagist/slim/slim", + "type": "tidelift" + } + ], + "time": "2024-06-13T08:54:48+00:00" + }, + { + "name": "slim/twig-view", + "version": "3.4.1", + "source": { + "type": "git", + "url": "https://github.com/slimphp/Twig-View.git", + "reference": "b4268d87d0e327feba5f88d32031e9123655b909" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/slimphp/Twig-View/zipball/b4268d87d0e327feba5f88d32031e9123655b909", + "reference": "b4268d87d0e327feba5f88d32031e9123655b909", + "shasum": "" + }, + "require": { + "php": "^7.4 || ^8.0", + "psr/http-message": "^1.1 || ^2.0", + "slim/slim": "^4.12", + "symfony/polyfill-php81": "^1.29", + "twig/twig": "^3.11" + }, + "require-dev": { + "phpspec/prophecy-phpunit": "^2.0", + "phpstan/phpstan": "^1.10.59", + "phpunit/phpunit": "^9.6 || ^10", + "psr/http-factory": "^1.0", + "squizlabs/php_codesniffer": "^3.9" + }, + "type": "library", + "autoload": { + "psr-4": { + "Slim\\Views\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Josh Lockhart", + "email": "hello@joshlockhart.com", + "homepage": "http://joshlockhart.com" + }, + { + "name": "Pierre Berube", + "email": "pierre@lgse.com", + "homepage": "http://www.lgse.com" + } + ], + "description": "Slim Framework 4 view helper built on top of the Twig 3 templating component", + "homepage": "https://www.slimframework.com", + "keywords": [ + "framework", + "slim", + "template", + "twig", + "view" + ], + "support": { + "issues": "https://github.com/slimphp/Twig-View/issues", + "source": "https://github.com/slimphp/Twig-View/tree/3.4.1" + }, + "time": "2024-09-26T05:42:02+00:00" }, { "name": "symfony/deprecation-contracts", @@ -144,80 +1367,6 @@ ], "time": "2024-09-25T14:20:29+00:00" }, - { - "name": "symfony/dotenv", - "version": "v7.2.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/dotenv.git", - "reference": "28347a897771d0c28e99b75166dd2689099f3045" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/dotenv/zipball/28347a897771d0c28e99b75166dd2689099f3045", - "reference": "28347a897771d0c28e99b75166dd2689099f3045", - "shasum": "" - }, - "require": { - "php": ">=8.2" - }, - "conflict": { - "symfony/console": "<6.4", - "symfony/process": "<6.4" - }, - "require-dev": { - "symfony/console": "^6.4|^7.0", - "symfony/process": "^6.4|^7.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/v7.2.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": "2024-11-27T11:18:42+00:00" - }, { "name": "symfony/polyfill-ctype", "version": "v1.31.0", @@ -377,6 +1526,162 @@ ], "time": "2024-09-09T11:45:10+00:00" }, + { + "name": "symfony/polyfill-php80", + "version": "v1.31.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php80.git", + "reference": "60328e362d4c2c802a54fcbf04f9d3fb892b4cf8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/60328e362d4c2c802a54fcbf04f9d3fb892b4cf8", + "reference": "60328e362d4c2c802a54fcbf04f9d3fb892b4cf8", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php80\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ion Bazan", + "email": "ion.bazan@gmail.com" + }, + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php80/tree/v1.31.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": "2024-09-09T11:45:10+00:00" + }, + { + "name": "symfony/polyfill-php81", + "version": "v1.31.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php81.git", + "reference": "4a4cfc2d253c21a5ad0e53071df248ed48c6ce5c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php81/zipball/4a4cfc2d253c21a5ad0e53071df248ed48c6ce5c", + "reference": "4a4cfc2d253c21a5ad0e53071df248ed48c6ce5c", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php81\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "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 backporting some PHP 8.1+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php81/tree/v1.31.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": "2024-09-09T11:45:10+00:00" + }, { "name": "twig/twig", "version": "v3.20.0", @@ -455,19 +1760,100 @@ } ], "time": "2025-02-13T08:34:43+00:00" + }, + { + "name": "vlucas/phpdotenv", + "version": "v5.6.1", + "source": { + "type": "git", + "url": "https://github.com/vlucas/phpdotenv.git", + "reference": "a59a13791077fe3d44f90e7133eb68e7d22eaff2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/a59a13791077fe3d44f90e7133eb68e7d22eaff2", + "reference": "a59a13791077fe3d44f90e7133eb68e7d22eaff2", + "shasum": "" + }, + "require": { + "ext-pcre": "*", + "graham-campbell/result-type": "^1.1.3", + "php": "^7.2.5 || ^8.0", + "phpoption/phpoption": "^1.9.3", + "symfony/polyfill-ctype": "^1.24", + "symfony/polyfill-mbstring": "^1.24", + "symfony/polyfill-php80": "^1.24" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.8.2", + "ext-filter": "*", + "phpunit/phpunit": "^8.5.34 || ^9.6.13 || ^10.4.2" + }, + "suggest": { + "ext-filter": "Required to use the boolean validator." + }, + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + }, + "branch-alias": { + "dev-master": "5.6-dev" + } + }, + "autoload": { + "psr-4": { + "Dotenv\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Vance Lucas", + "email": "vance@vancelucas.com", + "homepage": "https://github.com/vlucas" + } + ], + "description": "Loads environment variables from `.env` to `getenv()`, `$_ENV` and `$_SERVER` automagically.", + "keywords": [ + "dotenv", + "env", + "environment" + ], + "support": { + "issues": "https://github.com/vlucas/phpdotenv/issues", + "source": "https://github.com/vlucas/phpdotenv/tree/v5.6.1" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/vlucas/phpdotenv", + "type": "tidelift" + } + ], + "time": "2024-07-20T21:52:34+00:00" } ], "packages-dev": [], "aliases": [], - "minimum-stability": "stable", + "minimum-stability": "dev", "stability-flags": {}, - "prefer-stable": false, + "prefer-stable": true, "prefer-lowest": false, "platform": { - "php": "^8.3", - "ext-json": "*", - "ext-curl": "*", - "ext-fileinfo": "*" + "php": "^8.3" }, "platform-dev": {}, "plugin-api-version": "2.6.0" diff --git a/src/config/app.php b/src/config/app.php index 64b81ef..c7395b7 100644 --- a/src/config/app.php +++ b/src/config/app.php @@ -3,17 +3,13 @@ declare(strict_types=1); return [ - // https://flightphp.com/learn#configuration - 'flight.base_url' => env('APP_URL', 'http://localhost:8080'), - 'flight.case_sensitive' => bool(env('FLIGHT_CASE_SENSITIVE', false)), - 'flight.handle_errors' => bool(env('FLIGHT_HANDLE_ERRORS', true)), - 'flight.log_errors' => bool(env('FLIGHT_LOG_ERRORS', true)), - 'flight.views.path' => views_path(), - 'flight.views.extension' => '.twig', - 'twig.cache' => bool(env('TWIG_CACHE', true)) ? cache_path() . '/views' : false, - 'twig.debug' => bool(env('TWIG_DEBUG', false)), - 'app.title' => env('APP_TITLE', 'IPTV Playlists'), - 'app.pls_encodings' => [ + 'base_url' => env('APP_URL', 'http://localhost:8080'), + 'debug' => bool(env('APP_DEBUG', false)), + 'env' => env('APP_ENV', env('IPTV_ENV', 'prod')), + 'title' => env('APP_TITLE', 'IPTV Плейлисты'), + 'user_agent' => env('USER_AGENT'), + 'page_size' => (int)env('PAGE_SIZE', 10), + 'pls_encodings' => [ 'UTF-8', 'CP1251', // 'CP866', diff --git a/src/config/playlists.ini b/src/config/playlists.ini deleted file mode 100644 index bb1ecd4..0000000 --- a/src/config/playlists.ini +++ /dev/null @@ -1,1561 +0,0 @@ -;################################################### -; без группировки -;################################################### - -[ru] -name='Русские' -desc = -pls='https://raw.githubusercontent.com/iptv-org/iptv/master/streams/ru.m3u' -src='https://github.com/iptv-org/iptv' - -[ru2] -name='Российские федеральные' -desc='Несколько главных телеканалов' -pls='https://pastebin.com/raw/F7RiTyK2' -src='' - -[ru3] -name='Российские' -desc='Эфирные + WINK' -pls='https://dl.dropbox.com/scl/fi/jocyqlbk1wyi83v/efirtv.m3u' -src='' - -[sng] -name='Каналы СНГ' -desc = -pls='https://dl.dropboxusercontent.com/s/iw9v57cln6dfkpu/Vinnitsa.m3u' -src='https://iptvsensei.ru/samoobnovlyayemyye-pleylisty-iptv' - -[edem] -name='Эдем' -desc='Разные категории и страны' -pls='https://dl.dropbox.com/scl/fi/37c2sbo6kq8997u52ricj/.m3u?rlkey=gnt8h6iaqzf5vrsmfa0wh7xio&dl=0' -src='' - -[sh] -name='Российские и 18+' -desc='' -pls='https://gitlab.com/iptv135435/iptvshared/raw/main/IPTV_SHARED.m3u' -src='https://gitlab.com/iptv135435/iptvshared' - -[tp] -name='TaurerPlus' -desc = -pls='https://raw.githubusercontent.com/TaurerMedia/TaurerPlus/main/index.m3u8' -src='https://github.com/anthonyaxenov/iptv/issues/2' - -[b1] -name='IPTV плейлисты для GX3235T2C и AV2568T2C' -desc = -pls='https://www.digitaltv.ru/upload/iblock/034/tvlist.m3u' -src='https://www.digitaltv.ru/news/iptv_pleylisty.html' - -;################################################### -; музыкальные -;################################################### - -[mr] -name='Музыкальные радиоканалы' -desc='1800+ радиоканалов со всего мира и разных сервисов' -pls='https://dl.dropbox.com/scl/fi/tebxhojnowjccht/RADIO.m3u' -src='' - -[mr2] -name='Музыкальные радиоканалы' -desc='Расширенный mr' -pls='https://dl.dropbox.com/scl/fi/utrbaozx2t8tecsz9l3m5/R.A.D.I.O.m3u?rlkey=ap1e2ezi45fi8n4n38hiuvgb8' -src='' - -[mr3] -name='Музыкальные радиоканалы' -desc='250+ музыкальных радиоканалов по жанрам' -pls='https://dl.dropbox.com/scl/fi/f8hqauggbpvbxyp/Radio_Genres.m3u' -src='' - -;################################################### -; фильмы и сериалы -;################################################### - -[k1] -name='Кинофильмы' -desc='4000+ разных фильмов, без каналов' -pls='https://dl.dropbox.com/scl/fi/c3if4cnohx524ar/Film.m3u' -src='' - -[k2] -name='Кинофильмы' -desc='4000+ разных фильмов, без каналов; немного отличается от kino' -pls='https://dl.dropbox.com/scl/fi/n8qb6uyi51scqn3/film_4000.m3u' -src='' - -[k3] -name='Фильмы 5 (iptvsensei.ru)' -desc = -pls='https://pastebin.com/raw/jLaRge54' -src='https://iptvsensei.ru/novye-samoobnovlyaemye-plejlisty' - -[k4] -name='Фильмы' -pls='http://divantv.zz.mu/Playlist003.m3u' -desc= -src='https://t.me/iptv_aggregator_chat' - -;################################################### -; детские -;################################################### - -[kid] -name='Детские познавательные' -desc='' -src='' -pls='https://pastebin.com/raw/bvLkNCqV' - -[mult] -name='Советские мультики' -desc='' -pls='https://raw.githubusercontent.com/user797/Playlists/refs/heads/main/Cartoons.m3u8' -src='' - -;################################################### -; веб-камеры -;################################################### - -[cam] -name='Веб-камеры' -desc = -pls='http://gorod.tv/iptv.m3u' -src='https://iptvsensei.ru/samoobnovlyayemyye-pleylisty-iptv' - -[cam2] -name='Веб-камеры' -desc='Со всего мира' -pls='https://dl.dropbox.com/scl/fi/my7c21h9zk2ayxzd28rp1/.m3u?rlkey=ca7lh90r0qqm6ushz9rqyy91r' -src='' - -[cam3] -name='Веб-камеры' -desc='Расширенный cam2' -pls='https://dl.dropbox.com/scl/fi/1jous9zdcxtulcw/Camera.m3u' -src='' - -;################################################### -; миксы -;################################################### - -[mix1] -name='Микс 1' -desc='Россия, Беларусь, Польша, Украина, Литва, Молдова, Египет, Греция' -pls='https://dl.dropbox.com/scl/fi/5kkgez01gq41xdfobu9t1/887.m3u?rlkey=4cewg8idky1vgmax3v1w8kupr&st=pteb5t7l&dl=0' -src='' - -[mix2] -name='Микс 2' -desc='В основном Россия, есть Беларусь и др. зарубежные' -pls='https://dl.dropbox.com/scl/fi/2qqj4pl6fknbl2b/TV.m3u' -src='' - -[mix3] -name='Микс 3' -desc='Россия, Украина, Узбекистан, Казахстан и др. зарубежные' -pls='https://dl.dropbox.com/scl/fi/k8ssgyysmqw3h74fcv3gg/.m3u?rlkey=mr8u5kgl9uhnee0bi52yddurp' -src='' - -[mix4] -name='Микс 4 зарубежный' -desc='Без российских: Китай, Казахстан, Беларусь, Бразилия, Канада, США; Европа, Азия, Ближний восток и др.' -pls='https://dl.dropbox.com/scl/fi/8n22yhvwasfzrk1/Zarub.m3u' -src='' - -[mix5] -name='Микс 5 зарубежный' -desc='Расширенный mix4 с телепрограммой' -pls='https://dl.dropbox.com/scl/fi/l468bhnljav5vwp9mq9a7/.m3u?rlkey=hizmeptzfnjrzsiscu23454bb' -src='' - -[mix6] -name='Микс 6' -desc='Похож на mix1' -pls='https://dl.dropbox.com/scl/fi/tbri62bjrs372wy440ebg/ILook-4756-16.m3u?rlkey=0h1gh4g897el8fjvxvaf443tq&st=acj49kp5&dl=0' -src='' - -[mix6d] -name='Микс 6 дубль' -desc='Каналы те же, что в mix6, но ссылки на потоки другие' -pls='https://dl.dropbox.com/scl/fi/bdrqx8il33d0lfiwuzq4d/ILook-4756-15.m3u?rlkey=ra02fmla2jj6092a3u5yzb3zp&st=pxd0foe7&dl=0' -src='' - -[mix7] -name='Микс 7 зарубежный огромный' -desc='Каналы и фильмы -- Европа, Азия, Ближний восток' -pls='https://dl.dropbox.com/scl/fi/2jute41yvxjocw3/tv_channels_Matrix2022.m3u' -src='' - -[mix8] -name='Микс 8' -desc='Россия, зарубеж' -pls='https://dl.dropbox.com/scl/fi/xbyv4gnz5f3t4tpan0aqc/stream-5.m3u?rlkey=omga60ixe53dnghohnqufq8ch' -src='' - -[mix9] -name='Микс 9' -desc='Россия, зарубеж' -pls='https://dl.dropbox.com/scl/fi/r9o5lpmc23kpn8kqr2xq5/TV44444.m3u?rlkey=h1svmxccs466cd60l55kprmqt&dl=0' -src='' - -[mix10] -name='Микс 10' -desc='Российские каналы -- музыка, кино и сериалы' -pls='https://dl.dropbox.com/scl/fi/0btovnab1pjvqdo/kinokha.m3u' -src='' - -[mix11] -name='Микс 11' -desc='Российские каналы -- музыка, кино и сериалы' -pls='https://dl.dropbox.com/scl/fi/0btovnab1pjvqdo/kinokha.m3u' -src='' - -[mix12] -name='Микс 12 огромный' -desc='Очень много тематик и государств 22000+ теле/радио каналов со всего мира.' -pls='https://dl.dropbox.com/scl/fi/bknr5ndm9qnz8wklbpvin/13sh_tv.m3u?rlkey=9f5jf7teoxc0f77ri6213x7vu' -src='' - -[mix13] -name='Микс 13' -desc='Россия, Казахстан, Польша, Молдова, Турция, Корея, Украина, Великобритания, Азербайджан, Эстония, Беларусь, Италия, Индонезия, Таджикистан, Туркменистан' -pls='https://dl.dropbox.com/scl/fi/z2viao5wrva4bzhdegz42/ColtishkaIPTV.m3u?rlkey=c8tq73gpgyftrnohlc2gm4kmq&st=y57735ba&dl=0' -src='' - -[mix14] -name='Микс 14' -desc='Россия, Молдова, Канада, Казахстан, Турция и мн. др. 4K + HD' -pls='https://dl.dropbox.com/scl/fi/6b20hiys8j27k0icra79w/konkiv_Edi.m3u?rlkey=8ynw7l2b2utzaikfg5x2eyepu&dl=0' -src='' - -[mix15] -name='Микс 15' -desc='Спорт, музыка, кино, радио, детские; Россия, Беларусь, Украина' -pls='https://dl.dropbox.com/scl/fi/mbyei8mfurol7zi/LokoTV.m3u' -src='' - -[mix16] -name='Микс 16' -desc='Россия: очень много кино, спорта и музыки, но не только' -pls='https://dl.dropbox.com/scl/fi/74m171q8t8opwhve485pf/Moi_ip.m3u?rlkey=8cj23dvra85s2u7eh1kfzq4bp' -src='' - -[mix17] -name='Микс 17' -desc='Радио, кино, фильмы 18+; Торрент ТВ, Wink, Catcast, Rutube' -pls='https://dl.dropbox.com/scl/fi/ur595ef4cqmfst951kboh/.NET_2.m3u?rlkey=0cw1ficfrq0m6yg2udh16qn78&dl=0' -src='' - -[mix18] -name='Микс 18' -desc='Россия + каналы всего мира' -pls='https://dl.dropbox.com/scl/fi/ir89ujj1wnabkk88d57bb/2_5192754592034613084.m3u?rlkey=ov8kis4eztaccljnwnlmla1j7&st=4yzsr3m7&dl=0' -src='' - -[mix19] -name='Микс 19' -desc='Россия + каналы всего мира' -pls='https://dl.dropbox.com/scl/fi/idq3ih7akoety1shvhnqg/2_5390947455636366975.m3u?rlkey=hh4rm3ivg4nkdjzs6tmdvv8na&st=vqq4vpzt&dl=0' -src='' - -[mix20] -name='Микс 20' -desc='Расширенный mix19' -pls='https://dl.dropbox.com/scl/fi/9sn2z2yypakdb6xibka0h/2_5390947455636366984.m3u?rlkey=5af4qc5odgyp14wzy5obv5uwi&st=ouwbcr2x&dl=0' -src='' - -[mix20d] -name='Микс 20 дубль' -desc='Каналы те же, что в mix20, но ссылки на потоки другие' -pls='https://dl.dropbox.com/scl/fi/9sn2z2yypakdb6xibka0h/2_5390947455636366984.m3u?rlkey=5af4qc5odgyp14wzy5obv5uwi&st=ouwbcr2x&dl=0' -src='' - -[mix21] -name='Микс 21 большой' -desc='Очень много очень разных' -pls='https://raw.githubusercontent.com/suzukantv/IPTV/refs/heads/main/KolobokHatNet2.m3u' -src='' - -[mix22] -name='Микс 22 большой' -desc='Очень много очень разных' -pls='https://raw.githubusercontent.com/suzukantv/IPTV/refs/heads/main/SuzuSCloudFreeGedemTvNET.m3u' -src='' - -;################################################### -; конкретные страны -;################################################### - -[kz] -name='Казахстан' -desc = -pls='https://raw.githubusercontent.com/iptv-org/iptv/master/streams/kz.m3u' -src='https://github.com/iptv-org/iptv' - -[az] -name='Азербайджан' -desc = -pls='https://raw.githubusercontent.com/iptv-org/iptv/master/streams/az.m3u' -src='https://github.com/iptv-org/iptv' - -;################################################### -; smolnp -;################################################### - -[so] -name='smolnp/IPTVru - Основной плейлист' -desc='250 телеканалов и 13 радиоканалов.' -pls='https://smolnp.github.io/IPTVru/IPTVru.m3u' -src='https://smolnp.github.io/IPTVru' - -[sx] -name='smolnp/IPTVru - Плейлист с телеканалами для взрослых' -desc='250 телеканалов, 13 радиоканалов и 10 телеканалов для взрослых.' -pls='https://smolnp.github.io/IPTVru/IPTVххх.m3u' -src='https://smolnp.github.io/IPTVru' - -;################################################### -; iptv-playlisty.ru -;################################################### - -[2] -name='[iptv-playlisty.ru] Плейлист 2020' -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/' - -[3] -name='[iptv-playlisty.ru] Плейлист newplay' -desc='Общефедеральные. Каналы фильмов. Все на русском. Имеются с зарубежными лентами. Спортивные. Как трансляции, так и кино данной тематики. Детские. Мультфильмы и передачи.' -pls='https://iptv-playlisty.ru/wp-content/uploads/m3u/newplay.m3u' -src='https://iptv-playlisty.ru/collection/besplatnyj-iptv-plejlist-formata-m3u/' - -[mus] -name='[iptv-playlisty.ru] Музыкальные IPTV каналы' -desc='Музыкальный плейлист наиболее популярных каналов на нашем телевидении.' -pls='https://iptv-playlisty.ru/wp-content/uploads/m3u/music.m3u' -src='https://iptv-playlisty.ru/janriptv/muzykalnye-iptv-kanaly-v-formate-m3u/' - -[det] -name='[iptv-playlisty.ru] Плейлист детских IPTV каналов' -desc = -pls='https://iptv-playlisty.ru/wp-content/uploads/m3u/deti.m3u' -src='https://iptv-playlisty.ru/janriptv/plejlist-detskix-iptv-kanalov-v-formate-m3u/' - -[det2] -name='[iptv-playlisty.ru] Плейлист IPTV Мультфильмов для детей и подростков' -desc = -pls='https://iptv-playlisty.ru/wp-content/uploads/m3u/multy.m3u' -src='https://iptv-playlisty.ru/janriptv/plejlist-iptv-multfilmov-dlya-detej-i-podrostkov/' - -[moto] -name='[iptv-playlisty.ru] Плейлист Авто и Мото каналов IPTV' -desc='В список были включены популярные не только у нас каналы авто тематики.' -pls='https://iptv-playlisty.ru/wp-content/uploads/m3u' -src='https://iptv-playlisty.ru/janriptv/plejlist-avto-i-moto-kanalov-iptv/' - -[poz] -name='[iptv-playlisty.ru] Плейлист IPTV образовательных каналов' -desc='В список вошло более десятка трансляций. Зритель сможет найти здесь передачи для разностороннего обучения и в целом просвещения.' -pls='https://iptv-playlisty.ru/wp-content/uploads/m3u/obrazovanie.m3u' -src='https://iptv-playlisty.ru/janriptv/plejlist-iptv-obrazovatelnyx-kanalov-m3u/' - -[poz2] -name='[iptv-playlisty.ru] Познавательные каналы IPTV плейлист' -desc='Наиболее подходящие трансляции, с выпусками интересных познавательных передач на любой вкус' -pls='https://iptv-playlisty.ru/wp-content/uploads/m3u/poznav.m3u' -src='https://iptv-playlisty.ru/janriptv/poznavatelnye-kanaly-iptv-plejlist-v-formate-m3u/' - -[moda] -name='[iptv-playlisty.ru] Плейлист IPTV модных телеканалов' -desc='Сюда вошли только топовые компании, на которых ежедневно рассказывают о последних тенденциях в одежде и дизайне.' -pls='https://iptv-playlisty.ru/wp-content/uploads/m3u/moda.m3u' -src='https://iptv-playlisty.ru/janriptv/plejlist-iptv-modnyx-telekanalov-m3u/' - -[fun] -name='[iptv-playlisty.ru] Плейлист IPTV каналов развлечений' -desc='Огромное количество развлекательных каналов' -pls='https://iptv-playlisty.ru/wp-content/uploads/m3u/razvlechenie.m3u' -src='https://iptv-playlisty.ru/janriptv/plejlist-iptv-kanalov-razvlechenij/' - -[hd] -name='[iptv-playlisty.ru] Плейлист IPTV каналов в HD формате' -desc='Плейлист доступных по настоящий момент HD каналов' -pls='https://iptv-playlisty.ru/wp-content/uploads/m3u/hd.m3u' -src='https://iptv-playlisty.ru/janriptv/plejlist-iptv-kanalov-v-hd-formate/' - -[nauka] -name='[iptv-playlisty.ru] Научные IPTV каналы в формате' -desc='Современные научные каналы: история, документальное кино, психология, культура...' -pls='https://iptv-playlisty.ru/wp-content/uploads/m3u/nauka.m3u' -src='https://iptv-playlisty.ru/janriptv/nauchnye-iptv-kanaly-v-formate-m3u/' - -[eda] -name='[iptv-playlisty.ru] IPTV плейлист кулинарных каналов' -desc='В список вошли наиболее интересные кулинарные каналы в формате M3U. Большинство каналов на русском языке' -pls='https://iptv-playlisty.ru/wp-content/uploads/m3u/eda.m3u' -src='https://iptv-playlisty.ru/janriptv/iptv-plejlist-kulinarnyx-kanalov/' - -[heal] -name='[iptv-playlisty.ru] IPTV плейлист каналов здоровья' -desc = -pls='https://iptv-playlisty.ru/wp-content/uploads/m3u/zdorov.m3u' -src='https://iptv-playlisty.ru/janriptv/iptv-plejlist-kanalov-zdorovya/' - -[sport] -name='[iptv-playlisty.ru] Плейлист IPTV спортивных каналов' -desc='В коллекции можно встретить: футбольные матчи, баскетбольные встречи, теннис, хоккей, автоспорт и даже гольф.' -pls='https://iptv-playlisty.ru/wp-content/uploads/m3u/sport.m3u' -src='https://iptv-playlisty.ru/janriptv/plejlist-iptv-sportivnyx-kanalov/' - -[strah] -name='[iptv-playlisty.ru] Бесплатный IPTV плейлист каналов ужасов' -desc = -pls='https://iptv-playlisty.ru/wp-content/uploads/m3u/Strah.m3u' -src='https://iptv-playlisty.ru/janriptv/besplatnyj-iptv-plejlist-kanalov-uzhasov/' - -[his] -name='[iptv-playlisty.ru] IPTV плейлист телеканала History' -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-playlisty.ru] IPTV плейлист телеканала Discovery' -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-playlisty.ru] IPTV плейлист канала National Geographic' -desc = -pls='https://iptv-playlisty.ru/wp-content/uploads/m3u/ngeografik.m3u' -src='https://iptv-playlisty.ru/iptv-kanaly/iptv-plejlist-kanala-national-geographic/' - -;################################################### -; telekarta -;################################################### - -[tru] -name='[telekarta] IPTV плейлист с ТВ каналами России 2024' -desc='Бесплатный и рабочий iptv плейлист c ТВ каналами России.' -pls='https://telekarta-tv.ru/wp-content/uploads/ru-all-1.m3u' -src='https://telekarta-tv.ru/iptv/playlist-russia/' - -[tsa] -name='[telekarta] IPTV плейлист самообновляемый 2024' -desc='Бесплатный и рабочий iptv плейлист с самообновлением с различными каналами.' -pls='https://telekarta-tv.ru/wp-content/uploads/sam.m3u' -src='https://telekarta-tv.ru/iptv/playlist-samoobnovlyaemy/' - -[tby] -name='[telekarta] Бесплатный и рабочий iptv плейлист с ТВ каналами Беларуси.' -desc='Бесплатный и рабочий iptv плейлист с ТВ каналами Беларуси. В нём вы найдёте основные каналы страны – БелРос, Витебск, Беларусь 4 и другие.' -pls='https://telekarta-tv.ru/wp-content/uploads/by.m3u' -src='https://telekarta-tv.ru/iptv/playlist-s-tv-kanalami-belarusi/' - -[tkz] -name='[telekarta] IPTV плейлист с ТВ каналами Казахстана' -desc='Бесплатный и рабочий iptv плейлист с популярными ТВ каналами Казахстана.' -pls='https://telekarta-tv.ru/wp-content/uploads/kaz.m3u' -src='https://telekarta-tv.ru/iptv/playlist-s-tv-kanalami-kazahstana/' - -[tuz] -name='[telekarta] IPTV плейлист с ТВ каналами Узбекистана' -desc='Бесплатный и рабочий iptv плейлист с ТВ каналами Узбекистана. В данном плейлисте вы найдёте более 20 каналов.' -pls='https://telekarta-tv.ru/wp-content/uploads/uz.m3u' -src='https://telekarta-tv.ru/iptv/playlist-s-tv-kanalami-uzbekistana/' - -[ttr] -name='[telekarta] IPTV плейлист с ТВ каналами Турции' -desc='В плейлисте вы найдёте десятки каналов Турции. Также в плейлисты есть каналы других стран, которые ведут трансляцию в Турции.' -pls='https://telekarta-tv.ru/wp-content/uploads/tr.m3u' -src='https://telekarta-tv.ru/iptv/playlist-s-kanalami-turkey/' - -[tm] -name='[telekarta] IPTV плейлист с музыкальными каналами 2024' -desc='Бесплатный и рабочий iptv плейлист музыкальных каналов с клипами и треками.' -pls='https://telekarta-tv.ru/wp-content/uploads/music_new.m3u' -src='https://telekarta-tv.ru/iptv/playlist-music/' - -[tw] -name='[telekarta] IPTV плейлист с веб-камерами мира 2024' -desc='IPTV плейлист с веб-камерами перенесет вас в любую точку земного шара за считанные секунды. В данном плейлисте вы найдёте камеры со всего мира.' -pls='https://telekarta-tv.ru/wp-content/uploads/webcam.m3u' -src='https://telekarta-tv.ru/iptv/playlist-s-veb-kamerami-mira/' - -[tl] -name='[telekarta] IPTV плейлист с познавательными ТВ каналами 2024' -desc='Бесплатный и рабочий iptv плейлист с познавательными ТВ каналами.' -pls='https://telekarta-tv.ru/wp-content/uploads/sci-all.m3u' -src='https://telekarta-tv.ru/iptv/playlist-s-poznavatelnymi-tv-kanalami/' - -[te] -name='[telekarta] IPTV плейлист с русскими каналами и фильмами от Edem TV 2024' -desc='Бесплатный и рабочий iptv плейлист с русскими каналами и фильмами от Edem TV.' -pls='https://telekarta-tv.ru/wp-content/uploads/playlist_edemtv.m3u' -src='https://telekarta-tv.ru/iptv/playlist-russian/' - -[tf] -name='[telekarta] IPTV плейлист с фильмами 2024' -desc='Бесплатный и рабочий iptv плейлист фильмов и мультфильмов. 1700+ фильмов' -pls='https://telekarta-tv.ru/wp-content/uploads/cinematic-2.m3u' -src='https://telekarta-tv.ru/iptv/playlist-films/' - -[tf2] -name='[telekarta] IPTV плейлист с фильмами и сериалами 2024' -desc='Для киноманов мы подготовили IPTV плейлист с фильмами и сериалами.' -pls='https://telekarta-tv.ru/wp-content/uploads/movies1700.m3u' -src='https://telekarta-tv.ru/iptv/playlisty-s-filmami-i-serialami/' - -[th] -name='[telekarta] IPTV плейлист каналов ужасов на 2024' -desc='Бесплатный и рабочий iptv плейлист с ТВ каналами ужасов. В данном плейлисте вы найдёте 3 канала.' -pls='https://telekarta-tv.ru/wp-content/uploads/strah.m3u' -src='https://telekarta-tv.ru/iptv/playlist-kanalov-uzhasov/' - -[tng] -name='[telekarta] IPTV плейлист National Geographic' -desc='В трансляции канала входит множество телепрограмм, которые понравятся большему числу пользователей.' -pls='https://telekarta-tv.ru/wp-content/uploads/ngeografik.m3u' -src='https://telekarta-tv.ru/iptv/playlist-national-geographic/' - -[tr] -name='[telekarta] IPTV плейлист с радио 2024' -desc='Бесплатный и рабочий iptv плейлист с радио. 600+ каналов' -pls='https://telekarta-tv.ru/wp-content/uploads/radio.m3u' -src='https://telekarta-tv.ru/iptv/playlist-s-radio/' - -[tk] -name='[telekarta] IPTV плейлист с детскими каналами 2024' -desc='Бесплатный и рабочий iptv плейлист детских каналов. 30 каналов, 600+ фильмов' -pls='https://telekarta-tv.ru/wp-content/uploads/kids-all.m3u' -src='https://telekarta-tv.ru/iptv/playlist-kids/' - -[t3d] -name='[telekarta] IPTV плейлист с фильмами 3D 2024' -desc='Бесплатный и рабочий iptv плейлист с фильмами 3D.' -pls='https://telekarta-tv.ru/wp-content/uploads/3d-film.m3u' -src='https://telekarta-tv.ru/iptv/playlist-3d/' - -[t60] -name='[telekarta] IPTV плейлист с фильмами 60fps 2024' -desc='Бесплатный и рабочий iptv плейлист с фильмами 60fps. 27 фильмов' -pls='https://telekarta-tv.ru/wp-content/uploads/filmy_60_fps-1.m3u' -src='https://telekarta-tv.ru/iptv/playlist-60fps/' - -[ts] -name='[telekarta] IPTV плейлист со спортивными каналами 2024' -desc='По требованию правообладателей в списке нет и не будет: Матч ТВ!, Наш Футбол, Матч! Кхл, Матч! Боец, Матч! Спорт плюс, Конный, Футбол 1, 2, 3 (Украина), Футбол 1, Футбол 2, Футбол 3.' -pls='https://telekarta-tv.ru/wp-content/uploads/sport-all.m3u' -src='https://telekarta-tv.ru/iptv/playlist-sport/' - -[tx] -name='[telekarta] IPTV плейлист с каналами и фильмами для взрослых 2024' -desc='Бесплатный и рабочий iptv плейлист с каналами и фильмами для взрослых. 50 каналов, 400 фильмов' -pls='https://telekarta-tv.ru/wp-content/uploads/18_xxx.m3u' -src='https://telekarta-tv.ru/iptv/playlist-xxx/' - -;################################################### -; tviptv -;################################################### - -[25ru] -name='[tviptv] Российские федеральные' -desc='' -pls='https://tviptv.ru/upl/25rus238zl.m3u' -src='' - -[25kino] -name='[tviptv] Российские киноканалы' -desc='' -pls='https://tviptv.ru/upl/25kino8ds66.m3u' -src='' - -[25kids] -name='[tviptv] Российские детские' -desc='' -pls='https://tviptv.ru/upl/25kids18h4v.m3u' -src='' - -[25pozn] -name='[tviptv] Российские познавательные' -desc='' -pls='https://tviptv.ru/upl/25pozn0877.m3u' -src='' - -[25sp] -name='[tviptv] Спортивные' -desc='' -pls='https://tviptv.ru/upl/25sport56kr.m3u' -src='' - -[25mus] -name='[tviptv] Музыкальные' -desc='' -pls='https://tviptv.ru/upl/25music124x6.m3u' -src='' - -;################################################### -; Karnei4 -;################################################### - -[kk1] -name='[Karnei4] Films-FREE' -desc='' -pls='https://raw.githubusercontent.com/Karnei4/Karnei4.github.io/refs/heads/master/Films-FREE-Karnei4.m3u' -src='https://github.com/Karnei4/Karnei4.github.io' - -[kk2] -name='[Karnei4] Zabava-FREE' -desc='' -pls='https://raw.githubusercontent.com/Karnei4/Karnei4.github.io/refs/heads/master/s/Mediacentr/Zabava-FREE-Karnei4.m3u' -src='https://github.com/Karnei4/Karnei4.github.io' - -[kr] -name='[Karnei4] Россия' -desc='Каналов относительно немного, но очень много часовых поясов' -pls='https://dl.dropbox.com/scl/fi/3jaul2v9tbynfnp/PlayList-Karnei4-M3U-1763.m3u' -src='' - -;################################################### -; blackbird -;################################################### - -[bb1] -name='[blackbird] IPTVPlay' -desc='' -pls='https://raw.githubusercontent.com/blackbirdstudiorus/IPTVPlay/refs/heads/main/IPTVPlay.m3u' -src='https://github.com/blackbirdstudiorus/IPTVPlay' - -[bb2] -name='[blackbird] LoganetXAll' -desc='' -pls='https://raw.githubusercontent.com/blackbirdstudiorus/LoganetXIPTV/refs/heads/main/LoganetXAll.m3u' -src='https://github.com/blackbirdstudiorus/LoganetXIPTV' - -[bb3] -name='[blackbird] LoganetXStrawberry' -desc='' -pls='https://raw.githubusercontent.com/blackbirdstudiorus/LoganetXIPTV/refs/heads/main/LoganetXStrawberry.m3u' -src='https://github.com/blackbirdstudiorus/LoganetXIPTV' - -;################################################### -; suzukantv -;################################################### - -[sr] -name='[suzukantv] Радио' -desc='' -pls='https://raw.githubusercontent.com/suzukantv/IPTV/refs/heads/main/SuzuSCloudTvNET.m3u' -src='https://github.com/suzukantv/IPTV' - -[st] -name='[suzukantv] TorrentTV' -desc='' -pls='https://raw.githubusercontent.com/suzukantv/IPTV/refs/heads/main/TorrentTV.m3u' -src='https://github.com/suzukantv/IPTV' - -[swz] -name='[suzukantv] WinkZabava' -desc='' -pls='https://raw.githubusercontent.com/suzukantv/IPTV/refs/heads/main/WinkZabava.m3u' -src='https://github.com/suzukantv/IPTV' - -;################################################### -; dmi3y-tv -;################################################### - -[d] -name='[dmi3y-tv] D-TV6' -pls='http://dmi3y-tv.ru/iptv/D-TV6.m3u' -desc='Телеканал «D-TV6» - Интерактивный российский федеральный телеканал. Cпециализирующийся на сериалах, художественных и документальных фильмах познавательного характера.' -src='http://dmi3y-tv.ru/d-tv/ch_d-tv6/' - -[dh] -name='[dmi3y-tv] Dmitry-tv HD' -pls='http://dmi3y-tv.ru/iptv/Dmitry-tvHD.m3u' -desc='Телеканал «Dmitry-tv HD» - Интерактивный российский федеральный телеканал. Только русские музыкальные видеоклипы в Full HD качестве.' -src='http://dmi3y-tv.ru/d-tv/ch_dmitry-tv/' - -[d1] -name='[dmi3y-tv] Playlist-01' -pls='http://dmi3y-tv.ru/iptv/Playlist-01.m3u' -desc='300+ каналов (гибрид, Беларусь, СМОТРИМ)' -src='http://dmi3y-tv.ru/d-tv/playlist/m3u/' - -[dg] -name='[dmi3y-tv] Playlist-01 (GTMedia)' -pls='http://dmitry-tv.ddns.net/iptv/freesat/gtmedia/Playlist-01/custom_url.m3u' -desc='300+ каналов (гибрид, Беларусь, СМОТРИМ)' -src='http://dmi3y-tv.ru/d-tv/playlist/freesat/' - -[d1m1] -name='[dmi3y-tv] Playlist-01 Mobile 360p' -pls='http://dmi3y-tv.ru/iptv/Playlist-01_360p.m3u' -desc='14 каналов (гибрид)' -src='http://dmi3y-tv.ru/d-tv/playlist/mobile/' - -[d1m2] -name='[dmi3y-tv] Playlist-01 Mobile 480p' -pls='http://dmi3y-tv.ru/iptv/Playlist-01_480p.m3u' -desc='14 каналов (гибрид)' -src='http://dmi3y-tv.ru/d-tv/playlist/mobile/' - -[d4] -name='[dmi3y-tv] Playlist-04' -pls='http://dmi3y-tv.ru/iptv/Playlist-04.m3u' -desc='4 канала (Беларусь)' -src='http://dmi3y-tv.ru/d-tv/playlist/m3u/' - -[d5] -name='[dmi3y-tv] Playlist-05' -pls='http://dmi3y-tv.ru/iptv/Playlist-05.m3u' -desc='650+ каналов (гибрид, Беларусь, СМОТРИМ, ZABAVA)' -src='http://dmi3y-tv.ru/d-tv/playlist/freesat/' - -[d5m1] -name='[dmi3y-tv] Playlist-05 Mobile 360p' -pls='http://dmi3y-tv.ru/iptv/Playlist-05_360p.m3u' -desc='157 каналов (ZABAVA)' -src='http://dmi3y-tv.ru/d-tv/playlist/mobile/' - -[d5m2] -name='[dmi3y-tv] Playlist-05 Mobile 480p' -pls='http://dmi3y-tv.ru/iptv/Playlist-05_480p.m3u' -desc='157 каналов (ZABAVA)' -src='http://dmi3y-tv.ru/d-tv/playlist/mobile/' - -[d5g] -name='[dmi3y-tv] Playlist-05 (GTMedia)' -pls='http://dmitry-tv.ddns.net/iptv/freesat/gtmedia/Playlist-05/custom_url.m3u' -desc='650+ каналов (гибрид, Беларусь, СМОТРИМ, ZABAVA)' -src='http://dmi3y-tv.ru/d-tv/playlist/freesat/' - -[ds] -name='[dmi3y-tv] СМОТРИМ' -pls='http://dmi3y-tv.ru/iptv/smotrim.m3u' -desc='150+ каналов (СМОТРИМ)' -src='http://dmi3y-tv.ru/d-tv/playlist/m3u/' - -[dt] -name='[dmi3y-tv] Dmitry-tv' -pls='http://dmi3y-tv.ru/iptv/Dmitry-tv.m3u' -desc='390+ каналов (гибрид, Беларусь, СМОТРИМ, Яндекс ТВ)' -src='http://dmi3y-tv.ru/d-tv/playlist/m3u/' - -[dd] -name='[dmi3y-tv] ВСЕ КАНАЛЫ' -pls='http://dmi3y-tv.ru/iptv/d-tv.m3u' -desc='гибрид, Беларусь, СМОТРИМ, ZABAVA, Яндекс ТВ' -src='http://dmi3y-tv.ru/d-tv/playlist/freesat/' - -[dm] -name='[dmi3y-tv] ВСЕ МУЛЬТ/ФИЛЬМЫ' -pls='http://dmi3y-tv.ru/iptv/film_d-tv.m3u' -desc='420+ мульт/фильмов' -src='http://dmi3y-tv.ru/d-tv/playlist/freesat/' - -[dr] -name='[dmi3y-tv] AM Radio' -pls='http://dmi3y-tv.ru/iptv/AMRADIO.m3u' -desc='69 радиоканалов' -src='http://dmi3y-tv.ru/d-tv/playlist/fm/' - -[de] -name='[dmi3y-tv] "ЦИФРОВОЕ ЭФИРНОЕ IPTV" => "Android"' -pls='http://dmi3y-tv.ru/iptv/DVB-T2/rtrs' -desc='Все часовые пояса' -src='http://dmi3y-tv.ru/d-tv/playlist/c_iptv/' - -[de1] -name='[dmi3y-tv] "ЦИФРОВОЕ ЭФИРНОЕ IPTV" => "Android"' -pls='http://dmi3y-tv.ru/iptv/DVB-T2/CH-1.m3u' -desc='Калининград МСК-1' -src='http://dmi3y-tv.ru/d-tv/playlist/c_iptv/' - -[de2] -name='[dmi3y-tv] "ЦИФРОВОЕ ЭФИРНОЕ IPTV" => "Android"' -pls='http://dmi3y-tv.ru/iptv/DVB-T2/CH_0.m3u' -desc='МСК' -src='http://dmi3y-tv.ru/d-tv/playlist/c_iptv/' - -[de3] -name='[dmi3y-tv] "ЦИФРОВОЕ ЭФИРНОЕ IPTV" => "Android"' -pls='http://dmi3y-tv.ru/iptv/DVB-T2/CH_3.m3u' -desc='Омск МСК+3' -src='http://dmi3y-tv.ru/d-tv/playlist/c_iptv/' - -[de4] -name='[dmi3y-tv] "ЦИФРОВОЕ ЭФИРНОЕ IPTV" => "Android"' -pls='http://dmi3y-tv.ru/iptv/DVB-T2/CH_4.m3u' -desc='Красноярск МСК+4' -src='http://dmi3y-tv.ru/d-tv/playlist/c_iptv/' - -[de5] -name='[dmi3y-tv] "ЦИФРОВОЕ ЭФИРНОЕ IPTV" => "Android"' -pls='http://dmi3y-tv.ru/iptv/DVB-T2/CH_5.m3u' -desc='Иркутск МСК+5' -src='http://dmi3y-tv.ru/d-tv/playlist/c_iptv/' - -[de7] -name='[dmi3y-tv] "ЦИФРОВОЕ ЭФИРНОЕ IPTV" => "Android"' -pls='http://dmi3y-tv.ru/iptv/DVB-T2/CH_7.m3u' -desc='Владивосток МСК+7' -src='http://dmi3y-tv.ru/d-tv/playlist/c_iptv/' - -[de8] -name='[dmi3y-tv] "ЦИФРОВОЕ ЭФИРНОЕ IPTV" => "Android"' -pls='http://dmi3y-tv.ru/iptv/DVB-T2/CH_8.m3u' -desc='Сахалин МСК+8' -src='http://dmi3y-tv.ru/d-tv/playlist/c_iptv/' - -[de9] -name='[dmi3y-tv] "ЦИФРОВОЕ ЭФИРНОЕ IPTV" => "Android"' -pls='http://dmi3y-tv.ru/iptv/DVB-T2/CH_9.m3u' -desc='Камчатка МСК+9' -src='http://dmi3y-tv.ru/d-tv/playlist/c_iptv/' - -[dz] -name='[dmi3y-tv] ZABAVA' -pls='http://dmi3y-tv.ru/iptv/ZABAVA.m3u' -desc='350+ каналов (ZABAVA)' -src='http://dmi3y-tv.ru/d-tv/playlist/m3u/' - -[dzg] -name='[dmi3y-tv] ZABAVA (GTMedia)' -pls='http://dmitry-tv.ddns.net/iptv/freesat/gtmedia/ZABAVA/custom_url.m3u' -desc='350+ каналов (ZABAVA)' -src='http://dmi3y-tv.ru/d-tv/playlist/freesat/' - -[dza] -name='[dmi3y-tv] Региональные: Абакан (Республика Хакасия)/ABAKAN_KHAK.m3u' -pls='http://dmi3y-tv.ru/iptv/region/ZABAVA_ABAKAN_KHAK.m3u' -desc='' -src='http://dmi3y-tv.ru/d-tv/playlist/zabava/region/1' - -[dza2] -name='[dmi3y-tv] Региональные: Абакан/ABAKAN.m3u' -pls='http://dmi3y-tv.ru/iptv/region/ZABAVA_ABAKAN.m3u' -desc='' -src='http://dmi3y-tv.ru/d-tv/playlist/zabava/region/1' - -[dzal] -name='[dmi3y-tv] Региональные: Алтайский край/ZABAVA_ALTAY.m3u' -pls='http://dmi3y-tv.ru/iptv/region/ZABAVA_ALTAY.m3u' -desc='' -src='http://dmi3y-tv.ru/d-tv/playlist/zabava/region/1' - -[dzar] -name='[dmi3y-tv] Региональные: Архангельск/ARKH.m3u' -pls='http://dmi3y-tv.ru/iptv/region/ZABAVA_ARKH.m3u' -desc='' -src='http://dmi3y-tv.ru/d-tv/playlist/zabava/region/1' - -[dzar2] -name='[dmi3y-tv] Региональные: Архангельская область/ARKH_OBL.m3u' -pls='http://dmi3y-tv.ru/iptv/region/ZABAVA_ARKH_OBL.m3u' -desc='' -src='http://dmi3y-tv.ru/d-tv/playlist/zabava/region/1' - -[dzas] -name='[dmi3y-tv] Региональные: Астраханская область/ASTRH_OBL.m3u' -pls='http://dmi3y-tv.ru/iptv/region/ZABAVA_ASTRH_OBL.m3u' -desc='' -src='http://dmi3y-tv.ru/d-tv/playlist/zabava/region/1' - -[dzas2] -name='[dmi3y-tv] Региональные: Астрахань/ASTRH.m3u' -pls='http://dmi3y-tv.ru/iptv/region/ZABAVA_ASTRH.m3u' -desc='' -src='http://dmi3y-tv.ru/d-tv/playlist/zabava/region/1' - -[dzac] -name='[dmi3y-tv] Региональные: Ачинск/ACHINSK.m3u' -pls='http://dmi3y-tv.ru/iptv/region/ZABAVA_ACHINSK.m3u' -desc='' -src='http://dmi3y-tv.ru/d-tv/playlist/zabava/region/1' - -[dzb] -name='[dmi3y-tv] Региональные: Барнаул/BUL.m3u' -pls='http://dmi3y-tv.ru/iptv/region/ZABAVA_BUL.m3u' -desc='' -src='http://dmi3y-tv.ru/d-tv/playlist/zabava/region/1' - -[dzbe] -name='[dmi3y-tv] Региональные: Белгород/BELG.m3u' -pls='http://dmi3y-tv.ru/iptv/region/ZABAVA_BELG.m3u' -desc='' -src='http://dmi3y-tv.ru/d-tv/playlist/zabava/region/1' - -[dzbo] -name='[dmi3y-tv] Региональные: Белгородская область/BELG_OBL.m3u' -pls='http://dmi3y-tv.ru/iptv/region/ZABAVA_BELG_OBL.m3u' -desc='' -src='http://dmi3y-tv.ru/d-tv/playlist/zabava/region/1' - -[dzbi] -name='[dmi3y-tv] Региональные: Бийск/BIYSK.m3u' -pls='http://dmi3y-tv.ru/iptv/region/ZABAVA_BIYSK.m3u' -desc='' -src='http://dmi3y-tv.ru/d-tv/playlist/zabava/region/1' - -[dzd] -name='[dmi3y-tv] Региональные: Биробиджан/DV_BIR.m3u' -pls='http://dmi3y-tv.ru/iptv/region/ZABAVA_DV_BIR.m3u' -desc='' -src='http://dmi3y-tv.ru/d-tv/playlist/zabava/region/1' - -[dzbl] -name='[dmi3y-tv] Региональные: Благовещенск/DV_BLAG.m3u' -pls='http://dmi3y-tv.ru/iptv/region/ZABAVA_DV_BLAG.m3u' -desc='' -src='http://dmi3y-tv.ru/d-tv/playlist/zabava/region/1' - -[dzbr] -name='[dmi3y-tv] Региональные: Братск/BRATSK.m3u' -pls='http://dmi3y-tv.ru/iptv/region/ZABAVA_BRATSK.m3u' -desc='' -src='http://dmi3y-tv.ru/d-tv/playlist/zabava/region/1' - -[dzby] -name='[dmi3y-tv] Региональные: Брянск/BRYANSK.m3u' -pls='http://dmi3y-tv.ru/iptv/region/ZABAVA_BRYANSK.m3u' -desc='' -src='http://dmi3y-tv.ru/d-tv/playlist/zabava/region/1' - -[dzby2] -name='[dmi3y-tv] Региональные: Брянская область/BRYANSK_OBL.m3u' -pls='http://dmi3y-tv.ru/iptv/region/ZABAVA_BRYANSK_OBL.m3u' -desc='' -src='http://dmi3y-tv.ru/d-tv/playlist/zabava/region/1' - -[dzbu] -name='[dmi3y-tv] Региональные: Бурятия/BURAT.m3u' -pls='http://dmi3y-tv.ru/iptv/region/ZABAVA_BURAT.m3u' -desc='' -src='http://dmi3y-tv.ru/d-tv/playlist/zabava/region/1' - -[dzvn] -name='[dmi3y-tv] Региональные: Великий Новгород/NOVG.m3u' -pls='http://dmi3y-tv.ru/iptv/region/ZABAVA_NOVG.m3u' -desc='' -src='http://dmi3y-tv.ru/d-tv/playlist/zabava/region/1' - -[dzv] -name='[dmi3y-tv] Региональные: Владивосток/DV_VLD.m3u' -pls='http://dmi3y-tv.ru/iptv/region/ZABAVA_DV_VLD.m3u' -desc='' -src='http://dmi3y-tv.ru/d-tv/playlist/zabava/region/1' - -[dzvk] -name='[dmi3y-tv] Региональные: Владикавказ/VKAZ.m3u' -pls='http://dmi3y-tv.ru/iptv/region/ZABAVA_VKAZ.m3u' -desc='' -src='http://dmi3y-tv.ru/d-tv/playlist/zabava/region/1' - -[dzvl] -name='[dmi3y-tv] Региональные: Владимир/VLAD.m3u' -pls='http://dmi3y-tv.ru/iptv/region/ZABAVA_VLAD.m3u' -desc='' -src='http://dmi3y-tv.ru/d-tv/playlist/zabava/region/1' - -[dzvo] -name='[dmi3y-tv] Региональные: Владимирская область/VLAD_OBL.m3u' -pls='http://dmi3y-tv.ru/iptv/region/ZABAVA_VLAD_OBL.m3u' -desc='' -src='http://dmi3y-tv.ru/d-tv/playlist/zabava/region/1' - -[dzvg] -name='[dmi3y-tv] Региональные: Волгоград/VGRAD.m3u' -pls='http://dmi3y-tv.ru/iptv/region/ZABAVA_VGRAD.m3u' -desc='' -src='http://dmi3y-tv.ru/d-tv/playlist/zabava/region/1' - -[dzvgo] -name='[dmi3y-tv] Региональные: Волгоградская область/VGRAD_OBL.m3u' -pls='http://dmi3y-tv.ru/iptv/region/ZABAVA_VGRAD_OBL.m3u' -desc='' -src='http://dmi3y-tv.ru/d-tv/playlist/zabava/region/1' - -[dzvd] -name='[dmi3y-tv] Региональные: Вологда/VOL.m3u' -pls='http://dmi3y-tv.ru/iptv/region/ZABAVA_VOL.m3u' -desc='' -src='http://dmi3y-tv.ru/d-tv/playlist/zabava/region/1' - -[dzvdo] -name='[dmi3y-tv] Региональные: Вологодская область/VOL_OBL.m3u' -pls='http://dmi3y-tv.ru/iptv/region/ZABAVA_VOL_OBL.m3u' -desc='' -src='http://dmi3y-tv.ru/d-tv/playlist/zabava/region/1' - -[dzvr] -name='[dmi3y-tv] Региональные: Воронеж/VRZH.m3u' -pls='http://dmi3y-tv.ru/iptv/region/ZABAVA_VRZH.m3u' -desc='' -src='http://dmi3y-tv.ru/d-tv/playlist/zabava/region/1' - -[dzvro] -name='[dmi3y-tv] Региональные: Воронежская область/VRZH_OBL.m3u' -pls='http://dmi3y-tv.ru/iptv/region/ZABAVA_VRZH_OBL.m3u' -desc='' -src='http://dmi3y-tv.ru/d-tv/playlist/zabava/region/1' - -[dzga] -name='[dmi3y-tv] Региональные: Горно-Алтайск/GALT.m3u' -pls='http://dmi3y-tv.ru/iptv/region/ZABAVA_GALT.m3u' -desc='' -src='http://dmi3y-tv.ru/d-tv/playlist/zabava/region/1' - -[dze] -name='[dmi3y-tv] Региональные: Екатеринбург/EKT.m3u' -pls='http://dmi3y-tv.ru/iptv/region/ZABAVA_EKT.m3u' -desc='' -src='http://dmi3y-tv.ru/d-tv/playlist/zabava/region/1' - -[dzi] -name='[dmi3y-tv] Региональные: Иваново/IVAN.m3u' -pls='http://dmi3y-tv.ru/iptv/region/ZABAVA_IVAN.m3u' -desc='' -src='http://dmi3y-tv.ru/d-tv/playlist/zabava/region/1' - -[dziz] -name='[dmi3y-tv] Региональные: Ижевск/IZHSK.m3u' -pls='http://dmi3y-tv.ru/iptv/region/ZABAVA_IZHSK.m3u' -desc='' -src='http://dmi3y-tv.ru/d-tv/playlist/zabava/region/1' - -[dzig] -name='[dmi3y-tv] Региональные: Ингушетия/ING.m3u' -pls='http://dmi3y-tv.ru/iptv/region/ZABAVA_ING.m3u' -desc='' -src='http://dmi3y-tv.ru/d-tv/playlist/zabava/region/1' - -[dzir] -name='[dmi3y-tv] Региональные: Иркутск/IRK.m3u' -pls='http://dmi3y-tv.ru/iptv/region/ZABAVA_IRK.m3u' -desc='' -src='http://dmi3y-tv.ru/d-tv/playlist/zabava/region/1' - -[dzio] -name='[dmi3y-tv] Региональные: Иркутская область/IRK_OBL.m3u' -pls='http://dmi3y-tv.ru/iptv/region/ZABAVA_IRK_OBL.m3u' -desc='' -src='http://dmi3y-tv.ru/d-tv/playlist/zabava/region/1' - -[dzy] -name='[dmi3y-tv] Региональные: Йошкар-Ола/YOLA.m3u' -pls='http://dmi3y-tv.ru/iptv/region/ZABAVA_YOLA.m3u' -desc='' -src='http://dmi3y-tv.ru/d-tv/playlist/zabava/region/1' - -[dzk] -name='[dmi3y-tv] Региональные: Казань/KZN.m3u' -pls='http://dmi3y-tv.ru/iptv/region/ZABAVA_KZN.m3u' -desc='' -src='http://dmi3y-tv.ru/d-tv/playlist/zabava/region/1' - -[dzkl] -name='[dmi3y-tv] Региональные: Калининград/KLGR.m3u' -pls='http://dmi3y-tv.ru/iptv/region/ZABAVA_KLGR.m3u' -desc='' -src='http://dmi3y-tv.ru/d-tv/playlist/zabava/region/1' - -[dzkg] -name='[dmi3y-tv] Региональные: Калуга/KALUGA.m3u' -pls='http://dmi3y-tv.ru/iptv/region/ZABAVA_KALUGA.m3u' -desc='' -src='http://dmi3y-tv.ru/d-tv/playlist/zabava/region/1' - -[dzka] -name='[dmi3y-tv] Региональные: Камчатка/DV_KAM.m3u' -pls='http://dmi3y-tv.ru/iptv/region/ZABAVA_DV_KAM.m3u' -desc='' -src='http://dmi3y-tv.ru/d-tv/playlist/zabava/region/1' - -[dzke] -name='[dmi3y-tv] Региональные: Кемерово/KEM.m3u' -pls='http://dmi3y-tv.ru/iptv/region/ZABAVA_KEM.m3u' -desc='' -src='http://dmi3y-tv.ru/d-tv/playlist/zabava/region/1' - -[dzkeo] -name='[dmi3y-tv] Региональные: Кемеровская область/KEM_OBL.m3u' -pls='http://dmi3y-tv.ru/iptv/region/ZABAVA_KEM_OBL.m3u' -desc='' -src='http://dmi3y-tv.ru/d-tv/playlist/zabava/region/1' - -[dzki] -name='[dmi3y-tv] Региональные: Киров/KIROV.m3u' -pls='http://dmi3y-tv.ru/iptv/region/ZABAVA_KIROV.m3u' -desc='' -src='http://dmi3y-tv.ru/d-tv/playlist/zabava/region/1' - -[dzko] -name='[dmi3y-tv] Региональные: Коми/KOMI.m3u' -pls='http://dmi3y-tv.ru/iptv/region/ZABAVA_KOMI.m3u' -desc='' -src='http://dmi3y-tv.ru/d-tv/playlist/zabava/region/1' - -[dzks] -name='[dmi3y-tv] Региональные: Кострома/KOSTROMA.m3u' -pls='http://dmi3y-tv.ru/iptv/region/ZABAVA_KOSTROMA.m3u' -desc='' -src='http://dmi3y-tv.ru/d-tv/playlist/zabava/region/1' - -[dzkd] -name='[dmi3y-tv] Региональные: Краснодар/KRDAR.m3u' -pls='http://dmi3y-tv.ru/iptv/region/ZABAVA_KRDAR.m3u' -desc='' -src='http://dmi3y-tv.ru/d-tv/playlist/zabava/region/1' - -[dzkdk] -name='[dmi3y-tv] Региональные: Краснодарский край/KRDAR_KRAI.m3u' -pls='http://dmi3y-tv.ru/iptv/region/ZABAVA_KRDAR_KRAI.m3u' -desc='' -src='http://dmi3y-tv.ru/d-tv/playlist/zabava/region/1' - -[dzkk] -name='[dmi3y-tv] Региональные: Красноярск/KRSK.m3u' -pls='http://dmi3y-tv.ru/iptv/region/ZABAVA_KRSK.m3u' -desc='' -src='http://dmi3y-tv.ru/d-tv/playlist/zabava/region/1' - -[dzkkk] -name='[dmi3y-tv] Региональные: Красноярский край/KRSK_KRAI.m3u' -pls='http://dmi3y-tv.ru/iptv/region/ZABAVA_KRSK_KRAI.m3u' -desc='' -src='http://dmi3y-tv.ru/d-tv/playlist/zabava/region/1' - -[dzku] -name='[dmi3y-tv] Региональные: Курган/KRG.m3u' -pls='http://dmi3y-tv.ru/iptv/region/ZABAVA_KRG.m3u' -desc='' -src='http://dmi3y-tv.ru/d-tv/playlist/zabava/region/1' - -[dzkur] -name='[dmi3y-tv] Региональные: Курск/KURSK.m3u' -pls='http://dmi3y-tv.ru/iptv/region/ZABAVA_KURSK.m3u' -desc='' -src='http://dmi3y-tv.ru/d-tv/playlist/zabava/region/1' - -[dzkoro] -name='[dmi3y-tv] Региональные: Курская область/KURSK_OBL.m3u' -pls='http://dmi3y-tv.ru/iptv/region/ZABAVA_KURSK_OBL.m3u' -desc='' -src='http://dmi3y-tv.ru/d-tv/playlist/zabava/region/1' - -[dzt] -name='[dmi3y-tv] Региональные: Кызыл/TUVA.m3u' -pls='http://dmi3y-tv.ru/iptv/region/ZABAVA_TUVA.m3u' -desc='' -src='http://dmi3y-tv.ru/d-tv/playlist/zabava/region/1' - -[dzl] -name='[dmi3y-tv] Региональные: Липецк/LIPETSK.m3u' -pls='http://dmi3y-tv.ru/iptv/region/ZABAVA_LIPETSK.m3u' -desc='' -src='http://dmi3y-tv.ru/d-tv/playlist/zabava/region/1' - -[dzm] -name='[dmi3y-tv] Региональные: Магадан/DV_MGDN.m3u' -pls='http://dmi3y-tv.ru/iptv/region/ZABAVA_DV_MGDN.m3u' -desc='' -src='http://dmi3y-tv.ru/d-tv/playlist/zabava/region/1' - -[dzma] -name='[dmi3y-tv] Региональные: Майкоп/MAYK.m3u' -pls='http://dmi3y-tv.ru/iptv/region/ZABAVA_MAYK.m3u' -desc='' -src='http://dmi3y-tv.ru/d-tv/playlist/zabava/region/1' - -[dzmh] -name='[dmi3y-tv] Региональные: Махачкала/MAHACHK.m3u' -pls='http://dmi3y-tv.ru/iptv/region/ZABAVA_MAHACHK.m3u' -desc='' -src='http://dmi3y-tv.ru/d-tv/playlist/zabava/region/1' - -[dzmo] -name='[dmi3y-tv] Региональные: Москва/MOS.m3u' -pls='http://dmi3y-tv.ru/iptv/region/ZABAVA_MOS.m3u' -desc='' -src='http://dmi3y-tv.ru/d-tv/playlist/zabava/region/1' - -[dzmu] -name='[dmi3y-tv] Региональные: Мурманск/MURM.m3u' -pls='http://dmi3y-tv.ru/iptv/region/ZABAVA_MURM.m3u' -desc='' -src='http://dmi3y-tv.ru/d-tv/playlist/zabava/region/1' - -[dzn] -name='[dmi3y-tv] Региональные: Нальчик/NALCH.m3u' -pls='http://dmi3y-tv.ru/iptv/region/ZABAVA_NALCH.m3u' -desc='' -src='http://dmi3y-tv.ru/d-tv/playlist/zabava/region/1' - -[dznm] -name='[dmi3y-tv] Региональные: Нарьян-Мар/NAO.m3u' -pls='http://dmi3y-tv.ru/iptv/region/ZABAVA_NAO.m3u' -desc='' -src='http://dmi3y-tv.ru/d-tv/playlist/zabava/region/1' - -[dznn] -name='[dmi3y-tv] Региональные: Нижний Новгород/NN.m3u' -pls='http://dmi3y-tv.ru/iptv/region/ZABAVA_NN.m3u' -desc='' -src='http://dmi3y-tv.ru/d-tv/playlist/zabava/region/1' - -[dznt] -name='[dmi3y-tv] Региональные: Нижний Тагил/NTAG.m3u' -pls='http://dmi3y-tv.ru/iptv/region/ZABAVA_NTAG.m3u' -desc='' -src='http://dmi3y-tv.ru/d-tv/playlist/zabava/region/1' - -[dzno] -name='[dmi3y-tv] Региональные: Новгородская область/NOVG_OBL.m3u' -pls='http://dmi3y-tv.ru/iptv/region/ZABAVA_NOVG_OBL.m3u' -desc='' -src='http://dmi3y-tv.ru/d-tv/playlist/zabava/region/1' - -[dznz] -name='[dmi3y-tv] Региональные: Новозыбков/NZB.m3u' -pls='http://dmi3y-tv.ru/iptv/region/ZABAVA_NZB.m3u' -desc='' -src='http://dmi3y-tv.ru/d-tv/playlist/zabava/region/1' - -[dznk] -name='[dmi3y-tv] Региональные: Новокузнецк/NVKZ.m3u' -pls='http://dmi3y-tv.ru/iptv/region/ZABAVA_NVKZ.m3u' -desc='' -src='http://dmi3y-tv.ru/d-tv/playlist/zabava/region/1' - -[dznr] -name='[dmi3y-tv] Региональные: Новороссийск/NVR.m3u' -pls='http://dmi3y-tv.ru/iptv/region/ZABAVA_NVR.m3u' -desc='' -src='http://dmi3y-tv.ru/d-tv/playlist/zabava/region/1' - -[dzns] -name='[dmi3y-tv] Региональные: Новосибирск/NSK.m3u' -pls='http://dmi3y-tv.ru/iptv/region/ZABAVA_NSK.m3u' -desc='' -src='http://dmi3y-tv.ru/d-tv/playlist/zabava/region/1' - -[dznso] -name='[dmi3y-tv] Региональные: Новосибирская область/NSK_OBL.m3u' -pls='http://dmi3y-tv.ru/iptv/region/ZABAVA_NSK_OBL.m3u' -desc='' -src='http://dmi3y-tv.ru/d-tv/playlist/zabava/region/1' - -[dznb] -name='[dmi3y-tv] Региональные: Ноябрьск/NBK.m3u' -pls='http://dmi3y-tv.ru/iptv/region/ZABAVA_NBK.m3u' -desc='' -src='http://dmi3y-tv.ru/d-tv/playlist/zabava/region/1' - -[dzo] -name='[dmi3y-tv] Региональные: Обнинск/OBN.m3u' -pls='http://dmi3y-tv.ru/iptv/region/ZABAVA_OBN.m3u' -desc='' -src='http://dmi3y-tv.ru/d-tv/playlist/zabava/region/1' - -[dzom] -name='[dmi3y-tv] Региональные: Омск/OMSK.m3u' -pls='http://dmi3y-tv.ru/iptv/region/ZABAVA_OMSK.m3u' -desc='' -src='http://dmi3y-tv.ru/d-tv/playlist/zabava/region/1' - -[dzomo] -name='[dmi3y-tv] Региональные: Омская область/OMSK_OBL.m3u' -pls='http://dmi3y-tv.ru/iptv/region/ZABAVA_OMSK_OBL.m3u' -desc='' -src='http://dmi3y-tv.ru/d-tv/playlist/zabava/region/1' - -[dzor] -name='[dmi3y-tv] Региональные: Орел/ORL.m3u' -pls='http://dmi3y-tv.ru/iptv/region/ZABAVA_ORL.m3u' -desc='' -src='http://dmi3y-tv.ru/d-tv/playlist/zabava/region/1' - -[dzob] -name='[dmi3y-tv] Региональные: Оренбург/ORB.m3u' -pls='http://dmi3y-tv.ru/iptv/region/ZABAVA_ORB.m3u' -desc='' -src='http://dmi3y-tv.ru/d-tv/playlist/zabava/region/1' - -[dzoro] -name='[dmi3y-tv] Региональные: Орловская область/ORL_OBL.m3u' -pls='http://dmi3y-tv.ru/iptv/region/ZABAVA_ORL_OBL.m3u' -desc='' -src='http://dmi3y-tv.ru/d-tv/playlist/zabava/region/1' - -[dzos] -name='[dmi3y-tv] Региональные: Орск/ORSK.m3u' -pls='http://dmi3y-tv.ru/iptv/region/ZABAVA_ORSK.m3u' -desc='' -src='http://dmi3y-tv.ru/d-tv/playlist/zabava/region/1' - -[dzpe] -name='[dmi3y-tv] Региональные: Пенза/PENZA.m3u' -pls='http://dmi3y-tv.ru/iptv/region/ZABAVA_PENZA.m3u' -desc='' -src='http://dmi3y-tv.ru/d-tv/playlist/zabava/region/1' - -[dzpr] -name='[dmi3y-tv] Региональные: Пермь/PERM.m3u' -pls='http://dmi3y-tv.ru/iptv/region/ZABAVA_PERM.m3u' -desc='' -src='http://dmi3y-tv.ru/d-tv/playlist/zabava/region/1' - -[dzp] -name='[dmi3y-tv] Региональные: Петрозаводск (Республика Карелия)/PZAV_RK.m3u' -pls='http://dmi3y-tv.ru/iptv/region/ZABAVA_PZAV_RK.m3u' -desc='' -src='http://dmi3y-tv.ru/d-tv/playlist/zabava/region/1' - -[dzpz] -name='[dmi3y-tv] Региональные: Петрозаводск/PZAV.m3u' -pls='http://dmi3y-tv.ru/iptv/region/ZABAVA_PZAV.m3u' -desc='' -src='http://dmi3y-tv.ru/d-tv/playlist/zabava/region/1' - -[dzps] -name='[dmi3y-tv] Региональные: Псков/PSKV.m3u' -pls='http://dmi3y-tv.ru/iptv/region/ZABAVA_PSKV.m3u' -desc='' -src='http://dmi3y-tv.ru/d-tv/playlist/zabava/region/1' - -[dzpo] -name='[dmi3y-tv] Региональные: Псковская область/PSKV_OBL.m3u' -pls='http://dmi3y-tv.ru/iptv/region/ZABAVA_PSKV_OBL.m3u' -desc='' -src='http://dmi3y-tv.ru/d-tv/playlist/zabava/region/1' - -[dzr] -name='[dmi3y-tv] Региональные: Ростов на Дону/RD.m3u' -pls='http://dmi3y-tv.ru/iptv/region/ZABAVA_RD.m3u' -desc='' -src='http://dmi3y-tv.ru/d-tv/playlist/zabava/region/1' - -[dzro] -name='[dmi3y-tv] Региональные: Ростовская область/RD_OBL.m3u' -pls='http://dmi3y-tv.ru/iptv/region/ZABAVA_RD_OBL.m3u' -desc='' -src='http://dmi3y-tv.ru/d-tv/playlist/zabava/region/1' - -[dzry] -name='[dmi3y-tv] Региональные: Рыбинск/RYB.m3u' -pls='http://dmi3y-tv.ru/iptv/region/ZABAVA_RYB.m3u' -desc='' -src='http://dmi3y-tv.ru/d-tv/playlist/zabava/region/1' - -[dzrz] -name='[dmi3y-tv] Региональные: Рязань/RYAZ.m3u' -pls='http://dmi3y-tv.ru/iptv/region/ZABAVA_RYAZ.m3u' -desc='' -src='http://dmi3y-tv.ru/d-tv/playlist/zabava/region/1' - -[dzs] -name='[dmi3y-tv] Региональные: Самара/SAMARA.m3u' -pls='http://dmi3y-tv.ru/iptv/region/ZABAVA_SAMARA.m3u' -desc='' -src='http://dmi3y-tv.ru/d-tv/playlist/zabava/region/1' - -[dzsp] -name='[dmi3y-tv] Региональные: Санкт-Петербург/SPB.m3u' -pls='http://dmi3y-tv.ru/iptv/region/ZABAVA_SPB.m3u' -desc='' -src='http://dmi3y-tv.ru/d-tv/playlist/zabava/region/1' - -[dzsa] -name='[dmi3y-tv] Региональные: Саранск/SRNK.m3u' -pls='http://dmi3y-tv.ru/iptv/region/ZABAVA_SRNK.m3u' -desc='' -src='http://dmi3y-tv.ru/d-tv/playlist/zabava/region/1' - -[dzsr] -name='[dmi3y-tv] Региональные: Саратов/SARATOV.m3u' -pls='http://dmi3y-tv.ru/iptv/region/ZABAVA_SARATOV.m3u' -desc='' -src='http://dmi3y-tv.ru/d-tv/playlist/zabava/region/1' - -[dzsh] -name='[dmi3y-tv] Региональные: Сахалин/DV_SLIN.m3u' -pls='http://dmi3y-tv.ru/iptv/region/ZABAVA_DV_SLIN.m3u' -desc='' -src='http://dmi3y-tv.ru/d-tv/playlist/zabava/region/1' - -[dzsho] -name='[dmi3y-tv] Региональные: Сахалинская область/DV_SLIN_OBL.m3u' -pls='http://dmi3y-tv.ru/iptv/region/ZABAVA_DV_SLIN_OBL.m3u' -desc='' -src='http://dmi3y-tv.ru/d-tv/playlist/zabava/region/1' - -[dzsm] -name='[dmi3y-tv] Региональные: Смоленск/SMOL.m3u' -pls='http://dmi3y-tv.ru/iptv/region/ZABAVA_SMOL.m3u' -desc='' -src='http://dmi3y-tv.ru/d-tv/playlist/zabava/region/1' - -[dzso] -name='[dmi3y-tv] Региональные: Сочи/SOCHI.m3u' -pls='http://dmi3y-tv.ru/iptv/region/ZABAVA_SOCHI.m3u' -desc='' -src='http://dmi3y-tv.ru/d-tv/playlist/zabava/region/1' - -[dzst] -name='[dmi3y-tv] Региональные: Ставрополь/STAVR.m3u' -pls='http://dmi3y-tv.ru/iptv/region/ZABAVA_STAVR.m3u' -desc='' -src='http://dmi3y-tv.ru/d-tv/playlist/zabava/region/1' - -[dzstk] -name='[dmi3y-tv] Региональные: Ставропольский край/STAVR_KRAI.m3u' -pls='http://dmi3y-tv.ru/iptv/region/ZABAVA_STAVR_KRAI.m3u' -desc='' -src='http://dmi3y-tv.ru/d-tv/playlist/zabava/region/1' - -[dzsk] -name='[dmi3y-tv] Региональные: Старый Оскол/OSK.m3u' -pls='http://dmi3y-tv.ru/iptv/region/ZABAVA_OSK.m3u' -desc='' -src='http://dmi3y-tv.ru/d-tv/playlist/zabava/region/1' - -[dzsu] -name='[dmi3y-tv] Региональные: Сургут/SUR.m3u' -pls='http://dmi3y-tv.ru/iptv/region/ZABAVA_SUR.m3u' -desc='' -src='http://dmi3y-tv.ru/d-tv/playlist/zabava/region/1' - -[dzsy] -name='[dmi3y-tv] Региональные: Сыктывкар/SKTV.m3u' -pls='http://dmi3y-tv.ru/iptv/region/ZABAVA_SKTV.m3u' -desc='' -src='http://dmi3y-tv.ru/d-tv/playlist/zabava/region/1' - -[dzta] -name='[dmi3y-tv] Региональные: Тамбов/TAMB.m3u' -pls='http://dmi3y-tv.ru/iptv/region/ZABAVA_TAMB.m3u' -desc='' -src='http://dmi3y-tv.ru/d-tv/playlist/zabava/region/1' - -[dztvo] -name='[dmi3y-tv] Региональные: Тверская область/TVER_OBL.m3u' -pls='http://dmi3y-tv.ru/iptv/region/ZABAVA_TVER_OBL.m3u' -desc='' -src='http://dmi3y-tv.ru/d-tv/playlist/zabava/region/1' - -[dztv] -name='[dmi3y-tv] Региональные: Тверь/TVER.m3u' -pls='http://dmi3y-tv.ru/iptv/region/ZABAVA_TVER.m3u' -desc='' -src='http://dmi3y-tv.ru/d-tv/playlist/zabava/region/1' - -[dzto] -name='[dmi3y-tv] Региональные: Томск/TOMSK.m3u' -pls='http://dmi3y-tv.ru/iptv/region/ZABAVA_TOMSK.m3u' -desc='' -src='http://dmi3y-tv.ru/d-tv/playlist/zabava/region/1' - -[dztoo] -name='[dmi3y-tv] Региональные: Томская область/TOMSK_OBL.m3u' -pls='http://dmi3y-tv.ru/iptv/region/ZABAVA_TOMSK_OBL.m3u' -desc='' -src='http://dmi3y-tv.ru/d-tv/playlist/zabava/region/1' - -[dztu] -name='[dmi3y-tv] Региональные: Тула/TULA.m3u' -pls='http://dmi3y-tv.ru/iptv/region/ZABAVA_TULA.m3u' -desc='' -src='http://dmi3y-tv.ru/d-tv/playlist/zabava/region/1' - -[dzty] -name='[dmi3y-tv] Региональные: Тюмень/TUM.m3u' -pls='http://dmi3y-tv.ru/iptv/region/ZABAVA_TUM.m3u' -desc='' -src='http://dmi3y-tv.ru/d-tv/playlist/zabava/region/1' - -[dzu] -name='[dmi3y-tv] Региональные: Улан-Удэ/UUDE.m3u' -pls='http://dmi3y-tv.ru/iptv/region/ZABAVA_UUDE.m3u' -desc='' -src='http://dmi3y-tv.ru/d-tv/playlist/zabava/region/1' - -[dzul] -name='[dmi3y-tv] Региональные: Ульяновск/UL.m3u' -pls='http://dmi3y-tv.ru/iptv/region/ZABAVA_UL.m3u' -desc='' -src='http://dmi3y-tv.ru/d-tv/playlist/zabava/region/1' - -[dzuf] -name='[dmi3y-tv] Региональные: Уфа/UFA.m3u' -pls='http://dmi3y-tv.ru/iptv/region/ZABAVA_UFA.m3u' -desc='' -src='http://dmi3y-tv.ru/d-tv/playlist/zabava/region/1' - -[dzh] -name='[dmi3y-tv] Региональные: Хабаровск/DV_HVSK.m3u' -pls='http://dmi3y-tv.ru/iptv/region/ZABAVA_DV_HVSK.m3u' -desc='' -src='http://dmi3y-tv.ru/d-tv/playlist/zabava/region/1' - -[dzc] -name='[dmi3y-tv] Региональные: Чебоксары/CHR.m3u' -pls='http://dmi3y-tv.ru/iptv/region/ZABAVA_CHR.m3u' -desc='' -src='http://dmi3y-tv.ru/d-tv/playlist/zabava/region/1' - -[dzcb] -name='[dmi3y-tv] Региональные: Челябинск/CHEL.m3u' -pls='http://dmi3y-tv.ru/iptv/region/ZABAVA_CHEL.m3u' -desc='' -src='http://dmi3y-tv.ru/d-tv/playlist/zabava/region/1' - -[dzce] -name='[dmi3y-tv] Региональные: Черкесия/CHERKS.m3u' -pls='http://dmi3y-tv.ru/iptv/region/ZABAVA_CHERKS.m3u' -desc='' -src='http://dmi3y-tv.ru/d-tv/playlist/zabava/region/1' - -[dzct] -name='[dmi3y-tv] Региональные: Чита (Забайкальский край)/CHITA_ZK.m3u' -pls='http://dmi3y-tv.ru/iptv/region/ZABAVA_CHITA_ZK.m3u' -desc='' -src='http://dmi3y-tv.ru/d-tv/playlist/zabava/region/1' - -[dzct2] -name='[dmi3y-tv] Региональные: Чита/CHITA.m3u' -pls='http://dmi3y-tv.ru/iptv/region/ZABAVA_CHITA.m3u' -desc='' -src='http://dmi3y-tv.ru/d-tv/playlist/zabava/region/1' - -[dzel] -name='[dmi3y-tv] Региональные: Элиста/ELISTA.m3u' -pls='http://dmi3y-tv.ru/iptv/region/ZABAVA_ELISTA.m3u' -desc='' -src='http://dmi3y-tv.ru/d-tv/playlist/zabava/region/1' - -[dzyk] -name='[dmi3y-tv] Региональные: Якутск/DV_YKT.m3u' -pls='http://dmi3y-tv.ru/iptv/region/ZABAVA_DV_YKT.m3u' -desc='' -src='http://dmi3y-tv.ru/d-tv/playlist/zabava/region/1' - -[dzyr] -name='[dmi3y-tv] Региональные: Ярославль/YARL.m3u' -pls='http://dmi3y-tv.ru/iptv/region/ZABAVA_YARL.m3u' -desc='' -src='http://dmi3y-tv.ru/d-tv/playlist/zabava/region/1' - -;################################################### -; редиректы -;################################################### - -; [from_code] -; redirect=to_code - diff --git a/src/config/redis.php b/src/config/redis.php new file mode 100644 index 0000000..c97ab7d --- /dev/null +++ b/src/config/redis.php @@ -0,0 +1,11 @@ + env('REDIS_HOST', 'keydb'), + 'port' => (int)env('REDIS_PORT', 6379), + 'password' => env('REDIS_PASSWORD'), + 'db' => (int)env('REDIS_DB', 0), + 'ttl_days' => (int)env('REDIS_TTL_DAYS', 14) * 60 * 60 * 24, // 2 недели +]; diff --git a/src/config/routes.php b/src/config/routes.php index 5b192f1..d547ec1 100644 --- a/src/config/routes.php +++ b/src/config/routes.php @@ -1,17 +1,52 @@ [HomeController::class, 'index'], - 'GET /page/@page:[0-9]+' => [HomeController::class, 'index'], - 'GET /faq' => [HomeController::class, 'faq'], - 'GET /logo' => [PlaylistController::class, 'logo'], - 'GET /@id:[a-zA-Z0-9_-]+' => [PlaylistController::class, 'download'], - 'GET /?[a-zA-Z0-9_-]+' => [PlaylistController::class, 'download'], - 'GET /@id:[a-zA-Z0-9_-]+/details' => [PlaylistController::class, 'details'], - 'GET /@id:[a-zA-Z0-9_-]+/json' => [PlaylistController::class, 'json'], + [ + 'method' => 'GET', + 'path' => '/[page/{page:[0-9]+}]', + 'handler' => [WebController::class, 'home'], + 'name' => 'home', + ], + [ + 'method' => 'GET', + 'path' => '/faq', + 'handler' => [WebController::class, 'faq'], + 'name' => 'faq', + ], + [ + 'method' => 'GET', + 'path' => '/logo', + 'handler' => [WebController::class, 'logo'], + 'name' => 'logo', + ], + [ + 'method' => 'GET', + 'path' => '/{code:[0-9a-zA-Z]+}', + 'handler' => [WebController::class, 'redirect'], + 'name' => 'redirect', + ], + [ + 'method' => 'GET', + 'path' => '/{code:[0-9a-zA-Z]+}/details', + 'handler' => [WebController::class, 'details'], + 'name' => 'details', + ], + [ + 'method' => 'GET', + 'path' => '/{code:[0-9a-zA-Z]+}/json', + 'handler' => [ApiController::class, 'json'], + 'name' => 'json', + ], + [ + 'method' => '*', + 'path' => '/{path:.*}', + 'handler' => [BasicController::class, 'notFound'], + 'name' => 'not-found', + ], + // ... ]; + diff --git a/src/config/twig.php b/src/config/twig.php new file mode 100644 index 0000000..d3aed55 --- /dev/null +++ b/src/config/twig.php @@ -0,0 +1,8 @@ + bool(env('TWIG_USE_CACHE', true)) ? cache_path() . '/views' : false, + 'debug' => bool(env('TWIG_DEBUG', false)), +]; diff --git a/src/public/index.php b/src/public/index.php index e9638bf..58acd6d 100644 --- a/src/public/index.php +++ b/src/public/index.php @@ -2,19 +2,6 @@ declare(strict_types=1); -use App\Core\Bootstrapper; -use Symfony\Component\Dotenv\Dotenv; - -/* -|-------------------------------------------------------------------------- -| Bootstrap all classes, settings, etc. -|-------------------------------------------------------------------------- -*/ - require '../vendor/autoload.php'; -(new Dotenv())->loadEnv(root_path() . '/.env'); -Bootstrapper::bootSettings(); -Bootstrapper::bootTwig(); -Bootstrapper::bootCore(); -Bootstrapper::bootRoutes(); -Flight::start(); + +core()->boot()->run(); diff --git a/src/views/list.twig b/src/views/list.twig index 3178728..9222011 100644 --- a/src/views/list.twig +++ b/src/views/list.twig @@ -49,11 +49,11 @@ {% endfor %} - {% if pages.count > 0 %} + {% if pageCount > 0 %}