diff --git a/src/lib/apis/jellyfin/jellyfinApi.ts b/src/lib/apis/jellyfin/jellyfinApi.ts index 7a14dfa..7729161 100644 --- a/src/lib/apis/jellyfin/jellyfinApi.ts +++ b/src/lib/apis/jellyfin/jellyfinApi.ts @@ -62,7 +62,7 @@ export const getJellyfinItems = async () => hasTmdbId: true, recursive: true, includeItemTypes: ['Movie', 'Series'], - fields: ['ProviderIds'] + fields: ['ProviderIds', 'Genres', 'DateLastMediaAdded', 'DateCreated'] } } }) @@ -285,7 +285,7 @@ export const getJellyfinUsers = async ( .then((res) => res.data || []) .catch(() => []); -export const getJellyfinPoster = (item: JellyfinItem, quality = 100) => +export const getJellyfinPosterUrl = (item: JellyfinItem, quality = 100) => item.ImageTags?.Primary ? `${get(settings).jellyfin.baseUrl}/Items/${item?.Id}/Images/Primary?quality=${quality}&tag=${ item?.ImageTags?.Primary diff --git a/src/lib/apis/radarr/radarrApi.ts b/src/lib/apis/radarr/radarrApi.ts index 52cd4c6..84a14f0 100644 --- a/src/lib/apis/radarr/radarrApi.ts +++ b/src/lib/apis/radarr/radarrApi.ts @@ -222,3 +222,9 @@ export const getRadarrQualityProfiles = async ( } ) .then((res) => res.data || []); + +export function getRadarrPosterUrl(item: RadarrMovie) { + return ( + get(settings).radarr.baseUrl + (item.images?.find((i) => i.coverType === 'poster')?.url || '') + ); +} diff --git a/src/lib/apis/sonarr/sonarrApi.ts b/src/lib/apis/sonarr/sonarrApi.ts index a33873f..99bfc07 100644 --- a/src/lib/apis/sonarr/sonarrApi.ts +++ b/src/lib/apis/sonarr/sonarrApi.ts @@ -306,3 +306,9 @@ export const getSonarrLanguageProfiles = async ( } ) .then((res) => res.data || []); + +export function getSonarrPosterUrl(item: SonarrSeries) { + return ( + get(settings).sonarr.baseUrl + (item.images?.find((i) => i.coverType === 'poster')?.url || '') + ); +} diff --git a/src/lib/apis/tmdb/tmdbApi.ts b/src/lib/apis/tmdb/tmdbApi.ts index b4421df..a54db89 100644 --- a/src/lib/apis/tmdb/tmdbApi.ts +++ b/src/lib/apis/tmdb/tmdbApi.ts @@ -75,11 +75,11 @@ export const getTmdbMovie = async (tmdbId: number) => } }).then((res) => res.data as TmdbMovieFull2 | undefined); -export const getTmdbSeriesFromTvdbId = async (tvdbId: number) => +export const getTmdbSeriesFromTvdbId = async (tvdbId: string) => TmdbApiOpen.get('/3/find/{external_id}', { params: { path: { - external_id: String(tvdbId) + external_id: tvdbId }, query: { external_source: 'tvdb_id' diff --git a/src/lib/components/Card/Card.svelte b/src/lib/components/Card/Card.svelte index 440ffed..6241a8c 100644 --- a/src/lib/components/Card/Card.svelte +++ b/src/lib/components/Card/Card.svelte @@ -2,8 +2,8 @@ import { createJellyfinItemStore, createRadarrMovieStore, - createSonarrItemStore - } from '$lib/stores/library.store'; + createSonarrSeriesStore + } from '$lib/stores/data.store'; import type { TitleType } from '$lib/types'; import { formatMinutesToTime } from '$lib/utils'; import classNames from 'classnames'; @@ -30,7 +30,7 @@ let jellyfinItemStore = createJellyfinItemStore(tmdbId); let radarrMovieStore = createRadarrMovieStore(tmdbId); - let sonarrSeriesStore = createSonarrItemStore(title); + let sonarrSeriesStore = createSonarrSeriesStore(title); diff --git a/src/lib/components/ContextMenu/LibraryItemContextItems.svelte b/src/lib/components/ContextMenu/LibraryItemContextItems.svelte index 0f285c8..5e0eea6 100644 --- a/src/lib/components/ContextMenu/LibraryItemContextItems.svelte +++ b/src/lib/components/ContextMenu/LibraryItemContextItems.svelte @@ -6,7 +6,7 @@ } from '$lib/apis/jellyfin/jellyfinApi'; import type { RadarrMovie } from '$lib/apis/radarr/radarrApi'; import type { SonarrSeries } from '$lib/apis/sonarr/sonarrApi'; - import { library } from '$lib/stores/library.store'; + import { jellyfinItemsStore } from '$lib/stores/data.store'; import { settings } from '$lib/stores/settings.store'; import type { TitleType } from '$lib/types'; import ContextMenuDivider from './ContextMenuDivider.svelte'; @@ -25,14 +25,14 @@ function handleSetWatched() { if (jellyfinItem?.Id) { watched = true; - setJellyfinItemWatched(jellyfinItem.Id).finally(() => library.refreshIn(3000)); + setJellyfinItemWatched(jellyfinItem.Id).finally(() => jellyfinItemsStore.refreshIn(3000)); } } function handleSetUnwatched() { if (jellyfinItem?.Id) { watched = false; - setJellyfinItemUnwatched(jellyfinItem.Id).finally(() => library.refreshIn(3000)); + setJellyfinItemUnwatched(jellyfinItem.Id).finally(() => jellyfinItemsStore.refreshIn(3000)); } } diff --git a/src/lib/components/EpisodeCard/EpisodeCard.svelte b/src/lib/components/EpisodeCard/EpisodeCard.svelte index b81abc6..df82a74 100644 --- a/src/lib/components/EpisodeCard/EpisodeCard.svelte +++ b/src/lib/components/EpisodeCard/EpisodeCard.svelte @@ -1,6 +1,6 @@
import { getDiskSpace } from '$lib/apis/radarr/radarrApi'; - import { library } from '$lib/stores/library.store'; + import { radarrMoviesStore } from '$lib/stores/data.store'; import { settings } from '$lib/stores/settings.store'; import { formatSize } from '$lib/utils.js'; import RadarrIcon from '../svgs/RadarrIcon.svelte'; @@ -11,24 +11,15 @@ async function fetchStats() { const discSpacePromise = getDiskSpace(); - const { itemsArray } = await $library; - const availableMovies = itemsArray.filter( - (item) => - !item.download && - item.radarrMovie && - item.radarrMovie.isAvailable && - item.radarrMovie.movieFile - ); + const radarrMovies = await radarrMoviesStore.promise; + const availableMovies = radarrMovies.filter((item) => item.isAvailable && item.movieFile); const diskSpaceInfo = (await discSpacePromise).find((disk) => disk.path === '/') || (await discSpacePromise)[0] || undefined; - const spaceOccupied = availableMovies.reduce( - (acc, movie) => acc + (movie.radarrMovie?.sizeOnDisk || 0), - 0 - ); + const spaceOccupied = availableMovies.reduce((acc, movie) => acc + (movie?.sizeOnDisk || 0), 0); return { moviesCount: availableMovies.length, diff --git a/src/lib/components/SourceStats/SonarrStats.svelte b/src/lib/components/SourceStats/SonarrStats.svelte index 64e2316..bec22ae 100644 --- a/src/lib/components/SourceStats/SonarrStats.svelte +++ b/src/lib/components/SourceStats/SonarrStats.svelte @@ -1,6 +1,6 @@ - - {#if noItems}
-
-
-
-
- -
-
-
- {#if downloadingProps.length || nextUpProps.length} - -
- {#if downloadingProps.length} - - {/if} - {#if nextUpProps.length} - + - {/if} + {$_('titleShowcase.details')} + +
- {#if downloadingProps.length && openNextUpTab === 'downloading'} - {#each downloadingProps as props (props.tmdbId)} - - {/each} - {:else if openNextUpTab === 'nextUp'} - {#each nextUpProps as props (props.tmdbId)} - - {/each} - {/if} - - {/if} +
+
-
+ {/await}
-
- -
- - - -
-
-
- -
- - {$_('library.sort.byTitle')} - - -
-
- - - -
-
- - {#if loading} -
- {#each [...Array(20).keys()] as index (index)} - - {/each} -
- {:else} -
- {#each { available: availableProps, watched: watchedProps, unavailable: unavailableProps }[openTab] as props (props.tmdbId)} - - {:else} -
No items.
- {/each} + {#if downloadProps?.length} +
+ + {#each downloadProps as props} + + {/each} +
{/if} + +
{/if} diff --git a/src/routes/library/LibraryItems.svelte b/src/routes/library/LibraryItems.svelte new file mode 100644 index 0000000..4b3bed2 --- /dev/null +++ b/src/routes/library/LibraryItems.svelte @@ -0,0 +1,216 @@ + + + + +
+ +
+ + + +
+
+
+ +
+ + {$_('library.sort.byTitle')} + + +
+
+ + + +
+
+ +
+ {#await posterPropsPromise} + {#each [...Array(20).keys()] as index (index)} + + {/each} + {:then props} + {#each props as prop} + + {:else} +
No items.
+ {/each} + {:catch error} +

{error.message}

+ {/await} +
+ +{#await posterPropsPromise then props} +
+ +
+{/await} diff --git a/src/routes/movie/[id]/MoviePage.svelte b/src/routes/movie/[id]/MoviePage.svelte index b4fa6c5..9a0d8a8 100644 --- a/src/routes/movie/[id]/MoviePage.svelte +++ b/src/routes/movie/[id]/MoviePage.svelte @@ -9,7 +9,6 @@ import Card from '$lib/components/Card/Card.svelte'; import { fetchCardTmdbProps } from '$lib/components/Card/card'; import CarouselPlaceholderItems from '$lib/components/Carousel/CarouselPlaceholderItems.svelte'; - import { modalStack } from '$lib/stores/modal.store'; import PeopleCard from '$lib/components/PeopleCard/PeopleCard.svelte'; import ProgressBar from '$lib/components/ProgressBar.svelte'; import RequestModal from '$lib/components/RequestModal/RequestModal.svelte'; @@ -18,11 +17,10 @@ import { playerState } from '$lib/components/VideoPlayer/VideoPlayer'; import { createJellyfinItemStore, - createLibraryItemStore, createRadarrDownloadStore, - createRadarrMovieStore, - library - } from '$lib/stores/library.store'; + createRadarrMovieStore + } from '$lib/stores/data.store'; + import { modalStack } from '$lib/stores/modal.store'; import { settings } from '$lib/stores/settings.store'; import { formatMinutesToTime, formatSize } from '$lib/utils'; import classNames from 'classnames'; @@ -64,7 +62,7 @@ } async function refreshRadarr() { - await $radarrMovieStore.refreshIn(); + await radarrMovieStore.refreshIn(); } let addToRadarrLoading = false; diff --git a/src/routes/series/[id]/+page.ts b/src/routes/series/[id]/+page.ts index 3232ff4..f512584 100644 --- a/src/routes/series/[id]/+page.ts +++ b/src/routes/series/[id]/+page.ts @@ -1,11 +1,25 @@ -import { getTmdbSeries } from '$lib/apis/tmdb/tmdbApi'; +import { getTmdbSeries, getTmdbSeriesFromTvdbId } from '$lib/apis/tmdb/tmdbApi'; +import { sonarrSeriesStore } from '$lib/stores/data.store'; import type { PageLoad } from './$types'; export const load = (async ({ params }) => { - const tmdbSeries = await getTmdbSeries(Number(params.id)); + const sonarrItem = await sonarrSeriesStore.promise.then((series) => + series.find((s) => s.tvdbId === Number(params.id)) + ); - return { - tmdbId: params.id, - name: tmdbSeries?.name - }; + if (sonarrItem) { + const tmdbSeries = await getTmdbSeriesFromTvdbId(params.id); + + return { + tmdbId: tmdbSeries?.id, + name: tmdbSeries?.name + }; + } else { + const tmdbSeries = await getTmdbSeries(Number(params.id)); + + return { + tmdbId: tmdbSeries?.id, + name: tmdbSeries?.name + }; + } }) satisfies PageLoad; diff --git a/src/routes/series/[id]/SeriesPage.svelte b/src/routes/series/[id]/SeriesPage.svelte index 0d84ed8..1517b69 100644 --- a/src/routes/series/[id]/SeriesPage.svelte +++ b/src/routes/series/[id]/SeriesPage.svelte @@ -23,9 +23,8 @@ import { createJellyfinItemStore, createSonarrDownloadStore, - createSonarrItemStore, - library - } from '$lib/stores/library.store'; + createSonarrSeriesStore + } from '$lib/stores/data.store'; import { modalStack } from '$lib/stores/modal.store'; import { settings } from '$lib/stores/settings.store'; import { capitalize, formatMinutesToTime, formatSize } from '$lib/utils'; @@ -39,8 +38,13 @@ export let handleCloseModal: () => void = () => {}; const tmdbUrl = 'https://www.themoviedb.org/tv/' + tmdbId; + const tmdbSeriesPromise = getTmdbSeries(tmdbId); + const tmdbSeasonsPromise = tmdbSeriesPromise.then((s) => + getTmdbSeriesSeasons(tmdbId, s?.number_of_seasons || 0) + ); + const jellyfinItemStore = createJellyfinItemStore(tmdbId); - const sonarrSeriesStore = createSonarrItemStore(title); + const sonarrSeriesStore = createSonarrSeriesStore(title); const sonarrDownloadStore = createSonarrDownloadStore(sonarrSeriesStore); let sonarrSeries = $sonarrSeriesStore.item; @@ -64,11 +68,6 @@ let episodeComponents: HTMLDivElement[] = []; let nextJellyfinEpisode: JellyfinItem | undefined = undefined; - const tmdbSeriesPromise = getTmdbSeries(tmdbId); - const tmdbSeasonsPromise = tmdbSeriesPromise.then((s) => - getTmdbSeriesSeasons(tmdbId, s?.number_of_seasons || 0) - ); - const tmdbRecommendationProps = getTmdbSeriesRecommendations(tmdbId).then((r) => Promise.all(r.map(fetchCardTmdbProps)) ); @@ -124,15 +123,15 @@ if (nextJellyfinEpisode?.Id) playerState.streamJellyfinId(nextJellyfinEpisode?.Id || ''); } - async function refresh() { - await library.refresh(tmdbId); + async function refreshSonarr() { + await sonarrSeriesStore.refreshIn(); } let addToSonarrLoading = false; function addToSonarr() { addToSonarrLoading = true; addSeriesToSonarr(tmdbId) - .then(refresh) + .then(refreshSonarr) .finally(() => (addToSonarrLoading = false)); } diff --git a/static/placeholder.jpg b/static/placeholder.jpg new file mode 100644 index 0000000..5452b96 Binary files /dev/null and b/static/placeholder.jpg differ