diff --git a/api/src/controllers/entries.ts b/api/src/controllers/entries.ts index cdf4ea48..7a3783cb 100644 --- a/api/src/controllers/entries.ts +++ b/api/src/controllers/entries.ts @@ -111,9 +111,9 @@ async function getEntries({ .from(entryVideoJoin) .where(eq(entryVideoJoin.entryPk, entries.pk)) .leftJoin(videos, eq(videos.pk, entryVideoJoin.videoPk)) - .as("video"); + .as("videos"); const videosJ = db - .select({ videos: sql`json_agg("video")`.as("videos") }) + .select({ videos: sql`coalesce(json_agg("videos"), '[]'::json)`.as("videos") }) .from(videosQ) .as("videos_json"); @@ -130,9 +130,9 @@ async function getEntries({ .select({ ...entryCol, ...transCol, - videos: sql`${videosJ.videos}`.as("videos"), + videos: videosJ.videos, // specials don't have an `episodeNumber` but a `number` field. - number: sql`${episodeNumber}`.as("number"), + number: episodeNumber, // merge `extraKind` into `kind` kind: sql`case when ${kind} = 'extra' then ${extraKind} else ${kind}::text end`.as( @@ -140,11 +140,11 @@ async function getEntries({ ), // assign more restrained types to make typescript happy. - externalId: sql`${externalId}`.as("externalId"), - order: sql`${order}`.as("order"), - seasonNumber: sql`${seasonNumber}`.as("seasonNumber"), - episodeNumber: sql`${episodeNumber}`.as("episodeNumber"), - name: sql`${name}`.as("name"), + externalId: sql`${externalId}`, + order: sql`${order}`, + seasonNumber: sql`${seasonNumber}`, + episodeNumber: sql`${episodeNumber}`, + name: sql`${name}`, }) .from(entries) .innerJoin(transQ, eq(entries.pk, transQ.pk)) diff --git a/api/src/controllers/seed/insert/entries.ts b/api/src/controllers/seed/insert/entries.ts index a17e41b6..126e2562 100644 --- a/api/src/controllers/seed/insert/entries.ts +++ b/api/src/controllers/seed/insert/entries.ts @@ -131,7 +131,7 @@ export const insertEntries = async ( entryPk: retEntries[i].pk, entrySlug: retEntries[i].slug, // The first video should not have a rendering. - needRendering: j && seed.videos!.length > 1, + needRendering: j !== 0 && seed.videos!.length > 1, })); }); diff --git a/api/src/controllers/shows/movies.ts b/api/src/controllers/shows/movies.ts index 44e0c0f4..4d3eca69 100644 --- a/api/src/controllers/shows/movies.ts +++ b/api/src/controllers/shows/movies.ts @@ -39,7 +39,7 @@ export const movies = new Elysia({ prefix: "/movies", tags: ["movies"] }) if (!ret) { return error(404, { status: 404, - message: "Movie not found", + message: `No movie found with id or slug: '${id}'.`, }); } if (!ret.language) { diff --git a/api/src/models/entry/episode.ts b/api/src/models/entry/episode.ts index 8b07b74c..05478465 100644 --- a/api/src/models/entry/episode.ts +++ b/api/src/models/entry/episode.ts @@ -8,7 +8,7 @@ import { SeedImage, TranslationRecord, } from "../utils"; -import { Video } from "../video"; +import { EmbeddedVideo } from "../video"; import { BaseEntry, EntryTranslation } from "./base-entry"; export const BaseEpisode = t.Intersect([ @@ -27,7 +27,7 @@ export const Episode = t.Intersect([ EntryTranslation(), BaseEpisode, t.Object({ - videos: t.Optional(t.Array(Video)), + videos: t.Optional(t.Array(EmbeddedVideo)), }), DbMetadata, ]); diff --git a/api/src/models/entry/extra.ts b/api/src/models/entry/extra.ts index 5f1d1007..72149a88 100644 --- a/api/src/models/entry/extra.ts +++ b/api/src/models/entry/extra.ts @@ -3,7 +3,7 @@ import { type Prettify, comment } from "~/utils"; import { madeInAbyss, registerExamples } from "../examples"; import { DbMetadata, SeedImage } from "../utils"; import { Resource } from "../utils/resource"; -import { Video } from "../video"; +import { EmbeddedVideo } from "../video"; import { BaseEntry } from "./base-entry"; export const ExtraType = t.UnionEnum([ @@ -36,7 +36,7 @@ export const Extra = t.Intersect([ Resource(), BaseExtra, t.Object({ - video: t.Optional(Video), + video: t.Optional(EmbeddedVideo), }), DbMetadata, ]); diff --git a/api/src/models/entry/movie-entry.ts b/api/src/models/entry/movie-entry.ts index 77447d91..ceab00c4 100644 --- a/api/src/models/entry/movie-entry.ts +++ b/api/src/models/entry/movie-entry.ts @@ -9,7 +9,7 @@ import { SeedImage, TranslationRecord, } from "../utils"; -import { Video } from "../video"; +import { EmbeddedVideo } from "../video"; import { BaseEntry, EntryTranslation } from "./base-entry"; export const BaseMovieEntry = t.Intersect( @@ -45,7 +45,7 @@ export const MovieEntry = t.Intersect([ MovieEntryTranslation, BaseMovieEntry, t.Object({ - videos: t.Optional(t.Array(Video)), + videos: t.Optional(t.Array(EmbeddedVideo)), }), DbMetadata, ]); diff --git a/api/src/models/entry/special.ts b/api/src/models/entry/special.ts index e9fb851f..c062f6d4 100644 --- a/api/src/models/entry/special.ts +++ b/api/src/models/entry/special.ts @@ -8,7 +8,7 @@ import { SeedImage, TranslationRecord, } from "../utils"; -import { Video } from "../video"; +import { EmbeddedVideo } from "../video"; import { BaseEntry, EntryTranslation } from "./base-entry"; export const BaseSpecial = t.Intersect( @@ -37,7 +37,7 @@ export const Special = t.Intersect([ EntryTranslation(), BaseSpecial, t.Object({ - videos: t.Optional(t.Array(Video)), + videos: t.Optional(t.Array(EmbeddedVideo)), }), DbMetadata, ]); diff --git a/api/src/models/entry/unknown-entry.ts b/api/src/models/entry/unknown-entry.ts index efe1d380..e845a438 100644 --- a/api/src/models/entry/unknown-entry.ts +++ b/api/src/models/entry/unknown-entry.ts @@ -2,6 +2,7 @@ import { t } from "elysia"; import { type Prettify, comment } from "~/utils"; import { bubbleImages, registerExamples, youtubeExample } from "../examples"; import { DbMetadata, Resource } from "../utils"; +import { EmbeddedVideo } from "../video"; import { BaseEntry, EntryTranslation } from "./base-entry"; export const BaseUnknownEntry = t.Intersect( @@ -27,6 +28,9 @@ export const UnknownEntry = t.Intersect([ Resource(), UnknownEntryTranslation, BaseUnknownEntry, + t.Object({ + video: t.Optional(EmbeddedVideo), + }), DbMetadata, ]); export type UnknownEntry = Prettify; diff --git a/api/src/models/video.ts b/api/src/models/video.ts index 7f8f6681..6822cbb2 100644 --- a/api/src/models/video.ts +++ b/api/src/models/video.ts @@ -38,7 +38,7 @@ export const SeedVideo = t.Object({ season: t.Optional(t.Array(t.Integer(), { default: [] })), episode: t.Optional(t.Array(t.Integer(), { default: [] })), // TODO: maybe replace "extra" with the `extraKind` value (aka behind-the-scene, trailer, etc) - type: t.Optional(t.UnionEnum(["episode", "movie", "extra"])), + kind: t.Optional(t.UnionEnum(["episode", "movie", "extra"])), from: t.String({ description: "Name of the tool that made the guess", @@ -71,4 +71,8 @@ export type SeedVideo = typeof SeedVideo.static; export const Video = t.Intersect([Resource(), SeedVideo, DbMetadata]); export type Video = Prettify; +// type used in entry responses +export const EmbeddedVideo = t.Omit(Video, ["createdAt", "updatedAt"]); +export type EmbeddedVideo = Prettify; + registerExamples(Video, bubbleVideo); diff --git a/api/tests/manual.ts b/api/tests/manual.ts new file mode 100644 index 00000000..0fcd9d2f --- /dev/null +++ b/api/tests/manual.ts @@ -0,0 +1,15 @@ +import { db, migrate } from "~/db"; +import { shows, videos } from "~/db/schema"; +import { madeInAbyss, madeInAbyssVideo } from "~/models/examples"; +import { createSerie, createVideo } from "./helpers"; + +// test file used to run manually using `bun tests/manual.ts` + +await migrate(); +await db.delete(shows); +await db.delete(videos); + +const [_, vid] = await createVideo(madeInAbyssVideo); +console.log(vid); +const [__, ser] = await createSerie(madeInAbyss); +console.log(ser); diff --git a/api/tests/movies/get-movie.test.ts b/api/tests/movies/get-movie.test.ts index 2d9c8e54..b282c882 100644 --- a/api/tests/movies/get-movie.test.ts +++ b/api/tests/movies/get-movie.test.ts @@ -21,7 +21,7 @@ describe("Get movie", () => { expectStatus(resp, body).toBe(404); expect(body).toMatchObject({ status: 404, - message: "Movie not found", + message: expect.any(String), }); }); it("Retrive by slug", async () => { diff --git a/api/tests/series/get-entries.test.ts b/api/tests/series/get-entries.test.ts index f22416b9..d8dce9cd 100644 --- a/api/tests/series/get-entries.test.ts +++ b/api/tests/series/get-entries.test.ts @@ -3,12 +3,19 @@ import { createSerie, createVideo, getEntries, getExtras } from "tests/helpers"; import { expectStatus } from "tests/utils"; import { db } from "~/db"; import { shows, videos } from "~/db/schema"; -import { madeInAbyss, madeInAbyssVideo } from "~/models/examples"; +import { madeInAbyss as base, madeInAbyssVideo } from "~/models/examples"; + +// make a copy so we can mutate it. +const madeInAbyss = JSON.parse(JSON.stringify(base)) as typeof base; beforeAll(async () => { await db.delete(shows); await db.delete(videos); - console.log(await createVideo(madeInAbyssVideo)); + const [_, vid] = await createVideo(madeInAbyssVideo); + for (const entry of madeInAbyss.entries.filter((x) => x.videos?.length)) + entry.videos = [vid[0].id]; + for (const entry of madeInAbyss.extras.filter((x) => x.video)) + entry.video = vid[0].id; await createSerie(madeInAbyss); });