Skip to content

Commit

Permalink
feat: add upload videos to yt function
Browse files Browse the repository at this point in the history
  • Loading branch information
ChetanXpro committed Aug 9, 2023
1 parent 49e1a86 commit 74d4012
Show file tree
Hide file tree
Showing 9 changed files with 1,916 additions and 262 deletions.
Binary file removed basicaudio.mp3
Binary file not shown.
1,959 changes: 1,714 additions & 245 deletions package-lock.json

Large diffs are not rendered by default.

5 changes: 4 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,18 @@
"dependencies": {
"axios": "^1.4.0",
"dotenv": "^16.3.1",
"express": "^4.18.2",
"fluent-ffmpeg": "^2.1.2",
"fs-extra": "^11.1.1",
"googleapis": "^123.0.0",
"langchain": "0.0.124",
"nodejs-whisper": "0.1.1",
"nodejs-whisper": "^0.1.3",
"openai": "^3.3.0",
"shelljs": "^0.8.5"
},
"devDependencies": {
"@types/dotenv": "^8.2.0",
"@types/express": "^4.17.17",
"@types/fluent-ffmpeg": "^2.1.21",
"@types/fs-extra": "^11.0.1",
"@types/node": "^20.4.8",
Expand Down
6 changes: 6 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,13 @@ import { createShortScript } from './audio/videoScript'
import { convertToWav, createAudio } from './audio/elevenAudio'
import { whisper } from './transcript/transcribe'

import express from 'express'
const app = express()
import path from 'path'

import { mergeAudio } from './video/video'

import { uploadVideos } from './upoadYt/upload'
const inputFilePath = path.join(__dirname, '..', 'basicaudio.mp3')

const outputFilePath = path.join(__dirname, '..', 'basicaudio.wav')
Expand Down Expand Up @@ -37,6 +41,8 @@ const generateYoutubeShort = async () => {
audioFilePath: outputFilePath,
outputVideoPath: outputVideoFilePath,
})

uploadVideos('facts', 'facts', ['#facts', '#trending', '#shorts'], outputVideoFilePath)
} catch (error) {
console.log('Error in createShortScript: ', error)
}
Expand Down
2 changes: 1 addition & 1 deletion src/promptTemplates/templates.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ You are an expert video writer. You ONLY produce text that is read. You only pro
Your script will not have any reference to the audio footage / video footage shown. Only the text that will be narrated by the voice actor.
You will produce purely text.
Don't write any other textual thing than the text itself.
Make sure the text is not longer than "170" words (keep the video pretty short and neat).
Make sure the text is not longer than "150" words (keep the video pretty short and neat).
specifications delimited by angle brackets .
Expand Down
3 changes: 2 additions & 1 deletion src/transcript/transcribe.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,11 @@ export const whisper = async (filePath: string, options?: any) => {
try {
const transcript = await nodewhisper(filePath, {
modelName: 'tiny.en',
autoDownloadModelName: 'tiny.en',
whisperOptions: {
outputInSrt: true, // get output result in srt file

timestamps_length: 20, // amount of dialogue per timestamp pair
timestamps_length: 10, // amount of dialogue per timestamp pair
splitOnWord: true, //split on word rather than on token
},
})
Expand Down
2 changes: 2 additions & 0 deletions src/upoadYt/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
cred.json
token.json
158 changes: 158 additions & 0 deletions src/upoadYt/upload.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
// const fs = require('fs')
import fs from 'fs'
import readline from 'readline'

import assert from 'assert'
import { google } from 'googleapis'
import path from 'path'

const OAuth2 = google.auth.OAuth2

// video category IDs for YouTube:
const categoryIds = {
Entertainment: '24',
Education: '27',
ScienceTechnology: '28',
}

// If modifying these scopes, delete your previously saved credentials in client_oauth_token.json
const SCOPES = ['https://www.googleapis.com/auth/youtube.upload']
const TOKEN_PATH = path.join(__dirname, 'token.json')
const CLIENT_CRED = path.join(__dirname, 'cred.json')

// const thumbFilePath = "../thumb.png";

export const uploadVideos = (title: string, description: string, tags: string[], videoPath: string) => {
assert(fs.existsSync(videoPath))
// assert(fs.existsSync(thumbFilePath));

// Load client secrets from a local file.
fs.readFile(CLIENT_CRED, function processClientSecrets(err, content: any) {
if (err) {
console.log('Error loading client secret file: ' + err)
return
}
// Authorize a client with the loaded credentials, then call the YouTube API.
authorize(JSON.parse(content), (auth: any) => uploadVideo(auth, title, description, tags, videoPath))
})
}

/**
* Upload the video file.
*
* @param {google.auth.OAuth2} auth An authorized OAuth2 client.
*/
function uploadVideo(auth: any, title: string, description: string, tags: any, videoPath: string) {
const service = google.youtube('v3')

service.videos.insert(
{
auth: auth,
part: ['snippet,status'],
requestBody: {
snippet: {
title,
description,
tags,
categoryId: categoryIds.ScienceTechnology,

defaultLanguage: 'en',
defaultAudioLanguage: 'en',
},

status: {
privacyStatus: 'public',
madeForKids: false,
},
},
media: {
body: fs.createReadStream(videoPath),
},
},
function (err: any, response: any) {
if (err) {
console.log('The API returned an error: ' + err)
return
}
console.log(response.data)

return response.data
}
)
}

/**
* Create an OAuth2 client with the given credentials, and then execute the
* given callback function.
*
* @param {Object} credentials The authorization client credentials.
* @param {function} callback The callback to call with the authorized client.
*/
function authorize(credentials: any, callback: any) {
const clientSecret = credentials.web.client_secret
const clientId = credentials.web.client_id
const redirectUrl = credentials.web.redirect_uris[0]

console.log(clientSecret, clientId, redirectUrl)

const oauth2Client = new OAuth2(clientId, clientSecret, redirectUrl)

// Check if we have previously stored a token.
fs.readFile(TOKEN_PATH, function (err, token: any) {
if (err) {
getNewToken(oauth2Client, callback)
} else {
oauth2Client.credentials = JSON.parse(token)
callback(oauth2Client)
}
})
}

/**
* Get and store new token after prompting for user authorization, and then
* execute the given callback with the authorized OAuth2 client.
*
* @param {google.auth.OAuth2} oauth2Client The OAuth2 client to get token for.
* @param {getEventsCallback} callback The callback to call with the authorized
* client.
*/
function getNewToken(oauth2Client: any, callback: any) {
const authUrl = oauth2Client.generateAuthUrl({
access_type: 'offline',
scope: SCOPES,
})
console.log('Authorize this app by visiting this url: ', authUrl)
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
})
rl.question('Enter the code from that page here: ', function (code) {
rl.close()
oauth2Client.getToken(code, function (err: any, token: any) {
if (err) {
console.log('Error while trying to retrieve access token', err)
return
}
oauth2Client.credentials = token
storeToken(token)
callback(oauth2Client)
})
})
}

/**
* Store token to disk be used in later program executions.
*
* @param {Object} token The token to store to disk.
*/
function storeToken(token: any) {
fs.writeFile(TOKEN_PATH, JSON.stringify(token), err => {
if (err) throw err
console.log('Token stored to ' + TOKEN_PATH)
})
}

// getNewToken(oauth2Client, (auth) => {
// console.log(auth);

// });
43 changes: 29 additions & 14 deletions src/video/video.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,21 +48,36 @@ export const mergeAudio = async ({
const audioFilter = `atempo=${audioSpeed},aformat=sample_rates=44100:channel_layouts=stereo`
const srtFilePath = 'basicaudio.wav.srt'
const newSrtFilePath = path.join(__dirname, '..', '..', srtFilePath)
const subtitleStyle = "force_style='FontName=DejaVu,FontSize=18,PrimaryColour=&Hffffff&,OutlineColour=&H00000000&'"

// 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()
return new Promise((resolve, reject) => {
ffmpeg(videoFilePath)
.inputOptions(`-t ${adjustedTrimDuration}`)
.input(audioFilePath)

.videoFilter(videoFilter)
.audioFilter(audioFilter)
.outputOptions([
'-vf',
`subtitles=${newSrtFilePath}:${subtitleStyle}`,
'-map',
'0:v',
'-map',
'1:a',
'-c:v libx264',
'-c:a aac',
])
.output(outputVideoPath)
.on('end', () => {
console.log('Audio added to video complete!')
resolve('Audio added to video complete!')
})
.on('error', err => {
console.error('Error during audio adding to video:', err.message)
reject(err)
})
.run()
})
}

0 comments on commit 74d4012

Please sign in to comment.