Partial improvements to typescript types

This commit is contained in:
Aleksi Lassila
2023-07-04 20:50:52 +03:00
parent 109caab74d
commit a6be82c91f
11 changed files with 152 additions and 105 deletions

View File

@@ -1,6 +1,7 @@
<component name="InspectionProjectProfileManager"> <component name="InspectionProjectProfileManager">
<profile version="1.0"> <profile version="1.0">
<option name="myName" value="Project Default" /> <option name="myName" value="Project Default" />
<inspection_tool class="CheckEmptyScriptTag" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="Eslint" enabled="true" level="WARNING" enabled_by_default="true" /> <inspection_tool class="Eslint" enabled="true" level="WARNING" enabled_by_default="true" />
</profile> </profile>
</component> </component>

7
package-lock.json generated
View File

@@ -30,6 +30,7 @@
"@sveltejs/kit": "^1.5.0", "@sveltejs/kit": "^1.5.0",
"@types/axios": "^0.14.0", "@types/axios": "^0.14.0",
"@types/cookie": "^0.5.1", "@types/cookie": "^0.5.1",
"@types/node": "^20.3.3",
"@typescript-eslint/eslint-plugin": "^5.45.0", "@typescript-eslint/eslint-plugin": "^5.45.0",
"@typescript-eslint/parser": "^5.45.0", "@typescript-eslint/parser": "^5.45.0",
"autoprefixer": "^10.4.14", "autoprefixer": "^10.4.14",
@@ -2938,9 +2939,9 @@
"dev": true "dev": true
}, },
"node_modules/@types/node": { "node_modules/@types/node": {
"version": "20.2.6", "version": "20.3.3",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.2.6.tgz", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.3.3.tgz",
"integrity": "sha512-GQBWUtGoefMEOx/vu+emHEHU5aw6JdDoEtZhoBrHFPZbA/YNRFfN996XbBASEWdvmLSLyv9FKYppYGyZjCaq/g==", "integrity": "sha512-wheIYdr4NYML61AjC8MKj/2jrR/kDQri/CIpVoZwldwhnIrD/j9jIU5bJ8yBKuB2VhpFV7Ab6G2XkBjv9r9Zzw==",
"dev": true "dev": true
}, },
"node_modules/@types/pug": { "node_modules/@types/pug": {

View File

@@ -24,6 +24,7 @@
"@sveltejs/kit": "^1.5.0", "@sveltejs/kit": "^1.5.0",
"@types/axios": "^0.14.0", "@types/axios": "^0.14.0",
"@types/cookie": "^0.5.1", "@types/cookie": "^0.5.1",
"@types/node": "^20.3.3",
"@typescript-eslint/eslint-plugin": "^5.45.0", "@typescript-eslint/eslint-plugin": "^5.45.0",
"@typescript-eslint/parser": "^5.45.0", "@typescript-eslint/parser": "^5.45.0",
"autoprefixer": "^10.4.14", "autoprefixer": "^10.4.14",

View File

@@ -0,0 +1,30 @@
import type { LayoutServerLoad } from './$types';
import {
RADARR_API_KEY,
RADARR_BASE_URL,
SONARR_API_KEY,
SONARR_BASE_URL
} from '$env/static/private';
import { PUBLIC_JELLYFIN_API_KEY, PUBLIC_JELLYFIN_URL } from '$env/static/public';
export const load = (async () => {
const isApplicationSetUp =
!!RADARR_API_KEY &&
!!RADARR_BASE_URL &&
!!SONARR_API_KEY &&
!!SONARR_BASE_URL &&
!!PUBLIC_JELLYFIN_API_KEY &&
!!PUBLIC_JELLYFIN_URL;
return {
isApplicationSetUp,
missingEnvironmentVariables: {
RADARR_API_KEY: !RADARR_API_KEY,
RADARR_BASE_URL: !RADARR_BASE_URL,
SONARR_API_KEY: !SONARR_API_KEY,
SONARR_BASE_URL: !SONARR_BASE_URL,
PUBLIC_JELLYFIN_API_KEY: !PUBLIC_JELLYFIN_API_KEY,
PUBLIC_JELLYFIN_URL: !PUBLIC_JELLYFIN_URL
}
};
}) satisfies LayoutServerLoad;

View File

@@ -3,29 +3,26 @@
import Navbar from './components/Navbar/Navbar.svelte'; import Navbar from './components/Navbar/Navbar.svelte';
import VideoPlayer from './components/VideoPlayer/VideoPlayer.svelte'; import VideoPlayer from './components/VideoPlayer/VideoPlayer.svelte';
import { setContext } from 'svelte'; import { setContext } from 'svelte';
import { writable } from 'svelte/store'; import type { LayoutData } from './$types';
import { initialPlayerState } from './components/VideoPlayer/VideoPlayer';
let playerState = writable({ visible: false, jellyfinId: '' }); setContext('player', initialPlayerState);
setContext('player', { export let data: LayoutData;
playerState,
close: () => {
playerState.set({ visible: false, jellyfinId: '' });
},
streamJellyfinId: (id: string) => {
playerState.set({ visible: true, jellyfinId: id });
}
});
</script> </script>
<div class="app"> {#if data.isApplicationSetUp}
<Navbar /> <div class="app">
<main> <Navbar />
<slot /> <main>
</main> <slot />
<VideoPlayer /> </main>
<VideoPlayer />
<!-- <footer>--> <!-- <footer>-->
<!-- <p>visit <a href="https://kit.svelte.dev">kit.svelte.dev</a> to learn SvelteKit</p>--> <!-- <p>visit <a href="https://kit.svelte.dev">kit.svelte.dev</a> to learn SvelteKit</p>-->
<!-- </footer>--> <!-- </footer>-->
</div> </div>
{:else}
<div>Application not set up</div>
{/if}

View File

@@ -1,19 +1,15 @@
<script lang="ts"> <script lang="ts">
import type { Genre, TmdbMovie, TmdbMovieFull } from '$lib/tmdb-api'; import { formatMinutesToTime } from '$lib/utils';
import { formatGenres, formatMinutesToTime } from '$lib/utils';
import classNames from 'classnames'; import classNames from 'classnames';
import { TMDB_IMAGES } from '$lib/constants'; import { TMDB_IMAGES } from '$lib/constants';
import { onMount } from 'svelte'; import { Clock, Star } from 'radix-icons-svelte';
import { fetchTmdbMovie, fetchTmdbMovieImages, TmdbApi } from '$lib/tmdb-api';
import CardPlaceholder from './CardPlaceholder.svelte';
import { Clock, Star, StarFilled } from 'radix-icons-svelte';
export let tmdbId; export let tmdbId: string;
export let title; export let title: string;
export let genres: string[]; export let genres: string[];
export let runtimeMinutes; export let runtimeMinutes: number;
export let completionTime; export let completionTime = '';
export let backdropUrl; export let backdropUrl: string;
export let rating: number; export let rating: number;
export let available = true; export let available = true;
@@ -33,6 +29,7 @@
})} })}
> >
<div style={'width: ' + progress + '%'} class="h-[2px] bg-zinc-200 bottom-0 absolute z-[1]" /> <div style={'width: ' + progress + '%'} class="h-[2px] bg-zinc-200 bottom-0 absolute z-[1]" />
<!-- svelte-ignore a11y-click-events-have-key-events -->
<div <div
on:click={() => window.open('/movie/' + tmdbId, '_self')} on:click={() => window.open('/movie/' + tmdbId, '_self')}
class="h-full w-full opacity-0 hover:opacity-100 transition-opacity flex flex-col justify-between cursor-pointer p-2 px-3 relative z-[1] peer" class="h-full w-full opacity-0 hover:opacity-100 transition-opacity flex flex-col justify-between cursor-pointer p-2 px-3 relative z-[1] peer"

View File

@@ -14,14 +14,15 @@
import { getContext, onDestroy } from 'svelte'; import { getContext, onDestroy } from 'svelte';
import { PUBLIC_JELLYFIN_URL } from '$env/static/public'; import { PUBLIC_JELLYFIN_URL } from '$env/static/public';
import getDeviceProfile from '$lib/jellyfin/playback-profiles'; import getDeviceProfile from '$lib/jellyfin/playback-profiles';
import type { PlayerState, PlayerStateValue } from './VideoPlayer';
const { playerState, close } = getContext('player'); const { playerState, close }: PlayerState = getContext('player');
let video: HTMLVideoElement; let video: HTMLVideoElement;
let stopCallback; let stopCallback: () => void;
let progressInterval; let progressInterval: ReturnType<typeof setInterval>;
onDestroy(() => clearInterval(progressInterval)); onDestroy(() => clearInterval(progressInterval));
const fetchPlaybackInfo = (itemId: string) => const fetchPlaybackInfo = (itemId: string) =>
@@ -35,13 +36,14 @@
hls.loadSource(PUBLIC_JELLYFIN_URL + uri); hls.loadSource(PUBLIC_JELLYFIN_URL + uri);
hls.attachMedia(video); hls.attachMedia(video);
video.play().then(() => { video
console.log(item); .play()
if (item?.UserData?.PlaybackPositionTicks) { .then(() => video.requestFullscreen())
console.log('Setting time'); .then(() => {
video.currentTime = item?.UserData?.PlaybackPositionTicks / 10_000_000; if (item?.UserData?.PlaybackPositionTicks) {
} video.currentTime = item?.UserData?.PlaybackPositionTicks / 10_000_000;
}); }
});
await reportJellyfinPlaybackStarted(itemId, sessionId, mediaSourceId); await reportJellyfinPlaybackStarted(itemId, sessionId, mediaSourceId);
progressInterval = setInterval(() => { progressInterval = setInterval(() => {
reportJellyfinPlaybackProgress( reportJellyfinPlaybackProgress(
@@ -66,7 +68,7 @@
} }
let uiVisible = false; let uiVisible = false;
let timeout; let timeout: ReturnType<typeof setTimeout>;
function handleMouseMove() { function handleMouseMove() {
uiVisible = true; uiVisible = true;
clearTimeout(timeout); clearTimeout(timeout);
@@ -75,7 +77,7 @@
}, 2000); }, 2000);
} }
let state; let state: PlayerStateValue;
playerState.subscribe((s) => (state = s)); playerState.subscribe((s) => (state = s));
$: { $: {

View File

@@ -0,0 +1,17 @@
import { writable } from 'svelte/store';
const initialValue = { visible: false, jellyfinId: '' };
export const playerState = writable(initialValue);
export const initialPlayerState = {
playerState,
close: () => {
playerState.set({ visible: false, jellyfinId: '' });
},
streamJellyfinId: (id: string) => {
playerState.set({ visible: true, jellyfinId: id });
}
};
export type PlayerState = typeof initialPlayerState;
export type PlayerStateValue = typeof initialValue;

View File

@@ -3,6 +3,11 @@ import { RadarrApi } from '$lib/radarr/radarr';
import type { CardProps } from '../components/Card/card'; import type { CardProps } from '../components/Card/card';
import { fetchCardProps } from '../components/Card/card'; import { fetchCardProps } from '../components/Card/card';
interface DownloadingCardProps extends CardProps {
progress: number;
completionTime: string;
}
export const load = (() => { export const load = (() => {
const [downloading, available, unavailable] = getLibraryItems(); const [downloading, available, unavailable] = getLibraryItems();
@@ -22,7 +27,7 @@ export const load = (() => {
async function getLibraryInfo(): Promise<any> {} async function getLibraryInfo(): Promise<any> {}
function getLibraryItems() { function getLibraryItems(): [Promise<DownloadingCardProps[]>, Promise<CardProps[]>, Promise<CardProps[]>] {
const radarrMovies = RadarrApi.get('/api/v3/movie', { const radarrMovies = RadarrApi.get('/api/v3/movie', {
params: {} params: {}
}).then((r) => r.data); }).then((r) => r.data);
@@ -71,17 +76,22 @@ function getLibraryItems() {
); );
}); });
const downloading: Promise<CardProps[]> = downloadingRadarrMovies.then(async (movies) => { const downloading: Promise<DownloadingCardProps[]> = downloadingRadarrMovies.then(
return Promise.all( async (movies) => {
movies return Promise.all(
?.filter((m) => m?.movie?.tmdbId) movies
?.map(async (m) => ({ ?.filter((m) => m?.movie?.tmdbId)
...(await fetchCardProps(m.movie as any)), ?.map(
progress: m.sizeleft && m.size ? ((m.size - m.sizeleft) / m.size) * 100 : 0, async (m) =>
completionTime: m.estimatedCompletionTime ({
})) || [] ...(await fetchCardProps(m.movie as any)),
); progress: m.sizeleft && m.size ? ((m.size - m.sizeleft) / m.size) * 100 : 0,
}); completionTime: m.estimatedCompletionTime
} as DownloadingCardProps)
) || []
);
}
);
return [downloading, available, unavailable]; return [downloading, available, unavailable];
} }

View File

@@ -1,7 +1,6 @@
<script lang="ts"> <script lang="ts">
import type { PageData } from './$types'; import type { PageData } from './$types';
import Card from '../components/Card/Card.svelte'; import Card from '../components/Card/Card.svelte';
import { TMDB_IMAGES } from '$lib/constants.js';
import CardPlaceholder from '../components/Card/CardPlaceholder.svelte'; import CardPlaceholder from '../components/Card/CardPlaceholder.svelte';
import { formatSize } from '$lib/utils.js'; import { formatSize } from '$lib/utils.js';
export let data: PageData; export let data: PageData;
@@ -11,8 +10,8 @@
const headerStyle = 'uppercase tracking-widest font-bold text-center mt-2'; const headerStyle = 'uppercase tracking-widest font-bold text-center mt-2';
</script> </script>
<div class="bg-black pt-24 pb-8 px-8"> <div class="pt-24 pb-8 px-8">
<div class="grid grid-cols-1 lg:grid-cols-2 items-center justify-center gap-4"> <div class="bg-black grid grid-cols-1 lg:grid-cols-2 items-center justify-center gap-4">
<div class="bg-highlight-dim relative w-full m-auto p-3 px-4 rounded-xl overflow-hidden"> <div class="bg-highlight-dim relative w-full m-auto p-3 px-4 rounded-xl overflow-hidden">
<div class="absolute left-0 inset-y-0 w-[70%] bg-[#ffffff22]" /> <div class="absolute left-0 inset-y-0 w-[70%] bg-[#ffffff22]" />
<div class="relative z-[1] flex justify-between items-center"> <div class="relative z-[1] flex justify-between items-center">
@@ -62,54 +61,46 @@
</div> </div>
</div> </div>
<div> <div class="py-8 backdrop-blur-2xl bg-stone-900 px-8 flex flex-col gap-4">
<div class="py-8 backdrop-blur-2xl bg-darken px-8 flex flex-col gap-4"> <!-- Contains all the titles available locally, the ones already watched previously (greyed out at the-->
<!-- Contains all the titles available locally, the ones already watched previously (greyed out at the--> <!-- bottom), and the ones that are in some sort of watchlist and not available via any source.-->
<!-- bottom), and the ones that are in some sort of watchlist and not available via any source.-->
{#await Promise.all( [data.streamed.available, data.streamed.unavailable, data.streamed.downloading] )} {#await Promise.all( [data.streamed.available, data.streamed.unavailable, data.streamed.downloading] )}
<div class={posterGridStyle}>
{#each [...Array(20).keys()] as index (index)}
<CardPlaceholder {index} />
{/each}
</div>
{:then [available, unavailable, downloading]}
{#if downloading.length > 0}
<h1 class={headerStyle}>Downloading</h1>
<div class={posterGridStyle}> <div class={posterGridStyle}>
{#each [...Array(20).keys()] as index (index)} {#each downloading as movie (movie)}
<CardPlaceholder {index} /> <Card {...movie} progress={movie.progress} progressType="downloading" available={false} />
{/each} {/each}
</div> </div>
{:then [available, unavailable, downloading]} {/if}
{#if downloading.length > 0}
<h1 class={headerStyle}>Downloading</h1>
<div class={posterGridStyle}>
{#each downloading as movie (movie.tmdbId)}
<Card
{...movie}
progress={movie.progress}
progressType="downloading"
available={false}
type="download"
/>
{/each}
</div>
{/if}
{#if available.length > 0} {#if available.length > 0}
<h1 class={headerStyle}>Available</h1> <h1 class={headerStyle}>Available</h1>
<div class={posterGridStyle}> <div class={posterGridStyle}>
{#each available as movie (movie.tmdbId)} {#each available as movie (movie.tmdbId)}
<Card {...movie} randomProgress={false} /> <Card {...movie} randomProgress={false} />
{/each} {/each}
</div> </div>
{/if} {/if}
{#if unavailable.length > 0} {#if unavailable.length > 0}
<h1 class={headerStyle}>Unavailable</h1> <h1 class={headerStyle}>Unavailable</h1>
<div class={posterGridStyle}> <div class={posterGridStyle}>
{#each unavailable as movie (movie.tmdbId)} {#each unavailable as movie (movie.tmdbId)}
<Card {...movie} available={false} /> <Card {...movie} available={false} />
{/each} {/each}
</div> </div>
{/if} {/if}
{#if watched.length > 0} {#if watched.length > 0}
<h1 class={headerStyle}>Watched</h1> <h1 class={headerStyle}>Watched</h1>
{/if} {/if}
{/await} {/await}
</div>
</div> </div>