mirror of
https://github.com/zoriya/Kyoo.git
synced 2025-05-31 04:04:21 -04:00
Add staff seeding
This commit is contained in:
parent
46833ac06f
commit
43a128ebe8
47
api/src/controllers/seed/insert/staff.ts
Normal file
47
api/src/controllers/seed/insert/staff.ts
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
import { eq } from "drizzle-orm";
|
||||||
|
import { db } from "~/db";
|
||||||
|
import { roles, staff } from "~/db/schema";
|
||||||
|
import { conflictUpdateAllExcept } from "~/db/utils";
|
||||||
|
import type { SeedStaff } from "~/models/staff";
|
||||||
|
import { processOptImage } from "../images";
|
||||||
|
|
||||||
|
export const insertStaff = async (
|
||||||
|
seed: SeedStaff[] | undefined,
|
||||||
|
showPk: number,
|
||||||
|
) => {
|
||||||
|
if (!seed?.length) return [];
|
||||||
|
|
||||||
|
return await db.transaction(async (tx) => {
|
||||||
|
const people = seed.map((x) => ({
|
||||||
|
...x.staff,
|
||||||
|
image: processOptImage(x.staff.image),
|
||||||
|
}));
|
||||||
|
const ret = await tx
|
||||||
|
.insert(staff)
|
||||||
|
.values(people)
|
||||||
|
.onConflictDoUpdate({
|
||||||
|
target: staff.slug,
|
||||||
|
set: conflictUpdateAllExcept(staff, ["pk", "id", "slug", "createdAt"]),
|
||||||
|
})
|
||||||
|
.returning({ pk: staff.pk, id: staff.id, slug: staff.slug });
|
||||||
|
|
||||||
|
const rval = seed.map((x, i) => ({
|
||||||
|
showPk,
|
||||||
|
staffPk: ret[i].pk,
|
||||||
|
kind: x.kind,
|
||||||
|
order: i,
|
||||||
|
character: {
|
||||||
|
...x.character,
|
||||||
|
image: processOptImage(x.character.image),
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
|
// always replace all roles. this is because:
|
||||||
|
// - we want `order` to stay in sync (& without duplicates)
|
||||||
|
// - we don't have ways to identify a role so we can't onConflict
|
||||||
|
await tx.delete(roles).where(eq(roles.showPk, showPk));
|
||||||
|
await tx.insert(roles).values(rval);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
});
|
||||||
|
};
|
@ -5,6 +5,7 @@ import { processOptImage } from "./images";
|
|||||||
import { insertCollection } from "./insert/collection";
|
import { insertCollection } from "./insert/collection";
|
||||||
import { insertEntries } from "./insert/entries";
|
import { insertEntries } from "./insert/entries";
|
||||||
import { insertShow, updateAvailableCount } from "./insert/shows";
|
import { insertShow, updateAvailableCount } from "./insert/shows";
|
||||||
|
import { insertStaff } from "./insert/staff";
|
||||||
import { insertStudios } from "./insert/studios";
|
import { insertStudios } from "./insert/studios";
|
||||||
import { guessNextRefresh } from "./refresh";
|
import { guessNextRefresh } from "./refresh";
|
||||||
|
|
||||||
@ -26,6 +27,12 @@ export const SeedMovieResponse = t.Object({
|
|||||||
slug: t.String({ format: "slug", examples: ["disney"] }),
|
slug: t.String({ format: "slug", examples: ["disney"] }),
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
|
staff: t.Array(
|
||||||
|
t.Object({
|
||||||
|
id: t.String({ format: "uuid" }),
|
||||||
|
slug: t.String({ format: "slug", examples: ["hiroyuki-sawano"] }),
|
||||||
|
}),
|
||||||
|
),
|
||||||
});
|
});
|
||||||
export type SeedMovieResponse = typeof SeedMovieResponse.static;
|
export type SeedMovieResponse = typeof SeedMovieResponse.static;
|
||||||
|
|
||||||
@ -46,7 +53,7 @@ export const seedMovie = async (
|
|||||||
seed.slug = `random-${getYear(seed.airDate)}`;
|
seed.slug = `random-${getYear(seed.airDate)}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { translations, videos, collection, studios, ...movie } = seed;
|
const { translations, videos, collection, studios, staff, ...movie } = seed;
|
||||||
const nextRefresh = guessNextRefresh(movie.airDate ?? new Date());
|
const nextRefresh = guessNextRefresh(movie.airDate ?? new Date());
|
||||||
const original = translations[movie.originalLanguage];
|
const original = translations[movie.originalLanguage];
|
||||||
if (!original) {
|
if (!original) {
|
||||||
@ -101,6 +108,7 @@ export const seedMovie = async (
|
|||||||
await updateAvailableCount([show.pk], false);
|
await updateAvailableCount([show.pk], false);
|
||||||
|
|
||||||
const retStudios = await insertStudios(studios, show.pk);
|
const retStudios = await insertStudios(studios, show.pk);
|
||||||
|
const retStaff = await insertStaff(staff, show.pk);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
updated: show.updated,
|
updated: show.updated,
|
||||||
@ -109,5 +117,6 @@ export const seedMovie = async (
|
|||||||
videos: entry.videos,
|
videos: entry.videos,
|
||||||
collection: col,
|
collection: col,
|
||||||
studios: retStudios,
|
studios: retStudios,
|
||||||
|
staff: retStaff,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -6,6 +6,7 @@ import { insertCollection } from "./insert/collection";
|
|||||||
import { insertEntries } from "./insert/entries";
|
import { insertEntries } from "./insert/entries";
|
||||||
import { insertSeasons } from "./insert/seasons";
|
import { insertSeasons } from "./insert/seasons";
|
||||||
import { insertShow, updateAvailableCount } from "./insert/shows";
|
import { insertShow, updateAvailableCount } from "./insert/shows";
|
||||||
|
import { insertStaff } from "./insert/staff";
|
||||||
import { insertStudios } from "./insert/studios";
|
import { insertStudios } from "./insert/studios";
|
||||||
import { guessNextRefresh } from "./refresh";
|
import { guessNextRefresh } from "./refresh";
|
||||||
|
|
||||||
@ -53,6 +54,12 @@ export const SeedSerieResponse = t.Object({
|
|||||||
slug: t.String({ format: "slug", examples: ["mappa"] }),
|
slug: t.String({ format: "slug", examples: ["mappa"] }),
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
|
staff: t.Array(
|
||||||
|
t.Object({
|
||||||
|
id: t.String({ format: "uuid" }),
|
||||||
|
slug: t.String({ format: "slug", examples: ["hiroyuki-sawano"] }),
|
||||||
|
}),
|
||||||
|
),
|
||||||
});
|
});
|
||||||
export type SeedSerieResponse = typeof SeedSerieResponse.static;
|
export type SeedSerieResponse = typeof SeedSerieResponse.static;
|
||||||
|
|
||||||
@ -80,6 +87,7 @@ export const seedSerie = async (
|
|||||||
extras,
|
extras,
|
||||||
collection,
|
collection,
|
||||||
studios,
|
studios,
|
||||||
|
staff,
|
||||||
...serie
|
...serie
|
||||||
} = seed;
|
} = seed;
|
||||||
const nextRefresh = guessNextRefresh(serie.startAir ?? new Date());
|
const nextRefresh = guessNextRefresh(serie.startAir ?? new Date());
|
||||||
@ -127,6 +135,7 @@ export const seedSerie = async (
|
|||||||
await updateAvailableCount([show.pk]);
|
await updateAvailableCount([show.pk]);
|
||||||
|
|
||||||
const retStudios = await insertStudios(studios, show.pk);
|
const retStudios = await insertStudios(studios, show.pk);
|
||||||
|
const retStaff = await insertStaff(staff, show.pk);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
updated: show.updated,
|
updated: show.updated,
|
||||||
@ -137,5 +146,6 @@ export const seedSerie = async (
|
|||||||
extras: retExtras,
|
extras: retExtras,
|
||||||
collection: col,
|
collection: col,
|
||||||
studios: retStudios,
|
studios: retStudios,
|
||||||
|
staff: retStaff,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -7,7 +7,8 @@ import { roles, staff } from "~/db/schema/staff";
|
|||||||
import { getColumns, sqlarr } from "~/db/utils";
|
import { getColumns, sqlarr } from "~/db/utils";
|
||||||
import { KError } from "~/models/error";
|
import { KError } from "~/models/error";
|
||||||
import type { MovieStatus } from "~/models/movie";
|
import type { MovieStatus } from "~/models/movie";
|
||||||
import { Role, RoleWShow, RoleWStaff, Staff } from "~/models/staff";
|
import { Role, Staff } from "~/models/staff";
|
||||||
|
import { RoleWShow, RoleWStaff } from "~/models/staff-roles";
|
||||||
import {
|
import {
|
||||||
Filter,
|
Filter,
|
||||||
type FilterDef,
|
type FilterDef,
|
||||||
@ -42,11 +43,13 @@ const staffRoleSort = Sort(
|
|||||||
name: { sql: staff.name, accessor: (x) => x.staff.name },
|
name: { sql: staff.name, accessor: (x) => x.staff.name },
|
||||||
latinName: { sql: staff.latinName, accessor: (x) => x.staff.latinName },
|
latinName: { sql: staff.latinName, accessor: (x) => x.staff.latinName },
|
||||||
characterName: {
|
characterName: {
|
||||||
sql: sql`${roles.character}->'name'`,
|
sql: sql`${roles.character}->>'name'`,
|
||||||
|
isNullable: true,
|
||||||
accessor: (x) => x.character.name,
|
accessor: (x) => x.character.name,
|
||||||
},
|
},
|
||||||
characterLatinName: {
|
characterLatinName: {
|
||||||
sql: sql`${roles.character}->'latinName'`,
|
sql: sql`${roles.character}->>'latinName'`,
|
||||||
|
isNullable: true,
|
||||||
accessor: (x) => x.character.latinName,
|
accessor: (x) => x.character.latinName,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -42,6 +42,7 @@ export const staff = schema.table("staff", {
|
|||||||
export const roles = schema.table(
|
export const roles = schema.table(
|
||||||
"roles",
|
"roles",
|
||||||
{
|
{
|
||||||
|
pk: integer().primaryKey().generatedAlwaysAsIdentity(),
|
||||||
showPk: integer()
|
showPk: integer()
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => shows.pk, { onDelete: "cascade" }),
|
.references(() => shows.pk, { onDelete: "cascade" }),
|
||||||
@ -53,7 +54,6 @@ export const roles = schema.table(
|
|||||||
character: jsonb().$type<Character>(),
|
character: jsonb().$type<Character>(),
|
||||||
},
|
},
|
||||||
(t) => [
|
(t) => [
|
||||||
primaryKey({ columns: [t.showPk, t.staffPk] }),
|
|
||||||
index("role_kind").using("hash", t.kind),
|
index("role_kind").using("hash", t.kind),
|
||||||
index("role_order").on(t.order),
|
index("role_order").on(t.order),
|
||||||
],
|
],
|
||||||
|
@ -2,6 +2,7 @@ import { t } from "elysia";
|
|||||||
import type { Prettify } from "~/utils";
|
import type { Prettify } from "~/utils";
|
||||||
import { SeedCollection } from "./collections";
|
import { SeedCollection } from "./collections";
|
||||||
import { bubble, bubbleImages, registerExamples } from "./examples";
|
import { bubble, bubbleImages, registerExamples } from "./examples";
|
||||||
|
import { SeedStaff } from "./staff";
|
||||||
import { SeedStudio, Studio } from "./studio";
|
import { SeedStudio, Studio } from "./studio";
|
||||||
import {
|
import {
|
||||||
DbMetadata,
|
DbMetadata,
|
||||||
@ -90,6 +91,7 @@ export const SeedMovie = t.Intersect([
|
|||||||
videos: t.Optional(t.Array(t.String({ format: "uuid" }), { default: [] })),
|
videos: t.Optional(t.Array(t.String({ format: "uuid" }), { default: [] })),
|
||||||
collection: t.Optional(SeedCollection),
|
collection: t.Optional(SeedCollection),
|
||||||
studios: t.Optional(t.Array(SeedStudio, { default: [] })),
|
studios: t.Optional(t.Array(SeedStudio, { default: [] })),
|
||||||
|
staff: t.Optional(t.Array(SeedStaff, { default: [] })),
|
||||||
}),
|
}),
|
||||||
]);
|
]);
|
||||||
export type SeedMovie = Prettify<typeof SeedMovie.static>;
|
export type SeedMovie = Prettify<typeof SeedMovie.static>;
|
||||||
|
@ -4,6 +4,7 @@ import { SeedCollection } from "./collections";
|
|||||||
import { SeedEntry, SeedExtra } from "./entry";
|
import { SeedEntry, SeedExtra } from "./entry";
|
||||||
import { bubbleImages, madeInAbyss, registerExamples } from "./examples";
|
import { bubbleImages, madeInAbyss, registerExamples } from "./examples";
|
||||||
import { SeedSeason } from "./season";
|
import { SeedSeason } from "./season";
|
||||||
|
import { SeedStaff } from "./staff";
|
||||||
import { SeedStudio, Studio } from "./studio";
|
import { SeedStudio, Studio } from "./studio";
|
||||||
import {
|
import {
|
||||||
DbMetadata,
|
DbMetadata,
|
||||||
@ -106,6 +107,7 @@ export const SeedSerie = t.Intersect([
|
|||||||
extras: t.Optional(t.Array(SeedExtra, { default: [] })),
|
extras: t.Optional(t.Array(SeedExtra, { default: [] })),
|
||||||
collection: t.Optional(SeedCollection),
|
collection: t.Optional(SeedCollection),
|
||||||
studios: t.Optional(t.Array(SeedStudio, { default: [] })),
|
studios: t.Optional(t.Array(SeedStudio, { default: [] })),
|
||||||
|
staff: t.Optional(t.Array(SeedStaff, { default: [] })),
|
||||||
}),
|
}),
|
||||||
]);
|
]);
|
||||||
export type SeedSerie = typeof SeedSerie.static;
|
export type SeedSerie = typeof SeedSerie.static;
|
||||||
|
19
api/src/models/staff-roles.ts
Normal file
19
api/src/models/staff-roles.ts
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
import { t } from "elysia";
|
||||||
|
import { Show } from "./show";
|
||||||
|
import { Role, Staff } from "./staff";
|
||||||
|
|
||||||
|
export const RoleWShow = t.Intersect([
|
||||||
|
Role,
|
||||||
|
t.Object({
|
||||||
|
show: Show,
|
||||||
|
}),
|
||||||
|
]);
|
||||||
|
export type RoleWShow = typeof RoleWShow.static;
|
||||||
|
|
||||||
|
export const RoleWStaff = t.Intersect([
|
||||||
|
Role,
|
||||||
|
t.Object({
|
||||||
|
staff: Staff,
|
||||||
|
}),
|
||||||
|
]);
|
||||||
|
export type RoleWStaff = typeof RoleWStaff.static;
|
@ -1,6 +1,5 @@
|
|||||||
import { t } from "elysia";
|
import { t } from "elysia";
|
||||||
import { Show } from "./show";
|
import { DbMetadata, ExternalId, Image, Resource, SeedImage } from "./utils";
|
||||||
import { DbMetadata, ExternalId, Image, Resource } from "./utils";
|
|
||||||
|
|
||||||
export const Character = t.Object({
|
export const Character = t.Object({
|
||||||
name: t.String(),
|
name: t.String(),
|
||||||
@ -22,30 +21,31 @@ export const Role = t.Object({
|
|||||||
});
|
});
|
||||||
export type Role = typeof Role.static;
|
export type Role = typeof Role.static;
|
||||||
|
|
||||||
export const Staff = t.Intersect([
|
const StaffData = t.Object({
|
||||||
Resource(),
|
name: t.String(),
|
||||||
t.Object({
|
latinName: t.Nullable(t.String()),
|
||||||
name: t.String(),
|
image: t.Nullable(Image),
|
||||||
latinName: t.Nullable(t.String()),
|
externalId: ExternalId(),
|
||||||
image: t.Nullable(Image),
|
});
|
||||||
externalId: ExternalId(),
|
export const Staff = t.Intersect([Resource(), StaffData, DbMetadata]);
|
||||||
}),
|
|
||||||
DbMetadata,
|
|
||||||
]);
|
|
||||||
export type Staff = typeof Staff.static;
|
export type Staff = typeof Staff.static;
|
||||||
|
|
||||||
export const RoleWShow = t.Intersect([
|
export const SeedStaff = t.Intersect([
|
||||||
Role,
|
t.Omit(Role, ["character"]),
|
||||||
t.Object({
|
t.Object({
|
||||||
show: Show,
|
character: t.Intersect([
|
||||||
|
t.Omit(Character, ["image"]),
|
||||||
|
t.Object({
|
||||||
|
image: t.Nullable(SeedImage),
|
||||||
|
}),
|
||||||
|
]),
|
||||||
|
staff: t.Intersect([
|
||||||
|
t.Object({
|
||||||
|
slug: t.String({ format: "slug" }),
|
||||||
|
image: t.Nullable(SeedImage),
|
||||||
|
}),
|
||||||
|
t.Omit(StaffData, ["image"]),
|
||||||
|
]),
|
||||||
}),
|
}),
|
||||||
]);
|
]);
|
||||||
export type RoleWShow = typeof RoleWShow.static;
|
export type SeedStaff = typeof SeedStaff.static;
|
||||||
|
|
||||||
export const RoleWStaff = t.Intersect([
|
|
||||||
Role,
|
|
||||||
t.Object({
|
|
||||||
staff: Staff
|
|
||||||
}),
|
|
||||||
]);
|
|
||||||
export type RoleWStaff = typeof RoleWStaff.static;
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user