podcast-dl 11.1.1 → 11.2.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/LICENSE +21 -21
- package/README.md +121 -120
- package/bin/archive.js +39 -39
- package/bin/async.js +370 -370
- package/bin/bin.js +292 -289
- package/bin/commander.js +198 -193
- package/bin/exec.js +30 -30
- package/bin/ffmpeg.js +105 -105
- package/bin/items.js +247 -237
- package/bin/logger.js +84 -84
- package/bin/meta.js +66 -66
- package/bin/naming.js +112 -112
- package/bin/util.js +299 -299
- package/bin/validate.js +39 -39
- package/package.json +62 -62
package/bin/meta.js
CHANGED
|
@@ -1,66 +1,66 @@
|
|
|
1
|
-
import fs from "fs";
|
|
2
|
-
import { getIsInArchive, writeToArchive } from "./archive.js";
|
|
3
|
-
import { logMessage } from "./logger.js";
|
|
4
|
-
import { getPublicObject } from "./util.js";
|
|
5
|
-
|
|
6
|
-
export const writeFeedMeta = ({ outputPath, feed, key, archive, override }) => {
|
|
7
|
-
if (key && archive && getIsInArchive({ key, archive })) {
|
|
8
|
-
logMessage("Feed metadata exists in archive. Skipping...");
|
|
9
|
-
return;
|
|
10
|
-
}
|
|
11
|
-
const output = getPublicObject(feed, ["items"]);
|
|
12
|
-
|
|
13
|
-
try {
|
|
14
|
-
if (override || !fs.existsSync(outputPath)) {
|
|
15
|
-
fs.writeFileSync(outputPath, JSON.stringify(output, null, 4));
|
|
16
|
-
} else {
|
|
17
|
-
logMessage("Feed metadata exists locally. Skipping...");
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
if (key && archive && !getIsInArchive({ key, archive })) {
|
|
21
|
-
try {
|
|
22
|
-
writeToArchive({ key, archive });
|
|
23
|
-
} catch (error) {
|
|
24
|
-
throw new Error(`Error writing to archive: ${error.toString()}`);
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
} catch (error) {
|
|
28
|
-
throw new Error(
|
|
29
|
-
`Unable to save metadata file for feed: ${error.toString()}`
|
|
30
|
-
);
|
|
31
|
-
}
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
export const writeItemMeta = ({
|
|
35
|
-
marker,
|
|
36
|
-
outputPath,
|
|
37
|
-
item,
|
|
38
|
-
key,
|
|
39
|
-
archive,
|
|
40
|
-
override,
|
|
41
|
-
}) => {
|
|
42
|
-
if (key && archive && getIsInArchive({ key, archive })) {
|
|
43
|
-
logMessage(`${marker} | Episode metadata exists in archive. Skipping...`);
|
|
44
|
-
return;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
const output = getPublicObject(item);
|
|
48
|
-
|
|
49
|
-
try {
|
|
50
|
-
if (override || !fs.existsSync(outputPath)) {
|
|
51
|
-
fs.writeFileSync(outputPath, JSON.stringify(output, null, 4));
|
|
52
|
-
} else {
|
|
53
|
-
logMessage(`${marker} | Episode metadata exists locally. Skipping...`);
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
if (key && archive && !getIsInArchive({ key, archive })) {
|
|
57
|
-
try {
|
|
58
|
-
writeToArchive({ key, archive });
|
|
59
|
-
} catch (error) {
|
|
60
|
-
throw new Error("Error writing to archive", error);
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
} catch (error) {
|
|
64
|
-
throw new Error("Unable to save meta file for episode", error);
|
|
65
|
-
}
|
|
66
|
-
};
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
import { getIsInArchive, writeToArchive } from "./archive.js";
|
|
3
|
+
import { logMessage } from "./logger.js";
|
|
4
|
+
import { getPublicObject } from "./util.js";
|
|
5
|
+
|
|
6
|
+
export const writeFeedMeta = ({ outputPath, feed, key, archive, override }) => {
|
|
7
|
+
if (key && archive && getIsInArchive({ key, archive })) {
|
|
8
|
+
logMessage("Feed metadata exists in archive. Skipping...");
|
|
9
|
+
return;
|
|
10
|
+
}
|
|
11
|
+
const output = getPublicObject(feed, ["items"]);
|
|
12
|
+
|
|
13
|
+
try {
|
|
14
|
+
if (override || !fs.existsSync(outputPath)) {
|
|
15
|
+
fs.writeFileSync(outputPath, JSON.stringify(output, null, 4));
|
|
16
|
+
} else {
|
|
17
|
+
logMessage("Feed metadata exists locally. Skipping...");
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
if (key && archive && !getIsInArchive({ key, archive })) {
|
|
21
|
+
try {
|
|
22
|
+
writeToArchive({ key, archive });
|
|
23
|
+
} catch (error) {
|
|
24
|
+
throw new Error(`Error writing to archive: ${error.toString()}`);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
} catch (error) {
|
|
28
|
+
throw new Error(
|
|
29
|
+
`Unable to save metadata file for feed: ${error.toString()}`
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
export const writeItemMeta = ({
|
|
35
|
+
marker,
|
|
36
|
+
outputPath,
|
|
37
|
+
item,
|
|
38
|
+
key,
|
|
39
|
+
archive,
|
|
40
|
+
override,
|
|
41
|
+
}) => {
|
|
42
|
+
if (key && archive && getIsInArchive({ key, archive })) {
|
|
43
|
+
logMessage(`${marker} | Episode metadata exists in archive. Skipping...`);
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const output = getPublicObject(item);
|
|
48
|
+
|
|
49
|
+
try {
|
|
50
|
+
if (override || !fs.existsSync(outputPath)) {
|
|
51
|
+
fs.writeFileSync(outputPath, JSON.stringify(output, null, 4));
|
|
52
|
+
} else {
|
|
53
|
+
logMessage(`${marker} | Episode metadata exists locally. Skipping...`);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (key && archive && !getIsInArchive({ key, archive })) {
|
|
57
|
+
try {
|
|
58
|
+
writeToArchive({ key, archive });
|
|
59
|
+
} catch (error) {
|
|
60
|
+
throw new Error("Error writing to archive", error);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
} catch (error) {
|
|
64
|
+
throw new Error("Unable to save meta file for episode", error);
|
|
65
|
+
}
|
|
66
|
+
};
|
package/bin/naming.js
CHANGED
|
@@ -1,112 +1,112 @@
|
|
|
1
|
-
import dayjs from "dayjs";
|
|
2
|
-
import filenamify from "filenamify";
|
|
3
|
-
import path from "path";
|
|
4
|
-
|
|
5
|
-
const INVALID_CHAR_REPLACE = "_";
|
|
6
|
-
const MAX_LENGTH_FILENAME = process.env.MAX_LENGTH_FILENAME
|
|
7
|
-
? parseInt(process.env.MAX_LENGTH_FILENAME)
|
|
8
|
-
: 255;
|
|
9
|
-
|
|
10
|
-
export const getSafeName = (name, maxLength = MAX_LENGTH_FILENAME) => {
|
|
11
|
-
return filenamify(name, {
|
|
12
|
-
replacement: INVALID_CHAR_REPLACE,
|
|
13
|
-
maxLength,
|
|
14
|
-
});
|
|
15
|
-
};
|
|
16
|
-
|
|
17
|
-
export const getSimpleFilename = (name, ext = "") => {
|
|
18
|
-
return `${getSafeName(name, MAX_LENGTH_FILENAME - (ext?.length ?? 0))}${ext}`;
|
|
19
|
-
};
|
|
20
|
-
|
|
21
|
-
export const getItemFilename = ({
|
|
22
|
-
item,
|
|
23
|
-
ext,
|
|
24
|
-
url,
|
|
25
|
-
feed,
|
|
26
|
-
template,
|
|
27
|
-
width,
|
|
28
|
-
customTemplateOptions = [],
|
|
29
|
-
offset = 0,
|
|
30
|
-
}) => {
|
|
31
|
-
const episodeNum = feed.items.length - item._originalIndex + offset;
|
|
32
|
-
const title = item.title || "";
|
|
33
|
-
|
|
34
|
-
const releaseYear = item.pubDate
|
|
35
|
-
? dayjs(new Date(item.pubDate)).format("YYYY")
|
|
36
|
-
: null;
|
|
37
|
-
|
|
38
|
-
const releaseMonth = item.pubDate
|
|
39
|
-
? dayjs(new Date(item.pubDate)).format("MM")
|
|
40
|
-
: null;
|
|
41
|
-
|
|
42
|
-
const releaseDay = item.pubDate
|
|
43
|
-
? dayjs(new Date(item.pubDate)).format("DD")
|
|
44
|
-
: null;
|
|
45
|
-
|
|
46
|
-
const releaseDate = item.pubDate
|
|
47
|
-
? dayjs(new Date(item.pubDate)).format("YYYYMMDD")
|
|
48
|
-
: null;
|
|
49
|
-
|
|
50
|
-
const customReplacementTuples = customTemplateOptions.map((option, i) => {
|
|
51
|
-
const matchRegex = new RegExp(option);
|
|
52
|
-
const match = title.match(matchRegex);
|
|
53
|
-
|
|
54
|
-
return match && match[0] ? [`custom_${i}`, match[0]] : [`custom_${i}`, ""];
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
const templateReplacementsTuples = [
|
|
58
|
-
["title", title],
|
|
59
|
-
["release_date", releaseDate || ""],
|
|
60
|
-
["release_year", releaseYear || ""],
|
|
61
|
-
["release_month", releaseMonth || ""],
|
|
62
|
-
["release_day", releaseDay || ""],
|
|
63
|
-
["episode_num", `${episodeNum}`.padStart(width, "0")],
|
|
64
|
-
["url", url],
|
|
65
|
-
["podcast_title", feed.title || ""],
|
|
66
|
-
["podcast_link", feed.link || ""],
|
|
67
|
-
["duration", item.itunes?.duration || ""],
|
|
68
|
-
["guid", item.guid],
|
|
69
|
-
...customReplacementTuples,
|
|
70
|
-
];
|
|
71
|
-
|
|
72
|
-
const templateSegments = template.trim().split(path.sep);
|
|
73
|
-
const nameSegments = templateSegments.map((segment) => {
|
|
74
|
-
let name = segment;
|
|
75
|
-
templateReplacementsTuples.forEach((replacementTuple) => {
|
|
76
|
-
const [matcher, replacement] = replacementTuple;
|
|
77
|
-
const replaceRegex = new RegExp(`{{${matcher}}}`, "g");
|
|
78
|
-
|
|
79
|
-
name = replacement
|
|
80
|
-
? name.replace(replaceRegex, replacement)
|
|
81
|
-
: name.replace(replaceRegex, "");
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
return getSimpleFilename(name);
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
nameSegments[nameSegments.length - 1] = getSimpleFilename(
|
|
88
|
-
nameSegments[nameSegments.length - 1],
|
|
89
|
-
ext
|
|
90
|
-
);
|
|
91
|
-
|
|
92
|
-
return nameSegments.join(path.sep);
|
|
93
|
-
};
|
|
94
|
-
|
|
95
|
-
export const getFolderName = ({ feed, template }) => {
|
|
96
|
-
const templateReplacementsTuples = [
|
|
97
|
-
["podcast_title", feed.title || ""],
|
|
98
|
-
["podcast_link", feed.link || ""],
|
|
99
|
-
];
|
|
100
|
-
|
|
101
|
-
let name = template;
|
|
102
|
-
templateReplacementsTuples.forEach((replacementTuple) => {
|
|
103
|
-
const [matcher, replacement] = replacementTuple;
|
|
104
|
-
const replaceRegex = new RegExp(`{{${matcher}}}`, "g");
|
|
105
|
-
|
|
106
|
-
name = replacement
|
|
107
|
-
? name.replace(replaceRegex, getSafeName(replacement))
|
|
108
|
-
: name.replace(replaceRegex, "");
|
|
109
|
-
});
|
|
110
|
-
|
|
111
|
-
return name;
|
|
112
|
-
};
|
|
1
|
+
import dayjs from "dayjs";
|
|
2
|
+
import filenamify from "filenamify";
|
|
3
|
+
import path from "path";
|
|
4
|
+
|
|
5
|
+
const INVALID_CHAR_REPLACE = "_";
|
|
6
|
+
const MAX_LENGTH_FILENAME = process.env.MAX_LENGTH_FILENAME
|
|
7
|
+
? parseInt(process.env.MAX_LENGTH_FILENAME)
|
|
8
|
+
: 255;
|
|
9
|
+
|
|
10
|
+
export const getSafeName = (name, maxLength = MAX_LENGTH_FILENAME) => {
|
|
11
|
+
return filenamify(name, {
|
|
12
|
+
replacement: INVALID_CHAR_REPLACE,
|
|
13
|
+
maxLength,
|
|
14
|
+
});
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export const getSimpleFilename = (name, ext = "") => {
|
|
18
|
+
return `${getSafeName(name, MAX_LENGTH_FILENAME - (ext?.length ?? 0))}${ext}`;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export const getItemFilename = ({
|
|
22
|
+
item,
|
|
23
|
+
ext,
|
|
24
|
+
url,
|
|
25
|
+
feed,
|
|
26
|
+
template,
|
|
27
|
+
width,
|
|
28
|
+
customTemplateOptions = [],
|
|
29
|
+
offset = 0,
|
|
30
|
+
}) => {
|
|
31
|
+
const episodeNum = feed.items.length - item._originalIndex + offset;
|
|
32
|
+
const title = item.title || "";
|
|
33
|
+
|
|
34
|
+
const releaseYear = item.pubDate
|
|
35
|
+
? dayjs(new Date(item.pubDate)).format("YYYY")
|
|
36
|
+
: null;
|
|
37
|
+
|
|
38
|
+
const releaseMonth = item.pubDate
|
|
39
|
+
? dayjs(new Date(item.pubDate)).format("MM")
|
|
40
|
+
: null;
|
|
41
|
+
|
|
42
|
+
const releaseDay = item.pubDate
|
|
43
|
+
? dayjs(new Date(item.pubDate)).format("DD")
|
|
44
|
+
: null;
|
|
45
|
+
|
|
46
|
+
const releaseDate = item.pubDate
|
|
47
|
+
? dayjs(new Date(item.pubDate)).format("YYYYMMDD")
|
|
48
|
+
: null;
|
|
49
|
+
|
|
50
|
+
const customReplacementTuples = customTemplateOptions.map((option, i) => {
|
|
51
|
+
const matchRegex = new RegExp(option);
|
|
52
|
+
const match = title.match(matchRegex);
|
|
53
|
+
|
|
54
|
+
return match && match[0] ? [`custom_${i}`, match[0]] : [`custom_${i}`, ""];
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
const templateReplacementsTuples = [
|
|
58
|
+
["title", title],
|
|
59
|
+
["release_date", releaseDate || ""],
|
|
60
|
+
["release_year", releaseYear || ""],
|
|
61
|
+
["release_month", releaseMonth || ""],
|
|
62
|
+
["release_day", releaseDay || ""],
|
|
63
|
+
["episode_num", `${episodeNum}`.padStart(width, "0")],
|
|
64
|
+
["url", url],
|
|
65
|
+
["podcast_title", feed.title || ""],
|
|
66
|
+
["podcast_link", feed.link || ""],
|
|
67
|
+
["duration", item.itunes?.duration || ""],
|
|
68
|
+
["guid", item.guid],
|
|
69
|
+
...customReplacementTuples,
|
|
70
|
+
];
|
|
71
|
+
|
|
72
|
+
const templateSegments = template.trim().split(path.sep);
|
|
73
|
+
const nameSegments = templateSegments.map((segment) => {
|
|
74
|
+
let name = segment;
|
|
75
|
+
templateReplacementsTuples.forEach((replacementTuple) => {
|
|
76
|
+
const [matcher, replacement] = replacementTuple;
|
|
77
|
+
const replaceRegex = new RegExp(`{{${matcher}}}`, "g");
|
|
78
|
+
|
|
79
|
+
name = replacement
|
|
80
|
+
? name.replace(replaceRegex, replacement)
|
|
81
|
+
: name.replace(replaceRegex, "");
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
return getSimpleFilename(name);
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
nameSegments[nameSegments.length - 1] = getSimpleFilename(
|
|
88
|
+
nameSegments[nameSegments.length - 1],
|
|
89
|
+
ext
|
|
90
|
+
);
|
|
91
|
+
|
|
92
|
+
return nameSegments.join(path.sep);
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
export const getFolderName = ({ feed, template }) => {
|
|
96
|
+
const templateReplacementsTuples = [
|
|
97
|
+
["podcast_title", feed.title || ""],
|
|
98
|
+
["podcast_link", feed.link || ""],
|
|
99
|
+
];
|
|
100
|
+
|
|
101
|
+
let name = template;
|
|
102
|
+
templateReplacementsTuples.forEach((replacementTuple) => {
|
|
103
|
+
const [matcher, replacement] = replacementTuple;
|
|
104
|
+
const replaceRegex = new RegExp(`{{${matcher}}}`, "g");
|
|
105
|
+
|
|
106
|
+
name = replacement
|
|
107
|
+
? name.replace(replaceRegex, getSafeName(replacement))
|
|
108
|
+
: name.replace(replaceRegex, "");
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
return name;
|
|
112
|
+
};
|