mirror of
https://github.com/aleksilassila/reiverr.git
synced 2026-04-21 16:25:11 +02:00
Work in progress source stats components
This commit is contained in:
@@ -3,5 +3,10 @@
|
|||||||
@tailwind utilities;
|
@tailwind utilities;
|
||||||
|
|
||||||
a {
|
a {
|
||||||
@apply hover:text-amber-200;
|
@apply hover:text-amber-200;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.placeholder {
|
||||||
|
@apply bg-[#ffffff11];
|
||||||
|
@apply animate-pulse;
|
||||||
|
}
|
||||||
|
|||||||
@@ -29,6 +29,11 @@ export const RadarrApi = createClient<paths>({
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const getRadarrMovies = () =>
|
||||||
|
RadarrApi.get('/api/v3/movie', {
|
||||||
|
params: {}
|
||||||
|
}).then((r) => r.data);
|
||||||
|
|
||||||
export const requestRadarrMovie = () => request(getRadarrMovie);
|
export const requestRadarrMovie = () => request(getRadarrMovie);
|
||||||
|
|
||||||
export const getRadarrMovie = (tmdbId: string) =>
|
export const getRadarrMovie = (tmdbId: string) =>
|
||||||
|
|||||||
@@ -7,6 +7,15 @@ import {
|
|||||||
} from '$env/static/private';
|
} from '$env/static/private';
|
||||||
import { PUBLIC_JELLYFIN_API_KEY, PUBLIC_JELLYFIN_URL } from '$env/static/public';
|
import { PUBLIC_JELLYFIN_API_KEY, PUBLIC_JELLYFIN_URL } from '$env/static/public';
|
||||||
|
|
||||||
|
export type MissingEnvironmentVariables = {
|
||||||
|
RADARR_API_KEY: boolean;
|
||||||
|
RADARR_BASE_URL: boolean;
|
||||||
|
SONARR_API_KEY: boolean;
|
||||||
|
SONARR_BASE_URL: boolean;
|
||||||
|
PUBLIC_JELLYFIN_API_KEY: boolean;
|
||||||
|
PUBLIC_JELLYFIN_URL: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
export const load = (async () => {
|
export const load = (async () => {
|
||||||
const isApplicationSetUp =
|
const isApplicationSetUp =
|
||||||
!!RADARR_API_KEY &&
|
!!RADARR_API_KEY &&
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
import { setContext } from 'svelte';
|
import { setContext } from 'svelte';
|
||||||
import type { LayoutData } from './$types';
|
import type { LayoutData } from './$types';
|
||||||
import { initialPlayerState } from './components/VideoPlayer/VideoPlayer';
|
import { initialPlayerState } from './components/VideoPlayer/VideoPlayer';
|
||||||
|
import SetupRequired from './components/SetupRequired/SetupRequired.svelte';
|
||||||
|
|
||||||
setContext('player', initialPlayerState);
|
setContext('player', initialPlayerState);
|
||||||
|
|
||||||
@@ -24,5 +25,5 @@
|
|||||||
<!-- </footer>-->
|
<!-- </footer>-->
|
||||||
</div>
|
</div>
|
||||||
{:else}
|
{:else}
|
||||||
<div>Application not set up</div>
|
<SetupRequired missingEnvironmentVariables={data.missingEnvironmentVariables} />
|
||||||
{/if}
|
{/if}
|
||||||
|
|||||||
@@ -6,13 +6,10 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
class={classNames(
|
class={classNames('rounded overflow-hidden shadow-2xl placeholder shrink-0 aspect-video', {
|
||||||
'rounded overflow-hidden shadow-2xl bg-[#ffffff11] animate-pulse shrink-0 aspect-video',
|
'h-40': type === 'normal',
|
||||||
{
|
'h-60': type === 'large',
|
||||||
'h-40': type === 'normal',
|
'w-full': type === 'dynamic'
|
||||||
'h-60': type === 'large',
|
})}
|
||||||
'w-full': type === 'dynamic'
|
|
||||||
}
|
|
||||||
)}
|
|
||||||
style={'animation-delay: ' + ((index * 100) % 2000) + 'ms;'}
|
style={'animation-delay: ' + ((index * 100) % 2000) + 'ms;'}
|
||||||
/>
|
/>
|
||||||
|
|||||||
16
src/routes/components/SetupRequired/SetupRequired.svelte
Normal file
16
src/routes/components/SetupRequired/SetupRequired.svelte
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
export let missingEnvironmentVariables: Record<string, boolean>;
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="min-h-screen flex flex-col max-w-screen-2xl mx-auto p-4 md:p-8 lg:px-32 gap-2">
|
||||||
|
<h1 class="font-bold text-3xl">Welcome to Reiverr</h1>
|
||||||
|
<p>
|
||||||
|
It seems like the application is missing some environment variables that are necessary for the
|
||||||
|
application to function. Please provide the following environment variables:
|
||||||
|
</p>
|
||||||
|
<ul class="flex flex-col gap-1">
|
||||||
|
{#each Object.keys(missingEnvironmentVariables).filter((k) => missingEnvironmentVariables[k]) as variableName}
|
||||||
|
<code class="bg-highlight-dim p-0.5 px-2 rounded self-start">{variableName}</code>
|
||||||
|
{/each}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
@@ -1,27 +1,36 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { formatSize } from '$lib/utils.js';
|
import { formatSize } from '$lib/utils.js';
|
||||||
|
import { onMount } from 'svelte';
|
||||||
|
import StatsPlaceholder from './StatsPlaceholder.svelte';
|
||||||
|
import StatsContainer from './StatsContainer.svelte';
|
||||||
|
import RadarrIcon from '../svgs/RadarrIcon.svelte';
|
||||||
|
|
||||||
|
export let large = false;
|
||||||
|
|
||||||
|
let statsRequest: Promise<{ moviesAmount: number }> = new Promise((_) => {}) as any;
|
||||||
|
|
||||||
|
onMount(() => {
|
||||||
|
statsRequest = fetch('/radarr/stats')
|
||||||
|
.then((res) => res.json())
|
||||||
|
.then((data) => ({
|
||||||
|
moviesAmount: data?.movies?.length
|
||||||
|
}));
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="bg-highlight-dim relative w-full m-auto p-3 px-4 rounded-xl overflow-hidden">
|
{#await statsRequest}
|
||||||
<div class="absolute left-0 inset-y-0 w-[70%] bg-[#ffffff22]" />
|
<StatsPlaceholder {large} />
|
||||||
<div class="relative z-[1] flex justify-between items-center">
|
{:then { moviesAmount }}
|
||||||
<div class="flex flex-col">
|
<StatsContainer
|
||||||
<h3 class="text-zinc-400 font-medium text-xs tracking-wider">Mac-Mini</h3>
|
{large}
|
||||||
<a href="/" class="text-zinc-200 font-bold text-xl tracking-wide">Radarr</a>
|
title="Radarr"
|
||||||
</div>
|
subtitle="Movies Provider"
|
||||||
<div class="flex gap-8">
|
stats={[
|
||||||
<div class="flex flex-col items-center gap-0.5">
|
{ title: 'Movies', value: String(moviesAmount) },
|
||||||
<h3 class="uppercase text-zinc-400 font-medium text-xs tracking-wider">Movies</h3>
|
{ title: 'Space Taken', value: formatSize(120_000_000_000) },
|
||||||
<div class="font-medium text-sm">30</div>
|
{ title: 'Space Left', value: formatSize(50_000_000_000) }
|
||||||
</div>
|
]}
|
||||||
<div class="flex flex-col items-center gap-0.5">
|
>
|
||||||
<h3 class="uppercase text-zinc-400 font-medium text-xs tracking-wider">Space Taken</h3>
|
<RadarrIcon slot="icon" class="absolute opacity-20 p-4 h-full inset-y-0 right-2" />
|
||||||
<div class="font-medium text-sm">{formatSize(120_000_000_000)}</div>
|
</StatsContainer>
|
||||||
</div>
|
{/await}
|
||||||
<div class="flex flex-col items-center gap-0.5">
|
|
||||||
<h3 class="uppercase text-zinc-400 font-medium text-xs tracking-wider">Space Left</h3>
|
|
||||||
<div class="font-medium text-sm">{formatSize(50_000_000_000)}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|||||||
@@ -1,27 +1,37 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { formatSize } from '$lib/utils.js';
|
import { formatSize } from '$lib/utils.js';
|
||||||
|
import { onMount } from 'svelte';
|
||||||
|
import StatsPlaceholder from './StatsPlaceholder.svelte';
|
||||||
|
import StatsContainer from './StatsContainer.svelte';
|
||||||
|
import SonarrIcon from '../svgs/SonarrIcon.svelte';
|
||||||
|
|
||||||
|
export let large = false;
|
||||||
|
|
||||||
|
let statsRequest: Promise<{ moviesAmount: number }> = new Promise((_) => {}) as any;
|
||||||
|
|
||||||
|
onMount(() => {
|
||||||
|
statsRequest = fetch('/radarr/stats')
|
||||||
|
.then((res) => res.json())
|
||||||
|
.then((data) => ({
|
||||||
|
moviesAmount: data?.movies?.length
|
||||||
|
}));
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="bg-highlight-dim relative w-full m-auto p-3 px-4 rounded-xl overflow-hidden">
|
{#await statsRequest}
|
||||||
<div class="absolute left-0 inset-y-0 w-[70%] bg-[#ffffff22]" />
|
<StatsPlaceholder {large} />
|
||||||
<div class="relative z-[1] flex justify-between items-center">
|
{:then { moviesAmount }}
|
||||||
<div class="flex flex-col">
|
<StatsContainer
|
||||||
<h3 class="text-zinc-400 font-medium text-xs tracking-wider">Mac-Mini</h3>
|
{large}
|
||||||
<a href="/" class="text-zinc-200 font-bold text-xl tracking-wide">Sonarr</a>
|
title="Sonarr"
|
||||||
</div>
|
subtitle="Shows Provider"
|
||||||
<div class="flex gap-8">
|
stats={[
|
||||||
<div class="flex flex-col items-center gap-0.5">
|
{ title: 'Movies', value: String(moviesAmount) },
|
||||||
<h3 class="uppercase text-zinc-400 font-medium text-xs tracking-wider">Movies</h3>
|
{ title: 'Space Taken', value: formatSize(120_000_000_000) },
|
||||||
<div class="font-medium text-sm">30</div>
|
{ title: 'Space Left', value: formatSize(50_000_000_000) }
|
||||||
</div>
|
]}
|
||||||
<div class="flex flex-col items-center gap-0.5">
|
color="#8aacfd21"
|
||||||
<h3 class="uppercase text-zinc-400 font-medium text-xs tracking-wider">Space Taken</h3>
|
>
|
||||||
<div class="font-medium text-sm">{formatSize(120_000_000_000)}</div>
|
<SonarrIcon slot="icon" class="absolute opacity-20 p-4 h-full inset-y-0 right-2" />
|
||||||
</div>
|
</StatsContainer>
|
||||||
<div class="flex flex-col items-center gap-0.5">
|
{/await}
|
||||||
<h3 class="uppercase text-zinc-400 font-medium text-xs tracking-wider">Space Left</h3>
|
|
||||||
<div class="font-medium text-sm">{formatSize(50_000_000_000)}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|||||||
48
src/routes/components/SourceStats/StatsContainer.svelte
Normal file
48
src/routes/components/SourceStats/StatsContainer.svelte
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import classNames from 'classnames';
|
||||||
|
import RadarrIcon from '../svgs/RadarrIcon.svelte';
|
||||||
|
|
||||||
|
type Stat = {
|
||||||
|
title: string;
|
||||||
|
value: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export let title: string;
|
||||||
|
export let subtitle: string;
|
||||||
|
export let stats: Stat[] = [];
|
||||||
|
|
||||||
|
export let color: string = '#fde68a20';
|
||||||
|
export let large = false;
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div
|
||||||
|
class={classNames('relative w-full mx-auto px-6 rounded-xl overflow-hidden', {
|
||||||
|
'h-16': !large,
|
||||||
|
'h-28': large
|
||||||
|
})}
|
||||||
|
style={'background-color: ' + color + ';'}
|
||||||
|
>
|
||||||
|
<div class="absolute left-0 inset-y-0 w-[70%] bg-[#ffffff22]" />
|
||||||
|
{#if large}
|
||||||
|
<slot name="icon" />
|
||||||
|
{/if}
|
||||||
|
<div
|
||||||
|
class={classNames('relative z-[1] flex flex-1 h-full', {
|
||||||
|
'justify-between items-center': !large,
|
||||||
|
'flex-col justify-center gap-2': large
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
<div class="flex flex-col">
|
||||||
|
<h3 class="text-zinc-400 font-medium text-xs tracking-wider">{subtitle}</h3>
|
||||||
|
<a href="/" class="text-zinc-200 font-bold text-xl tracking-wide">{title}</a>
|
||||||
|
</div>
|
||||||
|
<div class="flex gap-8">
|
||||||
|
{#each stats as { title, value }}
|
||||||
|
<div class="flex flex-col items-center gap-0.5">
|
||||||
|
<h3 class="uppercase text-zinc-400 font-medium text-xs tracking-wider">{title}</h3>
|
||||||
|
<div class="font-medium text-sm">{value}</div>
|
||||||
|
</div>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
12
src/routes/components/SourceStats/StatsPlaceholder.svelte
Normal file
12
src/routes/components/SourceStats/StatsPlaceholder.svelte
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import classNames from 'classnames';
|
||||||
|
|
||||||
|
export let large = false;
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div
|
||||||
|
class={classNames('placeholder w-full rounded-xl', {
|
||||||
|
'h-16': !large,
|
||||||
|
'h-28': large
|
||||||
|
})}
|
||||||
|
/>
|
||||||
3
src/routes/components/Utils/WidthLimited.svelte
Normal file
3
src/routes/components/Utils/WidthLimited.svelte
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
<div class={$$restProps.class + ' mx-auto max-w-screen-2xl'}>
|
||||||
|
<slot />
|
||||||
|
</div>
|
||||||
@@ -1,4 +1,7 @@
|
|||||||
<svg viewBox="0 0 216.7 216.9" xmlns="http://www.w3.org/2000/svg" class="h-7 w-7 flex-shrink-0"
|
<svg
|
||||||
|
viewBox="0 0 216.7 216.9"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
class={$$restProps.class || 'h-10 w-10 flex-shrink-0'}
|
||||||
><path
|
><path
|
||||||
d="M216.7 108.45c0 29.833-10.533 55.4-31.6 76.7-.7.833-1.483 1.6-2.35 2.3a92.767 92.767 0 0 1-11 9.25c-18.267 13.467-39.367 20.2-63.3 20.2-23.967 0-45.033-6.733-63.2-20.2-4.8-3.4-9.3-7.25-13.5-11.55-16.367-16.266-26.417-35.167-30.15-56.7-.733-4.2-1.217-8.467-1.45-12.8-.1-2.4-.15-4.8-.15-7.2 0-2.533.05-4.95.15-7.25 0-.233.066-.467.2-.7 1.567-26.6 12.033-49.583 31.4-68.95C53.05 10.517 78.617 0 108.45 0c29.933 0 55.484 10.517 76.65 31.55 21.067 21.433 31.6 47.067 31.6 76.9z"
|
d="M216.7 108.45c0 29.833-10.533 55.4-31.6 76.7-.7.833-1.483 1.6-2.35 2.3a92.767 92.767 0 0 1-11 9.25c-18.267 13.467-39.367 20.2-63.3 20.2-23.967 0-45.033-6.733-63.2-20.2-4.8-3.4-9.3-7.25-13.5-11.55-16.367-16.266-26.417-35.167-30.15-56.7-.733-4.2-1.217-8.467-1.45-12.8-.1-2.4-.15-4.8-.15-7.2 0-2.533.05-4.95.15-7.25 0-.233.066-.467.2-.7 1.567-26.6 12.033-49.583 31.4-68.95C53.05 10.517 78.617 0 108.45 0c29.933 0 55.484 10.517 76.65 31.55 21.067 21.433 31.6 47.067 31.6 76.9z"
|
||||||
clip-rule="evenodd"
|
clip-rule="evenodd"
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 2.3 KiB |
@@ -1,5 +1,5 @@
|
|||||||
import type { PageServerLoad } from './$types';
|
import type { PageServerLoad } from './$types';
|
||||||
import { RadarrApi } from '$lib/radarr/radarr';
|
import { RadarrApi, getRadarrMovies } 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';
|
||||||
|
|
||||||
@@ -25,12 +25,10 @@ export const load = (() => {
|
|||||||
};
|
};
|
||||||
}) satisfies PageServerLoad;
|
}) satisfies PageServerLoad;
|
||||||
|
|
||||||
async function getLibraryInfo(): Promise<any> {}
|
async function getLibraryInfo(): Promise<any> { }
|
||||||
|
|
||||||
function getLibraryItems(): [Promise<DownloadingCardProps[]>, Promise<CardProps[]>, Promise<CardProps[]>] {
|
function getLibraryItems(): [Promise<DownloadingCardProps[]>, Promise<CardProps[]>, Promise<CardProps[]>] {
|
||||||
const radarrMovies = RadarrApi.get('/api/v3/movie', {
|
const radarrMovies = getRadarrMovies();
|
||||||
params: {}
|
|
||||||
}).then((r) => r.data);
|
|
||||||
|
|
||||||
const downloadingRadarrMovies = RadarrApi.get('/api/v3/queue', {
|
const downloadingRadarrMovies = RadarrApi.get('/api/v3/queue', {
|
||||||
params: {
|
params: {
|
||||||
@@ -83,11 +81,11 @@ function getLibraryItems(): [Promise<DownloadingCardProps[]>, Promise<CardProps[
|
|||||||
?.filter((m) => m?.movie?.tmdbId)
|
?.filter((m) => m?.movie?.tmdbId)
|
||||||
?.map(
|
?.map(
|
||||||
async (m) =>
|
async (m) =>
|
||||||
({
|
({
|
||||||
...(await fetchCardProps(m.movie as any)),
|
...(await fetchCardProps(m.movie as any)),
|
||||||
progress: m.sizeleft && m.size ? ((m.size - m.sizeleft) / m.size) * 100 : 0,
|
progress: m.sizeleft && m.size ? ((m.size - m.sizeleft) / m.size) * 100 : 0,
|
||||||
completionTime: m.estimatedCompletionTime
|
completionTime: m.estimatedCompletionTime
|
||||||
} as DownloadingCardProps)
|
} as DownloadingCardProps)
|
||||||
) || []
|
) || []
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
13
src/routes/radarr/stats/+server.ts
Normal file
13
src/routes/radarr/stats/+server.ts
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
import { RadarrApi, getRadarrMovies } from '$lib/radarr/radarr';
|
||||||
|
import { json } from '@sveltejs/kit';
|
||||||
|
import type { RequestHandler } from '@sveltejs/kit';
|
||||||
|
|
||||||
|
export type RadarrStatsDto = {
|
||||||
|
movies: Awaited<ReturnType<typeof getRadarrMovies>>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const GET = (async () => {
|
||||||
|
const radarrMovies = await getRadarrMovies();
|
||||||
|
|
||||||
|
return json({ movies: radarrMovies });
|
||||||
|
}) satisfies RequestHandler;
|
||||||
@@ -2,41 +2,14 @@
|
|||||||
import RadarrIcon from '../components/svgs/RadarrIcon.svelte';
|
import RadarrIcon from '../components/svgs/RadarrIcon.svelte';
|
||||||
import SonarrIcon from '../components/svgs/SonarrIcon.svelte';
|
import SonarrIcon from '../components/svgs/SonarrIcon.svelte';
|
||||||
import { formatSize } from '$lib/utils.js';
|
import { formatSize } from '$lib/utils.js';
|
||||||
|
import RadarrStats from '../components/SourceStats/RadarrStats.svelte';
|
||||||
|
import SonarrStats from '../components/SourceStats/SonarrStats.svelte';
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="pt-24 px-8 min-h-screen flex justify-center">
|
<div class="pt-24 px-8 min-h-screen flex justify-center">
|
||||||
<div class="flex flex-col gap-4 max-w-3xl flex-1">
|
<div class="flex flex-col gap-4 max-w-3xl flex-1">
|
||||||
<div
|
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||||
class="bg-highlight-dim relative w-full p-3 px-4 rounded-xl overflow-hidden group pointer"
|
<RadarrStats large />
|
||||||
on:click={() => (window.location.href = '/')}
|
|
||||||
>
|
|
||||||
<div class="absolute left-0 inset-y-0 w-[70%] bg-[#ffffff22]" />
|
|
||||||
<RadarrIcon class="absolute opacity-20 p-4 h-full inset-y-0 right-2" />
|
|
||||||
<div class="relative z-[1] flex flex-col justify-between gap-2">
|
|
||||||
<div class="flex flex-col flex-1">
|
|
||||||
<h3 class="text-zinc-400 font-medium text-xs tracking-wider">Mac-Mini</h3>
|
|
||||||
<a
|
|
||||||
href="/"
|
|
||||||
class="text-zinc-200 font-bold text-xl tracking-wide group-hover:text-amber-200"
|
|
||||||
>Radarr</a
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
<div class="flex gap-8">
|
|
||||||
<div class="flex flex-col items-center gap-0.5">
|
|
||||||
<h3 class="uppercase text-zinc-400 font-medium text-xs tracking-wider">Movies</h3>
|
|
||||||
<div class="font-medium text-sm">30</div>
|
|
||||||
</div>
|
|
||||||
<div class="flex flex-col items-center gap-0.5">
|
|
||||||
<h3 class="uppercase text-zinc-400 font-medium text-xs tracking-wider">Space Taken</h3>
|
|
||||||
<div class="font-medium text-sm">{formatSize(120_000_000_000)}</div>
|
|
||||||
</div>
|
|
||||||
<div class="flex flex-col items-center gap-0.5">
|
|
||||||
<h3 class="uppercase text-zinc-400 font-medium text-xs tracking-wider">Space Left</h3>
|
|
||||||
<div class="font-medium text-sm">{formatSize(50_000_000_000)}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div
|
<div
|
||||||
class="border-zinc-800 border-2 border-dashed relative w-full p-3 px-4 rounded-xl overflow-hidden text-zinc-500 text-center flex flex-col gap-1"
|
class="border-zinc-800 border-2 border-dashed relative w-full p-3 px-4 rounded-xl overflow-hidden text-zinc-500 text-center flex flex-col gap-1"
|
||||||
>
|
>
|
||||||
@@ -46,6 +19,7 @@
|
|||||||
variables.
|
variables.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
<SonarrStats large />
|
||||||
</div>
|
</div>
|
||||||
<!-- <div>Sources</div>-->
|
<!-- <div>Sources</div>-->
|
||||||
<!-- <div>Streaming services</div>-->
|
<!-- <div>Streaming services</div>-->
|
||||||
|
|||||||
Reference in New Issue
Block a user