podcast_index 0.1.0 → 0.2.0
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +6 -0
- data/Gemfile.lock +3 -5
- data/README.md +69 -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 +79 -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 +4 -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: be3811eb9cbde914e7e314e4d02e35a335fcb06250b8e22519ed0b59487d2e4b
|
4
|
+
data.tar.gz: 19de21d136dbc55f8cf0adcc65a710d773baceb99398ff22182d7e67b26ebc6b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 353f4074705cd1e93675d8bb8908698a57b07f321f54510d6d8e23af483736d6fd127b94aafcc67cd3d29e84e628c505aac290823e33425e30bc7c4343044cf8
|
7
|
+
data.tar.gz: 03075bb9e16b26edaa2b4d865435b0bdf919fd896f7b19e0b5c3f7d204cb0093a2f35e02d1312b2e0248c2a3385dbb54cd240a504b92afd1bbae3bb8d8a22d40
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,11 @@
|
|
1
1
|
## [Unreleased]
|
2
2
|
|
3
|
+
## [0.2.0] - 2023-04-24
|
4
|
+
|
5
|
+
* BREAKING: Switching to be more consistent with ActiveRecord conventions, using `find_by` and `where` methods for all models
|
6
|
+
* Add support for all "Search", "Podcasts", "Episodes", "Recent" and "Value" sections of the API. This introduces the `Soundbite` and `Value` domain models.
|
7
|
+
* Update README.md
|
8
|
+
|
3
9
|
## [0.1.0] - 2023-01-06
|
4
10
|
|
5
11
|
- Initial release
|
data/Gemfile.lock
CHANGED
@@ -1,25 +1,24 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
podcast_index (0.
|
4
|
+
podcast_index (0.2.0)
|
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:
|
@@ -44,23 +59,73 @@ podcast.title # => "Podcasting 2.0"
|
|
44
59
|
Find an episode by guid:
|
45
60
|
|
46
61
|
```ruby
|
47
|
-
episode = PodcastIndex::Episode.
|
62
|
+
episode = PodcastIndex::Episode.find_by(guid: "PC2084", feedurl: "http://mp3s.nashownotes.com/pc20rss.xml")
|
48
63
|
episode.title # => "Episode 84: All Aboard to On-Board!"
|
49
64
|
```
|
50
65
|
|
66
|
+
Find a Value block by feed_id:
|
67
|
+
|
68
|
+
```ruby
|
69
|
+
value = PodcastIndex::Value.find_by(feed_id: 920666)
|
70
|
+
value.model.type # => "lightning"
|
71
|
+
```
|
72
|
+
|
51
73
|
Methods that return multiple results are represented as an array of objects:
|
52
74
|
|
53
75
|
```ruby
|
54
|
-
episodes = PodcastIndex::Episode.
|
76
|
+
episodes = PodcastIndex::Episode.where(person: "Adam Curry")
|
55
77
|
episodes.count # => 57
|
56
78
|
episodes.first.title # => "Episode #2: A conversation with Adam Curry"
|
57
79
|
```
|
58
80
|
|
81
|
+
```ruby
|
82
|
+
soundbite = PodcastIndex::Soundbite.where(recent: true)
|
83
|
+
soundbite.first.episode_id # => 15082076307
|
84
|
+
```
|
85
|
+
|
86
|
+
|
87
|
+
|
59
88
|
### Supported Methods
|
60
89
|
|
61
|
-
|
90
|
+
```ruby
|
91
|
+
# Episode
|
92
|
+
Episode.find(id, fulltext: nil)
|
93
|
+
Episode.find_by(guid, feedurl: nil, feedid: nil, fulltext: nil)
|
94
|
+
Episode.where(feed_id:, since: nil, max: nil, fulltext: nil)
|
95
|
+
Episode.where(feed_url:, since: nil, max: nil, fulltext: nil)
|
96
|
+
Episode.where(podcast_guid:, since: nil, max: nil, fulltext: nil)
|
97
|
+
Episode.where(live: true, max: nil)
|
98
|
+
Episode.where(itunes_id:, since: nil, max: nil, fulltext: nil)
|
99
|
+
Episode.where(person:, fulltext: nil)
|
100
|
+
Episode.where(recent: true, max: nil, exclude_string: nil, before: nil, fulltext: nil)
|
101
|
+
Episode.sample(max: nil, lang: nil, categories: [], exclude_categories: [], fulltext: nil) # Find a random episode
|
102
|
+
|
103
|
+
# Podcast
|
104
|
+
Podcast.find(id)
|
105
|
+
Podcast.find_by(feed_url)
|
106
|
+
Podcast.find_by(guid)
|
107
|
+
Podcast.find_by(itunes_id)
|
108
|
+
Podcast.where(tag:)
|
109
|
+
Podcast.where(medium:)
|
110
|
+
# Additional parameters only for searching with "music" medium
|
111
|
+
Podcast.where(medium: "music", term:, val: nil, aponly: nil, clean: nil, fulltext: nil)
|
112
|
+
Podcast.where(term:, val: nil, aponly: nil, clean: nil, fulltext: nil)
|
113
|
+
Podcast.where(title:, val: nil, clean: nil, fulltext: nil)
|
114
|
+
Podcast.where(trending: true, max: nil, since: nil, lang: nil, categories: [], exclude_categories: [])
|
115
|
+
Podcast.where(dead: true)
|
116
|
+
Podcast.where(recent: true, max: nil, since: nil, lang: nil, categories: [], exclude_categories: [])
|
117
|
+
Podcast.where(new: true, max: nil, since: nil, feedid: nil, desc: nil)
|
118
|
+
Podcast.where(newly_found: true, max: nil, since: nil)
|
119
|
+
|
120
|
+
# Soundbite
|
121
|
+
Soundbite.where(recent: true, max: nil)
|
122
|
+
|
123
|
+
# Value
|
124
|
+
Value.find_by(feed_id)
|
125
|
+
Value.find_by(feed_url)
|
126
|
+
```
|
62
127
|
|
63
|
-
The attributes of the
|
128
|
+
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
129
|
|
65
130
|
### Exception Handling
|
66
131
|
|
@@ -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,40 @@
|
|
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)
|
9
13
|
from_response(response)
|
10
14
|
end
|
11
15
|
|
12
|
-
def
|
13
|
-
|
16
|
+
def find_by(attributes)
|
17
|
+
match = (attributes.keys & FIND_ONE_ATTRIBUTES)
|
18
|
+
raise ArgumentError, "Must supply one of the attributes: #{FIND_ONE_ATTRIBUTES}" unless match.present?
|
19
|
+
raise ArgumentError, "Must supply only one of the attributes: #{FIND_ONE_ATTRIBUTES}" if match.length > 1
|
20
|
+
|
21
|
+
send("find_by_#{match.first}", attributes[match.first])
|
22
|
+
end
|
23
|
+
|
24
|
+
def where(attributes)
|
25
|
+
return find_all_music_by_term(**attributes) if attributes[:medium] == "music" && attributes[:term].present?
|
26
|
+
|
27
|
+
match = (attributes.keys & FIND_MANY_ATTRIBUTES)
|
28
|
+
raise ArgumentError, "Must supply one of the attributes: #{FIND_MANY_ATTRIBUTES}" unless match.present?
|
29
|
+
raise ArgumentError, "Must supply only one of the attributes: #{FIND_MANY_ATTRIBUTES}" if match.length > 1
|
30
|
+
|
31
|
+
send("find_all_by_#{match.first}", **attributes)
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def find_by_feed_url(feed_url)
|
37
|
+
response = Api::Podcasts.by_feed_url(url: feed_url)
|
14
38
|
from_response(response)
|
15
39
|
end
|
16
40
|
|
@@ -19,22 +43,67 @@ module PodcastIndex
|
|
19
43
|
from_response(response)
|
20
44
|
end
|
21
45
|
|
22
|
-
def find_by_itunes_id(
|
23
|
-
response = Api::Podcasts.by_itunes_id(id:
|
46
|
+
def find_by_itunes_id(itunes_id)
|
47
|
+
response = Api::Podcasts.by_itunes_id(id: itunes_id)
|
24
48
|
from_response(response)
|
25
49
|
end
|
26
50
|
|
27
|
-
def
|
51
|
+
def find_all_by_tag(tag:)
|
52
|
+
response = Api::Podcasts.by_tag(tag: tag)
|
53
|
+
from_response_collection(response)
|
54
|
+
end
|
55
|
+
|
56
|
+
def find_all_music_by_term(medium:, term:, val: nil, aponly: nil, clean: nil, fulltext: nil)
|
57
|
+
response = Api::Search.music_by_term(term: term, val: val, aponly: aponly, clean: clean, fulltext: fulltext)
|
58
|
+
from_response_collection(response)
|
59
|
+
end
|
60
|
+
|
61
|
+
def find_all_by_medium(medium:)
|
62
|
+
response = Api::Podcasts.by_medium(medium: medium)
|
63
|
+
from_response_collection(response)
|
64
|
+
end
|
65
|
+
|
66
|
+
def find_all_by_trending(trending:, max: nil, since: nil, lang: nil, categories: [], exclude_categories: [])
|
67
|
+
response = Api::Podcasts.trending(max: max, since: since, lang: lang, cat: categories.join(","),
|
68
|
+
notcat: exclude_categories.join(","))
|
69
|
+
from_response_collection(response)
|
70
|
+
end
|
71
|
+
|
72
|
+
def find_all_by_dead(*)
|
73
|
+
response = Api::Podcasts.dead
|
74
|
+
from_response_collection(response)
|
75
|
+
end
|
76
|
+
|
77
|
+
def find_all_by_term(term:, val: nil, aponly: nil, clean: nil, fulltext: nil)
|
28
78
|
response = Api::Search.by_term(term: term, val: val, aponly: aponly, clean: clean, fulltext: fulltext)
|
29
79
|
from_response_collection(response)
|
30
80
|
end
|
31
81
|
|
32
|
-
def
|
82
|
+
def find_all_by_title(title:, val: nil, clean: nil, fulltext: nil)
|
33
83
|
response = Api::Search.by_title(title: title, val: val, clean: clean, fulltext: fulltext)
|
34
84
|
from_response_collection(response)
|
35
85
|
end
|
36
86
|
|
37
|
-
|
87
|
+
def find_all_by_recent(recent:, max: nil, since: nil, lang: nil, categories: [], exclude_categories: [])
|
88
|
+
response = Api::Recent.feeds(max: max, since: since, lang: lang, cat: categories.join(","),
|
89
|
+
notcat: exclude_categories.join(","))
|
90
|
+
from_response_collection(response)
|
91
|
+
end
|
92
|
+
|
93
|
+
def find_all_by_new(new:, max: nil, since: nil, feedid: nil, desc: nil)
|
94
|
+
response = Api::Recent.new_feeds(max: max, since: since, feedid: feedid, desc: desc)
|
95
|
+
from_response_collection(response)
|
96
|
+
end
|
97
|
+
|
98
|
+
def find_all_by_newly_found(newly_found:, max: nil, since: nil)
|
99
|
+
response = Api::Recent.data(max: max, since: since)
|
100
|
+
# This one has a bit of an oddball response. It's nested in a "data" attribute and all the keys
|
101
|
+
# for each feed are prefixed with "feed" (see example fixture)
|
102
|
+
response["data"]["feeds"].map do |item|
|
103
|
+
feed = item.transform_keys { |key| key.delete_prefix("feed").underscore }
|
104
|
+
new(JSON.parse(feed.to_json, object_class: OpenStruct)) # rubocop:disable Style/OpenStructUse
|
105
|
+
end
|
106
|
+
end
|
38
107
|
|
39
108
|
def from_response(response)
|
40
109
|
feed = response["feed"].transform_keys(&:underscore)
|
@@ -43,10 +112,11 @@ module PodcastIndex
|
|
43
112
|
|
44
113
|
def from_response_collection(response)
|
45
114
|
response["feeds"].map do |item|
|
46
|
-
|
47
|
-
new(JSON.parse(
|
115
|
+
feed = item.transform_keys(&:underscore)
|
116
|
+
new(JSON.parse(feed.to_json, object_class: OpenStruct)) # rubocop:disable Style/OpenStructUse
|
48
117
|
end
|
49
118
|
end
|
50
119
|
end
|
51
120
|
end
|
52
121
|
end
|
122
|
+
# 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
|
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.
|
4
|
+
version: 0.2.0
|
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-04-24 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
|