Loads of changes, experimental radarr queuing and integration

This commit is contained in:
Aleksi Lassila
2023-06-16 03:03:57 +03:00
parent fe422a72b6
commit 3429d2c5ee
23 changed files with 486 additions and 96 deletions

View File

@@ -11,7 +11,7 @@
<div
class={classNames('fixed inset-0 bg-[#00000088] justify-center items-center z-20', {
hidden: !visible,
flex: visible
'flex overflow-hidden': visible
})}
on:click|self={close}
>

View File

@@ -6,7 +6,6 @@
import { TmdbApi } from '$lib/tmdb-api';
import type { MultiSearchResponse } from '$lib/tmdb-api';
import { TMDB_IMAGES } from '$lib/constants';
import { onMount } from 'svelte';
export let visible = false;
let searchValue = '';
@@ -41,17 +40,10 @@
})
.finally(() => (fetching = false));
};
onMount(() => {
searchValue = 'incepti';
searchMovie('incepti');
});
$: console.log(results);
</script>
<Modal {visible} {close}>
<ModalContent {close}>
<ModalContent>
<div class="flex text-zinc-200 items-center p-3 px-5 gap-4 border-b border-zinc-700">
<MagnifyingGlass size="20" class="text-zinc-400" />
<input

View File

@@ -0,0 +1,68 @@
<script lang="ts">
import Modal from '../Modal/Modal.svelte';
import ModalContent from '../Modal/ModalContent.svelte';
import { getReleases, queueRelease } from '$lib/radarr/radarr';
import { formatSize } from '$lib/utils';
import IconButton from '../IconButton.svelte';
import { Download } from 'radix-icons-svelte';
import { createEventDispatcher } from 'svelte';
const dispatch = createEventDispatcher();
export let visible = false;
function close() {
visible = false;
}
export let radarrId;
const { data: releases, load: loadReleases, didLoad: didLoadReleases } = getReleases();
const { data, load: downloadRelease } = queueRelease();
$: if (visible) loadReleases(radarrId);
let releasesFiltered = [];
let releasesSkipped = 0;
releases.subscribe((releases: any[]) => {
if (!releases) return;
releasesFiltered = releases
.filter((release) => {
if (release.seeders < 5) return false;
else return true;
})
.sort((a, b) => b.size - a.size)
.slice(0, 5);
releasesSkipped = releases.length - releasesFiltered.length;
});
function handleDownload(guid) {
downloadRelease(guid).then(() => {
dispatch('download');
});
}
</script>
<Modal {visible} {close}>
<ModalContent>
{#if releasesFiltered?.length}
Releases:
<div class="flex flex-col">
{#each releasesFiltered as release}
<div class="flex items-center justify-between">
<div>{formatSize(release.size)}</div>
<IconButton on:click={() => handleDownload(release.guid)}>
<Download size="20" />
</IconButton>
</div>
{/each}
</div>
{#if releasesSkipped > 0}
<div>{releasesSkipped} releases hidden</div>
{/if}
{:else if !$didLoadReleases}
Loading...
{:else}
No releases found
{/if}
</ModalContent>
</Modal>

View File

@@ -1,14 +1,18 @@
<script lang="ts">
import type { MovieResource } from '$lib/types';
import { ChevronDown, Clock } from 'radix-icons-svelte';
import { onMount } from 'svelte';
import classNames from 'classnames';
import { fade } from 'svelte/transition';
import { TMDB_IMAGES } from '$lib/constants';
import Button from '../Ui/Button.svelte';
import type { MovieResource } from '$lib/radarr/radarr';
import type { TmdbMovie, TmdbMovieFull } from '$lib/tmdb-api';
export let resource: MovieResource;
export let trailer = true;
export let remoteResource: any;
export let remoteResource: TmdbMovieFull;
let tmdbPageUrl = '';
$: tmdbPageUrl = 'https://www.themoviedb.org/movie/' + remoteResource.id;
let video: any = remoteResource.videos.filter(
(v) => v.site === 'YouTube' && v.type === 'Trailer'
@@ -58,6 +62,7 @@
trailerStartTime = Date.now();
}
}, 2500);
console.log(remoteResource);
});
</script>
@@ -75,7 +80,7 @@
transition:fade
src={'https://www.youtube.com/embed/' +
video.key +
'?autoplay=1&mute=1&loop=1&color=white&controls=0&modestbranding=1&playsinline=1&rel=0&enablejsapi=1'}
'?autoplay=1&mute=1&loop=1&controls=0&modestbranding=1&playsinline=1&rel=0&enablejsapi=1'}
title="YouTube video player"
frameborder="0"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
@@ -131,22 +136,19 @@
</div>
<div class="flex gap-6 mt-10">
<div class="flex gap-1">
<button
class="bg-white border-2 border-white hover:bg-amber-400 hover:border-amber-400 transition-colors text-zinc-900 px-8 py-3.5 uppercase tracking-widest font-extrabold cursor-pointer text-xs"
style={opacityStyle}>Stream</button
>
<Button size="lg" style={opacityStyle}>Stream</Button>
<div
class="hidden items-center justify-center border-2 border-white w-10 cursor-pointer hover:bg-white hover:text-zinc-900 transition-colors"
>
<ChevronDown size="20" />
</div>
</div>
<button
<Button
size="lg"
type="secondary"
on:mouseover={() => (focusTrailer = trailer)}
on:mouseleave={() => (focusTrailer = false)}
on:click={openTrailer}
class="border-2 border-white cursor-pointer transition-colors px-8 py-3.5 uppercase tracking-widest font-semibold text-xs hover:bg-white hover:text-black opacity-100"
>Watch Trailer</button
on:click={openTrailer}>Watch Trailer</Button
>
</div>
</div>
@@ -165,9 +167,13 @@
</div>
</div>
<div class="tracking-widest font-extralight text-sm">Currently <b>Streaming</b></div>
<div class="tracking-widest font-extralight text-sm">
<b>{remoteResource.vote_average}</b> TMDB
</div>
<a
href={'https://www.themoviedb.org/movie/' + remoteResource.id}
target="_blank"
class="tracking-widest font-extralight text-sm"
>
<b>{remoteResource.vote_average.toFixed(1)}</b> TMDB
</a>
<div class="flex mt-4">
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" class="text-white w-4"
><g
@@ -183,8 +189,17 @@
<h3 class="text-xs tracking-wide uppercase">Starring</h3>
<div class="flex flex-col gap-1">
{#each remoteResource.credits.slice(0, 5) as a}
<div class="tracking-widest font-extralight text-sm">{a.name}</div>
<a
href={'https://www.themoviedb.org/person/' + a.id}
target="_blank"
class="tracking-widest font-extralight text-sm">{a.name}</a
>
{/each}
<a
href={'https://www.themoviedb.org/movie/' + remoteResource.id + '/cast'}
target="_blank"
class="tracking-widest font-extralight text-sm">View all...</a
>
</div>
</div>
<slot name="page-controls" />

View File

@@ -0,0 +1,124 @@
<script lang="ts">
import { onMount } from 'svelte';
import { formatSize } from '$lib/utils';
import Button from '../Ui/Button.svelte';
import { DotFilled, Plus } from 'radix-icons-svelte';
import RequestModal from '../RequestModal/RequestModal.svelte';
import { addRadarrMovie, getQueuedById, getRadarrMovie } from '$lib/radarr/radarr';
import IconButton from '../IconButton.svelte';
import { formatMinutes } from '$lib/utils.js';
import classNames from 'classnames';
let isRequestModalVisible = false;
export let tmdbId: string;
const { data: localResource, load, didLoad } = getRadarrMovie();
const { data: queueResponse, load: loadQueued } = getQueuedById();
const { data: addMovieResponse, loading: addMovieLoading, load: addToRadarr } = addRadarrMovie();
function refreshRadarrMovie() {
if (tmdbId) load(tmdbId);
}
onMount(() => {
refreshRadarrMovie();
});
addMovieResponse.subscribe(() => {
if ($addMovieResponse) refreshRadarrMovie();
});
const headerStyle = 'uppercase tracking-widest font-bold';
function openRequestModal() {
if ($localResource) isRequestModalVisible = true;
}
localResource.subscribe((resource) => {
if (resource?.id) loadQueued(resource.id);
});
</script>
<div class="flex flex-col gap-8 p-8">
{#if !$didLoad}
Loading...
{:else if !$localResource?.movieFile}
<div>
<h1 class="text-lg mb-1 font-medium tracking-wide">No sources found</h1>
<p class="text-zinc-300">
No local or remote sources found for this title. You can configure your sources on the <a
href="/sources"
class="text-amber-200 hover:text-amber-100">sources</a
> page.
</p>
</div>
{/if}
{#if $localResource}
<div class="">
<div class="flex items-center gap-2 mb-4">
<div class={headerStyle}>Local Library</div>
<IconButton on:click={openRequestModal}>
<Plus size="20" />
</IconButton>
</div>
{#each $queueResponse || [] as downloadingFile}
<div
class={classNames('border-l-2 p-1 px-4 flex justify-between items-center py-1', {
'border-purple-400': downloadingFile.status === 'downloading',
'border-amber-400': downloadingFile.status !== 'downloading'
})}
>
<div class="flex gap-1 items-center">
<b class="">{downloadingFile.quality.quality.resolution}p</b>
<DotFilled class="text-zinc-300" />
<h2 class="text-zinc-200 text-sm">{formatSize(downloadingFile.size)} on disk</h2>
<DotFilled class="text-zinc-300" />
<h2 class="uppercase text-zinc-200 text-sm">
{downloadingFile.quality.quality.source}
</h2>
<DotFilled class="text-zinc-300" />
<h2 class="text-zinc-200 text-sm">
Completed in {downloadingFile.timeleft}
</h2>
</div>
<Button size="sm" disabled={true}
>{downloadingFile.status === 'downloding' ? 'Downloading...' : 'Importing...'}</Button
>
</div>
{/each}
{#each $localResource?.movieFile ? [$localResource.movieFile] : [] as movieFile (movieFile.id)}
<div class="border-l-2 border-zinc-200 p-1 px-4 flex justify-between items-center my-1">
<div class="flex gap-1 items-center">
<b class="">{movieFile.quality.quality.resolution}p</b>
<DotFilled class="text-zinc-300" />
<h2 class="text-zinc-200 text-sm">{formatSize(movieFile.size)} on disk</h2>
<DotFilled class="text-zinc-300" />
<h2 class="uppercase text-zinc-200 text-sm">
{movieFile.quality.quality.source}
</h2>
<DotFilled class="text-zinc-300" />
<h2 class="uppercase text-zinc-200 text-sm">
{movieFile.mediaInfo.videoCodec}
</h2>
</div>
<Button size="sm">Stream</Button>
</div>
{/each}
</div>
{/if}
</div>
<div class="flex gap-4 items-center bg-black p-8 py-4 empty:hidden">
{#if !$localResource && $didLoad && tmdbId}
<Button on:click={() => addToRadarr(tmdbId)} disabled={$addMovieLoading}>Add to Radarr</Button>
{/if}
{#if $localResource?.movieFile}
<Button type="secondary">Manage Local Files</Button>
{/if}
</div>
{#if $localResource?.id}
<RequestModal
bind:visible={isRequestModalVisible}
radarrId={$localResource.id}
on:download={() => refreshRadarrMovie()}
/>
{/if}

View File

@@ -1,6 +1,6 @@
<script lang="ts">
import type { TmdbMovieFull } from '$lib/tmdb-api';
import { formatGenres, getRuntime } from '$lib/utils';
import { formatGenres, formatMinutes } from '$lib/utils';
import classNames from 'classnames';
import { TMDB_IMAGES } from '$lib/constants';
@@ -38,8 +38,8 @@
{#if progressType === 'watched'}
<div class="text-xs font-medium text-zinc-200">
{progress
? getRuntime(tmdbMovie.runtime - tmdbMovie.runtime * (progress / 100)) + ' left'
: getRuntime(tmdbMovie.runtime)}
? formatMinutes(tmdbMovie.runtime - tmdbMovie.runtime * (progress / 100)) + ' left'
: formatMinutes(tmdbMovie.runtime)}
</div>
{:else if progressType === 'downloading'}
<div class="text-xs font-medium text-zinc-200">

View File

@@ -0,0 +1,41 @@
<script lang="ts">
import classNames from 'classnames';
import { createEventDispatcher } from 'svelte';
const dispatch = createEventDispatcher();
export let size: 'md' | 'sm' | 'lg' = 'md';
export let type: 'primary' | 'secondary' | 'tertiary' = 'primary';
export let disabled = false;
export let href: string | undefined = undefined;
export let target: string | undefined = undefined;
const buttonStyle = classNames(
'border-2 border-white transition-colors uppercase tracking-widest text-xs',
{
'bg-white text-zinc-900 font-extrabold': type === 'primary',
'hover:bg-amber-400 hover:border-amber-400': type === 'primary' && !disabled,
'font-semibold': type === 'secondary',
'hover:bg-white hover:text-black': type === 'secondary' && !disabled,
'px-8 py-3.5': size === 'lg',
'px-6 py-2.5': size === 'md',
'px-5 py-2': size === 'sm',
'opacity-70': disabled,
'cursor-pointer': !disabled
}
);
const handleClick = (event) => {
if (href) {
if (target === '_blank') window.open(href, target).focus();
else window.open(href, target as string);
} else {
dispatch('click', event);
}
};
</script>
<button class={buttonStyle} on:click={handleClick} on:mouseover on:mouseleave {disabled}>
<slot />
</button>