Work on migrating files from sveltekit to svelte

This commit is contained in:
Aleksi Lassila
2024-01-15 01:25:07 +02:00
parent a36a65f874
commit fd1a87a2fe
19 changed files with 361 additions and 340 deletions

View File

@@ -72,7 +72,7 @@
<MoviesPage container={contentContainer} /> <MoviesPage container={contentContainer} />
</Route> </Route>
<Route path="library"> <Route path="library">
<LibraryPage container={contentContainer} /> <LibraryPage parent={contentContainer} />
</Route> </Route>
<Route path="manage"> <Route path="manage">
<ManagePage container={contentContainer} /> <ManagePage container={contentContainer} />

View File

@@ -1,9 +1,9 @@
import type { components, paths } from '$lib/apis/jellyfin/jellyfin.generated';
import type { DeviceProfile } from '$lib/apis/jellyfin/playback-profiles';
import { settings } from '$lib/stores/settings.store';
import axios from 'axios'; import axios from 'axios';
import createClient from 'openapi-fetch'; import createClient from 'openapi-fetch';
import { get } from 'svelte/store'; import { get } from 'svelte/store';
import type { components, paths } from './jellyfin.generated';
import { settings } from '../../stores/settings.store';
import type { DeviceProfile } from './playback-profiles';
export type JellyfinItem = components['schemas']['BaseItemDto']; export type JellyfinItem = components['schemas']['BaseItemDto'];
@@ -26,7 +26,7 @@ function getJellyfinApi() {
export const getJellyfinContinueWatching = async (): Promise<JellyfinItem[] | undefined> => export const getJellyfinContinueWatching = async (): Promise<JellyfinItem[] | undefined> =>
getJellyfinApi() getJellyfinApi()
?.get('/Users/{userId}/Items/Resume', { ?.GET('/Users/{userId}/Items/Resume', {
params: { params: {
path: { path: {
userId: get(settings)?.jellyfin.userId || '' userId: get(settings)?.jellyfin.userId || ''
@@ -41,7 +41,7 @@ export const getJellyfinContinueWatching = async (): Promise<JellyfinItem[] | un
export const getJellyfinNextUp = async () => export const getJellyfinNextUp = async () =>
getJellyfinApi() getJellyfinApi()
?.get('/Shows/NextUp', { ?.GET('/Shows/NextUp', {
params: { params: {
query: { query: {
userId: get(settings)?.jellyfin.userId || '', userId: get(settings)?.jellyfin.userId || '',
@@ -53,7 +53,7 @@ export const getJellyfinNextUp = async () =>
export const getJellyfinItems = async () => export const getJellyfinItems = async () =>
getJellyfinApi() getJellyfinApi()
?.get('/Users/{userId}/Items', { ?.GET('/Users/{userId}/Items', {
params: { params: {
path: { path: {
userId: get(settings)?.jellyfin.userId || '' userId: get(settings)?.jellyfin.userId || ''
@@ -84,7 +84,7 @@ export const getJellyfinItems = async () =>
export const getJellyfinEpisodes = async (parentId = '') => export const getJellyfinEpisodes = async (parentId = '') =>
getJellyfinApi() getJellyfinApi()
?.get('/Users/{userId}/Items', { ?.GET('/Users/{userId}/Items', {
params: { params: {
path: { path: {
userId: get(settings)?.jellyfin.userId || '' userId: get(settings)?.jellyfin.userId || ''
@@ -126,7 +126,7 @@ export const getJellyfinEpisodesInSeasons = async (seriesId: string) =>
export const getJellyfinItem = async (itemId: string) => export const getJellyfinItem = async (itemId: string) =>
getJellyfinApi() getJellyfinApi()
?.get('/Users/{userId}/Items/{itemId}', { ?.GET('/Users/{userId}/Items/{itemId}', {
params: { params: {
path: { path: {
itemId, itemId,
@@ -146,7 +146,7 @@ export const getJellyfinPlaybackInfo = async (
maxStreamingBitrate = 140000000 maxStreamingBitrate = 140000000
) => ) =>
getJellyfinApi() getJellyfinApi()
?.post('/Items/{itemId}/PlaybackInfo', { ?.POST('/Items/{itemId}/PlaybackInfo', {
params: { params: {
path: { path: {
itemId: itemId itemId: itemId
@@ -184,7 +184,7 @@ export const reportJellyfinPlaybackStarted = (
audioStreamIndex?: number, audioStreamIndex?: number,
subtitleStreamIndex?: number subtitleStreamIndex?: number
) => ) =>
getJellyfinApi()?.post('/Sessions/Playing', { getJellyfinApi()?.POST('/Sessions/Playing', {
body: { body: {
CanSeek: true, CanSeek: true,
ItemId: itemId, ItemId: itemId,
@@ -201,7 +201,7 @@ export const reportJellyfinPlaybackProgress = (
isPaused: boolean, isPaused: boolean,
positionTicks: number positionTicks: number
) => ) =>
getJellyfinApi()?.post('/Sessions/Playing/Progress', { getJellyfinApi()?.POST('/Sessions/Playing/Progress', {
body: { body: {
ItemId: itemId, ItemId: itemId,
PlaySessionId: sessionId, PlaySessionId: sessionId,
@@ -217,7 +217,7 @@ export const reportJellyfinPlaybackStopped = (
sessionId: string, sessionId: string,
positionTicks: number positionTicks: number
) => ) =>
getJellyfinApi()?.post('/Sessions/Playing/Stopped', { getJellyfinApi()?.POST('/Sessions/Playing/Stopped', {
body: { body: {
ItemId: itemId, ItemId: itemId,
PlaySessionId: sessionId, PlaySessionId: sessionId,
@@ -227,7 +227,7 @@ export const reportJellyfinPlaybackStopped = (
}); });
export const delteActiveEncoding = (playSessionId: string) => export const delteActiveEncoding = (playSessionId: string) =>
getJellyfinApi()?.del('/Videos/ActiveEncodings', { getJellyfinApi()?.DELETE('/Videos/ActiveEncodings', {
params: { params: {
query: { query: {
deviceId: JELLYFIN_DEVICE_ID, deviceId: JELLYFIN_DEVICE_ID,
@@ -237,7 +237,7 @@ export const delteActiveEncoding = (playSessionId: string) =>
}); });
export const setJellyfinItemWatched = async (jellyfinId: string) => export const setJellyfinItemWatched = async (jellyfinId: string) =>
getJellyfinApi()?.post('/Users/{userId}/PlayedItems/{itemId}', { getJellyfinApi()?.POST('/Users/{userId}/PlayedItems/{itemId}', {
params: { params: {
path: { path: {
userId: get(settings)?.jellyfin.userId || '', userId: get(settings)?.jellyfin.userId || '',
@@ -250,7 +250,7 @@ export const setJellyfinItemWatched = async (jellyfinId: string) =>
}); });
export const setJellyfinItemUnwatched = async (jellyfinId: string) => export const setJellyfinItemUnwatched = async (jellyfinId: string) =>
getJellyfinApi()?.del('/Users/{userId}/PlayedItems/{itemId}', { getJellyfinApi()?.DELETE('/Users/{userId}/PlayedItems/{itemId}', {
params: { params: {
path: { path: {
userId: get(settings)?.jellyfin.userId || '', userId: get(settings)?.jellyfin.userId || '',

View File

@@ -1,10 +1,10 @@
import type { components, paths } from '$lib/apis/radarr/radarr.generated';
import { getTmdbMovie } from '$lib/apis/tmdb/tmdbApi';
import { settings } from '$lib/stores/settings.store';
import { log } from '$lib/utils';
import axios from 'axios'; import axios from 'axios';
import createClient from 'openapi-fetch'; import createClient from 'openapi-fetch';
import { get } from 'svelte/store'; import { get } from 'svelte/store';
import { settings } from '../../stores/settings.store';
import type { components, paths } from './radarr.generated';
import { getTmdbMovie } from '../tmdb/tmdbApi';
import { log } from '../../utils';
export type RadarrMovie = components['schemas']['MovieResource']; export type RadarrMovie = components['schemas']['MovieResource'];
export type MovieFileResource = components['schemas']['MovieFileResource']; export type MovieFileResource = components['schemas']['MovieFileResource'];
@@ -43,14 +43,14 @@ function getRadarrApi() {
export const getRadarrMovies = (): Promise<RadarrMovie[]> => export const getRadarrMovies = (): Promise<RadarrMovie[]> =>
getRadarrApi() getRadarrApi()
?.get('/api/v3/movie', { ?.GET('/api/v3/movie', {
params: {} params: {}
}) })
.then((r) => r.data || []) || Promise.resolve([]); .then((r) => r.data || []) || Promise.resolve([]);
export const getRadarrMovieByTmdbId = (tmdbId: string): Promise<RadarrMovie | undefined> => export const getRadarrMovieByTmdbId = (tmdbId: string): Promise<RadarrMovie | undefined> =>
getRadarrApi() getRadarrApi()
?.get('/api/v3/movie', { ?.GET('/api/v3/movie', {
params: { params: {
query: { query: {
tmdbId: Number(tmdbId) tmdbId: Number(tmdbId)
@@ -82,7 +82,7 @@ export const addMovieToRadarr = async (tmdbId: number) => {
return ( return (
getRadarrApi() getRadarrApi()
?.post('/api/v3/movie', { ?.POST('/api/v3/movie', {
params: {}, params: {},
body: options body: options
}) })
@@ -92,7 +92,7 @@ export const addMovieToRadarr = async (tmdbId: number) => {
export const cancelDownloadRadarrMovie = async (downloadId: number) => { export const cancelDownloadRadarrMovie = async (downloadId: number) => {
const deleteResponse = await getRadarrApi() const deleteResponse = await getRadarrApi()
?.del('/api/v3/queue/{id}', { ?.DELETE('/api/v3/queue/{id}', {
params: { params: {
path: { path: {
id: downloadId id: downloadId
@@ -110,12 +110,12 @@ export const cancelDownloadRadarrMovie = async (downloadId: number) => {
export const fetchRadarrReleases = (movieId: number) => export const fetchRadarrReleases = (movieId: number) =>
getRadarrApi() getRadarrApi()
?.get('/api/v3/release', { params: { query: { movieId: movieId } } }) ?.GET('/api/v3/release', { params: { query: { movieId: movieId } } })
.then((r) => r.data || []) || Promise.resolve([]); .then((r) => r.data || []) || Promise.resolve([]);
export const downloadRadarrMovie = (guid: string, indexerId: number) => export const downloadRadarrMovie = (guid: string, indexerId: number) =>
getRadarrApi() getRadarrApi()
?.post('/api/v3/release', { ?.POST('/api/v3/release', {
params: {}, params: {},
body: { body: {
indexerId, indexerId,
@@ -126,7 +126,7 @@ export const downloadRadarrMovie = (guid: string, indexerId: number) =>
export const deleteRadarrMovie = (id: number) => export const deleteRadarrMovie = (id: number) =>
getRadarrApi() getRadarrApi()
?.del('/api/v3/moviefile/{id}', { ?.DELETE('/api/v3/moviefile/{id}', {
params: { params: {
path: { path: {
id id
@@ -137,7 +137,7 @@ export const deleteRadarrMovie = (id: number) =>
export const getRadarrDownloads = (): Promise<RadarrDownload[]> => export const getRadarrDownloads = (): Promise<RadarrDownload[]> =>
getRadarrApi() getRadarrApi()
?.get('/api/v3/queue', { ?.GET('/api/v3/queue', {
params: { params: {
query: { query: {
includeMovie: true includeMovie: true
@@ -155,7 +155,7 @@ export const getRadarrDownloadsByTmdbId = (tmdbId: number) =>
const lookupRadarrMovieByTmdbId = (tmdbId: number) => const lookupRadarrMovieByTmdbId = (tmdbId: number) =>
getRadarrApi() getRadarrApi()
?.get('/api/v3/movie/lookup/tmdb', { ?.GET('/api/v3/movie/lookup/tmdb', {
params: { params: {
query: { query: {
tmdbId tmdbId
@@ -166,12 +166,12 @@ const lookupRadarrMovieByTmdbId = (tmdbId: number) =>
export const getDiskSpace = (): Promise<DiskSpaceInfo[]> => export const getDiskSpace = (): Promise<DiskSpaceInfo[]> =>
getRadarrApi() getRadarrApi()
?.get('/api/v3/diskspace', {}) ?.GET('/api/v3/diskspace', {})
.then((d) => d.data || []) || Promise.resolve([]); .then((d) => d.data || []) || Promise.resolve([]);
export const removeFromRadarr = (id: number) => export const removeFromRadarr = (id: number) =>
getRadarrApi() getRadarrApi()
?.del('/api/v3/movie/{id}', { ?.DELETE('/api/v3/movie/{id}', {
params: { params: {
path: { path: {
id id

View File

@@ -1,10 +1,10 @@
import type { components, paths } from '$lib/apis/sonarr/sonarr.generated';
import { settings } from '$lib/stores/settings.store';
import { log } from '$lib/utils';
import axios from 'axios'; import axios from 'axios';
import createClient from 'openapi-fetch'; import createClient from 'openapi-fetch';
import { get } from 'svelte/store'; import { get } from 'svelte/store';
import { getTmdbSeries } from '../tmdb/tmdbApi'; import { getTmdbSeries } from '../tmdb/tmdbApi';
import type { components, paths } from './sonarr.generated';
import { settings } from '../../stores/settings.store';
import { log } from '../../utils';
export type SonarrSeries = components['schemas']['SeriesResource']; export type SonarrSeries = components['schemas']['SeriesResource'];
export type SonarrReleaseResource = components['schemas']['ReleaseResource']; export type SonarrReleaseResource = components['schemas']['ReleaseResource'];
@@ -58,14 +58,14 @@ function getSonarrApi() {
export const getSonarrSeries = (): Promise<SonarrSeries[]> => export const getSonarrSeries = (): Promise<SonarrSeries[]> =>
getSonarrApi() getSonarrApi()
?.get('/api/v3/series', { ?.GET('/api/v3/series', {
params: {} params: {}
}) })
.then((r) => r.data || []) || Promise.resolve([]); .then((r) => r.data || []) || Promise.resolve([]);
export const getSonarrSeriesByTvdbId = (tvdbId: number): Promise<SonarrSeries | undefined> => export const getSonarrSeriesByTvdbId = (tvdbId: number): Promise<SonarrSeries | undefined> =>
getSonarrApi() getSonarrApi()
?.get('/api/v3/series', { ?.GET('/api/v3/series', {
params: { params: {
query: { query: {
tvdbId: tvdbId tvdbId: tvdbId
@@ -76,7 +76,7 @@ export const getSonarrSeriesByTvdbId = (tvdbId: number): Promise<SonarrSeries |
export const getDiskSpace = (): Promise<DiskSpaceInfo[]> => export const getDiskSpace = (): Promise<DiskSpaceInfo[]> =>
getSonarrApi() getSonarrApi()
?.get('/api/v3/diskspace', {}) ?.GET('/api/v3/diskspace', {})
.then((d) => d.data || []) || Promise.resolve([]); .then((d) => d.data || []) || Promise.resolve([]);
export const addSeriesToSonarr = async (tmdbId: number) => { export const addSeriesToSonarr = async (tmdbId: number) => {
@@ -101,7 +101,7 @@ export const addSeriesToSonarr = async (tmdbId: number) => {
}; };
return getSonarrApi() return getSonarrApi()
?.post('/api/v3/series', { ?.POST('/api/v3/series', {
params: {}, params: {},
body: options body: options
}) })
@@ -110,7 +110,7 @@ export const addSeriesToSonarr = async (tmdbId: number) => {
export const cancelDownloadSonarrEpisode = async (downloadId: number) => { export const cancelDownloadSonarrEpisode = async (downloadId: number) => {
const deleteResponse = await getSonarrApi() const deleteResponse = await getSonarrApi()
?.del('/api/v3/queue/{id}', { ?.DELETE('/api/v3/queue/{id}', {
params: { params: {
path: { path: {
id: downloadId id: downloadId
@@ -128,7 +128,7 @@ export const cancelDownloadSonarrEpisode = async (downloadId: number) => {
export const downloadSonarrEpisode = (guid: string, indexerId: number) => export const downloadSonarrEpisode = (guid: string, indexerId: number) =>
getSonarrApi() getSonarrApi()
?.post('/api/v3/release', { ?.POST('/api/v3/release', {
params: {}, params: {},
body: { body: {
indexerId, indexerId,
@@ -139,7 +139,7 @@ export const downloadSonarrEpisode = (guid: string, indexerId: number) =>
export const deleteSonarrEpisode = (id: number) => export const deleteSonarrEpisode = (id: number) =>
getSonarrApi() getSonarrApi()
?.del('/api/v3/episodefile/{id}', { ?.DELETE('/api/v3/episodefile/{id}', {
params: { params: {
path: { path: {
id id
@@ -150,7 +150,7 @@ export const deleteSonarrEpisode = (id: number) =>
export const getSonarrDownloads = (): Promise<SonarrDownload[]> => export const getSonarrDownloads = (): Promise<SonarrDownload[]> =>
getSonarrApi() getSonarrApi()
?.get('/api/v3/queue', { ?.GET('/api/v3/queue', {
params: { params: {
query: { query: {
includeEpisode: true, includeEpisode: true,
@@ -171,7 +171,7 @@ export const getSonarrDownloadsById = (sonarrId: number) =>
export const removeFromSonarr = (id: number): Promise<boolean> => export const removeFromSonarr = (id: number): Promise<boolean> =>
getSonarrApi() getSonarrApi()
?.del('/api/v3/series/{id}', { ?.DELETE('/api/v3/series/{id}', {
params: { params: {
path: { path: {
id id
@@ -183,7 +183,7 @@ export const removeFromSonarr = (id: number): Promise<boolean> =>
export const getSonarrEpisodes = async (seriesId: number) => { export const getSonarrEpisodes = async (seriesId: number) => {
const episodesPromise = const episodesPromise =
getSonarrApi() getSonarrApi()
?.get('/api/v3/episode', { ?.GET('/api/v3/episode', {
params: { params: {
query: { query: {
seriesId seriesId
@@ -194,7 +194,7 @@ export const getSonarrEpisodes = async (seriesId: number) => {
const episodeFilesPromise = const episodeFilesPromise =
getSonarrApi() getSonarrApi()
?.get('/api/v3/episodefile', { ?.GET('/api/v3/episodefile', {
params: { params: {
query: { query: {
seriesId seriesId
@@ -214,7 +214,7 @@ export const getSonarrEpisodes = async (seriesId: number) => {
export const fetchSonarrReleases = async (episodeId: number) => export const fetchSonarrReleases = async (episodeId: number) =>
getSonarrApi() getSonarrApi()
?.get('/api/v3/release', { ?.GET('/api/v3/release', {
params: { params: {
query: { query: {
episodeId episodeId
@@ -225,7 +225,7 @@ export const fetchSonarrReleases = async (episodeId: number) =>
export const fetchSonarrSeasonReleases = async (seriesId: number, seasonNumber: number) => export const fetchSonarrSeasonReleases = async (seriesId: number, seasonNumber: number) =>
getSonarrApi() getSonarrApi()
?.get('/api/v3/release', { ?.GET('/api/v3/release', {
params: { params: {
query: { query: {
seriesId, seriesId,
@@ -238,7 +238,7 @@ export const fetchSonarrSeasonReleases = async (seriesId: number, seasonNumber:
export const fetchSonarrEpisodes = async (seriesId: number): Promise<SonarrEpisode[]> => { export const fetchSonarrEpisodes = async (seriesId: number): Promise<SonarrEpisode[]> => {
return ( return (
getSonarrApi() getSonarrApi()
?.get('/api/v3/episode', { ?.GET('/api/v3/episode', {
params: { params: {
query: { query: {
seriesId seriesId

View File

@@ -1,10 +1,9 @@
import { browser } from '$app/environment';
import { TMDB_API_KEY, TMDB_BACKDROP_SMALL } from '$lib/constants';
import { settings } from '$lib/stores/settings.store';
import createClient from 'openapi-fetch'; import createClient from 'openapi-fetch';
import { get } from 'svelte/store'; import { get } from 'svelte/store';
import type { operations, paths } from './tmdb.generated'; import type { operations, paths } from './tmdb.generated';
import type { TitleType } from '$lib/types'; import { TMDB_API_KEY, TMDB_BACKDROP_SMALL } from '../../constants';
import { settings } from '../../stores/settings.store';
import type { TitleType } from '../../types';
const CACHE_ONE_DAY = 'max-age=86400'; const CACHE_ONE_DAY = 'max-age=86400';
const CACHE_FOUR_DAYS = 'max-age=345600'; const CACHE_FOUR_DAYS = 'max-age=345600';
@@ -39,8 +38,8 @@ export interface TmdbSeriesFull2 extends TmdbSeries2 {
images: operations['tv-series-images']['responses']['200']['content']['application/json']; images: operations['tv-series-images']['responses']['200']['content']['application/json'];
} }
const backdropCache = browser ? window?.caches?.open('backdrops') : undefined; const backdropCache = window?.caches?.open('backdrops') || undefined;
const posterCache = browser ? window?.caches?.open('posters') : undefined; const posterCache = window?.caches?.open('posters') || undefined;
const getTmdbCache = async ( const getTmdbCache = async (
cachePromise: typeof backdropCache, cachePromise: typeof backdropCache,
@@ -72,7 +71,7 @@ export const TmdbApiOpen = createClient<paths>({
}); });
export const getTmdbMovie = async (tmdbId: number) => export const getTmdbMovie = async (tmdbId: number) =>
await TmdbApiOpen.get('/3/movie/{movie_id}', { await TmdbApiOpen.GET('/3/movie/{movie_id}', {
params: { params: {
path: { path: {
movie_id: tmdbId movie_id: tmdbId
@@ -85,7 +84,7 @@ export const getTmdbMovie = async (tmdbId: number) =>
}).then((res) => res.data as TmdbMovieFull2 | undefined); }).then((res) => res.data as TmdbMovieFull2 | undefined);
export const getTmdbSeriesFromTvdbId = async (tvdbId: string) => export const getTmdbSeriesFromTvdbId = async (tvdbId: string) =>
TmdbApiOpen.get('/3/find/{external_id}', { TmdbApiOpen.GET('/3/find/{external_id}', {
params: { params: {
path: { path: {
external_id: tvdbId external_id: tvdbId
@@ -107,7 +106,7 @@ export const getTmdbIdFromTvdbId = async (tvdbId: number) =>
}); });
export const getTmdbSeries = async (tmdbId: number): Promise<TmdbSeriesFull2 | undefined> => export const getTmdbSeries = async (tmdbId: number): Promise<TmdbSeriesFull2 | undefined> =>
await TmdbApiOpen.get('/3/tv/{series_id}', { await TmdbApiOpen.GET('/3/tv/{series_id}', {
params: { params: {
path: { path: {
series_id: tmdbId series_id: tmdbId
@@ -126,7 +125,7 @@ export const getTmdbSeriesSeason = async (
tmdbId: number, tmdbId: number,
season: number season: number
): Promise<TmdbSeason | undefined> => ): Promise<TmdbSeason | undefined> =>
TmdbApiOpen.get('/3/tv/{series_id}/season/{season_number}', { TmdbApiOpen.GET('/3/tv/{series_id}/season/{season_number}', {
params: { params: {
path: { path: {
series_id: tmdbId, series_id: tmdbId,
@@ -141,7 +140,7 @@ export const getTmdbSeriesSeasons = async (tmdbId: number, seasons: number) =>
); );
export const getTmdbSeriesImages = async (tmdbId: number) => export const getTmdbSeriesImages = async (tmdbId: number) =>
TmdbApiOpen.get('/3/tv/{series_id}/images', { TmdbApiOpen.GET('/3/tv/{series_id}/images', {
params: { params: {
path: { path: {
series_id: tmdbId series_id: tmdbId
@@ -153,7 +152,7 @@ export const getTmdbSeriesImages = async (tmdbId: number) =>
}).then((res) => res.data); }).then((res) => res.data);
export const getTmdbMovieImages = async (tmdbId: number) => export const getTmdbMovieImages = async (tmdbId: number) =>
await TmdbApiOpen.get('/3/movie/{movie_id}/images', { await TmdbApiOpen.GET('/3/movie/{movie_id}/images', {
params: { params: {
path: { path: {
movie_id: tmdbId movie_id: tmdbId
@@ -203,7 +202,7 @@ export const getTmdbMoviePoster = async (tmdbId: number) =>
/** Discover */ /** Discover */
export const getTmdbPopularMovies = () => export const getTmdbPopularMovies = () =>
TmdbApiOpen.get('/3/movie/popular', { TmdbApiOpen.GET('/3/movie/popular', {
params: { params: {
query: { query: {
language: get(settings)?.language, language: get(settings)?.language,
@@ -213,7 +212,7 @@ export const getTmdbPopularMovies = () =>
}).then((res) => res.data?.results || []); }).then((res) => res.data?.results || []);
export const getTmdbPopularSeries = () => export const getTmdbPopularSeries = () =>
TmdbApiOpen.get('/3/tv/popular', { TmdbApiOpen.GET('/3/tv/popular', {
params: { params: {
query: { query: {
language: get(settings)?.language language: get(settings)?.language
@@ -222,7 +221,7 @@ export const getTmdbPopularSeries = () =>
}).then((res) => res.data?.results || []); }).then((res) => res.data?.results || []);
export const getTmdbNetworkSeries = (networkId: number) => export const getTmdbNetworkSeries = (networkId: number) =>
TmdbApiOpen.get('/3/discover/tv', { TmdbApiOpen.GET('/3/discover/tv', {
params: { params: {
query: { query: {
with_networks: networkId with_networks: networkId
@@ -231,7 +230,7 @@ export const getTmdbNetworkSeries = (networkId: number) =>
}).then((res) => res.data?.results || []); }).then((res) => res.data?.results || []);
export const getTmdbGenreMovies = (genreId: number) => export const getTmdbGenreMovies = (genreId: number) =>
TmdbApiOpen.get('/3/discover/movie', { TmdbApiOpen.GET('/3/discover/movie', {
params: { params: {
query: { query: {
with_genres: String(genreId) with_genres: String(genreId)
@@ -240,7 +239,7 @@ export const getTmdbGenreMovies = (genreId: number) =>
}).then((res) => res.data?.results || []); }).then((res) => res.data?.results || []);
export const getTmdbSeriesRecommendations = (tmdbId: number) => export const getTmdbSeriesRecommendations = (tmdbId: number) =>
TmdbApiOpen.get('/3/tv/{series_id}/recommendations', { TmdbApiOpen.GET('/3/tv/{series_id}/recommendations', {
params: { params: {
path: { path: {
series_id: tmdbId series_id: tmdbId
@@ -249,7 +248,7 @@ export const getTmdbSeriesRecommendations = (tmdbId: number) =>
}).then((res) => res.data?.results || []); }).then((res) => res.data?.results || []);
export const getTmdbSeriesSimilar = (tmdbId: number) => export const getTmdbSeriesSimilar = (tmdbId: number) =>
TmdbApiOpen.get('/3/tv/{series_id}/similar', { TmdbApiOpen.GET('/3/tv/{series_id}/similar', {
params: { params: {
path: { path: {
series_id: String(tmdbId) series_id: String(tmdbId)
@@ -258,7 +257,7 @@ export const getTmdbSeriesSimilar = (tmdbId: number) =>
}).then((res) => res.data?.results || []); }).then((res) => res.data?.results || []);
export const getTmdbSeriesCredits = (tmdbId: number) => export const getTmdbSeriesCredits = (tmdbId: number) =>
TmdbApiOpen.get('/3/tv/{series_id}/credits', { TmdbApiOpen.GET('/3/tv/{series_id}/credits', {
params: { params: {
path: { path: {
series_id: tmdbId series_id: tmdbId
@@ -267,7 +266,7 @@ export const getTmdbSeriesCredits = (tmdbId: number) =>
}).then((res) => res.data?.cast || []); }).then((res) => res.data?.cast || []);
export const getTmdbMovieRecommendations = (tmdbId: number) => export const getTmdbMovieRecommendations = (tmdbId: number) =>
TmdbApiOpen.get('/3/movie/{movie_id}/recommendations', { TmdbApiOpen.GET('/3/movie/{movie_id}/recommendations', {
params: { params: {
path: { path: {
movie_id: tmdbId movie_id: tmdbId
@@ -276,7 +275,7 @@ export const getTmdbMovieRecommendations = (tmdbId: number) =>
}).then((res) => res.data?.results || []); }).then((res) => res.data?.results || []);
export const getTmdbMovieSimilar = (tmdbId: number) => export const getTmdbMovieSimilar = (tmdbId: number) =>
TmdbApiOpen.get('/3/movie/{movie_id}/similar', { TmdbApiOpen.GET('/3/movie/{movie_id}/similar', {
params: { params: {
path: { path: {
movie_id: tmdbId movie_id: tmdbId
@@ -285,7 +284,7 @@ export const getTmdbMovieSimilar = (tmdbId: number) =>
}).then((res) => res.data?.results || []); }).then((res) => res.data?.results || []);
export const searchTmdbTitles = (query: string) => export const searchTmdbTitles = (query: string) =>
TmdbApiOpen.get('/3/search/multi', { TmdbApiOpen.GET('/3/search/multi', {
params: { params: {
query: { query: {
query query
@@ -334,7 +333,7 @@ export const getPosterProps = async (
}; };
export const getTmdbPerson = async (person_id: number) => export const getTmdbPerson = async (person_id: number) =>
TmdbApiOpen.get('/3/person/{person_id}', { TmdbApiOpen.GET('/3/person/{person_id}', {
params: { params: {
path: { path: {
person_id: person_id person_id: person_id

View File

@@ -1,31 +1,31 @@
<script lang="ts"> <script lang="ts">
import Selectable from '../components/Selectable.svelte'; import Selectable from '../components/Selectable.svelte';
import classNames from 'classnames'; import classNames from 'classnames';
import { useNavigate } from 'svelte-navigator'; import { useNavigate } from 'svelte-navigator';
import { get } from 'svelte/store'; import { get } from 'svelte/store';
import { Container } from '../actions/focusAction'; import { Container } from '../actions/focusAction';
export let to: string; export let to: string;
export let parentContainer: Container; export let parentContainer: Container;
const { container, hasFocus } = parentContainer.createChild('navBarItem').getStores(); const { container, hasFocus } = parentContainer.createChild('navBarItem').getStores();
const navigate = useNavigate(); const navigate = useNavigate();
function handleClick() { function handleClick() {
navigate(to); navigate(to);
get(Container.focusedObject)?.giveFocus('right'); get(Container.focusedObject)?.giveFocus('right');
} }
</script> </script>
<button on:click={handleClick}> <button on:click={handleClick}>
<Selectable {container}> <Selectable {container}>
<div <div
class={classNames('flex items-center my-2', { class={classNames('flex items-center my-2', {
'text-amber-200': $hasFocus 'text-amber-200': $hasFocus
})} })}
> >
<slot name="icon" /> <slot name="icon" />
<slot name="text" /> <slot name="text" />
</div> </div>
</Selectable> </Selectable>
</button> </button>

View File

@@ -1,23 +1,23 @@
<script lang="ts"> <script lang="ts">
import classNames from 'classnames'; import classNames from 'classnames';
import { fade } from 'svelte/transition'; import { fade } from 'svelte/transition';
export let index = 0; export let index = 0;
export let size: 'dynamic' | 'md' | 'lg' = 'md'; export let size: 'dynamic' | 'md' | 'lg' = 'md';
export let orientation: 'portrait' | 'landscape' = 'landscape'; export let orientation: 'portrait' | 'landscape' = 'landscape';
</script> </script>
<div <div
class={classNames('rounded-xl overflow-hidden shadow-lg placeholder shrink-0', { class={classNames('rounded-xl overflow-hidden shadow-lg placeholder shrink-0', {
'aspect-video': orientation === 'landscape', 'aspect-video': orientation === 'landscape',
'aspect-[2/3]': orientation === 'portrait', 'aspect-[2/3]': orientation === 'portrait',
'w-44': size === 'md' && orientation === 'portrait', 'w-44': size === 'md' && orientation === 'portrait',
'h-44': size === 'md' && orientation === 'landscape', 'h-44': size === 'md' && orientation === 'landscape',
'w-60': size === 'lg' && orientation === 'portrait', 'w-60': size === 'lg' && orientation === 'portrait',
'h-60': size === 'lg' && orientation === 'landscape', 'h-60': size === 'lg' && orientation === 'landscape',
'w-full': size === 'dynamic' 'w-full': size === 'dynamic'
})} })}
style={'animation-delay: ' + ((index * 100) % 2000) + 'ms;'} style={'animation-delay: ' + ((index * 100) % 2000) + 'ms;'}
transition:fade|global transition:fade|global
tabindex="0" tabindex="0"
/> />

View File

@@ -1,73 +1,73 @@
<script lang="ts"> <script lang="ts">
import { fade } from 'svelte/transition'; import { fade } from 'svelte/transition';
import IconButton from '../IconButton.svelte'; import IconButton from '../IconButton.svelte';
import { ChevronLeft, ChevronRight } from 'radix-icons-svelte'; import { ChevronLeft, ChevronRight } from 'radix-icons-svelte';
import classNames from 'classnames'; import classNames from 'classnames';
import type { Registerer } from '../../actions/focusAction'; import type { Registerer } from '../../actions/focusAction';
export let gradientFromColor = 'from-stone-950'; export let gradientFromColor = 'from-stone-950';
export let heading = ''; export let heading = '';
let carousel: HTMLDivElement | undefined; let carousel: HTMLDivElement | undefined;
let scrollX = 0; let scrollX = 0;
export let scrollClass = ''; export let scrollClass = '';
</script> </script>
<div class={classNames('flex flex-col gap-4 group/carousel', $$restProps.class)}> <div class={classNames('flex flex-col gap-4 group/carousel', $$restProps.class)}>
<div class={'flex justify-between items-center gap-4 ' + scrollClass}> <div class={'flex justify-between items-center gap-4 ' + scrollClass}>
<slot name="title"> <slot name="title">
<div class="font-semibold text-xl">{heading}</div> <div class="font-semibold text-xl">{heading}</div>
</slot> </slot>
<div <div
class={classNames( class={classNames(
'flex gap-2 sm:opacity-0 transition-opacity sm:group-hover/carousel:opacity-100', 'flex gap-2 sm:opacity-0 transition-opacity sm:group-hover/carousel:opacity-100',
{ {
hidden: (carousel?.scrollWidth || 0) === (carousel?.clientWidth || 0) hidden: (carousel?.scrollWidth || 0) === (carousel?.clientWidth || 0)
} }
)} )}
> >
<IconButton <IconButton
on:click={() => { on:click={() => {
carousel?.scrollTo({ left: scrollX - carousel?.clientWidth * 0.8, behavior: 'smooth' }); carousel?.scrollTo({ left: scrollX - carousel?.clientWidth * 0.8, behavior: 'smooth' });
}} }}
> >
<ChevronLeft size={20} /> <ChevronLeft size={20} />
</IconButton> </IconButton>
<IconButton <IconButton
on:click={() => { on:click={() => {
carousel?.scrollTo({ left: scrollX + carousel?.clientWidth * 0.8, behavior: 'smooth' }); carousel?.scrollTo({ left: scrollX + carousel?.clientWidth * 0.8, behavior: 'smooth' });
}} }}
> >
<ChevronRight size={20} /> <ChevronRight size={20} />
</IconButton> </IconButton>
</div> </div>
</div> </div>
<div class="relative"> <div class="relative">
<div <div
class={classNames( class={classNames(
'flex overflow-x-scroll items-center overflow-y-visible gap-4 relative scrollbar-hide p-1', 'flex overflow-x-scroll items-center overflow-y-visible gap-4 relative scrollbar-hide p-1',
scrollClass scrollClass
)} )}
bind:this={carousel} bind:this={carousel}
tabindex="-1" tabindex="-1"
on:scroll={() => (scrollX = carousel?.scrollLeft || scrollX)} on:scroll={() => (scrollX = carousel?.scrollLeft || scrollX)}
> >
<slot /> <slot />
</div> </div>
{#if scrollX > 50} {#if scrollX > 50}
<div <div
transition:fade={{ duration: 200 }} transition:fade={{ duration: 200 }}
class={'absolute inset-y-0 left-0 w-0 sm:w-16 md:w-24 bg-gradient-to-r ' + class={'absolute inset-y-0 left-0 w-0 sm:w-16 md:w-24 bg-gradient-to-r ' +
gradientFromColor} gradientFromColor}
/> />
{/if} {/if}
{#if carousel && scrollX < carousel?.scrollWidth - carousel?.clientWidth - 50} {#if carousel && scrollX < carousel?.scrollWidth - carousel?.clientWidth - 50}
<div <div
transition:fade={{ duration: 200 }} transition:fade={{ duration: 200 }}
class={'absolute inset-y-0 right-0 w-0 sm:w-16 md:w-24 bg-gradient-to-l ' + class={'absolute inset-y-0 right-0 w-0 sm:w-16 md:w-24 bg-gradient-to-l ' +
gradientFromColor} gradientFromColor}
/> />
{/if} {/if}
</div> </div>
</div> </div>

View File

@@ -1,37 +1,37 @@
<script lang="ts"> <script lang="ts">
import CardPlaceholder from '../Card/CardPlaceholder.svelte'; import CardPlaceholder from '../Card/CardPlaceholder.svelte';
import { Container } from '../../actions/focusAction'; import { Container } from '../../actions/focusAction';
import classNames from 'classnames'; import classNames from 'classnames';
export let size: 'dynamic' | 'md' | 'lg' = 'md'; export let size: 'dynamic' | 'md' | 'lg' = 'md';
export let orientation: 'landscape' | 'portrait' = 'landscape'; export let orientation: 'landscape' | 'portrait' = 'landscape';
export let container: Container; export let container: Container;
let carousel = container.createChild('carousel').setDirection('horizontal'); let carousel = container.createChild('carousel').setDirection('horizontal');
let focusIndexStore = carousel.focusIndex; let focusIndexStore = carousel.focusIndex;
let focusWithinStore = carousel.hasFocusWithin; let focusWithinStore = carousel.hasFocusWithin;
Container.focusedObject.subscribe((fo) => console.log('focusedObject', fo)); Container.focusedObject.subscribe((fo) => console.log('focusedObject', fo));
carousel.hasFocus.subscribe((hf) => console.log('hasFocus', hf)); carousel.hasFocus.subscribe((hf) => console.log('hasFocus', hf));
let registerer = carousel.getChildRegisterer(); let registerer = carousel.getChildRegisterer();
</script> </script>
<p <p
class={classNames({ class={classNames({
'bg-blue-500': $focusWithinStore 'bg-blue-500': $focusWithinStore
})} })}
> >
Index: {$focusIndexStore} Index: {$focusIndexStore}
</p> </p>
{#each Array(10) as _, i (i)} {#each Array(10) as _, i (i)}
<div <div
tabindex="0" tabindex="0"
use:registerer use:registerer
class={classNames({ class={classNames({
'bg-red-500': $focusIndexStore === i && $focusWithinStore 'bg-red-500': $focusIndexStore === i && $focusWithinStore
})} })}
> >
<CardPlaceholder {size} index={i} {orientation} /> <CardPlaceholder {size} index={i} {orientation} />
</div> </div>
{/each} {/each}

View File

@@ -1,14 +1,14 @@
<script lang="ts"> <script lang="ts">
import { Container } from '../actions/focusAction'; import { Container } from '../actions/focusAction';
export let container: Container; export let container: Container;
const registerer = container.getHtmlElementRegisterer(); const registerer = container.getHtmlElementRegisterer();
export let handleClick = () => { export let handleClick = () => {
container.focus(); container.focus();
}; };
</script> </script>
<button use:registerer tabindex="0" on:click={handleClick} class="outline-none ring-0"> <button use:registerer tabindex="0" on:click={handleClick} class="outline-none ring-0">
<slot /> <slot />
</button> </button>

View File

@@ -1,22 +1,22 @@
<script lang="ts"> <script lang="ts">
import { Container } from '../actions/focusAction'; import { Container } from '../actions/focusAction';
import Carousel from '../components/Carousel/Carousel.svelte'; import Carousel from '../components/Carousel/Carousel.svelte';
import CarouselPlaceholderItems from '../components/Carousel/CarouselPlaceholderItems.svelte'; import CarouselPlaceholderItems from '../components/Carousel/CarouselPlaceholderItems.svelte';
import classNames from 'classnames'; import classNames from 'classnames';
export let container: Container; export let container: Container;
const focusWithin = container.hasFocusWithin; const focusWithin = container.hasFocusWithin;
</script> </script>
<div <div
class={classNames('flex flex-col', { class={classNames('flex flex-col', {
'bg-green-100': $focusWithin 'bg-green-100': $focusWithin
})} })}
> >
<Carousel> <Carousel>
<CarouselPlaceholderItems {container} /> <CarouselPlaceholderItems {container} />
</Carousel> </Carousel>
<Carousel> <Carousel>
<CarouselPlaceholderItems {container} /> <CarouselPlaceholderItems {container} />
</Carousel> </Carousel>
</div> </div>

View File

@@ -1,8 +1,34 @@
<script lang="ts"> <script lang="ts">
import type { Container } from '../actions/focusAction'; import type { Container } from '../actions/focusAction';
import { settings } from '../stores/settings.store';
import { jellyfinItemsStore } from '../stores/data.store';
import Carousel from '../components/Carousel/Carousel.svelte';
import CarouselPlaceholderItems from '../components/Carousel/CarouselPlaceholderItems.svelte';
export let container: Container; export let parent: Container;
let registerer = container.getChildRegisterer(); let registerer = parent.getChildRegisterer();
settings.update((prev) => ({
...prev,
initialised: true,
jellyfin: {
...prev.jellyfin,
apiKey: import.meta.env.VITE_JELLYFIN_API_KEY,
baseUrl: import.meta.env.VITE_JELLYFIN_BASE_URL,
userId: import.meta.env.VITE_JELLYFIN_USER_ID
}
}));
jellyfinItemsStore.subscribe((items) => {
console.warn('GOT ITEMS', items.data);
});
let asd: HTMLDivElement;
$: console.log('asd', asd);
</script> </script>
<div use:registerer>LibraryPage</div> <div use:registerer bind:this={asd}>
<div>LibraryPage</div>
<Carousel>
<CarouselPlaceholderItems container={parent} />
</Carousel>
</div>

View File

@@ -1,8 +1,8 @@
<script lang="ts"> <script lang="ts">
import type { Container } from '../actions/focusAction'; import type { Container } from '../actions/focusAction';
export let container: Container; export let container: Container;
let registerer = container.getChildRegisterer(); let registerer = container.getChildRegisterer();
</script> </script>
<div use:registerer>ManagePage</div> <div use:registerer>ManagePage</div>

View File

@@ -1,8 +1,8 @@
<script lang="ts"> <script lang="ts">
import type { Container } from '../actions/focusAction'; import type { Container } from '../actions/focusAction';
export let container: Container; export let container: Container;
let registerer = container.getChildRegisterer(); let registerer = container.getChildRegisterer();
</script> </script>
<div use:registerer>MoviesPage</div> <div use:registerer>MoviesPage</div>

View File

@@ -1,8 +1,8 @@
<script lang="ts"> <script lang="ts">
import type { Container } from '../actions/focusAction'; import type { Container } from '../actions/focusAction';
export let container: Container; export let container: Container;
let registerer = container.getChildRegisterer(); let registerer = container.getChildRegisterer();
</script> </script>
<div use:registerer>SearchPage</div> <div use:registerer>SearchPage</div>

View File

@@ -1,8 +1,8 @@
<script lang="ts"> <script lang="ts">
import type { Container } from '../actions/focusAction'; import type { Container } from '../actions/focusAction';
export let container: Container; export let container: Container;
let registerer = container.getChildRegisterer(); let registerer = container.getChildRegisterer();
</script> </script>
<div use:registerer>SeriesPage</div> <div use:registerer>SeriesPage</div>

View File

@@ -1,17 +1,13 @@
import { getJellyfinItems, type JellyfinItem } from '$lib/apis/jellyfin/jellyfinApi'; import { derived, writable } from 'svelte/store';
import { import { settings } from './settings.store';
getRadarrDownloads, import { getJellyfinItems, type JellyfinItem } from '../apis/jellyfin/jellyfinApi';
getRadarrMovies,
type RadarrDownload
} from '$lib/apis/radarr/radarrApi';
import { import {
getSonarrDownloads, getSonarrDownloads,
getSonarrSeries, getSonarrSeries,
type SonarrDownload, type SonarrDownload,
type SonarrSeries type SonarrSeries
} from '$lib/apis/sonarr/sonarrApi'; } from '../apis/sonarr/sonarrApi';
import { derived, writable } from 'svelte/store'; import { getRadarrDownloads, getRadarrMovies, type RadarrDownload } from '../apis/radarr/radarrApi';
import { settings } from './settings.store';
async function waitForSettings() { async function waitForSettings() {
return new Promise((resolve) => { return new Promise((resolve) => {

2
tizen/.gitignore vendored
View File

@@ -1,2 +1,2 @@
.sign/* .sign/*
*.wgt *.wgt

View File

@@ -1,24 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<projectDescription> <projectDescription>
<name>Reiverr</name> <name>Reiverr</name>
<comment></comment> <comment></comment>
<projects> <projects>
</projects> </projects>
<buildSpec> <buildSpec>
<buildCommand> <buildCommand>
<name>json.validation.builder</name> <name>json.validation.builder</name>
<arguments> <arguments>
</arguments> </arguments>
</buildCommand> </buildCommand>
<buildCommand> <buildCommand>
<name>org.tizen.web.project.builder.WebBuilder</name> <name>org.tizen.web.project.builder.WebBuilder</name>
<arguments> <arguments>
</arguments> </arguments>
</buildCommand> </buildCommand>
</buildSpec> </buildSpec>
<natures> <natures>
<nature>json.validation.nature</nature> <nature>json.validation.nature</nature>
<nature>org.eclipse.wst.jsdt.core.jsNature</nature> <nature>org.eclipse.wst.jsdt.core.jsNature</nature>
<nature>org.tizen.web.project.builder.WebNature</nature> <nature>org.tizen.web.project.builder.WebNature</nature>
</natures> </natures>
</projectDescription> </projectDescription>