diff --git a/src/lib/apis/tmdb/tmdbApi.ts b/src/lib/apis/tmdb/tmdbApi.ts
index a234aa9..0f8f6ec 100644
--- a/src/lib/apis/tmdb/tmdbApi.ts
+++ b/src/lib/apis/tmdb/tmdbApi.ts
@@ -260,6 +260,24 @@ export const getTmdbSeriesCredits = (tmdbId: number) =>
}
}).then((res) => res.data?.cast || []);
+export const getTmdbMovieRecommendations = (tmdbId: number) =>
+ TmdbApiOpen.get('/3/movie/{movie_id}/recommendations', {
+ params: {
+ path: {
+ movie_id: tmdbId
+ }
+ }
+ }).then((res) => res.data?.results || []);
+
+export const getTmdbMovieSimilar = (tmdbId: number) =>
+ TmdbApiOpen.get('/3/movie/{movie_id}/similar', {
+ params: {
+ path: {
+ movie_id: tmdbId
+ }
+ }
+ }).then((res) => res.data?.results || []);
+
// Deprecated hereon forward
/** @deprecated */
diff --git a/src/lib/components/DetailsPage/DetailsPage.svelte b/src/lib/components/DetailsPage/DetailsPage.svelte
new file mode 100644
index 0000000..8a3cc4d
--- /dev/null
+++ b/src/lib/components/DetailsPage/DetailsPage.svelte
@@ -0,0 +1,127 @@
+
+
+
+
+
+
+
+
+
+
{tagline}
+
+
+
{overview}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/lib/components/Modal/Modal.ts b/src/lib/components/Modal/Modal.ts
index 99c75a0..0a99d9a 100644
--- a/src/lib/components/Modal/Modal.ts
+++ b/src/lib/components/Modal/Modal.ts
@@ -48,7 +48,7 @@ export function createModalProps(onClose: () => void, onBack?: () => void) {
return {
close,
- back,
+ back: onBack ? back : undefined,
id
};
}
diff --git a/src/routes/movie/[id]/+page.server.ts b/src/routes/movie/[id]/+page.server.ts
deleted file mode 100644
index 079482e..0000000
--- a/src/routes/movie/[id]/+page.server.ts
+++ /dev/null
@@ -1,8 +0,0 @@
-import { getTmdbMovie } from '$lib/apis/tmdb/tmdbApi';
-import type { PageServerLoad } from './$types';
-
-export const load = (async ({ params }) => {
- return {
- movie: await getTmdbMovie(Number(params.id))
- };
-}) satisfies PageServerLoad;
diff --git a/src/routes/movie/[id]/+page.svelte b/src/routes/movie/[id]/+page.svelte
index bf1b664..993aa6c 100644
--- a/src/routes/movie/[id]/+page.svelte
+++ b/src/routes/movie/[id]/+page.svelte
@@ -1,28 +1,13 @@
-{#await $library then libraryData}
- {#if data.movie}
- {@const movie = data.movie}
- g.name || '') || []}
- runtime={movie?.runtime || 0}
- tmdbRating={movie?.vote_average || 0}
- starring={movie?.credits?.cast?.slice(0, 5)}
- videos={movie.videos?.results || []}
- backdropPath={movie?.backdrop_path || ''}
- showDetails={true}
- jellyfinId={libraryData.items[movie.id]?.jellyfinId}
- />
- {/if}
-{/await}
+{#key tmdbId}
+
+{/key}
diff --git a/src/routes/movie/[id]/+page.ts b/src/routes/movie/[id]/+page.ts
new file mode 100644
index 0000000..7f19f60
--- /dev/null
+++ b/src/routes/movie/[id]/+page.ts
@@ -0,0 +1,7 @@
+import type { PageLoad } from './$types';
+
+export const load = (async ({ params }) => {
+ return {
+ tmdbId: params.id
+ };
+}) satisfies PageLoad;
diff --git a/src/routes/movie/[id]/+server.ts b/src/routes/movie/[id]/+server.ts
deleted file mode 100644
index cb2c6b1..0000000
--- a/src/routes/movie/[id]/+server.ts
+++ /dev/null
@@ -1,39 +0,0 @@
-import { error, json } from '@sveltejs/kit';
-import type { RequestHandler } from '@sveltejs/kit';
-import { getJellyfinItemByTmdbId } from '$lib/apis/jellyfin/jellyfinApi';
-import { getRadarrMovieByTmdbId, getRadarrDownloadsById } from '$lib/apis/radarr/radarrApi';
-
-export const _parseMovieId = (params: any) => {
- const { id: tmdbId } = params;
-
- if (!tmdbId) throw error(400, 'NO_TMDB_ID');
-
- return tmdbId;
-};
-
-export const GET = (async ({ params }) => {
- const tmdbId = _parseMovieId(params);
-
- const jellyfinMoviePromise = getJellyfinItemByTmdbId(tmdbId);
- const radarrMoviePromise = getRadarrMovieByTmdbId(tmdbId);
- const radarrMovieQueuedPromise = radarrMoviePromise.then((movie) =>
- movie?.id ? getRadarrDownloadsById(movie.id) : undefined
- );
-
- const [jellyfinItem, radarrMovie, radarrDownloads] = await Promise.all([
- jellyfinMoviePromise,
- radarrMoviePromise,
- radarrMovieQueuedPromise
- ]);
-
- return json({
- canStream: !!jellyfinItem,
- hasLocalFiles: radarrMovie?.hasFile || !!jellyfinItem,
- isAdded: !!radarrMovie,
- isDownloading: !!radarrDownloads?.length,
-
- jellyfinItem,
- radarrMovie,
- radarrDownloads
- });
-}) satisfies RequestHandler;
diff --git a/src/routes/movie/[id]/MoviePage.svelte b/src/routes/movie/[id]/MoviePage.svelte
new file mode 100644
index 0000000..f9911b1
--- /dev/null
+++ b/src/routes/movie/[id]/MoviePage.svelte
@@ -0,0 +1,306 @@
+
+
+{#await tmdbMoviePromise then movie}
+
+ {new Date(movie?.release_date || Date.now()).getFullYear()}
+ {movie?.runtime} min
+ {movie?.vote_average?.toFixed(1)} TMDB
+
+ {@const progress = $itemStore.item?.continueWatching?.progress}
+ {#if progress}
+
+ {/if}
+
+
+
+ {#if $itemStore.loading}
+
+ {:else if $itemStore.item?.jellyfinItem}
+
+ {:else if !$itemStore.item?.radarrMovie}
+
+ {:else}
+
+ {/if}
+
+
+
+
+
Directed By
+
+ {movie?.credits.crew?.filter((c) => c.job == 'Director').map((p) => p.name)}
+
+
+
+
Release Date
+
+ {new Date(movie?.release_date || Date.now()).toLocaleDateString('en', {
+ year: 'numeric',
+ month: 'short',
+ day: 'numeric'
+ })}
+
+
+ {#if movie?.budget}
+
+
Budget
+
+ {movie?.budget?.toLocaleString('en-US', {
+ style: 'currency',
+ currency: 'USD'
+ })}
+
+
+ {/if}
+ {#if movie?.revenue}
+
+
Revenue
+
+ {movie?.revenue?.toLocaleString('en-US', {
+ style: 'currency',
+ currency: 'USD'
+ })}
+
+
+ {/if}
+
+
Status
+
+ {movie?.status}
+
+
+
+
Runtime
+
+ {movie?.runtime} Minutes
+
+
+
+
+
+
+ {#if !$itemStore.loading && $itemStore.item}
+ {@const item = $itemStore.item}
+ {#if item.radarrMovie?.movieFile?.quality}
+
+
Video
+
+ {item.radarrMovie?.movieFile?.quality.quality?.name}
+
+
+ {/if}
+ {#if item.radarrMovie?.movieFile?.size}
+
+
Size On Disk
+
+ {formatSize(item.radarrMovie?.movieFile?.size || 0)}
+
+
+ {/if}
+ {#if $itemStore.item?.download}
+
+
Download Completed In
+
+ {formatMinutesToTime(
+ (new Date($itemStore.item?.download.completionTime).getTime() - Date.now()) /
+ 1000 /
+ 60
+ )}
+
+
+ {/if}
+
+
+
+
+
+
+ {:else if $itemStore.loading}
+
+ {/if}
+
+
+ Cast & Crew
+
+ {#await castProps}
+
+ {:then props}
+ {#each props as prop}
+
+ {/each}
+ {/await}
+
+
+ Recommendations
+
+ {#await tmdbRecommendationProps}
+
+ {:then props}
+ {#each props as prop}
+
+ {/each}
+ {/await}
+
+
+ Similar Titles
+
+ {#await tmdbSimilarProps}
+
+ {:then props}
+ {#each props as prop}
+
+ {/each}
+ {/await}
+
+
+{/await}
+
+{#if requestModalVisible}
+ {@const radarrMovie = $itemStore.item?.radarrMovie}
+ {#if radarrMovie && radarrMovie.id && radarrMovie?.movieFile}
+
+ {/if}
+{/if}
diff --git a/src/routes/movie/[id]/file/+server.ts b/src/routes/movie/[id]/file/+server.ts
deleted file mode 100644
index e9b8067..0000000
--- a/src/routes/movie/[id]/file/+server.ts
+++ /dev/null
@@ -1,13 +0,0 @@
-import type { RequestHandler } from '@sveltejs/kit';
-import { json } from '@sveltejs/kit';
-import { _parseMovieId } from '../+server';
-import { addMovieToRadarr, deleteRadarrMovie } from '$lib/apis/radarr/radarrApi';
-
-// Delete download
-export const DELETE = (async ({ params }) => {
- const radarrMovieId = _parseMovieId(params);
-
- const success = await deleteRadarrMovie(radarrMovieId);
-
- return json({ success });
-}) satisfies RequestHandler;
diff --git a/src/routes/movie/[id]/radarr/+server.ts b/src/routes/movie/[id]/radarr/+server.ts
deleted file mode 100644
index 912189c..0000000
--- a/src/routes/movie/[id]/radarr/+server.ts
+++ /dev/null
@@ -1,21 +0,0 @@
-import { _parseMovieId } from '../+server';
-import { addMovieToRadarr, getRadarrMovieByTmdbId } from '$lib/apis/radarr/radarrApi';
-import { json } from '@sveltejs/kit';
-import type { RequestHandler } from '@sveltejs/kit';
-
-// Add to radarr
-export const POST = (async ({ params }) => {
- const tmdbId = _parseMovieId(params);
-
- const response = await addMovieToRadarr(Number(tmdbId));
-
- return json(response);
-}) satisfies RequestHandler;
-
-export const GET = (async ({ params }) => {
- const tmdbId = _parseMovieId(params);
-
- const response = await getRadarrMovieByTmdbId(tmdbId);
-
- return json(response);
-}) satisfies RequestHandler;
diff --git a/src/routes/movie/[id]/releases/+server.ts b/src/routes/movie/[id]/releases/+server.ts
deleted file mode 100644
index 8494675..0000000
--- a/src/routes/movie/[id]/releases/+server.ts
+++ /dev/null
@@ -1,50 +0,0 @@
-import type { RequestHandler } from '@sveltejs/kit';
-import { json } from '@sveltejs/kit';
-import { _parseMovieId } from '../+server';
-import {
- cancelDownloadRadarrMovie,
- addMovieToRadarr,
- fetchRadarrReleases,
- downloadRadarrMovie
-} from '$lib/apis/radarr/radarrApi';
-
-export const GET = (async ({ params }) => {
- const radarrId = _parseMovieId(params);
-
- const releases: any[] = (await fetchRadarrReleases(radarrId)) || [];
-
- let filtered = releases.slice();
-
- filtered.sort((a, b) => b.seeders - a.seeders);
- filtered = filtered.filter((release) => release.quality.quality.resolution > 720).slice(0, 5);
-
- const releasesSkipped = releases.length - filtered.length;
-
- releases.sort((a, b) => b.size - a.size);
- filtered.sort((a, b) => b.size - a.size);
-
- return json({
- filtered,
- releasesSkipped,
- allReleases: releases
- });
-}) satisfies RequestHandler;
-
-// Download movie
-export const POST = (async ({ params, request }) => {
- const body = await request.json();
-
- if (!body.guid) throw new Error('NO_GUID');
-
- const response = await downloadRadarrMovie(body.guid);
-
- return json(response);
-}) satisfies RequestHandler;
-
-export const DELETE = (async ({ params }) => {
- const downloadId = _parseMovieId(params);
-
- const success = await cancelDownloadRadarrMovie(downloadId);
-
- return json({ success });
-}) satisfies RequestHandler;
diff --git a/src/routes/series/[id]/SeriesPage.svelte b/src/routes/series/[id]/SeriesPage.svelte
index 5d72df2..dcfd359 100644
--- a/src/routes/series/[id]/SeriesPage.svelte
+++ b/src/routes/series/[id]/SeriesPage.svelte
@@ -23,7 +23,7 @@
import { capitalize, formatMinutesToTime, formatSize } from '$lib/utils';
import classNames from 'classnames';
import { Archive, ChevronLeft, ChevronRight, DotFilled, Plus } from 'radix-icons-svelte';
- import { tick, type ComponentProps, onMount } from 'svelte';
+ import type { ComponentProps } from 'svelte';
import { fade } from 'svelte/transition';
export let tmdbId: number;
@@ -35,7 +35,10 @@
let visibleEpisodeIndex: number | undefined = undefined;
let requestModalVisible = false;
- const requestModalProps = createModalProps(() => (requestModalVisible = false));
+ const requestModalProps = createModalProps(
+ () => (requestModalVisible = false),
+ () => {}
+ );
let episodeProps: ComponentProps[][] = [];
let episodeComponents: HTMLDivElement[] = [];
@@ -104,12 +107,12 @@
await library.refresh();
}
- let addToRadarrLoading = false;
+ let addToSonarrLoading = false;
function addToSonarr() {
- addToRadarrLoading = true;
+ addToSonarrLoading = true;
addSeriesToSonarr(tmdbId)
.then(refresh)
- .finally(() => (addToRadarrLoading = false));
+ .finally(() => (addToSonarrLoading = false));
}
let didFocusNextEpisode = false;
@@ -174,7 +177,7 @@
Next Episode
{:else if !$itemStore.item?.sonarrSeries}
-