Adiciona criacao de video

This commit is contained in:
LeoMortari
2025-11-02 00:07:42 -03:00
parent 43be1244c7
commit ca38cee6e3
8 changed files with 101 additions and 38 deletions

2
.gitignore vendored
View File

@@ -57,3 +57,5 @@ pids
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
/generated/prisma
/generated/prisma

View File

@@ -44,8 +44,7 @@ model videos {
situation video_situation
error_message String? @db.VarChar(244)
clips_quantity Int?
times Json?
duration Unsupported("interval")?
duration String? @db.VarChar(16)
title String? @db.VarChar(244)
filename String? @db.VarChar(244)
videoid String? @db.VarChar(244)

View File

@@ -9,9 +9,6 @@ import { VideosController } from './modules/videos/videos.controller';
import { UsuariosModule } from './modules/usuarios/usuarios.module';
import { LoggerMiddleware } from './middleware/logger.middleware';
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({
imports: [
@@ -20,11 +17,9 @@ import { GeminiModule } from './gemini/gemini.module';
VideosModule,
AuthModule,
UsuariosModule,
OllamaModule,
GeminiModule,
],
controllers: [AppController],
providers: [AppService, RolesGuard, GeminiService],
providers: [AppService, RolesGuard],
})
export class AppModule {
configure(consumer: MiddlewareConsumer) {

View File

@@ -1,6 +1,10 @@
import axios, { isAxiosError } from 'axios';
import { Injectable, UnauthorizedException } from '@nestjs/common';
import {
BadRequestException,
Injectable,
UnauthorizedException,
} from '@nestjs/common';
import { LoginDto } from './dto/login.dto';
@@ -42,10 +46,10 @@ export class AuthService {
} catch (error) {
if (isAxiosError(error)) {
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');
}
}

View File

@@ -1,7 +1,4 @@
import dayjs from 'dayjs';
import duration from 'dayjs/plugin/duration';
import { Transform } from 'class-transformer';
import { Type } from 'class-transformer';
import {
IsArray,
IsNumber,
@@ -11,8 +8,6 @@ import {
MaxLength,
} from 'class-validator';
dayjs.extend(duration);
export class CreateVideoDto {
@IsOptional()
@IsString({ message: 'Id deve ser uma string' })
@@ -46,34 +41,29 @@ export class CreateVideoDto {
@MaxLength(244)
videoid?: string;
@IsOptional()
@IsString({ message: 'Duration deve ser uma string' })
@Transform(({ value }: { value: number }) => {
const duration = dayjs.duration(value, 'seconds');
return duration.format('HH:mm:ss');
})
duration?: string;
@IsNumber()
@Type(() => Number)
duration!: number;
@IsOptional()
@IsString()
@MaxLength(244)
description?: string;
@IsString()
@MaxLength(244)
qualidade: string;
@IsOptional()
@IsUrl()
@MaxLength(244)
transcriptionUrl?: string;
@IsOptional()
@Type(() => Number)
@IsNumber(
{ allowNaN: false, allowInfinity: false },
{ message: 'Timestamp deve ser um número' },
)
@Transform(({ value }: { value: number }) => {
const duration = dayjs(value * 1000);
return duration.format('DD/MM/YYYY HH:mm:ss');
})
timestamp?: string;
timestamp?: number;
}

View File

@@ -1,13 +1,15 @@
import {
Body,
Controller,
Delete,
Get,
Param,
Query,
Patch,
Body,
Delete,
UseGuards,
Post,
Query,
UseGuards,
UsePipes,
ValidationPipe,
} from '@nestjs/common';
import { Prisma, videos, video_situation } from 'generated/prisma';
@@ -57,6 +59,13 @@ export class VideosController {
}
@Post()
@UsePipes(
new ValidationPipe({
whitelist: true,
forbidNonWhitelisted: true,
transform: true,
}),
)
@Roles('user', 'admin')
async create(@Body() body: CreateVideoDto): Promise<void> {
await this.videosService.createNewVideo(body);

View File

@@ -12,6 +12,24 @@ import { PaginatedResponse } from '@shared/dto/paginated';
import { ListVideosQueryDto } from './dto/list-videos-query.dto';
import { VideoMetadataDto } from '@shared/dto/video-metadata';
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()
export class VideosService {
@@ -137,17 +155,62 @@ export class VideosService {
}
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({
where: { url, videoid },
where: query,
});
if (!isEmpty(searchUrl)) {
if (searchUrl && !isEmpty(searchUrl)) {
throw new Error(
`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> {

1
src/shared/url.ts Normal file
View File

@@ -0,0 +1 @@
export const N8N_REMOTE_SERVER = 'https://becoming-poetic-lamb.ngrok-free.app';