fix-feat: ajax download call

This commit is contained in:
2025-04-23 16:17:03 -05:00
parent 5402680abf
commit 6dc6fbd449
15 changed files with 153 additions and 19 deletions

View File

@@ -0,0 +1,37 @@
import { Controller } from '@hotwired/stimulus';
/*
* The following line makes this controller "lazy": it won't be downloaded until needed
* See https://github.com/symfony/stimulus-bridge#lazy-controllers
*/
/* stimulusFetch: 'lazy' */
export default class extends Controller {
static values = {
url: String,
title: String,
filename: String,
mediaType: String,
imdbId: String,
}
download() {
fetch('/download', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
},
body: JSON.stringify({
url: this.urlValue,
title: this.titleValue,
filename: this.filenameValue,
mediaType: this.mediaTypeValue,
imdbId: this.imdbIdValue
})
})
.then(res => res.json())
.then(json => {
console.log(json)
})
}
}

View File

@@ -7,6 +7,7 @@ import { Controller } from '@hotwired/stimulus';
/* stimulusFetch: 'lazy' */
export default class extends Controller {
static values = {
tmdbId: String,
imdbId: String
};
@@ -20,7 +21,7 @@ export default class extends Controller {
async setOptions() {
if (this.options.length === 0) {
await fetch(`/torrentio/movies/${this.imdbIdValue}`)
await fetch(`/torrentio/movies/${this.tmdbIdValue}/${this.imdbIdValue}`)
.then(res => res.text())
.then(response => {
this.element.innerHTML = response;

View File

@@ -18,6 +18,7 @@
"php-tmdb/api": "^4.1",
"symfony/asset": "7.2.*",
"symfony/console": "7.2.*",
"symfony/doctrine-messenger": "7.2.*",
"symfony/dotenv": "7.2.*",
"symfony/flex": "^2",
"symfony/form": "7.2.*",

74
composer.lock generated
View File

@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "eeaaf3d88479bdcd00dcb637222408f4",
"content-hash": "0448ecb537f5d169d81a56ba2b3c2cc6",
"packages": [
{
"name": "1tomany/data-uri",
@@ -3469,6 +3469,78 @@
],
"time": "2025-03-25T15:54:33+00:00"
},
{
"name": "symfony/doctrine-messenger",
"version": "v7.2.5",
"source": {
"type": "git",
"url": "https://github.com/symfony/doctrine-messenger.git",
"reference": "c353e6ee6b41748d8ea6faa2d0b84ac501e3ec0c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/doctrine-messenger/zipball/c353e6ee6b41748d8ea6faa2d0b84ac501e3ec0c",
"reference": "c353e6ee6b41748d8ea6faa2d0b84ac501e3ec0c",
"shasum": ""
},
"require": {
"doctrine/dbal": "^3.6|^4",
"php": ">=8.2",
"symfony/messenger": "^6.4|^7.0",
"symfony/service-contracts": "^2.5|^3"
},
"conflict": {
"doctrine/persistence": "<1.3"
},
"require-dev": {
"doctrine/persistence": "^1.3|^2|^3",
"symfony/property-access": "^6.4|^7.0",
"symfony/serializer": "^6.4|^7.0"
},
"type": "symfony-messenger-bridge",
"autoload": {
"psr-4": {
"Symfony\\Component\\Messenger\\Bridge\\Doctrine\\": ""
},
"exclude-from-classmap": [
"/Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony Doctrine Messenger Bridge",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/doctrine-messenger/tree/v7.2.5"
},
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2025-03-25T15:54:33+00:00"
},
{
"name": "symfony/dotenv",
"version": "v7.2.0",

View File

@@ -5,14 +5,7 @@ framework:
transports:
# https://symfony.com/doc/current/messenger.html#transport-configuration
async:
dsn: '%env(MESSENGER_TRANSPORT_DSN)%'
options:
use_notify: true
check_delayed_interval: 60000
retry_strategy:
max_retries: 1
multiplier: 1
async: '%env(MESSENGER_TRANSPORT_DSN)%'
failed: 'doctrine://default?queue_name=failed'
default_bus: messenger.bus.default

View File

@@ -18,7 +18,11 @@ class DownloadController extends AbstractController
public function download(
DownloadMediaInput $input,
): Response {
$this->bus->dispatch($input->toCommand());
try {
$this->bus->dispatch($input->toCommand());
} catch (\Throwable $exception) {
return $this->json(['error' => $exception->getMessage()], Response::HTTP_INTERNAL_SERVER_ERROR);
}
return $this->json(['status' => 200, 'message' => 'Added to Queue']);
}

View File

@@ -17,7 +17,7 @@ final class TorrentioController extends AbstractController
private readonly GetTvShowOptionsHandler $getTvShowOptionsHandler,
) {}
#[Route('/torrentio/movies/{imdbId}', name: 'app_torrentio_movies')]
#[Route('/torrentio/movies/{tmdbId}/{imdbId}', name: 'app_torrentio_movies')]
public function movieOptions(GetMovieOptionsInput $input): Response
{
$results = $this->getMovieOptionsHandler->handle($input->toCommand());

View File

@@ -7,6 +7,7 @@ use OneToMany\RichBundle\Contract\CommandInterface;
class GetMovieOptionsCommand implements CommandInterface
{
public function __construct(
public string $tmdbId,
public string $imdbId,
) {}
}

View File

@@ -2,6 +2,7 @@
namespace App\Torrentio\Action\Handler;
use App\Tmdb\Tmdb;
use App\Torrentio\Action\Result\GetMovieOptionsResult;
use App\Torrentio\Client\Torrentio;
use OneToMany\RichBundle\Contract\CommandInterface;
@@ -11,13 +12,15 @@ use OneToMany\RichBundle\Contract\ResultInterface;
class GetMovieOptionsHandler implements HandlerInterface
{
public function __construct(
private readonly Tmdb $tmdb,
private readonly Torrentio $torrentio,
) {}
public function handle(CommandInterface $command): ResultInterface
{
return new GetMovieOptionsResult(
results: $this->torrentio->search($command->imdbId, 'movies')
media: $this->tmdb->mediaDetails($command->tmdbId, 'movies'),
results: $this->torrentio->search($command->imdbId, 'movies'),
);
}
}

View File

@@ -10,12 +10,15 @@ use OneToMany\RichBundle\Contract\InputInterface;
class GetMovieOptionsInput implements InputInterface
{
public function __construct(
#[SourceRoute('tmdbId')]
public string $tmdbId,
#[SourceRoute('imdbId')]
public string $imdbId,
) {}
public function toCommand(): CommandInterface
{
return new GetMovieOptionsCommand($this->imdbId);
return new GetMovieOptionsCommand($this->tmdbId, $this->imdbId);
}
}

View File

@@ -2,11 +2,13 @@
namespace App\Torrentio\Action\Result;
use App\Tmdb\TmdbResult;
use OneToMany\RichBundle\Contract\ResultInterface;
class GetMovieOptionsResult implements ResultInterface
{
public function __construct(
public TmdbResult $media,
public array $results
) {}
}

View File

@@ -15,7 +15,8 @@ class ResultFactory
$ptn = (object) (new PTN())->parse($title);
return new TorrentioResult(
self::trimTitle($title),
$url,
urldecode($url),
self::setFilename($url),
self::setSize($title),
self::setSeeders($title),
self::setProvider($title),
@@ -33,6 +34,12 @@ class ResultFactory
);
}
public static function setFilename(string $url)
{
$file = explode("/", urldecode($url));
return end($file);
}
public static function setSize(string $title): string
{
$sizeMatch = [];

View File

@@ -7,6 +7,7 @@ class TorrentioResult
public function __construct(
public ?string $title = "-",
public ?string $url = "-",
public ?string $filename = "-",
public ?string $size = "-",
public ?string $seeders = "-",
public ?string $provider = "-",

View File

@@ -27,7 +27,7 @@
{{ include('search/partial/filter.html.twig') }}
{% if "movies" == results.media.mediaType %}
<div class="results" {{ stimulus_controller('movie_results', {imdbId: results.media.imdbId}) }}>
<div class="results" {{ stimulus_controller('movie_results', {tmdbId: results.media.tmdbId, imdbId: results.media.imdbId}) }}>
</div>
{% elseif "tvshows" == results.media.mediaType %}
{% for season, episodes in results.media.episodes %}

View File

@@ -1,4 +1,4 @@
<table class="w-full text-sm text-left rtl:text-right text-gray-500 dark:text-gray-400 hidden"
<table class="w-full text-sm text-left rtl:text-right text-gray-500 dark:text-gray-400 {{ results.media.mediaType == "tvshows" ? "hidden" }}"
{{ stimulus_target(controller, "list") }}
>
<thead class="text-xs text-gray-700 uppercase bg-gray-50 dark:bg-gray-700 dark:text-gray-400">
@@ -34,7 +34,7 @@
</thead>
<tbody>
{% for result in results.results %}
<tr class="bg-white border-b dark:bg-slate-700 dark:border-gray-600 border-gray-200" data-languages="{{ result.languages|json_encode }}" data-season="{{ results.season }}">
<tr class="bg-white border-b dark:bg-slate-700 dark:border-gray-600 border-gray-200" data-languages="{{ result.languages|json_encode }}" {% if "tvshows" == results.media.mediaType %} data-season="{{ results.season }} {% endif %}">
<td id="size" class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-800 dark:text-gray-50">
{{ result.size }}
</td>
@@ -54,7 +54,16 @@
{{ result.languageFlags|raw }}
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-end text-gray-800 dark:text-gray-50 flex flex-row gap-2 items-center justify-end">
<button class="p-1.5 bg-green-600 rounded-md text-gray-50">
<button class="p-1.5 bg-green-600 rounded-md text-gray-50"
{{ stimulus_controller('download_button', {
url: result.url,
title: result.title,
filename: result.filename,
mediaType: results.media.mediaType,
imdbId: results.media.imdbId
}) }}
{{ stimulus_action('download_button', 'download', 'click') }}
>
Download
</button>
<label for="select">