feat: media result page
This commit is contained in:
107
src/Torrentio/Result/ResultFactory.php
Normal file
107
src/Torrentio/Result/ResultFactory.php
Normal file
@@ -0,0 +1,107 @@
|
||||
<?php
|
||||
|
||||
namespace App\Torrentio\Result;
|
||||
|
||||
use App\Util\CountryCodes;
|
||||
use Nihilarr\PTN;
|
||||
|
||||
class ResultFactory
|
||||
{
|
||||
public static function map(
|
||||
string $url,
|
||||
string $title,
|
||||
string $bingeGroup = "-"
|
||||
) {
|
||||
$ptn = (object) (new PTN())->parse($title);
|
||||
return new TorrentioResult(
|
||||
self::trimTitle($title),
|
||||
$url,
|
||||
self::setSize($title),
|
||||
self::setSeeders($title),
|
||||
self::setProvider($title),
|
||||
self::setEpisode($title),
|
||||
$ptn->season ?? "-",
|
||||
$bingeGroup,
|
||||
$ptn->resolution ?? "-",
|
||||
$ptn->codec ?? "-",
|
||||
$ptn,
|
||||
substr(base64_encode($url), strlen($url) - 10),
|
||||
$ptn->episode ?? "-",
|
||||
self::setLanguages($title),
|
||||
self::setLanguageFlags($title),
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
public static function setSize(string $title): string
|
||||
{
|
||||
$sizeMatch = [];
|
||||
preg_match('/(\d+\.?\d+ )(GB|MB)/', $title, $sizeMatch);
|
||||
return $sizeMatch[0] ?? "-";
|
||||
}
|
||||
|
||||
private static function setSeeders(string $title): string
|
||||
{
|
||||
$emoji = \Emoji\detect_emoji($title);
|
||||
return intval(
|
||||
grapheme_substr($title, $emoji[0]['grapheme_offset'] + 1, $emoji[1]['grapheme_offset'] - $emoji[0]['grapheme_offset'])
|
||||
);
|
||||
}
|
||||
|
||||
private static function setProvider(string $title): string
|
||||
{
|
||||
$emoji = \Emoji\detect_emoji($title);
|
||||
$provider = trim(
|
||||
grapheme_substr($title, $emoji[2]['grapheme_offset'] + 1, strlen($title) - $emoji[1]['grapheme_offset'])
|
||||
);
|
||||
$providerParts = explode("\n", $provider);
|
||||
return $providerParts[0];
|
||||
}
|
||||
|
||||
private static function setLanguageFlags(string $title): string
|
||||
{
|
||||
$emoji = \Emoji\detect_emoji($title);
|
||||
$provider = trim(
|
||||
grapheme_substr($title, $emoji[2]['grapheme_offset'] + 1, strlen($title) - $emoji[1]['grapheme_offset'])
|
||||
);
|
||||
|
||||
$providerParts = explode("\n", $provider);
|
||||
|
||||
if (array_key_exists(1, $providerParts)) {
|
||||
return $providerParts[1];
|
||||
} else {
|
||||
return "🇺🇸";
|
||||
}
|
||||
}
|
||||
|
||||
public static function setLanguages(string $title): array
|
||||
{
|
||||
$emoji = \Emoji\detect_emoji($title);
|
||||
$flags = array_filter($emoji, function ($emoji) {
|
||||
return str_starts_with($emoji['short_name'], 'flag-');
|
||||
});
|
||||
|
||||
$languages = array_map(function ($flag) {
|
||||
return CountryCodes::convertFromAbbr(strtoupper(substr($flag['short_name'], strlen('flag-'))));
|
||||
},
|
||||
$flags);
|
||||
if (count($languages) > 0) {
|
||||
return array_values($languages);
|
||||
} else {
|
||||
return ["English"];
|
||||
}
|
||||
}
|
||||
|
||||
private static function setEpisode(string $title)
|
||||
{
|
||||
$value = [];
|
||||
preg_match('/[sS]\d\d[eE]\d\d/', $title, $value);
|
||||
return array_key_exists(0, $value) ? strtoupper($value[0]) : "n/a";
|
||||
}
|
||||
|
||||
private static function trimTitle(string $title)
|
||||
{
|
||||
$emoji = \Emoji\detect_emoji($title);
|
||||
return trim(grapheme_substr($title, 0, $emoji[0]['grapheme_offset']));
|
||||
}
|
||||
}
|
||||
25
src/Torrentio/Result/TorrentioResult.php
Normal file
25
src/Torrentio/Result/TorrentioResult.php
Normal file
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
namespace App\Torrentio\Result;
|
||||
|
||||
class TorrentioResult
|
||||
{
|
||||
public function __construct(
|
||||
public ?string $title = "-",
|
||||
public ?string $url = "-",
|
||||
public ?string $size = "-",
|
||||
public ?string $seeders = "-",
|
||||
public ?string $provider = "-",
|
||||
public ?string $episode = "-",
|
||||
public ?string $season = "-",
|
||||
public ?string $bingeGroup = "-",
|
||||
public ?string $resolution = "-",
|
||||
public ?string $codec = "-",
|
||||
public object|array $ptn = [],
|
||||
public ?string $key = "-",
|
||||
public ?string $episodeNumber = "-",
|
||||
public ?array $languages = [],
|
||||
public ?string $languageFlags = "-",
|
||||
public ?bool $selected = false,
|
||||
) {}
|
||||
}
|
||||
18
src/Torrentio/Rule/DownloadOptionFilter/Resolution.php
Normal file
18
src/Torrentio/Rule/DownloadOptionFilter/Resolution.php
Normal file
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
namespace App\Torrentio\Rule\DownloadOptionFilter;
|
||||
|
||||
use App\Torrentio\Result\ResultFactory;
|
||||
|
||||
class Resolution
|
||||
{
|
||||
|
||||
public function __construct(
|
||||
public string $expectedValue
|
||||
) {}
|
||||
|
||||
public function __invoke(ResultFactory $result): bool
|
||||
{
|
||||
return $result->resolution === $this->expectedValue;
|
||||
}
|
||||
}
|
||||
34
src/Torrentio/Rule/RuleEngine.php
Normal file
34
src/Torrentio/Rule/RuleEngine.php
Normal file
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
namespace App\Torrentio\Rule;
|
||||
|
||||
class RuleEngine
|
||||
{
|
||||
private array $rules = [];
|
||||
|
||||
public function addRule(callable $rule): void
|
||||
{
|
||||
$this->rules[] = $rule;
|
||||
}
|
||||
|
||||
public function validateAny($fact): bool
|
||||
{
|
||||
foreach ($this->rules as $rule) {
|
||||
if ($rule($fact)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public function validateAll($fact): bool
|
||||
{
|
||||
foreach ($this->rules as $rule) {
|
||||
if (!$rule($fact)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
117
src/Torrentio/Torrentio.php
Normal file
117
src/Torrentio/Torrentio.php
Normal file
@@ -0,0 +1,117 @@
|
||||
<?php
|
||||
|
||||
namespace App\Torrentio;
|
||||
|
||||
use App\Torrentio\Rule\DownloadOptionFilter\Resolution;
|
||||
use App\Torrentio\Rule\RuleEngine;
|
||||
use App\Torrentio\Result\ResultFactory;
|
||||
use Symfony\Component\DependencyInjection\Attribute\Autowire;
|
||||
use Symfony\Contracts\Cache\CacheInterface;
|
||||
use Symfony\Contracts\Cache\ItemInterface;
|
||||
|
||||
/**
|
||||
* ToDo: Fix
|
||||
*/
|
||||
class Torrentio
|
||||
{
|
||||
private string $baseUrl = 'https://torrentio.strem.fun/providers%253Dyts%252Ceztv%252Crarbg%252C1337x%252Cthepiratebay%252Ckickasstorrents%252Ctorrentgalaxy%252Cmagnetdl%252Chorriblesubs%252Cnyaasi%7Csort%253Dqualitysize%7Cqualityfilter%253D480p%252Cscr%252Ccam%7Crealdebrid={realDebridKey}/stream/movie/{imdbCode}.json';
|
||||
// private string $baseUrl = 'https://torrentio.strem.fun/providers=yts,eztv,rarbg,1337x,thepiratebay,kickasstorrents,torrentgalaxy,magnetdl,horriblesubs|sort=qualitysize|qualityfilter=480p,cam,unknown|debridoptions=nodownloadlinks|realdebrid=QYYBR7OSQ4VEFKWASDEZ2B4VO67KHUJY6IWOT7HHA7ATXO7QCYDQ/stream/{imdbCode}.json';
|
||||
|
||||
private string $searchUrl;
|
||||
|
||||
public function __construct(
|
||||
#[Autowire(env: 'REAL_DEBRID_KEY')] private string $realDebridKey,
|
||||
private CacheInterface $cache,
|
||||
) {
|
||||
$this->searchUrl = str_replace('{realDebridKey}', $this->realDebridKey, $this->baseUrl);
|
||||
}
|
||||
|
||||
public function search(string $imdbCode, string $type, array $filter = []): array
|
||||
{
|
||||
$cacheKey = "torrentio.{$imdbCode}";
|
||||
|
||||
$results = $this->cache->get($cacheKey, function (ItemInterface $item) use ($imdbCode) {
|
||||
$item->expiresAt(new \DateTimeImmutable("today 11:59 pm"));
|
||||
$response = file_get_contents(str_replace('{imdbCode}', $imdbCode, $this->searchUrl));
|
||||
return json_decode(
|
||||
$response,
|
||||
true
|
||||
);
|
||||
});
|
||||
|
||||
return $this->parse($results, $filter);
|
||||
}
|
||||
|
||||
public function searchBySeriesSeason(MediaResult $series): MediaResult
|
||||
{
|
||||
$imdbCode = $series->imdbId;
|
||||
// foreach ($series->episodes as $season => $episodes) {
|
||||
// foreach ($episodes as $key => $episode) {
|
||||
// $cacheKey = "torrentio.$series->imdbId.$season.{$episode['episode_number']}";
|
||||
// $downloadOptions = $this->cache->get($cacheKey, function (ItemInterface $item) use ($imdbCode, $season, $episode) {
|
||||
// $item->expiresAt(new \DateTimeImmutable("today 11:59 pm"));
|
||||
// $response = file_get_contents(str_replace('{imdbCode}', "$imdbCode:$season:{$episode['episode_number']}", $this->searchUrl));
|
||||
// return json_decode(
|
||||
// $response,
|
||||
// true
|
||||
// );
|
||||
// });
|
||||
// $series->episodes[$season][$key]['download_options'] = $this->parse($downloadOptions, []);
|
||||
// }
|
||||
// }
|
||||
return $series;
|
||||
}
|
||||
|
||||
public function fetchEpisodeResults(string $imdbId, int $season, int $episode): array
|
||||
{
|
||||
$cacheKey = "torrentio.$imdbId.$season.$episode";
|
||||
$results = $this->cache->get($cacheKey, function (ItemInterface $item) use ($imdbId, $season, $episode) {
|
||||
$item->expiresAt(new \DateTimeImmutable("today 11:59 pm"));
|
||||
$response = file_get_contents(str_replace('{imdbCode}', "$imdbId:$season:$episode", $this->searchUrl));
|
||||
return json_decode(
|
||||
$response,
|
||||
true
|
||||
);
|
||||
});
|
||||
|
||||
return $this->parse($results, []);
|
||||
}
|
||||
|
||||
public function parse(array $data, array $filter): array
|
||||
{
|
||||
$ruleEngine = new RuleEngine();
|
||||
foreach ($filter as $rule => $value) {
|
||||
if ('resolution' === $rule) {
|
||||
$ruleEngine->addRule(new Resolution($value));
|
||||
}
|
||||
}
|
||||
|
||||
$results = [];
|
||||
foreach ($data['streams'] as $stream) {
|
||||
if (!str_starts_with($stream['url'], "https")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (
|
||||
array_key_exists('behaviorHints', $stream) &&
|
||||
array_key_exists('bingeGroup', $stream['behaviorHints'])
|
||||
) {
|
||||
$bingeGroup = $stream['behaviorHints']['bingeGroup'];
|
||||
} else {
|
||||
$bingeGroup = '-';
|
||||
}
|
||||
|
||||
$result = ResultFactory::map(
|
||||
$stream['url'],
|
||||
$stream['title'],
|
||||
$bingeGroup
|
||||
);
|
||||
|
||||
if ($ruleEngine->validateAll($result)) {
|
||||
$results[] = $result;
|
||||
}
|
||||
}
|
||||
|
||||
return $results;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user