diff --git a/.gitignore b/.gitignore index fee794f..8e63aff 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,6 @@ dist .env new.mp4 basicaudio.mp3 -basicaudio.wav \ No newline at end of file +basicaudio.wav +basicaudio.wav.srt +shorts \ No newline at end of file diff --git a/src/index.ts b/src/index.ts index 736582a..e82222c 100644 --- a/src/index.ts +++ b/src/index.ts @@ -13,11 +13,11 @@ const outputFilePath = path.join(__dirname, '..', 'basicaudio.wav') const videoFilePath = path.join(__dirname, '..', 'new.mp4') -const outputVideoFilePath = path.join(__dirname, '..', 'ytshort.mp4') +const outputVideoFilePath = path.join(__dirname, '..', 'shorts', 'ytshort.mp4') const generateYoutubeShort = async () => { try { - const script = await createShortScript({ language: 'english', topic: 'historical fact' }) + const script = await createShortScript({ language: 'english', topic: 'world war fact' }) console.log('SCRIPT GENERATED: ', script) if (!script) throw new Error('Script not generated') @@ -30,12 +30,13 @@ const generateYoutubeShort = async () => { await whisper(outputFilePath) - console.log('TRANSCRIPT GENERATED') + console.log('MERGING AUDIO AND VIDEO') - await whisper(outputFilePath) - console.log('MERGING AUDIO...') - - mergeAudio({ videoFilePath, audioFilePath: outputFilePath, outputVideoPath: outputVideoFilePath }) + await mergeAudio({ + videoFilePath, + audioFilePath: outputFilePath, + outputVideoPath: outputVideoFilePath, + }) } catch (error) { console.log('Error in createShortScript: ', error) } diff --git a/src/transcript/transcribe.ts b/src/transcript/transcribe.ts index 22c16a0..8a55373 100644 --- a/src/transcript/transcribe.ts +++ b/src/transcript/transcribe.ts @@ -3,7 +3,6 @@ import { nodewhisper } from 'nodejs-whisper' export const whisper = async (filePath: string, options?: any) => { try { - console.log('Transcribing', filePath) const transcript = await nodewhisper(filePath, { modelName: 'tiny.en', whisperOptions: { diff --git a/src/video/video.ts b/src/video/video.ts index 453ae15..64c88cf 100644 --- a/src/video/video.ts +++ b/src/video/video.ts @@ -1,7 +1,6 @@ import ffmpeg from 'fluent-ffmpeg' import fs from 'fs' - -const videoFilePath = 'new.mp4' // Replace with your original video file path +import path from 'path' export const mergeAudio = async ({ videoFilePath, @@ -12,68 +11,58 @@ export const mergeAudio = async ({ audioFilePath: string outputVideoPath: string }) => { - fs.access(videoFilePath, fs.constants.F_OK, err => { - console.log(`${videoFilePath} ${err ? 'does not exist' : 'exists'}`) - }) - - fs.access(videoFilePath, fs.constants.F_OK, err => { - console.log(`${audioFilePath} ${err ? 'does not exist' : 'exists'}`) - }) - - fs.access(videoFilePath, fs.constants.F_OK, err => { - console.log(`${outputVideoPath} ${err ? 'does not exist' : 'exists'}`) + const videodata: any = await new Promise((resolve, reject) => { + ffmpeg.ffprobe(videoFilePath, (err, videoMetadata) => { + if (err) { + console.error('Error getting video duration:', err.message) + reject(err) + } + resolve(videoMetadata) + }) }) - ffmpeg.ffprobe(videoFilePath, (err, videoMetadata) => { - if (err) { - console.error('Error getting video duration:', err.message) - return - } - - const videoDurationInSeconds = videoMetadata.format.duration - + const audiodata: any = await new Promise((resolve, reject) => { ffmpeg.ffprobe(audioFilePath, (err, audioMetadata) => { if (err) { console.error('Error getting audio duration:', err.message) - return + reject(err) } + resolve(audioMetadata) + }) + }) + + // console.log('videodata: ', videodata) + // console.log('audiodata: ', audiodata) - const audioDurationInSeconds = audioMetadata.format.duration + const videoDurationInSeconds = videodata.format.duration - // Calculate how many times the audio needs to be repeated to match the video duration - const trimDuration = Math.min(videoDurationInSeconds!, audioDurationInSeconds!) - const audioSpeed = 1 - const targetWidth = 1080 - const targetHeight = 1920 - const adjustedTrimDuration = trimDuration / audioSpeed - const videoFilter = `scale=${targetWidth}:-1,pad=${targetWidth}:${targetHeight}:(ow-iw)/2:(oh-ih)/2:black` - const audioFilter = `atempo=${audioSpeed},aformat=sample_rates=44100:channel_layouts=stereo` - const srtFilePath = 'basicaudio.wav.srt' - // Merge the audio with the video - ffmpeg(videoFilePath) - .inputOptions(`-t ${adjustedTrimDuration}`) - .input(audioFilePath) + const audioDurationInSeconds = audiodata.format.duration - .videoFilter(videoFilter) - .audioFilter(audioFilter) - .outputOptions([ - '-vf', - `subtitles=${srtFilePath}`, - '-map', - '0:v', - '-map', - '1:a', - '-c:v libx264', - '-c:a aac', - ]) - .output(outputVideoPath) - .on('end', () => { - console.log('Audio added to video complete!') - }) - .on('error', err => { - console.error('Error during audio adding to video:', err.message) - }) - .run() + // Calculate how many times the audio needs to be repeated to match the video duration + const trimDuration = Math.min(videoDurationInSeconds!, audioDurationInSeconds!) + const audioSpeed = 1 + const targetWidth = 1080 + const targetHeight = 1920 + const adjustedTrimDuration = trimDuration / audioSpeed + const videoFilter = `scale=${targetWidth}:-1,pad=${targetWidth}:${targetHeight}:(ow-iw)/2:(oh-ih)/2:black` + const audioFilter = `atempo=${audioSpeed},aformat=sample_rates=44100:channel_layouts=stereo` + const srtFilePath = 'basicaudio.wav.srt' + const newSrtFilePath = path.join(__dirname, '..', '..', srtFilePath) + + // Merge the audio with the video + ffmpeg(videoFilePath) + .inputOptions(`-t ${adjustedTrimDuration}`) + .input(audioFilePath) + + .videoFilter(videoFilter) + .audioFilter(audioFilter) + .outputOptions(['-vf', `subtitles=${newSrtFilePath}`, '-map', '0:v', '-map', '1:a', '-c:v libx264', '-c:a aac']) + .output(outputVideoPath) + .on('end', () => { + console.log('Audio added to video complete!') }) - }) + .on('error', err => { + console.error('Error during audio adding to video:', err.message) + }) + .run() }