mirror of
https://github.com/zoriya/Kyoo.git
synced 2025-07-09 03:04:20 -04:00
Fix & test movie, episodes & slug linking
This commit is contained in:
parent
060c4d74b4
commit
6a5862ddd2
@ -79,7 +79,6 @@ export const historyH = new Elysia({ tags: ["profiles"] })
|
|||||||
filter: and(
|
filter: and(
|
||||||
isNotNull(entryProgressQ.playedDate),
|
isNotNull(entryProgressQ.playedDate),
|
||||||
ne(entries.kind, "extra"),
|
ne(entries.kind, "extra"),
|
||||||
ne(entries.kind, "unknown"),
|
|
||||||
filter,
|
filter,
|
||||||
),
|
),
|
||||||
languages: langs,
|
languages: langs,
|
||||||
@ -125,7 +124,6 @@ export const historyH = new Elysia({ tags: ["profiles"] })
|
|||||||
filter: and(
|
filter: and(
|
||||||
isNotNull(entryProgressQ.playedDate),
|
isNotNull(entryProgressQ.playedDate),
|
||||||
ne(entries.kind, "extra"),
|
ne(entries.kind, "extra"),
|
||||||
ne(entries.kind, "unknown"),
|
|
||||||
filter,
|
filter,
|
||||||
),
|
),
|
||||||
languages: langs,
|
languages: langs,
|
||||||
|
@ -212,10 +212,10 @@ export const insertEntries = async (
|
|||||||
}));
|
}));
|
||||||
};
|
};
|
||||||
|
|
||||||
export function computeVideoSlug(showSlug: SQL | Column, needsRendering: SQL) {
|
export function computeVideoSlug(entrySlug: SQL | Column, needsRendering: SQL) {
|
||||||
return sql<string>`
|
return sql<string>`
|
||||||
concat(
|
concat(
|
||||||
${showSlug},
|
${entrySlug},
|
||||||
case when ${videos.part} is not null then ('-p' || ${videos.part}) else '' end,
|
case when ${videos.part} is not null then ('-p' || ${videos.part}) else '' end,
|
||||||
case when ${videos.version} <> 1 then ('-v' || ${videos.version}) else '' end,
|
case when ${videos.version} <> 1 then ('-v' || ${videos.version}) else '' end,
|
||||||
case when ${needsRendering} then concat('-', ${videos.rendering}) else '' end
|
case when ${needsRendering} then concat('-', ${videos.rendering}) else '' end
|
||||||
|
@ -210,11 +210,12 @@ export const videosH = new Elysia({ prefix: "/videos", tags: ["videos"] })
|
|||||||
pk: entries.pk,
|
pk: entries.pk,
|
||||||
id: entries.id,
|
id: entries.id,
|
||||||
slug: entries.slug,
|
slug: entries.slug,
|
||||||
|
kind: entries.kind,
|
||||||
seasonNumber: entries.seasonNumber,
|
seasonNumber: entries.seasonNumber,
|
||||||
episodeNumber: entries.episodeNumber,
|
episodeNumber: entries.episodeNumber,
|
||||||
order: entries.order,
|
order: entries.order,
|
||||||
showId: shows.id,
|
showId: sql`${shows.id}`.as("showId"),
|
||||||
showSlug: shows.slug,
|
showSlug: sql`${shows.slug}`.as("showSlug"),
|
||||||
})
|
})
|
||||||
.from(entries)
|
.from(entries)
|
||||||
.innerJoin(shows, eq(entries.showPk, shows.pk))
|
.innerJoin(shows, eq(entries.showPk, shows.pk))
|
||||||
@ -231,10 +232,10 @@ export const videosH = new Elysia({ prefix: "/videos", tags: ["videos"] })
|
|||||||
db
|
db
|
||||||
.select({
|
.select({
|
||||||
entryPk: entriesQ.pk,
|
entryPk: entriesQ.pk,
|
||||||
videoPk: sql`j.video`,
|
videoPk: videos.pk,
|
||||||
slug: computeVideoSlug(
|
slug: computeVideoSlug(
|
||||||
entriesQ.showSlug,
|
entriesQ.slug,
|
||||||
sql`j.needRendering || exists(${hasRenderingQ})`,
|
sql`j.needRendering or exists(${hasRenderingQ})`,
|
||||||
),
|
),
|
||||||
})
|
})
|
||||||
.from(
|
.from(
|
||||||
@ -244,39 +245,51 @@ export const videosH = new Elysia({ prefix: "/videos", tags: ["videos"] })
|
|||||||
entry: "jsonb",
|
entry: "jsonb",
|
||||||
}).as("j"),
|
}).as("j"),
|
||||||
)
|
)
|
||||||
|
.innerJoin(videos, eq(videos.pk, sql`j.video`))
|
||||||
.innerJoin(
|
.innerJoin(
|
||||||
entriesQ,
|
entriesQ,
|
||||||
or(
|
or(
|
||||||
and(
|
and(
|
||||||
sql`j.entry ? 'slug'`,
|
sql`j.entry ? 'slug'`,
|
||||||
eq(entriesQ.slug, sql`j.entry->'slug'`),
|
eq(entriesQ.slug, sql`j.entry->>'slug'`),
|
||||||
),
|
),
|
||||||
and(
|
and(
|
||||||
sql`j.entry ? 'movie'`,
|
sql`j.entry ? 'movie'`,
|
||||||
or(
|
or(
|
||||||
eq(entriesQ.showId, sql`j.entry #> '{movie, id}'`),
|
eq(entriesQ.showId, sql`(j.entry #>> '{movie, id}')::uuid`),
|
||||||
eq(entriesQ.showSlug, sql`j.entry #> '{movie, slug}'`),
|
eq(entriesQ.showSlug, sql`j.entry #>> '{movie, slug}'`),
|
||||||
),
|
),
|
||||||
|
eq(entriesQ.kind, "movie"),
|
||||||
),
|
),
|
||||||
and(
|
and(
|
||||||
sql`j.entry ? 'serie'`,
|
sql`j.entry ? 'serie'`,
|
||||||
or(
|
or(
|
||||||
eq(entriesQ.showId, sql`j.entry #> '{serie, id}'`),
|
eq(entriesQ.showId, sql`(j.entry #>> '{serie, id}')::uuid`),
|
||||||
eq(entriesQ.showSlug, sql`j.entry #> '{serie, slug}'`),
|
eq(entriesQ.showSlug, sql`j.entry #>> '{serie, slug}'`),
|
||||||
),
|
),
|
||||||
or(
|
or(
|
||||||
and(
|
and(
|
||||||
sql`j.entry ?& array['season', 'episode']`,
|
sql`j.entry ?& array['season', 'episode']`,
|
||||||
eq(entriesQ.seasonNumber, sql`j.entry->'season'`),
|
eq(
|
||||||
eq(entriesQ.episodeNumber, sql`j.entry->'episode'`),
|
entriesQ.seasonNumber,
|
||||||
|
sql`(j.entry->>'season')::integer`,
|
||||||
|
),
|
||||||
|
eq(
|
||||||
|
entriesQ.episodeNumber,
|
||||||
|
sql`(j.entry->>'episode')::integer`,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
and(
|
and(
|
||||||
sql`j.entry ? 'order'`,
|
sql`j.entry ? 'order'`,
|
||||||
eq(entriesQ.order, sql`j.entry->'order'`),
|
eq(entriesQ.order, sql`(j.entry->>'order')::float`),
|
||||||
),
|
),
|
||||||
and(
|
and(
|
||||||
sql`j.entry ? 'special'`,
|
sql`j.entry ? 'special'`,
|
||||||
eq(entriesQ.episodeNumber, sql`j.entry->'special'`),
|
eq(
|
||||||
|
entriesQ.episodeNumber,
|
||||||
|
sql`(j.entry->>'special')::integer`,
|
||||||
|
),
|
||||||
|
eq(entriesQ.kind, "special"),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -299,7 +312,11 @@ export const videosH = new Elysia({ prefix: "/videos", tags: ["videos"] })
|
|||||||
);
|
);
|
||||||
return error(
|
return error(
|
||||||
201,
|
201,
|
||||||
vids.map((x) => ({ id: x.id, path: x.path, entries: entr[x.pk] })),
|
vids.map((x) => ({
|
||||||
|
id: x.id,
|
||||||
|
path: x.path,
|
||||||
|
entries: entr[x.pk] ?? [],
|
||||||
|
})),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -313,7 +330,7 @@ export const videosH = new Elysia({ prefix: "/videos", tags: ["videos"] })
|
|||||||
`,
|
`,
|
||||||
},
|
},
|
||||||
body: t.Array(SeedVideo),
|
body: t.Array(SeedVideo),
|
||||||
// response: { 201: t.Array(CreatedVideo) },
|
response: { 201: t.Array(CreatedVideo) },
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.delete(
|
.delete(
|
||||||
|
@ -3,7 +3,6 @@ import type { Video } from "~/models/video";
|
|||||||
|
|
||||||
export const madeInAbyssVideo: Video = {
|
export const madeInAbyssVideo: Video = {
|
||||||
id: "3cd436ee-01ff-4f45-ba98-654282531234",
|
id: "3cd436ee-01ff-4f45-ba98-654282531234",
|
||||||
slug: "made-in-abyss-s1e13",
|
|
||||||
path: "/video/Made in abyss S01E13.mkv",
|
path: "/video/Made in abyss S01E13.mkv",
|
||||||
rendering: "459429fa062adeebedcc2bb04b9965de0262bfa453369783132d261be79021bd",
|
rendering: "459429fa062adeebedcc2bb04b9965de0262bfa453369783132d261be79021bd",
|
||||||
part: null,
|
part: null,
|
||||||
|
@ -78,7 +78,7 @@ export const SeedVideo = t.Object({
|
|||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
t.Object({
|
t.Object({
|
||||||
externalId: t.Optional(t.Union([EpisodeId, ExternalId()])),
|
externalId: t.Union([EpisodeId, ExternalId()]),
|
||||||
}),
|
}),
|
||||||
t.Object({
|
t.Object({
|
||||||
movie: t.Union([
|
movie: t.Union([
|
||||||
|
@ -1,29 +1,34 @@
|
|||||||
import { db, migrate } from "~/db";
|
import { db, migrate } from "~/db";
|
||||||
import { profiles, shows } from "~/db/schema";
|
import { profiles, shows } from "~/db/schema";
|
||||||
import { madeInAbyss } from "~/models/examples";
|
import { madeInAbyss } from "~/models/examples";
|
||||||
import { createSerie, getSerie, setSerieStatus } from "./helpers";
|
import { createSerie, createVideo } from "./helpers";
|
||||||
import { getJwtHeaders } from "./helpers/jwt";
|
|
||||||
|
|
||||||
// test file used to run manually using `bun tests/manual.ts`
|
// test file used to run manually using `bun tests/manual.ts`
|
||||||
|
// run those before running this script
|
||||||
|
// export JWT_SECRET="this is a secret";
|
||||||
|
// export JWT_ISSUER="https://kyoo.zoriya.dev";
|
||||||
|
|
||||||
|
|
||||||
await migrate();
|
await migrate();
|
||||||
await db.delete(shows);
|
await db.delete(shows);
|
||||||
await db.delete(profiles);
|
await db.delete(profiles);
|
||||||
|
|
||||||
console.log(await getJwtHeaders());
|
const [__, ser] = await createSerie(madeInAbyss);
|
||||||
|
|
||||||
const [_, ser] = await createSerie(madeInAbyss);
|
|
||||||
console.log(ser);
|
console.log(ser);
|
||||||
const [__, ret] = await setSerieStatus(madeInAbyss.slug, {
|
const [_, body] = await createVideo({
|
||||||
status: "watching",
|
guess: { title: "mia", season: [1], episode: [13], from: "test" },
|
||||||
startedAt: "2024-12-21",
|
part: null,
|
||||||
completedAt: "2024-12-21",
|
path: "/video/mia s1e13.mkv",
|
||||||
seenCount: 2,
|
rendering: "renderingsha",
|
||||||
score: 85,
|
version: 1,
|
||||||
|
for: [
|
||||||
|
{
|
||||||
|
serie: madeInAbyss.slug,
|
||||||
|
season: madeInAbyss.entries[0].seasonNumber!,
|
||||||
|
episode: madeInAbyss.entries[0].episodeNumber!,
|
||||||
|
},
|
||||||
|
],
|
||||||
});
|
});
|
||||||
console.log(ret);
|
console.log(body);
|
||||||
|
|
||||||
const [___, got] = await getSerie(madeInAbyss.slug, {});
|
|
||||||
console.log(JSON.stringify(got, undefined, 4));
|
|
||||||
|
|
||||||
process.exit(0);
|
process.exit(0);
|
||||||
|
@ -48,7 +48,7 @@ describe("Get entries", () => {
|
|||||||
expect(body.items[0].videos).toBeArrayOfSize(1);
|
expect(body.items[0].videos).toBeArrayOfSize(1);
|
||||||
expect(body.items[0].videos[0]).toMatchObject({
|
expect(body.items[0].videos[0]).toMatchObject({
|
||||||
path: madeInAbyssVideo.path,
|
path: madeInAbyssVideo.path,
|
||||||
slug: madeInAbyssVideo.slug,
|
slug: `${madeInAbyss.slug}-s1e13`,
|
||||||
version: madeInAbyssVideo.version,
|
version: madeInAbyssVideo.version,
|
||||||
rendering: madeInAbyssVideo.rendering,
|
rendering: madeInAbyssVideo.rendering,
|
||||||
part: madeInAbyssVideo.part,
|
part: madeInAbyssVideo.part,
|
||||||
@ -63,7 +63,7 @@ describe("Get entries", () => {
|
|||||||
expect(body.items[0].videos).toBeArrayOfSize(1);
|
expect(body.items[0].videos).toBeArrayOfSize(1);
|
||||||
expect(body.items[0].videos[0]).toMatchObject({
|
expect(body.items[0].videos[0]).toMatchObject({
|
||||||
path: madeInAbyssVideo.path,
|
path: madeInAbyssVideo.path,
|
||||||
slug: madeInAbyssVideo.slug,
|
slug: `${madeInAbyss.slug}-s1e13`,
|
||||||
version: madeInAbyssVideo.version,
|
version: madeInAbyssVideo.version,
|
||||||
rendering: madeInAbyssVideo.rendering,
|
rendering: madeInAbyssVideo.rendering,
|
||||||
part: madeInAbyssVideo.part,
|
part: madeInAbyssVideo.part,
|
||||||
|
@ -57,7 +57,7 @@ describe("Serie seeding", () => {
|
|||||||
],
|
],
|
||||||
evj: [
|
evj: [
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
slug: madeInAbyssVideo.slug,
|
slug: `${madeInAbyss.slug}-s1e13`,
|
||||||
video: expect.objectContaining({ path: madeInAbyssVideo.path }),
|
video: expect.objectContaining({ path: madeInAbyssVideo.path }),
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
|
@ -77,18 +77,50 @@ describe("Video seeding", () => {
|
|||||||
expect(vid!.evj[0].entry.slug).toBe(`${madeInAbyss.slug}-s1e13`);
|
expect(vid!.evj[0].entry.slug).toBe(`${madeInAbyss.slug}-s1e13`);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("With movie", async () => {
|
||||||
|
const [resp, body] = await createVideo({
|
||||||
|
guess: { title: "bubble", from: "test" },
|
||||||
|
part: null,
|
||||||
|
path: "/video/bubble.mkv",
|
||||||
|
rendering: "sha",
|
||||||
|
version: 1,
|
||||||
|
for: [{ movie: bubble.slug }],
|
||||||
|
});
|
||||||
|
|
||||||
|
expectStatus(resp, body).toBe(201);
|
||||||
|
expect(body).toBeArrayOfSize(1);
|
||||||
|
expect(body[0].id).toBeString();
|
||||||
|
|
||||||
|
const vid = await db.query.videos.findFirst({
|
||||||
|
where: eq(videos.id, body[0].id),
|
||||||
|
with: {
|
||||||
|
evj: { with: { entry: true } },
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(vid).not.toBeNil();
|
||||||
|
expect(vid!.path).toBe("/video/bubble.mkv");
|
||||||
|
expect(vid!.guess).toMatchObject({ title: "bubble", from: "test" });
|
||||||
|
|
||||||
|
expect(body[0].entries).toBeArrayOfSize(1);
|
||||||
|
expect(vid!.evj).toBeArrayOfSize(1);
|
||||||
|
|
||||||
|
expect(vid!.evj[0].slug).toBe(bubble.slug);
|
||||||
|
expect(vid!.evj[0].entry.slug).toBe(bubble.slug);
|
||||||
|
});
|
||||||
|
|
||||||
it("With season/episode", async () => {
|
it("With season/episode", async () => {
|
||||||
const [resp, body] = await createVideo({
|
const [resp, body] = await createVideo({
|
||||||
guess: { title: "mia", season: [1], episode: [13], from: "test" },
|
guess: { title: "mia", season: [2], episode: [1], from: "test" },
|
||||||
part: null,
|
part: null,
|
||||||
path: "/video/mia s1e13.mkv",
|
path: "/video/mia s2e1.mkv",
|
||||||
rendering: "renderingsha",
|
rendering: "renderingsha",
|
||||||
version: 1,
|
version: 1,
|
||||||
for: [
|
for: [
|
||||||
{
|
{
|
||||||
serie: madeInAbyss.slug,
|
serie: madeInAbyss.slug,
|
||||||
season: madeInAbyss.entries[0].seasonNumber!,
|
season: madeInAbyss.entries[3].seasonNumber!,
|
||||||
episode: madeInAbyss.entries[0].episodeNumber!,
|
episode: madeInAbyss.entries[3].episodeNumber!,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
@ -105,13 +137,87 @@ describe("Video seeding", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
expect(vid).not.toBeNil();
|
expect(vid).not.toBeNil();
|
||||||
expect(vid!.path).toBe("/video/mia s1e13.mkv");
|
expect(vid!.path).toBe("/video/mia s2e1.mkv");
|
||||||
expect(vid!.guess).toMatchObject({ title: "mia", from: "test" });
|
expect(vid!.guess).toMatchObject({ title: "mia", from: "test" });
|
||||||
|
|
||||||
expect(body[0].entries).toBeArrayOfSize(1);
|
expect(body[0].entries).toBeArrayOfSize(1);
|
||||||
expect(vid!.evj).toBeArrayOfSize(1);
|
expect(vid!.evj).toBeArrayOfSize(1);
|
||||||
|
|
||||||
expect(vid!.evj[0].slug).toBe(`${madeInAbyss.slug}-s1e13-renderingsha`);
|
expect(vid!.evj[0].slug).toBe(`${madeInAbyss.slug}-s2e1`);
|
||||||
expect(vid!.evj[0].entry.slug).toBe(`${madeInAbyss.slug}-s1e13`);
|
expect(vid!.evj[0].entry.slug).toBe(`${madeInAbyss.slug}-s2e1`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("With special", async () => {
|
||||||
|
const [resp, body] = await createVideo({
|
||||||
|
guess: { title: "mia", season: [0], episode: [3], from: "test" },
|
||||||
|
part: null,
|
||||||
|
path: "/video/mia sp3.mkv",
|
||||||
|
rendering: "notehu",
|
||||||
|
version: 1,
|
||||||
|
for: [
|
||||||
|
{
|
||||||
|
serie: madeInAbyss.slug,
|
||||||
|
special: madeInAbyss.entries[1].number!,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
expectStatus(resp, body).toBe(201);
|
||||||
|
expect(body).toBeArrayOfSize(1);
|
||||||
|
expect(body[0].id).toBeString();
|
||||||
|
|
||||||
|
const vid = await db.query.videos.findFirst({
|
||||||
|
where: eq(videos.id, body[0].id),
|
||||||
|
with: {
|
||||||
|
evj: { with: { entry: true } },
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(vid).not.toBeNil();
|
||||||
|
expect(vid!.path).toBe("/video/mia sp3.mkv");
|
||||||
|
expect(vid!.guess).toMatchObject({ title: "mia", from: "test" });
|
||||||
|
|
||||||
|
expect(body[0].entries).toBeArrayOfSize(1);
|
||||||
|
expect(vid!.evj).toBeArrayOfSize(1);
|
||||||
|
|
||||||
|
expect(vid!.evj[0].slug).toBe(`${madeInAbyss.slug}-sp3`);
|
||||||
|
expect(vid!.evj[0].entry.slug).toBe(`${madeInAbyss.slug}-sp3`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("With order", async () => {
|
||||||
|
const [resp, body] = await createVideo({
|
||||||
|
guess: { title: "mia", season: [0], episode: [3], from: "test" },
|
||||||
|
part: null,
|
||||||
|
path: "/video/mia 13.5.mkv",
|
||||||
|
rendering: "notehu",
|
||||||
|
version: 1,
|
||||||
|
for: [
|
||||||
|
{
|
||||||
|
serie: madeInAbyss.slug,
|
||||||
|
order: 13.5,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
expectStatus(resp, body).toBe(201);
|
||||||
|
expect(body).toBeArrayOfSize(1);
|
||||||
|
expect(body[0].id).toBeString();
|
||||||
|
|
||||||
|
const vid = await db.query.videos.findFirst({
|
||||||
|
where: eq(videos.id, body[0].id),
|
||||||
|
with: {
|
||||||
|
evj: { with: { entry: true } },
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(vid).not.toBeNil();
|
||||||
|
expect(vid!.path).toBe("/video/mia 13.5.mkv");
|
||||||
|
expect(vid!.guess).toMatchObject({ title: "mia", from: "test" });
|
||||||
|
|
||||||
|
expect(body[0].entries).toBeArrayOfSize(1);
|
||||||
|
expect(vid!.evj).toBeArrayOfSize(1);
|
||||||
|
|
||||||
|
expect(vid!.evj[0].slug).toBe("made-in-abyss-dawn-of-the-deep-soul");
|
||||||
|
expect(vid!.evj[0].entry.slug).toBe("made-in-abyss-dawn-of-the-deep-soul");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
Loading…
x
Reference in New Issue
Block a user