🚧 wip: create bulk insert endpoint
This commit is contained in:
3
bun.lock
3
bun.lock
@ -24,6 +24,7 @@
|
||||
"mock-aws-s3": "^4.0.2",
|
||||
"nock": "^14.0.4",
|
||||
"pg": "^8.17.1",
|
||||
"slugify": "^1.6.6",
|
||||
"zod": "^4.0.5",
|
||||
},
|
||||
"devDependencies": {
|
||||
@ -1150,6 +1151,8 @@
|
||||
|
||||
"slice-ansi": ["slice-ansi@1.0.0", "", { "dependencies": { "is-fullwidth-code-point": "^2.0.0" } }, "sha512-POqxBK6Lb3q6s047D/XsDVNPnF9Dl8JSaqe9h9lURl0OdNqy/ujDrOiIHtsqXMGbWWTIomRzAMaTyawAU//Reg=="],
|
||||
|
||||
"slugify": ["slugify@1.6.6", "", {}, "sha512-h+z7HKHYXj6wJU+AnS/+IH8Uh9fdcX1Lrhg1/VMdf9PwoBQXFcXiAdsy2tSK0P6gKwJLXp02r90ahUCqHk9rrw=="],
|
||||
|
||||
"spdx-correct": ["spdx-correct@3.2.0", "", { "dependencies": { "spdx-expression-parse": "^3.0.0", "spdx-license-ids": "^3.0.0" } }, "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA=="],
|
||||
|
||||
"spdx-exceptions": ["spdx-exceptions@2.5.0", "", {}, "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w=="],
|
||||
|
||||
@ -9,6 +9,7 @@
|
||||
"lint": "bunx eslint",
|
||||
"commit": "bun x git-cz",
|
||||
"push": "bun scripts/git-multipush.ts",
|
||||
"prisma:push": "bunx prisma db push",
|
||||
"prisma:generate": "bunx prisma generate",
|
||||
"prisma:dbml": "bunx prisma db pull && bunx prisma dbml --output ./prisma/dbml/schema.dbml",
|
||||
"prisma:seed": "bun run ./prisma/seed/index.ts",
|
||||
@ -35,6 +36,7 @@
|
||||
"mock-aws-s3": "^4.0.2",
|
||||
"nock": "^14.0.4",
|
||||
"pg": "^8.17.1",
|
||||
"slugify": "^1.6.6",
|
||||
"zod": "^4.0.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@ -6,23 +6,22 @@ Table medias {
|
||||
id String [pk]
|
||||
title String [not null]
|
||||
titleAlternative Json [not null]
|
||||
slug String [not null]
|
||||
slug String [unique, not null]
|
||||
malId Int [unique]
|
||||
pictureMedium String [not null]
|
||||
pictureLarge String [not null]
|
||||
genres genres [not null]
|
||||
country countries [not null]
|
||||
countryId String [not null]
|
||||
isAiring Boolean [not null, default: false]
|
||||
isTba Boolean [not null, default: false]
|
||||
startAiring DateTime [not null]
|
||||
endAiring DateTime [not null]
|
||||
country Country [not null, default: 'JP']
|
||||
score Decimal [not null, default: 0]
|
||||
status String [not null]
|
||||
startAiring DateTime
|
||||
endAiring DateTime
|
||||
synopsis String [not null]
|
||||
nfsw Boolean [not null, default: false]
|
||||
ageRating AgeRating [not null]
|
||||
ageRating String [not null]
|
||||
mediaType MediaType [not null]
|
||||
source Source [not null]
|
||||
source String
|
||||
studios studios [not null]
|
||||
pendingUpload Boolean [not null, default: true]
|
||||
onDraft Boolean [not null, default: true]
|
||||
uploader users [not null]
|
||||
uploadedBy String [not null]
|
||||
deletedAt DateTime
|
||||
@ -51,7 +50,7 @@ Table media_logs {
|
||||
Table genres {
|
||||
id String [pk]
|
||||
name String [not null]
|
||||
slug String [not null]
|
||||
slug String [unique, not null]
|
||||
malId Int [not null]
|
||||
malUrl String [not null]
|
||||
creator users [not null]
|
||||
@ -66,9 +65,10 @@ Table genres {
|
||||
Table studios {
|
||||
id String [pk]
|
||||
name String [not null]
|
||||
slug String [not null]
|
||||
logoUrl String [not null]
|
||||
colorHex String [not null]
|
||||
slug String [unique, not null]
|
||||
linkAbout String [not null]
|
||||
malId Int [not null]
|
||||
logoUrl String
|
||||
creator users [not null]
|
||||
createdBy String [not null]
|
||||
deletedAt DateTime
|
||||
@ -77,20 +77,6 @@ Table studios {
|
||||
medias medias [not null]
|
||||
}
|
||||
|
||||
Table countries {
|
||||
id String [pk]
|
||||
name String [not null]
|
||||
code String [not null]
|
||||
flag String [not null]
|
||||
creator users [not null]
|
||||
createdBy String [not null]
|
||||
deletedAt DateTime
|
||||
createdAt DateTime [default: `now()`, not null]
|
||||
updatedAt DateTime [default: `now()`, not null]
|
||||
medias medias [not null]
|
||||
user_show_countries user_preferences [not null]
|
||||
}
|
||||
|
||||
Table episodes {
|
||||
id String [pk]
|
||||
media medias [not null]
|
||||
@ -186,7 +172,6 @@ Table users {
|
||||
media_approveds media_logs [not null]
|
||||
genres genres [not null]
|
||||
studios studios [not null]
|
||||
countries countries [not null]
|
||||
episodes episodes [not null]
|
||||
episode_likes episode_likes [not null]
|
||||
videos videos [not null]
|
||||
@ -221,7 +206,7 @@ Table user_preferences {
|
||||
videoQuality VideoQuality [not null, default: 'Q1080']
|
||||
serviceDefault video_services
|
||||
serviceDefaultId String
|
||||
showContries countries [not null]
|
||||
hideContries Country[] [not null]
|
||||
favoriteGenres genres [not null]
|
||||
createdAt DateTime [default: `now()`, not null]
|
||||
updatedAt DateTime [default: `now()`, not null]
|
||||
@ -495,25 +480,11 @@ Table UserFavoriteGenres {
|
||||
favoritegenresId String [ref: > genres.id]
|
||||
}
|
||||
|
||||
Table UserShowContries {
|
||||
user_show_countriesId String [ref: > user_preferences.id]
|
||||
showcontriesId String [ref: > countries.id]
|
||||
}
|
||||
|
||||
Table UserSelectedSharingCollention {
|
||||
allowed_collectionsId String [ref: > collections.id]
|
||||
usersallowedId String [ref: > users.id]
|
||||
}
|
||||
|
||||
Enum AgeRating {
|
||||
G
|
||||
PG
|
||||
PG_13
|
||||
R
|
||||
R_plus
|
||||
Rx
|
||||
}
|
||||
|
||||
Enum MediaType {
|
||||
TV
|
||||
ONA
|
||||
@ -523,11 +494,11 @@ Enum MediaType {
|
||||
Music
|
||||
}
|
||||
|
||||
Enum Source {
|
||||
original
|
||||
manga
|
||||
light_novel
|
||||
game
|
||||
Enum Country {
|
||||
JP
|
||||
EN
|
||||
ID
|
||||
KR
|
||||
}
|
||||
|
||||
Enum MediaOperation {
|
||||
@ -622,8 +593,6 @@ Enum TypeSystemNotification {
|
||||
toast
|
||||
}
|
||||
|
||||
Ref: medias.countryId > countries.id
|
||||
|
||||
Ref: medias.uploadedBy > users.id
|
||||
|
||||
Ref: media_logs.proposedBy > users.id
|
||||
@ -636,8 +605,6 @@ Ref: genres.createdBy > users.id
|
||||
|
||||
Ref: studios.createdBy > users.id
|
||||
|
||||
Ref: countries.createdBy > users.id
|
||||
|
||||
Ref: episodes.mediaId > medias.id
|
||||
|
||||
Ref: episodes.uploadedBy > users.id
|
||||
|
||||
@ -32,16 +32,16 @@ model Media {
|
||||
pictureLarge String @db.Text
|
||||
genres Genre[] @relation("MediaGenres")
|
||||
country Country @default(JP)
|
||||
status Status
|
||||
score Decimal @db.Decimal(4, 2) @default(0.00)
|
||||
status String
|
||||
startAiring DateTime?
|
||||
finishAiring DateTime?
|
||||
endAiring DateTime?
|
||||
synopsis String @db.Text
|
||||
nfsw Boolean @default(false)
|
||||
ageRating AgeRating
|
||||
ageRating String
|
||||
mediaType MediaType
|
||||
source Source
|
||||
source String?
|
||||
studios Studio[] @relation("MediaStudios")
|
||||
onDraft Boolean @default(false)
|
||||
onDraft Boolean @default(true)
|
||||
uploader User @relation("UserUploadedMedias", fields: [uploadedBy], references: [id])
|
||||
uploadedBy String
|
||||
deletedAt DateTime?
|
||||
@ -52,6 +52,11 @@ model Media {
|
||||
episodes Episode[] @relation("MediaEpisodes")
|
||||
collections Collection[] @relation("MediaCollections")
|
||||
reviews MediaReview[] @relation("MediaReviews")
|
||||
|
||||
@@index([status, onDraft, deletedAt])
|
||||
@@index([mediaType])
|
||||
@@index([uploadedBy])
|
||||
@@index([createdAt])
|
||||
@@map("medias")
|
||||
}
|
||||
|
||||
@ -73,7 +78,7 @@ model MediaLog {
|
||||
model Genre {
|
||||
id String @id @default(uuid())
|
||||
name String @db.VarChar(255)
|
||||
slug String @db.VarChar(255)
|
||||
slug String @db.VarChar(255) @unique
|
||||
malId Int
|
||||
malUrl String @db.VarChar(255)
|
||||
creator User @relation("UserCreatedGenres", fields: [createdBy], references: [id])
|
||||
@ -84,15 +89,17 @@ model Genre {
|
||||
|
||||
medias Media[] @relation("MediaGenres")
|
||||
user_favourite_genres UserPreference[] @relation("UserFavoriteGenres")
|
||||
|
||||
@@map("genres")
|
||||
}
|
||||
|
||||
model Studio {
|
||||
id String @id @default(uuid())
|
||||
name String @db.VarChar(255)
|
||||
slug String @db.VarChar(255)
|
||||
logoUrl String @db.Text
|
||||
colorHex String @db.VarChar(10)
|
||||
slug String @db.VarChar(255) @unique
|
||||
linkAbout String @db.Text
|
||||
malId Int
|
||||
logoUrl String? @db.Text
|
||||
creator User @relation("UserCreatedStudios", fields: [createdBy], references: [id])
|
||||
createdBy String
|
||||
deletedAt DateTime?
|
||||
@ -103,6 +110,40 @@ model Studio {
|
||||
@@map("studios")
|
||||
}
|
||||
|
||||
model Character {
|
||||
id String @id @default(uuid()) @db.Uuid
|
||||
name String
|
||||
role CharacterRole
|
||||
favorites Int @default(0)
|
||||
imageUrl String?
|
||||
smallImageUrl String?
|
||||
createdBy User @relation("UserCreatedCharacters", fields: [creatorId], references: [id])
|
||||
creatorId String
|
||||
deletedAt DateTime?
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @default(now()) @updatedAt
|
||||
|
||||
@@map("characters")
|
||||
}
|
||||
|
||||
model VoiceActor {
|
||||
id String @id @default(uuid()) @db.Uuid
|
||||
malId Int
|
||||
name String
|
||||
birthday DateTime?
|
||||
description String @db.Text
|
||||
aboutUrl String
|
||||
imageUrl String?
|
||||
websiteUrl String?
|
||||
createdBy User @relation("UserCreatedVoiceActors", fields: [creatorId], references: [id])
|
||||
creatorId String
|
||||
deletedAt DateTime?
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @default(now()) @updatedAt
|
||||
|
||||
@@map("voice_actors")
|
||||
}
|
||||
|
||||
model Episode {
|
||||
id String @id @default(uuid())
|
||||
media Media @relation("MediaEpisodes", fields: [mediaId], references: [id])
|
||||
@ -205,6 +246,8 @@ model User {
|
||||
media_approveds MediaLog[] @relation("UserApprovedMedias")
|
||||
genres Genre[] @relation("UserCreatedGenres")
|
||||
studios Studio[] @relation("UserCreatedStudios")
|
||||
characters Character[] @relation("UserCreatedCharacters")
|
||||
voice_actor VoiceActor[] @relation("UserCreatedVoiceActors")
|
||||
episodes Episode[] @relation("UserEpisodes")
|
||||
episode_likes EpisodeLike[] @relation("UserEpisodeLikes")
|
||||
videos Video[] @relation("UserVideos")
|
||||
@ -523,14 +566,6 @@ model SystemLog {
|
||||
//// Prisma Enum Values ////
|
||||
|
||||
// Media Enum
|
||||
enum AgeRating {
|
||||
G // All Ages
|
||||
PG // Children
|
||||
PG_13 // Teens 13 or older
|
||||
R // 17+ (violance & profanity)
|
||||
R_plus // Mild Nudity
|
||||
Rx // Hentai
|
||||
}
|
||||
enum MediaType {
|
||||
TV
|
||||
ONA
|
||||
@ -539,17 +574,6 @@ enum MediaType {
|
||||
Special
|
||||
Music
|
||||
}
|
||||
enum Source {
|
||||
original
|
||||
manga
|
||||
light_novel
|
||||
game
|
||||
}
|
||||
enum Status {
|
||||
FINISHED_AIRING @map("Finished Airing")
|
||||
CURRENTLY_AIRING @map("Currently Airing")
|
||||
NOT_YET_AIRED @map("Not yet aired")
|
||||
}
|
||||
enum Country {
|
||||
JP @map("Japanese")
|
||||
EN @map("English")
|
||||
@ -557,6 +581,12 @@ enum Country {
|
||||
KR @map("Korea")
|
||||
}
|
||||
|
||||
// Character Enum
|
||||
enum CharacterRole {
|
||||
Main
|
||||
Supporting
|
||||
}
|
||||
|
||||
// MediaLog Enum
|
||||
enum MediaOperation {
|
||||
create
|
||||
|
||||
15
snowflake.d.ts
vendored
Normal file
15
snowflake.d.ts
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
declare module "node-snowflake" {
|
||||
interface SnowflakeOptions {
|
||||
workerId?: number;
|
||||
datacenterId?: number;
|
||||
sequence?: number;
|
||||
epoch?: number | bigint | Date;
|
||||
}
|
||||
|
||||
class Snowflake {
|
||||
constructor(options?: SnowflakeOptions);
|
||||
generate(): bigint;
|
||||
}
|
||||
|
||||
export { Snowflake };
|
||||
}
|
||||
6
src/config/apis/media.reference.ts
Normal file
6
src/config/apis/media.reference.ts
Normal file
@ -0,0 +1,6 @@
|
||||
export const getMediaReferenceAPI = (malId: number) => {
|
||||
return {
|
||||
baseURL: "https://api.jikan.moe/v4",
|
||||
getMediaFullInfo: `/anime/${malId}/full`,
|
||||
};
|
||||
};
|
||||
50
src/helpers/characters/generateSlug.ts
Normal file
50
src/helpers/characters/generateSlug.ts
Normal file
@ -0,0 +1,50 @@
|
||||
import slugify from "slugify";
|
||||
import { PrismaClient } from "@prisma/client";
|
||||
import { prisma } from "../../utils/databases/prisma/connection";
|
||||
|
||||
interface UniqueConfig {
|
||||
model?: keyof PrismaClient;
|
||||
target?: string;
|
||||
}
|
||||
|
||||
export async function generateSlug(
|
||||
input: string,
|
||||
config?: UniqueConfig,
|
||||
): Promise<string> {
|
||||
const baseSlug = slugify(input, { lower: true, strict: true });
|
||||
let uniqueSlug = baseSlug;
|
||||
|
||||
// CASE 1: Tidak ada config → langsung return slug
|
||||
if (!config) return uniqueSlug;
|
||||
|
||||
const { model, target } = config;
|
||||
|
||||
// CASE 2: Validasi pasangan model-target
|
||||
if (!model || !target) {
|
||||
throw new Error(`Both "model" and "target" must be provided together.`);
|
||||
}
|
||||
|
||||
// CASE 3: Cek unique
|
||||
const prismaModel = (prisma as any)[model];
|
||||
if (!prismaModel) {
|
||||
throw new Error(`Model "${model as string}" not found in PrismaClient.`);
|
||||
}
|
||||
|
||||
let counter = 1;
|
||||
|
||||
while (true) {
|
||||
const exists = await prismaModel.findFirst({
|
||||
where: {
|
||||
[target]: uniqueSlug,
|
||||
},
|
||||
select: { [target]: true },
|
||||
});
|
||||
|
||||
if (!exists) break;
|
||||
|
||||
uniqueSlug = `${baseSlug}-${counter}`;
|
||||
counter++;
|
||||
}
|
||||
|
||||
return uniqueSlug;
|
||||
}
|
||||
25
src/modules/internal/index.ts
Normal file
25
src/modules/internal/index.ts
Normal file
@ -0,0 +1,25 @@
|
||||
import Elysia, { Context } from "elysia";
|
||||
import { MediaFullInfoResponse } from "./types/mediaFullInfo.type";
|
||||
import { InsertMediaRepository } from "./repositories/insertMedia.repository";
|
||||
import { mainErrorHandler } from "../../helpers/error/handler";
|
||||
|
||||
const masterSourceAPI = "https://api.jikan.moe/v4";
|
||||
|
||||
export const internalModule = new Elysia({ prefix: "/internal" }).post(
|
||||
"/medias",
|
||||
async (ctx: Context & { body: { mal_id: number } }) => {
|
||||
try {
|
||||
const fullMediaData = await fetch(
|
||||
`${masterSourceAPI}/anime/${ctx.body.mal_id}/full`,
|
||||
)
|
||||
.then((res) => res.json())
|
||||
.then((data) => data as MediaFullInfoResponse);
|
||||
|
||||
// return fullMediaData;
|
||||
const createMedia = await InsertMediaRepository(fullMediaData);
|
||||
return createMedia;
|
||||
} catch (error) {
|
||||
return mainErrorHandler(ctx.set, error);
|
||||
}
|
||||
},
|
||||
);
|
||||
134
src/modules/internal/repositories/insertMedia.repository.ts
Normal file
134
src/modules/internal/repositories/insertMedia.repository.ts
Normal file
@ -0,0 +1,134 @@
|
||||
import { Prisma } from "@prisma/client";
|
||||
import { generateSlug } from "../../../helpers/characters/generateSlug";
|
||||
import { AppError } from "../../../helpers/error/instances/app";
|
||||
import { prisma } from "../../../utils/databases/prisma/connection";
|
||||
import { MediaFullInfoResponse } from "../types/mediaFullInfo.type";
|
||||
|
||||
export const InsertMediaRepository = async (data: MediaFullInfoResponse) => {
|
||||
try {
|
||||
/**
|
||||
* Genres Insertion
|
||||
*
|
||||
* This section handles the insertion of genres associated with the media.
|
||||
* It iterates over each genre in the media data, generates a slug for it,
|
||||
* and performs an upsert operation to ensure that the genre is either created
|
||||
* or updated in the database. The IDs of the inserted or updated genres are
|
||||
* collected for later association with the media.
|
||||
*
|
||||
* @param data - The full media data containing genres information.
|
||||
*/
|
||||
const genreIds: string[] = [];
|
||||
for (const genre of data.data.genres) {
|
||||
const slug = (await generateSlug(genre.name)) as string;
|
||||
const genrePayload = {
|
||||
name: genre.name,
|
||||
malId: genre.mal_id,
|
||||
malUrl: genre.url,
|
||||
createdBy: "b734b9bc-b4ea-408f-a80e-0a837ce884da",
|
||||
slug,
|
||||
};
|
||||
const insertedGenre = await prisma.genre.upsert({
|
||||
where: { slug },
|
||||
create: genrePayload,
|
||||
update: genrePayload,
|
||||
select: { id: true },
|
||||
});
|
||||
genreIds.push(insertedGenre.id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Studios Insertion
|
||||
*
|
||||
* This section manages the insertion of studios associated with the media.
|
||||
* It processes each studio listed in the media data, generating a slug for
|
||||
* each and performing an upsert operation to either create or update the
|
||||
* studio record in the database. The IDs of the inserted or updated studios
|
||||
* are collected for later association with the media.
|
||||
*
|
||||
* @param data - The full media data containing studios information.
|
||||
*/
|
||||
const studioIds: string[] = [];
|
||||
for (const studio of data.data.studios) {
|
||||
const slug = (await generateSlug(studio.name)) as string;
|
||||
const studioPayload = {
|
||||
name: studio.name,
|
||||
malId: studio.mal_id,
|
||||
linkAbout: studio.url,
|
||||
createdBy: "b734b9bc-b4ea-408f-a80e-0a837ce884da",
|
||||
slug,
|
||||
};
|
||||
const insertedStudio = await prisma.studio.upsert({
|
||||
where: { slug },
|
||||
create: studioPayload,
|
||||
update: studioPayload,
|
||||
select: { id: true },
|
||||
});
|
||||
studioIds.push(insertedStudio.id);
|
||||
}
|
||||
for (const studio of data.data.producers) {
|
||||
const slug = (await generateSlug(studio.name)) as string;
|
||||
const studioPayload = {
|
||||
name: studio.name,
|
||||
malId: studio.mal_id,
|
||||
linkAbout: studio.url,
|
||||
createdBy: "b734b9bc-b4ea-408f-a80e-0a837ce884da",
|
||||
slug,
|
||||
};
|
||||
const insertedStudio = await prisma.studio.upsert({
|
||||
where: { slug },
|
||||
create: studioPayload,
|
||||
update: studioPayload,
|
||||
select: { id: true },
|
||||
});
|
||||
studioIds.push(insertedStudio.id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Media Payload Construction and Upsert
|
||||
*
|
||||
* This section constructs the payload for the media insertion or update.
|
||||
* It gathers all necessary information from the media data, including
|
||||
* title, alternative titles, slug, associated genres and studios, score,
|
||||
* images, status, airing dates, synopsis, age rating, media type, source,
|
||||
* and other relevant details. This payload is then used in an upsert
|
||||
* operation to ensure that the media record is either created or updated
|
||||
* in the database.
|
||||
*
|
||||
* @param data - The full media data for constructing the media payload.
|
||||
*/
|
||||
const construct = {
|
||||
title: data.data.title,
|
||||
titleAlternative: (data.data.titles as unknown) as Prisma.InputJsonValue,
|
||||
slug: await generateSlug(data.data.title, {
|
||||
model: "media",
|
||||
target: "slug",
|
||||
}),
|
||||
malId: data.data.mal_id,
|
||||
genres: {
|
||||
connect: genreIds.map((id) => ({ id })),
|
||||
},
|
||||
studios: {
|
||||
connect: studioIds.map((id) => ({ id })),
|
||||
},
|
||||
score: data.data.score,
|
||||
pictureMedium: data.data.images.webp.image_url,
|
||||
pictureLarge: data.data.images.webp.large_image_url,
|
||||
status: data.data.status,
|
||||
startAiring: data.data.aired.from,
|
||||
endAiring: data.data.aired.to,
|
||||
synopsis: data.data.synopsis,
|
||||
ageRating: data.data.rating,
|
||||
mediaType: data.data.type,
|
||||
source: data.data.source,
|
||||
onDraft: false,
|
||||
uploadedBy: "b734b9bc-b4ea-408f-a80e-0a837ce884da",
|
||||
};
|
||||
return await prisma.media.upsert({
|
||||
where: { malId: data.data.mal_id },
|
||||
update: construct,
|
||||
create: construct,
|
||||
});
|
||||
} catch (error) {
|
||||
throw new AppError(500, "Failed to insert media", error);
|
||||
}
|
||||
};
|
||||
125
src/modules/internal/types/mediaFullInfo.type.ts
Normal file
125
src/modules/internal/types/mediaFullInfo.type.ts
Normal file
@ -0,0 +1,125 @@
|
||||
import { MediaType } from "@prisma/client";
|
||||
export interface MediaFullInfoResponse {
|
||||
data: Data;
|
||||
}
|
||||
|
||||
interface Data {
|
||||
mal_id: number;
|
||||
url: string;
|
||||
images: { [key: string]: Image };
|
||||
trailer: Trailer;
|
||||
approved: boolean;
|
||||
titles: Title[];
|
||||
title: string;
|
||||
title_english: string;
|
||||
title_japanese: string;
|
||||
title_synonyms: string[];
|
||||
type: MediaType;
|
||||
source: string;
|
||||
episodes: number;
|
||||
status: string;
|
||||
airing: boolean;
|
||||
aired: Aired;
|
||||
duration: string;
|
||||
rating: string;
|
||||
score: number;
|
||||
scored_by: number;
|
||||
rank: number;
|
||||
popularity: number;
|
||||
members: number;
|
||||
favorites: number;
|
||||
synopsis: string;
|
||||
background: string;
|
||||
season: string;
|
||||
year: number;
|
||||
broadcast: Broadcast;
|
||||
producers: Genre[];
|
||||
licensors: any[];
|
||||
studios: Genre[];
|
||||
genres: Genre[];
|
||||
explicit_genres: any[];
|
||||
themes: Genre[];
|
||||
demographics: any[];
|
||||
relations: Relation[];
|
||||
theme: Theme;
|
||||
external: External[];
|
||||
streaming: External[];
|
||||
}
|
||||
|
||||
interface Aired {
|
||||
from: Date;
|
||||
to: Date;
|
||||
prop: Prop;
|
||||
string: string;
|
||||
}
|
||||
|
||||
interface Prop {
|
||||
from: From;
|
||||
to: From;
|
||||
}
|
||||
|
||||
interface From {
|
||||
day: number;
|
||||
month: number;
|
||||
year: number;
|
||||
}
|
||||
|
||||
interface Broadcast {
|
||||
day: string;
|
||||
time: string;
|
||||
timezone: string;
|
||||
string: string;
|
||||
}
|
||||
|
||||
interface External {
|
||||
name: string;
|
||||
url: string;
|
||||
}
|
||||
|
||||
interface Genre {
|
||||
mal_id: number;
|
||||
type: Type;
|
||||
name: string;
|
||||
url: string;
|
||||
}
|
||||
|
||||
enum Type {
|
||||
Anime = "anime",
|
||||
Manga = "manga",
|
||||
}
|
||||
|
||||
interface Image {
|
||||
image_url: string;
|
||||
small_image_url: string;
|
||||
large_image_url: string;
|
||||
}
|
||||
|
||||
interface Relation {
|
||||
relation: string;
|
||||
entry: Genre[];
|
||||
}
|
||||
|
||||
interface Theme {
|
||||
openings: string[];
|
||||
endings: string[];
|
||||
}
|
||||
|
||||
interface Title {
|
||||
type: string;
|
||||
title: string;
|
||||
}
|
||||
|
||||
interface Trailer {
|
||||
youtube_id: null;
|
||||
url: null;
|
||||
embed_url: string;
|
||||
images: Images;
|
||||
}
|
||||
|
||||
interface Images {
|
||||
image_url: null;
|
||||
small_image_url: null;
|
||||
medium_image_url: null;
|
||||
large_image_url: null;
|
||||
maximum_image_url: null;
|
||||
}
|
||||
Reference in New Issue
Block a user