mirror of
https://github.com/maxdorninger/MediaManager.git
synced 2026-04-17 15:43:28 +02:00
Merge pull request #334 from hellow554/sort_by_score
allow sorting of torrent search results in web ui
This commit is contained in:
@@ -5,7 +5,7 @@
|
||||
import { toast } from 'svelte-sonner';
|
||||
import { Badge } from '$lib/components/ui/badge/index.js';
|
||||
|
||||
import { LoaderCircle } from 'lucide-svelte';
|
||||
import { ArrowDown, ArrowUp, LoaderCircle } from 'lucide-svelte';
|
||||
import * as Dialog from '$lib/components/ui/dialog/index.js';
|
||||
import * as Tabs from '$lib/components/ui/tabs/index.js';
|
||||
import * as Table from '$lib/components/ui/table/index.js';
|
||||
@@ -15,13 +15,45 @@
|
||||
|
||||
let { movie } = $props();
|
||||
let dialogueState = $state(false);
|
||||
let torrentsPromise: any = $state(null);
|
||||
let tabState: string = $state('basic');
|
||||
let isLoading: boolean = $state(false);
|
||||
let advancedMode: boolean = $derived(tabState === 'advanced');
|
||||
let torrentsError: string | null = $state(null);
|
||||
let queryOverride: string = $state('');
|
||||
let filePathSuffix: string = $state('');
|
||||
let torrentsError: string | null = $state(null);
|
||||
|
||||
let torrentsPromise: any = $state(null);
|
||||
let torrentsData: any[] | null = $state(null);
|
||||
let tabState: string = $state('basic');
|
||||
let isLoading: boolean = $state(false);
|
||||
let sortBy = $state({ col: 'score', ascending: false });
|
||||
|
||||
let advancedMode: boolean = $derived(tabState === 'advanced');
|
||||
|
||||
const tableColumnHeadings = [
|
||||
{ name: 'Size', id: 'size' },
|
||||
{ name: 'Seeders', id: 'seeders' },
|
||||
{ name: 'Score', id: 'score' },
|
||||
{ name: 'Indexer', id: 'indexer' },
|
||||
{ name: 'Indexer Flags', id: 'flags' }
|
||||
];
|
||||
|
||||
function getSortedColumnState(column: string | undefined): boolean | null {
|
||||
if (sortBy.col !== column) return null;
|
||||
return sortBy.ascending;
|
||||
}
|
||||
|
||||
function sortData(column?: string | undefined) {
|
||||
if (column !== undefined) {
|
||||
if (column === sortBy.col) {
|
||||
sortBy.ascending = !sortBy.ascending;
|
||||
} else {
|
||||
sortBy = { col: column, ascending: true };
|
||||
}
|
||||
}
|
||||
|
||||
let modifier = sortBy.ascending ? 1 : -1;
|
||||
torrentsData?.sort((a, b) =>
|
||||
a[sortBy.col] < b[sortBy.col] ? -1 * modifier : a[sortBy.col] > b[sortBy.col] ? modifier : 0
|
||||
);
|
||||
}
|
||||
|
||||
async function downloadTorrent(result_id: string) {
|
||||
torrentsError = null;
|
||||
@@ -56,6 +88,7 @@
|
||||
async function search() {
|
||||
isLoading = true;
|
||||
torrentsError = null;
|
||||
torrentsData = null;
|
||||
torrentsPromise = client
|
||||
.GET('/api/v1/movies/{movie_id}/torrents', {
|
||||
params: {
|
||||
@@ -67,9 +100,13 @@
|
||||
}
|
||||
}
|
||||
})
|
||||
.then((data) => data?.data)
|
||||
.finally(() => (isLoading = false));
|
||||
toast.info('Searching for torrents...');
|
||||
toast.info('Found ' + (await torrentsPromise).data?.length + ' torrents.');
|
||||
|
||||
torrentsData = await torrentsPromise;
|
||||
sortData();
|
||||
toast.info('Found ' + torrentsData?.length + ' torrents.');
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -131,23 +168,33 @@
|
||||
<LoaderCircle class="animate-spin" />
|
||||
<p>Loading torrents...</p>
|
||||
</div>
|
||||
{:then data}
|
||||
{:then}
|
||||
<h3 class="mb-2 text-lg font-semibold">Found Torrents:</h3>
|
||||
<div class="overflow-y-auto rounded-md border p-2">
|
||||
<Table.Root>
|
||||
<Table.Header>
|
||||
<Table.Row>
|
||||
<Table.Head>Title</Table.Head>
|
||||
<Table.Head>Size</Table.Head>
|
||||
<Table.Head>Seeders</Table.Head>
|
||||
<Table.Head>Score</Table.Head>
|
||||
<Table.Head>Indexer</Table.Head>
|
||||
<Table.Head>Indexer Flags</Table.Head>
|
||||
{#each tableColumnHeadings as { name, id } (id)}
|
||||
<Table.Head onclick={() => sortData(id)} class="cursor-pointer">
|
||||
<div class="inline-flex items-center">
|
||||
{name}
|
||||
{#if getSortedColumnState(id) === true}
|
||||
<ArrowUp />
|
||||
{:else if getSortedColumnState(id) === false}
|
||||
<ArrowDown />
|
||||
{:else}
|
||||
<!-- Preserve layout (column width) when no sort is applied -->
|
||||
<ArrowUp class="invisible"></ArrowUp>
|
||||
{/if}
|
||||
</div>
|
||||
</Table.Head>
|
||||
{/each}
|
||||
<Table.Head class="text-right">Actions</Table.Head>
|
||||
</Table.Row>
|
||||
</Table.Header>
|
||||
<Table.Body>
|
||||
{#each data?.data as torrent (torrent.id)}
|
||||
{#each torrentsData as torrent (torrent.id)}
|
||||
<Table.Row>
|
||||
<Table.Cell class="max-w-[300px] font-medium">{torrent.title}</Table.Cell>
|
||||
<Table.Cell>{(torrent.size / 1024 / 1024 / 1024).toFixed(2)}GB</Table.Cell>
|
||||
@@ -168,7 +215,7 @@
|
||||
</Table.Cell>
|
||||
</Table.Row>
|
||||
{:else}
|
||||
{#if data === null}
|
||||
{#if torrentsData === null}
|
||||
<Table.Cell colspan={7}>
|
||||
<div class="font-light text-center w-full">
|
||||
Start searching by clicking the search button!
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
import { Label } from '$lib/components/ui/label/index.js';
|
||||
import { toast } from 'svelte-sonner';
|
||||
import { convertTorrentSeasonRangeToIntegerRange, formatSecondsToOptimalUnit } from '$lib/utils';
|
||||
import { LoaderCircle } from 'lucide-svelte';
|
||||
import { ArrowDown, ArrowUp, LoaderCircle } from 'lucide-svelte';
|
||||
import * as Dialog from '$lib/components/ui/dialog/index.js';
|
||||
import * as Tabs from '$lib/components/ui/tabs/index.js';
|
||||
import * as Table from '$lib/components/ui/table/index.js';
|
||||
@@ -22,10 +22,44 @@
|
||||
let filePathSuffix: string = $state('');
|
||||
|
||||
let torrentsPromise: any = $state();
|
||||
let torrentsData: any[] | null = $state(null);
|
||||
let tabState: string = $state('basic');
|
||||
let isLoading: boolean = $state(false);
|
||||
let sortBy = $state({ col: 'score', ascending: false });
|
||||
|
||||
let advancedMode: boolean = $derived(tabState === 'advanced');
|
||||
|
||||
const tableColumnHeadings = [
|
||||
{ name: 'Size', id: 'size' },
|
||||
{ name: 'Usenet', id: 'usenet' },
|
||||
{ name: 'Seeders', id: 'seeders' },
|
||||
{ name: 'Age', id: 'age' },
|
||||
{ name: 'Score', id: 'score' },
|
||||
{ name: 'Indexer', id: 'indexer' },
|
||||
{ name: 'Indexer Flags', id: 'flags' },
|
||||
{ name: 'Seasons', id: 'season' }
|
||||
];
|
||||
|
||||
function getSortedColumnState(column: string | undefined): boolean | null {
|
||||
if (sortBy.col !== column) return null;
|
||||
return sortBy.ascending;
|
||||
}
|
||||
|
||||
function sortData(column?: string | undefined) {
|
||||
if (column !== undefined) {
|
||||
if (column === sortBy.col) {
|
||||
sortBy.ascending = !sortBy.ascending;
|
||||
} else {
|
||||
sortBy = { col: column, ascending: true };
|
||||
}
|
||||
}
|
||||
|
||||
let modifier = sortBy.ascending ? 1 : -1;
|
||||
torrentsData?.sort((a, b) =>
|
||||
a[sortBy.col] < b[sortBy.col] ? -1 * modifier : a[sortBy.col] > b[sortBy.col] ? modifier : 0
|
||||
);
|
||||
}
|
||||
|
||||
async function downloadTorrent(result_id: string) {
|
||||
torrentsError = null;
|
||||
const { response } = await client.POST('/api/v1/tv/torrents', {
|
||||
@@ -56,6 +90,7 @@
|
||||
async function search() {
|
||||
isLoading = true;
|
||||
torrentsError = null;
|
||||
torrentsData = null;
|
||||
torrentsPromise = client
|
||||
.GET('/api/v1/tv/torrents', {
|
||||
params: {
|
||||
@@ -66,9 +101,14 @@
|
||||
}
|
||||
}
|
||||
})
|
||||
.then((data) => data?.data)
|
||||
.finally(() => (isLoading = false));
|
||||
|
||||
toast.info('Searching for torrents...');
|
||||
toast.info('Found ' + (await torrentsPromise).data?.length + ' torrents.');
|
||||
|
||||
torrentsData = await torrentsPromise;
|
||||
sortData();
|
||||
toast.info('Found ' + torrentsData?.length + ' torrents.');
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -146,26 +186,33 @@
|
||||
<LoaderCircle class="animate-spin" />
|
||||
<p>Loading torrents...</p>
|
||||
</div>
|
||||
{:then data}
|
||||
{:then}
|
||||
<h3 class="mb-2 text-lg font-semibold">Found Torrents:</h3>
|
||||
<div class="overflow-y-auto rounded-md border p-2">
|
||||
<Table.Root>
|
||||
<Table.Root class="torrentResult">
|
||||
<Table.Header>
|
||||
<Table.Row>
|
||||
<Table.Head>Title</Table.Head>
|
||||
<Table.Head>Size</Table.Head>
|
||||
<Table.Head>Usenet</Table.Head>
|
||||
<Table.Head>Seeders</Table.Head>
|
||||
<Table.Head>Age</Table.Head>
|
||||
<Table.Head>Score</Table.Head>
|
||||
<Table.Head>Indexer</Table.Head>
|
||||
<Table.Head>Indexer Flags</Table.Head>
|
||||
<Table.Head>Seasons</Table.Head>
|
||||
{#each tableColumnHeadings as { name, id } (id)}
|
||||
<Table.Head onclick={() => sortData(id)} class="cursor-pointer">
|
||||
<div class="inline-flex items-center">
|
||||
{name}
|
||||
{#if getSortedColumnState(id) === true}
|
||||
<ArrowUp />
|
||||
{:else if getSortedColumnState(id) === false}
|
||||
<ArrowDown />
|
||||
{:else}
|
||||
<!-- Preserve layout (column width) when no sort is applied -->
|
||||
<ArrowUp class="invisible"></ArrowUp>
|
||||
{/if}
|
||||
</div>
|
||||
</Table.Head>
|
||||
{/each}
|
||||
<Table.Head class="text-right">Actions</Table.Head>
|
||||
</Table.Row>
|
||||
</Table.Header>
|
||||
<Table.Body>
|
||||
{#each data?.data as torrent (torrent.id)}
|
||||
{#each torrentsData as torrent (torrent.id)}
|
||||
<Table.Row>
|
||||
<Table.Cell class="max-w-[300px] font-medium">{torrent.title}</Table.Cell>
|
||||
<Table.Cell>{(torrent.size / 1024 / 1024 / 1024).toFixed(2)}GB</Table.Cell>
|
||||
@@ -201,7 +248,7 @@
|
||||
</Table.Cell>
|
||||
</Table.Row>
|
||||
{:else}
|
||||
{#if data === null}
|
||||
{#if torrentsData === null}
|
||||
<Table.Cell colspan={7}>
|
||||
<div class="font-light text-center w-full">
|
||||
Start searching by clicking the search button!
|
||||
|
||||
Reference in New Issue
Block a user