Compare commits

...

6 Commits

15 changed files with 73 additions and 47 deletions

View File

@@ -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;

View File

@@ -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;

View File

@@ -1,22 +1,4 @@
services: services:
caddy:
image: caddy:2.9.1
restart: unless-stopped
cap_add:
- NET_ADMIN
ports:
- "80:80"
- "443:443"
- "443:443/udp"
volumes:
- $PWD/../../bash/caddy:/etc/caddy
- $PWD/../../bash/certs:/etc/ssl
# The "entrypoint" into the application. This reverse proxy
# proxies traffic back to their respective services. If not
# running behind a reverse proxy inject your SSL certificates
# into this container.
# This container runs the actual web app in a php:8.4-fpm
# base container.
app: app:
image: code.caldwell.digital/home/torsearch-app:latest image: code.caldwell.digital/home/torsearch-app:latest
ports: ports:
@@ -48,7 +30,7 @@ services:
- ./downloads/tvshows:/var/download/tvshows - ./downloads/tvshows:/var/download/tvshows
environment: environment:
TZ: America/Chicago TZ: America/Chicago
command: -vvv command: -vv --time-limit=3600 --limit=10
env_file: env_file:
- .env - .env
restart: always restart: always
@@ -68,6 +50,7 @@ services:
- ./downloads/tvshows:/var/download/tvshows - ./downloads/tvshows:/var/download/tvshows
env_file: env_file:
- .env - .env
command: -vv
environment: environment:
TZ: America/Chicago TZ: America/Chicago
restart: always restart: always

View 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);
}
}

View File

@@ -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);
} }

View File

@@ -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();

View File

@@ -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;
} }

View File

@@ -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);

View File

@@ -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()
); );
} }

View File

@@ -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 = "-"
) {} ) {}
} }

View File

@@ -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;
} }
/** /**

View File

@@ -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 %}

View File

@@ -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..." />

View File

@@ -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 }} &mdash; <strong>{{ results.file.size|filesize }}</strong></li> <li class="font-normal">{{ results.file.realPath|strip_media_path }} &mdash; <strong>{{ results.file.size|filesize }}</strong></li>
</ul> </ul>
</div> </div>

View File

@@ -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>