Adiciona criacao de video
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -57,3 +57,5 @@ pids
|
|||||||
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
|
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
|
||||||
|
|
||||||
/generated/prisma
|
/generated/prisma
|
||||||
|
|
||||||
|
/generated/prisma
|
||||||
|
|||||||
@@ -44,8 +44,7 @@ model videos {
|
|||||||
situation video_situation
|
situation video_situation
|
||||||
error_message String? @db.VarChar(244)
|
error_message String? @db.VarChar(244)
|
||||||
clips_quantity Int?
|
clips_quantity Int?
|
||||||
times Json?
|
duration String? @db.VarChar(16)
|
||||||
duration Unsupported("interval")?
|
|
||||||
title String? @db.VarChar(244)
|
title String? @db.VarChar(244)
|
||||||
filename String? @db.VarChar(244)
|
filename String? @db.VarChar(244)
|
||||||
videoid String? @db.VarChar(244)
|
videoid String? @db.VarChar(244)
|
||||||
|
|||||||
@@ -9,9 +9,6 @@ import { VideosController } from './modules/videos/videos.controller';
|
|||||||
import { UsuariosModule } from './modules/usuarios/usuarios.module';
|
import { UsuariosModule } from './modules/usuarios/usuarios.module';
|
||||||
import { LoggerMiddleware } from './middleware/logger.middleware';
|
import { LoggerMiddleware } from './middleware/logger.middleware';
|
||||||
import { RolesGuard } from './modules/auth/roles.guard';
|
import { RolesGuard } from './modules/auth/roles.guard';
|
||||||
import { OllamaModule } from './modules/ollama/ollama.module';
|
|
||||||
import { GeminiService } from './gemini/gemini.service';
|
|
||||||
import { GeminiModule } from './gemini/gemini.module';
|
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [
|
imports: [
|
||||||
@@ -20,11 +17,9 @@ import { GeminiModule } from './gemini/gemini.module';
|
|||||||
VideosModule,
|
VideosModule,
|
||||||
AuthModule,
|
AuthModule,
|
||||||
UsuariosModule,
|
UsuariosModule,
|
||||||
OllamaModule,
|
|
||||||
GeminiModule,
|
|
||||||
],
|
],
|
||||||
controllers: [AppController],
|
controllers: [AppController],
|
||||||
providers: [AppService, RolesGuard, GeminiService],
|
providers: [AppService, RolesGuard],
|
||||||
})
|
})
|
||||||
export class AppModule {
|
export class AppModule {
|
||||||
configure(consumer: MiddlewareConsumer) {
|
configure(consumer: MiddlewareConsumer) {
|
||||||
|
|||||||
@@ -1,6 +1,10 @@
|
|||||||
import axios, { isAxiosError } from 'axios';
|
import axios, { isAxiosError } from 'axios';
|
||||||
|
|
||||||
import { Injectable, UnauthorizedException } from '@nestjs/common';
|
import {
|
||||||
|
BadRequestException,
|
||||||
|
Injectable,
|
||||||
|
UnauthorizedException,
|
||||||
|
} from '@nestjs/common';
|
||||||
|
|
||||||
import { LoginDto } from './dto/login.dto';
|
import { LoginDto } from './dto/login.dto';
|
||||||
|
|
||||||
@@ -42,10 +46,10 @@ export class AuthService {
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (isAxiosError(error)) {
|
if (isAxiosError(error)) {
|
||||||
console.error(error.response?.data);
|
console.error(error.response?.data);
|
||||||
throw new UnauthorizedException(error.response?.data);
|
throw new BadRequestException(error.response?.data);
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new UnauthorizedException('Usuário ou senha inválidos');
|
throw new BadRequestException('Usuário ou senha inválidos');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,4 @@
|
|||||||
import dayjs from 'dayjs';
|
import { Type } from 'class-transformer';
|
||||||
import duration from 'dayjs/plugin/duration';
|
|
||||||
|
|
||||||
import { Transform } from 'class-transformer';
|
|
||||||
import {
|
import {
|
||||||
IsArray,
|
IsArray,
|
||||||
IsNumber,
|
IsNumber,
|
||||||
@@ -11,8 +8,6 @@ import {
|
|||||||
MaxLength,
|
MaxLength,
|
||||||
} from 'class-validator';
|
} from 'class-validator';
|
||||||
|
|
||||||
dayjs.extend(duration);
|
|
||||||
|
|
||||||
export class CreateVideoDto {
|
export class CreateVideoDto {
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@IsString({ message: 'Id deve ser uma string' })
|
@IsString({ message: 'Id deve ser uma string' })
|
||||||
@@ -46,34 +41,29 @@ export class CreateVideoDto {
|
|||||||
@MaxLength(244)
|
@MaxLength(244)
|
||||||
videoid?: string;
|
videoid?: string;
|
||||||
|
|
||||||
@IsOptional()
|
@IsNumber()
|
||||||
@IsString({ message: 'Duration deve ser uma string' })
|
@Type(() => Number)
|
||||||
@Transform(({ value }: { value: number }) => {
|
duration!: number;
|
||||||
const duration = dayjs.duration(value, 'seconds');
|
|
||||||
|
|
||||||
return duration.format('HH:mm:ss');
|
|
||||||
})
|
|
||||||
duration?: string;
|
|
||||||
|
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@IsString()
|
@IsString()
|
||||||
@MaxLength(244)
|
@MaxLength(244)
|
||||||
description?: string;
|
description?: string;
|
||||||
|
|
||||||
|
@IsString()
|
||||||
|
@MaxLength(244)
|
||||||
|
qualidade: string;
|
||||||
|
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@IsUrl()
|
@IsUrl()
|
||||||
@MaxLength(244)
|
@MaxLength(244)
|
||||||
transcriptionUrl?: string;
|
transcriptionUrl?: string;
|
||||||
|
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
|
@Type(() => Number)
|
||||||
@IsNumber(
|
@IsNumber(
|
||||||
{ allowNaN: false, allowInfinity: false },
|
{ allowNaN: false, allowInfinity: false },
|
||||||
{ message: 'Timestamp deve ser um número' },
|
{ message: 'Timestamp deve ser um número' },
|
||||||
)
|
)
|
||||||
@Transform(({ value }: { value: number }) => {
|
timestamp?: number;
|
||||||
const duration = dayjs(value * 1000);
|
|
||||||
|
|
||||||
return duration.format('DD/MM/YYYY HH:mm:ss');
|
|
||||||
})
|
|
||||||
timestamp?: string;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,15 @@
|
|||||||
import {
|
import {
|
||||||
|
Body,
|
||||||
Controller,
|
Controller,
|
||||||
|
Delete,
|
||||||
Get,
|
Get,
|
||||||
Param,
|
Param,
|
||||||
Query,
|
|
||||||
Patch,
|
Patch,
|
||||||
Body,
|
|
||||||
Delete,
|
|
||||||
UseGuards,
|
|
||||||
Post,
|
Post,
|
||||||
|
Query,
|
||||||
|
UseGuards,
|
||||||
|
UsePipes,
|
||||||
|
ValidationPipe,
|
||||||
} from '@nestjs/common';
|
} from '@nestjs/common';
|
||||||
import { Prisma, videos, video_situation } from 'generated/prisma';
|
import { Prisma, videos, video_situation } from 'generated/prisma';
|
||||||
|
|
||||||
@@ -57,6 +59,13 @@ export class VideosController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Post()
|
@Post()
|
||||||
|
@UsePipes(
|
||||||
|
new ValidationPipe({
|
||||||
|
whitelist: true,
|
||||||
|
forbidNonWhitelisted: true,
|
||||||
|
transform: true,
|
||||||
|
}),
|
||||||
|
)
|
||||||
@Roles('user', 'admin')
|
@Roles('user', 'admin')
|
||||||
async create(@Body() body: CreateVideoDto): Promise<void> {
|
async create(@Body() body: CreateVideoDto): Promise<void> {
|
||||||
await this.videosService.createNewVideo(body);
|
await this.videosService.createNewVideo(body);
|
||||||
|
|||||||
@@ -12,6 +12,24 @@ import { PaginatedResponse } from '@shared/dto/paginated';
|
|||||||
import { ListVideosQueryDto } from './dto/list-videos-query.dto';
|
import { ListVideosQueryDto } from './dto/list-videos-query.dto';
|
||||||
import { VideoMetadataDto } from '@shared/dto/video-metadata';
|
import { VideoMetadataDto } from '@shared/dto/video-metadata';
|
||||||
import { CreateVideoDto } from './dto/create-video-dto';
|
import { CreateVideoDto } from './dto/create-video-dto';
|
||||||
|
import { N8N_REMOTE_SERVER } from '@/shared/url';
|
||||||
|
|
||||||
|
const formatSecondsToClock = (totalSeconds: number): string => {
|
||||||
|
if (!Number.isFinite(totalSeconds)) {
|
||||||
|
return '00:00:00';
|
||||||
|
}
|
||||||
|
|
||||||
|
const safeSeconds = Math.max(0, Math.floor(totalSeconds));
|
||||||
|
const hours = Math.floor(safeSeconds / 3600)
|
||||||
|
.toString()
|
||||||
|
.padStart(2, '0');
|
||||||
|
const minutes = Math.floor((safeSeconds % 3600) / 60)
|
||||||
|
.toString()
|
||||||
|
.padStart(2, '0');
|
||||||
|
const seconds = (safeSeconds % 60).toString().padStart(2, '0');
|
||||||
|
|
||||||
|
return `${hours}:${minutes}:${seconds}`;
|
||||||
|
};
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class VideosService {
|
export class VideosService {
|
||||||
@@ -137,17 +155,62 @@ export class VideosService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async createNewVideo(data: CreateVideoDto): Promise<void> {
|
async createNewVideo(data: CreateVideoDto): Promise<void> {
|
||||||
const { url, videoid } = data;
|
const { url, videoid, duration, qualidade, title, timestamp } = data;
|
||||||
|
|
||||||
|
const query: Prisma.videosWhereInput = { url };
|
||||||
|
|
||||||
|
if (videoid) {
|
||||||
|
query.videoid = videoid;
|
||||||
|
}
|
||||||
|
|
||||||
const searchUrl = await this.prisma.videos.findFirst({
|
const searchUrl = await this.prisma.videos.findFirst({
|
||||||
where: { url, videoid },
|
where: query,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!isEmpty(searchUrl)) {
|
if (searchUrl && !isEmpty(searchUrl)) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Esta video já foi renderizado, e se encontra na situação ${searchUrl.situation}`,
|
`Esta video já foi renderizado, e se encontra na situação ${searchUrl.situation}`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const formattedDuration = Number.isFinite(duration)
|
||||||
|
? formatSecondsToClock(duration)
|
||||||
|
: undefined;
|
||||||
|
|
||||||
|
const createData: Prisma.videosCreateInput = {
|
||||||
|
url,
|
||||||
|
situation: video_situation.FILA,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (videoid) {
|
||||||
|
createData.videoid = videoid;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (title) {
|
||||||
|
createData.title = title;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (formattedDuration) {
|
||||||
|
createData.duration = formattedDuration;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof timestamp === 'number' && Number.isFinite(timestamp)) {
|
||||||
|
createData.datetime_download = new Date(timestamp * 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.prisma.videos.create({
|
||||||
|
data: createData,
|
||||||
|
});
|
||||||
|
|
||||||
|
// const params = {
|
||||||
|
// url,
|
||||||
|
// videoid,
|
||||||
|
// qualidade: qualidade === 'automatic' ? 'medium' : qualidade,
|
||||||
|
// };
|
||||||
|
|
||||||
|
// await axios.get(`${N8N_REMOTE_SERVER}/webhook/download-video`, {
|
||||||
|
// params,
|
||||||
|
// });
|
||||||
}
|
}
|
||||||
|
|
||||||
async update(id: number, data: Prisma.videosUpdateInput): Promise<videos> {
|
async update(id: number, data: Prisma.videosUpdateInput): Promise<videos> {
|
||||||
|
|||||||
1
src/shared/url.ts
Normal file
1
src/shared/url.ts
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export const N8N_REMOTE_SERVER = 'https://becoming-poetic-lamb.ngrok-free.app';
|
||||||
Reference in New Issue
Block a user