mirror of
https://github.com/StepanovPlaton/torrent_frontend.git
synced 2026-04-03 20:30:48 +04:00
Add audiobooks
This commit is contained in:
@@ -1,5 +1,7 @@
|
||||
import { HTTPService } from "@/shared/utils/http";
|
||||
import { coverNameSchema } from "./schemas/cover";
|
||||
import { torrentNameSchema } from "./schemas/torrent";
|
||||
import { fragmentNameSchema } from "./schemas/fragment";
|
||||
|
||||
export abstract class FilesService {
|
||||
public static async UploadCover(cover: File) {
|
||||
@@ -19,7 +21,19 @@ export abstract class FilesService {
|
||||
formData.append("torrent", torrent);
|
||||
return await HTTPService.post(
|
||||
`/files/torrent`,
|
||||
coverNameSchema,
|
||||
torrentNameSchema,
|
||||
formData,
|
||||
{},
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
public static async UploadFragment(fragment: File) {
|
||||
const formData = new FormData();
|
||||
formData.append("fragment", fragment);
|
||||
return await HTTPService.post(
|
||||
`/files/audio`,
|
||||
fragmentNameSchema,
|
||||
formData,
|
||||
{},
|
||||
false
|
||||
|
||||
4
src/entities/files/schemas/fragment.ts
Normal file
4
src/entities/files/schemas/fragment.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
import { z } from "zod";
|
||||
|
||||
export const fragmentNameSchema = z.string().min(5);
|
||||
export type FragmentNameType = z.infer<typeof fragmentNameSchema>;
|
||||
4
src/entities/files/schemas/torrent.ts
Normal file
4
src/entities/files/schemas/torrent.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
import { z } from "zod";
|
||||
|
||||
export const torrentNameSchema = z.string().min(5);
|
||||
export type TorrentNameType = z.infer<typeof torrentNameSchema>;
|
||||
@@ -1,25 +0,0 @@
|
||||
import { HTTPService } from "@/shared/utils/http";
|
||||
import { gameCardsSchema } from "./schemas/gameCard";
|
||||
import { GameCreateType, gameSchema } from "./schemas/game";
|
||||
|
||||
export abstract class GameService {
|
||||
public static async GetGameCards() {
|
||||
return await HTTPService.get("/games/cards", gameCardsSchema);
|
||||
}
|
||||
public static async GetGame(id: number) {
|
||||
return await HTTPService.get(`/games/${id}`, gameSchema);
|
||||
}
|
||||
public static async ChangeGame(id: number, gameInfo: GameCreateType) {
|
||||
return await HTTPService.put(`/games/${id}`, gameSchema, gameInfo);
|
||||
}
|
||||
public static async AddGame(gameInfo: GameCreateType) {
|
||||
return await HTTPService.post(`/games`, gameSchema, gameInfo);
|
||||
}
|
||||
|
||||
public static GetEmptyGame(): GameCreateType {
|
||||
return {
|
||||
title: "",
|
||||
torrent_file: "",
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
import { GameCardType } from "./schemas/gameCard";
|
||||
import { GameType, GameCreateType, gameCreateSchema } from "./schemas/game";
|
||||
import { GameService } from "./game";
|
||||
|
||||
export {
|
||||
GameService,
|
||||
gameCreateSchema,
|
||||
type GameType,
|
||||
type GameCreateType,
|
||||
type GameCardType,
|
||||
};
|
||||
66
src/entities/item/audiobook/audiobook.ts
Normal file
66
src/entities/item/audiobook/audiobook.ts
Normal file
@@ -0,0 +1,66 @@
|
||||
import { HTTPService } from "@/shared/utils/http";
|
||||
import { audiobookCardsSchema } from "./schemas/audiobookCard";
|
||||
import {
|
||||
AudiobookCreateType,
|
||||
audiobookSchema,
|
||||
AudiobookType,
|
||||
} from "./schemas/audiobook";
|
||||
import {
|
||||
IItemService,
|
||||
ItemCreateType,
|
||||
ItemPropertiesDescriptionType,
|
||||
ItemType,
|
||||
staticImplements,
|
||||
TypesOfItems,
|
||||
} from "../types";
|
||||
import { ItemService } from "../item";
|
||||
|
||||
@staticImplements<IItemService>()
|
||||
export abstract class AudiobookService {
|
||||
public static async GetCards() {
|
||||
return await HTTPService.get("/audiobooks/cards", audiobookCardsSchema);
|
||||
}
|
||||
public static async Get(id: number) {
|
||||
return await HTTPService.get(`/audiobooks/${id}`, audiobookSchema);
|
||||
}
|
||||
public static async Add(info: AudiobookCreateType) {
|
||||
return await HTTPService.post(`/audiobooks`, audiobookSchema, info);
|
||||
}
|
||||
public static async Change(id: number, info: AudiobookCreateType) {
|
||||
return await HTTPService.put(`/audiobooks/${id}`, audiobookSchema, info);
|
||||
}
|
||||
|
||||
public static GetEmpty(): AudiobookCreateType {
|
||||
return {
|
||||
title: "",
|
||||
torrent_file: "",
|
||||
type: TypesOfItems.audiobook,
|
||||
};
|
||||
}
|
||||
|
||||
static propertiesDescription: ItemPropertiesDescriptionType<AudiobookType> = [
|
||||
[
|
||||
{ name: "Автор", key: "author" },
|
||||
{ name: "Язык", key: "language" },
|
||||
{ name: "Читает", key: "reader" },
|
||||
{ name: "Продолжительность", key: "duration" },
|
||||
],
|
||||
[
|
||||
{
|
||||
name: "Дата обновления раздачи",
|
||||
key: "update_date",
|
||||
value: (item: ItemType | ItemCreateType) => {
|
||||
return ItemService.isExistingItem(item)
|
||||
? item.update_date.toLocaleDateString("ru-ru")
|
||||
: new Date().toLocaleDateString("ru-ru");
|
||||
},
|
||||
editable: false,
|
||||
},
|
||||
{
|
||||
name: "Год выхода",
|
||||
key: "release_date",
|
||||
},
|
||||
{ name: "Объём загрузки", key: "download_size" },
|
||||
],
|
||||
];
|
||||
}
|
||||
58
src/entities/item/audiobook/schemas/audiobook.ts
Normal file
58
src/entities/item/audiobook/schemas/audiobook.ts
Normal file
@@ -0,0 +1,58 @@
|
||||
import { z } from "zod";
|
||||
import { audiobookCardBaseSchema } from "./audiobookCard";
|
||||
|
||||
export const audiobookBaseSchema = audiobookCardBaseSchema.merge(
|
||||
z.object({
|
||||
torrent_file: z.string().min(3, "У раздачи должен быть .torrent файл"),
|
||||
fragment: z.string().optional().nullable(),
|
||||
|
||||
language: z.string().optional().nullable(),
|
||||
download_size: z.string().optional().nullable(),
|
||||
duration: z.string().optional().nullable(),
|
||||
reader: z.string().optional().nullable(),
|
||||
|
||||
release_date: z
|
||||
.string()
|
||||
.optional()
|
||||
.nullable()
|
||||
.transform((d) =>
|
||||
d
|
||||
? new Date(d).toLocaleDateString("en-us", {
|
||||
year: "numeric",
|
||||
})
|
||||
: undefined
|
||||
),
|
||||
})
|
||||
);
|
||||
|
||||
export const audiobookCreateSchema = audiobookBaseSchema.merge(z.object({}));
|
||||
export type AudiobookCreateType = z.infer<typeof audiobookCreateSchema>;
|
||||
|
||||
export const audiobookSchema = audiobookBaseSchema.merge(
|
||||
z.object({
|
||||
id: z.number().positive(),
|
||||
owner_id: z.number().positive(),
|
||||
update_date: z
|
||||
.string()
|
||||
.min(1)
|
||||
.transform((d) => new Date(d)),
|
||||
upload_date: z
|
||||
.string()
|
||||
.min(1)
|
||||
.transform((d) => new Date(d)),
|
||||
})
|
||||
);
|
||||
export type AudiobookType = z.infer<typeof audiobookSchema>;
|
||||
|
||||
export const isAudiobook = (a: any): a is AudiobookType => {
|
||||
return audiobookSchema.safeParse(a).success;
|
||||
};
|
||||
|
||||
export const audiobooksSchema = z.array(z.any()).transform((a) => {
|
||||
const audiobooks: AudiobookType[] = [];
|
||||
a.forEach((e) => {
|
||||
if (isAudiobook(e)) audiobooks.push(audiobookSchema.parse(e));
|
||||
else console.error("Audiobook parse error - ", e);
|
||||
});
|
||||
return audiobooks;
|
||||
});
|
||||
43
src/entities/item/audiobook/schemas/audiobookCard.ts
Normal file
43
src/entities/item/audiobook/schemas/audiobookCard.ts
Normal file
@@ -0,0 +1,43 @@
|
||||
import { z } from "zod";
|
||||
import { TypesOfItems } from "../../types";
|
||||
|
||||
export const audiobookCardBaseSchema = z.object({
|
||||
title: z.string().min(3, "Слишком короткое название"),
|
||||
cover: z.string().optional().nullable(),
|
||||
description: z.string().optional().nullable(),
|
||||
author: z.string().optional().nullable(),
|
||||
|
||||
// Добавляем к каждой аудиокниге поле, которое
|
||||
// показывает, что item является аудиокнигой
|
||||
type: z
|
||||
.any()
|
||||
.optional()
|
||||
.transform(() => TypesOfItems.audiobook),
|
||||
});
|
||||
|
||||
export const audiobookCardSchema = audiobookCardBaseSchema.merge(
|
||||
z.object({
|
||||
id: z.number().positive(),
|
||||
})
|
||||
);
|
||||
export type AudiobookCardType = z.infer<typeof audiobookCardSchema>;
|
||||
|
||||
export const isAudiobookCardStrict = (a: any): a is AudiobookCardType => {
|
||||
return audiobookCardSchema.safeParse(a).success;
|
||||
};
|
||||
|
||||
export const audiobookCardsSchema = z.array(z.any()).transform((a) => {
|
||||
const cards: AudiobookCardType[] = [];
|
||||
a.forEach((e) => {
|
||||
if (isAudiobookCardStrict(e)) cards.push(audiobookCardSchema.parse(e));
|
||||
else console.error("AudiobookCard parse error - ", e);
|
||||
});
|
||||
return cards;
|
||||
});
|
||||
|
||||
export const isAudiobook = (a: any): a is AudiobookCardType => {
|
||||
return (
|
||||
audiobookCardBaseSchema.safeParse(a).success &&
|
||||
(a as AudiobookCardType).type === TypesOfItems.audiobook
|
||||
);
|
||||
};
|
||||
66
src/entities/item/game/game.ts
Normal file
66
src/entities/item/game/game.ts
Normal file
@@ -0,0 +1,66 @@
|
||||
import { HTTPService } from "@/shared/utils/http";
|
||||
import { gameCardsSchema } from "./schemas/gameCard";
|
||||
import { GameCreateType, gameSchema, GameType } from "./schemas/game";
|
||||
import {
|
||||
IItemService,
|
||||
ItemCreateType,
|
||||
ItemPropertiesDescriptionType,
|
||||
ItemType,
|
||||
staticImplements,
|
||||
TypesOfItems,
|
||||
} from "../types";
|
||||
import { ItemService } from "../item";
|
||||
|
||||
@staticImplements<IItemService>()
|
||||
export abstract class GameService {
|
||||
public static async GetCards() {
|
||||
return await HTTPService.get("/games/cards", gameCardsSchema);
|
||||
}
|
||||
public static async Get(id: number) {
|
||||
return await HTTPService.get(`/games/${id}`, gameSchema);
|
||||
}
|
||||
public static async Add(info: GameCreateType) {
|
||||
return await HTTPService.post(`/games`, gameSchema, info);
|
||||
}
|
||||
public static async Change(id: number, info: GameCreateType) {
|
||||
return await HTTPService.put(`/games/${id}`, gameSchema, info);
|
||||
}
|
||||
|
||||
public static GetEmpty(): GameCreateType {
|
||||
return {
|
||||
title: "",
|
||||
torrent_file: "",
|
||||
type: TypesOfItems.game,
|
||||
};
|
||||
}
|
||||
|
||||
static propertiesDescription: ItemPropertiesDescriptionType<GameType> = [
|
||||
[
|
||||
{ name: "Система", key: "system" },
|
||||
{ name: "Процессор", key: "processor" },
|
||||
{ name: "Оперативная память", key: "memory" },
|
||||
{ name: "Видеокарта", key: "graphics" },
|
||||
{ name: "Место на диске", key: "storage" },
|
||||
],
|
||||
[
|
||||
{ name: "Версия игры", key: "version" },
|
||||
{
|
||||
name: "Дата обновления раздачи",
|
||||
key: "update_date",
|
||||
value: (item: ItemType | ItemCreateType) => {
|
||||
return ItemService.isExistingItem(item)
|
||||
? item.update_date.toLocaleDateString("ru-ru")
|
||||
: new Date().toLocaleDateString("ru-ru");
|
||||
},
|
||||
editable: false,
|
||||
},
|
||||
{ name: "Язык", key: "language" },
|
||||
{ name: "Разработчик", key: "developer" },
|
||||
{
|
||||
name: "Год выхода",
|
||||
key: "release_date",
|
||||
},
|
||||
{ name: "Объём загрузки", key: "download_size" },
|
||||
],
|
||||
];
|
||||
}
|
||||
@@ -1,10 +1,18 @@
|
||||
import { z } from "zod";
|
||||
import { TypesOfItems } from "../../types";
|
||||
|
||||
export const gameCardBaseSchema = z.object({
|
||||
title: z.string().min(3, "Слишком короткое название"),
|
||||
cover: z.string().optional().nullable(),
|
||||
description: z.string().optional().nullable(),
|
||||
version: z.string().optional().nullable(),
|
||||
|
||||
// Добавляем к каждой игре поле, которое
|
||||
// показывает, что item является игрой
|
||||
type: z
|
||||
.any()
|
||||
.optional()
|
||||
.transform(() => TypesOfItems.game),
|
||||
});
|
||||
|
||||
export const gameCardSchema = gameCardBaseSchema.merge(
|
||||
@@ -14,15 +22,22 @@ export const gameCardSchema = gameCardBaseSchema.merge(
|
||||
);
|
||||
export type GameCardType = z.infer<typeof gameCardSchema>;
|
||||
|
||||
export const isGameCard = (a: any): a is GameCardType => {
|
||||
export const isGameCardStrict = (a: any): a is GameCardType => {
|
||||
return gameCardSchema.safeParse(a).success;
|
||||
};
|
||||
|
||||
export const gameCardsSchema = z.array(z.any()).transform((a) => {
|
||||
const cards: GameCardType[] = [];
|
||||
a.forEach((e) => {
|
||||
if (isGameCard(e)) cards.push(gameCardSchema.parse(e));
|
||||
if (isGameCardStrict(e)) cards.push(gameCardSchema.parse(e));
|
||||
else console.error("GameCard parse error - ", e);
|
||||
});
|
||||
return cards;
|
||||
});
|
||||
|
||||
export const isGame = (a: any): a is GameCardType => {
|
||||
return (
|
||||
gameCardBaseSchema.safeParse(a).success &&
|
||||
(a as GameCardType).type === TypesOfItems.game
|
||||
);
|
||||
};
|
||||
104
src/entities/item/index.ts
Normal file
104
src/entities/item/index.ts
Normal file
@@ -0,0 +1,104 @@
|
||||
import {
|
||||
gameSchema,
|
||||
gamesSchema,
|
||||
gameCreateSchema,
|
||||
type GameType,
|
||||
type GameCreateType,
|
||||
} from "./game/schemas/game";
|
||||
export {
|
||||
gameSchema,
|
||||
gamesSchema,
|
||||
gameCreateSchema,
|
||||
type GameType,
|
||||
type GameCreateType,
|
||||
};
|
||||
|
||||
import {
|
||||
gameCardSchema,
|
||||
gameCardsSchema,
|
||||
type GameCardType,
|
||||
isGame,
|
||||
} from "./game/schemas/gameCard";
|
||||
export { gameCardSchema, gameCardsSchema, type GameCardType, isGame };
|
||||
|
||||
import { GameService } from "./game/game";
|
||||
export { GameService };
|
||||
|
||||
import {
|
||||
movieSchema,
|
||||
moviesSchema,
|
||||
movieCreateSchema,
|
||||
type MovieType,
|
||||
type MovieCreateType,
|
||||
} from "./movie/schemas/movie";
|
||||
export {
|
||||
movieSchema,
|
||||
moviesSchema,
|
||||
movieCreateSchema,
|
||||
type MovieType,
|
||||
type MovieCreateType,
|
||||
};
|
||||
|
||||
import {
|
||||
movieCardSchema,
|
||||
movieCardsSchema,
|
||||
type MovieCardType,
|
||||
isMovie,
|
||||
} from "./movie/schemas/movieCard";
|
||||
export { movieCardSchema, movieCardsSchema, type MovieCardType, isMovie };
|
||||
|
||||
import { MovieService } from "./movie/movie";
|
||||
export { MovieService };
|
||||
|
||||
import {
|
||||
audiobookSchema,
|
||||
audiobooksSchema,
|
||||
audiobookCreateSchema,
|
||||
type AudiobookType,
|
||||
type AudiobookCreateType,
|
||||
} from "./audiobook/schemas/audiobook";
|
||||
export {
|
||||
audiobookSchema,
|
||||
audiobooksSchema,
|
||||
audiobookCreateSchema,
|
||||
type AudiobookType,
|
||||
type AudiobookCreateType,
|
||||
};
|
||||
|
||||
import {
|
||||
audiobookCardSchema,
|
||||
audiobookCardsSchema,
|
||||
type AudiobookCardType,
|
||||
isAudiobook,
|
||||
} from "./audiobook/schemas/audiobookCard";
|
||||
export {
|
||||
audiobookCardSchema,
|
||||
audiobookCardsSchema,
|
||||
type AudiobookCardType,
|
||||
isAudiobook,
|
||||
};
|
||||
|
||||
import { AudiobookService } from "./audiobook/audiobook";
|
||||
export { AudiobookService };
|
||||
|
||||
import { ItemService } from "./item";
|
||||
export { ItemService };
|
||||
|
||||
import {
|
||||
isSection,
|
||||
type ItemType,
|
||||
type ItemCardType,
|
||||
type ItemCreateType,
|
||||
type TypesOfItems,
|
||||
type ItemSectionsType,
|
||||
ItemSections,
|
||||
} from "./types";
|
||||
export {
|
||||
isSection,
|
||||
type ItemType,
|
||||
type ItemCardType,
|
||||
type ItemCreateType,
|
||||
type TypesOfItems,
|
||||
type ItemSectionsType,
|
||||
ItemSections,
|
||||
};
|
||||
131
src/entities/item/item.ts
Normal file
131
src/entities/item/item.ts
Normal file
@@ -0,0 +1,131 @@
|
||||
import { ZodSchema } from "zod";
|
||||
import { gameCreateSchema, gameSchema } from "./game/schemas/game";
|
||||
import { movieCreateSchema, movieSchema } from "./movie/schemas/movie";
|
||||
import { MovieService } from "./movie/movie";
|
||||
import { HTTPService } from "@/shared/utils/http";
|
||||
import { GameService } from "./game/game";
|
||||
import {
|
||||
audiobookCreateSchema,
|
||||
audiobookSchema,
|
||||
} from "./audiobook/schemas/audiobook";
|
||||
import { AudiobookService } from "./audiobook/audiobook";
|
||||
import {
|
||||
IItemService,
|
||||
ItemCardType,
|
||||
ItemCreateType,
|
||||
ItemPropertiesDescriptionType,
|
||||
ItemSectionsType,
|
||||
ItemType,
|
||||
TypesOfItems,
|
||||
UnionItemType,
|
||||
} from "./types";
|
||||
|
||||
export abstract class ItemService {
|
||||
private static get itemsConfiguration(): {
|
||||
[k in TypesOfItems]: {
|
||||
sectionUrl: ItemSectionsType;
|
||||
formResolver: ZodSchema;
|
||||
propertiesDescription: ItemPropertiesDescriptionType<UnionItemType>;
|
||||
AddItem: (itemInfo: ItemCreateType) => Promise<ItemType | null>;
|
||||
ChangeItem: (
|
||||
id: number,
|
||||
itemInfo: ItemCreateType
|
||||
) => Promise<ItemType | null>;
|
||||
};
|
||||
} {
|
||||
return {
|
||||
[TypesOfItems.game]: {
|
||||
sectionUrl: "games",
|
||||
formResolver: gameCreateSchema,
|
||||
propertiesDescription: GameService.propertiesDescription,
|
||||
AddItem: async (itemInfo) =>
|
||||
await HTTPService.post(`/games`, gameSchema, itemInfo),
|
||||
ChangeItem: async (id: number, itemInfo) =>
|
||||
await HTTPService.put(`/games/${id}`, gameSchema, itemInfo),
|
||||
},
|
||||
[TypesOfItems.movie]: {
|
||||
sectionUrl: "movies",
|
||||
formResolver: movieCreateSchema,
|
||||
propertiesDescription: MovieService.propertiesDescription,
|
||||
AddItem: async (itemInfo) =>
|
||||
await HTTPService.post(`/movies`, movieSchema, itemInfo),
|
||||
ChangeItem: async (id: number, itemInfo) =>
|
||||
await HTTPService.put(`/movies/${id}`, movieSchema, itemInfo),
|
||||
},
|
||||
[TypesOfItems.audiobook]: {
|
||||
sectionUrl: "audiobooks",
|
||||
formResolver: audiobookCreateSchema,
|
||||
propertiesDescription: AudiobookService.propertiesDescription,
|
||||
AddItem: async (itemInfo) =>
|
||||
await HTTPService.post(`/audiobooks`, audiobookSchema, itemInfo),
|
||||
ChangeItem: async (id: number, itemInfo) =>
|
||||
await HTTPService.put(`/audiobooks/${id}`, audiobookSchema, itemInfo),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
static get itemSections(): {
|
||||
[k in ItemSectionsType]: {
|
||||
itemType: TypesOfItems;
|
||||
popularSubsectionName: string;
|
||||
sectionInviteText: string;
|
||||
service: IItemService;
|
||||
};
|
||||
} {
|
||||
return {
|
||||
games: {
|
||||
itemType: TypesOfItems.game,
|
||||
popularSubsectionName: "Популярные игры",
|
||||
sectionInviteText: 'Перейти в раздел "Игры"',
|
||||
service: GameService,
|
||||
},
|
||||
movies: {
|
||||
itemType: TypesOfItems.movie,
|
||||
popularSubsectionName: "Популярные фильмы",
|
||||
sectionInviteText: 'Перейти в раздел "Фильмы"',
|
||||
service: MovieService,
|
||||
},
|
||||
audiobooks: {
|
||||
itemType: TypesOfItems.audiobook,
|
||||
popularSubsectionName: "Популярные аудиокниги",
|
||||
sectionInviteText: 'Перейти в раздел "Аудиокниги"',
|
||||
service: AudiobookService,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
public static isExistingItem(
|
||||
item: ItemCreateType | ItemType
|
||||
): item is ItemType {
|
||||
return (item as ItemType).id !== undefined;
|
||||
}
|
||||
|
||||
public static GetFormResolver(
|
||||
item: ItemCardType | ItemCreateType | ItemType
|
||||
) {
|
||||
return this.itemsConfiguration[item.type].formResolver;
|
||||
}
|
||||
|
||||
public static GetSectionUrlByItemType(
|
||||
item: ItemCardType | ItemCreateType | ItemType
|
||||
) {
|
||||
return this.itemsConfiguration[item.type].sectionUrl;
|
||||
}
|
||||
|
||||
public static GetPropertiesDescriptionForItem<
|
||||
T extends ItemType | ItemCreateType
|
||||
>(item: T) {
|
||||
return this.itemsConfiguration[item.type]
|
||||
.propertiesDescription as ItemPropertiesDescriptionType<T>;
|
||||
}
|
||||
|
||||
public static async AddItem(itemInfo: ItemCreateType) {
|
||||
return await this.itemsConfiguration[itemInfo.type].AddItem(itemInfo);
|
||||
}
|
||||
public static async ChangeItem(id: number, itemInfo: ItemCreateType) {
|
||||
return await this.itemsConfiguration[itemInfo.type].ChangeItem(
|
||||
id,
|
||||
itemInfo
|
||||
);
|
||||
}
|
||||
}
|
||||
65
src/entities/item/movie/movie.ts
Normal file
65
src/entities/item/movie/movie.ts
Normal file
@@ -0,0 +1,65 @@
|
||||
import { HTTPService } from "@/shared/utils/http";
|
||||
import { movieCardsSchema } from "./schemas/movieCard";
|
||||
import { MovieCreateType, movieSchema, MovieType } from "./schemas/movie";
|
||||
import {
|
||||
IItemService,
|
||||
ItemCreateType,
|
||||
ItemPropertiesDescriptionType,
|
||||
ItemType,
|
||||
staticImplements,
|
||||
TypesOfItems,
|
||||
} from "../types";
|
||||
import { ItemService } from "../item";
|
||||
|
||||
@staticImplements<IItemService>()
|
||||
export abstract class MovieService {
|
||||
public static async GetCards() {
|
||||
return await HTTPService.get("/movies/cards", movieCardsSchema);
|
||||
}
|
||||
public static async Get(id: number) {
|
||||
return await HTTPService.get(`/movies/${id}`, movieSchema);
|
||||
}
|
||||
public static async Add(info: MovieCreateType) {
|
||||
return await HTTPService.post(`/movies`, movieSchema, info);
|
||||
}
|
||||
public static async Change(id: number, info: MovieCreateType) {
|
||||
return await HTTPService.put(`/movies/${id}`, movieSchema, info);
|
||||
}
|
||||
|
||||
public static GetEmpty(): MovieCreateType {
|
||||
return {
|
||||
title: "",
|
||||
torrent_file: "",
|
||||
type: TypesOfItems.movie,
|
||||
};
|
||||
}
|
||||
|
||||
public static propertiesDescription: ItemPropertiesDescriptionType<MovieType> =
|
||||
[
|
||||
[
|
||||
{ name: "Возраст", key: "age" },
|
||||
{ name: "Язык", key: "language" },
|
||||
{ name: "Субтитры", key: "subtitles" },
|
||||
{
|
||||
name: "Год выхода",
|
||||
key: "release_date",
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
name: "Дата обновления раздачи",
|
||||
key: "update_date",
|
||||
value: (item: ItemType | ItemCreateType) => {
|
||||
return ItemService.isExistingItem(item)
|
||||
? item.update_date.toLocaleDateString("ru-ru")
|
||||
: new Date().toLocaleDateString("ru-ru");
|
||||
},
|
||||
editable: false,
|
||||
},
|
||||
{ name: "Режисёр", key: "director" },
|
||||
{ name: "Продолжительность", key: "duration" },
|
||||
{ name: "Страна", key: "country" },
|
||||
{ name: "Объём загрузки", key: "download_size" },
|
||||
],
|
||||
];
|
||||
}
|
||||
60
src/entities/item/movie/schemas/movie.ts
Normal file
60
src/entities/item/movie/schemas/movie.ts
Normal file
@@ -0,0 +1,60 @@
|
||||
import { z } from "zod";
|
||||
import { movieCardBaseSchema } from "./movieCard";
|
||||
|
||||
export const movieBaseSchema = movieCardBaseSchema.merge(
|
||||
z.object({
|
||||
torrent_file: z.string().min(3, "У раздачи должен быть .torrent файл"),
|
||||
trailer: z.string().optional(),
|
||||
|
||||
language: z.string().optional().nullable(),
|
||||
subtitles: z.string().optional().nullable(),
|
||||
download_size: z.string().optional().nullable(),
|
||||
director: z.string().optional().nullable(),
|
||||
duration: z.string().optional().nullable(),
|
||||
country: z.string().optional().nullable(),
|
||||
|
||||
release_date: z
|
||||
.string()
|
||||
.optional()
|
||||
.nullable()
|
||||
.transform((d) =>
|
||||
d
|
||||
? new Date(d).toLocaleDateString("en-us", {
|
||||
year: "numeric",
|
||||
})
|
||||
: undefined
|
||||
),
|
||||
})
|
||||
);
|
||||
|
||||
export const movieCreateSchema = movieBaseSchema.merge(z.object({}));
|
||||
export type MovieCreateType = z.infer<typeof movieCreateSchema>;
|
||||
|
||||
export const movieSchema = movieBaseSchema.merge(
|
||||
z.object({
|
||||
id: z.number().positive(),
|
||||
owner_id: z.number().positive(),
|
||||
update_date: z
|
||||
.string()
|
||||
.min(1)
|
||||
.transform((d) => new Date(d)),
|
||||
upload_date: z
|
||||
.string()
|
||||
.min(1)
|
||||
.transform((d) => new Date(d)),
|
||||
})
|
||||
);
|
||||
export type MovieType = z.infer<typeof movieSchema>;
|
||||
|
||||
export const isMovie = (a: any): a is MovieType => {
|
||||
return movieSchema.safeParse(a).success;
|
||||
};
|
||||
|
||||
export const moviesSchema = z.array(z.any()).transform((a) => {
|
||||
const games: MovieType[] = [];
|
||||
a.forEach((e) => {
|
||||
if (isMovie(e)) games.push(movieSchema.parse(e));
|
||||
else console.error("Movie parse error - ", e);
|
||||
});
|
||||
return games;
|
||||
});
|
||||
43
src/entities/item/movie/schemas/movieCard.ts
Normal file
43
src/entities/item/movie/schemas/movieCard.ts
Normal file
@@ -0,0 +1,43 @@
|
||||
import { z } from "zod";
|
||||
import { TypesOfItems } from "../../types";
|
||||
|
||||
export const movieCardBaseSchema = z.object({
|
||||
title: z.string().min(3, "Слишком короткое название"),
|
||||
cover: z.string().optional().nullable(),
|
||||
description: z.string().optional().nullable(),
|
||||
age: z.string().optional().nullable(),
|
||||
|
||||
// Добавляем к каждому фильму поле, которое
|
||||
// показывает, что item является фильмом
|
||||
type: z
|
||||
.any()
|
||||
.optional()
|
||||
.transform(() => TypesOfItems.movie),
|
||||
});
|
||||
|
||||
export const movieCardSchema = movieCardBaseSchema.merge(
|
||||
z.object({
|
||||
id: z.number().positive(),
|
||||
})
|
||||
);
|
||||
export type MovieCardType = z.infer<typeof movieCardSchema>;
|
||||
|
||||
export const isMovieCardStrict = (a: any): a is MovieCardType => {
|
||||
return movieCardSchema.safeParse(a).success;
|
||||
};
|
||||
|
||||
export const movieCardsSchema = z.array(z.any()).transform((a) => {
|
||||
const cards: MovieCardType[] = [];
|
||||
a.forEach((e) => {
|
||||
if (isMovieCardStrict(e)) cards.push(movieCardSchema.parse(e));
|
||||
else console.error("MovieCard parse error - ", e);
|
||||
});
|
||||
return cards;
|
||||
});
|
||||
|
||||
export const isMovie = (a: any): a is MovieCardType => {
|
||||
return (
|
||||
movieCardBaseSchema.safeParse(a).success &&
|
||||
(a as MovieCardType).type === TypesOfItems.movie
|
||||
);
|
||||
};
|
||||
62
src/entities/item/types.ts
Normal file
62
src/entities/item/types.ts
Normal file
@@ -0,0 +1,62 @@
|
||||
import { GameCreateType, GameType } from "./game/schemas/game";
|
||||
import { GameCardType } from "./game/schemas/gameCard";
|
||||
import { MovieCreateType, MovieType } from "./movie/schemas/movie";
|
||||
import { MovieCardType } from "./movie/schemas/movieCard";
|
||||
import {
|
||||
AudiobookCreateType,
|
||||
AudiobookType,
|
||||
} from "./audiobook/schemas/audiobook";
|
||||
import { AudiobookCardType } from "./audiobook/schemas/audiobookCard";
|
||||
|
||||
export type ItemType = GameType | MovieType | AudiobookType;
|
||||
export type ItemCardType = GameCardType | MovieCardType | AudiobookCardType;
|
||||
export type ItemCreateType =
|
||||
| GameCreateType
|
||||
| MovieCreateType
|
||||
| AudiobookCreateType;
|
||||
|
||||
export type UnionItemType = GameType & MovieType & AudiobookType;
|
||||
export type UnionItemCardType = GameCardType &
|
||||
MovieCardType &
|
||||
AudiobookCardType;
|
||||
export type UnionItemCreateType = GameCreateType &
|
||||
MovieCreateType &
|
||||
AudiobookCreateType;
|
||||
|
||||
export enum TypesOfItems {
|
||||
game,
|
||||
movie,
|
||||
audiobook,
|
||||
}
|
||||
|
||||
export type ItemSectionsType = "games" | "movies" | "audiobooks";
|
||||
export const ItemSections = [
|
||||
"games",
|
||||
"movies",
|
||||
"audiobooks",
|
||||
] as ItemSectionsType[];
|
||||
|
||||
export const isSection = (a: string): a is ItemSectionsType => {
|
||||
return (ItemSections as string[]).includes(a);
|
||||
};
|
||||
|
||||
export type ItemPropertiesDescriptionType<T extends ItemType | ItemCreateType> =
|
||||
{
|
||||
name: string;
|
||||
key: keyof T;
|
||||
value?: (item: T) => string;
|
||||
editable?: boolean;
|
||||
}[][];
|
||||
|
||||
export interface IItemService {
|
||||
GetCards(): Promise<ItemCardType[] | null>;
|
||||
Get(id: number): Promise<ItemType | null>;
|
||||
Add(info: ItemCreateType): Promise<ItemType | null>;
|
||||
Change(id: number, info: ItemCreateType): Promise<ItemType | null>;
|
||||
GetEmpty(): ItemCreateType;
|
||||
propertiesDescription: ItemPropertiesDescriptionType<UnionItemType>;
|
||||
}
|
||||
export const staticImplements =
|
||||
<T>() =>
|
||||
<U extends T>(constructor: U) =>
|
||||
constructor;
|
||||
Reference in New Issue
Block a user