import * as functions from "firebase-functions" ; import { Storage } from "@google-cloud/storage" ; import * as path from "path" ; import sharp = require ( "sharp" ) ; const storage = new Storage ( ) ; const runtimeOpts = { timeoutSeconds : 540 , memory : "2GB" , } ; /**
 * Creates thumbnails for image when uploaded to `/keysets`, then deletes original.
 */ export const createThumbsAuto = functions . runWith ( runtimeOpts ) . storage . object ( ) . onFinalize ( async ( object ) => { const fileBucket = object . bucket ; const filePath = object . name ; const contentType = object . contentType ; if ( ! contentType || ( contentType && ! contentType . startsWith ( "image/" ) ) ) { return null ; } if ( filePath ) { const fileName = path . basename ( filePath ) ; if ( ! filePath . startsWith ( "keysets/" ) ) { return null ; } const bucket = storage . bucket ( fileBucket ) ; const metadata = { contentType : contentType , } ; const cardFilePath = path . join ( "card/" , fileName ) ; const cardUploadStream = bucket . file ( cardFilePath ) . createWriteStream ( { metadata } ) ; const cardPipeline = sharp ( ) ; cardPipeline . resize ( 320 , 180 ) . pipe ( cardUploadStream ) ; bucket . file ( filePath ) . createReadStream ( ) . pipe ( cardPipeline ) ; cardUploadStream . on ( "finish" , ( ) => { console . log ( "Created card thumbnail for " + fileName ) ; } ) . on ( "error" , ( err ) => { console . log ( "Failed to create card thumbnail for " + fileName + ": " + err ) ; } ) ; const listFilePath = path . join ( "list/" , fileName ) ; const listUploadStream = bucket . file ( listFilePath ) . createWriteStream ( { metadata } ) ; const listPipeline = sharp ( ) ; listPipeline . resize ( 100 , 56 ) . pipe ( listUploadStream ) ; bucket . file ( filePath ) . createReadStream ( ) . pipe ( listPipeline ) ; listUploadStream . on ( "finish" , ( ) => { console . log ( "Created list thumbnail for " + fileName ) ; } ) . on ( "error" , ( err ) => { console . log ( "Failed to create list thumbnail for " + fileName + ": " + err ) ; } ) ; const imageListFilePath = path . join ( "image-list/" , fileName ) ; const imageListUploadStream = bucket . file ( imageListFilePath ) . createWriteStream ( { metadata } ) ; const imageListPipeline = sharp ( ) ; imageListPipeline . resize ( 320 , 320 ) . pipe ( imageListUploadStream ) ; bucket . file ( filePath ) . createReadStream ( ) . pipe ( imageListPipeline ) ; imageListUploadStream . on ( "finish" , ( ) => { console . log ( "Created image list thumbnail for " + fileName ) ; } ) . on ( "error" , ( err ) => { console . log ( "Failed to create image list thumbnail for " + fileName + ": " + err ) ; } ) ; const thumbsFilePath = path . join ( "thumbs/" , fileName ) ; const thumbsUploadStream = bucket . file ( thumbsFilePath ) . createWriteStream ( { metadata } ) ; const thumbsPipeline = sharp ( ) ; thumbsPipeline . resize ( 480 , 270 ) . pipe ( thumbsUploadStream ) ; bucket . file ( filePath ) . createReadStream ( ) . pipe ( thumbsPipeline ) ; thumbsUploadStream . on ( "finish" , ( ) => { console . log ( "Created generic thumbnail for " + fileName ) ; } ) . on ( "error" , ( err ) => { console . log ( "Failed to create generic thumbnail for " + fileName + ": " + err ) ; } ) ; const streams = [ cardUploadStream , listUploadStream , imageListUploadStream , thumbsUploadStream , ] ; const allPromises = Promise . all ( streams . map ( ( stream ) => { return new Promise ( ( resolve , reject ) => stream . on ( "finish" , resolve ) . on ( "error" , reject ) ) ; } ) ) ; allPromises . then ( ( ) => { return bucket . deleteFiles ( { maxResults : 1 , prefix : filePath , } ) ; } ) . catch ( ( error ) => { console . log ( "Failed to create thumbnail for " + fileName + ": " + error ) ; } ) ; return await allPromises ; } return null ; } ) ; /**
 *  Create thumbs for all images within `/keysets`.
 */ export const createThumbs = functions . runWith ( runtimeOpts ) . https . onCall ( async ( data , context ) => { if ( ! context . auth || context . auth . token . admin !== true ) { return { error : "Current user is not an admin. Access is not permitted." , } ; } console . log ( "Creating thumbnails" ) ; const bucket = storage . bucket ( "keycaplendar.appspot.com" ) ; const [ files ] = await bucket . getFiles ( { prefix : "keysets/" } ) ; const metadata = { contentType : "image/png" , } ; const filesPromise = new Promise < void > ( ( resolve , reject ) => { let filesProcessed = 0 ; const increase = ( ) => filesProcessed ++ ; for ( const file of files ) { const fileName = path . basename ( file . name ) ; console . log ( fileName ) ; const thumbsFilePath = path . join ( "thumbs/" , fileName ) ; const thumbsUploadStream = bucket . file ( thumbsFilePath ) . createWriteStream ( { metadata } ) ; const thumbsPipeline = sharp ( ) ; thumbsPipeline . resize ( 480 , 270 ) . pipe ( thumbsUploadStream ) ; file . createReadStream ( ) . pipe ( thumbsPipeline ) ; thumbsUploadStream . on ( "finish" , ( ) => { console . log ( "Created generic thumbnail for " + fileName ) ; increase ( ) ; } ) . on ( "error" , ( err ) => { console . log ( "Failed to generic thumbnail for " + fileName + ": " + err ) ; reject ( err ) ; } ) ; if ( filesProcessed === files . length ) { resolve ( ) ; } } } ) ; return filesPromise ; } ) ;