mirror of
https://github.com/zoriya/Kyoo.git
synced 2025-05-30 19:54:16 -04:00
Start the smart video controller
This commit is contained in:
parent
015f58226a
commit
1a2ab48c73
@ -1,16 +1,23 @@
|
|||||||
|
import { and, eq, inArray, sql } from "drizzle-orm";
|
||||||
import { Elysia, t } from "elysia";
|
import { Elysia, t } from "elysia";
|
||||||
import { db } from "~/db";
|
import { db } from "~/db";
|
||||||
import { videos as videosT } from "~/db/schema";
|
import { entries, entryVideoJoin, shows, videos } from "~/db/schema";
|
||||||
import { bubbleVideo } from "~/models/examples";
|
import { bubbleVideo } from "~/models/examples";
|
||||||
import { SeedVideo, Video } from "~/models/video";
|
import { SeedVideo, Video } from "~/models/video";
|
||||||
import { comment } from "~/utils";
|
import { comment } from "~/utils";
|
||||||
|
import { computeVideoSlug } from "./seed/insert/entries";
|
||||||
|
|
||||||
const CreatedVideo = t.Object({
|
const CreatedVideo = t.Object({
|
||||||
id: t.String({ format: "uuid" }),
|
id: t.String({ format: "uuid" }),
|
||||||
path: t.String({ example: bubbleVideo.path }),
|
path: t.String({ examples: [bubbleVideo.path] }),
|
||||||
|
// entries: t.Array(
|
||||||
|
// t.Object({
|
||||||
|
// slug: t.String({ format: "slug", examples: ["bubble-v2"] }),
|
||||||
|
// }),
|
||||||
|
// ),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const videos = new Elysia({ prefix: "/videos", tags: ["videos"] })
|
export const videosH = new Elysia({ prefix: "/videos", tags: ["videos"] })
|
||||||
.model({
|
.model({
|
||||||
video: Video,
|
video: Video,
|
||||||
"created-videos": t.Array(CreatedVideo),
|
"created-videos": t.Array(CreatedVideo),
|
||||||
@ -22,20 +29,101 @@ export const videos = new Elysia({ prefix: "/videos", tags: ["videos"] })
|
|||||||
.post(
|
.post(
|
||||||
"",
|
"",
|
||||||
async ({ body, error }) => {
|
async ({ body, error }) => {
|
||||||
const ret = await db
|
const oldRet = await db
|
||||||
.insert(videosT)
|
.insert(videos)
|
||||||
.values(body)
|
.values(body)
|
||||||
.onConflictDoNothing()
|
.onConflictDoNothing()
|
||||||
.returning({ id: videosT.id, path: videosT.path });
|
.returning({
|
||||||
return error(201, ret);
|
pk: videos.pk,
|
||||||
|
id: videos.id,
|
||||||
|
path: videos.path,
|
||||||
|
guess: videos.guess,
|
||||||
|
});
|
||||||
|
return error(201, oldRet);
|
||||||
|
|
||||||
|
// TODO: this is a huge untested wip
|
||||||
|
const vidsI = db.$with("vidsI").as(
|
||||||
|
db.insert(videos).values(body).onConflictDoNothing().returning({
|
||||||
|
pk: videos.pk,
|
||||||
|
id: videos.id,
|
||||||
|
path: videos.path,
|
||||||
|
guess: videos.guess,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
const findEntriesQ = db
|
||||||
|
.select({
|
||||||
|
guess: videos.guess,
|
||||||
|
entryPk: entries.pk,
|
||||||
|
showSlug: shows.slug,
|
||||||
|
// TODO: handle extras here
|
||||||
|
// guessit can't know if an episode is a special or not. treat specials like a normal episode.
|
||||||
|
kind: sql`
|
||||||
|
case when ${entries.kind} = 'movie' then 'movie' else 'episode' end
|
||||||
|
`.as("kind"),
|
||||||
|
season: entries.seasonNumber,
|
||||||
|
episode: entries.episodeNumber,
|
||||||
|
})
|
||||||
|
.from(entries)
|
||||||
|
.leftJoin(entryVideoJoin, eq(entryVideoJoin.entry, entries.pk))
|
||||||
|
.leftJoin(videos, eq(videos.pk, entryVideoJoin.video))
|
||||||
|
.leftJoin(shows, eq(shows.pk, entries.showPk))
|
||||||
|
.as("find_entries");
|
||||||
|
|
||||||
|
const hasRenderingQ = db
|
||||||
|
.select()
|
||||||
|
.from(entryVideoJoin)
|
||||||
|
.where(eq(entryVideoJoin.entry, findEntriesQ.entryPk));
|
||||||
|
|
||||||
|
const ret = await db
|
||||||
|
.with(vidsI)
|
||||||
|
.insert(entryVideoJoin)
|
||||||
|
.select(
|
||||||
|
db
|
||||||
|
.select({
|
||||||
|
entry: findEntriesQ.entryPk,
|
||||||
|
video: vidsI.pk,
|
||||||
|
slug: computeVideoSlug(
|
||||||
|
findEntriesQ.showSlug,
|
||||||
|
sql`exists(${hasRenderingQ})`,
|
||||||
|
),
|
||||||
|
})
|
||||||
|
.from(vidsI)
|
||||||
|
.leftJoin(
|
||||||
|
findEntriesQ,
|
||||||
|
and(
|
||||||
|
eq(
|
||||||
|
sql`${findEntriesQ.guess}->'title'`,
|
||||||
|
sql`${vidsI.guess}->'title'`,
|
||||||
|
),
|
||||||
|
// TODO: find if @> with a jsonb created on the fly is
|
||||||
|
// better than multiples checks
|
||||||
|
sql`${vidsI.guess} @> {"kind": }::jsonb`,
|
||||||
|
inArray(findEntriesQ.kind, sql`${vidsI.guess}->'type'`),
|
||||||
|
inArray(findEntriesQ.episode, sql`${vidsI.guess}->'episode'`),
|
||||||
|
inArray(findEntriesQ.season, sql`${vidsI.guess}->'season'`),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.onConflictDoNothing()
|
||||||
|
.returning({
|
||||||
|
slug: entryVideoJoin.slug,
|
||||||
|
entryPk: entryVideoJoin.entry,
|
||||||
|
id: vidsI.id,
|
||||||
|
path: vidsI.path,
|
||||||
|
});
|
||||||
|
return error(201, ret as any);
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
body: t.Array(SeedVideo),
|
body: t.Array(SeedVideo),
|
||||||
response: { 201: "created-videos" },
|
response: { 201: t.Array(CreatedVideo) },
|
||||||
detail: {
|
detail: {
|
||||||
description: comment`
|
description: comment`
|
||||||
Create videos in bulk.
|
Create videos in bulk.
|
||||||
Duplicated videos will simply be ignored.
|
Duplicated videos will simply be ignored.
|
||||||
|
|
||||||
|
If a videos has a \`guess\` field, it will be used to automatically register the video under an existing
|
||||||
|
movie or entry.
|
||||||
`,
|
`,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -7,7 +7,7 @@ import { movies } from "./controllers/movies";
|
|||||||
import { seasonsH } from "./controllers/seasons";
|
import { seasonsH } from "./controllers/seasons";
|
||||||
import { seed } from "./controllers/seed";
|
import { seed } from "./controllers/seed";
|
||||||
import { series } from "./controllers/series";
|
import { series } from "./controllers/series";
|
||||||
import { videos } from "./controllers/videos";
|
import { videosH } from "./controllers/videos";
|
||||||
import { migrate } from "./db";
|
import { migrate } from "./db";
|
||||||
import { Image } from "./models/utils";
|
import { Image } from "./models/utils";
|
||||||
import { comment } from "./utils";
|
import { comment } from "./utils";
|
||||||
@ -75,7 +75,7 @@ const app = new Elysia()
|
|||||||
.use(series)
|
.use(series)
|
||||||
.use(entries)
|
.use(entries)
|
||||||
.use(seasonsH)
|
.use(seasonsH)
|
||||||
.use(videos)
|
.use(videosH)
|
||||||
.use(seed)
|
.use(seed)
|
||||||
.listen(3000);
|
.listen(3000);
|
||||||
|
|
||||||
|
@ -2,9 +2,6 @@ import { type TSchema, t } from "elysia";
|
|||||||
import { comment } from "../utils";
|
import { comment } from "../utils";
|
||||||
import { bubbleVideo, registerExamples } from "./examples";
|
import { bubbleVideo, registerExamples } from "./examples";
|
||||||
|
|
||||||
const Guess = <T extends TSchema>(schema: T) =>
|
|
||||||
t.Optional(t.Union([schema, t.Array(schema)]));
|
|
||||||
|
|
||||||
export const Video = t.Object({
|
export const Video = t.Object({
|
||||||
id: t.String({ format: "uuid" }),
|
id: t.String({ format: "uuid" }),
|
||||||
slug: t.String({ format: "slug" }),
|
slug: t.String({ format: "slug" }),
|
||||||
@ -40,9 +37,9 @@ export const Video = t.Object({
|
|||||||
t.Object(
|
t.Object(
|
||||||
{
|
{
|
||||||
title: t.String(),
|
title: t.String(),
|
||||||
year: Guess(t.Integer()),
|
year: t.Array(t.Integer(), { default: [] }),
|
||||||
season: Guess(t.Integer()),
|
season: t.Array(t.Integer(), { default: [] }),
|
||||||
episode: Guess(t.Integer()),
|
episode: t.Array(t.Integer(), { default: [] }),
|
||||||
// TODO: maybe replace "extra" with the `extraKind` value (aka behind-the-scene, trailer, etc)
|
// TODO: maybe replace "extra" with the `extraKind` value (aka behind-the-scene, trailer, etc)
|
||||||
type: t.Optional(t.UnionEnum(["episode", "movie", "extra"])),
|
type: t.Optional(t.UnionEnum(["episode", "movie", "extra"])),
|
||||||
|
|
||||||
|
@ -4,7 +4,7 @@ import { base } from "~/base";
|
|||||||
import { movies } from "~/controllers/movies";
|
import { movies } from "~/controllers/movies";
|
||||||
import { seed } from "~/controllers/seed";
|
import { seed } from "~/controllers/seed";
|
||||||
import { series } from "~/controllers/series";
|
import { series } from "~/controllers/series";
|
||||||
import { videos } from "~/controllers/videos";
|
import { videosH } from "~/controllers/videos";
|
||||||
import type { SeedMovie } from "~/models/movie";
|
import type { SeedMovie } from "~/models/movie";
|
||||||
import type { SeedVideo } from "~/models/video";
|
import type { SeedVideo } from "~/models/video";
|
||||||
|
|
||||||
@ -12,7 +12,7 @@ export const app = new Elysia()
|
|||||||
.use(base)
|
.use(base)
|
||||||
.use(movies)
|
.use(movies)
|
||||||
.use(series)
|
.use(series)
|
||||||
.use(videos)
|
.use(videosH)
|
||||||
.use(seed);
|
.use(seed);
|
||||||
|
|
||||||
export const getMovie = async (
|
export const getMovie = async (
|
||||||
|
Loading…
x
Reference in New Issue
Block a user