From beb9a773859678eaf41cf949d942f728a8aa486d Mon Sep 17 00:00:00 2001 From: Abhishek Jain Date: Sat, 17 Oct 2020 17:10:37 +0200 Subject: [PATCH 1/3] refactor: Refactor to ts from JS --- app.js => app.ts | 0 deploy.js => deploy.ts | 0 functions/extractAudio.js | 19 ----- functions/extractAudio.ts | 23 +++++++ ...{transcribeAudio.js => transcribeAudio.ts} | 0 .../{download-utils.js => download-utils.ts} | 0 utils/ffmpeg-utils.js | 29 -------- utils/ffmpeg-utils.ts | 29 ++++++++ utils/{speech-utils.js => speech-utils.ts} | 0 utils/storage-utils.js | 69 ------------------- utils/storage-utils.ts | 44 ++++++++++++ 11 files changed, 96 insertions(+), 117 deletions(-) rename app.js => app.ts (100%) rename deploy.js => deploy.ts (100%) delete mode 100644 functions/extractAudio.js create mode 100644 functions/extractAudio.ts rename functions/{transcribeAudio.js => transcribeAudio.ts} (100%) rename utils/{download-utils.js => download-utils.ts} (100%) delete mode 100644 utils/ffmpeg-utils.js create mode 100644 utils/ffmpeg-utils.ts rename utils/{speech-utils.js => speech-utils.ts} (100%) delete mode 100644 utils/storage-utils.js create mode 100644 utils/storage-utils.ts diff --git a/app.js b/app.ts similarity index 100% rename from app.js rename to app.ts diff --git a/deploy.js b/deploy.ts similarity index 100% rename from deploy.js rename to deploy.ts diff --git a/functions/extractAudio.js b/functions/extractAudio.js deleted file mode 100644 index 2e507e6..0000000 --- a/functions/extractAudio.js +++ /dev/null @@ -1,19 +0,0 @@ -const storageUtils = require('../utils/storage-utils'); -const ffmpegUtils = require('../utils/ffmpeg-utils'); -module.exports = (event) => { - return Promise.resolve() - .then(() => { - const videoFile = storageUtils.videoBucket.file(event.data.name); - console.log('downloading video file 2...'); - return storageUtils.downloadFile(videoFile, event.data.name); - }).then((fileinfo) => { - //extract audio and transcode to FLAC - return ffmpegUtils.extractAudio(fileinfo); - }).then((flacOutput) => { - console.log(`Uploading ${flacOutput.destination.temp.audio} to flac bucket`); - return storageUtils.uploadFlac(flacOutput.destination.temp.audio); - }) - .catch((err) => { - return Promise.reject(err); - }); -}; \ No newline at end of file diff --git a/functions/extractAudio.ts b/functions/extractAudio.ts new file mode 100644 index 0000000..8332ca1 --- /dev/null +++ b/functions/extractAudio.ts @@ -0,0 +1,23 @@ +import { videoBucket, downloadFile, uploadFlac } from "../utils/storage-utils"; +const ffmpegUtils = require("../utils/ffmpeg-utils"); +module.exports = (event) => { + return Promise.resolve() + .then(() => { + const videoFile = videoBucket.file(event.data.name); + console.log("downloading video file 2..."); + return downloadFile(videoFile, event.data.name); + }) + .then((fileinfo) => { + //extract audio and transcode to FLAC + return ffmpegUtils.extractAudio(fileinfo); + }) + .then((flacOutput) => { + console.log( + `Uploading ${flacOutput.destination.temp.audio} to flac bucket` + ); + return uploadFlac(flacOutput.destination.temp.audio); + }) + .catch((err) => { + return Promise.reject(err); + }); +}; diff --git a/functions/transcribeAudio.js b/functions/transcribeAudio.ts similarity index 100% rename from functions/transcribeAudio.js rename to functions/transcribeAudio.ts diff --git a/utils/download-utils.js b/utils/download-utils.ts similarity index 100% rename from utils/download-utils.js rename to utils/download-utils.ts diff --git a/utils/ffmpeg-utils.js b/utils/ffmpeg-utils.js deleted file mode 100644 index d5e8038..0000000 --- a/utils/ffmpeg-utils.js +++ /dev/null @@ -1,29 +0,0 @@ -const ffmpegPath = require('@ffmpeg-installer/ffmpeg').path; -const ffmpeg = require('fluent-ffmpeg'); - -ffmpeg.setFfmpegPath(ffmpegPath); - -function extractAudio(fileinfo) { - let tempAudioPath = '/tmp/' + fileinfo.source.name + '.flac'; - fileinfo.destination.temp.audio = tempAudioPath; - return new Promise((resolve, reject) => { - ffmpeg(fileinfo.destination.temp.video) - .videoBitrate(19200) - .inputOptions('-vn') - .format('flac') - .audioChannels(1) - .output(tempAudioPath) - .on('end', function () { - console.log('extracted audio'); - resolve(fileinfo); - }) - .on('error', function (err, stdout, stderr) { - reject(err); - }) - .run(); - }); -} - -module.exports = { - extractAudio: extractAudio -}; \ No newline at end of file diff --git a/utils/ffmpeg-utils.ts b/utils/ffmpeg-utils.ts new file mode 100644 index 0000000..3162a0f --- /dev/null +++ b/utils/ffmpeg-utils.ts @@ -0,0 +1,29 @@ +import { path as ffmpegPath } from "@ffmpeg-installer/ffmpeg"; +import ffmpeg from "fluent-ffmpeg"; + +ffmpeg.setFfmpegPath(ffmpegPath); + +export const extractAudio = async (fileinfo) => { + let tempAudioPath = "/tmp/" + fileinfo.source.name + ".flac"; + fileinfo.destination.temp.audio = tempAudioPath; + return new Promise((resolve, reject) => { + ffmpeg(fileinfo.destination.temp.video) + .videoBitrate(19200) + .inputOptions("-vn") + .format("flac") + .audioChannels(1) + .output(tempAudioPath) + .on("end", function () { + console.log("extracted audio"); + resolve(fileinfo); + }) + .on("error", function (err, stdout, stderr) { + reject(err); + }) + .run(); + }); +}; + +module.exports = { + extractAudio: extractAudio, +}; diff --git a/utils/speech-utils.js b/utils/speech-utils.ts similarity index 100% rename from utils/speech-utils.js rename to utils/speech-utils.ts diff --git a/utils/storage-utils.js b/utils/storage-utils.js deleted file mode 100644 index a027489..0000000 --- a/utils/storage-utils.js +++ /dev/null @@ -1,69 +0,0 @@ -const path = require('path'); -const storageClient = require('@google-cloud/storage')(); -const appConfig = require('../app-config'); - -const videoBucket = storageClient.bucket(appConfig.buckets.video); -const flacBucket = storageClient.bucket(appConfig.buckets.audio); -const outputBucket = storageClient.bucket(appConfig.buckets.speechResponse); - -function getFilePathFromFile(storageFile) { - return `gs://${storageFile.bucket.name}/${storageFile.name}`; -} - -function downloadFile(file, fileName) { - console.log('Download started for ' + fileName); - let sourcePath = path.parse(fileName); - return new Promise((resolve, reject) => { - let tempDestination = '/tmp/' + fileName; - file.download({ - destination: tempDestination - }).then((error) => { - console.log('Download is done ' + error); - if (error.length > 0) { - reject(error); - } else { - resolve({ - source: { - name: sourcePath.name, - ext: sourcePath.ext - }, - destination: { temp: { video: tempDestination } } - }); - } - }); - }); -} - -function uploadToBucket(bucket, filepath) { - return bucket - .upload(filepath) - .then(() => { - console.log(`${filepath} uploaded to bucket.`); - return Promise.resolve('resolve'); - }) - .catch(err => { - console.error('ERROR:', err); - return Promise.reject(err); - }); -} - -// function uploadVideo(filepath) { -// uploadToBucket(videoBucket, filepath); -// } -function uploadFlac(filepath) { - return uploadToBucket(flacBucket, filepath); -} - -function uploadJsonOutput(filepath) { - return uploadToBucket(outputBucket, filepath); -} - -module.exports = { - videoBucket: videoBucket, - flacBucket: flacBucket, - outputBucket: outputBucket, - getFilePathFromFile: getFilePathFromFile, - downloadFile: downloadFile, - uploadFlac: uploadFlac, - uploadJsonOutput: uploadJsonOutput -}; \ No newline at end of file diff --git a/utils/storage-utils.ts b/utils/storage-utils.ts new file mode 100644 index 0000000..d46c61a --- /dev/null +++ b/utils/storage-utils.ts @@ -0,0 +1,44 @@ +import path from "path"; +import { Storage, Bucket, File } from "@google-cloud/storage"; + +const appConfig = require("../app-config"); + +const storageClient = new Storage(); + +export const videoBucket = storageClient.bucket(appConfig.buckets.video); +export const flacBucket = storageClient.bucket(appConfig.buckets.audio); +export const outputBucket = storageClient.bucket( + appConfig.buckets.speechResponse +); + +export const getFilePathFromFile = (storageFile: File) => { + return `gs://${storageFile.bucket.name}/${storageFile.name}`; +}; + +export const downloadFile = async (file: File, fileName: string) => { + console.log("Download started for " + fileName); + let sourcePath = path.parse(fileName); + let tempDestination = "/tmp/" + fileName; + const response = await file.download({ + destination: tempDestination, + }); + return { + source: { + name: sourcePath.name, + ext: sourcePath.ext, + }, + destination: { temp: { video: tempDestination } }, + }; +}; + +export const uploadToBucket = async (bucket: Bucket, filepath: string) => { + return bucket.upload(filepath); +}; + +export const uploadFlac = (filepath: string) => { + return flacBucket.upload(filepath); +}; + +export const uploadJsonOutput = (filepath: string) => { + return outputBucket.upload(filepath); +}; From 4ca61d0b1bb650cfb6080ce8966d09b8fda7d1ba Mon Sep 17 00:00:00 2001 From: Abhishek Jain Date: Sat, 17 Oct 2020 17:43:27 +0200 Subject: [PATCH 2/3] feat: Adds types --- @types/types.ts | 7 +++++++ package.json | 7 ++++++- yarn.lock | 56 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 69 insertions(+), 1 deletion(-) create mode 100644 @types/types.ts diff --git a/@types/types.ts b/@types/types.ts new file mode 100644 index 0000000..6548fb1 --- /dev/null +++ b/@types/types.ts @@ -0,0 +1,7 @@ +export interface DownloadedFileInfo { + source: { + name: string; + ext: string; + }; + destination: { temp: { video: string; audio?: string } }; +} diff --git a/package.json b/package.json index 456c27f..854cb0d 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,7 @@ ], "main": "app.js", "scripts": { - "deploy": "node deploy.js", + "deploy": "ts-node deploy.js", "test": "echo \"Error: no test specified\" && exit 1" }, "author": "Abhishek Jain ", @@ -30,5 +30,10 @@ "@google-cloud/speech": "^4.1.3", "@google-cloud/storage": "^5.3.0", "fluent-ffmpeg": "^2.1.2" + }, + "devDependencies": { + "@types/fluent-ffmpeg": "^2.1.16", + "ts-node": "^9.0.0", + "typescript": "^4.0.3" } } diff --git a/yarn.lock b/yarn.lock index 0172ea1..172ac0f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -215,6 +215,13 @@ dependencies: "@types/node" "*" +"@types/fluent-ffmpeg@^2.1.16": + version "2.1.16" + resolved "https://registry.yarnpkg.com/@types/fluent-ffmpeg/-/fluent-ffmpeg-2.1.16.tgz#63949b0cb6bc88c9157a579cdd80858a269f3a3a" + integrity sha512-1FTstm6xY/2WsJVt6ARV7CiJjNCQDlR/nfw6xuYk5ITbVjk7sw89Biyqm2DGW4c3aZ3vBx+5irZvsql4eybpoQ== + dependencies: + "@types/node" "*" + "@types/long@^4.0.0", "@types/long@^4.0.1": version "4.0.1" resolved "https://registry.yarnpkg.com/@types/long/-/long-4.0.1.tgz#459c65fa1867dafe6a8f322c4c51695663cc55e9" @@ -269,6 +276,11 @@ ansi-styles@^4.0.0: dependencies: color-convert "^2.0.1" +arg@^4.1.0: + version "4.1.3" + resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089" + integrity sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA== + arrify@^2.0.0, arrify@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/arrify/-/arrify-2.0.1.tgz#c9655e9331e0abcd588d2a7cad7e9956f66701fa" @@ -381,6 +393,11 @@ decamelize@^1.2.0: resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA= +diff@^4.0.1: + version "4.0.2" + resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" + integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== + dot-prop@^5.2.0: version "5.3.0" resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-5.3.0.tgz#90ccce708cd9cd82cc4dc8c3ddd9abdd55b20e88" @@ -675,6 +692,11 @@ make-dir@^3.0.0: dependencies: semver "^6.0.0" +make-error@^1.1.1: + version "1.3.6" + resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" + integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== + mime-db@1.44.0: version "1.44.0" resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.44.0.tgz#fa11c5eb0aca1334b4233cb4d52f10c5a6272f92" @@ -872,6 +894,19 @@ snakeize@^0.1.0: resolved "https://registry.yarnpkg.com/snakeize/-/snakeize-0.1.0.tgz#10c088d8b58eb076b3229bb5a04e232ce126422d" integrity sha1-EMCI2LWOsHazIpu1oE4jLOEmQi0= +source-map-support@^0.5.17: + version "0.5.19" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.19.tgz#a98b62f86dcaf4f67399648c085291ab9e8fed61" + integrity sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw== + dependencies: + buffer-from "^1.0.0" + source-map "^0.6.0" + +source-map@^0.6.0: + version "0.6.1" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" + integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== + stream-events@^1.0.1, stream-events@^1.0.4, stream-events@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/stream-events/-/stream-events-1.0.5.tgz#bbc898ec4df33a4902d892333d47da9bf1c406d5" @@ -930,6 +965,17 @@ teeny-request@^7.0.0: stream-events "^1.0.5" uuid "^8.0.0" +ts-node@^9.0.0: + version "9.0.0" + resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-9.0.0.tgz#e7699d2a110cc8c0d3b831715e417688683460b3" + integrity sha512-/TqB4SnererCDR/vb4S/QvSZvzQMJN8daAslg7MeaiHvD8rDZsSfXmNeNumyZZzMned72Xoq/isQljYSt8Ynfg== + dependencies: + arg "^4.1.0" + diff "^4.0.1" + make-error "^1.1.1" + source-map-support "^0.5.17" + yn "3.1.1" + typedarray-to-buffer@^3.1.5: version "3.1.5" resolved "https://registry.yarnpkg.com/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz#a97ee7a9ff42691b9f783ff1bc5112fe3fca9080" @@ -942,6 +988,11 @@ typedarray@^0.0.6: resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= +typescript@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.0.3.tgz#153bbd468ef07725c1df9c77e8b453f8d36abba5" + integrity sha512-tEu6DGxGgRJPb/mVPIZ48e69xCn2yRmCgYmDugAVwmJ6o+0u1RI18eO7E7WBTLYLaEVVOhwQmcdhQHweux/WPg== + unique-string@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/unique-string/-/unique-string-2.0.0.tgz#39c6451f81afb2749de2b233e3f7c5e8843bd89d" @@ -1034,3 +1085,8 @@ yargs@^15.3.1: which-module "^2.0.0" y18n "^4.0.0" yargs-parser "^18.1.2" + +yn@3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50" + integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q== From 668f46c81330646d82c1decc93017ac1df138e53 Mon Sep 17 00:00:00 2001 From: Abhishek Jain Date: Sat, 17 Oct 2020 17:43:59 +0200 Subject: [PATCH 3/3] refactor: Refactor with types --- functions/extractAudio.ts | 37 ++++++++++++++----------------- utils/ffmpeg-utils.ts | 46 ++++++++++++++++++++++----------------- utils/storage-utils.ts | 3 ++- 3 files changed, 44 insertions(+), 42 deletions(-) diff --git a/functions/extractAudio.ts b/functions/extractAudio.ts index 8332ca1..f5e05a8 100644 --- a/functions/extractAudio.ts +++ b/functions/extractAudio.ts @@ -1,23 +1,18 @@ import { videoBucket, downloadFile, uploadFlac } from "../utils/storage-utils"; -const ffmpegUtils = require("../utils/ffmpeg-utils"); -module.exports = (event) => { - return Promise.resolve() - .then(() => { - const videoFile = videoBucket.file(event.data.name); - console.log("downloading video file 2..."); - return downloadFile(videoFile, event.data.name); - }) - .then((fileinfo) => { - //extract audio and transcode to FLAC - return ffmpegUtils.extractAudio(fileinfo); - }) - .then((flacOutput) => { - console.log( - `Uploading ${flacOutput.destination.temp.audio} to flac bucket` - ); - return uploadFlac(flacOutput.destination.temp.audio); - }) - .catch((err) => { - return Promise.reject(err); - }); +import { extractAudio } from "../utils/ffmpeg-utils"; + +export default async (event) => { + const videoFile = videoBucket.file(event.data.name); + console.log("downloading video file 2..."); + const fileInfo = await downloadFile(videoFile, event.data.name); + + //extract audio and transcode to FLAC + const { + destination: { + temp: { audio: tempAudioPath }, + }, + } = await extractAudio(fileInfo); + + console.log(`Uploading ${tempAudioPath} to flac bucket`); + return uploadFlac(tempAudioPath); }; diff --git a/utils/ffmpeg-utils.ts b/utils/ffmpeg-utils.ts index 3162a0f..0723f57 100644 --- a/utils/ffmpeg-utils.ts +++ b/utils/ffmpeg-utils.ts @@ -1,29 +1,35 @@ +import { EventEmitter } from "events"; + import { path as ffmpegPath } from "@ffmpeg-installer/ffmpeg"; import ffmpeg from "fluent-ffmpeg"; +import { DownloadedFileInfo } from "../@types/types"; ffmpeg.setFfmpegPath(ffmpegPath); -export const extractAudio = async (fileinfo) => { - let tempAudioPath = "/tmp/" + fileinfo.source.name + ".flac"; - fileinfo.destination.temp.audio = tempAudioPath; +export const extractAudio = async (fileInfo: DownloadedFileInfo) => { + let tempAudioPath = "/tmp/" + fileInfo.source.name + ".flac"; + fileInfo.destination.temp.audio = tempAudioPath; + const { + destination: { + temp: { video: tempVideoPath }, + }, + } = fileInfo; + const stream = ffmpeg(tempVideoPath) + .videoBitrate(19200) + .inputOptions("-vn") + .format("flac") + .audioChannels(1) + .output(tempAudioPath); + stream.run(); + await streamToPromise(stream); + return fileInfo; +}; + +const streamToPromise = async (stream: EventEmitter) => { return new Promise((resolve, reject) => { - ffmpeg(fileinfo.destination.temp.video) - .videoBitrate(19200) - .inputOptions("-vn") - .format("flac") - .audioChannels(1) - .output(tempAudioPath) - .on("end", function () { - console.log("extracted audio"); - resolve(fileinfo); - }) - .on("error", function (err, stdout, stderr) { - reject(err); - }) - .run(); + stream.on("end", () => resolve()); + stream.on("error", (err) => reject(err)); }); }; -module.exports = { - extractAudio: extractAudio, -}; +export default extractAudio; diff --git a/utils/storage-utils.ts b/utils/storage-utils.ts index d46c61a..d03afe4 100644 --- a/utils/storage-utils.ts +++ b/utils/storage-utils.ts @@ -1,5 +1,6 @@ import path from "path"; import { Storage, Bucket, File } from "@google-cloud/storage"; +import { DownloadedFileInfo } from "../@types/types"; const appConfig = require("../app-config"); @@ -15,7 +16,7 @@ export const getFilePathFromFile = (storageFile: File) => { return `gs://${storageFile.bucket.name}/${storageFile.name}`; }; -export const downloadFile = async (file: File, fileName: string) => { +export const downloadFile = async (file: File, fileName: string):Promise => { console.log("Download started for " + fileName); let sourcePath = path.parse(fileName); let tempDestination = "/tmp/" + fileName;