diff --git a/src/lib/apis/sonarr/sonarrApi.ts b/src/lib/apis/sonarr/sonarrApi.ts index b73860f..58088a6 100644 --- a/src/lib/apis/sonarr/sonarrApi.ts +++ b/src/lib/apis/sonarr/sonarrApi.ts @@ -1,9 +1,8 @@ -import createClient from 'openapi-fetch'; -import type { components, paths } from '$lib/apis/sonarr/sonarr.generated'; import { PUBLIC_SONARR_API_KEY, PUBLIC_SONARR_BASE_URL } from '$env/static/public'; -import type { SeriesResource } from '$lib/types'; -import { getTmdbMovie, getTmdbSeries } from '../tmdb/tmdbApi'; +import type { components, paths } from '$lib/apis/sonarr/sonarr.generated'; import { log } from '$lib/utils'; +import createClient from 'openapi-fetch'; +import { getTmdbSeries } from '../tmdb/tmdbApi'; export type SonarrSeries = components['schemas']['SeriesResource']; // export type MovieFileResource = components['schemas']['MovieFileResource']; @@ -45,12 +44,12 @@ export const SonarrApi = createClient({ } }); -export const getSonarrSeries = (): Promise => +export const getSonarrSeries = (): Promise => SonarrApi.get('/api/v3/series', { params: {} }).then((r) => r.data || []); -export const getSonarrSeriesByTvdbId = (tvdbId: number): Promise => +export const getSonarrSeriesByTvdbId = (tvdbId: number): Promise => SonarrApi.get('/api/v3/series', { params: { query: { diff --git a/src/lib/components/Card/Card.svelte b/src/lib/components/Card/Card.svelte index 08c4712..1505e54 100644 --- a/src/lib/components/Card/Card.svelte +++ b/src/lib/components/Card/Card.svelte @@ -7,11 +7,14 @@ import ContextMenuItem from '../ContextMenu/ContextMenuItem.svelte'; import { setJellyfinItemUnwatched, setJellyfinItemWatched } from '$lib/apis/jellyfin/jellyfinApi'; import { library } from '$lib/stores/library.store'; - import { titlePageModal } from '../TitlePageLayout/TitlePageModal'; + + import { modalStack, openTitleModal } from '../Modal/Modal'; + import TitlePageModal from '../TitlePageLayout/TitlePageModal.svelte'; + import type { TitleType } from '$lib/types'; export let tmdbId: number; export let jellyfinId: string | undefined = undefined; - export let type: 'movie' | 'series' = 'movie'; + export let type: TitleType = 'movie'; export let title: string; export let genres: string[] = []; export let runtimeMinutes = 0; @@ -23,6 +26,7 @@ export let available = true; export let progress = 0; export let size: 'dynamic' | 'md' | 'lg' = 'md'; + export let openInModal = true; let watched = false; $: watched = !available && !!jellyfinId; @@ -58,7 +62,13 @@ 'w-full': size === 'dynamic' } )} - on:click={() => titlePageModal.set(tmdbId, type)} + on:click={() => { + if (openInModal) { + openTitleModal(tmdbId, type); + } else { + window.location.href = `/${type}/${tmdbId}`; + } + }} >
+ import classNames from 'classnames'; + import { modalStack } from './Modal'; + import { fade } from 'svelte/transition'; + import { onDestroy } from 'svelte'; + + function handleShortcuts(event: KeyboardEvent) { + if (event.key === 'Escape' && $modalStack.top) { + modalStack.close($modalStack.top.id); + } + } + + onDestroy(() => { + modalStack.reset(); + }); + + + + + + {#if $modalStack.top} + + {/if} + + +{#each $modalStack.stack || [] as modal (modal.id)} + {@const hidden = $modalStack.top?.group === modal.group && $modalStack.top?.id !== modal.id} + + {#if modal.group === modal.id} +
+ {/if} + + + + +{/each} diff --git a/src/lib/components/Modal/Modal.svelte b/src/lib/components/Modal/Modal.svelte deleted file mode 100644 index 3d0a0ac..0000000 --- a/src/lib/components/Modal/Modal.svelte +++ /dev/null @@ -1,40 +0,0 @@ - - - - - {#if $modalStack.top} - - {/if} - - -{#if $modalStack.top === id} - - -
- -
-{/if} diff --git a/src/lib/components/Modal/Modal.ts b/src/lib/components/Modal/Modal.ts index ef4ad3c..eb32aab 100644 --- a/src/lib/components/Modal/Modal.ts +++ b/src/lib/components/Modal/Modal.ts @@ -1,56 +1,71 @@ +import type { TitleType } from '$lib/types'; import { writable } from 'svelte/store'; +import TitlePageModal from '../TitlePageLayout/TitlePageModal.svelte'; -function createModalStack() { - const store = writable<{ stack: Symbol[]; top: Symbol | undefined }>({ +type ModalItem = { + id: Symbol; + group: Symbol; + component: ConstructorOfATypedSvelteComponent; + props: Record; +}; +function createDynamicModalStack() { + const store = writable<{ stack: ModalItem[]; top: ModalItem | undefined }>({ stack: [], top: undefined }); store.subscribe(console.log); + function close(symbol: Symbol) { + store.update((s) => { + s.stack = s.stack.filter((i) => i.id !== symbol); + s.top = s.stack[s.stack.length - 1]; + return s; + }); + } + + function closeGroup(group: Symbol) { + store.update((s) => { + s.stack = s.stack.filter((i) => i.group !== group); + s.top = s.stack[s.stack.length - 1]; + return s; + }); + } + + function create( + component: ConstructorOfATypedSvelteComponent, + props: Record, + group: Symbol | undefined = undefined + ) { + const id = Symbol(); + const item = { id, component, props, group: group || id }; + store.update((s) => { + s.stack.push(item); + s.top = item; + return s; + }); + return id; + } + + function reset() { + store.set({ stack: [], top: undefined }); + } + return { ...store, - push: (symbol: Symbol) => { - store.update((s) => { - if (s.stack.includes(symbol)) { - return s; - } - - s.stack.push(symbol); - s.top = symbol; - return s; - }); - }, - remove: (symbol: Symbol) => { - store.update((s) => { - s.stack = s.stack.filter((x) => x !== symbol); - s.top = s.stack[s.stack.length - 1]; - return s; - }); - } - }; -} - -export const modalStack = createModalStack(); - -export type ModalProps = ReturnType; - -export function createModalProps(onClose: () => void, onBack?: () => void) { - const id = Symbol(); - - function close() { - onClose(); // ORDER MATTERS HERE - modalStack.remove(id); - } - - function back() { - onBack?.(); - modalStack.remove(id); - } - - return { + create, close, - back: onBack ? back : undefined, - id + closeGroup, + reset }; } + +export const modalStack = createDynamicModalStack(); + +let lastTitleModal: Symbol | undefined = undefined; +export function openTitleModal(tmdbId: number, type: TitleType) { + if (lastTitleModal) { + modalStack.close(lastTitleModal); + } + lastTitleModal = modalStack.create(TitlePageModal, { tmdbId, type }); +} diff --git a/src/lib/components/Modal/ModalBackground.svelte b/src/lib/components/Modal/ModalBackground.svelte deleted file mode 100644 index 992bcab..0000000 --- a/src/lib/components/Modal/ModalBackground.svelte +++ /dev/null @@ -1,11 +0,0 @@ - - -{#if $modalStack.top} -
-{/if} diff --git a/src/lib/components/Navbar/Navbar.svelte b/src/lib/components/Navbar/Navbar.svelte index bdbef10..0c89008 100644 --- a/src/lib/components/Navbar/Navbar.svelte +++ b/src/lib/components/Navbar/Navbar.svelte @@ -5,12 +5,12 @@ import TitleSearchModal from './TitleSearchModal.svelte'; import IconButton from '../IconButton.svelte'; import { fade } from 'svelte/transition'; + import { modalStack } from '../Modal/Modal'; let y = 0; let transparent = true; let baseStyle = ''; - let isSearchVisible = false; let isMobileMenuVisible = false; function getLinkStyle(path: string) { @@ -20,7 +20,17 @@ }); } - // TODO: on mobile don't act sticky + function openSearchModal() { + modalStack.create(TitleSearchModal, {}); + } + + function handleShortcuts(event: KeyboardEvent) { + if (event.key === 'k' && (event.metaKey || event.ctrlKey)) { + event.preventDefault(); + openSearchModal(); + } + } + $: { transparent = y <= 0; baseStyle = classNames( @@ -35,7 +45,7 @@ } - +
- (isSearchVisible = true)}> + @@ -71,7 +81,7 @@
- (isSearchVisible = true)}> + @@ -119,5 +129,3 @@
{/if} - - diff --git a/src/lib/components/Navbar/TitleSearchModal.svelte b/src/lib/components/Navbar/TitleSearchModal.svelte index 6de3cf5..06b3188 100644 --- a/src/lib/components/Navbar/TitleSearchModal.svelte +++ b/src/lib/components/Navbar/TitleSearchModal.svelte @@ -3,20 +3,22 @@ import { searchTmdbTitles } from '$lib/apis/tmdb/tmdbApi'; import { TMDB_POSTER_SMALL } from '$lib/constants'; import { MagnifyingGlass } from 'radix-icons-svelte'; - import { createModalProps } from '../Modal/Modal'; - import Modal from '../Modal/Modal.svelte'; + import { modalStack } from '../Modal/Modal'; import ModalContent from '../Modal/ModalContainer.svelte'; import ModalHeader from '../Modal/ModalHeader.svelte'; + import { onMount } from 'svelte'; + import type { TitleType } from '$lib/types'; + + export let modalId: Symbol; - export let visible = false; - let inputElement: HTMLInputElement; let inputValue = ''; + let inputElement: HTMLInputElement; let fetching = false; let resultProps: | { tmdbId: number; - type: 'movie' | 'series'; + type: TitleType; overview: string; posterUri: string; title: string; @@ -25,19 +27,8 @@ }[] | undefined = undefined; - const modalProps = createModalProps(() => { - visible = false; - }); - let searchTimeout: NodeJS.Timeout; - function clear() { - inputValue = ''; - fetching = false; - resultProps = undefined; - clearTimeout(searchTimeout); - } - const handleInput = () => { clearTimeout(searchTimeout); searchTimeout = setTimeout(() => { @@ -53,7 +44,7 @@ .filter((i) => i.media_type !== 'person') .filter((i: TmdbMovie2 & TmdbSeries2) => i.release_date || i.first_air_date) .map((i: TmdbMovie2 & TmdbSeries2) => ({ - // Types not accurate! + // ^ Types not accurate! ^ tmdbId: i.id || 0, type: (i as any).media_type === 'movie' ? 'movie' : 'series', posterUri: i.poster_path || '', @@ -66,72 +57,59 @@ .finally(() => (fetching = false)); }; - $: if (visible && inputElement) inputElement.focus(); - - function handleShortcuts(event: KeyboardEvent) { - if (event.key === 'k' && (event.metaKey || event.ctrlKey)) { - event.preventDefault(); - visible = true; - } else if (event.key === 'Escape' && visible) { - clear(); - modalProps.close(); - } + function handleClose() { + modalStack.close(modalId); } + + $: if (inputElement) inputElement.focus(); + + onMount(() => () => clearTimeout(searchTimeout)); - - -{#if visible} - - - - - - - {#if resultProps === undefined || inputValue === ''} -
No recent searches
- {:else if resultProps?.length === 0 && !fetching} -
No search results
- {:else} -
- {#each resultProps.slice(0, 5) as result} - - - { - modalProps.close(); - clear(); - }} - > -
-
-
-
{result.title}
-
- {result.year} -
- {#if result.seasons} -
{result.seasons}
- {/if} -
-
{result.overview}
+ + + + + + {#if resultProps === undefined || inputValue === ''} +
No recent searches
+ {:else if resultProps?.length === 0 && !fetching} +
No search results
+ {:else} +
+ {#each resultProps.slice(0, 5) as result} + + + +
+
+ - {/if} - - -{/if} + {#if result.seasons} +
{result.seasons}
+ {/if} +
+
{result.overview}
+
+ + {/each} +
+ {/if} + diff --git a/src/lib/components/Poster/Poster.svelte b/src/lib/components/Poster/Poster.svelte index 9f3005a..1a4ba4a 100644 --- a/src/lib/components/Poster/Poster.svelte +++ b/src/lib/components/Poster/Poster.svelte @@ -8,10 +8,11 @@ import classNames from 'classnames'; import PlayButton from '../PlayButton.svelte'; import ProgressBar from '../ProgressBar.svelte'; + import type { TitleType } from '$lib/types'; export let tmdbId: number; export let jellyfinId: string = ''; - export let type: 'movie' | 'series' = 'movie'; + export let type: TitleType = 'movie'; export let backdropUri: string; export let title = ''; diff --git a/src/lib/components/RequestModal/EpisodeSelectModal.svelte b/src/lib/components/RequestModal/EpisodeSelectModal.svelte index eb8fc4a..4b0dee1 100644 --- a/src/lib/components/RequestModal/EpisodeSelectModal.svelte +++ b/src/lib/components/RequestModal/EpisodeSelectModal.svelte @@ -1,60 +1,74 @@ - - - - -
- {#await fetchEpisodes(sonarrId, seasonNumber)} - Loading... - {:then episodes} - {#if episodes.length === 0} -
No episodes
- {:else} - {#each episodes as episode} - - -
selectEpisode(episode)} - > -
-
{episode.title}
-
- {episode.episodeNumber ? `Episode ${episode.episodeNumber}` : 'Special'} -
-
+ + modalStack.closeGroup(groupId)} + back={() => modalStack.close(modalId)} + text="Seasons" + /> + +
+ {#await fetchEpisodes(sonarrId, seasonNumber)} + Loading... + {:then episodes} + {#if episodes.length === 0} +
No episodes
+ {:else} + {#each episodes as episode} + + +
selectEpisode(episode)} + > +
+
{episode.title}
- {new Date(episode.airDate || Date.now()).toLocaleDateString('en', { - year: 'numeric', - month: 'short', - day: 'numeric' - })} + {episode.episodeNumber ? `Episode ${episode.episodeNumber}` : 'Special'}
- {/each} - {/if} - {/await} -
- - - +
+ {new Date(episode.airDate || Date.now()).toLocaleDateString('en', { + year: 'numeric', + month: 'short', + day: 'numeric' + })} +
+
+ {/each} + {/if} + {/await} +
+ + diff --git a/src/lib/components/RequestModal/RequestModal.svelte b/src/lib/components/RequestModal/RequestModal.svelte index 9f875dc..3ef0793 100644 --- a/src/lib/components/RequestModal/RequestModal.svelte +++ b/src/lib/components/RequestModal/RequestModal.svelte @@ -10,16 +10,16 @@ import { createEventDispatcher } from 'svelte'; import HeightHider from '../HeightHider.svelte'; import IconButton from '../IconButton.svelte'; - import Modal from '../Modal/Modal.svelte'; + import { modalStack } from '../Modal/Modal'; import ModalContent from '../Modal/ModalContainer.svelte'; import ModalHeader from '../Modal/ModalHeader.svelte'; - import type { ModalProps } from '../Modal/Modal'; const dispatch = createEventDispatcher(); - // TODO: Switch to grid + export let modalId: Symbol; + export let groupId: Symbol | undefined = undefined; + export let title = 'Releases'; - export let modalProps: ModalProps; export let radarrId: number | undefined = undefined; export let sonarrEpisodeId: number | undefined = undefined; export let seasonPack: { sonarrId: number; seasonNumber: number } | undefined = undefined; @@ -100,76 +100,78 @@ } - - - - {#await fetchReleases()} -
Loading...
- {:then { releases, filtered, releasesSkipped }} - {#if showAllReleases ? releases?.length : filtered?.length} -
- {#each showAllReleases ? releases : filtered as release} -
- - -
toggleShowDetails(release.guid || null)} - > -
-
{release.indexer}
-
{release?.quality?.quality?.name}
-
{release.seeders} seeders
-
-
-
{formatSize(release?.size || 0)}
- {#if release.guid !== downloadingGuid} - release.guid && handleDownload(release.guid)} - disabled={downloadFetchingGuid === release.guid} - > - - - {:else} -
- -
- {/if} -
+ + modalStack.close(modalId) : undefined} + close={() => (groupId ? modalStack.closeGroup(groupId) : modalStack.close(modalId))} + text={title} + /> + {#await fetchReleases()} +
Loading...
+ {:then { releases, filtered, releasesSkipped }} + {#if showAllReleases ? releases?.length : filtered?.length} +
+ {#each showAllReleases ? releases : filtered as release} +
+ + +
toggleShowDetails(release.guid || null)} + > +
+
{release.indexer}
+
{release?.quality?.quality?.name}
+
{release.seeders} seeders
- -
-
- {release.title} +
+
{formatSize(release?.size || 0)}
+ {#if release.guid !== downloadingGuid} + release.guid && handleDownload(release.guid)} + disabled={downloadFetchingGuid === release.guid} + > + + + {:else} +
+
- -
{formatMinutesToTime(release.ageMinutes || 0)} old
- -
{release.seeders} seeders / {release.leechers} leechers
- - {#if release.seeders} -
- {formatSize((release.size || 0) / release.seeders)} per seeder -
- {/if} -
- + {/if} +
- {/each} -
- - {#if releasesSkipped > 0} - -
- {showAllReleases ? 'Show less' : `Show all ${releasesSkipped} releases`} + +
+
+ {release.title} +
+ +
{formatMinutesToTime(release.ageMinutes || 0)} old
+ +
{release.seeders} seeders / {release.leechers} leechers
+ + {#if release.seeders} +
+ {formatSize((release.size || 0) / release.seeders)} per seeder +
+ {/if} +
+
- {/if} - {:else} -
No releases found.
+ {/each} +
+ + {#if releasesSkipped > 0} + +
+ {showAllReleases ? 'Show less' : `Show all ${releasesSkipped} releases`} +
{/if} - {/await} - - + {:else} +
No releases found.
+ {/if} + {/await} + diff --git a/src/lib/components/RequestModal/SeasonSelectModal.svelte b/src/lib/components/RequestModal/SeasonSelectModal.svelte deleted file mode 100644 index fa1391d..0000000 --- a/src/lib/components/RequestModal/SeasonSelectModal.svelte +++ /dev/null @@ -1,62 +0,0 @@ - - - - - - -
- {#each sonarrSeries.seasons || [] as season} -
{season.seasonNumber}
- {/each} -
-
-
-
- -{console.log(sonarrSeries)} - - diff --git a/src/lib/components/RequestModal/SeriesRequestModal.svelte b/src/lib/components/RequestModal/SeriesRequestModal.svelte index c479459..eb4b440 100644 --- a/src/lib/components/RequestModal/SeriesRequestModal.svelte +++ b/src/lib/components/RequestModal/SeriesRequestModal.svelte @@ -1,100 +1,65 @@ - - - - -
- {#each [...Array(seasons).keys()].map((i) => i + 1) as seasonNumber} -
-
- Season {seasonNumber} -
-
- - -
+ + modalStack.close(modalId)} back={undefined} text={heading} /> + +
+ {#each [...Array(seasons).keys()].map((i) => i + 1) as seasonNumber} +
+
+ Season {seasonNumber}
- {/each} -
- - - - -{#if episodeSelectProps} - -{/if} - -{#if requestProps} - -{/if} +
+ + +
+
+ {/each} +
+ + diff --git a/src/lib/components/TitlePageLayout/TitlePageLayout.svelte b/src/lib/components/TitlePageLayout/TitlePageLayout.svelte index 9475f94..a3df892 100644 --- a/src/lib/components/TitlePageLayout/TitlePageLayout.svelte +++ b/src/lib/components/TitlePageLayout/TitlePageLayout.svelte @@ -1,17 +1,18 @@ -{#if $titlePageModal.tmdbId} - {@const tmdbId = $titlePageModal.tmdbId} - -
-
- {#if $titlePageModal.type === 'movie'} - - {:else} - - {/if} -
-
-
-{/if} +
+
+ {#if type === 'movie'} + + {:else} + + {/if} +
+
diff --git a/src/lib/components/TitlePageLayout/TitlePageModal.ts b/src/lib/components/TitlePageLayout/TitlePageModal.ts index c929c4d..ebceb48 100644 --- a/src/lib/components/TitlePageLayout/TitlePageModal.ts +++ b/src/lib/components/TitlePageLayout/TitlePageModal.ts @@ -1,6 +1,7 @@ +import type { TitleType } from '$lib/types'; import { writable } from 'svelte/store'; -type Type = 'movie' | 'series' | undefined; +type Type = TitleType | undefined; function createTitlePageModalStore() { const store = writable<{ tmdbId: number | undefined; type: Type }>({ diff --git a/src/lib/components/TitleShowcase/TitleShowcase.svelte b/src/lib/components/TitleShowcase/TitleShowcase.svelte index 6333445..7ff01a8 100644 --- a/src/lib/components/TitleShowcase/TitleShowcase.svelte +++ b/src/lib/components/TitleShowcase/TitleShowcase.svelte @@ -9,13 +9,14 @@ import { settings } from '$lib/stores/settings.store'; import { onMount } from 'svelte'; import { fade, fly } from 'svelte/transition'; + import type { TitleType } from '$lib/types'; const TRAILER_TIMEOUT = 3000; const TRAILER_LOAD_TIME = 1000; const ANIMATION_DURATION = 150; export let tmdbId: number; - export let type: 'movie' | 'series'; + export let type: TitleType; export let title: string; export let genres: string[]; diff --git a/src/lib/components/VideoPlayer/VideoPlayer.svelte b/src/lib/components/VideoPlayer/VideoPlayer.svelte index 5e4e174..c66a40d 100644 --- a/src/lib/components/VideoPlayer/VideoPlayer.svelte +++ b/src/lib/components/VideoPlayer/VideoPlayer.svelte @@ -13,23 +13,17 @@ import { Cross2 } from 'radix-icons-svelte'; import { onDestroy } from 'svelte'; import IconButton from '../IconButton.svelte'; - import Modal from '../Modal/Modal.svelte'; - import { playerState, type PlayerStateValue } from './VideoPlayer'; - import { createModalProps } from '../Modal/Modal'; + import { playerState } from './VideoPlayer'; + import { modalStack } from '../Modal/Modal'; - let modalProps = createModalProps(() => { - playerState.close(); - video?.pause(); - clearInterval(progressInterval); - stopCallback?.(); - }); + export let modalId: Symbol; + + let uiVisible = false; let video: HTMLVideoElement; - + let mouseMovementTimeout: NodeJS.Timeout; let stopCallback: () => void; - - let progressInterval: ReturnType; - onDestroy(() => clearInterval(progressInterval)); + let progressInterval: NodeJS.Timeout; const fetchPlaybackInfo = (itemId: string) => getJellyfinPlaybackInfo(itemId, getDeviceProfile()).then( @@ -65,46 +59,47 @@ } ); - let uiVisible = false; - let timeout: ReturnType; + function handleClose() { + playerState.close(); + video?.pause(); + clearInterval(progressInterval); + stopCallback?.(); + modalStack.close(modalId); + } + function handleMouseMove() { uiVisible = true; - clearTimeout(timeout); - timeout = setTimeout(() => { + clearTimeout(mouseMovementTimeout); + mouseMovementTimeout = setTimeout(() => { uiVisible = false; }, 2000); } - let state: PlayerStateValue; - playerState.subscribe((s) => (state = s)); + onDestroy(() => clearInterval(progressInterval)); $: { - if (video && state.jellyfinId) { + if (video && $playerState.jellyfinId) { if (!Hls.isSupported()) { throw new Error('HLS is not supported'); } - if (video.src === '') fetchPlaybackInfo(state.jellyfinId); + if (video.src === '') fetchPlaybackInfo($playerState.jellyfinId); } } -{#if $playerState.visible} - - -
- -
-
-{/if} + +
+ +
diff --git a/src/lib/components/VideoPlayer/VideoPlayer.ts b/src/lib/components/VideoPlayer/VideoPlayer.ts index 50eb163..092b87a 100644 --- a/src/lib/components/VideoPlayer/VideoPlayer.ts +++ b/src/lib/components/VideoPlayer/VideoPlayer.ts @@ -1,5 +1,7 @@ import { library } from '$lib/stores/library.store'; import { writable } from 'svelte/store'; +import { modalStack } from '../Modal/Modal'; +import VideoPlayer from './VideoPlayer.svelte'; const initialValue = { visible: false, jellyfinId: '' }; export type PlayerStateValue = typeof initialValue; @@ -11,6 +13,7 @@ function createPlayerState() { ...store, streamJellyfinId: (id: string) => { store.set({ visible: true, jellyfinId: id }); + modalStack.create(VideoPlayer, {}); // FIXME }, close: () => { store.set({ visible: false, jellyfinId: '' }); diff --git a/src/lib/stores/library.store.ts b/src/lib/stores/library.store.ts index 1f21171..e86bec2 100644 --- a/src/lib/stores/library.store.ts +++ b/src/lib/stores/library.store.ts @@ -21,10 +21,9 @@ import { getTmdbMovieBackdrop, getTmdbMoviePoster, getTmdbSeriesBackdrop, - getTmdbSeriesFromTvdbId, - type TmdbMovieFull2, - type TmdbSeriesFull2 + getTmdbSeriesFromTvdbId } from '$lib/apis/tmdb/tmdbApi'; +import type { TitleType } from '$lib/types'; import { get, writable } from 'svelte/store'; export interface PlayableItem { @@ -42,7 +41,7 @@ export interface PlayableItem { isPlayed: boolean; jellyfinId?: string; - type: 'movie' | 'series'; + type: TitleType; tmdbId: number; jellyfinItem?: JellyfinItem; jellyfinEpisodes?: JellyfinItem[]; diff --git a/src/lib/types.ts b/src/lib/types.ts index ff9fb25..359d9e7 100644 --- a/src/lib/types.ts +++ b/src/lib/types.ts @@ -1,3 +1 @@ -import type { components as sonarrComponents } from '$lib/apis/sonarr/sonarr-types'; - -export type SeriesResource = sonarrComponents['schemas']['SeriesResource']; +export type TitleType = 'movie' | 'series'; diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte index b2fe6c5..f96ea73 100644 --- a/src/routes/+layout.svelte +++ b/src/routes/+layout.svelte @@ -1,9 +1,8 @@ {#await tmdbMoviePromise then movie} @@ -111,7 +116,7 @@ Add to Radarr {:else} - {/if} @@ -255,7 +260,7 @@ {/if}
- {:else} - {/if} @@ -328,7 +342,7 @@ {/if}
-