@makefully/dutifully
v1.20.0
Published
Client tools for Cheerfully service.
Maintainers
Readme
Dutifully (@makefully/dutifully)
Client API for Cheerfully services. Cheerfully makes voice-over easy.
npm install @makefully/dutifullyTo use the API, you will need to specify a root ./env-cheerfully.json file with the following format:
{
"server": "http://server:port",
"accessToken": "BearerTokenForShowfullyYap"
}accessToken is sent as Authorization: Bearer … on yap submit/status/download (Showfully 1.4+). For local Showfully, use the same value as SHOWFULLY_DEBUG_TOKEN with SHOWFULLY_ALLOW_DEBUG_TOKEN=1.
Services
All services can be specified in cheerfully.json as described in the following sections. Services can be run as scripts specified in package.json as:
{
"scripts": {
"speech": "cheer -service elevenlabs",
"lipsync": "cheer -service rhubarb",
"captions": "cheer -service transcription"
}
}Alternatively, services can be run from Node by including Dutifully as shown:
const
cheerfully = require('@makefully/dutifully');
cheerfully({
service: 'polly'
});Say (spoken notifications)
say generates short spoken phrases through Cheerfully's ElevenLabs service, caches the audio under .cache/tts/, and plays it locally via node-web-audio-api. Use it from npm scripts instead of wiring ElevenLabs into the consuming project:
{
"scripts": {
"say": "say",
"web:release": "… && npm run say the web release is complete"
}
}Or programmatically:
const
cheerfully = require('@makefully/dutifully');
await cheerfully.say('The build is complete');Voice settings come from the first elevenlabs entry in cheerfully.json (same as other ElevenLabs chores). Server credentials and elevenLabsApiKey come from env-cheerfully.json. Cached files are keyed by voice + text, so repeats play immediately without regenerating.
ElevenLabs
This text-to-voice service requires a script as input and will download audio VO generated by ElevenLabs. You can set ElevenLabs settings in cheerfully.json like so:
{
"elevenlabs": [{
"script": "./example/script.json",
"output": "./example/vo/en/",
"model": "eleven_multilingual_v1",
"updateAllMetaData": true,
"voice": "21m00Tcm4TlvDq8ikWAM"
}]
}You may set a single voice value to an ElevenLabs voice id, or an object of speaker name keys mapped to voice ids. The speaker names must match speakers listed in the script. If "default" is provided as a key, this voice id will be used for any scripts not specifying a speaker.
Specifying the model is optional: if unspecified, Cheerfully tries to look up the voice id and pick a good model; if lookup fails it falls back to eleven_multilingual_v2.
Specifying updateAllMetaData is false by default. If it's set to true, album, title, and unsynchronized lyrics are appended to any MP3's in the source directory that were not generated.
You may include your ElevenLabs API Key directly in the ElevenLabs spec above as apiKey, or set your API Key separately in env-cheerfully.json:
{
"elevenLabsApiKey": "elevenlabsapikeyforyouraccount"
}FFMPEG
This audio file process accepts ffmpeg commandline arguments to perform operations on an audio file.
{
"ffmpeg": [{
"options": {"ac": 1},
"src": "./example/vo/en/",
"output": "./example/vo/en/"
}]
}Packfully
This image-to-atlas service packs PNG/JPG/WebP sprites into texture atlases and downloads atlas PNG(s) plus descriptor file(s).
{
"packfully": [{
"format": "texturepacker",
"src": "./example/sprites/",
"output": "./example/atlases/",
"options": {
"maxWidth": 2048,
"maxHeight": 2048,
"pot": true,
"smart": true,
"square": false,
"padding": 1,
"extrude": 1,
"border": 1,
"allowRotation": false,
"trim": true,
"trimThreshold": 0,
"prefix": "packed",
"scale": 1
},
"files": {
"hero.png": { "trim": false }
}
}]
}Use exports: ["texturepacker", "createjs"] (or format) to choose one or more atlas descriptors. Supported formats:
texturepacker/json-hash/pixi/phaser-hash— TexturePacker JSON Hashjson-array/phaser-array— TexturePacker JSON Arraycreatejs/easeljs— CreateJS SpriteSheet JSONspine— Spine.atlastextcss— CSS sprite classesstarling/sparrow— Starling/Sparrow XMLunity— Unity-oriented JSONgodot— Godot atlas JSON
Spacing: padding is empty gap beyond extrude; extrude is edge-pixel bleed into that gap; border is margin from the atlas edge.
Up-to-date atlas PNGs are skipped via an embedded CheerfullyPackfullyHash (source bytes + options). Descriptor files (json/css/atlas/xml) are not hash carriers.
Classfully
This image-to-CSS service approximates still images as a CSS class rule (deterministic mutation search scored via headless Chromium screenshots). Every image under src is processed; files overrides are optional.
{
"classfully": [{
"src": "./example/classfully/",
"output": "./example/classfully/out/",
"options": {
"maxIterations": 40,
"minAccuracy": 0.9,
"maxMutations": 256
},
"files": {
"swatch.png": {
"className": "swatch",
"css": "swatch.css"
}
}
}]
}- Omit
seedto start from the image’s averagebackground-color; passseed(declarations or a.cssfile insrc) to refine existing CSS. - Output defaults to
{stem}.csswith.{stem} { … }; override withcss/outputandclassName.
Sharp
This still-image service converts, compresses, resizes, center-crops (extract), and can emit multi-size thumbnails or favicon packs (including .ico). Every image under src is processed; files overrides are optional.
{
"sharp": [{
"src": "./example/images/",
"output": "./example/out/",
"options": {
"format": "webp",
"quality": 80,
"resize": { "width": 1920, "fit": "inside", "withoutEnlargement": true }
},
"files": {
"logo.png": {
"extract": { "width": 1024, "height": 1024 },
"format": ["ico", "png"],
"sizes": [16, 32, 48, 64, 128, 256],
"prefix": "favicon"
}
}
}]
}Unchanged outputs are skipped using embedded CheerfullySharpHash metadata (PNG tEXt / GIF comment / JPEG·WebP·TIFF·AVIF XMP). Per-service keys so Sharp and Packfully/Stackfully hashes do not clobber each other. Formats that cannot store metadata (e.g. .ico) are always reprocessed, with a console note.
Stackfully
This multi-layer compositing service builds sized outputs from ordered image layers (positioning, blend, alpha). Every image under src is available as a layer source; define composites under outputs.
{
"stackfully": [{
"src": "./example/images/",
"output": "./example/composites/",
"options": {
"format": "png"
},
"files": {
"badge.png": { "composite": true }
},
"outputs": {
"hero.png": {
"width": 1024,
"height": 1024,
"format": "png",
"layers": [
"background.png",
{
"input": "foreground.png",
"alpha": 0.85,
"blend": "over",
"bottom": 0,
"right": 0
}
]
}
}
}]
}Unchanged composite outputs are skipped via embedded CheerfullyStackfullyHash (layer source bytes + recipe). Unsupported output types are always reprocessed with a console note.
Polly
This text-to-voice service requires a script as input and will download audio VO generated by Amazon Polly. You can set Polly settings in cheerfully.json like so:
{
"polly": [{
"script": "./example/script.json",
"output": "./example/vo/en/",
"language": "en-US",
"updateAllMetaData": true,
"voice": "Ivy"
}]
}You may set a single voice value matching an available AMazon Polly voice or this may be an object of key/value pairs with speaker name keys matched to voices. The speaker names must match speakers listed in the script. If "default" is provided as a key, this voice will be used for any scripts not specifying a speaker.
Specifying updateAllMetaData is false by default. If it's set to true, album, title, and unsynchronized lyrics are appended to any MP3's in the source directory that were not generated.
Allosaurus
This audio-to-lip-flap service requires audio as input and will download a mouth cues JSON file delineating mouth shapes for particular timestamps using Allosaurus. You can set Allosaurus settings in cheerfully.json like so:
{
"allosaurus": [{
"options" {
"compress": true,
"exportFormat": "json"
},
"src": "./example/vo/en/",
"output": "./example/lipsync/en/"
}]
}Export formats include those supported by Rhubarb as well as "mp3" which will embed lipsync timings into the MP3's id3 meta data.
For exporting to JSON, you can set compress to true for the format to use JSON arrays similar to Rhubarb's shorter tsv format.
Rhubarb
This audio-to-lip-flap service requires audio as input and will download a mouth cues JSON file delineating mouth shapes for particular timestamps using Rhubarb Lip-Sync. You can set Rhubarb settings in cheerfully.json like so:
{
"rhubarb": [{
"options" {
"compress": true,
"exportFormat": "json"
},
"src": "./example/vo/en/",
"output": "./example/lipsync/en/"
}]
}Export formats include those supported by Rhubarb as well as "mp3" which will embed lipsync timings into the MP3's id3 meta data.
For exporting to JSON, you can set compress to true for the format to use JSON arrays similar to Rhubarb's shorter tsv format.
Transcription
This audio-to-captions service requires audio as input and will download a captions JSON file delineating captions with timestamps using Amazon Transcription. You can set Transcription settings in cheerfully.json like so:
{
"transcription": [{
"format": "sami",
"language": "en-US",
"limit": 40,
"prependSpeaker": "[]",
"script": "./example/script.json",
"src": "./example/vo/en/",
"nowrap": [],
"output": "./example/captions/en/"
}]
}Transcription supports the following formats:
- JSON
- LRC
- MP3 (Embedded in source MP3 as a SYLT ID3 tag labeled "captions".)
- SAMI / SMI
- SRT
- VTT
Specify limit to set the maximum number of characters for a single caption line. The default is 40.
If your scripts include any phrases that should remain unbroken by the captioning process, add them to the nowrap list. For example: "nowrap": ["Makefully Studios"].
Script lines may include an optional class (for example "title" or "stage"). In VTT exports this becomes a cue class span (<c.title>…</c>). In SAMI/SMI the paragraph Class is the job language plus CC (for example ENUSCC for en-US); when a speaker and/or script class is present, the dialog is wrapped in <SPAN Class="…"> (for example Class="todd stage"). In JSON exports it is included on each caption entry. SRT, LRC, and MP3 SYLT ignore it.
Script lines may also include an optional events array for one-shot timed markers. Entries may be:
- a number — add to the running absolute time offset (ms)
- a string
"<phrase"/">phrase"— set the offset to just before/after that phrase in the caption (first word may be an incomplete suffix, last word an incomplete prefix); unmatched phrases leave the offset unchanged and are warned/logged for the client; any other string fires that event at the current offset - an object
{ "event", "time"?, "before"?, "after"? }— firesevent; optional fields set the absolute offset (timewins overbeforeoverafter; same phrase edge rules)
Cheerfully resolves these against caption timings and exports them in parallel using the same caption format (events.json, {id}.events.vtt / .srt / .sami / .smi / .lrc, or a separate MP3 SYLT frame with content type EVENTS labeled "events").
Translate
This text-to-text service translates Cheerfully scripts or language-matrix CSV/TSV via Amazon Translate. Set jobs in cheerfully.json:
{
"translate": [{
"script": "./example/script.json",
"language": "en-US",
"languages": ["es-ES", "fr-FR"],
"output": "./example/scripts/",
"ignore": ["Makefully Studios"]
}, {
"script": "./example/strings.csv",
"language": "en-US",
"languages": ["es-ES", "fr-FR"],
"output": "./example/scripts/"
}]
}script— path to a Cheerfully.jsonscript or a language-matrix.csv/.tsv(first column = ids, first row = language tags).language— source locale (CSV: source column). Matrix jobs require this; JSON may useauto.languages— target locales to produce or fill.format/exports— optional. Defaults to the source extension (json,csv, ortsv).ignore— phrases left untranslated.
JSON sources write {lang}/{basename}.{ext} under output. CSV/TSV sources write an updated matrix file and leave existing non-empty cells unchanged.
Supported formats: json, i18n, csv, tsv, po, xliff, properties, yml / yaml, strings, xml.
Example
Find an example script in the ./examples/ folder. You can run either npm run test-elevenlabs or npm run test-polly to create VO tracks in the examples folder that you can then use to try out npm run test-rhubarb and npm run test-transcription. Sample sprites for atlas packing are under ./example/sprites/; run npm run test-packfully to pack them into ./example/atlases/. Run npm run test-translate to translate ./example/script.json and ./example/strings.csv into ./example/scripts/.
These examples are specified using a JSON definition in ./cheerfully.json which you can copy or modify to set different generation parameters as supported by Cheerfully.
Maintainers: see PUBLISHING.md for npm trusted publishing setup.
