-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: ✨ (api) transcribeYoutubeVideo を実装
- Loading branch information
Showing
13 changed files
with
163 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,5 +4,9 @@ schema.gql | |
# auto generated folder | ||
generated | ||
|
||
# tmp files | ||
tmp/* | ||
!tmp/.gitkeep | ||
|
||
# certificates | ||
*.crt |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
import { Controller, Get, Inject, Logger, Param } from '@nestjs/common'; | ||
import { OpenaiService } from '#api/infra/openai/openai.service'; | ||
import { YoutubeService } from '../youtube/youtube.service'; | ||
|
||
@Controller() | ||
export class LangchainController { | ||
private readonly logger = new Logger(LangchainController.name); | ||
|
||
constructor( | ||
@Inject(OpenaiService) | ||
private readonly openaiService: OpenaiService, | ||
@Inject(YoutubeService) | ||
private readonly youtubeService: YoutubeService, | ||
) {} | ||
|
||
@Get('/predict/:id') | ||
async transcribeYoutubeVideo(@Param('id') id: string): Promise<string> { | ||
this.logger.log(`${this.transcribeYoutubeVideo.name} called`); | ||
this.logger.debug(`Transcribing video: https://www.youtube.com/watch?v=${id}`); | ||
|
||
const outputFilePath = await this.youtubeService.saveVideoStreamToFile(`https://www.youtube.com/watch?v=${id}`); | ||
|
||
const transcription = await this.openaiService.transcribeFromFile(outputFilePath); | ||
|
||
return transcription; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
import { Module } from '@nestjs/common'; | ||
import { OpenaiService } from './openai.service'; | ||
|
||
@Module({ | ||
providers: [OpenaiService], | ||
exports: [OpenaiService], | ||
}) | ||
export class OpenaiModule {} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
import fs from 'node:fs'; | ||
import { Inject, Injectable } from '@nestjs/common'; | ||
import { OpenAI } from 'openai'; | ||
import { EnvService } from '#api/common/service/env/env.service'; | ||
|
||
@Injectable() | ||
export class OpenaiService { | ||
private readonly openai: OpenAI; | ||
|
||
constructor(@Inject(EnvService) private readonly envService: EnvService) { | ||
this.openai = new OpenAI({ apiKey: this.envService.OpenaiApiKey }); | ||
} | ||
|
||
async transcribeFromFile(path: string): Promise<string> { | ||
const res = await this.openai.audio.transcriptions.create({ | ||
model: 'whisper-1', | ||
language: 'ko', | ||
timestamp_granularities: ['segment'], | ||
response_format: 'verbose_json', | ||
file: fs.createReadStream(path), | ||
}); | ||
|
||
return res.text; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
import { Module } from '@nestjs/common'; | ||
import { YoutubeService } from './youtube.service'; | ||
|
||
@Module({ | ||
providers: [YoutubeService], | ||
exports: [YoutubeService], | ||
}) | ||
export class YoutubeModule {} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
import fs from 'node:fs'; | ||
import ytdl from '@distube/ytdl-core'; | ||
import { Injectable, Logger } from '@nestjs/common'; | ||
import ffmpeg from 'fluent-ffmpeg'; | ||
|
||
@Injectable() | ||
export class YoutubeService { | ||
private readonly logger = new Logger(YoutubeService.name); | ||
|
||
async saveVideoStreamToFile(url: string): Promise<string> { | ||
const videoInfo = await this.getVideoInfo(url); | ||
|
||
const downloadedPath = this.isAlreadyDownloaded(videoInfo.videoDetails.videoId) | ||
? `./tmp/${videoInfo.videoDetails.videoId}.mp4` | ||
: await this.getSoundOnlyVideoStream(videoInfo); | ||
|
||
const audioFilePath = this.isAlreadyConverted(videoInfo.videoDetails.videoId) | ||
? `./tmp/${videoInfo.videoDetails.videoId}.mp3` | ||
: await this.convertToMp3(downloadedPath, videoInfo.videoDetails.videoId); | ||
|
||
return audioFilePath; | ||
} | ||
|
||
private async getVideoInfo(url: string) { | ||
const videoInfo = await ytdl.getInfo(url); | ||
|
||
return videoInfo; | ||
} | ||
|
||
private isAlreadyDownloaded(id: string): boolean { | ||
const downloaded = fs.existsSync(`./tmp/${id}.mp4`); | ||
|
||
return downloaded; | ||
} | ||
|
||
private async getSoundOnlyVideoStream(videoInfo: ytdl.videoInfo): Promise<string> { | ||
const stream = await ytdl | ||
.downloadFromInfo(videoInfo, { | ||
filter: 'audioonly', | ||
requestOptions: { | ||
reset: true, | ||
}, | ||
}) | ||
.pipe(fs.createWriteStream(`./tmp/${videoInfo.videoDetails.videoId}.mp4`)); | ||
|
||
this.logger.debug(`Downloaded: ${videoInfo.videoDetails.videoId}`); | ||
|
||
return stream.path as string; | ||
} | ||
|
||
private isAlreadyConverted(id: string): boolean { | ||
const downloaded = fs.existsSync(`./tmp/${id}.mp3`); | ||
|
||
return downloaded; | ||
} | ||
|
||
private async convertToMp3(inputPath: string, id: string): Promise<string> { | ||
await new Promise<void>((resolve, reject) => { | ||
ffmpeg(inputPath) | ||
.outputFormat('mp3') | ||
.on('end', () => { | ||
resolve(void undefined); | ||
}) | ||
.on('error', error => { | ||
reject(error); | ||
}) | ||
.save(`./tmp/${id}.mp3`); | ||
}); | ||
|
||
this.logger.debug(`Converted to mp3: ${id}`); | ||
|
||
return `./tmp/${id}.mp3`; | ||
} | ||
} |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters