Delete unknown entries, rework them as part of unmatched videos

This commit is contained in:
Zoe Roux 2025-04-30 14:52:17 +02:00
parent 1fca8957a2
commit 6194d806cc
No known key found for this signature in database
8 changed files with 65 additions and 105 deletions

View File

@ -26,7 +26,6 @@ import {
ExtraType,
MovieEntry,
Special,
UnknownEntry,
} from "~/models/entry";
import { KError } from "~/models/error";
import { madeInAbyss } from "~/models/examples";
@ -81,11 +80,6 @@ const extraFilters: FilterDef = {
playedDate: { column: entryProgressQ.playedDate, type: "date" },
};
const unknownFilters: FilterDef = {
runtime: { column: entries.runtime, type: "float" },
playedDate: { column: entryProgressQ.playedDate, type: "date" },
};
export const entrySort = Sort(
{
order: entries.order,
@ -176,7 +170,7 @@ export async function getEntries({
languages: string[];
userId: string;
progressQ?: typeof entryProgressQ;
}): Promise<(Entry | Extra | UnknownEntry)[]> {
}): Promise<(Entry | Extra)[]> {
const transQ = db
.selectDistinctOn([entryTranslations.pk])
.from(entryTranslations)
@ -244,7 +238,6 @@ export const entriesH = new Elysia({ tags: ["series"] })
movie_entry: MovieEntry,
special: Special,
extra: Extra,
unknown_entry: UnknownEntry,
error: t.Object({}),
})
.model((models) => ({
@ -289,7 +282,6 @@ export const entriesH = new Elysia({ tags: ["series"] })
filter: and(
eq(entries.showPk, serie.pk),
ne(entries.kind, "extra"),
ne(entries.kind, "unknown"),
filter,
),
languages: langs,
@ -407,46 +399,6 @@ export const entriesH = new Elysia({ tags: ["series"] })
},
},
)
.get(
"/unknowns",
async ({
query: { limit, after, query, sort, filter },
request: { url },
jwt: { sub },
}) => {
const items = (await getEntries({
limit,
after,
query,
sort: sort,
filter: and(eq(entries.kind, "unknown"), filter),
languages: ["extra"],
userId: sub,
})) as UnknownEntry[];
return createPage(items, { url, sort, limit });
},
{
detail: { description: "Get unknown/unmatch videos." },
query: t.Object({
sort: extraSort,
filter: t.Optional(Filter({ def: unknownFilters })),
query: t.Optional(t.String({ description: description.query })),
limit: t.Integer({
minimum: 1,
maximum: 250,
default: 50,
description: "Max page size.",
}),
after: t.Optional(t.String({ description: description.after })),
}),
response: {
200: Page(UnknownEntry),
422: KError,
},
tags: ["videos"],
},
)
.get(
"/news",
async ({
@ -462,7 +414,6 @@ export const entriesH = new Elysia({ tags: ["series"] })
sort,
filter: and(
isNotNull(entries.availableSince),
ne(entries.kind, "unknown"),
ne(entries.kind, "extra"),
filter,
),
@ -489,6 +440,6 @@ export const entriesH = new Elysia({ tags: ["series"] })
200: Page(Entry),
422: KError,
},
tags: ["videos"],
tags: ["shows"],
},
);

View File

@ -1,4 +1,4 @@
import { and, eq, exists, inArray, not, or, sql } from "drizzle-orm";
import { and, eq, exists, inArray, not, notExists, or, sql } from "drizzle-orm";
import { alias } from "drizzle-orm/pg-core";
import { Elysia, t } from "elysia";
import { db } from "~/db";
@ -9,8 +9,17 @@ import {
jsonbObjectAgg,
values,
} from "~/db/utils";
import { KError } from "~/models/error";
import { bubbleVideo } from "~/models/examples";
import { isUuid } from "~/models/utils";
import {
Page,
Sort,
createPage,
isUuid,
keysetPaginate,
sortToSql,
} from "~/models/utils";
import { desc as description } from "~/models/utils/descriptions";
import { Guesses, SeedVideo, Video } from "~/models/video";
import { comment } from "~/utils";
import { computeVideoSlug } from "./seed/insert/entries";
@ -84,6 +93,55 @@ export const videosH = new Elysia({ prefix: "/videos", tags: ["videos"] })
},
},
)
.get(
"unknowns",
async ({ query: { sort, query, limit, after }, request: { url } }) => {
const ret = await db
.select()
.from(videos)
.where(
and(
notExists(
db
.select()
.from(entryVideoJoin)
.where(eq(videos.pk, entryVideoJoin.videoPk)),
),
query
? or(
sql`${videos.path} %> ${query}::text`,
sql`${videos.guess}->'title' %> ${query}::text`,
)
: undefined,
keysetPaginate({ after, sort }),
),
)
.orderBy(...(query ? [] : sortToSql(sort)), videos.pk)
.limit(limit);
return createPage(ret, { url, sort, limit });
},
{
detail: { description: "Get unknown/unmatch videos." },
query: t.Object({
sort: Sort(
{ createdAt: videos.createdAt, path: videos.path },
{ default: ["-createdAt"], tablePk: videos.pk },
),
query: t.Optional(t.String({ description: description.query })),
limit: t.Integer({
minimum: 1,
maximum: 250,
default: 50,
description: "Max page size.",
}),
after: t.Optional(t.String({ description: description.after })),
}),
response: {
200: Page(Video),
422: KError,
},
},
)
.post(
"",
async ({ body, error }) => {

View File

@ -18,7 +18,6 @@ import { image, language, schema } from "./utils";
import { entryVideoJoin } from "./videos";
export const entryType = schema.enum("entry_type", [
"unknown",
"episode",
"movie",
"special",

View File

@ -17,4 +17,3 @@ export * from "./episode";
export * from "./movie-entry";
export * from "./special";
export * from "./extra";
export * from "./unknown-entry";

View File

@ -1,38 +0,0 @@
import { t } from "elysia";
import { type Prettify, comment } from "~/utils";
import { bubbleImages, registerExamples, youtubeExample } from "../examples";
import { Progress } from "../history";
import { DbMetadata, Resource } from "../utils";
import { BaseEntry, EntryTranslation } from "./base-entry";
export const BaseUnknownEntry = t.Intersect(
[
t.Object({
kind: t.Literal("unknown"),
}),
t.Omit(BaseEntry(), ["airDate"]),
],
{
description: comment`
A video not releated to any series or movie. This can be due to a matching error but it can be a youtube
video or any other video content.
`,
},
);
export const UnknownEntryTranslation = t.Omit(EntryTranslation(), [
"description",
]);
export const UnknownEntry = t.Intersect([
Resource(),
UnknownEntryTranslation,
BaseUnknownEntry,
t.Object({
progress: t.Omit(Progress, ["videoId"]),
}),
DbMetadata,
]);
export type UnknownEntry = Prettify<typeof UnknownEntry.static>;
registerExamples(UnknownEntry, { ...youtubeExample, ...bubbleImages });

View File

@ -35,4 +35,3 @@ export * from "./made-in-abyss";
export * from "./dune-1984";
export * from "./dune-2021";
export * from "./dune-collection";
export * from "./others";

View File

@ -1,10 +0,0 @@
import type { UnknownEntry } from "~/models/entry";
export const youtubeExample: Partial<UnknownEntry> = {
kind: "unknown",
// idk if we'll keep non-ascii characters or if we can find a way to convert them
slug: "lisa-炎-the-first-take",
name: "LiSA - 炎 / THE FIRST TAKE",
runtime: 10,
thumbnail: null,
};

View File

@ -114,7 +114,9 @@ export const SeedVideo = t.Object({
export type SeedVideo = Prettify<typeof SeedVideo.static>;
export const Video = t.Intersect([
Resource(),
t.Object({
id: t.String({ format: "uuid" }),
}),
t.Omit(SeedVideo, ["for"]),
DbMetadata,
]);