1 from .common
import InfoExtractor
12 class SimplecastBaseIE(InfoExtractor
):
13 _UUID_REGEX
= r
'[\da-f]{8}-(?:[\da-f]{4}-){3}[\da-f]{12}'
14 _API_BASE
= 'https://api.simplecast.com/'
16 def _call_api(self
, path_tmpl
, video_id
):
17 return self
._download
_json
(
18 self
._API
_BASE
+ path_tmpl
% video_id
, video_id
)
20 def _call_search_api(self
, resource
, resource_id
, resource_url
):
21 return self
._download
_json
(
22 f
'https://api.simplecast.com/{resource}s/search', resource_id
,
23 data
=urlencode_postdata({'url': resource_url
}))
25 def _parse_episode(self
, episode
):
26 episode_id
= episode
['id']
27 title
= episode
['title'].strip()
28 audio_file
= episode
.get('audio_file') or {}
29 audio_file_url
= audio_file
.get('url') or episode
.get('audio_file_url') or episode
['enclosure_url']
31 season
= episode
.get('season') or {}
32 season_href
= season
.get('href')
35 season_id
= self
._search
_regex
(
36 rf
'https?://api.simplecast.com/seasons/({self._UUID_REGEX})',
37 season_href
, 'season id', default
=None)
39 webpage_url
= episode
.get('episode_url')
42 channel_url
= self
._search
_regex
(
43 r
'(https?://[^/]+\.simplecast\.com)',
44 webpage_url
, 'channel url', default
=None)
48 'display_id': episode
.get('slug'),
50 'url': clean_podcast_url(audio_file_url
),
51 'webpage_url': webpage_url
,
52 'channel_url': channel_url
,
53 'series': try_get(episode
, lambda x
: x
['podcast']['title']),
54 'season_number': int_or_none(season
.get('number')),
55 'season_id': season_id
,
56 'thumbnail': episode
.get('image_url'),
57 'episode_id': episode_id
,
58 'episode_number': int_or_none(episode
.get('number')),
59 'description': strip_or_none(episode
.get('description')),
60 'timestamp': parse_iso8601(episode
.get('published_at')),
61 'duration': int_or_none(episode
.get('duration')),
62 'filesize': int_or_none(audio_file
.get('size') or episode
.get('audio_file_size')),
66 class SimplecastIE(SimplecastBaseIE
):
67 IE_NAME
= 'simplecast'
68 _VALID_URL
= rf
'https?://(?:api\.simplecast\.com/episodes|player\.simplecast\.com)/(?P<id>{SimplecastBaseIE._UUID_REGEX})'
69 _EMBED_REGEX
= [rf
'''(?x)<iframe[^>]+src=["\']
71 embed\.simplecast\.com/[0-9a-f]{8}|
72 player\.simplecast\.com/{SimplecastBaseIE._UUID_REGEX}
75 'display_id': 'errant-signal-chris-franklin-new-wave-video-essays',
76 'id': 'b6dc49a2-9404-4853-9aa9-9cfc097be876',
78 'title': 'Errant Signal - Chris Franklin & New Wave Video Essays',
80 'episode_id': 'b6dc49a2-9404-4853-9aa9-9cfc097be876',
81 'description': 'md5:34752789d3d2702e2d2c975fbd14f357',
83 'season_id': 'e23df0da-bae4-4531-8bbf-71364a88dc13',
84 'series': 'The RE:BIND.io Podcast',
86 'timestamp': 1580979475,
87 'upload_date': '20200206',
88 'webpage_url': r
're:^https?://the-re-bind-io-podcast\.simplecast\.com/episodes/errant-signal-chris-franklin-new-wave-video-essays',
89 'channel_url': r
're:^https?://the-re-bind-io-podcast\.simplecast\.com$',
92 'url': 'https://api.simplecast.com/episodes/b6dc49a2-9404-4853-9aa9-9cfc097be876',
93 'md5': '8c93be7be54251bf29ee97464eabe61c',
94 'info_dict': _COMMON_TEST_INFO
,
96 'url': 'https://player.simplecast.com/b6dc49a2-9404-4853-9aa9-9cfc097be876',
97 'only_matching': True,
100 def _real_extract(self
, url
):
101 episode_id
= self
._match
_id
(url
)
102 episode
= self
._call
_api
('episodes/%s', episode_id
)
103 return self
._parse
_episode
(episode
)
106 class SimplecastEpisodeIE(SimplecastBaseIE
):
107 IE_NAME
= 'simplecast:episode'
108 _VALID_URL
= r
'https?://(?!api\.)[^/]+\.simplecast\.com/episodes/(?P<id>[^/?&#]+)'
110 'url': 'https://the-re-bind-io-podcast.simplecast.com/episodes/errant-signal-chris-franklin-new-wave-video-essays',
111 'md5': '8c93be7be54251bf29ee97464eabe61c',
112 'info_dict': SimplecastIE
._COMMON
_TEST
_INFO
,
115 def _real_extract(self
, url
):
116 mobj
= self
._match
_valid
_url
(url
)
117 episode
= self
._call
_search
_api
(
118 'episode', mobj
.group(1), mobj
.group(0))
119 return self
._parse
_episode
(episode
)
122 class SimplecastPodcastIE(SimplecastBaseIE
):
123 IE_NAME
= 'simplecast:podcast'
124 _VALID_URL
= r
'https?://(?!(?:api|cdn|embed|feeds|player)\.)(?P<id>[^/]+)\.simplecast\.com(?!/episodes/[^/?&#]+)'
126 'url': 'https://the-re-bind-io-podcast.simplecast.com',
127 'playlist_mincount': 33,
129 'id': '07d28d26-7522-42eb-8c53-2bdcfc81c43c',
130 'title': 'The RE:BIND.io Podcast',
133 'url': 'https://the-re-bind-io-podcast.simplecast.com/episodes',
134 'only_matching': True,
137 def _real_extract(self
, url
):
138 subdomain
= self
._match
_id
(url
)
139 site
= self
._call
_search
_api
('site', subdomain
, url
)
140 podcast
= site
['podcast']
141 podcast_id
= podcast
['id']
142 podcast_title
= podcast
.get('title')
145 episodes
= self
._call
_api
('podcasts/%s/episodes', podcast_id
)
146 for episode
in (episodes
.get('collection') or []):
147 info
= self
._parse
_episode
(episode
)
148 info
['series'] = podcast_title
151 return self
.playlist_result(entries(), podcast_id
, podcast_title
)