@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 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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@makefully/dutifully",
3
- "version": "1.11.0",
3
+ "version": "1.12.0",
4
4
  "description": "Client tools for Cheerfully service.",
5
5
  "main": "./src/index.js",
6
6
  "bin": {
@@ -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 fs.readdir(path)).filter(filter.bind(null, fileType)),
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 fs.readdir(src)).filter(filter.bind(null, 'mp3')).filter((file) => !check(file.substring(0, file.length - 4)));
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 fs.readdir(output))
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 fs.readdir(src)).filter(filter.bind(null, 'mp3')).filter((file) => {
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];
@@ -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
 
@@ -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
  }
@@ -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 fs.readdir(path)).filter(filter.bind(null, fileType)),
24
+ files = (await readdirOrEmpty(path)).filter(filter.bind(null, fileType)),
25
25
  captions = {},
26
26
  fileMap = {};
27
27