mirror of
https://github.com/anthonyaxenov/iptv.git
synced 2024-11-21 21:14:46 +00:00
404 + рефакторинг + докблоки и комменты
This commit is contained in:
parent
f18fb9dd7a
commit
bcadc316bc
@ -1,9 +1,27 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types = 1);
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Controllers;
|
||||
|
||||
use Exception;
|
||||
use Flight;
|
||||
|
||||
/**
|
||||
* Абстрактный контроллер для расширения
|
||||
*/
|
||||
abstract class Controller
|
||||
{
|
||||
/**
|
||||
* Перебрасывает на страницу 404 при ненайденном плейлисте
|
||||
*
|
||||
* @param string $id
|
||||
* @return void
|
||||
* @throws Exception
|
||||
*/
|
||||
public function notFound(string $id): void
|
||||
{
|
||||
Flight::response()->status(404)->sendHeaders();
|
||||
view('notfound', ['id' => $id]);
|
||||
}
|
||||
}
|
||||
|
@ -1,25 +1,35 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types = 1);
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Controllers;
|
||||
|
||||
use App\Core\PlaylistProcessor;
|
||||
use App\Core\RedirectedPlaylist;
|
||||
use Exception;
|
||||
use Flight;
|
||||
|
||||
/**
|
||||
* Контроллер домашней страницы (списка плейлистов)
|
||||
*/
|
||||
class HomeController extends Controller
|
||||
{
|
||||
/**
|
||||
* @var PlaylistProcessor Обработчик ini-списка
|
||||
*/
|
||||
protected PlaylistProcessor $ini;
|
||||
|
||||
/**
|
||||
* Конструктор
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->ini = new PlaylistProcessor();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
* Возвращает размер одной страницы списка плейлистов
|
||||
*
|
||||
* @return int Указанный в конфиге размер либо 10, если он выходит за диапазоны от 5 до 100
|
||||
*/
|
||||
protected function getPageSize(): int
|
||||
{
|
||||
@ -28,19 +38,25 @@ class HomeController extends Controller
|
||||
}
|
||||
|
||||
/**
|
||||
* Отображает главную страницу на указанной странице списка плейлистов
|
||||
*
|
||||
* @param int $page
|
||||
* @return void
|
||||
* @throws Exception
|
||||
*/
|
||||
public function index(int $page = 1)
|
||||
public function index(int $page = 1): void
|
||||
{
|
||||
// если пришёл любой get-параметр, то считаем его как id плейлиста и перебрасываем на страницу о нём
|
||||
if (Flight::request()->query->count() > 0) {
|
||||
$id = Flight::request()->query->keys()[0];
|
||||
Flight::redirect(base_url($id));
|
||||
die;
|
||||
}
|
||||
// иначе формируем и сортируем список при необходимости, рисуем страницу
|
||||
$per_page = $this->getPageSize();
|
||||
$list = $this->ini->playlists->where('redirect_id', null);
|
||||
if (config('app.sort_by')) {
|
||||
$list = $list->sortBy(config('app.sort_by'));
|
||||
if ($sort_by = config('app.sort_by')) {
|
||||
$list = $list->sortBy($sort_by);
|
||||
}
|
||||
$list = $list->forPage($page, $per_page);
|
||||
view('list', [
|
||||
@ -55,41 +71,13 @@ class HomeController extends Controller
|
||||
}
|
||||
|
||||
/**
|
||||
* Отображает страницу FAQ
|
||||
*
|
||||
* @return void
|
||||
* @throws Exception
|
||||
*/
|
||||
public function faq()
|
||||
public function faq(): void
|
||||
{
|
||||
view('faq');
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws Exception
|
||||
*/
|
||||
public function details(string $id): void
|
||||
{
|
||||
$playlist = $this->ini->playlist($id);
|
||||
if ($playlist instanceof RedirectedPlaylist) {
|
||||
Flight::redirect(base_url($playlist->redirect_id . '/info'));
|
||||
}
|
||||
view('details', [
|
||||
'id' => $id,
|
||||
'playlist' => $playlist->toArray(),
|
||||
'info' => $this->ini->parse($id),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws Exception
|
||||
*/
|
||||
public function ajax(string $id): void
|
||||
{
|
||||
$playlist = $this->ini->playlist($id);
|
||||
if ($playlist instanceof RedirectedPlaylist) {
|
||||
Flight::redirect(base_url($playlist->redirect_id . '/getInfo'));
|
||||
}
|
||||
Flight::json([
|
||||
'playlist' => $playlist->toArray(),
|
||||
'info' => $this->ini->parse($id),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
@ -1,66 +1,102 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types = 1);
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Controllers;
|
||||
|
||||
use App\Core\PlaylistProcessor;
|
||||
use App\Core\RedirectedPlaylist;
|
||||
use App\Core\{
|
||||
PlaylistProcessor,
|
||||
RedirectedPlaylist};
|
||||
use App\Exceptions\PlaylistNotFoundException;
|
||||
use Exception;
|
||||
use Flight;
|
||||
|
||||
/**
|
||||
* Контроллер методов получения описания плейлистов
|
||||
*/
|
||||
class PlaylistController extends Controller
|
||||
{
|
||||
/**
|
||||
* @var PlaylistProcessor Обработчик ini-списка
|
||||
*/
|
||||
protected PlaylistProcessor $ini;
|
||||
|
||||
/**
|
||||
* Конструктор
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->ini = new PlaylistProcessor();
|
||||
}
|
||||
|
||||
/**
|
||||
* Отправляет запрос с клиента по прямой ссылке плейлиста
|
||||
*
|
||||
* @param $id
|
||||
* @return void
|
||||
* @throws Exception
|
||||
*/
|
||||
public function download($id)
|
||||
public function download($id): void
|
||||
{
|
||||
$playlist = $this->ini->playlist($id);
|
||||
if ($playlist instanceof RedirectedPlaylist) {
|
||||
Flight::redirect(base_url($playlist->redirect_id));
|
||||
die;
|
||||
try {
|
||||
$playlist = $this->ini->playlist($id);
|
||||
if ($playlist instanceof RedirectedPlaylist) {
|
||||
Flight::redirect(base_url($playlist->redirect_id));
|
||||
die;
|
||||
}
|
||||
Flight::redirect($playlist->pls);
|
||||
} catch (PlaylistNotFoundException) {
|
||||
$this->notFound($id);
|
||||
}
|
||||
header('Location: ' . $playlist->pls);
|
||||
die;
|
||||
}
|
||||
|
||||
/**
|
||||
* Отображает страницу описания плейлиста
|
||||
*
|
||||
* @param string $id
|
||||
* @return void
|
||||
* @throws Exception
|
||||
*/
|
||||
public function details(string $id): void
|
||||
{
|
||||
$playlist = $this->ini->playlist($id);
|
||||
if ($playlist instanceof RedirectedPlaylist) {
|
||||
Flight::redirect(base_url($playlist->redirect_id . '/details'));
|
||||
die;
|
||||
try {
|
||||
$playlist = $this->ini->playlist($id);
|
||||
if ($playlist instanceof RedirectedPlaylist) {
|
||||
Flight::redirect(base_url($playlist->redirect_id . '/details'));
|
||||
die;
|
||||
}
|
||||
view('details', [
|
||||
...$playlist->toArray(),
|
||||
...$this->ini->parse($id),
|
||||
]);
|
||||
} catch (PlaylistNotFoundException) {
|
||||
$this->notFound($id);
|
||||
}
|
||||
view('details', [
|
||||
...$playlist->toArray(),
|
||||
...$this->ini->parse($id),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Возвращает JSON с описанием плейлиста
|
||||
*
|
||||
* @param string $id
|
||||
* @return void
|
||||
* @throws Exception
|
||||
*/
|
||||
public function json(string $id): void
|
||||
{
|
||||
$playlist = $this->ini->playlist($id);
|
||||
if ($playlist instanceof RedirectedPlaylist) {
|
||||
Flight::redirect(base_url($playlist->redirect_id . '/json'));
|
||||
die;
|
||||
try {
|
||||
$playlist = $this->ini->playlist($id);
|
||||
if ($playlist instanceof RedirectedPlaylist) {
|
||||
Flight::redirect(base_url($playlist->redirect_id . '/json'));
|
||||
die;
|
||||
}
|
||||
Flight::json([
|
||||
...$playlist->toArray(),
|
||||
...$this->ini->parse($id),
|
||||
]);
|
||||
} catch (PlaylistNotFoundException) {
|
||||
Flight::response()->status(404)->sendHeaders();
|
||||
Flight::json(['error' => ['message' => 'Playlist not found']]);
|
||||
}
|
||||
Flight::json([
|
||||
...$playlist->toArray(),
|
||||
...$this->ini->parse($id),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
@ -1,15 +1,26 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types = 1);
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Core;
|
||||
|
||||
use Illuminate\Contracts\Support\Arrayable;
|
||||
|
||||
/**
|
||||
* Базовый класс плейлиста
|
||||
*/
|
||||
abstract class BasicPlaylist implements Arrayable
|
||||
{
|
||||
/**
|
||||
* @var string ID плейлиста
|
||||
*/
|
||||
public string $id;
|
||||
|
||||
/**
|
||||
* Возвращает ссылку на плейлист в рамках проекта
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function url(): string
|
||||
{
|
||||
return sprintf('%s/%s', base_url(), $this->id);
|
||||
|
@ -1,6 +1,6 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types = 1);
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Core;
|
||||
|
||||
@ -15,13 +15,26 @@ use Twig\Environment;
|
||||
use Twig\Extension\DebugExtension;
|
||||
use Twig\Loader\FilesystemLoader;
|
||||
|
||||
/**
|
||||
* Сборщик приложения
|
||||
*/
|
||||
final class Bootstrapper
|
||||
{
|
||||
/**
|
||||
* Загружает env-переменные
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function bootEnv(): void
|
||||
{
|
||||
(new Dotenv())->loadEnv(root_path() . '/.env');
|
||||
}
|
||||
|
||||
/**
|
||||
* Загружает конфигурацию приложения в контейнер
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function bootSettings(): void
|
||||
{
|
||||
$settings = Arr::dot(require_once config_path('app.php'));
|
||||
@ -31,6 +44,11 @@ final class Bootstrapper
|
||||
Flight::set('config', $settings);
|
||||
}
|
||||
|
||||
/**
|
||||
* Загружает шаблонизатор и его расщирения
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function bootTwig(): void
|
||||
{
|
||||
$filesystemLoader = new FilesystemLoader(config('views.path'));
|
||||
@ -47,6 +65,11 @@ final class Bootstrapper
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Загружает маршруты
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function bootRoutes(): void
|
||||
{
|
||||
Flight::route(
|
||||
|
@ -1,27 +1,51 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types = 1);
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Core;
|
||||
|
||||
use Exception;
|
||||
|
||||
/**
|
||||
* Плейлист без редиректа
|
||||
*/
|
||||
class Playlist extends BasicPlaylist
|
||||
{
|
||||
/**
|
||||
* @var string|null Название плейлиста
|
||||
*/
|
||||
public ?string $name;
|
||||
|
||||
/**
|
||||
* @var string|null Описание плейлиста
|
||||
*/
|
||||
public ?string $desc;
|
||||
|
||||
/**
|
||||
* @var string Прямой URL до файла плейлиста на третьей стороне
|
||||
*/
|
||||
public string $pls;
|
||||
|
||||
/**
|
||||
* @var string|null Источник плейлиста
|
||||
*/
|
||||
public ?string $src;
|
||||
|
||||
/**
|
||||
* @var string Ссылка на плейлист в рамках проекта
|
||||
*/
|
||||
public string $url;
|
||||
|
||||
/**
|
||||
* @throws \Exception
|
||||
* Конструктор
|
||||
*
|
||||
* @param string $id
|
||||
* @param array $params
|
||||
* @throws Exception
|
||||
*/
|
||||
public function __construct(public string $id, array $params)
|
||||
{
|
||||
empty($params['pls']) && throw new \Exception(
|
||||
empty($params['pls']) && throw new Exception(
|
||||
"Плейлист с ID=$id обязан иметь параметр pls или redirect"
|
||||
);
|
||||
$this->url = base_url($id);
|
||||
@ -31,6 +55,9 @@ class Playlist extends BasicPlaylist
|
||||
$this->src = empty($params['src']) ? null : $params['src'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function toArray(): array
|
||||
{
|
||||
return [
|
||||
|
@ -1,17 +1,30 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types = 1);
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Core;
|
||||
|
||||
use App\Exceptions\PlaylistNotFoundException;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
/**
|
||||
* Обработчик списка плейлистов
|
||||
*/
|
||||
final class PlaylistProcessor
|
||||
{
|
||||
/**
|
||||
* @var Collection Коллекция подгруженных плейлистов
|
||||
*/
|
||||
public Collection $playlists;
|
||||
|
||||
/**
|
||||
* @var string Дата последнего обновления списка
|
||||
*/
|
||||
protected string $updated_at;
|
||||
|
||||
/**
|
||||
* Конструктор
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$filepath = config_path('playlists.ini');
|
||||
@ -24,31 +37,60 @@ final class PlaylistProcessor
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Проверяет есть ли в списке плейлист по его id
|
||||
*
|
||||
* @param string $id
|
||||
* @return bool
|
||||
*/
|
||||
public function hasId(string $id): bool
|
||||
{
|
||||
return in_array($id, $this->playlists->keys()->toArray());
|
||||
return $this->playlists->keys()->contains($id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Возвращает из коллекции указанный плейлист, если он существует
|
||||
*
|
||||
* @param string $id
|
||||
* @return Playlist|RedirectedPlaylist
|
||||
* @throws PlaylistNotFoundException
|
||||
*/
|
||||
public function playlist(string $id): Playlist|RedirectedPlaylist
|
||||
{
|
||||
!$this->hasId($id) && throw new \InvalidArgumentException("Плейлист с ID=$id не найден");
|
||||
!$this->hasId($id) && throw new PlaylistNotFoundException($id);
|
||||
return $this->playlists[$id];
|
||||
}
|
||||
|
||||
/**
|
||||
* Проверяет доступность плейлиста на третьей стороне
|
||||
*
|
||||
* @param string $id
|
||||
* @return bool
|
||||
* @throws PlaylistNotFoundException
|
||||
*/
|
||||
public function check(string $id): bool
|
||||
{
|
||||
$curl = curl_init();
|
||||
curl_setopt($curl, CURLOPT_URL, $this->playlist($id)['pls']);
|
||||
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
|
||||
curl_setopt($curl, CURLOPT_TIMEOUT, 5);
|
||||
curl_setopt($curl, CURLOPT_HEADER, 0);
|
||||
curl_setopt($curl, CURLOPT_NOBODY, 1);
|
||||
curl_setopt_array($curl, [
|
||||
CURLOPT_URL => $this->playlist($id)->pls,
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
CURLOPT_TIMEOUT => 5,
|
||||
CURLOPT_HEADER => false,
|
||||
CURLOPT_NOBODY => true,
|
||||
]);
|
||||
curl_exec($curl);
|
||||
$code = curl_getinfo($curl, CURLINFO_RESPONSE_CODE);
|
||||
curl_close($curl);
|
||||
return $code < 400;
|
||||
}
|
||||
|
||||
/**
|
||||
* Получает содержимое плейлиста с третьей стороны
|
||||
*
|
||||
* @param string $id
|
||||
* @return array
|
||||
* @throws PlaylistNotFoundException
|
||||
*/
|
||||
protected function fetch(string $id): array
|
||||
{
|
||||
$curl = curl_init();
|
||||
@ -72,6 +114,12 @@ final class PlaylistProcessor
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Возвращает статус проверки плейлиста по коду ошибки curl
|
||||
*
|
||||
* @param int $curl_err_code
|
||||
* @return string
|
||||
*/
|
||||
protected function guessStatus(int $curl_err_code): string
|
||||
{
|
||||
return match ($curl_err_code) {
|
||||
@ -82,6 +130,13 @@ final class PlaylistProcessor
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Парсит полученный от третьей стороны плейлист
|
||||
*
|
||||
* @param string $id
|
||||
* @return array Информация о составе плейлиста
|
||||
* @throws PlaylistNotFoundException
|
||||
*/
|
||||
public function parse(string $id): array
|
||||
{
|
||||
$fetched = $this->fetch($id);
|
||||
@ -110,6 +165,8 @@ final class PlaylistProcessor
|
||||
}
|
||||
|
||||
/**
|
||||
* Возвращает дату последнего обновления списка плейлистов
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function updatedAt(): string
|
||||
|
@ -1,13 +1,19 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types = 1);
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Core;
|
||||
|
||||
/**
|
||||
* Плейлист с редиректом
|
||||
*/
|
||||
class RedirectedPlaylist extends BasicPlaylist
|
||||
{
|
||||
/**
|
||||
* @throws \Exception
|
||||
* Конструктор
|
||||
*
|
||||
* @param string $id
|
||||
* @param string $redirect_id
|
||||
*/
|
||||
public function __construct(
|
||||
public string $id,
|
||||
@ -15,6 +21,9 @@ class RedirectedPlaylist extends BasicPlaylist
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function toArray(): array
|
||||
{
|
||||
return [
|
||||
|
15
src/app/Exceptions/PlaylistNotFoundException.php
Normal file
15
src/app/Exceptions/PlaylistNotFoundException.php
Normal file
@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Exceptions;
|
||||
|
||||
use Exception;
|
||||
|
||||
class PlaylistNotFoundException extends Exception
|
||||
{
|
||||
public function __construct(string $pls_code)
|
||||
{
|
||||
parent::__construct("Плейлист $pls_code не найден!");
|
||||
}
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types = 1);
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Extensions;
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types = 1);
|
||||
declare(strict_types=1);
|
||||
|
||||
use flight\Engine;
|
||||
use flight\net\Response;
|
||||
|
@ -1,6 +1,6 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types = 1);
|
||||
declare(strict_types=1);
|
||||
|
||||
return [
|
||||
'flight' => [
|
||||
|
@ -1,6 +1,6 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types = 1);
|
||||
declare(strict_types=1);
|
||||
|
||||
use App\Core\Bootstrapper;
|
||||
|
||||
|
18
src/views/notfound.twig
Normal file
18
src/views/notfound.twig
Normal file
@ -0,0 +1,18 @@
|
||||
{% extends "layouts/default.twig" %}
|
||||
|
||||
{% block header %}
|
||||
<h2>Плейлист не найден</h2>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<p>
|
||||
Плейлист {{ id }} не найден
|
||||
</p>
|
||||
<a class="navbar-brand" href="{{ base_url() }}" title="На главную">
|
||||
Перейти к списку
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
Loading…
Reference in New Issue
Block a user