feat: Bulk delete local files and downloads, confirm dialogs

This commit is contained in:
Aleksi Lassila
2024-05-03 01:22:18 +03:00
parent d608c4b917
commit aa1168b6b2
18 changed files with 335 additions and 163 deletions

View File

@@ -150,7 +150,7 @@ export class SonarrApi implements Api<paths> {
.then((r) => r.data);
};
cancelDownloadSonarrEpisode = async (downloadId: number) => {
cancelDownload = async (downloadId: number) => {
const deleteResponse = await this.getClient()
?.DELETE('/api/v3/queue/{id}', {
params: {
@@ -168,6 +168,15 @@ export class SonarrApi implements Api<paths> {
return !!deleteResponse?.response.ok;
};
cancelDownloads = async (downloadIds: number[]) =>
this.getClient()
?.DELETE('/api/v3/queue/bulk', {
body: {
ids: downloadIds
}
})
.then((r) => r.response.ok) || Promise.resolve(false);
downloadSonarrRelease = (guid: string, indexerId: number) =>
this.getClient()
?.POST('/api/v3/release', {

View File

@@ -27,12 +27,12 @@
const handleKeydown = (event: KeyboardEvent) => {
if (event.repeat) return;
if (event.key === 'Shift') showOverlay = !showOverlay;
if (event.key === 'Shift') showOverlay = true;
};
const handleKeyup = (event: KeyboardEvent) => {
if (event.repeat) return;
if (event.key === 'Shift') showOverlay = !showOverlay;
if (event.key === 'Shift') showOverlay = false;
};
onMount(() => {

View File

@@ -1,88 +0,0 @@
<script lang="ts">
import ButtonGhost from '../Ghosts/ButtonGhost.svelte';
import type { FileResource } from '../../apis/combined-types';
import Table from '../Table/Table.svelte';
import MMLocalFileRow from '../MediaManagerModal/MMLocalFileRow.svelte';
import TableHeaderRow from '../Table/TableHeaderRow.svelte';
import TableHeaderSortBy from '../Table/TableHeaderSortBy.svelte';
import TableHeaderCell from '../Table/TableHeaderCell.svelte';
import Container from '../../../Container.svelte';
import Button from '../Button.svelte';
import { Trash } from 'radix-icons-svelte';
import { scrollIntoView } from '../../selectable';
import type { DeleteFile } from '../MediaManagerModal/MediaManagerModal';
import { modalStack } from '../Modal/modal.store';
import MMConfirmDeleteFileDialog from '../MediaManagerModal/Dialogs/MMConfirmDeleteFileDialog.svelte';
import { sonarrApi } from '../../apis/sonarr/sonarr-api';
export let files: Promise<FileResource[]>;
export let deleteFile: DeleteFile;
// export let handleSelectFile: (file: FileResource) => void;
let sortBy: 'size' | 'quality' | 'title' | 'runtime' | undefined = 'title';
let sortDirection: 'asc' | 'desc' = 'desc';
const toggleSortBy = (sort: typeof sortBy) => () => {
if (sortBy === sort) {
sortDirection = sortDirection === 'asc' ? 'desc' : 'asc';
} else {
sortBy = sort;
sortDirection = 'desc';
}
};
</script>
{#await files}
{#each new Array(5) as _, index}
<div class="flex-1 my-2">
<ButtonGhost />
</div>
{/each}
{:then files}
<div class="grid grid-cols-[1fr_max-content_max-content_max-content_max-content] gap-y-4">
<TableHeaderRow>
<TableHeaderSortBy
icon={sortBy === 'title' ? sortDirection : undefined}
on:clickOrSelect={toggleSortBy('title')}>Title</TableHeaderSortBy
>
<TableHeaderSortBy
icon={sortBy === 'runtime' ? sortDirection : undefined}
on:clickOrSelect={toggleSortBy('runtime')}>Runtime</TableHeaderSortBy
>
<TableHeaderSortBy
icon={sortBy === 'size' ? sortDirection : undefined}
on:clickOrSelect={toggleSortBy('size')}>Size</TableHeaderSortBy
>
<TableHeaderSortBy
icon={sortBy === 'quality' ? sortDirection : undefined}
on:clickOrSelect={toggleSortBy('quality')}>Quality</TableHeaderSortBy
>
<TableHeaderCell />
</TableHeaderRow>
{#each files as file}
<MMLocalFileRow {file} {deleteFile} />
{/each}
</div>
{#if files?.length}
<Container
direction="horizontal"
class="flex mt-8 mx-12"
on:enter={scrollIntoView({ vertical: 128 })}
>
<Button
on:clickOrSelect={() =>
modalStack.create(MMConfirmDeleteFileDialog, {
deleteFile: () => sonarrApi.deleteSonarrEpisodes(files.map((f) => f.id || -1))
})}
>
Delete all
<Trash size={19} slot="icon" />
</Button>
</Container>
{:else}
<div class="text-zinc-400 font-medium mx-12 flex flex-col items-center justify-center h-full">
<h1 class="text-xl text-zinc-300">No local files found</h1>
<div>Your local files will appear here.</div>
</div>
{/if}
{/await}

View File

@@ -1,6 +1,6 @@
<script lang="ts">
import FullScreenModal from '../../Modal/FullScreenModal.svelte';
import ReleaseList from '../ReleaseList.svelte';
import ReleaseList from '../../MediaManagerModal/Releases/MMReleasesTab.svelte';
import type { Release } from '../../../apis/combined-types';
export let modalId: symbol;

View File

@@ -6,8 +6,8 @@
radarrApi,
type RadarrRelease
} from '../../../apis/radarr/radarr-api';
import ReleaseList from '../ReleaseList.svelte';
import FilesList from '../FileList.svelte';
import ReleaseList from '../../MediaManagerModal/Releases/MMReleasesTab.svelte';
import FilesList from '../../MediaManagerModal/LocalFiles/MMLocalFilesTab.svelte';
import { modalStack } from '../../Modal/modal.store';
import FileActionsModal from '../modals/FileActionsModal.svelte';
import DownloadsList from '../DownloadList.svelte';

View File

@@ -1,7 +1,7 @@
<script lang="ts">
import FullScreenModal from '../../Modal/FullScreenModal.svelte';
import ManageMediaMenuLayout from '../MediaManagerMenuLayout.svelte';
import FilesList from '../FileList.svelte';
import FilesList from '../../MediaManagerModal/LocalFiles/MMLocalFilesTab.svelte';
import { modalStack } from '../../Modal/modal.store';
import FileActionsModal from '../modals/FileActionsModal.svelte';
import DownloadsList from '../DownloadList.svelte';
@@ -39,7 +39,7 @@
setTimeout(() => refreshDownloads(id), 8000);
});
const handleCancelDownload = (id: number) =>
sonarrApi.cancelDownloadSonarrEpisode(id).then(() => refreshDownloads(id));
sonarrApi.cancelDownload(id).then(() => refreshDownloads(id));
const grabbedReleases: Readable<Record<string, boolean>> = derived(downloadsData, ($downloads) =>
($downloads || []).reduce((acc: Record<string, boolean>, download) => {

View File

@@ -0,0 +1,67 @@
<script lang="ts">
import TableButton from '../../Table/TableButton.svelte';
import { Cross1 } from 'radix-icons-svelte';
import type { Download } from '../../../apis/combined-types';
import type { CancelDownloadFn } from '../MediaManagerModal';
import { scrollIntoView } from '../../../selectable';
import Container from '../../../../Container.svelte';
import classNames from 'classnames';
import { modalStack } from '../../Modal/modal.store';
import MMConfirmDeleteFileDialog from '../Dialogs/MMConfirmDeleteFileDialog.svelte';
export let download: Download;
export let cancelDownload: CancelDownloadFn;
let title = '';
let subtitle = '';
function handleCancelDownload() {
modalStack.create(MMConfirmDeleteFileDialog, {
deleteFile: () => cancelDownload(download.id || -1)
});
}
$: {
if ('series' in download && 'episode' in download) {
title = download.episode?.title || '';
subtitle = `Episode ${download.episode?.episodeNumber || ''}`;
}
}
$: console.log('download', download);
</script>
<Container class="contents" let:hasFocusWithin>
<div
class={classNames(
'flex items-center justify-between relative overflow-hidden',
'px-6 py-3 bg-secondary-800 border-2 border-secondary-800 rounded-xl',
{
'bg-secondary-900 border-secondary-500': hasFocusWithin
}
)}
>
<!-- Background -->
<div
class="absolute inset-y-0 bg-secondary-50/10 left-0"
style={`width: ${
(((download.size || download.sizeleft || 0) - (download.sizeleft || 0)) /
(download.size || 1)) *
100
}%`}
/>
<div>
<h2 class="text-zinc-300 font-medium">{subtitle}</h2>
<h1 class="text-lg font-medium tracking-wide">{title}</h1>
</div>
<div>
<TableButton
on:clickOrSelect={handleCancelDownload}
on:enter={scrollIntoView({ vertical: 128 })}
>
<Cross1 size={19} />
</TableButton>
</div>
</div>
</Container>

View File

@@ -2,10 +2,10 @@
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 MMModal from './MMModal.svelte';
import ReleaseList from './Releases/MMReleasesTab.svelte';
import DownloadList from '../MediaManager/DownloadList.svelte';
import FileList from '../MediaManager/FileList.svelte';
import FileList from './LocalFiles/MMLocalFilesTab.svelte';
import { log } from '../../utils';
export let id: number; // Tmdb ID
@@ -32,7 +32,7 @@
const getReleases = () => sonarrEpisode.then((se) => sonarrApi.getEpisodeReleases(se?.id || -1));
const selectRelease = () => {};
const cancelDownload = sonarrApi.cancelDownloadSonarrEpisode;
const cancelDownload = sonarrApi.cancelDownload;
const handleSelectFile = () => {};
</script>

View File

@@ -1,18 +1,17 @@
<script lang="ts">
import TableRow from '../Table/TableRow.svelte';
import { scrollIntoView } from '../../selectable';
import type { FileResource } from '../../apis/combined-types';
import { formatSize } from '../../utils';
import TableButton from '../Table/TableButton.svelte';
import TableRow from '../../Table/TableRow.svelte';
import { scrollIntoView } from '../../../selectable';
import type { FileResource } from '../../../apis/combined-types';
import { formatSize } from '../../../utils';
import TableButton from '../../Table/TableButton.svelte';
import { Trash } from 'radix-icons-svelte';
import TableCell from '../Table/TableCell.svelte';
import type { DeleteFile } from './MediaManagerModal';
import { modalStack } from '../Modal/modal.store';
import MMConfirmDeleteFileDialog from './Dialogs/MMConfirmDeleteFileDialog.svelte';
import TableCell from '../../Table/TableCell.svelte';
import type { DeleteFileFn } from '../MediaManagerModal';
import { modalStack } from '../../Modal/modal.store';
import MMConfirmDeleteFileDialog from '../Dialogs/MMConfirmDeleteFileDialog.svelte';
export let file: FileResource;
export let deleteFile: DeleteFile;
console.log(file);
export let deleteFile: DeleteFileFn;
</script>
<TableRow class="font-medium">

View File

@@ -0,0 +1,128 @@
<script lang="ts">
import ButtonGhost from '../../Ghosts/ButtonGhost.svelte';
import type { Download, FileResource } from '../../../apis/combined-types';
import MMLocalFileRow from './MMLocalFileRow.svelte';
import TableHeaderRow from '../../Table/TableHeaderRow.svelte';
import TableHeaderSortBy from '../../Table/TableHeaderSortBy.svelte';
import TableHeaderCell from '../../Table/TableHeaderCell.svelte';
import Container from '../../../../Container.svelte';
import Button from '../../Button.svelte';
import { Cross1, Trash } from 'radix-icons-svelte';
import { scrollIntoView } from '../../../selectable';
import type {
CancelDownloadFn,
CancelDownloadsFn,
DeleteFileFn,
DeleteFilesFn
} from '../MediaManagerModal';
import { modalStack } from '../../Modal/modal.store';
import MMConfirmDeleteFileDialog from '../Dialogs/MMConfirmDeleteFileDialog.svelte';
import MMDownloadRow from '../Downloads/MMDownloadRow.svelte';
export let files: Promise<FileResource[]>;
export let deleteFile: DeleteFileFn;
export let deleteFiles: DeleteFilesFn;
export let downloads: Promise<Download[]>;
export let cancelDownload: CancelDownloadFn;
export let cancelDownloads: CancelDownloadsFn;
let sortBy: 'size' | 'quality' | 'title' | 'runtime' | undefined = 'title';
let sortDirection: 'asc' | 'desc' = 'desc';
const toggleSortBy = (sort: typeof sortBy) => () => {
if (sortBy === sort) {
sortDirection = sortDirection === 'asc' ? 'desc' : 'asc';
} else {
sortBy = sort;
sortDirection = 'desc';
}
};
function confirmCancelAllDownloads(downloads: Download[]) {
modalStack.create(MMConfirmDeleteFileDialog, {
deleteFile: () => cancelDownloads(downloads.map((f) => f.id || -1))
});
}
function confirmDeleteAllFiles(files: FileResource[]) {
modalStack.create(MMConfirmDeleteFileDialog, {
deleteFile: () => deleteFiles(files.map((f) => f.id || -1))
});
}
</script>
{#await downloads then downloads}
{#if downloads?.length}
<h1 class="text-lg font-semibold tracking-wide text-zinc-300 mb-4 mx-12">Downloads</h1>
<div class="grid grid-cols-1 gap-4 mx-12 mb-4">
{#each downloads as download}
<MMDownloadRow {download} {cancelDownload} />
{/each}
</div>
<Container
direction="horizontal"
class="flex mb-16 mx-12"
on:enter={scrollIntoView({ vertical: 128 })}
>
<Button on:clickOrSelect={() => confirmCancelAllDownloads(downloads)}>
Cancel All Downloads
<Cross1 size={19} slot="icon" />
</Button>
</Container>
<h1 class="text-2xl font-semibold mb-4 mt-8 mx-12">Local Files</h1>
{/if}
{/await}
{#await files}
{#each new Array(5) as _, index}
<div class="flex-1 my-2">
<ButtonGhost />
</div>
{/each}
{:then files}
<div class="grid grid-cols-[1fr_max-content_max-content_max-content_max-content] gap-y-4">
<TableHeaderRow>
<TableHeaderSortBy
icon={sortBy === 'title' ? sortDirection : undefined}
on:clickOrSelect={toggleSortBy('title')}>Title</TableHeaderSortBy
>
<TableHeaderSortBy
icon={sortBy === 'runtime' ? sortDirection : undefined}
on:clickOrSelect={toggleSortBy('runtime')}>Runtime</TableHeaderSortBy
>
<TableHeaderSortBy
icon={sortBy === 'size' ? sortDirection : undefined}
on:clickOrSelect={toggleSortBy('size')}>Size</TableHeaderSortBy
>
<TableHeaderSortBy
icon={sortBy === 'quality' ? sortDirection : undefined}
on:clickOrSelect={toggleSortBy('quality')}>Quality</TableHeaderSortBy
>
<TableHeaderCell />
</TableHeaderRow>
{#each files as file}
<MMLocalFileRow {file} {deleteFile} />
{/each}
</div>
{#if files?.length}
<Container
direction="horizontal"
class="flex mt-8 mx-12"
on:enter={scrollIntoView({ vertical: 128 })}
>
<Button on:clickOrSelect={() => confirmDeleteAllFiles(files)}>
Delete all
<Trash size={19} slot="icon" />
</Button>
</Container>
{:else}
<div class="text-zinc-400 font-medium mx-12 flex flex-col items-center justify-center h-full">
<h1 class="text-xl text-zinc-300">No local files found</h1>
<div>Your local files will appear here.</div>
</div>
{/if}
{/await}

View File

@@ -6,7 +6,7 @@
</script>
<div class="flex flex-col h-screen">
<div class="flex items-center pb-8 mb-8 pt-16 px-28">
<div class="flex items-center pb-8 mb-8 pt-16 px-32">
<div class="flex-1">
<div class="text-4xl font-semibold">
<slot name="title" />
@@ -20,30 +20,30 @@
<slot name="downloads" />
</div>
</div>
<div class="flex mb-8 mx-28">
<h1
<div class="flex mb-8 mx-32">
<button
class={classNames('text-2xl font-semibold mr-8 transition-opacity cursor-pointer', {
'opacity-40': activeTab !== 'releases'
})}
on:click={() => (activeTab = 'releases')}
>
Releases
</h1>
<h1
</button>
<button
class={classNames('text-2xl font-semibold mr-8 transition-opacity cursor-pointer', {
'opacity-40': activeTab !== 'local-files'
})}
on:click={() => (activeTab = 'local-files')}
>
Local Files
</h1>
</button>
</div>
<Container focusOnMount direction="horizontal" class="flex-1 grid grid-cols-1 min-h-0">
<Container
focusOnMount
on:enter={() => (activeTab = 'releases')}
class={classNames(
'row-start-1 col-start-1 pb-16 mx-16',
'row-start-1 col-start-1 pb-16 mx-20',
'transition-all overflow-y-auto overflow-x-hidden scrollbar-hide',
{
'opacity-30 -translate-x-full': activeTab !== 'releases'
@@ -55,7 +55,7 @@
<Container
on:enter={() => (activeTab = 'local-files')}
class={classNames(
'row-start-1 col-start-1 pb-16 mx-16',
'row-start-1 col-start-1 pb-16 mx-20',
'transition-all overflow-y-auto overflow-x-hidden scrollbar-hide',
{
'opacity-30 translate-x-full': activeTab !== 'local-files'

View File

@@ -17,7 +17,7 @@
focusOnMount
trapFocus
class={classNames(
'fixed inset-0 overflow-x-hidden overflow-y-auto',
'fixed inset-0 overflow-hidden',
{
'opacity-0': hidden
},

View File

@@ -1,4 +1,7 @@
import type { Release } from '../../apis/combined-types';
export type GrabRelease = (release: Release) => Promise<boolean>;
export type DeleteFile = (id: number) => Promise<boolean>;
export type GrabReleaseFn = (release: Release) => Promise<boolean>;
export type DeleteFileFn = (id: number) => Promise<boolean>;
export type DeleteFilesFn = (ids: number[]) => Promise<boolean>;
export type CancelDownloadFn = (downloadId: number) => Promise<boolean>;
export type CancelDownloadsFn = (downloadIds: number[]) => Promise<boolean>;

View File

@@ -1,10 +1,10 @@
<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 MMModal from './MMModal.svelte';
import ReleaseList from './Releases/MMReleasesTab.svelte';
import DownloadList from '../MediaManager/DownloadList.svelte';
import FileList from '../MediaManager/FileList.svelte';
import FileList from './LocalFiles/MMLocalFilesTab.svelte';
import { radarrApi } from '../../apis/radarr/radarr-api';
export let id: number; // Tmdb ID

View File

@@ -1,16 +1,16 @@
<script lang="ts">
import { formatMinutesToTime, formatSize } from '../../utils.js';
import type { RadarrRelease } from '../../apis/radarr/radarr-api';
import type { SonarrRelease } from '../../apis/sonarr/sonarr-api';
import { scrollIntoView } from '../../selectable';
import { formatMinutesToTime, formatSize } from '../../../utils.js';
import type { RadarrRelease } from '../../../apis/radarr/radarr-api';
import type { SonarrRelease } from '../../../apis/sonarr/sonarr-api';
import { scrollIntoView } from '../../../selectable';
import { Check, Download } from 'radix-icons-svelte';
import TableRow from '../Table/TableRow.svelte';
import type { GrabRelease } from './MediaManagerModal';
import TableButton from '../Table/TableButton.svelte';
import TableCell from '../Table/TableCell.svelte';
import TableRow from '../../Table/TableRow.svelte';
import type { GrabReleaseFn } from '../MediaManagerModal';
import TableButton from '../../Table/TableButton.svelte';
import TableCell from '../../Table/TableCell.svelte';
export let release: RadarrRelease | SonarrRelease;
export let grabRelease: GrabRelease;
export let grabRelease: GrabReleaseFn;
let fetching = false;
let didGrab = false;

View File

@@ -1,19 +1,18 @@
<script lang="ts">
import { type RadarrRelease } from '../../apis/radarr/radarr-api';
import ButtonGhost from '../Ghosts/ButtonGhost.svelte';
import type { SonarrRelease } from '../../apis/sonarr/sonarr-api';
import MMReleaseListRow from '../MediaManagerModal/MMReleaseListRow.svelte';
import Table from '../Table/Table.svelte';
import TableHeaderRow from '../Table/TableHeaderRow.svelte';
import TableHeaderSortBy from '../Table/TableHeaderSortBy.svelte';
import type { GrabRelease } from '../MediaManagerModal/MediaManagerModal';
import Container from '../../../Container.svelte';
import TableHeaderCell from '../Table/TableHeaderCell.svelte';
import { type RadarrRelease } from '../../../apis/radarr/radarr-api';
import ButtonGhost from '../../Ghosts/ButtonGhost.svelte';
import type { SonarrRelease } from '../../../apis/sonarr/sonarr-api';
import MMReleaseListRow from './MMReleaseListRow.svelte';
import TableHeaderRow from '../../Table/TableHeaderRow.svelte';
import TableHeaderSortBy from '../../Table/TableHeaderSortBy.svelte';
import type { GrabReleaseFn } from '../MediaManagerModal';
import Container from '../../../../Container.svelte';
import TableHeaderCell from '../../Table/TableHeaderCell.svelte';
type Release = RadarrRelease | SonarrRelease;
export let releases: Promise<Release[]>;
export let grabRelease: GrabRelease;
export let grabRelease: GrabReleaseFn;
let sortBy: 'size' | 'quality' | 'seeders' | 'age' | undefined = 'seeders';
let sortDirection: 'asc' | 'desc' = 'desc';

View File

@@ -2,12 +2,18 @@
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 type { DeleteFile, GrabRelease } from './MediaManagerModal';
import MMModal from './MMModal.svelte';
import MMReleasesTab from './Releases/MMReleasesTab.svelte';
import MMLocalFilesTab from './LocalFiles/MMLocalFilesTab.svelte';
import type {
CancelDownloadFn,
CancelDownloadsFn,
DeleteFileFn,
DeleteFilesFn,
GrabReleaseFn
} from './MediaManagerModal';
import type { Release } from '../../apis/combined-types';
import { onDestroy } from 'svelte';
export let id: number; // Tmdb ID
export let season: number;
@@ -15,18 +21,59 @@
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));
// Releases
const releases: Promise<Release[]> = sonarrItem.then((si) =>
sonarrApi.getSeasonReleases(si?.id || -1, season)
);
const grabRelease: GrabRelease = (release) =>
sonarrApi.downloadSonarrRelease(release.guid || '', release.indexerId || -1);
let releases: Promise<Release[]> = getReleases();
let files = getLocalFiles();
let downloads = getDownloads();
const cancelDownload = sonarrApi.cancelDownloadSonarrEpisode;
const deleteFile: DeleteFile = sonarrApi.deleteSonarrEpisode;
let refreshDownloadsTimeout: ReturnType<typeof setTimeout>;
const grabRelease: GrabReleaseFn = (release) =>
sonarrApi.downloadSonarrRelease(release.guid || '', release.indexerId || -1).then((r) => {
refreshDownloadsTimeout = setTimeout(() => {
downloads = getDownloads();
}, 8000);
return r;
});
const deleteFile: DeleteFileFn = (...args) =>
sonarrApi.deleteSonarrEpisode(...args).then((r) => {
files = getLocalFiles();
return r;
});
const deleteFiles: DeleteFilesFn = (...args) =>
sonarrApi.deleteSonarrEpisodes(...args).then((r) => {
files = getLocalFiles();
return r;
});
const cancelDownload: CancelDownloadFn = (...args) =>
sonarrApi.cancelDownload(...args).then((r) => {
downloads = getDownloads();
return r;
});
const cancelDownloads: CancelDownloadsFn = (...args) =>
sonarrApi.cancelDownloads(...args).then((r) => {
downloads = getDownloads();
return r;
});
function getReleases() {
return sonarrItem.then((si) => sonarrApi.getSeasonReleases(si?.id || -1, season));
}
function getLocalFiles() {
return sonarrItem.then((si) => sonarrApi.getFilesBySeriesId(si?.id || -1));
}
function getDownloads() {
return sonarrItem
.then((si) => sonarrApi.getDownloadsBySeriesId(si?.id || -1))
.then((ds) => ds.filter((d) => d.episode?.seasonNumber === season));
}
onDestroy(() => {
clearTimeout(refreshDownloadsTimeout);
});
</script>
<MMModal {modalId} {hidden}>
@@ -37,9 +84,17 @@
<MMMainLayout>
<h1 slot="title">{series?.title}</h1>
<h2 slot="subtitle">Season {season} Packs</h2>
<ReleaseList slot="releases" {releases} {grabRelease} />
<FileList slot="local-files" {files} {deleteFile} />
<DownloadList slot="downloads" {downloads} {cancelDownload} />
<MMReleasesTab slot="releases" {releases} {grabRelease} />
<MMLocalFilesTab
slot="local-files"
{files}
{deleteFile}
{deleteFiles}
{downloads}
{cancelDownload}
{cancelDownloads}
/>
<!-- <DownloadList slot="downloads" {downloads} {cancelDownload} />-->
</MMMainLayout>
{/if}
{/await}