@axium/kino 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.md +157 -0
- package/db.json +106 -0
- package/dist/client/api.d.ts +49 -0
- package/dist/client/api.js +136 -0
- package/dist/client/frontend.d.ts +3 -0
- package/dist/client/frontend.js +33 -0
- package/dist/client/index.d.ts +1 -0
- package/dist/client/index.js +1 -0
- package/dist/client/web_hook.d.ts +8 -0
- package/dist/client/web_hook.js +4 -0
- package/dist/common.d.ts +749 -0
- package/dist/common.js +212 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/server/api/delete.d.ts +1 -0
- package/dist/server/api/delete.js +57 -0
- package/dist/server/api/metadata.d.ts +4 -0
- package/dist/server/api/metadata.js +153 -0
- package/dist/server/api/raw.d.ts +1 -0
- package/dist/server/api/raw.js +86 -0
- package/dist/server/api/search.d.ts +1 -0
- package/dist/server/api/search.js +108 -0
- package/dist/server/api/upload.d.ts +1 -0
- package/dist/server/api/upload.js +122 -0
- package/dist/server/api/views.d.ts +1 -0
- package/dist/server/api/views.js +128 -0
- package/dist/server/db.d.ts +11 -0
- package/dist/server/db.js +100 -0
- package/dist/server/hooks.d.ts +9 -0
- package/dist/server/hooks.js +19 -0
- package/dist/server/images.d.ts +12 -0
- package/dist/server/images.js +152 -0
- package/dist/server/media.d.ts +47 -0
- package/dist/server/media.js +136 -0
- package/dist/server/tmdb.d.ts +2 -0
- package/dist/server/tmdb.js +7 -0
- package/lib/EpisodeList.svelte +89 -0
- package/lib/MediaActions.svelte +110 -0
- package/lib/MediaCard.svelte +41 -0
- package/lib/MediaDetail.svelte +140 -0
- package/lib/MediaGrid.svelte +27 -0
- package/lib/Poster.svelte +49 -0
- package/lib/ProgressBar.svelte +22 -0
- package/lib/RecentGrid.svelte +82 -0
- package/lib/SearchBar.svelte +174 -0
- package/lib/SeasonList.svelte +50 -0
- package/lib/index.ts +10 -0
- package/lib/tsconfig.json +12 -0
- package/lib/watch.svelte.ts +93 -0
- package/locales/en.json +103 -0
- package/package.json +102 -0
- package/routes/+layout.svelte +22 -0
- package/routes/+layout.ts +21 -0
- package/routes/kino/+page.svelte +57 -0
- package/routes/kino/+page.ts +19 -0
- package/routes/movies/+page.svelte +22 -0
- package/routes/movies/+page.ts +12 -0
- package/routes/movies/[id]/+page.svelte +36 -0
- package/routes/movies/[id]/+page.ts +7 -0
- package/routes/movies/[id]/watch/+page.svelte +34 -0
- package/routes/movies/[id]/watch/+page.ts +18 -0
- package/routes/tsconfig.json +12 -0
- package/routes/tv/+page.svelte +22 -0
- package/routes/tv/+page.ts +12 -0
- package/routes/tv/[id]/+page.svelte +39 -0
- package/routes/tv/[id]/+page.ts +7 -0
- package/routes/tv/[id]/[season]/+page.svelte +43 -0
- package/routes/tv/[id]/[season]/+page.ts +11 -0
- package/routes/tv/[id]/[season]/[episode]/+page.svelte +46 -0
- package/routes/tv/[id]/[season]/[episode]/+page.ts +12 -0
- package/routes/tv/[id]/[season]/[episode]/watch/+page.svelte +46 -0
- package/routes/tv/[id]/[season]/[episode]/watch/+page.ts +22 -0
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { text } from '@axium/client';
|
|
3
|
+
import { MediaGrid, RecentGrid } from '@axium/kino/components';
|
|
4
|
+
|
|
5
|
+
const { data } = $props();
|
|
6
|
+
|
|
7
|
+
const movies = $derived(data.movies.map(movie => ({ ...movie, type: 'movie' as const })));
|
|
8
|
+
const shows = $derived(data.shows.map(show => ({ ...show, type: 'tv' as const })));
|
|
9
|
+
</script>
|
|
10
|
+
|
|
11
|
+
<svelte:head>
|
|
12
|
+
<title>{text('page.kino.title')}</title>
|
|
13
|
+
</svelte:head>
|
|
14
|
+
|
|
15
|
+
<h1>{text('page.kino.heading')}</h1>
|
|
16
|
+
|
|
17
|
+
{#if data.views.length}
|
|
18
|
+
<section>
|
|
19
|
+
<h2>{text('page.kino.recently_watched')}</h2>
|
|
20
|
+
<RecentGrid views={data.views} empty={text('page.kino.no_recent')} />
|
|
21
|
+
</section>
|
|
22
|
+
{/if}
|
|
23
|
+
|
|
24
|
+
{#if !movies.length && !shows.length}
|
|
25
|
+
<p class="subtle">{text('page.kino.empty')}</p>
|
|
26
|
+
{:else}
|
|
27
|
+
{#if movies.length}
|
|
28
|
+
<section>
|
|
29
|
+
<h2>{text('page.kino.recent_movies')}</h2>
|
|
30
|
+
<MediaGrid items={movies} empty={text('page.kino.movies.empty')} />
|
|
31
|
+
</section>
|
|
32
|
+
{/if}
|
|
33
|
+
|
|
34
|
+
{#if shows.length}
|
|
35
|
+
<section>
|
|
36
|
+
<h2>{text('page.kino.recent_tv')}</h2>
|
|
37
|
+
<MediaGrid items={shows} empty={text('page.kino.tv.empty')} />
|
|
38
|
+
</section>
|
|
39
|
+
{/if}
|
|
40
|
+
{/if}
|
|
41
|
+
|
|
42
|
+
<style>
|
|
43
|
+
h1 {
|
|
44
|
+
margin: 0;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
section {
|
|
48
|
+
display: flex;
|
|
49
|
+
flex-direction: column;
|
|
50
|
+
gap: 0.75em;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
h2 {
|
|
54
|
+
margin: 0;
|
|
55
|
+
font-size: 1.15em;
|
|
56
|
+
}
|
|
57
|
+
</style>
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { getUploadedMovies, getUploadedShows, getViews } from '@axium/kino/client';
|
|
2
|
+
import { redirect } from '@sveltejs/kit';
|
|
3
|
+
|
|
4
|
+
export const ssr = false;
|
|
5
|
+
|
|
6
|
+
export async function load({ parent }) {
|
|
7
|
+
const { session } = await parent();
|
|
8
|
+
|
|
9
|
+
if (!session) redirect(307, '/login?after=/kino');
|
|
10
|
+
|
|
11
|
+
const [movies, shows, views] = await Promise.all([
|
|
12
|
+
getUploadedMovies(),
|
|
13
|
+
getUploadedShows(),
|
|
14
|
+
// The library is the point of the page, so don't fail it over the recently watched list
|
|
15
|
+
getViews().catch(() => []),
|
|
16
|
+
]);
|
|
17
|
+
|
|
18
|
+
return { movies, shows, views };
|
|
19
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { text } from '@axium/client';
|
|
3
|
+
import { MediaGrid } from '@axium/kino/components';
|
|
4
|
+
|
|
5
|
+
const { data } = $props();
|
|
6
|
+
|
|
7
|
+
const movies = $derived(data.movies.map(movie => ({ ...movie, type: 'movie' as const })));
|
|
8
|
+
</script>
|
|
9
|
+
|
|
10
|
+
<svelte:head>
|
|
11
|
+
<title>{text('page.kino.movies.title')}</title>
|
|
12
|
+
</svelte:head>
|
|
13
|
+
|
|
14
|
+
<h1>{text('page.kino.movies.title')}</h1>
|
|
15
|
+
|
|
16
|
+
<MediaGrid items={movies} empty={text('page.kino.movies.empty')} />
|
|
17
|
+
|
|
18
|
+
<style>
|
|
19
|
+
h1 {
|
|
20
|
+
margin: 0;
|
|
21
|
+
}
|
|
22
|
+
</style>
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { getUploadedMovies } from '@axium/kino/client';
|
|
2
|
+
import { redirect } from '@sveltejs/kit';
|
|
3
|
+
|
|
4
|
+
export const ssr = false;
|
|
5
|
+
|
|
6
|
+
export async function load({ parent }) {
|
|
7
|
+
const { session } = await parent();
|
|
8
|
+
|
|
9
|
+
if (!session) redirect(307, '/login?after=/movies');
|
|
10
|
+
|
|
11
|
+
return { movies: await getUploadedMovies() };
|
|
12
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { text } from '@axium/client';
|
|
3
|
+
import { deleteMovieUpload, movieDataURL } from '@axium/kino/client';
|
|
4
|
+
import { uploadMovieFile } from '@axium/kino/client/frontend';
|
|
5
|
+
import { MediaActions, MediaDetail } from '@axium/kino/components';
|
|
6
|
+
|
|
7
|
+
const { data } = $props();
|
|
8
|
+
|
|
9
|
+
const movie = $derived(data.movie);
|
|
10
|
+
|
|
11
|
+
let upload = $state(data.movie.upload);
|
|
12
|
+
</script>
|
|
13
|
+
|
|
14
|
+
<svelte:head>
|
|
15
|
+
<title>{movie.title}</title>
|
|
16
|
+
</svelte:head>
|
|
17
|
+
|
|
18
|
+
<MediaDetail
|
|
19
|
+
title={movie.title}
|
|
20
|
+
imagePath={movie.poster_path}
|
|
21
|
+
backdropPath={movie.backdrop_path}
|
|
22
|
+
date={movie.release_date}
|
|
23
|
+
overview={movie.overview}
|
|
24
|
+
>
|
|
25
|
+
{#snippet actions()}
|
|
26
|
+
<MediaActions
|
|
27
|
+
bind:upload
|
|
28
|
+
watchHref="/movies/{movie.id}/watch"
|
|
29
|
+
dataURL={movieDataURL(movie.id, true)}
|
|
30
|
+
uploadText={text('kino.upload_movie')}
|
|
31
|
+
canDelete={data.session?.user.isAdmin}
|
|
32
|
+
uploadFile={file => uploadMovieFile(file, movie.id)}
|
|
33
|
+
deleteUpload={() => deleteMovieUpload(movie.id)}
|
|
34
|
+
/>
|
|
35
|
+
{/snippet}
|
|
36
|
+
</MediaDetail>
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { text } from '@axium/client';
|
|
3
|
+
import { Video } from '@axium/client/components';
|
|
4
|
+
import { MediaState } from '@axium/client/reactive';
|
|
5
|
+
import { movieDataURL } from '@axium/kino/client';
|
|
6
|
+
import { trackWatch } from '@axium/kino/watch';
|
|
7
|
+
|
|
8
|
+
const { data } = $props();
|
|
9
|
+
|
|
10
|
+
const { movie, upload } = $derived(data);
|
|
11
|
+
|
|
12
|
+
const media = new MediaState();
|
|
13
|
+
|
|
14
|
+
trackWatch({ media, target: { type: 'movie', id: data.movie.id }, resumeFrom: data.view?.position });
|
|
15
|
+
</script>
|
|
16
|
+
|
|
17
|
+
<svelte:head>
|
|
18
|
+
<title>{text('page.kino.watch_title', { name: movie.title })}</title>
|
|
19
|
+
</svelte:head>
|
|
20
|
+
|
|
21
|
+
<div class="watch">
|
|
22
|
+
<a class="subtle" href="/movies/{movie.id}">{movie.title}</a>
|
|
23
|
+
<Video {media} src={movieDataURL(movie.id)} size={upload.size} type={upload.type} name={movie.title} />
|
|
24
|
+
</div>
|
|
25
|
+
|
|
26
|
+
<style>
|
|
27
|
+
.watch {
|
|
28
|
+
display: flex;
|
|
29
|
+
flex-direction: column;
|
|
30
|
+
gap: 0.5em;
|
|
31
|
+
height: 80vh;
|
|
32
|
+
min-height: 0;
|
|
33
|
+
}
|
|
34
|
+
</style>
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { getMovie, getViews } from '@axium/kino/client';
|
|
2
|
+
import { error } from '@sveltejs/kit';
|
|
3
|
+
|
|
4
|
+
export const ssr = false;
|
|
5
|
+
|
|
6
|
+
export async function load({ params }) {
|
|
7
|
+
const id = Number(params.id);
|
|
8
|
+
|
|
9
|
+
const movie = await getMovie(id);
|
|
10
|
+
|
|
11
|
+
if (!movie.upload) error(404, 'This movie has not been uploaded');
|
|
12
|
+
|
|
13
|
+
// Used to resume where the last session left off; not worth failing the page over
|
|
14
|
+
const views = await getViews().catch(() => []);
|
|
15
|
+
const view = views.find(v => v.type == 'movie' && v.movie.id == id);
|
|
16
|
+
|
|
17
|
+
return { movie, upload: movie.upload, view };
|
|
18
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
{
|
|
2
|
+
"extends": ["../tsconfig.json", "../.svelte-kit/tsconfig.json"],
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
"moduleResolution": "bundler",
|
|
5
|
+
"module": "esnext",
|
|
6
|
+
"noEmit": true,
|
|
7
|
+
"target": "esnext",
|
|
8
|
+
"rootDir": ".."
|
|
9
|
+
},
|
|
10
|
+
"include": ["**/*", "../lib/*"],
|
|
11
|
+
"references": [{ "path": ".." }]
|
|
12
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { text } from '@axium/client';
|
|
3
|
+
import { MediaGrid } from '@axium/kino/components';
|
|
4
|
+
|
|
5
|
+
const { data } = $props();
|
|
6
|
+
|
|
7
|
+
const shows = $derived(data.shows.map(show => ({ ...show, type: 'tv' as const })));
|
|
8
|
+
</script>
|
|
9
|
+
|
|
10
|
+
<svelte:head>
|
|
11
|
+
<title>{text('page.kino.tv.title')}</title>
|
|
12
|
+
</svelte:head>
|
|
13
|
+
|
|
14
|
+
<h1>{text('page.kino.tv.title')}</h1>
|
|
15
|
+
|
|
16
|
+
<MediaGrid items={shows} empty={text('page.kino.tv.empty')} />
|
|
17
|
+
|
|
18
|
+
<style>
|
|
19
|
+
h1 {
|
|
20
|
+
margin: 0;
|
|
21
|
+
}
|
|
22
|
+
</style>
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { getUploadedShows } from '@axium/kino/client';
|
|
2
|
+
import { redirect } from '@sveltejs/kit';
|
|
3
|
+
|
|
4
|
+
export const ssr = false;
|
|
5
|
+
|
|
6
|
+
export async function load({ parent }) {
|
|
7
|
+
const { session } = await parent();
|
|
8
|
+
|
|
9
|
+
if (!session) redirect(307, '/login?after=/tv');
|
|
10
|
+
|
|
11
|
+
return { shows: await getUploadedShows() };
|
|
12
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { text } from '@axium/client';
|
|
3
|
+
import { MediaDetail, SeasonList } from '@axium/kino/components';
|
|
4
|
+
|
|
5
|
+
const { data } = $props();
|
|
6
|
+
|
|
7
|
+
const show = $derived(data.show);
|
|
8
|
+
</script>
|
|
9
|
+
|
|
10
|
+
<svelte:head>
|
|
11
|
+
<title>{show.name}</title>
|
|
12
|
+
</svelte:head>
|
|
13
|
+
|
|
14
|
+
<MediaDetail
|
|
15
|
+
title={show.name}
|
|
16
|
+
imagePath={show.poster_path}
|
|
17
|
+
backdropPath={show.backdrop_path}
|
|
18
|
+
date={show.first_air_date}
|
|
19
|
+
dateKind="aired"
|
|
20
|
+
overview={show.overview}
|
|
21
|
+
>
|
|
22
|
+
<section>
|
|
23
|
+
<h2>{text('kino.seasons')}</h2>
|
|
24
|
+
<SeasonList id={show.id} seasons={show.seasons} />
|
|
25
|
+
</section>
|
|
26
|
+
</MediaDetail>
|
|
27
|
+
|
|
28
|
+
<style>
|
|
29
|
+
section {
|
|
30
|
+
display: flex;
|
|
31
|
+
flex-direction: column;
|
|
32
|
+
gap: 0.75em;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
h2 {
|
|
36
|
+
margin: 0;
|
|
37
|
+
font-size: 1.15em;
|
|
38
|
+
}
|
|
39
|
+
</style>
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { text } from '@axium/client';
|
|
3
|
+
import { EpisodeList, MediaDetail } from '@axium/kino/components';
|
|
4
|
+
|
|
5
|
+
const { data } = $props();
|
|
6
|
+
|
|
7
|
+
const { show, season } = $derived(data);
|
|
8
|
+
</script>
|
|
9
|
+
|
|
10
|
+
<svelte:head>
|
|
11
|
+
<title>{show.name} — {season.name}</title>
|
|
12
|
+
</svelte:head>
|
|
13
|
+
|
|
14
|
+
<MediaDetail
|
|
15
|
+
title={season.name}
|
|
16
|
+
imagePath={season.poster_path}
|
|
17
|
+
backdropPath={show.backdrop_path}
|
|
18
|
+
date={season.air_date}
|
|
19
|
+
dateKind="aired"
|
|
20
|
+
overview={season.overview}
|
|
21
|
+
>
|
|
22
|
+
{#snippet context()}
|
|
23
|
+
<a href="/tv/{show.id}">{show.name}</a>
|
|
24
|
+
{/snippet}
|
|
25
|
+
|
|
26
|
+
<section>
|
|
27
|
+
<h2>{text('kino.episodes')}</h2>
|
|
28
|
+
<EpisodeList id={show.id} season={season.season_number} episodes={season.episodes} />
|
|
29
|
+
</section>
|
|
30
|
+
</MediaDetail>
|
|
31
|
+
|
|
32
|
+
<style>
|
|
33
|
+
section {
|
|
34
|
+
display: flex;
|
|
35
|
+
flex-direction: column;
|
|
36
|
+
gap: 0.75em;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
h2 {
|
|
40
|
+
margin: 0;
|
|
41
|
+
font-size: 1.15em;
|
|
42
|
+
}
|
|
43
|
+
</style>
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { getSeason, getTv } from '@axium/kino/client';
|
|
2
|
+
|
|
3
|
+
export const ssr = false;
|
|
4
|
+
|
|
5
|
+
export async function load({ params }) {
|
|
6
|
+
const id = Number(params.id);
|
|
7
|
+
|
|
8
|
+
const [show, season] = await Promise.all([getTv(id), getSeason(id, Number(params.season))]);
|
|
9
|
+
|
|
10
|
+
return { show, season };
|
|
11
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { text } from '@axium/client';
|
|
3
|
+
import { deleteEpisodeUpload, episodeDataURL } from '@axium/kino/client';
|
|
4
|
+
import { uploadEpisodeFile } from '@axium/kino/client/frontend';
|
|
5
|
+
import { MediaActions, MediaDetail } from '@axium/kino/components';
|
|
6
|
+
|
|
7
|
+
const { data } = $props();
|
|
8
|
+
|
|
9
|
+
const { show, season, episode } = $derived(data);
|
|
10
|
+
|
|
11
|
+
const code = $derived(text('kino.episode_code', { season, episode: episode.episode_number }));
|
|
12
|
+
|
|
13
|
+
let upload = $state(data.episode.upload);
|
|
14
|
+
</script>
|
|
15
|
+
|
|
16
|
+
<svelte:head>
|
|
17
|
+
<title>{show.name} — {code}</title>
|
|
18
|
+
</svelte:head>
|
|
19
|
+
|
|
20
|
+
<MediaDetail
|
|
21
|
+
title={episode.name}
|
|
22
|
+
imagePath={episode.still_path}
|
|
23
|
+
imageType="still"
|
|
24
|
+
backdropPath={show.backdrop_path}
|
|
25
|
+
date={episode.air_date}
|
|
26
|
+
dateKind="aired"
|
|
27
|
+
overview={null}
|
|
28
|
+
>
|
|
29
|
+
{#snippet context()}
|
|
30
|
+
<a href="/tv/{show.id}">{show.name}</a>
|
|
31
|
+
·
|
|
32
|
+
<a href="/tv/{show.id}/{season}">{code}</a>
|
|
33
|
+
{/snippet}
|
|
34
|
+
|
|
35
|
+
{#snippet actions()}
|
|
36
|
+
<MediaActions
|
|
37
|
+
bind:upload
|
|
38
|
+
watchHref="/tv/{show.id}/{season}/{episode.episode_number}/watch"
|
|
39
|
+
dataURL={episodeDataURL(show.id, season, episode.episode_number, true)}
|
|
40
|
+
uploadText={text('kino.upload_episode')}
|
|
41
|
+
canDelete={data.session?.user.isAdmin}
|
|
42
|
+
uploadFile={file => uploadEpisodeFile(file, show.id, season, episode.episode_number)}
|
|
43
|
+
deleteUpload={() => deleteEpisodeUpload(show.id, season, episode.episode_number)}
|
|
44
|
+
/>
|
|
45
|
+
{/snippet}
|
|
46
|
+
</MediaDetail>
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { getEpisode, getTv } from '@axium/kino/client';
|
|
2
|
+
|
|
3
|
+
export const ssr = false;
|
|
4
|
+
|
|
5
|
+
export async function load({ params }) {
|
|
6
|
+
const id = Number(params.id),
|
|
7
|
+
season = Number(params.season);
|
|
8
|
+
|
|
9
|
+
const [show, episode] = await Promise.all([getTv(id), getEpisode(id, season, Number(params.episode))]);
|
|
10
|
+
|
|
11
|
+
return { show, season, episode };
|
|
12
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { text } from '@axium/client';
|
|
3
|
+
import { Video } from '@axium/client/components';
|
|
4
|
+
import { MediaState } from '@axium/client/reactive';
|
|
5
|
+
import { episodeDataURL } from '@axium/kino/client';
|
|
6
|
+
import { trackWatch } from '@axium/kino/watch';
|
|
7
|
+
|
|
8
|
+
const { data } = $props();
|
|
9
|
+
|
|
10
|
+
const { show, season, episode, upload } = $derived(data);
|
|
11
|
+
|
|
12
|
+
const code = $derived(text('kino.episode_code', { season, episode: episode.episode_number }));
|
|
13
|
+
|
|
14
|
+
const media = new MediaState();
|
|
15
|
+
|
|
16
|
+
trackWatch({
|
|
17
|
+
media,
|
|
18
|
+
target: { type: 'tv', id: data.show.id, season: data.season, episode: data.episode.episode_number },
|
|
19
|
+
resumeFrom: data.view?.position,
|
|
20
|
+
});
|
|
21
|
+
</script>
|
|
22
|
+
|
|
23
|
+
<svelte:head>
|
|
24
|
+
<title>{text('page.kino.watch_title', { name: `${show.name} ${code}` })}</title>
|
|
25
|
+
</svelte:head>
|
|
26
|
+
|
|
27
|
+
<div class="watch">
|
|
28
|
+
<a class="subtle" href="/tv/{show.id}/{season}/{episode.episode_number}">{show.name} — {code} — {episode.name}</a>
|
|
29
|
+
<Video
|
|
30
|
+
{media}
|
|
31
|
+
src={episodeDataURL(show.id, season, episode.episode_number)}
|
|
32
|
+
size={upload.size}
|
|
33
|
+
type={upload.type}
|
|
34
|
+
name={episode.name}
|
|
35
|
+
/>
|
|
36
|
+
</div>
|
|
37
|
+
|
|
38
|
+
<style>
|
|
39
|
+
.watch {
|
|
40
|
+
display: flex;
|
|
41
|
+
flex-direction: column;
|
|
42
|
+
gap: 0.5em;
|
|
43
|
+
height: 80vh;
|
|
44
|
+
min-height: 0;
|
|
45
|
+
}
|
|
46
|
+
</style>
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { getEpisode, getTv, getViews } from '@axium/kino/client';
|
|
2
|
+
import { error } from '@sveltejs/kit';
|
|
3
|
+
|
|
4
|
+
export const ssr = false;
|
|
5
|
+
|
|
6
|
+
export async function load({ params }) {
|
|
7
|
+
const id = Number(params.id),
|
|
8
|
+
season = Number(params.season),
|
|
9
|
+
episodeNumber = Number(params.episode);
|
|
10
|
+
|
|
11
|
+
const [show, episode] = await Promise.all([getTv(id), getEpisode(id, season, episodeNumber)]);
|
|
12
|
+
|
|
13
|
+
if (!episode.upload) error(404, 'This episode has not been uploaded');
|
|
14
|
+
|
|
15
|
+
// Used to resume where the last session left off; not worth failing the page over
|
|
16
|
+
const views = await getViews().catch(() => []);
|
|
17
|
+
const view = views.find(
|
|
18
|
+
v => v.type == 'tv' && v.show.id == id && v.episode.season_number == season && v.episode.episode_number == episodeNumber
|
|
19
|
+
);
|
|
20
|
+
|
|
21
|
+
return { show, season, episode, upload: episode.upload, view };
|
|
22
|
+
}
|