2
2
from __future__ import unicode_literals
3
3
4
4
import re
5
- import functools
6
5
7
6
from .common import InfoExtractor
8
- from ..compat import compat_str
9
7
from ..utils import (
10
8
clean_html ,
11
- float_or_none ,
12
9
int_or_none ,
13
- try_get ,
14
- unified_timestamp ,
15
- OnDemandPagedList ,
10
+ parse_iso8601 ,
16
11
)
17
12
18
13
19
- class ACastIE (InfoExtractor ):
14
+ class ACastBaseIE (InfoExtractor ):
15
+ def _extract_episode (self , episode , show_info ):
16
+ title = episode ['title' ]
17
+ info = {
18
+ 'id' : episode ['id' ],
19
+ 'display_id' : episode .get ('episodeUrl' ),
20
+ 'url' : episode ['url' ],
21
+ 'title' : title ,
22
+ 'description' : clean_html (episode .get ('description' ) or episode .get ('summary' )),
23
+ 'thumbnail' : episode .get ('image' ),
24
+ 'timestamp' : parse_iso8601 (episode .get ('publishDate' )),
25
+ 'duration' : int_or_none (episode .get ('duration' )),
26
+ 'filesize' : int_or_none (episode .get ('contentLength' )),
27
+ 'season_number' : int_or_none (episode .get ('season' )),
28
+ 'episode' : title ,
29
+ 'episode_number' : int_or_none (episode .get ('episode' )),
30
+ }
31
+ info .update (show_info )
32
+ return info
33
+
34
+ def _extract_show_info (self , show ):
35
+ return {
36
+ 'creator' : show .get ('author' ),
37
+ 'series' : show .get ('title' ),
38
+ }
39
+
40
+ def _call_api (self , path , video_id , query = None ):
41
+ return self ._download_json (
42
+ 'https://feeder.acast.com/api/v1/shows/' + path , video_id , query = query )
43
+
44
+
45
+ class ACastIE (ACastBaseIE ):
20
46
IE_NAME = 'acast'
21
47
_VALID_URL = r'''(?x)
22
48
https?://
@@ -28,15 +54,15 @@ class ACastIE(InfoExtractor):
28
54
'''
29
55
_TESTS = [{
30
56
'url' : 'https://www.acast.com/sparpodcast/2.raggarmordet-rosterurdetforflutna' ,
31
- 'md5' : '16d936099ec5ca2d5869e3a813ee8dc4 ' ,
57
+ 'md5' : 'f5598f3ad1e4776fed12ec1407153e4b ' ,
32
58
'info_dict' : {
33
59
'id' : '2a92b283-1a75-4ad8-8396-499c641de0d9' ,
34
60
'ext' : 'mp3' ,
35
61
'title' : '2. Raggarmordet - Röster ur det förflutna' ,
36
- 'description' : 'md5:4f81f6d8cf2e12ee21a321d8bca32db4 ' ,
62
+ 'description' : 'md5:a992ae67f4d98f1c0141598f7bebbf67 ' ,
37
63
'timestamp' : 1477346700 ,
38
64
'upload_date' : '20161024' ,
39
- 'duration' : 2766.602563 ,
65
+ 'duration' : 2766 ,
40
66
'creator' : 'Anton Berg & Martin Johnson' ,
41
67
'series' : 'Spår' ,
42
68
'episode' : '2. Raggarmordet - Röster ur det förflutna' ,
@@ -45,7 +71,7 @@ class ACastIE(InfoExtractor):
45
71
'url' : 'http://embed.acast.com/adambuxton/ep.12-adam-joeschristmaspodcast2015' ,
46
72
'only_matching' : True ,
47
73
}, {
48
- 'url' : 'https://play.acast.com/s/rattegangspodden/s04e09-styckmordet-i-helenelund-del-22 ' ,
74
+ 'url' : 'https://play.acast.com/s/rattegangspodden/s04e09styckmordetihelenelund-del2-2 ' ,
49
75
'only_matching' : True ,
50
76
}, {
51
77
'url' : 'https://play.acast.com/s/sparpodcast/2a92b283-1a75-4ad8-8396-499c641de0d9' ,
@@ -54,40 +80,14 @@ class ACastIE(InfoExtractor):
54
80
55
81
def _real_extract (self , url ):
56
82
channel , display_id = re .match (self ._VALID_URL , url ).groups ()
57
- s = self ._download_json (
58
- 'https://feeder.acast.com/api/v1/shows/%s/episodes/%s' % (channel , display_id ),
59
- display_id )
60
- media_url = s ['url' ]
61
- if re .search (r'[0-9a-f]{8}-(?:[0-9a-f]{4}-){3}[0-9a-f]{12}' , display_id ):
62
- episode_url = s .get ('episodeUrl' )
63
- if episode_url :
64
- display_id = episode_url
65
- else :
66
- channel , display_id = re .match (self ._VALID_URL , s ['link' ]).groups ()
67
- cast_data = self ._download_json (
68
- 'https://play-api.acast.com/splash/%s/%s' % (channel , display_id ),
69
- display_id )['result' ]
70
- e = cast_data ['episode' ]
71
- title = e .get ('name' ) or s ['title' ]
72
- return {
73
- 'id' : compat_str (e ['id' ]),
74
- 'display_id' : display_id ,
75
- 'url' : media_url ,
76
- 'title' : title ,
77
- 'description' : e .get ('summary' ) or clean_html (e .get ('description' ) or s .get ('description' )),
78
- 'thumbnail' : e .get ('image' ),
79
- 'timestamp' : unified_timestamp (e .get ('publishingDate' ) or s .get ('publishDate' )),
80
- 'duration' : float_or_none (e .get ('duration' ) or s .get ('duration' )),
81
- 'filesize' : int_or_none (e .get ('contentLength' )),
82
- 'creator' : try_get (cast_data , lambda x : x ['show' ]['author' ], compat_str ),
83
- 'series' : try_get (cast_data , lambda x : x ['show' ]['name' ], compat_str ),
84
- 'season_number' : int_or_none (e .get ('seasonNumber' )),
85
- 'episode' : title ,
86
- 'episode_number' : int_or_none (e .get ('episodeNumber' )),
87
- }
83
+ episode = self ._call_api (
84
+ '%s/episodes/%s' % (channel , display_id ),
85
+ display_id , {'showInfo' : 'true' })
86
+ return self ._extract_episode (
87
+ episode , self ._extract_show_info (episode .get ('show' ) or {}))
88
88
89
89
90
- class ACastChannelIE (InfoExtractor ):
90
+ class ACastChannelIE (ACastBaseIE ):
91
91
IE_NAME = 'acast:channel'
92
92
_VALID_URL = r'''(?x)
93
93
https?://
@@ -102,34 +102,24 @@ class ACastChannelIE(InfoExtractor):
102
102
'info_dict' : {
103
103
'id' : '4efc5294-5385-4847-98bd-519799ce5786' ,
104
104
'title' : 'Today in Focus' ,
105
- 'description' : 'md5:9ba5564de5ce897faeb12963f4537a64 ' ,
105
+ 'description' : 'md5:c09ce28c91002ce4ffce71d6504abaae ' ,
106
106
},
107
- 'playlist_mincount' : 35 ,
107
+ 'playlist_mincount' : 200 ,
108
108
}, {
109
109
'url' : 'http://play.acast.com/s/ft-banking-weekly' ,
110
110
'only_matching' : True ,
111
111
}]
112
- _API_BASE_URL = 'https://play.acast.com/api/'
113
- _PAGE_SIZE = 10
114
112
115
113
@classmethod
116
114
def suitable (cls , url ):
117
115
return False if ACastIE .suitable (url ) else super (ACastChannelIE , cls ).suitable (url )
118
116
119
- def _fetch_page (self , channel_slug , page ):
120
- casts = self ._download_json (
121
- self ._API_BASE_URL + 'channels/%s/acasts?page=%s' % (channel_slug , page ),
122
- channel_slug , note = 'Download page %d of channel data' % page )
123
- for cast in casts :
124
- yield self .url_result (
125
- 'https://play.acast.com/s/%s/%s' % (channel_slug , cast ['url' ]),
126
- 'ACast' , cast ['id' ])
127
-
128
117
def _real_extract (self , url ):
129
- channel_slug = self ._match_id (url )
130
- channel_data = self ._download_json (
131
- self ._API_BASE_URL + 'channels/%s' % channel_slug , channel_slug )
132
- entries = OnDemandPagedList (functools .partial (
133
- self ._fetch_page , channel_slug ), self ._PAGE_SIZE )
134
- return self .playlist_result (entries , compat_str (
135
- channel_data ['id' ]), channel_data ['name' ], channel_data .get ('description' ))
118
+ show_slug = self ._match_id (url )
119
+ show = self ._call_api (show_slug , show_slug )
120
+ show_info = self ._extract_show_info (show )
121
+ entries = []
122
+ for episode in (show .get ('episodes' ) or []):
123
+ entries .append (self ._extract_episode (episode , show_info ))
124
+ return self .playlist_result (
125
+ entries , show .get ('id' ), show .get ('title' ), show .get ('description' ))
0 commit comments