@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 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 to specify a root `./env-cheerfully.json` file with the following format:
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 your ElevenLabs API Key directly in the ElevenLabs spec above as `apiKey`, or set your API Key separately in `env-cheerfully.json`:
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
- "elevenLabsApiKey": "elevenlabsapikeyforyouraccount"
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@makefully/dutifully",
3
- "version": "1.23.0",
3
+ "version": "1.23.1",
4
4
  "description": "Client tools for Cheerfully service.",
5
5
  "main": "./src/index.js",
6
6
  "bin": {
@@ -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, elevenLabsApiKey} = data.contents;
7
+ {id} = data.contents;
8
8
 
9
9
  data.album = id;
10
10
  data.encodedBy = 'ElevenLabs';
11
- if (elevenLabsApiKey) {
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
+ };