handle series
This commit is contained in:
@@ -7,20 +7,27 @@ export const load: PageServerLoad = async ({ parent }) => {
|
||||
const db = getDb();
|
||||
scanLibraries(db);
|
||||
|
||||
const games =
|
||||
loggedIn
|
||||
? db
|
||||
.prepare(
|
||||
`SELECT id, slug, title, library, has_cover, has_wide, genre
|
||||
FROM games ORDER BY title ASC`
|
||||
)
|
||||
.all()
|
||||
: db
|
||||
.prepare(
|
||||
`SELECT id, slug, title, library, has_cover, has_wide, genre
|
||||
FROM games WHERE library = 'public' ORDER BY title ASC`
|
||||
)
|
||||
.all();
|
||||
const games = loggedIn
|
||||
? db
|
||||
.prepare(
|
||||
`SELECT id, slug, title, library, has_cover, has_wide, genre
|
||||
FROM games WHERE series_id IS NULL ORDER BY title ASC`
|
||||
)
|
||||
.all()
|
||||
: db
|
||||
.prepare(
|
||||
`SELECT id, slug, title, library, has_cover, has_wide, genre
|
||||
FROM games WHERE series_id IS NULL AND library = 'public' ORDER BY title ASC`
|
||||
)
|
||||
.all();
|
||||
|
||||
return { games };
|
||||
const series = loggedIn
|
||||
? db.prepare(`SELECT id, slug, title, library, has_cover FROM series ORDER BY title ASC`).all()
|
||||
: db
|
||||
.prepare(
|
||||
`SELECT id, slug, title, library, has_cover FROM series WHERE library = 'public' ORDER BY title ASC`
|
||||
)
|
||||
.all();
|
||||
|
||||
return { games, series };
|
||||
};
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script lang="ts">
|
||||
import type { PageData } from './$types';
|
||||
import GameGrid from '$lib/components/GameGrid.svelte';
|
||||
import type { Game } from '$lib/types';
|
||||
import type { Game, Series } from '$lib/types';
|
||||
|
||||
let { data }: { data: PageData } = $props();
|
||||
let filter = $state('');
|
||||
@@ -20,5 +20,10 @@
|
||||
class="w-full max-w-md px-4 py-2 rounded-lg bg-gray-800 border border-gray-700 text-gray-100 placeholder-gray-400 focus:outline-none focus:border-purple-500 transition-colors"
|
||||
/>
|
||||
</div>
|
||||
<GameGrid games={data.games as Game[]} loggedIn={data.loggedIn} {filter} />
|
||||
<GameGrid
|
||||
games={data.games as Game[]}
|
||||
series={data.series as Series[]}
|
||||
loggedIn={data.loggedIn}
|
||||
{filter}
|
||||
/>
|
||||
</div>
|
||||
|
||||
34
src/routes/api/cover/series/[slug]/+server.ts
Normal file
34
src/routes/api/cover/series/[slug]/+server.ts
Normal file
@@ -0,0 +1,34 @@
|
||||
import type { RequestHandler } from './$types';
|
||||
import { getDb } from '$lib/server/db';
|
||||
import { getSession } from '$lib/server/auth';
|
||||
import { findCoverFile } from '$lib/server/files';
|
||||
import { contentType } from '$lib/utils';
|
||||
import fs from 'node:fs';
|
||||
import { Readable } from 'node:stream';
|
||||
import type { Series } from '$lib/types';
|
||||
|
||||
export const GET: RequestHandler = ({ params, cookies }) => {
|
||||
const db = getDb();
|
||||
const series = db
|
||||
.prepare('SELECT * FROM series WHERE slug = ?')
|
||||
.get(params.slug) as Series | undefined;
|
||||
|
||||
if (!series) return new Response('Not found', { status: 404 });
|
||||
if (series.library === 'private' && !getSession(cookies)) {
|
||||
return new Response('Unauthorized', { status: 401 });
|
||||
}
|
||||
|
||||
const coverPath = findCoverFile(series.folder_path);
|
||||
if (!coverPath) return new Response('No cover image', { status: 404 });
|
||||
|
||||
const mime = contentType(coverPath);
|
||||
const nodeStream = fs.createReadStream(coverPath);
|
||||
const webStream = Readable.toWeb(nodeStream) as ReadableStream;
|
||||
|
||||
return new Response(webStream, {
|
||||
headers: {
|
||||
'Content-Type': mime,
|
||||
'Cache-Control': 'public, max-age=86400'
|
||||
}
|
||||
});
|
||||
};
|
||||
25
src/routes/series/[slug]/+page.server.ts
Normal file
25
src/routes/series/[slug]/+page.server.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import type { PageServerLoad } from './$types';
|
||||
import { error } from '@sveltejs/kit';
|
||||
import { getDb } from '$lib/server/db';
|
||||
import { getSession } from '$lib/server/auth';
|
||||
import type { Series } from '$lib/types';
|
||||
|
||||
export const load: PageServerLoad = async ({ params, cookies, parent }) => {
|
||||
await parent();
|
||||
const db = getDb();
|
||||
const series = db
|
||||
.prepare('SELECT * FROM series WHERE slug = ?')
|
||||
.get(params.slug) as Series | undefined;
|
||||
|
||||
if (!series) error(404, 'Series not found');
|
||||
if (series.library === 'private' && !getSession(cookies)) error(401, 'Unauthorized');
|
||||
|
||||
const games = db
|
||||
.prepare(
|
||||
`SELECT id, slug, title, library, has_cover, has_wide, genre
|
||||
FROM games WHERE series_id = ? ORDER BY title ASC`
|
||||
)
|
||||
.all(series.id);
|
||||
|
||||
return { series, games };
|
||||
};
|
||||
30
src/routes/series/[slug]/+page.svelte
Normal file
30
src/routes/series/[slug]/+page.svelte
Normal file
@@ -0,0 +1,30 @@
|
||||
<script lang="ts">
|
||||
import type { PageData } from './$types';
|
||||
import GameGrid from '$lib/components/GameGrid.svelte';
|
||||
import type { Game } from '$lib/types';
|
||||
|
||||
let { data }: { data: PageData } = $props();
|
||||
let filter = $state('');
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<title>{data.series.title} — Game Grid</title>
|
||||
</svelte:head>
|
||||
|
||||
<div class="max-w-screen-2xl mx-auto px-4 py-6">
|
||||
<div class="mb-6">
|
||||
<a href="/" class="text-gray-400 hover:text-white text-sm transition-colors">← Back</a>
|
||||
<h1 class="mt-3 text-2xl font-bold text-white">{data.series.title}</h1>
|
||||
</div>
|
||||
|
||||
<div class="mb-6">
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Search games..."
|
||||
bind:value={filter}
|
||||
class="w-full max-w-md px-4 py-2 rounded-lg bg-gray-800 border border-gray-700 text-gray-100 placeholder-gray-400 focus:outline-none focus:border-purple-500 transition-colors"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<GameGrid games={data.games as Game[]} loggedIn={data.loggedIn} {filter} />
|
||||
</div>
|
||||
Reference in New Issue
Block a user