@makefully/dutifully 1.10.1 → 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.
@@ -1,163 +1,170 @@
1
- const
2
- Cheer = require('./Cheer'),
3
- Transcript = require('./Transcript'),
4
- fs = require('fs').promises,
5
- getCaptions = require('../helpers/getCaptions'),
6
- getFileData = require('../helpers/getFileData'),
7
- getJSON = require('../helpers/getJSON'),
8
- SEPARATE_CHARACTER = '|',
9
- JOIN_CHARACTER = '^',
10
- filter = (fileType, file) => (file.indexOf('.') !== 0) && (file.slice(-(fileType.length + 1)) === `.${fileType}`),
11
- mapReduction = (map, list) => list.reduce((obj, file) => {
12
- const
13
- id = file.substring(0, file.length - 4);
14
-
15
- obj[id] = map[id];
16
- return obj;
17
- }, {}),
18
- Transcription = class Transcription extends Cheer {
19
- async prepare (data) {
20
- this.differenceOnly = false;
21
- this.updateList = null;
22
- await this.replaceConfigPathWithJSON('script', 'files');
23
-
24
- // create standardized transcripts and an accompanying hash for each.
25
- const
26
- {files, voice} = this.config,
27
- standardizedFiles = {},
28
- transcripts = {};
29
-
30
- Object.keys(files).forEach((key) => {
31
- const
32
- transcript = new Transcript(files[key], {voice});
33
-
34
- standardizedFiles[key] = transcript.toJSON();
35
- transcripts[key] = transcript;
36
- });
37
- this.config.files = standardizedFiles;
38
- this.transcripts = transcripts;
39
-
40
- return super.prepare(data);
41
- }
42
-
43
- async checkDifference () {
44
- const
45
- {config} = this,
46
- {files, format = 'json', output, src} = config,
47
- fileType = format.toLowerCase(),
48
- {captions: alreadyCaptioned, file: captionsFile, files: fileMap} = await getCaptions(output, fileType),
49
- check = (id, caption) => {
50
- const
51
- file = fileMap?.[id],
52
- original = alreadyCaptioned[id],
53
- hash = file?.getHash();
54
-
55
- delete alreadyCaptioned[id];
56
-
57
- if (hash) {
58
- const
59
- same = hash === this.transcripts[id].getHash();
60
-
61
- if (!same) {
62
- console.log(`Updating "${id}".`);
63
- }
64
-
65
- return same;
66
- } else {
67
- const
68
- cap = (Array.isArray(caption) ? caption : [caption]).map((cap) => cap?.caption ?? cap ?? null).filter((cap) => cap !== null).join(' ').replaceAll(JOIN_CHARACTER, ' ').replaceAll(SEPARATE_CHARACTER, ' ');
69
-
70
- if (cap && cap !== original) {
71
- console.log(`Updating "${id}".`);
72
- }
73
-
74
- // if a caption is not supplied, we'll assume the existence of an original caption means we don't need to redo.
75
- return cap === '' || cap === original;
76
- }
77
- },
78
- mergeCaptions = async (newCaptions = false) => { // Must run _after_ all checks and will remove what's left.
79
- const
80
- olds = Object.keys(alreadyCaptioned);
81
-
82
- for (let i = 0; i < olds.length; i++) {
83
- const
84
- caption = olds[i];
85
-
86
- if (captionsFile) {
87
- captionsFile.removeKey(caption);
88
- } else {
89
- await fs.rm(`${output}${caption}.${fileType}`);
90
- }
91
- console.log(`Removed "${caption}"`);
92
- }
93
- if (captionsFile) {
94
- if (newCaptions) {
95
- captionsFile.mergeData(await getJSON(`${output}captions.json`));
96
- }
97
- await captionsFile.save();
98
- }
99
-
100
- return olds.length;
101
- },
102
- list = (await fs.readdir(src)).filter(filter.bind(null, 'mp3')).filter((file) => {
103
- const
104
- id = file.substring(0, file.length - 4),
105
- caption = files?.[id];
106
-
107
- return !check(id, caption);
108
- });
109
-
110
- this.differenceOnly = true;
111
-
112
- if (list.length === 0) {
113
- // We'll still run the merge in case any have been removed.
114
- if (await mergeCaptions()) {
115
- throw Error('Old captions removed, but no new captions required generation.');
116
- } else {
117
- throw Error('Captions already up to date.');
118
- }
119
- }
120
-
121
- this.updateList = list;
122
- config.files = mapReduction(files, list);
123
- this.mergeCaptions = mergeCaptions;
124
- }
125
-
126
- beforeSend (archive) {
127
- const
128
- {src} = this.config;
129
-
130
- if (this.differenceOnly) {
131
- this.updateList.forEach((file) => archive.file(`${src}${file}`, {
132
- name: file
133
- }));
134
- } else {
135
- archive.directory(src, false);
136
- }
137
- super.beforeSend(archive);
138
- }
139
-
140
- async afterExport (...args) {
141
- const
142
- {config, transcripts, updateList} = this,
143
- {format = 'json', output} = config;
144
-
145
- if (this.mergeCaptions) {
146
- await this.mergeCaptions(true);
147
- }
148
- if (format !== 'json') {
149
- for (let i = 0; i < updateList.length; i++) {
150
- const
151
- filename = updateList[i],
152
- id = filename.substring(0, filename.length - 4),
153
- file = await getFileData(`${output}${id}.${format}`, format);
154
-
155
- file.addHash(transcripts[id].getHash());
156
- await file.save();
157
- }
158
- }
159
- super.afterExport(...args)
160
- }
161
- };
162
-
1
+ const
2
+ Cheer = require('./Cheer'),
3
+ Transcript = require('./Transcript'),
4
+ fs = require('fs').promises,
5
+ getCaptions = require('../helpers/getCaptions'),
6
+ getFileData = require('../helpers/getFileData'),
7
+ getJSON = require('../helpers/getJSON'),
8
+ {ensureDir, readdirOrEmpty} = require('../helpers/dirs'),
9
+ SEPARATE_CHARACTER = '|',
10
+ JOIN_CHARACTER = '^',
11
+ filter = (fileType, file) => (file.indexOf('.') !== 0) && (file.slice(-(fileType.length + 1)) === `.${fileType}`),
12
+ mapReduction = (map, list) => list.reduce((obj, file) => {
13
+ const
14
+ id = file.substring(0, file.length - 4);
15
+
16
+ obj[id] = map[id];
17
+ return obj;
18
+ }, {}),
19
+ Transcription = class Transcription extends Cheer {
20
+ async prepare (data) {
21
+ this.differenceOnly = false;
22
+ this.updateList = null;
23
+ await this.replaceConfigPathWithJSON('script', 'files');
24
+
25
+ // create standardized transcripts and an accompanying hash for each.
26
+ const
27
+ {files, voice} = this.config,
28
+ standardizedFiles = {},
29
+ transcripts = {};
30
+
31
+ Object.keys(files).forEach((key) => {
32
+ const
33
+ transcript = new Transcript(files[key], {voice});
34
+
35
+ standardizedFiles[key] = transcript.toJSON();
36
+ transcripts[key] = transcript;
37
+ });
38
+ this.config.files = standardizedFiles;
39
+ this.transcripts = transcripts;
40
+
41
+ return super.prepare(data);
42
+ }
43
+
44
+ async checkDifference () {
45
+ const
46
+ {config} = this,
47
+ {files, format = 'json', output, src} = config,
48
+ fileType = format.toLowerCase();
49
+
50
+ if (output) {
51
+ await ensureDir(output);
52
+ }
53
+
54
+ const
55
+ {captions: alreadyCaptioned, file: captionsFile, files: fileMap} = await getCaptions(output, fileType),
56
+ check = (id, caption) => {
57
+ const
58
+ file = fileMap?.[id],
59
+ original = alreadyCaptioned[id],
60
+ hash = file?.getHash();
61
+
62
+ delete alreadyCaptioned[id];
63
+
64
+ if (hash) {
65
+ const
66
+ same = hash === this.transcripts[id].getHash();
67
+
68
+ if (!same) {
69
+ console.log(`Updating "${id}".`);
70
+ }
71
+
72
+ return same;
73
+ } else {
74
+ const
75
+ cap = (Array.isArray(caption) ? caption : [caption]).map((cap) => cap?.caption ?? cap ?? null).filter((cap) => cap !== null).join(' ').replaceAll(JOIN_CHARACTER, ' ').replaceAll(SEPARATE_CHARACTER, ' ');
76
+
77
+ if (cap && cap !== original) {
78
+ console.log(`Updating "${id}".`);
79
+ }
80
+
81
+ // if a caption is not supplied, we'll assume the existence of an original caption means we don't need to redo.
82
+ return cap === '' || cap === original;
83
+ }
84
+ },
85
+ mergeCaptions = async (newCaptions = false) => { // Must run _after_ all checks and will remove what's left.
86
+ const
87
+ olds = Object.keys(alreadyCaptioned);
88
+
89
+ for (let i = 0; i < olds.length; i++) {
90
+ const
91
+ caption = olds[i];
92
+
93
+ if (captionsFile) {
94
+ captionsFile.removeKey(caption);
95
+ } else {
96
+ await fs.rm(`${output}${caption}.${fileType}`);
97
+ }
98
+ console.log(`Removed "${caption}"`);
99
+ }
100
+ if (captionsFile) {
101
+ if (newCaptions) {
102
+ captionsFile.mergeData(await getJSON(`${output}captions.json`));
103
+ }
104
+ await captionsFile.save();
105
+ }
106
+
107
+ return olds.length;
108
+ },
109
+ list = (await readdirOrEmpty(src)).filter(filter.bind(null, 'mp3')).filter((file) => {
110
+ const
111
+ id = file.substring(0, file.length - 4),
112
+ caption = files?.[id];
113
+
114
+ return !check(id, caption);
115
+ });
116
+
117
+ this.differenceOnly = true;
118
+
119
+ if (list.length === 0) {
120
+ // We'll still run the merge in case any have been removed.
121
+ if (await mergeCaptions()) {
122
+ throw Error('Old captions removed, but no new captions required generation.');
123
+ } else {
124
+ throw Error('Captions already up to date.');
125
+ }
126
+ }
127
+
128
+ this.updateList = list;
129
+ config.files = mapReduction(files, list);
130
+ this.mergeCaptions = mergeCaptions;
131
+ }
132
+
133
+ beforeSend (archive) {
134
+ const
135
+ {src} = this.config;
136
+
137
+ if (this.differenceOnly) {
138
+ this.updateList.forEach((file) => archive.file(`${src}${file}`, {
139
+ name: file
140
+ }));
141
+ } else {
142
+ archive.directory(src, false);
143
+ }
144
+ super.beforeSend(archive);
145
+ }
146
+
147
+ async afterExport (...args) {
148
+ const
149
+ {config, transcripts, updateList} = this,
150
+ {format = 'json', output} = config;
151
+
152
+ if (this.mergeCaptions) {
153
+ await this.mergeCaptions(true);
154
+ }
155
+ if (format !== 'json') {
156
+ for (let i = 0; i < updateList.length; i++) {
157
+ const
158
+ filename = updateList[i],
159
+ id = filename.substring(0, filename.length - 4),
160
+ file = await getFileData(`${output}${id}.${format}`, format);
161
+
162
+ file.addHash(transcripts[id].getHash());
163
+ await file.save();
164
+ }
165
+ }
166
+ super.afterExport(...args)
167
+ }
168
+ };
169
+
163
170
  module.exports = Transcription;
@@ -1,27 +1,29 @@
1
- const
2
- fs = require('fs').promises,
3
- File = class {
4
- constructor ({fileType}) {
5
- this.data = null;
6
- this.fileType = fileType;
7
- }
8
-
9
- async load (path) {
10
- this.path = path;
11
- return null;
12
- }
13
-
14
- getHash () {
15
- return null; // does not support hashes.
16
- }
17
-
18
- save () { // return a promise
19
- return fs.writeFile(`${this.path}`, this.data ?? '');
20
- }
21
-
22
- toString () {
23
- return '';
24
- }
25
- }
26
-
1
+ const
2
+ fs = require('fs').promises,
3
+ {ensureParentDir} = require('../helpers/dirs'),
4
+ File = class {
5
+ constructor ({fileType}) {
6
+ this.data = null;
7
+ this.fileType = fileType;
8
+ }
9
+
10
+ async load (path) {
11
+ this.path = path;
12
+ return null;
13
+ }
14
+
15
+ getHash () {
16
+ return null; // does not support hashes.
17
+ }
18
+
19
+ async save () { // return a promise
20
+ await ensureParentDir(this.path);
21
+ return fs.writeFile(`${this.path}`, this.data ?? '');
22
+ }
23
+
24
+ toString () {
25
+ return '';
26
+ }
27
+ }
28
+
27
29
  module.exports = File;
@@ -1,40 +1,42 @@
1
- const
2
- crypto = require('crypto'),
3
- fs = require('fs').promises,
4
- File = require("./File"),
5
- getJSON = require('../helpers/getJSON'),
6
- sortKeys = (obj) => Object.keys(obj).sort().reduce((s, key) => {
7
- s[key] = obj[key];
8
- return s;
9
- }, {});
10
-
11
- module.exports = class JSONFile extends File {
12
- async load (path) {
13
- await super.load(path);
14
- this.data = await getJSON(path);
15
- return this.data;
16
- }
17
-
18
- getHash () {
19
- return this.data ? crypto.createHash('sha256').update(JSON.stringify(this.data)).digest('hex') : null;
20
- }
21
-
22
- toString () {
23
- return Object.keys(this.data).map((key) => this.data[key].map(({content}) => content).join(' ')).join('\n');
24
- }
25
-
26
- removeKey (key) {
27
- delete this.data[key];
28
- }
29
-
30
- mergeData (data) {
31
- this.data = sortKeys({
32
- ...this.data,
33
- ...data
34
- });
35
- }
36
-
37
- save () { // return a promise
38
- return fs.writeFile(`${this.path}`, JSON.stringify(this.data, null, 4));
39
- }
1
+ const
2
+ crypto = require('crypto'),
3
+ fs = require('fs').promises,
4
+ File = require("./File"),
5
+ {ensureParentDir} = require('../helpers/dirs'),
6
+ getJSON = require('../helpers/getJSON'),
7
+ sortKeys = (obj) => Object.keys(obj).sort().reduce((s, key) => {
8
+ s[key] = obj[key];
9
+ return s;
10
+ }, {});
11
+
12
+ module.exports = class JSONFile extends File {
13
+ async load (path) {
14
+ await super.load(path);
15
+ this.data = await getJSON(path) ?? {};
16
+ return this.data;
17
+ }
18
+
19
+ getHash () {
20
+ return this.data ? crypto.createHash('sha256').update(JSON.stringify(this.data)).digest('hex') : null;
21
+ }
22
+
23
+ toString () {
24
+ return Object.keys(this.data).map((key) => this.data[key].map(({content}) => content).join(' ')).join('\n');
25
+ }
26
+
27
+ removeKey (key) {
28
+ delete this.data[key];
29
+ }
30
+
31
+ mergeData (data) {
32
+ this.data = sortKeys({
33
+ ...this.data,
34
+ ...data
35
+ });
36
+ }
37
+
38
+ async save () { // return a promise
39
+ await ensureParentDir(this.path);
40
+ return fs.writeFile(`${this.path}`, JSON.stringify(this.data, null, 4));
41
+ }
40
42
  }
@@ -1,38 +1,38 @@
1
- const
2
- fs = require('fs').promises,
3
- os = require('os'),
4
- File = require("./File"),
5
- getSeparater = (str) => str.indexOf(os.EOL) >= 0 ? os.EOL : '\n';
6
-
7
- module.exports = class LRC extends File {
8
- async load (path) {
9
- await super.load(path);
10
- this.data = await fs.readFile(path, 'utf8');
11
- return this.data;
12
- }
13
-
14
- addHash (hash) {
15
- this.data = this.data.replace(/^\[hash:[a-f0-9]+\]\n?/im, '');
16
-
17
- const
18
- sep = getSeparater(this.data),
19
- insertAfter = this.data.match(/^\[generator:[^\]]*\]/im);
20
-
21
- if (insertAfter) {
22
- this.data = this.data.replace(insertAfter[0], `${insertAfter[0]}${sep}[hash:${hash}]`);
23
- } else {
24
- this.data = `[hash:${hash}]${sep}${this.data}`;
25
- }
26
- }
27
-
28
- getHash () {
29
- const
30
- match = this.data.match(/^\[hash:([a-f0-9]+)\]/im);
31
-
32
- return match ? match[1] : null;
33
- }
34
-
35
- toString () {
36
- return this.data.split(getSeparater(this.data)).filter((line) => line.length > 0 && line.indexOf('[') === 0 && line.indexOf(']') === 9).map((text) => text.substring(10)).join(' ');
37
- }
1
+ const
2
+ fs = require('fs').promises,
3
+ os = require('os'),
4
+ File = require("./File"),
5
+ getSeparater = (str) => str.indexOf(os.EOL) >= 0 ? os.EOL : '\n';
6
+
7
+ module.exports = class LRC extends File {
8
+ async load (path) {
9
+ await super.load(path);
10
+ this.data = await fs.readFile(path, 'utf8');
11
+ return this.data;
12
+ }
13
+
14
+ addHash (hash) {
15
+ this.data = this.data.replace(/^\[hash:[a-f0-9]+\]\n?/im, '');
16
+
17
+ const
18
+ sep = getSeparater(this.data),
19
+ insertAfter = this.data.match(/^\[generator:[^\]]*\]/im);
20
+
21
+ if (insertAfter) {
22
+ this.data = this.data.replace(insertAfter[0], `${insertAfter[0]}${sep}[hash:${hash}]`);
23
+ } else {
24
+ this.data = `[hash:${hash}]${sep}${this.data}`;
25
+ }
26
+ }
27
+
28
+ getHash () {
29
+ const
30
+ match = this.data.match(/^\[hash:([a-f0-9]+)\]/im);
31
+
32
+ return match ? match[1] : null;
33
+ }
34
+
35
+ toString () {
36
+ return this.data.split(getSeparater(this.data)).filter((line) => line.length > 0 && line.indexOf('[') === 0 && line.indexOf(']') === 9).map((text) => text.substring(10)).join(' ');
37
+ }
38
38
  }