Compare commits

...

3 Commits

Author SHA1 Message Date
f9ddd70668 wip: apparently torrentio doesn't need configuration 2025-08-31 09:06:29 -05:00
33bfe40b11 fix: increases columns size 2025-08-31 00:23:27 -05:00
f3a2f35571 fix: adds favicon 2025-08-31 00:20:35 -05:00
20 changed files with 563 additions and 29 deletions

View File

@@ -168,6 +168,7 @@ dialog[data-dialog-target="dialog"][closing] {
background: transparent;
}
form[name="torrentio_preferences_form"],
#filter {
.ts-wrapper {
box-shadow: none !important;

View File

@@ -0,0 +1,47 @@
<?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 Version20250831013403 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'
DROP TABLE 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'
ALTER TABLE user_preference CHANGE preference_value preference_value VARCHAR(1024) DEFAULT NULL
SQL);
}
public function down(Schema $schema): void
{
// 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'
ALTER TABLE download CHANGE created_at created_at DATETIME DEFAULT NULL, CHANGE updated_at updated_at DATETIME DEFAULT NULL
SQL);
$this->addSql(<<<'SQL'
ALTER TABLE user_preference CHANGE preference_value preference_value VARCHAR(255) DEFAULT NULL
SQL);
}
}

BIN
public/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

View File

@@ -4,7 +4,6 @@ namespace App\Base\Framework\Command;
use App\User\Framework\Entity\Preference;
use App\User\Framework\Entity\UserPreference;
use App\User\Framework\Repository\PreferenceOptionRepository;
use App\User\Framework\Repository\PreferencesRepository;
use App\User\Framework\Repository\UserRepository;
use Symfony\Component\Console\Attribute\AsCommand;
@@ -139,9 +138,16 @@ class SeedDatabaseCommand extends Command
'id' => 'enable_ical_up_ep',
'name' => 'Enable a publicly available iCal calendar?',
'description' => 'Enable a publicly accessible iCal URL for your upcoming episodes.',
'enabled' => false,
'enabled' => true,
'type' => 'calendar'
],
[
'id' => 'torrentio_url',
'name' => 'A custom Torrentio URL',
'description' => 'If you want to use a custom Torrentio URL, enter it here. Otherwise, leave it blank to use the one provided as an environment variable.',
'enabled' => true,
'type' => 'torrentio'
],
];
}
}

View File

@@ -9,13 +9,14 @@ use Carbon\Carbon;
use App\Torrentio\Exception\TorrentioRateLimitException;
use GuzzleHttp\Client;
use Psr\Log\LoggerInterface;
use Symfony\Bundle\SecurityBundle\Security;
use Symfony\Component\DependencyInjection\Attribute\Autowire;
use Symfony\Contracts\Cache\ItemInterface;
use Symfony\Contracts\Cache\TagAwareCacheInterface;
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';
private string $baseUrl = 'https://torrentio.strem.fun/realdebrid={realDebridKey}/stream/movie';
private string $searchUrl;
@@ -25,8 +26,15 @@ class Torrentio
#[Autowire(env: 'REAL_DEBRID_KEY')] private string $realDebridKey,
private TagAwareCacheInterface $cache,
private LoggerInterface $logger,
private Security $security,
) {
$this->searchUrl = str_replace('{realDebridKey}', $this->realDebridKey, $this->baseUrl);
// $this->searchUrl = str_replace('{realDebridKey}', $this->realDebridKey, $this->baseUrl);
$user = $this->security->getUser();
$this->searchUrl = $user->getUserPreference('torrentio_url')->getPreferenceValue() . '/stream/movie';
// dd($this->searchUrl);
$this->client = new Client([
'base_uri' => $this->searchUrl,
]);
@@ -36,25 +44,26 @@ class Torrentio
{
$cacheKey = "torrentio.{$imdbCode}";
$results = $this->cache->get($cacheKey, function (ItemInterface $item) use ($imdbCode, $type) {
$item->expiresAt(Carbon::now()->addHour()->setMinute(0)->setSecond(0));
$item->tag(['torrentio', $type, $imdbCode]);
// $results = $this->cache->get($cacheKey, function (ItemInterface $item) use ($imdbCode, $type) {
// $item->expiresAt(Carbon::now()->addHour()->setMinute(0)->setSecond(0));
// $item->tag(['torrentio', $type, $imdbCode]);
try {
$response = $this->client->get("$this->searchUrl/$imdbCode.json");
return json_decode(
$results = json_decode(
$response->getBody()->getContents(),
true
);
// dd($results);
} catch (\Throwable $exception) {
if ($exception->getCode() === 429) {
$this->logger->warning("> [TorrentioClient] Rate limit exceeded");
return null;
throw $exception;
}
}
$this->logger->error("> [TorrentioClient] Request error: " . $response->getStatusCode() . " - " . $response->getBody()->getContents());
return [];
});
// return [];
// });
if (true === $parseResults) {
return $this->parse($results);
@@ -65,26 +74,26 @@ class Torrentio
public function fetchEpisodeResults(string $imdbId, int $season, int $episode, bool $parseResults = true): array
{
$cacheKey = "torrentio.$imdbId.$season.$episode";
$results = $this->cache->get($cacheKey, function (ItemInterface $item) use ($imdbId, $season, $episode) {
$item->expiresAt(Carbon::now()->addHour()->setMinute(0)->setSecond(0));
$item->tag(['torrentio', 'tvshows', 'torrentio.tvshows', $imdbId, "torrentio.$imdbId", "$imdbId.$season", "torrentio.$imdbId.$season", "$imdbId.$season.$episode", "torrentio.$imdbId.$season.$episode"]);
// $cacheKey = "torrentio.$imdbId.$season.$episode";
// $results = $this->cache->get($cacheKey, function (ItemInterface $item) use ($imdbId, $season, $episode) {
// $item->expiresAt(Carbon::now()->addHour()->setMinute(0)->setSecond(0));
// $item->tag(['torrentio', 'tvshows', 'torrentio.tvshows', $imdbId, "torrentio.$imdbId", "$imdbId.$season", "torrentio.$imdbId.$season", "$imdbId.$season.$episode", "torrentio.$imdbId.$season.$episode"]);
try {
$response = $this->client->get("$this->searchUrl/$imdbId:$season:$episode.json");
return json_decode(
$results = json_decode(
$response->getBody()->getContents(),
true
);
} catch (\Throwable $exception) {
if ($exception->getCode() === 429) {
$this->logger->warning("> [TorrentioClient] Rate limit exceeded");
return null;
throw $exception;
}
}
$this->logger->error("> [TorrentioClient] Request error: " . $response->getStatusCode() . " - " . $response->getBody()->getContents());
return [];
});
// return [];
// });
if (null === $results) {
throw new TorrentioRateLimitException();

View File

@@ -0,0 +1,50 @@
<?php
namespace App\Torrentio\Client;
use App\Values\TorrentioDebridProviderChoices;
class TorrentioUrl
{
const BASE_URL = "https://torrentio.strem.fun/";
public ?array $providers = null;
public ?array $language = null;
public ?array $qualityfilter = null;
public ?string $sorting = null;
public ?string $debridProvider = null;
public ?string $limit = null;
public ?string $sizefilter = null;
public ?string $debridToken = null;
public function __toString(): string
{
$result = "";
if (null !== $this->debridProvider && null !== $this->debridToken) {
$result .= "$this->debridProvider=$this->debridToken";
}
return static::BASE_URL . $result;
}
public static function fromString(string $url): self
{
$arrayData = ['providers', 'language', 'qualityfilter'];
$data = explode('|', str_replace('/', '', urldecode(urldecode(parse_url($url)['path']))));
$result = new self();
foreach ($data as $item) {$item = explode('=', $item);
if (count($item) !== 2) continue;
if (in_array($item[0], array_keys(TorrentioDebridProviderChoices::$providers))) {
$result->debridProvider = $item[0];
$result->debridToken = $item[1];
} elseif (in_array($item[0], $arrayData)) {
$result->{$item[0]} = explode(',', $item[1]);
} else {
$result->{$item[0]} = $item[1];
}
}
return $result;
}
}

View File

@@ -0,0 +1,99 @@
<?php
namespace App\Torrentio\Framework\Form;
use Aimeos\Map;
use App\User\Database\ProviderList;
use App\Values\TorrentioDebridProviderChoices;
use App\Values\TorrentioExcludeQualityChoices;
use App\Values\TorrentioLanguageChoices;
use App\Values\TorrentioProviderChoices;
use App\Values\TorrentioSortChoices;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
class TorrentioPreferencesForm extends AbstractType
{
public function __construct(
private readonly UrlGeneratorInterface $urlGenerator,
) {}
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$this->addChoiceField($builder, 'providers', TorrentioProviderChoices::asSelectOptions());
$this->addChoiceField($builder, 'sorting', TorrentioSortChoices::asSelectOptions(), 1);
$this->addChoiceField($builder, 'language', TorrentioLanguageChoices::asSelectOptions());
$this->addChoiceField($builder, 'qualityfilter', TorrentioExcludeQualityChoices::asSelectOptions());
$this->addTextField($builder, 'limit');
$this->addTextField($builder, 'sizefilter');
$this->addChoiceField($builder, 'debridProvider', TorrentioDebridProviderChoices::asSelectOptions(), 1);
$this->addTextField($builder, 'debridToken');
}
private function addChoiceField(FormBuilderInterface $builder, string $fieldName, array $choices, ?int $maxItems = null): void
{
$question = [
'attr' => [
'class' => 'min-w-24 text-input mb-4',
],
'row_attr' => [
'class' => 'filter-label text-white'
],
'label_attr' => ['class' => 'block font-semibold mb-2'],
'choices' => $choices,
'required' => false,
];
if (null === $maxItems) {
$question['multiple'] = true;
$question['attr'] += [
'data-result-filter-target' => $fieldName,
'data-controller' => 'symfony--ux-autocomplete--autocomplete',
'data-symfony--ux-autocomplete--autocomplete-tom-select-options-value' => json_encode([
'highlight' => false,
'maxItems' => $maxItems,
]),
];
} else {
$question += [
'multiple' => false,
'expanded' => false,
];
}
$builder->add($fieldName, ChoiceType::class, $question);
}
public function addTextField(FormBuilderInterface $builder, string $fieldName, ?array $options = null): void
{
$optinos = $options ?? [
'required' => false,
'attr' => [
'method' => 'post',
'action' => $this->urlGenerator->generate('app.torrentio-preferences.save'),
'class' => 'min-w-24 text-input mb-4 block',
]
];
$builder->add(
$fieldName,
TextType::class,
$optinos,
);
}
public function configureOptions(OptionsResolver $resolver): void
{
$resolver->setDefaults([
'id' => 'torrentio-preferences-form',
// 'action' => $this->urlGenerator->generate('app_user_media_preferences_submit'),
'attr' => [
'class' => 'filter-items w-full p-4 text-md dark:text-gray-50 rounded-lg',
]
]);
}
}

View File

@@ -0,0 +1,14 @@
<?php
namespace App\User\Action\Command;
use App\Torrentio\Client\TorrentioUrl;
use OneToMany\RichBundle\Contract\CommandInterface;
/** @implements CommandInterface<SaveUserMediaPreferencesCommand> */
class SaveUserTorrentioPreferencesCommand implements CommandInterface
{
public function __construct(
public TorrentioUrl $torrentioUrl,
) {}
}

View File

@@ -0,0 +1,48 @@
<?php
namespace App\User\Action\Handler;
use App\User\Action\Command\SaveUserMediaPreferencesCommand;
use App\User\Action\Result\SaveUserDownloadPreferencesResult;
use App\User\Action\Result\SaveUserMediaPreferencesResult;
use App\User\Framework\Entity\User;
use App\User\Framework\Entity\UserPreference;
use App\User\Framework\Repository\PreferencesRepository;
use Doctrine\ORM\EntityManagerInterface;
use OneToMany\RichBundle\Contract\CommandInterface as C;
use OneToMany\RichBundle\Contract\HandlerInterface;
use OneToMany\RichBundle\Contract\ResultInterface as R;
use Symfony\Bundle\SecurityBundle\Security;
/** @implements HandlerInterface<SaveUserMediaPreferencesCommand> */
class SaveUserTorrentioPreferencesHandler implements HandlerInterface
{
public function __construct(
private readonly EntityManagerInterface $entityManager,
private readonly PreferencesRepository $preferenceRepository,
private readonly Security $token,
) {}
public function handle(C $command): R
{
/** @var User $user */
$user = $this->token->getUser();
if ($user->hasUserPreference('torrentio_url')) {
$user->updateUserPreference('torrentio_url', (string) $command->torrentioUrl);
$this->entityManager->flush();
} else {
$preference = $this->preferenceRepository->find('torrentio_url');
$user->addUserPreference(
(new UserPreference())
->setUser($user)
->setPreference($preference)
->setPreferenceValue((string) $command->torrentioUrl)
);
}
$this->entityManager->flush();
return new SaveUserDownloadPreferencesResult($user->getDownloadPreferences());
}
}

View File

@@ -5,20 +5,31 @@ namespace App\User\Database;
class ProviderList
{
public static $providers = [
'1337x',
'Comando',
'YTS',
'EZTV',
'ilCorSaRoNeRo',
'MagnetDL',
'MejorTorrent',
'RARBG',
'Rutor',
'Rutracker',
'1337x',
'ThePirateBay',
'Torrent9',
'KickassTorrents',
'TorrentGalaxy',
'MagnetDL',
'HorribleSubs',
'NyaaSi',
'TokyoTosho',
'AniDex',
'🇷🇺 Rutor',
'🇷🇺 Rutracker',
'🇵🇹 Comando',
'🇵🇹 BluDV',
'🇫🇷 Torrent9',
'🇮🇹 ilCorSaRoNeRo',
'🇪🇸 MejorTorrent',
'🇪🇸 Wolfmax4k',
'🇲🇽 Cinecalidad',
'🇵🇱 BestTorrents'
];
public static function getProviders()
{
return self::$providers;

View File

@@ -5,10 +5,14 @@ declare(strict_types=1);
namespace App\User\Framework\Controller\Web;
use App\Base\Service\Broadcaster;
use App\Torrentio\Client\TorrentioUrl;
use App\Torrentio\Framework\Form\TorrentioPreferencesForm;
use App\User\Action\Command\SaveUserMediaPreferencesCommand;
use App\User\Action\Command\SaveUserTorrentioPreferencesCommand;
use App\User\Action\Handler\SaveUserCalendarPreferencesHandler;
use App\User\Action\Handler\SaveUserDownloadPreferencesHandler;
use App\User\Action\Handler\SaveUserMediaPreferencesHandler;
use App\User\Action\Handler\SaveUserTorrentioPreferencesHandler;
use App\User\Action\Input\SaveUserCalendarPreferencesInput;
use App\User\Action\Input\SaveUserDownloadPreferencesInput;
use App\User\Action\Input\SaveUserMediaPreferencesInput;
@@ -38,6 +42,9 @@ class PreferencesController extends AbstractController
$calendarPreferences = $this->getUser()->getCalendarPreferences();
$formData = (array) UserPreferencesFactory::createFromUser($this->getUser());
$form = $this->createForm(UserMediaPreferencesForm::class, $formData);
$torrentioForm = $this->createForm(TorrentioPreferencesForm::class, TorrentioUrl::fromString(
$this->getUser()->getUserPreference('torrentio_url')->getPreferenceValue()
));
return $this->render(
'user/preferences.html.twig',
@@ -45,6 +52,7 @@ class PreferencesController extends AbstractController
'downloadPreferences' => $downloadPreferences,
'calendarPreferences' => $calendarPreferences,
'preferences_form' => $form,
'torrentio_form' => $torrentioForm,
]
);
}
@@ -131,4 +139,26 @@ class PreferencesController extends AbstractController
]
);
}
#[Route('/user/preferences/torrentio', 'app.torrentio-preferences.save', methods: ['POST'])]
public function saveTorrentioPreferences(Request $request, SaveUserTorrentioPreferencesHandler $handler)
{
$form = $this->createForm(TorrentioPreferencesForm::class, new TorrentioUrl());
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$command = new SaveUserTorrentioPreferencesCommand(
$form->getData(),
);
$handler->handle($command);
$this->broadcaster->alert('Success', 'Your Torrentio preferences have been saved.');
}
return $this->render(
'user/preferences.html.twig',
[
'preferences_form' => $form,
]
);
}
}

View File

@@ -5,6 +5,7 @@ namespace App\User\Framework\Entity;
use Aimeos\Map;
use App\Download\Framework\Entity\Download;
use App\Monitor\Framework\Entity\Monitor;
use App\Torrentio\Client\TorrentioUrl;
use App\User\Framework\Repository\UserRepository;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
@@ -342,4 +343,11 @@ class User implements UserInterface, PasswordAuthenticatedUserInterface
return $this->hasUserPreference('enable_ical_up_ep') &&
(bool) $this->getUserPreference('enable_ical_up_ep')->getPreferenceValue() === true;
}
public function getTorrentioUrl()
{
return $this->hasUserPreference('torrentio_url')
? TorrentioUrl::fromString($this->getUserPreference('torrentio_url')->getPreferenceValue())
: null;
}
}

View File

@@ -21,7 +21,7 @@ class UserPreference
#[ORM\JoinColumn(nullable: false)]
private ?Preference $preference = null;
#[ORM\Column(length: 255, nullable: true)]
#[ORM\Column(length: 1024, nullable: true)]
private ?string $preference_value = null;
public function getId(): ?int

View File

@@ -0,0 +1,27 @@
<?php
namespace App\Values;
class TorrentioDebridProviderChoices
{
public static array $providers = [
'realdebrid' => 'RealDebrid',
'premiumize' => 'Premiumize',
'alldebrid' => 'AllDebrid',
'debridlink' => 'DebridLink',
'easydebrid' => 'EasyDebrid',
'offcloud' => 'Offcloud',
'torbox' => 'TorBox',
'putio' => 'Put.io',
];
public static function getProviders(): array
{
return self::$providers;
}
public static function asSelectOptions(): array
{
return array_flip(self::$providers);
}
}

View File

@@ -0,0 +1,34 @@
<?php
namespace App\Values;
class TorrentioExcludeQualityChoices
{
public static array $choices = [
'brremux' => 'BluRay REMUX',
'hdrall' => 'HDR/HDR10+/Dolby Vision',
'dolbyvision' => 'Dolby Vision',
'dolbyvisionwithhdr' => 'Dolby Vision + HDR',
'threed' => '3D',
'nonthreed' => 'Non 3D (DO NOT SELECT IF NOT SURE)',
'4k' => '4k',
'1080p' => '1080p',
'720p' => '720p',
'480p' => '480p',
'other' => 'Other (DVDRip/HDRip/BDRip...)',
'scr' => 'Screener',
'cam' => 'Cam',
'unknown' => 'Unknown'
];
public static function getChoices(): array
{
return self::$choices;
}
public static function asSelectOptions(): array
{
return array_flip(self::$choices);
}
}

View File

@@ -0,0 +1,60 @@
<?php
namespace App\Values;
class TorrentioLanguageChoices
{
public static array $languages = [
'japanese' => '🇯🇵 Japanese',
'russian' => '🇷🇺 Russian',
'italian' => '🇮🇹 Italian',
'portuguese' => '🇵🇹 Portuguese',
'spanish' => '🇪🇸 Spanish',
'latino' => '🇲🇽 Latino',
'korean' => '🇰🇷 Korean',
'chinese' => '🇨🇳 Chinese',
'taiwanese' => '🇹🇼 Taiwanese',
'french' => '🇫🇷 French',
'german' => '🇩🇪 German',
'dutch' => '🇳🇱 Dutch',
'hindi' => '🇮🇳 Hindi',
'telugu' => '🇮🇳 Telugu',
'tamil' => '🇮🇳 Tamil',
'polish' => '🇵🇱 Polish',
'lithuanian' => '🇱🇹 Lithuanian',
'latvian' => '🇱🇻 Latvian',
'estonian' => '🇪🇪 Estonian',
'czech' => '🇨🇿 Czech',
'slovakian' => '🇸🇰 Slovakian',
'slovenian' => '🇸🇮 Slovenian',
'hungarian' => '🇭🇺 Hungarian',
'romanian' => '🇷🇴 Romanian',
'bulgarian' => '🇧🇬 Bulgarian',
'serbian' => '🇷🇸 Serbian',
'croatian' => '🇭🇷 Croatian',
'ukrainian' => '🇺🇦 Ukrainian',
'greek' => '🇬🇷 Greek',
'danish' => '🇩🇰 Danish',
'finnish' => '🇫🇮 Finnish',
'swedish' => '🇸🇪 Swedish',
'norwegian' => '🇳🇴 Norwegian',
'turkish' => '🇹🇷 Turkish',
'arabic' => '🇸🇦 Arabic',
'persian' => '🇮🇷 Persian',
'hebrew' => '🇮🇱 Hebrew',
'vietnamese' => '🇻🇳 Vietnamese',
'indonesian' => '🇮🇩 Indonesian',
'malay' => '🇲🇾 Malay',
'thai' => '🇹🇭 Thai'
];
public static function getLanguages(): array
{
return self::$languages;
}
public static function asSelectOptions(): array
{
return array_flip(self::$languages);
}
}

View File

@@ -0,0 +1,41 @@
<?php
namespace App\Values;
class TorrentioProviderChoices
{
public static array $choices = [
'yts' => 'YTS',
'eztv' => 'EZTV',
'rarbg' => 'RARBG',
'1337x' => '1337x',
'thepiratebay' => 'ThePirateBay',
'kickasstorrents' => 'KickassTorrents',
'torrentgalaxy' => 'TorrentGalaxy',
'magnetdl' => 'MagnetDL',
'horriblesubs' => 'HorribleSubs',
'nyaasi' => 'NyaaSi',
'tokyotosho' => 'TokyoTosho',
'anidex' => 'AniDex',
'rutor' => '🇷🇺 Rutor',
'rutracker' => '🇷🇺 Rutracker',
'comando' => '🇵🇹 Comando',
'bludv' => '🇵🇹 BluDV',
'torrent9' => '🇫🇷 Torrent9',
'ilcorsaronero' => '🇮🇹 ilCorSaRoNeRo',
'mejortorrent' => '🇪🇸 MejorTorrent',
'wolfmax4k' => '🇪🇸 Wolfmax4k',
'cinecalidad' => '🇲🇽 Cinecalidad',
'besttorrents' => '🇵🇱 BestTorrents'
];
public static function getChoices(): array
{
return self::$choices;
}
public static function asSelectOptions(): array
{
return array_flip(self::$choices);
}
}

View File

@@ -0,0 +1,23 @@
<?php
namespace App\Values;
class TorrentioSortChoices
{
public static array $choices = [
'quality' => 'By quality then seeders',
'qualitysize' => 'By quality then size',
'seeders' => 'By seeders',
'size' => 'By size',
];
public static function getChoices(): array
{
return self::$choices;
}
public static function asSelectOptions(): array
{
return array_flip(self::$choices);
}
}

View File

@@ -1,4 +1,4 @@
<button class="submit-button flex flex-row gap-2 items-center">
<button class="submit-button {{ class|default('flex flex-row gap-2 items-center') }}">
{% if show_icon|default %}
<twig:ux:icon name="zondicons:checkmark" width=".8rem" class="text-green-500" />
{% endif %}

View File

@@ -57,4 +57,30 @@
</form>
</twig:Card>
</div>
<div class="p-4 flex flex-col md:flex-row gap-2">
<twig:Card title="Torrentio Preferences" class="w-full">
<p class="text-gray-50 mb-4">Configure your Torrentio client.</p>
{{ form_start(torrentio_form, {
action: path('app.torrentio-preferences.save')
}) }}
<div class="flex flex-col md:flex-row gap-2">
<div class="self-end mb-4">
{{ form_row(torrentio_form.providers) }}
{{ form_row(torrentio_form.sorting) }}
{{ form_row(torrentio_form.language) }}
{{ form_row(torrentio_form.qualityfilter) }}
{{ form_row(torrentio_form.limit) }}
{{ form_row(torrentio_form.sizefilter) }}
{{ form_row(torrentio_form.debridProvider) }}
{{ form_row(torrentio_form.debridToken) }}
{{ form_widget(torrentio_form._token) }}
<div class="w-[5rem]">
<twig:SubmitButton show_icon text="Save"/>
</div>
</div>
</div>
{{ form_end(preferences_form) }}
</twig:Card>
</div>
{% endblock %}