Merge pull request #171 from maxdorninger/rework-torrent-handling

Add ability to manually mark torrent as imported and speed up eslint
This commit is contained in:
Maximilian Dorninger
2025-10-30 16:36:38 +01:00
committed by GitHub
6 changed files with 155 additions and 5 deletions

View File

@@ -17,7 +17,7 @@ class IndexerQueryResult(BaseModel):
title: str
download_url: str = pydantic.Field(
exclude=True,
description="This can be a magnet link or URL to the .torrent file"
description="This can be a magnet link or URL to the .torrent file",
)
seeders: int
flags: list[str]

View File

@@ -1,10 +1,16 @@
from fastapi.exceptions import HTTPException
from fastapi import APIRouter
from fastapi import status
from fastapi.params import Depends
from media_manager.auth.users import current_active_user, current_superuser
from media_manager.torrent.dependencies import torrent_service_dep, torrent_dep
from media_manager.torrent.schemas import Torrent
from media_manager.torrent.dependencies import (
torrent_service_dep,
torrent_dep,
torrent_repository_dep,
)
from media_manager.torrent.schemas import Torrent, TorrentStatus
router = APIRouter()
@@ -53,3 +59,29 @@ def retry_torrent_download(
):
service.pause_download(torrent=torrent)
service.resume_download(torrent=torrent)
@router.patch(
"/{torrent_id}/status",
status_code=status.HTTP_200_OK,
dependencies=[Depends(current_superuser)],
response_model=Torrent,
)
def update_torrent_status(
rep: torrent_repository_dep,
torrent: torrent_dep,
state: TorrentStatus | None = None,
imported: bool | None = None,
):
if imported is not None:
torrent.imported = imported
if state is not None:
torrent.status = state
if state is None and imported is None:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="No status or imported value provided",
)
rep.save_torrent(torrent=torrent)
return torrent

View File

@@ -10,7 +10,7 @@
"prepare": "svelte-kit sync || echo ''",
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
"lint": "eslint --fix . && prettier --check .",
"lint": "eslint --concurrency auto --fix . && prettier --check .",
"format": "prettier --write .",
"openapi": "npx openapi-typescript http://localhost:8000/openapi.json -o src/lib/api/api.d.ts"
},

View File

@@ -633,6 +633,23 @@ export interface paths {
patch?: never;
trace?: never;
};
'/api/v1/torrent/{torrent_id}/status': {
parameters: {
query?: never;
header?: never;
path?: never;
cookie?: never;
};
get?: never;
put?: never;
post?: never;
delete?: never;
options?: never;
head?: never;
/** Update Torrent Status */
patch: operations['update_torrent_status_api_v1_torrent__torrent_id__status_patch'];
trace?: never;
};
'/api/v1/movies': {
parameters: {
query?: never;
@@ -3150,6 +3167,40 @@ export interface operations {
};
};
};
update_torrent_status_api_v1_torrent__torrent_id__status_patch: {
parameters: {
query?: {
state?: components['schemas']['TorrentStatus'] | null;
imported?: boolean | null;
};
header?: never;
path: {
torrent_id: string;
};
cookie?: never;
};
requestBody?: never;
responses: {
/** @description Successful Response */
200: {
headers: {
[name: string]: unknown;
};
content: {
'application/json': components['schemas']['Torrent'];
};
};
/** @description Validation Error */
422: {
headers: {
[name: string]: unknown;
};
content: {
'application/json': components['schemas']['HTTPValidationError'];
};
};
};
};
get_all_movies_api_v1_movies_get: {
parameters: {
query?: never;

View File

@@ -0,0 +1,66 @@
<script lang="ts">
import { Button } from '$lib/components/ui/button/index.js';
import * as Dialog from '$lib/components/ui/dialog/index.js';
import type { components } from '$lib/api/api';
import { Switch } from '$lib/components/ui/switch';
import { Label } from '$lib/components/ui/label';
import client from '$lib/api';
import { toast } from 'svelte-sonner';
import { invalidateAll } from '$app/navigation';
let {
torrent
}: {
torrent: components['schemas']['MovieTorrent'] | components['schemas']['RichSeasonTorrent'];
} = $props();
let dialogOpen = $state(false);
let importedState = $state(torrent.imported || false);
async function closeDialog() {
dialogOpen = false;
importedState = torrent.imported || false;
}
async function saveTorrent() {
const { error } = await client.PATCH('/api/v1/torrent/{torrent_id}/status', {
params: {
path: {
torrent_id: torrent.torrent_id!
},
query: {
imported: importedState
}
}
});
if (error) {
console.error(`Failed to update torrent ${torrent.torrent_id} imported state: ${error}`);
toast.error(`Failed to update torrent: ${error}`);
return;
}
await invalidateAll();
await closeDialog();
}
</script>
<Dialog.Root bind:open={dialogOpen}>
<Dialog.Trigger>
<Button class="w-full" onclick={() => (dialogOpen = true)}>Edit Torrent</Button>
</Dialog.Trigger>
<Dialog.Content class="w-full max-w-[600px] rounded-lg p-6 shadow-lg">
<Dialog.Header>
<Dialog.Title class="mb-1 text-xl font-semibold">Edit Torrent</Dialog.Title>
<Dialog.Description class="mb-4 text-sm">
Edit torrent "{torrent.torrent_title}".
</Dialog.Description>
</Dialog.Header>
<div class="flex gap-2">
<Switch bind:checked={importedState} id="imported-state" />
<Label for="imported-state"
>Change Torrent import state to: {importedState ? 'is' : 'is not'} imported.</Label
>
</div>
<Dialog.Footer class="mt-8 flex justify-between gap-2">
<Button onclick={() => closeDialog()} variant="secondary">Cancel</Button>
<Button onclick={() => saveTorrent()}>Save Torrent</Button>
</Dialog.Footer>
</Dialog.Content>
</Dialog.Root>

View File

@@ -12,7 +12,7 @@
import client from '$lib/api';
import { toast } from 'svelte-sonner';
import DeleteTorrentDialog from '$lib/components/delete-torrent-dialog.svelte';
import EditTorrentDialog from '$lib/components/edit-torrent-dialog.svelte';
let {
torrents,
isShow = true
@@ -96,6 +96,7 @@
{/if}
<DeleteTorrentDialog torrentName={torrent.torrent_title} torrentId={torrent.torrent_id!}
></DeleteTorrentDialog>
<EditTorrentDialog {torrent} />
</Table.Cell>
{/if}
</Table.Row>