@makefully/dutifully 1.22.0 → 1.23.0
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 +9 -0
- package/README.md +29 -4
- package/package.json +5 -2
- package/src/helpers/jobExports.js +17 -1
- package/src/helpers/resolveProviderService.js +86 -0
- package/src/index.js +41 -5
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,15 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
+
## 1.23.0
|
|
6
|
+
|
|
7
|
+
- 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.
|
|
8
|
+
- Concrete MT parsers: `amazontranslate`, `deepl`, `googletranslate`, `microsofttranslate`.
|
|
9
|
+
|
|
10
|
+
## 1.22.1
|
|
11
|
+
|
|
12
|
+
- Allow translate `format: "catalog"` (Cheerfully flat `{lang}.json` app catalogs) alongside existing loc formats.
|
|
13
|
+
|
|
5
14
|
## 1.22.0
|
|
6
15
|
|
|
7
16
|
- Select a subset of service listings via optional job `id` and top-level `only` (`cheer -service packfully -only heroes,ui`).
|
package/README.md
CHANGED
|
@@ -432,13 +432,22 @@ Script lines may also include an optional `events` array for one-shot timed mark
|
|
|
432
432
|
|
|
433
433
|
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"`).
|
|
434
434
|
|
|
435
|
-
###
|
|
435
|
+
### Provider umbrellas (`translate`, `lipsync`, `voiceover`)
|
|
436
436
|
|
|
437
|
-
|
|
437
|
+
Dutifully logical services pick a Cheerfully processor via per-job `provider` (stripped before upload):
|
|
438
|
+
|
|
439
|
+
| Dutifully `-service` | `provider` | Default | Cheerfully target |
|
|
440
|
+
| --- | --- | --- | --- |
|
|
441
|
+
| `translate` | `amazon`, `deepl`, `google`, `microsoft` | `amazon` | `amazontranslate`, `deepl`, `googletranslate`, `microsofttranslate` |
|
|
442
|
+
| `lipsync` | `rhubarb`, `allosaurus` | `rhubarb` | same as provider |
|
|
443
|
+
| `voiceover` | `polly`, `elevenlabs` | `elevenlabs` | same as provider |
|
|
444
|
+
|
|
445
|
+
Concrete keys (`deepl`, `polly`, `rhubarb`, …) still work without `provider`.
|
|
438
446
|
|
|
439
447
|
```javascript
|
|
440
448
|
{
|
|
441
449
|
"translate": [{
|
|
450
|
+
"provider": "amazon",
|
|
442
451
|
"script": "./example/script.json",
|
|
443
452
|
"language": "en-US",
|
|
444
453
|
"languages": ["es-ES", "fr-FR"],
|
|
@@ -449,19 +458,35 @@ This text-to-text service translates Cheerfully scripts or language-matrix CSV/T
|
|
|
449
458
|
"language": "en-US",
|
|
450
459
|
"languages": ["es-ES", "fr-FR"],
|
|
451
460
|
"output": "./example/scripts/"
|
|
461
|
+
}],
|
|
462
|
+
"voiceover": [{
|
|
463
|
+
"provider": "elevenlabs",
|
|
464
|
+
"script": "./example/script.json",
|
|
465
|
+
"output": "./example/vo/en/",
|
|
466
|
+
"voice": "21m00Tcm4TlvDq8ikWAM"
|
|
467
|
+
}],
|
|
468
|
+
"lipsync": [{
|
|
469
|
+
"provider": "rhubarb",
|
|
470
|
+
"src": "./example/vo/en/",
|
|
471
|
+
"output": "./example/lipsync/en/"
|
|
452
472
|
}]
|
|
453
473
|
}
|
|
454
474
|
```
|
|
455
475
|
|
|
476
|
+
Translate job fields:
|
|
477
|
+
|
|
456
478
|
* `script` — path to a Cheerfully `.json` script or a language-matrix `.csv` / `.tsv` (first column = ids, first row = language tags).
|
|
457
479
|
* `language` — source locale (CSV: source column). Matrix jobs require this; JSON may use `auto`.
|
|
458
480
|
* `languages` — target locales to produce or fill.
|
|
459
481
|
* `format` / `exports` — optional. Defaults to the source extension (`json`, `csv`, or `tsv`).
|
|
460
482
|
* `ignore` — phrases left untranslated.
|
|
483
|
+
* `provider` — MT engine (see table); default `amazon`.
|
|
484
|
+
|
|
485
|
+
JSON sources write `{lang}/{basename}.{ext}` under `output` (`catalog` → `{lang}.json`). CSV/TSV sources write an updated matrix file and leave existing non-empty cells unchanged.
|
|
461
486
|
|
|
462
|
-
|
|
487
|
+
Supported formats: `json`, `i18n`, `catalog`, `csv`, `tsv`, `po`, `xliff`, `properties`, `yml` / `yaml`, `strings`, `xml`.
|
|
463
488
|
|
|
464
|
-
|
|
489
|
+
`catalog` writes flat key → caption JSON as `{lang}.json` (app localization layout). `i18n` uses the same payload under `{lang}/{basename}.i18n.json`.
|
|
465
490
|
|
|
466
491
|
## Example
|
|
467
492
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@makefully/dutifully",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.23.0",
|
|
4
4
|
"description": "Client tools for Cheerfully service.",
|
|
5
5
|
"main": "./src/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -52,7 +52,10 @@
|
|
|
52
52
|
"atlas",
|
|
53
53
|
"spritesheet",
|
|
54
54
|
"classfully",
|
|
55
|
-
"translate"
|
|
55
|
+
"translate",
|
|
56
|
+
"deepl",
|
|
57
|
+
"voiceover",
|
|
58
|
+
"lipsync"
|
|
56
59
|
],
|
|
57
60
|
"author": "Derek Detweiler <derek@makefullystudios.com>",
|
|
58
61
|
"contributors": [
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
const LYRIC_FORMATS = ['json', 'lrc', 'mp3', 'sami', 'smi', 'srt', 'vtt', 'tsv', 'xml', 'dat'];
|
|
7
7
|
const TRANSCRIPTION_FORMATS = ['json', 'lrc', 'mp3', 'sami', 'smi', 'srt', 'vtt'];
|
|
8
8
|
const PACKFULLY_FORMATS = ['texturepacker', 'json-array', 'createjs', 'spine', 'css', 'starling', 'unity', 'godot'];
|
|
9
|
-
const TRANSLATE_FORMATS = ['json', 'i18n', 'po', 'xliff', 'properties', 'yml', 'yaml', 'strings', 'xml', 'csv', 'tsv'];
|
|
9
|
+
const TRANSLATE_FORMATS = ['json', 'i18n', 'catalog', 'po', 'xliff', 'properties', 'yml', 'yaml', 'strings', 'xml', 'csv', 'tsv'];
|
|
10
10
|
|
|
11
11
|
const EXPORT_CATALOG = {
|
|
12
12
|
transcription: {
|
|
@@ -28,6 +28,22 @@ const EXPORT_CATALOG = {
|
|
|
28
28
|
keys: ['format', 'exports'],
|
|
29
29
|
formats: TRANSLATE_FORMATS
|
|
30
30
|
},
|
|
31
|
+
amazontranslate: {
|
|
32
|
+
keys: ['format', 'exports'],
|
|
33
|
+
formats: TRANSLATE_FORMATS
|
|
34
|
+
},
|
|
35
|
+
deepl: {
|
|
36
|
+
keys: ['format', 'exports'],
|
|
37
|
+
formats: TRANSLATE_FORMATS
|
|
38
|
+
},
|
|
39
|
+
googletranslate: {
|
|
40
|
+
keys: ['format', 'exports'],
|
|
41
|
+
formats: TRANSLATE_FORMATS
|
|
42
|
+
},
|
|
43
|
+
microsofttranslate: {
|
|
44
|
+
keys: ['format', 'exports'],
|
|
45
|
+
formats: TRANSLATE_FORMATS
|
|
46
|
+
},
|
|
31
47
|
packfully: {
|
|
32
48
|
keys: ['format', 'exports', 'exportFormat'],
|
|
33
49
|
formats: PACKFULLY_FORMATS
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Map Dutifully umbrella services + provider → Cheerfully concrete service ids.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
const
|
|
6
|
+
UMBRELLAS = {
|
|
7
|
+
translate: {
|
|
8
|
+
defaultProvider: 'amazon',
|
|
9
|
+
providers: {
|
|
10
|
+
amazon: 'amazontranslate',
|
|
11
|
+
amazontranslate: 'amazontranslate',
|
|
12
|
+
deepl: 'deepl',
|
|
13
|
+
google: 'googletranslate',
|
|
14
|
+
googletranslate: 'googletranslate',
|
|
15
|
+
microsoft: 'microsofttranslate',
|
|
16
|
+
microsofttranslate: 'microsofttranslate'
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
lipsync: {
|
|
20
|
+
defaultProvider: 'rhubarb',
|
|
21
|
+
providers: {
|
|
22
|
+
rhubarb: 'rhubarb',
|
|
23
|
+
allosaurus: 'allosaurus'
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
voiceover: {
|
|
27
|
+
defaultProvider: 'elevenlabs',
|
|
28
|
+
providers: {
|
|
29
|
+
polly: 'polly',
|
|
30
|
+
elevenlabs: 'elevenlabs'
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
isUmbrella = (service) => Boolean(UMBRELLAS[service]),
|
|
35
|
+
/**
|
|
36
|
+
* @returns {{ cheerfullyService: string, provider: string, job: object }}
|
|
37
|
+
*/
|
|
38
|
+
resolveProviderService = (umbrella, job = {}) => {
|
|
39
|
+
const
|
|
40
|
+
table = UMBRELLAS[umbrella];
|
|
41
|
+
|
|
42
|
+
if (!table) {
|
|
43
|
+
const
|
|
44
|
+
cleaned = {...job};
|
|
45
|
+
|
|
46
|
+
delete cleaned.provider;
|
|
47
|
+
|
|
48
|
+
return {
|
|
49
|
+
cheerfullyService: umbrella,
|
|
50
|
+
provider: null,
|
|
51
|
+
job: cleaned
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const
|
|
56
|
+
raw = job.provider == null || job.provider === ''
|
|
57
|
+
? table.defaultProvider
|
|
58
|
+
: String(job.provider).toLowerCase().trim(),
|
|
59
|
+
cheerfullyService = table.providers[raw];
|
|
60
|
+
|
|
61
|
+
if (!cheerfullyService) {
|
|
62
|
+
const
|
|
63
|
+
allowed = [...new Set(Object.keys(table.providers))].sort().join(', ');
|
|
64
|
+
|
|
65
|
+
throw new Error(
|
|
66
|
+
`Unknown provider "${job.provider}" for "${umbrella}". Use one of: ${allowed}.`
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const
|
|
71
|
+
cleaned = {...job};
|
|
72
|
+
|
|
73
|
+
delete cleaned.provider;
|
|
74
|
+
|
|
75
|
+
return {
|
|
76
|
+
cheerfullyService,
|
|
77
|
+
provider: raw,
|
|
78
|
+
job: cleaned
|
|
79
|
+
};
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
module.exports = {
|
|
83
|
+
UMBRELLAS,
|
|
84
|
+
isUmbrella,
|
|
85
|
+
resolveProviderService
|
|
86
|
+
};
|
package/src/index.js
CHANGED
|
@@ -2,11 +2,16 @@
|
|
|
2
2
|
/* global console, process, require */
|
|
3
3
|
const
|
|
4
4
|
LipSync = require('./classes/LipSync'),
|
|
5
|
+
Translate = require('./classes/Translate'),
|
|
5
6
|
parsers = {
|
|
6
7
|
allosaurus: LipSync,
|
|
8
|
+
amazontranslate: Translate,
|
|
7
9
|
classfully: require('./classes/Classfully'),
|
|
10
|
+
deepl: Translate,
|
|
8
11
|
elevenlabs: require('./classes/ElevenLabs'),
|
|
9
12
|
ffmpeg: require('./classes/FFMPEG'),
|
|
13
|
+
googletranslate: Translate,
|
|
14
|
+
microsofttranslate: Translate,
|
|
10
15
|
packfully: require('./classes/Packfully'),
|
|
11
16
|
polly: require('./classes/Polly'),
|
|
12
17
|
rasterize: require('./classes/Rasterize'),
|
|
@@ -14,10 +19,12 @@ const
|
|
|
14
19
|
sharp: require('./classes/Sharp'),
|
|
15
20
|
stackfully: require('./classes/Stackfully'),
|
|
16
21
|
transcription: require('./classes/Transcription'),
|
|
17
|
-
translate
|
|
22
|
+
// Concrete only — umbrellas (translate / lipsync / voiceover) resolve first.
|
|
23
|
+
translate: Translate
|
|
18
24
|
},
|
|
19
25
|
getJSON = require('./helpers/getJSON'),
|
|
20
26
|
{normalizeOnly, filterJobsById} = require('./helpers/filterJobs'),
|
|
27
|
+
{isUmbrella, resolveProviderService} = require('./helpers/resolveProviderService'),
|
|
21
28
|
send = async function (contents) {
|
|
22
29
|
const
|
|
23
30
|
{id, service} = contents,
|
|
@@ -36,11 +43,40 @@ const
|
|
|
36
43
|
continue;
|
|
37
44
|
}
|
|
38
45
|
|
|
46
|
+
let
|
|
47
|
+
cheerfullyService = service,
|
|
48
|
+
jobConfig = config;
|
|
49
|
+
|
|
50
|
+
if (isUmbrella(service)) {
|
|
51
|
+
try {
|
|
52
|
+
const
|
|
53
|
+
resolved = resolveProviderService(service, config);
|
|
54
|
+
|
|
55
|
+
cheerfullyService = resolved.cheerfullyService;
|
|
56
|
+
jobConfig = resolved.job;
|
|
57
|
+
} catch (e) {
|
|
58
|
+
console.warn(e.message);
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const
|
|
64
|
+
Parser = parsers[cheerfullyService];
|
|
65
|
+
|
|
66
|
+
if (!Parser) {
|
|
67
|
+
console.warn(`No Dutifully parser for Cheerfully service "${cheerfullyService}".`);
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
|
|
39
71
|
const
|
|
40
|
-
|
|
72
|
+
contentsForJob = {
|
|
73
|
+
...contents,
|
|
74
|
+
service: cheerfullyService
|
|
75
|
+
},
|
|
76
|
+
serviceHandler = new Parser({config: jobConfig, contents: contentsForJob});
|
|
41
77
|
|
|
42
78
|
try {
|
|
43
|
-
await serviceHandler.prepare({...
|
|
79
|
+
await serviceHandler.prepare({...contentsForJob, ...jobConfig});
|
|
44
80
|
} catch (e) {
|
|
45
81
|
console.warn(e.message);
|
|
46
82
|
continue;
|
|
@@ -48,10 +84,10 @@ const
|
|
|
48
84
|
|
|
49
85
|
try {
|
|
50
86
|
await serviceHandler.send({
|
|
51
|
-
instanceId: `${id}-${
|
|
87
|
+
instanceId: `${id}-${cheerfullyService}${length > 1 ? `-${index}` : ''}`
|
|
52
88
|
});
|
|
53
89
|
} catch (e) {
|
|
54
|
-
console.warn(`Error running "${
|
|
90
|
+
console.warn(`Error running "${cheerfullyService}" (${index}): ${e.message || e}`);
|
|
55
91
|
}
|
|
56
92
|
}
|
|
57
93
|
};
|