Handle extra seeding

This commit is contained in:
Zoe Roux 2025-01-30 18:54:30 +01:00
parent 32a1e89b27
commit 4424e9b40a
No known key found for this signature in database
5 changed files with 75 additions and 23 deletions

View File

@ -7,13 +7,26 @@ import {
videos, videos,
} from "~/db/schema"; } from "~/db/schema";
import { conflictUpdateAllExcept, values } from "~/db/utils"; import { conflictUpdateAllExcept, values } from "~/db/utils";
import type { SeedEntry } from "~/models/entry"; import type { SeedEntry as SEntry, SeedExtra as SExtra } from "~/models/entry";
import { processOptImage } from "../images"; import { processOptImage } from "../images";
import { guessNextRefresh } from "../refresh"; import { guessNextRefresh } from "../refresh";
type SeedEntry = SEntry & {
video?: undefined;
};
type SeedExtra = Omit<SExtra, "kind"> & {
videos?: undefined;
translations?: undefined;
kind: "extra";
extraKind: SExtra["kind"];
};
type EntryI = typeof entries.$inferInsert; type EntryI = typeof entries.$inferInsert;
const generateSlug = (showSlug: string, entry: SeedEntry): string => { const generateSlug = (
showSlug: string,
entry: SeedEntry | SeedExtra,
): string => {
switch (entry.kind) { switch (entry.kind) {
case "episode": case "episode":
return `${showSlug}-s${entry.seasonNumber}e${entry.episodeNumber}`; return `${showSlug}-s${entry.seasonNumber}e${entry.episodeNumber}`;
@ -22,22 +35,29 @@ const generateSlug = (showSlug: string, entry: SeedEntry): string => {
case "movie": case "movie":
if (entry.slug) return entry.slug; if (entry.slug) return entry.slug;
return entry.order === 1 ? showSlug : `${showSlug}-${entry.order}`; return entry.order === 1 ? showSlug : `${showSlug}-${entry.order}`;
case "extra":
return entry.slug;
} }
}; };
export const insertEntries = async ( export const insertEntries = async (
show: { pk: number; slug: string }, show: { pk: number; slug: string },
items: SeedEntry[], items: (SeedEntry | SeedExtra)[],
) => { ) => {
if (!items) return [];
const retEntries = await db.transaction(async (tx) => { const retEntries = await db.transaction(async (tx) => {
const vals: EntryI[] = items.map((seed) => { const vals: EntryI[] = items.map((seed) => {
const { translations, videos, ...entry } = seed; const { translations, videos, video, ...entry } = seed;
return { return {
...entry, ...entry,
showPk: show.pk, showPk: show.pk,
slug: generateSlug(show.slug, seed), slug: generateSlug(show.slug, seed),
thumbnail: processOptImage(seed.thumbnail), thumbnail: processOptImage(seed.thumbnail),
nextRefresh: guessNextRefresh(entry.airDate ?? new Date()), nextRefresh:
entry.kind !== "extra"
? guessNextRefresh(entry.airDate ?? new Date())
: guessNextRefresh(new Date()),
episodeNumber: episodeNumber:
entry.kind === "episode" entry.kind === "episode"
? entry.episodeNumber ? entry.episodeNumber
@ -61,14 +81,25 @@ export const insertEntries = async (
}) })
.returning({ pk: entries.pk, id: entries.id, slug: entries.slug }); .returning({ pk: entries.pk, id: entries.id, slug: entries.slug });
const trans = items.flatMap((seed, i) => const trans = items.flatMap((seed, i) => {
Object.entries(seed.translations).map(([lang, tr]) => ({ if (seed.kind === "extra") {
return {
pk: ret[i].pk,
// yeah we hardcode the language to extra because if we want to support
// translations one day it won't be awkward
language: "extra",
name: seed.name,
description: null,
};
}
return Object.entries(seed.translations).map(([lang, tr]) => ({
// assumes ret is ordered like items. // assumes ret is ordered like items.
pk: ret[i].pk, pk: ret[i].pk,
language: lang, language: lang,
...tr, ...tr,
})), }));
); });
await tx await tx
.insert(entryTranslations) .insert(entryTranslations)
.values(trans) .values(trans)
@ -80,15 +111,22 @@ export const insertEntries = async (
return ret; return ret;
}); });
const vids = items.flatMap( const vids = items.flatMap((seed, i) => {
(seed, i) => if (seed.kind === "extra") {
seed.videos?.map((x, j) => ({ return {
videoId: x, videoId: seed.video,
entryPk: retEntries[i].pk, entryPk: retEntries[i].pk,
// The first video should not have a rendering. needRendering: false,
needRendering: j && seed.videos!.length > 1, };
})) ?? [], }
); if (!seed.videos) return [];
return seed.videos.map((x, j) => ({
videoId: x,
entryPk: retEntries[i].pk,
// The first video should not have a rendering.
needRendering: j && seed.videos!.length > 1,
}));
});
if (vids.length === 0) if (vids.length === 0)
return retEntries.map((x) => ({ id: x.id, slug: x.slug, videos: [] })); return retEntries.map((x) => ({ id: x.id, slug: x.slug, videos: [] }));

View File

@ -2,9 +2,9 @@ import { t } from "elysia";
import type { SeedSerie } from "~/models/serie"; import type { SeedSerie } from "~/models/serie";
import { getYear } from "~/utils"; import { getYear } from "~/utils";
import { insertEntries } from "./insert/entries"; import { insertEntries } from "./insert/entries";
import { insertSeasons } from "./insert/seasons";
import { insertShow } from "./insert/shows"; import { insertShow } from "./insert/shows";
import { guessNextRefresh } from "./refresh"; import { guessNextRefresh } from "./refresh";
import { insertSeasons } from "./insert/seasons";
export const SeedSerieResponse = t.Object({ export const SeedSerieResponse = t.Object({
id: t.String({ format: "uuid" }), id: t.String({ format: "uuid" }),
@ -29,6 +29,12 @@ export const SeedSerieResponse = t.Object({
), ),
}), }),
), ),
extras: t.Array(
t.Object({
id: t.String({ format: "uuid" }),
slug: t.String({ format: "slug", examples: ["made-in-abyss-s1e1"] }),
}),
),
}); });
export type SeedSerieResponse = typeof SeedSerieResponse.static; export type SeedSerieResponse = typeof SeedSerieResponse.static;
@ -49,7 +55,7 @@ export const seedSerie = async (
seed.slug = `random-${getYear(seed.startAir)}`; seed.slug = `random-${getYear(seed.startAir)}`;
} }
const { translations, seasons, entries, ...serie } = seed; const { translations, seasons, entries, extras, ...serie } = seed;
const nextRefresh = guessNextRefresh(serie.startAir ?? new Date()); const nextRefresh = guessNextRefresh(serie.startAir ?? new Date());
const show = await insertShow( const show = await insertShow(
@ -64,6 +70,10 @@ export const seedSerie = async (
const retSeasons = await insertSeasons(show, seasons); const retSeasons = await insertSeasons(show, seasons);
const retEntries = await insertEntries(show, entries); const retEntries = await insertEntries(show, entries);
const retExtras = await insertEntries(
show,
(extras ?? []).map((x) => ({ ...x, kind: "extra", extraKind: x.kind })),
);
return { return {
updated: show.updated, updated: show.updated,
@ -71,5 +81,6 @@ export const seedSerie = async (
slug: show.slug, slug: show.slug,
seasons: retSeasons, seasons: retSeasons,
entries: retEntries, entries: retEntries,
extras: retExtras,
}; };
}; };

View File

@ -36,8 +36,9 @@ export type Extra = typeof Extra.static;
export const SeedExtra = t.Intersect([ export const SeedExtra = t.Intersect([
t.Omit(BaseExtra, ["thumbnail", "createdAt"]), t.Omit(BaseExtra, ["thumbnail", "createdAt"]),
t.Object({ t.Object({
slug: t.String({ format: "slug" }),
thumbnail: t.Nullable(SeedImage), thumbnail: t.Nullable(SeedImage),
videos: t.Optional(t.Array(t.String({ format: "uuid" }))), video: t.String({ format: "uuid" }),
}), }),
]); ]);
export type SeedExtra = typeof SeedExtra.static; export type SeedExtra = typeof SeedExtra.static;

View File

@ -237,9 +237,11 @@ export const madeInAbyss = {
extras: [ extras: [
{ {
kind: "behind-the-scene", kind: "behind-the-scene",
slug: "made-in-abyss-making-of",
name: "The Making of MADE IN ABYSS 01", name: "The Making of MADE IN ABYSS 01",
runtime: 17, runtime: 17,
thumbnail: null, thumbnail: null,
video: "3cd436ee-01ff-4f45-ba98-654282531234",
}, },
], ],
} satisfies SeedSerie; } satisfies SeedSerie;

View File

@ -28,8 +28,8 @@ describe("Serie seeding", () => {
expect(ret!.seasons).toBeArrayOfSize(2); expect(ret!.seasons).toBeArrayOfSize(2);
expect(ret!.seasons[0].slug).toBe("made-in-abyss-s1"); expect(ret!.seasons[0].slug).toBe("made-in-abyss-s1");
expect(ret!.seasons[1].slug).toBe("made-in-abyss-s2"); expect(ret!.seasons[1].slug).toBe("made-in-abyss-s2");
// expect(ret!.entries).toBeArrayOfSize( expect(ret!.entries).toBeArrayOfSize(
// madeInAbyss.entries.length + madeInAbyss.extras.length, madeInAbyss.entries.length + madeInAbyss.extras.length,
// ); );
}); });
}); });