@makefully/dutifully 1.23.0 → 1.23.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +6 -0
- package/README.md +22 -5
- package/package.json +1 -1
- package/src/classes/Cheer.js +2 -1
- package/src/classes/ElevenLabs.js +3 -6
- package/src/helpers/applyServiceAuth.js +89 -0
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
+
## 1.23.1
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
- Shared `auth` on jobs and `env-cheerfully.json` `auth.<service>` map (job wins; env fills defaults). Legacy `elevenLabsApiKey` and top-level ElevenLabs `apiKey` still map into `auth.apiKey`.
|
|
10
|
+
|
|
5
11
|
## 1.23.0
|
|
6
12
|
|
|
7
13
|
- Provider umbrellas: `translate` (`amazon` default → `amazontranslate`, or `deepl` / `google` / `microsoft`), `lipsync` (`rhubarb` default / `allosaurus`), and `voiceover` (`elevenlabs` default / `polly`) resolve to Cheerfully concrete services before Yap submit.
|
package/README.md
CHANGED
|
@@ -6,17 +6,28 @@ Client API for Cheerfully services. Cheerfully makes voice-over easy.
|
|
|
6
6
|
npm install @makefully/dutifully
|
|
7
7
|
```
|
|
8
8
|
|
|
9
|
-
To use the API, you will need
|
|
9
|
+
To use the API, you will need a root `./env-cheerfully.json` with Showfully Yap credentials and optional per-service auth defaults:
|
|
10
10
|
|
|
11
11
|
```javascript
|
|
12
12
|
{
|
|
13
13
|
"server": "http://server:port",
|
|
14
|
-
"accessToken": "BearerTokenForShowfullyYap"
|
|
14
|
+
"accessToken": "BearerTokenForShowfullyYap",
|
|
15
|
+
"auth": {
|
|
16
|
+
"elevenlabs": { "apiKey": "…" },
|
|
17
|
+
"deepl": { "apiKey": "…", "serverUrl": "https://api-free.deepl.com" },
|
|
18
|
+
"googletranslate": { "apiKey": "…" },
|
|
19
|
+
"microsofttranslate": { "key": "…", "region": "eastus" },
|
|
20
|
+
"amazontranslate": { "config": { } },
|
|
21
|
+
"polly": { "config": { }, "bucket": "…", "root": "…" },
|
|
22
|
+
"transcription": { "config": { }, "bucket": "…", "root": "…" }
|
|
23
|
+
}
|
|
15
24
|
}
|
|
16
25
|
```
|
|
17
26
|
|
|
18
27
|
`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`.
|
|
19
28
|
|
|
29
|
+
Job listings may include their own `auth` object (same shapes). Dutifully merges `auth.<service>` from env into each job; **job `auth` wins**. Legacy `elevenLabsApiKey` still maps to `auth.elevenlabs.apiKey`.
|
|
30
|
+
|
|
20
31
|
## Services
|
|
21
32
|
|
|
22
33
|
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:
|
|
@@ -100,7 +111,7 @@ await cheerfully.say('The build is complete');
|
|
|
100
111
|
```
|
|
101
112
|
|
|
102
113
|
|
|
103
|
-
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.
|
|
114
|
+
Voice settings come from the first `elevenlabs` entry in `cheerfully.json` (same as other ElevenLabs chores). Server credentials and optional `auth.elevenlabs` (or legacy `elevenLabsApiKey`) come from `env-cheerfully.json`. Cached files are keyed by voice + text, so repeats play immediately without regenerating.
|
|
104
115
|
|
|
105
116
|
### ElevenLabs
|
|
106
117
|
|
|
@@ -124,14 +135,20 @@ Specifying the `model` is optional: if unspecified, Cheerfully tries to look up
|
|
|
124
135
|
|
|
125
136
|
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.
|
|
126
137
|
|
|
127
|
-
You may include
|
|
138
|
+
You may include credentials on the job as `auth.apiKey`, or set defaults in `env-cheerfully.json`:
|
|
128
139
|
|
|
129
140
|
```javascript
|
|
130
141
|
{
|
|
131
|
-
"
|
|
142
|
+
"auth": {
|
|
143
|
+
"elevenlabs": {
|
|
144
|
+
"apiKey": "elevenlabsapikeyforyouraccount"
|
|
145
|
+
}
|
|
146
|
+
}
|
|
132
147
|
}
|
|
133
148
|
```
|
|
134
149
|
|
|
150
|
+
Legacy `elevenLabsApiKey` at the top level of `env-cheerfully.json` is still accepted.
|
|
151
|
+
|
|
135
152
|
### FFMPEG
|
|
136
153
|
|
|
137
154
|
This audio file process accepts ffmpeg commandline arguments to perform operations on an audio file.
|
package/package.json
CHANGED
package/src/classes/Cheer.js
CHANGED
|
@@ -6,6 +6,7 @@ const
|
|
|
6
6
|
https = require('https'),
|
|
7
7
|
{PassThrough} = require('stream'),
|
|
8
8
|
unzipper = require('unzip-stream'),
|
|
9
|
+
{applyServiceAuth} = require('../helpers/applyServiceAuth'),
|
|
9
10
|
cleanPath = (path) => path[path.length - 1] === '/' ? path.substring(0, path.length - 1) : path,
|
|
10
11
|
isZipMagic = (buf) => buf && buf.length >= 4 && buf[0] === 0x50 && buf[1] === 0x4b &&
|
|
11
12
|
((buf[2] === 0x03 && buf[3] === 0x04) || (buf[2] === 0x05 && buf[3] === 0x06) || (buf[2] === 0x07 && buf[3] === 0x08)),
|
|
@@ -264,9 +265,9 @@ const
|
|
|
264
265
|
},
|
|
265
266
|
Cheer = class {
|
|
266
267
|
constructor ({config, contents}) {
|
|
267
|
-
this.config = config;
|
|
268
268
|
this.contents = contents;
|
|
269
269
|
this.service = contents.service;
|
|
270
|
+
this.config = applyServiceAuth(config, contents, contents.service);
|
|
270
271
|
}
|
|
271
272
|
|
|
272
273
|
prepare ({difference = true, extract = true}) {
|
|
@@ -4,14 +4,11 @@ const
|
|
|
4
4
|
module.exports = class ElevenLabs extends TextToSpeech {
|
|
5
5
|
constructor (data) {
|
|
6
6
|
const
|
|
7
|
-
{id
|
|
7
|
+
{id} = data.contents;
|
|
8
8
|
|
|
9
9
|
data.album = id;
|
|
10
10
|
data.encodedBy = 'ElevenLabs';
|
|
11
|
-
|
|
12
|
-
data.config.apiKey = elevenLabsApiKey;
|
|
13
|
-
}
|
|
14
|
-
|
|
11
|
+
// Auth (env auth.elevenlabs / elevenLabsApiKey / job.auth) is applied in Cheer.
|
|
15
12
|
super(data);
|
|
16
13
|
}
|
|
17
|
-
};
|
|
14
|
+
};
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Merge Dutifully env-cheerfully.json `auth.<service>` into a job's `auth`.
|
|
3
|
+
* Job-level auth wins; env fills defaults. Legacy `elevenLabsApiKey` maps to elevenlabs.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const isPlainObject = (value) => (
|
|
7
|
+
value != null && typeof value === 'object' && !Array.isArray(value)
|
|
8
|
+
);
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Normalize legacy env aliases into `contents.auth`.
|
|
12
|
+
* @param {Record<string, unknown>} contents
|
|
13
|
+
* @returns {Record<string, unknown>}
|
|
14
|
+
*/
|
|
15
|
+
const normalizeContentsAuth = (contents = {}) => {
|
|
16
|
+
const
|
|
17
|
+
auth = isPlainObject(contents.auth) ? {...contents.auth} : {};
|
|
18
|
+
|
|
19
|
+
if (contents.elevenLabsApiKey) {
|
|
20
|
+
auth.elevenlabs = {
|
|
21
|
+
...(isPlainObject(auth.elevenlabs) ? auth.elevenlabs : {}),
|
|
22
|
+
apiKey: contents.elevenLabsApiKey
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
return auth;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Apply service auth defaults onto a job config (mutates config).
|
|
31
|
+
* @param {Record<string, unknown>} config job listing from cheerfully.json
|
|
32
|
+
* @param {Record<string, unknown>} contents merged cheer + env + CLI
|
|
33
|
+
* @param {string} [service] Cheerfully service id (defaults to contents.service)
|
|
34
|
+
* @returns {Record<string, unknown>} config
|
|
35
|
+
*/
|
|
36
|
+
const applyServiceAuth = (config, contents = {}, service = contents.service) => {
|
|
37
|
+
if (!config || typeof config !== 'object') {
|
|
38
|
+
return config;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const
|
|
42
|
+
authMap = normalizeContentsAuth(contents),
|
|
43
|
+
fromEnv = service && isPlainObject(authMap[service]) ? {...authMap[service]} : {},
|
|
44
|
+
fromJob = isPlainObject(config.auth) ? {...config.auth} : {},
|
|
45
|
+
// Legacy ElevenLabs top-level apiKey on the job
|
|
46
|
+
legacyJobKey = service === 'elevenlabs'
|
|
47
|
+
&& typeof config.apiKey === 'string'
|
|
48
|
+
&& config.apiKey
|
|
49
|
+
&& fromJob.apiKey == null
|
|
50
|
+
? {apiKey: config.apiKey}
|
|
51
|
+
: {};
|
|
52
|
+
|
|
53
|
+
const
|
|
54
|
+
merged = {
|
|
55
|
+
...fromEnv,
|
|
56
|
+
...legacyJobKey,
|
|
57
|
+
...fromJob
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
if (isPlainObject(fromEnv.config) || isPlainObject(fromJob.config) || isPlainObject(legacyJobKey.config)) {
|
|
61
|
+
merged.config = {
|
|
62
|
+
...(isPlainObject(fromEnv.config) ? fromEnv.config : {}),
|
|
63
|
+
...(isPlainObject(fromJob.config) ? fromJob.config : {})
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
if (isPlainObject(fromEnv.config?.credentials) || isPlainObject(fromJob.config?.credentials)) {
|
|
67
|
+
merged.config.credentials = {
|
|
68
|
+
...(isPlainObject(fromEnv.config?.credentials) ? fromEnv.config.credentials : {}),
|
|
69
|
+
...(isPlainObject(fromJob.config?.credentials) ? fromJob.config.credentials : {})
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if (Object.keys(merged).length) {
|
|
75
|
+
config.auth = merged;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Prefer auth container; drop legacy top-level apiKey once folded in.
|
|
79
|
+
if (service === 'elevenlabs' && config.apiKey != null && config.auth?.apiKey) {
|
|
80
|
+
delete config.apiKey;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return config;
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
module.exports = {
|
|
87
|
+
normalizeContentsAuth,
|
|
88
|
+
applyServiceAuth
|
|
89
|
+
};
|