diff --git a/assets/controllers/tv_results_controller.js b/assets/controllers/tv_results_controller.js index d6d3529..408ae50 100644 --- a/assets/controllers/tv_results_controller.js +++ b/assets/controllers/tv_results_controller.js @@ -47,6 +47,13 @@ export default class extends Controller { } } + // + // async clearCache() { + // await fetch(`/torrentio/tvshows/clear/${this.tmdbIdValue}/${this.imdbIdValue}/${this.seasonValue}/${this.episodeValue}`) + // .then(res => res.text()) + // .then(response => {}); + // } + async setActive() { this.activeValue = true; this.element.classList.remove('hidden'); diff --git a/assets/icons/hugeicons/loading-01.svg b/assets/icons/hugeicons/loading-01.svg new file mode 100644 index 0000000..5e53bb5 --- /dev/null +++ b/assets/icons/hugeicons/loading-01.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/build.xml b/build.xml index a1ccf93..4721f02 100644 --- a/build.xml +++ b/build.xml @@ -32,4 +32,12 @@ + + + + + + + + diff --git a/composer.json b/composer.json index f899aa6..ae91faa 100644 --- a/composer.json +++ b/composer.json @@ -14,6 +14,7 @@ "doctrine/doctrine-migrations-bundle": "^3.4", "doctrine/orm": "^3.3", "dragonmantank/cron-expression": "^3.4", + "nesbot/carbon": "^3.9", "nihilarr/parse-torrent-name": "^0.0.1", "nyholm/psr7": "*", "p3k/emoji-detector": "^1.2", diff --git a/composer.lock b/composer.lock index 9a8479d..47443a6 100644 --- a/composer.lock +++ b/composer.lock @@ -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": "1acedc6a795947368d0673ec79564bec", + "content-hash": "c9b0e6d834b028ec81a8ba1d24005d77", "packages": [ { "name": "1tomany/rich-bundle", @@ -117,6 +117,75 @@ }, "time": "2025-03-05T09:16:18+00:00" }, + { + "name": "carbonphp/carbon-doctrine-types", + "version": "2.1.0", + "source": { + "type": "git", + "url": "https://github.com/CarbonPHP/carbon-doctrine-types.git", + "reference": "99f76ffa36cce3b70a4a6abce41dba15ca2e84cb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/CarbonPHP/carbon-doctrine-types/zipball/99f76ffa36cce3b70a4a6abce41dba15ca2e84cb", + "reference": "99f76ffa36cce3b70a4a6abce41dba15ca2e84cb", + "shasum": "" + }, + "require": { + "php": "^7.4 || ^8.0" + }, + "conflict": { + "doctrine/dbal": "<3.7.0 || >=4.0.0" + }, + "require-dev": { + "doctrine/dbal": "^3.7.0", + "nesbot/carbon": "^2.71.0 || ^3.0.0", + "phpunit/phpunit": "^10.3" + }, + "type": "library", + "autoload": { + "psr-4": { + "Carbon\\Doctrine\\": "src/Carbon/Doctrine/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "KyleKatarn", + "email": "kylekatarnls@gmail.com" + } + ], + "description": "Types to use Carbon in Doctrine", + "keywords": [ + "carbon", + "date", + "datetime", + "doctrine", + "time" + ], + "support": { + "issues": "https://github.com/CarbonPHP/carbon-doctrine-types/issues", + "source": "https://github.com/CarbonPHP/carbon-doctrine-types/tree/2.1.0" + }, + "funding": [ + { + "url": "https://github.com/kylekatarnls", + "type": "github" + }, + { + "url": "https://opencollective.com/Carbon", + "type": "open_collective" + }, + { + "url": "https://tidelift.com/funding/github/packagist/nesbot/carbon", + "type": "tidelift" + } + ], + "time": "2023-12-11T17:09:12+00:00" + }, { "name": "composer/semver", "version": "3.4.3", @@ -1554,6 +1623,112 @@ ], "time": "2025-01-26T21:29:45+00:00" }, + { + "name": "nesbot/carbon", + "version": "3.9.1", + "source": { + "type": "git", + "url": "https://github.com/CarbonPHP/carbon.git", + "reference": "ced71f79398ece168e24f7f7710462f462310d4d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/CarbonPHP/carbon/zipball/ced71f79398ece168e24f7f7710462f462310d4d", + "reference": "ced71f79398ece168e24f7f7710462f462310d4d", + "shasum": "" + }, + "require": { + "carbonphp/carbon-doctrine-types": "<100.0", + "ext-json": "*", + "php": "^8.1", + "psr/clock": "^1.0", + "symfony/clock": "^6.3 || ^7.0", + "symfony/polyfill-mbstring": "^1.0", + "symfony/translation": "^4.4.18 || ^5.2.1|| ^6.0 || ^7.0" + }, + "provide": { + "psr/clock-implementation": "1.0" + }, + "require-dev": { + "doctrine/dbal": "^3.6.3 || ^4.0", + "doctrine/orm": "^2.15.2 || ^3.0", + "friendsofphp/php-cs-fixer": "^3.57.2", + "kylekatarnls/multi-tester": "^2.5.3", + "ondrejmirtes/better-reflection": "^6.25.0.4", + "phpmd/phpmd": "^2.15.0", + "phpstan/extension-installer": "^1.3.1", + "phpstan/phpstan": "^1.11.2", + "phpunit/phpunit": "^10.5.20", + "squizlabs/php_codesniffer": "^3.9.0" + }, + "bin": [ + "bin/carbon" + ], + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Carbon\\Laravel\\ServiceProvider" + ] + }, + "phpstan": { + "includes": [ + "extension.neon" + ] + }, + "branch-alias": { + "dev-2.x": "2.x-dev", + "dev-master": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Carbon\\": "src/Carbon/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Brian Nesbitt", + "email": "brian@nesbot.com", + "homepage": "https://markido.com" + }, + { + "name": "kylekatarnls", + "homepage": "https://github.com/kylekatarnls" + } + ], + "description": "An API extension for DateTime that supports 281 different languages.", + "homepage": "https://carbon.nesbot.com", + "keywords": [ + "date", + "datetime", + "time" + ], + "support": { + "docs": "https://carbon.nesbot.com/docs", + "issues": "https://github.com/CarbonPHP/carbon/issues", + "source": "https://github.com/CarbonPHP/carbon" + }, + "funding": [ + { + "url": "https://github.com/sponsors/kylekatarnls", + "type": "github" + }, + { + "url": "https://opencollective.com/Carbon#sponsor", + "type": "opencollective" + }, + { + "url": "https://tidelift.com/subscription/pkg/packagist-nesbot-carbon?utm_source=packagist-nesbot-carbon&utm_medium=referral&utm_campaign=readme", + "type": "tidelift" + } + ], + "time": "2025-05-01T19:51:51+00:00" + }, { "name": "nihilarr/parse-torrent-name", "version": "v0.0.1", @@ -7039,6 +7214,101 @@ ], "time": "2025-04-20T20:18:16+00:00" }, + { + "name": "symfony/translation", + "version": "v7.2.6", + "source": { + "type": "git", + "url": "https://github.com/symfony/translation.git", + "reference": "e7fd8e2a4239b79a0fd9fb1fef3e0e7f969c6dc6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/translation/zipball/e7fd8e2a4239b79a0fd9fb1fef3e0e7f969c6dc6", + "reference": "e7fd8e2a4239b79a0fd9fb1fef3e0e7f969c6dc6", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/polyfill-mbstring": "~1.0", + "symfony/translation-contracts": "^2.5|^3.0" + }, + "conflict": { + "symfony/config": "<6.4", + "symfony/console": "<6.4", + "symfony/dependency-injection": "<6.4", + "symfony/http-client-contracts": "<2.5", + "symfony/http-kernel": "<6.4", + "symfony/service-contracts": "<2.5", + "symfony/twig-bundle": "<6.4", + "symfony/yaml": "<6.4" + }, + "provide": { + "symfony/translation-implementation": "2.3|3.0" + }, + "require-dev": { + "nikic/php-parser": "^4.18|^5.0", + "psr/log": "^1|^2|^3", + "symfony/config": "^6.4|^7.0", + "symfony/console": "^6.4|^7.0", + "symfony/dependency-injection": "^6.4|^7.0", + "symfony/finder": "^6.4|^7.0", + "symfony/http-client-contracts": "^2.5|^3.0", + "symfony/http-kernel": "^6.4|^7.0", + "symfony/intl": "^6.4|^7.0", + "symfony/polyfill-intl-icu": "^1.21", + "symfony/routing": "^6.4|^7.0", + "symfony/service-contracts": "^2.5|^3", + "symfony/yaml": "^6.4|^7.0" + }, + "type": "library", + "autoload": { + "files": [ + "Resources/functions.php" + ], + "psr-4": { + "Symfony\\Component\\Translation\\": "" + }, + "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": "Provides tools to internationalize your application", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/translation/tree/v7.2.6" + }, + "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-04-07T19:09:28+00:00" + }, { "name": "symfony/translation-contracts", "version": "v3.5.1", diff --git a/config/packages/translation.yaml b/config/packages/translation.yaml new file mode 100644 index 0000000..b3f8f9c --- /dev/null +++ b/config/packages/translation.yaml @@ -0,0 +1,7 @@ +framework: + default_locale: en + translator: + default_path: '%kernel.project_dir%/translations' + fallbacks: + - en + providers: diff --git a/src/Controller/SearchController.php b/src/Controller/SearchController.php index 3d5f55c..4f50049 100644 --- a/src/Controller/SearchController.php +++ b/src/Controller/SearchController.php @@ -6,6 +6,7 @@ use App\Search\Action\Handler\GetMediaInfoHandler; use App\Search\Action\Handler\SearchHandler; use App\Search\Action\Input\GetMediaInfoInput; use App\Search\Action\Input\SearchInput; +use Carbon\Carbon; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Attribute\Route; @@ -37,8 +38,8 @@ final class SearchController extends AbstractController ): Response { $cacheId = sprintf("page.%s.%s", $input->mediaType, $input->tmdbId); -// return $cache->get($cacheId, function (ItemInterface $item) use ($input) { -// $item->expiresAt(new \DateTimeImmutable("today 11:59 pm")); + return $cache->get($cacheId, function (ItemInterface $item) use ($input) { + $item->expiresAt(Carbon::now()->addHour()->setMinute(0)->setSecond(0)); $result = $this->getMediaInfoHandler->handle($input->toCommand()); return $this->render('search/result.html.twig', [ 'results' => $result, @@ -51,6 +52,6 @@ final class SearchController extends AbstractController 'episode' => '' ] ]); -// }); + }); } } diff --git a/src/Controller/TorrentioController.php b/src/Controller/TorrentioController.php index c5fb8b3..37968a7 100644 --- a/src/Controller/TorrentioController.php +++ b/src/Controller/TorrentioController.php @@ -6,8 +6,10 @@ use App\Torrentio\Action\Handler\GetMovieOptionsHandler; use App\Torrentio\Action\Handler\GetTvShowOptionsHandler; use App\Torrentio\Action\Input\GetMovieOptionsInput; use App\Torrentio\Action\Input\GetTvShowOptionsInput; +use Carbon\Carbon; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\Mercure\HubInterface; use Symfony\Component\Routing\Attribute\Route; use Symfony\Contracts\Cache\CacheInterface; use Symfony\Contracts\Cache\ItemInterface; @@ -17,6 +19,8 @@ final class TorrentioController extends AbstractController public function __construct( private readonly GetMovieOptionsHandler $getMovieOptionsHandler, private readonly GetTvShowOptionsHandler $getTvShowOptionsHandler, + private readonly HubInterface $hub, + private readonly \Twig\Environment $renderer, ) {} #[Route('/torrentio/movies/{tmdbId}/{imdbId}', name: 'app_torrentio_movies')] @@ -29,7 +33,7 @@ final class TorrentioController extends AbstractController ); return $cache->get($cacheId, function (ItemInterface $item) use ($input) { - $item->expiresAt(new \DateTimeImmutable("today 11:59 pm")); + $item->expiresAt(Carbon::now()->addHour()->setMinute(0)->setSecond(0)); $results = $this->getMovieOptionsHandler->handle($input->toCommand()); return $this->render('torrentio/movies.html.twig', [ 'results' => $results, @@ -48,12 +52,45 @@ final class TorrentioController extends AbstractController $input->episode, ); -// return $cache->get($cacheId, function (ItemInterface $item) use ($input) { -// $item->expiresAt(new \DateTimeImmutable("today 11:59 pm")); + return $cache->get($cacheId, function (ItemInterface $item) use ($input) { + $item->expiresAt(Carbon::now()->addHour()->setMinute(0)->setSecond(0)); $results = $this->getTvShowOptionsHandler->handle($input->toCommand()); return $this->render('torrentio/tvshows.html.twig', [ 'results' => $results, ]); -// }); + }); + } + + #[Route('/torrentio/tvshows/clear/{tmdbId}/{imdbId}/{season?}/{episode?}', name: 'app_clear_torrentio_tvshows')] + public function clearTvShowOptions(GetTvShowOptionsInput $input, CacheInterface $cache): Response + { + $cacheId = sprintf( + "page.torrentio.tvshows.%s.%s.%s.%s", + $input->tmdbId, + $input->imdbId, + $input->season, + $input->episode, + ); + $cache->delete($cacheId); + + $cacheId = sprintf("page.%s.%s", "tvshows", $input->tmdbId); + $cache->delete($cacheId); + + $this->hub->publish(new \Symfony\Component\Mercure\Update( + 'alerts', + $this->renderer->render('broadcast/Alert.html.twig', [ + 'alert_id' => uniqid(), + 'title' => 'Success', + 'message' => 'Torrentio cache Cleared.', + ]) + )); + + return $cache->get($cacheId, function (ItemInterface $item) use ($input) { + $item->expiresAt(Carbon::now()->addHour()->setMinute(0)->setSecond(0)); + $results = $this->getTvShowOptionsHandler->handle($input->toCommand()); + return $this->render('torrentio/tvshows.html.twig', [ + 'results' => $results, + ]); + }); } } diff --git a/src/Tmdb/Tmdb.php b/src/Tmdb/Tmdb.php index d4b964b..1caa706 100644 --- a/src/Tmdb/Tmdb.php +++ b/src/Tmdb/Tmdb.php @@ -222,7 +222,7 @@ class Tmdb imdbId: $data['external_ids']['imdb_id'], tmdbId: $data['id'], title: $data['name'], - poster: $posterBasePath . $data['poster_path'], + poster: (null !== $data['poster_path']) ? $posterBasePath . $data['poster_path'] : null, description: $data['overview'], year: (new \DateTime($data['first_air_date']))->format('Y'), mediaType: "tvshows", @@ -236,7 +236,7 @@ class Tmdb imdbId: $data['external_ids']['imdb_id'], tmdbId: $data['id'], title: $data['name'], - poster: $posterBasePath . $data['still_path'], + poster: (null !== $data['still_path']) ? $posterBasePath . $data['still_path'] : null, description: $data['overview'], year: (new \DateTime($data['air_date']))->format('Y'), mediaType: "tvshows", @@ -251,7 +251,7 @@ class Tmdb imdbId: $data['external_ids']['imdb_id'], tmdbId: $data['id'], title: $data['title'], - poster: $posterBasePath . $data['poster_path'], + poster: (null !== $data['poster_path']) ? $posterBasePath . $data['poster_path'] : null, description: $data['overview'], year: (new \DateTime($data['release_date']))->format('Y'), mediaType: "movies", diff --git a/src/Torrentio/Client/Torrentio.php b/src/Torrentio/Client/Torrentio.php index 8bc70cd..37a4202 100644 --- a/src/Torrentio/Client/Torrentio.php +++ b/src/Torrentio/Client/Torrentio.php @@ -6,6 +6,7 @@ use App\Torrentio\Client\Rule\DownloadOptionFilter\Resolution; use App\Torrentio\Client\Rule\RuleEngine; use App\Torrentio\MediaResult; use App\Torrentio\Result\ResultFactory; +use Carbon\Carbon; use Symfony\Component\DependencyInjection\Attribute\Autowire; use Symfony\Contracts\Cache\CacheInterface; use Symfony\Contracts\Cache\ItemInterface; @@ -32,7 +33,7 @@ class Torrentio $cacheKey = "torrentio.{$imdbCode}"; $results = $this->cache->get($cacheKey, function (ItemInterface $item) use ($imdbCode) { - $item->expiresAt(new \DateTimeImmutable("today 11:59 pm")); + $item->expiresAt(Carbon::now()->addHour()->setMinute(0)->setSecond(0)); $response = file_get_contents(str_replace('{imdbCode}', $imdbCode, $this->searchUrl)); return json_decode( $response, @@ -67,7 +68,7 @@ class Torrentio { $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")); + $item->expiresAt(Carbon::now()->addHour()->setMinute(0)->setSecond(0)); $response = file_get_contents(str_replace('{imdbCode}', "$imdbId:$season:$episode", $this->searchUrl)); return json_decode( $response, diff --git a/symfony.lock b/symfony.lock index 50f27a4..bec7650 100644 --- a/symfony.lock +++ b/symfony.lock @@ -210,6 +210,19 @@ "assets/controllers/hello_controller.js" ] }, + "symfony/translation": { + "version": "7.2", + "recipe": { + "repo": "github.com/symfony/recipes", + "branch": "main", + "version": "6.3", + "ref": "e28e27f53663cc34f0be2837aba18e3a1bef8e7b" + }, + "files": [ + "config/packages/translation.yaml", + "translations/.gitignore" + ] + }, "symfony/twig-bundle": { "version": "7.2", "recipe": { diff --git a/templates/components/SearchResult.html.twig b/templates/components/SearchResult.html.twig index a7c1465..2d3fa24 100644 --- a/templates/components/SearchResult.html.twig +++ b/templates/components/SearchResult.html.twig @@ -1,6 +1,13 @@
- + {% if poster != null and poster != "https://image.tmdb.org/t/p/w500" %} + + {% else %} +
+ +
+ {% endif %} +

{{ title }} - {{ year }} diff --git a/templates/search/result.html.twig b/templates/search/result.html.twig index 1aafd93..fd05806 100644 --- a/templates/search/result.html.twig +++ b/templates/search/result.html.twig @@ -8,7 +8,14 @@
- + {% if results.media.poster != null %} + + {% else %} +
+ +
+ {% endif %} +

diff --git a/templates/torrentio/tvshows.html.twig b/templates/torrentio/tvshows.html.twig index 78a9358..627c561 100644 --- a/templates/torrentio/tvshows.html.twig +++ b/templates/torrentio/tvshows.html.twig @@ -1,6 +1,12 @@
- + {% if results.media.poster != null %} + + {% else %} +
+ +
+ {% endif %}

{{ results.episode }}. {{ results.media.title }}

{{ results.media.description }}

@@ -10,6 +16,9 @@ >{{ results.results|length }} results {{ results.media.episodeAirDate }} +{# Clear Cache#}
diff --git a/translations/.gitignore b/translations/.gitignore new file mode 100644 index 0000000..e69de29