Compare commits

...

6 Commits

21 changed files with 177 additions and 80 deletions

View File

@@ -0,0 +1,48 @@
import { Controller } from '@hotwired/stimulus';
/*
* The following line makes this controller "lazy": it won't be downloaded until needed
* See https://symfony.com/bundles/StimulusBundle/current/index.html#lazy-stimulus-controllers
*/
/* stimulusFetch: 'lazy' */
export default class extends Controller {
static outlets = ['navbar']
initialize() {
// Called once when the controller is first instantiated (per element)
// Here you can initialize variables, create scoped callables for event
// listeners, instantiate external libraries, etc.
// this._fooBar = this.fooBar.bind(this)
}
connect() {
// Called every time the controller is connected to the DOM
// (on page load, when it's added to the DOM, moved in the DOM, etc.)
// Here you can add event listeners on the element or target elements,
// add or remove classes, attributes, dispatch custom events, etc.
// this.fooTarget.addEventListener('click', this._fooBar)
}
navbarOutletConnected(outlet) {
console.log(outlet)
}
toggleMenu() {
console.log(this.navbarOutlet);
this.navbarOutlet.toggle();
}
// Add custom controller actions here
// fooBar() { this.fooTarget.classList.toggle(this.bazClass) }
disconnect() {
// Called anytime its element is disconnected from the DOM
// (on page change, when it's removed from or moved in the DOM, etc.)
// Here you should remove all event listeners added in "connect()"
// this.fooTarget.removeEventListener('click', this._fooBar)
}
}

View File

@@ -17,9 +17,15 @@ export default class extends Controller {
link.className = this.activeStyles; link.className = this.activeStyles;
} }
}); });
window.addEventListener("resize", (event) => {
});
} }
setActive() { toggle() {
this.element.parentElement.classList.toggle('hidden');
this.element.classList.toggle('fixed');
this.element.classList.toggle('z-20');
} }
} }

View File

@@ -90,13 +90,7 @@ export default class extends Controller {
} }
toggleList() { toggleList() {
// if (!this.isOpen) { this.listTarget.classList.toggle('options-table');
// this.toggleButtonTarget.classList.add('rotate-180');
// this.toggleButtonTarget.classList.remove('-rotate-180');
// } else {
// this.toggleButtonTarget.classList.add('-rotate-180');
// this.toggleButtonTarget.classList.remove('rotate-180');
// }
this.listTarget.classList.toggle('hidden'); this.listTarget.classList.toggle('hidden');
this.toggleButtonTarget.classList.toggle('rotate-90'); this.toggleButtonTarget.classList.toggle('rotate-90');
this.toggleButtonTarget.classList.toggle('-rotate-90'); this.toggleButtonTarget.classList.toggle('-rotate-90');

View File

@@ -0,0 +1 @@
<svg xmlns="https://www.w3.org/2000/svg" viewBox="0 0 0 0"><path fill="currentColor" fill-rule="evenodd" d="M0 3.75A.75.75 0 0 1 .75 3h14.5a.75.75 0 0 1 0 1.5H.75A.75.75 0 0 1 0 3.75M0 8a.75.75 0 0 1 .75-.75h14.5a.75.75 0 0 1 0 1.5H.75A.75.75 0 0 1 0 8m.75 3.5a.75.75 0 0 0 0 1.5h14.5a.75.75 0 0 0 0-1.5z" clip-rule="evenodd"/></svg>

After

Width:  |  Height:  |  Size: 333 B

View File

@@ -63,3 +63,17 @@ dialog[data-dialog-target="dialog"][open] {
dialog[data-dialog-target="dialog"][closing] { dialog[data-dialog-target="dialog"][closing] {
animation: fade-out 200ms forwards; animation: fade-out 200ms forwards;
} }
.options-table {
display: flex;
:last-child {
border-bottom: none;
}
}
@media screen and (min-width: 768px) {
.options-table {
display: inline-table;
}
}

View File

@@ -27,7 +27,7 @@ DATABASE_URL="mysql://root:password@database:3306/app?serverVersion=10.6.19.2-Ma
# Popular Movies and TV Shows section. # Popular Movies and TV Shows section.
#TMDB_API= #TMDB_API=
REAL_DEBRID_KEY="QYYBR7OSQ4VEFKWASDEZ2B4VO67KHUJY6IWOT7HHA7ATXO7QCYDQ" REAL_DEBRID_KEY=""
TMDB_API=eyJhbGciOiJIUzI1NiJ9.eyJhdWQiOiI0ZTJjYjJhOGUzOGJhNjdiNjVhOGU1NGM0ZWI1MzhmOCIsIm5iZiI6MTczNzkyNjA0NC41NjQsInN1YiI6IjY3OTZhNTljYzdiMDFiNzJjNzIzZWM5YiIsInNjb3BlcyI6WyJhcGlfcmVhZCJdLCJ2ZXJzaW9uIjoxfQ.e8DbNe9qrSBC1y-ANRv-VWBAtls-ZS2r7aNCiI68mpw TMDB_API=eyJhbGciOiJIUzI1NiJ9.eyJhdWQiOiI0ZTJjYjJhOGUzOGJhNjdiNjVhOGU1NGM0ZWI1MzhmOCIsIm5iZiI6MTczNzkyNjA0NC41NjQsInN1YiI6IjY3OTZhNTljYzdiMDFiNzJjNzIzZWM5YiIsInNjb3BlcyI6WyJhcGlfcmVhZCJdLCJ2ZXJzaW9uIjoxfQ.e8DbNe9qrSBC1y-ANRv-VWBAtls-ZS2r7aNCiI68mpw

View File

@@ -11,6 +11,13 @@ services:
- '8006:80' - '8006:80'
env_file: env_file:
- .env - .env
volumes:
- /mnt/media/downloads/movies:/var/download/movies
- /mnt/media/downloads/tvshows:/var/download/tvshows
environment:
TZ: America/Chicago
MERCURE_PUBLISHER_JWT_KEY: '!ChangeThisMercureHubJWTSecretKey!'
MERCURE_SUBSCRIBER_JWT_KEY: '!ChangeThisMercureHubJWTSecretKey!'
depends_on: depends_on:
database: database:
condition: service_healthy condition: service_healthy
@@ -27,6 +34,8 @@ services:
volumes: volumes:
- /mnt/media/downloads/movies:/var/download/movies - /mnt/media/downloads/movies:/var/download/movies
- /mnt/media/downloads/tvshows:/var/download/tvshows - /mnt/media/downloads/tvshows:/var/download/tvshows
environment:
TZ: America/Chicago
command: -vvv command: -vvv
env_file: env_file:
- .env - .env
@@ -43,35 +52,17 @@ services:
scheduler: scheduler:
image: code.caldwell.digital/home/torsearch-scheduler:latest image: code.caldwell.digital/home/torsearch-scheduler:latest
volumes: volumes:
- ./downloads:/var/download - /mnt/media/downloads/movies:/var/download/movies
- /mnt/media/downloads/tvshows:/var/download/tvshows
env_file: env_file:
- .env - .env
environment:
TZ: America/Chicago
restart: always restart: always
depends_on: depends_on:
app: app:
condition: service_healthy condition: service_healthy
# This container facilitates viewing the progress of downloads
# in realtime. It also handles sending alerts and notifications.
# The MERCURE_PUBLISHER_JWT key & MERCURE_SUBSCRIBER_JWT_KEY should
# match the MERCURE_JWT_SECRET environment variable.
mercure:
image: dunglas/mercure
restart: unless-stopped
ports:
- "3001:80"
environment:
SERVER_NAME: ':80'
MERCURE_PUBLISHER_JWT_KEY: '!ChangeThisMercureHubJWTSecretKey!'
MERCURE_SUBSCRIBER_JWT_KEY: '!ChangeThisMercureHubJWTSecretKey!'
MERCURE_EXTRA_DIRECTIVES: |
cors_origins *
anonymous
command: /usr/bin/caddy run --config /etc/caddy/dev.Caddyfile
volumes:
- mercure_data:/data
- mercure_config:/config
database: database:
image: mariadb:10.11.2 image: mariadb:10.11.2
volumes: volumes:

View File

@@ -40,4 +40,11 @@ return [
'stimulus-use' => [ 'stimulus-use' => [
'version' => '0.52.2', 'version' => '0.52.2',
], ],
'animate.css' => [
'version' => '4.1.1',
],
'animate.css/animate.min.css' => [
'version' => '4.1.1',
'type' => 'css',
],
]; ];

View File

@@ -10,8 +10,8 @@ use App\Torrentio\Exception\TorrentioRateLimitException;
use GuzzleHttp\Client; use GuzzleHttp\Client;
use Psr\Log\LoggerInterface; 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\ItemInterface; use Symfony\Contracts\Cache\ItemInterface;
use Symfony\Contracts\Cache\TagAwareCacheInterface;
class Torrentio class Torrentio
{ {
@@ -23,7 +23,7 @@ class Torrentio
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 TagAwareCacheInterface $cache,
private LoggerInterface $logger, private LoggerInterface $logger,
) { ) {
$this->searchUrl = str_replace('{realDebridKey}', $this->realDebridKey, $this->baseUrl); $this->searchUrl = str_replace('{realDebridKey}', $this->realDebridKey, $this->baseUrl);
@@ -36,8 +36,9 @@ class Torrentio
{ {
$cacheKey = "torrentio.{$imdbCode}"; $cacheKey = "torrentio.{$imdbCode}";
$results = $this->cache->get($cacheKey, function (ItemInterface $item) use ($imdbCode) { $results = $this->cache->get($cacheKey, function (ItemInterface $item) use ($imdbCode, $type) {
$item->expiresAt(Carbon::now()->addHour()->setMinute(0)->setSecond(0)); $item->expiresAt(Carbon::now()->addHour()->setMinute(0)->setSecond(0));
$item->tag(['torrentio', $type, $imdbCode]);
try { try {
$response = $this->client->get("$this->searchUrl/$imdbCode.json"); $response = $this->client->get("$this->searchUrl/$imdbCode.json");
return json_decode( return json_decode(
@@ -63,6 +64,7 @@ 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));
$item->tag(['torrentio', 'tvshows', 'torrentio.tvshows', $imdbId, "torrentio.$imdbId", "$imdbId.$season", "torrentio.$imdbId.$season", "$imdbId.$season.$episode", "torrentio.$imdbId.$season.$episode"]);
try { try {
$response = $this->client->get("$this->searchUrl/$imdbId:$season:$episode.json"); $response = $this->client->get("$this->searchUrl/$imdbId:$season:$episode.json");
return json_decode( return json_decode(

View File

@@ -17,13 +17,16 @@ use Psr\Log\LoggerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Bundle\SecurityBundle\Security; use Symfony\Bundle\SecurityBundle\Security;
use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\KernelInterface; use Symfony\Component\HttpKernel\KernelInterface;
use Symfony\Component\Routing\Attribute\Route; use Symfony\Component\Routing\Attribute\Route;
class RegistrationController extends AbstractController class RegistrationController extends AbstractController
{ {
public function __construct(private readonly RegisterUserHandler $registerUserHandler) public function __construct(private readonly RegisterUserHandler $registerUserHandler,
private readonly RequestStack $requestStack
)
{ {
} }
@@ -71,6 +74,7 @@ class RegistrationController extends AbstractController
)); ));
$security->login($user->user); $security->login($user->user);
$this->requestStack->getCurrentRequest()->getSession()->set('mercure_alert_topic', 'alerts_' . uniqid());
return $this->redirectToRoute('app_index'); return $this->redirectToRoute('app_index');
} }

View File

@@ -2,6 +2,7 @@
<html> <html>
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>{% block title %}Welcome!{% endblock %}</title> <title>{% block title %}Welcome!{% endblock %}</title>
<link rel="icon" href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 128 128%22><text y=%221.2em%22 font-size=%2296%22>⚫️</text><text y=%221.3em%22 x=%220.2em%22 font-size=%2276%22 fill=%22%23fff%22>sf</text></svg>"> <link rel="icon" href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 128 128%22><text y=%221.2em%22 font-size=%2296%22>⚫️</text><text y=%221.3em%22 x=%220.2em%22 font-size=%2276%22 fill=%22%23fff%22>sf</text></svg>">
{% block stylesheets %} {% block stylesheets %}

View File

@@ -2,6 +2,7 @@
<html> <html>
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>{% block title %}Welcome!{% endblock %}</title> <title>{% block title %}Welcome!{% endblock %}</title>
<link rel="icon" href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 128 128%22><text y=%221.2em%22 font-size=%2296%22>⚫️</text><text y=%221.3em%22 x=%220.2em%22 font-size=%2276%22 fill=%22%23fff%22>sf</text></svg>"> <link rel="icon" href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 128 128%22><text y=%221.2em%22 font-size=%2296%22>⚫️</text><text y=%221.3em%22 x=%220.2em%22 font-size=%2276%22 fill=%22%23fff%22>sf</text></svg>">
{% block stylesheets %} {% block stylesheets %}
@@ -13,11 +14,11 @@
{% endblock %} {% endblock %}
</head> </head>
<body class="flex flex-col bg-stone-700"> <body class="flex flex-col bg-stone-700">
<div class="grid grid-cols-6"> <div class="grid md:grid-cols-6">
<div class="col-span-1 h-screen"> <div class="hidden md:block md:col-span-1 md:h-screen">
<twig:NavBar /> <twig:NavBar />
</div> </div>
<div class="col-span-5 h-screen overflow-y-scroll"> <div class="col-span-6 md:col-span-5 h-screen overflow-y-scroll">
<twig:Header /> <twig:Header />
<h2 class="px-4 my-2 text-3xl font-bold text-gray-50">{% block h2 %}{% endblock %}</h2> <h2 class="px-4 my-2 text-3xl font-bold text-gray-50">{% block h2 %}{% endblock %}</h2>
{% block body %}{% endblock %} {% block body %}{% endblock %}

View File

@@ -6,7 +6,7 @@
data-result-filter-tv-results-outlet=".results" data-result-filter-tv-results-outlet=".results"
data-result-filter-tv-episode-list-outlet=".episode-list" data-result-filter-tv-episode-list-outlet=".episode-list"
> >
<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-col md: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" <select id="resolution"

View File

@@ -5,12 +5,21 @@
<div class="md:flex md:items-center md:gap-12"> <div class="md:flex md:items-center md:gap-12">
<nav aria-label="Global" class="md:block"> <nav aria-label="Global" class="md:block">
<ul class="flex items-center gap-6 text-sm"> <ul class="flex items-center gap-6 text-sm">
<li><twig:ux:icon name="fluent:alert-12-regular" width="30px" class="text-gray-950 bg-orange-500 rounded-full p-2"/></li> <li class="hidden">
<li> <twig:ux:icon name="fluent:alert-12-regular" width="30px" class="text-gray-950 bg-orange-500 rounded-full p-2"/>
</li>
<li class="hidden md:block">
<a href="{{ path('app_logout') }}"> <a href="{{ path('app_logout') }}">
<twig:ux:icon name="material-symbols:logout" width="25px" class="text-orange-500" /> <twig:ux:icon name="material-symbols:logout" width="25px" class="text-orange-500" />
</a> </a>
</li> </li>
<li {{ stimulus_controller('hamburger') }}
{{ stimulus_action('hamburger', 'toggleMenu', 'click') }}
data-hamburger-navbar-outlet="#navbar"
id="hamburger" class="cursor-pointer md:hidden"
>
<svg xmlns="http://www.w3.org/2000/svg" class="text-orange-500 ml-4" width="25px" height="25px" viewBox="0 0 16 16"><path fill="currentColor" fill-rule="evenodd" d="M0 3.75A.75.75 0 0 1 .75 3h14.5a.75.75 0 0 1 0 1.5H.75A.75.75 0 0 1 0 3.75M0 8a.75.75 0 0 1 .75-.75h14.5a.75.75 0 0 1 0 1.5H.75A.75.75 0 0 1 0 8m.75 3.5a.75.75 0 0 0 0 1.5h14.5a.75.75 0 0 0 0-1.5z" clip-rule="evenodd"/></svg>
</li>
</ul> </ul>
</nav> </nav>
</div> </div>

View File

@@ -1,4 +1,4 @@
<nav {{ attributes }} {{ stimulus_controller('navbar') }} {{ stimulus_action('navbar', 'setActive')}} class="flex h-screen flex-col justify-between bg-cyan-950"> <nav id="navbar" {{ attributes }} {{ stimulus_controller('navbar') }} {{ stimulus_action('navbar', 'setActive')}} class="flex h-screen flex-col justify-between bg-cyan-950 animate__animated animate__slideInLeft animate__slow">
<div class="px-4 py-4 flex flex-col gap-12"> <div class="px-4 py-4 flex flex-col gap-12">
<h1 class="text-3xl font-extrabold text-orange-500 mb-3">Torsearch</h1> <h1 class="text-3xl font-extrabold text-orange-500 mb-3">Torsearch</h1>
<ul class="space-y-1"> <ul class="space-y-1">

View File

@@ -3,12 +3,12 @@
mediaType: mediaType, mediaType: mediaType,
imdbId: imdbId imdbId: imdbId
}) }}"> }) }}">
<img src="{{ image }}" class="w-40 rounded-md" /> <img src="{{ image }}" class="w-full md:w-40 rounded-md" />
</a> </a>
<a href="{{ path('app_search_result', { <a href="{{ path('app_search_result', {
mediaType: mediaType, mediaType: mediaType,
imdbId: imdbId imdbId: imdbId
}) }}"> }) }}">
<h3 class="text-center text-gray-50 max-w-[16ch] text-extrabold">{{ title }}</h3> <h3 class="text-center text-white text-xl md:text-base md:max-w-[16ch]">{{ title }}</h3>
</a> </a>
</div> </div>

View File

@@ -1,9 +1,9 @@
<div{{ attributes }}> <div{{ attributes }}>
<div class="p-4 flex flex-row gap-6 bg-orange-500 bg-clip-padding backdrop-filter backdrop-blur-md bg-opacity-60 rounded-md"> <div class="p-4 flex flex-col md:flex-row gap-6 bg-orange-500 bg-clip-padding backdrop-filter backdrop-blur-md bg-opacity-60 rounded-md">
{% if poster != null and poster != "https://image.tmdb.org/t/p/w500" %} {% if poster != null and poster != "https://image.tmdb.org/t/p/w500" %}
<img class="w-24 rounded-lg" src="{{ poster }}" /> <img class="w-full md:w-24 rounded-lg" src="{{ poster }}" />
{% else %} {% else %}
<div class="w-32 h-[144px] rounded-lg bg-gray-700 flex items-center justify-center"> <div class="w-full md:w-32 h-[144px] rounded-lg bg-gray-700 flex items-center justify-center">
<twig:ux:icon width="16" name="hugeicons:loading-01" /> <twig:ux:icon width="16" name="hugeicons:loading-01" />
</div> </div>
{% endif %} {% endif %}
@@ -12,11 +12,11 @@
<h3 class="mb-4 text-xl font-medium leading-tight font-bold text-gray-50"> <h3 class="mb-4 text-xl font-medium leading-tight font-bold text-gray-50">
{{ title }} - {{ year }} {{ title }} - {{ year }}
</h3> </h3>
<p class="text-gray-50"> <p class="hidden md:text-gray-50">
{{ description }} {{ description }}
</p> </p>
</div> </div>
<a class="h-9 rounded-md py-1 px-2 bg-green-600 text-gray-50" <a class="h-9 rounded-md py-1 px-2 bg-green-600 text-gray-50 text-center"
href="{{ path('app_search_result', {mediaType: mediaType, imdbId: imdbId}) }}" href="{{ path('app_search_result', {mediaType: mediaType, imdbId: imdbId}) }}"
>choose</a> >choose</a>
</div> </div>

View File

@@ -15,12 +15,12 @@
active: 'true', active: 'true',
}) }} }) }}
> >
<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="p-4 md: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"> <div class="flex flex-col md:flex-row gap-4">
{% if episode['poster'] != null %} {% if episode['poster'] != null %}
<img class="w-64 rounded-lg" src="{{ episode['poster'] }}" /> <img class="w-full md:w-64 rounded-lg" src="{{ episode['poster'] }}" />
{% else %} {% else %}
<div class="w-64 min-w-64 sticky h-[144px] rounded-lg bg-gray-700 flex items-center justify-center"> <div class="w-full md: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" /> <twig:ux:icon width="32" name="hugeicons:loading-01" />
</div> </div>
{% endif %} {% endif %}

View File

@@ -5,7 +5,7 @@
{% block body %} {% block body %}
<div class="p-4 flex flex-col grow gap-4 z-30"> <div class="p-4 flex flex-col grow gap-4 z-30">
<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-col md:flex-row gap-4">
<twig:Card title="Active Downloads" class="w-full"> <twig:Card title="Active Downloads" class="w-full">
<twig:DownloadList :type="'active'" /> <twig:DownloadList :type="'active'" />
</twig:Card> </twig:Card>
@@ -14,13 +14,13 @@
<twig:DownloadList :type="'complete'" /> <twig:DownloadList :type="'complete'" />
</twig:Card> </twig:Card>
</div> </div>
<div class="flex flex-row gap-4"> <div class="flex flex-col md:flex-row gap-4">
<twig:Card title="Monitors" class="w-full"> <twig:Card title="Monitors" class="w-full">
<twig:MonitorList :type="'active'" :isWidget="true" /> <twig:MonitorList :type="'active'" :isWidget="true" />
</twig:Card> </twig:Card>
</div> </div>
<div class="flex flex-col gap-4"> <div class="flex flex-col gap-4">
<twig:Card title="Popular Movies" contentClass="flex flex-row justify-between w-full"> <twig:Card title="Popular Movies" contentClass="flex flex-col gap-4 md:flex-row md:justify-between w-full">
{% for movie in popular_movies %} {% for movie in popular_movies %}
<twig:Poster imdbId="{{ movie.imdbId }}" <twig:Poster imdbId="{{ movie.imdbId }}"
tmdbId="{{ movie.tmdbId }}" tmdbId="{{ movie.tmdbId }}"
@@ -32,7 +32,7 @@
/> />
{% endfor %} {% endfor %}
</twig:Card> </twig:Card>
<twig:Card title="Popular TV Shows" contentClass="flex flex-row justify-between w-full"> <twig:Card title="Popular TV Shows" contentClass="flex flex-col md:flex-row justify-between w-full">
{% for movie in popular_tvshows %} {% for movie in popular_tvshows %}
<twig:Poster imdbId="{{ movie.imdbId }}" <twig:Poster imdbId="{{ movie.imdbId }}"
tmdbId="{{ movie.tmdbId }}" tmdbId="{{ movie.tmdbId }}"

View File

@@ -7,11 +7,11 @@
<h2 class="mb-2 text-3xl font-bold text-gray-50">Media Results</h2> <h2 class="mb-2 text-3xl font-bold text-gray-50">Media Results</h2>
<div class="flex flex-row w-full gap-2"> <div class="flex flex-row w-full gap-2">
<twig:Card title="" contentClass="flex flex-col gap-4 justify-between w-full text-gray-50"> <twig:Card title="" contentClass="flex flex-col gap-4 justify-between w-full text-gray-50">
<div class="p-4 flex flex-row gap-6"> <div class="p-2 md:p-4 flex flex-col md:flex-row gap-6">
{% if results.media.poster != null %} {% if results.media.poster != null %}
<img class="w-40 rounded-lg" src="{{ results.media.poster }}" /> <img class="w-full md:w-40 rounded-lg" src="{{ results.media.poster }}" />
{% else %} {% else %}
<div class="w-40 h-[144px] rounded-lg bg-gray-700 flex items-center justify-center"> <div class="w-full md:w-40 h-[144px] rounded-lg bg-gray-700 flex items-center justify-center">
<twig:ux:icon width="24" name="hugeicons:loading-01" /> <twig:ux:icon width="24" name="hugeicons:loading-01" />
</div> </div>
{% endif %} {% endif %}
@@ -127,4 +127,20 @@
</twig:Card> </twig:Card>
</div> </div>
</div> </div>
<style>
html,
body {
height: 100%;
}
@media (min-width: 640px) {
thead tr:not(:first-child) {
display: none;
}
}
td:not(:last-child) {
border-bottom: 0;
}
</style>
{% endblock %} {% endblock %}

View File

@@ -1,59 +1,62 @@
<table class="w-full text-sm text-left rtl:text-right text-gray-500 dark:text-gray-400 {{ results.media.mediaType == "tvshows" ? "hidden" }}" <table class="w-full max-w-[75vw] text-sm text-left rtl:text-right text-gray-500 dark:text-gray-400 flex-row flex-no-wrap {{ results.media.mediaType == "tvshows" ? "hidden" : "options-table" }}"
{{ stimulus_target(controller, "list") }} {{ stimulus_target(controller, "list") }}
> >
<thead class="text-xs text-gray-700 uppercase bg-gray-50 dark:bg-gray-700 dark:text-gray-400"> <thead class="text-xs text-gray-700 uppercase dark:text-gray-400">
<tr class="dark:bg-stone-600 overflow-hidden"> {% for result in results.results %}
<tr class="dark:bg-stone-600 overflow-hidden flex flex-col md:flex-col flex-no wrap md:table-row border-b border-gray-500">
<th scope="col" <th scope="col"
class="px-6 py-4 font-medium text-gray-900 whitespace-nowrap dark:text-white"> class="px-4 py-4 leading-[20px] font-medium text-gray-900 whitespace-nowrap dark:text-white">
Size Size
</th> </th>
<th scope="col" <th scope="col"
class="px-6 py-4 font-medium text-gray-900 whitespace-nowrap dark:text-white"> class="px-4 py-4 leading-[20px] font-medium text-gray-900 whitespace-nowrap dark:text-white">
Resolution Resolution
</th> </th>
<th scope="col" <th scope="col"
class="px-6 py-4 font-medium text-gray-900 whitespace-nowrap dark:text-white"> class="px-4 py-4 leading-[20px] font-medium text-gray-900 whitespace-nowrap dark:text-white">
Codec Codec
</th> </th>
<th scope="col" <th scope="col"
class="px-6 py-4 font-medium text-gray-900 whitespace-nowrap dark:text-white"> class="px-4 py-4 leading-[20px] font-medium text-gray-900 whitespace-nowrap dark:text-white">
Seeders Seeders
</th> </th>
<th scope="col" <th scope="col"
class="px-6 py-4 font-medium text-gray-900 whitespace-nowrap dark:text-white"> class="px-4 py-4 leading-[20px] font-medium text-gray-900 whitespace-nowrap dark:text-white">
Provider Provider
</th> </th>
<th scope="col" <th scope="col"
class="px-6 py-4 font-medium text-gray-900 whitespace-nowrap dark:text-white"> class="px-4 py-4 leading-[20px] font-medium text-gray-900 whitespace-nowrap dark:text-white">
Language Language
</th> </th>
<th scope="col" <th scope="col"
class="px-6 py-4 font-medium text-gray-900 whitespace-nowrap dark:text-white"> class="px-4 py-4 leading-[32px] font-medium text-gray-900 whitespace-nowrap dark:text-white">
Actions
</th> </th>
</tr> </tr>
{% endfor %}
</thead> </thead>
<tbody> <tbody class="flex-1 sm:flex-none">
{% for result in results.results %} {% for result in results.results %}
<tr class="bg-white border-b dark:bg-slate-700 dark:border-gray-600 border-gray-200" data-provider="{{ result.provider }}" data-languages="{{ result.languages|json_encode }}" {% if "tvshows" == results.media.mediaType %} data-season="{{ results.season }}"{% endif %}> <tr class="bg-white dark:bg-slate-700 flex flex-col flex-no wrap sm:table-row border-b border-gray-500" data-provider="{{ result.provider }}" data-languages="{{ result.languages|json_encode }}" {% if "tvshows" == results.media.mediaType %} data-season="{{ results.season }}"{% endif %}>
<td id="size" class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-800 dark:text-gray-50"> <td id="size" class="px-4 py-4 whitespace-nowrap text-sm font-medium text-gray-800 dark:text-gray-50">
{{ result.size }} {{ result.size }}
</td> </td>
<td id="resolution" class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-800 dark:text-gray-50"> <td id="resolution" class="px-4 py-4 whitespace-nowrap text-sm font-medium text-gray-800 dark:text-gray-50">
{{ result.resolution }} {{ result.resolution }}
</td> </td>
<td id="codec" class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-800 dark:text-gray-50"> <td id="codec" class="px-4 py-4 whitespace-nowrap text-sm font-medium text-gray-800 dark:text-gray-50">
{{ result.codec }} {{ result.codec }}
</td> </td>
<td id="seeders" class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-800 dark:text-gray-50"> <td id="seeders" class="px-4 py-4 whitespace-nowrap text-sm font-medium text-gray-800 dark:text-gray-50">
{{ result.seeders }} {{ result.seeders }}
</td> </td>
<td id="provider" class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-800 dark:text-gray-50" data-provider="{{ result.provider }}"> <td id="provider" class="px-4 py-4 whitespace-nowrap text-sm font-medium text-gray-800 dark:text-gray-50 " data-provider="{{ result.provider }}">
{{ result.provider }} {{ result.provider }}
</td> </td>
<td id="language" class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-800 dark:text-gray-50" data-languages="{{ result.languages|json_encode }}"> <td id="language" class="px-4 py-4 whitespace-nowrap text-sm font-medium text-gray-800 dark:text-gray-50 overflow-scroll" data-languages="{{ result.languages|json_encode }}">
{{ result.languageFlags|raw }} {{ result.languageFlags|raw }}
</td> </td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-end text-gray-800 dark:text-gray-50 flex flex-row gap-2 items-center justify-end"> <td class="px-4 py-4 whitespace-nowrap text-sm text-end text-gray-800 dark:text-gray-50 flex flex-row gap-2 items-center justify-start mb:justify-end">
<button class="download-btn p-1.5 bg-green-600 rounded-md text-gray-50" <button class="download-btn p-1.5 bg-green-600 rounded-md text-gray-50"
{{ stimulus_controller('download_button', { {{ stimulus_controller('download_button', {
url: result.url, url: result.url,