@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,60 +1,62 @@
1
- const
2
- id3 = require('node-id3').Promise,
3
- File = require("./File");
4
-
5
- module.exports = class MP3 extends File {
6
- async load (path) {
7
- await super.load(path);
8
- this.data = await id3.read(path);
9
- return this.data;
10
- }
11
-
12
- addHash (hash, {hashKey = 'CaptionHash'} = {}) {
13
- const
14
- existing = (this.data.userDefinedText ?? []).filter(({description}) => description !== hashKey);
15
-
16
- this.data.userDefinedText = [...existing, {description: hashKey, value: hash}];
17
- }
18
-
19
- getHash ({hashKey = 'CaptionHash'} = {}) {
20
- const
21
- {userDefinedText} = this.data;
22
-
23
- if (userDefinedText) {
24
- for (let i = 0; i < userDefinedText.length; i++) {
25
- if (userDefinedText[i].description === hashKey) {
26
- return userDefinedText[i].value;
27
- }
28
- }
29
- }
30
-
31
- return null;
32
- }
33
-
34
- save () { // return a promise
35
- return id3.write(this.data, this.path);
36
- }
37
-
38
- toString () {
39
- const
40
- {synchronisedLyrics, unsynchronisedLyrics} = this.data;
41
-
42
- if (unsynchronisedLyrics?.text) {
43
- return unsynchronisedLyrics.text;
44
- }
45
-
46
- if (synchronisedLyrics) {
47
- const
48
- index = synchronisedLyrics.map(({shortText}) => shortText).indexOf('captions');
49
-
50
- if (index >= 0) {
51
- const
52
- {synchronisedText} = synchronisedLyrics[index];
53
-
54
- return synchronisedText.map(({text}) => text).join(' ');
55
- }
56
- }
57
-
58
- return '';
59
- }
1
+ const
2
+ id3 = require('node-id3').Promise,
3
+ File = require("./File"),
4
+ {ensureParentDir} = require('../helpers/dirs');
5
+
6
+ module.exports = class MP3 extends File {
7
+ async load (path) {
8
+ await super.load(path);
9
+ this.data = await id3.read(path);
10
+ return this.data;
11
+ }
12
+
13
+ addHash (hash, {hashKey = 'CaptionHash'} = {}) {
14
+ const
15
+ existing = (this.data.userDefinedText ?? []).filter(({description}) => description !== hashKey);
16
+
17
+ this.data.userDefinedText = [...existing, {description: hashKey, value: hash}];
18
+ }
19
+
20
+ getHash ({hashKey = 'CaptionHash'} = {}) {
21
+ const
22
+ {userDefinedText} = this.data;
23
+
24
+ if (userDefinedText) {
25
+ for (let i = 0; i < userDefinedText.length; i++) {
26
+ if (userDefinedText[i].description === hashKey) {
27
+ return userDefinedText[i].value;
28
+ }
29
+ }
30
+ }
31
+
32
+ return null;
33
+ }
34
+
35
+ async save () { // return a promise
36
+ await ensureParentDir(this.path);
37
+ return id3.write(this.data, this.path);
38
+ }
39
+
40
+ toString () {
41
+ const
42
+ {synchronisedLyrics, unsynchronisedLyrics} = this.data;
43
+
44
+ if (unsynchronisedLyrics?.text) {
45
+ return unsynchronisedLyrics.text;
46
+ }
47
+
48
+ if (synchronisedLyrics) {
49
+ const
50
+ index = synchronisedLyrics.map(({shortText}) => shortText).indexOf('captions');
51
+
52
+ if (index >= 0) {
53
+ const
54
+ {synchronisedText} = synchronisedLyrics[index];
55
+
56
+ return synchronisedText.map(({text}) => text).join(' ');
57
+ }
58
+ }
59
+
60
+ return '';
61
+ }
60
62
  }
@@ -1,59 +1,59 @@
1
- const
2
- fs = require('fs').promises,
3
- File = require("./File"),
4
- removeComments = (html) => html.replace(/<!--[\s\S]*?-->/g, ''),
5
- cleanHTML = (html) => html
6
- .replace(/<[^>]+>/g, '') // remove HTML tags
7
- .replace(/&nbsp;/gi, ' ') // decode common HTML entities
8
- .replace(/&amp;/gi, '&')
9
- .replace(/&lt;/gi, '<')
10
- .replace(/&gt;/gi, '>')
11
- .replace(/&quot;/gi, '"')
12
- .trim();
13
-
14
- module.exports = class SAMI extends File {
15
- async load (path) {
16
- await super.load(path);
17
- this.data = await fs.readFile(path, 'utf8');
18
- return this.data;
19
- }
20
-
21
- addHash (hash) {
22
- this.data = this.data.replace(/<META[\s]+Hash="[a-f0-9]+"[\s]*\/>\n?/i, '');
23
-
24
- const
25
- insertAfter = this.data.match(/<META[\s]+Generator="[^"]*"[\s]*\/>/i);
26
-
27
- if (insertAfter) {
28
- this.data = this.data.replace(insertAfter[0], `${insertAfter[0]}\n <META Hash="${hash}"/>`);
29
- } else {
30
- this.data = this.data.replace(/<HEAD>/i, `<HEAD>\n <META Hash="${hash}"/>`);
31
- }
32
- }
33
-
34
- getHash () {
35
- const
36
- match = this.data.match(/<META[\s]+Hash="([a-f0-9]+)"/i);
37
-
38
- return match ? match[1] : null;
39
- }
40
-
41
- toString () {
42
- const
43
- sami = removeComments(this.data),
44
- regex = /<P[^>]*>([\s\S]*?)<\/P>/gi,
45
- matches = [];
46
- let match;
47
-
48
- while ((match = regex.exec(sami)) !== null) {
49
- const
50
- innerText = cleanHTML(match[1]);
51
-
52
- if (innerText) {
53
- matches.push(innerText);
54
- }
55
- }
56
-
57
- return matches.join(' ');
58
- }
1
+ const
2
+ fs = require('fs').promises,
3
+ File = require("./File"),
4
+ removeComments = (html) => html.replace(/<!--[\s\S]*?-->/g, ''),
5
+ cleanHTML = (html) => html
6
+ .replace(/<[^>]+>/g, '') // remove HTML tags
7
+ .replace(/&nbsp;/gi, ' ') // decode common HTML entities
8
+ .replace(/&amp;/gi, '&')
9
+ .replace(/&lt;/gi, '<')
10
+ .replace(/&gt;/gi, '>')
11
+ .replace(/&quot;/gi, '"')
12
+ .trim();
13
+
14
+ module.exports = class SAMI extends File {
15
+ async load (path) {
16
+ await super.load(path);
17
+ this.data = await fs.readFile(path, 'utf8');
18
+ return this.data;
19
+ }
20
+
21
+ addHash (hash) {
22
+ this.data = this.data.replace(/<META[\s]+Hash="[a-f0-9]+"[\s]*\/>\n?/i, '');
23
+
24
+ const
25
+ insertAfter = this.data.match(/<META[\s]+Generator="[^"]*"[\s]*\/>/i);
26
+
27
+ if (insertAfter) {
28
+ this.data = this.data.replace(insertAfter[0], `${insertAfter[0]}\n <META Hash="${hash}"/>`);
29
+ } else {
30
+ this.data = this.data.replace(/<HEAD>/i, `<HEAD>\n <META Hash="${hash}"/>`);
31
+ }
32
+ }
33
+
34
+ getHash () {
35
+ const
36
+ match = this.data.match(/<META[\s]+Hash="([a-f0-9]+)"/i);
37
+
38
+ return match ? match[1] : null;
39
+ }
40
+
41
+ toString () {
42
+ const
43
+ sami = removeComments(this.data),
44
+ regex = /<P[^>]*>([\s\S]*?)<\/P>/gi,
45
+ matches = [];
46
+ let match;
47
+
48
+ while ((match = regex.exec(sami)) !== null) {
49
+ const
50
+ innerText = cleanHTML(match[1]);
51
+
52
+ if (innerText) {
53
+ matches.push(innerText);
54
+ }
55
+ }
56
+
57
+ return matches.join(' ');
58
+ }
59
59
  }
@@ -1,40 +1,40 @@
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 SRT 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(/<u[\s]+hash="[a-f0-9]+"[\s]*(?:\/>|><\/u>)\n?/im, '');
16
-
17
- const
18
- insertAfter = this.data.match(/<u[\s]+generator="[^"]*"[\s]*(?:\/>|><\/u>)/im);
19
-
20
- if (insertAfter) {
21
- this.data = this.data.replace(insertAfter[0], `${insertAfter[0]}${getSeparater(this.data)}<u hash="${hash}"></u>`);
22
- } else {
23
- const
24
- sep = getSeparater(this.data);
25
-
26
- this.data = `1${sep}00:00:00,000 --> 00:00:00,001${sep}<u hash="${hash}"></u>${sep}${sep}${this.data}`;
27
- }
28
- }
29
-
30
- getHash () {
31
- const
32
- match = this.data.match(/<u[\s]+hash="([a-f0-9]+)"[\s]*(?:\/>|><\/u>)/im);
33
-
34
- return match ? match[1] : null;
35
- }
36
-
37
- toString () {
38
- return this.data.split(getSeparater(this.data)).filter((line) => line.length > 0 && line.indexOf(' --> ') === -1 && parseInt(line) != line).join(' ');
39
- }
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 SRT 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(/<u[\s]+hash="[a-f0-9]+"[\s]*(?:\/>|><\/u>)\n?/im, '');
16
+
17
+ const
18
+ insertAfter = this.data.match(/<u[\s]+generator="[^"]*"[\s]*(?:\/>|><\/u>)/im);
19
+
20
+ if (insertAfter) {
21
+ this.data = this.data.replace(insertAfter[0], `${insertAfter[0]}${getSeparater(this.data)}<u hash="${hash}"></u>`);
22
+ } else {
23
+ const
24
+ sep = getSeparater(this.data);
25
+
26
+ this.data = `1${sep}00:00:00,000 --> 00:00:00,001${sep}<u hash="${hash}"></u>${sep}${sep}${this.data}`;
27
+ }
28
+ }
29
+
30
+ getHash () {
31
+ const
32
+ match = this.data.match(/<u[\s]+hash="([a-f0-9]+)"[\s]*(?:\/>|><\/u>)/im);
33
+
34
+ return match ? match[1] : null;
35
+ }
36
+
37
+ toString () {
38
+ return this.data.split(getSeparater(this.data)).filter((line) => line.length > 0 && line.indexOf(' --> ') === -1 && parseInt(line) != line).join(' ');
39
+ }
40
40
  }
@@ -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 VTT 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(/^NOTE[\s]+Hash:[\s]+[a-f0-9]+\n?/im, '');
16
-
17
- const
18
- sep = getSeparater(this.data),
19
- insertAfter = this.data.match(/^NOTE[\s]+Generator:[^\n]*/im);
20
-
21
- if (insertAfter) {
22
- this.data = this.data.replace(insertAfter[0], `${insertAfter[0]}${sep}NOTE Hash: ${hash}`);
23
- } else {
24
- this.data = this.data.replace('WEBVTT', `WEBVTT${sep}${sep}NOTE Hash: ${hash}`);
25
- }
26
- }
27
-
28
- getHash () {
29
- const
30
- match = this.data.match(/^NOTE[\s]+Hash:[\s]+([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(' --> ') === -1 && parseInt(line) != line).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 VTT 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(/^NOTE[\s]+Hash:[\s]+[a-f0-9]+\n?/im, '');
16
+
17
+ const
18
+ sep = getSeparater(this.data),
19
+ insertAfter = this.data.match(/^NOTE[\s]+Generator:[^\n]*/im);
20
+
21
+ if (insertAfter) {
22
+ this.data = this.data.replace(insertAfter[0], `${insertAfter[0]}${sep}NOTE Hash: ${hash}`);
23
+ } else {
24
+ this.data = this.data.replace('WEBVTT', `WEBVTT${sep}${sep}NOTE Hash: ${hash}`);
25
+ }
26
+ }
27
+
28
+ getHash () {
29
+ const
30
+ match = this.data.match(/^NOTE[\s]+Hash:[\s]+([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(' --> ') === -1 && parseInt(line) != line).join(' ');
37
+ }
38
38
  }
@@ -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,44 +1,44 @@
1
- const
2
- fs = require('fs').promises,
3
- getFileData = require("./getFileData"),
4
- filter = (fileType, file) => (file.indexOf('.') !== 0) && (file.slice(-(fileType.length + 1)) === `.${fileType}`);
5
-
6
- module.exports = async (path, fileType) => {
7
-
8
- // "captions.json" is a proprietary format that includes all captions.
9
- if (fileType.toLowerCase() === 'json') {
10
- const
11
- file = await getFileData(`${path}captions.json`, 'json'),
12
- {data: json} = file;
13
-
14
- return {
15
- captions: Object.keys(json).reduce((obj, key) => {
16
- obj[key] = json[key].map(({content}) => content).join(' ');
17
- return obj;
18
- }, {}),
19
- file,
20
- files: null
21
- };
22
- } else { // Otherwise we pull all the files and construct a key/value object.
23
- const
24
- files = (await fs.readdir(path)).filter(filter.bind(null, fileType)),
25
- captions = {},
26
- fileMap = {};
27
-
28
- for (let i = 0; i < files.length; i++) {
29
- const
30
- filename = files[i],
31
- file = await getFileData(`${path}${filename}`, fileType),
32
- key = filename.substring(0, filename.length - fileType.length - 1);
33
-
34
- captions[key] = file.toString();
35
- fileMap[key] = file;
36
- }
37
-
38
- return {
39
- captions,
40
- file: null,
41
- files: fileMap
42
- };
43
- }
1
+ const
2
+ getFileData = require("./getFileData"),
3
+ {readdirOrEmpty} = require("./dirs"),
4
+ filter = (fileType, file) => (file.indexOf('.') !== 0) && (file.slice(-(fileType.length + 1)) === `.${fileType}`);
5
+
6
+ module.exports = async (path, fileType) => {
7
+
8
+ // "captions.json" is a proprietary format that includes all captions.
9
+ if (fileType.toLowerCase() === 'json') {
10
+ const
11
+ file = await getFileData(`${path}captions.json`, 'json'),
12
+ {data: json = {}} = file;
13
+
14
+ return {
15
+ captions: Object.keys(json ?? {}).reduce((obj, key) => {
16
+ obj[key] = json[key].map(({content}) => content).join(' ');
17
+ return obj;
18
+ }, {}),
19
+ file,
20
+ files: null
21
+ };
22
+ } else { // Otherwise we pull all the files and construct a key/value object.
23
+ const
24
+ files = (await readdirOrEmpty(path)).filter(filter.bind(null, fileType)),
25
+ captions = {},
26
+ fileMap = {};
27
+
28
+ for (let i = 0; i < files.length; i++) {
29
+ const
30
+ filename = files[i],
31
+ file = await getFileData(`${path}${filename}`, fileType),
32
+ key = filename.substring(0, filename.length - fileType.length - 1);
33
+
34
+ captions[key] = file.toString();
35
+ fileMap[key] = file;
36
+ }
37
+
38
+ return {
39
+ captions,
40
+ file: null,
41
+ files: fileMap
42
+ };
43
+ }
44
44
  };
@@ -1,33 +1,33 @@
1
- const
2
- JSONFile = require("../formats/JSONFile"),
3
- LRC = require("../formats/LRC"),
4
- MP3 = require("../formats/MP3"),
5
- SAMI = require("../formats/SAMI"),
6
- SRT = require("../formats/SRT"),
7
- VTT = require("../formats/VTT"),
8
- formats = {
9
- json: JSONFile,
10
- lrc: LRC,
11
- mp3: MP3,
12
- sami: SAMI,
13
- smi: SAMI,
14
- srt: SRT,
15
- vtt: VTT
16
- };
17
-
18
- module.exports = async (path, fileType) => {
19
- const
20
- lcFormat = fileType.toLowerCase(),
21
- File = formats[lcFormat] ?? formats.JSONFile;
22
-
23
- // Find file type and throw if that doesn't work.
24
- if (File) {
25
- const
26
- file = new File({fileType: lcFormat});
27
-
28
- await file.load(path);
29
- return file;
30
- } else {
31
- throw `"${fileType}" not supported.`;
32
- }
1
+ const
2
+ JSONFile = require("../formats/JSONFile"),
3
+ LRC = require("../formats/LRC"),
4
+ MP3 = require("../formats/MP3"),
5
+ SAMI = require("../formats/SAMI"),
6
+ SRT = require("../formats/SRT"),
7
+ VTT = require("../formats/VTT"),
8
+ formats = {
9
+ json: JSONFile,
10
+ lrc: LRC,
11
+ mp3: MP3,
12
+ sami: SAMI,
13
+ smi: SAMI,
14
+ srt: SRT,
15
+ vtt: VTT
16
+ };
17
+
18
+ module.exports = async (path, fileType) => {
19
+ const
20
+ lcFormat = fileType.toLowerCase(),
21
+ File = formats[lcFormat] ?? formats.JSONFile;
22
+
23
+ // Find file type and throw if that doesn't work.
24
+ if (File) {
25
+ const
26
+ file = new File({fileType: lcFormat});
27
+
28
+ await file.load(path);
29
+ return file;
30
+ } else {
31
+ throw `"${fileType}" not supported.`;
32
+ }
33
33
  };
@@ -1,33 +1,33 @@
1
- const
2
- fs = require('fs'),
3
- getJSON = (path) => new Promise ((resolve, reject) => {
4
- fs.readFile(path, (err, data) => {
5
- if (err) {
6
- reject(err);
7
- } else {
8
- let json = null;
9
-
10
- try {
11
- json = JSON.parse(data);
12
- } catch (e) {
13
- reject(e);
14
- }
15
-
16
- if (json) {
17
- resolve(json);
18
- }
19
- }
20
- });
21
- });
22
-
23
- module.exports = async (path) => {
24
- let json = null;
25
-
26
- try {
27
- json = await getJSON(path);
28
- } catch (e) {
29
- console.warn(e);
30
- }
31
-
32
- return json;
1
+ const
2
+ fs = require('fs'),
3
+ getJSON = (path) => new Promise ((resolve, reject) => {
4
+ fs.readFile(path, (err, data) => {
5
+ if (err) {
6
+ reject(err);
7
+ } else {
8
+ let json = null;
9
+
10
+ try {
11
+ json = JSON.parse(data);
12
+ } catch (e) {
13
+ reject(e);
14
+ }
15
+
16
+ if (json) {
17
+ resolve(json);
18
+ }
19
+ }
20
+ });
21
+ });
22
+
23
+ module.exports = async (path) => {
24
+ let json = null;
25
+
26
+ try {
27
+ json = await getJSON(path);
28
+ } catch (e) {
29
+ console.warn(e);
30
+ }
31
+
32
+ return json;
33
33
  };