Merge pull request #6 from rafiarrafif/feat/bulk-insert-video

Feat/bulk insert video
This commit is contained in:
Rafi Arrafif
2026-01-30 15:58:51 +07:00
committed by GitHub
10 changed files with 205 additions and 1 deletions

View File

@ -181,6 +181,10 @@ Table videos {
deletedAt DateTime
createdAt DateTime [default: `now()`, not null]
updatedAt DateTime [default: `now()`, not null]
indexes {
(serviceId, code) [unique]
}
}
Table video_services {
@ -191,6 +195,7 @@ Table video_services {
hexColor String [not null]
endpointVideo String [not null]
endpointThumbnail String
endpointDownload String
creator users [not null]
createdBy String [not null]
deletedAt DateTime

View File

@ -218,6 +218,8 @@ model Video {
deletedAt DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @default(now()) @updatedAt
@@unique([serviceId, code])
@@map("videos")
}
@ -229,6 +231,7 @@ model VideoService {
hexColor String @db.VarChar(10)
endpointVideo String @db.Text
endpointThumbnail String? @db.Text
endpointDownload String?
creator User @relation("UserVideoServices", fields: [createdBy], references: [id])
createdBy String @db.Uuid
deletedAt DateTime?

View File

@ -0,0 +1,26 @@
import { Context } from "elysia";
import { mainErrorHandler } from "../../../helpers/error/handler";
import { bulkInsertVideoService } from "../services/http/bulkInsertVideo.service";
import { returnWriteResponse } from "../../../helpers/callback/httpResponse";
export interface BulkInsertVideoBodyRequest {
media_id: string;
data: Array<{
episode: number;
videos: Array<{
service_id: string;
code: string;
}>;
}>;
}
export const bulkInsertVideoController = async (
ctx: Context & { body: BulkInsertVideoBodyRequest },
) => {
try {
const insertedVideos = await bulkInsertVideoService(ctx.body);
return returnWriteResponse(ctx.set, 201, "Videos inserted", insertedVideos);
} catch (error) {
throw mainErrorHandler(ctx.set, error);
}
};

View File

@ -0,0 +1,32 @@
import { Context } from "elysia";
import { mainErrorHandler } from "../../../helpers/error/handler";
import { returnWriteResponse } from "../../../helpers/callback/httpResponse";
import { createVideoServiceInternalService } from "../services/http/createVideoService.service";
export interface CreateVideoServiceBodyRequest {
name: string;
domain: string;
logo: string;
hexColor: string;
endpointVideo: string;
endpointThumbnail: string;
endpointDownload?: string;
}
export const createVideoServiceInternalController = async (
ctx: Context & { body: CreateVideoServiceBodyRequest },
) => {
try {
const createdVideoService = await createVideoServiceInternalService(
ctx.body,
);
return returnWriteResponse(
ctx.set,
201,
"Video service created",
createdVideoService,
);
} catch (error) {
throw mainErrorHandler(ctx.set, error);
}
};

View File

@ -1,7 +1,11 @@
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";
export const internalModule = new Elysia({ prefix: "/internal" })
.post("/media/bulk-insert", bulkInsertMediaController)
.post("/episode/bulk-insert", bulkInsertEpisodeController);
.post("/episode/bulk-insert", bulkInsertEpisodeController)
.post("/video/bulk-insert", bulkInsertVideoController)
.post("/video-service", createVideoServiceInternalController);

View File

@ -0,0 +1,26 @@
import { Prisma } from "@prisma/client";
import { AppError } from "../../../helpers/error/instances/app";
import { prisma } from "../../../utils/databases/prisma/connection";
import { generateUUIDv7 } from "../../../helpers/databases/uuidv7";
export const bulkInsertVideoRepository = async (
payload: Omit<Prisma.VideoUncheckedCreateInput, "id">,
) => {
try {
return await prisma.video.upsert({
where: {
serviceId_code: {
serviceId: payload.serviceId,
code: payload.code,
},
},
create: {
id: generateUUIDv7(),
...payload,
},
update: payload,
});
} catch (error) {
throw new AppError(500, "Error inserting video", error);
}
};

View File

@ -0,0 +1,23 @@
import { Prisma } from "@prisma/client";
import { AppError } from "../../../helpers/error/instances/app";
import { prisma } from "../../../utils/databases/prisma/connection";
import { generateUUIDv7 } from "../../../helpers/databases/uuidv7";
export const createVideoServiceInternalRepository = async (
payload: Omit<Prisma.VideoServiceUncheckedCreateInput, "id">,
) => {
try {
return await prisma.videoService.upsert({
where: {
name: payload.name,
},
create: {
id: generateUUIDv7(),
...payload,
},
update: payload,
});
} catch (error) {
throw new AppError(500, "Failed to create video service", error);
}
};

View File

@ -0,0 +1,28 @@
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);
}
};

View File

@ -0,0 +1,34 @@
import { SystemAccountId } from "../../../../config/account/system";
import { ErrorForwarder } from "../../../../helpers/error/instances/forwarder";
import { BulkInsertVideoBodyRequest } from "../../controllers/bulkInsertVideo.controller";
import { findEpisodeWithMediaIdRepository } from "../../repositories/findEpisodeWithMediaId.repository";
import { bulkInsertVideoRepository } from "../../repositories/bulkInsertVideo.repository";
export const bulkInsertVideoService = async (
body: BulkInsertVideoBodyRequest,
) => {
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({
episodeId: episodeId.id,
serviceId: videoData.service_id,
code: videoData.code,
uploadedBy: SystemAccountId,
});
insertedVideos.push(insertedVideo.id);
}
}
return insertedVideos;
} catch (error) {
ErrorForwarder(error);
}
};

View File

@ -0,0 +1,23 @@
import { SystemAccountId } from "../../../../config/account/system";
import { ErrorForwarder } from "../../../../helpers/error/instances/forwarder";
import { CreateVideoServiceBodyRequest } from "../../controllers/createVideoService.controller";
import { createVideoServiceInternalRepository } from "../../repositories/createVideoService.repository";
export const createVideoServiceInternalService = async (
body: CreateVideoServiceBodyRequest,
) => {
try {
return await createVideoServiceInternalRepository({
name: body.name,
domain: body.domain,
logo: body.logo,
hexColor: body.hexColor,
endpointVideo: body.endpointVideo,
endpointThumbnail: body.endpointThumbnail,
endpointDownload: body.endpointDownload,
createdBy: SystemAccountId,
});
} catch (error) {
ErrorForwarder(error);
}
};