120 lines
3.7 KiB
PHP
120 lines
3.7 KiB
PHP
|
<?php
|
|||
|
|
|||
|
declare(strict_types = 1);
|
|||
|
|
|||
|
namespace App\Core;
|
|||
|
|
|||
|
use Illuminate\Support\Collection;
|
|||
|
|
|||
|
final class PlaylistProcessor
|
|||
|
{
|
|||
|
public Collection $playlists;
|
|||
|
|
|||
|
protected string $updated_at;
|
|||
|
|
|||
|
public function __construct()
|
|||
|
{
|
|||
|
$filepath = config_path('playlists.ini');
|
|||
|
$this->updated_at = date('d.m.Y h:i', filemtime($filepath));
|
|||
|
$this->playlists = collect(parse_ini_file($filepath, true))
|
|||
|
->transform(function ($playlist, $id) {
|
|||
|
return empty($playlist['redirect'])
|
|||
|
? new Playlist((string)$id, $playlist)
|
|||
|
: new RedirectedPlaylist((string)$id, $playlist['redirect']);
|
|||
|
});
|
|||
|
}
|
|||
|
|
|||
|
public function hasId(string $id): bool
|
|||
|
{
|
|||
|
return in_array($id, $this->playlists->keys()->toArray());
|
|||
|
}
|
|||
|
|
|||
|
public function playlist(string $id): Playlist|RedirectedPlaylist
|
|||
|
{
|
|||
|
!$this->hasId($id) && throw new \InvalidArgumentException("Плейлист с ID=$id не найден");
|
|||
|
return $this->playlists[$id];
|
|||
|
}
|
|||
|
|
|||
|
public function check(string $id): bool
|
|||
|
{
|
|||
|
$curl = curl_init();
|
|||
|
curl_setopt($curl, CURLOPT_URL, $this->playlist($id)['pls']);
|
|||
|
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
|
|||
|
curl_setopt($curl, CURLOPT_TIMEOUT, 5);
|
|||
|
curl_setopt($curl, CURLOPT_HEADER, 0);
|
|||
|
curl_setopt($curl, CURLOPT_NOBODY, 1);
|
|||
|
curl_exec($curl);
|
|||
|
$code = curl_getinfo($curl, CURLINFO_RESPONSE_CODE);
|
|||
|
curl_close($curl);
|
|||
|
return $code < 400;
|
|||
|
}
|
|||
|
|
|||
|
protected function fetch(string $id)
|
|||
|
{
|
|||
|
$curl = curl_init();
|
|||
|
curl_setopt_array($curl, [
|
|||
|
CURLOPT_URL => $this->playlist($id)->pls,
|
|||
|
CURLOPT_RETURNTRANSFER => true,
|
|||
|
CURLOPT_TIMEOUT => 5,
|
|||
|
CURLOPT_HEADER => false,
|
|||
|
CURLOPT_FAILONERROR => true,
|
|||
|
]);
|
|||
|
$content = curl_exec($curl);
|
|||
|
$http_code = curl_getinfo($curl, CURLINFO_RESPONSE_CODE);
|
|||
|
$err_code = curl_errno($curl);
|
|||
|
$err_text = curl_error($curl);
|
|||
|
curl_close($curl);
|
|||
|
return [
|
|||
|
'content' => $content,
|
|||
|
'http_code' => $http_code,
|
|||
|
'err_code' => $err_code,
|
|||
|
'err_text' => $err_text,
|
|||
|
];
|
|||
|
}
|
|||
|
|
|||
|
protected function guessStatus(int $curl_err_code): string
|
|||
|
{
|
|||
|
return match ($curl_err_code) {
|
|||
|
0 => 'online',
|
|||
|
28 => 'timeout',
|
|||
|
5, 6, 7, 22, 35 => 'offline',
|
|||
|
default => 'error',
|
|||
|
};
|
|||
|
}
|
|||
|
|
|||
|
public function parse(string $id): array
|
|||
|
{
|
|||
|
$fetched = $this->fetch($id);
|
|||
|
if ($fetched['err_code'] > 0) {
|
|||
|
return [
|
|||
|
'status' => $this->guessStatus($fetched['err_code']),
|
|||
|
'error' => [
|
|||
|
'code' => $fetched['err_code'],
|
|||
|
'message' => $fetched['err_text'],
|
|||
|
],
|
|||
|
];
|
|||
|
}
|
|||
|
$result['status'] = $this->guessStatus($fetched['err_code']);
|
|||
|
$result['encoding']['name'] = 'UTF-8';
|
|||
|
$result['encoding']['alert'] = false;
|
|||
|
if (($enc = mb_detect_encoding($fetched['content'], config('app.pls_encodings'))) !== 'UTF-8') {
|
|||
|
$fetched['content'] = mb_convert_encoding($fetched['content'], 'UTF-8', $enc);
|
|||
|
$result['encoding']['name'] = $enc;
|
|||
|
$result['encoding']['alert'] = true;
|
|||
|
}
|
|||
|
$matches = [];
|
|||
|
preg_match_all("/^#EXTINF:-?\d.*,\s*(.*)/m", $fetched['content'], $matches);
|
|||
|
$result['channels'] = array_map('trim', $matches[1]);
|
|||
|
$result['count'] = $fetched['http_code'] < 400 ? count($result['channels']) : 0;
|
|||
|
return $result;
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* @return string
|
|||
|
*/
|
|||
|
public function updatedAt(): string
|
|||
|
{
|
|||
|
return $this->updated_at;
|
|||
|
}
|
|||
|
}
|