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