Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| dd52a903f6 | |||
| 5729949774 | |||
| e055ed0c15 | |||
| 46d90e800c | |||
| 9fb513bfbd | |||
| 2384bb2414 | |||
| 7c8fa0c439 |
@@ -51,6 +51,7 @@ export default class extends Controller {
|
|||||||
let selectedCount = 0;
|
let selectedCount = 0;
|
||||||
|
|
||||||
this.options.forEach((option) => {
|
this.options.forEach((option) => {
|
||||||
|
const optionHeader = document.querySelector(`[data-option-id="${option.dataset['localId']}"]`)
|
||||||
const props = {
|
const props = {
|
||||||
"resolution": option.querySelector('#resolution').textContent.trim(),
|
"resolution": option.querySelector('#resolution').textContent.trim(),
|
||||||
"codec": option.querySelector('#codec').textContent.trim(),
|
"codec": option.querySelector('#codec').textContent.trim(),
|
||||||
@@ -62,6 +63,8 @@ export default class extends Controller {
|
|||||||
let include = true;
|
let include = true;
|
||||||
option.classList.add('r-tablerow');
|
option.classList.add('r-tablerow');
|
||||||
option.classList.remove('hidden');
|
option.classList.remove('hidden');
|
||||||
|
optionHeader.classList.add('r-tablerow');
|
||||||
|
optionHeader.classList.remove('hidden');
|
||||||
option.querySelector('input[type="checkbox"]').checked = false;
|
option.querySelector('input[type="checkbox"]').checked = false;
|
||||||
|
|
||||||
for (let [key, value] of Object.entries(activeFilter)) {
|
for (let [key, value] of Object.entries(activeFilter)) {
|
||||||
@@ -88,6 +91,8 @@ export default class extends Controller {
|
|||||||
if (false === include) {
|
if (false === include) {
|
||||||
option.classList.remove('r-tablerow');
|
option.classList.remove('r-tablerow');
|
||||||
option.classList.add('hidden');
|
option.classList.add('hidden');
|
||||||
|
optionHeader.classList.remove('r-tablerow');
|
||||||
|
optionHeader.classList.add('hidden');
|
||||||
} else if (true === firstIncluded) {
|
} else if (true === firstIncluded) {
|
||||||
count = 1;
|
count = 1;
|
||||||
selectedCount = selectedCount + 1;
|
selectedCount = selectedCount + 1;
|
||||||
|
|||||||
@@ -128,6 +128,7 @@ export default class extends Controller {
|
|||||||
let selectedCount = 0;
|
let selectedCount = 0;
|
||||||
|
|
||||||
this.options.forEach((option) => {
|
this.options.forEach((option) => {
|
||||||
|
const optionHeader = document.querySelector(`[data-option-id="${option.dataset['localId']}"]`)
|
||||||
const props = {
|
const props = {
|
||||||
"resolution": option.querySelector('#resolution').textContent.trim(),
|
"resolution": option.querySelector('#resolution').textContent.trim(),
|
||||||
"codec": option.querySelector('#codec').textContent.trim(),
|
"codec": option.querySelector('#codec').textContent.trim(),
|
||||||
@@ -138,6 +139,8 @@ export default class extends Controller {
|
|||||||
let include = true;
|
let include = true;
|
||||||
option.classList.add('r-tablerow');
|
option.classList.add('r-tablerow');
|
||||||
option.classList.remove('hidden');
|
option.classList.remove('hidden');
|
||||||
|
optionHeader.classList.add('r-tablerow');
|
||||||
|
optionHeader.classList.remove('hidden');
|
||||||
option.querySelector('input[type="checkbox"]').checked = false;
|
option.querySelector('input[type="checkbox"]').checked = false;
|
||||||
|
|
||||||
for (let [key, value] of Object.entries(activeFilter)) {
|
for (let [key, value] of Object.entries(activeFilter)) {
|
||||||
@@ -164,6 +167,8 @@ export default class extends Controller {
|
|||||||
if (false === include) {
|
if (false === include) {
|
||||||
option.classList.remove('r-tablerow');
|
option.classList.remove('r-tablerow');
|
||||||
option.classList.add('hidden');
|
option.classList.add('hidden');
|
||||||
|
optionHeader.classList.remove('r-tablerow');
|
||||||
|
optionHeader.classList.add('hidden');
|
||||||
} else if (true === firstIncluded) {
|
} else if (true === firstIncluded) {
|
||||||
count = 1;
|
count = 1;
|
||||||
selectedCount = selectedCount + 1;
|
selectedCount = selectedCount + 1;
|
||||||
|
|||||||
@@ -20,12 +20,6 @@ final class Version20250708033046 extends AbstractMigration
|
|||||||
public function up(Schema $schema): void
|
public function up(Schema $schema): void
|
||||||
{
|
{
|
||||||
// this up() migration is auto-generated, please modify it to your needs
|
// this up() migration is auto-generated, please modify it to your needs
|
||||||
$this->addSql(<<<'SQL'
|
|
||||||
DROP TABLE IF EXISTS sessions
|
|
||||||
SQL);
|
|
||||||
$this->addSql(<<<'SQL'
|
|
||||||
ALTER TABLE download CHANGE created_at created_at DATETIME NOT NULL, CHANGE updated_at updated_at DATETIME NOT NULL
|
|
||||||
SQL);
|
|
||||||
$this->addSql(<<<'SQL'
|
$this->addSql(<<<'SQL'
|
||||||
ALTER TABLE monitor ADD only_future TINYINT(1) NOT NULL DEFAULT 1
|
ALTER TABLE monitor ADD only_future TINYINT(1) NOT NULL DEFAULT 1
|
||||||
SQL);
|
SQL);
|
||||||
@@ -34,14 +28,8 @@ final class Version20250708033046 extends AbstractMigration
|
|||||||
public function down(Schema $schema): void
|
public function down(Schema $schema): void
|
||||||
{
|
{
|
||||||
// this down() migration is auto-generated, please modify it to your needs
|
// this down() migration is auto-generated, please modify it to your needs
|
||||||
$this->addSql(<<<'SQL'
|
|
||||||
CREATE TABLE sessions (sess_id VARBINARY(128) NOT NULL, sess_data LONGBLOB NOT NULL, sess_lifetime INT UNSIGNED NOT NULL, sess_time INT UNSIGNED NOT NULL, INDEX sess_lifetime_idx (sess_lifetime), PRIMARY KEY(sess_id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_bin` ENGINE = InnoDB COMMENT = ''
|
|
||||||
SQL);
|
|
||||||
$this->addSql(<<<'SQL'
|
$this->addSql(<<<'SQL'
|
||||||
ALTER TABLE monitor DROP only_future
|
ALTER TABLE monitor DROP only_future
|
||||||
SQL);
|
SQL);
|
||||||
$this->addSql(<<<'SQL'
|
|
||||||
ALTER TABLE download CHANGE created_at created_at DATETIME DEFAULT NULL, CHANGE updated_at updated_at DATETIME DEFAULT NULL
|
|
||||||
SQL);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
35
migrations/Version20250709161037.php
Normal file
35
migrations/Version20250709161037.php
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace DoctrineMigrations;
|
||||||
|
|
||||||
|
use Doctrine\DBAL\Schema\Schema;
|
||||||
|
use Doctrine\Migrations\AbstractMigration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Auto-generated Migration: Please modify to your needs!
|
||||||
|
*/
|
||||||
|
final class Version20250709161037 extends AbstractMigration
|
||||||
|
{
|
||||||
|
public function getDescription(): string
|
||||||
|
{
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function up(Schema $schema): void
|
||||||
|
{
|
||||||
|
// this up() migration is auto-generated, please modify it to your needs
|
||||||
|
$this->addSql(<<<'SQL'
|
||||||
|
ALTER TABLE download CHANGE batch_id episode_id VARCHAR(255) DEFAULT NULL
|
||||||
|
SQL);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function down(Schema $schema): void
|
||||||
|
{
|
||||||
|
// this down() migration is auto-generated, please modify it to your needs
|
||||||
|
$this->addSql(<<<'SQL'
|
||||||
|
ALTER TABLE download CHANGE episode_id batch_id VARCHAR(255) DEFAULT NULL
|
||||||
|
SQL);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace App\Monitor\Service;
|
namespace App\Base\Service;
|
||||||
|
|
||||||
use Aimeos\Map;
|
use Aimeos\Map;
|
||||||
use App\Download\Framework\Entity\Download;
|
use App\Download\Framework\Entity\Download;
|
||||||
@@ -3,17 +3,12 @@
|
|||||||
namespace App\Download\Action\Handler;
|
namespace App\Download\Action\Handler;
|
||||||
|
|
||||||
use Aimeos\Map;
|
use Aimeos\Map;
|
||||||
|
use App\Base\Service\MediaFiles;
|
||||||
use App\Download\Action\Command\DownloadMediaCommand;
|
use App\Download\Action\Command\DownloadMediaCommand;
|
||||||
use App\Download\Action\Command\DownloadSeasonCommand;
|
use App\Download\Action\Command\DownloadSeasonCommand;
|
||||||
use App\Download\Action\Result\DownloadMediaResult;
|
use App\Download\Action\Result\DownloadMediaResult;
|
||||||
use App\Download\Action\Result\DownloadSeasonResult;
|
use App\Download\Action\Result\DownloadSeasonResult;
|
||||||
use App\Download\DownloadOptionEvaluator;
|
use App\Download\DownloadOptionEvaluator;
|
||||||
use App\Download\Framework\Repository\DownloadRepository;
|
|
||||||
use App\Download\Downloader\DownloaderInterface;
|
|
||||||
use App\Monitor\Action\Command\MonitorTvEpisodeCommand;
|
|
||||||
use App\Monitor\Action\Handler\MonitorTvEpisodeHandler;
|
|
||||||
use App\Monitor\Framework\Entity\Monitor;
|
|
||||||
use App\Monitor\Service\MediaFiles;
|
|
||||||
use App\Tmdb\Tmdb;
|
use App\Tmdb\Tmdb;
|
||||||
use App\Torrentio\Action\Command\GetTvShowOptionsCommand;
|
use App\Torrentio\Action\Command\GetTvShowOptionsCommand;
|
||||||
use App\Torrentio\Action\Handler\GetTvShowOptionsHandler;
|
use App\Torrentio\Action\Handler\GetTvShowOptionsHandler;
|
||||||
|
|||||||
@@ -2,8 +2,8 @@
|
|||||||
|
|
||||||
namespace App\Download\Downloader;
|
namespace App\Download\Downloader;
|
||||||
|
|
||||||
|
use App\Base\Service\MediaFiles;
|
||||||
use App\Download\Framework\Entity\Download;
|
use App\Download\Framework\Entity\Download;
|
||||||
use App\Monitor\Service\MediaFiles;
|
|
||||||
use Doctrine\ORM\EntityManagerInterface;
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
use Symfony\Component\Cache\Adapter\RedisAdapter;
|
use Symfony\Component\Cache\Adapter\RedisAdapter;
|
||||||
use Symfony\Component\Process\Exception\ProcessFailedException;
|
use Symfony\Component\Process\Exception\ProcessFailedException;
|
||||||
@@ -15,7 +15,6 @@ class ProcessDownloader implements DownloaderInterface
|
|||||||
/**
|
/**
|
||||||
* @var RedisAdapter $cache
|
* @var RedisAdapter $cache
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
private EntityManagerInterface $entityManager,
|
private EntityManagerInterface $entityManager,
|
||||||
private MediaFiles $mediaFiles,
|
private MediaFiles $mediaFiles,
|
||||||
@@ -34,11 +33,11 @@ class ProcessDownloader implements DownloaderInterface
|
|||||||
$downloadPreferences = $downloadEntity->getUser()->getDownloadPreferences();
|
$downloadPreferences = $downloadEntity->getUser()->getDownloadPreferences();
|
||||||
$path = $this->getDownloadPath($mediaType, $title, $downloadPreferences);
|
$path = $this->getDownloadPath($mediaType, $title, $downloadPreferences);
|
||||||
|
|
||||||
$processArgs = ['wget', $url];
|
$processArgs = ['wget', '-O', $downloadEntity->getFilename(), $url];
|
||||||
|
|
||||||
if ($downloadEntity->getStatus() === 'Paused') {
|
if ($downloadEntity->getStatus() === 'Paused') {
|
||||||
$downloadEntity->setStatus('In Progress');
|
$downloadEntity->setStatus('In Progress');
|
||||||
$processArgs = ['wget', '-c', $url];
|
$processArgs = ['wget', '-c', '-O', $downloadEntity->getFilename(), $url];
|
||||||
} else {
|
} else {
|
||||||
$downloadEntity->setProgress(0);
|
$downloadEntity->setProgress(0);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,13 +32,6 @@ class ApiController extends AbstractController
|
|||||||
public function download(
|
public function download(
|
||||||
DownloadMediaInput $input,
|
DownloadMediaInput $input,
|
||||||
): Response {
|
): Response {
|
||||||
$ptn = (object) new Ptn()->parse($input->filename);
|
|
||||||
if ($input->mediaType === "tvshows" &&
|
|
||||||
!property_exists($ptn, 'episode') && !property_exists($ptn, 'season')
|
|
||||||
) {
|
|
||||||
$input->filename = $input->episodeId . '_' . $input->filename;
|
|
||||||
}
|
|
||||||
|
|
||||||
$download = $this->downloadRepository->insert(
|
$download = $this->downloadRepository->insert(
|
||||||
$this->getUser(),
|
$this->getUser(),
|
||||||
$input->url,
|
$input->url,
|
||||||
@@ -46,10 +39,8 @@ class ApiController extends AbstractController
|
|||||||
$input->filename,
|
$input->filename,
|
||||||
$input->imdbId,
|
$input->imdbId,
|
||||||
$input->mediaType,
|
$input->mediaType,
|
||||||
"",
|
$input->episodeId,
|
||||||
);
|
);
|
||||||
$this->downloadRepository->getEntityManager()->persist($download);
|
|
||||||
$this->downloadRepository->getEntityManager()->flush();
|
|
||||||
$input->downloadId = $download->getId();
|
$input->downloadId = $download->getId();
|
||||||
$input->userId = $this->getUser()->getId();
|
$input->userId = $this->getUser()->getId();
|
||||||
|
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ class Download
|
|||||||
private ?int $progress = null;
|
private ?int $progress = null;
|
||||||
|
|
||||||
#[ORM\Column(length: 255, nullable: true)]
|
#[ORM\Column(length: 255, nullable: true)]
|
||||||
private ?string $batchId = null;
|
private ?string $episodeId = null;
|
||||||
|
|
||||||
#[ORM\ManyToOne(inversedBy: 'downloads')]
|
#[ORM\ManyToOne(inversedBy: 'downloads')]
|
||||||
private ?User $user = null;
|
private ?User $user = null;
|
||||||
@@ -143,14 +143,14 @@ class Download
|
|||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getBatchId(): ?string
|
public function getEpisodeId(): ?string
|
||||||
{
|
{
|
||||||
return $this->batchId;
|
return $this->episodeId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setBatchId(?string $batchId): static
|
public function setEpisodeId(?string $episodeId): static
|
||||||
{
|
{
|
||||||
$this->batchId = $batchId;
|
$this->episodeId = $episodeId;
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ use App\Download\Framework\Entity\Download;
|
|||||||
use App\User\Framework\Entity\User;
|
use App\User\Framework\Entity\User;
|
||||||
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
|
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
|
||||||
use Doctrine\Persistence\ManagerRegistry;
|
use Doctrine\Persistence\ManagerRegistry;
|
||||||
|
use Nihilarr\PTN;
|
||||||
use Symfony\Component\Security\Core\User\UserInterface;
|
use Symfony\Component\Security\Core\User\UserInterface;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -62,9 +63,15 @@ class DownloadRepository extends ServiceEntityRepository
|
|||||||
string $filename,
|
string $filename,
|
||||||
string $imdbId,
|
string $imdbId,
|
||||||
string $mediaType,
|
string $mediaType,
|
||||||
string $batchId,
|
?string $episodeId = null,
|
||||||
string $status = 'New'
|
string $status = 'New'
|
||||||
): Download {
|
): Download {
|
||||||
|
$ptn = (object) new Ptn()->parse($filename);
|
||||||
|
if ($mediaType === "tvshows" &&
|
||||||
|
!property_exists($ptn, 'episode') && !property_exists($ptn, 'season')
|
||||||
|
) {
|
||||||
|
$filename = $episodeId . '_' . $filename;
|
||||||
|
}
|
||||||
/** @var User $user */
|
/** @var User $user */
|
||||||
$download = (new Download())
|
$download = (new Download())
|
||||||
->setUser($user)
|
->setUser($user)
|
||||||
@@ -73,7 +80,7 @@ class DownloadRepository extends ServiceEntityRepository
|
|||||||
->setFilename($filename)
|
->setFilename($filename)
|
||||||
->setImdbId($imdbId)
|
->setImdbId($imdbId)
|
||||||
->setMediaType($mediaType)
|
->setMediaType($mediaType)
|
||||||
->setBatchId($batchId)
|
->setEpisodeId($episodeId)
|
||||||
->setProgress(0)
|
->setProgress(0)
|
||||||
->setStatus($status);
|
->setStatus($status);
|
||||||
|
|
||||||
|
|||||||
@@ -25,7 +25,6 @@ readonly class MonitorMovieHandler implements HandlerInterface
|
|||||||
public function __construct(
|
public function __construct(
|
||||||
private MonitorRepository $movieMonitorRepository,
|
private MonitorRepository $movieMonitorRepository,
|
||||||
private GetMovieOptionsHandler $getMovieOptionsHandler,
|
private GetMovieOptionsHandler $getMovieOptionsHandler,
|
||||||
private MonitorOptionEvaluator $monitorOptionEvaluator,
|
|
||||||
private EntityManagerInterface $entityManager,
|
private EntityManagerInterface $entityManager,
|
||||||
private MessageBusInterface $bus,
|
private MessageBusInterface $bus,
|
||||||
private LoggerInterface $logger,
|
private LoggerInterface $logger,
|
||||||
|
|||||||
@@ -3,12 +3,12 @@
|
|||||||
namespace App\Monitor\Action\Handler;
|
namespace App\Monitor\Action\Handler;
|
||||||
|
|
||||||
use Aimeos\Map;
|
use Aimeos\Map;
|
||||||
|
use App\Base\Service\MediaFiles;
|
||||||
use App\Monitor\Action\Command\MonitorTvEpisodeCommand;
|
use App\Monitor\Action\Command\MonitorTvEpisodeCommand;
|
||||||
use App\Monitor\Action\Command\MonitorTvSeasonCommand;
|
use App\Monitor\Action\Command\MonitorTvSeasonCommand;
|
||||||
use App\Monitor\Action\Result\MonitorTvSeasonResult;
|
use App\Monitor\Action\Result\MonitorTvSeasonResult;
|
||||||
use App\Monitor\Framework\Entity\Monitor;
|
use App\Monitor\Framework\Entity\Monitor;
|
||||||
use App\Monitor\Framework\Repository\MonitorRepository;
|
use App\Monitor\Framework\Repository\MonitorRepository;
|
||||||
use App\Monitor\Service\MediaFiles;
|
|
||||||
use App\Tmdb\Tmdb;
|
use App\Tmdb\Tmdb;
|
||||||
use DateTimeImmutable;
|
use DateTimeImmutable;
|
||||||
use Doctrine\ORM\EntityManagerInterface;
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
|
|||||||
@@ -3,12 +3,12 @@
|
|||||||
namespace App\Monitor\Action\Handler;
|
namespace App\Monitor\Action\Handler;
|
||||||
|
|
||||||
use Aimeos\Map;
|
use Aimeos\Map;
|
||||||
|
use App\Base\Service\MediaFiles;
|
||||||
use App\Monitor\Action\Command\MonitorMovieCommand;
|
use App\Monitor\Action\Command\MonitorMovieCommand;
|
||||||
use App\Monitor\Action\Command\MonitorTvEpisodeCommand;
|
use App\Monitor\Action\Command\MonitorTvEpisodeCommand;
|
||||||
use App\Monitor\Action\Result\MonitorTvShowResult;
|
use App\Monitor\Action\Result\MonitorTvShowResult;
|
||||||
use App\Monitor\Framework\Entity\Monitor;
|
use App\Monitor\Framework\Entity\Monitor;
|
||||||
use App\Monitor\Framework\Repository\MonitorRepository;
|
use App\Monitor\Framework\Repository\MonitorRepository;
|
||||||
use App\Monitor\Service\MediaFiles;
|
|
||||||
use App\Tmdb\Tmdb;
|
use App\Tmdb\Tmdb;
|
||||||
use Carbon\Carbon;
|
use Carbon\Carbon;
|
||||||
use DateTimeImmutable;
|
use DateTimeImmutable;
|
||||||
@@ -149,7 +149,7 @@ readonly class MonitorTvShowHandler implements HandlerInterface
|
|||||||
'monitorType' => 'tvepisode',
|
'monitorType' => 'tvepisode',
|
||||||
'season' => $episode['season_number'],
|
'season' => $episode['season_number'],
|
||||||
'episode' => $episode['episode_number'],
|
'episode' => $episode['episode_number'],
|
||||||
'status' => ['New', 'Active', 'In Progress']
|
'status' => ['New', 'Active', 'In Progress', 'Complete']
|
||||||
]) !== null;
|
]) !== null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,95 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Monitor\Service;
|
|
||||||
|
|
||||||
use Aimeos\Map;
|
|
||||||
use App\Monitor\Framework\Entity\Monitor;
|
|
||||||
use App\Torrentio\Result\TorrentioResult;
|
|
||||||
|
|
||||||
class MonitorOptionEvaluator
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* @param Monitor $monitor
|
|
||||||
* @param TorrentioResult[] $results
|
|
||||||
* @return TorrentioResult|null
|
|
||||||
* @throws \Throwable
|
|
||||||
*/
|
|
||||||
public function evaluateOptions(Monitor $monitor, array $results): ?TorrentioResult
|
|
||||||
{
|
|
||||||
$sizeLow = 000;
|
|
||||||
$sizeHigh = 4096;
|
|
||||||
|
|
||||||
$bestMatches = [];
|
|
||||||
$matches = [];
|
|
||||||
|
|
||||||
$userPreferences = $monitor->getUser()->getUserPreferenceValues();
|
|
||||||
|
|
||||||
foreach ($results as $result) {
|
|
||||||
if (!in_array($userPreferences['language'], $result->languages)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($result->resolution === $userPreferences['resolution']
|
|
||||||
&& $result->codec === $userPreferences['codec']
|
|
||||||
) {
|
|
||||||
$bestMatches[] = $result;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($userPreferences['resolution'] === '2160p'
|
|
||||||
&& $userPreferences['codec'] === $result->codec
|
|
||||||
&& $result->resolution === '1080p'
|
|
||||||
) {
|
|
||||||
$matches[] = $result;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($userPreferences['codec'] === 'h264'
|
|
||||||
&& $userPreferences['resolution'] === $result->resolution
|
|
||||||
&& $result->codec === 'h265'
|
|
||||||
) {
|
|
||||||
$matches[] = $result;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (($userPreferences['codec'] === null )
|
|
||||||
&& ($userPreferences['resolution'] === null )) {
|
|
||||||
$matches[] = $result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$sizeMatches = [];
|
|
||||||
|
|
||||||
foreach ($bestMatches as $result) {
|
|
||||||
if (str_contains($result->size, 'GB')) {
|
|
||||||
$size = (int) trim(str_replace('GB', '', $result->size)) * 1024;
|
|
||||||
} else {
|
|
||||||
$size = (int) trim(str_replace('MB', '', $result->size));
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($size > $sizeLow && $size < $sizeHigh) {
|
|
||||||
$sizeMatches[] = $result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!empty($sizeMatches)) {
|
|
||||||
return Map::from($sizeMatches)->usort(fn($a, $b) => $a->seeders <=> $b->seeders)->last();
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ($matches as $result) {
|
|
||||||
$size = 0;
|
|
||||||
if (str_contains($result->size, 'GB')) {
|
|
||||||
$size = (int) trim(str_replace('GB', '', $result->size)) * 1024;
|
|
||||||
} else {
|
|
||||||
$size = (int) trim(str_replace('MB', '', $result->size));
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($size > $sizeLow && $size < $sizeHigh) {
|
|
||||||
$sizeMatches[] = $result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!empty($sizeMatches)) {
|
|
||||||
return Map::from($sizeMatches)->usort(fn($a, $b) => $a->seeders <=> $b->seeders)->last();
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
namespace App\Search\Action\Handler;
|
namespace App\Search\Action\Handler;
|
||||||
|
|
||||||
|
use App\Base\Service\MediaFiles;
|
||||||
use App\Search\Action\Command\GetMediaInfoCommand;
|
use App\Search\Action\Command\GetMediaInfoCommand;
|
||||||
use App\Search\Action\Result\GetMediaInfoResult;
|
use App\Search\Action\Result\GetMediaInfoResult;
|
||||||
use App\Tmdb\Tmdb;
|
use App\Tmdb\Tmdb;
|
||||||
@@ -14,12 +15,19 @@ class GetMediaInfoHandler implements HandlerInterface
|
|||||||
{
|
{
|
||||||
public function __construct(
|
public function __construct(
|
||||||
private readonly Tmdb $tmdb,
|
private readonly Tmdb $tmdb,
|
||||||
|
private readonly MediaFiles $mediaFiles
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
public function handle(CommandInterface $command): ResultInterface
|
public function handle(CommandInterface $command): ResultInterface
|
||||||
{
|
{
|
||||||
$media = $this->tmdb->mediaDetails($command->imdbId, $command->mediaType);
|
$media = $this->tmdb->mediaDetails($command->imdbId, $command->mediaType);
|
||||||
|
|
||||||
|
if ("tvshows" === $command->mediaType) {
|
||||||
|
foreach ($media->episodes[$command->season] as $key => $episode) {
|
||||||
|
$media->episodes[$command->season][$key]['file'] = $this->mediaFiles->episodeExists($media->title, $command->season, $episode['episode_number']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return new GetMediaInfoResult($media, $command->season);
|
return new GetMediaInfoResult($media, $command->season);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
namespace App\Torrentio\Action\Handler;
|
namespace App\Torrentio\Action\Handler;
|
||||||
|
|
||||||
use App\Monitor\Service\MediaFiles;
|
use App\Base\Service\MediaFiles;
|
||||||
use App\Tmdb\Tmdb;
|
use App\Tmdb\Tmdb;
|
||||||
use App\Torrentio\Action\Result\GetMovieOptionsResult;
|
use App\Torrentio\Action\Result\GetMovieOptionsResult;
|
||||||
use App\Torrentio\Client\Torrentio;
|
use App\Torrentio\Client\Torrentio;
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
namespace App\Torrentio\Action\Handler;
|
namespace App\Torrentio\Action\Handler;
|
||||||
|
|
||||||
use App\Monitor\Service\MediaFiles;
|
use App\Base\Service\MediaFiles;
|
||||||
use App\Tmdb\Tmdb;
|
use App\Tmdb\Tmdb;
|
||||||
use App\Torrentio\Action\Command\GetTvShowOptionsCommand;
|
use App\Torrentio\Action\Command\GetTvShowOptionsCommand;
|
||||||
use App\Torrentio\Action\Result\GetTvShowOptionsResult;
|
use App\Torrentio\Action\Result\GetTvShowOptionsResult;
|
||||||
|
|||||||
@@ -21,7 +21,6 @@ class ResultFactory
|
|||||||
string $bingeGroup = "-"
|
string $bingeGroup = "-"
|
||||||
) {
|
) {
|
||||||
$ptn = (object) (new PTN())->parse($title);
|
$ptn = (object) (new PTN())->parse($title);
|
||||||
// dump($ptn);
|
|
||||||
return new TorrentioResult(
|
return new TorrentioResult(
|
||||||
self::trimTitle($title),
|
self::trimTitle($title),
|
||||||
urldecode($url),
|
urldecode($url),
|
||||||
@@ -40,7 +39,8 @@ class ResultFactory
|
|||||||
$ptn->episode ?? "-",
|
$ptn->episode ?? "-",
|
||||||
self::setLanguages($title),
|
self::setLanguages($title),
|
||||||
self::setLanguageFlags($title),
|
self::setLanguageFlags($title),
|
||||||
false
|
false,
|
||||||
|
uniqid()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -23,5 +23,6 @@ class TorrentioResult
|
|||||||
public ?array $languages = [],
|
public ?array $languages = [],
|
||||||
public ?string $languageFlags = "-",
|
public ?string $languageFlags = "-",
|
||||||
public ?bool $selected = false,
|
public ?bool $selected = false,
|
||||||
|
public ?string $localId = "-"
|
||||||
) {}
|
) {}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,12 +2,9 @@
|
|||||||
|
|
||||||
namespace App\Twig\Extensions;
|
namespace App\Twig\Extensions;
|
||||||
|
|
||||||
use App\Monitor\Framework\Entity\Monitor;
|
use App\Base\Service\MediaFiles;
|
||||||
use App\Monitor\Service\MediaFiles;
|
|
||||||
use App\Torrentio\Action\Result\GetTvShowOptionsResult;
|
use App\Torrentio\Action\Result\GetTvShowOptionsResult;
|
||||||
use App\Torrentio\Result\TorrentioResult;
|
|
||||||
use ChrisUllyott\FileSize;
|
use ChrisUllyott\FileSize;
|
||||||
use Tmdb\Model\Tv\Episode;
|
|
||||||
use Twig\Attribute\AsTwigFilter;
|
use Twig\Attribute\AsTwigFilter;
|
||||||
use Twig\Attribute\AsTwigFunction;
|
use Twig\Attribute\AsTwigFunction;
|
||||||
|
|
||||||
|
|||||||
@@ -99,7 +99,7 @@ class User implements UserInterface, PasswordAuthenticatedUserInterface
|
|||||||
*/
|
*/
|
||||||
public function getUserIdentifier(): string
|
public function getUserIdentifier(): string
|
||||||
{
|
{
|
||||||
return (string) $this->username ?? $this->email;
|
return (string) $this->email;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<div{{ attributes.defaults(stimulus_controller('download_list')) }} class="min-w-48" >
|
<div{{ attributes.defaults(stimulus_controller('download_list')) }} class="min-w-48 overflow-scroll" >
|
||||||
{% set table_body_id = (type == "complete") ? "complete_downloads" : "active_downloads" %}
|
{% set table_body_id = (type == "complete") ? "complete_downloads" : "active_downloads" %}
|
||||||
|
|
||||||
{% if this.isWidget == false %}
|
{% if this.isWidget == false %}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<div{{ attributes.defaults(stimulus_controller('monitor_list')) }}>
|
<div{{ attributes.defaults(stimulus_controller('monitor_list')) }} class="overflow-scroll">
|
||||||
{% if this.isWidget == false %}
|
{% if this.isWidget == false %}
|
||||||
<div class="flex flex-row mb-2 justify-end">
|
<div class="flex flex-row mb-2 justify-end">
|
||||||
<twig:DownloadSearch search_path="app_search" placeholder="Find {{ type == "complete" ? "a" : "an" }} {{ type }} monitor..." />
|
<twig:DownloadSearch search_path="app_search" placeholder="Find {{ type == "complete" ? "a" : "an" }} {{ type }} monitor..." />
|
||||||
|
|||||||
@@ -39,6 +39,31 @@
|
|||||||
<small class="py-1 px-1.5 mr-1 grow-0 font-bold bg-gray-700 rounded-lg font-normal text-white" title="Air date {{ episode['name'] }}">
|
<small class="py-1 px-1.5 mr-1 grow-0 font-bold bg-gray-700 rounded-lg font-normal text-white" title="Air date {{ episode['name'] }}">
|
||||||
{{ episode['air_date']|date(null, 'UTC') }}
|
{{ episode['air_date']|date(null, 'UTC') }}
|
||||||
</small>
|
</small>
|
||||||
|
|
||||||
|
{% if episode['file'] != false %}
|
||||||
|
<span data-controller="popover">
|
||||||
|
<template data-popover-target="content">
|
||||||
|
<div data-popover-target="card" class="absolute z-40 p-1 bg-stone-400 p-1 text-black rounded-md m-1 animate-fade">
|
||||||
|
<p class="font-bold text-sm text-left">Existing file(s) for this episode:</p>
|
||||||
|
<ul class="list-disc ml-3">
|
||||||
|
<li class="font-normal">{{ episode['file'].realPath|strip_media_path }} — <strong>{{ episode['file'].size|filesize }}</strong></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<small
|
||||||
|
class="py-1 px-1.5 mr-1 grow-0 font-bold bg-blue-600 rounded-lg text-center text-white"
|
||||||
|
data-action="mouseenter->popover#show mouseleave->popover#hide"
|
||||||
|
>
|
||||||
|
exists
|
||||||
|
</small>
|
||||||
|
</span>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if episode['file'] == false %}
|
||||||
|
<small class="py-1 px-1.5 mr-1 grow-0 font-bold bg-rose-600 rounded-lg text-white" title="Episode has not been downloaded yet.">
|
||||||
|
missing
|
||||||
|
</small>
|
||||||
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-col gap-4 justify-between">
|
<div class="flex flex-col gap-4 justify-between">
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
{% if results.file != false %}
|
{% if results.file != false %}
|
||||||
<div class="p-3 bg-stone-400 p-1 text-black rounded-md m-1 animate-fade">
|
<div class="p-3 bg-stone-400 p-1 text-black rounded-md m-1 animate-fade">
|
||||||
<p class="font-bold text-sm text-left">Existing file(s) for this movie:</p>
|
<p class="font-bold text-sm text-left">Existing file(s) for this movie:</p>
|
||||||
<ul class="list-disc ml-3">
|
<ul class="list-disc ml-3 overflow-scroll">
|
||||||
<li class="font-normal">{{ results.file.realPath|strip_media_path }} — <strong>{{ results.file.size|filesize }}</strong></li>
|
<li class="font-normal">{{ results.file.realPath|strip_media_path }} — <strong>{{ results.file.size|filesize }}</strong></li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
>
|
>
|
||||||
<thead class="text-xs text-gray-700 uppercase dark:text-gray-400">
|
<thead class="text-xs text-gray-700 uppercase dark:text-gray-400">
|
||||||
{% for result in results.results %}
|
{% for result in results.results %}
|
||||||
<tr class="dark:bg-stone-600 overflow-hidden flex flex-col md:flex-col flex-no wrap md:table-row border-b border-gray-500">
|
<tr data-option-id="{{ result.localId }}" class="dark:bg-stone-600 overflow-hidden flex flex-col md:flex-col flex-no wrap md:table-row border-b border-gray-500">
|
||||||
<th scope="col"
|
<th scope="col"
|
||||||
class="px-4 py-4 leading-[20px] font-medium text-gray-900 whitespace-nowrap dark:text-white">
|
class="px-4 py-4 leading-[20px] font-medium text-gray-900 whitespace-nowrap dark:text-white">
|
||||||
Size
|
Size
|
||||||
@@ -41,7 +41,7 @@
|
|||||||
</thead>
|
</thead>
|
||||||
<tbody class="flex-1 sm:flex-none">
|
<tbody class="flex-1 sm:flex-none">
|
||||||
{% for result in results.results %}
|
{% for result in results.results %}
|
||||||
<tr class="bg-white dark:bg-slate-700 flex flex-col flex-no wrap r-tablerow border-b border-gray-500" data-provider="{{ result.provider }}" data-quality="{{ result.quality }}" data-languages="{{ result.languages|json_encode }}" {% if "tvshows" == results.media.mediaType %} data-season="{{ results.season }}"{% endif %}>
|
<tr class="bg-white dark:bg-slate-700 flex flex-col flex-no wrap r-tablerow border-b border-gray-500" data-local-id="{{ result.localId }}" data-provider="{{ result.provider }}" data-quality="{{ result.quality }}" data-languages="{{ result.languages|json_encode }}" {% if "tvshows" == results.media.mediaType %} data-season="{{ results.season }}"{% endif %}>
|
||||||
<td id="size" class="px-4 py-4 whitespace-nowrap text-sm font-medium text-gray-800 dark:text-gray-50">
|
<td id="size" class="px-4 py-4 whitespace-nowrap text-sm font-medium text-gray-800 dark:text-gray-50">
|
||||||
{{ result.size }}
|
{{ result.size }}
|
||||||
</td>
|
</td>
|
||||||
|
|||||||
Reference in New Issue
Block a user