mirror of
				https://github.com/zoriya/Kyoo.git
				synced 2025-10-31 02:27:11 -04:00 
			
		
		
		
	Create movie seed route handler
This commit is contained in:
		
							parent
							
								
									1309749e46
								
							
						
					
					
						commit
						30d5d65755
					
				
							
								
								
									
										19
									
								
								api/src/controllers/seed/images.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								api/src/controllers/seed/images.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,19 @@ | |||||||
|  | import type { Image } from "~/models/utils"; | ||||||
|  | 
 | ||||||
|  | export const processImage = async (url: string): Promise<Image> => { | ||||||
|  | 	const hasher = new Bun.CryptoHasher("sha256"); | ||||||
|  | 	hasher.update(url); | ||||||
|  | 
 | ||||||
|  | 	// TODO: download source, save it in multiples qualities & process blurhash
 | ||||||
|  | 
 | ||||||
|  | 	return { | ||||||
|  | 		id: hasher.digest().toString(), | ||||||
|  | 		source: url, | ||||||
|  | 		blurhash: "", | ||||||
|  | 	}; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | export const processOptImage = (url: string | null): Promise<Image | null> => { | ||||||
|  | 	if (!url) return Promise.resolve(null); | ||||||
|  | 	return processImage(url); | ||||||
|  | }; | ||||||
							
								
								
									
										79
									
								
								api/src/controllers/seed/index.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										79
									
								
								api/src/controllers/seed/index.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,79 @@ | |||||||
|  | import Elysia, { t } from "elysia"; | ||||||
|  | import { Movie, SeedMovie } from "~/models/movie"; | ||||||
|  | import { db } from "~/db"; | ||||||
|  | import { | ||||||
|  | 	shows, | ||||||
|  | 	showTranslations, | ||||||
|  | 	entries, | ||||||
|  | 	entryTranslations, | ||||||
|  | } from "~/db/schema"; | ||||||
|  | import { guessNextRefresh } from "./refresh"; | ||||||
|  | import { processOptImage } from "./images"; | ||||||
|  | 
 | ||||||
|  | type Show = typeof shows.$inferInsert; | ||||||
|  | type ShowTrans = typeof showTranslations.$inferInsert; | ||||||
|  | type Entry = typeof entries.$inferInsert; | ||||||
|  | 
 | ||||||
|  | export const seed = new Elysia() | ||||||
|  | 	.model({ | ||||||
|  | 		movie: Movie, | ||||||
|  | 		"seed-movie": SeedMovie, | ||||||
|  | 		error: t.String(), | ||||||
|  | 	}) | ||||||
|  | 	.post( | ||||||
|  | 		"/movies", | ||||||
|  | 		async ({ body }) => { | ||||||
|  | 			const { translations, videos, ...bMovie } = body; | ||||||
|  | 
 | ||||||
|  | 			const ret = await db.transaction(async (tx) => { | ||||||
|  | 				const movie: Show = { | ||||||
|  | 					kind: "movie", | ||||||
|  | 					startAir: bMovie.airDate, | ||||||
|  | 					nextRefresh: guessNextRefresh(bMovie.airDate ?? new Date()), | ||||||
|  | 					...bMovie, | ||||||
|  | 				}; | ||||||
|  | 				const [ret] = await tx | ||||||
|  | 					.insert(shows) | ||||||
|  | 					.values(movie) | ||||||
|  | 					.returning({ pk: shows.pk, id: shows.id }); | ||||||
|  | 
 | ||||||
|  | 				// even if never shown to the user, a movie still has an entry.
 | ||||||
|  | 				const movieEntry: Entry = { type: "movie", ...bMovie }; | ||||||
|  | 				const [entry] = await tx | ||||||
|  | 					.insert(entries) | ||||||
|  | 					.values(movieEntry) | ||||||
|  | 					.returning({ pk: entries.pk }); | ||||||
|  | 
 | ||||||
|  | 				const trans: ShowTrans[] = await Promise.all( | ||||||
|  | 					Object.entries(translations).map(async ([lang, tr]) => ({ | ||||||
|  | 						pk: ret.pk, | ||||||
|  | 						// TODO: normalize lang or error if invalid
 | ||||||
|  | 						language: lang, | ||||||
|  | 						...tr, | ||||||
|  | 						poster: await processOptImage(tr.poster), | ||||||
|  | 						thumbnail: await processOptImage(tr.thumbnail), | ||||||
|  | 						logo: await processOptImage(tr.logo), | ||||||
|  | 						banner: await processOptImage(tr.banner), | ||||||
|  | 					})), | ||||||
|  | 				); | ||||||
|  | 				await tx.insert(showTranslations).values(trans); | ||||||
|  | 
 | ||||||
|  | 				const entryTrans = trans.map((x) => ({ ...x, pk: entry.pk })); | ||||||
|  | 				await tx.insert(entryTranslations).values(entryTrans); | ||||||
|  | 
 | ||||||
|  | 				return { ...ret, entry: entry.pk }; | ||||||
|  | 			}); | ||||||
|  | 
 | ||||||
|  | 			// TODO: insert entry-video links
 | ||||||
|  | 			// await db.transaction(async tx => {
 | ||||||
|  | 			// 	await tx.insert(videos).values(videos);
 | ||||||
|  | 			// });
 | ||||||
|  | 
 | ||||||
|  | 			return ret.id; | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			body: "seed-movie", | ||||||
|  | 			response: { 200: "movie", 400: "error" }, | ||||||
|  | 			tags: ["movies"], | ||||||
|  | 		}, | ||||||
|  | 	); | ||||||
							
								
								
									
										12
									
								
								api/src/controllers/seed/refresh.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								api/src/controllers/seed/refresh.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,12 @@ | |||||||
|  | // oh i hate js dates so much.
 | ||||||
|  | export const guessNextRefresh = (airDate: Date | string) => { | ||||||
|  | 	if (typeof airDate === "string") airDate = new Date(airDate); | ||||||
|  | 	const diff = new Date().getTime() - airDate.getTime(); | ||||||
|  | 	const days = diff / (24 * 60 * 60 * 1000); | ||||||
|  | 
 | ||||||
|  | 	const ret = new Date(); | ||||||
|  | 	if (days <= 4) ret.setDate(ret.getDate() + 4); | ||||||
|  | 	else if (days <= 21) ret.setDate(ret.getDate() + 14); | ||||||
|  | 	else ret.setMonth(ret.getMonth() + 2); | ||||||
|  | 	return ret.toISOString().substring(0, 10); | ||||||
|  | }; | ||||||
| @ -1,7 +1,7 @@ | |||||||
| import { t } from "elysia"; | import { t } from "elysia"; | ||||||
| 
 | 
 | ||||||
| export const Image = t.Object({ | export const Image = t.Object({ | ||||||
| 	id: t.String({ format: "uuid" }), | 	id: t.String(), | ||||||
| 	source: t.String({ format: "uri" }), | 	source: t.String({ format: "uri" }), | ||||||
| 	blurhash: t.String(), | 	blurhash: t.String(), | ||||||
| }); | }); | ||||||
|  | |||||||
| @ -3,11 +3,19 @@ | |||||||
| 		"target": "ES2021", | 		"target": "ES2021", | ||||||
| 		"module": "ES2022", | 		"module": "ES2022", | ||||||
| 		"moduleResolution": "node", | 		"moduleResolution": "node", | ||||||
| 		"types": ["bun-types"], | 		"types": [ | ||||||
|  | 			"bun-types" | ||||||
|  | 		], | ||||||
| 		"esModuleInterop": true, | 		"esModuleInterop": true, | ||||||
| 		"forceConsistentCasingInFileNames": true, | 		"forceConsistentCasingInFileNames": true, | ||||||
| 		"strict": true, | 		"strict": true, | ||||||
| 		"skipLibCheck": true, | 		"skipLibCheck": true, | ||||||
| 		"noErrorTruncation": true | 		"noErrorTruncation": true, | ||||||
|  | 		"baseUrl": ".", | ||||||
|  | 		"paths": { | ||||||
|  | 			"~/*": [ | ||||||
|  | 				"./src/*" | ||||||
|  | 			] | ||||||
|  | 		} | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user