]>
Raphaƫl G. Git Repositories - youtubedl/blob - youtube_dl/extractor/bandcamp.py
   1 from __future__ 
import unicode_literals
 
   8 from .common 
import InfoExtractor
 
  25 class BandcampIE(InfoExtractor
): 
  26     _VALID_URL 
= r
'https?://.*?\.bandcamp\.com/track/(?P<title>[^/?#&]+)' 
  28         'url': 'http://youtube-dl.bandcamp.com/track/youtube-dl-test-song', 
  29         'md5': 'c557841d5e50261777a6585648adf439', 
  33             'title': "youtube-dl  \"'/\\\u00e4\u21ad - youtube-dl test song \"'/\\\u00e4\u21ad", 
  36         '_skip': 'There is a limit of 200 free downloads / month for the test song' 
  38         'url': 'http://benprunty.bandcamp.com/track/lanius-battle', 
  39         'md5': '0369ace6b939f0927e62c67a1a8d9fa7', 
  43             'title': 'Ben Prunty - Lanius (Battle)', 
  44             'uploader': 'Ben Prunty', 
  48     def _real_extract(self
, url
): 
  49         mobj 
= re
.match(self
._VALID
_URL
, url
) 
  50         title 
= mobj
.group('title') 
  51         webpage 
= self
._download
_webpage
(url
, title
) 
  52         thumbnail 
= self
._html
_search
_meta
('og:image', webpage
, default
=None) 
  53         m_download 
= re
.search(r
'freeDownloadPage: "(.*?)"', webpage
) 
  55             m_trackinfo 
= re
.search(r
'trackinfo: (.+),\s*?\n', webpage
) 
  57                 json_code 
= m_trackinfo
.group(1) 
  58                 data 
= json
.loads(json_code
)[0] 
  59                 track_id 
= compat_str(data
['id']) 
  61                 if not data
.get('file'): 
  62                     raise ExtractorError('Not streamable', video_id
=track_id
, expected
=True) 
  65                 for format_id
, format_url 
in data
['file'].items(): 
  66                     ext
, abr_str 
= format_id
.split('-', 1) 
  68                         'format_id': format_id
, 
  69                         'url': self
._proto
_relative
_url
(format_url
, 'http:'), 
  73                         'abr': int_or_none(abr_str
), 
  76                 self
._sort
_formats
(formats
) 
  80                     'title': data
['title'], 
  81                     'thumbnail': thumbnail
, 
  83                     'duration': float_or_none(data
.get('duration')), 
  86                 raise ExtractorError('No free songs found') 
  88         download_link 
= m_download
.group(1) 
  89         video_id 
= self
._search
_regex
( 
  90             r
'(?ms)var TralbumData = .*?[{,]\s*id: (?P<id>\d+),?$', 
  93         download_webpage 
= self
._download
_webpage
( 
  94             download_link
, video_id
, 'Downloading free downloads page') 
  96         blob 
= self
._parse
_json
( 
  98                 r
'data-blob=(["\'])(?P
<blob
>{.+?
})\
1', download_webpage, 
  99                 'blob
', group='blob
'), 
 100             video_id, transform_source=unescapeHTML) 
 102         info = blob['digital_items
'][0] 
 104         downloads = info['downloads
'] 
 105         track = info['title
'] 
 107         artist = info.get('artist
') 
 108         title = '%s - %s' % (artist, track) if artist else track 
 110         download_formats = {} 
 111         for f in blob['download_formats
']: 
 112             name, ext = f.get('name
'), f.get('file_extension
') 
 113             if all(isinstance(x, compat_str) for x in (name, ext)): 
 114                 download_formats[name] = ext.strip('.') 
 117         for format_id, f in downloads.items(): 
 118             format_url = f.get('url
') 
 121             # Stat URL generation algorithm is reverse engineered from 
 122             # download_*_bundle_*.js 
 123             stat_url = update_url_query( 
 124                 format_url.replace('/download
/', '/statdownload
/'), { 
 125                     '.rand
': int(time.time() * 1000 * random.random()), 
 127             format_id = f.get('encoding_name
') or format_id 
 128             stat = self._download_json( 
 129                 stat_url, video_id, 'Downloading 
%s JSON
' % format_id, 
 130                 transform_source=lambda s: s[s.index('{'):s.rindex('}') + 1], 
 134             retry_url = stat.get('retry_url
') 
 135             if not isinstance(retry_url, compat_str): 
 138                 'url
': self._proto_relative_url(retry_url, 'http
:'), 
 139                 'ext
': download_formats.get(format_id), 
 140                 'format_id
': format_id, 
 141                 'format_note
': f.get('description
'), 
 142                 'filesize
': parse_filesize(f.get('size_mb
')), 
 145         self._sort_formats(formats) 
 150             'thumbnail
': info.get('thumb_url
') or thumbnail, 
 151             'uploader
': info.get('artist
'), 
 158 class BandcampAlbumIE(InfoExtractor): 
 159     IE_NAME = 'Bandcamp
:album
' 
 160     _VALID_URL = r'https?
://(?
:(?P
<subdomain
>[^
.]+)\
.)?bandcamp\
.com(?
:/album
/(?P
<album_id
>[^
/?
#&]+))?' 
 163         'url': 'http://blazo.bandcamp.com/album/jazz-format-mixtape-vol-1', 
 166                 'md5': '39bc1eded3476e927c724321ddf116cf', 
 174                 'md5': '1a2c32e2691474643e912cc6cd4bffaa', 
 178                     'title': 'Kero One - Keep It Alive (Blazo remix)', 
 183             'title': 'Jazz Format Mixtape vol.1', 
 184             'id': 'jazz-format-mixtape-vol-1', 
 185             'uploader_id': 'blazo', 
 190         'skip': 'Bandcamp imposes download limits.' 
 192         'url': 'http://nightbringer.bandcamp.com/album/hierophany-of-the-open-grave', 
 194             'title': 'Hierophany of the Open Grave', 
 195             'uploader_id': 'nightbringer', 
 196             'id': 'hierophany-of-the-open-grave', 
 198         'playlist_mincount': 9, 
 200         'url': 'http://dotscale.bandcamp.com', 
 204             'uploader_id': 'dotscale', 
 206         'playlist_mincount': 7, 
 208         # with escaped quote in title 
 209         'url': 'https://jstrecords.bandcamp.com/album/entropy-ep', 
 211             'title': '"Entropy" EP', 
 212             'uploader_id': 'jstrecords', 
 215         'playlist_mincount': 3, 
 217         # not all tracks have songs 
 218         'url': 'https://insulters.bandcamp.com/album/we-are-the-plague', 
 220             'id': 'we-are-the-plague', 
 221             'title': 'WE ARE THE PLAGUE', 
 222             'uploader_id': 'insulters', 
 228     def suitable(cls
, url
): 
 230                 if BandcampWeeklyIE
.suitable(url
) or BandcampIE
.suitable(url
) 
 231                 else super(BandcampAlbumIE
, cls
).suitable(url
)) 
 233     def _real_extract(self
, url
): 
 234         mobj 
= re
.match(self
._VALID
_URL
, url
) 
 235         uploader_id 
= mobj
.group('subdomain') 
 236         album_id 
= mobj
.group('album_id') 
 237         playlist_id 
= album_id 
or uploader_id
 
 238         webpage 
= self
._download
_webpage
(url
, playlist_id
) 
 239         track_elements 
= re
.findall( 
 240             r
'(?s)<div[^>]*>(.*?<a[^>]+href="([^"]+?)"[^>]+itemprop="url"[^>]*>.*?)</div>', webpage
) 
 241         if not track_elements
: 
 242             raise ExtractorError('The page doesn\'t contain any tracks') 
 243         # Only tracks with duration info have songs 
 246                 compat_urlparse
.urljoin(url
, t_path
), 
 247                 ie
=BandcampIE
.ie_key(), 
 248                 video_title
=self
._search
_regex
( 
 249                     r
'<span\b[^>]+\bitemprop=["\']name
["\'][^>]*>([^<]+)', 
 250                     elem_content, 'track title', fatal=False)) 
 251             for elem_content, t_path in track_elements 
 252             if self._html_search_meta('duration', elem_content, default=None)] 
 254         title = self._html_search_regex( 
 255             r'album_title\s*:\s*"((?
:\\.|
[^
"\\])+?)"', 
 256             webpage, 'title
', fatal=False) 
 258             title = title.replace(r'\"', '"') 
 261             'uploader_id': uploader_id, 
 268 class BandcampWeeklyIE(InfoExtractor): 
 269     IE_NAME = 'Bandcamp:weekly' 
 270     _VALID_URL = r'https?://(?:www\.)?bandcamp\.com/?\?(?:.*?&)?show=(?P<id>\d+)' 
 272         'url': 'https://bandcamp.com/?show=224', 
 273         'md5': 'b00df799c733cf7e0c567ed187dea0fd', 
 277             'title': 'BC Weekly April 4th 2017 - Magic Moments', 
 278             'description': 'md5:5d48150916e8e02d030623a48512c874', 
 280             'release_date': '20170404', 
 281             'series': 'Bandcamp Weekly', 
 282             'episode': 'Magic Moments', 
 283             'episode_number': 208, 
 287         'url': 'https://bandcamp.com/?blah/blah@&show=228', 
 288         'only_matching': True 
 291     def _real_extract(self, url): 
 292         video_id = self._match_id(url) 
 293         webpage = self._download_webpage(url, video_id) 
 295         blob = self._parse_json( 
 297                 r'data-blob=(["\'])(?P
<blob
>{.+?
})\
1', webpage, 
 298                 'blob
', group='blob
'), 
 299             video_id, transform_source=unescapeHTML) 
 301         show = blob['bcw_show
'] 
 303         # This is desired because any invalid show id redirects to `bandcamp.com` 
 304         # which happens to expose the latest Bandcamp Weekly episode. 
 305         show_id = int_or_none(show.get('show_id
')) or int_or_none(video_id) 
 308         for format_id, format_url in show['audio_stream
'].items(): 
 309             if not isinstance(format_url, compat_str): 
 311             for known_ext in KNOWN_EXTENSIONS: 
 312                 if known_ext in format_id: 
 318                 'format_id
': format_id, 
 323         self._sort_formats(formats) 
 325         title = show.get('audio_title
') or 'Bandcamp Weekly
' 
 326         subtitle = show.get('subtitle
') 
 328             title += ' - %s' % subtitle 
 330         episode_number = None 
 331         seq = blob.get('bcw_seq
') 
 333         if seq and isinstance(seq, list): 
 335                 episode_number = next( 
 336                     int_or_none(e.get('episode_number
')) 
 338                     if isinstance(e, dict) and int_or_none(e.get('id')) == show_id) 
 339             except StopIteration: 
 345             'description
': show.get('desc
') or show.get('short_desc
'), 
 346             'duration
': float_or_none(show.get('audio_duration
')), 
 348             'release_date
': unified_strdate(show.get('published_date
')), 
 349             'series
': 'Bandcamp Weekly
', 
 350             'episode
': show.get('subtitle
'), 
 351             'episode_number
': episode_number, 
 352             'episode_id
': compat_str(video_id),