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/bin/items.js CHANGED
@@ -1,237 +1,247 @@
1
- import dayjs from "dayjs";
2
- import path from "path";
3
- import { getArchive, getArchiveFilename, getArchiveKey } from "./archive.js";
4
- import { logErrorAndExit } from "./logger.js";
5
- import { getItemFilename } from "./naming.js";
6
- import {
7
- getEpisodeAudioUrlAndExt,
8
- getImageUrl,
9
- getLoopControls,
10
- getTranscriptUrl,
11
- getUrlExt,
12
- } from "./util.js";
13
-
14
- export const ITEM_LIST_FORMATS = ["table", "json"];
15
-
16
- export const getItemsToDownload = ({
17
- archive,
18
- archivePrefix,
19
- addMp3MetadataFlag,
20
- basePath,
21
- feed,
22
- limit,
23
- offset,
24
- reverse,
25
- before,
26
- after,
27
- episodeDigits,
28
- episodeNumOffset,
29
- episodeRegex,
30
- episodeRegexExclude,
31
- episodeSourceOrder,
32
- episodeTemplate,
33
- episodeCustomTemplateOptions,
34
- includeEpisodeImages,
35
- includeEpisodeTranscripts,
36
- episodeTranscriptTypes,
37
- }) => {
38
- const { startIndex, shouldGo, next } = getLoopControls({
39
- offset,
40
- reverse,
41
- length: feed.items.length,
42
- });
43
-
44
- let i = startIndex;
45
- const items = [];
46
-
47
- const savedArchive = archive ? getArchive(archive) : [];
48
-
49
- while (shouldGo(i)) {
50
- const { title, pubDate } = feed.items[i];
51
- const pubDateDay = dayjs(new Date(pubDate));
52
- let isValid = true;
53
-
54
- if (episodeRegex) {
55
- const generatedEpisodeRegex = new RegExp(episodeRegex);
56
- if (title && !generatedEpisodeRegex.test(title)) {
57
- isValid = false;
58
- }
59
- }
60
-
61
- if (episodeRegexExclude) {
62
- const generatedEpisodeRegexExclude = new RegExp(episodeRegexExclude);
63
- if (title && generatedEpisodeRegexExclude.test(title)) {
64
- isValid = false;
65
- }
66
- }
67
-
68
- if (before) {
69
- const beforeDateDay = dayjs(new Date(before));
70
- if (
71
- !pubDateDay.isSame(beforeDateDay, "day") &&
72
- !pubDateDay.isBefore(beforeDateDay, "day")
73
- ) {
74
- isValid = false;
75
- }
76
- }
77
-
78
- if (after) {
79
- const afterDateDay = dayjs(new Date(after));
80
- if (
81
- !pubDateDay.isSame(afterDateDay, "day") &&
82
- !pubDateDay.isAfter(afterDateDay, "day")
83
- ) {
84
- isValid = false;
85
- }
86
- }
87
-
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
- }
103
-
104
- if (isValid) {
105
- const item = feed.items[i];
106
- item._originalIndex = i;
107
-
108
- if (includeEpisodeImages || addMp3MetadataFlag) {
109
- const episodeImageUrl = getImageUrl(item);
110
-
111
- if (episodeImageUrl) {
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
- });
121
-
122
- const episodeImageName = getItemFilename({
123
- item,
124
- feed,
125
- url: episodeAudioUrl,
126
- ext: episodeImageFileExt,
127
- template: episodeTemplate,
128
- customTemplateOptions: episodeCustomTemplateOptions,
129
- width: episodeDigits,
130
- offset: episodeNumOffset,
131
- });
132
-
133
- const outputImagePath = path.resolve(basePath, episodeImageName);
134
- item._episodeImage = {
135
- url: episodeImageUrl,
136
- outputPath: outputImagePath,
137
- key: episodeImageArchiveKey,
138
- };
139
- }
140
- }
141
-
142
- if (includeEpisodeTranscripts) {
143
- const episodeTranscriptUrl = getTranscriptUrl(
144
- item,
145
- episodeTranscriptTypes
146
- );
147
-
148
- if (episodeTranscriptUrl) {
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
- });
158
-
159
- const episodeTranscriptName = getItemFilename({
160
- item,
161
- feed,
162
- url: episodeAudioUrl,
163
- ext: episodeTranscriptFileExt,
164
- template: episodeTemplate,
165
- width: episodeDigits,
166
- offset: episodeNumOffset,
167
- });
168
-
169
- const outputTranscriptPath = path.resolve(
170
- basePath,
171
- episodeTranscriptName
172
- );
173
-
174
- item._episodeTranscript = {
175
- url: episodeTranscriptUrl,
176
- outputPath: outputTranscriptPath,
177
- key: episodeTranscriptArchiveKey,
178
- };
179
- }
180
- }
181
-
182
- items.push(item);
183
- }
184
-
185
- i = next(i);
186
- }
187
-
188
- return limit ? items.slice(0, limit) : items;
189
- };
190
-
191
- export const logItemsList = ({
192
- type,
193
- feed,
194
- limit,
195
- offset,
196
- reverse,
197
- before,
198
- after,
199
- episodeRegex,
200
- episodeRegexExclude,
201
- }) => {
202
- const items = getItemsToDownload({
203
- feed,
204
- limit,
205
- offset,
206
- reverse,
207
- before,
208
- after,
209
- episodeRegex,
210
- episodeRegexExclude,
211
- });
212
-
213
- if (!items.length) {
214
- logErrorAndExit("No episodes found with provided criteria to list");
215
- }
216
-
217
- const isJson = type === "json";
218
-
219
- const output = items.map((item) => {
220
- const data = {
221
- episodeNum: feed.items.length - item._originalIndex,
222
- title: item.title,
223
- pubDate: item.pubDate,
224
- };
225
-
226
- return data;
227
- });
228
-
229
- if (isJson) {
230
- // eslint-disable-next-line no-console
231
- console.log(JSON.stringify(output));
232
- return;
233
- }
234
-
235
- // eslint-disable-next-line no-console
236
- console.table(output);
237
- };
1
+ import dayjs from "dayjs";
2
+ import path from "path";
3
+ import { getArchive, getArchiveFilename, getArchiveKey } from "./archive.js";
4
+ import { logErrorAndExit } from "./logger.js";
5
+ import { getItemFilename } from "./naming.js";
6
+ import {
7
+ getEpisodeAudioUrlAndExt,
8
+ getImageUrl,
9
+ getLoopControls,
10
+ getTranscriptUrl,
11
+ getUrlExt,
12
+ } from "./util.js";
13
+
14
+ export const ITEM_LIST_FORMATS = ["table", "json"];
15
+
16
+ export const getItemsToDownload = ({
17
+ archive,
18
+ archivePrefix,
19
+ addMp3MetadataFlag,
20
+ basePath,
21
+ feed,
22
+ limit,
23
+ offset,
24
+ reverse,
25
+ before,
26
+ after,
27
+ episodeDigits,
28
+ episodeNumOffset,
29
+ episodeRegex,
30
+ episodeRegexExclude,
31
+ episodeSourceOrder,
32
+ episodeTemplate,
33
+ episodeCustomTemplateOptions,
34
+ includeEpisodeImages,
35
+ includeEpisodeTranscripts,
36
+ episodeTranscriptTypes,
37
+ season,
38
+ }) => {
39
+ const { startIndex, shouldGo, next } = getLoopControls({
40
+ offset,
41
+ reverse,
42
+ length: feed.items.length,
43
+ });
44
+
45
+ let i = startIndex;
46
+ const items = [];
47
+
48
+ const savedArchive = archive ? getArchive(archive) : [];
49
+
50
+ while (shouldGo(i)) {
51
+ const { title, pubDate, itunes } = feed.items[i];
52
+ const actualSeasonNum = itunes?.season ? parseInt(itunes.season) : null;
53
+ const pubDateDay = dayjs(new Date(pubDate));
54
+ let isValid = true;
55
+
56
+ if (episodeRegex) {
57
+ const generatedEpisodeRegex = new RegExp(episodeRegex);
58
+ if (title && !generatedEpisodeRegex.test(title)) {
59
+ isValid = false;
60
+ }
61
+ }
62
+
63
+ if (episodeRegexExclude) {
64
+ const generatedEpisodeRegexExclude = new RegExp(episodeRegexExclude);
65
+ if (title && generatedEpisodeRegexExclude.test(title)) {
66
+ isValid = false;
67
+ }
68
+ }
69
+
70
+ if (before) {
71
+ const beforeDateDay = dayjs(new Date(before));
72
+ if (
73
+ !pubDateDay.isSame(beforeDateDay, "day") &&
74
+ !pubDateDay.isBefore(beforeDateDay, "day")
75
+ ) {
76
+ isValid = false;
77
+ }
78
+ }
79
+
80
+ if (after) {
81
+ const afterDateDay = dayjs(new Date(after));
82
+ if (
83
+ !pubDateDay.isSame(afterDateDay, "day") &&
84
+ !pubDateDay.isAfter(afterDateDay, "day")
85
+ ) {
86
+ isValid = false;
87
+ }
88
+ }
89
+
90
+ if (season && season != actualSeasonNum) {
91
+ isValid = false;
92
+ }
93
+
94
+ const { url: episodeAudioUrl, ext: audioFileExt } =
95
+ getEpisodeAudioUrlAndExt(feed.items[i], episodeSourceOrder);
96
+
97
+ const key = getArchiveKey({
98
+ prefix: archivePrefix,
99
+ name: getArchiveFilename({
100
+ pubDate,
101
+ name: title,
102
+ ext: audioFileExt,
103
+ }),
104
+ });
105
+
106
+ if (key && savedArchive.includes(key)) {
107
+ isValid = false;
108
+ }
109
+
110
+ if (isValid) {
111
+ const item = feed.items[i];
112
+ item._originalIndex = i;
113
+ item.seasonNum = actualSeasonNum;
114
+
115
+ if (includeEpisodeImages || addMp3MetadataFlag) {
116
+ const episodeImageUrl = getImageUrl(item);
117
+
118
+ if (episodeImageUrl) {
119
+ const episodeImageFileExt = getUrlExt(episodeImageUrl);
120
+ const episodeImageArchiveKey = getArchiveKey({
121
+ prefix: archivePrefix,
122
+ name: getArchiveFilename({
123
+ pubDate,
124
+ name: title,
125
+ ext: episodeImageFileExt,
126
+ }),
127
+ });
128
+
129
+ const episodeImageName = getItemFilename({
130
+ item,
131
+ feed,
132
+ url: episodeAudioUrl,
133
+ ext: episodeImageFileExt,
134
+ template: episodeTemplate,
135
+ customTemplateOptions: episodeCustomTemplateOptions,
136
+ width: episodeDigits,
137
+ offset: episodeNumOffset,
138
+ });
139
+
140
+ const outputImagePath = path.resolve(basePath, episodeImageName);
141
+ item._episodeImage = {
142
+ url: episodeImageUrl,
143
+ outputPath: outputImagePath,
144
+ key: episodeImageArchiveKey,
145
+ };
146
+ }
147
+ }
148
+
149
+ if (includeEpisodeTranscripts) {
150
+ const episodeTranscriptUrl = getTranscriptUrl(
151
+ item,
152
+ episodeTranscriptTypes
153
+ );
154
+
155
+ if (episodeTranscriptUrl) {
156
+ const episodeTranscriptFileExt = getUrlExt(episodeTranscriptUrl);
157
+ const episodeTranscriptArchiveKey = getArchiveKey({
158
+ prefix: archivePrefix,
159
+ name: getArchiveFilename({
160
+ pubDate,
161
+ name: title,
162
+ ext: episodeTranscriptFileExt,
163
+ }),
164
+ });
165
+
166
+ const episodeTranscriptName = getItemFilename({
167
+ item,
168
+ feed,
169
+ url: episodeAudioUrl,
170
+ ext: episodeTranscriptFileExt,
171
+ template: episodeTemplate,
172
+ width: episodeDigits,
173
+ offset: episodeNumOffset,
174
+ });
175
+
176
+ const outputTranscriptPath = path.resolve(
177
+ basePath,
178
+ episodeTranscriptName
179
+ );
180
+
181
+ item._episodeTranscript = {
182
+ url: episodeTranscriptUrl,
183
+ outputPath: outputTranscriptPath,
184
+ key: episodeTranscriptArchiveKey,
185
+ };
186
+ }
187
+ }
188
+
189
+ items.push(item);
190
+ }
191
+
192
+ i = next(i);
193
+ }
194
+
195
+ return limit ? items.slice(0, limit) : items;
196
+ };
197
+
198
+ export const logItemsList = ({
199
+ type,
200
+ feed,
201
+ limit,
202
+ offset,
203
+ reverse,
204
+ before,
205
+ after,
206
+ episodeRegex,
207
+ episodeRegexExclude,
208
+ season,
209
+ }) => {
210
+ const items = getItemsToDownload({
211
+ feed,
212
+ limit,
213
+ offset,
214
+ reverse,
215
+ before,
216
+ after,
217
+ episodeRegex,
218
+ episodeRegexExclude,
219
+ season,
220
+ });
221
+
222
+ if (!items.length) {
223
+ logErrorAndExit("No episodes found with provided criteria to list");
224
+ }
225
+
226
+ const isJson = type === "json";
227
+
228
+ const output = items.map((item) => {
229
+ const data = {
230
+ seasonNum: item.seasonNum,
231
+ episodeNum: feed.items.length - item._originalIndex,
232
+ title: item.title,
233
+ pubDate: item.pubDate,
234
+ };
235
+
236
+ return data;
237
+ });
238
+
239
+ if (isJson) {
240
+ // eslint-disable-next-line no-console
241
+ console.log(JSON.stringify(output));
242
+ return;
243
+ }
244
+
245
+ // eslint-disable-next-line no-console
246
+ console.table(output);
247
+ };
package/bin/logger.js CHANGED
@@ -1,84 +1,84 @@
1
- /* eslint-disable no-console */
2
-
3
- export const ERROR_STATUSES = {
4
- general: 1,
5
- nothingDownloaded: 2,
6
- completedWithErrors: 3,
7
- };
8
-
9
- export const LOG_LEVEL_TYPES = {
10
- debug: "debug",
11
- quiet: "quiet",
12
- silent: "silent",
13
- static: "static",
14
- };
15
-
16
- export const LOG_LEVELS = {
17
- debug: 0,
18
- info: 1,
19
- important: 2,
20
- };
21
-
22
- export const getShouldOutputProgressIndicator = () => {
23
- return (
24
- process.stdout.isTTY &&
25
- process.env.LOG_LEVEL !== LOG_LEVEL_TYPES.static &&
26
- process.env.LOG_LEVEL !== LOG_LEVEL_TYPES.quiet &&
27
- process.env.LOG_LEVEL !== LOG_LEVEL_TYPES.silent
28
- );
29
- };
30
-
31
- export const logMessage = (message = "", logLevel = 1) => {
32
- if (
33
- !process.env.LOG_LEVEL ||
34
- process.env.LOG_LEVEL === LOG_LEVEL_TYPES.debug ||
35
- process.env.LOG_LEVEL === LOG_LEVEL_TYPES.static
36
- ) {
37
- console.log(message);
38
- return;
39
- }
40
-
41
- if (process.env.LOG_LEVEL === LOG_LEVEL_TYPES.silent) {
42
- return;
43
- }
44
-
45
- if (
46
- process.env.LOG_LEVEL === LOG_LEVEL_TYPES.quiet &&
47
- logLevel > LOG_LEVELS.info
48
- ) {
49
- console.log(message);
50
- return;
51
- }
52
- };
53
-
54
- export const getLogMessageWithMarker = (marker) => {
55
- return (message, logLevel) => {
56
- if (marker) {
57
- logMessage(`${marker} | ${message}`, logLevel);
58
- } else {
59
- logMessage(message, logLevel);
60
- }
61
- };
62
- };
63
-
64
- export const logError = (msg, error) => {
65
- if (process.env.LOG_LEVEL === LOG_LEVEL_TYPES.silent) {
66
- return;
67
- }
68
-
69
- console.error(msg);
70
-
71
- if (error) {
72
- console.error(error.message);
73
- }
74
- };
75
-
76
- export const logErrorAndExit = (msg, error) => {
77
- console.error(msg);
78
-
79
- if (error) {
80
- console.error(error.message);
81
- }
82
-
83
- process.exit(ERROR_STATUSES.general);
84
- };
1
+ /* eslint-disable no-console */
2
+
3
+ export const ERROR_STATUSES = {
4
+ general: 1,
5
+ nothingDownloaded: 2,
6
+ completedWithErrors: 3,
7
+ };
8
+
9
+ export const LOG_LEVEL_TYPES = {
10
+ debug: "debug",
11
+ quiet: "quiet",
12
+ silent: "silent",
13
+ static: "static",
14
+ };
15
+
16
+ export const LOG_LEVELS = {
17
+ debug: 0,
18
+ info: 1,
19
+ important: 2,
20
+ };
21
+
22
+ export const getShouldOutputProgressIndicator = () => {
23
+ return (
24
+ process.stdout.isTTY &&
25
+ process.env.LOG_LEVEL !== LOG_LEVEL_TYPES.static &&
26
+ process.env.LOG_LEVEL !== LOG_LEVEL_TYPES.quiet &&
27
+ process.env.LOG_LEVEL !== LOG_LEVEL_TYPES.silent
28
+ );
29
+ };
30
+
31
+ export const logMessage = (message = "", logLevel = 1) => {
32
+ if (
33
+ !process.env.LOG_LEVEL ||
34
+ process.env.LOG_LEVEL === LOG_LEVEL_TYPES.debug ||
35
+ process.env.LOG_LEVEL === LOG_LEVEL_TYPES.static
36
+ ) {
37
+ console.log(message);
38
+ return;
39
+ }
40
+
41
+ if (process.env.LOG_LEVEL === LOG_LEVEL_TYPES.silent) {
42
+ return;
43
+ }
44
+
45
+ if (
46
+ process.env.LOG_LEVEL === LOG_LEVEL_TYPES.quiet &&
47
+ logLevel > LOG_LEVELS.info
48
+ ) {
49
+ console.log(message);
50
+ return;
51
+ }
52
+ };
53
+
54
+ export const getLogMessageWithMarker = (marker) => {
55
+ return (message, logLevel) => {
56
+ if (marker) {
57
+ logMessage(`${marker} | ${message}`, logLevel);
58
+ } else {
59
+ logMessage(message, logLevel);
60
+ }
61
+ };
62
+ };
63
+
64
+ export const logError = (msg, error) => {
65
+ if (process.env.LOG_LEVEL === LOG_LEVEL_TYPES.silent) {
66
+ return;
67
+ }
68
+
69
+ console.error(msg);
70
+
71
+ if (error) {
72
+ console.error(error.message);
73
+ }
74
+ };
75
+
76
+ export const logErrorAndExit = (msg, error) => {
77
+ console.error(msg);
78
+
79
+ if (error) {
80
+ console.error(error.message);
81
+ }
82
+
83
+ process.exit(ERROR_STATUSES.general);
84
+ };