diff --git a/src/lib/apis/tmdb/tmdbApi.ts b/src/lib/apis/tmdb/tmdbApi.ts index d4ad83e..a234aa9 100644 --- a/src/lib/apis/tmdb/tmdbApi.ts +++ b/src/lib/apis/tmdb/tmdbApi.ts @@ -25,7 +25,7 @@ export interface TmdbMovieFull2 extends TmdbMovie2 { export interface TmdbSeriesFull2 extends TmdbSeries2 { videos: operations['tv-series-videos']['responses']['200']['content']['application/json']; - credits: operations['tv-series-credits']['responses']['200']['content']['application/json']; + aggregate_credits: operations['tv-series-credits']['responses']['200']['content']['application/json']; external_ids: operations['tv-series-external-ids']['responses']['200']['content']['application/json']; images: operations['tv-series-images']['responses']['200']['content']['application/json']; } @@ -75,7 +75,7 @@ export const getTmdbSeries = async (tmdbId: number): Promise }).then((res) => res.data); export const getTmdbSeriesBackdrop = async (tmdbId: number) => - getTmdbSeriesImages(tmdbId).then( - (r) => - ( - r?.backdrops?.find((b) => b.iso_639_1 === get(settings).language) || - r?.backdrops?.find((b) => b.iso_639_1 === 'en') || - r?.backdrops?.find((b) => b.iso_639_1) || - r?.backdrops?.[0] - )?.file_path - ); + getTmdbSeries(tmdbId) + .then((s) => s?.images) + .then( + (r) => + ( + r?.backdrops?.find((b) => b.iso_639_1 === get(settings).language) || + r?.backdrops?.find((b) => b.iso_639_1 === 'en') || + r?.backdrops?.find((b) => b.iso_639_1) || + r?.backdrops?.[0] + )?.file_path + ); export const getTmdbMovieImages = async (tmdbId: number) => await TmdbApiOpen.get('/3/movie/{movie_id}/images', { @@ -138,15 +140,17 @@ export const getTmdbMovieImages = async (tmdbId: number) => }).then((res) => res.data); export const getTmdbMovieBackdrop = async (tmdbId: number) => - getTmdbMovieImages(tmdbId).then( - (r) => - ( - r?.backdrops?.find((b) => b.iso_639_1 === get(settings).language) || - r?.backdrops?.find((b) => b.iso_639_1 === 'en') || - r?.backdrops?.find((b) => b.iso_639_1) || - r?.backdrops?.[0] - )?.file_path - ); + getTmdbMovie(tmdbId) + .then((m) => m?.images) + .then( + (r) => + ( + r?.backdrops?.find((b) => b.iso_639_1 === get(settings).language) || + r?.backdrops?.find((b) => b.iso_639_1 === 'en') || + r?.backdrops?.find((b) => b.iso_639_1) || + r?.backdrops?.[0] + )?.file_path + ); export const getTmdbPopularMovies = () => TmdbApiOpen.get('/3/movie/popular', { @@ -229,6 +233,33 @@ export const getTmdbGenreMovies = (genreId: number) => } }).then((res) => res.data?.results || []); +export const getTmdbSeriesRecommendations = (tmdbId: number) => + TmdbApiOpen.get('/3/tv/{series_id}/recommendations', { + params: { + path: { + series_id: tmdbId + } + } + }).then((res) => res.data?.results || []); + +export const getTmdbSeriesSimilar = (tmdbId: number) => + TmdbApiOpen.get('/3/tv/{series_id}/similar', { + params: { + path: { + series_id: String(tmdbId) + } + } + }).then((res) => res.data?.results || []); + +export const getTmdbSeriesCredits = (tmdbId: number) => + TmdbApiOpen.get('/3/tv/{series_id}/credits', { + params: { + path: { + series_id: tmdbId + } + } + }).then((res) => res.data?.cast || []); + // Deprecated hereon forward /** @deprecated */ diff --git a/src/lib/components/Button.svelte b/src/lib/components/Button.svelte index 8f98b02..ea9dcc0 100644 --- a/src/lib/components/Button.svelte +++ b/src/lib/components/Button.svelte @@ -12,31 +12,18 @@ export let target: string | undefined = '_self'; let buttonStyle: string; - // $: buttonStyle = classNames( - // 'border-2 border-white transition-all uppercase tracking-widest text-xs whitespace-nowrap', - // { - // 'bg-white text-zinc-900 font-extrabold': type === 'primary', - // 'hover:bg-amber-400 hover:border-amber-400': type === 'primary' && !disabled, - // 'font-semibold': type === 'secondary', - // 'hover:bg-white hover:text-black': type === 'secondary' && !disabled, - // 'px-8 py-3.5': size === 'lg', - // 'px-6 py-2.5': size === 'md', - // 'px-5 py-2': size === 'sm', - // 'opacity-70': disabled, - // 'cursor-pointer': !disabled - // } - // ); $: buttonStyle = classNames( - 'flex items-center gap-1 py-3 px-6 rounded-xl font-medium select-none cursor-pointer selectable transition-all backdrop-blur-lg', + 'flex items-center gap-1 rounded-xl font-medium select-none cursor-pointer selectable transition-all flex-shrink-0', { - 'bg-white text-zinc-900 font-extrabold': type === 'primary', + 'bg-white text-zinc-900 font-extrabold backdrop-blur-lg': type === 'primary', 'hover:bg-amber-400 hover:border-amber-400': type === 'primary' && !disabled, - 'text-zinc-200 bg-stone-800 bg-opacity-30': type === 'secondary', + 'text-zinc-200 bg-zinc-500 bg-opacity-30 backdrop-blur-lg': type === 'secondary', 'focus-visible:bg-zinc-200 focus-visible:text-zinc-800 hover:bg-zinc-200 hover:text-zinc-800': - type === 'secondary' && !disabled, + (type === 'secondary' || type === 'tertiary') && !disabled, + 'rounded-full': type === 'tertiary', 'py-3 px-6': size === 'lg', - // 'py-3 px-6': size === 'md', - 'px-5 py-2': size === 'sm', + 'py-2 px-6': size === 'md', + 'py-1 px-3': size === 'sm', 'opacity-50': disabled, 'cursor-pointer': !disabled } diff --git a/src/lib/components/Card/card.ts b/src/lib/components/Card/card.ts index a02c7d7..1031742 100644 --- a/src/lib/components/Card/card.ts +++ b/src/lib/components/Card/card.ts @@ -1,12 +1,5 @@ -import type { RadarrMovie } from '$lib/apis/radarr/radarrApi'; -import { - fetchTmdbMovieImages, - getTmdbMovieBackdrop, - getTmdbMovieImages, - getTmdbSeriesBackdrop, - getTmdbSeriesImages -} from '$lib/apis/tmdb/tmdbApi'; -import type { TmdbMovie, TmdbMovie2, TmdbSeries2 } from '$lib/apis/tmdb/tmdbApi'; +import type { TmdbMovie2, TmdbSeries2 } from '$lib/apis/tmdb/tmdbApi'; +import { getTmdbMovieBackdrop, getTmdbSeriesBackdrop } from '$lib/apis/tmdb/tmdbApi'; import type { ComponentProps } from 'svelte'; import type Card from './Card.svelte'; diff --git a/src/lib/components/Carousel/Carousel.svelte b/src/lib/components/Carousel/Carousel.svelte index c377efe..1905dea 100644 --- a/src/lib/components/Carousel/Carousel.svelte +++ b/src/lib/components/Carousel/Carousel.svelte @@ -40,13 +40,13 @@ {#if scrollX > 50}
{/if} {#if carousel && scrollX < carousel?.scrollWidth - carousel?.clientWidth - 50}
{/if}
diff --git a/src/lib/components/EpisodeCard/EpisodeCard.svelte b/src/lib/components/EpisodeCard/EpisodeCard.svelte index f846786..8b872d1 100644 --- a/src/lib/components/EpisodeCard/EpisodeCard.svelte +++ b/src/lib/components/EpisodeCard/EpisodeCard.svelte @@ -1,9 +1,9 @@ -
@@ -62,14 +60,14 @@
-
+
-
+
{#if subtitle}
{subtitle}
{/if} {#if title} -
+
{title}
{/if} @@ -94,4 +92,4 @@
{/if} -
+ diff --git a/src/lib/components/Modal/Modal.svelte b/src/lib/components/Modal/Modal.svelte index a99cc75..3d0a0ac 100644 --- a/src/lib/components/Modal/Modal.svelte +++ b/src/lib/components/Modal/Modal.svelte @@ -1,6 +1,5 @@ -->
-
+

{department}

-

{name}

+

+ {name} +

, 'modalProps'> | undefined = undefined; @@ -64,21 +67,23 @@ - +
{#each [...Array(seasons).keys()].map((i) => i + 1) as seasonNumber}
-
+
Season {seasonNumber}
- selectSeasonPack(seasonNumber)} - >Season Packs - selectSeason(seasonNumber)}>Episodes + +
{/each} diff --git a/src/lib/components/ResourceDetails/ResourceDetails.svelte b/src/lib/components/ResourceDetails/ResourceDetails.svelte index 3f0dd52..1ed1122 100644 --- a/src/lib/components/ResourceDetails/ResourceDetails.svelte +++ b/src/lib/components/ResourceDetails/ResourceDetails.svelte @@ -283,7 +283,7 @@
-->
- diff --git a/src/routes/discover/+page.svelte b/src/routes/discover/+page.svelte index a3abe8a..1e800ff 100644 --- a/src/routes/discover/+page.svelte +++ b/src/routes/discover/+page.svelte @@ -67,7 +67,8 @@ name: actor.name || '', backdropUri: actor.profile_path || '', knownFor: actor.known_for?.map((movie) => movie.title || '') || [], - department: actor.known_for_department || '' + department: actor.known_for_department || '', + size: 'lg' })) ); diff --git a/src/routes/series/[id]/+page.svelte b/src/routes/series/[id]/+page.svelte index 9747b2c..4c024df 100644 --- a/src/routes/series/[id]/+page.svelte +++ b/src/routes/series/[id]/+page.svelte @@ -1,217 +1,13 @@ -
- -{#await tmdbSeriesPromise.series then series} -
-
-
-
- -
-
- -
- {#each [...Array(series?.number_of_seasons || 0).keys()].map((i) => i + 1) as seasonNumber} - {@const season = series?.seasons?.find((s) => s.season_number === seasonNumber)} - {@const isSelected = season?.season_number === visibleSeason} - - {/each} -
- {#each episodeProps[visibleSeason] || [] as props} -
- - -
- {:else} - - {/each} -
-
-
- -
-
-
-

{series?.tagline || series?.name}

-

{series?.overview}

-
-
-
-
-

Created By

-

{series?.created_by?.map((c) => c.name).join(', ')}

-
- {#if series?.first_air_date} -
-

First Air Date

-

- {new Date(series?.first_air_date).toLocaleDateString('en', { - year: 'numeric', - month: 'short', - day: 'numeric' - })} -

-
- {/if} - {#if series?.next_episode_to_air} -
-

Next Air Date

-

- {new Date(series.next_episode_to_air?.air_date).toLocaleDateString('en', { - year: 'numeric', - month: 'short', - day: 'numeric' - })} -

-
- {:else if series?.last_air_date} -
-

Last Air Date

-

- {new Date(series.last_air_date).toLocaleDateString('en', { - year: 'numeric', - month: 'short', - day: 'numeric' - })} -

-
- {/if} -
-

Networks

-

{series?.networks?.map((n) => n.name).join(', ')}

-
-
-

Episode Run Time

-

{series?.episode_run_time} Minutes

-
-
-

Spoken Languages

-

- {series?.spoken_languages?.map((l) => capitalize(l.name || '')).join(', ')} -

-
-
-
-
-
-

No sources found

-

Check your source settings

-
- -
- -
Cast & Crew
- -
- -
Recommended
- -
- -
Similar Titles
- -
-{/await} +{#key tmdbId} + +{/key} diff --git a/src/routes/series/[id]/SeriesPage.svelte b/src/routes/series/[id]/SeriesPage.svelte new file mode 100644 index 0000000..c61d0e8 --- /dev/null +++ b/src/routes/series/[id]/SeriesPage.svelte @@ -0,0 +1,428 @@ + + +{#await tmdbSeriesPromise then series} +
+
+
+
+ +
+ + + {#each [...Array(series?.number_of_seasons || 0).keys()].map((i) => i + 1) as seasonNumber} + {@const season = series?.seasons?.find((s) => s.season_number === seasonNumber)} + {@const isSelected = season?.season_number === (visibleSeasonNumber || 1)} + + {/each} + + {#key visibleSeasonNumber} + {#each episodeProps[visibleSeasonNumber || 1] || [] as props, i} +
+ (visibleEpisodeNumber = i)} /> +
+ {:else} + + {/each} + {/key} +
+
+
+ +
+
+ {#if visibleEpisodeNumber !== undefined} + {#await tmdbSeasonsPromise.then((season) => season?.[visibleSeasonNumber ? visibleSeasonNumber - 1 : 0]?.episodes?.[visibleEpisodeNumber || 0]) then episode} +
+ +

+ {episode?.name || 'Episode ' + episode?.episode_number} +

+

{episode?.overview}

+
+ {/await} + {:else} +
+
+

{series?.tagline || series?.name}

+ +
+

{series?.overview}

+
+ +
+

Created By

+

{series?.created_by?.map((c) => c.name).join(', ')}

+
+ {#if series?.first_air_date} +
+

First Air Date

+

+ {new Date(series?.first_air_date).toLocaleDateString('en', { + year: 'numeric', + month: 'short', + day: 'numeric' + })} +

+
+ {/if} + {#if series?.next_episode_to_air} +
+

Next Air Date

+

+ {new Date(series.next_episode_to_air?.air_date).toLocaleDateString('en', { + year: 'numeric', + month: 'short', + day: 'numeric' + })} +

+
+ {:else if series?.last_air_date} +
+

Last Air Date

+

+ {new Date(series.last_air_date).toLocaleDateString('en', { + year: 'numeric', + month: 'short', + day: 'numeric' + })} +

+
+ {/if} +
+

Networks

+

{series?.networks?.map((n) => n.name).join(', ')}

+
+
+

Episode Run Time

+

{series?.episode_run_time} Minutes

+
+
+

Spoken Languages

+

+ {series?.spoken_languages?.map((l) => capitalize(l.name || '')).join(', ')} +

+
+ + {#if $itemStore.loading} +
+
+
+
+ {:else} + {@const item = $itemStore.item} + {#if !!item?.sonarrSeries} + {#if item.sonarrSeries.statistics?.episodeFileCount} +
+

Available

+

+ {item.sonarrSeries.statistics?.episodeFileCount || 0} Episodes +

+
+ {/if} + {#if item.sonarrSeries.statistics?.sizeOnDisk} +
+

Size On Disk

+

+ {formatSize(item.sonarrSeries.statistics?.sizeOnDisk || 0)} +

+
+ {/if} + {#if $itemStore.item?.download} +
+

Download Completed In

+

+ {formatMinutesToTime( + (new Date($itemStore.item?.download.completionTime).getTime() - Date.now()) / + 1000 / + 60 + )} +

+
+ {/if} + +
+ + +
+ {:else} + + {/if} + {/if} + {/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 Series
+ {#await tmdbSimilarProps} + + {:then props} + {#each props as prop} + + {/each} + {/await} +
+
+
+{/await} + +{#if requestModalVisible} + {@const sonarrSeries = $itemStore.item?.sonarrSeries} + {#if sonarrSeries && sonarrSeries.id && sonarrSeries?.statistics?.seasonCount} + + {/if} +{/if} diff --git a/static/radarr.svg b/static/radarr.svg new file mode 100644 index 0000000..eb052e2 --- /dev/null +++ b/static/radarr.svg @@ -0,0 +1,9 @@ + diff --git a/static/sonarr.svg b/static/sonarr.svg new file mode 100644 index 0000000..13c47a8 --- /dev/null +++ b/static/sonarr.svg @@ -0,0 +1,33 @@ + diff --git a/static/tmdb.svg b/static/tmdb.svg new file mode 100644 index 0000000..42f31f1 --- /dev/null +++ b/static/tmdb.svg @@ -0,0 +1 @@ +Asset 2 \ No newline at end of file