Files
web/app/Core/IniFile.php
2026-01-03 01:12:18 +08:00

191 lines
5.8 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
/*
* Copyright (c) 2025, Антон Аксенов
* This file is part of m3u.su project
* MIT License: https://git.axenov.dev/IPTV/web/src/branch/master/LICENSE
*/
declare(strict_types=1);
namespace App\Core;
use App\Errors\PlaylistNotFoundException;
use Exception;
/**
* Класс для работы со списком плейлистов
*/
class IniFile
{
/**
* @var array[] Коллекция подгруженных плейлистов
*/
protected array $playlists;
/**
* @var string Дата последнего обновления списка
*/
protected string $updatedAt;
/**
* Считывает ini-файл и инициализирует плейлисты
*
* @return array
*/
public function load(): array
{
$filepath = config_path('playlists.ini');
$this->playlists = parse_ini_file($filepath, true);
$this->updatedAt = date('d.m.Y h:i', filemtime($filepath));
return $this->playlists;
}
/**
* Возвращает плейлисты
*
* @return array[]
* @throws Exception
*/
public function getPlaylists(array $plsCodes = []): array
{
$playlists = [];
empty($this->playlists) && $this->load();
empty($plsCodes) && $plsCodes = array_keys($this->playlists);
$cached = array_combine($plsCodes, redis()->mget($plsCodes));
foreach ($cached as $code => $data) {
$playlists[$code] = $this->initPlaylist($code, $data);
}
return $playlists;
}
/**
* Возвращает плейлист по его коду
*
* @param string $code Код плейлиста
* @return array|null
* @throws PlaylistNotFoundException
* @throws Exception
*/
public function getPlaylist(string $code): ?array
{
empty($this->playlists) && $this->load();
$data = redis()->get($code);
return $this->initPlaylist($code, $data);
}
/**
* Возвращает дату обновления ini-файла
*
* @return string
*/
public function updatedAt(): string
{
return $this->updatedAt;
}
/**
* Подготавливает данные о плейлисте в расширенном формате
*
* @param string $code
* @param array|false $data
* @return array
* @throws PlaylistNotFoundException
*/
protected function initPlaylist(string $code, array|false $data): array
{
if ($data === false) {
$raw = $this->playlists[$code]
?? throw new PlaylistNotFoundException($code);
$data = [
'code' => $code,
'name' => $raw['name'] ?? "Плейлист #{$code}",
'description' => $raw['desc'] ?? null,
'url' => $raw['pls'],
'source' => $raw['src'] ?? null,
'content' => null,
'isOnline' => null,
'attributes' => [],
'groups' => [],
'channels' => [],
'checkedAt' => null,
];
}
// приколы golang
$data['attributes'] === null && $data['attributes'] = [];
$data['groups'] === null && $data['groups'] = [];
$data['channels'] === null && $data['channels'] = [];
$data['onlinePercent'] = 0;
$data['offlinePercent'] = 0;
if ($data['isOnline'] === true && count($data['channels']) > 0) {
$data['onlinePercent'] = round($data['onlineCount'] / count($data['channels']) * 100);
$data['offlinePercent'] = round($data['offlineCount'] / count($data['channels']) * 100);
}
$data['hasCatchup'] = str_contains($data['content'] ?? '', 'catchup');
$data['hasTvg'] = !empty($data['attributes']['url-tvg']) || !empty($data['attributes']['x-tvg-url']);
$data['hasTokens'] = $this->hasTokens($data);
$data['tags'] = [];
foreach ($data['channels'] as &$channel) {
$data['tags'] = array_merge($data['tags'], $channel['tags']);
$channel['hasToken'] = $this->hasTokens($channel);
}
$data['tags'] = array_values(array_unique($data['tags']));
sort($data['tags']);
return $data;
}
/**
* Проверяет наличие токенов в плейлисте
*
* Сделано именно так, а не через тег unstable, чтобы разделить логику: есть заведомо нестабильные каналы,
* которые могут не транслироваться круглосуточно, а есть платные круглосуточные, которые могут оборваться
* в любой момент.
*
* @param array $data
* @return bool
*/
protected function hasTokens(array $data): bool
{
$string = ($data['url'] ?? '') . ($data['content'] ?? '');
if (empty($string)) {
return false;
}
$badAttributes = [
// токены и ключи
'[?&]token=',
'[?&]drmreq=',
// логины
'[?&]u=',
'[?&]user=',
'[?&]username=',
// пароли
'[?&]p=',
'[?&]pwd=',
'[?&]password=',
// неизвестные
// 'free=true',
// 'uid=',
// 'c_uniq_tag=',
// 'rlkey=',
// '?s=',
// '&s=',
// '?q=',
// '&q=',
];
return array_any(
$badAttributes,
static fn (string $badAttribute) => preg_match_all("/{$badAttribute}/", $string) >= 1,
);
}
}