Compare commits
15 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| be7b610111 | |||
| 3e93a7c9c1 | |||
| bc78b83f8d | |||
| c5bcaeb1d4 | |||
| e39182ba91 | |||
| 965b747594 | |||
| 937e3c6270 | |||
| 2bb2845ead | |||
| fca189648b | |||
| 2121466322 | |||
| 1e130c3490 | |||
| 4b97faeadb | |||
| 3701e31ee0 | |||
| 210c674f25 | |||
| 175f4330f1 |
@@ -3,6 +3,7 @@ FROM dunglas/frankenphp:php8.4
|
|||||||
ENV SERVER_NAME=":80"
|
ENV SERVER_NAME=":80"
|
||||||
ENV CADDY_GLOBAL_OPTIONS="auto_https off"
|
ENV CADDY_GLOBAL_OPTIONS="auto_https off"
|
||||||
ENV APP_RUNTIME="Runtime\\FrankenPhpSymfony\\Runtime"
|
ENV APP_RUNTIME="Runtime\\FrankenPhpSymfony\\Runtime"
|
||||||
|
ENV APP_VERSION="0.0.1"
|
||||||
|
|
||||||
RUN install-php-extensions \
|
RUN install-php-extensions \
|
||||||
pdo_mysql \
|
pdo_mysql \
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ export default class extends Controller {
|
|||||||
active: Boolean,
|
active: Boolean,
|
||||||
};
|
};
|
||||||
|
|
||||||
static targets = ['list', 'count', 'episodeSelector', 'toggleButton']
|
static targets = ['list', 'count', 'episodeSelector', 'toggleButton', 'listContainer']
|
||||||
static outlets = ['loading-icon']
|
static outlets = ['loading-icon']
|
||||||
|
|
||||||
options = []
|
options = []
|
||||||
@@ -32,19 +32,29 @@ export default class extends Controller {
|
|||||||
async setOptions() {
|
async setOptions() {
|
||||||
if (this.optionsLoaded === false) {
|
if (this.optionsLoaded === false) {
|
||||||
this.optionsLoaded = true;
|
this.optionsLoaded = true;
|
||||||
await fetch(`/torrentio/tvshows/${this.tmdbIdValue}/${this.imdbIdValue}/${this.seasonValue}/${this.episodeValue}`)
|
let response;
|
||||||
.then(res => res.text())
|
|
||||||
.then(response => {
|
try {
|
||||||
this.element.innerHTML = response;
|
response = await fetch(`/torrentio/tvshows/${this.tmdbIdValue}/${this.imdbIdValue}/${this.seasonValue}/${this.episodeValue}`)
|
||||||
this.options = this.element.querySelectorAll('tbody tr');
|
} catch (error) {
|
||||||
if (this.options.length > 0) {
|
console.log('There was an error', error);
|
||||||
this.options.forEach((option) => option.querySelector('.download-btn').dataset['title'] = this.titleValue);
|
}
|
||||||
this.options[0].querySelector('input[type="checkbox"]').checked = true;
|
|
||||||
} else {
|
if (response?.ok) {
|
||||||
this.episodeSelectorTarget.disabled = true;
|
response = await response.text()
|
||||||
}
|
this.listContainerTarget.innerHTML = response;
|
||||||
this.loadingIconOutlet.increaseCount();
|
this.options = this.element.querySelectorAll('tbody tr');
|
||||||
});
|
if (this.options.length > 0) {
|
||||||
|
this.options.forEach((option) => option.querySelector('.download-btn').dataset['title'] = this.titleValue);
|
||||||
|
this.options[0].querySelector('input[type="checkbox"]').checked = true;
|
||||||
|
} else {
|
||||||
|
this.countTarget.innerText = 0;
|
||||||
|
this.episodeSelectorTarget.disabled = true;
|
||||||
|
}
|
||||||
|
this.loadingIconOutlet.increaseCount();
|
||||||
|
} else {
|
||||||
|
console.log(`HTTP Response Code: ${response?.status}`)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -15,6 +15,18 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@layer components {
|
||||||
|
.alert {
|
||||||
|
@apply text-white text-sm min-w-[250px] border px-4 py-3 rounded-md
|
||||||
|
}
|
||||||
|
.alert-success {
|
||||||
|
@apply bg-green-950 hover:bg-green-900 border-green-500
|
||||||
|
}
|
||||||
|
.alert-warning {
|
||||||
|
@apply bg-yellow-500/70 hover:bg-yellow-600 border-yellow-400 text-black
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Prevent scrolling while dialog is open */
|
/* Prevent scrolling while dialog is open */
|
||||||
body:has(dialog[data-dialog-target="dialog"][open]) {
|
body:has(dialog[data-dialog-target="dialog"][open]) {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
"doctrine/doctrine-migrations-bundle": "^3.4",
|
"doctrine/doctrine-migrations-bundle": "^3.4",
|
||||||
"doctrine/orm": "^3.3",
|
"doctrine/orm": "^3.3",
|
||||||
"dragonmantank/cron-expression": "^3.4",
|
"dragonmantank/cron-expression": "^3.4",
|
||||||
|
"guzzlehttp/guzzle": "^7.9",
|
||||||
"league/pipeline": "^1.1",
|
"league/pipeline": "^1.1",
|
||||||
"nesbot/carbon": "^3.9",
|
"nesbot/carbon": "^3.9",
|
||||||
"nihilarr/parse-torrent-name": "^0.0.1",
|
"nihilarr/parse-torrent-name": "^0.0.1",
|
||||||
|
|||||||
371
composer.lock
generated
371
composer.lock
generated
@@ -4,7 +4,7 @@
|
|||||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||||
"This file is @generated automatically"
|
"This file is @generated automatically"
|
||||||
],
|
],
|
||||||
"content-hash": "e8b5e39f9d73a6ace2b9e39240186b4f",
|
"content-hash": "3b0840f4e60d44d341c934f6ca153944",
|
||||||
"packages": [
|
"packages": [
|
||||||
{
|
{
|
||||||
"name": "1tomany/rich-bundle",
|
"name": "1tomany/rich-bundle",
|
||||||
@@ -1947,6 +1947,331 @@
|
|||||||
],
|
],
|
||||||
"time": "2025-04-04T17:19:27+00:00"
|
"time": "2025-04-04T17:19:27+00:00"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "guzzlehttp/guzzle",
|
||||||
|
"version": "7.9.3",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/guzzle/guzzle.git",
|
||||||
|
"reference": "7b2f29fe81dc4da0ca0ea7d42107a0845946ea77"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/guzzle/guzzle/zipball/7b2f29fe81dc4da0ca0ea7d42107a0845946ea77",
|
||||||
|
"reference": "7b2f29fe81dc4da0ca0ea7d42107a0845946ea77",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"ext-json": "*",
|
||||||
|
"guzzlehttp/promises": "^1.5.3 || ^2.0.3",
|
||||||
|
"guzzlehttp/psr7": "^2.7.0",
|
||||||
|
"php": "^7.2.5 || ^8.0",
|
||||||
|
"psr/http-client": "^1.0",
|
||||||
|
"symfony/deprecation-contracts": "^2.2 || ^3.0"
|
||||||
|
},
|
||||||
|
"provide": {
|
||||||
|
"psr/http-client-implementation": "1.0"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"bamarni/composer-bin-plugin": "^1.8.2",
|
||||||
|
"ext-curl": "*",
|
||||||
|
"guzzle/client-integration-tests": "3.0.2",
|
||||||
|
"php-http/message-factory": "^1.1",
|
||||||
|
"phpunit/phpunit": "^8.5.39 || ^9.6.20",
|
||||||
|
"psr/log": "^1.1 || ^2.0 || ^3.0"
|
||||||
|
},
|
||||||
|
"suggest": {
|
||||||
|
"ext-curl": "Required for CURL handler support",
|
||||||
|
"ext-intl": "Required for Internationalized Domain Name (IDN) support",
|
||||||
|
"psr/log": "Required for using the Log middleware"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"extra": {
|
||||||
|
"bamarni-bin": {
|
||||||
|
"bin-links": true,
|
||||||
|
"forward-command": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"files": [
|
||||||
|
"src/functions_include.php"
|
||||||
|
],
|
||||||
|
"psr-4": {
|
||||||
|
"GuzzleHttp\\": "src/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Graham Campbell",
|
||||||
|
"email": "hello@gjcampbell.co.uk",
|
||||||
|
"homepage": "https://github.com/GrahamCampbell"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Michael Dowling",
|
||||||
|
"email": "mtdowling@gmail.com",
|
||||||
|
"homepage": "https://github.com/mtdowling"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Jeremy Lindblom",
|
||||||
|
"email": "jeremeamia@gmail.com",
|
||||||
|
"homepage": "https://github.com/jeremeamia"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "George Mponos",
|
||||||
|
"email": "gmponos@gmail.com",
|
||||||
|
"homepage": "https://github.com/gmponos"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Tobias Nyholm",
|
||||||
|
"email": "tobias.nyholm@gmail.com",
|
||||||
|
"homepage": "https://github.com/Nyholm"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Márk Sági-Kazár",
|
||||||
|
"email": "mark.sagikazar@gmail.com",
|
||||||
|
"homepage": "https://github.com/sagikazarmark"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Tobias Schultze",
|
||||||
|
"email": "webmaster@tubo-world.de",
|
||||||
|
"homepage": "https://github.com/Tobion"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Guzzle is a PHP HTTP client library",
|
||||||
|
"keywords": [
|
||||||
|
"client",
|
||||||
|
"curl",
|
||||||
|
"framework",
|
||||||
|
"http",
|
||||||
|
"http client",
|
||||||
|
"psr-18",
|
||||||
|
"psr-7",
|
||||||
|
"rest",
|
||||||
|
"web service"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"issues": "https://github.com/guzzle/guzzle/issues",
|
||||||
|
"source": "https://github.com/guzzle/guzzle/tree/7.9.3"
|
||||||
|
},
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"url": "https://github.com/GrahamCampbell",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://github.com/Nyholm",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://tidelift.com/funding/github/packagist/guzzlehttp/guzzle",
|
||||||
|
"type": "tidelift"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"time": "2025-03-27T13:37:11+00:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "guzzlehttp/promises",
|
||||||
|
"version": "2.2.0",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/guzzle/promises.git",
|
||||||
|
"reference": "7c69f28996b0a6920945dd20b3857e499d9ca96c"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/guzzle/promises/zipball/7c69f28996b0a6920945dd20b3857e499d9ca96c",
|
||||||
|
"reference": "7c69f28996b0a6920945dd20b3857e499d9ca96c",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"php": "^7.2.5 || ^8.0"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"bamarni/composer-bin-plugin": "^1.8.2",
|
||||||
|
"phpunit/phpunit": "^8.5.39 || ^9.6.20"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"extra": {
|
||||||
|
"bamarni-bin": {
|
||||||
|
"bin-links": true,
|
||||||
|
"forward-command": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"GuzzleHttp\\Promise\\": "src/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Graham Campbell",
|
||||||
|
"email": "hello@gjcampbell.co.uk",
|
||||||
|
"homepage": "https://github.com/GrahamCampbell"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Michael Dowling",
|
||||||
|
"email": "mtdowling@gmail.com",
|
||||||
|
"homepage": "https://github.com/mtdowling"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Tobias Nyholm",
|
||||||
|
"email": "tobias.nyholm@gmail.com",
|
||||||
|
"homepage": "https://github.com/Nyholm"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Tobias Schultze",
|
||||||
|
"email": "webmaster@tubo-world.de",
|
||||||
|
"homepage": "https://github.com/Tobion"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Guzzle promises library",
|
||||||
|
"keywords": [
|
||||||
|
"promise"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"issues": "https://github.com/guzzle/promises/issues",
|
||||||
|
"source": "https://github.com/guzzle/promises/tree/2.2.0"
|
||||||
|
},
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"url": "https://github.com/GrahamCampbell",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://github.com/Nyholm",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://tidelift.com/funding/github/packagist/guzzlehttp/promises",
|
||||||
|
"type": "tidelift"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"time": "2025-03-27T13:27:01+00:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "guzzlehttp/psr7",
|
||||||
|
"version": "2.7.1",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/guzzle/psr7.git",
|
||||||
|
"reference": "c2270caaabe631b3b44c85f99e5a04bbb8060d16"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/guzzle/psr7/zipball/c2270caaabe631b3b44c85f99e5a04bbb8060d16",
|
||||||
|
"reference": "c2270caaabe631b3b44c85f99e5a04bbb8060d16",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"php": "^7.2.5 || ^8.0",
|
||||||
|
"psr/http-factory": "^1.0",
|
||||||
|
"psr/http-message": "^1.1 || ^2.0",
|
||||||
|
"ralouphie/getallheaders": "^3.0"
|
||||||
|
},
|
||||||
|
"provide": {
|
||||||
|
"psr/http-factory-implementation": "1.0",
|
||||||
|
"psr/http-message-implementation": "1.0"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"bamarni/composer-bin-plugin": "^1.8.2",
|
||||||
|
"http-interop/http-factory-tests": "0.9.0",
|
||||||
|
"phpunit/phpunit": "^8.5.39 || ^9.6.20"
|
||||||
|
},
|
||||||
|
"suggest": {
|
||||||
|
"laminas/laminas-httphandlerrunner": "Emit PSR-7 responses"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"extra": {
|
||||||
|
"bamarni-bin": {
|
||||||
|
"bin-links": true,
|
||||||
|
"forward-command": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"GuzzleHttp\\Psr7\\": "src/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Graham Campbell",
|
||||||
|
"email": "hello@gjcampbell.co.uk",
|
||||||
|
"homepage": "https://github.com/GrahamCampbell"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Michael Dowling",
|
||||||
|
"email": "mtdowling@gmail.com",
|
||||||
|
"homepage": "https://github.com/mtdowling"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "George Mponos",
|
||||||
|
"email": "gmponos@gmail.com",
|
||||||
|
"homepage": "https://github.com/gmponos"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Tobias Nyholm",
|
||||||
|
"email": "tobias.nyholm@gmail.com",
|
||||||
|
"homepage": "https://github.com/Nyholm"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Márk Sági-Kazár",
|
||||||
|
"email": "mark.sagikazar@gmail.com",
|
||||||
|
"homepage": "https://github.com/sagikazarmark"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Tobias Schultze",
|
||||||
|
"email": "webmaster@tubo-world.de",
|
||||||
|
"homepage": "https://github.com/Tobion"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Márk Sági-Kazár",
|
||||||
|
"email": "mark.sagikazar@gmail.com",
|
||||||
|
"homepage": "https://sagikazarmark.hu"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "PSR-7 message implementation that also provides common utility methods",
|
||||||
|
"keywords": [
|
||||||
|
"http",
|
||||||
|
"message",
|
||||||
|
"psr-7",
|
||||||
|
"request",
|
||||||
|
"response",
|
||||||
|
"stream",
|
||||||
|
"uri",
|
||||||
|
"url"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"issues": "https://github.com/guzzle/psr7/issues",
|
||||||
|
"source": "https://github.com/guzzle/psr7/tree/2.7.1"
|
||||||
|
},
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"url": "https://github.com/GrahamCampbell",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://github.com/Nyholm",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://tidelift.com/funding/github/packagist/guzzlehttp/psr7",
|
||||||
|
"type": "tidelift"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"time": "2025-03-27T12:30:47+00:00"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "lcobucci/jwt",
|
"name": "lcobucci/jwt",
|
||||||
"version": "5.5.0",
|
"version": "5.5.0",
|
||||||
@@ -3527,6 +3852,50 @@
|
|||||||
},
|
},
|
||||||
"time": "2021-10-29T13:26:27+00:00"
|
"time": "2021-10-29T13:26:27+00:00"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "ralouphie/getallheaders",
|
||||||
|
"version": "3.0.3",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/ralouphie/getallheaders.git",
|
||||||
|
"reference": "120b605dfeb996808c31b6477290a714d356e822"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/120b605dfeb996808c31b6477290a714d356e822",
|
||||||
|
"reference": "120b605dfeb996808c31b6477290a714d356e822",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"php": ">=5.6"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"php-coveralls/php-coveralls": "^2.1",
|
||||||
|
"phpunit/phpunit": "^5 || ^6.5"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"autoload": {
|
||||||
|
"files": [
|
||||||
|
"src/getallheaders.php"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Ralph Khattar",
|
||||||
|
"email": "ralph.khattar@gmail.com"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "A polyfill for getallheaders.",
|
||||||
|
"support": {
|
||||||
|
"issues": "https://github.com/ralouphie/getallheaders/issues",
|
||||||
|
"source": "https://github.com/ralouphie/getallheaders/tree/develop"
|
||||||
|
},
|
||||||
|
"time": "2019-03-08T08:55:37+00:00"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "runtime/frankenphp-symfony",
|
"name": "runtime/frankenphp-symfony",
|
||||||
"version": "0.2.0",
|
"version": "0.2.0",
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ framework:
|
|||||||
trusted_headers: [ 'x-forwarded-for', 'x-forwarded-host', 'x-forwarded-proto', 'x-forwarded-port', 'x-forwarded-prefix' ]
|
trusted_headers: [ 'x-forwarded-for', 'x-forwarded-host', 'x-forwarded-proto', 'x-forwarded-port', 'x-forwarded-prefix' ]
|
||||||
|
|
||||||
session:
|
session:
|
||||||
handler_id: Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler
|
handler_id: '%env(REDIS_HOST)%'
|
||||||
|
|
||||||
#esi: true
|
#esi: true
|
||||||
#fragments: true
|
#fragments: true
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
twig:
|
twig:
|
||||||
|
globals:
|
||||||
|
version: '%app.version%'
|
||||||
file_name_pattern: '*.twig'
|
file_name_pattern: '*.twig'
|
||||||
date:
|
date:
|
||||||
|
format: 'm/d/Y'
|
||||||
timezone: '%env(default:app.default.timezone:TZ)%'
|
timezone: '%env(default:app.default.timezone:TZ)%'
|
||||||
|
|
||||||
when@test:
|
when@test:
|
||||||
|
|||||||
@@ -22,8 +22,11 @@ parameters:
|
|||||||
app.cache.redis.host.default: 'redis://redis'
|
app.cache.redis.host.default: 'redis://redis'
|
||||||
|
|
||||||
# Various configs
|
# Various configs
|
||||||
|
app.default.version: '0.dev'
|
||||||
app.default.timezone: 'America/Chicago'
|
app.default.timezone: 'America/Chicago'
|
||||||
|
|
||||||
|
app.version: '%env(default:app.default.version:APP_VERSION)%'
|
||||||
|
|
||||||
services:
|
services:
|
||||||
# default configuration for services in *this* file
|
# default configuration for services in *this* file
|
||||||
_defaults:
|
_defaults:
|
||||||
|
|||||||
@@ -4,6 +4,9 @@ ENV SERVER_NAME=":80"
|
|||||||
ENV CADDY_GLOBAL_OPTIONS="auto_https off"
|
ENV CADDY_GLOBAL_OPTIONS="auto_https off"
|
||||||
ENV APP_RUNTIME="Runtime\\FrankenPhpSymfony\\Runtime"
|
ENV APP_RUNTIME="Runtime\\FrankenPhpSymfony\\Runtime"
|
||||||
|
|
||||||
|
ARG APP_VERSION="0.dev"
|
||||||
|
ENV APP_VERSION="${APP_VERSION}"
|
||||||
|
|
||||||
RUN install-php-extensions \
|
RUN install-php-extensions \
|
||||||
pdo_mysql \
|
pdo_mysql \
|
||||||
gd \
|
gd \
|
||||||
|
|||||||
@@ -4,6 +4,9 @@ ENV SERVER_NAME=":80"
|
|||||||
ENV CADDY_GLOBAL_OPTIONS="auto_https off"
|
ENV CADDY_GLOBAL_OPTIONS="auto_https off"
|
||||||
ENV APP_RUNTIME="Runtime\\FrankenPhpSymfony\\Runtime"
|
ENV APP_RUNTIME="Runtime\\FrankenPhpSymfony\\Runtime"
|
||||||
|
|
||||||
|
ARG APP_VERSION="0.dev"
|
||||||
|
ENV APP_VERSION="${APP_VERSION}"
|
||||||
|
|
||||||
RUN install-php-extensions \
|
RUN install-php-extensions \
|
||||||
pdo_mysql \
|
pdo_mysql \
|
||||||
gd \
|
gd \
|
||||||
|
|||||||
@@ -4,6 +4,9 @@ ENV SERVER_NAME=":80"
|
|||||||
ENV CADDY_GLOBAL_OPTIONS="auto_https off"
|
ENV CADDY_GLOBAL_OPTIONS="auto_https off"
|
||||||
ENV APP_RUNTIME="Runtime\\FrankenPhpSymfony\\Runtime"
|
ENV APP_RUNTIME="Runtime\\FrankenPhpSymfony\\Runtime"
|
||||||
|
|
||||||
|
ARG APP_VERSION="0.dev"
|
||||||
|
ENV APP_VERSION="${APP_VERSION}"
|
||||||
|
|
||||||
RUN install-php-extensions \
|
RUN install-php-extensions \
|
||||||
pdo_mysql \
|
pdo_mysql \
|
||||||
gd \
|
gd \
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ use App\Torrentio\Action\Handler\GetMovieOptionsHandler;
|
|||||||
use App\Torrentio\Action\Handler\GetTvShowOptionsHandler;
|
use App\Torrentio\Action\Handler\GetTvShowOptionsHandler;
|
||||||
use App\Torrentio\Action\Input\GetMovieOptionsInput;
|
use App\Torrentio\Action\Input\GetMovieOptionsInput;
|
||||||
use App\Torrentio\Action\Input\GetTvShowOptionsInput;
|
use App\Torrentio\Action\Input\GetTvShowOptionsInput;
|
||||||
|
use App\Torrentio\Exception\TorrentioRateLimitException;
|
||||||
use App\Util\Broadcaster;
|
use App\Util\Broadcaster;
|
||||||
use Carbon\Carbon;
|
use Carbon\Carbon;
|
||||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||||
@@ -52,13 +53,24 @@ final class TorrentioController extends AbstractController
|
|||||||
$input->episode,
|
$input->episode,
|
||||||
);
|
);
|
||||||
|
|
||||||
return $cache->get($cacheId, function (ItemInterface $item) use ($input) {
|
try {
|
||||||
$item->expiresAt(Carbon::now()->addHour()->setMinute(0)->setSecond(0));
|
return $cache->get($cacheId, function (ItemInterface $item) use ($input) {
|
||||||
$results = $this->getTvShowOptionsHandler->handle($input->toCommand());
|
$item->expiresAt(Carbon::now()->addHour()->setMinute(0)->setSecond(0));
|
||||||
return $this->render('torrentio/tvshows.html.twig', [
|
$results = $this->getTvShowOptionsHandler->handle($input->toCommand());
|
||||||
'results' => $results,
|
return $this->render('torrentio/tvshows.html.twig', [
|
||||||
]);
|
'results' => $results,
|
||||||
});
|
]);
|
||||||
|
});
|
||||||
|
} catch (TorrentioRateLimitException $exception) {
|
||||||
|
$this->broadcaster->alert('Warning', 'Torrentio has rate limited your requests. Please wait a few minutes before trying again.', 'warning');
|
||||||
|
return $this->render('bare.html.twig',
|
||||||
|
[],
|
||||||
|
new Response('Too many requests',
|
||||||
|
Response::HTTP_TOO_MANY_REQUESTS,
|
||||||
|
['Retry-After' => 4000]
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[Route('/torrentio/tvshows/clear/{tmdbId}/{imdbId}/{season?}/{episode?}', name: 'app_clear_torrentio_tvshows')]
|
#[Route('/torrentio/tvshows/clear/{tmdbId}/{imdbId}/{season?}/{episode?}', name: 'app_clear_torrentio_tvshows')]
|
||||||
|
|||||||
@@ -61,10 +61,10 @@ readonly class MonitorTvShowHandler implements HandlerInterface
|
|||||||
// Dispatch Episode commands for each missing Episode
|
// Dispatch Episode commands for each missing Episode
|
||||||
foreach ($episodesInShow as $episode) {
|
foreach ($episodesInShow as $episode) {
|
||||||
// Only monitor future episodes
|
// Only monitor future episodes
|
||||||
$episodeInFuture = $this->episodeInFuture($episode);
|
$episodeInFuture = $this->episodeReleasedAfterMonitorCreated($monitor->getCreatedAt(), $episode);
|
||||||
$this->logger->info('> [MonitorTvShowHandler] Episode is in future for season ' . $episode['season_number'] . ' episode ' . $episode['episode_number'] . ' for title: ' . $monitor->getTitle() . ' ? ' . (true === $episodeInFuture ? 'YES' : 'NO'));
|
$this->logger->info('> [MonitorTvShowHandler] Episode released after monitor started for season ' . $episode['season_number'] . ' episode ' . $episode['episode_number'] . ' for title: ' . $monitor->getTitle() . ' ? ' . (true === $episodeInFuture ? 'YES' : 'NO'));
|
||||||
if (false === $episodeInFuture) {
|
if (false === $episodeInFuture) {
|
||||||
$this->logger->info('> [MonitorTvShowHandler] Episode not in future for title: ' . 'for season ' . $episode['season_number'] . ' episode ' . $episode['episode_number'] . ', skipping');
|
$this->logger->info('> [MonitorTvShowHandler] Episode released after monitor started for title: ' . 'for season ' . $episode['season_number'] . ' episode ' . $episode['episode_number'] . ', skipping');
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -125,11 +125,11 @@ readonly class MonitorTvShowHandler implements HandlerInterface
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function episodeInFuture(array $episodeInShow): bool
|
private function episodeReleasedAfterMonitorCreated(string|DateTimeImmutable $monitorStartDate, array $episodeInShow): bool
|
||||||
{
|
{
|
||||||
static $today = Carbon::today();
|
$monitorStartDate = Carbon::parse($monitorStartDate)->setTime(0, 0);
|
||||||
$episodeAirDate = Carbon::parse($episodeInShow['air_date']);
|
$episodeAirDate = Carbon::parse($episodeInShow['air_date']);
|
||||||
return $episodeAirDate > $today;
|
return $episodeAirDate >= $monitorStartDate;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function episodeExists(array $episodeInShow, Map $downloadedEpisodes): bool
|
private function episodeExists(array $episodeInShow, Map $downloadedEpisodes): bool
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ namespace App\Tmdb;
|
|||||||
use Aimeos\Map;
|
use Aimeos\Map;
|
||||||
use App\Enum\MediaType;
|
use App\Enum\MediaType;
|
||||||
use App\ValueObject\ResultFactory;
|
use App\ValueObject\ResultFactory;
|
||||||
|
use Carbon\Carbon;
|
||||||
use Symfony\Component\DependencyInjection\Attribute\Autowire;
|
use Symfony\Component\DependencyInjection\Attribute\Autowire;
|
||||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||||
use Symfony\Contracts\Cache\CacheInterface;
|
use Symfony\Contracts\Cache\CacheInterface;
|
||||||
@@ -209,7 +210,12 @@ class Tmdb
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
$series['episodes'][$season['season_number']] = $client->getApi()->getSeason($series['id'], $season['season_number'])['episodes'];
|
$series['episodes'][$season['season_number']] = Map::from(
|
||||||
|
$client->getApi()->getSeason($series['id'], $season['season_number'])['episodes']
|
||||||
|
)->map(function ($data) {
|
||||||
|
$data['poster'] = (null !== $data['still_path']) ? self::POSTER_IMG_PATH . $data['still_path'] : null;
|
||||||
|
return $data;
|
||||||
|
})->toArray();
|
||||||
}
|
}
|
||||||
return $series;
|
return $series;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,21 +6,30 @@ use App\Torrentio\Client\Rule\DownloadOptionFilter\Resolution;
|
|||||||
use App\Torrentio\Client\Rule\RuleEngine;
|
use App\Torrentio\Client\Rule\RuleEngine;
|
||||||
use App\Torrentio\Result\ResultFactory;
|
use App\Torrentio\Result\ResultFactory;
|
||||||
use Carbon\Carbon;
|
use Carbon\Carbon;
|
||||||
|
use App\Torrentio\Exception\TorrentioRateLimitException;
|
||||||
|
use GuzzleHttp\Client;
|
||||||
|
use Psr\Log\LoggerInterface;
|
||||||
use Symfony\Component\DependencyInjection\Attribute\Autowire;
|
use Symfony\Component\DependencyInjection\Attribute\Autowire;
|
||||||
use Symfony\Contracts\Cache\CacheInterface;
|
use Symfony\Contracts\Cache\CacheInterface;
|
||||||
use Symfony\Contracts\Cache\ItemInterface;
|
use Symfony\Contracts\Cache\ItemInterface;
|
||||||
|
|
||||||
class Torrentio
|
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/{imdbCode}.json';
|
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 $searchUrl;
|
private string $searchUrl;
|
||||||
|
|
||||||
|
private Client $client;
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
#[Autowire(env: 'REAL_DEBRID_KEY')] private string $realDebridKey,
|
#[Autowire(env: 'REAL_DEBRID_KEY')] private string $realDebridKey,
|
||||||
private CacheInterface $cache,
|
private CacheInterface $cache,
|
||||||
|
private LoggerInterface $logger,
|
||||||
) {
|
) {
|
||||||
$this->searchUrl = str_replace('{realDebridKey}', $this->realDebridKey, $this->baseUrl);
|
$this->searchUrl = str_replace('{realDebridKey}', $this->realDebridKey, $this->baseUrl);
|
||||||
|
$this->client = new Client([
|
||||||
|
'base_uri' => $this->searchUrl,
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function search(string $imdbCode, string $type, array $filter = []): array
|
public function search(string $imdbCode, string $type, array $filter = []): array
|
||||||
@@ -29,11 +38,21 @@ class Torrentio
|
|||||||
|
|
||||||
$results = $this->cache->get($cacheKey, function (ItemInterface $item) use ($imdbCode) {
|
$results = $this->cache->get($cacheKey, function (ItemInterface $item) use ($imdbCode) {
|
||||||
$item->expiresAt(Carbon::now()->addHour()->setMinute(0)->setSecond(0));
|
$item->expiresAt(Carbon::now()->addHour()->setMinute(0)->setSecond(0));
|
||||||
$response = file_get_contents(str_replace('{imdbCode}', $imdbCode, $this->searchUrl));
|
try {
|
||||||
return json_decode(
|
$response = $this->client->get("$this->searchUrl/$imdbCode.json");
|
||||||
$response,
|
return json_decode(
|
||||||
true
|
$response->getBody()->getContents(),
|
||||||
);
|
true
|
||||||
|
);
|
||||||
|
} catch (\Throwable $exception) {
|
||||||
|
if ($exception->getCode() === 429) {
|
||||||
|
$this->logger->warning("> [TorrentioClient] Rate limit exceeded");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->logger->error("> [TorrentioClient] Request error: " . $response->getStatusCode() . " - " . $response->getBody()->getContents());
|
||||||
|
return [];
|
||||||
});
|
});
|
||||||
|
|
||||||
return $this->parse($results, $filter);
|
return $this->parse($results, $filter);
|
||||||
@@ -44,13 +63,27 @@ class Torrentio
|
|||||||
$cacheKey = "torrentio.$imdbId.$season.$episode";
|
$cacheKey = "torrentio.$imdbId.$season.$episode";
|
||||||
$results = $this->cache->get($cacheKey, function (ItemInterface $item) use ($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->expiresAt(Carbon::now()->addHour()->setMinute(0)->setSecond(0));
|
||||||
$response = file_get_contents(str_replace('{imdbCode}', "$imdbId:$season:$episode", $this->searchUrl));
|
try {
|
||||||
return json_decode(
|
$response = $this->client->get("$this->searchUrl/$imdbId:$season:$episode.json");
|
||||||
$response,
|
return json_decode(
|
||||||
true
|
$response->getBody()->getContents(),
|
||||||
);
|
true
|
||||||
|
);
|
||||||
|
} catch (\Throwable $exception) {
|
||||||
|
if ($exception->getCode() === 429) {
|
||||||
|
$this->logger->warning("> [TorrentioClient] Rate limit exceeded");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->logger->error("> [TorrentioClient] Request error: " . $response->getStatusCode() . " - " . $response->getBody()->getContents());
|
||||||
|
return [];
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (null === $results) {
|
||||||
|
throw new TorrentioRateLimitException();
|
||||||
|
}
|
||||||
|
|
||||||
return $this->parse($results, []);
|
return $this->parse($results, []);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
11
src/Torrentio/Exception/TorrentioRateLimitException.php
Normal file
11
src/Torrentio/Exception/TorrentioRateLimitException.php
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Torrentio\Exception;
|
||||||
|
|
||||||
|
class TorrentioRateLimitException extends \Exception
|
||||||
|
{
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
parent::__construct(sprintf("[TorrentioClient] Rate limit exceeded"));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -21,6 +21,15 @@ class UtilExtension
|
|||||||
return uniqid();
|
return uniqid();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[AsTwigFilter('truncate')]
|
||||||
|
public function truncate(string $text)
|
||||||
|
{
|
||||||
|
if (strlen($text) > 300) {
|
||||||
|
$text = substr($text, 0, 300) . '...';
|
||||||
|
}
|
||||||
|
return $text;
|
||||||
|
}
|
||||||
|
|
||||||
#[AsTwigFilter('filesize')]
|
#[AsTwigFilter('filesize')]
|
||||||
public function type(string|int $size)
|
public function type(string|int $size)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ readonly class Broadcaster
|
|||||||
private RequestStack $requestStack,
|
private RequestStack $requestStack,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
public function alert(string $title, string $message): void
|
public function alert(string $title, string $message, string $type = "success"): void
|
||||||
{
|
{
|
||||||
$userAlertTopic = $this->requestStack->getCurrentRequest()->getSession()->get('mercure_alert_topic');
|
$userAlertTopic = $this->requestStack->getCurrentRequest()->getSession()->get('mercure_alert_topic');
|
||||||
$update = new Update(
|
$update = new Update(
|
||||||
@@ -26,6 +26,7 @@ readonly class Broadcaster
|
|||||||
'alert_id' => uniqid(),
|
'alert_id' => uniqid(),
|
||||||
'title' => $title,
|
'title' => $title,
|
||||||
'message' => $message,
|
'message' => $message,
|
||||||
|
'type' => $type,
|
||||||
])
|
])
|
||||||
);
|
);
|
||||||
$this->hub->publish($update);
|
$this->hub->publish($update);
|
||||||
|
|||||||
@@ -13,6 +13,8 @@ module.exports = {
|
|||||||
"bg-orange-400",
|
"bg-orange-400",
|
||||||
"bg-blue-600",
|
"bg-blue-600",
|
||||||
"bg-rose-600",
|
"bg-rose-600",
|
||||||
|
"alert-success",
|
||||||
|
"alert-warning",
|
||||||
"min-w-64",
|
"min-w-64",
|
||||||
"rotate-180",
|
"rotate-180",
|
||||||
"-rotate-180",
|
"-rotate-180",
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<turbo-stream action="prepend" target="alert_list">
|
<turbo-stream action="prepend" target="alert_list">
|
||||||
<template>
|
<template>
|
||||||
<twig:Alert :title="title|default('')" :message="message" :alert_id="alert_id" data-controller="alert" />
|
<twig:Alert :title="title|default('')" :message="message" :alert_id="alert_id" type="{{ type|default('success') }}" data-controller="alert" />
|
||||||
</template>
|
</template>
|
||||||
</turbo-stream>
|
</turbo-stream>
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
<li {{ attributes }} id="alert_{{ alert_id }}" class="
|
<li {{ attributes }} id="alert_{{ alert_id }}"
|
||||||
text-white bg-green-950 text-sm min-w-[250px]
|
class="alert alert-{{ type|default('success') }}"
|
||||||
hover:bg-green-900 border border-green-500 px-4 py-3
|
|
||||||
rounded-md"
|
|
||||||
role="alert"
|
role="alert"
|
||||||
>
|
>
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
|
|||||||
@@ -38,13 +38,13 @@
|
|||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="sticky inset-x-0 bottom-0 border-t border-orange-500">
|
<div class="sticky inset-x-0 bottom-0 border-t border-b border-orange-500 bg-orange-500 hover:bg-opacity-80 bg-clip-padding backdrop-filter backdrop-blur-md bg-opacity-60 flex flex-col">
|
||||||
<a href="#" class="nav-foot flex items-center gap-2 p-4 bg-orange-500 hover:bg-opacity-80 bg-clip-padding backdrop-filter backdrop-blur-md bg-opacity-60">
|
<a href="#" class="nav-foot flex items-center gap-2 pt-4 px-4">
|
||||||
<span class="rounded-full p-2 border-orange-500 border-2">
|
<span class="rounded-full p-2 border-orange-500 border-2">
|
||||||
<twig:ux:icon name="ri:user-line" width="30" class="text-gray-50"/>
|
<twig:ux:icon name="ri:user-line" width="30" class="text-gray-50"/>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<div>
|
<div class="flex flex-col text-white">
|
||||||
<p class="text-xs">
|
<p class="text-xs">
|
||||||
{% if app.user.name %}
|
{% if app.user.name %}
|
||||||
<strong class="block font-medium text-white">{{ app.user.name }}</strong>
|
<strong class="block font-medium text-white">{{ app.user.name }}</strong>
|
||||||
@@ -54,5 +54,8 @@
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</a>
|
</a>
|
||||||
|
<p class="px-4 pt-1 inline-flex justify-center">
|
||||||
|
<small class="text-white text-xs">v{{ version|default('0.0') }}</small>
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
|
|||||||
@@ -13,7 +13,60 @@
|
|||||||
season: episode['season_number'],
|
season: episode['season_number'],
|
||||||
episode: episode['episode_number'],
|
episode: episode['episode_number'],
|
||||||
active: 'true',
|
active: 'true',
|
||||||
}) }}></div>
|
}) }}
|
||||||
|
>
|
||||||
|
<div class="p-6 flex flex-col gap-6 bg-orange-500 bg-clip-padding backdrop-filter backdrop-blur-md bg-opacity-60 rounded-md">
|
||||||
|
<div class="flex flex-row gap-4">
|
||||||
|
{% if episode['poster'] != null %}
|
||||||
|
<img class="w-64 rounded-lg" src="{{ episode['poster'] }}" />
|
||||||
|
{% else %}
|
||||||
|
<div class="w-64 min-w-64 sticky h-[144px] rounded-lg bg-gray-700 flex items-center justify-center">
|
||||||
|
<twig:ux:icon width="32" name="hugeicons:loading-01" />
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
<div class="flex flex-col gap-4 grow">
|
||||||
|
<h4 class="text-md font-bold">
|
||||||
|
{{ episode['episode_number'] }}. {{ episode['name'] }}
|
||||||
|
</h4>
|
||||||
|
<p>{{ episode['overview']|truncate }}</p>
|
||||||
|
<div>
|
||||||
|
<button class="py-1 px-1.5 mr-1 grow-0 font-bold text-xs bg-green-600 rounded-lg hover:cursor-pointer hover:bg-green-700 text-white"
|
||||||
|
{{ stimulus_action('tv-results', 'toggleList', 'click') }}
|
||||||
|
>
|
||||||
|
<span {{ stimulus_target('tv-results', 'count') }}>-</span> results
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<small class="py-1 px-1.5 mr-1 grow-0 font-bold bg-gray-700 rounded-lg font-normal text-white" title="Air date {{ episode['name'] }}">
|
||||||
|
{{ episode['air_date']|date }}
|
||||||
|
</small>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex flex-col gap-4 justify-between">
|
||||||
|
<div class="flex flex-col items-center">
|
||||||
|
<input type="checkbox"
|
||||||
|
{{ stimulus_target('tv-results', 'episodeSelector') }}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<button class="flex flex-col items-end transition-transform duration-300 ease-in-out rotate-90"
|
||||||
|
{{ stimulus_target('tv-results', 'toggleButton') }}
|
||||||
|
{{ stimulus_action('tv-results', 'toggleList', 'click') }}>
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" width="32">
|
||||||
|
<path
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
stroke-width="2"
|
||||||
|
d="M20 6L10 16l10 10" />
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div {{ stimulus_target('tv-results', 'listContainer') }} class="inline-block overflow-hidden rounded-lg">
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
{% set paginator = this.episodes %}
|
{% set paginator = this.episodes %}
|
||||||
|
|||||||
@@ -119,6 +119,7 @@
|
|||||||
</div>
|
</div>
|
||||||
{% elseif "tvshows" == results.media.mediaType %}
|
{% elseif "tvshows" == results.media.mediaType %}
|
||||||
<twig:TvEpisodeList
|
<twig:TvEpisodeList
|
||||||
|
results="results"
|
||||||
:imdbId="results.media.imdbId" :season="results.season" :perPage="20" :pageNumber="1"
|
:imdbId="results.media.imdbId" :season="results.season" :perPage="20" :pageNumber="1"
|
||||||
:tmdbId="results.media.tmdbId" :title="results.media.title" loading="defer"
|
:tmdbId="results.media.tmdbId" :title="results.media.title" loading="defer"
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -1,82 +1 @@
|
|||||||
<div class="p-6 flex flex-col gap-6 bg-orange-500 bg-clip-padding backdrop-filter backdrop-blur-md bg-opacity-60 rounded-md">
|
{{ include('torrentio/partial/option-table.html.twig', {controller: 'tv-results'}) }}
|
||||||
<div class="flex flex-row gap-4">
|
|
||||||
{% if results.media.poster != null %}
|
|
||||||
<img class="w-64 rounded-lg" src="{{ results.media.poster }}" />
|
|
||||||
{% else %}
|
|
||||||
<div class="w-64 min-w-64 sticky h-[144px] rounded-lg bg-gray-700 flex items-center justify-center">
|
|
||||||
<twig:ux:icon width="32" name="hugeicons:loading-01" />
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
<div class="flex flex-col gap-4 grow">
|
|
||||||
<h4 class="text-md font-bold">
|
|
||||||
{{ results.episode }}. {{ results.media.title }}
|
|
||||||
</h4>
|
|
||||||
<p>{{ results.media.description }}</p>
|
|
||||||
<div>
|
|
||||||
<button class="py-1 px-1.5 mr-1 grow-0 font-bold text-xs bg-green-600 rounded-lg hover:cursor-pointer hover:bg-green-700 text-white"
|
|
||||||
{{ stimulus_action('tv-results', 'toggleList', 'click') }}
|
|
||||||
>
|
|
||||||
<span {{ stimulus_target('tv-results', 'count') }}>{{ results.results|length }}</span> results
|
|
||||||
</button>
|
|
||||||
|
|
||||||
{% if results.file != false %}
|
|
||||||
<span data-controller="popover">
|
|
||||||
<template data-popover-target="content">
|
|
||||||
<div data-popover-target="card" class="absolute z-40 p-1 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 episode:</p>
|
|
||||||
<ul class="list-disc ml-3">
|
|
||||||
<li class="font-normal">{{ results.file.realPath|strip_media_path }} — <strong>{{ results.file.size|filesize }}</strong></li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<small
|
|
||||||
class="py-1 px-1.5 mr-1 grow-0 font-bold bg-blue-600 rounded-lg text-center text-white"
|
|
||||||
data-action="mouseenter->popover#show mouseleave->popover#hide"
|
|
||||||
>
|
|
||||||
exists
|
|
||||||
</small>
|
|
||||||
</span>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
{% if results.file == false %}
|
|
||||||
<small class="py-1 px-1.5 mr-1 grow-0 font-bold bg-rose-600 rounded-lg text-white" title="Episode has not been downloaded yet.">
|
|
||||||
missing
|
|
||||||
</small>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
<small class="py-1 px-1.5 mr-1 grow-0 font-bold bg-gray-700 rounded-lg font-normal text-white" title="Air date {{ results.media.episodeAirDate }}">
|
|
||||||
{{ results.media.episodeAirDate }}
|
|
||||||
</small>
|
|
||||||
{# <small class="py-1 px-1.5 grow-0 font-bold bg-red-600 hover:bg-red-700 rounded-lg font-normal text-white cursor-pointer" title="Clear cache for {{ results.media.title }}"#}
|
|
||||||
{# {{ stimulus_action('tv-results', 'clearCache', 'click') }}#}
|
|
||||||
{# >Clear Cache</small>#}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="flex flex-col gap-4 justify-between">
|
|
||||||
<div class="flex flex-col items-center">
|
|
||||||
<input type="checkbox"
|
|
||||||
{{ stimulus_target('tv-results', 'episodeSelector') }}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<button class="flex flex-col items-end transition-transform duration-300 ease-in-out rotate-90"
|
|
||||||
{{ stimulus_target('tv-results', 'toggleButton') }}
|
|
||||||
{{ stimulus_action('tv-results', 'toggleList', 'click') }}>
|
|
||||||
{# <svg xmlns="http://www.w3.org/2000/svg" width="2em" height="2em" viewBox="0 0 32 32">#}
|
|
||||||
{# <path fill="currentColor" d="m16 10l10 10l-1.4 1.4l-8.6-8.6l-8.6 8.6L6 20z"/>#}
|
|
||||||
{# </svg>#}
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" width="32">
|
|
||||||
<path
|
|
||||||
fill="none"
|
|
||||||
stroke="currentColor"
|
|
||||||
stroke-linecap="round"
|
|
||||||
stroke-linejoin="round"
|
|
||||||
stroke-width="2"
|
|
||||||
d="M20 6L10 16l10 10" />
|
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="inline-block overflow-hidden rounded-lg">
|
|
||||||
{{ include('torrentio/partial/option-table.html.twig', {controller: 'tv-results'}) }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|||||||
Reference in New Issue
Block a user