Return deleted paths in DELETE /videos

This commit is contained in:
Zoe Roux 2025-05-04 15:35:07 +02:00
parent 4df171386b
commit a96813fe30
No known key found for this signature in database
3 changed files with 48 additions and 39 deletions

View File

@ -1,4 +1,4 @@
import { type Column, type SQL, and, eq, isNull, sql } from "drizzle-orm"; import { type Column, type SQL, eq, sql } from "drizzle-orm";
import { db } from "~/db"; import { db } from "~/db";
import { import {
entries, entries,
@ -6,7 +6,7 @@ import {
entryVideoJoin, entryVideoJoin,
videos, videos,
} from "~/db/schema"; } from "~/db/schema";
import { conflictUpdateAllExcept, sqlarr, values } from "~/db/utils"; import { conflictUpdateAllExcept, values } from "~/db/utils";
import type { SeedEntry as SEntry, SeedExtra as SExtra } from "~/models/entry"; import type { SeedEntry as SEntry, SeedExtra as SExtra } from "~/models/entry";
import { enqueueOptImage } from "../images"; import { enqueueOptImage } from "../images";
import { guessNextRefresh } from "../refresh"; import { guessNextRefresh } from "../refresh";

View File

@ -392,48 +392,48 @@ export const videosH = new Elysia({ prefix: "/videos", tags: ["videos"] })
.delete( .delete(
"", "",
async ({ body }) => { async ({ body }) => {
await db.transaction(async (tx) => { return await db.transaction(async (tx) => {
const vids = tx.$with("vids").as( const vids = tx.$with("vids").as(
tx tx
.delete(videos) .delete(videos)
.where(eq(videos.path, sql`any(${body})`)) .where(eq(videos.path, sql`any(${sqlarr(body)})`))
.returning({ pk: videos.pk }), .returning({ pk: videos.pk, path: videos.path }),
); );
const evj = alias(entryVideoJoin, "evj");
const delEntries = tx.$with("del_entries").as( const deletedJoin = await tx
tx .with(vids)
.with(vids) .select({ entryPk: entryVideoJoin.entryPk, path: vids.path })
.select({ entry: entryVideoJoin.entryPk }) .from(entryVideoJoin)
.from(entryVideoJoin) .rightJoin(vids, eq(vids.pk, entryVideoJoin.videoPk));
.where(
and( const delEntries = await tx
inArray(entryVideoJoin.videoPk, tx.select().from(vids)),
notExists(
tx
.select()
.from(evj)
.where(
and(
eq(evj.entryPk, entryVideoJoin.entryPk),
not(inArray(evj.videoPk, db.select().from(vids))),
),
),
),
),
),
);
const delShows = await tx
.with(delEntries)
.update(entries) .update(entries)
.set({ availableSince: null }) .set({ availableSince: null })
.where(inArray(entries.pk, db.select().from(delEntries))) .where(
and(
eq(
entries.pk,
sql`any(${sqlarr(
deletedJoin.filter((x) => x.entryPk).map((x) => x.entryPk!),
)})`,
),
notExists(
tx
.select()
.from(entryVideoJoin)
.where(eq(entries.pk, entryVideoJoin.entryPk)),
),
),
)
.returning({ show: entries.showPk }); .returning({ show: entries.showPk });
await updateAvailableCount( await updateAvailableCount(
tx, tx,
delShows.map((x) => x.show), delEntries.map((x) => x.show),
false, false,
); );
return [...new Set(deletedJoin.map((x) => x.path))];
}); });
}, },
{ {
@ -444,6 +444,6 @@ export const videosH = new Elysia({ prefix: "/videos", tags: ["videos"] })
examples: [bubbleVideo.path], examples: [bubbleVideo.path],
}), }),
), ),
response: { 204: t.Void() }, response: { 200: t.Array(t.String()) },
}, },
); );

View File

@ -171,7 +171,9 @@ describe("Video get/deletion", () => {
it("Delete video", async () => { it("Delete video", async () => {
const [resp, body] = await deleteVideo(["/video/mia s1e13 mismatch.mkv"]); const [resp, body] = await deleteVideo(["/video/mia s1e13 mismatch.mkv"]);
expectStatus(resp, body).toBe(204); expectStatus(resp, body).toBe(200);
expect(body).toBeArrayOfSize(1);
expect(body).toContain("/video/mia s1e13 mismatch.mkv");
const bubble = await db.query.shows.findFirst({ const bubble = await db.query.shows.findFirst({
where: eq(shows.slug, "bubble"), where: eq(shows.slug, "bubble"),
@ -181,7 +183,9 @@ describe("Video get/deletion", () => {
it("Delete all videos of a movie", async () => { it("Delete all videos of a movie", async () => {
const [resp, body] = await deleteVideo(["/video/bubble.mkv"]); const [resp, body] = await deleteVideo(["/video/bubble.mkv"]);
expectStatus(resp, body).toBe(204); expectStatus(resp, body).toBe(200);
expect(body).toBeArrayOfSize(1);
expect(body).toContain("/video/bubble.mkv");
const bubble = await db.query.shows.findFirst({ const bubble = await db.query.shows.findFirst({
where: eq(shows.slug, "bubble"), where: eq(shows.slug, "bubble"),
@ -190,9 +194,9 @@ describe("Video get/deletion", () => {
}); });
it("Delete non existing video", async () => { it("Delete non existing video", async () => {
// it's way too much of a pain to return deleted paths with the current query so this will do
const [resp, body] = await deleteVideo(["/video/toto.mkv"]); const [resp, body] = await deleteVideo(["/video/toto.mkv"]);
expectStatus(resp, body).toBe(204); expectStatus(resp, body).toBe(200);
expect(body).toBeArrayOfSize(0);
}); });
it("Delete episodes", async () => { it("Delete episodes", async () => {
@ -200,7 +204,10 @@ describe("Video get/deletion", () => {
"/video/mia s1e13.mkv", "/video/mia s1e13.mkv",
"/video/mia 2017 s2e1.mkv", "/video/mia 2017 s2e1.mkv",
]); ]);
expectStatus(resp, body).toBe(204); expectStatus(resp, body).toBe(200);
expect(body).toBeArrayOfSize(2);
expect(body).toContain("/video/mia s1e13.mkv");
expect(body).toContain("/video/mia 2017 s2e1.mkv");
const mia = await db.query.shows.findFirst({ const mia = await db.query.shows.findFirst({
where: eq(shows.slug, "made-in-abyss"), where: eq(shows.slug, "made-in-abyss"),
@ -222,6 +229,8 @@ describe("Video get/deletion", () => {
const [resp, body] = await deleteVideo([ const [resp, body] = await deleteVideo([
"/video/mia s1e13 unknown test.mkv", "/video/mia s1e13 unknown test.mkv",
]); ]);
expectStatus(resp, body).toBe(204); expectStatus(resp, body).toBe(200);
expect(body).toBeArrayOfSize(1);
expect(body[0]).toBe("/video/mia s1e13 unknown test.mkv");
}); });
}); });