🚧 wip: try to create character with VA in bulk

This commit is contained in:
Rafi Arrafif
2026-01-24 22:02:34 +07:00
parent 7d26ca7f7b
commit fe10412f1a
9 changed files with 153 additions and 13 deletions

View File

@ -77,6 +77,37 @@ Table studios {
medias medias [not null]
}
Table characters {
id String [pk]
malId Int [unique, not null]
name String [not null]
role CharacterRole [not null]
favorites Int [not null, default: 0]
imageUrl String
smallImageUrl String
createdBy users [not null]
creatorId String [not null]
deletedAt DateTime
createdAt DateTime [default: `now()`, not null]
updatedAt DateTime [default: `now()`, not null]
}
Table voice_actors {
id String [pk]
malId Int [unique, not null]
name String [not null]
birthday DateTime
description String [not null]
aboutUrl String [not null]
imageUrl String
websiteUrl String
createdBy users [not null]
creatorId String [not null]
deletedAt DateTime
createdAt DateTime [default: `now()`, not null]
updatedAt DateTime [default: `now()`, not null]
}
Table episodes {
id String [pk]
media medias [not null]
@ -172,6 +203,8 @@ Table users {
media_approveds media_logs [not null]
genres genres [not null]
studios studios [not null]
characters characters [not null]
voice_actor voice_actors [not null]
episodes episodes [not null]
episode_likes episode_likes [not null]
videos videos [not null]
@ -501,6 +534,11 @@ Enum Country {
KR
}
Enum CharacterRole {
Main
Supporting
}
Enum MediaOperation {
create
update
@ -605,6 +643,10 @@ Ref: genres.createdBy > users.id
Ref: studios.createdBy > users.id
Ref: characters.creatorId > users.id
Ref: voice_actors.creatorId > users.id
Ref: episodes.mediaId > medias.id
Ref: episodes.uploadedBy > users.id

View File

@ -112,6 +112,7 @@ model Studio {
model Character {
id String @id @default(uuid()) @db.Uuid
malId Int @unique
name String
role CharacterRole
favorites Int @default(0)
@ -128,7 +129,7 @@ model Character {
model VoiceActor {
id String @id @default(uuid()) @db.Uuid
malId Int
malId Int @unique
name String
birthday DateTime?
description String @db.Text

View File

@ -2,5 +2,6 @@ export const getContentReferenceAPI = (malId: number) => {
return {
baseURL: "https://api.jikan.moe/v4",
getMediaFullInfo: `/anime/${malId}/full`,
getMediaCharactersWithVA: `/anime/${malId}/characters`,
};
};

View File

@ -2,12 +2,14 @@ import { Context } from "elysia";
import { mainErrorHandler } from "../../../helpers/error/handler";
import { bulkInsertAnimeService } from "../services/bulkInsertAnime.service";
import { returnWriteResponse } from "../../../helpers/callback/httpResponse";
import { bulkInsertCharWithVAService } from "../services/internal/bulkInsertCharWithVA.service";
export const bulkInsertAnimeController = async (
ctx: Context & { body: { mal_id: number } },
) => {
try {
const bulkInsertResult = await bulkInsertAnimeService(ctx.body.mal_id);
// const bulkInsertResult = await bulkInsertAnimeService(ctx.body.mal_id);
const bulkInsertResult = await bulkInsertCharWithVAService(ctx.body.mal_id);
return returnWriteResponse(
ctx.set,
201,

View File

@ -0,0 +1,19 @@
import { Prisma } from "@prisma/client";
import { AppError } from "../../../helpers/error/instances/app";
import { prisma } from "../../../utils/databases/prisma/connection";
export const bulkInsertVARepository = async (
payload: Prisma.VoiceActorUpsertArgs["create"],
) => {
try {
const insertedVA = await prisma.voiceActor.upsert({
where: { malId: payload.malId },
create: payload,
update: payload,
select: { id: true },
});
return insertedVA.id;
} catch (error) {
throw new AppError(500, "Failed to bulk insert VAs", error);
}
};

View File

@ -2,7 +2,7 @@ import { Prisma } from "@prisma/client";
import { getContentReferenceAPI } from "../../../config/apis/media.reference";
import { ErrorForwarder } from "../../../helpers/error/instances/forwarder";
import { bulkInsertGenresRepository } from "../repositories/bulkInsertGenres.repository";
import { InsertMediaRepository } from "../repositories/bulkinsertMediaa.repository";
import { InsertMediaRepository } from "../repositories/bulkinsertMedia.repository";
import { bulkInsertStudiosRepository } from "../repositories/bulkInsertStudios.repository";
import { MediaFullInfoResponse } from "../types/mediaFullInfo.type";
import { generateSlug } from "../../../helpers/characters/generateSlug";

View File

@ -0,0 +1,16 @@
import { getContentReferenceAPI } from "../../../../config/apis/media.reference";
import { ErrorForwarder } from "../../../../helpers/error/instances/forwarder";
import { MediaCharWithVAInfo } from "../../types/mediaCharWithVAInfo";
export const bulkInsertCharWithVAService = async (malId: number) => {
try {
const { baseURL, getMediaCharactersWithVA } = getContentReferenceAPI(malId);
const charactersWithVAData = (await fetch(
`${baseURL}${getMediaCharactersWithVA}`,
).then((res) => res.json())) as MediaCharWithVAInfo;
return;
} catch (error) {
ErrorForwarder(error);
}
};

View File

@ -0,0 +1,59 @@
export interface MediaCharWithVAInfo {
data: Datum[];
}
interface Datum {
character: Character;
role: Role;
favorites: number;
voice_actors: VoiceActor[];
}
interface Character {
mal_id: number;
url: string;
images: CharacterImages;
name: string;
}
interface CharacterImages {
jpg: Jpg;
webp: Webp;
}
interface Jpg {
image_url: string;
}
interface Webp {
image_url: string;
small_image_url: string;
}
enum Role {
Main = "Main",
Supporting = "Supporting",
}
interface VoiceActor {
person: Person;
language: Language;
}
enum Language {
English = "English",
Japanese = "Japanese",
PortugueseBR = "Portuguese (BR)",
Spanish = "Spanish",
}
interface Person {
mal_id: number;
url: string;
images: PersonImages;
name: string;
}
interface PersonImages {
jpg: Jpg;
}