@rapthi/podca-ts 1.0.1 → 1.0.4
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/dist/index.cjs +160 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +118 -0
- package/dist/index.d.ts +118 -5
- package/dist/index.js +157 -2
- package/dist/index.js.map +1 -1
- package/package.json +5 -3
- package/dist/index.d.ts.map +0 -1
- package/dist/itunes-search/iTunesSearch.d.ts +0 -6
- package/dist/itunes-search/iTunesSearch.d.ts.map +0 -1
- package/dist/itunes-search/iTunesSearch.js +0 -7
- package/dist/itunes-search/iTunesSearch.js.map +0 -1
- package/dist/itunes-search/itunes-search-options.d.ts +0 -15
- package/dist/itunes-search/itunes-search-options.d.ts.map +0 -1
- package/dist/itunes-search/itunes-search-options.js +0 -1
- package/dist/itunes-search/itunes-search-options.js.map +0 -1
- package/dist/itunes-search/itunes-search-result.d.ts +0 -41
- package/dist/itunes-search/itunes-search-result.d.ts.map +0 -1
- package/dist/itunes-search/itunes-search-result.js +0 -1
- package/dist/itunes-search/itunes-search.d.ts +0 -9
- package/dist/itunes-search/itunes-search.d.ts.map +0 -1
- package/dist/itunes-search/itunes-search.js +0 -30
- package/dist/itunes-search/itunes-search.spec.d.ts +0 -2
- package/dist/itunes-search/itunes-search.spec.d.ts.map +0 -1
- package/dist/itunes-search/itunes-search.spec.js +0 -46
- package/dist/podcast/podcast.d.ts +0 -55
- package/dist/podcast/podcast.d.ts.map +0 -1
- package/dist/podcast/podcast.js +0 -120
- package/dist/podcast/podcast.spec.d.ts +0 -2
- package/dist/podcast/podcast.spec.d.ts.map +0 -1
- package/dist/podcast/podcast.spec.js +0 -364
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var podcastParser = require('@sesamy/podcast-parser');
|
|
4
|
+
|
|
5
|
+
// src/itunes-search/itunes-search.ts
|
|
6
|
+
var _ItunesSearch = class _ItunesSearch {
|
|
7
|
+
constructor() {
|
|
8
|
+
}
|
|
9
|
+
async search(option) {
|
|
10
|
+
const searchUrlWithParams = this.buildSearchUrl(option);
|
|
11
|
+
try {
|
|
12
|
+
const response = await fetch(searchUrlWithParams);
|
|
13
|
+
if (!response.ok) {
|
|
14
|
+
throw new Error(
|
|
15
|
+
`Failed to fetch data from iTunes Search API: ${response.status}}`
|
|
16
|
+
);
|
|
17
|
+
}
|
|
18
|
+
return await response.json();
|
|
19
|
+
} catch (error) {
|
|
20
|
+
if (error instanceof Error) {
|
|
21
|
+
throw new Error(`Fetch failed: ${error.message}`);
|
|
22
|
+
}
|
|
23
|
+
throw error;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
buildSearchUrl(option) {
|
|
27
|
+
const url = new URL(_ItunesSearch.ITUNES_SEARCH_URL);
|
|
28
|
+
for (const [key, value] of Object.entries(option)) {
|
|
29
|
+
if (value !== void 0) {
|
|
30
|
+
url.searchParams.append(key, String(value));
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return url.toString();
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
_ItunesSearch.ITUNES_SEARCH_URL = "https://itunes.apple.com/search";
|
|
37
|
+
var ItunesSearch = _ItunesSearch;
|
|
38
|
+
var PodcastLoader = class {
|
|
39
|
+
constructor() {
|
|
40
|
+
this.FETCH_TIMEOUT_MS = 3e4;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Fetches and parses a podcast feed from the given URL
|
|
44
|
+
* @param feedUrl - The URL of the podcast feed
|
|
45
|
+
* @returns A Promise resolving to the parsed Podcast
|
|
46
|
+
* @throws Error if the feed is invalid or the fetch fails
|
|
47
|
+
*/
|
|
48
|
+
async getPodcastFromFeed(feedUrl) {
|
|
49
|
+
this.validateUrl(feedUrl);
|
|
50
|
+
const controller = new AbortController();
|
|
51
|
+
const timeoutId = setTimeout(() => controller.abort(), this.FETCH_TIMEOUT_MS);
|
|
52
|
+
try {
|
|
53
|
+
const response = await fetch(feedUrl, { signal: controller.signal });
|
|
54
|
+
if (!response.ok) {
|
|
55
|
+
throw new Error(
|
|
56
|
+
`Failed to fetch podcast feed: ${response.status} ${response.statusText}`
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
const xmlString = await response.text();
|
|
60
|
+
if (!xmlString.trim()) {
|
|
61
|
+
throw new Error("Podcast feed is empty");
|
|
62
|
+
}
|
|
63
|
+
const podcastFromXml = await podcastParser.parseFeedToJson(xmlString);
|
|
64
|
+
const channel = this.extractChannel(podcastFromXml);
|
|
65
|
+
this.validateChannel(channel);
|
|
66
|
+
return this.mapChannel(channel);
|
|
67
|
+
} catch (error) {
|
|
68
|
+
if (error instanceof Error && error.name === "AbortError") {
|
|
69
|
+
throw new Error(`Podcast feed request timeout after ${this.FETCH_TIMEOUT_MS}ms`);
|
|
70
|
+
}
|
|
71
|
+
if (error instanceof Error) {
|
|
72
|
+
throw new Error(`Failed to load podcast feed: ${error.message}`);
|
|
73
|
+
}
|
|
74
|
+
throw error;
|
|
75
|
+
} finally {
|
|
76
|
+
clearTimeout(timeoutId);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
validateUrl(feedUrl) {
|
|
80
|
+
try {
|
|
81
|
+
new URL(feedUrl);
|
|
82
|
+
} catch {
|
|
83
|
+
throw new Error(`Invalid feed URL: ${feedUrl}`);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
extractChannel(podcastFromXml) {
|
|
87
|
+
const channel = podcastFromXml?.rss?.channel;
|
|
88
|
+
if (!channel) {
|
|
89
|
+
throw new Error("Invalid podcast feed: missing channel data");
|
|
90
|
+
}
|
|
91
|
+
return channel;
|
|
92
|
+
}
|
|
93
|
+
validateChannel(channel) {
|
|
94
|
+
if (!channel.title) {
|
|
95
|
+
throw new Error('Invalid podcast feed: missing required field "title"');
|
|
96
|
+
}
|
|
97
|
+
if (!channel.link) {
|
|
98
|
+
throw new Error('Invalid podcast feed: missing required field "link"');
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
mapChannel(channel) {
|
|
102
|
+
return {
|
|
103
|
+
title: channel.title,
|
|
104
|
+
description: channel.description,
|
|
105
|
+
link: channel.link,
|
|
106
|
+
language: channel.language,
|
|
107
|
+
categories: this.mapCategories(channel["itunes:category"]),
|
|
108
|
+
explicit: channel["itunes:explicit"] === "true",
|
|
109
|
+
imageUrl: channel["itunes:image"]?.["@_href"],
|
|
110
|
+
author: channel["itunes:author"],
|
|
111
|
+
copyright: channel["copyright"],
|
|
112
|
+
fundingUrl: channel["podcast:funding"]?.["@_url"],
|
|
113
|
+
type: channel["itunes:type"],
|
|
114
|
+
episodes: this.mapEpisodes(channel.item)
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
mapCategories(categories) {
|
|
118
|
+
if (!categories) {
|
|
119
|
+
return [];
|
|
120
|
+
}
|
|
121
|
+
return categories.flatMap((category) => [
|
|
122
|
+
{ name: category["@_text"] },
|
|
123
|
+
...category["itunes:category"]?.map((sub) => ({ name: sub["@_text"] })) || []
|
|
124
|
+
]);
|
|
125
|
+
}
|
|
126
|
+
mapEpisodes(items) {
|
|
127
|
+
if (!items) {
|
|
128
|
+
return [];
|
|
129
|
+
}
|
|
130
|
+
return items.map((item) => ({
|
|
131
|
+
title: item.title,
|
|
132
|
+
enclosure: this.mapEnclosure(item.enclosure),
|
|
133
|
+
guid: item.guid["#text"],
|
|
134
|
+
linkUrl: item.link,
|
|
135
|
+
pubDate: item.pubDate,
|
|
136
|
+
description: item.description,
|
|
137
|
+
durationInSeconds: item["itunes:duration"],
|
|
138
|
+
imageUrl: item["itunes:image"]?.["@_href"],
|
|
139
|
+
explicit: item["itunes:explicit"] === "yes",
|
|
140
|
+
number: item["itunes:episode"],
|
|
141
|
+
season: item["itunes:season"],
|
|
142
|
+
type: item["itunes:episodeType"]
|
|
143
|
+
}));
|
|
144
|
+
}
|
|
145
|
+
mapEnclosure(enclosure) {
|
|
146
|
+
if (!enclosure?.[0]) {
|
|
147
|
+
return void 0;
|
|
148
|
+
}
|
|
149
|
+
return {
|
|
150
|
+
url: enclosure[0]["@_url"],
|
|
151
|
+
type: enclosure[0]["@_type"],
|
|
152
|
+
length: enclosure[0]["@_length"]
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
exports.ItunesSearch = ItunesSearch;
|
|
158
|
+
exports.PodcastLoader = PodcastLoader;
|
|
159
|
+
//# sourceMappingURL=index.cjs.map
|
|
160
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/itunes-search/itunes-search.ts","../src/podcast/podcast.ts"],"names":["parseFeedToJson"],"mappings":";;;;;AAGO,IAAM,aAAA,GAAN,MAAM,aAAA,CAAa;AAAA,EAGxB,WAAA,GAAc;AAAA,EAAC;AAAA,EAEf,MAAM,OAA4B,MAAA,EAA8D;AAC9F,IAAA,MAAM,mBAAA,GAAsB,IAAA,CAAK,cAAA,CAAe,MAAM,CAAA;AAEtD,IAAA,IAAI;AACF,MAAA,MAAM,QAAA,GAAW,MAAM,KAAA,CAAM,mBAAmB,CAAA;AAEhD,MAAA,IAAI,CAAC,SAAS,EAAA,EAAI;AAEhB,QAAA,MAAM,IAAI,KAAA;AAAA,UACR,CAAA,6CAAA,EAAgD,SAAS,MAAM,CAAA,CAAA;AAAA,SACjE;AAAA,MACF;AAEA,MAAA,OAAO,MAAM,SAAS,IAAA,EAAK;AAAA,IAC7B,SAAS,KAAA,EAAO;AACd,MAAA,IAAI,iBAAiB,KAAA,EAAO;AAC1B,QAAA,MAAM,IAAI,KAAA,CAAM,CAAA,cAAA,EAAiB,KAAA,CAAM,OAAO,CAAA,CAAE,CAAA;AAAA,MAClD;AAEA,MAAA,MAAM,KAAA;AAAA,IACR;AAAA,EACF;AAAA,EAEQ,eAAoC,MAAA,EAAuC;AACjF,IAAA,MAAM,GAAA,GAAM,IAAI,GAAA,CAAI,aAAA,CAAa,iBAAiB,CAAA;AAElD,IAAA,KAAA,MAAW,CAAC,GAAA,EAAK,KAAK,KAAK,MAAA,CAAO,OAAA,CAAQ,MAAM,CAAA,EAAG;AACjD,MAAA,IAAI,UAAU,MAAA,EAAW;AACvB,QAAA,GAAA,CAAI,YAAA,CAAa,MAAA,CAAO,GAAA,EAAK,MAAA,CAAO,KAAK,CAAC,CAAA;AAAA,MAC5C;AAAA,IACF;AAEA,IAAA,OAAO,IAAI,QAAA,EAAS;AAAA,EACtB;AACF,CAAA;AAvCa,aAAA,CACa,iBAAA,GAAoB,iCAAA;AADvC,IAAM,YAAA,GAAN;AC8CA,IAAM,gBAAN,MAAoB;AAAA,EAApB,WAAA,GAAA;AACL,IAAA,IAAA,CAAiB,gBAAA,GAAmB,GAAA;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQpC,MAAM,mBAAmB,OAAA,EAAmC;AAC1D,IAAA,IAAA,CAAK,YAAY,OAAO,CAAA;AAExB,IAAA,MAAM,UAAA,GAAa,IAAI,eAAA,EAAgB;AACvC,IAAA,MAAM,YAAY,UAAA,CAAW,MAAM,WAAW,KAAA,EAAM,EAAG,KAAK,gBAAgB,CAAA;AAE5E,IAAA,IAAI;AACF,MAAA,MAAM,QAAA,GAAW,MAAM,KAAA,CAAM,OAAA,EAAS,EAAE,MAAA,EAAQ,UAAA,CAAW,QAAQ,CAAA;AAEnE,MAAA,IAAI,CAAC,SAAS,EAAA,EAAI;AAChB,QAAA,MAAM,IAAI,KAAA;AAAA,UACR,CAAA,8BAAA,EAAiC,QAAA,CAAS,MAAM,CAAA,CAAA,EAAI,SAAS,UAAU,CAAA;AAAA,SACzE;AAAA,MACF;AAEA,MAAA,MAAM,SAAA,GAAY,MAAM,QAAA,CAAS,IAAA,EAAK;AAEtC,MAAA,IAAI,CAAC,SAAA,CAAU,IAAA,EAAK,EAAG;AACrB,QAAA,MAAM,IAAI,MAAM,uBAAuB,CAAA;AAAA,MACzC;AAEA,MAAA,MAAM,cAAA,GAAiB,MAAMA,6BAAA,CAAgB,SAAS,CAAA;AACtD,MAAA,MAAM,OAAA,GAAU,IAAA,CAAK,cAAA,CAAe,cAAc,CAAA;AAElD,MAAA,IAAA,CAAK,gBAAgB,OAAO,CAAA;AAE5B,MAAA,OAAO,IAAA,CAAK,WAAW,OAAO,CAAA;AAAA,IAChC,SAAS,KAAA,EAAO;AACd,MAAA,IAAI,KAAA,YAAiB,KAAA,IAAS,KAAA,CAAM,IAAA,KAAS,YAAA,EAAc;AACzD,QAAA,MAAM,IAAI,KAAA,CAAM,CAAA,mCAAA,EAAsC,IAAA,CAAK,gBAAgB,CAAA,EAAA,CAAI,CAAA;AAAA,MACjF;AAEA,MAAA,IAAI,iBAAiB,KAAA,EAAO;AAC1B,QAAA,MAAM,IAAI,KAAA,CAAM,CAAA,6BAAA,EAAgC,KAAA,CAAM,OAAO,CAAA,CAAE,CAAA;AAAA,MACjE;AAEA,MAAA,MAAM,KAAA;AAAA,IACR,CAAA,SAAE;AACA,MAAA,YAAA,CAAa,SAAS,CAAA;AAAA,IACxB;AAAA,EACF;AAAA,EAEQ,YAAY,OAAA,EAAuB;AACzC,IAAA,IAAI;AACF,MAAA,IAAI,IAAI,OAAO,CAAA;AAAA,IACjB,CAAA,CAAA,MAAQ;AACN,MAAA,MAAM,IAAI,KAAA,CAAM,CAAA,kBAAA,EAAqB,OAAO,CAAA,CAAE,CAAA;AAAA,IAChD;AAAA,EACF;AAAA,EAEQ,eAAe,cAAA,EAA4C;AACjE,IAAA,MAAM,OAAA,GAAU,gBAAgB,GAAA,EAAK,OAAA;AAErC,IAAA,IAAI,CAAC,OAAA,EAAS;AACZ,MAAA,MAAM,IAAI,MAAM,4CAA4C,CAAA;AAAA,IAC9D;AAEA,IAAA,OAAO,OAAA;AAAA,EACT;AAAA,EAEQ,gBAAgB,OAAA,EAA2B;AACjD,IAAA,IAAI,CAAC,QAAQ,KAAA,EAAO;AAClB,MAAA,MAAM,IAAI,MAAM,sDAAsD,CAAA;AAAA,IACxE;AAEA,IAAA,IAAI,CAAC,QAAQ,IAAA,EAAM;AACjB,MAAA,MAAM,IAAI,MAAM,qDAAqD,CAAA;AAAA,IACvE;AAAA,EACF;AAAA,EAEQ,WAAW,OAAA,EAA8B;AAC/C,IAAA,OAAO;AAAA,MACL,OAAO,OAAA,CAAQ,KAAA;AAAA,MACf,aAAa,OAAA,CAAQ,WAAA;AAAA,MACrB,MAAM,OAAA,CAAQ,IAAA;AAAA,MACd,UAAU,OAAA,CAAQ,QAAA;AAAA,MAClB,UAAA,EAAY,IAAA,CAAK,aAAA,CAAc,OAAA,CAAQ,iBAAiB,CAAC,CAAA;AAAA,MACzD,QAAA,EAAU,OAAA,CAAQ,iBAAiB,CAAA,KAAM,MAAA;AAAA,MACzC,QAAA,EAAU,OAAA,CAAQ,cAAc,CAAA,GAAI,QAAQ,CAAA;AAAA,MAC5C,MAAA,EAAQ,QAAQ,eAAe,CAAA;AAAA,MAC/B,SAAA,EAAW,QAAQ,WAAW,CAAA;AAAA,MAC9B,UAAA,EAAY,OAAA,CAAQ,iBAAiB,CAAA,GAAI,OAAO,CAAA;AAAA,MAChD,IAAA,EAAM,QAAQ,aAAa,CAAA;AAAA,MAC3B,QAAA,EAAU,IAAA,CAAK,WAAA,CAAY,OAAA,CAAQ,IAAI;AAAA,KACzC;AAAA,EACF;AAAA,EAEQ,cAAc,UAAA,EAAmD;AACvE,IAAA,IAAI,CAAC,UAAA,EAAY;AAAC,MAAA,OAAO,EAAC;AAAA,IAAE;AAE5B,IAAA,OAAO,UAAA,CAAW,OAAA,CAAQ,CAAC,QAAA,KAAa;AAAA,MACtC,EAAE,IAAA,EAAM,QAAA,CAAS,QAAQ,CAAA,EAAE;AAAA,MAC3B,GAAI,QAAA,CAAS,iBAAiB,CAAA,EAAG,IAAI,CAAC,GAAA,MAAS,EAAE,IAAA,EAAM,GAAA,CAAI,QAAQ,CAAA,EAAE,CAAE,KAAK;AAAC,KAC9E,CAAA;AAAA,EACH;AAAA,EAEQ,YAAY,KAAA,EAA4C;AAC9D,IAAA,IAAI,CAAC,KAAA,EAAO;AAAC,MAAA,OAAO,EAAC;AAAA,IAAE;AAEvB,IAAA,OAAO,KAAA,CAAM,GAAA,CAAI,CAAC,IAAA,MAAU;AAAA,MAC1B,OAAO,IAAA,CAAK,KAAA;AAAA,MACZ,SAAA,EAAW,IAAA,CAAK,YAAA,CAAa,IAAA,CAAK,SAAS,CAAA;AAAA,MAC3C,IAAA,EAAM,IAAA,CAAK,IAAA,CAAK,OAAO,CAAA;AAAA,MACvB,SAAS,IAAA,CAAK,IAAA;AAAA,MACd,SAAS,IAAA,CAAK,OAAA;AAAA,MACd,aAAa,IAAA,CAAK,WAAA;AAAA,MAClB,iBAAA,EAAmB,KAAK,iBAAiB,CAAA;AAAA,MACzC,QAAA,EAAU,IAAA,CAAK,cAAc,CAAA,GAAI,QAAQ,CAAA;AAAA,MACzC,QAAA,EAAU,IAAA,CAAK,iBAAiB,CAAA,KAAM,KAAA;AAAA,MACtC,MAAA,EAAQ,KAAK,gBAAgB,CAAA;AAAA,MAC7B,MAAA,EAAQ,KAAK,eAAe,CAAA;AAAA,MAC5B,IAAA,EAAM,KAAK,oBAAoB;AAAA,KACjC,CAAE,CAAA;AAAA,EACJ;AAAA,EAEQ,aAAa,SAAA,EAA8D;AACjF,IAAA,IAAI,CAAC,SAAA,GAAY,CAAC,CAAA,EAAG;AAAC,MAAA,OAAO,MAAA;AAAA,IAAU;AAEvC,IAAA,OAAO;AAAA,MACL,GAAA,EAAK,SAAA,CAAU,CAAC,CAAA,CAAE,OAAO,CAAA;AAAA,MACzB,IAAA,EAAM,SAAA,CAAU,CAAC,CAAA,CAAE,QAAQ,CAAA;AAAA,MAC3B,MAAA,EAAQ,SAAA,CAAU,CAAC,CAAA,CAAE,UAAU;AAAA,KACjC;AAAA,EACF;AACF","file":"index.cjs","sourcesContent":["import type { ITunesSearchParams, MediaType } from './itunes-search-options.js';\nimport type { ITunesSearchResponse } from './itunes-search-result.js';\n\nexport class ItunesSearch {\n private static readonly ITUNES_SEARCH_URL = 'https://itunes.apple.com/search';\n\n constructor() {}\n\n async search<T extends MediaType>(option: ITunesSearchParams<T>): Promise<ITunesSearchResponse> {\n const searchUrlWithParams = this.buildSearchUrl(option);\n\n try {\n const response = await fetch(searchUrlWithParams);\n\n if (!response.ok) {\n // noinspection ExceptionCaughtLocallyJS\n throw new Error(\n `Failed to fetch data from iTunes Search API: ${response.status}}`,\n );\n }\n\n return await response.json() as ITunesSearchResponse;\n } catch (error) {\n if (error instanceof Error) {\n throw new Error(`Fetch failed: ${error.message}`);\n }\n\n throw error;\n }\n }\n\n private buildSearchUrl<T extends MediaType>(option: ITunesSearchParams<T>): string {\n const url = new URL(ItunesSearch.ITUNES_SEARCH_URL);\n\n for (const [key, value] of Object.entries(option)) {\n if (value !== undefined) {\n url.searchParams.append(key, String(value));\n }\n }\n\n return url.toString();\n }\n}\n","import { parseFeedToJson } from '@sesamy/podcast-parser';\n\ninterface RawCategory {\n '@_text': string;\n 'itunes:category'?: RawCategory[];\n}\n\ninterface RawEnclosure {\n '@_url': string;\n '@_type': string;\n '@_length': string;\n}\n\ninterface RawEpisode {\n title?: string;\n guid: { '#text': string };\n link?: string;\n pubDate?: string;\n description?: string;\n 'itunes:duration'?: string | number;\n 'itunes:image'?: { '@_href': string };\n 'itunes:explicit'?: 'yes' | 'no';\n 'itunes:episode'?: number;\n 'itunes:season'?: number;\n 'itunes:episodeType'?: string;\n enclosure?: RawEnclosure[];\n}\n\ninterface RawChannel {\n title: string;\n description?: string;\n link: string;\n language?: string;\n 'itunes:category'?: RawCategory[];\n 'itunes:explicit'?: 'true' | 'false';\n 'itunes:image'?: { '@_href': string };\n 'itunes:author'?: string;\n copyright?: string;\n 'podcast:funding'?: { '@_url': string };\n 'itunes:type'?: string;\n item?: RawEpisode[];\n}\n\ninterface RawPodcastFeed {\n rss?: {\n channel?: RawChannel;\n };\n}\n\nexport class PodcastLoader {\n private readonly FETCH_TIMEOUT_MS = 30000;\n\n /**\n * Fetches and parses a podcast feed from the given URL\n * @param feedUrl - The URL of the podcast feed\n * @returns A Promise resolving to the parsed Podcast\n * @throws Error if the feed is invalid or the fetch fails\n */\n async getPodcastFromFeed(feedUrl: string): Promise<Podcast> {\n this.validateUrl(feedUrl);\n\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), this.FETCH_TIMEOUT_MS);\n\n try {\n const response = await fetch(feedUrl, { signal: controller.signal });\n\n if (!response.ok) {\n throw new Error(\n `Failed to fetch podcast feed: ${response.status} ${response.statusText}`,\n );\n }\n\n const xmlString = await response.text();\n\n if (!xmlString.trim()) {\n throw new Error('Podcast feed is empty');\n }\n\n const podcastFromXml = await parseFeedToJson(xmlString) as RawPodcastFeed;\n const channel = this.extractChannel(podcastFromXml);\n\n this.validateChannel(channel);\n\n return this.mapChannel(channel);\n } catch (error) {\n if (error instanceof Error && error.name === 'AbortError') {\n throw new Error(`Podcast feed request timeout after ${this.FETCH_TIMEOUT_MS}ms`);\n }\n\n if (error instanceof Error) {\n throw new Error(`Failed to load podcast feed: ${error.message}`);\n }\n\n throw error;\n } finally {\n clearTimeout(timeoutId);\n }\n }\n\n private validateUrl(feedUrl: string): void {\n try {\n new URL(feedUrl);\n } catch {\n throw new Error(`Invalid feed URL: ${feedUrl}`);\n }\n }\n\n private extractChannel(podcastFromXml: RawPodcastFeed): RawChannel {\n const channel = podcastFromXml?.rss?.channel;\n\n if (!channel) {\n throw new Error('Invalid podcast feed: missing channel data');\n }\n\n return channel;\n }\n\n private validateChannel(channel: RawChannel): void {\n if (!channel.title) {\n throw new Error('Invalid podcast feed: missing required field \"title\"');\n }\n\n if (!channel.link) {\n throw new Error('Invalid podcast feed: missing required field \"link\"');\n }\n }\n\n private mapChannel(channel: RawChannel): Podcast {\n return {\n title: channel.title,\n description: channel.description,\n link: channel.link,\n language: channel.language,\n categories: this.mapCategories(channel['itunes:category']),\n explicit: channel['itunes:explicit'] === 'true',\n imageUrl: channel['itunes:image']?.['@_href'],\n author: channel['itunes:author'],\n copyright: channel['copyright'],\n fundingUrl: channel['podcast:funding']?.['@_url'],\n type: channel['itunes:type'],\n episodes: this.mapEpisodes(channel.item),\n };\n }\n\n private mapCategories(categories: RawCategory[] | undefined): Category[] {\n if (!categories) {return [];}\n\n return categories.flatMap((category) => [\n { name: category['@_text'] },\n ...(category['itunes:category']?.map((sub) => ({ name: sub['@_text'] })) || []),\n ]);\n }\n\n private mapEpisodes(items: RawEpisode[] | undefined): Episode[] {\n if (!items) {return [];}\n\n return items.map((item) => ({\n title: item.title,\n enclosure: this.mapEnclosure(item.enclosure),\n guid: item.guid['#text'],\n linkUrl: item.link,\n pubDate: item.pubDate,\n description: item.description,\n durationInSeconds: item['itunes:duration'],\n imageUrl: item['itunes:image']?.['@_href'],\n explicit: item['itunes:explicit'] === 'yes',\n number: item['itunes:episode'],\n season: item['itunes:season'],\n type: item['itunes:episodeType'],\n }));\n }\n\n private mapEnclosure(enclosure: RawEnclosure[] | undefined): Enclosure | undefined {\n if (!enclosure?.[0]) {return undefined;}\n\n return {\n url: enclosure[0]['@_url'],\n type: enclosure[0]['@_type'],\n length: enclosure[0]['@_length'],\n };\n }\n}\n\nexport interface Category {\n name: string;\n}\n\nexport interface Episode {\n title: string | undefined;\n enclosure: Enclosure | undefined;\n guid: string;\n linkUrl?: string;\n pubDate?: string;\n description?: string;\n durationInSeconds?: string | number | undefined;\n imageUrl?: string;\n explicit?: boolean;\n number?: number;\n season?: number;\n type?: string | undefined;\n}\n\nexport interface Enclosure {\n length: string;\n type: string;\n url: string;\n}\n\nexport interface Podcast {\n title: string;\n description: string | undefined;\n link: string;\n language: string | undefined;\n categories: Category[];\n explicit: boolean;\n imageUrl?: string;\n author?: string;\n copyright?: string;\n fundingUrl?: string;\n type?: string;\n complete?: boolean;\n episodes?: Episode[];\n}\n"]}
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
type MediaType = 'movie' | 'podcast' | 'music' | 'musicVideo' | 'audiobook' | 'shortFilm' | 'tvShow' | 'software' | 'ebook' | 'all';
|
|
2
|
+
type Entity = 'movieArtist' | 'movie' | 'podcastAuthor' | 'podcast' | 'musicArtist' | 'musicTrack' | 'album' | 'musicVideo' | 'mix' | 'song' | 'audiobookAuthor' | 'audiobook' | 'shortFilmArtist' | 'shortFilm' | 'tvEpisode' | 'tvSeason' | 'software' | 'iPadSoftware' | 'macSoftware' | 'ebook' | 'allArtist' | 'allTrack';
|
|
3
|
+
type EntityForMediaType<T extends MediaType> = T extends 'movie' ? 'movieArtist' | 'movie' : T extends 'podcast' ? 'podcastAuthor' | 'podcast' : T extends 'music' ? 'musicArtist' | 'musicTrack' | 'album' | 'musicVideo' | 'mix' | 'song' : T extends 'musicVideo' ? 'musicArtist' | 'musicVideo' : T extends 'audiobook' ? 'audiobookAuthor' | 'audiobook' : T extends 'shortFilm' ? 'shortFilmArtist' | 'shortFilm' : T extends 'tvShow' ? 'tvEpisode' | 'tvSeason' : T extends 'software' ? 'software' | 'iPadSoftware' | 'macSoftware' : T extends 'ebook' ? 'ebook' : T extends 'all' ? 'movie' | 'album' | 'allArtist' | 'podcast' | 'musicVideo' | 'mix' | 'audiobook' | 'tvSeason' | 'allTrack' : never;
|
|
4
|
+
interface ITunesSearchParams<T extends MediaType> {
|
|
5
|
+
media: T;
|
|
6
|
+
entity?: EntityForMediaType<T>;
|
|
7
|
+
term: string;
|
|
8
|
+
country?: string;
|
|
9
|
+
limit?: number;
|
|
10
|
+
lang?: string;
|
|
11
|
+
version?: number;
|
|
12
|
+
explicit?: 'Yes' | 'No';
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
type WrapperType = 'track' | 'collection' | 'artist';
|
|
16
|
+
type Explicitness = 'explicit' | 'cleaned' | 'notExplicit';
|
|
17
|
+
type Kind = 'book' | 'album' | 'coached-audio' | 'feature-movie' | 'interactive-booklet' | 'music-video' | 'pdf' | 'podcast' | 'podcast-episode' | 'software-package' | 'song' | 'tv-episode' | 'artist';
|
|
18
|
+
interface ITunesSearchResult {
|
|
19
|
+
wrapperType: WrapperType;
|
|
20
|
+
kind: Kind;
|
|
21
|
+
artistId?: number;
|
|
22
|
+
collectionId?: number;
|
|
23
|
+
trackId?: number;
|
|
24
|
+
artistName?: string;
|
|
25
|
+
collectionName?: string;
|
|
26
|
+
trackName?: string;
|
|
27
|
+
collectionCensoredName?: string;
|
|
28
|
+
trackCensoredName?: string;
|
|
29
|
+
artistViewUrl?: string;
|
|
30
|
+
collectionViewUrl?: string;
|
|
31
|
+
trackViewUrl?: string;
|
|
32
|
+
previewUrl?: string;
|
|
33
|
+
artworkUrl30?: string;
|
|
34
|
+
artworkUrl60?: string;
|
|
35
|
+
artworkUrl100?: string;
|
|
36
|
+
collectionPrice?: number;
|
|
37
|
+
trackPrice?: number;
|
|
38
|
+
collectionExplicitness?: Explicitness;
|
|
39
|
+
trackExplicitness?: Explicitness;
|
|
40
|
+
discCount?: number;
|
|
41
|
+
discNumber?: number;
|
|
42
|
+
trackCount?: number;
|
|
43
|
+
trackNumber?: number;
|
|
44
|
+
trackTimeMillis?: number;
|
|
45
|
+
country?: string;
|
|
46
|
+
currency?: string;
|
|
47
|
+
primaryGenreName?: string;
|
|
48
|
+
releaseDate?: string;
|
|
49
|
+
feedUrl?: string;
|
|
50
|
+
}
|
|
51
|
+
interface ITunesSearchResponse {
|
|
52
|
+
resultCount: number;
|
|
53
|
+
results: ITunesSearchResult[];
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
declare class ItunesSearch {
|
|
57
|
+
private static readonly ITUNES_SEARCH_URL;
|
|
58
|
+
constructor();
|
|
59
|
+
search<T extends MediaType>(option: ITunesSearchParams<T>): Promise<ITunesSearchResponse>;
|
|
60
|
+
private buildSearchUrl;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
declare class PodcastLoader {
|
|
64
|
+
private readonly FETCH_TIMEOUT_MS;
|
|
65
|
+
/**
|
|
66
|
+
* Fetches and parses a podcast feed from the given URL
|
|
67
|
+
* @param feedUrl - The URL of the podcast feed
|
|
68
|
+
* @returns A Promise resolving to the parsed Podcast
|
|
69
|
+
* @throws Error if the feed is invalid or the fetch fails
|
|
70
|
+
*/
|
|
71
|
+
getPodcastFromFeed(feedUrl: string): Promise<Podcast>;
|
|
72
|
+
private validateUrl;
|
|
73
|
+
private extractChannel;
|
|
74
|
+
private validateChannel;
|
|
75
|
+
private mapChannel;
|
|
76
|
+
private mapCategories;
|
|
77
|
+
private mapEpisodes;
|
|
78
|
+
private mapEnclosure;
|
|
79
|
+
}
|
|
80
|
+
interface Category {
|
|
81
|
+
name: string;
|
|
82
|
+
}
|
|
83
|
+
interface Episode {
|
|
84
|
+
title: string | undefined;
|
|
85
|
+
enclosure: Enclosure | undefined;
|
|
86
|
+
guid: string;
|
|
87
|
+
linkUrl?: string;
|
|
88
|
+
pubDate?: string;
|
|
89
|
+
description?: string;
|
|
90
|
+
durationInSeconds?: string | number | undefined;
|
|
91
|
+
imageUrl?: string;
|
|
92
|
+
explicit?: boolean;
|
|
93
|
+
number?: number;
|
|
94
|
+
season?: number;
|
|
95
|
+
type?: string | undefined;
|
|
96
|
+
}
|
|
97
|
+
interface Enclosure {
|
|
98
|
+
length: string;
|
|
99
|
+
type: string;
|
|
100
|
+
url: string;
|
|
101
|
+
}
|
|
102
|
+
interface Podcast {
|
|
103
|
+
title: string;
|
|
104
|
+
description: string | undefined;
|
|
105
|
+
link: string;
|
|
106
|
+
language: string | undefined;
|
|
107
|
+
categories: Category[];
|
|
108
|
+
explicit: boolean;
|
|
109
|
+
imageUrl?: string;
|
|
110
|
+
author?: string;
|
|
111
|
+
copyright?: string;
|
|
112
|
+
fundingUrl?: string;
|
|
113
|
+
type?: string;
|
|
114
|
+
complete?: boolean;
|
|
115
|
+
episodes?: Episode[];
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export { type Category, type Enclosure, type Entity, type Episode, type Explicitness, type ITunesSearchParams, type ITunesSearchResponse, type ITunesSearchResult, ItunesSearch, type Kind, type MediaType, type Podcast, PodcastLoader, type WrapperType };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,118 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
type MediaType = 'movie' | 'podcast' | 'music' | 'musicVideo' | 'audiobook' | 'shortFilm' | 'tvShow' | 'software' | 'ebook' | 'all';
|
|
2
|
+
type Entity = 'movieArtist' | 'movie' | 'podcastAuthor' | 'podcast' | 'musicArtist' | 'musicTrack' | 'album' | 'musicVideo' | 'mix' | 'song' | 'audiobookAuthor' | 'audiobook' | 'shortFilmArtist' | 'shortFilm' | 'tvEpisode' | 'tvSeason' | 'software' | 'iPadSoftware' | 'macSoftware' | 'ebook' | 'allArtist' | 'allTrack';
|
|
3
|
+
type EntityForMediaType<T extends MediaType> = T extends 'movie' ? 'movieArtist' | 'movie' : T extends 'podcast' ? 'podcastAuthor' | 'podcast' : T extends 'music' ? 'musicArtist' | 'musicTrack' | 'album' | 'musicVideo' | 'mix' | 'song' : T extends 'musicVideo' ? 'musicArtist' | 'musicVideo' : T extends 'audiobook' ? 'audiobookAuthor' | 'audiobook' : T extends 'shortFilm' ? 'shortFilmArtist' | 'shortFilm' : T extends 'tvShow' ? 'tvEpisode' | 'tvSeason' : T extends 'software' ? 'software' | 'iPadSoftware' | 'macSoftware' : T extends 'ebook' ? 'ebook' : T extends 'all' ? 'movie' | 'album' | 'allArtist' | 'podcast' | 'musicVideo' | 'mix' | 'audiobook' | 'tvSeason' | 'allTrack' : never;
|
|
4
|
+
interface ITunesSearchParams<T extends MediaType> {
|
|
5
|
+
media: T;
|
|
6
|
+
entity?: EntityForMediaType<T>;
|
|
7
|
+
term: string;
|
|
8
|
+
country?: string;
|
|
9
|
+
limit?: number;
|
|
10
|
+
lang?: string;
|
|
11
|
+
version?: number;
|
|
12
|
+
explicit?: 'Yes' | 'No';
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
type WrapperType = 'track' | 'collection' | 'artist';
|
|
16
|
+
type Explicitness = 'explicit' | 'cleaned' | 'notExplicit';
|
|
17
|
+
type Kind = 'book' | 'album' | 'coached-audio' | 'feature-movie' | 'interactive-booklet' | 'music-video' | 'pdf' | 'podcast' | 'podcast-episode' | 'software-package' | 'song' | 'tv-episode' | 'artist';
|
|
18
|
+
interface ITunesSearchResult {
|
|
19
|
+
wrapperType: WrapperType;
|
|
20
|
+
kind: Kind;
|
|
21
|
+
artistId?: number;
|
|
22
|
+
collectionId?: number;
|
|
23
|
+
trackId?: number;
|
|
24
|
+
artistName?: string;
|
|
25
|
+
collectionName?: string;
|
|
26
|
+
trackName?: string;
|
|
27
|
+
collectionCensoredName?: string;
|
|
28
|
+
trackCensoredName?: string;
|
|
29
|
+
artistViewUrl?: string;
|
|
30
|
+
collectionViewUrl?: string;
|
|
31
|
+
trackViewUrl?: string;
|
|
32
|
+
previewUrl?: string;
|
|
33
|
+
artworkUrl30?: string;
|
|
34
|
+
artworkUrl60?: string;
|
|
35
|
+
artworkUrl100?: string;
|
|
36
|
+
collectionPrice?: number;
|
|
37
|
+
trackPrice?: number;
|
|
38
|
+
collectionExplicitness?: Explicitness;
|
|
39
|
+
trackExplicitness?: Explicitness;
|
|
40
|
+
discCount?: number;
|
|
41
|
+
discNumber?: number;
|
|
42
|
+
trackCount?: number;
|
|
43
|
+
trackNumber?: number;
|
|
44
|
+
trackTimeMillis?: number;
|
|
45
|
+
country?: string;
|
|
46
|
+
currency?: string;
|
|
47
|
+
primaryGenreName?: string;
|
|
48
|
+
releaseDate?: string;
|
|
49
|
+
feedUrl?: string;
|
|
50
|
+
}
|
|
51
|
+
interface ITunesSearchResponse {
|
|
52
|
+
resultCount: number;
|
|
53
|
+
results: ITunesSearchResult[];
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
declare class ItunesSearch {
|
|
57
|
+
private static readonly ITUNES_SEARCH_URL;
|
|
58
|
+
constructor();
|
|
59
|
+
search<T extends MediaType>(option: ITunesSearchParams<T>): Promise<ITunesSearchResponse>;
|
|
60
|
+
private buildSearchUrl;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
declare class PodcastLoader {
|
|
64
|
+
private readonly FETCH_TIMEOUT_MS;
|
|
65
|
+
/**
|
|
66
|
+
* Fetches and parses a podcast feed from the given URL
|
|
67
|
+
* @param feedUrl - The URL of the podcast feed
|
|
68
|
+
* @returns A Promise resolving to the parsed Podcast
|
|
69
|
+
* @throws Error if the feed is invalid or the fetch fails
|
|
70
|
+
*/
|
|
71
|
+
getPodcastFromFeed(feedUrl: string): Promise<Podcast>;
|
|
72
|
+
private validateUrl;
|
|
73
|
+
private extractChannel;
|
|
74
|
+
private validateChannel;
|
|
75
|
+
private mapChannel;
|
|
76
|
+
private mapCategories;
|
|
77
|
+
private mapEpisodes;
|
|
78
|
+
private mapEnclosure;
|
|
79
|
+
}
|
|
80
|
+
interface Category {
|
|
81
|
+
name: string;
|
|
82
|
+
}
|
|
83
|
+
interface Episode {
|
|
84
|
+
title: string | undefined;
|
|
85
|
+
enclosure: Enclosure | undefined;
|
|
86
|
+
guid: string;
|
|
87
|
+
linkUrl?: string;
|
|
88
|
+
pubDate?: string;
|
|
89
|
+
description?: string;
|
|
90
|
+
durationInSeconds?: string | number | undefined;
|
|
91
|
+
imageUrl?: string;
|
|
92
|
+
explicit?: boolean;
|
|
93
|
+
number?: number;
|
|
94
|
+
season?: number;
|
|
95
|
+
type?: string | undefined;
|
|
96
|
+
}
|
|
97
|
+
interface Enclosure {
|
|
98
|
+
length: string;
|
|
99
|
+
type: string;
|
|
100
|
+
url: string;
|
|
101
|
+
}
|
|
102
|
+
interface Podcast {
|
|
103
|
+
title: string;
|
|
104
|
+
description: string | undefined;
|
|
105
|
+
link: string;
|
|
106
|
+
language: string | undefined;
|
|
107
|
+
categories: Category[];
|
|
108
|
+
explicit: boolean;
|
|
109
|
+
imageUrl?: string;
|
|
110
|
+
author?: string;
|
|
111
|
+
copyright?: string;
|
|
112
|
+
fundingUrl?: string;
|
|
113
|
+
type?: string;
|
|
114
|
+
complete?: boolean;
|
|
115
|
+
episodes?: Episode[];
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export { type Category, type Enclosure, type Entity, type Episode, type Explicitness, type ITunesSearchParams, type ITunesSearchResponse, type ITunesSearchResult, ItunesSearch, type Kind, type MediaType, type Podcast, PodcastLoader, type WrapperType };
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,157 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
import { parseFeedToJson } from '@sesamy/podcast-parser';
|
|
2
|
+
|
|
3
|
+
// src/itunes-search/itunes-search.ts
|
|
4
|
+
var _ItunesSearch = class _ItunesSearch {
|
|
5
|
+
constructor() {
|
|
6
|
+
}
|
|
7
|
+
async search(option) {
|
|
8
|
+
const searchUrlWithParams = this.buildSearchUrl(option);
|
|
9
|
+
try {
|
|
10
|
+
const response = await fetch(searchUrlWithParams);
|
|
11
|
+
if (!response.ok) {
|
|
12
|
+
throw new Error(
|
|
13
|
+
`Failed to fetch data from iTunes Search API: ${response.status}}`
|
|
14
|
+
);
|
|
15
|
+
}
|
|
16
|
+
return await response.json();
|
|
17
|
+
} catch (error) {
|
|
18
|
+
if (error instanceof Error) {
|
|
19
|
+
throw new Error(`Fetch failed: ${error.message}`);
|
|
20
|
+
}
|
|
21
|
+
throw error;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
buildSearchUrl(option) {
|
|
25
|
+
const url = new URL(_ItunesSearch.ITUNES_SEARCH_URL);
|
|
26
|
+
for (const [key, value] of Object.entries(option)) {
|
|
27
|
+
if (value !== void 0) {
|
|
28
|
+
url.searchParams.append(key, String(value));
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
return url.toString();
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
_ItunesSearch.ITUNES_SEARCH_URL = "https://itunes.apple.com/search";
|
|
35
|
+
var ItunesSearch = _ItunesSearch;
|
|
36
|
+
var PodcastLoader = class {
|
|
37
|
+
constructor() {
|
|
38
|
+
this.FETCH_TIMEOUT_MS = 3e4;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Fetches and parses a podcast feed from the given URL
|
|
42
|
+
* @param feedUrl - The URL of the podcast feed
|
|
43
|
+
* @returns A Promise resolving to the parsed Podcast
|
|
44
|
+
* @throws Error if the feed is invalid or the fetch fails
|
|
45
|
+
*/
|
|
46
|
+
async getPodcastFromFeed(feedUrl) {
|
|
47
|
+
this.validateUrl(feedUrl);
|
|
48
|
+
const controller = new AbortController();
|
|
49
|
+
const timeoutId = setTimeout(() => controller.abort(), this.FETCH_TIMEOUT_MS);
|
|
50
|
+
try {
|
|
51
|
+
const response = await fetch(feedUrl, { signal: controller.signal });
|
|
52
|
+
if (!response.ok) {
|
|
53
|
+
throw new Error(
|
|
54
|
+
`Failed to fetch podcast feed: ${response.status} ${response.statusText}`
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
const xmlString = await response.text();
|
|
58
|
+
if (!xmlString.trim()) {
|
|
59
|
+
throw new Error("Podcast feed is empty");
|
|
60
|
+
}
|
|
61
|
+
const podcastFromXml = await parseFeedToJson(xmlString);
|
|
62
|
+
const channel = this.extractChannel(podcastFromXml);
|
|
63
|
+
this.validateChannel(channel);
|
|
64
|
+
return this.mapChannel(channel);
|
|
65
|
+
} catch (error) {
|
|
66
|
+
if (error instanceof Error && error.name === "AbortError") {
|
|
67
|
+
throw new Error(`Podcast feed request timeout after ${this.FETCH_TIMEOUT_MS}ms`);
|
|
68
|
+
}
|
|
69
|
+
if (error instanceof Error) {
|
|
70
|
+
throw new Error(`Failed to load podcast feed: ${error.message}`);
|
|
71
|
+
}
|
|
72
|
+
throw error;
|
|
73
|
+
} finally {
|
|
74
|
+
clearTimeout(timeoutId);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
validateUrl(feedUrl) {
|
|
78
|
+
try {
|
|
79
|
+
new URL(feedUrl);
|
|
80
|
+
} catch {
|
|
81
|
+
throw new Error(`Invalid feed URL: ${feedUrl}`);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
extractChannel(podcastFromXml) {
|
|
85
|
+
const channel = podcastFromXml?.rss?.channel;
|
|
86
|
+
if (!channel) {
|
|
87
|
+
throw new Error("Invalid podcast feed: missing channel data");
|
|
88
|
+
}
|
|
89
|
+
return channel;
|
|
90
|
+
}
|
|
91
|
+
validateChannel(channel) {
|
|
92
|
+
if (!channel.title) {
|
|
93
|
+
throw new Error('Invalid podcast feed: missing required field "title"');
|
|
94
|
+
}
|
|
95
|
+
if (!channel.link) {
|
|
96
|
+
throw new Error('Invalid podcast feed: missing required field "link"');
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
mapChannel(channel) {
|
|
100
|
+
return {
|
|
101
|
+
title: channel.title,
|
|
102
|
+
description: channel.description,
|
|
103
|
+
link: channel.link,
|
|
104
|
+
language: channel.language,
|
|
105
|
+
categories: this.mapCategories(channel["itunes:category"]),
|
|
106
|
+
explicit: channel["itunes:explicit"] === "true",
|
|
107
|
+
imageUrl: channel["itunes:image"]?.["@_href"],
|
|
108
|
+
author: channel["itunes:author"],
|
|
109
|
+
copyright: channel["copyright"],
|
|
110
|
+
fundingUrl: channel["podcast:funding"]?.["@_url"],
|
|
111
|
+
type: channel["itunes:type"],
|
|
112
|
+
episodes: this.mapEpisodes(channel.item)
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
mapCategories(categories) {
|
|
116
|
+
if (!categories) {
|
|
117
|
+
return [];
|
|
118
|
+
}
|
|
119
|
+
return categories.flatMap((category) => [
|
|
120
|
+
{ name: category["@_text"] },
|
|
121
|
+
...category["itunes:category"]?.map((sub) => ({ name: sub["@_text"] })) || []
|
|
122
|
+
]);
|
|
123
|
+
}
|
|
124
|
+
mapEpisodes(items) {
|
|
125
|
+
if (!items) {
|
|
126
|
+
return [];
|
|
127
|
+
}
|
|
128
|
+
return items.map((item) => ({
|
|
129
|
+
title: item.title,
|
|
130
|
+
enclosure: this.mapEnclosure(item.enclosure),
|
|
131
|
+
guid: item.guid["#text"],
|
|
132
|
+
linkUrl: item.link,
|
|
133
|
+
pubDate: item.pubDate,
|
|
134
|
+
description: item.description,
|
|
135
|
+
durationInSeconds: item["itunes:duration"],
|
|
136
|
+
imageUrl: item["itunes:image"]?.["@_href"],
|
|
137
|
+
explicit: item["itunes:explicit"] === "yes",
|
|
138
|
+
number: item["itunes:episode"],
|
|
139
|
+
season: item["itunes:season"],
|
|
140
|
+
type: item["itunes:episodeType"]
|
|
141
|
+
}));
|
|
142
|
+
}
|
|
143
|
+
mapEnclosure(enclosure) {
|
|
144
|
+
if (!enclosure?.[0]) {
|
|
145
|
+
return void 0;
|
|
146
|
+
}
|
|
147
|
+
return {
|
|
148
|
+
url: enclosure[0]["@_url"],
|
|
149
|
+
type: enclosure[0]["@_type"],
|
|
150
|
+
length: enclosure[0]["@_length"]
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
export { ItunesSearch, PodcastLoader };
|
|
156
|
+
//# sourceMappingURL=index.js.map
|
|
157
|
+
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,iCAAiC,CAAC;AAChD,cAAc,0CAA0C,CAAC"}
|
|
1
|
+
{"version":3,"sources":["../src/itunes-search/itunes-search.ts","../src/podcast/podcast.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["import type { ITunesSearchParams, MediaType } from './itunes-search-options.js';\nimport type { ITunesSearchResponse } from './itunes-search-result.js';\n\nexport class ItunesSearch {\n private static readonly ITUNES_SEARCH_URL = 'https://itunes.apple.com/search';\n\n constructor() {}\n\n async search<T extends MediaType>(option: ITunesSearchParams<T>): Promise<ITunesSearchResponse> {\n const searchUrlWithParams = this.buildSearchUrl(option);\n\n try {\n const response = await fetch(searchUrlWithParams);\n\n if (!response.ok) {\n // noinspection ExceptionCaughtLocallyJS\n throw new Error(\n `Failed to fetch data from iTunes Search API: ${response.status}}`,\n );\n }\n\n return await response.json() as ITunesSearchResponse;\n } catch (error) {\n if (error instanceof Error) {\n throw new Error(`Fetch failed: ${error.message}`);\n }\n\n throw error;\n }\n }\n\n private buildSearchUrl<T extends MediaType>(option: ITunesSearchParams<T>): string {\n const url = new URL(ItunesSearch.ITUNES_SEARCH_URL);\n\n for (const [key, value] of Object.entries(option)) {\n if (value !== undefined) {\n url.searchParams.append(key, String(value));\n }\n }\n\n return url.toString();\n }\n}\n","import { parseFeedToJson } from '@sesamy/podcast-parser';\n\ninterface RawCategory {\n '@_text': string;\n 'itunes:category'?: RawCategory[];\n}\n\ninterface RawEnclosure {\n '@_url': string;\n '@_type': string;\n '@_length': string;\n}\n\ninterface RawEpisode {\n title?: string;\n guid: { '#text': string };\n link?: string;\n pubDate?: string;\n description?: string;\n 'itunes:duration'?: string | number;\n 'itunes:image'?: { '@_href': string };\n 'itunes:explicit'?: 'yes' | 'no';\n 'itunes:episode'?: number;\n 'itunes:season'?: number;\n 'itunes:episodeType'?: string;\n enclosure?: RawEnclosure[];\n}\n\ninterface RawChannel {\n title: string;\n description?: string;\n link: string;\n language?: string;\n 'itunes:category'?: RawCategory[];\n 'itunes:explicit'?: 'true' | 'false';\n 'itunes:image'?: { '@_href': string };\n 'itunes:author'?: string;\n copyright?: string;\n 'podcast:funding'?: { '@_url': string };\n 'itunes:type'?: string;\n item?: RawEpisode[];\n}\n\ninterface RawPodcastFeed {\n rss?: {\n channel?: RawChannel;\n };\n}\n\nexport class PodcastLoader {\n private readonly FETCH_TIMEOUT_MS = 30000;\n\n /**\n * Fetches and parses a podcast feed from the given URL\n * @param feedUrl - The URL of the podcast feed\n * @returns A Promise resolving to the parsed Podcast\n * @throws Error if the feed is invalid or the fetch fails\n */\n async getPodcastFromFeed(feedUrl: string): Promise<Podcast> {\n this.validateUrl(feedUrl);\n\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), this.FETCH_TIMEOUT_MS);\n\n try {\n const response = await fetch(feedUrl, { signal: controller.signal });\n\n if (!response.ok) {\n throw new Error(\n `Failed to fetch podcast feed: ${response.status} ${response.statusText}`,\n );\n }\n\n const xmlString = await response.text();\n\n if (!xmlString.trim()) {\n throw new Error('Podcast feed is empty');\n }\n\n const podcastFromXml = await parseFeedToJson(xmlString) as RawPodcastFeed;\n const channel = this.extractChannel(podcastFromXml);\n\n this.validateChannel(channel);\n\n return this.mapChannel(channel);\n } catch (error) {\n if (error instanceof Error && error.name === 'AbortError') {\n throw new Error(`Podcast feed request timeout after ${this.FETCH_TIMEOUT_MS}ms`);\n }\n\n if (error instanceof Error) {\n throw new Error(`Failed to load podcast feed: ${error.message}`);\n }\n\n throw error;\n } finally {\n clearTimeout(timeoutId);\n }\n }\n\n private validateUrl(feedUrl: string): void {\n try {\n new URL(feedUrl);\n } catch {\n throw new Error(`Invalid feed URL: ${feedUrl}`);\n }\n }\n\n private extractChannel(podcastFromXml: RawPodcastFeed): RawChannel {\n const channel = podcastFromXml?.rss?.channel;\n\n if (!channel) {\n throw new Error('Invalid podcast feed: missing channel data');\n }\n\n return channel;\n }\n\n private validateChannel(channel: RawChannel): void {\n if (!channel.title) {\n throw new Error('Invalid podcast feed: missing required field \"title\"');\n }\n\n if (!channel.link) {\n throw new Error('Invalid podcast feed: missing required field \"link\"');\n }\n }\n\n private mapChannel(channel: RawChannel): Podcast {\n return {\n title: channel.title,\n description: channel.description,\n link: channel.link,\n language: channel.language,\n categories: this.mapCategories(channel['itunes:category']),\n explicit: channel['itunes:explicit'] === 'true',\n imageUrl: channel['itunes:image']?.['@_href'],\n author: channel['itunes:author'],\n copyright: channel['copyright'],\n fundingUrl: channel['podcast:funding']?.['@_url'],\n type: channel['itunes:type'],\n episodes: this.mapEpisodes(channel.item),\n };\n }\n\n private mapCategories(categories: RawCategory[] | undefined): Category[] {\n if (!categories) {return [];}\n\n return categories.flatMap((category) => [\n { name: category['@_text'] },\n ...(category['itunes:category']?.map((sub) => ({ name: sub['@_text'] })) || []),\n ]);\n }\n\n private mapEpisodes(items: RawEpisode[] | undefined): Episode[] {\n if (!items) {return [];}\n\n return items.map((item) => ({\n title: item.title,\n enclosure: this.mapEnclosure(item.enclosure),\n guid: item.guid['#text'],\n linkUrl: item.link,\n pubDate: item.pubDate,\n description: item.description,\n durationInSeconds: item['itunes:duration'],\n imageUrl: item['itunes:image']?.['@_href'],\n explicit: item['itunes:explicit'] === 'yes',\n number: item['itunes:episode'],\n season: item['itunes:season'],\n type: item['itunes:episodeType'],\n }));\n }\n\n private mapEnclosure(enclosure: RawEnclosure[] | undefined): Enclosure | undefined {\n if (!enclosure?.[0]) {return undefined;}\n\n return {\n url: enclosure[0]['@_url'],\n type: enclosure[0]['@_type'],\n length: enclosure[0]['@_length'],\n };\n }\n}\n\nexport interface Category {\n name: string;\n}\n\nexport interface Episode {\n title: string | undefined;\n enclosure: Enclosure | undefined;\n guid: string;\n linkUrl?: string;\n pubDate?: string;\n description?: string;\n durationInSeconds?: string | number | undefined;\n imageUrl?: string;\n explicit?: boolean;\n number?: number;\n season?: number;\n type?: string | undefined;\n}\n\nexport interface Enclosure {\n length: string;\n type: string;\n url: string;\n}\n\nexport interface Podcast {\n title: string;\n description: string | undefined;\n link: string;\n language: string | undefined;\n categories: Category[];\n explicit: boolean;\n imageUrl?: string;\n author?: string;\n copyright?: string;\n fundingUrl?: string;\n type?: string;\n complete?: boolean;\n episodes?: Episode[];\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rapthi/podca-ts",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.4",
|
|
4
4
|
"description": "A TypeScript library for parsing and managing podcast feeds from RSS/iTunes feeds",
|
|
5
5
|
"author": "Thierry Rapillard <rapthi@gmail.com>",
|
|
6
6
|
"license": "MIT",
|
|
@@ -22,7 +22,8 @@
|
|
|
22
22
|
"xml"
|
|
23
23
|
],
|
|
24
24
|
"type": "module",
|
|
25
|
-
"main": "./dist/index.
|
|
25
|
+
"main": "./dist/index.cjs",
|
|
26
|
+
"module": "./dist/index.js",
|
|
26
27
|
"types": "./dist/index.d.ts",
|
|
27
28
|
"exports": {
|
|
28
29
|
".": {
|
|
@@ -38,7 +39,7 @@
|
|
|
38
39
|
"CHANGELOG.md"
|
|
39
40
|
],
|
|
40
41
|
"scripts": {
|
|
41
|
-
"build": "
|
|
42
|
+
"build": "tsup",
|
|
42
43
|
"test": "vitest run",
|
|
43
44
|
"test:watch": "vitest --watch",
|
|
44
45
|
"test:coverage": "vitest --coverage",
|
|
@@ -61,6 +62,7 @@
|
|
|
61
62
|
"@vitest/coverage-v8": "^4.0.16",
|
|
62
63
|
"eslint": "^9.39.2",
|
|
63
64
|
"prettier": "^3.7.4",
|
|
65
|
+
"tsup": "^8.5.1",
|
|
64
66
|
"typescript": "^5.9.3",
|
|
65
67
|
"typescript-eslint": "^8.51.0",
|
|
66
68
|
"vitest": "^4.0.16"
|
package/dist/index.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,mBAAmB,0CAA0C,CAAC;AAC9D,mBAAmB,yCAAyC,CAAC;AAC7D,cAAc,kCAAkC,CAAC;AACjD,cAAc,sBAAsB,CAAC"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"iTunesSearch.d.ts","sourceRoot":"","sources":["../../src/itunes-search/iTunesSearch.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,SAAS,EAAE,MAAM,4BAA4B,CAAC;AAEhF,qBAAa,YAAY;;IAGvB,MAAM,CAAC,CAAC,SAAS,SAAS,EAAE,MAAM,EAAE,kBAAkB,CAAC,CAAC,CAAC;CAG1D"}
|