mirror of
				https://github.com/zoriya/Kyoo.git
				synced 2025-10-30 18:22:41 -04:00 
			
		
		
		
	Put POST /videos in a transaction, handle dups
				
					
				
			This commit is contained in:
		
							parent
							
								
									379765b28f
								
							
						
					
					
						commit
						466b67afe5
					
				| @ -160,166 +160,174 @@ export const videosH = new Elysia({ prefix: "/videos", tags: ["videos"] }) | ||||
| 	.post( | ||||
| 		"", | ||||
| 		async ({ body, error }) => { | ||||
| 			const vids = await db | ||||
| 				.insert(videos) | ||||
| 				.values(body) | ||||
| 				.onConflictDoUpdate({ | ||||
| 					target: [videos.path], | ||||
| 					set: conflictUpdateAllExcept(videos, ["pk", "id", "createdAt"]), | ||||
| 				}) | ||||
| 				.returning({ | ||||
| 					pk: videos.pk, | ||||
| 					id: videos.id, | ||||
| 					path: videos.path, | ||||
| 			return await db.transaction(async (tx) => { | ||||
| 				const vids = await tx | ||||
| 					.insert(videos) | ||||
| 					.values(body) | ||||
| 					.onConflictDoUpdate({ | ||||
| 						target: [videos.path], | ||||
| 						set: conflictUpdateAllExcept(videos, ["pk", "id", "createdAt"]), | ||||
| 					}) | ||||
| 					.returning({ | ||||
| 						pk: videos.pk, | ||||
| 						id: videos.id, | ||||
| 						path: videos.path, | ||||
| 					}); | ||||
| 
 | ||||
| 				const vidEntries = body.flatMap((x) => { | ||||
| 					if (!x.for) return []; | ||||
| 					return x.for.map((e) => ({ | ||||
| 						video: vids.find((v) => v.path === x.path)!.pk, | ||||
| 						path: x.path, | ||||
| 						entry: { | ||||
| 							...e, | ||||
| 							movie: | ||||
| 								"movie" in e | ||||
| 									? isUuid(e.movie) | ||||
| 										? { id: e.movie } | ||||
| 										: { slug: e.movie } | ||||
| 									: undefined, | ||||
| 							serie: | ||||
| 								"serie" in e | ||||
| 									? isUuid(e.serie) | ||||
| 										? { id: e.serie } | ||||
| 										: { slug: e.serie } | ||||
| 									: undefined, | ||||
| 						}, | ||||
| 					})); | ||||
| 				}); | ||||
| 
 | ||||
| 			const vidEntries = body.flatMap((x) => { | ||||
| 				if (!x.for) return []; | ||||
| 				return x.for.map((e) => ({ | ||||
| 					video: vids.find((v) => v.path === x.path)!.pk, | ||||
| 					path: x.path, | ||||
| 					entry: { | ||||
| 						...e, | ||||
| 						movie: | ||||
| 							"movie" in e | ||||
| 								? isUuid(e.movie) | ||||
| 									? { id: e.movie } | ||||
| 									: { slug: e.movie } | ||||
| 								: undefined, | ||||
| 						serie: | ||||
| 							"serie" in e | ||||
| 								? isUuid(e.serie) | ||||
| 									? { id: e.serie } | ||||
| 									: { slug: e.serie } | ||||
| 								: undefined, | ||||
| 					}, | ||||
| 				})); | ||||
| 			}); | ||||
| 				if (!vidEntries.length) { | ||||
| 					return error( | ||||
| 						201, | ||||
| 						vids.map((x) => ({ id: x.id, path: x.path, entries: [] })), | ||||
| 					); | ||||
| 				} | ||||
| 
 | ||||
| 			if (!vidEntries.length) { | ||||
| 				const entriesQ = tx | ||||
| 					.select({ | ||||
| 						pk: entries.pk, | ||||
| 						id: entries.id, | ||||
| 						slug: entries.slug, | ||||
| 						kind: entries.kind, | ||||
| 						seasonNumber: entries.seasonNumber, | ||||
| 						episodeNumber: entries.episodeNumber, | ||||
| 						order: entries.order, | ||||
| 						showId: sql`${shows.id}`.as("showId"), | ||||
| 						showSlug: sql`${shows.slug}`.as("showSlug"), | ||||
| 						externalId: entries.externalId, | ||||
| 					}) | ||||
| 					.from(entries) | ||||
| 					.innerJoin(shows, eq(entries.showPk, shows.pk)) | ||||
| 					.as("entriesQ"); | ||||
| 
 | ||||
| 				const hasRenderingQ = tx | ||||
| 					.select() | ||||
| 					.from(entryVideoJoin) | ||||
| 					.where(eq(entryVideoJoin.entryPk, entriesQ.pk)); | ||||
| 
 | ||||
| 				const ret = await tx | ||||
| 					.insert(entryVideoJoin) | ||||
| 					.select( | ||||
| 						tx | ||||
| 							.selectDistinctOn([entriesQ.pk, videos.pk], { | ||||
| 								entryPk: entriesQ.pk, | ||||
| 								videoPk: videos.pk, | ||||
| 								slug: computeVideoSlug( | ||||
| 									entriesQ.slug, | ||||
| 									sql`exists(${hasRenderingQ})`, | ||||
| 								), | ||||
| 							}) | ||||
| 							.from( | ||||
| 								values(vidEntries, { | ||||
| 									video: "integer", | ||||
| 									entry: "jsonb", | ||||
| 								}).as("j"), | ||||
| 							) | ||||
| 							.innerJoin(videos, eq(videos.pk, sql`j.video`)) | ||||
| 							.innerJoin( | ||||
| 								entriesQ, | ||||
| 								or( | ||||
| 									and( | ||||
| 										sql`j.entry ? 'slug'`, | ||||
| 										eq(entriesQ.slug, sql`j.entry->>'slug'`), | ||||
| 									), | ||||
| 									and( | ||||
| 										sql`j.entry ? 'movie'`, | ||||
| 										or( | ||||
| 											eq( | ||||
| 												entriesQ.showId, | ||||
| 												sql`(j.entry #>> '{movie, id}')::uuid`, | ||||
| 											), | ||||
| 											eq(entriesQ.showSlug, sql`j.entry #>> '{movie, slug}'`), | ||||
| 										), | ||||
| 										eq(entriesQ.kind, "movie"), | ||||
| 									), | ||||
| 									and( | ||||
| 										sql`j.entry ? 'serie'`, | ||||
| 										or( | ||||
| 											eq( | ||||
| 												entriesQ.showId, | ||||
| 												sql`(j.entry #>> '{serie, id}')::uuid`, | ||||
| 											), | ||||
| 											eq(entriesQ.showSlug, sql`j.entry #>> '{serie, slug}'`), | ||||
| 										), | ||||
| 										or( | ||||
| 											and( | ||||
| 												sql`j.entry ?& array['season', 'episode']`, | ||||
| 												eq( | ||||
| 													entriesQ.seasonNumber, | ||||
| 													sql`(j.entry->>'season')::integer`, | ||||
| 												), | ||||
| 												eq( | ||||
| 													entriesQ.episodeNumber, | ||||
| 													sql`(j.entry->>'episode')::integer`, | ||||
| 												), | ||||
| 											), | ||||
| 											and( | ||||
| 												sql`j.entry ? 'order'`, | ||||
| 												eq(entriesQ.order, sql`(j.entry->>'order')::float`), | ||||
| 											), | ||||
| 											and( | ||||
| 												sql`j.entry ? 'special'`, | ||||
| 												eq( | ||||
| 													entriesQ.episodeNumber, | ||||
| 													sql`(j.entry->>'special')::integer`, | ||||
| 												), | ||||
| 												eq(entriesQ.kind, "special"), | ||||
| 											), | ||||
| 										), | ||||
| 									), | ||||
| 									and( | ||||
| 										sql`j.entry ? 'externalId'`, | ||||
| 										sql`j.entry->'externalId' <@ ${entriesQ.externalId}`, | ||||
| 									), | ||||
| 								), | ||||
| 							), | ||||
| 					) | ||||
| 					.onConflictDoNothing() | ||||
| 					.returning({ | ||||
| 						slug: entryVideoJoin.slug, | ||||
| 						entryPk: entryVideoJoin.entryPk, | ||||
| 						videoPk: entryVideoJoin.videoPk, | ||||
| 					}); | ||||
| 				const entr = ret.reduce( | ||||
| 					(acc, x) => { | ||||
| 						acc[x.videoPk] ??= []; | ||||
| 						acc[x.videoPk].push({ slug: x.slug }); | ||||
| 						return acc; | ||||
| 					}, | ||||
| 					{} as Record<number, { slug: string }[]>, | ||||
| 				); | ||||
| 				return error( | ||||
| 					201, | ||||
| 					vids.map((x) => ({ id: x.id, path: x.path, entries: [] })), | ||||
| 					vids.map((x) => ({ | ||||
| 						id: x.id, | ||||
| 						path: x.path, | ||||
| 						entries: entr[x.pk] ?? [], | ||||
| 					})), | ||||
| 				); | ||||
| 			} | ||||
| 
 | ||||
| 			const entriesQ = db | ||||
| 				.select({ | ||||
| 					pk: entries.pk, | ||||
| 					id: entries.id, | ||||
| 					slug: entries.slug, | ||||
| 					kind: entries.kind, | ||||
| 					seasonNumber: entries.seasonNumber, | ||||
| 					episodeNumber: entries.episodeNumber, | ||||
| 					order: entries.order, | ||||
| 					showId: sql`${shows.id}`.as("showId"), | ||||
| 					showSlug: sql`${shows.slug}`.as("showSlug"), | ||||
| 					externalId: entries.externalId, | ||||
| 				}) | ||||
| 				.from(entries) | ||||
| 				.innerJoin(shows, eq(entries.showPk, shows.pk)) | ||||
| 				.as("entriesQ"); | ||||
| 
 | ||||
| 			const hasRenderingQ = db | ||||
| 				.select() | ||||
| 				.from(entryVideoJoin) | ||||
| 				.where(eq(entryVideoJoin.entryPk, entriesQ.pk)); | ||||
| 
 | ||||
| 			const ret = await db | ||||
| 				.insert(entryVideoJoin) | ||||
| 				.select( | ||||
| 					db | ||||
| 						.select({ | ||||
| 							entryPk: entriesQ.pk, | ||||
| 							videoPk: videos.pk, | ||||
| 							slug: computeVideoSlug( | ||||
| 								entriesQ.slug, | ||||
| 								sql`exists(${hasRenderingQ})`, | ||||
| 							), | ||||
| 						}) | ||||
| 						.from( | ||||
| 							values(vidEntries, { | ||||
| 								video: "integer", | ||||
| 								entry: "jsonb", | ||||
| 							}).as("j"), | ||||
| 						) | ||||
| 						.innerJoin(videos, eq(videos.pk, sql`j.video`)) | ||||
| 						.innerJoin( | ||||
| 							entriesQ, | ||||
| 							or( | ||||
| 								and( | ||||
| 									sql`j.entry ? 'slug'`, | ||||
| 									eq(entriesQ.slug, sql`j.entry->>'slug'`), | ||||
| 								), | ||||
| 								and( | ||||
| 									sql`j.entry ? 'movie'`, | ||||
| 									or( | ||||
| 										eq(entriesQ.showId, sql`(j.entry #>> '{movie, id}')::uuid`), | ||||
| 										eq(entriesQ.showSlug, sql`j.entry #>> '{movie, slug}'`), | ||||
| 									), | ||||
| 									eq(entriesQ.kind, "movie"), | ||||
| 								), | ||||
| 								and( | ||||
| 									sql`j.entry ? 'serie'`, | ||||
| 									or( | ||||
| 										eq(entriesQ.showId, sql`(j.entry #>> '{serie, id}')::uuid`), | ||||
| 										eq(entriesQ.showSlug, sql`j.entry #>> '{serie, slug}'`), | ||||
| 									), | ||||
| 									or( | ||||
| 										and( | ||||
| 											sql`j.entry ?& array['season', 'episode']`, | ||||
| 											eq( | ||||
| 												entriesQ.seasonNumber, | ||||
| 												sql`(j.entry->>'season')::integer`, | ||||
| 											), | ||||
| 											eq( | ||||
| 												entriesQ.episodeNumber, | ||||
| 												sql`(j.entry->>'episode')::integer`, | ||||
| 											), | ||||
| 										), | ||||
| 										and( | ||||
| 											sql`j.entry ? 'order'`, | ||||
| 											eq(entriesQ.order, sql`(j.entry->>'order')::float`), | ||||
| 										), | ||||
| 										and( | ||||
| 											sql`j.entry ? 'special'`, | ||||
| 											eq( | ||||
| 												entriesQ.episodeNumber, | ||||
| 												sql`(j.entry->>'special')::integer`, | ||||
| 											), | ||||
| 											eq(entriesQ.kind, "special"), | ||||
| 										), | ||||
| 									), | ||||
| 								), | ||||
| 								and( | ||||
| 									sql`j.entry ? 'externalId'`, | ||||
| 									sql`j.entry->'externalId' <@ ${entriesQ.externalId}`, | ||||
| 								), | ||||
| 							), | ||||
| 						), | ||||
| 				) | ||||
| 				.onConflictDoNothing() | ||||
| 				.returning({ | ||||
| 					slug: entryVideoJoin.slug, | ||||
| 					entryPk: entryVideoJoin.entryPk, | ||||
| 					videoPk: entryVideoJoin.videoPk, | ||||
| 				}); | ||||
| 			const entr = ret.reduce( | ||||
| 				(acc, x) => { | ||||
| 					acc[x.videoPk] ??= []; | ||||
| 					acc[x.videoPk].push({ slug: x.slug }); | ||||
| 					return acc; | ||||
| 				}, | ||||
| 				{} as Record<number, { slug: string }[]>, | ||||
| 			); | ||||
| 			return error( | ||||
| 				201, | ||||
| 				vids.map((x) => ({ | ||||
| 					id: x.id, | ||||
| 					path: x.path, | ||||
| 					entries: entr[x.pk] ?? [], | ||||
| 				})), | ||||
| 			); | ||||
| 			}); | ||||
| 		}, | ||||
| 		{ | ||||
| 			detail: { | ||||
|  | ||||
| @ -1,4 +1,4 @@ | ||||
| import { PatternString } from "@sinclair/typebox"; | ||||
| import { PatternStringExact } from "@sinclair/typebox"; | ||||
| import { t } from "elysia"; | ||||
| import { type Prettify, comment } from "~/utils"; | ||||
| import { ExtraType } from "./entry/extra"; | ||||
| @ -9,12 +9,13 @@ const ExternalIds = t.Record( | ||||
| 	t.String(), | ||||
| 	t.Omit( | ||||
| 		t.Union([ | ||||
| 			EpisodeId.patternProperties[PatternString], | ||||
| 			ExternalId().patternProperties[PatternString], | ||||
| 			EpisodeId.patternProperties[PatternStringExact], | ||||
| 			ExternalId().patternProperties[PatternStringExact], | ||||
| 		]), | ||||
| 		["link"], | ||||
| 	), | ||||
| ); | ||||
| type ExternalIds = typeof ExternalIds.static; | ||||
| 
 | ||||
| export const Guess = t.Recursive((Self) => | ||||
| 	t.Object( | ||||
|  | ||||
| @ -109,6 +109,38 @@ describe("Video seeding", () => { | ||||
| 		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: "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 () => { | ||||
| 		const [resp, body] = await createVideo({ | ||||
| 			guess: { title: "mia", season: [2], episode: [1], from: "test" }, | ||||
| @ -229,7 +261,7 @@ describe("Video seeding", () => { | ||||
| 				episode: [3], | ||||
| 				from: "test", | ||||
| 				externalId: { | ||||
| 					themoviedb: { serieId: "72636", season: 1, episode: 13 }, | ||||
| 					themoviedatabase: { serieId: "72636", season: 1, episode: 13 }, | ||||
| 				}, | ||||
| 			}, | ||||
| 			part: null, | ||||
| @ -239,7 +271,7 @@ describe("Video seeding", () => { | ||||
| 			for: [ | ||||
| 				{ | ||||
| 					externalId: { | ||||
| 						themoviedb: { serieId: "72636", season: 1, episode: 13 }, | ||||
| 						themoviedatabase: { serieId: "72636", season: 1, episode: 13 }, | ||||
| 					}, | ||||
| 				}, | ||||
| 			], | ||||
| @ -273,7 +305,7 @@ describe("Video seeding", () => { | ||||
| 				title: "bubble", | ||||
| 				from: "test", | ||||
| 				externalId: { | ||||
| 					themoviedb: { dataId: "912598", season: 1, episode: 13 }, | ||||
| 					themoviedatabase: { dataId: "912598" }, | ||||
| 				}, | ||||
| 			}, | ||||
| 			part: null, | ||||
| @ -283,7 +315,7 @@ describe("Video seeding", () => { | ||||
| 			for: [ | ||||
| 				{ | ||||
| 					externalId: { | ||||
| 						themoviedb: { serieId: "912598", season: 1, episode: 13 }, | ||||
| 						themoviedatabase: { dataId: "912598" }, | ||||
| 					}, | ||||
| 				}, | ||||
| 			], | ||||
| @ -317,18 +349,18 @@ describe("Video seeding", () => { | ||||
| 				title: "bubble", | ||||
| 				from: "test", | ||||
| 				externalId: { | ||||
| 					themoviedb: { dataId: "912598", season: 1, episode: 13 }, | ||||
| 					themoviedatabase: { dataId: "912598" }, | ||||
| 				}, | ||||
| 			}, | ||||
| 			part: null, | ||||
| 			path: "/video/bubble [tmdb=912598].mkv", | ||||
| 			rendering: "cwhtn", | ||||
| 			path: "/video/bubble ue [tmdb=912598].mkv", | ||||
| 			rendering: "aoeubnht", | ||||
| 			version: 1, | ||||
| 			for: [ | ||||
| 				{ movie: "bubble" }, | ||||
| 				{ | ||||
| 					externalId: { | ||||
| 						themoviedb: { serieId: "912598", season: 1, episode: 13 }, | ||||
| 						themoviedatabase: { dataId: "912598" }, | ||||
| 					}, | ||||
| 				}, | ||||
| 			], | ||||
| @ -346,13 +378,13 @@ describe("Video seeding", () => { | ||||
| 		}); | ||||
| 
 | ||||
| 		expect(vid).not.toBeNil(); | ||||
| 		expect(vid!.path).toBe("/video/bubble [tmdb=912598].mkv"); | ||||
| 		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-cwhtn"); | ||||
| 		expect(vid!.evj[0].slug).toBe("bubble-aoeubnht"); | ||||
| 		expect(vid!.evj[0].entry.slug).toBe("bubble"); | ||||
| 	}); | ||||
| 
 | ||||
| @ -363,7 +395,7 @@ describe("Video seeding", () => { | ||||
| 				title: "bubble", | ||||
| 				from: "test", | ||||
| 				externalId: { | ||||
| 					themoviedb: { dataId: "912598", season: 1, episode: 13 }, | ||||
| 					themoviedatabase: { dataId: "912598" }, | ||||
| 				}, | ||||
| 			}, | ||||
| 			part: null, | ||||
| @ -374,7 +406,7 @@ describe("Video seeding", () => { | ||||
| 				{ movie: "bubble" }, | ||||
| 				{ | ||||
| 					externalId: { | ||||
| 						themoviedb: { serieId: "912598", season: 1, episode: 13 }, | ||||
| 						themoviedatabase: { dataId: "912598" }, | ||||
| 					}, | ||||
| 				}, | ||||
| 			], | ||||
| @ -401,4 +433,120 @@ describe("Video seeding", () => { | ||||
| 		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: "notehu", | ||||
| 			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"); | ||||
| 	}); | ||||
| }); | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user