mirror of
https://github.com/zoriya/Kyoo.git
synced 2025-07-08 18:54:22 -04:00
Rework POST /videos
This commit is contained in:
parent
621c9cec82
commit
1369da1845
@ -1,11 +1,17 @@
|
|||||||
import { and, eq, exists, inArray, not, sql } from "drizzle-orm";
|
import { and, eq, exists, inArray, not, or, sql } from "drizzle-orm";
|
||||||
import { alias } from "drizzle-orm/pg-core";
|
import { alias } from "drizzle-orm/pg-core";
|
||||||
import { Elysia, t } from "elysia";
|
import { Elysia, t } from "elysia";
|
||||||
import { db } from "~/db";
|
import { db } from "~/db";
|
||||||
import { entries, entryVideoJoin, shows, videos } from "~/db/schema";
|
import { entries, entryVideoJoin, shows, videos } from "~/db/schema";
|
||||||
import { jsonbBuildObject, jsonbObjectAgg, sqlarr } from "~/db/utils";
|
import {
|
||||||
|
conflictUpdateAllExcept,
|
||||||
|
jsonbBuildObject,
|
||||||
|
jsonbObjectAgg,
|
||||||
|
sqlarr,
|
||||||
|
values,
|
||||||
|
} from "~/db/utils";
|
||||||
import { bubbleVideo } from "~/models/examples";
|
import { bubbleVideo } from "~/models/examples";
|
||||||
import { Page } from "~/models/utils";
|
import { Page, isUuid } from "~/models/utils";
|
||||||
import { Guesses, SeedVideo, Video } from "~/models/video";
|
import { Guesses, SeedVideo, Video } from "~/models/video";
|
||||||
import { comment } from "~/utils";
|
import { comment } from "~/utils";
|
||||||
import { computeVideoSlug } from "./seed/insert/entries";
|
import { computeVideoSlug } from "./seed/insert/entries";
|
||||||
@ -14,11 +20,11 @@ import { updateAvailableCount } from "./seed/insert/shows";
|
|||||||
const CreatedVideo = t.Object({
|
const CreatedVideo = t.Object({
|
||||||
id: t.String({ format: "uuid" }),
|
id: t.String({ format: "uuid" }),
|
||||||
path: t.String({ examples: [bubbleVideo.path] }),
|
path: t.String({ examples: [bubbleVideo.path] }),
|
||||||
// entries: t.Array(
|
entries: t.Array(
|
||||||
// t.Object({
|
t.Object({
|
||||||
// slug: t.String({ format: "slug", examples: ["bubble-v2"] }),
|
slug: t.String({ format: "slug", examples: ["bubble-v2"] }),
|
||||||
// }),
|
}),
|
||||||
// ),
|
),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const videosH = new Elysia({ prefix: "/videos", tags: ["videos"] })
|
export const videosH = new Elysia({ prefix: "/videos", tags: ["videos"] })
|
||||||
@ -63,7 +69,9 @@ export const videosH = new Elysia({ prefix: "/videos", tags: ["videos"] })
|
|||||||
|
|
||||||
const [{ guesses }] = await db
|
const [{ guesses }] = await db
|
||||||
.with(years, guess)
|
.with(years, guess)
|
||||||
.select({ guesses: jsonbObjectAgg<Guesses["guesses"]>(guess.guess, guess.years) })
|
.select({
|
||||||
|
guesses: jsonbObjectAgg<Guesses["guesses"]>(guess.guess, guess.years),
|
||||||
|
})
|
||||||
.from(guess);
|
.from(guess);
|
||||||
|
|
||||||
const paths = await db.select({ path: videos.path }).from(videos);
|
const paths = await db.select({ path: videos.path }).from(videos);
|
||||||
@ -80,90 +88,128 @@ export const videosH = new Elysia({ prefix: "/videos", tags: ["videos"] })
|
|||||||
.post(
|
.post(
|
||||||
"",
|
"",
|
||||||
async ({ body, error }) => {
|
async ({ body, error }) => {
|
||||||
const oldRet = await db
|
const vidsI = db.$with("vidsI").as(
|
||||||
|
db
|
||||||
.insert(videos)
|
.insert(videos)
|
||||||
.values(body)
|
.values(body)
|
||||||
.onConflictDoNothing()
|
.onConflictDoUpdate({
|
||||||
|
target: [videos.path],
|
||||||
|
set: conflictUpdateAllExcept(videos, ["pk", "id", "createdAt"]),
|
||||||
|
})
|
||||||
.returning({
|
.returning({
|
||||||
pk: videos.pk,
|
pk: videos.pk,
|
||||||
id: videos.id,
|
id: videos.id,
|
||||||
path: videos.path,
|
path: videos.path,
|
||||||
guess: videos.guess,
|
}),
|
||||||
});
|
);
|
||||||
return error(201, oldRet);
|
|
||||||
|
|
||||||
// TODO: this is a huge untested wip
|
const entriesQ = db
|
||||||
// const vidsI = db.$with("vidsI").as(
|
.select({
|
||||||
// db.insert(videos).values(body).onConflictDoNothing().returning({
|
pk: entries.pk,
|
||||||
// pk: videos.pk,
|
id: entries.id,
|
||||||
// id: videos.id,
|
slug: entries.slug,
|
||||||
// path: videos.path,
|
seasonNumber: entries.seasonNumber,
|
||||||
// guess: videos.guess,
|
episodeNumber: entries.episodeNumber,
|
||||||
// }),
|
order: entries.order,
|
||||||
// );
|
showId: shows.id,
|
||||||
//
|
showSlug: shows.slug,
|
||||||
// const findEntriesQ = db
|
})
|
||||||
// .select({
|
.from(entries)
|
||||||
// guess: videos.guess,
|
.innerJoin(shows, eq(entries.showPk, shows.pk))
|
||||||
// entryPk: entries.pk,
|
.as("entriesQ");
|
||||||
// showSlug: shows.slug,
|
|
||||||
// // TODO: handle extras here
|
const hasRenderingQ = db
|
||||||
// // guessit can't know if an episode is a special or not. treat specials like a normal episode.
|
.select()
|
||||||
// kind: sql`
|
.from(entryVideoJoin)
|
||||||
// case when ${entries.kind} = 'movie' then 'movie' else 'episode' end
|
.where(eq(entryVideoJoin.entryPk, entriesQ.pk));
|
||||||
// `.as("kind"),
|
|
||||||
// season: entries.seasonNumber,
|
const ret = await db
|
||||||
// episode: entries.episodeNumber,
|
.with(vidsI)
|
||||||
// })
|
.insert(entryVideoJoin)
|
||||||
// .from(entries)
|
.select(
|
||||||
// .leftJoin(entryVideoJoin, eq(entryVideoJoin.entry, entries.pk))
|
db
|
||||||
// .leftJoin(videos, eq(videos.pk, entryVideoJoin.video))
|
.select({
|
||||||
// .leftJoin(shows, eq(shows.pk, entries.showPk))
|
entry: entries.pk,
|
||||||
// .as("find_entries");
|
video: vidsI.pk,
|
||||||
//
|
slug: computeVideoSlug(
|
||||||
// const hasRenderingQ = db
|
entriesQ.showSlug,
|
||||||
// .select()
|
sql`j.needRendering::boolean || exists(${hasRenderingQ})`,
|
||||||
// .from(entryVideoJoin)
|
),
|
||||||
// .where(eq(entryVideoJoin.entry, findEntriesQ.entryPk));
|
})
|
||||||
//
|
.from(
|
||||||
// const ret = await db
|
values(
|
||||||
// .with(vidsI)
|
body.flatMap((x) =>
|
||||||
// .insert(entryVideoJoin)
|
x.for.map((e) => ({
|
||||||
// .select(
|
path: x.path,
|
||||||
// db
|
needRendering: x.for.length > 1,
|
||||||
// .select({
|
entry: {
|
||||||
// entry: findEntriesQ.entryPk,
|
...e,
|
||||||
// video: vidsI.pk,
|
movie:
|
||||||
// slug: computeVideoSlug(
|
"movie" in e
|
||||||
// findEntriesQ.showSlug,
|
? isUuid(e.movie)
|
||||||
// sql`exists(${hasRenderingQ})`,
|
? { id: e.movie }
|
||||||
// ),
|
: { slug: e.movie }
|
||||||
// })
|
: undefined,
|
||||||
// .from(vidsI)
|
serie:
|
||||||
// .leftJoin(
|
"serie" in e
|
||||||
// findEntriesQ,
|
? isUuid(e.serie)
|
||||||
// and(
|
? { id: e.serie }
|
||||||
// eq(
|
: { slug: e.serie }
|
||||||
// sql`${findEntriesQ.guess}->'title'`,
|
: undefined,
|
||||||
// sql`${vidsI.guess}->'title'`,
|
},
|
||||||
// ),
|
})),
|
||||||
// // TODO: find if @> with a jsonb created on the fly is
|
),
|
||||||
// // better than multiples checks
|
).as("j"),
|
||||||
// sql`${vidsI.guess} @> {"kind": }::jsonb`,
|
)
|
||||||
// inArray(findEntriesQ.kind, sql`${vidsI.guess}->'type'`),
|
.innerJoin(vidsI, eq(vidsI.path, sql`j.path`))
|
||||||
// inArray(findEntriesQ.episode, sql`${vidsI.guess}->'episode'`),
|
.innerJoin(
|
||||||
// inArray(findEntriesQ.season, sql`${vidsI.guess}->'season'`),
|
entriesQ,
|
||||||
// ),
|
or(
|
||||||
// ),
|
and(
|
||||||
// )
|
sql`j.entry ? 'slug'`,
|
||||||
// .onConflictDoNothing()
|
eq(entriesQ.slug, sql`j.entry->'slug'`),
|
||||||
// .returning({
|
),
|
||||||
// slug: entryVideoJoin.slug,
|
and(
|
||||||
// entryPk: entryVideoJoin.entry,
|
sql`j.entry ? 'movie'`,
|
||||||
// id: vidsI.id,
|
or(
|
||||||
// path: vidsI.path,
|
eq(entriesQ.showId, sql`j.entry #> '{movie, id}'`),
|
||||||
// });
|
eq(entriesQ.showSlug, sql`j.entry #> '{movie, slug}'`),
|
||||||
// return error(201, ret as any);
|
),
|
||||||
|
),
|
||||||
|
and(
|
||||||
|
sql`j.entry ? 'serie'`,
|
||||||
|
or(
|
||||||
|
eq(entriesQ.showId, sql`j.entry #> '{serie, id}'`),
|
||||||
|
eq(entriesQ.showSlug, sql`j.entry #> '{serie, slug}'`),
|
||||||
|
),
|
||||||
|
or(
|
||||||
|
and(
|
||||||
|
sql`j.entry ?& array['season', 'episode']`,
|
||||||
|
eq(entriesQ.seasonNumber, sql`j.entry->'season'`),
|
||||||
|
eq(entriesQ.episodeNumber, sql`j.entry->'episode'`),
|
||||||
|
),
|
||||||
|
and(
|
||||||
|
sql`j.entry ? 'order'`,
|
||||||
|
eq(entriesQ.order, sql`j.entry->'order'`),
|
||||||
|
),
|
||||||
|
and(
|
||||||
|
sql`j.entry ? 'special'`,
|
||||||
|
eq(entriesQ.episodeNumber, sql`j.entry->'special'`),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.onConflictDoNothing()
|
||||||
|
.returning({
|
||||||
|
slug: entryVideoJoin.slug,
|
||||||
|
entryPk: entryVideoJoin.entryPk,
|
||||||
|
id: vidsI.id,
|
||||||
|
path: vidsI.path,
|
||||||
|
});
|
||||||
|
return error(201, ret);
|
||||||
|
// return error(201, ret.map(x => ({ id: x.id, slug: x.})));
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
detail: {
|
detail: {
|
||||||
@ -176,7 +222,7 @@ export const videosH = new Elysia({ prefix: "/videos", tags: ["videos"] })
|
|||||||
`,
|
`,
|
||||||
},
|
},
|
||||||
body: t.Array(SeedVideo),
|
body: t.Array(SeedVideo),
|
||||||
response: { 201: t.Array(CreatedVideo) },
|
// response: { 201: t.Array(CreatedVideo) },
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.delete(
|
.delete(
|
||||||
|
@ -70,12 +70,20 @@ export const SeedVideo = t.Object({
|
|||||||
|
|
||||||
for: t.Array(
|
for: t.Array(
|
||||||
t.Union([
|
t.Union([
|
||||||
|
t.Object({
|
||||||
|
slug: t.String({
|
||||||
|
format: "slug",
|
||||||
|
examples: ["made-in-abyss-dawn-of-the-deep-soul"],
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
t.Object({
|
||||||
|
externalId: t.Optional(t.Union([EpisodeId, ExternalId()])),
|
||||||
|
}),
|
||||||
t.Object({
|
t.Object({
|
||||||
movie: t.Union([
|
movie: t.Union([
|
||||||
t.String({ format: "uuid" }),
|
t.String({ format: "uuid" }),
|
||||||
t.String({ format: "slug", examples: ["bubble"] }),
|
t.String({ format: "slug", examples: ["bubble"] }),
|
||||||
]),
|
]),
|
||||||
externalId: t.Optional(ExternalId()),
|
|
||||||
}),
|
}),
|
||||||
t.Intersect([
|
t.Intersect([
|
||||||
t.Object({
|
t.Object({
|
||||||
@ -88,22 +96,12 @@ export const SeedVideo = t.Object({
|
|||||||
t.Object({
|
t.Object({
|
||||||
season: t.Integer({ minimum: 1 }),
|
season: t.Integer({ minimum: 1 }),
|
||||||
episode: t.Integer(),
|
episode: t.Integer(),
|
||||||
externalId: t.Optional(EpisodeId),
|
|
||||||
}),
|
}),
|
||||||
t.Object({
|
t.Object({
|
||||||
absolute: t.Integer(),
|
order: t.Number(),
|
||||||
externalId: t.Optional(t.Union([EpisodeId, ExternalId()])),
|
|
||||||
}),
|
}),
|
||||||
t.Object({
|
t.Object({
|
||||||
special: t.Integer(),
|
special: t.Integer(),
|
||||||
externalId: t.Optional(EpisodeId),
|
|
||||||
}),
|
|
||||||
t.Object({
|
|
||||||
slug: t.String({
|
|
||||||
format: "slug",
|
|
||||||
examples: ["made-in-abyss-dawn-of-the-deep-soul"],
|
|
||||||
}),
|
|
||||||
externalId: t.Optional(t.Union([EpisodeId, ExternalId()])),
|
|
||||||
}),
|
}),
|
||||||
]),
|
]),
|
||||||
]),
|
]),
|
||||||
|
Loading…
x
Reference in New Issue
Block a user