diff --git a/web/src/lib/components/add-media-card.svelte b/web/src/lib/components/add-media-card.svelte index 17b6d44..0a6830b 100644 --- a/web/src/lib/components/add-media-card.svelte +++ b/web/src/lib/components/add-media-card.svelte @@ -1,94 +1,102 @@ - - - {result.name} - {#if result.year != null} - ({result.year}) - {/if} - - {result.overview !== '' ? result.overview : 'No overview available'} - - - {#if result.poster_path != null} - {result.name}'s Poster Image - {:else} -
- -
- {/if} -
- - -
- {#if result.vote_average != null} + + + {result.name} + {#if result.year != null} + ({result.year}) + {/if} + + {result.overview !== '' ? result.overview : 'No overview available'} + + + {#if result.poster_path != null} + {result.name}'s Poster Image + {:else} +
+ +
+ {/if} +
+ + +
+ {#if result.vote_average != null} + > Rating: {Math.round(result.vote_average)}/10 - {/if} -
- {#if errorMessage} -

{errorMessage}

- {/if} -
+ {/if} +
+ {#if errorMessage} +

{errorMessage}

+ {/if} +
diff --git a/web/src/lib/components/download-movie-dialog.svelte b/web/src/lib/components/download-movie-dialog.svelte index 830a3eb..691f667 100644 --- a/web/src/lib/components/download-movie-dialog.svelte +++ b/web/src/lib/components/download-movie-dialog.svelte @@ -1,190 +1,162 @@ -{#snippet saveDirectoryPreview( - movie: { name: string; metadata_provider: string; external_id: number; year: number | null }, - filePathSuffix: string -)} - /{getFullyQualifiedMediaName(movie)} [{movie.metadata_provider}id-{movie.external_id} - ]/{movie.name}{filePathSuffix === '' ? '' : ' - ' + filePathSuffix}.mkv +{#snippet saveDirectoryPreview(movie: components["schemas"]["Movie"], filePathSuffix: string)} + /{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. -

+ variant="secondary" + > + Search + +
+

+ 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 - Score - Indexer Flags - Actions - - - - {#each torrents as torrent (torrent.id)} - - {torrent.title} - {(torrent.size / 1024 / 1024 / 1024).toFixed(2)}GB - {torrent.seeders} - {torrent.score} - - {#each torrent.flags as flag (flag)} - {flag} - {/each} - - -
+ + +
+ {#if isLoadingTorrents} +
+ +

Loading torrents...

+
+ {:else if torrentsError} +

Error: {torrentsError}

+ {:else if torrents.length > 0} +

Found Torrents:

+
+ + + + Title + Size + Seeders + Score + Indexer Flags + Actions + + + + {#each torrents as torrent (torrent.id)} + + {torrent.title} + {(torrent.size / 1024 / 1024 / 1024).toFixed(2)}GB + {torrent.seeders} + {torrent.score} + + {#each torrent.flags as flag (flag)} + {flag} + {/each} + + + - - - {/each} - - -
- {:else} -

No torrents found!

- {/if} -
- + > + Download + + + + {/each} + + +
+ {:else} +

No torrents found!

+ {/if} + +
diff --git a/web/src/lib/components/download-season-dialog.svelte b/web/src/lib/components/download-season-dialog.svelte index ab1905d..943254c 100644 --- a/web/src/lib/components/download-season-dialog.svelte +++ b/web/src/lib/components/download-season-dialog.svelte @@ -1,12 +1,8 @@ {#snippet saveDirectoryPreview( - show: { name: string; metadata_provider: string; external_id: number; year: number | null }, + show: components['schemas']['Show'], filePathSuffix: string )} /{getFullyQualifiedMediaName(show)} [{show.metadata_provider}id-{show.external_id}]/ Season XX/{show.name} @@ -154,7 +118,7 @@
{#if show?.seasons?.length > 0} Enter a season number from 1 to {show.seasons.at(-1)?.number}
- {/snippet} - - - - - - No library found. - - {#each libraries as item (item.name)} - { + + {#snippet child({props})} + + {/snippet} + + + + + + No library found. + + {#each libraries as item (item.name)} + { value = item.name; handleSelect(); closeAndFocusTrigger(); }} - > - - {item.name} - - {/each} - - - - + > + + {item.name} + + {/each} + + + + diff --git a/web/src/lib/components/login-card.svelte b/web/src/lib/components/login-card.svelte index 69ebd0b..7e48823 100644 --- a/web/src/lib/components/login-card.svelte +++ b/web/src/lib/components/login-card.svelte @@ -1,175 +1,151 @@ - - Login - Enter your email below to log in to your account - - -
-
- - -
-
- - -
+ + Login + Enter your email below to log in to your account + + + +
+ + +
+
+ + +
- {#if errorMessage} - - - Error - {errorMessage} - - {/if} - {#if isLoading} - - {/if} - - - {#await oauthProvider} - - {:then result} - {#if result.oauth_name != null} -
+ {#if errorMessage} + + + Error + {errorMessage} + + {/if} + {#if isLoading} + + {/if} + + + {#await oauthProvider} + + {:then result} + {#if result.oauth_name != null} +
Or continue with -
- - {/if} - {/await} -
- -
- +
+ + {/if} + {/await} +
+ +
+
diff --git a/web/src/lib/components/request-movie-dialog.svelte b/web/src/lib/components/request-movie-dialog.svelte index 41c2d08..750f93a 100644 --- a/web/src/lib/components/request-movie-dialog.svelte +++ b/web/src/lib/components/request-movie-dialog.svelte @@ -1,132 +1,118 @@ - { + { dialogOpen = true; }} - > - Request Movie - - - - Request {getFullyQualifiedMediaName(movie)} - Select desired qualities to submit a request. - -
- -
- - - - {minQuality ? getTorrentQualityString(parseInt(minQuality)) : 'Select Minimum Quality'} - - - {#each qualityOptions as option (option.value)} - {option.label} - {/each} - - -
+ > + Request Movie + + + + Request {getFullyQualifiedMediaName(movie)} + Select desired qualities to submit a request. + +
+ +
+ + + + {minQuality ? getTorrentQualityString(parseInt(minQuality)) : 'Select Minimum Quality'} + + + {#each qualityOptions as option (option.value)} + {option.label} + {/each} + + +
- -
- - - - {wantedQuality - ? getTorrentQualityString(parseInt(wantedQuality)) - : 'Select Wanted Quality'} - - - {#each qualityOptions as option (option.value)} - {option.label} - {/each} - - -
+ +
+ + + + {wantedQuality + ? getTorrentQualityString(parseInt(wantedQuality)) + : 'Select Wanted Quality'} + + + {#each qualityOptions as option (option.value)} + {option.label} + {/each} + + +
- {#if submitRequestError} -

{submitRequestError}

- {/if} -
- - - - -
+ {#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 cf787c0..44dfcbc 100644 --- a/web/src/lib/components/request-season-dialog.svelte +++ b/web/src/lib/components/request-season-dialog.svelte @@ -8,6 +8,7 @@ import type { CreateSeasonRequest, PublicShow, Quality } from '$lib/types.js'; import { getFullyQualifiedMediaName, getTorrentQualityString } from '$lib/utils.js'; import { toast } from 'svelte-sonner'; + import client from "$lib/api"; const apiUrl = env.PUBLIC_API_URL; let { show }: { show: PublicShow } = $props(); @@ -35,34 +36,18 @@ isSubmittingRequest = true; submitRequestError = null; - const payloads: CreateSeasonRequest[] = selectedSeasonsIds.map((seasonId) => ({ - season_id: seasonId, - min_quality: parseInt(minQuality!) as Quality, - wanted_quality: parseInt(wantedQuality!) as Quality - })); - for (const payload of payloads) { - try { - const response = await fetch(`${apiUrl}/tv/seasons/requests`, { - method: 'POST', - headers: { - 'Content-Type': 'application/json' - }, - credentials: 'include', - body: JSON.stringify(payload) - }); - - if (!response.ok) { - const errorData = await response.json().catch(() => ({ message: response.statusText })); - submitRequestError = `Failed to submit request: ${errorData.message || response.statusText}`; - toast.error(submitRequestError); - console.error('Failed to submit request', response.statusText, errorData); - break; + for (const id of selectedSeasonsIds) { + const { response, error } = await client.POST("/api/v1/tv/seasons/requests", { + body: { + season_id: id, + min_quality: parseInt(minQuality!) as Quality, + wanted_quality: parseInt(wantedQuality!) as Quality } - } catch (error) { - submitRequestError = `Error submitting request: ${error instanceof Error ? error.message : String(error)}`; - toast.error(submitRequestError); - console.error('Error submitting request:', error); - break; + }); + + if (!response.ok) { + toast.error("Failed to submit request: " + error); + submitRequestError = `Failed to submit request for season ID ${id}: ${error}`; } } diff --git a/web/src/lib/components/season-requests-table.svelte b/web/src/lib/components/season-requests-table.svelte index ac89e03..f6b0ec7 100644 --- a/web/src/lib/components/season-requests-table.svelte +++ b/web/src/lib/components/season-requests-table.svelte @@ -1,185 +1,201 @@ - A list of all requests. - - - {isShow ? 'Show' : 'Movie'} - {#if isShow} - Season - {/if} - Minimum Quality - Wanted Quality - Requested by - Approved - Approved by - Actions - - - - {#each requests as request (request.id)} - {#if filter(request)} - - - {#if isShow} - {getFullyQualifiedMediaName((request as SeasonRequest).show)} - {:else} - {getFullyQualifiedMediaName((request as MovieRequest).movie)} - {/if} - - {#if isShow} - - {(request as SeasonRequest).season.number} - - {/if} - - {getTorrentQualityString(request.min_quality)} - - - {getTorrentQualityString(request.wanted_quality)} - - - {request.requested_by?.email ?? 'N/A'} - - - - - - {request.authorized_by?.email ?? 'N/A'} - - - - {#if user().is_superuser} - - {#if isShow} - - {:else} - + {#if isShow} + + {:else} + - {/if} - {/if} - {#if user().is_superuser || user().id === request.requested_by?.id} - - {/if} - - - {/if} - {:else} - - There are currently no requests. - - {/each} - + > + Download manually + + {/if} + {/if} + {#if user().is_superuser || user().id === request.requested_by?.id} + + {/if} + + + {/if} + {:else} + + There are currently no requests. + + {/each} + diff --git a/web/src/lib/components/signup-card.svelte b/web/src/lib/components/signup-card.svelte index c086d0c..ef2568a 100644 --- a/web/src/lib/components/signup-card.svelte +++ b/web/src/lib/components/signup-card.svelte @@ -1,161 +1,152 @@ - - Sign Up - Enter your information to create an account - - -
-
- - -
-
- - -
-
- - -
- {#if errorMessage} - - - Error - {errorMessage} - - {/if} - {#if successMessage} - - - Success - {successMessage} - - {/if} - {#if isLoading} - - {/if} - - - {#await oauthProvider} - - {:then result} - {#if result.oauth_name != null} -
+ + Sign Up + Enter your information to create an account + + +
+
+ + +
+
+ + +
+
+ + +
+ {#if errorMessage} + + + Error + {errorMessage} + + {/if} + {#if successMessage} + + + Success + {successMessage} + + {/if} + {#if isLoading} + + {/if} + + + {#await oauthProvider} + + {:then result} + {#if result.oauth_name != null} +
Or continue with -
- - {/if} - {/await} -
- -
-
+
+ + {/if} + {/await} +
+ +
+
diff --git a/web/src/lib/components/user-data-table.svelte b/web/src/lib/components/user-data-table.svelte index 6ac6abf..aac6029 100644 --- a/web/src/lib/components/user-data-table.svelte +++ b/web/src/lib/components/user-data-table.svelte @@ -10,7 +10,7 @@ import * as RadioGroup from '$lib/components/ui/radio-group/index.js'; import { Input } from '$lib/components/ui/input/index.js'; import { invalidateAll } from '$app/navigation'; - + import client from '$lib/api'; const apiUrl = env.PUBLIC_API_URL; let { users }: { users: User[] } = $props(); let sortedUsers = $derived(users.sort((a, b) => a.email.localeCompare(b.email))); @@ -21,43 +21,26 @@ async function saveUser() { if (!selectedUser) return; - - try { - const response = await fetch(`${apiUrl}/users/${selectedUser.id}`, { - method: 'PATCH', - headers: { - 'Content-Type': 'application/json' - }, - credentials: 'include', - body: JSON.stringify({ - is_verified: selectedUser.is_verified, - is_active: selectedUser.is_active, - is_superuser: selectedUser.is_superuser, - ...(newPassword !== '' && { password: newPassword }), - ...(newEmail !== '' && { email: newEmail }) - }) - }); - - if (response.ok) { - toast.success(`User ${selectedUser.email} updated successfully.`); - dialogOpen = false; - selectedUser = null; - newPassword = ''; - newEmail = ''; - await invalidateAll(); - } else { - const errorText = await response.text(); - console.error(`Failed to update user ${response.statusText}`, errorText); - toast.error(`Failed to update user: ${response.statusText}`); + const {data} = await client.PATCH("/api/v1/users/{id}", { + params: { + query: { + id: selectedUser.id + } + }, + body: { + is_verified: selectedUser.is_verified, + is_active: selectedUser.is_active, + is_superuser: selectedUser.is_superuser, + ...(newPassword !== '' && { password: newPassword }), + ...(newEmail !== '' && { email: newEmail }) } - } catch (error) { - console.error('Error updating user:', error); - toast.error( - 'Error updating user: ' + (error instanceof Error ? error.message : String(error)) - ); - } finally { - newPassword = ''; - } + }) + toast.success(`User ${selectedUser.email} updated successfully.`); + dialogOpen = false; + selectedUser = null; + newPassword = ''; + newEmail = ''; + await invalidateAll(); } diff --git a/web/src/lib/components/user-settings.svelte b/web/src/lib/components/user-settings.svelte index 904a643..a7730a4 100644 --- a/web/src/lib/components/user-settings.svelte +++ b/web/src/lib/components/user-settings.svelte @@ -1,89 +1,75 @@ - - - - - - Edit User Details - - Change your email or password. Leave fields empty to not change them. - - -
- -
- - -
- -
- - -
-
-
- -
-
+ + + + + + Edit User Details + + Change your email or password. Leave fields empty to not change them. + + +
+ +
+ + +
+ +
+ + +
+
+
+ +
+