Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| beed7d6940 | |||
| 924472ed56 | |||
| 7dd61355b7 |
@@ -41,6 +41,8 @@ export default class extends Controller {
|
|||||||
if (this.countValue === this.totalValue) {
|
if (this.countValue === this.totalValue) {
|
||||||
this.toggleIcon();
|
this.toggleIcon();
|
||||||
this.countValue = 0;
|
this.countValue = 0;
|
||||||
|
console.log('filtering')
|
||||||
|
document.getElementById('filter').filterResults();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,14 +12,14 @@ export default class extends Controller {
|
|||||||
seasons = []
|
seasons = []
|
||||||
|
|
||||||
activeFilter = {
|
activeFilter = {
|
||||||
"resolution": "",
|
"resolution": [],
|
||||||
"codec": "",
|
"codec": [],
|
||||||
"language": "",
|
"language": [],
|
||||||
"provider": "",
|
"provider": [],
|
||||||
"quality": "",
|
"quality": [],
|
||||||
}
|
}
|
||||||
|
|
||||||
defaultOptions = '<option value="n/a">n/a</option><option value="-">-</option>';
|
defaultOptions = '<option value="-">-</option>';
|
||||||
|
|
||||||
static outlets = ['tv-episode-list']
|
static outlets = ['tv-episode-list']
|
||||||
static targets = ['resolution', 'codec', 'language', 'provider', 'season', 'quality', 'loadingIcon', 'selectAll', 'downloadSelected']
|
static targets = ['resolution', 'codec', 'language', 'provider', 'season', 'quality', 'loadingIcon', 'selectAll', 'downloadSelected']
|
||||||
@@ -31,13 +31,21 @@ export default class extends Controller {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async connect() {
|
async connect() {
|
||||||
|
await this.setInitialFilter();
|
||||||
|
this.setTimerToStopLoadingIcon();
|
||||||
|
this.element.filterResults = this.filter.bind(this);
|
||||||
|
document.addEventListener('optionsLoaded', this.loadOptions.bind(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
async setInitialFilter() {
|
||||||
|
const response = await fetch('/api/user/filters');
|
||||||
|
const filters = await response.json();
|
||||||
|
if (filters.length > 0) {
|
||||||
|
this.activeFilter = filters[0];
|
||||||
|
}
|
||||||
if (this.mediaTypeValue === "tvshows") {
|
if (this.mediaTypeValue === "tvshows") {
|
||||||
this.activeFilter['season'] = 1;
|
this.activeFilter['season'] = 1;
|
||||||
}
|
}
|
||||||
this.filter();
|
|
||||||
this.setTimerToStopLoadingIcon();
|
|
||||||
|
|
||||||
document.addEventListener('optionsLoaded', this.loadOptions.bind(this));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
setTimerToStopLoadingIcon() {
|
setTimerToStopLoadingIcon() {
|
||||||
@@ -45,13 +53,10 @@ export default class extends Controller {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Event is fired from movies/tvshows controllers to populate this data
|
// Event is fired from movies/tvshows controllers to populate this data
|
||||||
loadOptions({detail: { options }}) {
|
async loadOptions({detail: { options }}) {
|
||||||
options.forEach((option) => {
|
await options.forEach((option) => {
|
||||||
this.addLanguages(option);
|
option.filter({detail: {activeFilter: this.activeFilter }});
|
||||||
this.addProviders(option);
|
|
||||||
this.addQualities(option);
|
|
||||||
})
|
})
|
||||||
this.filter();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
selectAllEpisodes() {
|
selectAllEpisodes() {
|
||||||
@@ -66,41 +71,6 @@ export default class extends Controller {
|
|||||||
document.dispatchEvent(new CustomEvent('downloadSelectedEpisodes', {}));
|
document.dispatchEvent(new CustomEvent('downloadSelectedEpisodes', {}));
|
||||||
}
|
}
|
||||||
|
|
||||||
addLanguages(option) {
|
|
||||||
option.languages.forEach((language) => {
|
|
||||||
if (!this.languages.includes(language)) {
|
|
||||||
this.languages.push(language);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
const preferred = JSON.parse(this.languageTarget.dataset.preferred) ?? [];
|
|
||||||
this.languageTarget.innerHTML = this.#serializeSelectOptions(this.languages);
|
|
||||||
this.languageTarget.tomselect.items = preferred;
|
|
||||||
}
|
|
||||||
|
|
||||||
addProviders(option) {
|
|
||||||
if (!this.providers.includes(option.provider)) {
|
|
||||||
this.providers.push(option.provider);
|
|
||||||
}
|
|
||||||
const preferred = JSON.parse(this.providerTarget.dataset.preferred) ?? [];
|
|
||||||
this.providerTarget.innerHTML = this.#serializeSelectOptions(this.providers);
|
|
||||||
this.providerTarget.tomselect.items = preferred;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
addQualities(option) {
|
|
||||||
if (option.quality.toLowerCase() in this.reverseMappedQualitiesValue) {
|
|
||||||
let quality = this.reverseMappedQualitiesValue[option.quality.toLowerCase()];
|
|
||||||
// converts api returned quality with a value the system recognizes
|
|
||||||
option.quality = quality;
|
|
||||||
if (!this.qualities.includes(option.quality)) {
|
|
||||||
this.qualities.push(quality);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const preferred = JSON.parse(this.qualityTarget.dataset.preferred) ?? [];
|
|
||||||
this.qualityTarget.innerHTML = this.#serializeSelectOptions(this.qualities);
|
|
||||||
this.qualityTarget.tomselect.items = preferred;
|
|
||||||
}
|
|
||||||
|
|
||||||
filter() {
|
filter() {
|
||||||
const downloadSeasonSpan = document.querySelector("#downloadSeasonModal");
|
const downloadSeasonSpan = document.querySelector("#downloadSeasonModal");
|
||||||
|
|
||||||
|
|||||||
@@ -30,11 +30,11 @@ export default class extends Controller {
|
|||||||
option.querySelector('.download-btn').dataset['title'] = this.titleValue
|
option.querySelector('.download-btn').dataset['title'] = this.titleValue
|
||||||
);
|
);
|
||||||
this.element.options[0].querySelector('input[type="checkbox"]').checked = true;
|
this.element.options[0].querySelector('input[type="checkbox"]').checked = true;
|
||||||
this.loadingIconOutlet.increaseCount();
|
|
||||||
document.dispatchEvent(new CustomEvent('optionsLoaded', {detail: {options: this.element.options}}));
|
document.dispatchEvent(new CustomEvent('optionsLoaded', {detail: {options: this.element.options}}));
|
||||||
} else {
|
} else {
|
||||||
this.countTarget.innerText = 0;
|
this.countTarget.innerText = 0;
|
||||||
this.episodeSelectorTarget.disabled = true;
|
this.episodeSelectorTarget.disabled = true;
|
||||||
}
|
}
|
||||||
|
this.loadingIconOutlet.increaseCount();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -165,9 +165,9 @@ dialog[data-dialog-target="dialog"][closing] {
|
|||||||
padding: 0;
|
padding: 0;
|
||||||
|
|
||||||
.ts-control {
|
.ts-control {
|
||||||
background: transparent !important;
|
|
||||||
border: none !important;
|
border: none !important;
|
||||||
box-shadow: none !important;
|
box-shadow: none !important;
|
||||||
|
@apply bg-orange-500/60 backdrop-filter backdrop-blur-md;
|
||||||
}
|
}
|
||||||
|
|
||||||
.item[data-ts-item] {
|
.item[data-ts-item] {
|
||||||
@@ -175,10 +175,10 @@ dialog[data-dialog-target="dialog"][closing] {
|
|||||||
border: none;
|
border: none;
|
||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
text-shadow: none;
|
text-shadow: none;
|
||||||
@apply bg-orange-500 rounded-ms font-bold;
|
@apply bg-orange-500 rounded-ms font-bold text-black;
|
||||||
}
|
}
|
||||||
|
|
||||||
@apply border-b-2 border-b-orange-600 bg-transparent;
|
@apply border border-orange-500 bg-transparent rounded-ms;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ts-wrapper.plugin-remove_button:not(.rtl) .item .remove {
|
.ts-wrapper.plugin-remove_button:not(.rtl) .item .remove {
|
||||||
|
|||||||
@@ -3,92 +3,80 @@
|
|||||||
namespace App\Download;
|
namespace App\Download;
|
||||||
|
|
||||||
use Aimeos\Map;
|
use Aimeos\Map;
|
||||||
use App\Monitor\Framework\Entity\Monitor;
|
|
||||||
use App\Torrentio\Result\TorrentioResult;
|
use App\Torrentio\Result\TorrentioResult;
|
||||||
use App\User\Dto\UserPreferences;
|
use App\User\Dto\UserPreferences;
|
||||||
|
|
||||||
class DownloadOptionEvaluator
|
class DownloadOptionEvaluator
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* @param Monitor $monitor
|
|
||||||
* @param TorrentioResult[] $results
|
* @param TorrentioResult[] $results
|
||||||
|
* @param UserPreferences $filter
|
||||||
* @return TorrentioResult|null
|
* @return TorrentioResult|null
|
||||||
* @throws \Throwable
|
* @throws \Throwable
|
||||||
*/
|
*/
|
||||||
public function evaluateOptions(array $results, UserPreferences $userPreferences): ?TorrentioResult
|
public function evaluateOptions(array $results, UserPreferences $filter): ?TorrentioResult
|
||||||
{
|
{
|
||||||
$sizeLow = 000;
|
$matches = Map::from($results)->filter(function ($result) use ($filter) {
|
||||||
$sizeHigh = 4096;
|
if (false === $this->validateFilterItems($result, $filter)) {
|
||||||
|
return false;
|
||||||
$bestMatches = [];
|
|
||||||
$matches = [];
|
|
||||||
|
|
||||||
foreach ($results as $result) {
|
|
||||||
if (!in_array($userPreferences->language, $result->languages)) {
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($result->resolution === $userPreferences->resolution
|
if (false === $this->validateSize($result, $filter)) {
|
||||||
&& $result->codec === $userPreferences->codec
|
return false;
|
||||||
) {
|
|
||||||
$bestMatches[] = $result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($userPreferences->resolution === '2160p'
|
return true;
|
||||||
&& $userPreferences->codec === $result->codec
|
});
|
||||||
&& $result->resolution === '1080p'
|
|
||||||
) {
|
|
||||||
$matches[] = $result;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($userPreferences->codec === 'h264'
|
if ($matches->count() > 0) {
|
||||||
&& $userPreferences->resolution === $result->resolution
|
return Map::from($matches)->usort(fn($a, $b) => $a->seeders <=> $b->seeders)->last();
|
||||||
&& $result->codec === 'h265'
|
|
||||||
) {
|
|
||||||
$matches[] = $result;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (($userPreferences->codec === null )
|
|
||||||
&& ($userPreferences->resolution === null )) {
|
|
||||||
$matches[] = $result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$sizeMatches = [];
|
|
||||||
|
|
||||||
foreach ($bestMatches as $result) {
|
|
||||||
if (str_contains($result->size, 'GB')) {
|
|
||||||
$size = (int) trim(str_replace('GB', '', $result->size)) * 1024;
|
|
||||||
} else {
|
|
||||||
$size = (int) trim(str_replace('MB', '', $result->size));
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($size > $sizeLow && $size < $sizeHigh) {
|
|
||||||
$sizeMatches[] = $result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!empty($sizeMatches)) {
|
|
||||||
return Map::from($sizeMatches)->usort(fn($a, $b) => $a->seeders <=> $b->seeders)->last();
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ($matches as $result) {
|
|
||||||
$size = 0;
|
|
||||||
if (str_contains($result->size, 'GB')) {
|
|
||||||
$size = (int) trim(str_replace('GB', '', $result->size)) * 1024;
|
|
||||||
} else {
|
|
||||||
$size = (int) trim(str_replace('MB', '', $result->size));
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($size > $sizeLow && $size < $sizeHigh) {
|
|
||||||
$sizeMatches[] = $result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!empty($sizeMatches)) {
|
|
||||||
return Map::from($sizeMatches)->usort(fn($a, $b) => $a->seeders <=> $b->seeders)->last();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function validateFilterItems(TorrentioResult $result, UserPreferences $filter): bool
|
||||||
|
{
|
||||||
|
if (array_intersect($filter->language, $result->languages) === []) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$valid = true;
|
||||||
|
|
||||||
|
if (null !== $filter->resolution && !in_array($result->resolution, $filter->resolution)) {
|
||||||
|
$valid = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (null !== $filter->codec && in_array($result->codec, $filter->codec)) {
|
||||||
|
$valid = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (null !== $filter->quality && in_array($result->quality, $filter->quality)) {
|
||||||
|
$valid = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (null !== $filter->provider && in_array($result->provider, $filter->provider)) {
|
||||||
|
$valid = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $valid;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function validateSize(TorrentioResult $result, UserPreferences $filter): bool
|
||||||
|
{
|
||||||
|
$sizeLow = 000;
|
||||||
|
$sizeHigh = 4096;
|
||||||
|
|
||||||
|
if (str_contains($result->size, 'GB')) {
|
||||||
|
$size = (int) trim(str_replace('GB', '', $result->size)) * 1024;
|
||||||
|
} else {
|
||||||
|
$size = (int) trim(str_replace('MB', '', $result->size));
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($size > $sizeLow && $size < $sizeHigh) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -80,7 +80,7 @@ class UtilExtension
|
|||||||
|
|
||||||
// Capture season
|
// Capture season
|
||||||
$seasonMatch = [];
|
$seasonMatch = [];
|
||||||
preg_match('/[sS]\d\d/', $episodeId, $seasonMatch);
|
preg_match('/[sS]\d\d(\d)?(\d)?/', $episodeId, $seasonMatch);
|
||||||
if (empty($seasonMatch)) {
|
if (empty($seasonMatch)) {
|
||||||
$season = "";
|
$season = "";
|
||||||
} else {
|
} else {
|
||||||
@@ -89,7 +89,7 @@ class UtilExtension
|
|||||||
|
|
||||||
// Capture episode
|
// Capture episode
|
||||||
$episodeMatch = [];
|
$episodeMatch = [];
|
||||||
preg_match('/[eE]\d\d/', $episodeId, $episodeMatch);
|
preg_match('/[eE]\d\d(\d)?(\d)?/', $episodeId, $episodeMatch);
|
||||||
if (empty($episodeMatch)) {
|
if (empty($episodeMatch)) {
|
||||||
$episode = "";
|
$episode = "";
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\User\Framework\Controller\Api;
|
||||||
|
|
||||||
|
use App\User\Dto\UserPreferences;
|
||||||
|
use App\User\Dto\UserPreferencesFactory;
|
||||||
|
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||||
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
|
use Symfony\Component\Routing\Attribute\Route;
|
||||||
|
|
||||||
|
#[Route('/api/user/filters')]
|
||||||
|
class UserFilterApiController extends AbstractController
|
||||||
|
{
|
||||||
|
#[Route('', 'api.user.filters', methods: ['GET'])]
|
||||||
|
public function getFilters(): Response
|
||||||
|
{
|
||||||
|
return $this->json([
|
||||||
|
UserPreferencesFactory::createFromUser($this->getUser())
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -22,7 +22,7 @@
|
|||||||
<label for="season">
|
<label for="season">
|
||||||
Season
|
Season
|
||||||
</label>
|
</label>
|
||||||
<select id="season" name="season" value="1" data-result-filter-target="season" class="px-1 mb-4 py-1 md:py-2 text-center bg-orange-500 rounded-ms"
|
<select id="season" name="season" value="1" data-result-filter-target="season" class="px-1 mb-4 py-1 md:py-2 text-center bg-orange-500 rounded-ms text-black"
|
||||||
{{ stimulus_action('result_filter', 'setSeason', 'change') }}
|
{{ stimulus_action('result_filter', 'setSeason', 'change') }}
|
||||||
{{ stimulus_action('result_filter', 'uncheckSelectAllBtn', 'change') }}
|
{{ stimulus_action('result_filter', 'uncheckSelectAllBtn', 'change') }}
|
||||||
>
|
>
|
||||||
|
|||||||
Reference in New Issue
Block a user