Skip to content

Commit

Permalink
Add support for PC2.0 <podcast:remoteItem> tag.
Browse files Browse the repository at this point in the history
  • Loading branch information
amugofjava committed May 8, 2024
1 parent 842dd96 commit acc3f34
Show file tree
Hide file tree
Showing 6 changed files with 172 additions and 13 deletions.
19 changes: 13 additions & 6 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,15 +1,21 @@
## 0.7.0

- Add support for PC2.0 `<podcast:remoteItem>` tag (at channel level).

## 0.6.9

- Bug fix: handle empty fields from iTunes and PodcastIndex API call results.

## 0.6.8

- BREAKING CHANGE: Language is now a text parameter (2-3 letter code) rather than enum. This is because iTunes largely ignores language, but PodcastIndex can use it for trending podcasts.
- BREAKING CHANGE: Language is now a text parameter (2-3 letter code) rather than enum. This is because iTunes largely
ignores language, but PodcastIndex can use it for trending podcasts.

## 0.6.7

- Update example to handle null feed.
- Default podcast image to take iTunes version if available and fallback to channel version to be consistent with episode image handling.
- Default podcast image to take iTunes version if available and fallback to channel version to be consistent with
episode image handling.

## 0.6.6

Expand Down Expand Up @@ -63,7 +69,8 @@

## 0.5.1

- Breaking change: Search provider is now passed when instantiating a Search object, rather than passing one at search time.
- Breaking change: Search provider is now passed when instantiating a Search object, rather than passing one at search
time.
- Support for genres across iTunes & PodcastIndex.

## 0.5.0
Expand Down Expand Up @@ -128,15 +135,15 @@

## 0.3.1

- Add support for returning podcast chart.
- Add support for returning podcast chart.

## 0.3.0

- Fix formatting.
- Fix formatting.

## 0.2.9

- Fix lints for newer version of Pedantic.
- Fix lints for newer version of Pedantic.

## 0.2.8

Expand Down
26 changes: 23 additions & 3 deletions lib/src/model/podcast.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,20 @@ import 'dart:async';
import 'dart:convert';
import 'dart:io';

import 'package:podcast_search/src/model/block.dart';
import 'package:podcast_search/src/model/value_recipient.dart';
import 'package:rss_dart/dart_rss.dart';
import 'package:dio/dio.dart';
import 'package:podcast_search/podcast_search.dart';
import 'package:podcast_search/src/model/block.dart';
import 'package:podcast_search/src/model/chapter.dart';
import 'package:podcast_search/src/model/chapter_headers.dart';
import 'package:podcast_search/src/model/locked.dart';
import 'package:podcast_search/src/model/person.dart';
import 'package:podcast_search/src/model/remote_item.dart';
import 'package:podcast_search/src/model/value_recipient.dart';
import 'package:podcast_search/src/search/base_search.dart';
import 'package:podcast_search/src/utils/json_parser.dart';
import 'package:podcast_search/src/utils/srt_parser.dart';
import 'package:podcast_search/src/utils/utils.dart';
import 'package:rss_dart/dart_rss.dart';

/// This class represents a podcast and its episodes. The Podcast is instantiated with a feed URL which is
/// then parsed and the episode list generated.
Expand Down Expand Up @@ -64,6 +65,9 @@ class Podcast {
/// A list of current episodes.
final List<Episode> episodes;

/// A list of remote items at the channel level
final List<RemoteItem> remoteItems;

Podcast._({
this.guid,
this.url,
Expand All @@ -78,6 +82,7 @@ class Podcast {
this.value = const <Value>[],
this.block = const <Block>[],
this.episodes = const <Episode>[],
this.remoteItems = const <RemoteItem>[],
});

/// This method takes a Url pointing to an RSS feed containing the Podcast details and episodes. You
Expand Down Expand Up @@ -167,6 +172,7 @@ class Podcast {
static Podcast _loadFeed(RssFeed rssFeed, String url) {
// Parse the episodes
var episodes = <Episode>[];
var remoteItems = <RemoteItem>[];
var author = rssFeed.itunes!.author;
var locked = Locked(
locked: rssFeed.podcastIndex!.locked?.locked ?? false,
Expand All @@ -189,6 +195,19 @@ class Podcast {
}
}

if (rssFeed.podcastIndex!.remoteItem != null) {
for (var r in rssFeed.podcastIndex!.remoteItem!) {
if (r != null) {
remoteItems.add(RemoteItem(
feedGuid: r.feedGuid,
itemGuid: r.itemGuid,
feedUrl: r.feedUrl,
medium: r.medium,
));
}
}
}

if (rssFeed.podcastIndex!.persons != null) {
for (var p in rssFeed.podcastIndex!.persons!) {
persons.add(Person(
Expand Down Expand Up @@ -253,6 +272,7 @@ class Podcast {
block: block,
value: value,
episodes: episodes,
remoteItems: remoteItems,
);
}

Expand Down
17 changes: 17 additions & 0 deletions lib/src/model/remote_item.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// Copyright (c) 2019 Ben Hills and the project contributors. Use of this source
// code is governed by a MIT license that can be found in the LICENSE file.

/// This class represents a PC2.0 [remote item](https://github.com/Podcastindex-org/podcast-namespace/blob/main/docs/1.0.md#remote-item)
class RemoteItem {
String feedGuid;
String? itemGuid;
String? feedUrl;
String? medium;

RemoteItem({
required this.feedGuid,
this.itemGuid,
this.feedUrl,
this.medium,
});
}
4 changes: 2 additions & 2 deletions pubspec.yaml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name: podcast_search
description: A library for searching for podcasts and parsing podcast RSS feeds. Supports iTunes and PodcastIndex directories, and newer features such as chapters and transcripts.

version: 0.6.9
version: 0.7.0
homepage: https://github.com/amugofjava/podcast_search

environment:
Expand All @@ -12,7 +12,7 @@ dependencies:
convert: ^3.0.1
crypto: ^3.0.1
dio: ^5.4.3+1
rss_dart: ^1.0.6
rss_dart: ^1.0.7
meta: ^1.15.0

dev_dependencies:
Expand Down
40 changes: 38 additions & 2 deletions test/podcast_load_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -69,10 +69,46 @@ void main() {
});

test('Load podcast with no block tags', () async {
var podcast =
await Podcast.loadFeedFile(file: 'test_resources/podcast-no-block.rss');
var podcast = await Podcast.loadFeedFile(
file: 'test_resources/podcast-no-block.rss');

expect(podcast.block.length, 0);
});
});

group('Remote item test', () {
test('No remote items', () async {
var podcast = await Podcast.loadFeedFile(
file: 'test_resources/podcast-no-block.rss');

expect(podcast.remoteItems.length, 0);
});

test('Load podcast 3 remote items', () async {
var podcast = await Podcast.loadFeedFile(
file: 'test_resources/podcast-remote-item.rss');

expect(podcast.remoteItems.length, 3);

var item1 = podcast.remoteItems[0];
var item2 = podcast.remoteItems[1];
var item3 = podcast.remoteItems[2];

expect(item1.feedGuid, '917393e3-1b1e-5cef-ace4-edaa54e1f810');
expect(item1.itemGuid, null);
expect(item1.feedUrl, null);
expect(item1.medium, null);

expect(item2.feedGuid, '917393e3-1b1e-5cef-ace4-edaa54e1f811');
expect(item2.itemGuid, 'asdf089j0-ep240-20230510');
expect(item2.feedUrl, null);
expect(item2.medium, null);

expect(item3.feedGuid, '917393e3-1b1e-5cef-ace4-edaa54e1f812');
expect(item3.itemGuid, 'asdf089j0-ep240-20230511');
expect(item3.feedUrl,
'https://feeds.example.org/917393e3-1b1e-5cef-ace4-edaa54e1f811/rss.xml');
expect(item3.medium, 'music');
});
});
}
79 changes: 79 additions & 0 deletions test_resources/podcast-remote-item.rss
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd" xmlns:media="http://search.yahoo.com/mrss/" xmlns:georss="http://www.georss.org/georss" xmlns:audioboom="https://audioboom.com/rss/1.0" version="2.0" xml:base="https://audioboom.com/">
<channel>
<title>Podcast Load Test 2</title>
<description>Unit test podcast test 1</description>
<link>https://nowhere.com/podcastsearchtest1</link>
<pubDate>Thu, 24 Jun 2021 18:00:20 +0000</pubDate>
<language>en</language>
<image>
<url>https://nowhere.com/podcastsearchtest1/image1.png</url>
<title>Podcast Load Test 1</title>
<link>https://nowhere.com/podcastsearchtest1</link>
</image>
<itunes:image href="https://nowhere.com/podcastsearchtest1/image1.png" />
<itunes:category text="Comedy"><itunes:category text="Comedy Interviews" /></itunes:category>
<itunes:category text="Society &amp; Culture"><itunes:category text="Relationships" /></itunes:category>
<itunes:category text="TV &amp; Film"><itunes:category text="After Shows" /></itunes:category>
<itunes:explicit>no</itunes:explicit>
<itunes:summary>Unit test podcast test 1</itunes:summary>
<itunes:author>Podcast Search Author</itunes:author>
<itunes:owner>
<itunes:name>Ben Hills</itunes:name>
<itunes:email>anytime@amugofjava.me.uk</itunes:email>
</itunes:owner>
<itunes:new-feed-url>podcast1.rss</itunes:new-feed-url>
<itunes:type>episodic</itunes:type>
<podcast:value type="lightning" method="keysend" suggested="0.00000005000">
<podcast:valueRecipient name="channel recipient1" type="node" address="03ae9f91a0cb8ff43840e3c322c55341a19d8c1c3cea15a25cfc425ac60a34e14a" split="80"/>
<podcast:valueRecipient name="channel recipient2" type="node" address="03ae9f91a0cb8ff43840e3c322c55341a19d8c1c3cea15a25cfc425ac60a34e14b" split="20"/>
</podcast:value>

<podcast:remoteItem feedGuid="917393e3-1b1e-5cef-ace4-edaa54e1f810" />

<podcast:remoteItem
feedGuid="917393e3-1b1e-5cef-ace4-edaa54e1f811"
itemGuid="asdf089j0-ep240-20230510" />

<podcast:remoteItem
feedGuid="917393e3-1b1e-5cef-ace4-edaa54e1f812"
feedUrl="https://feeds.example.org/917393e3-1b1e-5cef-ace4-edaa54e1f811/rss.xml"
itemGuid="asdf089j0-ep240-20230511"
medium="music" />

<item>
<title>Episode 001</title>
<link>https://nowhere.com/podcastsearchtest1/podcast1</link>
<enclosure url="https://nowhere.com/podcastsearchtest1/podcast1/episode001.mp3" length="0" type="audio/mpeg" />
<itunes:duration>1200</itunes:duration>
<itunes:explicit>yes</itunes:explicit>
<itunes:episodeType>full</itunes:episodeType>
<description><![CDATA[<div>Test of episode 001</div>]]></description>
<itunes:summary>Episode summary 001</itunes:summary>
<pubDate>Thu, 24 Jun 2021 18:00:00 +0000</pubDate>
<guid isPermaLink="true"></guid>
<itunes:author>Ben Hills</itunes:author>
<dc:creator>Ben Hills</dc:creator>
<itunes:keywords>Podcast, NowPlaying, Listen, Test</itunes:keywords>
<media:keywords>Podcast, NowPlaying, Listen, Test</media:keywords>
<media:rights status="userCreated" />
</item>

<item>
<title>Episode 002</title>
<link>https://nowhere.com/podcastsearchtest1/podcast2</link>
<enclosure url="https://nowhere.com/podcastsearchtest1/podcast1/episode002.mp3" length="0" type="audio/mpeg" />
<itunes:duration>1200</itunes:duration>
<itunes:explicit>no</itunes:explicit>
<itunes:episodeType>full</itunes:episodeType>
<description><![CDATA[<div>Test of episode 002</div>]]></description>
<itunes:summary>Episode summary 002</itunes:summary>
<guid isPermaLink="true"></guid>
<itunes:author>Ben Hills</itunes:author>
<dc:creator>Ben Hills</dc:creator>
<itunes:keywords>Podcast, NowPlaying, Listen, Test</itunes:keywords>
<media:keywords>Podcast, NowPlaying, Listen, Test</media:keywords>
<media:rights status="userCreated" />
</item>
</channel>
</rss>

0 comments on commit acc3f34

Please sign in to comment.