@makefully/dutifully 1.11.0 → 1.12.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 +4 -0
- package/package.json +1 -1
- package/src/classes/LipSync.js +11 -3
- package/src/classes/TextToSpeech.js +14 -3
- package/src/classes/Transcription.js +9 -2
- package/src/formats/File.js +3 -1
- package/src/formats/JSONFile.js +4 -2
- package/src/formats/MP3.js +4 -2
- package/src/helpers/dirs.js +30 -0
- package/src/helpers/getCaptions.js +4 -4
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.12.0
|
|
4
|
+
|
|
5
|
+
- Treat missing `src`/`output` directories as empty instead of failing; create output directories when writing.
|
|
6
|
+
|
|
3
7
|
## 1.11.0
|
|
4
8
|
|
|
5
9
|
- Preserve optional script-line `class` through transcription for VTT cue classes, SAMI spans, and JSON caption output.
|
package/package.json
CHANGED
package/src/classes/LipSync.js
CHANGED
|
@@ -2,11 +2,12 @@ const
|
|
|
2
2
|
Cheer = require('./Cheer'),
|
|
3
3
|
fs = require('fs').promises,
|
|
4
4
|
getJSON = require('../helpers/getJSON'),
|
|
5
|
+
{ensureDir, ensureParentDir, readdirOrEmpty} = require('../helpers/dirs'),
|
|
5
6
|
id3 = require('node-id3').Promise,
|
|
6
7
|
filter = (fileType, file) => (file.indexOf('.') !== 0) && (file.slice(-(fileType.length + 1)) === `.${fileType}`),
|
|
7
8
|
combine = async (path, fileType) => {
|
|
8
9
|
const
|
|
9
|
-
files = (await
|
|
10
|
+
files = (await readdirOrEmpty(path)).filter(filter.bind(null, fileType)),
|
|
10
11
|
list = [];
|
|
11
12
|
|
|
12
13
|
for (let i = 0; i < files.length; i++) {
|
|
@@ -56,7 +57,13 @@ const
|
|
|
56
57
|
{config} = this,
|
|
57
58
|
{files = {}, options = {}, output, src} = config,
|
|
58
59
|
{exportFormat = 'json', output: outputType = 'json'} = options,
|
|
59
|
-
file = 'mouthCues.json'
|
|
60
|
+
file = 'mouthCues.json';
|
|
61
|
+
|
|
62
|
+
if (output) {
|
|
63
|
+
await ensureDir(output);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const
|
|
60
67
|
raw = outputType === 'json' && exportFormat !== 'mp3' ? await getJSON(`${output}${file}`) ?? {} : null,
|
|
61
68
|
alreadyLipSynced = raw ? Object.keys(raw) : await combine(output, exportFormat),
|
|
62
69
|
check = (id) => {
|
|
@@ -83,6 +90,7 @@ const
|
|
|
83
90
|
console.log(`Removed "${key}"`);
|
|
84
91
|
}
|
|
85
92
|
if (raw) {
|
|
93
|
+
await ensureParentDir(`${output}${file}`);
|
|
86
94
|
await fs.writeFile(`${output}${file}`, JSON.stringify(sortKeys({
|
|
87
95
|
...raw,
|
|
88
96
|
...newLipSync ? await getJSON(`${output}${file}`) ?? {} : {} // get new version.
|
|
@@ -91,7 +99,7 @@ const
|
|
|
91
99
|
|
|
92
100
|
return alreadyLipSynced.length;
|
|
93
101
|
},
|
|
94
|
-
list = (await
|
|
102
|
+
list = (await readdirOrEmpty(src)).filter(filter.bind(null, 'mp3')).filter((file) => !check(file.substring(0, file.length - 4)));
|
|
95
103
|
|
|
96
104
|
this.differenceOnly = true;
|
|
97
105
|
|
|
@@ -3,6 +3,7 @@ const
|
|
|
3
3
|
Transcript = require('./Transcript'),
|
|
4
4
|
fs = require('fs').promises,
|
|
5
5
|
getFileData = require('../helpers/getFileData'),
|
|
6
|
+
{ensureDir, readdirOrEmpty} = require('../helpers/dirs'),
|
|
6
7
|
id3 = require('node-id3').Promise,
|
|
7
8
|
isoLanguageMap = {
|
|
8
9
|
'en-US': 'eng',
|
|
@@ -11,7 +12,7 @@ const
|
|
|
11
12
|
matches = (file, types) => types.reduce((prev, current) => prev || (file.slice(-(current.length + 1)) === `.${current}`), false),
|
|
12
13
|
getMP3s = async ({encodedBy = '', output = './', fileTypes = [], transcripts}) => {
|
|
13
14
|
const
|
|
14
|
-
all = (await
|
|
15
|
+
all = (await readdirOrEmpty(output))
|
|
15
16
|
.filter((file) => (file.indexOf('.') !== 0) && (fileTypes.length === 0 || matches(file, fileTypes)));
|
|
16
17
|
|
|
17
18
|
if (transcripts) {
|
|
@@ -76,7 +77,11 @@ const
|
|
|
76
77
|
}
|
|
77
78
|
}
|
|
78
79
|
},
|
|
79
|
-
appendMP3Meta = ({album, captions, generated = false, language, output, voice}) => {
|
|
80
|
+
appendMP3Meta = async ({album, captions, generated = false, language, output, voice}) => {
|
|
81
|
+
if (output) {
|
|
82
|
+
await ensureDir(output);
|
|
83
|
+
}
|
|
84
|
+
|
|
80
85
|
Object.keys(captions).forEach(async (id) => {
|
|
81
86
|
const
|
|
82
87
|
text = captionText(captions[id]),
|
|
@@ -140,7 +145,13 @@ const
|
|
|
140
145
|
async checkDifference () {
|
|
141
146
|
const
|
|
142
147
|
{album, config, encodedBy = '', transcripts} = this,
|
|
143
|
-
{output, files = {}, language, updateAllMetaData = false} = config
|
|
148
|
+
{output, files = {}, language, updateAllMetaData = false} = config;
|
|
149
|
+
|
|
150
|
+
if (output) {
|
|
151
|
+
await ensureDir(output);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
const
|
|
144
155
|
{missing, present, unlisted} = await getMP3s({
|
|
145
156
|
encodedBy,
|
|
146
157
|
fileTypes: ['mp3'],
|
|
@@ -5,6 +5,7 @@ const
|
|
|
5
5
|
getCaptions = require('../helpers/getCaptions'),
|
|
6
6
|
getFileData = require('../helpers/getFileData'),
|
|
7
7
|
getJSON = require('../helpers/getJSON'),
|
|
8
|
+
{ensureDir, readdirOrEmpty} = require('../helpers/dirs'),
|
|
8
9
|
SEPARATE_CHARACTER = '|',
|
|
9
10
|
JOIN_CHARACTER = '^',
|
|
10
11
|
filter = (fileType, file) => (file.indexOf('.') !== 0) && (file.slice(-(fileType.length + 1)) === `.${fileType}`),
|
|
@@ -44,7 +45,13 @@ const
|
|
|
44
45
|
const
|
|
45
46
|
{config} = this,
|
|
46
47
|
{files, format = 'json', output, src} = config,
|
|
47
|
-
fileType = format.toLowerCase()
|
|
48
|
+
fileType = format.toLowerCase();
|
|
49
|
+
|
|
50
|
+
if (output) {
|
|
51
|
+
await ensureDir(output);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const
|
|
48
55
|
{captions: alreadyCaptioned, file: captionsFile, files: fileMap} = await getCaptions(output, fileType),
|
|
49
56
|
check = (id, caption) => {
|
|
50
57
|
const
|
|
@@ -99,7 +106,7 @@ const
|
|
|
99
106
|
|
|
100
107
|
return olds.length;
|
|
101
108
|
},
|
|
102
|
-
list = (await
|
|
109
|
+
list = (await readdirOrEmpty(src)).filter(filter.bind(null, 'mp3')).filter((file) => {
|
|
103
110
|
const
|
|
104
111
|
id = file.substring(0, file.length - 4),
|
|
105
112
|
caption = files?.[id];
|
package/src/formats/File.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
const
|
|
2
2
|
fs = require('fs').promises,
|
|
3
|
+
{ensureParentDir} = require('../helpers/dirs'),
|
|
3
4
|
File = class {
|
|
4
5
|
constructor ({fileType}) {
|
|
5
6
|
this.data = null;
|
|
@@ -15,7 +16,8 @@ const
|
|
|
15
16
|
return null; // does not support hashes.
|
|
16
17
|
}
|
|
17
18
|
|
|
18
|
-
save () { // return a promise
|
|
19
|
+
async save () { // return a promise
|
|
20
|
+
await ensureParentDir(this.path);
|
|
19
21
|
return fs.writeFile(`${this.path}`, this.data ?? '');
|
|
20
22
|
}
|
|
21
23
|
|
package/src/formats/JSONFile.js
CHANGED
|
@@ -2,6 +2,7 @@ const
|
|
|
2
2
|
crypto = require('crypto'),
|
|
3
3
|
fs = require('fs').promises,
|
|
4
4
|
File = require("./File"),
|
|
5
|
+
{ensureParentDir} = require('../helpers/dirs'),
|
|
5
6
|
getJSON = require('../helpers/getJSON'),
|
|
6
7
|
sortKeys = (obj) => Object.keys(obj).sort().reduce((s, key) => {
|
|
7
8
|
s[key] = obj[key];
|
|
@@ -11,7 +12,7 @@ const
|
|
|
11
12
|
module.exports = class JSONFile extends File {
|
|
12
13
|
async load (path) {
|
|
13
14
|
await super.load(path);
|
|
14
|
-
this.data = await getJSON(path);
|
|
15
|
+
this.data = await getJSON(path) ?? {};
|
|
15
16
|
return this.data;
|
|
16
17
|
}
|
|
17
18
|
|
|
@@ -34,7 +35,8 @@ module.exports = class JSONFile extends File {
|
|
|
34
35
|
});
|
|
35
36
|
}
|
|
36
37
|
|
|
37
|
-
save () { // return a promise
|
|
38
|
+
async save () { // return a promise
|
|
39
|
+
await ensureParentDir(this.path);
|
|
38
40
|
return fs.writeFile(`${this.path}`, JSON.stringify(this.data, null, 4));
|
|
39
41
|
}
|
|
40
42
|
}
|
package/src/formats/MP3.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
const
|
|
2
2
|
id3 = require('node-id3').Promise,
|
|
3
|
-
File = require("./File")
|
|
3
|
+
File = require("./File"),
|
|
4
|
+
{ensureParentDir} = require('../helpers/dirs');
|
|
4
5
|
|
|
5
6
|
module.exports = class MP3 extends File {
|
|
6
7
|
async load (path) {
|
|
@@ -31,7 +32,8 @@ module.exports = class MP3 extends File {
|
|
|
31
32
|
return null;
|
|
32
33
|
}
|
|
33
34
|
|
|
34
|
-
save () { // return a promise
|
|
35
|
+
async save () { // return a promise
|
|
36
|
+
await ensureParentDir(this.path);
|
|
35
37
|
return id3.write(this.data, this.path);
|
|
36
38
|
}
|
|
37
39
|
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
const
|
|
2
|
+
fs = require('fs').promises,
|
|
3
|
+
path = require('path'),
|
|
4
|
+
ensureDir = async (dir) => {
|
|
5
|
+
if (dir) {
|
|
6
|
+
await fs.mkdir(dir, {recursive: true});
|
|
7
|
+
}
|
|
8
|
+
},
|
|
9
|
+
ensureParentDir = async (filePath) => {
|
|
10
|
+
if (filePath) {
|
|
11
|
+
await ensureDir(path.dirname(filePath));
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
readdirOrEmpty = async (dir) => {
|
|
15
|
+
try {
|
|
16
|
+
return await fs.readdir(dir);
|
|
17
|
+
} catch (e) {
|
|
18
|
+
if (e?.code === 'ENOENT') {
|
|
19
|
+
return [];
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
throw e;
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
module.exports = {
|
|
27
|
+
ensureDir,
|
|
28
|
+
ensureParentDir,
|
|
29
|
+
readdirOrEmpty
|
|
30
|
+
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
const
|
|
2
|
-
fs = require('fs').promises,
|
|
3
2
|
getFileData = require("./getFileData"),
|
|
3
|
+
{readdirOrEmpty} = require("./dirs"),
|
|
4
4
|
filter = (fileType, file) => (file.indexOf('.') !== 0) && (file.slice(-(fileType.length + 1)) === `.${fileType}`);
|
|
5
5
|
|
|
6
6
|
module.exports = async (path, fileType) => {
|
|
@@ -9,10 +9,10 @@ module.exports = async (path, fileType) => {
|
|
|
9
9
|
if (fileType.toLowerCase() === 'json') {
|
|
10
10
|
const
|
|
11
11
|
file = await getFileData(`${path}captions.json`, 'json'),
|
|
12
|
-
{data: json} = file;
|
|
12
|
+
{data: json = {}} = file;
|
|
13
13
|
|
|
14
14
|
return {
|
|
15
|
-
captions: Object.keys(json).reduce((obj, key) => {
|
|
15
|
+
captions: Object.keys(json ?? {}).reduce((obj, key) => {
|
|
16
16
|
obj[key] = json[key].map(({content}) => content).join(' ');
|
|
17
17
|
return obj;
|
|
18
18
|
}, {}),
|
|
@@ -21,7 +21,7 @@ module.exports = async (path, fileType) => {
|
|
|
21
21
|
};
|
|
22
22
|
} else { // Otherwise we pull all the files and construct a key/value object.
|
|
23
23
|
const
|
|
24
|
-
files = (await
|
|
24
|
+
files = (await readdirOrEmpty(path)).filter(filter.bind(null, fileType)),
|
|
25
25
|
captions = {},
|
|
26
26
|
fileMap = {};
|
|
27
27
|
|