UNPKG

@jackdbd/eleventy-plugin-text-to-speech

Version:
144 lines 6.62 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.audioAssetsFromText = void 0; const node_crypto_1 = __importDefault(require("node:crypto")); const debug_1 = __importDefault(require("debug")); const eleventyFetch = __importStar(require("@11ty/eleventy-fetch")); const constants_js_1 = require("./constants.js"); const text_to_speech_js_1 = require("./text-to-speech.js"); const utils_js_1 = require("./utils.js"); const debug = (0, debug_1.default)(`${constants_js_1.DEBUG_PREFIX}/audio-assets-from-text`); /** * Synthesize a `text` into audio assets using the Cloud Text-to-Speech API. * * Generate as many audio buffers as the specified `audioEncodings`, * using the specified `voice`. * * Write all generated audio assets using the specified `writer`. */ const audioAssetsFromText = async ({ audioEncodings, cacheExpiration, outputPath, text, textToSpeechClient, voice, writer }) => { const md5 = node_crypto_1.default.createHash('md5'); const contentHash = md5.update(text).digest('hex'); const audioBasename = contentHash; const languageCode = voice.slice(0, 5); const name = voice; debug(`synthesize audio content on ${outputPath} (hash: ${contentHash}) using voice ${voice}`); const conversionPromises = audioEncodings.map(async (audioEncoding) => { const extension = (0, utils_js_1.audioExtension)(audioEncoding); const assetName = `${audioBasename}.${extension}`; // TODO: use AssetCache when self-hosting, and RemoteAssetCache when hosting // on Cloud Storage // https://github.com/11ty/eleventy-fetch/blob/master/src/AssetCache.js // https://github.com/11ty/eleventy-fetch/blob/master/src/RemoteAssetCache.js // https://www.11ty.dev/docs/plugins/fetch/#options const AssetCache = eleventyFetch.AssetCache; const uniqueKey = `${contentHash}_${extension}_${audioEncoding}`; const cachedAsset = new AssetCache(uniqueKey); if (cachedAsset.isCacheValid(cacheExpiration)) { debug(`cached asset ${uniqueKey} still not expired. Try retrieving it from the cache`); const buffer = await cachedAsset.getCachedValue(); // even if the asset was retrieved from the 11ty cache (e.g. .cache/), we // still need to write it to the 11ty output directory (e.g. _site/) const { href } = await writer.write({ assetName, buffer }); return { href }; } //////////////////////////////////////////////////////////////////////////// // if (audioEncoding === 'MP3') { // const summary = `could not synthesize ${contentHash} as ${audioEncoding}` // const detail = `testing a failure of speech synthesis using MP3 encoding` // throw new Error(`${summary}: ${detail}`) // } // if (audioEncoding === 'OGG_OPUS') { // const summary = `could not synthesize ${contentHash} as ${audioEncoding}` // const detail = `testing a failure of speech synthesis using OGG_OPUS encoding` // throw new Error(`${summary}: ${detail}`) // } //////////////////////////////////////////////////////////////////////////// const { error, value: buffer } = await (0, text_to_speech_js_1.synthesizeSpeech)({ audioEncoding, client: textToSpeechClient, text, voice: { languageCode, name } }); if (error) { throw new Error(`could not synthesize audio content ${contentHash} as ${audioEncoding}: ${error.message}`); } if (buffer) { const { href } = await writer.write({ assetName, buffer }); debug(`try caching asset ${uniqueKey}`); await cachedAsset.save(buffer, 'buffer'); debug(`cached asset ${uniqueKey}`); return { href }; } throw new Error('unreachable code: there must be either an error or a value'); }); const conversionResults = await Promise.allSettled(conversionPromises); const successes = []; const failures = []; conversionResults.forEach((res) => { if (res.status === 'fulfilled') { successes.push({ href: res.value.href }); } else { failures.push(res.reason.message); } }); const hrefs = successes.map((s) => s.href); if (successes.length === conversionResults.length) { const message = `all ${conversionResults.length} requested audio assets for ${audioBasename} were generated (${audioEncodings.join(', ')})`; debug(message); return { contentHash, hrefs, warnings: [] }; } else if (successes.length >= 1 && successes.length < conversionResults.length) { const message = `${conversionResults.length} requests for ${audioBasename}, but only ${successes.length} were successful`; debug(message); return { contentHash, hrefs, warnings: failures }; } else { const message = `cannot synthesize ${audioBasename}: ${failures.join(' ')}`; debug(message); return { contentHash, error: new Error(message), hrefs: [], warnings: [] }; } }; exports.audioAssetsFromText = audioAssetsFromText; //# sourceMappingURL=audio-assets-from-text.js.map