{"id":16256798,"url":"https://github.com/krestaino/podcast-xml-parser","last_synced_at":"2025-03-19T21:31:15.502Z","repository":{"id":186943196,"uuid":"676026447","full_name":"krestaino/podcast-xml-parser","owner":"krestaino","description":"🎙 Parse podcast feeds in browsers, React Native, or Node.js environments.","archived":false,"fork":false,"pushed_at":"2024-04-14T22:42:22.000Z","size":1246,"stargazers_count":5,"open_issues_count":1,"forks_count":1,"subscribers_count":1,"default_branch":"main","last_synced_at":"2024-04-22T09:37:57.418Z","etag":null,"topics":["javascript","node","parse","podcast","typescript","xml"],"latest_commit_sha":null,"homepage":"https://podcast-xml-parser.kmr.io","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/krestaino.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-08-08T09:08:17.000Z","updated_at":"2024-05-15T07:33:57.464Z","dependencies_parsed_at":null,"dependency_job_id":"d4ebfff4-0492-4de1-8e68-9477242eb8c0","html_url":"https://github.com/krestaino/podcast-xml-parser","commit_stats":null,"previous_names":["krestaino/podcast-xml-parser"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/krestaino%2Fpodcast-xml-parser","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/krestaino%2Fpodcast-xml-parser/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/krestaino%2Fpodcast-xml-parser/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/krestaino%2Fpodcast-xml-parser/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/krestaino","download_url":"https://codeload.github.com/krestaino/podcast-xml-parser/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":244056428,"owners_count":20390720,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["javascript","node","parse","podcast","typescript","xml"],"created_at":"2024-10-10T15:46:22.483Z","updated_at":"2025-03-19T21:31:15.192Z","avatar_url":"https://github.com/krestaino.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003c!-- HIDE_SECTION_START --\u003e\n\n[![GitHub Repo](https://img.shields.io/github/package-json/v/krestaino/podcast-xml-parser/main?label=GitHub)](https://github.com/krestaino/podcast-xml-parser)\n[![NPM Package](https://img.shields.io/npm/v/podcast-xml-parser?color=red)](https://www.npmjs.com/package/podcast-xml-parser)\n[![MIT License](https://img.shields.io/github/license/krestaino/podcast-xml-parser.svg)](https://raw.githubusercontent.com/krestaino/podcast-xml-parser/main/LICENSE.md)\n[![Build](https://img.shields.io/github/actions/workflow/status/krestaino/podcast-xml-parser/build.yml)](https://github.com/krestaino/podcast-xml-parser/actions/workflows/build.yml)\n[![Codecov](https://codecov.io/github/krestaino/podcast-xml-parser/graph/badge.svg?token=IS0T58N4FQ)](https://codecov.io/github/krestaino/podcast-xml-parser)\n[![Live Demo](https://img.shields.io/badge/demo-live-blueviolet)](https://podcast-xml-parser.kmr.io/)\n\n\u003c!-- HIDE_SECTION_END --\u003e\n\n# podcast-xml-parser\n\nParse podcast feeds in browsers, React Native, or Node.js environments.\n\n- **📜 Simple Parsing:** Parse XML podcast feeds into JavaScript objects.\n- **🔗 Versatile Input:** Parse from URLs, iTunes IDs, or directly from XML strings.\n- **💻 Cross-platform:** Designed to work in browsers, React Native, and Node.js.\n- **🚫 Graceful Handling:** In cases of missing elements in the XML feed, the parser returns empty strings instead of throwing errors.\n- **🎧 Support for iTunes:** Additional details can be fetched from iTunes.\n- **🔢 Partial Feed Support:** Allows fetching and parsing a specific byte range of a feed.\n- **🌐 OPML Parsing:** Extracts podcast feed URLs from OPML outlines, commonly used for podcast subscriptions.\n\n\u003c!-- HIDE_SECTION_START --\u003e\n\n## Live Demo\n\nWant to see the library in action? Check the [demo](https://podcast-xml-parser.kmr.io/) and test out `podcast-xml-parser` with various XML podcast feeds.\n\n\u003c!-- HIDE_SECTION_END --\u003e\n\n## Requirements\n\nThis library uses the [Fetch API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API). In environments where the Fetch API is not available, you need to use a polyfill. Notably, Node.js 17 or lower and Internet Explorer do not support the Fetch API. See [here](https://github.com/BuilderIO/this-package-uses-fetch) for more information about how to polyfill the Fetch API.\n\n## Installation\n\nYou can install the library using `npm` or `yarn`.\n\n```bash\nnpm install podcast-xml-parser\n# or\nyarn add podcast-xml-parser\n```\n\n## Basic Example\n\n```javascript\nimport { podcastXmlParser } from \"podcast-xml-parser\";\n\nconst url = new URL(\"https://feeds.simplecast.com/dHoohVNH\");\nconst { podcast } = await podcastXmlParser(url);\n\nconsole.log(podcast.title); // \"Conan O’Brien Needs A Friend\"\n```\n\n## Usage `podcastXmlParser()`\n\n**Purpose**: Parses a podcast's XML feed to retrieve podcast and episode details.\n\n**Parameters**:\n\n- `source` _(URL | number | string)_: The source of the XML content. Can be a URL object, an iTunes ID, or an XML string.\n- `config` _(Config)_: Configuration options for the request, like request size, request headers, pagination, or to additionally return iTunes details.\n\n**Returns**:\nA promise that resolves with an object containing:\n\n- `podcast`: Details of the podcast.\n- `episodes`: An array of episode details.\n- `itunes?`: Additional iTunes details.\n\n**Signature**:\n\n```typescript\npodcastXmlParser(source: URL | number | string, config?: Config): Promise\u003c{\n  podcast: Podcast;\n  episodes: Episode[];\n  itunes?: Itunes;\n}\u003e\n```\n\n## Usage `podcastOpmlParser()`\n\n**Purpose**: Parses an OPML outline to extract podcast feed URLs and optional titles.\n\n**Parameters**:\n\n- `source` _(URL | string)_: The source of the OPML content. Can be a URL object or an XML string.\n\n**Returns**:\nA promise that resolves with an array of objects, each containing a feed URL and an optional title extracted from the OPML outline.\n\n**Signature**:\n\n```typescript\npodcastOpmlParser(source: URL | string): Promise\u003c{ title?: string; url: string }[]\u003e\n```\n\n## Configuration Options\n\nThe `podcastXmlParser` function accepts a configuration object as its second parameter, allowing you to customize various aspects of the parsing process.\n\n#### `requestHeaders`: Record\u003cstring, string\u003e\n\nAllows you to set custom headers for the HTTP request when fetching the XML feed. This can be useful for setting a custom `User-Agent` or other headers required by the server. If no `User-Agent` is specified, the default user agent `podcast-xml-parser/{version}` is used, where `{version}` is the version of the library.\n\n```javascript\nconst config = {\n  requestHeaders: {\n    \"User-Agent\": \"MyCustomUserAgent/1.0\",\n  },\n}; // Sets a custom User-Agent header\n```\n\n#### `requestSize`: number\n\nSpecifies the number of bytes to fetch from the XML feed, allowing you to limit the size of the request. Useful for improving response times when you only need a portion of the feed.\n\n```javascript\nconst config = { requestSize: 50000 }; // First 50,000 bytes of the feed\n```\n\n#### `start`: number\n\nThe starting index for episode pagination. Combined with the `limit` option, this allows you to paginate through the episodes in the feed.\n\n```javascript\nconst config = { start: 0, limit: 10 }; // Retrieves the last 10 episodes\n```\n\n#### `limit`: number\n\nThe number of episodes to retrieve from the starting index. Used in conjunction with the `start` option for pagination.\n\n```javascript\nconst config = { start: 5, limit: 5 }; // Retrieves episodes 6 through 10\n```\n\n#### `itunes`: boolean\n\nA boolean flag to control whether iTunes data is retrieved. If set to `true`, the parser will fetch additional details from iTunes based on the podcast's feed URL.\n\n```javascript\nconst config = { itunes: true }; // Fetch additional details from iTunes\n```\n\n## Examples\n\n### From a URL\n\nWhen parsing a URL, you must call `new URL()` otherwise the library will try to parse the URL as XML, rather than the contents of the URL.\n\n```javascript\nimport { podcastXmlParser } from \"podcast-xml-parser\";\n\nconst url = new URL(\"https://feeds.simplecast.com/dHoohVNH\"); // Use 'new URL()' to ensure the library treats it as a URL\nconst { podcast, episodes, itunes } = await podcastXmlParser(url);\n\nconsole.log(podcast.title); // \"Conan O’Brien Needs A Friend\"\nconsole.log(episodes[episodes.length - 1].title); // \"Introducing Conan’s new podcast\"\nconsole.log(itunes.collectionId); // 1438054347\n```\n\n### From an iTunes ID\n\nWhen parsing from an iTunes ID, make sure the value is a number.\n\n```javascript\nimport { podcastXmlParser } from \"podcast-xml-parser\";\n\nconst collectionId = 1438054347; // iTunes collectionId\nconst { podcast } = await podcastXmlParser(collectionId);\n\nconsole.log(podcast.title); // \"Conan O’Brien Needs A Friend\"\n```\n\n### From an XML string\n\nYou can read from the filesystem or pass a string.\n\n#### From the filesystem\n\n```javascript\nimport fs from \"fs\";\nimport { podcastXmlParser } from \"podcast-xml-parser\";\n\nconst xmlData = fs.readFileSync(\"test.xml\", \"utf-8\");\nconst { podcast } = await podcastXmlParser(xmlData);\n\nconsole.log(podcast.title);\n```\n\n#### From a string\n\n```javascript\nimport { podcastXmlParser } from \"podcast-xml-parser\";\n\nconst xmlString = `\n  \u003crss version=\"2.0\"\u003e\n    \u003cchannel\u003e\n      \u003ctitle\u003ePodcast Title\u003c/title\u003e\n      \u003citem\u003e\n        \u003ctitle\u003eEpisode 1 Title\u003c/title\u003e\n      \u003c/item\u003e\n    \u003c/channel\u003e\n  \u003c/rss\u003e\n`;\n\nconst { podcast, episodes } = await podcastXmlParser(xmlString);\n\nconsole.log(podcast.title); // Podcast Title\nconsole.log(episodes[0].title); // Episode 1 Title\n```\n\n### iTunes Data\n\nYou can optionally return additional data from iTunes. This can be useful for obtaining consistent and reasonable artwork. Some podcast feeds use large uncompressed artwork. The artwork returned from the iTunes response is compressed and standardized, making it much easier to work with. You can see the data structure in the [iTunes Search API documentation](https://developer.apple.com/library/archive/documentation/AudioVideo/Conceptual/iTuneSearchAPI/UnderstandingSearchResults.html). Enabling this feature requires an additional network request.\n\n```javascript\nimport { podcastXmlParser } from \"podcast-xml-parser\";\n\nconst url = new URL(\"https://feeds.simplecast.com/dHoohVNH\"); // From a URL\nconst config = { itunes: true }; // Enable additional details from iTunes\nconst { podcast, itunes } = await podcastXmlParser(url, config);\n\nconsole.log(podcast.title); // \"Conan O’Brien Needs A Friend\"\nconsole.log(itunes.artworkUrl600); // \"https://is1-ssl.mzstatic.com/image/thumb/Podcasts116/v4/0f/89/ef/0f89ef97-d10c-4b90-6127-ccee04776d54/mza_5706050832923787468.jpg/600x600bb.jpg\"\n```\n\n**Note**: If the podcast is not available on iTunes, the `itunes` property in the returned object from the `podcastXmlParser()` function will be `undefined`.\n\n### Partial Feed\n\nYou can limit the request size of the XML fetch to improve response times. This can be useful to return the latest episodes quickly when you don't need the entire feed.\n\n```javascript\nimport { podcastXmlParser } from \"podcast-xml-parser\";\n\nconst url = new URL(\"https://feeds.simplecast.com/54nAGcIl\"); // From a URL with a huge feed\nconst config = { requestSize: 50000, itunes: true }; // First 50,000 bytes of the feed\nconst { episodes, itunes } = await podcastXmlParser(url, config);\n\nconsole.log(episodes.length !== itunes.trackCount); // true\n```\n\n**Note**: To use the `requestSize` option, it is not necessary to enable the iTunes data fetch by setting `itunes: true`. In the above example, the `itunes: true` option is used to demonstrate that the partial feed is returning a subset of episodes. By fetching additional iTunes data, we can compare the total number of episodes from the iTunes metadata with the number of episodes returned in the partial feed.\n\n### Pagination\n\nPagination allows you to control the number of episodes returned by the parser. It lets you define the starting point and the limit for fetching the episodes. When parsing a partial feed with `requestSize` set, be aware that the episodes you request may not be in the feed.\n\n```javascript\nimport { podcastXmlParser } from \"podcast-xml-parser\";\n\nconst config = { start: 0, limit: 10 }; // Last 10 episodes\nconst { episodes } = await podcastXmlParser(1438054347, config); // From an iTunes ID\n\nconsole.log(episodes.length); // 10\n```\n\n## TypeScript\n\n```typescript\nimport { podcastXmlParser, Podcast, Episode, Itunes } from \"podcast-xml-parser\";\n\nconst url = new URL(\"https://feeds.simplecast.com/dHoohVNH\"); // From a URL\nconst { podcast }: { podcast: Podcast } = await podcastXmlParser(url);\n\nconsole.log(podcast.title); // \"Conan O’Brien Needs A Friend\"\n```\n\n### Types\n\nThe library defines the following custom types that can be used in your code:\n\n#### 1. `Podcast`\n\n```typescript\ninterface Podcast {\n  copyright: string;\n  contentEncoded: string;\n  description: string;\n  feedUrl: string;\n  image: {\n    link: string;\n    title: string;\n    url: string;\n  };\n  itunesAuthor: string;\n  itunesCategory: string;\n  itunesExplicit: boolean;\n  itunesImage: string;\n  itunesOwner: {\n    name: string;\n    email: string;\n  };\n  itunesSubtitle: string;\n  itunesSummary: string;\n  itunesType: string;\n  language: string;\n  link: string;\n  title: string;\n}\n```\n\n#### 2. `Episode`\n\n```typescript\ninterface Episode {\n  author: string;\n  contentEncoded: string;\n  description: string;\n  enclosure: {\n    url: string;\n    type: string;\n  };\n  guid: string;\n  itunesAuthor: string;\n  itunesDuration: number;\n  itunesEpisode: number | null;\n  itunesEpisodeType: string;\n  itunesExplicit: boolean;\n  itunesImage: string;\n  itunesSeason: number | null;\n  itunesSubtitle: string;\n  itunesSummary: string;\n  itunesTitle: string;\n  link: string;\n  pubDate: string;\n  title: string;\n}\n```\n\n#### 3. `Config`\n\n```typescript\ninterface Config {\n  start?: number;\n  limit?: number;\n  requestHeaders?: Record\u003cstring, string\u003e;\n  requestSize?: number;\n  itunes?: boolean;\n}\n```\n\n#### 4. `Itunes`\n\n**Note**: The `Itunes` type interface reflects the current structure of the iTunes API response. However, this structure is subject to change without notice, and fields may be absent in some responses. Developers should implement appropriate error handling to accommodate potential changes and missing fields in the iTunes data.\n\n```typescript\ninterface Itunes {\n  wrapperType?: string;\n  kind?: string;\n  collectionId?: number;\n  trackId?: number;\n  artistName?: string;\n  collectionName?: string;\n  trackName?: string;\n  collectionCensoredName?: string;\n  trackCensoredName?: string;\n  collectionViewUrl?: string;\n  feedUrl?: string;\n  trackViewUrl?: string;\n  artworkUrl30?: string;\n  artworkUrl60?: string;\n  artworkUrl100?: string;\n  artworkUrl600?: string;\n  collectionPrice?: number;\n  trackPrice?: number;\n  trackRentalPrice?: number;\n  collectionHdPrice?: number;\n  trackHdPrice?: number;\n  trackHdRentalPrice?: number;\n  releaseDate?: string;\n  collectionExplicitness?: string;\n  trackExplicitness?: string;\n  trackCount?: number;\n  country?: string;\n  currency?: string;\n  primaryGenreName?: string;\n  contentAdvisoryRating?: string;\n  genreIds?: string[];\n  genres?: string[];\n}\n```\n\n## Running Tests\n\nTo run the test suite, execute the following command:\n\n```bash\nnpm test\n# or\nyarn test\n```\n\n## Contribution\n\nContributions to `podcast-xml-parser` are welcome! If you find any bugs, have feature requests, or want to improve the library, feel free to open an issue or submit a pull request.\n\n## License\n\nThis project is licensed under the MIT License.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkrestaino%2Fpodcast-xml-parser","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkrestaino%2Fpodcast-xml-parser","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkrestaino%2Fpodcast-xml-parser/lists"}