mirror of
https://github.com/maxdorninger/MediaManager.git
synced 2026-04-21 16:25:36 +02:00
working on the frontend for the movies
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
<script lang="ts" module>
|
||||
import {Home, Info, LifeBuoy, Send, Settings, TvIcon} from 'lucide-svelte';
|
||||
import {Home, Info, LifeBuoy, Send, Settings, TvIcon, Clapperboard} from 'lucide-svelte';
|
||||
import {PUBLIC_VERSION} from '$env/static/public';
|
||||
|
||||
const data = {
|
||||
@@ -30,6 +30,26 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
title: 'Movies',
|
||||
url: '/dashboard/movies',
|
||||
icon: Clapperboard,
|
||||
isActive: true,
|
||||
items: [
|
||||
{
|
||||
title: 'Add a movie',
|
||||
url: '/dashboard/movies/add-movie'
|
||||
},
|
||||
{
|
||||
title: 'Torrents',
|
||||
url: '/dashboard/movies/torrents'
|
||||
},
|
||||
{
|
||||
title: 'Requests',
|
||||
url: '/dashboard/movies/requests'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
title: 'Settings',
|
||||
url: '/dashboard/settings',
|
||||
|
||||
18
web/src/lib/components/media-picture.svelte
Normal file
18
web/src/lib/components/media-picture.svelte
Normal file
@@ -0,0 +1,18 @@
|
||||
<script>
|
||||
import {getFullyQualifiedMediaName} from '$lib/utils.js';
|
||||
import {env} from '$env/dynamic/public';
|
||||
|
||||
const apiUrl = env.PUBLIC_API_URL;
|
||||
let {media} = $props();
|
||||
console.log("got media: ", media);
|
||||
</script>
|
||||
|
||||
<picture>
|
||||
<source srcset="{apiUrl}/static/image/{media.id}.avif" type="image/avif"/>
|
||||
<source srcset="{apiUrl}/static/image/{media.id}.webp" type="image/webp"/>
|
||||
<img
|
||||
alt="{getFullyQualifiedMediaName(media)}'s Poster Image"
|
||||
class="aspect-9/16 center h-auto w-full rounded-lg object-cover"
|
||||
src="{apiUrl}/static/image/{media.id}.jpeg"
|
||||
/>
|
||||
</picture>
|
||||
@@ -1,17 +0,0 @@
|
||||
<script>
|
||||
import {getFullyQualifiedShowName} from '$lib/utils.js';
|
||||
import {env} from '$env/dynamic/public';
|
||||
|
||||
const apiUrl = env.PUBLIC_API_URL;
|
||||
let {show} = $props();
|
||||
</script>
|
||||
|
||||
<picture>
|
||||
<source srcset="{apiUrl}/static/image/{show.id}.avif" type="image/avif"/>
|
||||
<source srcset="{apiUrl}/static/image/{show.id}.webp" type="image/webp"/>
|
||||
<img
|
||||
alt="{getFullyQualifiedShowName(show)}'s Poster Image"
|
||||
class="aspect-9/16 center h-auto w-full rounded-lg object-cover"
|
||||
src="{apiUrl}/static/image/{show.id}.jpeg"
|
||||
/>
|
||||
</picture>
|
||||
@@ -119,6 +119,25 @@ export interface PublicShow {
|
||||
ended: boolean;
|
||||
}
|
||||
|
||||
export interface Movie {
|
||||
name: string;
|
||||
overview: string;
|
||||
year: number; // type: integer
|
||||
external_id: number; // type: integer
|
||||
metadata_provider: string;
|
||||
id: string; // type: string, format: uuid
|
||||
}
|
||||
|
||||
export interface PublicMovie {
|
||||
name: string;
|
||||
overview: string;
|
||||
year: number; // type: integer
|
||||
external_id: number; // type: integer
|
||||
metadata_provider: string;
|
||||
id: string; // type: string, format: uuid
|
||||
downloaded: boolean;
|
||||
}
|
||||
|
||||
export interface Torrent {
|
||||
status: TorrentStatus; // $ref: #/components/schemas/TorrentStatus
|
||||
title: string;
|
||||
|
||||
@@ -32,10 +32,11 @@ export function getTorrentQualityString(value: number): string {
|
||||
export function getTorrentStatusString(value: number): string {
|
||||
return torrentStatusMap[value] || 'unknown';
|
||||
}
|
||||
export function getFullyQualifiedShowName(show: { name: string; year: number }): string {
|
||||
let name = show.name;
|
||||
if (show.year != null) {
|
||||
name += ' (' + show.year + ')';
|
||||
|
||||
export function getFullyQualifiedMediaName(media: { name: string; year: number }): string {
|
||||
let name = media.name;
|
||||
if (media.year != null) {
|
||||
name += ' (' + media.year + ')';
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
91
web/src/routes/dashboard/movies/+page.svelte
Normal file
91
web/src/routes/dashboard/movies/+page.svelte
Normal file
@@ -0,0 +1,91 @@
|
||||
<script lang="ts">
|
||||
import {page} from '$app/state';
|
||||
import * as Card from '$lib/components/ui/card/index.js';
|
||||
import {Separator} from '$lib/components/ui/separator/index.js';
|
||||
import * as Sidebar from '$lib/components/ui/sidebar/index.js';
|
||||
import * as Breadcrumb from '$lib/components/ui/breadcrumb/index.js';
|
||||
import {getFullyQualifiedMediaName} from '$lib/utils';
|
||||
import LoadingBar from '$lib/components/loading-bar.svelte';
|
||||
import MediaPicture from '$lib/components/media-picture.svelte';
|
||||
import {onMount} from "svelte";
|
||||
import {toast} from "svelte-sonner";
|
||||
import {env} from '$env/dynamic/public';
|
||||
|
||||
const apiUrl = env.PUBLIC_API_URL;
|
||||
let movies
|
||||
let loading = false;
|
||||
onMount(async () => {
|
||||
loading = true;
|
||||
const response = await fetch(apiUrl + '/movies/', {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
credentials: 'include'
|
||||
});
|
||||
if (!response.ok) {
|
||||
toast.error(`Failed to fetch movies`)
|
||||
throw new Error(`Failed to fetch movies: ${response.status} ${response.statusText}`);
|
||||
}
|
||||
movies = await response.json();
|
||||
console.log("got movies: ", movies);
|
||||
loading = false;
|
||||
})
|
||||
</script>
|
||||
|
||||
<header class="flex h-16 shrink-0 items-center gap-2">
|
||||
<div class="flex items-center gap-2 px-4">
|
||||
<Sidebar.Trigger class="-ml-1"/>
|
||||
<Separator class="mr-2 h-4" orientation="vertical"/>
|
||||
<Breadcrumb.Root>
|
||||
<Breadcrumb.List>
|
||||
<Breadcrumb.Item class="hidden md:block">
|
||||
<Breadcrumb.Link href="/dashboard">MediaManager</Breadcrumb.Link>
|
||||
</Breadcrumb.Item>
|
||||
<Breadcrumb.Separator class="hidden md:block"/>
|
||||
<Breadcrumb.Item>
|
||||
<Breadcrumb.Link href="/dashboard">Home</Breadcrumb.Link>
|
||||
</Breadcrumb.Item>
|
||||
<Breadcrumb.Separator class="hidden md:block"/>
|
||||
<Breadcrumb.Item>
|
||||
<Breadcrumb.Page>Movies</Breadcrumb.Page>
|
||||
</Breadcrumb.Item>
|
||||
</Breadcrumb.List>
|
||||
</Breadcrumb.Root>
|
||||
</div>
|
||||
</header>
|
||||
{#snippet loadingbar()}
|
||||
<div class="animate-fade-in col-span-full flex w-full flex-col items-center justify-center py-16">
|
||||
<div class="w-1/2 max-w-xs">
|
||||
<LoadingBar/>
|
||||
</div>
|
||||
</div>
|
||||
{/snippet}
|
||||
<div class="flex w-full flex-1 flex-col gap-4 p-4 pt-0">
|
||||
<h1 class="scroll-m-20 text-center text-4xl font-extrabold tracking-tight lg:text-5xl">
|
||||
Movies
|
||||
</h1>
|
||||
<div
|
||||
class="grid w-full auto-rows-min gap-4 sm:grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 2xl:grid-cols-5"
|
||||
>
|
||||
{#if loading}
|
||||
{@render loadingbar()}
|
||||
{:else}
|
||||
{#each movies as movie}
|
||||
<a href={'/dashboard/movies/' + movie.id}>
|
||||
<Card.Root class="col-span-full max-w-[90vw] ">
|
||||
<Card.Header>
|
||||
<Card.Title class="h-6 truncate">{getFullyQualifiedMediaName(movie)}</Card.Title>
|
||||
<Card.Description class="truncate">{movie.overview}</Card.Description>
|
||||
</Card.Header>
|
||||
<Card.Content>
|
||||
<MediaPicture media={movie}/>
|
||||
</Card.Content>
|
||||
</Card.Root>
|
||||
</a>
|
||||
{:else}
|
||||
<div class="col-span-full text-center text-muted-foreground">No movies added yet.</div>
|
||||
{/each}
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
Reference in New Issue
Block a user