1 from __future__ 
import unicode_literals
 
   3 from .common 
import InfoExtractor
 
   4 from ..compat 
import compat_urllib_parse_urlencode
 
  12 class FlickrIE(InfoExtractor
): 
  13     _VALID_URL 
= r
'https?://(?:www\.|secure\.)?flickr\.com/photos/[\w\-_@]+/(?P<id>\d+)' 
  15         'url': 'http://www.flickr.com/photos/forestwander-nature-pictures/5645318632/in/photostream/', 
  16         'md5': '164fe3fa6c22e18d448d4d5af2330f31', 
  20             '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.', 
  21             'title': 'Dark Hollow Waterfalls', 
  23             'timestamp': 1303528740, 
  24             'upload_date': '20110423', 
  25             'uploader_id': '10922353@N03', 
  26             'uploader': 'Forest Wander', 
  27             'uploader_url': 'https://www.flickr.com/photos/forestwander-nature-pictures/', 
  31             'license': 'Attribution-ShareAlike', 
  34     _API_BASE_URL 
= 'https://api.flickr.com/services/rest?' 
  35     # https://help.yahoo.com/kb/flickr/SLN25525.html 
  37         '0': 'All Rights Reserved', 
  38         '1': 'Attribution-NonCommercial-ShareAlike', 
  39         '2': 'Attribution-NonCommercial', 
  40         '3': 'Attribution-NonCommercial-NoDerivs', 
  42         '5': 'Attribution-ShareAlike', 
  43         '6': 'Attribution-NoDerivs', 
  44         '7': 'No known copyright restrictions', 
  45         '8': 'United States government work', 
  46         '9': 'Public Domain Dedication (CC0)', 
  47         '10': 'Public Domain Work', 
  50     def _call_api(self
, method
, video_id
, api_key
, note
, secret
=None): 
  53             'method': 'flickr.%s' % method
, 
  59             query
['secret'] = secret
 
  60         data 
= self
._download
_json
(self
._API
_BASE
_URL 
+ compat_urllib_parse_urlencode(query
), video_id
, note
) 
  61         if data
['stat'] != 'ok': 
  62             raise ExtractorError(data
['message']) 
  65     def _real_extract(self
, url
): 
  66         video_id 
= self
._match
_id
(url
) 
  68         api_key 
= self
._download
_json
( 
  69             'https://www.flickr.com/hermes_error_beacon.gne', video_id
, 
  70             'Downloading api key')['site_key'] 
  72         video_info 
= self
._call
_api
( 
  73             'photos.getInfo', video_id
, api_key
, 'Downloading video info')['photo'] 
  74         if video_info
['media'] == 'video': 
  75             streams 
= self
._call
_api
( 
  76                 'video.getStreamInfo', video_id
, api_key
, 
  77                 'Downloading streams info', video_info
['secret'])['streams'] 
  79             preference 
= qualities( 
  80                 ['288p', 'iphone_wifi', '100', '300', '700', '360p', 'appletv', '720p', '1080p', 'orig']) 
  83             for stream 
in streams
['stream']: 
  84                 stream_type 
= str(stream
.get('type')) 
  86                     'format_id': stream_type
, 
  87                     'url': stream
['_content'], 
  88                     'preference': preference(stream_type
), 
  90             self
._sort
_formats
(formats
) 
  92             owner 
= video_info
.get('owner', {}) 
  93             uploader_id 
= owner
.get('nsid') 
  94             uploader_path 
= owner
.get('path_alias') or uploader_id
 
  95             uploader_url 
= 'https://www.flickr.com/photos/%s/' % uploader_path 
if uploader_path 
else None 
  99                 'title': video_info
['title']['_content'], 
 100                 'description': video_info
.get('description', {}).get('_content'), 
 102                 'timestamp': int_or_none(video_info
.get('dateuploaded')), 
 103                 'duration': int_or_none(video_info
.get('video', {}).get('duration')), 
 104                 'uploader_id': uploader_id
, 
 105                 'uploader': owner
.get('realname'), 
 106                 'uploader_url': uploader_url
, 
 107                 'comment_count': int_or_none(video_info
.get('comments', {}).get('_content')), 
 108                 'view_count': int_or_none(video_info
.get('views')), 
 109                 'tags': [tag
.get('_content') for tag 
in video_info
.get('tags', {}).get('tag', [])], 
 110                 'license': self
._LICENSES
.get(video_info
.get('license')), 
 113             raise ExtractorError('not a video', expected
=True)