youtube-dl

Another place where youtube-dl lives on
git clone git://git.oshgnacknak.de/youtube-dl.git
Log | Files | Refs | README | LICENSE

flickr.py (4775B)


      1 from __future__ import unicode_literals
      2 
      3 from .common import InfoExtractor
      4 from ..compat import (
      5     compat_str,
      6     compat_urllib_parse_urlencode,
      7 )
      8 from ..utils import (
      9     ExtractorError,
     10     int_or_none,
     11     qualities,
     12 )
     13 
     14 
     15 class FlickrIE(InfoExtractor):
     16     _VALID_URL = r'https?://(?:www\.|secure\.)?flickr\.com/photos/[\w\-_@]+/(?P<id>\d+)'
     17     _TEST = {
     18         'url': 'http://www.flickr.com/photos/forestwander-nature-pictures/5645318632/in/photostream/',
     19         'md5': '164fe3fa6c22e18d448d4d5af2330f31',
     20         'info_dict': {
     21             'id': '5645318632',
     22             'ext': 'mpg',
     23             'description': 'Waterfalls in the Springtime at Dark Hollow Waterfalls. These are located just off of Skyline Drive in Virginia. They are only about 6/10 of a mile hike but it is a pretty steep hill and a good climb back up.',
     24             'title': 'Dark Hollow Waterfalls',
     25             'duration': 19,
     26             'timestamp': 1303528740,
     27             'upload_date': '20110423',
     28             'uploader_id': '10922353@N03',
     29             'uploader': 'Forest Wander',
     30             'uploader_url': 'https://www.flickr.com/photos/forestwander-nature-pictures/',
     31             'comment_count': int,
     32             'view_count': int,
     33             'tags': list,
     34             'license': 'Attribution-ShareAlike',
     35         }
     36     }
     37     _API_BASE_URL = 'https://api.flickr.com/services/rest?'
     38     # https://help.yahoo.com/kb/flickr/SLN25525.html
     39     _LICENSES = {
     40         '0': 'All Rights Reserved',
     41         '1': 'Attribution-NonCommercial-ShareAlike',
     42         '2': 'Attribution-NonCommercial',
     43         '3': 'Attribution-NonCommercial-NoDerivs',
     44         '4': 'Attribution',
     45         '5': 'Attribution-ShareAlike',
     46         '6': 'Attribution-NoDerivs',
     47         '7': 'No known copyright restrictions',
     48         '8': 'United States government work',
     49         '9': 'Public Domain Dedication (CC0)',
     50         '10': 'Public Domain Work',
     51     }
     52 
     53     def _call_api(self, method, video_id, api_key, note, secret=None):
     54         query = {
     55             'photo_id': video_id,
     56             'method': 'flickr.%s' % method,
     57             'api_key': api_key,
     58             'format': 'json',
     59             'nojsoncallback': 1,
     60         }
     61         if secret:
     62             query['secret'] = secret
     63         data = self._download_json(self._API_BASE_URL + compat_urllib_parse_urlencode(query), video_id, note)
     64         if data['stat'] != 'ok':
     65             raise ExtractorError(data['message'])
     66         return data
     67 
     68     def _real_extract(self, url):
     69         video_id = self._match_id(url)
     70 
     71         api_key = self._download_json(
     72             'https://www.flickr.com/hermes_error_beacon.gne', video_id,
     73             'Downloading api key')['site_key']
     74 
     75         video_info = self._call_api(
     76             'photos.getInfo', video_id, api_key, 'Downloading video info')['photo']
     77         if video_info['media'] == 'video':
     78             streams = self._call_api(
     79                 'video.getStreamInfo', video_id, api_key,
     80                 'Downloading streams info', video_info['secret'])['streams']
     81 
     82             preference = qualities(
     83                 ['288p', 'iphone_wifi', '100', '300', '700', '360p', 'appletv', '720p', '1080p', 'orig'])
     84 
     85             formats = []
     86             for stream in streams['stream']:
     87                 stream_type = compat_str(stream.get('type'))
     88                 formats.append({
     89                     'format_id': stream_type,
     90                     'url': stream['_content'],
     91                     'preference': preference(stream_type),
     92                 })
     93             self._sort_formats(formats)
     94 
     95             owner = video_info.get('owner', {})
     96             uploader_id = owner.get('nsid')
     97             uploader_path = owner.get('path_alias') or uploader_id
     98             uploader_url = 'https://www.flickr.com/photos/%s/' % uploader_path if uploader_path else None
     99 
    100             return {
    101                 'id': video_id,
    102                 'title': video_info['title']['_content'],
    103                 'description': video_info.get('description', {}).get('_content'),
    104                 'formats': formats,
    105                 'timestamp': int_or_none(video_info.get('dateuploaded')),
    106                 'duration': int_or_none(video_info.get('video', {}).get('duration')),
    107                 'uploader_id': uploader_id,
    108                 'uploader': owner.get('realname'),
    109                 'uploader_url': uploader_url,
    110                 'comment_count': int_or_none(video_info.get('comments', {}).get('_content')),
    111                 'view_count': int_or_none(video_info.get('views')),
    112                 'tags': [tag.get('_content') for tag in video_info.get('tags', {}).get('tag', [])],
    113                 'license': self._LICENSES.get(video_info.get('license')),
    114             }
    115         else:
    116             raise ExtractorError('not a video', expected=True)