acast.py (4493B)
1 # coding: utf-8 2 from __future__ import unicode_literals 3 4 import re 5 6 from .common import InfoExtractor 7 from ..utils import ( 8 clean_html, 9 clean_podcast_url, 10 int_or_none, 11 parse_iso8601, 12 ) 13 14 15 class ACastBaseIE(InfoExtractor): 16 def _extract_episode(self, episode, show_info): 17 title = episode['title'] 18 info = { 19 'id': episode['id'], 20 'display_id': episode.get('episodeUrl'), 21 'url': clean_podcast_url(episode['url']), 22 'title': title, 23 'description': clean_html(episode.get('description') or episode.get('summary')), 24 'thumbnail': episode.get('image'), 25 'timestamp': parse_iso8601(episode.get('publishDate')), 26 'duration': int_or_none(episode.get('duration')), 27 'filesize': int_or_none(episode.get('contentLength')), 28 'season_number': int_or_none(episode.get('season')), 29 'episode': title, 30 'episode_number': int_or_none(episode.get('episode')), 31 } 32 info.update(show_info) 33 return info 34 35 def _extract_show_info(self, show): 36 return { 37 'creator': show.get('author'), 38 'series': show.get('title'), 39 } 40 41 def _call_api(self, path, video_id, query=None): 42 return self._download_json( 43 'https://feeder.acast.com/api/v1/shows/' + path, video_id, query=query) 44 45 46 class ACastIE(ACastBaseIE): 47 IE_NAME = 'acast' 48 _VALID_URL = r'''(?x) 49 https?:// 50 (?: 51 (?:(?:embed|www)\.)?acast\.com/| 52 play\.acast\.com/s/ 53 ) 54 (?P<channel>[^/]+)/(?P<id>[^/#?]+) 55 ''' 56 _TESTS = [{ 57 'url': 'https://www.acast.com/sparpodcast/2.raggarmordet-rosterurdetforflutna', 58 'md5': 'f5598f3ad1e4776fed12ec1407153e4b', 59 'info_dict': { 60 'id': '2a92b283-1a75-4ad8-8396-499c641de0d9', 61 'ext': 'mp3', 62 'title': '2. Raggarmordet - Röster ur det förflutna', 63 'description': 'md5:a992ae67f4d98f1c0141598f7bebbf67', 64 'timestamp': 1477346700, 65 'upload_date': '20161024', 66 'duration': 2766, 67 'creator': 'Anton Berg & Martin Johnson', 68 'series': 'Spår', 69 'episode': '2. Raggarmordet - Röster ur det förflutna', 70 } 71 }, { 72 'url': 'http://embed.acast.com/adambuxton/ep.12-adam-joeschristmaspodcast2015', 73 'only_matching': True, 74 }, { 75 'url': 'https://play.acast.com/s/rattegangspodden/s04e09styckmordetihelenelund-del2-2', 76 'only_matching': True, 77 }, { 78 'url': 'https://play.acast.com/s/sparpodcast/2a92b283-1a75-4ad8-8396-499c641de0d9', 79 'only_matching': True, 80 }] 81 82 def _real_extract(self, url): 83 channel, display_id = re.match(self._VALID_URL, url).groups() 84 episode = self._call_api( 85 '%s/episodes/%s' % (channel, display_id), 86 display_id, {'showInfo': 'true'}) 87 return self._extract_episode( 88 episode, self._extract_show_info(episode.get('show') or {})) 89 90 91 class ACastChannelIE(ACastBaseIE): 92 IE_NAME = 'acast:channel' 93 _VALID_URL = r'''(?x) 94 https?:// 95 (?: 96 (?:www\.)?acast\.com/| 97 play\.acast\.com/s/ 98 ) 99 (?P<id>[^/#?]+) 100 ''' 101 _TESTS = [{ 102 'url': 'https://www.acast.com/todayinfocus', 103 'info_dict': { 104 'id': '4efc5294-5385-4847-98bd-519799ce5786', 105 'title': 'Today in Focus', 106 'description': 'md5:c09ce28c91002ce4ffce71d6504abaae', 107 }, 108 'playlist_mincount': 200, 109 }, { 110 'url': 'http://play.acast.com/s/ft-banking-weekly', 111 'only_matching': True, 112 }] 113 114 @classmethod 115 def suitable(cls, url): 116 return False if ACastIE.suitable(url) else super(ACastChannelIE, cls).suitable(url) 117 118 def _real_extract(self, url): 119 show_slug = self._match_id(url) 120 show = self._call_api(show_slug, show_slug) 121 show_info = self._extract_show_info(show) 122 entries = [] 123 for episode in (show.get('episodes') or []): 124 entries.append(self._extract_episode(episode, show_info)) 125 return self.playlist_result( 126 entries, show.get('id'), show.get('title'), show.get('description'))