mirror of
https://github.com/zoriya/Kyoo.git
synced 2025-07-09 03:04:20 -04:00
Add tests for video linking
This commit is contained in:
parent
6e2743a4be
commit
489336c77a
@ -515,10 +515,13 @@ export const videosH = new Elysia({ prefix: "/videos", tags: ["videos"] })
|
||||
201: t.Array(
|
||||
t.Object({
|
||||
id: t.String({ format: "uuid" }),
|
||||
path: t.String(),
|
||||
path: t.String({ examples: ["/video/made in abyss s1e13.mkv"] }),
|
||||
entries: t.Array(
|
||||
t.Object({
|
||||
slug: t.String({ format: "slug" }),
|
||||
slug: t.String({
|
||||
format: "slug",
|
||||
examples: ["made-in-abyss-s1e13"],
|
||||
}),
|
||||
}),
|
||||
),
|
||||
}),
|
||||
|
@ -58,7 +58,7 @@ export const toDrizzle = (expr: Expression, config: FilterDef): SQL => {
|
||||
{ in: where },
|
||||
);
|
||||
}
|
||||
expr.value = { type: "bool", value: expr.value.value === "true" }
|
||||
expr.value = { type: "bool", value: expr.value.value === "true" };
|
||||
}
|
||||
if (prop.type !== expr.value.type) {
|
||||
throw new KErrorT(
|
||||
|
@ -43,3 +43,20 @@ export const deleteVideo = async (paths: string[]) => {
|
||||
const body = await resp.json();
|
||||
return [resp, body] as const;
|
||||
};
|
||||
|
||||
export const linkVideos = async (
|
||||
links: { id: string; for: SeedVideo["for"] }[],
|
||||
) => {
|
||||
const resp = await handlers.handle(
|
||||
new Request(buildUrl("videos/link"), {
|
||||
method: "POST",
|
||||
body: JSON.stringify(links),
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
...(await getJwtHeaders()),
|
||||
},
|
||||
}),
|
||||
);
|
||||
const body = await resp.json();
|
||||
return [resp, body] as const;
|
||||
};
|
||||
|
@ -9,6 +9,7 @@ let bubbleId = "";
|
||||
|
||||
beforeAll(async () => {
|
||||
await db.delete(shows);
|
||||
await db.delete(videos);
|
||||
await db.insert(videos).values(bubbleVideo);
|
||||
const [ret, body] = await createMovie(bubble);
|
||||
expect(ret.status).toBe(201);
|
||||
@ -66,21 +67,29 @@ describe("Get movie", () => {
|
||||
const [resp, body] = await getMovie(bubble.slug, { langs: "fr,pr,*" });
|
||||
|
||||
expectStatus(resp, body).toBe(200);
|
||||
expect(body).toMatchObject({
|
||||
slug: bubble.slug,
|
||||
name: bubble.translations.en.name,
|
||||
});
|
||||
expect(resp.headers.get("Content-Language")).toBe("en");
|
||||
expect(body.slug).toBe(bubble.slug);
|
||||
const lang = resp.headers.get("Content-Language");
|
||||
if (lang === "en") {
|
||||
expect(body.name).toBe(bubble.translations.en.name);
|
||||
} else if (lang === "ja") {
|
||||
expect(body.name).toBe(bubble.translations.ja.name);
|
||||
} else {
|
||||
expect(lang).toBe("en");
|
||||
}
|
||||
});
|
||||
it("Works without accept-language header", async () => {
|
||||
const [resp, body] = await getMovie(bubble.slug, { langs: undefined });
|
||||
|
||||
expectStatus(resp, body).toBe(200);
|
||||
expect(body).toMatchObject({
|
||||
slug: bubble.slug,
|
||||
name: bubble.translations.en.name,
|
||||
});
|
||||
expect(resp.headers.get("Content-Language")).toBe("en");
|
||||
expect(body.slug).toBe(bubble.slug);
|
||||
const lang = resp.headers.get("Content-Language");
|
||||
if (lang === "en") {
|
||||
expect(body.name).toBe(bubble.translations.en.name);
|
||||
} else if (lang === "ja") {
|
||||
expect(body.name).toBe(bubble.translations.ja.name);
|
||||
} else {
|
||||
expect(lang).toBe("en");
|
||||
}
|
||||
});
|
||||
it("Fallback if translations does not exist", async () => {
|
||||
const [resp, body] = await getMovie(bubble.slug, { langs: "en-au" });
|
||||
|
@ -6,10 +6,11 @@ import {
|
||||
createVideo,
|
||||
deleteVideo,
|
||||
getVideos,
|
||||
linkVideos,
|
||||
} from "tests/helpers";
|
||||
import { expectStatus } from "tests/utils";
|
||||
import { db } from "~/db";
|
||||
import { entries, shows, videos } from "~/db/schema";
|
||||
import { entries, entryVideoJoin, shows, videos } from "~/db/schema";
|
||||
import { bubble, madeInAbyss } from "~/models/examples";
|
||||
|
||||
beforeAll(async () => {
|
||||
@ -57,12 +58,35 @@ beforeAll(async () => {
|
||||
version: 1,
|
||||
for: [{ movie: bubble.slug }],
|
||||
},
|
||||
{
|
||||
guess: {
|
||||
title: "mia",
|
||||
episodes: [{ season: 1, episode: 1 }], // Different episode for unlinked
|
||||
from: "test",
|
||||
history: [],
|
||||
},
|
||||
part: null,
|
||||
path: "/video/mia-unlinked.mkv",
|
||||
rendering: "sha-unlinked-1",
|
||||
version: 1,
|
||||
// No 'for' initially
|
||||
},
|
||||
{
|
||||
guess: { title: "bubble", from: "test", history: [] },
|
||||
part: null,
|
||||
path: "/video/bubble-unlinked.mkv",
|
||||
rendering: "sha-unlinked-2",
|
||||
version: 1,
|
||||
// No 'for' initially
|
||||
},
|
||||
]);
|
||||
expectStatus(ret, body).toBe(201);
|
||||
expect(body).toBeArrayOfSize(3);
|
||||
expect(body).toBeArrayOfSize(5);
|
||||
expect(body[0].entries).toBeArrayOfSize(1);
|
||||
expect(body[1].entries).toBeArrayOfSize(1);
|
||||
expect(body[2].entries).toBeArrayOfSize(1);
|
||||
expect(body[3].entries).toBeArrayOfSize(0); // Unlinked
|
||||
expect(body[4].entries).toBeArrayOfSize(0); // Unlinked
|
||||
|
||||
const items = await db.query.shows.findMany();
|
||||
expect(items.find((x) => x.slug === "bubble")!.availableCount).toBe(1);
|
||||
@ -141,8 +165,10 @@ describe("Video get/deletion", () => {
|
||||
},
|
||||
},
|
||||
});
|
||||
expect(body.unmatched).toBeArrayOfSize(1);
|
||||
expect(body.unmatched[0]).toBe("/video/mia s1e13 unknown test.mkv");
|
||||
expect(body.unmatched).toBeArrayOfSize(3);
|
||||
expect(body.unmatched).toContain("/video/mia s1e13 unknown test.mkv");
|
||||
expect(body.unmatched).toContain("/video/mia-unlinked.mkv");
|
||||
expect(body.unmatched).toContain("/video/bubble-unlinked.mkv");
|
||||
});
|
||||
|
||||
it("Mismatch title guess", async () => {
|
||||
@ -249,3 +275,69 @@ describe("Video get/deletion", () => {
|
||||
expect(body[0]).toBe("/video/mia s1e13 unknown test.mkv");
|
||||
});
|
||||
});
|
||||
|
||||
describe("Video linking", () => {
|
||||
it("Should link videos to entries", async () => {
|
||||
const allVideos = await db
|
||||
.select({
|
||||
id: videos.id,
|
||||
path: videos.path,
|
||||
rendering: videos.rendering,
|
||||
})
|
||||
.from(videos);
|
||||
|
||||
const miaUnlinkedVideo = allVideos.find(
|
||||
(v) => v.rendering === "sha-unlinked-1",
|
||||
);
|
||||
const bubbleUnlinkedVideo = allVideos.find(
|
||||
(v) => v.rendering === "sha-unlinked-2",
|
||||
);
|
||||
|
||||
expect(miaUnlinkedVideo).toBeDefined();
|
||||
expect(bubbleUnlinkedVideo).toBeDefined();
|
||||
|
||||
const [resp, body] = await linkVideos([
|
||||
{
|
||||
id: miaUnlinkedVideo!.id,
|
||||
for: [{ slug: `${madeInAbyss.slug}-s1e13` }],
|
||||
},
|
||||
{
|
||||
id: bubbleUnlinkedVideo!.id,
|
||||
for: [{ movie: bubble.slug }],
|
||||
},
|
||||
]);
|
||||
|
||||
expectStatus(resp, body).toBe(201);
|
||||
expect(body).toBeArrayOfSize(2);
|
||||
|
||||
expect(body[0]).toMatchObject({
|
||||
id: miaUnlinkedVideo!.id,
|
||||
path: "/video/mia-unlinked.mkv",
|
||||
entries: [
|
||||
{
|
||||
slug: expect.stringContaining(`${madeInAbyss.slug}-s1e13`),
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
expect(body[1]).toMatchObject({
|
||||
id: bubbleUnlinkedVideo!.id,
|
||||
path: "/video/bubble-unlinked.mkv",
|
||||
entries: [
|
||||
{
|
||||
slug: expect.stringContaining(bubble.slug),
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
const miaShow = await db.query.shows.findFirst({
|
||||
where: eq(shows.slug, madeInAbyss.slug),
|
||||
});
|
||||
expect(miaShow!.availableCount).toBe(1);
|
||||
|
||||
const bubbleShow = await db.query.shows.findFirst({
|
||||
where: eq(shows.slug, bubble.slug),
|
||||
});
|
||||
expect(bubbleShow!.availableCount).toBe(1);
|
||||
});
|
||||
});
|
||||
|
@ -7,9 +7,9 @@ from aiohttp import ClientSession
|
||||
from pydantic import TypeAdapter
|
||||
|
||||
from .models.movie import Movie
|
||||
from .models.request import Request
|
||||
from .models.serie import Serie
|
||||
from .models.videos import For, Resource, Video, VideoCreated, VideoInfo, VideoLink
|
||||
from .requests import Request
|
||||
from .utils import Singleton
|
||||
|
||||
logger = getLogger(__name__)
|
||||
@ -96,7 +96,7 @@ class KyooClient(metaclass=Singleton):
|
||||
)
|
||||
|
||||
async with self._client.post(
|
||||
"videos",
|
||||
"videos/link",
|
||||
data=TypeAdapter(list[VideoLink]).dump_json(
|
||||
[map_request(x) for x in videos],
|
||||
by_alias=True,
|
||||
|
20
scanner/scanner/models/request.py
Normal file
20
scanner/scanner/models/request.py
Normal file
@ -0,0 +1,20 @@
|
||||
from __future__ import annotations
|
||||
from typing import Literal
|
||||
|
||||
from pydantic import Field
|
||||
|
||||
from .videos import Guess
|
||||
from ..utils import Model
|
||||
|
||||
|
||||
class Request(Model, extra="allow"):
|
||||
pk: int | None = Field(exclude=True, default=None)
|
||||
kind: Literal["episode", "movie"]
|
||||
title: str
|
||||
year: int | None
|
||||
external_id: dict[str, str]
|
||||
videos: list[Request.Video]
|
||||
|
||||
class Video(Model):
|
||||
id: str
|
||||
episodes: list[Guess.Episode]
|
@ -1,33 +1,18 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from asyncio import CancelledError, Event, TaskGroup
|
||||
from logging import getLogger
|
||||
from typing import Literal, cast
|
||||
from typing import cast
|
||||
|
||||
from asyncpg import Connection, Pool
|
||||
from pydantic import Field, TypeAdapter
|
||||
from pydantic import TypeAdapter
|
||||
|
||||
from .client import KyooClient
|
||||
from .models.videos import Guess, Resource
|
||||
from .models.request import Request
|
||||
from .models.videos import Resource
|
||||
from .providers.provider import Provider
|
||||
from .utils import Model
|
||||
|
||||
logger = getLogger(__name__)
|
||||
|
||||
|
||||
class Request(Model, extra="allow"):
|
||||
pk: int | None = Field(exclude=True, default=None)
|
||||
kind: Literal["episode", "movie"]
|
||||
title: str
|
||||
year: int | None
|
||||
external_id: dict[str, str]
|
||||
videos: list[Request.Video]
|
||||
|
||||
class Video(Model):
|
||||
id: str
|
||||
episodes: list[Guess.Episode]
|
||||
|
||||
|
||||
class RequestCreator:
|
||||
def __init__(self, database: Connection):
|
||||
self._database = database
|
||||
|
Loading…
x
Reference in New Issue
Block a user