feat: SEO Power Engine backend updates and remove temp media files
Backend Deploy 🚀 / build-and-deploy (push) Has been cancelled

This commit is contained in:
Harun CAN
2026-04-30 16:57:46 +02:00
parent 7745102584
commit 35bfc311e7
14 changed files with 828 additions and 88 deletions
+103 -12
View File
@@ -26,7 +26,15 @@ import {
} from '@nestjs/swagger';
import { FileInterceptor } from '@nestjs/platform-express';
import { ProjectsService } from './projects.service';
import { CreateProjectDto, UpdateProjectDto, CreateFromTweetDto, CreateFromYoutubeDto, CreateFromDocumentDto, CreateFromExtractedTextDto } from './dto/project.dto';
import {
CreateProjectDto,
UpdateProjectDto,
CreateFromTweetDto,
CreateFromYoutubeDto,
CreateFromDocumentDto,
CreateFromExtractedTextDto,
CreateFromTextDto,
} from './dto/project.dto';
@ApiTags('projects')
@ApiBearerAuth()
@@ -71,6 +79,17 @@ export class ProjectsController {
});
}
/**
* Render kuyruğu genel görünümü — aktif, bekleyen ve son tamamlanan işler.
*/
@Get('render-queue')
@ApiOperation({ summary: 'Render kuyruğu genel görünümünü getir' })
@ApiResponse({ status: 200, description: 'Render kuyruk özeti' })
async getRenderQueue(@Req() req: any) {
const userId = req.user?.id || req.user?.sub;
return this.projectsService.getRenderQueue(userId);
}
/**
* Tek bir projeyi sahneleri ve medya asset'leriyle birlikte getirir.
*/
@@ -157,17 +176,6 @@ export class ProjectsController {
return this.projectsService.cancelRenderJob(userId, id);
}
/**
* Render kuyruğu genel görünümü — aktif, bekleyen ve son tamamlanan işler.
*/
@Get('render-queue')
@ApiOperation({ summary: 'Render kuyruğu genel görünümünü getir' })
@ApiResponse({ status: 200, description: 'Render kuyruk özeti' })
async getRenderQueue(@Req() req: any) {
const userId = req.user?.id || req.user?.sub;
return this.projectsService.getRenderQueue(userId);
}
/**
* X/Twitter tweet URL'sinden otomatik proje oluşturur ve senaryo üretir.
* Tweet çekilir → prompt'a dönüştürülür → AI senaryo üretir → proje kaydedilir.
@@ -190,12 +198,26 @@ export class ProjectsController {
@HttpCode(HttpStatus.CREATED)
@ApiOperation({ summary: 'YouTube videosundan proje oluştur' })
@ApiResponse({ status: 201, description: 'YouTube videosundan proje oluşturuldu ve senaryo üretildi' })
@ApiResponse({ status: 400, description: 'Geçersiz YouTube URL\'si veya video bulunamadı' })
async createFromYoutube(@Body() dto: CreateFromYoutubeDto, @Req() req: any) {
const userId = req.user?.id || req.user?.sub;
this.logger.log(`YouTube'dan proje oluşturuluyor: ${dto.youtubeUrl}`);
return this.projectsService.createFromYoutube(userId, dto);
}
/**
* Serbest metin veya fikir üzerinden proje oluşturur.
*/
@Post('from-text')
@HttpCode(HttpStatus.CREATED)
@ApiOperation({ summary: 'Serbest metinden proje oluştur' })
@ApiResponse({ status: 201, description: 'Metinden proje oluşturuldu ve senaryo üretildi' })
async createFromText(@Body() dto: CreateFromTextDto, @Req() req: any) {
const userId = req.user?.id || req.user?.sub;
this.logger.log(`Serbest metinden proje oluşturuluyor...`);
return this.projectsService.createFromText(userId, dto);
}
/**
* Yüklenen dokümandan (Word, PDF, Excel vb.) otomatik proje oluşturur.
*/
@@ -285,6 +307,75 @@ export class ProjectsController {
return this.projectsService.regenerateScene(userId, id, sceneId);
}
@Delete(':id/media/:mediaId')
@ApiOperation({ summary: 'Sahnede bulunan bir medyayı (MediaAsset) sil' })
@ApiResponse({ status: 200, description: 'Medya başarıyla silindi' })
async deleteMedia(
@Param('id', ParseUUIDPipe) id: string,
@Param('mediaId', ParseUUIDPipe) mediaId: string,
@Req() req: any,
) {
const userId = req.user?.id || req.user?.sub;
this.logger.log(`Medya silme isteği. Proje: ${id}, Medya: ${mediaId}`);
return this.projectsService.deleteSceneMedia(userId, id, mediaId);
}
/**
* Proje için 5 yeni SEO-optimized başlık üretir (Gemini AI).
*/
@Post(':id/generate-seo-titles')
@HttpCode(HttpStatus.OK)
@ApiOperation({ summary: 'AI ile 5 yeni SEO başlığı üret' })
@ApiResponse({ status: 200, description: 'SEO başlıkları başarıyla üretildi' })
async generateSeoTitles(
@Param('id', ParseUUIDPipe) id: string,
@Req() req: any,
) {
const userId = req.user?.id || req.user?.sub;
this.logger.log(`SEO başlık üretimi isteniyor: ${id}`);
return this.projectsService.generateSeoTitles(userId, id);
}
/**
* Alternatif SEO başlıklarından birini seçerek projenin ana başlığını günceller.
*/
@Patch(':id/select-title')
@HttpCode(HttpStatus.OK)
@ApiOperation({ summary: 'SEO başlığı seç ve proje başlığını güncelle' })
@ApiResponse({ status: 200, description: 'Başlık başarıyla güncellendi' })
async selectSeoTitle(
@Param('id', ParseUUIDPipe) id: string,
@Body('title') title: string,
@Req() req: any,
) {
const userId = req.user?.id || req.user?.sub;
if (!title) {
throw new BadRequestException('Başlık (title) belirtilmelidir.');
}
this.logger.log(`SEO başlık seçimi: ${id} — "${title}"`);
return this.projectsService.selectSeoTitle(userId, id, title);
}
/**
* Projeyi farklı bir dile çevirir.
*/
@Post(':id/translate')
@HttpCode(HttpStatus.CREATED)
@ApiOperation({ summary: 'Projeyi farklı bir dile çevir ve kopyasını oluştur' })
@ApiResponse({ status: 201, description: 'Proje çevirisi başarıyla tamamlandı' })
async translateProject(
@Param('id', ParseUUIDPipe) id: string,
@Body('targetLanguage') targetLanguage: string,
@Req() req: any,
) {
if (!targetLanguage) {
throw new BadRequestException('Hedef dil (targetLanguage) belirtilmelidir.');
}
const userId = req.user?.id || req.user?.sub;
this.logger.log(`Proje çevirisi isteniyor: ${id} -> ${targetLanguage}`);
return this.projectsService.translateProject(userId, id, targetLanguage);
}
/**
* Sahne için ID bazında görsel üret (Gemini AI).
* Kullanıcı custom prompt sağlarsa, önce prompt güncellenir ardından resim üretilir.