Skip to content

Commit a53c26f

Browse files
committed
Add support for <podcast:medium> tag. v0.7.1
1 parent ee5b505 commit a53c26f

File tree

9 files changed

+256
-13
lines changed

9 files changed

+256
-13
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
## 0.7.1
2+
3+
- Add support for PC2.0 `<podcast:medium>` tag.
4+
15
## 0.7.0
26

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

lib/src/model/item.dart

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,9 @@ class Item {
117117
required Map<String, dynamic>? json,
118118
ResultType type = ResultType.itunes,
119119
}) {
120-
return type == ResultType.itunes ? _fromItunes(json!) : _fromPodcastIndex(json!);
120+
return type == ResultType.itunes
121+
? _fromItunes(json!)
122+
: _fromPodcastIndex(json!);
121123
}
122124

123125
static Item _fromItunes(Map<String, dynamic> json) {
@@ -144,7 +146,8 @@ class Item {
144146
artworkUrl600: json['artworkUrl600'] as String?,
145147
genre: json['genreIds'] == null
146148
? <Genre>[]
147-
: Item._loadGenres(json['genreIds'].cast<String>(), json['genres'].cast<String>()),
149+
: Item._loadGenres(
150+
json['genreIds'].cast<String>(), json['genres'].cast<String>()),
148151
releaseDate: DateTime.tryParse(json['releaseDate'] ?? ''),
149152
country: json['country'] as String?,
150153
primaryGenreName: json['primaryGenreName'] as String?,
@@ -153,14 +156,16 @@ class Item {
153156
}
154157

155158
static Item _fromPodcastIndex(Map<String, dynamic> json) {
156-
int pubDateSeconds = json['lastUpdateTime'] ?? DateTime.now().millisecondsSinceEpoch ~/ 1000;
159+
int pubDateSeconds =
160+
json['lastUpdateTime'] ?? DateTime.now().millisecondsSinceEpoch ~/ 1000;
157161

158162
var pubDate = Duration(seconds: pubDateSeconds);
159163
var categories = json['categories'];
160164
var genres = <Genre>[];
161165

162166
if (categories != null) {
163-
categories.forEach((key, value) => genres.add(Genre(int.parse(key), value)));
167+
categories
168+
.forEach((key, value) => genres.add(Genre(int.parse(key), value)));
164169
}
165170

166171
return Item(

lib/src/model/medium.dart

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// Copyright (c) 2019 Ben Hills and the project contributors. Use of this source
2+
// code is governed by a MIT license that can be found in the LICENSE file.
3+
4+
/// This enum represents the PC2.0 [podcast:medium](https://github.com/Podcastindex-org/podcast-namespace/blob/main/docs/1.0.md#medium) tag.
5+
enum Medium {
6+
podcast,
7+
music,
8+
video,
9+
film,
10+
audiobook,
11+
newsletter,
12+
blog,
13+
podcastL,
14+
musicL,
15+
videoL,
16+
filmL,
17+
audiobookL,
18+
newsletterL,
19+
blogL;
20+
}

lib/src/model/podcast.dart

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import 'package:podcast_search/src/model/block.dart';
1111
import 'package:podcast_search/src/model/chapter.dart';
1212
import 'package:podcast_search/src/model/chapter_headers.dart';
1313
import 'package:podcast_search/src/model/locked.dart';
14+
import 'package:podcast_search/src/model/medium.dart';
1415
import 'package:podcast_search/src/model/person.dart';
1516
import 'package:podcast_search/src/model/remote_item.dart';
1617
import 'package:podcast_search/src/model/value_recipient.dart';
@@ -68,6 +69,8 @@ class Podcast {
6869
/// A list of remote items at the channel level
6970
final List<RemoteItem> remoteItems;
7071

72+
final Medium medium;
73+
7174
Podcast._({
7275
this.guid,
7376
this.url,
@@ -77,6 +80,7 @@ class Podcast {
7780
this.image,
7881
this.copyright,
7982
this.locked,
83+
this.medium = Medium.podcast,
8084
this.funding = const <Funding>[],
8185
this.persons = const <Person>[],
8286
this.value = const <Value>[],
@@ -183,6 +187,7 @@ class Podcast {
183187
var persons = <Person>[];
184188
var block = <Block>[];
185189
var value = <Value>[];
190+
var medium = Medium.podcast;
186191

187192
var guid = rssFeed.podcastIndex?.guid;
188193

@@ -226,6 +231,25 @@ class Podcast {
226231
}
227232
}
228233

234+
if (rssFeed.podcastIndex!.medium != null) {
235+
medium = switch (rssFeed.podcastIndex!.medium) {
236+
'podcastL' => Medium.podcastL,
237+
'music' => Medium.music,
238+
'musicL' => Medium.musicL,
239+
'video' => Medium.video,
240+
'videoL' => Medium.videoL,
241+
'film' => Medium.film,
242+
'filmL' => Medium.filmL,
243+
'audiobook' => Medium.audiobook,
244+
'audiobookL' => Medium.audiobookL,
245+
'newsletter' => Medium.newsletter,
246+
'newsletterL' => Medium.newsletterL,
247+
'blog' => Medium.blog,
248+
'blogL' => Medium.blogL,
249+
_ => Medium.podcast
250+
};
251+
}
252+
229253
if (rssFeed.podcastIndex!.value != null) {
230254
for (var v in rssFeed.podcastIndex!.value!) {
231255
var recipients = <ValueRecipient>[];
@@ -271,6 +295,7 @@ class Podcast {
271295
persons: persons,
272296
block: block,
273297
value: value,
298+
medium: medium,
274299
episodes: episodes,
275300
remoteItems: remoteItems,
276301
);

lib/src/search/itunes_search.dart

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,9 @@ final class ITunesSearch extends BaseSearch {
7777
connectTimeout: Duration(milliseconds: timeout),
7878
receiveTimeout: Duration(milliseconds: timeout),
7979
headers: {
80-
'User-Agent': userAgent == null || userAgent.isEmpty ? podcastSearchAgent : userAgent,
80+
'User-Agent': userAgent == null || userAgent.isEmpty
81+
? podcastSearchAgent
82+
: userAgent,
8183
},
8284
),
8385
);
@@ -114,7 +116,8 @@ final class ITunesSearch extends BaseSearch {
114116
setLastError(e);
115117
}
116118

117-
return SearchResult.fromError(lastError: lastError ?? '', lastErrorType: lastErrorType);
119+
return SearchResult.fromError(
120+
lastError: lastError ?? '', lastErrorType: lastErrorType);
118121
}
119122

120123
/// Fetches the list of top podcasts
@@ -150,7 +153,8 @@ final class ITunesSearch extends BaseSearch {
150153
setLastError(e);
151154
}
152155

153-
return SearchResult.fromError(lastError: lastError ?? '', lastErrorType: lastErrorType);
156+
return SearchResult.fromError(
157+
lastError: lastError ?? '', lastErrorType: lastErrorType);
154158
}
155159

156160
@override
@@ -176,7 +180,8 @@ final class ITunesSearch extends BaseSearch {
176180

177181
if (count == 0) {
178182
// ignore: avoid_print
179-
print('Warning: Could not find $title via lookup id: $feedApiEndpoint/lookup?id=$id - skipped');
183+
print(
184+
'Warning: Could not find $title via lookup id: $feedApiEndpoint/lookup?id=$id - skipped');
180185
}
181186

182187
if (count > 0 && results['results'] != null) {
@@ -192,7 +197,8 @@ final class ITunesSearch extends BaseSearch {
192197
setLastError(e);
193198
}
194199

195-
return SearchResult.fromError(lastError: lastError ?? '', lastErrorType: lastErrorType);
200+
return SearchResult.fromError(
201+
lastError: lastError ?? '', lastErrorType: lastErrorType);
196202
}
197203

198204
/// This internal method constructs a correctly encoded URL which is then
@@ -243,15 +249,19 @@ final class ITunesSearch extends BaseSearch {
243249
}
244250

245251
String _termParam() {
246-
return term != null && term!.isNotEmpty ? '?term=${Uri.encodeComponent(term!)}' : '';
252+
return term != null && term!.isNotEmpty
253+
? '?term=${Uri.encodeComponent(term!)}'
254+
: '';
247255
}
248256

249257
String _countryParam() {
250258
return _country != Country.none ? '&country=${_country.code}' : '';
251259
}
252260

253261
String _attributeParam() {
254-
return _attribute != Attribute.none ? '&attribute=${Uri.encodeComponent(_attribute!.attribute)}' : '';
262+
return _attribute != Attribute.none
263+
? '&attribute=${Uri.encodeComponent(_attribute!.attribute)}'
264+
: '';
255265
}
256266

257267
String _limitParam() {

pubspec.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
name: podcast_search
22
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.
33

4-
version: 0.7.0
4+
version: 0.7.1
55
homepage: https://github.com/amugofjava/podcast_search
66

77
environment:
@@ -12,7 +12,7 @@ dependencies:
1212
convert: ^3.0.1
1313
crypto: ^3.0.1
1414
dio: ^5.4.3+1
15-
rss_dart: ^1.0.8
15+
rss_dart: ^1.0.9
1616
meta: ^1.12.0
1717

1818
dev_dependencies:

test/podcast_load_test.dart

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// code is governed by a MIT license that can be found in the LICENSE file.
33

44
import 'package:podcast_search/podcast_search.dart';
5+
import 'package:podcast_search/src/model/medium.dart';
56
import 'package:test/test.dart';
67

78
void main() {
@@ -111,4 +112,28 @@ void main() {
111112
expect(item3.medium, 'music');
112113
});
113114
});
115+
116+
group('Medium test', () {
117+
test('No medium', () async {
118+
var podcast =
119+
await Podcast.loadFeedFile(file: 'test_resources/podcast1.rss');
120+
121+
expect(podcast.medium, Medium.podcast);
122+
});
123+
124+
test('Audiobook medium', () async {
125+
var podcast = await Podcast.loadFeedFile(
126+
file: 'test_resources/podcast-medium-audiobook.rss');
127+
128+
expect(podcast.medium, Medium.audiobook);
129+
});
130+
131+
test('Music list medium', () async {
132+
var podcast = await Podcast.loadFeedFile(
133+
file: 'test_resources/podcast-medium-music-list.rss');
134+
135+
expect(podcast.medium, Medium.musicL);
136+
expect(podcast.remoteItems.length, 2);
137+
});
138+
});
114139
}
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<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/">
3+
<channel>
4+
<title>Podcast Medium Test - Audiobook</title>
5+
<description>Unit medium test - audiobook</description>
6+
<link>https://nowhere.com/podcastsearchtest1</link>
7+
<pubDate>Thu, 16 May 2024 14:00:20 +0000</pubDate>
8+
<language>en</language>
9+
<image>
10+
<url>https://nowhere.com/podcastsearchtest1/image1.png</url>
11+
<title>Podcast Load Test 1</title>
12+
<link>https://nowhere.com/podcastsearchtest1</link>
13+
</image>
14+
<itunes:image href="https://nowhere.com/podcastsearchtest1/image1.png" />
15+
<itunes:category text="Comedy"><itunes:category text="Comedy Interviews" /></itunes:category>
16+
<itunes:category text="Society &amp; Culture"><itunes:category text="Relationships" /></itunes:category>
17+
<itunes:category text="TV &amp; Film"><itunes:category text="After Shows" /></itunes:category>
18+
<itunes:explicit>no</itunes:explicit>
19+
<itunes:summary>Unit test podcast test 1</itunes:summary>
20+
<itunes:author>Podcast Search Author</itunes:author>
21+
<itunes:owner>
22+
<itunes:name>Ben Hills</itunes:name>
23+
<itunes:email>[email protected]</itunes:email>
24+
</itunes:owner>
25+
<itunes:new-feed-url>podcast1.rss</itunes:new-feed-url>
26+
<itunes:type>episodic</itunes:type>
27+
<podcast:block>yes</podcast:block>
28+
<podcast:block id="google">no</podcast:block>
29+
<podcast:block id="amazon">yes</podcast:block>
30+
<podcast:medium>audiobook</podcast:medium>
31+
<podcast:value type="lightning" method="keysend" suggested="0.00000005000">
32+
<podcast:valueRecipient name="channel recipient1" type="node" address="03ae9f91a0cb8ff43840e3c322c55341a19d8c1c3cea15a25cfc425ac60a34e14a" split="80"/>
33+
<podcast:valueRecipient name="channel recipient2" type="node" address="03ae9f91a0cb8ff43840e3c322c55341a19d8c1c3cea15a25cfc425ac60a34e14b" split="20"/>
34+
</podcast:value>
35+
36+
<item>
37+
<title>Episode 001</title>
38+
<link>https://nowhere.com/podcastsearchtest1/podcast1</link>
39+
<enclosure url="https://nowhere.com/podcastsearchtest1/podcast1/episode001.mp3" length="0" type="audio/mpeg" />
40+
<itunes:duration>1200</itunes:duration>
41+
<itunes:explicit>yes</itunes:explicit>
42+
<itunes:episodeType>full</itunes:episodeType>
43+
<description><![CDATA[<div>Test of episode 001</div>]]></description>
44+
<itunes:summary>Episode summary 001</itunes:summary>
45+
<pubDate>Thu, 24 Jun 2021 18:00:00 +0000</pubDate>
46+
<guid isPermaLink="true"></guid>
47+
<itunes:author>Ben Hills</itunes:author>
48+
<dc:creator>Ben Hills</dc:creator>
49+
<itunes:keywords>Podcast, NowPlaying, Listen, Test</itunes:keywords>
50+
<media:keywords>Podcast, NowPlaying, Listen, Test</media:keywords>
51+
<media:rights status="userCreated" />
52+
</item>
53+
54+
<item>
55+
<title>Episode 002</title>
56+
<link>https://nowhere.com/podcastsearchtest1/podcast2</link>
57+
<enclosure url="https://nowhere.com/podcastsearchtest1/podcast1/episode002.mp3" length="0" type="audio/mpeg" />
58+
<itunes:duration>1200</itunes:duration>
59+
<itunes:explicit>no</itunes:explicit>
60+
<itunes:episodeType>full</itunes:episodeType>
61+
<description><![CDATA[<div>Test of episode 002</div>]]></description>
62+
<itunes:summary>Episode summary 002</itunes:summary>
63+
<guid isPermaLink="true"></guid>
64+
<itunes:author>Ben Hills</itunes:author>
65+
<dc:creator>Ben Hills</dc:creator>
66+
<itunes:keywords>Podcast, NowPlaying, Listen, Test</itunes:keywords>
67+
<media:keywords>Podcast, NowPlaying, Listen, Test</media:keywords>
68+
<media:rights status="userCreated" />
69+
</item>
70+
</channel>
71+
</rss>

0 commit comments

Comments
 (0)