diff --git a/backend/app/routes/downloads.py b/backend/app/routes/downloads.py
index ee669e0..783eddb 100644
--- a/backend/app/routes/downloads.py
+++ b/backend/app/routes/downloads.py
@@ -258,8 +258,12 @@ def enqueue_download(
def enqueue_edit(
payload: dict, user: User = Depends(require_human), db: Session = Depends(get_db)
) -> dict:
- """Queue a phase-2 editor derivative (trim/crop) of one of the user's finished downloads."""
- source_job = _own_job(db, user, int(payload.get("source_job_id") or 0))
+ """Queue a phase-2 editor derivative (trim/crop) of a download this user can access.
+
+ The source may be the user's own download OR one shared with them — either way the resulting
+ clip is a NEW per-user derivative in the editor's own library (counts against their quota); the
+ source file is only read, never modified, so editing a shared video is safe."""
+ source_job = _accessible_job(db, user, int(payload.get("source_job_id") or 0))
if source_job.status != "done" or source_job.asset_id is None:
raise HTTPException(status_code=409, detail="You can only edit a finished download.")
spec = payload.get("edit_spec")
@@ -619,6 +623,24 @@ def unshare_download(
return {"ok": True}
+@router.delete("/shared/{job_id}")
+def remove_shared_with_me(
+ job_id: int, user: User = Depends(require_human), db: Session = Depends(get_db)
+) -> dict:
+ """Remove a download that was shared WITH this user from their 'Shared with me' list. Deletes
+ only the recipient's share grant — the owner's job and the physical file are untouched (this is
+ a per-user dismissal, not a delete)."""
+ row = db.execute(
+ select(DownloadShare).where(
+ DownloadShare.job_id == job_id, DownloadShare.to_user_id == user.id
+ )
+ ).scalar_one_or_none()
+ if row is not None:
+ db.delete(row)
+ db.commit()
+ return {"removed": job_id}
+
+
# --- recipients (for the internal "share with a user" picker) ------------------------------
@router.get("/recipients")
diff --git a/frontend/src/components/DownloadCenter.tsx b/frontend/src/components/DownloadCenter.tsx
index 074fff0..bc29a71 100644
--- a/frontend/src/components/DownloadCenter.tsx
+++ b/frontend/src/components/DownloadCenter.tsx
@@ -428,6 +428,12 @@ export default function DownloadCenter({ me }: { me: Me }) {
onSuccess: invalidate,
});
+ // Remove a shared-with-me item from your list (only your grant; the owner's file is untouched).
+ const removeShared = useMutation({
+ mutationFn: (id: number) => api.removeSharedDownload(id),
+ onSuccess: () => qc.invalidateQueries({ queryKey: ["downloads-shared"] }),
+ });
+
const confirmThen = async (title: string, body: string, fn: () => void) => {
if (await confirm({ title, message: body, confirmLabel: title, danger: true })) fn();
};
@@ -545,6 +551,24 @@ export default function DownloadCenter({ me }: { me: Me }) {
{(sharedQ.data ?? []).map((job) => (
{job.can_download && saveBtn(job)}
+ {job.can_download && (job.asset?.duration_s ?? 0) > 0 && (
+ setEditJob(job)} title={t("downloads.actions.edit")}>
+
+
+ )}
+
+ confirmThen(
+ t("downloads.confirm.removeSharedTitle"),
+ t("downloads.confirm.removeSharedBody"),
+ () => removeShared.mutate(job.id)
+ )
+ }
+ title={t("downloads.actions.removeShared")}
+ danger
+ >
+
+
))}
{sharedQ.data && sharedQ.data.length === 0 && (
diff --git a/frontend/src/i18n/locales/de/downloads.json b/frontend/src/i18n/locales/de/downloads.json
index 13cc467..0054699 100644
--- a/frontend/src/i18n/locales/de/downloads.json
+++ b/frontend/src/i18n/locales/de/downloads.json
@@ -57,7 +57,8 @@
"download": "Auf mein Gerät speichern",
"rename": "Umbenennen",
"share": "Teilen",
- "edit": "Bearbeiten / schneiden"
+ "edit": "Bearbeiten / schneiden",
+ "removeShared": "Aus meiner Liste entfernen"
},
"empty": {
"queue": "Die Warteschlange ist leer.",
@@ -110,7 +111,9 @@
"cancelTitle": "Download abbrechen?",
"cancelBody": "Dies stoppt den Download und entfernt ihn aus deiner Warteschlange.",
"deleteTitle": "Download entfernen?",
- "deleteBody": "Dies entfernt ihn aus deiner Liste. Die Datei kann bis zum Ablauf für andere Nutzer im Cache bleiben."
+ "deleteBody": "Dies entfernt ihn aus deiner Liste. Die Datei kann bis zum Ablauf für andere Nutzer im Cache bleiben.",
+ "removeSharedTitle": "Aus deiner Liste entfernen?",
+ "removeSharedBody": "Entfernt es nur aus deiner geteilten Liste — die Datei des Besitzers wird nicht gelöscht."
},
"profiles": {
"manage": "Formate verwalten",
diff --git a/frontend/src/i18n/locales/en/downloads.json b/frontend/src/i18n/locales/en/downloads.json
index f96cd82..06db1f6 100644
--- a/frontend/src/i18n/locales/en/downloads.json
+++ b/frontend/src/i18n/locales/en/downloads.json
@@ -57,7 +57,8 @@
"download": "Save to my device",
"rename": "Rename",
"share": "Share",
- "edit": "Edit / trim"
+ "edit": "Edit / trim",
+ "removeShared": "Remove from my list"
},
"empty": {
"queue": "Nothing in the queue.",
@@ -110,7 +111,9 @@
"cancelTitle": "Cancel download?",
"cancelBody": "This stops the download and removes it from your queue.",
"deleteTitle": "Remove download?",
- "deleteBody": "This removes it from your list. The file may stay cached for other users until it expires."
+ "deleteBody": "This removes it from your list. The file may stay cached for other users until it expires.",
+ "removeSharedTitle": "Remove from your list?",
+ "removeSharedBody": "This removes it from your shared list only — it won't delete the owner's file."
},
"profiles": {
"manage": "Manage formats",
diff --git a/frontend/src/i18n/locales/hu/downloads.json b/frontend/src/i18n/locales/hu/downloads.json
index 1a097ea..4091429 100644
--- a/frontend/src/i18n/locales/hu/downloads.json
+++ b/frontend/src/i18n/locales/hu/downloads.json
@@ -57,7 +57,8 @@
"download": "Mentés a gépemre",
"rename": "Átnevezés",
"share": "Megosztás",
- "edit": "Szerkesztés / vágás"
+ "edit": "Szerkesztés / vágás",
+ "removeShared": "Eltávolítás a listámból"
},
"empty": {
"queue": "A sor üres.",
@@ -110,7 +111,9 @@
"cancelTitle": "Megszakítod a letöltést?",
"cancelBody": "Ez leállítja a letöltést és eltávolítja a sorból.",
"deleteTitle": "Eltávolítod a letöltést?",
- "deleteBody": "Ez eltávolítja a listádról. A fájl a lejáratáig még a gyorsítótárban maradhat mások számára."
+ "deleteBody": "Ez eltávolítja a listádról. A fájl a lejáratáig még a gyorsítótárban maradhat mások számára.",
+ "removeSharedTitle": "Eltávolítod a listádból?",
+ "removeSharedBody": "Csak a te megosztott listádból tűnik el — a tulajdonos fájlját nem törli."
},
"profiles": {
"manage": "Formátumok kezelése",
diff --git a/frontend/src/lib/api.ts b/frontend/src/lib/api.ts
index e7ae4bd..0e60943 100644
--- a/frontend/src/lib/api.ts
+++ b/frontend/src/lib/api.ts
@@ -1075,6 +1075,9 @@ export const api = {
req(`/api/downloads/${id}/storyboard`),
downloads: (): Promise => req("/api/downloads"),
downloadsShared: (): Promise => req("/api/downloads/shared"),
+ // Remove a shared-with-me item from your list (deletes only your share grant, not the file).
+ removeSharedDownload: (jobId: number): Promise<{ removed: number }> =>
+ req(`/api/downloads/shared/${jobId}`, { method: "DELETE" }),
downloadUsage: (): Promise => req("/api/downloads/usage"),
downloadIndex: (): Promise> => req("/api/downloads/index"),
pauseDownload: (id: number): Promise =>