@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
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"iTunesSearch.js","sourceRoot":"","sources":["../../src/itunes-search/iTunesSearch.ts"],"names":[],"mappings":"AAEA,MAAM,OAAO,YAAY;IACvB,gBAAe,CAAC;IAEhB,MAAM,CAAsB,MAA6B;QACvD,OAAO,CAAC,GAAG,CAAC,sBAAsB,EAAE,MAAM,CAAC,CAAC;IAC9C,CAAC;CACF"}
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
export type MediaType = 'movie' | 'podcast' | 'music' | 'musicVideo' | 'audiobook' | 'shortFilm' | 'tvShow' | 'software' | 'ebook' | 'all';
|
|
2
|
-
export 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
|
-
export 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
|
-
export {};
|
|
15
|
-
//# sourceMappingURL=itunes-search-options.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"itunes-search-options.d.ts","sourceRoot":"","sources":["../../src/itunes-search/itunes-search-options.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,SAAS,GACjB,OAAO,GACP,SAAS,GACT,OAAO,GACP,YAAY,GACZ,WAAW,GACX,WAAW,GACX,QAAQ,GACR,UAAU,GACV,OAAO,GACP,KAAK,CAAC;AAEV,MAAM,MAAM,MAAM,GACd,aAAa,GACb,OAAO,GACP,eAAe,GACf,SAAS,GACT,aAAa,GACb,YAAY,GACZ,OAAO,GACP,YAAY,GACZ,KAAK,GACL,MAAM,GACN,iBAAiB,GACjB,WAAW,GACX,iBAAiB,GACjB,WAAW,GACX,WAAW,GACX,UAAU,GACV,UAAU,GACV,cAAc,GACd,aAAa,GACb,OAAO,GACP,WAAW,GACX,UAAU,CAAC;AAEf,KAAK,kBAAkB,CAAC,CAAC,SAAS,SAAS,IAAI,CAAC,SAAS,OAAO,GAC5D,aAAa,GAAG,OAAO,GACvB,CAAC,SAAS,SAAS,GACjB,eAAe,GAAG,SAAS,GAC3B,CAAC,SAAS,OAAO,GACf,aAAa,GAAG,YAAY,GAAG,OAAO,GAAG,YAAY,GAAG,KAAK,GAAG,MAAM,GACtE,CAAC,SAAS,YAAY,GACpB,aAAa,GAAG,YAAY,GAC5B,CAAC,SAAS,WAAW,GACnB,iBAAiB,GAAG,WAAW,GAC/B,CAAC,SAAS,WAAW,GACnB,iBAAiB,GAAG,WAAW,GAC/B,CAAC,SAAS,QAAQ,GAChB,WAAW,GAAG,UAAU,GACxB,CAAC,SAAS,UAAU,GAClB,UAAU,GAAG,cAAc,GAAG,aAAa,GAC3C,CAAC,SAAS,OAAO,GACf,OAAO,GACP,CAAC,SAAS,KAAK,GAET,OAAO,GACP,OAAO,GACP,WAAW,GACX,SAAS,GACT,YAAY,GACZ,KAAK,GACL,WAAW,GACX,UAAU,GACV,UAAU,GACd,KAAK,CAAC;AAE5B,MAAM,WAAW,kBAAkB,CAAC,CAAC,SAAS,SAAS;IACrD,KAAK,EAAE,CAAC,CAAC;IACT,MAAM,CAAC,EAAE,kBAAkB,CAAC,CAAC,CAAC,CAAC;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,KAAK,GAAG,IAAI,CAAC;CACzB"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"itunes-search-options.js","sourceRoot":"","sources":["../../src/itunes-search/itunes-search-options.ts"],"names":[],"mappings":""}
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
export type WrapperType = 'track' | 'collection' | 'artist';
|
|
2
|
-
export type Explicitness = 'explicit' | 'cleaned' | 'notExplicit';
|
|
3
|
-
export type Kind = 'book' | 'album' | 'coached-audio' | 'feature-movie' | 'interactive-booklet' | 'music-video' | 'pdf' | 'podcast' | 'podcast-episode' | 'software-package' | 'song' | 'tv-episode' | 'artist';
|
|
4
|
-
export interface ITunesSearchResult {
|
|
5
|
-
wrapperType: WrapperType;
|
|
6
|
-
kind: Kind;
|
|
7
|
-
artistId?: number;
|
|
8
|
-
collectionId?: number;
|
|
9
|
-
trackId?: number;
|
|
10
|
-
artistName?: string;
|
|
11
|
-
collectionName?: string;
|
|
12
|
-
trackName?: string;
|
|
13
|
-
collectionCensoredName?: string;
|
|
14
|
-
trackCensoredName?: string;
|
|
15
|
-
artistViewUrl?: string;
|
|
16
|
-
collectionViewUrl?: string;
|
|
17
|
-
trackViewUrl?: string;
|
|
18
|
-
previewUrl?: string;
|
|
19
|
-
artworkUrl30?: string;
|
|
20
|
-
artworkUrl60?: string;
|
|
21
|
-
artworkUrl100?: string;
|
|
22
|
-
collectionPrice?: number;
|
|
23
|
-
trackPrice?: number;
|
|
24
|
-
collectionExplicitness?: Explicitness;
|
|
25
|
-
trackExplicitness?: Explicitness;
|
|
26
|
-
discCount?: number;
|
|
27
|
-
discNumber?: number;
|
|
28
|
-
trackCount?: number;
|
|
29
|
-
trackNumber?: number;
|
|
30
|
-
trackTimeMillis?: number;
|
|
31
|
-
country?: string;
|
|
32
|
-
currency?: string;
|
|
33
|
-
primaryGenreName?: string;
|
|
34
|
-
releaseDate?: string;
|
|
35
|
-
feedUrl?: string;
|
|
36
|
-
}
|
|
37
|
-
export interface ITunesSearchResponse {
|
|
38
|
-
resultCount: number;
|
|
39
|
-
results: ITunesSearchResult[];
|
|
40
|
-
}
|
|
41
|
-
//# sourceMappingURL=itunes-search-result.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"itunes-search-result.d.ts","sourceRoot":"","sources":["../../src/itunes-search/itunes-search-result.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,WAAW,GAAG,OAAO,GAAG,YAAY,GAAG,QAAQ,CAAC;AAE5D,MAAM,MAAM,YAAY,GAAG,UAAU,GAAG,SAAS,GAAG,aAAa,CAAC;AAElE,MAAM,MAAM,IAAI,GACZ,MAAM,GACN,OAAO,GACP,eAAe,GACf,eAAe,GACf,qBAAqB,GACrB,aAAa,GACb,KAAK,GACL,SAAS,GACT,iBAAiB,GACjB,kBAAkB,GAClB,MAAM,GACN,YAAY,GACZ,QAAQ,CAAC;AAEb,MAAM,WAAW,kBAAkB;IACjC,WAAW,EAAE,WAAW,CAAC;IACzB,IAAI,EAAE,IAAI,CAAC;IAEX,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAE3B,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,aAAa,CAAC,EAAE,MAAM,CAAC;IAEvB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB,sBAAsB,CAAC,EAAE,YAAY,CAAC;IACtC,iBAAiB,CAAC,EAAE,YAAY,CAAC;IAEjC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,eAAe,CAAC,EAAE,MAAM,CAAC;IAEzB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAElB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAE1B,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,oBAAoB;IACnC,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,kBAAkB,EAAE,CAAC;CAC/B"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
import type { ITunesSearchParams, MediaType } from './itunes-search-options.js';
|
|
2
|
-
import type { ITunesSearchResponse } from './itunes-search-result.js';
|
|
3
|
-
export declare class ItunesSearch {
|
|
4
|
-
private static readonly ITUNES_SEARCH_URL;
|
|
5
|
-
constructor();
|
|
6
|
-
search<T extends MediaType>(option: ITunesSearchParams<T>): Promise<ITunesSearchResponse>;
|
|
7
|
-
private buildSearchUrl;
|
|
8
|
-
}
|
|
9
|
-
//# sourceMappingURL=itunes-search.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"itunes-search.d.ts","sourceRoot":"","sources":["../../src/itunes-search/itunes-search.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,SAAS,EAAE,MAAM,4BAA4B,CAAC;AAChF,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAC;AAEtE,qBAAa,YAAY;IACvB,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,iBAAiB,CAAqC;;IAIxE,MAAM,CAAC,CAAC,SAAS,SAAS,EAAE,MAAM,EAAE,kBAAkB,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,oBAAoB,CAAC;IAuB/F,OAAO,CAAC,cAAc;CAWvB"}
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
export class ItunesSearch {
|
|
2
|
-
constructor() { }
|
|
3
|
-
async search(option) {
|
|
4
|
-
const searchUrlWithParams = this.buildSearchUrl(option);
|
|
5
|
-
try {
|
|
6
|
-
const response = await fetch(searchUrlWithParams);
|
|
7
|
-
if (!response.ok) {
|
|
8
|
-
// noinspection ExceptionCaughtLocallyJS
|
|
9
|
-
throw new Error(`Failed to fetch data from iTunes Search API: ${response.status}}`);
|
|
10
|
-
}
|
|
11
|
-
return await response.json();
|
|
12
|
-
}
|
|
13
|
-
catch (error) {
|
|
14
|
-
if (error instanceof Error) {
|
|
15
|
-
throw new Error(`Fetch failed: ${error.message}`);
|
|
16
|
-
}
|
|
17
|
-
throw error;
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
buildSearchUrl(option) {
|
|
21
|
-
const url = new URL(ItunesSearch.ITUNES_SEARCH_URL);
|
|
22
|
-
for (const [key, value] of Object.entries(option)) {
|
|
23
|
-
if (value !== undefined) {
|
|
24
|
-
url.searchParams.append(key, String(value));
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
return url.toString();
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
ItunesSearch.ITUNES_SEARCH_URL = 'https://itunes.apple.com/search';
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"itunes-search.spec.d.ts","sourceRoot":"","sources":["../../src/itunes-search/itunes-search.spec.ts"],"names":[],"mappings":""}
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
2
|
-
import { ItunesSearch } from './itunes-search';
|
|
3
|
-
describe('itunesSearch', () => {
|
|
4
|
-
let searcher;
|
|
5
|
-
beforeEach(() => {
|
|
6
|
-
searcher = new ItunesSearch();
|
|
7
|
-
vi.stubGlobal('fetch', vi.fn());
|
|
8
|
-
});
|
|
9
|
-
it('should return data with success when the response is ok', async () => {
|
|
10
|
-
const mockData = {
|
|
11
|
-
resultCount: 1,
|
|
12
|
-
results: [
|
|
13
|
-
{
|
|
14
|
-
wrapperType: 'track',
|
|
15
|
-
kind: 'song',
|
|
16
|
-
trackName: 'Test Song',
|
|
17
|
-
artistName: 'Test Artist',
|
|
18
|
-
collectionName: 'Test Album',
|
|
19
|
-
trackPrice: 0.99,
|
|
20
|
-
country: 'USA',
|
|
21
|
-
currency: 'USD',
|
|
22
|
-
releaseDate: '2023-01-01T08:00:00Z',
|
|
23
|
-
primaryGenreName: 'Pop',
|
|
24
|
-
},
|
|
25
|
-
],
|
|
26
|
-
};
|
|
27
|
-
vi.mocked(fetch).mockResolvedValueOnce({
|
|
28
|
-
ok: true,
|
|
29
|
-
json: async () => mockData,
|
|
30
|
-
});
|
|
31
|
-
const result = await searcher.search({ term: 'test', media: 'music' });
|
|
32
|
-
expect(result).toEqual(mockData);
|
|
33
|
-
expect(fetch).toHaveBeenCalledWith(expect.stringContaining('https://itunes.apple.com/search?term=test&media=music'));
|
|
34
|
-
});
|
|
35
|
-
it("should raise an error if the http response is not ok (ex: 404)", async () => {
|
|
36
|
-
vi.mocked(fetch).mockResolvedValueOnce({
|
|
37
|
-
ok: false,
|
|
38
|
-
status: 404,
|
|
39
|
-
});
|
|
40
|
-
await expect(searcher.search({ term: 'invalid', media: 'podcast' })).rejects.toThrow('Failed to fetch data from iTunes Search API: 404');
|
|
41
|
-
});
|
|
42
|
-
it('should raise an error "Fetch failed" in case of network failure', async () => {
|
|
43
|
-
vi.mocked(fetch).mockRejectedValueOnce(new Error('Network Error'));
|
|
44
|
-
await expect(searcher.search({ term: 'test', media: 'podcast' })).rejects.toThrow('Fetch failed: Network Error');
|
|
45
|
-
});
|
|
46
|
-
});
|
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
export declare class PodcastLoader {
|
|
2
|
-
private readonly FETCH_TIMEOUT_MS;
|
|
3
|
-
/**
|
|
4
|
-
* Fetches and parses a podcast feed from the given URL
|
|
5
|
-
* @param feedUrl - The URL of the podcast feed
|
|
6
|
-
* @returns A Promise resolving to the parsed Podcast
|
|
7
|
-
* @throws Error if the feed is invalid or the fetch fails
|
|
8
|
-
*/
|
|
9
|
-
getPodcastFromFeed(feedUrl: string): Promise<Podcast>;
|
|
10
|
-
private validateUrl;
|
|
11
|
-
private extractChannel;
|
|
12
|
-
private validateChannel;
|
|
13
|
-
private mapChannel;
|
|
14
|
-
private mapCategories;
|
|
15
|
-
private mapEpisodes;
|
|
16
|
-
private mapEnclosure;
|
|
17
|
-
}
|
|
18
|
-
export interface Category {
|
|
19
|
-
name: string;
|
|
20
|
-
}
|
|
21
|
-
export interface Episode {
|
|
22
|
-
title: string | undefined;
|
|
23
|
-
enclosure: Enclosure | undefined;
|
|
24
|
-
guid: string;
|
|
25
|
-
linkUrl?: string;
|
|
26
|
-
pubDate?: string;
|
|
27
|
-
description?: string;
|
|
28
|
-
durationInSeconds?: string | number | undefined;
|
|
29
|
-
imageUrl?: string;
|
|
30
|
-
explicit?: boolean;
|
|
31
|
-
number?: number;
|
|
32
|
-
season?: number;
|
|
33
|
-
type?: string | undefined;
|
|
34
|
-
}
|
|
35
|
-
export interface Enclosure {
|
|
36
|
-
length: string;
|
|
37
|
-
type: string;
|
|
38
|
-
url: string;
|
|
39
|
-
}
|
|
40
|
-
export interface Podcast {
|
|
41
|
-
title: string;
|
|
42
|
-
description: string | undefined;
|
|
43
|
-
link: string;
|
|
44
|
-
language: string | undefined;
|
|
45
|
-
categories: Category[];
|
|
46
|
-
explicit: boolean;
|
|
47
|
-
imageUrl?: string;
|
|
48
|
-
author?: string;
|
|
49
|
-
copyright?: string;
|
|
50
|
-
fundingUrl?: string;
|
|
51
|
-
type?: string;
|
|
52
|
-
complete?: boolean;
|
|
53
|
-
episodes?: Episode[];
|
|
54
|
-
}
|
|
55
|
-
//# sourceMappingURL=podcast.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"podcast.d.ts","sourceRoot":"","sources":["../../src/podcast/podcast.ts"],"names":[],"mappings":"AAiDA,qBAAa,aAAa;IACxB,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAS;IAE1C;;;;;OAKG;IACG,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IA0C3D,OAAO,CAAC,WAAW;IAQnB,OAAO,CAAC,cAAc;IAUtB,OAAO,CAAC,eAAe;IAUvB,OAAO,CAAC,UAAU;IAiBlB,OAAO,CAAC,aAAa;IASrB,OAAO,CAAC,WAAW;IAmBnB,OAAO,CAAC,YAAY;CASrB;AAED,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,OAAO;IACtB,KAAK,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1B,SAAS,EAAE,SAAS,GAAG,SAAS,CAAC;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,iBAAiB,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;IAChD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC3B;AAED,MAAM,WAAW,SAAS;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,OAAO;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,GAAG,SAAS,CAAC;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,GAAG,SAAS,CAAC;IAC7B,UAAU,EAAE,QAAQ,EAAE,CAAC;IACvB,QAAQ,EAAE,OAAO,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,EAAE,CAAC;CACtB"}
|
package/dist/podcast/podcast.js
DELETED
|
@@ -1,120 +0,0 @@
|
|
|
1
|
-
import { parseFeedToJson } from '@sesamy/podcast-parser';
|
|
2
|
-
export class PodcastLoader {
|
|
3
|
-
constructor() {
|
|
4
|
-
this.FETCH_TIMEOUT_MS = 30000;
|
|
5
|
-
}
|
|
6
|
-
/**
|
|
7
|
-
* Fetches and parses a podcast feed from the given URL
|
|
8
|
-
* @param feedUrl - The URL of the podcast feed
|
|
9
|
-
* @returns A Promise resolving to the parsed Podcast
|
|
10
|
-
* @throws Error if the feed is invalid or the fetch fails
|
|
11
|
-
*/
|
|
12
|
-
async getPodcastFromFeed(feedUrl) {
|
|
13
|
-
this.validateUrl(feedUrl);
|
|
14
|
-
const controller = new AbortController();
|
|
15
|
-
const timeoutId = setTimeout(() => controller.abort(), this.FETCH_TIMEOUT_MS);
|
|
16
|
-
try {
|
|
17
|
-
const response = await fetch(feedUrl, { signal: controller.signal });
|
|
18
|
-
if (!response.ok) {
|
|
19
|
-
throw new Error(`Failed to fetch podcast feed: ${response.status} ${response.statusText}`);
|
|
20
|
-
}
|
|
21
|
-
const xmlString = await response.text();
|
|
22
|
-
if (!xmlString.trim()) {
|
|
23
|
-
throw new Error('Podcast feed is empty');
|
|
24
|
-
}
|
|
25
|
-
const podcastFromXml = await parseFeedToJson(xmlString);
|
|
26
|
-
const channel = this.extractChannel(podcastFromXml);
|
|
27
|
-
this.validateChannel(channel);
|
|
28
|
-
return this.mapChannel(channel);
|
|
29
|
-
}
|
|
30
|
-
catch (error) {
|
|
31
|
-
if (error instanceof Error && error.name === 'AbortError') {
|
|
32
|
-
throw new Error(`Podcast feed request timeout after ${this.FETCH_TIMEOUT_MS}ms`);
|
|
33
|
-
}
|
|
34
|
-
if (error instanceof Error) {
|
|
35
|
-
throw new Error(`Failed to load podcast feed: ${error.message}`);
|
|
36
|
-
}
|
|
37
|
-
throw error;
|
|
38
|
-
}
|
|
39
|
-
finally {
|
|
40
|
-
clearTimeout(timeoutId);
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
validateUrl(feedUrl) {
|
|
44
|
-
try {
|
|
45
|
-
new URL(feedUrl);
|
|
46
|
-
}
|
|
47
|
-
catch {
|
|
48
|
-
throw new Error(`Invalid feed URL: ${feedUrl}`);
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
extractChannel(podcastFromXml) {
|
|
52
|
-
const channel = podcastFromXml?.rss?.channel;
|
|
53
|
-
if (!channel) {
|
|
54
|
-
throw new Error('Invalid podcast feed: missing channel data');
|
|
55
|
-
}
|
|
56
|
-
return channel;
|
|
57
|
-
}
|
|
58
|
-
validateChannel(channel) {
|
|
59
|
-
if (!channel.title) {
|
|
60
|
-
throw new Error('Invalid podcast feed: missing required field "title"');
|
|
61
|
-
}
|
|
62
|
-
if (!channel.link) {
|
|
63
|
-
throw new Error('Invalid podcast feed: missing required field "link"');
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
mapChannel(channel) {
|
|
67
|
-
return {
|
|
68
|
-
title: channel.title,
|
|
69
|
-
description: channel.description,
|
|
70
|
-
link: channel.link,
|
|
71
|
-
language: channel.language,
|
|
72
|
-
categories: this.mapCategories(channel['itunes:category']),
|
|
73
|
-
explicit: channel['itunes:explicit'] === 'true',
|
|
74
|
-
imageUrl: channel['itunes:image']?.['@_href'],
|
|
75
|
-
author: channel['itunes:author'],
|
|
76
|
-
copyright: channel['copyright'],
|
|
77
|
-
fundingUrl: channel['podcast:funding']?.['@_url'],
|
|
78
|
-
type: channel['itunes:type'],
|
|
79
|
-
episodes: this.mapEpisodes(channel.item),
|
|
80
|
-
};
|
|
81
|
-
}
|
|
82
|
-
mapCategories(categories) {
|
|
83
|
-
if (!categories) {
|
|
84
|
-
return [];
|
|
85
|
-
}
|
|
86
|
-
return categories.flatMap((category) => [
|
|
87
|
-
{ name: category['@_text'] },
|
|
88
|
-
...(category['itunes:category']?.map((sub) => ({ name: sub['@_text'] })) || []),
|
|
89
|
-
]);
|
|
90
|
-
}
|
|
91
|
-
mapEpisodes(items) {
|
|
92
|
-
if (!items) {
|
|
93
|
-
return [];
|
|
94
|
-
}
|
|
95
|
-
return items.map((item) => ({
|
|
96
|
-
title: item.title,
|
|
97
|
-
enclosure: this.mapEnclosure(item.enclosure),
|
|
98
|
-
guid: item.guid['#text'],
|
|
99
|
-
linkUrl: item.link,
|
|
100
|
-
pubDate: item.pubDate,
|
|
101
|
-
description: item.description,
|
|
102
|
-
durationInSeconds: item['itunes:duration'],
|
|
103
|
-
imageUrl: item['itunes:image']?.['@_href'],
|
|
104
|
-
explicit: item['itunes:explicit'] === 'yes',
|
|
105
|
-
number: item['itunes:episode'],
|
|
106
|
-
season: item['itunes:season'],
|
|
107
|
-
type: item['itunes:episodeType'],
|
|
108
|
-
}));
|
|
109
|
-
}
|
|
110
|
-
mapEnclosure(enclosure) {
|
|
111
|
-
if (!enclosure?.[0]) {
|
|
112
|
-
return undefined;
|
|
113
|
-
}
|
|
114
|
-
return {
|
|
115
|
-
url: enclosure[0]['@_url'],
|
|
116
|
-
type: enclosure[0]['@_type'],
|
|
117
|
-
length: enclosure[0]['@_length'],
|
|
118
|
-
};
|
|
119
|
-
}
|
|
120
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"podcast.spec.d.ts","sourceRoot":"","sources":["../../src/podcast/podcast.spec.ts"],"names":[],"mappings":""}
|