wip-feat: populates filter from api options
This commit is contained in:
@@ -27,10 +27,23 @@ export default class extends Controller {
|
|||||||
'episodes': Array,
|
'episodes': Array,
|
||||||
}
|
}
|
||||||
|
|
||||||
connect() {
|
async connect() {
|
||||||
if (this.mediaTypeValue === "tvshows") {
|
await fetch('/api/preferences/media')
|
||||||
this.activeFilter['season'] = 1;
|
.then(response => response.json())
|
||||||
}
|
.then(response => {
|
||||||
|
this.addResolutions(response['resolution']);
|
||||||
|
this.addCodecs(response['codec']);
|
||||||
|
});
|
||||||
|
|
||||||
|
await fetch('/api/user/preferences/values')
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(response => {
|
||||||
|
this.activeFilter = response;
|
||||||
|
if (this.mediaTypeValue === "tvshows") {
|
||||||
|
this.activeFilter['season'] = 1;
|
||||||
|
}
|
||||||
|
console.log(this.activeFilter);
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async movieResultsOutletConnected(outlet) {
|
async movieResultsOutletConnected(outlet) {
|
||||||
@@ -51,6 +64,20 @@ export default class extends Controller {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
addResolutions(resolutions) {
|
||||||
|
this.resolutionTarget.innerHTML = '<option value="">n/a</option>';
|
||||||
|
this.resolutionTarget.innerHTML += resolutions.preferenceOptions
|
||||||
|
.map((resolution) => '<option value="'+resolution.name.toLowerCase()+'">'+resolution.name+'</option>')
|
||||||
|
.join();
|
||||||
|
}
|
||||||
|
|
||||||
|
addCodecs(codecs) {
|
||||||
|
this.codecTarget.innerHTML = '<option value="">n/a</option>';
|
||||||
|
this.codecTarget.innerHTML += codecs.preferenceOptions
|
||||||
|
.map((codec) => '<option value="'+codec.name.toLowerCase()+'">'+codec.name+'</option>')
|
||||||
|
.join();
|
||||||
|
}
|
||||||
|
|
||||||
addLanguages(option, props) {
|
addLanguages(option, props) {
|
||||||
const languages = Object.assign([], JSON.parse(props['languages']));
|
const languages = Object.assign([], JSON.parse(props['languages']));
|
||||||
languages.forEach((language) => {
|
languages.forEach((language) => {
|
||||||
|
|||||||
12
src/Twig/Components/Filter.php
Normal file
12
src/Twig/Components/Filter.php
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Twig\Components;
|
||||||
|
|
||||||
|
use Symfony\UX\LiveComponent\Attribute\AsLiveComponent;
|
||||||
|
use Symfony\UX\LiveComponent\DefaultActionTrait;
|
||||||
|
|
||||||
|
#[AsLiveComponent]
|
||||||
|
final class Filter
|
||||||
|
{
|
||||||
|
use DefaultActionTrait;
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\User\Framework\Controller\Api;
|
||||||
|
|
||||||
|
use Aimeos\Map;
|
||||||
|
use App\User\Framework\Repository\PreferencesRepository;
|
||||||
|
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||||
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
|
use Symfony\Component\Routing\Attribute\Route;
|
||||||
|
|
||||||
|
class PreferencesApiController extends AbstractController
|
||||||
|
{
|
||||||
|
#[Route('/api/preferences/media', name: 'api_preferences_media')]
|
||||||
|
public function getMediaPreferences(
|
||||||
|
PreferencesRepository $preferencesRepository,
|
||||||
|
): Response {
|
||||||
|
return $this->json(
|
||||||
|
Map::from($preferencesRepository->findEnabled())
|
||||||
|
->rekey(fn($element) => $element->getId())
|
||||||
|
->toArray()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
20
src/User/Framework/Controller/Api/UserApiController.php
Normal file
20
src/User/Framework/Controller/Api/UserApiController.php
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\User\Framework\Controller\Api;
|
||||||
|
|
||||||
|
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||||
|
use Symfony\Bundle\SecurityBundle\Security;
|
||||||
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
|
use Symfony\Component\Routing\Attribute\Route;
|
||||||
|
|
||||||
|
class UserApiController extends AbstractController
|
||||||
|
{
|
||||||
|
#[Route('/api/user/preferences/values', name: 'api_user_preferences_values')]
|
||||||
|
public function getUserPreferenceValues(
|
||||||
|
Security $security
|
||||||
|
): Response {
|
||||||
|
return $this->json(
|
||||||
|
$security->getUser()->getUserPreferenceValues()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,6 +4,7 @@ namespace App\User\Framework\Entity;
|
|||||||
|
|
||||||
use App\User\Framework\Repository\PreferenceOptionRepository;
|
use App\User\Framework\Repository\PreferenceOptionRepository;
|
||||||
use Doctrine\ORM\Mapping as ORM;
|
use Doctrine\ORM\Mapping as ORM;
|
||||||
|
use Symfony\Component\Serializer\Attribute\Ignore;
|
||||||
|
|
||||||
#[ORM\Entity(repositoryClass: PreferenceOptionRepository::class)]
|
#[ORM\Entity(repositoryClass: PreferenceOptionRepository::class)]
|
||||||
class PreferenceOption
|
class PreferenceOption
|
||||||
@@ -19,6 +20,7 @@ class PreferenceOption
|
|||||||
#[ORM\Column(length: 255, nullable: true)]
|
#[ORM\Column(length: 255, nullable: true)]
|
||||||
private ?string $value = null;
|
private ?string $value = null;
|
||||||
|
|
||||||
|
#[Ignore]
|
||||||
#[ORM\ManyToOne(inversedBy: 'preferenceOptions')]
|
#[ORM\ManyToOne(inversedBy: 'preferenceOptions')]
|
||||||
private ?Preference $preference = null;
|
private ?Preference $preference = null;
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
namespace App\User\Framework\Entity;
|
namespace App\User\Framework\Entity;
|
||||||
|
|
||||||
|
use Aimeos\Map;
|
||||||
use App\User\Framework\Repository\PreferencesRepository;
|
use App\User\Framework\Repository\PreferencesRepository;
|
||||||
use App\User\Framework\Repository\UserRepository;
|
use App\User\Framework\Repository\UserRepository;
|
||||||
use Doctrine\Common\Collections\ArrayCollection;
|
use Doctrine\Common\Collections\ArrayCollection;
|
||||||
@@ -184,4 +185,12 @@ class User implements UserInterface, PasswordAuthenticatedUserInterface
|
|||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getUserPreferenceValues()
|
||||||
|
{
|
||||||
|
return Map::from($this->userPreferences)
|
||||||
|
->rekey(fn(UserPreference $userPreference) => $userPreference->getPreference()->getId())
|
||||||
|
->map(fn(UserPreference $userPreference) => $userPreference->getPreferenceValue())
|
||||||
|
->toArray();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
88
templates/components/Filter.html.twig
Normal file
88
templates/components/Filter.html.twig
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
<div id="filter" class="flex flex-col gap-4"
|
||||||
|
{{ stimulus_controller('result_filter') }}
|
||||||
|
{{ stimulus_action('result_filter', 'filter', 'change') }}
|
||||||
|
data-result-filter-media-type-value="{{ results.media.mediaType }}"
|
||||||
|
data-result-filter-movie-results-outlet=".results"
|
||||||
|
data-result-filter-tv-results-outlet=".results"
|
||||||
|
>
|
||||||
|
<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">
|
||||||
|
<label for="resolution">
|
||||||
|
Resolution
|
||||||
|
<select id="resolution"
|
||||||
|
data-result-filter-target="resolution"
|
||||||
|
class="px-1 py-0.5 bg-stone-100 text-gray-800 rounded-md"
|
||||||
|
value="{{ app.user.userPreferenceValues["resolution"] }}"
|
||||||
|
>
|
||||||
|
{# <option {{ filter.resolution == "n/a" ? "selected" }}#}
|
||||||
|
{# value="">n/a</option>#}
|
||||||
|
{# <option {{ filter.resolution == "720p" ? "selected" }}#}
|
||||||
|
{# value="720p">720p</option>#}
|
||||||
|
{# <option {{ filter.resolution == "1080p" ? "selected" }}#}
|
||||||
|
{# value="1080p">1080p</option>#}
|
||||||
|
{# <option {{ filter.resolution == "2160p" ? "selected" }}#}
|
||||||
|
{# value="2160p">2160p</option>#}
|
||||||
|
</select>
|
||||||
|
</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-md">
|
||||||
|
{# <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-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 %}
|
||||||
|
</div>
|
||||||
@@ -8,7 +8,11 @@
|
|||||||
<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">
|
<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">
|
||||||
<label for="resolution">
|
<label for="resolution">
|
||||||
Resolution
|
Resolution
|
||||||
<select id="resolution" data-result-filter-target="resolution" class="px-1 py-0.5 bg-stone-100 text-gray-800 rounded-md">
|
<select id="resolution"
|
||||||
|
data-result-filter-target="resolution"
|
||||||
|
class="px-1 py-0.5 bg-stone-100 text-gray-800 rounded-md"
|
||||||
|
value="{{ app.user.userPreferenceValues["resolution"] }}"
|
||||||
|
>
|
||||||
<option {{ filter.resolution == "n/a" ? "selected" }}
|
<option {{ filter.resolution == "n/a" ? "selected" }}
|
||||||
value="">n/a</option>
|
value="">n/a</option>
|
||||||
<option {{ filter.resolution == "720p" ? "selected" }}
|
<option {{ filter.resolution == "720p" ? "selected" }}
|
||||||
|
|||||||
@@ -19,7 +19,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{{ include('search/partial/filter.html.twig') }}
|
<twig:Filter results="{{ results }}" filter="{{ filter }}" />
|
||||||
|
|
||||||
{% if "movies" == results.media.mediaType %}
|
{% if "movies" == results.media.mediaType %}
|
||||||
<div class="results" {{ stimulus_controller('movie_results', {title: results.media.title, tmdbId: results.media.tmdbId, imdbId: results.media.imdbId}) }}>
|
<div class="results" {{ stimulus_controller('movie_results', {title: results.media.title, tmdbId: results.media.tmdbId, imdbId: results.media.imdbId}) }}>
|
||||||
|
|||||||
Reference in New Issue
Block a user