mirror of
https://github.com/aleksilassila/reiverr.git
synced 2026-04-21 16:25:11 +02:00
refactor: Media Manager Modals
This commit is contained in:
@@ -207,7 +207,7 @@ export class SonarrApi implements Api<paths> {
|
||||
) as EpisodeDownload[]) || []
|
||||
) || Promise.resolve([]);
|
||||
|
||||
getSonarrDownloadsById = (sonarrId: number) =>
|
||||
getDownloadsBySeriesId = (sonarrId: number) =>
|
||||
this.getSonarrDownloads().then((downloads) =>
|
||||
downloads.filter((d) => d.seriesId === sonarrId)
|
||||
) || Promise.resolve([]);
|
||||
@@ -266,7 +266,7 @@ export class SonarrApi implements Api<paths> {
|
||||
// }));
|
||||
// };
|
||||
|
||||
fetchSonarrReleases = async (episodeId: number) =>
|
||||
getEpisodeReleases = async (episodeId: number) =>
|
||||
this.getClient()
|
||||
?.GET('/api/v3/release', {
|
||||
params: {
|
||||
@@ -277,7 +277,7 @@ export class SonarrApi implements Api<paths> {
|
||||
})
|
||||
.then((r) => r.data || []) || Promise.resolve([]);
|
||||
|
||||
fetchSonarrSeasonReleases = async (seriesId: number, seasonNumber: number) =>
|
||||
getSeasonReleases = async (seriesId: number, seasonNumber: number) =>
|
||||
this.getClient()
|
||||
?.GET('/api/v3/release', {
|
||||
params: {
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
<script lang="ts">
|
||||
import Container from '../../../Container.svelte';
|
||||
import HeroShowcaseBackground from './HeroBackground.svelte';
|
||||
import { scrollIntoView, Selectable } from '../../selectable';
|
||||
import IconButton from '../IconButton.svelte';
|
||||
import { ChevronRight } from 'radix-icons-svelte';
|
||||
import PageDots from '../HeroShowcase/PageDots.svelte';
|
||||
import SidebarMargin from '../SidebarMargin.svelte';
|
||||
import type { Readable, Writable } from 'svelte/store';
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
|
||||
|
||||
@@ -4,45 +4,48 @@
|
||||
import { formatSize } from '../../utils';
|
||||
import { ChevronRight } from 'radix-icons-svelte';
|
||||
import type { Download } from '../../apis/combined-types';
|
||||
import Container from '../../../Container.svelte';
|
||||
|
||||
export let downloads: Promise<Download[]>;
|
||||
export let cancelDownload: (downloadId: number) => Promise<any>;
|
||||
</script>
|
||||
|
||||
<div class="-my-1">
|
||||
<Container class="flex flex-col -my-2">
|
||||
{#await downloads}
|
||||
{#each new Array(5) as _, index}
|
||||
<div class="flex-1 my-1">
|
||||
<div class="flex-1 my-2">
|
||||
<ButtonGhost />
|
||||
</div>
|
||||
{/each}
|
||||
{:then downloads}
|
||||
{#each downloads as download, index}
|
||||
<Button on:clickOrSelect={() => cancelDownload(download.id || -1)} let:hasFocus>
|
||||
<div class="flex w-full">
|
||||
<h1 class="flex-1 line-clamp-1">
|
||||
{download.title}
|
||||
</h1>
|
||||
<div>
|
||||
{#if !hasFocus}
|
||||
{#if download.status === 'downloading'}
|
||||
{formatSize((download.size || 0) - (download.sizeleft || 0))}/{formatSize(
|
||||
download.size || 0
|
||||
)}
|
||||
<div class="my-2">
|
||||
<Button on:clickOrSelect={() => cancelDownload(download.id || -1)} let:hasFocus>
|
||||
<div class="flex w-full">
|
||||
<h1 class="flex-1 line-clamp-1">
|
||||
{download.title}
|
||||
</h1>
|
||||
<div>
|
||||
{#if !hasFocus}
|
||||
{#if download.status === 'downloading'}
|
||||
{formatSize((download.size || 0) - (download.sizeleft || 0))}/{formatSize(
|
||||
download.size || 0
|
||||
)}
|
||||
{:else}
|
||||
{download.status}
|
||||
{/if}
|
||||
{:else}
|
||||
{download.status}
|
||||
<div class="flex items-center">
|
||||
Cancel
|
||||
<ChevronRight size={19} class="ml-1" />
|
||||
</div>
|
||||
{/if}
|
||||
{:else}
|
||||
<div class="flex items-center">
|
||||
Cancel
|
||||
<ChevronRight size={19} class="ml-1" />
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Button>
|
||||
</Button>
|
||||
</div>
|
||||
{:else}
|
||||
<h1 class="text-sm text-zinc-400">No downloads found</h1>
|
||||
{/each}
|
||||
{/await}
|
||||
</div>
|
||||
</Container>
|
||||
|
||||
@@ -5,21 +5,22 @@
|
||||
import { formatSize } from '../../utils.js';
|
||||
import type { FileResource } from '../../apis/combined-types';
|
||||
import { scrollIntoView } from '../../selectable';
|
||||
import Container from '../../../Container.svelte';
|
||||
|
||||
export let files: Promise<FileResource[]>;
|
||||
export let handleSelectFile: (file: FileResource) => void;
|
||||
</script>
|
||||
|
||||
<div class="-my-1">
|
||||
<Container class="flex flex-col -my-2">
|
||||
{#await files}
|
||||
{#each new Array(5) as _, index}
|
||||
<div class="flex-1 my-1">
|
||||
<div class="flex-1 my-2">
|
||||
<ButtonGhost />
|
||||
</div>
|
||||
{/each}
|
||||
{:then files}
|
||||
{#each files as file, index}
|
||||
<div class="flex-1 my-1">
|
||||
<div class="flex-1 my-2">
|
||||
<Button
|
||||
on:clickOrSelect={() => handleSelectFile(file)}
|
||||
let:hasFocus
|
||||
@@ -46,4 +47,4 @@
|
||||
<div class="text-sm text-zinc-400">No local files found</div>
|
||||
{/each}
|
||||
{/await}
|
||||
</div>
|
||||
</Container>
|
||||
|
||||
27
src/lib/components/MediaManager/MMModal.svelte
Normal file
27
src/lib/components/MediaManager/MMModal.svelte
Normal file
@@ -0,0 +1,27 @@
|
||||
<script lang="ts">
|
||||
import classNames from 'classnames';
|
||||
import Container from '../../../Container.svelte';
|
||||
import { modalStack } from '../Modal/modal.store';
|
||||
|
||||
export let modalId: symbol;
|
||||
export let hidden: boolean = false;
|
||||
</script>
|
||||
|
||||
<Container
|
||||
on:navigate={({ detail }) => {
|
||||
if (detail.direction === 'left' && detail.willLeaveContainer) {
|
||||
modalStack.close(modalId);
|
||||
detail.preventNavigation();
|
||||
}
|
||||
}}
|
||||
focusOnMount
|
||||
trapFocus
|
||||
class={classNames('fixed inset-0 bg-stone-950/80 overflow-auto', {
|
||||
'opacity-0': hidden
|
||||
})}
|
||||
canFocusEmpty
|
||||
>
|
||||
<div class="mx-20 py-16">
|
||||
<slot />
|
||||
</div>
|
||||
</Container>
|
||||
@@ -8,6 +8,7 @@
|
||||
import { derived } from 'svelte/store';
|
||||
import ButtonGhost from '../Ghosts/ButtonGhost.svelte';
|
||||
import type { SonarrRelease } from '../../apis/sonarr/sonarr-api';
|
||||
import Container from '../../../Container.svelte';
|
||||
|
||||
type Release = RadarrRelease | SonarrRelease;
|
||||
|
||||
@@ -33,16 +34,16 @@
|
||||
});
|
||||
</script>
|
||||
|
||||
<div class="flex flex-col -my-1">
|
||||
<Container class="flex flex-col -my-2">
|
||||
{#if $isLoading}
|
||||
{#each new Array(5) as _, index}
|
||||
<div class="flex-1 my-1">
|
||||
<div class="flex-1 my-2">
|
||||
<ButtonGhost />
|
||||
</div>
|
||||
{/each}
|
||||
{:else}
|
||||
{#each (showAll ? $releases : $filteredReleases)?.filter((r) => r.guid && r.indexerId) || [] as release, index}
|
||||
<div class="flex-1 my-1">
|
||||
<div class="flex-1 my-2">
|
||||
<Button
|
||||
on:clickOrSelect={() => selectRelease(release)}
|
||||
let:hasFocus
|
||||
@@ -118,4 +119,4 @@
|
||||
</div>
|
||||
{/if}
|
||||
{/if}
|
||||
</div>
|
||||
</Container>
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
promise: downloads,
|
||||
data: downloadsData,
|
||||
refresh: refreshDownloads
|
||||
} = useRequest(sonarrApi.getSonarrDownloadsById, id);
|
||||
} = useRequest(sonarrApi.getDownloadsBySeriesId, id);
|
||||
|
||||
const handleGrabRelease = (guid: string, indexerId: number) =>
|
||||
sonarrApi
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
modalStack.create(
|
||||
ReleaseListModal,
|
||||
{
|
||||
getReleases: () => sonarrApi.fetchSonarrReleases(id),
|
||||
getReleases: () => sonarrApi.getEpisodeReleases(id),
|
||||
selectRelease: handleSelectRelease
|
||||
},
|
||||
groupId
|
||||
@@ -47,7 +47,7 @@
|
||||
modalStack.create(
|
||||
ReleaseListModal,
|
||||
{
|
||||
getReleases: () => sonarrApi.fetchSonarrSeasonReleases(seriesId, seasonNumber),
|
||||
getReleases: () => sonarrApi.getSeasonReleases(seriesId, seasonNumber),
|
||||
selectRelease: handleSelectRelease
|
||||
},
|
||||
groupId
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
<script lang="ts">
|
||||
import { sonarrApi } from '../../apis/sonarr/sonarr-api';
|
||||
import MMMainLayout from './MMMainLayout.svelte';
|
||||
import MMAddToSonarr from './MMAddToSonarr.svelte';
|
||||
import MMModal from '../MediaManager/MMModal.svelte';
|
||||
import ReleaseList from '../MediaManager/ReleaseList.svelte';
|
||||
import DownloadList from '../MediaManager/DownloadList.svelte';
|
||||
import FileList from '../MediaManager/FileList.svelte';
|
||||
import { log } from '../../utils';
|
||||
|
||||
export let id: number; // Tmdb ID
|
||||
export let season: number;
|
||||
export let episode: number;
|
||||
export let modalId: symbol;
|
||||
export let hidden: boolean;
|
||||
|
||||
const sonarrItem = sonarrApi.getSeriesByTmdbId(id);
|
||||
const downloads = sonarrItem.then((si) => sonarrApi.getDownloadsBySeriesId(si?.id || -1));
|
||||
const files = sonarrItem.then((si) => sonarrApi.getFilesBySeriesId(si?.id || -1));
|
||||
|
||||
const sonarrEpisode = sonarrItem.then((si) =>
|
||||
sonarrApi
|
||||
.getEpisodes(si?.id || -1, season)
|
||||
.then(log)
|
||||
.then((episodes) => episodes.find((e) => e.episodeNumber === episode))
|
||||
);
|
||||
|
||||
sonarrItem.then((si) => console.log('sonarrItem', si));
|
||||
sonarrEpisode.then((se) => console.log('sonarrEpisode', se));
|
||||
console.log(id, season, episode);
|
||||
|
||||
const getReleases = () => sonarrEpisode.then((se) => sonarrApi.getEpisodeReleases(se?.id || -1));
|
||||
const selectRelease = () => {};
|
||||
|
||||
const cancelDownload = sonarrApi.cancelDownloadSonarrEpisode;
|
||||
const handleSelectFile = () => {};
|
||||
</script>
|
||||
|
||||
<MMModal {modalId} {hidden}>
|
||||
{#await sonarrEpisode then sonarrEpisode}
|
||||
{#if !sonarrEpisode}
|
||||
<MMAddToSonarr />
|
||||
{:else}
|
||||
<MMMainLayout>
|
||||
<h1 slot="title">{sonarrEpisode?.title}</h1>
|
||||
<h2 slot="subtitle">Season {season} Episode {episode}</h2>
|
||||
<ReleaseList slot="releases" {getReleases} {selectRelease} />
|
||||
<DownloadList slot="downloads" {downloads} {cancelDownload} />
|
||||
<FileList slot="local-files" {files} {handleSelectFile} />
|
||||
</MMMainLayout>
|
||||
{/if}
|
||||
{/await}
|
||||
</MMModal>
|
||||
31
src/lib/components/MediaManagerModal/MMMainLayout.svelte
Normal file
31
src/lib/components/MediaManagerModal/MMMainLayout.svelte
Normal file
@@ -0,0 +1,31 @@
|
||||
<script>
|
||||
import Container from '../../../Container.svelte';
|
||||
</script>
|
||||
|
||||
<div class="flex flex-col">
|
||||
<div class="mb-16">
|
||||
<div class="text-4xl font-semibold">
|
||||
<slot name="title" />
|
||||
</div>
|
||||
<div class="text-zinc-300 font-medium text-lg mt-2">
|
||||
<slot name="subtitle" />
|
||||
</div>
|
||||
</div>
|
||||
<Container direction="horizontal" class="grid grid-cols-2 gap-16">
|
||||
<div class="flex flex-col">
|
||||
<h1 class="text-2xl font-semibold mb-4">Releases</h1>
|
||||
<slot name="releases" />
|
||||
</div>
|
||||
<div class="flex flex-col">
|
||||
<div class="flex flex-col mb-8">
|
||||
<h1 class="text-2xl font-semibold mb-4">Local Files</h1>
|
||||
<slot name="local-files" />
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col mb-8">
|
||||
<h1 class="text-2xl font-semibold mb-4">Downloads</h1>
|
||||
<slot name="downloads" />
|
||||
</div>
|
||||
</div>
|
||||
</Container>
|
||||
</div>
|
||||
@@ -0,0 +1,38 @@
|
||||
<script lang="ts">
|
||||
import MMMainLayout from './MMMainLayout.svelte';
|
||||
import MMAddToSonarr from './MMAddToSonarr.svelte';
|
||||
import MMModal from '../MediaManager/MMModal.svelte';
|
||||
import ReleaseList from '../MediaManager/ReleaseList.svelte';
|
||||
import DownloadList from '../MediaManager/DownloadList.svelte';
|
||||
import FileList from '../MediaManager/FileList.svelte';
|
||||
import { radarrApi } from '../../apis/radarr/radarr-api';
|
||||
|
||||
export let id: number; // Tmdb ID
|
||||
export let modalId: symbol;
|
||||
export let hidden: boolean;
|
||||
|
||||
const radarrItem = radarrApi.getMovieByTmdbId(id);
|
||||
const downloads = radarrItem.then((i) => radarrApi.getDownloadsById(i?.id || -1));
|
||||
const files = radarrItem.then((i) => radarrApi.getFilesByMovieId(i?.id || -1));
|
||||
|
||||
const getReleases = () => radarrItem.then((si) => radarrApi.getReleases(si?.id || -1));
|
||||
const selectRelease = () => {};
|
||||
|
||||
const cancelDownload = radarrApi.cancelDownloadRadarrMovie;
|
||||
const handleSelectFile = () => {};
|
||||
</script>
|
||||
|
||||
<MMModal {modalId} {hidden}>
|
||||
{#await radarrItem then movie}
|
||||
{#if !movie}
|
||||
<MMAddToSonarr />
|
||||
{:else}
|
||||
<MMMainLayout>
|
||||
<h1 slot="title">{movie?.title}</h1>
|
||||
<ReleaseList slot="releases" {getReleases} {selectRelease} />
|
||||
<DownloadList slot="downloads" {downloads} {cancelDownload} />
|
||||
<FileList slot="local-files" {files} {handleSelectFile} />
|
||||
</MMMainLayout>
|
||||
{/if}
|
||||
{/await}
|
||||
</MMModal>
|
||||
@@ -0,0 +1,41 @@
|
||||
<script lang="ts">
|
||||
import { sonarrApi } from '../../apis/sonarr/sonarr-api';
|
||||
import MMMainLayout from './MMMainLayout.svelte';
|
||||
import MMAddToSonarr from './MMAddToSonarr.svelte';
|
||||
import MMModal from '../MediaManager/MMModal.svelte';
|
||||
import ReleaseList from '../MediaManager/ReleaseList.svelte';
|
||||
import DownloadList from '../MediaManager/DownloadList.svelte';
|
||||
import FileList from '../MediaManager/FileList.svelte';
|
||||
|
||||
export let id: number; // Tmdb ID
|
||||
export let season: number;
|
||||
export let modalId: symbol;
|
||||
export let hidden: boolean;
|
||||
|
||||
const sonarrItem = sonarrApi.getSeriesByTmdbId(id);
|
||||
const downloads = sonarrItem.then((si) => sonarrApi.getDownloadsBySeriesId(si?.id || -1));
|
||||
const files = sonarrItem.then((si) => sonarrApi.getFilesBySeriesId(si?.id || -1));
|
||||
|
||||
const getReleases = () =>
|
||||
sonarrItem.then((si) => sonarrApi.getSeasonReleases(si?.id || -1, season));
|
||||
const selectRelease = () => {};
|
||||
|
||||
const cancelDownload = sonarrApi.cancelDownloadSonarrEpisode;
|
||||
const handleSelectFile = () => {};
|
||||
</script>
|
||||
|
||||
<MMModal {modalId} {hidden}>
|
||||
{#await sonarrItem then series}
|
||||
{#if !series}
|
||||
<MMAddToSonarr />
|
||||
{:else}
|
||||
<MMMainLayout>
|
||||
<h1 slot="title">{series?.title}</h1>
|
||||
<h2 slot="subtitle">Season {season} Packs</h2>
|
||||
<ReleaseList slot="releases" {getReleases} {selectRelease} />
|
||||
<DownloadList slot="downloads" {downloads} {cancelDownload} />
|
||||
<FileList slot="local-files" {files} {handleSelectFile} />
|
||||
</MMMainLayout>
|
||||
{/if}
|
||||
{/await}
|
||||
</MMModal>
|
||||
@@ -1,5 +1,8 @@
|
||||
import type { ComponentType, SvelteComponentTyped } from 'svelte';
|
||||
import { derived, writable } from 'svelte/store';
|
||||
import SeasonMediaManagerModal from '../MediaManagerModal/SeasonMediaManagerModal.svelte';
|
||||
import EpisodeMediaManagerModal from '../MediaManagerModal/EpisodeMediaManagerModal.svelte';
|
||||
import MovieMediaManagerModal from '../MediaManagerModal/MovieMediaManagerModal.svelte';
|
||||
|
||||
type ModalItem = {
|
||||
id: symbol;
|
||||
@@ -49,6 +52,15 @@ function createModalStack() {
|
||||
export const modalStack = createModalStack();
|
||||
export const modalStackTop = modalStack.top;
|
||||
|
||||
export const openSeasonMediaManager = (tmdbId: number, season: number) =>
|
||||
modalStack.create(SeasonMediaManagerModal, { id: tmdbId, season });
|
||||
|
||||
export const openEpisodeMediaManager = (tmdbId: number, season: number, episode: number) =>
|
||||
modalStack.create(EpisodeMediaManagerModal, { id: tmdbId, season, episode });
|
||||
|
||||
export const openMovieMediaManager = (tmdbId: number) =>
|
||||
modalStack.create(MovieMediaManagerModal, { id: tmdbId });
|
||||
|
||||
// let lastTitleModal: symbol | undefined = undefined;
|
||||
// export function openTitleModal(titleId: TitleId) {
|
||||
// if (lastTitleModal) {
|
||||
|
||||
@@ -12,6 +12,9 @@
|
||||
import classNames from 'classnames';
|
||||
import ScrollHelper from '../ScrollHelper.svelte';
|
||||
import { useNavigate } from 'svelte-navigator';
|
||||
import ManageSeasonCard from './ManageSeasonCard.svelte';
|
||||
import { TMDB_BACKDROP_SMALL } from '../../constants';
|
||||
import { modalStack, openSeasonMediaManager } from '../Modal/modal.store';
|
||||
|
||||
const navigate = useNavigate();
|
||||
|
||||
@@ -107,5 +110,9 @@
|
||||
on:clickOrSelect={() => handleOpenEpisodePage(episode)}
|
||||
/>
|
||||
{/each}
|
||||
<ManageSeasonCard
|
||||
backdropUrl={TMDB_BACKDROP_SMALL + $tmdbSeries?.backdrop_path}
|
||||
on:clickOrSelect={() => openSeasonMediaManager(id, seasonIndex + 1)}
|
||||
/>
|
||||
</CardGrid>
|
||||
</Container>
|
||||
|
||||
33
src/lib/components/SeriesPage/ManageSeasonCard.svelte
Normal file
33
src/lib/components/SeriesPage/ManageSeasonCard.svelte
Normal file
@@ -0,0 +1,33 @@
|
||||
<script lang="ts">
|
||||
import Container from '../../../Container.svelte';
|
||||
import type { Readable } from 'svelte/store';
|
||||
import AnimateScale from '../AnimateScale.svelte';
|
||||
import classNames from 'classnames';
|
||||
import { Plus, PlusCircled } from 'radix-icons-svelte';
|
||||
|
||||
export let backdropUrl: string;
|
||||
|
||||
let hasFocus: Readable<boolean>;
|
||||
</script>
|
||||
|
||||
<AnimateScale hasFocus={$hasFocus}>
|
||||
<Container
|
||||
class={classNames(
|
||||
'w-full h-64',
|
||||
'flex flex-col shrink-0',
|
||||
'overflow-hidden rounded-2xl cursor-pointer group relative selectable transition-opacity'
|
||||
)}
|
||||
on:clickOrSelect
|
||||
bind:hasFocus
|
||||
>
|
||||
<div
|
||||
class="bg-cover bg-center absolute inset-0"
|
||||
style={`background-image: url('${backdropUrl}')`}
|
||||
/>
|
||||
<div class="absolute inset-0 bg-secondary-800/75 flex items-center justify-center">
|
||||
<div class="rounded-full p-2.5 bg-secondary-800/75">
|
||||
<Plus size={32} />
|
||||
</div>
|
||||
</div>
|
||||
</Container>
|
||||
</AnimateScale>
|
||||
@@ -15,13 +15,13 @@
|
||||
import { derived } from 'svelte/store';
|
||||
import { scrollIntoView, useRegistrar } from '../../selectable';
|
||||
import ScrollHelper from '../ScrollHelper.svelte';
|
||||
import SonarrMediaMangerModal from '../MediaManager/sonarr/SonarrMediaMangerModal.svelte';
|
||||
import Carousel from '../Carousel/Carousel.svelte';
|
||||
import TmdbPersonCard from '../PersonCard/TmdbPersonCard.svelte';
|
||||
import TmdbCard from '../Card/TmdbCard.svelte';
|
||||
import EpisodeGrid from './EpisodeGrid.svelte';
|
||||
import { Route } from 'svelte-navigator';
|
||||
import EpisodePage from '../../pages/EpisodePage.svelte';
|
||||
import SeriesMediaManagerModal from '../MediaManagerModal/SeasonMediaManagerModal.svelte';
|
||||
|
||||
export let id: string;
|
||||
|
||||
@@ -168,29 +168,18 @@
|
||||
<Play size={19} slot="icon" />
|
||||
</Button>
|
||||
{/if}
|
||||
{#if sonarrItem}
|
||||
<Button
|
||||
class="mr-4"
|
||||
on:clickOrSelect={() =>
|
||||
modalStack.create(SonarrMediaMangerModal, { id: sonarrItem.id || -1 })}
|
||||
>
|
||||
{#if jellyfinItem}
|
||||
Manage Files
|
||||
{:else}
|
||||
Request
|
||||
{/if}
|
||||
<svelte:component this={jellyfinItem ? File : Download} size={19} slot="icon" />
|
||||
</Button>
|
||||
{:else}
|
||||
<Button
|
||||
class="mr-4"
|
||||
on:clickOrSelect={() => addSeriesToSonarr(Number(id))}
|
||||
inactive={$addSeriesToSonarrFetching}
|
||||
>
|
||||
Add to Sonarr
|
||||
<Plus slot="icon" size={19} />
|
||||
</Button>
|
||||
{/if}
|
||||
<Button
|
||||
class="mr-4"
|
||||
on:clickOrSelect={() =>
|
||||
modalStack.create(SeriesMediaManagerModal, { id: Number(id) })}
|
||||
>
|
||||
{#if jellyfinItem}
|
||||
Manage Media
|
||||
{:else}
|
||||
Request
|
||||
{/if}
|
||||
<svelte:component this={jellyfinItem ? File : Download} size={19} slot="icon" />
|
||||
</Button>
|
||||
{#if PLATFORM_WEB}
|
||||
<Button class="mr-4">
|
||||
Open In TMDB
|
||||
|
||||
@@ -5,13 +5,14 @@
|
||||
import { useActionRequest, useDependantRequest, useRequest } from '../stores/data.store';
|
||||
import { TMDB_IMAGES_ORIGINAL } from '../constants';
|
||||
import classNames from 'classnames';
|
||||
import { Check, DotFilled, Download, Play, Trash } from 'radix-icons-svelte';
|
||||
import { Check, DotFilled, Download, File, Play, Trash } from 'radix-icons-svelte';
|
||||
import HeroInfoTitle from '../components/HeroInfo/HeroInfoTitle.svelte';
|
||||
import Button from '../components/Button.svelte';
|
||||
import { jellyfinApi } from '../apis/jellyfin/jellyfin-api';
|
||||
import { playerState } from '../components/VideoPlayer/VideoPlayer';
|
||||
import { formatSize } from '../utils';
|
||||
import { tick } from 'svelte';
|
||||
import { openEpisodeMediaManager } from '../components/Modal/modal.store';
|
||||
|
||||
export let id: string; // Series ID
|
||||
export let season: string;
|
||||
@@ -44,10 +45,10 @@
|
||||
</script>
|
||||
|
||||
<DetachedPage let:handleGoBack let:registrar>
|
||||
{#await tmdbEpisode then episode}
|
||||
{#await tmdbEpisode then tmdbEpisode}
|
||||
<div
|
||||
class="bg-center bg-cover absolute inset-x-0 h-screen -z-10"
|
||||
style={`background-image: url('${TMDB_IMAGES_ORIGINAL + episode?.still_path}')`}
|
||||
style={`background-image: url('${TMDB_IMAGES_ORIGINAL + tmdbEpisode?.still_path}')`}
|
||||
/>
|
||||
<div class="absolute inset-0 flex flex-col -z-10">
|
||||
<div class="h-screen bg-gradient-to-t from-secondary-500 to-transparent" />
|
||||
@@ -63,9 +64,9 @@
|
||||
class="h-screen flex flex-col justify-end mx-20 py-16"
|
||||
>
|
||||
<div class="mt-2 text-zinc-200 font-medium text-lg tracking-wider">
|
||||
Season {episode?.season_number} Episode {episode?.episode_number}
|
||||
Season {tmdbEpisode?.season_number} Episode {tmdbEpisode?.episode_number}
|
||||
</div>
|
||||
<HeroInfoTitle title={episode?.name} />
|
||||
<HeroInfoTitle title={tmdbEpisode?.name} />
|
||||
<div
|
||||
class="flex items-center gap-1 uppercase text-zinc-300 font-semibold tracking-wider mt-2 text-lg"
|
||||
>
|
||||
@@ -78,12 +79,12 @@
|
||||
<!-- </p>-->
|
||||
<!-- <DotFilled /> -->
|
||||
<p class="flex-shrink-0">
|
||||
<a href={'https://www.themoviedb.org/movie/' + episode?.id}>
|
||||
{episode?.vote_average} TMDB
|
||||
<a href={'https://www.themoviedb.org/movie/' + tmdbEpisode?.id}>
|
||||
{tmdbEpisode?.vote_average} TMDB
|
||||
</a>
|
||||
</p>
|
||||
<DotFilled />
|
||||
<p class="flex-shrink-0">{episode?.runtime} Minutes</p>
|
||||
<p class="flex-shrink-0">{tmdbEpisode?.runtime} Minutes</p>
|
||||
|
||||
{#await jellyfinEpisode then episode}
|
||||
{#if episode?.MediaSources?.[0]?.Size}
|
||||
@@ -99,13 +100,13 @@
|
||||
{/await}
|
||||
</div>
|
||||
<div class="text-stone-300 font-medium line-clamp-3 opacity-75 max-w-4xl mt-4">
|
||||
{episode?.overview}
|
||||
{tmdbEpisode?.overview}
|
||||
</div>
|
||||
<Container direction="horizontal" class="flex mt-8">
|
||||
{#await jellyfinEpisode then episode}
|
||||
{#await jellyfinEpisode then jEpisode}
|
||||
<Button
|
||||
class="mr-4"
|
||||
on:clickOrSelect={() => episode?.Id && playerState.streamJellyfinId(episode.Id)}
|
||||
on:clickOrSelect={() => jEpisode?.Id && playerState.streamJellyfinId(jEpisode.Id)}
|
||||
>
|
||||
Play
|
||||
<Play size={19} slot="icon" />
|
||||
@@ -119,7 +120,12 @@
|
||||
<Check slot="icon" size={19} />
|
||||
</Button>
|
||||
{/await}
|
||||
<Button class="mr-4">Request <Download slot="icon" size={19} /></Button>
|
||||
<Button
|
||||
class="mr-4"
|
||||
on:clickOrSelect={() =>
|
||||
openEpisodeMediaManager(Number(id), Number(season), Number(episode))}
|
||||
>Manage Media <File slot="icon" size={19} /></Button
|
||||
>
|
||||
<Button class="mr-4">Delete Files <Trash slot="icon" size={19} /></Button>
|
||||
</Container>
|
||||
</Container>
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
import { radarrApi } from '../apis/radarr/radarr-api';
|
||||
import { useActionRequests, useRequest } from '../stores/data.store';
|
||||
import DetachedPage from '../components/DetachedPage/DetachedPage.svelte';
|
||||
import { modalStack } from '../components/Modal/modal.store';
|
||||
import { modalStack, openMovieMediaManager } from '../components/Modal/modal.store';
|
||||
import { playerState } from '../components/VideoPlayer/VideoPlayer';
|
||||
import ManageMediaModal from '../components/MediaManager/radarr/RadarrMediaMangerModal.svelte';
|
||||
|
||||
@@ -98,13 +98,9 @@
|
||||
</Button>
|
||||
{/if}
|
||||
{#if radarrItem}
|
||||
<Button
|
||||
class="mr-4"
|
||||
on:clickOrSelect={() =>
|
||||
modalStack.create(ManageMediaModal, { id: radarrItem.id || -1 })}
|
||||
>
|
||||
<Button class="mr-4" on:clickOrSelect={() => openMovieMediaManager(Number(id))}>
|
||||
{#if jellyfinItem}
|
||||
Manage Files
|
||||
Manage Media
|
||||
{:else}
|
||||
Request
|
||||
{/if}
|
||||
|
||||
Reference in New Issue
Block a user