podcast_index 0.1.0 → 0.2.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +10 -0
- data/Gemfile.lock +3 -5
- data/README.md +79 -4
- data/lib/podcast_index/api/episodes.rb +23 -0
- data/lib/podcast_index/api/podcasts.rb +21 -0
- data/lib/podcast_index/api/recent.rb +35 -0
- data/lib/podcast_index/api/search.rb +4 -4
- data/lib/podcast_index/api/value.rb +19 -0
- data/lib/podcast_index/episode.rb +48 -8
- data/lib/podcast_index/podcast.rb +81 -9
- data/lib/podcast_index/soundbite.rb +30 -0
- data/lib/podcast_index/value.rb +35 -0
- data/lib/podcast_index/version.rb +1 -1
- data/lib/podcast_index.rb +5 -0
- metadata +6 -3
- data/podcast_index.gemspec +0 -41
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: fc3ddf4428c6108e1b2e5c3e5a9886283d34234cd85ccb8c6d927129d73d09fc
|
4
|
+
data.tar.gz: ebe518a53c0aeed362d57922464c13caae545b7535c7ea8622442286a55e2bd2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 83fe55e0065975fe9c6ee193247dacf1ba710cbf41660065ca81b1bdf7964f626f2540f7d2f63b6ef74b8937e6c5a3161e4d401777e23e0bee7bcdd74cfa4f55
|
7
|
+
data.tar.gz: 69a5aef31c4658c5934029522dd66f8e5e9b7d8c4040978302e03457b6ba181c6a70ddad786a12c8dab0d7d5de74fbb770d54daef08f197b4aada0a6e7818351
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,15 @@
|
|
1
1
|
## [Unreleased]
|
2
2
|
|
3
|
+
## [0.2.1] - 2023-07-06
|
4
|
+
|
5
|
+
* Raise exception when `Podcast.find` returns no result ([#6](https://github.com/jasonyork/podcast-index/issues/6))
|
6
|
+
|
7
|
+
## [0.2.0] - 2023-04-24
|
8
|
+
|
9
|
+
* BREAKING: Switching to be more consistent with ActiveRecord conventions, using `find_by` and `where` methods for all models
|
10
|
+
* Add support for all "Search", "Podcasts", "Episodes", "Recent" and "Value" sections of the API. This introduces the `Soundbite` and `Value` domain models.
|
11
|
+
* Update README.md
|
12
|
+
|
3
13
|
## [0.1.0] - 2023-01-06
|
4
14
|
|
5
15
|
- Initial release
|
data/Gemfile.lock
CHANGED
@@ -1,25 +1,24 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
podcast_index (0.1
|
4
|
+
podcast_index (0.2.1)
|
5
5
|
activesupport (>= 6.0, < 8)
|
6
6
|
addressable (~> 2)
|
7
7
|
|
8
8
|
GEM
|
9
9
|
remote: https://rubygems.org/
|
10
10
|
specs:
|
11
|
-
activesupport (
|
11
|
+
activesupport (7.0.3.1)
|
12
12
|
concurrent-ruby (~> 1.0, >= 1.0.2)
|
13
13
|
i18n (>= 1.6, < 2)
|
14
14
|
minitest (>= 5.1)
|
15
15
|
tzinfo (~> 2.0)
|
16
|
-
zeitwerk (~> 2.3)
|
17
16
|
addressable (2.8.1)
|
18
17
|
public_suffix (>= 2.0.2, < 6.0)
|
19
18
|
ast (2.4.2)
|
20
19
|
codecov (0.6.0)
|
21
20
|
simplecov (>= 0.15, < 0.22)
|
22
|
-
concurrent-ruby (1.
|
21
|
+
concurrent-ruby (1.2.2)
|
23
22
|
crack (0.4.5)
|
24
23
|
rexml
|
25
24
|
debug (1.7.1)
|
@@ -94,7 +93,6 @@ GEM
|
|
94
93
|
addressable (>= 2.8.0)
|
95
94
|
crack (>= 0.3.2)
|
96
95
|
hashdiff (>= 0.4.0, < 2.0.0)
|
97
|
-
zeitwerk (2.6.6)
|
98
96
|
|
99
97
|
PLATFORMS
|
100
98
|
x86_64-darwin-20
|
data/README.md
CHANGED
@@ -32,6 +32,21 @@ In a Rails app, this configuration would typically be placed in an initializer f
|
|
32
32
|
|
33
33
|
## Usage
|
34
34
|
|
35
|
+
This client currently implements the following sections of the API:
|
36
|
+
* [Search](https://podcastindex-org.github.io/docs-api/#tag--Search)
|
37
|
+
* [Podcasts](https://podcastindex-org.github.io/docs-api/#tag--Podcasts)
|
38
|
+
* [Episodes](https://podcastindex-org.github.io/docs-api/#tag--Episodes)
|
39
|
+
* [Recent](https://podcastindex-org.github.io/docs-api/#tag--Recent)
|
40
|
+
* [Value](https://podcastindex-org.github.io/docs-api/#tag--Value)
|
41
|
+
|
42
|
+
These are exposed through the following domain models:
|
43
|
+
* [Episode](lib/podcast_index/episode.rb)
|
44
|
+
* [Podcast](lib/podcast_index/podcast.rb)
|
45
|
+
* [Soundbite](lib/podcast_index/soundbite.rb)
|
46
|
+
* [Value](lib/podcast_index/value.rb)
|
47
|
+
|
48
|
+
The intent is to follow ActiveRecord conventions as reasonably possible. Therefore, most of the requests are accessed through the model's `.find_by` and `.where` methods.
|
49
|
+
|
35
50
|
### Examples
|
36
51
|
|
37
52
|
Find a podcast by podcastindex id:
|
@@ -41,26 +56,86 @@ podcast = PodcastIndex::Podcast.find(920666)
|
|
41
56
|
podcast.title # => "Podcasting 2.0"
|
42
57
|
```
|
43
58
|
|
59
|
+
When the podcast cannot be found:
|
60
|
+
|
61
|
+
```ruby
|
62
|
+
begin
|
63
|
+
podcast = PodcastIndex::Podcast.find("invalid")
|
64
|
+
rescue PodcastIndex::PodcastNotFound
|
65
|
+
puts "Podcast not found"
|
66
|
+
end
|
67
|
+
```
|
68
|
+
|
44
69
|
Find an episode by guid:
|
45
70
|
|
46
71
|
```ruby
|
47
|
-
episode = PodcastIndex::Episode.
|
72
|
+
episode = PodcastIndex::Episode.find_by(guid: "PC2084", feedurl: "http://mp3s.nashownotes.com/pc20rss.xml")
|
48
73
|
episode.title # => "Episode 84: All Aboard to On-Board!"
|
49
74
|
```
|
50
75
|
|
76
|
+
Find a Value block by feed_id:
|
77
|
+
|
78
|
+
```ruby
|
79
|
+
value = PodcastIndex::Value.find_by(feed_id: 920666)
|
80
|
+
value.model.type # => "lightning"
|
81
|
+
```
|
82
|
+
|
51
83
|
Methods that return multiple results are represented as an array of objects:
|
52
84
|
|
53
85
|
```ruby
|
54
|
-
episodes = PodcastIndex::Episode.
|
86
|
+
episodes = PodcastIndex::Episode.where(person: "Adam Curry")
|
55
87
|
episodes.count # => 57
|
56
88
|
episodes.first.title # => "Episode #2: A conversation with Adam Curry"
|
57
89
|
```
|
58
90
|
|
91
|
+
```ruby
|
92
|
+
soundbite = PodcastIndex::Soundbite.where(recent: true)
|
93
|
+
soundbite.first.episode_id # => 15082076307
|
94
|
+
```
|
95
|
+
|
96
|
+
|
97
|
+
|
59
98
|
### Supported Methods
|
60
99
|
|
61
|
-
|
100
|
+
```ruby
|
101
|
+
# Episode
|
102
|
+
Episode.find(id, fulltext: nil)
|
103
|
+
Episode.find_by(guid, feedurl: nil, feedid: nil, fulltext: nil)
|
104
|
+
Episode.where(feed_id:, since: nil, max: nil, fulltext: nil)
|
105
|
+
Episode.where(feed_url:, since: nil, max: nil, fulltext: nil)
|
106
|
+
Episode.where(podcast_guid:, since: nil, max: nil, fulltext: nil)
|
107
|
+
Episode.where(live: true, max: nil)
|
108
|
+
Episode.where(itunes_id:, since: nil, max: nil, fulltext: nil)
|
109
|
+
Episode.where(person:, fulltext: nil)
|
110
|
+
Episode.where(recent: true, max: nil, exclude_string: nil, before: nil, fulltext: nil)
|
111
|
+
Episode.sample(max: nil, lang: nil, categories: [], exclude_categories: [], fulltext: nil) # Find a random episode
|
112
|
+
|
113
|
+
# Podcast
|
114
|
+
Podcast.find(id)
|
115
|
+
Podcast.find_by(feed_url)
|
116
|
+
Podcast.find_by(guid)
|
117
|
+
Podcast.find_by(itunes_id)
|
118
|
+
Podcast.where(tag:)
|
119
|
+
Podcast.where(medium:)
|
120
|
+
# Additional parameters only for searching with "music" medium
|
121
|
+
Podcast.where(medium: "music", term:, val: nil, aponly: nil, clean: nil, fulltext: nil)
|
122
|
+
Podcast.where(term:, val: nil, aponly: nil, clean: nil, fulltext: nil)
|
123
|
+
Podcast.where(title:, val: nil, clean: nil, fulltext: nil)
|
124
|
+
Podcast.where(trending: true, max: nil, since: nil, lang: nil, categories: [], exclude_categories: [])
|
125
|
+
Podcast.where(dead: true)
|
126
|
+
Podcast.where(recent: true, max: nil, since: nil, lang: nil, categories: [], exclude_categories: [])
|
127
|
+
Podcast.where(new: true, max: nil, since: nil, feedid: nil, desc: nil)
|
128
|
+
Podcast.where(newly_found: true, max: nil, since: nil)
|
129
|
+
|
130
|
+
# Soundbite
|
131
|
+
Soundbite.where(recent: true, max: nil)
|
132
|
+
|
133
|
+
# Value
|
134
|
+
Value.find_by(feed_id)
|
135
|
+
Value.find_by(feed_url)
|
136
|
+
```
|
62
137
|
|
63
|
-
The attributes of the
|
138
|
+
The attributes of the models mirror the names in the API, but have been translated to "underscore" format to more closely follow Ruby conventions. For example, the `lastUpdateTime` attribute for a `Podcast` is exposed as `last_update_time`.
|
64
139
|
|
65
140
|
### Exception Handling
|
66
141
|
|
@@ -4,30 +4,53 @@ module PodcastIndex
|
|
4
4
|
extend Request
|
5
5
|
|
6
6
|
class << self
|
7
|
+
# https://podcastindex-org.github.io/docs-api/#get-/episodes/byid
|
7
8
|
def by_id(id:, fulltext: nil)
|
8
9
|
response = get("/episodes/byid", id: id, fulltext: fulltext)
|
9
10
|
JSON.parse(response.body)
|
10
11
|
end
|
11
12
|
|
13
|
+
# https://podcastindex-org.github.io/docs-api/#get-/episodes/byfeedid
|
12
14
|
def by_feed_id(id:, since: nil, max: nil, fulltext: nil)
|
13
15
|
response = get("/episodes/byfeedid", id: id, since: since, max: max, fulltext: fulltext)
|
14
16
|
JSON.parse(response.body)
|
15
17
|
end
|
16
18
|
|
19
|
+
# https://podcastindex-org.github.io/docs-api/#get-/episodes/byfeedurl
|
17
20
|
def by_feed_url(url:, since: nil, max: nil, fulltext: nil)
|
18
21
|
response = get("/episodes/byfeedurl", url: url, since: since, max: max, fulltext: fulltext)
|
19
22
|
JSON.parse(response.body)
|
20
23
|
end
|
21
24
|
|
25
|
+
# https://podcastindex-org.github.io/docs-api/#get-/episodes/bypodcastguid
|
26
|
+
def by_podcast_guid(podcast_guid:, since: nil, max: nil, fulltext: nil)
|
27
|
+
response = get("/episodes/bypodcastguid", guid: podcast_guid, since: since, max: max, fulltext: fulltext)
|
28
|
+
JSON.parse(response.body)
|
29
|
+
end
|
30
|
+
|
31
|
+
# https://podcastindex-org.github.io/docs-api/#get-/episodes/byguid
|
22
32
|
def by_guid(guid:, feedurl: nil, feedid: nil, fulltext: nil)
|
23
33
|
response = get("/episodes/byguid", guid: guid, feedurl: feedurl, feedid: feedid, fulltext: fulltext)
|
24
34
|
JSON.parse(response.body)
|
25
35
|
end
|
26
36
|
|
37
|
+
# https://podcastindex-org.github.io/docs-api/#get-/episodes/byitunesid
|
27
38
|
def by_itunes_id(id:, since: nil, max: nil, fulltext: nil)
|
28
39
|
response = get("/episodes/byitunesid", id: id, since: since, max: max, fulltext: fulltext)
|
29
40
|
JSON.parse(response.body)
|
30
41
|
end
|
42
|
+
|
43
|
+
# https://podcastindex-org.github.io/docs-api/#get-/episodes/live
|
44
|
+
def live(max: nil)
|
45
|
+
response = get("/episodes/live", max: max)
|
46
|
+
JSON.parse(response.body)
|
47
|
+
end
|
48
|
+
|
49
|
+
# https://podcastindex-org.github.io/docs-api/#get-/episodes/random
|
50
|
+
def random(max: nil, lang: nil, cat: nil, notcat: nil, fulltext: nil)
|
51
|
+
response = get("/episodes/random", max: max, lang: lang, cat: cat, notcat: notcat, fulltext: fulltext)
|
52
|
+
JSON.parse(response.body)
|
53
|
+
end
|
31
54
|
end
|
32
55
|
end
|
33
56
|
end
|
@@ -23,6 +23,27 @@ module PodcastIndex
|
|
23
23
|
response = get("/podcasts/byguid", guid: guid)
|
24
24
|
JSON.parse(response.body)
|
25
25
|
end
|
26
|
+
|
27
|
+
def by_tag(tag:)
|
28
|
+
params = {}.tap { |p| p[tag] = true }
|
29
|
+
response = get("/podcasts/bytag", params)
|
30
|
+
JSON.parse(response.body)
|
31
|
+
end
|
32
|
+
|
33
|
+
def by_medium(medium:)
|
34
|
+
response = get("/podcasts/bymedium", medium: medium)
|
35
|
+
JSON.parse(response.body)
|
36
|
+
end
|
37
|
+
|
38
|
+
def trending(max: nil, since: nil, lang: nil, cat: nil, notcat: nil)
|
39
|
+
response = get("/podcasts/trending", max: max, since: since, lang: lang, cat: cat, notcat: notcat)
|
40
|
+
JSON.parse(response.body)
|
41
|
+
end
|
42
|
+
|
43
|
+
def dead
|
44
|
+
response = get("/podcasts/dead", {})
|
45
|
+
JSON.parse(response.body)
|
46
|
+
end
|
26
47
|
end
|
27
48
|
end
|
28
49
|
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module PodcastIndex
|
2
|
+
module Api
|
3
|
+
class Recent
|
4
|
+
extend Request
|
5
|
+
|
6
|
+
class << self
|
7
|
+
def episodes(max: nil, exclude_string: nil, before: nil, fulltext: nil)
|
8
|
+
response = get("/recent/episodes", max: max, exclude_string: exclude_string, before: before,
|
9
|
+
fulltext: fulltext)
|
10
|
+
JSON.parse(response.body)
|
11
|
+
end
|
12
|
+
|
13
|
+
def feeds(max: nil, since: nil, lang: nil, cat: nil, notcat: nil)
|
14
|
+
response = get("/recent/feeds", max: max, since: since, lang: lang, cat: cat, notcat: notcat)
|
15
|
+
JSON.parse(response.body)
|
16
|
+
end
|
17
|
+
|
18
|
+
def new_feeds(max: nil, since: nil, feedid: nil, desc: nil)
|
19
|
+
response = get("/recent/newfeeds", max: max, since: since, feedid: feedid, desc: desc)
|
20
|
+
JSON.parse(response.body)
|
21
|
+
end
|
22
|
+
|
23
|
+
def data(max: nil, since: nil)
|
24
|
+
response = get("/recent/data", max: max, since: since)
|
25
|
+
JSON.parse(response.body)
|
26
|
+
end
|
27
|
+
|
28
|
+
def soundbites(max: nil)
|
29
|
+
response = get("/recent/soundbites", max: max)
|
30
|
+
JSON.parse(response.body)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -19,10 +19,10 @@ module PodcastIndex
|
|
19
19
|
JSON.parse(response.body)
|
20
20
|
end
|
21
21
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
22
|
+
def music_by_term(term:, val: nil, aponly: nil, clean: nil, fulltext: nil)
|
23
|
+
response = get("/search/music/byterm", q: term, val: val, aponly: aponly, clean: clean, fulltext: fulltext)
|
24
|
+
JSON.parse(response.body)
|
25
|
+
end
|
26
26
|
end
|
27
27
|
end
|
28
28
|
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module PodcastIndex
|
2
|
+
module Api
|
3
|
+
class Value
|
4
|
+
extend Request
|
5
|
+
|
6
|
+
class << self
|
7
|
+
def by_feed_id(id:)
|
8
|
+
response = get("/value/byfeedid", id: id)
|
9
|
+
JSON.parse(response.body)
|
10
|
+
end
|
11
|
+
|
12
|
+
def by_feed_url(url:)
|
13
|
+
response = get("/value/byfeedurl", url: url)
|
14
|
+
JSON.parse(response.body)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -4,18 +4,50 @@ require "delegate"
|
|
4
4
|
module PodcastIndex
|
5
5
|
class Episode < SimpleDelegator
|
6
6
|
class << self
|
7
|
+
FIND_ONE_ATTRIBUTES = %i[guid].freeze
|
8
|
+
FIND_MANY_ATTRIBUTES = %i[feed_id feed_url podcast_guid live itunes_id person recent].freeze
|
9
|
+
|
7
10
|
def find(id, fulltext: nil)
|
8
11
|
response = Api::Episodes.by_id(id: id, fulltext: fulltext)
|
9
12
|
from_response(response)
|
10
13
|
end
|
11
14
|
|
12
|
-
def
|
15
|
+
def find_by(attributes)
|
16
|
+
match = (attributes.keys & FIND_ONE_ATTRIBUTES)
|
17
|
+
raise ArgumentError, "Must supply one of the attributes: #{FIND_ONE_ATTRIBUTES}" unless match.present?
|
18
|
+
raise ArgumentError, "Must supply only one of the attributes: #{FIND_ONE_ATTRIBUTES}" if match.length > 1
|
19
|
+
|
20
|
+
send("find_by_#{match.first}", attributes[match.first])
|
21
|
+
end
|
22
|
+
|
23
|
+
def where(attributes)
|
24
|
+
match = (attributes.keys & FIND_MANY_ATTRIBUTES)
|
25
|
+
raise ArgumentError, "Must supply one of the attributes: #{FIND_MANY_ATTRIBUTES}" unless match.present?
|
26
|
+
raise ArgumentError, "Must supply only one of the attributes: #{FIND_MANY_ATTRIBUTES}" if match.length > 1
|
27
|
+
|
28
|
+
send("find_all_by_#{match.first}", **attributes)
|
29
|
+
end
|
30
|
+
|
31
|
+
def sample(max: nil, lang: nil, categories: [], exclude_categories: [], fulltext: nil)
|
32
|
+
response = Api::Episodes.random(max: max, lang: lang, cat: categories.join(","),
|
33
|
+
notcat: exclude_categories.join(","), fulltext: fulltext)
|
34
|
+
from_response_collection(response, "episodes")
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def find_all_by_feed_id(feed_id:, since: nil, max: nil, fulltext: nil)
|
13
40
|
response = Api::Episodes.by_feed_id(id: feed_id, since: since, max: max, fulltext: fulltext)
|
14
41
|
from_response_collection(response)
|
15
42
|
end
|
16
43
|
|
17
|
-
def
|
18
|
-
response = Api::Episodes.by_feed_url(url:
|
44
|
+
def find_all_by_feed_url(feed_url:, since: nil, max: nil, fulltext: nil)
|
45
|
+
response = Api::Episodes.by_feed_url(url: feed_url, since: since, max: max, fulltext: fulltext)
|
46
|
+
from_response_collection(response)
|
47
|
+
end
|
48
|
+
|
49
|
+
def find_all_by_podcast_guid(podcast_guid:, since: nil, max: nil, fulltext: nil)
|
50
|
+
response = Api::Episodes.by_podcast_guid(podcast_guid: podcast_guid, since: since, max: max, fulltext: fulltext)
|
19
51
|
from_response_collection(response)
|
20
52
|
end
|
21
53
|
|
@@ -24,25 +56,33 @@ module PodcastIndex
|
|
24
56
|
from_response(response)
|
25
57
|
end
|
26
58
|
|
27
|
-
def
|
59
|
+
def find_all_by_live(live:, max: nil) # rubocop:disable Lint/UnusedMethodArgument
|
60
|
+
response = Api::Episodes.live(max: max)
|
61
|
+
from_response_collection(response)
|
62
|
+
end
|
63
|
+
|
64
|
+
def find_all_by_itunes_id(itunes_id:, since: nil, max: nil, fulltext: nil)
|
28
65
|
response = Api::Episodes.by_itunes_id(id: itunes_id, since: since, max: max, fulltext: fulltext)
|
29
66
|
from_response_collection(response)
|
30
67
|
end
|
31
68
|
|
32
|
-
def
|
69
|
+
def find_all_by_person(person:, fulltext: nil)
|
33
70
|
response = Api::Search.by_person(person: person, fulltext: fulltext)
|
34
71
|
from_response_collection(response)
|
35
72
|
end
|
36
73
|
|
37
|
-
|
74
|
+
def find_all_by_recent(recent:, max: nil, exclude_string: nil, before: nil, fulltext: nil) # rubocop:disable Lint/UnusedMethodArgument
|
75
|
+
response = Api::Recent.episodes(max: max, exclude_string: exclude_string, before: before, fulltext: fulltext)
|
76
|
+
from_response_collection(response)
|
77
|
+
end
|
38
78
|
|
39
79
|
def from_response(response)
|
40
80
|
feed = response["episode"].transform_keys(&:underscore)
|
41
81
|
new(JSON.parse(feed.to_json, object_class: OpenStruct)) # rubocop:disable Style/OpenStructUse
|
42
82
|
end
|
43
83
|
|
44
|
-
def from_response_collection(response)
|
45
|
-
response[
|
84
|
+
def from_response_collection(response, collection_key = "items")
|
85
|
+
response[collection_key].map do |item|
|
46
86
|
episode = item.transform_keys(&:underscore)
|
47
87
|
new(JSON.parse(episode.to_json, object_class: OpenStruct)) # rubocop:disable Style/OpenStructUse
|
48
88
|
end
|
@@ -1,16 +1,42 @@
|
|
1
1
|
require "ostruct"
|
2
2
|
require "delegate"
|
3
3
|
|
4
|
+
# rubocop:disable Metrics/ParameterLists, Lint/UnusedMethodArgument
|
4
5
|
module PodcastIndex
|
5
6
|
class Podcast < SimpleDelegator
|
6
7
|
class << self
|
8
|
+
FIND_ONE_ATTRIBUTES = %i[feed_url guid itunes_id].freeze
|
9
|
+
FIND_MANY_ATTRIBUTES = %i[tag medium term title trending dead recent new newly_found].freeze
|
10
|
+
|
7
11
|
def find(id)
|
8
12
|
response = Api::Podcasts.by_feed_id(id: id)
|
13
|
+
raise PodcastIndex::PodcastNotFound, response["description"] if response["feed"].empty?
|
14
|
+
|
9
15
|
from_response(response)
|
10
16
|
end
|
11
17
|
|
12
|
-
def
|
13
|
-
|
18
|
+
def find_by(attributes)
|
19
|
+
match = (attributes.keys & FIND_ONE_ATTRIBUTES)
|
20
|
+
raise ArgumentError, "Must supply one of the attributes: #{FIND_ONE_ATTRIBUTES}" unless match.present?
|
21
|
+
raise ArgumentError, "Must supply only one of the attributes: #{FIND_ONE_ATTRIBUTES}" if match.length > 1
|
22
|
+
|
23
|
+
send("find_by_#{match.first}", attributes[match.first])
|
24
|
+
end
|
25
|
+
|
26
|
+
def where(attributes)
|
27
|
+
return find_all_music_by_term(**attributes) if attributes[:medium] == "music" && attributes[:term].present?
|
28
|
+
|
29
|
+
match = (attributes.keys & FIND_MANY_ATTRIBUTES)
|
30
|
+
raise ArgumentError, "Must supply one of the attributes: #{FIND_MANY_ATTRIBUTES}" unless match.present?
|
31
|
+
raise ArgumentError, "Must supply only one of the attributes: #{FIND_MANY_ATTRIBUTES}" if match.length > 1
|
32
|
+
|
33
|
+
send("find_all_by_#{match.first}", **attributes)
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def find_by_feed_url(feed_url)
|
39
|
+
response = Api::Podcasts.by_feed_url(url: feed_url)
|
14
40
|
from_response(response)
|
15
41
|
end
|
16
42
|
|
@@ -19,22 +45,67 @@ module PodcastIndex
|
|
19
45
|
from_response(response)
|
20
46
|
end
|
21
47
|
|
22
|
-
def find_by_itunes_id(
|
23
|
-
response = Api::Podcasts.by_itunes_id(id:
|
48
|
+
def find_by_itunes_id(itunes_id)
|
49
|
+
response = Api::Podcasts.by_itunes_id(id: itunes_id)
|
24
50
|
from_response(response)
|
25
51
|
end
|
26
52
|
|
27
|
-
def
|
53
|
+
def find_all_by_tag(tag:)
|
54
|
+
response = Api::Podcasts.by_tag(tag: tag)
|
55
|
+
from_response_collection(response)
|
56
|
+
end
|
57
|
+
|
58
|
+
def find_all_music_by_term(medium:, term:, val: nil, aponly: nil, clean: nil, fulltext: nil)
|
59
|
+
response = Api::Search.music_by_term(term: term, val: val, aponly: aponly, clean: clean, fulltext: fulltext)
|
60
|
+
from_response_collection(response)
|
61
|
+
end
|
62
|
+
|
63
|
+
def find_all_by_medium(medium:)
|
64
|
+
response = Api::Podcasts.by_medium(medium: medium)
|
65
|
+
from_response_collection(response)
|
66
|
+
end
|
67
|
+
|
68
|
+
def find_all_by_trending(trending:, max: nil, since: nil, lang: nil, categories: [], exclude_categories: [])
|
69
|
+
response = Api::Podcasts.trending(max: max, since: since, lang: lang, cat: categories.join(","),
|
70
|
+
notcat: exclude_categories.join(","))
|
71
|
+
from_response_collection(response)
|
72
|
+
end
|
73
|
+
|
74
|
+
def find_all_by_dead(*)
|
75
|
+
response = Api::Podcasts.dead
|
76
|
+
from_response_collection(response)
|
77
|
+
end
|
78
|
+
|
79
|
+
def find_all_by_term(term:, val: nil, aponly: nil, clean: nil, fulltext: nil)
|
28
80
|
response = Api::Search.by_term(term: term, val: val, aponly: aponly, clean: clean, fulltext: fulltext)
|
29
81
|
from_response_collection(response)
|
30
82
|
end
|
31
83
|
|
32
|
-
def
|
84
|
+
def find_all_by_title(title:, val: nil, clean: nil, fulltext: nil)
|
33
85
|
response = Api::Search.by_title(title: title, val: val, clean: clean, fulltext: fulltext)
|
34
86
|
from_response_collection(response)
|
35
87
|
end
|
36
88
|
|
37
|
-
|
89
|
+
def find_all_by_recent(recent:, max: nil, since: nil, lang: nil, categories: [], exclude_categories: [])
|
90
|
+
response = Api::Recent.feeds(max: max, since: since, lang: lang, cat: categories.join(","),
|
91
|
+
notcat: exclude_categories.join(","))
|
92
|
+
from_response_collection(response)
|
93
|
+
end
|
94
|
+
|
95
|
+
def find_all_by_new(new:, max: nil, since: nil, feedid: nil, desc: nil)
|
96
|
+
response = Api::Recent.new_feeds(max: max, since: since, feedid: feedid, desc: desc)
|
97
|
+
from_response_collection(response)
|
98
|
+
end
|
99
|
+
|
100
|
+
def find_all_by_newly_found(newly_found:, max: nil, since: nil)
|
101
|
+
response = Api::Recent.data(max: max, since: since)
|
102
|
+
# This one has a bit of an oddball response. It's nested in a "data" attribute and all the keys
|
103
|
+
# for each feed are prefixed with "feed" (see example fixture)
|
104
|
+
response["data"]["feeds"].map do |item|
|
105
|
+
feed = item.transform_keys { |key| key.delete_prefix("feed").underscore }
|
106
|
+
new(JSON.parse(feed.to_json, object_class: OpenStruct)) # rubocop:disable Style/OpenStructUse
|
107
|
+
end
|
108
|
+
end
|
38
109
|
|
39
110
|
def from_response(response)
|
40
111
|
feed = response["feed"].transform_keys(&:underscore)
|
@@ -43,10 +114,11 @@ module PodcastIndex
|
|
43
114
|
|
44
115
|
def from_response_collection(response)
|
45
116
|
response["feeds"].map do |item|
|
46
|
-
|
47
|
-
new(JSON.parse(
|
117
|
+
feed = item.transform_keys(&:underscore)
|
118
|
+
new(JSON.parse(feed.to_json, object_class: OpenStruct)) # rubocop:disable Style/OpenStructUse
|
48
119
|
end
|
49
120
|
end
|
50
121
|
end
|
51
122
|
end
|
52
123
|
end
|
124
|
+
# rubocop:enable Metrics/ParameterLists, Lint/UnusedMethodArgument
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require "ostruct"
|
2
|
+
require "delegate"
|
3
|
+
|
4
|
+
module PodcastIndex
|
5
|
+
class Soundbite < SimpleDelegator
|
6
|
+
class << self
|
7
|
+
FIND_MANY_ATTRIBUTES = %i[recent].freeze
|
8
|
+
|
9
|
+
def where(attributes)
|
10
|
+
match = (attributes.keys & FIND_MANY_ATTRIBUTES)
|
11
|
+
raise ArgumentError, "Must supply one of the attributes: #{FIND_MANY_ATTRIBUTES}" unless match.present?
|
12
|
+
raise ArgumentError, "Must supply only one of the attributes: #{FIND_MANY_ATTRIBUTES}" if match.length > 1
|
13
|
+
|
14
|
+
send("find_all_by_#{match.first}", **attributes)
|
15
|
+
end
|
16
|
+
|
17
|
+
def find_all_by_recent(recent:, max: nil) # rubocop:disable Lint/UnusedMethodArgument
|
18
|
+
response = Api::Recent.soundbites(max: max)
|
19
|
+
from_response_collection(response)
|
20
|
+
end
|
21
|
+
|
22
|
+
def from_response_collection(response, collection_key = "items")
|
23
|
+
response[collection_key].map do |item|
|
24
|
+
soundbite = item.transform_keys(&:underscore)
|
25
|
+
new(JSON.parse(soundbite.to_json, object_class: OpenStruct)) # rubocop:disable Style/OpenStructUse
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require "ostruct"
|
2
|
+
require "delegate"
|
3
|
+
|
4
|
+
module PodcastIndex
|
5
|
+
class Value < SimpleDelegator
|
6
|
+
class << self
|
7
|
+
FIND_ONE_ATTRIBUTES = %i[feed_id feed_url].freeze
|
8
|
+
|
9
|
+
def find_by(attributes)
|
10
|
+
match = (attributes.keys & FIND_ONE_ATTRIBUTES)
|
11
|
+
raise ArgumentError, "Must supply one of the attributes: #{FIND_ONE_ATTRIBUTES}" unless match.present?
|
12
|
+
raise ArgumentError, "Must supply only one of the attributes: #{FIND_ONE_ATTRIBUTES}" if match.length > 1
|
13
|
+
|
14
|
+
send("find_by_#{match.first}", attributes[match.first])
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def find_by_feed_id(feed_id)
|
20
|
+
response = Api::Value.by_feed_id(id: feed_id)
|
21
|
+
from_response(response)
|
22
|
+
end
|
23
|
+
|
24
|
+
def find_by_feed_url(feed_url)
|
25
|
+
response = Api::Value.by_feed_url(url: feed_url)
|
26
|
+
from_response(response)
|
27
|
+
end
|
28
|
+
|
29
|
+
def from_response(response)
|
30
|
+
value = response["value"].transform_keys(&:underscore)
|
31
|
+
new(JSON.parse(value.to_json, object_class: OpenStruct)) # rubocop:disable Style/OpenStructUse
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
data/lib/podcast_index.rb
CHANGED
@@ -7,8 +7,12 @@ require_relative "podcast_index/api/request"
|
|
7
7
|
require_relative "podcast_index/api/podcasts"
|
8
8
|
require_relative "podcast_index/api/episodes"
|
9
9
|
require_relative "podcast_index/api/search"
|
10
|
+
require_relative "podcast_index/api/recent"
|
11
|
+
require_relative "podcast_index/api/value"
|
10
12
|
require_relative "podcast_index/podcast"
|
11
13
|
require_relative "podcast_index/episode"
|
14
|
+
require_relative "podcast_index/soundbite"
|
15
|
+
require_relative "podcast_index/value"
|
12
16
|
|
13
17
|
module PodcastIndex
|
14
18
|
include ActiveSupport::Configurable
|
@@ -16,6 +20,7 @@ module PodcastIndex
|
|
16
20
|
config_accessor :api_key, :api_secret, :base_url
|
17
21
|
|
18
22
|
class Error < StandardError; end
|
23
|
+
class PodcastNotFound < Error; end
|
19
24
|
|
20
25
|
def self.configure
|
21
26
|
self.base_url = "https://api.podcastindex.org/api/1.0".freeze
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: podcast_index
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1
|
4
|
+
version: 0.2.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jason York
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-
|
11
|
+
date: 2023-07-06 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -173,12 +173,15 @@ files:
|
|
173
173
|
- lib/podcast_index.rb
|
174
174
|
- lib/podcast_index/api/episodes.rb
|
175
175
|
- lib/podcast_index/api/podcasts.rb
|
176
|
+
- lib/podcast_index/api/recent.rb
|
176
177
|
- lib/podcast_index/api/request.rb
|
177
178
|
- lib/podcast_index/api/search.rb
|
179
|
+
- lib/podcast_index/api/value.rb
|
178
180
|
- lib/podcast_index/episode.rb
|
179
181
|
- lib/podcast_index/podcast.rb
|
182
|
+
- lib/podcast_index/soundbite.rb
|
183
|
+
- lib/podcast_index/value.rb
|
180
184
|
- lib/podcast_index/version.rb
|
181
|
-
- podcast_index.gemspec
|
182
185
|
homepage: https://github.com/jasonyork/podcast-index
|
183
186
|
licenses:
|
184
187
|
- MIT
|
data/podcast_index.gemspec
DELETED
@@ -1,41 +0,0 @@
|
|
1
|
-
require_relative "lib/podcast_index/version"
|
2
|
-
|
3
|
-
Gem::Specification.new do |spec|
|
4
|
-
spec.name = "podcast_index"
|
5
|
-
spec.version = PodcastIndex::VERSION
|
6
|
-
spec.authors = ["Jason York"]
|
7
|
-
|
8
|
-
spec.summary = "Ruby client for the podcastindex.org API"
|
9
|
-
spec.description = "Exposes the podcastindex.org API through Ruby domain models."
|
10
|
-
spec.homepage = "https://github.com/jasonyork/podcast-index"
|
11
|
-
spec.license = "MIT"
|
12
|
-
spec.required_ruby_version = ">= 3.0.0"
|
13
|
-
|
14
|
-
spec.metadata["homepage_uri"] = spec.homepage
|
15
|
-
spec.metadata["source_code_uri"] = "https://github.com/jasonyork/podcast-index"
|
16
|
-
spec.metadata["changelog_uri"] = "https://github.com/jasonyork/podcast-index/blob/master/CHANGELOG.md"
|
17
|
-
spec.metadata["rubygems_mfa_required"] = "true"
|
18
|
-
|
19
|
-
# Specify which files should be added to the gem when it is released.
|
20
|
-
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
21
|
-
spec.files = Dir.chdir(__dir__) do
|
22
|
-
`git ls-files -z`.split("\x0").reject do |f|
|
23
|
-
(f == __FILE__) || f.match(%r{\A(?:(?:bin|test|spec|features)/|\.(?:git|travis|circleci)|appveyor)})
|
24
|
-
end
|
25
|
-
end
|
26
|
-
spec.bindir = "exe"
|
27
|
-
spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
|
28
|
-
spec.require_paths = ["lib"]
|
29
|
-
|
30
|
-
spec.add_dependency "activesupport", ">= 6.0", "< 8"
|
31
|
-
spec.add_dependency "addressable", "~> 2"
|
32
|
-
|
33
|
-
spec.add_development_dependency "codecov", "~> 0.4"
|
34
|
-
spec.add_development_dependency "debug", "~> 1"
|
35
|
-
spec.add_development_dependency "rspec-core", "~> 3"
|
36
|
-
spec.add_development_dependency "rspec-its", "~> 1"
|
37
|
-
spec.add_development_dependency "rubocop-performance", "~> 1"
|
38
|
-
spec.add_development_dependency "rubocop-rake", "~> 0.6"
|
39
|
-
spec.add_development_dependency "rubocop-rspec", "~> 2"
|
40
|
-
spec.add_development_dependency "webmock", "~> 3"
|
41
|
-
end
|