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 + + +
+ + + {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. +

+ +

+ {@render saveDirectoryPreview(movie, filePathSuffix)} +

+
+
+ +
+ +
+ + +
+

+ The custom query will override the default search string like "A Minecraft Movie (2025)". +

+ + +

+ This is necessary to differentiate between versions of the same movie, for + example a 1080p and a 4K version. +

+ + +

+ {@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} + + + + + + {/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. + + +
+ +
+ + + + {minQuality ? getTorrentQualityString(minQuality) : 'Select Minimum Quality'} + + + {#each qualityOptions as option (option.value)} + {option.label} + {/each} + + +
+ + +
+ + + + {wantedQuality ? getTorrentQualityString(wantedQuality) : 'Select Wanted Quality'} + + + {#each qualityOptions as option (option.value)} + {option.label} + {/each} + + +
+ + {#if submitRequestError} +

{submitRequestError}

+ {/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 @@ + + +
+
+ + + + + + + +
+
+

+ {getFullyQualifiedMediaName(movie)} +

+
+
+
+ {#if movie.id} + + {:else} +
+ +
+ {/if} +
+
+

+ {movie.overview} +

+
+
+ {#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