Compare commits
9 Commits
dev-downlo
...
dev-ajax-n
| Author | SHA1 | Date | |
|---|---|---|---|
| 48a601f58d | |||
| a1a38cb74c | |||
| e9ccb5ad2b | |||
| 9d350a572d | |||
| cd271b568b | |||
| 6b88483635 | |||
| 0120ddcedd | |||
| 7270fa2936 | |||
| 6a2567bf98 |
@@ -1,3 +1,4 @@
|
|||||||
|
APP_SECRET="%%app_secret%%"
|
||||||
DATABASE_URL="%%db_url%%"
|
DATABASE_URL="%%db_url%%"
|
||||||
DOWNLOAD_DIR=%%download_dir%%
|
DOWNLOAD_DIR=%%download_dir%%
|
||||||
REAL_DEBRID_KEY="%%rd_key%%"
|
REAL_DEBRID_KEY="%%rd_key%%"
|
||||||
|
|||||||
@@ -1,4 +1,14 @@
|
|||||||
{
|
{
|
||||||
"controllers": [],
|
"controllers": {
|
||||||
|
"@symfony/ux-live-component": {
|
||||||
|
"live": {
|
||||||
|
"enabled": true,
|
||||||
|
"fetch": "eager",
|
||||||
|
"autoimport": {
|
||||||
|
"@symfony/ux-live-component/dist/live.min.css": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"entrypoints": []
|
"entrypoints": []
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Controller } from '@hotwired/stimulus';
|
import { Controller } from '@hotwired/stimulus';
|
||||||
|
import flasher from '@flasher/flasher'
|
||||||
/*
|
/*
|
||||||
* The following line makes this controller "lazy": it won't be downloaded until needed
|
* The following line makes this controller "lazy": it won't be downloaded until needed
|
||||||
* See https://github.com/symfony/stimulus-bridge#lazy-controllers
|
* See https://github.com/symfony/stimulus-bridge#lazy-controllers
|
||||||
@@ -31,7 +31,7 @@ export default class extends Controller {
|
|||||||
})
|
})
|
||||||
.then(res => res.json())
|
.then(res => res.json())
|
||||||
.then(json => {
|
.then(json => {
|
||||||
console.log(json)
|
flasher.success(json.message);
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ export default class extends Controller {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static outlets = ['movie-results', 'tv-results']
|
static outlets = ['movie-results', 'tv-results']
|
||||||
static targets = ['resolution', 'codec', 'language', 'provider', 'season']
|
static targets = ['resolution', 'codec', 'language', 'provider', 'season', 'selectAll', 'downloadSelected']
|
||||||
static values = {
|
static values = {
|
||||||
'media-type': String,
|
'media-type': String,
|
||||||
'episodes': Array,
|
'episodes': Array,
|
||||||
@@ -29,7 +29,8 @@ export default class extends Controller {
|
|||||||
|
|
||||||
connect() {
|
connect() {
|
||||||
if (this.mediaTypeValue === "tvshows") {
|
if (this.mediaTypeValue === "tvshows") {
|
||||||
this.activeFilter['season'] = 1;}
|
this.activeFilter['season'] = 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async movieResultsOutletConnected(outlet) {
|
async movieResultsOutletConnected(outlet) {
|
||||||
@@ -148,7 +149,7 @@ export default class extends Controller {
|
|||||||
} else if (true === firstIncluded) {
|
} else if (true === firstIncluded) {
|
||||||
count = 1;
|
count = 1;
|
||||||
selectedCount = selectedCount + 1;
|
selectedCount = selectedCount + 1;
|
||||||
// option.selectInput.checked = true;
|
option.querySelector('input[type="checkbox"]').checked = true;
|
||||||
firstIncluded = false;
|
firstIncluded = false;
|
||||||
} else {
|
} else {
|
||||||
count = count + 1;
|
count = count + 1;
|
||||||
@@ -158,8 +159,30 @@ export default class extends Controller {
|
|||||||
resultList.countTarget.innerText = count;
|
resultList.countTarget.innerText = count;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
await results.forEach((list) => filterOperation(list, currentSeason));
|
await results.forEach((list) => filterOperation(list, currentSeason));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uncheckSelectAllBtn() {
|
||||||
|
this.selectAllTarget.checked = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
selectAllEpisodes() {
|
||||||
|
this.tvResultsOutlets.forEach((episode) => {
|
||||||
|
if (episode.isActive()) {
|
||||||
|
episode.selectEpisodeForDownload()
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
downloadSelectedEpisodes() {
|
||||||
|
this.tvResultsOutlets.forEach(episode => {
|
||||||
|
if (episode.isActive() && episode.isSelected()) {
|
||||||
|
episode.download();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.selectAllTarget.checked = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ export default class extends Controller {
|
|||||||
active: Boolean,
|
active: Boolean,
|
||||||
};
|
};
|
||||||
|
|
||||||
static targets = ['list', 'count']
|
static targets = ['list', 'count', 'episodeSelector']
|
||||||
static outlets = ['loading-icon']
|
static outlets = ['loading-icon']
|
||||||
|
|
||||||
options = []
|
options = []
|
||||||
@@ -32,7 +32,12 @@ export default class extends Controller {
|
|||||||
.then(response => {
|
.then(response => {
|
||||||
this.element.innerHTML = response;
|
this.element.innerHTML = response;
|
||||||
this.options = this.element.querySelectorAll('tbody tr');
|
this.options = this.element.querySelectorAll('tbody tr');
|
||||||
this.options.forEach((option) => option.querySelector('.download-btn').dataset['title'] = this.titleValue);
|
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.episodeSelectorTarget.disabled = true;
|
||||||
|
}
|
||||||
this.optionsLoaded = true;
|
this.optionsLoaded = true;
|
||||||
this.loadingIconOutlet.increaseCount();
|
this.loadingIconOutlet.increaseCount();
|
||||||
});
|
});
|
||||||
@@ -49,6 +54,7 @@ export default class extends Controller {
|
|||||||
|
|
||||||
setInActive() {
|
setInActive() {
|
||||||
this.activeValue = false;
|
this.activeValue = false;
|
||||||
|
this.episodeSelectorTarget.checked = false;
|
||||||
this.element.classList.add('hidden');
|
this.element.classList.add('hidden');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -56,7 +62,30 @@ export default class extends Controller {
|
|||||||
return this.activeValue;
|
return this.activeValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
isSelected() {
|
||||||
|
return this.episodeSelectorTarget.checked;
|
||||||
|
}
|
||||||
|
|
||||||
|
selectEpisodeForDownload() {
|
||||||
|
if (true === this.isActive() && this.episodeSelectorTarget.disabled === false) {
|
||||||
|
this.episodeSelectorTarget.checked = !this.episodeSelectorTarget.checked;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
toggleList() {
|
toggleList() {
|
||||||
this.listTarget.classList.toggle('hidden');
|
this.listTarget.classList.toggle('hidden');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
download() {
|
||||||
|
this.options.forEach(option => {
|
||||||
|
const optionSelector = option.querySelector('input[type="checkbox"]');
|
||||||
|
if (true === optionSelector.checked) {
|
||||||
|
const downloadBtn = option.querySelector('button.download-btn');
|
||||||
|
const downloadBtnController = this.application.getControllerForElementAndIdentifier(downloadBtn, 'download-button');
|
||||||
|
downloadBtnController.download();
|
||||||
|
optionSelector.checked = false;
|
||||||
|
this.episodeSelectorTarget.checked = false;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,6 +24,7 @@
|
|||||||
<copy file="${project.basedir}/.env.dist" tofile="${project.basedir}/.env.local" overwrite="true">
|
<copy file="${project.basedir}/.env.dist" tofile="${project.basedir}/.env.local" overwrite="true">
|
||||||
<filterchain>
|
<filterchain>
|
||||||
<replacetokens begintoken="%%" endtoken="%%">
|
<replacetokens begintoken="%%" endtoken="%%">
|
||||||
|
<token key="app_secret" value="${APP_SECRET}" />
|
||||||
<token key="db_url" value="${DATABASE_URL}" />
|
<token key="db_url" value="${DATABASE_URL}" />
|
||||||
<token key="download_dir" value="${DOWNLOAD_DIR}" />
|
<token key="download_dir" value="${DOWNLOAD_DIR}" />
|
||||||
<token key="rd_key" value="${REAL_DEBRID_KEY}" />
|
<token key="rd_key" value="${REAL_DEBRID_KEY}" />
|
||||||
|
|||||||
@@ -15,6 +15,7 @@
|
|||||||
"nihilarr/parse-torrent-name": "^0.0.1",
|
"nihilarr/parse-torrent-name": "^0.0.1",
|
||||||
"nyholm/psr7": "*",
|
"nyholm/psr7": "*",
|
||||||
"p3k/emoji-detector": "^1.2",
|
"p3k/emoji-detector": "^1.2",
|
||||||
|
"php-flasher/flasher-symfony": "^2.1",
|
||||||
"php-tmdb/api": "^4.1",
|
"php-tmdb/api": "^4.1",
|
||||||
"symfony/asset": "7.2.*",
|
"symfony/asset": "7.2.*",
|
||||||
"symfony/console": "7.2.*",
|
"symfony/console": "7.2.*",
|
||||||
@@ -29,6 +30,7 @@
|
|||||||
"symfony/stimulus-bundle": "^2.24",
|
"symfony/stimulus-bundle": "^2.24",
|
||||||
"symfony/twig-bundle": "7.2.*",
|
"symfony/twig-bundle": "7.2.*",
|
||||||
"symfony/ux-icons": "^2.24",
|
"symfony/ux-icons": "^2.24",
|
||||||
|
"symfony/ux-live-component": "^2.24",
|
||||||
"symfony/ux-twig-component": "^2.24",
|
"symfony/ux-twig-component": "^2.24",
|
||||||
"symfony/yaml": "7.2.*",
|
"symfony/yaml": "7.2.*",
|
||||||
"symfonycasts/tailwind-bundle": "^0.10.0",
|
"symfonycasts/tailwind-bundle": "^0.10.0",
|
||||||
|
|||||||
231
composer.lock
generated
231
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": "0448ecb537f5d169d81a56ba2b3c2cc6",
|
"content-hash": "02f1ff0023fc6fb23a0307520495e75c",
|
||||||
"packages": [
|
"packages": [
|
||||||
{
|
{
|
||||||
"name": "1tomany/data-uri",
|
"name": "1tomany/data-uri",
|
||||||
@@ -1780,6 +1780,141 @@
|
|||||||
},
|
},
|
||||||
"time": "2024-02-19T18:29:05+00:00"
|
"time": "2024-02-19T18:29:05+00:00"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "php-flasher/flasher",
|
||||||
|
"version": "v2.1.6",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/php-flasher/flasher.git",
|
||||||
|
"reference": "054a209515d2eb1bb72467023d8614a29b5d2e60"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/php-flasher/flasher/zipball/054a209515d2eb1bb72467023d8614a29b5d2e60",
|
||||||
|
"reference": "054a209515d2eb1bb72467023d8614a29b5d2e60",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"php": ">=8.2"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"autoload": {
|
||||||
|
"files": [
|
||||||
|
"functions.php",
|
||||||
|
"helpers.php"
|
||||||
|
],
|
||||||
|
"psr-4": {
|
||||||
|
"Flasher\\Prime\\": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Younes ENNAJI",
|
||||||
|
"email": "younes.ennaji.pro@gmail.com",
|
||||||
|
"homepage": "https://www.linkedin.com/in/younes--ennaji/",
|
||||||
|
"role": "Developer"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "The foundational PHP library for PHPFlasher, enabling the creation of framework-agnostic flash notifications. Ideal for building custom integrations or for use in PHP projects.",
|
||||||
|
"homepage": "https://php-flasher.io",
|
||||||
|
"keywords": [
|
||||||
|
"custom-integrations",
|
||||||
|
"flash-notifications",
|
||||||
|
"flasher-core",
|
||||||
|
"framework-agnostic",
|
||||||
|
"open-source",
|
||||||
|
"php"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"issues": "https://github.com/php-flasher/php-flasher/issues",
|
||||||
|
"source": "https://github.com/php-flasher/php-flasher"
|
||||||
|
},
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"url": "https://www.paypal.com/paypalme/yoeunes",
|
||||||
|
"type": "custom"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://github.com/yoeunes",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"time": "2025-02-21T20:05:00+00:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "php-flasher/flasher-symfony",
|
||||||
|
"version": "v2.1.6",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/php-flasher/flasher-symfony.git",
|
||||||
|
"reference": "14bd1ba6bbd1184bde0300a5b02455e886845cea"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/php-flasher/flasher-symfony/zipball/14bd1ba6bbd1184bde0300a5b02455e886845cea",
|
||||||
|
"reference": "14bd1ba6bbd1184bde0300a5b02455e886845cea",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"php": ">=8.2",
|
||||||
|
"php-flasher/flasher": "^2.1.6",
|
||||||
|
"symfony/config": "^7.0",
|
||||||
|
"symfony/console": "^7.0",
|
||||||
|
"symfony/dependency-injection": "^7.0",
|
||||||
|
"symfony/http-kernel": "^7.0"
|
||||||
|
},
|
||||||
|
"suggest": {
|
||||||
|
"symfony/translation": "To translate flash messages, title and presets",
|
||||||
|
"symfony/ux-twig-component": "To utilize and interact with flash messages components in Twig templates"
|
||||||
|
},
|
||||||
|
"type": "symfony-bundle",
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"Flasher\\Symfony\\": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Younes ENNAJI",
|
||||||
|
"email": "younes.ennaji.pro@gmail.com",
|
||||||
|
"homepage": "https://www.linkedin.com/in/younes--ennaji/",
|
||||||
|
"role": "Developer"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Integrate flash notifications into Symfony projects effortlessly with PHPFlasher. Improve user experience and application feedback loops easily.",
|
||||||
|
"homepage": "https://php-flasher.io",
|
||||||
|
"keywords": [
|
||||||
|
"flash-notifications",
|
||||||
|
"open-source",
|
||||||
|
"php",
|
||||||
|
"phpflasher",
|
||||||
|
"symfony",
|
||||||
|
"user-feedback"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"issues": "https://github.com/php-flasher/php-flasher/issues",
|
||||||
|
"source": "https://github.com/php-flasher/php-flasher"
|
||||||
|
},
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"url": "https://www.paypal.com/paypalme/yoeunes",
|
||||||
|
"type": "custom"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://github.com/yoeunes",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"time": "2025-02-21T20:05:00+00:00"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "php-http/discovery",
|
"name": "php-http/discovery",
|
||||||
"version": "1.20.0",
|
"version": "1.20.0",
|
||||||
@@ -6976,6 +7111,100 @@
|
|||||||
],
|
],
|
||||||
"time": "2025-04-04T17:32:18+00:00"
|
"time": "2025-04-04T17:32:18+00:00"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "symfony/ux-live-component",
|
||||||
|
"version": "v2.24.0",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/symfony/ux-live-component.git",
|
||||||
|
"reference": "ee1a8e5d01f5b3f2f8e6856941fa8c944677e41c"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/symfony/ux-live-component/zipball/ee1a8e5d01f5b3f2f8e6856941fa8c944677e41c",
|
||||||
|
"reference": "ee1a8e5d01f5b3f2f8e6856941fa8c944677e41c",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"php": ">=8.1",
|
||||||
|
"symfony/deprecation-contracts": "^2.5|^3.0",
|
||||||
|
"symfony/property-access": "^5.4.5|^6.0|^7.0",
|
||||||
|
"symfony/property-info": "^5.4|^6.0|^7.0",
|
||||||
|
"symfony/stimulus-bundle": "^2.9",
|
||||||
|
"symfony/ux-twig-component": "^2.8",
|
||||||
|
"twig/twig": "^3.8.0"
|
||||||
|
},
|
||||||
|
"conflict": {
|
||||||
|
"symfony/config": "<5.4.0"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"doctrine/annotations": "^1.0",
|
||||||
|
"doctrine/collections": "^1.6.8|^2.0",
|
||||||
|
"doctrine/doctrine-bundle": "^2.4.3",
|
||||||
|
"doctrine/orm": "^2.9.4",
|
||||||
|
"doctrine/persistence": "^2.5.2|^3.0",
|
||||||
|
"phpdocumentor/reflection-docblock": "5.x-dev",
|
||||||
|
"symfony/dependency-injection": "^5.4|^6.0|^7.0",
|
||||||
|
"symfony/expression-language": "^5.4|^6.0|^7.0",
|
||||||
|
"symfony/form": "^5.4|^6.0|^7.0",
|
||||||
|
"symfony/framework-bundle": "^5.4|^6.0|^7.0",
|
||||||
|
"symfony/options-resolver": "^5.4|^6.0|^7.0",
|
||||||
|
"symfony/phpunit-bridge": "^6.1|^7.0",
|
||||||
|
"symfony/security-bundle": "^5.4|^6.0|^7.0",
|
||||||
|
"symfony/serializer": "^5.4|^6.0|^7.0",
|
||||||
|
"symfony/twig-bundle": "^5.4|^6.0|^7.0",
|
||||||
|
"symfony/validator": "^5.4|^6.0|^7.0",
|
||||||
|
"zenstruck/browser": "^1.2.0",
|
||||||
|
"zenstruck/foundry": "^2.0"
|
||||||
|
},
|
||||||
|
"type": "symfony-bundle",
|
||||||
|
"extra": {
|
||||||
|
"thanks": {
|
||||||
|
"url": "https://github.com/symfony/ux",
|
||||||
|
"name": "symfony/ux"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"Symfony\\UX\\LiveComponent\\": "src/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Symfony Community",
|
||||||
|
"homepage": "https://symfony.com/contributors"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Live components for Symfony",
|
||||||
|
"homepage": "https://symfony.com",
|
||||||
|
"keywords": [
|
||||||
|
"components",
|
||||||
|
"symfony-ux",
|
||||||
|
"twig"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"source": "https://github.com/symfony/ux-live-component/tree/v2.24.0"
|
||||||
|
},
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"url": "https://symfony.com/sponsor",
|
||||||
|
"type": "custom"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://github.com/fabpot",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
|
||||||
|
"type": "tidelift"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"time": "2025-03-12T08:41:47+00:00"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/ux-twig-component",
|
"name": "symfony/ux-twig-component",
|
||||||
"version": "v2.24.0",
|
"version": "v2.24.0",
|
||||||
|
|||||||
@@ -13,4 +13,6 @@ return [
|
|||||||
Symfony\UX\StimulusBundle\StimulusBundle::class => ['all' => true],
|
Symfony\UX\StimulusBundle\StimulusBundle::class => ['all' => true],
|
||||||
Doctrine\Bundle\DoctrineBundle\DoctrineBundle::class => ['all' => true],
|
Doctrine\Bundle\DoctrineBundle\DoctrineBundle::class => ['all' => true],
|
||||||
Doctrine\Bundle\MigrationsBundle\DoctrineMigrationsBundle::class => ['all' => true],
|
Doctrine\Bundle\MigrationsBundle\DoctrineMigrationsBundle::class => ['all' => true],
|
||||||
|
Symfony\UX\LiveComponent\LiveComponentBundle::class => ['all' => true],
|
||||||
|
Flasher\Symfony\FlasherSymfonyBundle::class => ['all' => true],
|
||||||
];
|
];
|
||||||
|
|||||||
48
config/packages/flasher.yaml
Normal file
48
config/packages/flasher.yaml
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
flasher:
|
||||||
|
# Default notification library (e.g., 'flasher', 'toastr', 'noty', 'notyf', 'sweetalert')
|
||||||
|
default: flasher
|
||||||
|
|
||||||
|
# Path to the main PHPFlasher JavaScript file
|
||||||
|
main_script: '/vendor/flasher/flasher.min.js'
|
||||||
|
|
||||||
|
# List of CSS files to style your notifications
|
||||||
|
styles:
|
||||||
|
- '/vendor/flasher/flasher.min.css'
|
||||||
|
|
||||||
|
# Set global options for all notifications (optional)
|
||||||
|
# options:
|
||||||
|
# # Time in milliseconds before the notification disappears
|
||||||
|
# timeout: 5000
|
||||||
|
# # Where the notification appears on the screen
|
||||||
|
# position: 'top-right'
|
||||||
|
|
||||||
|
# Automatically inject JavaScript and CSS assets into your HTML pages
|
||||||
|
inject_assets: true
|
||||||
|
|
||||||
|
# Enable message translation using Symfony's translation service
|
||||||
|
translate: true
|
||||||
|
|
||||||
|
# URL patterns to exclude from asset injection and flash_bag conversion
|
||||||
|
excluded_paths:
|
||||||
|
- '/^\/_profiler/'
|
||||||
|
- '/^\/_fragment/'
|
||||||
|
|
||||||
|
# Map Symfony flash message keys to notification types
|
||||||
|
flash_bag:
|
||||||
|
success: ['success']
|
||||||
|
error: ['error', 'danger']
|
||||||
|
warning: ['warning', 'alarm']
|
||||||
|
info: ['info', 'notice', 'alert']
|
||||||
|
|
||||||
|
# Set criteria to filter which notifications are displayed (optional)
|
||||||
|
# filter:
|
||||||
|
# # Maximum number of notifications to show at once
|
||||||
|
# limit: 5
|
||||||
|
|
||||||
|
# Define notification presets to simplify notification creation (optional)
|
||||||
|
# presets:
|
||||||
|
# # Example preset:
|
||||||
|
# entity_saved:
|
||||||
|
# type: 'success'
|
||||||
|
# title: 'Entity saved'
|
||||||
|
# message: 'Entity saved successfully'
|
||||||
5
config/routes/ux_live_component.yaml
Normal file
5
config/routes/ux_live_component.yaml
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
live_component:
|
||||||
|
resource: '@LiveComponentBundle/config/routes.php'
|
||||||
|
prefix: '/_components'
|
||||||
|
# adjust prefix to add localization to your components
|
||||||
|
#prefix: '/{_locale}/_components'
|
||||||
@@ -22,4 +22,10 @@ return [
|
|||||||
'@symfony/stimulus-bundle' => [
|
'@symfony/stimulus-bundle' => [
|
||||||
'path' => './vendor/symfony/stimulus-bundle/assets/dist/loader.js',
|
'path' => './vendor/symfony/stimulus-bundle/assets/dist/loader.js',
|
||||||
],
|
],
|
||||||
|
'@symfony/ux-live-component' => [
|
||||||
|
'path' => './vendor/symfony/ux-live-component/assets/dist/live_controller.js',
|
||||||
|
],
|
||||||
|
'@flasher/flasher' => [
|
||||||
|
'version' => '2.1.5',
|
||||||
|
],
|
||||||
];
|
];
|
||||||
|
|||||||
2
public/vendor/flasher/flasher.min.css
vendored
Normal file
2
public/vendor/flasher/flasher.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
1
public/vendor/flasher/flasher.min.js
vendored
Normal file
1
public/vendor/flasher/flasher.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
4
public/vendor/flasher/manifest.json
vendored
Normal file
4
public/vendor/flasher/manifest.json
vendored
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"/vendor/flasher/flasher.min.js": "/vendor/flasher/flasher.min.js?id=9a255a6680873c0d5fc3d394a2ba3195",
|
||||||
|
"/vendor/flasher/flasher.min.css": "/vendor/flasher/flasher.min.css?id=7a96e40c68626198d5128ad2fb5d77e0"
|
||||||
|
}
|
||||||
@@ -2,17 +2,26 @@
|
|||||||
|
|
||||||
namespace App\Controller;
|
namespace App\Controller;
|
||||||
|
|
||||||
|
use App\Download\Framework\Repository\DownloadRepository;
|
||||||
|
use App\Tmdb\Tmdb;
|
||||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||||
use Symfony\Component\HttpFoundation\Response;
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
use Symfony\Component\Routing\Attribute\Route;
|
use Symfony\Component\Routing\Attribute\Route;
|
||||||
|
|
||||||
final class IndexController extends AbstractController
|
final class IndexController extends AbstractController
|
||||||
{
|
{
|
||||||
|
public function __construct(
|
||||||
|
private readonly DownloadRepository $downloadRepository,
|
||||||
|
private readonly Tmdb $tmdb,
|
||||||
|
) {}
|
||||||
|
|
||||||
#[Route('/', name: 'app_index')]
|
#[Route('/', name: 'app_index')]
|
||||||
public function index(): Response
|
public function index(): Response
|
||||||
{
|
{
|
||||||
return $this->render('index/index.html.twig', [
|
return $this->render('index/index.html.twig', [
|
||||||
'controller_name' => 'IndexController',
|
'active_downloads' => $this->downloadRepository->getActivePaginated(),
|
||||||
|
'recent_downloads' => $this->downloadRepository->latest(5),
|
||||||
|
'popular_movies' => $this->tmdb->popularMovies(1, 6),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,11 +36,12 @@ class DownloadRepository extends ServiceEntityRepository
|
|||||||
return new \Doctrine\ORM\Tools\Pagination\Paginator($query);
|
return new \Doctrine\ORM\Tools\Pagination\Paginator($query);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getActivePaginated(int $pageNumber = 1, int $perPage = 10)
|
public function getActivePaginated(int $pageNumber = 1, int $perPage = 5)
|
||||||
{
|
{
|
||||||
$firstResult = ($pageNumber - 1) * $perPage;
|
$firstResult = ($pageNumber - 1) * $perPage;
|
||||||
$query = $this->createQueryBuilder('d')
|
$query = $this->createQueryBuilder('d')
|
||||||
->andWhere('d.status IN (:statuses)')
|
->andWhere('d.status IN (:statuses)')
|
||||||
|
->orderBy('d.id', 'DESC')
|
||||||
->setParameter('statuses', ['New', 'In Progress'])
|
->setParameter('statuses', ['New', 'In Progress'])
|
||||||
->setFirstResult($firstResult)
|
->setFirstResult($firstResult)
|
||||||
->setMaxResults($perPage)
|
->setMaxResults($perPage)
|
||||||
@@ -115,4 +116,15 @@ class DownloadRepository extends ServiceEntityRepository
|
|||||||
|
|
||||||
return $query->getResult();
|
return $query->getResult();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function latest(int $limit = 1)
|
||||||
|
{
|
||||||
|
return $this->createQueryBuilder('d')
|
||||||
|
->andWhere('d.status IN (:statuses)')
|
||||||
|
->setParameter('statuses', ['Complete'])
|
||||||
|
->setMaxResults($limit)
|
||||||
|
->orderBy('d.id', 'DESC')
|
||||||
|
->getQuery()
|
||||||
|
->getResult();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,9 +4,9 @@ namespace App\Search\Action\Command;
|
|||||||
|
|
||||||
use OneToMany\RichBundle\Contract\CommandInterface;
|
use OneToMany\RichBundle\Contract\CommandInterface;
|
||||||
|
|
||||||
|
/** @implements CommandInterface<GetMediaInfoCommand> */
|
||||||
class GetMediaInfoCommand implements CommandInterface
|
class GetMediaInfoCommand implements CommandInterface
|
||||||
{
|
{
|
||||||
/** @implements CommandInterface<GetMediaInfoCommand> */
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
public string $tmdbId,
|
public string $tmdbId,
|
||||||
public string $mediaType,
|
public string $mediaType,
|
||||||
|
|||||||
@@ -4,12 +4,9 @@ namespace App\Search\Action\Command;
|
|||||||
|
|
||||||
use OneToMany\RichBundle\Contract\CommandInterface;
|
use OneToMany\RichBundle\Contract\CommandInterface;
|
||||||
|
|
||||||
|
/** @implements CommandInterface<SearchCommand> */
|
||||||
class SearchCommand implements CommandInterface
|
class SearchCommand implements CommandInterface
|
||||||
{
|
{
|
||||||
/**
|
|
||||||
* @param string $term
|
|
||||||
* @implements CommandInterface<SearchCommand>
|
|
||||||
*/
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
public string $term
|
public string $term
|
||||||
) {}
|
) {}
|
||||||
|
|||||||
@@ -2,12 +2,14 @@
|
|||||||
|
|
||||||
namespace App\Search\Action\Handler;
|
namespace App\Search\Action\Handler;
|
||||||
|
|
||||||
|
use App\Search\Action\Command\GetMediaInfoCommand;
|
||||||
use App\Search\Action\Result\GetMediaInfoResult;
|
use App\Search\Action\Result\GetMediaInfoResult;
|
||||||
use App\Tmdb\Tmdb;
|
use App\Tmdb\Tmdb;
|
||||||
use OneToMany\RichBundle\Contract\CommandInterface;
|
use OneToMany\RichBundle\Contract\CommandInterface;
|
||||||
use OneToMany\RichBundle\Contract\HandlerInterface;
|
use OneToMany\RichBundle\Contract\HandlerInterface;
|
||||||
use OneToMany\RichBundle\Contract\ResultInterface;
|
use OneToMany\RichBundle\Contract\ResultInterface;
|
||||||
|
|
||||||
|
/** @implements HandlerInterface<GetMediaInfoCommand, GetMediaInfoResult> */
|
||||||
class GetMediaInfoHandler implements HandlerInterface
|
class GetMediaInfoHandler implements HandlerInterface
|
||||||
{
|
{
|
||||||
public function __construct(
|
public function __construct(
|
||||||
|
|||||||
@@ -8,13 +8,13 @@ use OneToMany\RichBundle\Contract\CommandInterface;
|
|||||||
use OneToMany\RichBundle\Contract\HandlerInterface;
|
use OneToMany\RichBundle\Contract\HandlerInterface;
|
||||||
use OneToMany\RichBundle\Contract\ResultInterface;
|
use OneToMany\RichBundle\Contract\ResultInterface;
|
||||||
|
|
||||||
|
/*** @implements HandlerInterface<SearchResult> */
|
||||||
class SearchHandler implements HandlerInterface
|
class SearchHandler implements HandlerInterface
|
||||||
{
|
{
|
||||||
public function __construct(
|
public function __construct(
|
||||||
private Tmdb $tmdb,
|
private Tmdb $tmdb,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
/*** @implements HandlerInterface<SearchResult> */
|
|
||||||
public function handle(CommandInterface $command): ResultInterface
|
public function handle(CommandInterface $command): ResultInterface
|
||||||
{
|
{
|
||||||
return new SearchResult(
|
return new SearchResult(
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ use OneToMany\RichBundle\Attribute\SourceRoute;
|
|||||||
use OneToMany\RichBundle\Contract\CommandInterface;
|
use OneToMany\RichBundle\Contract\CommandInterface;
|
||||||
use OneToMany\RichBundle\Contract\InputInterface;
|
use OneToMany\RichBundle\Contract\InputInterface;
|
||||||
|
|
||||||
|
/** @implements InputInterface<GetMediaInfoInput> */
|
||||||
class GetMediaInfoInput implements InputInterface
|
class GetMediaInfoInput implements InputInterface
|
||||||
{
|
{
|
||||||
public function __construct(
|
public function __construct(
|
||||||
|
|||||||
@@ -8,9 +8,7 @@ use OneToMany\RichBundle\Attribute\SourceRequest;
|
|||||||
use OneToMany\RichBundle\Contract\CommandInterface;
|
use OneToMany\RichBundle\Contract\CommandInterface;
|
||||||
use OneToMany\RichBundle\Contract\InputInterface;
|
use OneToMany\RichBundle\Contract\InputInterface;
|
||||||
|
|
||||||
/**
|
/** @implements InputInterface<SearchCommand> */
|
||||||
* @implements InputInterface<SearchCommand>
|
|
||||||
*/
|
|
||||||
class SearchInput implements InputInterface
|
class SearchInput implements InputInterface
|
||||||
{
|
{
|
||||||
public function __construct(
|
public function __construct(
|
||||||
|
|||||||
@@ -5,9 +5,9 @@ namespace App\Search\Action\Result;
|
|||||||
use App\Tmdb\TmdbResult;
|
use App\Tmdb\TmdbResult;
|
||||||
use OneToMany\RichBundle\Contract\ResultInterface;
|
use OneToMany\RichBundle\Contract\ResultInterface;
|
||||||
|
|
||||||
|
/** @implements ResultInterface<GetMediaInfoResult> */
|
||||||
class GetMediaInfoResult implements ResultInterface
|
class GetMediaInfoResult implements ResultInterface
|
||||||
{
|
{
|
||||||
/** @implements ResultInterface<GetMediaInfoResult> */
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
public TmdbResult $media,
|
public TmdbResult $media,
|
||||||
) {}
|
) {}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ namespace App\Search\Action\Result;
|
|||||||
|
|
||||||
use OneToMany\RichBundle\Contract\ResultInterface;
|
use OneToMany\RichBundle\Contract\ResultInterface;
|
||||||
|
|
||||||
|
/** @implements ResultInterface<SearchResult> */
|
||||||
class SearchResult implements ResultInterface
|
class SearchResult implements ResultInterface
|
||||||
{
|
{
|
||||||
public function __construct(
|
public function __construct(
|
||||||
|
|||||||
@@ -94,12 +94,18 @@ class Tmdb
|
|||||||
$this->tvRepository = new TvRepository($this->client);
|
$this->tvRepository = new TvRepository($this->client);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function popularMovies(int $page = 1)
|
public function popularMovies(int $page = 1, ?int $limit = null)
|
||||||
{
|
{
|
||||||
$movies = $this->movieRepository->getPopular(['page' => $page]);
|
$movies = $this->movieRepository->getPopular(['page' => $page]);
|
||||||
|
|
||||||
foreach ($movies as $movie) {
|
$movies = $movies->map(function ($movie) use ($movies) {
|
||||||
$this->parseResult($movie);
|
return $this->parseResult($movies[$movie], "movie");
|
||||||
|
});
|
||||||
|
|
||||||
|
$movies = array_values($movies->toArray());
|
||||||
|
|
||||||
|
if (null !== $limit) {
|
||||||
|
$movies = array_slice($movies, 0, $limit);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $movies;
|
return $movies;
|
||||||
|
|||||||
24
src/Twig/Components/ActiveDownloadList.php
Normal file
24
src/Twig/Components/ActiveDownloadList.php
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Twig\Components;
|
||||||
|
|
||||||
|
use App\Download\Framework\Repository\DownloadRepository;
|
||||||
|
use Symfony\UX\LiveComponent\Attribute\AsLiveComponent;
|
||||||
|
use Symfony\UX\LiveComponent\Attribute\LiveAction;
|
||||||
|
use Symfony\UX\LiveComponent\DefaultActionTrait;
|
||||||
|
|
||||||
|
#[AsLiveComponent]
|
||||||
|
final class ActiveDownloadList
|
||||||
|
{
|
||||||
|
use DefaultActionTrait;
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
private DownloadRepository $downloadRepository,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
#[LiveAction]
|
||||||
|
public function getDownloads()
|
||||||
|
{
|
||||||
|
return $this->downloadRepository->getActivePaginated();
|
||||||
|
}
|
||||||
|
}
|
||||||
15
symfony.lock
15
symfony.lock
@@ -29,6 +29,9 @@
|
|||||||
"migrations/.gitignore"
|
"migrations/.gitignore"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"php-flasher/flasher-symfony": {
|
||||||
|
"version": "v2.1.6"
|
||||||
|
},
|
||||||
"php-http/discovery": {
|
"php-http/discovery": {
|
||||||
"version": "1.20",
|
"version": "1.20",
|
||||||
"recipe": {
|
"recipe": {
|
||||||
@@ -199,6 +202,18 @@
|
|||||||
"assets/icons/symfony.svg"
|
"assets/icons/symfony.svg"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"symfony/ux-live-component": {
|
||||||
|
"version": "2.24",
|
||||||
|
"recipe": {
|
||||||
|
"repo": "github.com/symfony/recipes",
|
||||||
|
"branch": "main",
|
||||||
|
"version": "2.6",
|
||||||
|
"ref": "73e69baf18f47740d6f58688c5464b10cdacae06"
|
||||||
|
},
|
||||||
|
"files": [
|
||||||
|
"config/routes/ux_live_component.yaml"
|
||||||
|
]
|
||||||
|
},
|
||||||
"symfony/ux-twig-component": {
|
"symfony/ux-twig-component": {
|
||||||
"version": "2.24",
|
"version": "2.24",
|
||||||
"recipe": {
|
"recipe": {
|
||||||
|
|||||||
39
templates/components/ActiveDownloadList.html.twig
Normal file
39
templates/components/ActiveDownloadList.html.twig
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
<div{{ attributes }} class="min-w-48" data-poll="getDownloads">
|
||||||
|
<table class="divide-y divide-gray-200 dark:divide-gray-50 dark:bg-gray-50 table-fixed">
|
||||||
|
<thead>
|
||||||
|
<tr class="dark:bg-gray-50">
|
||||||
|
<th scope="col"
|
||||||
|
class="px-6 py-3 text-start text-xs font-medium text-stone-500 uppercase dark:text-stone-800 min-w-[55ch] max-w-[55ch] truncate">
|
||||||
|
Title
|
||||||
|
</th>
|
||||||
|
<th scope="col"
|
||||||
|
class="px-6 py-3 text-start text-xs font-medium text-gray-500 uppercase dark:text-stone-800">
|
||||||
|
Progress
|
||||||
|
</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody class="divide-y divide-gray-200 dark:divide-gray-50">
|
||||||
|
{% if this.getDownloads()|length > 0 %}
|
||||||
|
{% for download in this.getDownloads() %}
|
||||||
|
<tr>
|
||||||
|
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-800 dark:text-stone-800 min-w-[45ch] max-w-[45ch] truncate">
|
||||||
|
{{ download.title }}
|
||||||
|
</td>
|
||||||
|
<td class="px-6 py-4 whitespace-nowrap text-sm text-end text-gray-800 dark:text-gray-50">
|
||||||
|
<span class="p-1.5 bg-purple-600 rounded-full">
|
||||||
|
<span class="w-4 inline-block text-center text-gray-50">{{ download.progress }}</span>
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
{% else %}
|
||||||
|
<tr>
|
||||||
|
<td class="px-6 py-4 whitespace-nowrap text-xs uppercase text-center col-span-2 font-medium text-gray-800 dark:text-stone-800" colspan="2">
|
||||||
|
No active downloads
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endif %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
@@ -1,4 +1,14 @@
|
|||||||
<div{{ attributes }}>
|
<div{{ attributes }}>
|
||||||
<img src="{{ image }}" class="w-40 rounded-md" />
|
<a href="{{ path('app_search_result', {
|
||||||
<h3 class="text-center text-gray-50 text-extrabold">{{ title }}</h3>
|
mediaType: "movies",
|
||||||
|
tmdbId: tmdbId
|
||||||
|
}) }}">
|
||||||
|
<img src="{{ image }}" class="w-40 rounded-md" />
|
||||||
|
</a>
|
||||||
|
<a href="{{ path('app_search_result', {
|
||||||
|
mediaType: "movies",
|
||||||
|
tmdbId: tmdbId
|
||||||
|
}) }}">
|
||||||
|
<h3 class="text-center text-gray-50 max-w-[16ch] text-extrabold">{{ title }}</h3>
|
||||||
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,64 +1,17 @@
|
|||||||
{% extends 'base.html.twig' %}
|
{% extends 'base.html.twig' %}
|
||||||
|
|
||||||
{% block title %}Dashboard &mdash - Torsearch{% endblock %}
|
{% block title %}Dashboard — Torsearch{% endblock %}
|
||||||
|
|
||||||
{% block body %}
|
{% block body %}
|
||||||
<div class="p-4 flex flex-col grow gap-4">
|
<div class="p-4 flex flex-col grow gap-4">
|
||||||
<h2 class="mb-2 text-3xl font-bold text-gray-50">Dashboard</h2>
|
<h2 class="mb-2 text-3xl font-bold text-gray-50">Dashboard</h2>
|
||||||
<div class="flex flex-row gap-4">
|
<div class="flex flex-row gap-4">
|
||||||
<twig:Card title="Active Downloads" class="w-full">
|
<twig:Card title="Active Downloads" class="w-full">
|
||||||
<table class="divide-y divide-gray-200 dark:divide-gray-50 dark:bg-gray-50">
|
<twig:ActiveDownloadList />
|
||||||
<thead>
|
|
||||||
<tr class="dark:bg-gray-50">
|
|
||||||
<th scope="col"
|
|
||||||
class="px-6 py-3 text-start text-xs font-medium text-stone-500 uppercase dark:text-stone-800">
|
|
||||||
Title
|
|
||||||
</th>
|
|
||||||
<th scope="col"
|
|
||||||
class="px-6 py-3 text-start text-xs font-medium text-gray-500 uppercase dark:text-stone-800">
|
|
||||||
Progress
|
|
||||||
</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody class="divide-y divide-gray-200 dark:divide-gray-50">
|
|
||||||
<tr>
|
|
||||||
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-800 dark:text-stone-800">
|
|
||||||
The Wolf of Wallstreet
|
|
||||||
</td>
|
|
||||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-end text-gray-800 dark:text-gray-50">
|
|
||||||
<span class="p-1.5 bg-purple-600 rounded-full">
|
|
||||||
<span class="text-gray-50">11</span>
|
|
||||||
</span>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
|
|
||||||
<tr>
|
|
||||||
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-800 dark:text-stone-800">
|
|
||||||
Inception
|
|
||||||
</td>
|
|
||||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-end text-gray-800 dark:text-gray-50">
|
|
||||||
<span class="p-1.5 bg-purple-600 rounded-full">
|
|
||||||
<span class="text-gray-50">36</span>
|
|
||||||
</span>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
|
|
||||||
<tr>
|
|
||||||
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-800 dark:text-stone-800">
|
|
||||||
Hop
|
|
||||||
</td>
|
|
||||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-end text-gray-800 dark:text-gray-50">
|
|
||||||
<span class="p-1.5 bg-purple-600 rounded-full">
|
|
||||||
<span class="text-gray-50">0</span>
|
|
||||||
</span>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</twig:Card>
|
</twig:Card>
|
||||||
|
|
||||||
<twig:Card title="Recent Downloads" class="w-full">
|
<twig:Card title="Recent Downloads" class="w-full">
|
||||||
<table class="divide-y divide-gray-200 dark:divide-gray-50 dark:bg-gray-50">
|
<table class="divide-y divide-gray-200 dark:divide-gray-50 dark:bg-gray-50 table-fixed">
|
||||||
<thead>
|
<thead>
|
||||||
<tr class="dark:bg-gray-50">
|
<tr class="dark:bg-gray-50">
|
||||||
<th scope="col"
|
<th scope="col"
|
||||||
@@ -66,56 +19,51 @@
|
|||||||
Title
|
Title
|
||||||
</th>
|
</th>
|
||||||
<th scope="col"
|
<th scope="col"
|
||||||
class="px-6 py-3 text-start text-xs font-medium text-end text-gray-500 uppercase dark:text-stone-800">
|
class="px-6 py-3 text-start text-xs font-medium text-gray-500 uppercase dark:text-stone-800">
|
||||||
Status
|
Status
|
||||||
</th>
|
</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody class="divide-y divide-gray-200 dark:divide-gray-50">
|
<tbody class="divide-y divide-gray-200 dark:divide-gray-50">
|
||||||
<tr>
|
{% if recent_downloads|length > 0 %}
|
||||||
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-800 dark:text-stone-800">
|
{% for download in recent_downloads %}
|
||||||
The Family Plan
|
<tr>
|
||||||
</td>
|
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-800 dark:text-stone-800">
|
||||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-end text-gray-800 dark:text-gray-50">
|
{{ download.title }}
|
||||||
<span class="p-1 bg-green-600 rounded-lg">
|
</td>
|
||||||
<span class="text-gray-50">Complete</span>
|
<td class="px-6 py-4 whitespace-nowrap text-sm text-end text-gray-800 dark:text-gray-50">
|
||||||
</span>
|
<span class="p-1 bg-green-600 rounded-lg">
|
||||||
</td>
|
<span class="text-gray-50">Complete</span>
|
||||||
</tr>
|
</span>
|
||||||
|
</td>
|
||||||
<tr>
|
</tr>
|
||||||
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-800 dark:text-stone-800">
|
{% endfor %}
|
||||||
It
|
<tr class="bg-blue-400">
|
||||||
</td>
|
<td class="px-6 py-3 whitespace-nowrap text-xs uppercase text-center col-span-2 font-medium text-rose-400 dark:text-stone-800" colspan="2">
|
||||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-end text-gray-800 dark:text-gray-50">
|
<a href="#">View all downloads</a>
|
||||||
<span class="p-1 bg-green-600 rounded-lg">
|
</td>
|
||||||
<span class="text-gray-50">Complete</span>
|
</tr>
|
||||||
</span>
|
{% else %}
|
||||||
</td>
|
<tr>
|
||||||
</tr>
|
<td class="px-6 py-4 whitespace-nowrap text-xs uppercase text-center col-span-2 font-medium text-gray-800 dark:text-stone-800" colspan="2">
|
||||||
|
No recent downloads
|
||||||
<tr>
|
</td>
|
||||||
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-800 dark:text-stone-800">
|
</tr>
|
||||||
Silicon Cowboys
|
{% endif %}
|
||||||
</td>
|
|
||||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-end text-gray-800 dark:text-gray-50">
|
|
||||||
<span class="p-1 bg-green-600 rounded-lg">
|
|
||||||
<span class="text-gray-50">Complete</span>
|
|
||||||
</span>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</twig:Card>
|
</twig:Card>
|
||||||
</div>
|
</div>
|
||||||
<div class="">
|
<div class="">
|
||||||
<twig:Card title="Popular Movies" contentClass="flex flex-row justify-between w-full">
|
<twig:Card title="Popular Movies" contentClass="flex flex-row justify-between w-full">
|
||||||
<twig:Poster imdbId="" title="A Working Man" description="" image="https://image.tmdb.org/t/p/w500/xUkUZ8eOnrOnnJAfusZUqKYZiDu.jpg" year="" />
|
{% for movie in popular_movies %}
|
||||||
<twig:Poster imdbId="" title="In the Lost Lands" description="" image="https://image.tmdb.org/t/p/w500/iHf6bXPghWB6gT8kFkL1zo00x6X.jpg" year="" />
|
<twig:Poster imdbId=""
|
||||||
<twig:Poster imdbId="" title="A Minecraft Movie" description="" image="https://image.tmdb.org/t/p/w500/yFHHfHcUgGAxziP1C3lLt0q2T4s.jpg" year="" />
|
tmdbId="{{ movie.tmdbId }}"
|
||||||
<twig:Poster imdbId="" title="G20" description="" image="https://image.tmdb.org/t/p/w500/wv6oWAleCJZUk5htrGg413t3GCy.jpg" year="" />
|
title="{{ movie.title }}"
|
||||||
<twig:Poster imdbId="" title="Novocaine" description="" image="https://image.tmdb.org/t/p/w500/xmMHGz9dVRaMY6rRAlEX4W0Wdhm.jpg" year="" />
|
description="{{ movie.description }}"
|
||||||
<twig:Poster imdbId="" title="Gunslingers" description="" image="https://image.tmdb.org/t/p/w500/O7REXWPANWXvX2jhQydHjAq2DV.jpg" year="" />
|
image="{{ movie.poster }}"
|
||||||
|
year="{{ movie.year }}" />
|
||||||
|
{% endfor %}
|
||||||
</twig:Card>
|
</twig:Card>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,63 +1,84 @@
|
|||||||
<div id="filter" class="w-full p-4 flex flex-row gap-4 bg-stone-500 text-md text-gray-500 dark:text-gray-50 rounded-lg"
|
<div id="filter" class="flex flex-col gap-4"
|
||||||
{{ stimulus_controller('result_filter') }}
|
{{ stimulus_controller('result_filter') }}
|
||||||
{{ stimulus_action('result_filter', 'filter', 'change') }}
|
{{ stimulus_action('result_filter', 'filter', 'change') }}
|
||||||
data-result-filter-media-type-value="{{ results.media.mediaType }}"
|
data-result-filter-media-type-value="{{ results.media.mediaType }}"
|
||||||
data-result-filter-movie-results-outlet=".results"
|
data-result-filter-movie-results-outlet=".results"
|
||||||
data-result-filter-tv-results-outlet=".results"
|
data-result-filter-tv-results-outlet=".results"
|
||||||
>
|
>
|
||||||
<label for="resolution">
|
<div class="w-full p-4 flex flex-row gap-4 bg-stone-500 text-md text-gray-500 dark:text-gray-50 rounded-lg">
|
||||||
Resolution
|
<label for="resolution">
|
||||||
<select id="resolution" data-result-filter-target="resolution" class="px-1 py-0.5 bg-stone-100 text-gray-800 rounded-sm">
|
Resolution
|
||||||
<option {{ filter.resolution == "n/a" ? "selected" }}
|
<select id="resolution" data-result-filter-target="resolution" class="px-1 py-0.5 bg-stone-100 text-gray-800 rounded-md">
|
||||||
value="">n/a</option>
|
<option {{ filter.resolution == "n/a" ? "selected" }}
|
||||||
<option {{ filter.resolution == "720p" ? "selected" }}
|
value="">n/a</option>
|
||||||
value="720p">720p</option>
|
<option {{ filter.resolution == "720p" ? "selected" }}
|
||||||
<option {{ filter.resolution == "1080p" ? "selected" }}
|
value="720p">720p</option>
|
||||||
value="1080p">1080p</option>
|
<option {{ filter.resolution == "1080p" ? "selected" }}
|
||||||
<option {{ filter.resolution == "2160p" ? "selected" }}
|
value="1080p">1080p</option>
|
||||||
value="2160p">2160p</option>
|
<option {{ filter.resolution == "2160p" ? "selected" }}
|
||||||
</select>
|
value="2160p">2160p</option>
|
||||||
</label>
|
|
||||||
<label for="codec">
|
|
||||||
Codec
|
|
||||||
<select id="codec" data-result-filter-target="codec" class="px-1 py-0.5 bg-stone-100 text-sm text-gray-800 rounded-sm">
|
|
||||||
<option {{ filter.codec == "n/a" ? "selected" }}
|
|
||||||
value="">n/a</option>
|
|
||||||
<option {{ filter.codec == "-" ? "selected" }}
|
|
||||||
value="-">-</option>
|
|
||||||
<option {{ filter.codec == "h264" ? "selected" }}
|
|
||||||
value="h264">h264</option>
|
|
||||||
<option {{ filter.codec == "h265" ? "selected" }}
|
|
||||||
value="h265">h265/HEVC</option>
|
|
||||||
</select>
|
|
||||||
</label>
|
|
||||||
<label for="language">
|
|
||||||
Language
|
|
||||||
<select id="language" data-result-filter-target="language" class="px-1 py-0.5 bg-stone-100 text-gray-800 rounded-sm">
|
|
||||||
<option selected value="{{ filter.language }}">{{ filter.language }}</option>
|
|
||||||
</select>
|
|
||||||
</label>
|
|
||||||
<label for="provider">
|
|
||||||
Provider
|
|
||||||
<select id="provider" data-result-filter-target="provider" class="px-1 py-0.5 bg-stone-100 text-gray-800 rounded-sm">
|
|
||||||
<option selected value="">n/a</option>
|
|
||||||
</select>
|
|
||||||
</label>
|
|
||||||
{% if results.media.mediaType == "tvshows" %}
|
|
||||||
<label for="season">
|
|
||||||
Season
|
|
||||||
<select id="season" name="season" value="1" data-result-filter-target="season" class="px-1 py-0.5 bg-stone-100 text-gray-800 rounded-sm">
|
|
||||||
<option selected value="1">1</option>
|
|
||||||
{% for season in range(2, results.media.episodes|length) %}
|
|
||||||
<option value="{{ season }}">{{ season }}</option>
|
|
||||||
{% endfor %}
|
|
||||||
</select>
|
</select>
|
||||||
</label>
|
</label>
|
||||||
{# <label for="episodeNumber">#}
|
<label for="codec">
|
||||||
{# Episode#}
|
Codec
|
||||||
{# <select id="episodeNumber" name="episodeNumber" data-result-filter-target="episode" class="px-1 py-0.5 bg-stone-100 text-gray-800 rounded-sm">#}
|
<select id="codec" data-result-filter-target="codec" class="px-1 py-0.5 bg-stone-100 text-sm text-gray-800 rounded-md">
|
||||||
{# <option selected value="">n/a</option>#}
|
<option {{ filter.codec == "n/a" ? "selected" }}
|
||||||
{# </select>#}
|
value="">n/a</option>
|
||||||
{# </label>#}
|
<option {{ filter.codec == "-" ? "selected" }}
|
||||||
|
value="-">-</option>
|
||||||
|
<option {{ filter.codec == "h264" ? "selected" }}
|
||||||
|
value="h264">h264</option>
|
||||||
|
<option {{ filter.codec == "h265" ? "selected" }}
|
||||||
|
value="h265">h265/HEVC</option>
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
|
<label for="language">
|
||||||
|
Language
|
||||||
|
<select id="language" data-result-filter-target="language" class="px-1 py-0.5 bg-stone-100 text-gray-800 rounded-md">
|
||||||
|
<option selected value="{{ filter.language }}">{{ filter.language }}</option>
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
|
<label for="provider">
|
||||||
|
Provider
|
||||||
|
<select id="provider" data-result-filter-target="provider" class="px-1 py-0.5 bg-stone-100 text-gray-800 rounded-md">
|
||||||
|
<option selected value="">n/a</option>
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
|
{% if results.media.mediaType == "tvshows" %}
|
||||||
|
<label for="season">
|
||||||
|
Season
|
||||||
|
<select id="season" name="season" value="1" data-result-filter-target="season" class="px-1 py-0.5 bg-stone-100 text-gray-800 rounded-md"
|
||||||
|
{{ stimulus_action('result_filter', 'uncheckSelectAllBtn', 'change') }}>
|
||||||
|
<option selected value="1">1</option>
|
||||||
|
{% for season in range(2, results.media.episodes|length) %}
|
||||||
|
<option value="{{ season }}">{{ season }}</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
|
{# <label for="episodeNumber">#}
|
||||||
|
{# Episode#}
|
||||||
|
{# <select id="episodeNumber" name="episodeNumber" data-result-filter-target="episode" class="px-1 py-0.5 bg-stone-100 text-gray-800 rounded-sm">#}
|
||||||
|
{# <option selected value="">n/a</option>#}
|
||||||
|
{# </select>#}
|
||||||
|
{# </label>#}
|
||||||
|
{% endif %}
|
||||||
|
<span {{ stimulus_controller('loading_icon', {total: (results.media.mediaType == "tvshows") ? results.media.episodes[1]|length : 1, count: 0}) }}
|
||||||
|
class="loading-icon"
|
||||||
|
>
|
||||||
|
<twig:ux:icon name="codex:loader" height="20" width="20" data-loading-icon-target="icon" class="text-end" />
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% if results.media.mediaType == "tvshows" %}
|
||||||
|
<div class="flex flex-row gap-2 justify-end px-8">
|
||||||
|
<button class="px-1.5 py-1 bg-green-600 rounded-md text-sm"
|
||||||
|
{{ stimulus_target('result_filter', 'downloadSelected') }}
|
||||||
|
{{ stimulus_action('result_filter', 'downloadSelectedEpisodes', 'click') }}
|
||||||
|
>Download Selected</button>
|
||||||
|
<input type="checkbox" name="selectAll" id="selectAll"
|
||||||
|
{{ stimulus_target('result_filter', 'selectAll') }}
|
||||||
|
{{ stimulus_action('result_filter', 'selectAllEpisodes', 'change') }}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
@@ -17,11 +17,6 @@
|
|||||||
{{ results.media.description }}
|
{{ results.media.description }}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<span {{ stimulus_controller('loading_icon', {total: (results.media.mediaType == "tvshows") ? results.media.episodes[1]|length : 1, count: 0}) }}
|
|
||||||
class="loading-icon"
|
|
||||||
>
|
|
||||||
<twig:ux:icon name="codex:loader" height="20" width="20" data-loading-icon-target="icon" />
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{{ include('search/partial/filter.html.twig') }}
|
{{ include('search/partial/filter.html.twig') }}
|
||||||
|
|||||||
@@ -10,11 +10,16 @@
|
|||||||
><span {{ stimulus_target('tv-results', 'count') }}>{{ results.results|length }}</span> results</small>
|
><span {{ stimulus_target('tv-results', 'count') }}>{{ results.results|length }}</span> results</small>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-col items-end hover:cursor-pointer"
|
<div class="flex flex-col gap-4 justify-between">
|
||||||
{{ stimulus_action('tv-results', 'toggleList', 'click') }}>
|
<input type="checkbox"
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="2em" height="2em" viewBox="0 0 32 32">
|
{{ stimulus_target('tv-results', 'episodeSelector') }}
|
||||||
<path fill="currentColor" d="m16 10l10 10l-1.4 1.4l-8.6-8.6l-8.6 8.6L6 20z"/>
|
/>
|
||||||
</svg>
|
<div class="flex flex-col items-end hover:cursor-pointer"
|
||||||
|
{{ 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>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="inline-block overflow-hidden rounded-lg">
|
<div class="inline-block overflow-hidden rounded-lg">
|
||||||
|
|||||||
Reference in New Issue
Block a user