diff --git a/prisma/dbml/schema.dbml b/prisma/dbml/schema.dbml index def2224..230d00c 100644 --- a/prisma/dbml/schema.dbml +++ b/prisma/dbml/schema.dbml @@ -252,8 +252,6 @@ Table medias { age_rating_id String country_id String related_media media_relations [not null] - approver_id String - created_by_id String updated_by_id String deleted_by_id String home_media_banners home_media_banners [not null] @@ -296,7 +294,7 @@ Table media_collections { Table media_trailers { media_id String [pk] url String - embed_url String + embed_url String [unique] small_image_url String large_image_url String maximum_image_url String @@ -661,7 +659,7 @@ Table countries { id String [pk] name String [not null] slug String [not null] - code String [not null] + code String [unique, not null] flag String banner String UserCountry users [not null] diff --git a/prisma/migrations/20260530045455_make_embed_url_unique/migration.sql b/prisma/migrations/20260530045455_make_embed_url_unique/migration.sql new file mode 100644 index 0000000..a9637c5 --- /dev/null +++ b/prisma/migrations/20260530045455_make_embed_url_unique/migration.sql @@ -0,0 +1,8 @@ +/* + Warnings: + + - A unique constraint covering the columns `[embed_url]` on the table `media_trailers` will be added. If there are existing duplicate values, this will fail. + +*/ +-- CreateIndex +CREATE UNIQUE INDEX "media_trailers_embed_url_key" ON "media_trailers"("embed_url"); diff --git a/prisma/migrations/20260530050918_make_cc_as_unique/migration.sql b/prisma/migrations/20260530050918_make_cc_as_unique/migration.sql new file mode 100644 index 0000000..d6056a2 --- /dev/null +++ b/prisma/migrations/20260530050918_make_cc_as_unique/migration.sql @@ -0,0 +1,8 @@ +/* + Warnings: + + - A unique constraint covering the columns `[code]` on the table `countries` will be added. If there are existing duplicate values, this will fail. + +*/ +-- CreateIndex +CREATE UNIQUE INDEX "countries_code_key" ON "countries"("code"); diff --git a/prisma/migrations/20260531041413_remove_unused_media_column/migration.sql b/prisma/migrations/20260531041413_remove_unused_media_column/migration.sql new file mode 100644 index 0000000..07d2b5f --- /dev/null +++ b/prisma/migrations/20260531041413_remove_unused_media_column/migration.sql @@ -0,0 +1,10 @@ +/* + Warnings: + + - You are about to drop the column `approver_id` on the `medias` table. All the data in the column will be lost. + - You are about to drop the column `created_by_id` on the `medias` table. All the data in the column will be lost. + +*/ +-- AlterTable +ALTER TABLE "medias" DROP COLUMN "approver_id", +DROP COLUMN "created_by_id"; diff --git a/prisma/schema.prisma b/prisma/schema.prisma index cdcde98..becd916 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -366,8 +366,6 @@ model Media { age_rating_id String? @db.Uuid country_id String? @db.Uuid related_media MediaRelation[] @relation("MediaRelationRelatedMedia") - approver_id String? @db.Uuid - created_by_id String? @db.Uuid updated_by_id String? @db.Uuid deleted_by_id String? @db.Uuid home_media_banners HomeMediaBanner[] @@ -407,7 +405,7 @@ model MediaCollection { model MediaTrailer { media_id String @id @db.Uuid url String? @db.VarChar(255) - embed_url String? @db.VarChar(255) + embed_url String? @db.VarChar(255) @unique small_image_url String? @db.VarChar(255) large_image_url String? @db.VarChar(255) maximum_image_url String? @db.VarChar(255) @@ -802,7 +800,7 @@ model Country { id String @id @db.Uuid @default(uuid(7)) name String @db.VarChar(155) slug String @db.VarChar(165) - code String @db.VarChar(3) + code String @db.VarChar(3) @unique flag String? @db.VarChar(255) banner String? @db.VarChar(255) diff --git a/src/modules/internal/repositories/bulkinsertMedia.repository.ts b/src/modules/internal/repositories/bulkinsertMedia.repository.ts index 35b9063..5fe7b54 100644 --- a/src/modules/internal/repositories/bulkinsertMedia.repository.ts +++ b/src/modules/internal/repositories/bulkinsertMedia.repository.ts @@ -2,6 +2,8 @@ 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"; +import { MediaFullInfoResponse } from "../types/mediaFullInfo.type"; +import { SystemAccountId } from "../../../config/account/system"; /** * Media Payload Construction and Upsert @@ -17,21 +19,100 @@ import { generateUUIDv7 } from "../../../helpers/databases/uuidv7"; * @param data - The full media data for constructing the media payload. * @returns The inserted or updated media record. */ -export const InsertMediaRepository = async ({ - malId, - payload, -}: { - malId: number; - payload: Omit; -}) => { +export const InsertMediaRepository = async ({ payload }: { payload: MediaFullInfoResponse["data"] }) => { try { - return await prisma.media.upsert({ - where: { malId }, - update: payload, - create: { - id: generateUUIDv7(), - ...payload, + const constructMediaPayload: Prisma.MediaUpsertArgs["create"] = { + mal_id: payload.mal_id, + title: payload.title, + title_secondary: payload.title_english, + title_original: payload.title_japanese, + title_synonyms: payload.title_synonyms, + trailer: { + connectOrCreate: { + where: { + embed_url: payload.trailer.embed_url, + }, + create: { + embed_url: payload.trailer.embed_url, + url: payload.trailer.url, + small_image_url: payload.trailer.images.small_image_url, + large_image_url: payload.trailer.images.large_image_url, + maximum_image_url: payload.trailer.images.maximum_image_url, + }, + }, }, + synopsis: payload.synopsis, + small_image_url: payload.images.jpg.small_image_url, + medium_image_url: payload.images.jpg.image_url, + large_image_url: payload.images.jpg.large_image_url, + type: { + connectOrCreate: { + where: { + name: payload.type.toLowerCase(), + }, + create: { + name: payload.type.toLowerCase(), + }, + }, + }, + source: { + connectOrCreate: { + where: { + name: payload.source.toLowerCase(), + }, + create: { + name: payload.source.toLowerCase(), + }, + }, + }, + status: { + connectOrCreate: { + where: { + name: payload.status.toLowerCase(), + }, + create: { + name: payload.status.toLowerCase(), + }, + }, + }, + airing: payload.airing, + start_airing: payload.aired.from, + end_airing: payload.aired.to, + age_rating: { + connectOrCreate: { + where: { + name: payload.rating.toLowerCase(), + }, + create: { + name: payload.rating.toLowerCase(), + min_age: 0, // Placeholder, as the actual age rating details may require additional mapping + }, + }, + }, + score_total: 0, + score_count: 0, + background: payload.background, + season: payload.season, + year: payload.year, + country: { + connectOrCreate: { + where: { + code: "jpn", + }, + create: { + name: "japan", + slug: "japan", + code: "jpn", + }, + }, + }, + broadcast_day: payload.broadcast.day, + }; + + return await prisma.media.upsert({ + where: { mal_id: payload.mal_id }, + create: constructMediaPayload, + update: constructMediaPayload, }); } catch (error) { throw new AppError(500, "Failed to insert media", error); diff --git a/src/modules/internal/services/http/bulkInsertAnime.service.ts b/src/modules/internal/services/http/bulkInsertAnime.service.ts index 45f97ce..cf986a1 100644 --- a/src/modules/internal/services/http/bulkInsertAnime.service.ts +++ b/src/modules/internal/services/http/bulkInsertAnime.service.ts @@ -1,61 +1,15 @@ -import { Prisma } from "@prisma/client"; import { ErrorForwarder } from "../../../../helpers/error/instances/forwarder"; -import { bulkInsertGenresRepository } from "../../repositories/bulkInsertGenres.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"; -import { bulkInsertCharWithVAService } from "../internal/bulkInsertCharWithVA.service"; -import { generateUUIDv7 } from "../../../../helpers/databases/uuidv7"; -import { SystemAccountId } from "../../../../config/account/system"; import { getContentReferenceAPI } from "../../../../config/apis/jikan/media.reference"; export const bulkInsertAnimeService = async (malId: number) => { try { const { baseURL, getMediaFullInfo } = getContentReferenceAPI(malId); - const mediaFullInfo = (await fetch(baseURL + getMediaFullInfo).then((res) => - res.json(), - )) as MediaFullInfoResponse; + const mediaFullInfo = (await fetch(baseURL + getMediaFullInfo).then((res) => res.json())) as MediaFullInfoResponse; - const insertedGenres = await bulkInsertGenresRepository(mediaFullInfo); - const insertedStudios = await bulkInsertStudiosRepository(mediaFullInfo); - const insertedCharacters = await bulkInsertCharWithVAService(malId); - - const constructMediaPayload: Prisma.MediaUpsertArgs["create"] = { - id: generateUUIDv7(), - title: mediaFullInfo.data.title, - titleAlternative: mediaFullInfo.data - .titles as unknown as Prisma.InputJsonValue, - slug: await generateSlug(mediaFullInfo.data.title, { - model: "media", - target: "slug", - }), - malId: mediaFullInfo.data.mal_id, - genres: { - connect: insertedGenres.map((id) => ({ id })), - }, - studios: { - connect: insertedStudios.map((id) => ({ id })), - }, - characters: { - connect: insertedCharacters.map(({ id }) => ({ id })), - }, - score: mediaFullInfo.data.score, - pictureMedium: mediaFullInfo.data.images.webp.image_url, - pictureLarge: mediaFullInfo.data.images.webp.large_image_url, - status: mediaFullInfo.data.status, - startAiring: mediaFullInfo.data.aired.from, - endAiring: mediaFullInfo.data.aired.to, - synopsis: mediaFullInfo.data.synopsis, - ageRating: mediaFullInfo.data.rating, - mediaType: mediaFullInfo.data.type, - source: mediaFullInfo.data.source, - onDraft: false, - uploadedBy: SystemAccountId, - }; const insertedMedia = await InsertMediaRepository({ - malId: mediaFullInfo.data.mal_id, - payload: constructMediaPayload, + payload: mediaFullInfo.data, }); return insertedMedia; diff --git a/src/modules/internal/services/http/bulkInsertProducer.service.ts b/src/modules/internal/services/http/bulkInsertProducer.service.ts new file mode 100644 index 0000000..7a80058 --- /dev/null +++ b/src/modules/internal/services/http/bulkInsertProducer.service.ts @@ -0,0 +1,28 @@ +import { Prisma } from "@prisma/client"; +import { MediaFullInfoResponse } from "../../types/mediaFullInfo.type"; +import { prisma } from "../../../../utils/databases/prisma/connection"; + +interface InsertedProducer { + producer: string[]; + licensor: string[]; + studio: string[]; +} + +export const bulkInsertProducerService = async (payload: MediaFullInfoResponse, systemAccountId: string) => { + const insertedPayload: InsertedProducer = { + producer: [], + licensor: [], + studio: [], + }; + + const insertingMainProducer = await prisma.producer.createMany({ + data: payload.data.producers.map((producer) => ({ + mal_id: producer.mal_id, + type: producer.type, + name: producer.name, + url: producer.url, + created_by_id: systemAccountId, + })), + skipDuplicates: true, + }); +}; diff --git a/src/modules/internal/types/mediaFullInfo.type.ts b/src/modules/internal/types/mediaFullInfo.type.ts index a31da2b..0725a14 100644 --- a/src/modules/internal/types/mediaFullInfo.type.ts +++ b/src/modules/internal/types/mediaFullInfo.type.ts @@ -1,4 +1,4 @@ -import { MediaType } from "@prisma/client"; +import { media_season } from "@prisma/client"; export interface MediaFullInfoResponse { data: Data; } @@ -14,7 +14,7 @@ interface Data { title_english: string; title_japanese: string; title_synonyms: string[]; - type: MediaType; + type: string; source: string; episodes: number; status: string; @@ -30,7 +30,7 @@ interface Data { favorites: number; synopsis: string; background: string; - season: string; + season: media_season; year: number; broadcast: Broadcast; producers: Genre[];