1 from __future__ 
import unicode_literals
 
   3 from .common 
import InfoExtractor
 
   6     compat_urllib_parse_urlencode
, 
  15 class FlickrIE(InfoExtractor
): 
  16     _VALID_URL 
= r
'https?://(?:www\.|secure\.)?flickr\.com/photos/[\w\-_@]+/(?P<id>\d+)' 
  18         'url': 'http://www.flickr.com/photos/forestwander-nature-pictures/5645318632/in/photostream/', 
  19         'md5': '164fe3fa6c22e18d448d4d5af2330f31', 
  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', 
  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/', 
  34             'license': 'Attribution-ShareAlike', 
  37     _API_BASE_URL 
= 'https://api.flickr.com/services/rest?' 
  38     # https://help.yahoo.com/kb/flickr/SLN25525.html 
  40         '0': 'All Rights Reserved', 
  41         '1': 'Attribution-NonCommercial-ShareAlike', 
  42         '2': 'Attribution-NonCommercial', 
  43         '3': 'Attribution-NonCommercial-NoDerivs', 
  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', 
  53     def _call_api(self
, method
, video_id
, api_key
, note
, secret
=None): 
  56             'method': 'flickr.%s' % method
, 
  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']) 
  68     def _real_extract(self
, url
): 
  69         video_id 
= self
._match
_id
(url
) 
  71         api_key 
= self
._download
_json
( 
  72             'https://www.flickr.com/hermes_error_beacon.gne', video_id
, 
  73             'Downloading api key')['site_key'] 
  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'] 
  82             preference 
= qualities( 
  83                 ['288p', 'iphone_wifi', '100', '300', '700', '360p', 'appletv', '720p', '1080p', 'orig']) 
  86             for stream 
in streams
['stream']: 
  87                 stream_type 
= compat_str(stream
.get('type')) 
  89                     'format_id': stream_type
, 
  90                     'url': stream
['_content'], 
  91                     'preference': preference(stream_type
), 
  93             self
._sort
_formats
(formats
) 
  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 
 102                 'title': video_info
['title']['_content'], 
 103                 'description': video_info
.get('description', {}).get('_content'), 
 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')), 
 116             raise ExtractorError('not a video', expected
=True)