From a46b465409c1cc695e1bda57456afbfa7500abeb Mon Sep 17 00:00:00 2001 From: Rafi Arrafif Date: Thu, 2 Jul 2026 22:40:33 +0700 Subject: [PATCH] wip: reconstruct bulk insert video service --- prisma/dbml/schema.dbml | 15 +++-- prisma/schema.prisma | 4 +- .../updateAllEpisodeThumbnail.controller.ts | 25 --------- src/modules/internal/index.ts | 43 +++++++-------- .../findEpisodeWithMediaId.repository.ts | 28 ---------- .../services/http/bulkInsertVideo.service.ts | 55 ++++++++----------- .../http/updateAllEpisodeThumbnail.service.ts | 40 -------------- ...etAllVideoServiceWithEpisode.repository.ts | 44 --------------- 8 files changed, 55 insertions(+), 199 deletions(-) delete mode 100644 src/modules/internal/controllers/updateAllEpisodeThumbnail.controller.ts delete mode 100644 src/modules/internal/repositories/findEpisodeWithMediaId.repository.ts delete mode 100644 src/modules/internal/services/http/updateAllEpisodeThumbnail.service.ts delete mode 100644 src/modules/videoService/repositories/GET/getAllVideoServiceWithEpisode.repository.ts diff --git a/prisma/dbml/schema.dbml b/prisma/dbml/schema.dbml index ff9e59a..c65ea85 100644 --- a/prisma/dbml/schema.dbml +++ b/prisma/dbml/schema.dbml @@ -538,8 +538,9 @@ Table episodes { Table videos { id String [pk] - service video_services [not null] + video_service video_services [not null] episode episodes [not null] + priority Int video_code String [not null] short_code String thumbnail_code String @@ -551,6 +552,11 @@ Table videos { media_id String [not null] created_by_id String [not null] video_submission video_submissions + video_service_id String [not null] + + indexes { + (media_id, episode_number, priority) [unique] + } } Table video_submissions { @@ -695,11 +701,6 @@ Table home_media_banners { created_by_id String [not null] } -Table VideoToVideoService { - serviceId String [ref: > video_services.id] - videosId String [ref: > videos.id] -} - Enum user_role { user contributor @@ -887,6 +888,8 @@ Ref: episodes.created_by_id > users.id Ref: episodes.media_id > medias.id +Ref: videos.video_service_id > video_services.id + Ref: videos.(episode_number, media_id) > episodes.(episode_number, media_id) Ref: video_submissions.created_by_id > users.id diff --git a/prisma/schema.prisma b/prisma/schema.prisma index f4bffa2..8d6b42b 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -690,7 +690,7 @@ model Episode { model Video { id String @id @default(uuid(7)) @db.Uuid - video_service VideoService @relation(fields: [videoServiceId], references: [id]) + video_service VideoService @relation(fields: [video_service_id], references: [id]) episode Episode @relation(fields: [episode_number, media_id], references: [episode_number, media_id]) priority Int? @db.SmallInt video_code String @db.VarChar(255) @@ -705,7 +705,7 @@ model Video { media_id String @db.Uuid created_by_id String @db.Uuid video_submission VideoSubmission? - videoServiceId String @db.Uuid + video_service_id String @db.Uuid @@unique([media_id, episode_number, priority]) @@map("videos") diff --git a/src/modules/internal/controllers/updateAllEpisodeThumbnail.controller.ts b/src/modules/internal/controllers/updateAllEpisodeThumbnail.controller.ts deleted file mode 100644 index 3d5d387..0000000 --- a/src/modules/internal/controllers/updateAllEpisodeThumbnail.controller.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { Context, Static } from "elysia"; -import { mainErrorHandler } from "../../../helpers/error/handler"; -import { returnWriteResponse } from "../../../helpers/callback/httpResponse"; -import { updateAllEpisodeThumbnailService } from "../services/http/updateAllEpisodeThumbnail.service"; -import { updateAllEpisodeThumbnailSchema } from "../schemas/updateAllEpisodeThumbnail.schema"; - -/** - * Updating all episode thumbnails for a specific target service reference ID. - * - * This controller handles the bulk update of episode thumbnails for all episodes associated with a specific service reference ID. - * It fetches the latest thumbnail data from external sources and updates the existing episode records in the database accordingly. - * - * See OpenAPI documentation for request/response schema. - */ -export const updateAllEpisodeThumbnailController = async (ctx: { - set: Context["set"]; - body: Static; -}) => { - try { - const newEpisodeThumbnailsCount = await updateAllEpisodeThumbnailService(ctx.body.service_reference_id); - return returnWriteResponse(ctx.set, 204, `Updating ${newEpisodeThumbnailsCount} episode thumbnails successfully.`); - } catch (error) { - return mainErrorHandler(ctx.set, error); - } -}; diff --git a/src/modules/internal/index.ts b/src/modules/internal/index.ts index 2a6810b..bf94a66 100644 --- a/src/modules/internal/index.ts +++ b/src/modules/internal/index.ts @@ -1,27 +1,24 @@ import Elysia from "elysia"; -import { bulkInsertEpisodeController } from "./controllers/bulkInsertEpisode.controller"; -import { bulkInsertMediaController } from "./controllers/bulkInsertMedia.controller"; -import { createVideoServiceInternalController } from "./controllers/createVideoService.controller"; -import { bulkInsertVideoController } from "./controllers/bulkInsertVideo.controller"; -import { updateAllEpisodeThumbnailController } from "./controllers/updateAllEpisodeThumbnail.controller"; -import { purgeUnusedSessionController } from "./controllers/purgeUnusedSession.controller"; -import { createHeroBannerController } from "./controllers/createHeroBanner.controller"; -import { bulkInsertMediaSchema } from "./schemas/bulkInsertMedia.schema"; -import { bulkInsertEpisodeSchema } from "./schemas/bulkInsertEpisode.schema"; -import { updateAllEpisodeThumbnailSchema } from "./schemas/updateAllEpisodeThumbnail.schema"; -import { bulkInsertVideoSchema } from "./schemas/bulkInsertVideo.schema"; -import { createVideoServiceInternalSchema } from "./schemas/createVideoServiceInternal.schema"; -import { purgeUnusedSessionSchema } from "./schemas/purgeUnusedSession.schema"; -import { createHeroBannerSchema } from "./schemas/createHeroBanner.schema"; +import {bulkInsertEpisodeController} from "./controllers/bulkInsertEpisode.controller"; +import {bulkInsertMediaController} from "./controllers/bulkInsertMedia.controller"; +import {createVideoServiceInternalController} from "./controllers/createVideoService.controller"; +import {bulkInsertVideoController} from "./controllers/bulkInsertVideo.controller"; +import {purgeUnusedSessionController} from "./controllers/purgeUnusedSession.controller"; +import {createHeroBannerController} from "./controllers/createHeroBanner.controller"; +import {bulkInsertMediaSchema} from "./schemas/bulkInsertMedia.schema"; +import {bulkInsertEpisodeSchema} from "./schemas/bulkInsertEpisode.schema"; +import {bulkInsertVideoSchema} from "./schemas/bulkInsertVideo.schema"; +import {createVideoServiceInternalSchema} from "./schemas/createVideoServiceInternal.schema"; +import {purgeUnusedSessionSchema} from "./schemas/purgeUnusedSession.schema"; +import {createHeroBannerSchema} from "./schemas/createHeroBanner.schema"; export const internalModule = new Elysia({ - prefix: "/internal", - tags: ["Internal"], + prefix: "/internal", + tags: ["Internal"], }) - .post("/media/bulk-insert", bulkInsertMediaController, bulkInsertMediaSchema) - .post("/episode/bulk-insert", bulkInsertEpisodeController, bulkInsertEpisodeSchema) - .put("/episode/update-thumbnails", updateAllEpisodeThumbnailController, updateAllEpisodeThumbnailSchema) - .post("/video/bulk-insert", bulkInsertVideoController, bulkInsertVideoSchema) - .post("/video-service", createVideoServiceInternalController, createVideoServiceInternalSchema) - .post("/user-session/purge-unused", purgeUnusedSessionController, purgeUnusedSessionSchema) - .post("/hero-banner", createHeroBannerController, createHeroBannerSchema); + .post("/media/bulk-insert", bulkInsertMediaController, bulkInsertMediaSchema) + .post("/episode/bulk-insert", bulkInsertEpisodeController, bulkInsertEpisodeSchema) + .post("/video/bulk-insert", bulkInsertVideoController, bulkInsertVideoSchema) + .post("/video-service", createVideoServiceInternalController, createVideoServiceInternalSchema) + .post("/user-session/purge-unused", purgeUnusedSessionController, purgeUnusedSessionSchema) + .post("/hero-banner", createHeroBannerController, createHeroBannerSchema); diff --git a/src/modules/internal/repositories/findEpisodeWithMediaId.repository.ts b/src/modules/internal/repositories/findEpisodeWithMediaId.repository.ts deleted file mode 100644 index 0817e06..0000000 --- a/src/modules/internal/repositories/findEpisodeWithMediaId.repository.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { AppError } from "../../../helpers/error/instances/app"; -import { prisma } from "../../../utils/databases/prisma/connection"; - -export const findEpisodeWithMediaIdRepository = async ({ - media, - episode, -}: { - media: string; - episode: number; -}) => { - try { - const foundEpisode = await prisma.episode.findUnique({ - where: { - mediaId_episode: { - mediaId: media, - episode: episode, - }, - }, - select: { - id: true, - }, - }); - if (!foundEpisode) throw new AppError(404, "Episode not found"); - return foundEpisode; - } catch (error) { - throw new AppError(500, "Error finding episode with media id", error); - } -}; diff --git a/src/modules/internal/services/http/bulkInsertVideo.service.ts b/src/modules/internal/services/http/bulkInsertVideo.service.ts index 2c206f4..f13260f 100644 --- a/src/modules/internal/services/http/bulkInsertVideo.service.ts +++ b/src/modules/internal/services/http/bulkInsertVideo.service.ts @@ -1,35 +1,28 @@ -import { SystemAccountId } from "../../../../config/account/system"; -import { ErrorForwarder } from "../../../../helpers/error/instances/forwarder"; -import { findEpisodeWithMediaIdRepository } from "../../repositories/findEpisodeWithMediaId.repository"; -import { bulkInsertVideoRepository } from "../../repositories/bulkInsertVideo.repository"; -import { Static } from "elysia"; -import { bulkInsertVideoSchema } from "../../schemas/bulkInsertVideo.schema"; +import {SystemAccountId} from "../../../../config/account/system"; +import {ErrorForwarder} from "../../../../helpers/error/instances/forwarder"; +import {bulkInsertVideoRepository} from "../../repositories/bulkInsertVideo.repository"; +import {Static} from "elysia"; +import {bulkInsertVideoSchema} from "../../schemas/bulkInsertVideo.schema"; +import {Prisma} from "@prisma/client"; export const bulkInsertVideoService = async (body: Static) => { - try { - const insertedVideos: string[] = []; - for (const episodeData of body.data) { - const episodeId = await findEpisodeWithMediaIdRepository({ - media: body.media_id, - episode: episodeData.episode, - }); - - for (const videoData of episodeData.videos) { - const insertedVideo = await bulkInsertVideoRepository({ - pendingUpload: false, - episodeId: episodeId.id, - serviceId: videoData.service_id, - videoCode: videoData.video_code, - thumbnailCode: videoData.thumbnail_code, - uploadedBy: SystemAccountId, - }); - - insertedVideos.push(insertedVideo.id); - } + try { + const constructedInput: Prisma.VideoCreateManyInput[] = body.data.flatMap((d) => ( + d.videos.flatMap((v) => ( + { + created_by_id: SystemAccountId, + media_id: body.media_id, + episode_number: d.episode, + video_service_id: v.service_id, + video_code: v.video_code, + short_code: v.short_code, + thumbnail_code: v.thumbnail_code, + download_code: v.download_code + } + )) + )); + return constructedInput + } catch (error) { + ErrorForwarder(error); } - - return insertedVideos; - } catch (error) { - ErrorForwarder(error); - } }; diff --git a/src/modules/internal/services/http/updateAllEpisodeThumbnail.service.ts b/src/modules/internal/services/http/updateAllEpisodeThumbnail.service.ts deleted file mode 100644 index 43bc55b..0000000 --- a/src/modules/internal/services/http/updateAllEpisodeThumbnail.service.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { AppError } from "../../../../helpers/error/instances/app"; -import { ErrorForwarder } from "../../../../helpers/error/instances/forwarder"; -import { bulkUpdateThumbnailRepository } from "../../../episode/repositories/PUT/bulkUpdateThumbnail.repository"; -import { getAllVideoServiceWithEpisodeRepository } from "../../../videoService/repositories/GET/getAllVideoServiceWithEpisode.repository"; - -export const updateAllEpisodeThumbnailService = async ( - serviceReferenceId?: string, -) => { - try { - if (!serviceReferenceId) - throw new AppError(400, "Service Reference ID is required."); - - const videosData = await getAllVideoServiceWithEpisodeRepository( - serviceReferenceId, - ); - - if (!videosData || videosData.length === 0) - throw new AppError( - 404, - "No episode with no thumbnail found in the specified video service.", - ); - - const updatePayload = videosData.flatMap((videoService) => { - const { endpointThumbnail, videos } = videoService; - return videos.map((video) => ({ - episodeId: video.episode.id, - thumbnailCode: endpointThumbnail!.replace( - ":code:", - video.thumbnailCode || video.videoCode, - ), - })); - }); - - await bulkUpdateThumbnailRepository(updatePayload); - - return updatePayload.length; - } catch (error) { - ErrorForwarder(error); - } -}; diff --git a/src/modules/videoService/repositories/GET/getAllVideoServiceWithEpisode.repository.ts b/src/modules/videoService/repositories/GET/getAllVideoServiceWithEpisode.repository.ts deleted file mode 100644 index 89fea43..0000000 --- a/src/modules/videoService/repositories/GET/getAllVideoServiceWithEpisode.repository.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { AppError } from "../../../../helpers/error/instances/app"; -import { videoServiceModel } from "../../model"; - -export const getAllVideoServiceWithEpisodeRepository = async ( - videoServiceId: string, -) => { - try { - return await videoServiceModel.findMany({ - where: { - id: videoServiceId, - endpointThumbnail: { - not: null, - }, - videos: { - some: { - episode: { - pictureThumbnail: null, - }, - }, - }, - }, - select: { - endpointThumbnail: true, - videos: { - select: { - thumbnailCode: true, - videoCode: true, - episode: { - select: { - id: true, - }, - }, - }, - }, - }, - }); - } catch (error) { - throw new AppError( - 500, - "An error occurred while fetching video services with episodes.", - error, - ); - } -};