diff --git a/media_manager/movies/router.py b/media_manager/movies/router.py
index 57ef3c5..ed11f2c 100644
--- a/media_manager/movies/router.py
+++ b/media_manager/movies/router.py
@@ -225,9 +225,9 @@ def get_movie_by_id(movie_service: movie_service_dep, movie_id: MovieId):
response_model=list[PublicIndexerQueryResult],
)
def get_all_available_torrents_for_a_movie(
- movie_service: movie_service_dep, movie_id: MovieId
+ movie_service: movie_service_dep, movie_id: MovieId, search_query_override: str | None = None
):
- return movie_service.get_all_available_torrents_for_a_movie(movie_id=movie_id)
+ return movie_service.get_all_available_torrents_for_a_movie(movie_id=movie_id, search_query_override=search_query_override)
@router.post(
diff --git a/web/src/lib/components/download-movie-dialog.svelte b/web/src/lib/components/download-movie-dialog.svelte
new file mode 100644
index 0000000..ca21461
--- /dev/null
+++ b/web/src/lib/components/download-movie-dialog.svelte
@@ -0,0 +1,270 @@
+
+
+{#snippet saveDirectoryPreview(movie, filePathSuffix)}
+ /{getFullyQualifiedMediaName(movie)} [{movie.metadata_provider}id-{movie.external_id}
+ ]/{movie.name}{filePathSuffix === '' ? '' : ' - ' + filePathSuffix}.mkv
+{/snippet}
+
+
+ Download Movie
+
+
+ Download a Movie
+
+ Search and download torrents for a specific season or season packs.
+
+
+
+
+ Standard Mode
+ Advanced Mode
+
+
+
+
Filepath suffix
+
+ {filePathSuffix}
+
+ None
+ 2160p
+ 1080p
+ 720p
+ 480p
+ 360p
+
+
+
+ This is necessary to differentiate between versions of the same movie, for
+ example a 1080p and a 4K version.
+
+
The files will be saved in the following directory:
+
+ {@render saveDirectoryPreview(movie, filePathSuffix)}
+
+
+
+
+
+
Enter a custom query
+
+
+ {
+ isLoadingTorrents = true;
+ torrentsError = null;
+ torrents = [];
+ try {
+ torrents = await getTorrents(true);
+ } catch (error) {
+ console.log(error);
+ } finally {
+ isLoadingTorrents = false;
+ }
+ }}
+ variant="secondary"
+ >
+ Search
+
+
+
+ The custom query will override the default search string like "A Minecraft Movie (2025)".
+
+
Filepath suffix
+
+
+ This is necessary to differentiate between versions of the same movie, for
+ example a 1080p and a 4K version.
+
+
+
The files will be saved in the following directory:
+
+ {@render saveDirectoryPreview(movie, filePathSuffix)}
+
+
+
+
+
+ {#if isLoadingTorrents}
+
+
+
Loading torrents...
+
+ {:else if torrentsError}
+
Error: {torrentsError}
+ {:else if torrents.length > 0}
+
Found Torrents:
+
+
+
+
+ Title
+ Size
+ Seeders
+ Indexer Flags
+ Actions
+
+
+
+ {#each torrents as torrent (torrent.id)}
+
+ {torrent.title}
+ {(torrent.size / 1024 / 1024 / 1024).toFixed(2)}GB
+ {torrent.seeders}
+
+ {#each torrent.flags as flag}
+ {flag}
+ {/each}
+
+
+ {
+ downloadTorrent(torrent.id);
+ }}
+ >
+ Download
+
+
+
+ {/each}
+
+
+
+ {/if}
+
+
+
diff --git a/web/src/lib/components/download-season-dialog.svelte b/web/src/lib/components/download-season-dialog.svelte
index 235b693..9b95c4f 100644
--- a/web/src/lib/components/download-season-dialog.svelte
+++ b/web/src/lib/components/download-season-dialog.svelte
@@ -6,12 +6,13 @@
import {toast} from 'svelte-sonner';
import type {PublicIndexerQueryResult} from '$lib/types.js';
- import {convertTorrentSeasonRangeToIntegerRange, getFullyQualifiedShowName} from '$lib/utils';
+ import {convertTorrentSeasonRangeToIntegerRange, getFullyQualifiedMediaName} from '$lib/utils';
import {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 Select from '$lib/components/ui/select/index.js';
import * as Table from '$lib/components/ui/table/index.js';
+ import {Badge} from "$lib/components/ui/badge";
const apiUrl = env.PUBLIC_API_URL;
let {show} = $props();
@@ -129,7 +130,7 @@
{#snippet saveDirectoryPreview(show, filePathSuffix)}
- /{getFullyQualifiedShowName(show)} [{show.metadata_provider}id-{show.external_id}]/ Season XX/{show.name}
+ /{getFullyQualifiedMediaName(show)} [{show.metadata_provider}id-{show.external_id}]/ Season XX/{show.name}
SXXEXX {filePathSuffix === '' ? '' : ' - ' + filePathSuffix}.mkv
{/snippet}
@@ -279,7 +280,7 @@
{torrent.seeders}
{#each torrent.flags as flag}
- {flag},
+ {flag}
{/each}
diff --git a/web/src/lib/components/request-movie-dialog.svelte b/web/src/lib/components/request-movie-dialog.svelte
new file mode 100644
index 0000000..da934a6
--- /dev/null
+++ b/web/src/lib/components/request-movie-dialog.svelte
@@ -0,0 +1,134 @@
+
+
+
+ {
+ dialogOpen = true;
+ }}
+ >
+ Request Movie
+
+
+
+ Request {getFullyQualifiedMediaName(movie)}
+
+ Select desired qualities to submit a request.
+
+
+
+
+
+ Min Quality
+
+
+ {minQuality ? getTorrentQualityString(minQuality) : 'Select Minimum Quality'}
+
+
+ {#each qualityOptions as option (option.value)}
+ {option.label}
+ {/each}
+
+
+
+
+
+
+ Wanted Quality
+
+
+ {wantedQuality ? getTorrentQualityString(wantedQuality) : 'Select Wanted Quality'}
+
+
+ {#each qualityOptions as option (option.value)}
+ {option.label}
+ {/each}
+
+
+
+
+ {#if submitRequestError}
+
{submitRequestError}
+ {/if}
+
+
+ (dialogOpen = false)} variant="outline"
+ >Cancel
+
+
+ {#if isSubmittingRequest}
+
+ Submitting...
+ {:else}
+ Submit Request
+ {/if}
+
+
+
+
diff --git a/web/src/lib/components/request-season-dialog.svelte b/web/src/lib/components/request-season-dialog.svelte
index 7925df1..4848fcb 100644
--- a/web/src/lib/components/request-season-dialog.svelte
+++ b/web/src/lib/components/request-season-dialog.svelte
@@ -6,7 +6,7 @@
import * as Select from '$lib/components/ui/select/index.js';
import LoaderCircle from '@lucide/svelte/icons/loader-circle';
import type {CreateSeasonRequest, PublicShow, Quality} from '$lib/types.js';
- import {getFullyQualifiedShowName, getTorrentQualityString} from '$lib/utils.js';
+ import {getFullyQualifiedMediaName, getTorrentQualityString} from '$lib/utils.js';
import {toast} from 'svelte-sonner';
const apiUrl = env.PUBLIC_API_URL;
@@ -87,7 +87,7 @@
- Request a Season for {getFullyQualifiedShowName(show)}
+ Request a Season for {getFullyQualifiedMediaName(show)}
Select a season and desired qualities to submit a request.
diff --git a/web/src/routes/dashboard/movies/[movieId=uuid]/+page.svelte b/web/src/routes/dashboard/movies/[movieId=uuid]/+page.svelte
new file mode 100644
index 0000000..1566e54
--- /dev/null
+++ b/web/src/routes/dashboard/movies/[movieId=uuid]/+page.svelte
@@ -0,0 +1,93 @@
+
+
+
+
+
+
+
+ {#if movie.id}
+
+ {:else}
+
+
+
+ {/if}
+
+
+
+ {#if user().is_superuser}
+
+
+ {/if}
+
+
+
+
+
+
diff --git a/web/src/routes/dashboard/movies/[movieId=uuid]/+page.ts b/web/src/routes/dashboard/movies/[movieId=uuid]/+page.ts
new file mode 100644
index 0000000..0c82ad1
--- /dev/null
+++ b/web/src/routes/dashboard/movies/[movieId=uuid]/+page.ts
@@ -0,0 +1,12 @@
+import type {LayoutLoad} from './$types';
+import {PUBLIC_API_URL} from '$env/static/public';
+import {error} from '@sveltejs/kit';
+
+export const load: LayoutLoad = async ({params, fetch}) => {
+ const res = await fetch(`${PUBLIC_API_URL}/movies/${params.movieId}`, {
+ credentials: 'include'
+ });
+ if (!res.ok) throw error(res.status, `Failed to load movie`);
+ const movieData = await res.json();
+ return {movie: movieData, torrents: []};
+};
\ No newline at end of file