podcast-dl 11.7.3 → 11.7.5

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/bin/archive.js CHANGED
@@ -43,10 +43,7 @@ export const writeToArchive = ({ archiveKeys, archive }) => {
43
43
  });
44
44
 
45
45
  if (data.dirty) {
46
- fs.writeFileSync(
47
- path.resolve(cwd, archive),
48
- JSON.stringify([...data.entries], null, 4)
49
- );
46
+ fs.writeFileSync(path.resolve(cwd, archive), JSON.stringify([...data.entries], null, 4));
50
47
  data.dirty = false;
51
48
  }
52
49
  };
@@ -57,9 +54,7 @@ export const getIsInArchive = ({ archiveKeys, archive }) => {
57
54
  };
58
55
 
59
56
  export const getArchiveFilename = ({ pubDate, name, ext }) => {
60
- const formattedPubDate = pubDate
61
- ? dayjs(new Date(pubDate)).format("YYYYMMDD")
62
- : null;
57
+ const formattedPubDate = pubDate ? dayjs(new Date(pubDate)).format("YYYYMMDD") : null;
63
58
 
64
59
  const baseName = formattedPubDate ? `${formattedPubDate}-${name}` : name;
65
60
 
package/bin/async.js CHANGED
@@ -5,12 +5,7 @@ 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
- getArchiveKeys,
11
- getIsInArchive,
12
- writeToArchive,
13
- } from "./archive.js";
8
+ import { getArchiveFilename, getArchiveKeys, getIsInArchive, writeToArchive } from "./archive.js";
14
9
  import { runExec } from "./exec.js";
15
10
  import { runFfmpeg } from "./ffmpeg.js";
16
11
  import {
@@ -61,11 +56,7 @@ export const download = async (options) => {
61
56
  return outputPath;
62
57
  }
63
58
 
64
- if (
65
- archive &&
66
- archiveKeys.length &&
67
- getIsInArchive({ archiveKeys, archive })
68
- ) {
59
+ if (archive && archiveKeys.length && getIsInArchive({ archiveKeys, archive })) {
69
60
  logMessage("Download exists in archive. Skipping...");
70
61
  return null;
71
62
  }
@@ -98,23 +89,17 @@ export const download = async (options) => {
98
89
 
99
90
  logMessage(
100
91
  `Starting download${
101
- expectedSize
102
- ? ` of ${(expectedSize / BYTES_IN_MB).toFixed(2)} MB...`
103
- : "..."
104
- }`
92
+ expectedSize ? ` of ${(expectedSize / BYTES_IN_MB).toFixed(2)} MB...` : "..."
93
+ }`,
105
94
  );
106
95
 
107
96
  try {
108
97
  const onDownloadProgress = throttle(3000, (progress) => {
109
- if (
110
- getShouldOutputProgressIndicator() &&
111
- progress.transferred > 0 &&
112
- progress.percent < 1
113
- ) {
98
+ if (getShouldOutputProgressIndicator() && progress.transferred > 0 && progress.percent < 1) {
114
99
  logMessage(
115
- `${(progress.percent * 100).toFixed(0)}% of ${(
116
- progress.total / BYTES_IN_MB
117
- ).toFixed(2)} MB...`
100
+ `${(progress.percent * 100).toFixed(0)}% of ${(progress.total / BYTES_IN_MB).toFixed(
101
+ 2,
102
+ )} MB...`,
118
103
  );
119
104
  }
120
105
  });
@@ -123,7 +108,7 @@ export const download = async (options) => {
123
108
  got
124
109
  .stream(url, { headers: { "user-agent": userAgent } })
125
110
  .on("downloadProgress", onDownloadProgress),
126
- fs.createWriteStream(tempOutputPath)
111
+ fs.createWriteStream(tempOutputPath),
127
112
  );
128
113
  } catch (error) {
129
114
  removeFile();
@@ -145,10 +130,7 @@ export const download = async (options) => {
145
130
  if (fileSize === 0) {
146
131
  removeFile();
147
132
 
148
- logMessage(
149
- "Unable to write to file. Suggestion: verify permissions",
150
- LOG_LEVELS.important
151
- );
133
+ logMessage("Unable to write to file. Suggestion: verify permissions", LOG_LEVELS.important);
152
134
 
153
135
  return null;
154
136
  }
@@ -159,10 +141,7 @@ export const download = async (options) => {
159
141
  outputPath,
160
142
  contentType: headResponse?.headers?.["content-type"],
161
143
  onCorrect: (from, to) =>
162
- logMessage(
163
- `Correcting extension: ${from} --> ${to}`,
164
- LOG_LEVELS.important
165
- ),
144
+ logMessage(`Correcting extension: ${from} --> ${to}`, LOG_LEVELS.important),
166
145
  });
167
146
 
168
147
  fs.renameSync(tempOutputPath, finalOutputPath);
@@ -217,8 +196,10 @@ export const downloadItemsAsync = async ({
217
196
  const threadIndex = index % threads;
218
197
  const marker = threads > 1 ? `[${threadIndex}] ${item.title}` : item.title;
219
198
  const logMessage = getLogMessageWithMarker(marker);
220
- const { url: episodeAudioUrl, ext: audioFileExt } =
221
- getEpisodeAudioUrlAndExt(item, episodeSourceOrder);
199
+ const { url: episodeAudioUrl, ext: audioFileExt } = getEpisodeAudioUrlAndExt(
200
+ item,
201
+ episodeSourceOrder,
202
+ );
222
203
 
223
204
  if (!episodeAudioUrl) {
224
205
  hasErrors = true;
@@ -273,9 +254,7 @@ export const downloadItemsAsync = async ({
273
254
  } catch (error) {
274
255
  hasErrors = true;
275
256
  logError(
276
- `${marker} | Error downloading ${
277
- item._episodeImage.url
278
- }: ${error.toString()}`
257
+ `${marker} | Error downloading ${item._episodeImage.url}: ${error.toString()}`,
279
258
  );
280
259
  }
281
260
  }
@@ -300,9 +279,7 @@ export const downloadItemsAsync = async ({
300
279
  } catch (error) {
301
280
  hasErrors = true;
302
281
  logError(
303
- `${marker} | Error downloading ${
304
- item._episodeTranscript.url
305
- }: ${error.toString()}`
282
+ `${marker} | Error downloading ${item._episodeTranscript.url}: ${error.toString()}`,
306
283
  );
307
284
  }
308
285
  }
@@ -316,9 +293,7 @@ export const downloadItemsAsync = async ({
316
293
  audioFormat,
317
294
  bitrate,
318
295
  embedMetadata: embedMetadataFlag,
319
- episodeImageOutputPath: hasEpisodeImage
320
- ? item._episodeImage.outputPath
321
- : undefined,
296
+ episodeImageOutputPath: hasEpisodeImage ? item._episodeImage.outputPath : undefined,
322
297
  ext: audioFileExt,
323
298
  feed,
324
299
  item,
@@ -355,10 +330,7 @@ export const downloadItemsAsync = async ({
355
330
  width: episodeDigits,
356
331
  offset: episodeNumOffset,
357
332
  });
358
- const outputEpisodeMetaPath = _path.resolve(
359
- basePath,
360
- episodeMetaName
361
- );
333
+ const outputEpisodeMetaPath = _path.resolve(basePath, episodeMetaName);
362
334
 
363
335
  try {
364
336
  logMessage("Saving episode metadata...");
@@ -393,9 +365,7 @@ export const downloadItemsAsync = async ({
393
365
  }
394
366
  };
395
367
 
396
- const itemPromises = targetItems.map((item, index) =>
397
- limit(() => downloadItem(item, index))
398
- );
368
+ const itemPromises = targetItems.map((item, index) => limit(() => downloadItem(item, index)));
399
369
 
400
370
  await Promise.all(itemPromises);
401
371
 
package/bin/bin.js CHANGED
@@ -9,13 +9,7 @@ import { getArchiveKey } from "./archive.js";
9
9
  import { download, downloadItemsAsync } from "./async.js";
10
10
  import { setupCommander } from "./commander.js";
11
11
  import { getItemsToDownload, logItemsList } from "./items.js";
12
- import {
13
- ERROR_STATUSES,
14
- LOG_LEVELS,
15
- logError,
16
- logErrorAndExit,
17
- logMessage,
18
- } from "./logger.js";
12
+ import { ERROR_STATUSES, LOG_LEVELS, logError, logErrorAndExit, logMessage } from "./logger.js";
19
13
  import { writeFeedMeta } from "./meta.js";
20
14
  import { getFolderName, getSimpleFilename } from "./naming.js";
21
15
  import {
@@ -87,9 +81,7 @@ const main = async () => {
87
81
  bootstrapProxy();
88
82
  }
89
83
 
90
- const feed = url
91
- ? await getUrlFeed(url, parserConfig)
92
- : await getFileFeed(file, parserConfig);
84
+ const feed = url ? await getUrlFeed(url, parserConfig) : await getFileFeed(file, parserConfig);
93
85
 
94
86
  const archivePrefix = (() => {
95
87
  if (feed.feedUrl || url) {
@@ -101,10 +93,7 @@ const main = async () => {
101
93
  return feed.title || file;
102
94
  })();
103
95
 
104
- const basePath = _path.resolve(
105
- cwd,
106
- getFolderName({ feed, template: outDir })
107
- );
96
+ const basePath = _path.resolve(cwd, getFolderName({ feed, template: outDir }));
108
97
 
109
98
  if (info) {
110
99
  logFeedInfo(feed);
@@ -142,10 +131,7 @@ const main = async () => {
142
131
  }
143
132
 
144
133
  if (archive) {
145
- archive =
146
- typeof archive === "boolean"
147
- ? "./{{podcast_title}}/archive.json"
148
- : archive;
134
+ archive = typeof archive === "boolean" ? "./{{podcast_title}}/archive.json" : archive;
149
135
  archive = getFolderName({ feed, template: archive });
150
136
  }
151
137
 
@@ -156,7 +142,7 @@ const main = async () => {
156
142
  const podcastImageFileExt = getUrlExt(podcastImageUrl);
157
143
  const outputImagePath = _path.resolve(
158
144
  basePath,
159
- getSimpleFilename(feed.title || "image", podcastImageFileExt)
145
+ getSimpleFilename(feed.title || "image", podcastImageFileExt),
160
146
  );
161
147
 
162
148
  try {
@@ -184,10 +170,7 @@ const main = async () => {
184
170
 
185
171
  const outputMetaPath = _path.resolve(
186
172
  basePath,
187
- getSimpleFilename(
188
- feed.title ? feed.title : "meta",
189
- feed.title ? ".meta.json" : ".json"
190
- )
173
+ getSimpleFilename(feed.title ? feed.title : "meta", feed.title ? ".meta.json" : ".json"),
191
174
  );
192
175
 
193
176
  try {
@@ -243,9 +226,7 @@ const main = async () => {
243
226
  logErrorAndExit("No episodes found with provided criteria to download");
244
227
  }
245
228
 
246
- logMessage(
247
- `\nStarting download of ${pluralize("episode", targetItems.length, true)}\n`
248
- );
229
+ logMessage(`\nStarting download of ${pluralize("episode", targetItems.length, true)}\n`);
249
230
 
250
231
  const { numEpisodesDownloaded, hasErrors } = await downloadItemsAsync({
251
232
  archive,
@@ -278,17 +259,11 @@ const main = async () => {
278
259
  `\n${numEpisodesDownloaded} of ${pluralize(
279
260
  "episode",
280
261
  targetItems.length,
281
- true
282
- )} downloaded\n`
262
+ true,
263
+ )} downloaded\n`,
283
264
  );
284
265
  } else if (numEpisodesDownloaded > 0) {
285
- logMessage(
286
- `\nSuccessfully downloaded ${pluralize(
287
- "episode",
288
- numEpisodesDownloaded,
289
- true
290
- )}\n`
291
- );
266
+ logMessage(`\nSuccessfully downloaded ${pluralize("episode", numEpisodesDownloaded, true)}\n`);
292
267
  }
293
268
 
294
269
  if (numEpisodesDownloaded === 0) {
package/bin/commander.js CHANGED
@@ -7,29 +7,22 @@ export const setupCommander = (program) => {
7
7
  program
8
8
  .option("--url <string>", "url to podcast rss feed")
9
9
  .option("--file <path>", "local path to podcast rss feed")
10
- .option(
11
- "--out-dir <path>",
12
- "specify output directory",
13
- "./{{podcast_title}}"
14
- )
15
- .option(
16
- "--archive [path]",
17
- "download or write only items not listed in archive file"
18
- )
10
+ .option("--out-dir <path>", "specify output directory", "./{{podcast_title}}")
11
+ .option("--archive [path]", "download or write only items not listed in archive file")
19
12
  .option(
20
13
  "--episode-template <string>",
21
14
  "template for generating episode related filenames",
22
- "{{release_date}}-{{title}}"
15
+ "{{release_date}}-{{title}}",
23
16
  )
24
17
  .option(
25
18
  "--episode-custom-template-options <patterns...>",
26
- "create custom options for the episode template"
19
+ "create custom options for the episode template",
27
20
  )
28
21
  .option(
29
22
  "--episode-digits <number>",
30
23
  "minimum number of digits to use for episode numbering (leading zeros)",
31
24
  createParseNumber({ min: 0, name: "--episode-digits" }),
32
- 1
25
+ 1,
33
26
  )
34
27
  .option(
35
28
  "--episode-num-offset <number>",
@@ -39,7 +32,7 @@ export const setupCommander = (program) => {
39
32
  max: Number.MAX_SAFE_INTEGER,
40
33
  name: "--episode-num-offset",
41
34
  }),
42
- 0
35
+ 0,
43
36
  )
44
37
  .option(
45
38
  "--episode-source-order <string>",
@@ -49,24 +42,16 @@ export const setupCommander = (program) => {
49
42
  const isValid = parsed.every((type) => !!AUDIO_ORDER_TYPES[type]);
50
43
 
51
44
  if (!isValid) {
52
- logErrorAndExit(
53
- `Invalid type found in --episode-source-order: ${value}\n`
54
- );
45
+ logErrorAndExit(`Invalid type found in --episode-source-order: ${value}\n`);
55
46
  }
56
47
 
57
48
  return parsed;
58
49
  },
59
- [AUDIO_ORDER_TYPES.enclosure, AUDIO_ORDER_TYPES.link]
60
- )
61
- .option("--include-meta", "write out podcast metadata to json")
62
- .option(
63
- "--include-episode-meta",
64
- "write out individual episode metadata to json"
65
- )
66
- .option(
67
- "--include-episode-transcripts",
68
- "download found episode transcripts"
50
+ [AUDIO_ORDER_TYPES.enclosure, AUDIO_ORDER_TYPES.link],
69
51
  )
52
+ .option("--include-meta", "write out podcast metadata to json", false)
53
+ .option("--include-episode-meta", "write out individual episode metadata to json", false)
54
+ .option("--include-episode-transcripts", "download found episode transcripts", false)
70
55
  .option(
71
56
  "--episode-transcript-types <string>",
72
57
  "list of allowed transcript types in preferred order",
@@ -75,9 +60,7 @@ export const setupCommander = (program) => {
75
60
  const isValid = parsed.every((type) => !!TRANSCRIPT_TYPES[type]);
76
61
 
77
62
  if (!isValid) {
78
- logErrorAndExit(
79
- `Invalid type found in --transcript-types: ${value}\n`
80
- );
63
+ logErrorAndExit(`Invalid type found in --transcript-types: ${value}\n`);
81
64
  }
82
65
 
83
66
  return parsed;
@@ -90,98 +73,68 @@ export const setupCommander = (program) => {
90
73
  TRANSCRIPT_TYPES["text/vtt"],
91
74
  TRANSCRIPT_TYPES["text/html"],
92
75
  TRANSCRIPT_TYPES["text/plain"],
93
- ]
76
+ ],
94
77
  )
95
- .option("--include-episode-images", "download found episode images")
78
+ .option("--include-episode-images", "download found episode images", false)
96
79
  .option(
97
80
  "--offset <number>",
98
81
  "offset episode to start downloading from (most recent = 0)",
99
82
  createParseNumber({ min: 0, name: "--offset" }),
100
- 0
83
+ 0,
101
84
  )
102
85
  .option(
103
86
  "--limit <number>",
104
87
  "max amount of episodes to download",
105
- createParseNumber({ min: 1, name: "--limit", require: false })
106
- )
107
- .option(
108
- "--episode-regex <string>",
109
- "match episode title against regex before downloading"
110
- )
111
- .option(
112
- "--episode-regex-exclude <string>",
113
- "episode titles matching regex will be excluded"
88
+ createParseNumber({ min: 1, name: "--limit", required: false }),
114
89
  )
90
+ .option("--episode-regex <string>", "match episode title against regex before downloading")
91
+ .option("--episode-regex-exclude <string>", "episode titles matching regex will be excluded")
115
92
  .option(
116
93
  "--season <number>",
117
94
  "download only episodes from this season",
118
- createParseNumber({ min: 0, name: "--season" })
119
- )
120
- .option(
121
- "--after <string>",
122
- "download episodes only after this date (inclusive)"
123
- )
124
- .option(
125
- "--before <string>",
126
- "download episodes only before this date (inclusive)"
127
- )
128
- .option(
129
- "--embed-metadata",
130
- "add metadata to episode files using ffmpeg",
131
- hasFfmpeg
132
- )
133
- .option(
134
- "--add-mp3-metadata",
135
- "deprecated: use --embed-metadata instead",
136
- hasFfmpeg
95
+ createParseNumber({ min: 0, name: "--season" }),
137
96
  )
97
+ .option("--after <string>", "download episodes only after this date (inclusive)")
98
+ .option("--before <string>", "download episodes only before this date (inclusive)")
99
+ .option("--embed-metadata", "add metadata to episode files using ffmpeg", hasFfmpeg, false)
100
+ .option("--add-mp3-metadata", "deprecated: use --embed-metadata instead", hasFfmpeg, false)
138
101
  .option(
139
102
  "--audio-format <string>",
140
103
  "convert audio to format (mp3, m4a, aac, opus, ogg, flac, wav)",
141
104
  (value) => {
142
105
  if (!AUDIO_FORMATS[value]) {
143
106
  logErrorAndExit(
144
- `Invalid audio format: ${value}\nSupported formats: ${Object.keys(
145
- AUDIO_FORMATS
146
- ).join(", ")}`
107
+ `Invalid audio format: ${value}\nSupported formats: ${Object.keys(AUDIO_FORMATS).join(
108
+ ", ",
109
+ )}`,
147
110
  );
148
111
  }
149
112
 
150
113
  return hasFfmpeg(value);
151
- }
152
- )
153
- .option(
154
- "--adjust-bitrate <string>",
155
- "adjust bitrate of episode files using ffmpeg",
156
- hasFfmpeg
114
+ },
157
115
  )
158
- .option("--mono", "force episode files into mono using ffmpeg", hasFfmpeg)
159
- .option("--override", "override local files on collision")
116
+ .option("--adjust-bitrate <string>", "adjust bitrate of episode files using ffmpeg", hasFfmpeg)
117
+ .option("--mono", "force episode files into mono using ffmpeg", hasFfmpeg, false)
118
+ .option("--override", "override local files on collision", false)
160
119
  .option(
161
120
  "--always-postprocess",
162
- "always run additional tasks on the file regardless of whether the file already exists"
163
- )
164
- .option("--reverse", "download episodes in reverse order")
165
- .option("--info", "print retrieved podcast info instead of downloading")
166
- .option(
167
- "--list [table|json]",
168
- "print episode info instead of downloading",
169
- (value) => {
170
- if (!ITEM_LIST_FORMATS.includes(value)) {
171
- logErrorAndExit(
172
- `${value} is an invalid format for --list\nUse one of the following: ${ITEM_LIST_FORMATS.join(
173
- ", "
174
- )}`
175
- );
176
- }
177
-
178
- return value;
121
+ "always run additional tasks on the file regardless of whether the file already exists",
122
+ false,
123
+ )
124
+ .option("--reverse", "download episodes in reverse order", false)
125
+ .option("--info", "print retrieved podcast info instead of downloading", false)
126
+ .option("--list [table|json]", "print episode info instead of downloading", (value) => {
127
+ if (!ITEM_LIST_FORMATS.includes(value)) {
128
+ logErrorAndExit(
129
+ `${value} is an invalid format for --list\nUse one of the following: ${ITEM_LIST_FORMATS.join(
130
+ ", ",
131
+ )}`,
132
+ );
179
133
  }
180
- )
181
- .option(
182
- "--exec <string>",
183
- "execute a command after each episode is downloaded"
184
- )
134
+
135
+ return value;
136
+ })
137
+ .option("--exec <string>", "execute a command after each episode is downloaded")
185
138
  .option(
186
139
  "--threads <number>",
187
140
  "the number of downloads that can happen concurrently",
@@ -190,7 +143,7 @@ export const setupCommander = (program) => {
190
143
  max: Number.MAX_SAFE_INTEGER,
191
144
  name: "threads",
192
145
  }),
193
- 1
146
+ 1,
194
147
  )
195
148
  .option(
196
149
  "--attempts <number>",
@@ -200,15 +153,12 @@ export const setupCommander = (program) => {
200
153
  max: Number.MAX_SAFE_INTEGER,
201
154
  name: "attempts",
202
155
  }),
203
- 3
204
- )
205
- .option(
206
- "--parser-config <string>",
207
- "path to JSON config to override RSS parser"
156
+ 3,
208
157
  )
209
- .option("--proxy", "enable proxy support via global-agent")
158
+ .option("--parser-config <string>", "path to JSON config to override RSS parser")
159
+ .option("--proxy", "enable proxy support via global-agent", false)
210
160
  .option("--user-agent <string>", "specify custom user agent string")
211
- .option("--trust-ext", "trust file extension, skip MIME-based correction");
161
+ .option("--trust-ext", "trust file extension, skip MIME-based correction", false);
212
162
 
213
163
  program.parse();
214
164