Kyoo/api/tests/videos/scanner.test.ts
2025-05-04 15:35:37 +02:00

567 lines
14 KiB
TypeScript

import { beforeAll, describe, expect, it } from "bun:test";
import { eq } from "drizzle-orm";
import { createMovie, createSerie, createVideo } from "tests/helpers";
import { expectStatus } from "tests/utils";
import { db } from "~/db";
import { entries, shows, videos } from "~/db/schema";
import { bubble, madeInAbyss } from "~/models/examples";
beforeAll(async () => {
await db.delete(shows);
await db.delete(videos);
let [ret, body] = await createSerie(madeInAbyss);
expectStatus(ret, body).toBe(201);
[ret, body] = await createMovie(bubble);
expectStatus(ret, body).toBe(201);
});
describe("Video seeding", () => {
it("Can create a video without entry", async () => {
const [resp, body] = await createVideo({
guess: { title: "unknown", from: "test" },
part: null,
path: "/video/unknown s1e13.mkv",
rendering: "sha",
version: 1,
});
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/unknown s1e13.mkv");
expect(vid!.guess).toMatchObject({ title: "unknown", from: "test" });
expect(body[0].entries).toBeArrayOfSize(0);
expect(vid!.evj).toBeArrayOfSize(0);
});
it("With slug", async () => {
const [resp, body] = await createVideo({
guess: { title: "mia", season: [1], episode: [13], from: "test" },
part: null,
path: "/video/mia s1e13.mkv",
rendering: "sha2",
version: 1,
for: [{ slug: `${madeInAbyss.slug}-s1e13` }],
});
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 s1e13.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}-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: "sha3",
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("Conflicting path", async () => {
const [resp, body] = await createVideo({
guess: { title: "bubble", from: "test" },
part: null,
path: "/video/bubble.mkv",
rendering: "sha4",
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 () => {
const [resp, body] = await createVideo({
guess: { title: "mia", season: [2], episode: [1], from: "test" },
part: null,
path: "/video/mia s2e1.mkv",
rendering: "renderingsha",
version: 1,
for: [
{
serie: madeInAbyss.slug,
season: madeInAbyss.entries[3].seasonNumber!,
episode: madeInAbyss.entries[3].episodeNumber!,
},
],
});
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 s2e1.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}-s2e1`);
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: "notehu2",
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");
});
it("With external id", async () => {
const [resp, body] = await createVideo({
guess: {
title: "mia",
season: [0],
episode: [3],
from: "test",
externalId: {
themoviedatabase: { serieId: "72636", season: 1, episode: 13 },
},
},
part: null,
path: "/video/mia s1e13 [tmdb=72636].mkv",
rendering: "notehu3",
version: 1,
for: [
{
externalId: {
themoviedatabase: { serieId: "72636", season: 1, episode: 13 },
},
},
],
});
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 s1e13 [tmdb=72636].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-s1e13-notehu3");
expect(vid!.evj[0].entry.slug).toBe("made-in-abyss-s1e13");
});
it("With movie external id", async () => {
const [resp, body] = await createVideo({
guess: {
title: "bubble",
from: "test",
externalId: {
themoviedatabase: { dataId: "912598" },
},
},
part: null,
path: "/video/bubble [tmdb=912598].mkv",
rendering: "onetuh",
version: 1,
for: [
{
externalId: {
themoviedatabase: { dataId: "912598" },
},
},
],
});
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 [tmdb=912598].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-onetuh");
expect(vid!.evj[0].entry.slug).toBe("bubble");
});
it("Different path, same sha", async () => {
const [resp, body] = await createVideo({
guess: { title: "bubble", from: "test" },
part: null,
path: "/video/bubble invalid-sha.mkv",
rendering: "sha",
version: 1,
for: [{ movie: bubble.slug }],
});
// conflict with existing video, message will contain an explanation on how to fix this
expectStatus(resp, body).toBe(409);
expect(body.message).toBeString();
});
it("Two for the same entry", async () => {
const [resp, body] = await createVideo({
guess: {
title: "bubble",
from: "test",
externalId: {
themoviedatabase: { dataId: "912598" },
},
},
part: null,
path: "/video/bubble ue [tmdb=912598].mkv",
rendering: "aoeubnht",
version: 1,
for: [
{ movie: "bubble" },
{
externalId: {
themoviedatabase: { dataId: "912598" },
},
},
],
});
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 ue [tmdb=912598].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-aoeubnht");
expect(vid!.evj[0].entry.slug).toBe("bubble");
});
it("Two for the same entry WITHOUT rendering", async () => {
await db.delete(videos);
const [resp, body] = await createVideo({
guess: {
title: "bubble",
from: "test",
externalId: {
themoviedatabase: { dataId: "912598" },
},
},
part: null,
path: "/video/bubble [tmdb=912598].mkv",
rendering: "cwhtn",
version: 1,
for: [
{ movie: "bubble" },
{
externalId: {
themoviedatabase: { dataId: "912598" },
},
},
],
});
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 [tmdb=912598].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");
expect(vid!.evj[0].entry.slug).toBe("bubble");
});
it("Multi part", async () => {
await db.delete(videos);
const [resp, body] = await createVideo([
{
guess: {
title: "bubble",
from: "test",
externalId: {
themoviedatabase: { dataId: "912598" },
},
},
part: 1,
path: "/video/bubble p1 [tmdb=912598].mkv",
rendering: "cwhtn",
version: 1,
for: [
{ movie: "bubble" },
{
externalId: {
themoviedatabase: { dataId: "912598" },
},
},
],
},
{
guess: {
title: "bubble",
from: "test",
externalId: {
themoviedatabase: { dataId: "912598" },
},
},
part: 2,
path: "/video/bubble p2 [tmdb=912598].mkv",
rendering: "cwhtn",
version: 1,
for: [
{ movie: "bubble" },
{
externalId: {
themoviedatabase: { dataId: "912598" },
},
},
],
},
]);
expectStatus(resp, body).toBe(201);
expect(body).toBeArrayOfSize(2);
expect(body[0].id).toBeString();
expect(body[1].id).toBeString();
expect(body[0].entries).toBeArrayOfSize(1);
expect(body[1].entries).toBeArrayOfSize(1);
const entr = (await db.query.entries.findFirst({
where: eq(entries.slug, bubble.slug),
with: {
evj: { with: { video: true } },
},
}))!;
expect(entr.evj).toBeArrayOfSize(2);
expect(entr.evj[0].video.path).toBe("/video/bubble p1 [tmdb=912598].mkv");
expect(entr.evj[0].slug).toBe("bubble-p1");
expect(entr.evj[1].slug).toBe("bubble-p2");
});
it("Multi entry", async () => {
await db.delete(videos);
const [resp, body] = await createVideo({
guess: {
title: "mia",
season: [1, 2],
episode: [13, 1],
from: "test",
},
part: null,
path: "/video/mia s1e13 & s2e1 [tmdb=72636].mkv",
rendering: "notehu5",
version: 1,
for: [
{ serie: madeInAbyss.slug, season: 1, episode: 13 },
{
externalId: {
themoviedatabase: { serieId: "72636", season: 1, episode: 13 },
},
},
{ serie: madeInAbyss.slug, season: 2, episode: 1 },
],
});
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 s1e13 & s2e1 [tmdb=72636].mkv");
expect(vid!.guess).toMatchObject({ title: "mia", from: "test" });
expect(body[0].entries).toBeArrayOfSize(2);
expect(vid!.evj).toBeArrayOfSize(2);
expect(vid!.evj[0].slug).toBe("made-in-abyss-s1e13");
expect(vid!.evj[0].entry.slug).toBe("made-in-abyss-s1e13");
expect(vid!.evj[1].slug).toBe("made-in-abyss-s2e1");
expect(vid!.evj[1].entry.slug).toBe("made-in-abyss-s2e1");
});
});