index.js
1'use strict'; 2 3const functions = require('firebase-functions'); 4const admin = require('firebase-admin'); 5admin.initializeApp(); 6const mkdirp = require('mkdirp-promise'); 7const gcs = require('@google-cloud/storage'); 8const spawn = require('child-process-promise').spawn; 9const ffmpegPath = require('@ffmpeg-installer/ffmpeg').path; 10const path = require('path'); 11const os = require('os'); 12const fs = require('fs'); 13 14const db = admin.firestore(); 15 16// Max height and width of the thumbnail in pixels. 17const THUMB_MAX_WIDTH = 384; 18 19const SERVICE_ACCOUNT = '<credentials file>.json'; 20 21const adminConfig = JSON.parse(process.env.FIREBASE_CONFIG); 22 23module.exports.thumbGenerator = functions.storage.bucket(adminConfig.storageBucket).object().onFinalize(object => { 24 const fileBucket = object.bucket; // The Storage bucket that contains the file. 25 const filePathInBucket = object.name; 26 const resourceState = object.resourceState; // The resourceState is 'exists' or 'not_exists' (for file/folder deletions). 27 const metageneration = object.metageneration; // Number of times metadata has been generated. New objects have a value of 1. 28 const contentType = object.contentType; // This is the image MIME type 29 const isImage = contentType.startsWith('image/'); 30 const isVideo = contentType.startsWith('video/'); 31 32 // Exit if this is a move or deletion event. 33 if (resourceState === 'not_exists') { 34 return Promise.resolve(); 35 } 36 // Exit if file exists but is not new and is only being triggered 37 // because of a metadata change. 38 else if (resourceState === 'exists' && metageneration > 1) { 39 return Promise.resolve(); 40 } 41 // Exit if the image is already a thumbnail. 42 else if (filePathInBucket.indexOf('.thumbnail.') !== -1) { 43 return Promise.resolve(); 44 } 45 // Exit if this is triggered on a file that is not an image or video. 46 else if (!(isImage || isVideo)) { 47 return Promise.resolve(); 48 } 49 50 51 const fileDir = path.dirname(filePathInBucket); 52 const fileName = path.basename(filePathInBucket); 53 const fileInfo = parseName(fileName); 54 const thumbFileExt = isVideo ? 'jpg' : fileInfo.ext; 55 let thumbFilePath = path.normalize(path.join(fileDir, `${fileInfo.name}_${fileInfo.timestamp}.thumbnail.${thumbFileExt}`)); 56 const tempLocalThumbFile = path.join(os.tmpdir(), thumbFilePath); 57 const tempLocalDir = path.join(os.tmpdir(), fileDir); 58 const generateOperation = isVideo ? generateFromVideo : generateFromImage; 59 60 61 // Cloud Storage files. 62 const bucket = gcs({keyFilename: SERVICE_ACCOUNT}).bucket(fileBucket); 63 const file = bucket.file(filePathInBucket); 64 65 const metadata = { 66 contentType: isVideo ? 'image/jpeg' : contentType, 67 // To enable Client-side caching you can set the Cache-Control headers here. Uncomment below. 68 // 'Cache-Control': 'public,max-age=3600', 69 }; 70 // Create the temp directory where the storage file will be downloaded. 71 return mkdirp(tempLocalDir).then(() => { 72 return generateOperation(file, tempLocalThumbFile, fileName); 73 }).then(() => { 74 console.info('Thumbnail created at', tempLocalThumbFile); 75 // Get the thumbnail dimensions 76 return spawn('identify', ['-ping', '-format', '%wx%h', tempLocalThumbFile], {capture: ['stdout', 'stderr']}); 77 }).then((result) => { 78 const dim = result.stdout.toString(); 79 const idx = thumbFilePath.indexOf('.'); 80 81 thumbFilePath = `${thumbFilePath.substring(0,idx)}_${dim}${thumbFilePath.substring(idx)}`; 82 console.info('Thumbnail dimensions:', dim); 83 // Uploading the Thumbnail. 84 return bucket.upload(tempLocalThumbFile, {destination: thumbFilePath, metadata: metadata}); 85 }).then(() => { 86 console.info('Thumbnail uploaded to Storage at', thumbFilePath); 87 88 const thumbFilename = path.basename(thumbFilePath); 89 90 return updateDatabase(fileDir, fileName, thumbFilename); 91 }).then(() => { 92 console.info('Thumbnail generated.'); 93 94 fs.unlinkSync(tempLocalThumbFile); 95 96 return Promise.resolve(); 97 }) 98}); 99 100function generateFromImage(file, tempLocalThumbFile, fileName) { 101 const tempLocalFile = path.join(os.tmpdir(), fileName); 102 103 // Download file from bucket. 104 return file.download({destination: tempLocalFile}).then(() => { 105 console.info('The file has been downloaded to', tempLocalFile); 106 // Generate a thumbnail using ImageMagick with constant width and variable height (maintains ratio) 107 return spawn('convert', [tempLocalFile, '-thumbnail', THUMB_MAX_WIDTH, tempLocalThumbFile], {capture: ['stdout', 'stderr']}); 108 }).then(() => { 109 fs.unlinkSync(tempLocalFile); 110 return Promise.resolve(); 111 }) 112} 113 114function generateFromVideo(file, tempLocalThumbFile) { 115 return file.getSignedUrl({action: 'read', expires: '05-24-2999'}).then((signedUrl) => { 116 const fileUrl = signedUrl[0]; 117 const promise = spawn(ffmpegPath, ['-ss', '0', '-i', fileUrl, '-f', 'image2', '-vframes', '1', '-vf', `scale=${THUMB_MAX_WIDTH}:-1`, tempLocalThumbFile]); 118 // promise.childProcess.stdout.on('data', (data) => console.info('[spawn] stdout: ', data.toString())); 119 // promise.childProcess.stderr.on('data', (data) => console.info('[spawn] stderr: ', data.toString())); 120 return promise; 121 }) 122} 123 124//Firebase側で出力されるログ 125//Function execution started 126//Function execution took 858 ms, finished with status: 'ok'
https://stackoverflow.com/questions/43750325/getting-a-thumbnail-from-a-video-using-cloud-functions-for-firebase
上記のサイトから動画と画像サムネイルを作れるコードを拝借したのですが、deployは成功したものの実際にはたらいてくれませんでした。何が原因か見当もつかないので、知恵を貸していただきたいです。
あなたの回答
tips
プレビュー