podverse-external-services 5.1.1-alpha.4 → 5.1.1-alpha.6
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.
|
@@ -10,11 +10,15 @@ export declare class PodcastIndexService {
|
|
|
10
10
|
secretKey: string;
|
|
11
11
|
constructor({ authKey, baseUrl, secretKey }: Constructor);
|
|
12
12
|
podcastIndexAPIRequest: (url: string, config?: any) => Promise<any>;
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
13
|
+
deadFeedsDownloadAndExtractCSV: () => Promise<any[]>;
|
|
14
|
+
podcastGetByGuid: (podcastGuid: string) => Promise<PodcastByGuidResponse | null>;
|
|
15
|
+
recentGetData: () => Promise<any[]>;
|
|
16
|
+
trendingGetPodcasts: (max?: number, since?: number, lang?: string, cat?: string) => Promise<{
|
|
17
|
+
feeds: any[];
|
|
18
|
+
nextSince?: number;
|
|
19
|
+
}>;
|
|
20
|
+
valueGetByPodcastIds: () => Promise<number[]>;
|
|
21
|
+
valueGetByPodcastIdsRecursively: (accumulatedPodcastIndexIds: number[], startAt?: number) => Promise<number[]>;
|
|
18
22
|
}
|
|
19
23
|
export {};
|
|
20
24
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/services/podcast-index/index.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAG9D,KAAK,WAAW,GAAG;IACjB,OAAO,EAAE,MAAM,CAAA;IACf,OAAO,EAAE,MAAM,CAAA;IACf,SAAS,EAAE,MAAM,CAAA;CAClB,CAAA;AASD,qBAAa,mBAAmB;IACtB,OAAO,EAAE,MAAM,CAAA;IACf,OAAO,EAAE,MAAM,CAAA;IACf,SAAS,EAAE,MAAM,CAAA;gBAEZ,EAAE,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,EAAE,WAAW;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/services/podcast-index/index.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAG9D,KAAK,WAAW,GAAG;IACjB,OAAO,EAAE,MAAM,CAAA;IACf,OAAO,EAAE,MAAM,CAAA;IACf,SAAS,EAAE,MAAM,CAAA;CAClB,CAAA;AASD,qBAAa,mBAAmB;IACtB,OAAO,EAAE,MAAM,CAAA;IACf,OAAO,EAAE,MAAM,CAAA;IACf,SAAS,EAAE,MAAM,CAAA;gBAEZ,EAAE,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,EAAE,WAAW;IAQzD,sBAAsB,GAAU,KAAK,MAAM,EAAE,SAAS,GAAG,kBA0BxD;IAID,8BAA8B,QAAa,OAAO,CAAC,GAAG,EAAE,CAAC,CAwCxD;IAID,gBAAgB,GAAU,aAAa,MAAM,KAAG,OAAO,CAAC,qBAAqB,GAAG,IAAI,CAAC,CAYpF;IAID,aAAa,uBA6BZ;IAID,mBAAmB,GACjB,MAAK,MAAW,EAChB,QAAQ,MAAM,EACd,OAAO,MAAM,EACb,MAAM,MAAM,KACX,OAAO,CAAC;QAAE,KAAK,EAAE,GAAG,EAAE,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAoB/C;IAID,oBAAoB,QAAa,OAAO,CAAC,MAAM,EAAE,CAAC,CAMjD;IAED,+BAA+B,GAC7B,4BAA4B,MAAM,EAAE,EAAE,gBAAW,KAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAatE;CACF"}
|
|
@@ -28,39 +28,17 @@ const config_1 = require("@external-services/config");
|
|
|
28
28
|
*/
|
|
29
29
|
class PodcastIndexService {
|
|
30
30
|
constructor({ authKey, baseUrl, secretKey }) {
|
|
31
|
+
// Request handler
|
|
31
32
|
this.podcastIndexAPIRequest = (url, config) => __awaiter(this, void 0, void 0, function* () {
|
|
32
33
|
var _a, _b, _c;
|
|
33
34
|
const apiHeaderTime = Math.floor(Date.now() / 1000);
|
|
34
35
|
const hash = (0, sha1_1.default)(this.authKey + this.secretKey + apiHeaderTime).toString(enc_hex_1.default);
|
|
35
|
-
console.log('[PodcastIndex] Request details', {
|
|
36
|
-
url,
|
|
37
|
-
apiHeaderTime,
|
|
38
|
-
authKey: this.authKey,
|
|
39
|
-
baseUrl: this.baseUrl,
|
|
40
|
-
secretKeyPresent: !!this.secretKey,
|
|
41
|
-
headers: {
|
|
42
|
-
'X-Auth-Key': this.authKey,
|
|
43
|
-
'X-Auth-Date': apiHeaderTime,
|
|
44
|
-
Authorization: hash
|
|
45
|
-
},
|
|
46
|
-
config
|
|
47
|
-
});
|
|
48
|
-
// Log system time for drift debugging
|
|
49
|
-
console.log('[PodcastIndex] System time (UTC)', {
|
|
50
|
-
iso: new Date().toISOString(),
|
|
51
|
-
epoch: Math.floor(Date.now() / 1000)
|
|
52
|
-
});
|
|
53
36
|
try {
|
|
54
37
|
const response = yield (0, podverse_helpers_1.request)(url, Object.assign({ headers: {
|
|
55
38
|
'X-Auth-Key': this.authKey,
|
|
56
39
|
'X-Auth-Date': apiHeaderTime,
|
|
57
40
|
Authorization: hash
|
|
58
41
|
} }, config));
|
|
59
|
-
console.log('[PodcastIndex] Response received', {
|
|
60
|
-
status: response === null || response === void 0 ? void 0 : response.status,
|
|
61
|
-
statusText: response === null || response === void 0 ? void 0 : response.statusText,
|
|
62
|
-
dataKeys: (response === null || response === void 0 ? void 0 : response.data) ? Object.keys(response.data) : undefined
|
|
63
|
-
});
|
|
64
42
|
return response;
|
|
65
43
|
}
|
|
66
44
|
catch (error) {
|
|
@@ -75,8 +53,55 @@ class PodcastIndexService {
|
|
|
75
53
|
throw error;
|
|
76
54
|
}
|
|
77
55
|
});
|
|
78
|
-
|
|
79
|
-
|
|
56
|
+
// Dead Feeds
|
|
57
|
+
this.deadFeedsDownloadAndExtractCSV = () => __awaiter(this, void 0, void 0, function* () {
|
|
58
|
+
const url = 'https://public.podcastindex.org/podcastindex_dead_feeds.csv';
|
|
59
|
+
const tmpDir = path_1.default.join(__dirname, 'tmp');
|
|
60
|
+
const filePath = path_1.default.join(tmpDir, 'podcastindex_dead_feeds.csv');
|
|
61
|
+
if (!fs_1.default.existsSync(tmpDir)) {
|
|
62
|
+
fs_1.default.mkdirSync(tmpDir);
|
|
63
|
+
}
|
|
64
|
+
const data = yield this.podcastIndexAPIRequest(url, { responseType: 'stream' });
|
|
65
|
+
const writer = fs_1.default.createWriteStream(filePath);
|
|
66
|
+
data.pipe(writer);
|
|
67
|
+
yield new Promise((resolve, reject) => {
|
|
68
|
+
writer.on('finish', () => resolve());
|
|
69
|
+
writer.on('error', reject);
|
|
70
|
+
});
|
|
71
|
+
const results = [];
|
|
72
|
+
yield new Promise((resolve, reject) => {
|
|
73
|
+
fs_1.default.createReadStream(filePath)
|
|
74
|
+
.pipe((0, csv_parser_1.default)())
|
|
75
|
+
.on('data', (data) => results.push(data))
|
|
76
|
+
.on('end', resolve)
|
|
77
|
+
.on('error', reject);
|
|
78
|
+
});
|
|
79
|
+
fs_1.default.unlinkSync(filePath);
|
|
80
|
+
const parsedResults = results.map((row) => {
|
|
81
|
+
const [id, duplicateOf] = Object.values(row).map((value) => value.trim());
|
|
82
|
+
return {
|
|
83
|
+
podcast_index_id: parseInt(id, 10),
|
|
84
|
+
duplicateOf: duplicateOf ? parseInt(duplicateOf, 10) : null
|
|
85
|
+
};
|
|
86
|
+
});
|
|
87
|
+
return parsedResults;
|
|
88
|
+
});
|
|
89
|
+
// Podcast
|
|
90
|
+
this.podcastGetByGuid = (podcastGuid) => __awaiter(this, void 0, void 0, function* () {
|
|
91
|
+
const url = `${this.baseUrl}/podcasts/byguid?guid=${podcastGuid}`;
|
|
92
|
+
let podcastIndexPodcast = null;
|
|
93
|
+
try {
|
|
94
|
+
const data = yield this.podcastIndexAPIRequest(url);
|
|
95
|
+
podcastIndexPodcast = data;
|
|
96
|
+
}
|
|
97
|
+
catch (error) {
|
|
98
|
+
// assume a 404
|
|
99
|
+
}
|
|
100
|
+
return podcastIndexPodcast || null;
|
|
101
|
+
});
|
|
102
|
+
// Recent
|
|
103
|
+
this.recentGetData = () => __awaiter(this, void 0, void 0, function* () {
|
|
104
|
+
podverse_helpers_1.logger.info('recentGetData beginning...');
|
|
80
105
|
const currentTimeInSeconds = Math.floor(Date.now() / 1000);
|
|
81
106
|
const sinceRange = config_1.config.podcastIndex.recentlyUpdatedDataInterval;
|
|
82
107
|
const sinceTimeInSeconds = currentTimeInSeconds - sinceRange;
|
|
@@ -100,67 +125,44 @@ class PodcastIndexService {
|
|
|
100
125
|
});
|
|
101
126
|
return fetchData(sinceTimeInSeconds);
|
|
102
127
|
});
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
128
|
+
// Trending
|
|
129
|
+
this.trendingGetPodcasts = (...args_1) => __awaiter(this, [...args_1], void 0, function* (max = 25, since, lang, cat) {
|
|
130
|
+
const safeMax = Math.min(max, 1000);
|
|
131
|
+
let url = `${this.baseUrl}/podcasts/trending?max=${safeMax}`;
|
|
132
|
+
if (since) {
|
|
133
|
+
url += `&since=${since}`;
|
|
109
134
|
}
|
|
110
|
-
|
|
111
|
-
|
|
135
|
+
if (lang) {
|
|
136
|
+
url += `&lang=${encodeURIComponent(lang)}`;
|
|
112
137
|
}
|
|
113
|
-
|
|
138
|
+
if (cat) {
|
|
139
|
+
url += `&cat=${encodeURIComponent(cat)}`;
|
|
140
|
+
}
|
|
141
|
+
podverse_helpers_1.logger.info(`[PodcastIndex] Fetching trending feeds (max: ${safeMax}, since: ${since}, lang: ${lang}, cat: ${cat})`);
|
|
142
|
+
const response = yield this.podcastIndexAPIRequest(url);
|
|
143
|
+
return {
|
|
144
|
+
feeds: response.feeds || [],
|
|
145
|
+
nextSince: response.nextSince
|
|
146
|
+
};
|
|
147
|
+
});
|
|
148
|
+
// Value
|
|
149
|
+
this.valueGetByPodcastIds = () => __awaiter(this, void 0, void 0, function* () {
|
|
150
|
+
const accumulatedPodcastIndexIds = [];
|
|
151
|
+
const nextStartAt = 1;
|
|
152
|
+
const podcastIndexIds = yield this.valueGetByPodcastIdsRecursively(accumulatedPodcastIndexIds, nextStartAt);
|
|
153
|
+
return podcastIndexIds;
|
|
114
154
|
});
|
|
115
|
-
this.
|
|
155
|
+
this.valueGetByPodcastIdsRecursively = (accumulatedPodcastIndexIds_1, ...args_1) => __awaiter(this, [accumulatedPodcastIndexIds_1, ...args_1], void 0, function* (accumulatedPodcastIndexIds, startAt = 1) {
|
|
116
156
|
const url = `${this.baseUrl}/podcasts/bytag?podcast-valueTimeSplit=true&max=5000&start_at=${startAt}`;
|
|
117
157
|
const data = yield this.podcastIndexAPIRequest(url);
|
|
118
158
|
for (const feed of data.feeds) {
|
|
119
159
|
accumulatedPodcastIndexIds.push(feed.id);
|
|
120
160
|
}
|
|
121
161
|
if (data.nextStartAt) {
|
|
122
|
-
return yield this.
|
|
162
|
+
return yield this.valueGetByPodcastIdsRecursively(accumulatedPodcastIndexIds, data.nextStartAt);
|
|
123
163
|
}
|
|
124
164
|
return accumulatedPodcastIndexIds;
|
|
125
165
|
});
|
|
126
|
-
this.getValueTagEnabledPodcastIds = () => __awaiter(this, void 0, void 0, function* () {
|
|
127
|
-
const accumulatedPodcastIndexIds = [];
|
|
128
|
-
const nextStartAt = 1;
|
|
129
|
-
const podcastIndexIds = yield this.getValueTagEnabledPodcastIdsRecursively(accumulatedPodcastIndexIds, nextStartAt);
|
|
130
|
-
return podcastIndexIds;
|
|
131
|
-
});
|
|
132
|
-
this.downloadAndExtractCSV = () => __awaiter(this, void 0, void 0, function* () {
|
|
133
|
-
const url = 'https://public.podcastindex.org/podcastindex_dead_feeds.csv';
|
|
134
|
-
const tmpDir = path_1.default.join(__dirname, 'tmp');
|
|
135
|
-
const filePath = path_1.default.join(tmpDir, 'podcastindex_dead_feeds.csv');
|
|
136
|
-
if (!fs_1.default.existsSync(tmpDir)) {
|
|
137
|
-
fs_1.default.mkdirSync(tmpDir);
|
|
138
|
-
}
|
|
139
|
-
const data = yield this.podcastIndexAPIRequest(url, { responseType: 'stream' });
|
|
140
|
-
const writer = fs_1.default.createWriteStream(filePath);
|
|
141
|
-
data.pipe(writer);
|
|
142
|
-
yield new Promise((resolve, reject) => {
|
|
143
|
-
writer.on('finish', () => resolve());
|
|
144
|
-
writer.on('error', reject);
|
|
145
|
-
});
|
|
146
|
-
const results = [];
|
|
147
|
-
yield new Promise((resolve, reject) => {
|
|
148
|
-
fs_1.default.createReadStream(filePath)
|
|
149
|
-
.pipe((0, csv_parser_1.default)())
|
|
150
|
-
.on('data', (data) => results.push(data))
|
|
151
|
-
.on('end', resolve)
|
|
152
|
-
.on('error', reject);
|
|
153
|
-
});
|
|
154
|
-
fs_1.default.unlinkSync(filePath);
|
|
155
|
-
const parsedResults = results.map((row) => {
|
|
156
|
-
const [id, duplicateOf] = Object.values(row).map((value) => value.trim());
|
|
157
|
-
return {
|
|
158
|
-
podcast_index_id: parseInt(id, 10),
|
|
159
|
-
duplicateOf: duplicateOf ? parseInt(duplicateOf, 10) : null
|
|
160
|
-
};
|
|
161
|
-
});
|
|
162
|
-
return parsedResults;
|
|
163
|
-
});
|
|
164
166
|
this.authKey = authKey;
|
|
165
167
|
this.baseUrl = baseUrl;
|
|
166
168
|
this.secretKey = secretKey;
|