podcast-dl 11.0.1 → 11.1.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/README.md +42 -36
- package/bin/archive.js +39 -0
- package/bin/async.js +45 -0
- package/bin/bin.js +36 -0
- package/bin/commander.js +6 -2
- package/bin/items.js +39 -5
- package/bin/meta.js +35 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -24,41 +24,47 @@ Either `--url` or `--file` must be provided.
|
|
|
24
24
|
|
|
25
25
|
Type values surrounded in square brackets (`[]`) can be used as used as boolean options (no argument required).
|
|
26
26
|
|
|
27
|
-
| Option | Type | Required | Description
|
|
28
|
-
| --------------------------------- | ------------------- | -------- |
|
|
29
|
-
| --url | String | true\* | URL to podcast RSS feed.
|
|
30
|
-
| --file | String | true\* | Path to local RSS file.
|
|
31
|
-
| --out-dir | String | false | Specify output directory for episodes and metadata. Defaults to "./{{podcast_title}}". See "Template Options" for more details.
|
|
32
|
-
| --threads | Number | false | Determines the number of downloads that will happen concurrently. Default is 1.
|
|
33
|
-
| --attempts | Number | false | Sets the number of download attempts per individual file. Default is 3.
|
|
34
|
-
| --
|
|
35
|
-
| --episode-
|
|
36
|
-
| --
|
|
37
|
-
| --include-
|
|
38
|
-
| --include-episode-
|
|
39
|
-
| --include-episode-
|
|
40
|
-
| --
|
|
41
|
-
| --
|
|
42
|
-
| --
|
|
43
|
-
| --
|
|
44
|
-
| --
|
|
45
|
-
| --episode-regex
|
|
46
|
-
| --episode-
|
|
47
|
-
| --episode-
|
|
48
|
-
| --episode-
|
|
49
|
-
| --episode-
|
|
50
|
-
| --
|
|
51
|
-
| --
|
|
52
|
-
| --
|
|
53
|
-
| --
|
|
54
|
-
| --
|
|
55
|
-
| --
|
|
56
|
-
| --
|
|
57
|
-
| --
|
|
58
|
-
| --
|
|
59
|
-
| --
|
|
60
|
-
| --
|
|
61
|
-
| --
|
|
27
|
+
| Option | Type | Required | Description |
|
|
28
|
+
| --------------------------------- | ------------------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
29
|
+
| --url | String | true\* | URL to podcast RSS feed. |
|
|
30
|
+
| --file | String | true\* | Path to local RSS file. |
|
|
31
|
+
| --out-dir | String | false | Specify output directory for episodes and metadata. Defaults to "./{{podcast_title}}". See "Template Options" for more details. |
|
|
32
|
+
| --threads | Number | false | Determines the number of downloads that will happen concurrently. Default is 1. |
|
|
33
|
+
| --attempts | Number | false | Sets the number of download attempts per individual file. Default is 3. |
|
|
34
|
+
| --archive | [String] | false | Download or write out items not listed in archive file. Generates archive file at path if not found. Defaults to "./{{podcast_title}}/archive.json" when used as a boolean option. See "Template Options" for more details. |
|
|
35
|
+
| --episode-template | String | false | Template for generating episode related filenames. See "Template Options" for details. |
|
|
36
|
+
| --episode-custom-template-options | <String...> | false | Provide custom options for the episode template. See "Template Options" for details. |
|
|
37
|
+
| --include-meta | | false | Write out podcast metadata to JSON. |
|
|
38
|
+
| --include-episode-meta | | false | Write out individual episode metadata **to** JSON. |
|
|
39
|
+
| --include-episode-images | | false | Download found episode images. |
|
|
40
|
+
| --include-episode-transcripts | | false | Download found episode transcripts. |
|
|
41
|
+
| --offset | Number | false | Offset starting download position. Default is 0. |
|
|
42
|
+
| --limit | Number | false | Max number of episodes to download. Downloads all by default. |
|
|
43
|
+
| --after | String | false | Only download episodes after this date (i.e. MM/DD/YYY, inclusive). |
|
|
44
|
+
| --before | String | false | Only download episodes before this date (i.e. MM/DD/YYY, inclusive) |
|
|
45
|
+
| --episode-regex | String | false | Match episode title against provided regex before starting download. |
|
|
46
|
+
| --episode-regex-exclude | String | false | Matched episode titles against provided regex will be excluded. |
|
|
47
|
+
| --episode-digits | Number | false | Minimum number of digits to use for episode numbering (e.g. 3 would generate "001" instead of "1"). Default is 0. |
|
|
48
|
+
| --episode-num-offset | Number | false | Offset the acquired episode number. Default is 0. |
|
|
49
|
+
| --episode-source-order | String | false | Attempted order to extract episode audio URL from RSS feed. Default is "enclosure,link". |
|
|
50
|
+
| --episode-transcript-types | String | false | List of allowed transcript types in preferred order. Default is "application/json,application/x-subrip,application/srr,application/srt,text/vtt,text/html,text/plain". |
|
|
51
|
+
| --add-mp3-metadata | | false | Attempts to add a base level of episode metadata to each episode. Recommended only in cases where the original metadata is of poor quality. (**ffmpeg required**) |
|
|
52
|
+
| --adjust-bitrate | String (e.g. "48k") | false | Attempts to adjust bitrate of episodes. (**ffmpeg required**) |
|
|
53
|
+
| --mono | | false | Attempts to force episodes into mono. (**ffmpeg required**) |
|
|
54
|
+
| --override | | false | Override local files on collision. |
|
|
55
|
+
| --always-postprocess | | false | Always run additional tasks on the file regardless if the file already exists. This includes --add-mp3-metadata, --adjust-bitrate, --mono, and --exec. |
|
|
56
|
+
| --reverse | | false | Reverse download direction and start at last RSS item. |
|
|
57
|
+
| --info | | false | Print retrieved podcast info instead of downloading. |
|
|
58
|
+
| --list | [String] | false | Print episode list instead of downloading. Defaults to "table" when used as a boolean option. "json" is also supported. |
|
|
59
|
+
| --exec | String | false | Execute a command after each episode is downloaded. See "Template Options" for more details. |
|
|
60
|
+
| --parser-config | String | false | Path to JSON file that will be parsed and used to override the default config passed to [rss-parser](https://github.com/rbren/rss-parser#xml-options). |
|
|
61
|
+
| --proxy | | false | Enable proxy support. Specify environment variables listed by [global-agent](https://github.com/gajus/global-agent#environment-variables). |
|
|
62
|
+
| --help | | false | Output usage information. |
|
|
63
|
+
|
|
64
|
+
## Archive
|
|
65
|
+
|
|
66
|
+
- If passed the `--archive [path]` option, `podcast-dl` will generate/use a JSON archive at the provided path.
|
|
67
|
+
- Before downloading an episode or writing out metadata, it'll check if the item was saved previously and abort the save if found.
|
|
62
68
|
|
|
63
69
|
## Template Options
|
|
64
70
|
|
|
@@ -68,7 +74,7 @@ Options that support templates allow users to specify a template for the generat
|
|
|
68
74
|
|
|
69
75
|
`--episode-template "{{release_date}}-{{title}}"`
|
|
70
76
|
|
|
71
|
-
### `--out-dir`
|
|
77
|
+
### `--out-dir` & `--archive`
|
|
72
78
|
|
|
73
79
|
- `podcast_title`: Title of the podcast feed.
|
|
74
80
|
- `podcast_link`: `link` value provided for the podcast feed. Typically the homepage URL.
|
package/bin/archive.js
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import dayjs from "dayjs";
|
|
2
|
+
import fs from "fs";
|
|
3
|
+
import path from "path";
|
|
4
|
+
import { getJsonFile } from "./util.js";
|
|
5
|
+
|
|
6
|
+
export const getArchiveKey = ({ prefix, name }) => {
|
|
7
|
+
return `${prefix}-${name}`;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export const getArchive = (archive) => {
|
|
11
|
+
const archiveContent = getJsonFile(archive);
|
|
12
|
+
return archiveContent === null ? [] : archiveContent;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export const writeToArchive = ({ key, archive }) => {
|
|
16
|
+
const archivePath = path.resolve(process.cwd(), archive);
|
|
17
|
+
const archiveResult = getArchive(archive);
|
|
18
|
+
|
|
19
|
+
if (!archiveResult.includes(key)) {
|
|
20
|
+
archiveResult.push(key);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
fs.writeFileSync(archivePath, JSON.stringify(archiveResult, null, 4));
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export const getIsInArchive = ({ key, archive }) => {
|
|
27
|
+
const archiveResult = getArchive(archive);
|
|
28
|
+
return archiveResult.includes(key);
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export const getArchiveFilename = ({ pubDate, name, ext }) => {
|
|
32
|
+
const formattedPubDate = pubDate
|
|
33
|
+
? dayjs(new Date(pubDate)).format("YYYYMMDD")
|
|
34
|
+
: null;
|
|
35
|
+
|
|
36
|
+
const baseName = formattedPubDate ? `${formattedPubDate}-${name}` : name;
|
|
37
|
+
|
|
38
|
+
return `${baseName}${ext}`;
|
|
39
|
+
};
|
package/bin/async.js
CHANGED
|
@@ -5,6 +5,12 @@ import _path from "path";
|
|
|
5
5
|
import stream from "stream";
|
|
6
6
|
import { throttle } from "throttle-debounce";
|
|
7
7
|
import { promisify } from "util";
|
|
8
|
+
import {
|
|
9
|
+
getArchiveFilename,
|
|
10
|
+
getArchiveKey,
|
|
11
|
+
getIsInArchive,
|
|
12
|
+
writeToArchive,
|
|
13
|
+
} from "./archive.js";
|
|
8
14
|
import { runExec } from "./exec.js";
|
|
9
15
|
import { runFfmpeg } from "./ffmpeg.js";
|
|
10
16
|
import {
|
|
@@ -32,6 +38,8 @@ export const download = async (options) => {
|
|
|
32
38
|
marker,
|
|
33
39
|
url,
|
|
34
40
|
outputPath,
|
|
41
|
+
key,
|
|
42
|
+
archive,
|
|
35
43
|
override,
|
|
36
44
|
alwaysPostprocess,
|
|
37
45
|
onAfterDownload,
|
|
@@ -50,6 +58,11 @@ export const download = async (options) => {
|
|
|
50
58
|
return;
|
|
51
59
|
}
|
|
52
60
|
|
|
61
|
+
if (key && archive && getIsInArchive({ key, archive })) {
|
|
62
|
+
logMessage("Download exists in archive. Skipping...");
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
|
|
53
66
|
let headResponse = null;
|
|
54
67
|
try {
|
|
55
68
|
headResponse = await got(url, {
|
|
@@ -140,10 +153,20 @@ export const download = async (options) => {
|
|
|
140
153
|
if (onAfterDownload) {
|
|
141
154
|
await onAfterDownload();
|
|
142
155
|
}
|
|
156
|
+
|
|
157
|
+
if (key && archive) {
|
|
158
|
+
try {
|
|
159
|
+
writeToArchive({ key, archive });
|
|
160
|
+
} catch (error) {
|
|
161
|
+
throw new Error(`Error writing to archive: ${error.toString()}`);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
143
164
|
};
|
|
144
165
|
|
|
145
166
|
export const downloadItemsAsync = async ({
|
|
146
167
|
addMp3MetadataFlag,
|
|
168
|
+
archive,
|
|
169
|
+
archivePrefix,
|
|
147
170
|
attempts,
|
|
148
171
|
basePath,
|
|
149
172
|
bitrate,
|
|
@@ -195,9 +218,18 @@ export const downloadItemsAsync = async ({
|
|
|
195
218
|
|
|
196
219
|
try {
|
|
197
220
|
await download({
|
|
221
|
+
archive,
|
|
198
222
|
override,
|
|
199
223
|
alwaysPostprocess,
|
|
200
224
|
marker,
|
|
225
|
+
key: getArchiveKey({
|
|
226
|
+
prefix: archivePrefix,
|
|
227
|
+
name: getArchiveFilename({
|
|
228
|
+
name: item.title,
|
|
229
|
+
pubDate: item.pubDate,
|
|
230
|
+
ext: audioFileExt,
|
|
231
|
+
}),
|
|
232
|
+
}),
|
|
201
233
|
maxAttempts: attempts,
|
|
202
234
|
outputPath: outputPodcastPath,
|
|
203
235
|
url: episodeAudioUrl,
|
|
@@ -205,7 +237,9 @@ export const downloadItemsAsync = async ({
|
|
|
205
237
|
if (item._episodeImage) {
|
|
206
238
|
try {
|
|
207
239
|
await download({
|
|
240
|
+
archive,
|
|
208
241
|
override,
|
|
242
|
+
key: item._episodeImage.key,
|
|
209
243
|
marker: item._episodeImage.url,
|
|
210
244
|
maxAttempts: attempts,
|
|
211
245
|
outputPath: item._episodeImage.outputPath,
|
|
@@ -224,7 +258,9 @@ export const downloadItemsAsync = async ({
|
|
|
224
258
|
if (item._episodeTranscript) {
|
|
225
259
|
try {
|
|
226
260
|
await download({
|
|
261
|
+
archive,
|
|
227
262
|
override,
|
|
263
|
+
key: item._episodeTranscript.key,
|
|
228
264
|
marker: item._episodeTranscript.url,
|
|
229
265
|
maxAttempts: attempts,
|
|
230
266
|
outputPath: item._episodeTranscript.outputPath,
|
|
@@ -296,8 +332,17 @@ export const downloadItemsAsync = async ({
|
|
|
296
332
|
logMessage("Saving episode metadata...");
|
|
297
333
|
writeItemMeta({
|
|
298
334
|
marker,
|
|
335
|
+
archive,
|
|
299
336
|
override,
|
|
300
337
|
item,
|
|
338
|
+
key: getArchiveKey({
|
|
339
|
+
prefix: archivePrefix,
|
|
340
|
+
name: getArchiveFilename({
|
|
341
|
+
pubDate: item.pubDate,
|
|
342
|
+
name: item.title,
|
|
343
|
+
ext: episodeMetaExt,
|
|
344
|
+
}),
|
|
345
|
+
}),
|
|
301
346
|
outputPath: outputEpisodeMetaPath,
|
|
302
347
|
});
|
|
303
348
|
} catch (error) {
|
package/bin/bin.js
CHANGED
|
@@ -5,6 +5,7 @@ import fs from "fs";
|
|
|
5
5
|
import { bootstrap as bootstrapProxy } from "global-agent";
|
|
6
6
|
import _path from "path";
|
|
7
7
|
import pluralize from "pluralize";
|
|
8
|
+
import { getArchiveKey } from "./archive.js";
|
|
8
9
|
import { download, downloadItemsAsync } from "./async.js";
|
|
9
10
|
import { setupCommander } from "./commander.js";
|
|
10
11
|
import { getItemsToDownload, logItemsList } from "./items.js";
|
|
@@ -62,6 +63,8 @@ const {
|
|
|
62
63
|
adjustBitrate: bitrate,
|
|
63
64
|
} = opts;
|
|
64
65
|
|
|
66
|
+
let { archive } = opts;
|
|
67
|
+
|
|
65
68
|
const main = async () => {
|
|
66
69
|
if (!url && !file) {
|
|
67
70
|
logErrorAndExit("No URL or file location provided");
|
|
@@ -79,6 +82,15 @@ const main = async () => {
|
|
|
79
82
|
? await getUrlFeed(url, parserConfig)
|
|
80
83
|
: await getFileFeed(file, parserConfig);
|
|
81
84
|
|
|
85
|
+
const archivePrefix = (() => {
|
|
86
|
+
if (feed.feedUrl || url) {
|
|
87
|
+
const { hostname, pathname } = new URL(feed.feedUrl || url);
|
|
88
|
+
return `${hostname}${pathname}`;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return feed.title || file;
|
|
92
|
+
})();
|
|
93
|
+
|
|
82
94
|
const basePath = _path.resolve(
|
|
83
95
|
process.cwd(),
|
|
84
96
|
getFolderName({ feed, template: outDir })
|
|
@@ -118,6 +130,14 @@ const main = async () => {
|
|
|
118
130
|
fs.mkdirSync(basePath, { recursive: true });
|
|
119
131
|
}
|
|
120
132
|
|
|
133
|
+
if (archive) {
|
|
134
|
+
archive =
|
|
135
|
+
typeof archive === "boolean"
|
|
136
|
+
? "./{{podcast_title}}/archive.json"
|
|
137
|
+
: archive;
|
|
138
|
+
archive = getFolderName({ feed, template: archive });
|
|
139
|
+
}
|
|
140
|
+
|
|
121
141
|
if (includeMeta) {
|
|
122
142
|
const podcastImageUrl = getImageUrl(feed);
|
|
123
143
|
|
|
@@ -134,8 +154,15 @@ const main = async () => {
|
|
|
134
154
|
try {
|
|
135
155
|
logMessage("\nDownloading podcast image...");
|
|
136
156
|
await download({
|
|
157
|
+
archive,
|
|
137
158
|
override,
|
|
138
159
|
marker: podcastImageUrl,
|
|
160
|
+
key: getArchiveKey({
|
|
161
|
+
prefix: archivePrefix,
|
|
162
|
+
name: `${
|
|
163
|
+
feed.title ? `${feed.title}.image` : "image"
|
|
164
|
+
}${podcastImageFileExt}`,
|
|
165
|
+
}),
|
|
139
166
|
outputPath: outputImagePath,
|
|
140
167
|
url: podcastImageUrl,
|
|
141
168
|
maxAttempts: attempts,
|
|
@@ -156,8 +183,13 @@ const main = async () => {
|
|
|
156
183
|
try {
|
|
157
184
|
logMessage("\nSaving podcast metadata...");
|
|
158
185
|
writeFeedMeta({
|
|
186
|
+
archive,
|
|
159
187
|
override,
|
|
160
188
|
feed,
|
|
189
|
+
key: getArchiveKey({
|
|
190
|
+
prefix: archivePrefix,
|
|
191
|
+
name: `${feed.title ? `${feed.title}.meta` : "meta"}.json`,
|
|
192
|
+
}),
|
|
161
193
|
outputPath: outputMetaPath,
|
|
162
194
|
});
|
|
163
195
|
} catch (error) {
|
|
@@ -174,6 +206,8 @@ const main = async () => {
|
|
|
174
206
|
}
|
|
175
207
|
|
|
176
208
|
const targetItems = getItemsToDownload({
|
|
209
|
+
archive,
|
|
210
|
+
archivePrefix,
|
|
177
211
|
addMp3MetadataFlag,
|
|
178
212
|
basePath,
|
|
179
213
|
feed,
|
|
@@ -204,6 +238,8 @@ const main = async () => {
|
|
|
204
238
|
|
|
205
239
|
const { numEpisodesDownloaded, hasErrors } = await downloadItemsAsync({
|
|
206
240
|
addMp3MetadataFlag,
|
|
241
|
+
archive,
|
|
242
|
+
archivePrefix,
|
|
207
243
|
attempts,
|
|
208
244
|
basePath,
|
|
209
245
|
bitrate,
|
package/bin/commander.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
+
import { ITEM_LIST_FORMATS } from "./items.js";
|
|
2
|
+
import { logErrorAndExit } from "./logger.js";
|
|
1
3
|
import { AUDIO_ORDER_TYPES, TRANSCRIPT_TYPES } from "./util.js";
|
|
2
4
|
import { createParseNumber, hasFfmpeg } from "./validate.js";
|
|
3
|
-
import { logErrorAndExit } from "./logger.js";
|
|
4
|
-
import { ITEM_LIST_FORMATS } from "./items.js";
|
|
5
5
|
|
|
6
6
|
export const setupCommander = (program) => {
|
|
7
7
|
program
|
|
@@ -12,6 +12,10 @@ export const setupCommander = (program) => {
|
|
|
12
12
|
"specify output directory",
|
|
13
13
|
"./{{podcast_title}}"
|
|
14
14
|
)
|
|
15
|
+
.option(
|
|
16
|
+
"--archive [path]",
|
|
17
|
+
"download or write only items not listed in archive file"
|
|
18
|
+
)
|
|
15
19
|
.option(
|
|
16
20
|
"--episode-template <string>",
|
|
17
21
|
"template for generating episode related filenames",
|
package/bin/items.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import dayjs from "dayjs";
|
|
2
2
|
import path from "path";
|
|
3
|
+
import { getArchive, getArchiveFilename, getArchiveKey } from "./archive.js";
|
|
4
|
+
import { logErrorAndExit } from "./logger.js";
|
|
3
5
|
import { getItemFilename } from "./naming.js";
|
|
4
6
|
import {
|
|
5
7
|
getEpisodeAudioUrlAndExt,
|
|
@@ -8,11 +10,12 @@ import {
|
|
|
8
10
|
getTranscriptUrl,
|
|
9
11
|
getUrlExt,
|
|
10
12
|
} from "./util.js";
|
|
11
|
-
import { logErrorAndExit } from "./logger.js";
|
|
12
13
|
|
|
13
14
|
export const ITEM_LIST_FORMATS = ["table", "json"];
|
|
14
15
|
|
|
15
16
|
export const getItemsToDownload = ({
|
|
17
|
+
archive,
|
|
18
|
+
archivePrefix,
|
|
16
19
|
addMp3MetadataFlag,
|
|
17
20
|
basePath,
|
|
18
21
|
feed,
|
|
@@ -41,6 +44,8 @@ export const getItemsToDownload = ({
|
|
|
41
44
|
let i = startIndex;
|
|
42
45
|
const items = [];
|
|
43
46
|
|
|
47
|
+
const savedArchive = archive ? getArchive(archive) : [];
|
|
48
|
+
|
|
44
49
|
while (shouldGo(i)) {
|
|
45
50
|
const { title, pubDate } = feed.items[i];
|
|
46
51
|
const pubDateDay = dayjs(new Date(pubDate));
|
|
@@ -80,10 +85,21 @@ export const getItemsToDownload = ({
|
|
|
80
85
|
}
|
|
81
86
|
}
|
|
82
87
|
|
|
83
|
-
const { url: episodeAudioUrl } =
|
|
84
|
-
feed.items[i],
|
|
85
|
-
|
|
86
|
-
|
|
88
|
+
const { url: episodeAudioUrl, ext: audioFileExt } =
|
|
89
|
+
getEpisodeAudioUrlAndExt(feed.items[i], episodeSourceOrder);
|
|
90
|
+
|
|
91
|
+
const key = getArchiveKey({
|
|
92
|
+
prefix: archivePrefix,
|
|
93
|
+
name: getArchiveFilename({
|
|
94
|
+
pubDate,
|
|
95
|
+
name: title,
|
|
96
|
+
ext: audioFileExt,
|
|
97
|
+
}),
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
if (key && savedArchive.includes(key)) {
|
|
101
|
+
isValid = false;
|
|
102
|
+
}
|
|
87
103
|
|
|
88
104
|
if (isValid) {
|
|
89
105
|
const item = feed.items[i];
|
|
@@ -94,6 +110,14 @@ export const getItemsToDownload = ({
|
|
|
94
110
|
|
|
95
111
|
if (episodeImageUrl) {
|
|
96
112
|
const episodeImageFileExt = getUrlExt(episodeImageUrl);
|
|
113
|
+
const episodeImageArchiveKey = getArchiveKey({
|
|
114
|
+
prefix: archivePrefix,
|
|
115
|
+
name: getArchiveFilename({
|
|
116
|
+
pubDate,
|
|
117
|
+
name: title,
|
|
118
|
+
ext: episodeImageFileExt,
|
|
119
|
+
}),
|
|
120
|
+
});
|
|
97
121
|
|
|
98
122
|
const episodeImageName = getItemFilename({
|
|
99
123
|
item,
|
|
@@ -110,6 +134,7 @@ export const getItemsToDownload = ({
|
|
|
110
134
|
item._episodeImage = {
|
|
111
135
|
url: episodeImageUrl,
|
|
112
136
|
outputPath: outputImagePath,
|
|
137
|
+
key: episodeImageArchiveKey,
|
|
113
138
|
};
|
|
114
139
|
}
|
|
115
140
|
}
|
|
@@ -122,6 +147,14 @@ export const getItemsToDownload = ({
|
|
|
122
147
|
|
|
123
148
|
if (episodeTranscriptUrl) {
|
|
124
149
|
const episodeTranscriptFileExt = getUrlExt(episodeTranscriptUrl);
|
|
150
|
+
const episodeTranscriptArchiveKey = getArchiveKey({
|
|
151
|
+
prefix: archivePrefix,
|
|
152
|
+
name: getArchiveFilename({
|
|
153
|
+
pubDate,
|
|
154
|
+
name: title,
|
|
155
|
+
ext: episodeTranscriptFileExt,
|
|
156
|
+
}),
|
|
157
|
+
});
|
|
125
158
|
|
|
126
159
|
const episodeTranscriptName = getItemFilename({
|
|
127
160
|
item,
|
|
@@ -141,6 +174,7 @@ export const getItemsToDownload = ({
|
|
|
141
174
|
item._episodeTranscript = {
|
|
142
175
|
url: episodeTranscriptUrl,
|
|
143
176
|
outputPath: outputTranscriptPath,
|
|
177
|
+
key: episodeTranscriptArchiveKey,
|
|
144
178
|
};
|
|
145
179
|
}
|
|
146
180
|
}
|
package/bin/meta.js
CHANGED
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
import fs from "fs";
|
|
2
|
+
import { getIsInArchive, writeToArchive } from "./archive.js";
|
|
2
3
|
import { logMessage } from "./logger.js";
|
|
3
4
|
import { getPublicObject } from "./util.js";
|
|
4
5
|
|
|
5
|
-
export const writeFeedMeta = ({ outputPath, feed, override }) => {
|
|
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
|
+
}
|
|
6
11
|
const output = getPublicObject(feed, ["items"]);
|
|
7
12
|
|
|
8
13
|
try {
|
|
@@ -11,6 +16,14 @@ export const writeFeedMeta = ({ outputPath, feed, override }) => {
|
|
|
11
16
|
} else {
|
|
12
17
|
logMessage("Feed metadata exists locally. Skipping...");
|
|
13
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
|
+
}
|
|
14
27
|
} catch (error) {
|
|
15
28
|
throw new Error(
|
|
16
29
|
`Unable to save metadata file for feed: ${error.toString()}`
|
|
@@ -18,7 +31,19 @@ export const writeFeedMeta = ({ outputPath, feed, override }) => {
|
|
|
18
31
|
}
|
|
19
32
|
};
|
|
20
33
|
|
|
21
|
-
export const writeItemMeta = ({
|
|
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
|
+
|
|
22
47
|
const output = getPublicObject(item);
|
|
23
48
|
|
|
24
49
|
try {
|
|
@@ -27,6 +52,14 @@ export const writeItemMeta = ({ marker, outputPath, item, override }) => {
|
|
|
27
52
|
} else {
|
|
28
53
|
logMessage(`${marker} | Episode metadata exists locally. Skipping...`);
|
|
29
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
|
+
}
|
|
30
63
|
} catch (error) {
|
|
31
64
|
throw new Error("Unable to save meta file for episode", error);
|
|
32
65
|
}
|