]>
Raphaël G. Git Repositories - youtubedl/blob - youtube_dl/extractor/vimeo.py 
   2  from  __future__ 
import  unicode_literals
   8  from  . common 
import  InfoExtractor
  12      compat_urllib_request
,   28  class  VimeoBaseInfoExtractor ( InfoExtractor
):   29      _NETRC_MACHINE 
=  'vimeo'   30      _LOGIN_REQUIRED 
=  False   33          ( username
,  password
) =  self
._ get
_l ogin
_ info
()   35              if  self
._L OGIN
_ REQUIRED
:   36                  raise  ExtractorError ( 'No login info available, needed for using  %s .'  %  self
. IE_NAME
,  expected
= True )   39          login_url 
=  'https://vimeo.com/log_in'   40          webpage 
=  self
._ download
_ webpage
( login_url
,  None ,  False )   41          token 
=  self
._ search
_ regex
( r
'xsrft":"(.*?)"' ,  webpage
,  'login token' )   42          data 
=  urlencode_postdata ({   49          login_request 
=  compat_urllib_request
. Request ( login_url
,  data
)   50          login_request
. add_header ( 'Content-Type' ,  'application/x-www-form-urlencoded' )   51          login_request
. add_header ( 'Cookie' ,  'xsrft= %s '  %  token
)   52          self
._ download
_ webpage
( login_request
,  None ,  False ,  'Wrong login info' )   55  class  VimeoIE ( VimeoBaseInfoExtractor
):   56      """Information extractor for vimeo.com."""   58      # _VALID_URL matches Vimeo URLs   61          (?:(?:www|(?P<player>player))\.)?   62          vimeo(?P<pro>pro)?\.com/   63          (?!channels/[^/?#]+/?(?:$|[?#])|album/)   65          (?:(?:play_redirect_hls|moogaloop\.swf)\?clip_id=)?   68          /?(?:[?&].*)?(?:[#].*)?$'''   72              'url' :  'http://vimeo.com/56015672#at=0' ,   73              'md5' :  '8879b6cc097e987f02484baf890129e5' ,   77                  "upload_date" :  "20121220" ,   78                  "description" :  "This is a test case for youtube-dl. \n For more information, see github.com/rg3/youtube-dl \n Test chars: \u2605  \"  ' \u5e78 /  \\  \u00e4 \u21ad \U0001d550" ,   79                  "uploader_id" :  "user7108434" ,   80                  "uploader" :  "Filippo Valsorda" ,   81                  "title" :  "youtube-dl test video - \u2605  \"  ' \u5e78 /  \\  \u00e4 \u21ad \U0001d550" ,   86              'url' :  'http://vimeopro.com/openstreetmapus/state-of-the-map-us-2013/video/68093876' ,   87              'md5' :  '3b5ca6aa22b60dfeeadf50b72e44ed82' ,   88              'note' :  'Vimeo Pro video (#1197)' ,   92                  'uploader_id' :  'openstreetmapus' ,   93                  'uploader' :  'OpenStreetMap US' ,   94                  'title' :  'Andy Allan - Putting the Carto into OpenStreetMap Cartography' ,   95                  'description' :  'md5:380943ec71b89736ff4bf27183233d09' ,  100              'url' :  'http://player.vimeo.com/video/54469442' ,  101              'md5' :  '619b811a4417aa4abe78dc653becf511' ,  102              'note' :  'Videos that embed the url in the player page' ,  106                  'title' :  'Kathy Sierra: Building the minimum Badass User, Business of Software 2012' ,  107                  'uploader' :  'The BLN & Business of Software' ,  108                  'uploader_id' :  'theblnbusinessofsoftware' ,  114              'url' :  'http://vimeo.com/68375962' ,  115              'md5' :  'aaf896bdb7ddd6476df50007a0ac0ae7' ,  116              'note' :  'Video protected with password' ,  120                  'title' :  'youtube-dl password protected test video' ,  121                  'upload_date' :  '20130614' ,  122                  'uploader_id' :  'user18948128' ,  123                  'uploader' :  'Jaime Marquínez Ferrándiz' ,  125                  'description' :  'This is "youtube-dl password protected test video" by Jaime Marquínez Ferrándiz on Vimeo, the home for high quality videos and the people who love them.' ,  128                  'videopassword' :  'youtube-dl' ,  132              'url' :  'http://vimeo.com/channels/keypeele/75629013' ,  133              'md5' :  '2f86a05afe9d7abc0b9126d229bbe15d' ,  134              'note' :  'Video is freely available via original URL '  135                      'and protected with password when accessed via http://vimeo.com/75629013' ,  139                  'title' :  'Key & Peele: Terrorist Interrogation' ,  140                  'description' :  'md5:8678b246399b070816b12313e8b4eb5c' ,  141                  'uploader_id' :  'atencio' ,  142                  'uploader' :  'Peter Atencio' ,  143                  'upload_date' :  '20130927' ,  148              'url' :  'http://vimeo.com/76979871' ,  149              'md5' :  '3363dd6ffebe3784d56f4132317fd446' ,  150              'note' :  'Video with subtitles' ,  154                  'title' :  'The New Vimeo Player (You Know, For Videos)' ,  155                  'description' :  'md5:2ec900bf97c3f389378a96aee11260ea' ,  156                  'upload_date' :  '20131015' ,  157                  'uploader_id' :  'staff' ,  158                  'uploader' :  'Vimeo Staff' ,  163              # from https://www.ouya.tv/game/Pier-Solar-and-the-Great-Architects/  164              'url' :  'https://player.vimeo.com/video/98044508' ,  165              'note' :  'The js code contains assignments to the same variable as the config' ,  169                  'title' :  'Pier Solar OUYA Official Trailer' ,  170                  'uploader' :  'Tulio Gonçalves' ,  171                  'uploader_id' :  'user28849593' ,  176      def  _verify_video_password ( self
,  url
,  video_id
,  webpage
):  177          password 
=  self
._ downloader
. params
. get ( 'videopassword' ,  None )  179              raise  ExtractorError ( 'This video is protected by a password, use the --video-password option' ,  expected
= True )  180          token 
=  self
._ search
_ regex
( r
'xsrft[\s=:"\' ]+([ ^
" \' ]+)', webpage, 'login token')  181          data = urlencode_postdata({  182              'password': password,  185          if url.startswith('http://'):  186              # vimeo only supports https now, but the user can give an http url  187              url = url.replace('http://', 'https://')  188          password_request = compat_urllib_request.Request(url + '/password', data)  189          password_request.add_header('Content-Type', 'application/x-www-form-urlencoded')  190          password_request.add_header('Cookie', 'xsrft= %s ' % token)  191          return self._download_webpage(  192              password_request, video_id,  193              'Verifying the password', 'Wrong password')  195      def _verify_player_video_password(self, url, video_id):  196          password = self._downloader.params.get('videopassword', None)  198              raise ExtractorError('This video is protected by a password, use the --video-password option')  199          data = compat_urllib_parse.urlencode({'password': password})  200          pass_url = url + '/check-password'  201          password_request = compat_urllib_request.Request(pass_url, data)  202          password_request.add_header('Content-Type', 'application/x-www-form-urlencoded')  203          return self._download_json(  204              password_request, video_id,  205              'Verifying the password',  208      def _real_initialize(self):  211      def _real_extract(self, url):  212          url, data = unsmuggle_url(url)  213          headers = std_headers  215              headers = headers.copy()  217          if 'Referer' not in headers:  218              headers['Referer'] = url  220          # Extract ID from URL  221          mobj = re.match(self._VALID_URL, url)  222          video_id = mobj.group('id')  224          if mobj.group('pro') or mobj.group('player'):  225              url = 'https://player.vimeo.com/video/' + video_id  227              url = 'https://vimeo.com/' + video_id  229          # Retrieve video webpage to extract further information  230          request = compat_urllib_request.Request(url, None, headers)  232              webpage = self._download_webpage(request, video_id)  233          except ExtractorError as ee:  234              if isinstance(ee.cause, compat_HTTPError) and ee.cause.code == 403:  235                  errmsg = ee.cause.read()  236                  if b'Because of its privacy settings, this video cannot be played here' in errmsg:  237                      raise ExtractorError(  238                          'Cannot download embed-only video without embedding '  239                          'URL. Please call youtube-dl with the URL of the page '  240                          'that embeds this video.',  244          # Now we begin extracting as much information as we can from what we  245          # retrieved. First we extract the information common to all extractors,  246          # and latter we extract those that are Vimeo specific.  247          self.report_extraction(video_id)  249          vimeo_config = self._search_regex(  250              r'vimeo\.config\s*=\s*({.+?});', webpage,  251              'vimeo config', default=None)  253              seed_status = self._parse_json(vimeo_config, video_id).get('seed_status', {})  254              if seed_status.get('state') == 'failed':  255                  raise ExtractorError(  256                      ' %s  returned error:  %s ' % (self.IE_NAME, seed_status['title']),  259          # Extract the config JSON  262                  config_url = self._html_search_regex(  263                      r' data-config-url=" (.+ ?
) "', webpage, 'config URL')  264                  config_json = self._download_webpage(config_url, video_id)  265                  config = json.loads(config_json)  266              except RegexNotFoundError:  267                  # For pro videos or player.vimeo.com urls  268                  # We try to find out to which variable is assigned the config dic  269                  m_variable_name = re.search('(\w)\.video\.id', webpage)  270                  if m_variable_name is not None:  271                      config_re = r' %s =({[^}].+?});' % re.escape(m_variable_name.group(1))  273                      config_re = [r' = {config:({.+?}),assets:', r'(?:[abc])=({.+?});']  274                  config = self._search_regex(config_re, webpage, 'info section',  276                  config = json.loads(config)  277          except Exception as e:  278              if re.search('The creator of this video has not given you permission to embed it on this domain.', webpage):  279                  raise ExtractorError('The author has restricted the access to this video, try with the " -- referer
" option')  281              if re.search(r'<form[^>]+?id=" pw_form
"', webpage) is not None:  282                  if data and '_video_password_verified' in data:  283                      raise ExtractorError('video password verification failed!')  284                  self._verify_video_password(url, video_id, webpage)  285                  return self._real_extract(  286                      smuggle_url(url, {'_video_password_verified': 'verified'}))  288                  raise ExtractorError('Unable to extract info section',  291              if config.get('view') == 4:  292                  config = self._verify_player_video_password(url, video_id)  295          video_title = config[" video
"][" title
"]  297          # Extract uploader and uploader_id  298          video_uploader = config[" video
"][" owner
"][" name
"]  299          video_uploader_id = config[" video
"][" owner
"][" url
"].split('/')[-1] if config[" video
"][" owner
"][" url
"] else None  301          # Extract video thumbnail  302          video_thumbnail = config[" video
"].get(" thumbnail
")  303          if video_thumbnail is None:  304              video_thumbs = config[" video
"].get(" thumbs
")  305              if video_thumbs and isinstance(video_thumbs, dict):  306                  _, video_thumbnail = sorted((int(width if width.isdigit() else 0), t_url) for (width, t_url) in video_thumbs.items())[-1]  308          # Extract video description  310          video_description = self._html_search_regex(  311              r'(?s)<div\s+class=" [ ^
"]*description[^" ]* "[^>]*>(.*?)</div>',  312              webpage, 'description', default=None)  313          if not video_description:  314              video_description = self._html_search_meta(  315                  'description', webpage, default=None)  316          if not video_description and mobj.group('pro'):  317              orig_webpage = self._download_webpage(  319                  note='Downloading webpage for description',  322                  video_description = self._html_search_meta(  323                      'description', orig_webpage, default=None)  324          if not video_description and not mobj.group('player'):  325              self._downloader.report_warning('Cannot find video description')  327          # Extract video duration  328          video_duration = int_or_none(config[" video
"].get(" duration
"))  330          # Extract upload date  331          video_upload_date = None  332          mobj = re.search(r'<time[^>]+datetime=" ([ ^
"]+)" ', webpage)  334              video_upload_date = unified_strdate(mobj.group(1))  337              view_count = int(self._search_regex(r' UserPlays
:( \d
+) ', webpage, ' view count
'))  338              like_count = int(self._search_regex(r' UserLikes
:( \d
+) ', webpage, ' like count
'))  339              comment_count = int(self._search_regex(r' UserComments
:( \d
+) ', webpage, ' comment count
'))  340          except RegexNotFoundError:  341              # This info is only available in vimeo.com/ {id}  urls  346          # Vimeo specific: extract request signature and timestamp  347          sig = config[' request
'][' signature
']  348          timestamp = config[' request
'][' timestamp
']  350          # Vimeo specific: extract video codec and quality information  351          # First consider quality, then codecs, then take everything  352          codecs = [(' vp6
', ' flv
'), (' vp8
', ' flv
'), (' h264
', ' mp4
')]  353          files = {' hd
': [], ' sd
': [], ' other
': []}  354          config_files = config["video"].get("files") or config["request"].get("files")  355          for codec_name, codec_extension in codecs:  356              for quality in config_files.get(codec_name, []):  357                  format_id = ' - '.join((codec_name, quality)).lower()  358                  key = quality if quality in files else ' other
'  360                  if isinstance(config_files[codec_name], dict):  361                      file_info = config_files[codec_name][quality]  362                      video_url = file_info.get(' url
')  365                  if video_url is None:  366                      video_url = "http://player.vimeo.com/play_redirect?clip_id= %s &sig= %s &time= %s &quality= %s &codecs= %s &type=moogaloop_local&embed_location=" \  367                          % (video_id, sig, timestamp, quality, codec_name.upper())  370                      ' ext
': codec_extension,  372                      ' format_id
': format_id,  373                      ' width
': file_info.get(' width
'),  374                      ' height
': file_info.get(' height
'),  377          for key in (' other
', ' sd
', ' hd
'):  378              formats += files[key]  379          if len(formats) == 0:  380              raise ExtractorError(' No known codec found
')  383          text_tracks = config[' request
'].get(' text_tracks
')  385              for tt in text_tracks:  386                  subtitles[tt[' lang
']] = [{  388                      ' url
': ' https
:// vimeo
. com
' + tt[' url
'],  393              ' uploader
': video_uploader,  394              ' uploader_id
': video_uploader_id,  395              ' upload_date
': video_upload_date,  396              ' title
': video_title,  397              ' thumbnail
': video_thumbnail,  398              ' description
': video_description,  399              ' duration
': video_duration,  402              ' view_count
': view_count,  403              ' like_count
': like_count,  404              ' comment_count
': comment_count,  405              ' subtitles
': subtitles,  409  class VimeoChannelIE(InfoExtractor):  410      IE_NAME = ' vimeo
: channel
'  411      _VALID_URL = r' https
:// vimeo\
. com
/ channels
/( ?P
< id >[ ^
/ ?
#]+)/?(?:$|[?#])'  412      _MORE_PAGES_INDICATOR 
=  r
'<a.+?rel="next"'  413      _TITLE_RE 
=  r
'<link rel="alternate"[^>]+?title="(.*?)"'  415          'url' :  'https://vimeo.com/channels/tributes' ,  418              'title' :  'Vimeo Tributes' ,  420          'playlist_mincount' :  25 ,  423      def  _page_url ( self
,  base_url
,  pagenum
):  424          return  ' %s /videos/page: %d /'  % ( base_url
,  pagenum
)  426      def  _extract_list_title ( self
,  webpage
):  427          return  self
._ html
_ search
_ regex
( self
._ TITLE
_ RE
,  webpage
,  'list title' )  429      def  _login_list_password ( self
,  page_url
,  list_id
,  webpage
):  430          login_form 
=  self
._ search
_ regex
(  431              r
'(?s)<form[^>]+?id="pw_form"(.*?)</form>' ,  432              webpage
,  'login form' ,  default
= None )  436          password 
=  self
._ downloader
. params
. get ( 'videopassword' ,  None )  438              raise  ExtractorError ( 'This album is protected by a password, use the --video-password option' ,  expected
= True )  439          fields 
=  dict ( re
. findall ( r
'''(?x)<input\s+  444          token 
=  self
._ search
_ regex
( r
'xsrft[\s=:"\' ]+([ ^
" \' ]+)', webpage, 'login token')  445          fields['token'] = token  446          fields['password'] = password  447          post = urlencode_postdata(fields)  448          password_path = self._search_regex(  449              r'action=" ([ ^
"]+)" ', login_form, ' password URL
')  450          password_url = compat_urlparse.urljoin(page_url, password_path)  451          password_request = compat_urllib_request.Request(password_url, post)  452          password_request.add_header(' Content
- type ', ' application
/ x
- www
- form
- urlencoded
')  453          self._set_cookie(' vimeo
. com
', ' xsrft
', token)  455          return self._download_webpage(  456              password_request, list_id,  457              ' Verifying the password
', ' Wrong password
')  459      def _extract_videos(self, list_id, base_url):  461          for pagenum in itertools.count(1):  462              page_url = self._page_url(base_url, pagenum)  463              webpage = self._download_webpage(  465                  ' Downloading page 
%s ' % pagenum)  468                  webpage = self._login_list_password(page_url, list_id, webpage)  470              video_ids.extend(re.findall(r' id = "clip_(\d+?)" ', webpage))  471              if re.search(self._MORE_PAGES_INDICATOR, webpage, re.DOTALL) is None:  474          entries = [self.url_result(' https
:// vimeo
. com
/ %s ' % video_id, ' Vimeo
')  475                     for video_id in video_ids]  476          return {' _type
': ' playlist
',  478                  ' title
': self._extract_list_title(webpage),  482      def _real_extract(self, url):  483          mobj = re.match(self._VALID_URL, url)  484          channel_id = mobj.group(' id ')  485          return self._extract_videos(channel_id, ' https
:// vimeo
. com
/ channels
/ %s ' % channel_id)  488  class VimeoUserIE(VimeoChannelIE):  489      IE_NAME = ' vimeo
: user
'  490      _VALID_URL = r' https
:// vimeo\
. com
/( ?
![ 0 - 9 ]+( ?
: $|
[ ?
#/]))(?P<name>[^/]+)(?:/videos|[#?]|$)'  491      _TITLE_RE 
=  r
'<a[^>]+?class="user">([^<>]+?)</a>'  493          'url' :  'https://vimeo.com/nkistudio/videos' ,  498          'playlist_mincount' :  66 ,  501      def  _real_extract ( self
,  url
):  502          mobj 
=  re
. match ( self
._ VALID
_U RL
,  url
)  503          name 
=  mobj
. group ( 'name' )  504          return  self
._ extract
_ videos
( name
,  'https://vimeo.com/ %s '  %  name
)  507  class  VimeoAlbumIE ( VimeoChannelIE
):  508      IE_NAME 
=  'vimeo:album'  509      _VALID_URL 
=  r
'https://vimeo\.com/album/(?P<id>\d+)'  510      _TITLE_RE 
=  r
'<header id="page_header">\n\s*<h1>(.*?)</h1>'  512          'url' :  'https://vimeo.com/album/2632481' ,  515              'title' :  'Staff Favorites: November 2013' ,  517          'playlist_mincount' :  13 ,  519          'note' :  'Password-protected album' ,  520          'url' :  'https://vimeo.com/album/3253534' ,  527              'videopassword' :  'youtube-dl' ,  531      def  _page_url ( self
,  base_url
,  pagenum
):  532          return  ' %s /page: %d /'  % ( base_url
,  pagenum
)  534      def  _real_extract ( self
,  url
):  535          album_id 
=  self
._ match
_ id
( url
)  536          return  self
._ extract
_ videos
( album_id
,  'https://vimeo.com/album/ %s '  %  album_id
)  539  class  VimeoGroupsIE ( VimeoAlbumIE
):  540      IE_NAME 
=  'vimeo:group'  541      _VALID_URL 
=  r
'https://vimeo\.com/groups/(?P<name>[^/]+)'  543          'url' :  'https://vimeo.com/groups/rolexawards' ,  546              'title' :  'Rolex Awards for Enterprise' ,  548          'playlist_mincount' :  73 ,  551      def  _extract_list_title ( self
,  webpage
):  552          return  self
._ og
_ search
_ title
( webpage
)  554      def  _real_extract ( self
,  url
):  555          mobj 
=  re
. match ( self
._ VALID
_U RL
,  url
)  556          name 
=  mobj
. group ( 'name' )  557          return  self
._ extract
_ videos
( name
,  'https://vimeo.com/groups/ %s '  %  name
)  560  class  VimeoReviewIE ( InfoExtractor
):  561      IE_NAME 
=  'vimeo:review'  562      IE_DESC 
=  'Review pages on vimeo'  563      _VALID_URL 
=  r
'https://vimeo\.com/[^/]+/review/(?P<id>[^/]+)'  565          'url' :  'https://vimeo.com/user21297594/review/75524534/3c257a1b5d' ,  566          'md5' :  'c507a72f780cacc12b2248bb4006d253' ,  570              'title' :  "DICK HARDWICK 'Comedian'" ,  571              'uploader' :  'Richard Hardwick' ,  574          'note' :  'video player needs Referer' ,  575          'url' :  'https://vimeo.com/user22258446/review/91613211/13f927e053' ,  576          'md5' :  '6295fdab8f4bf6a002d058b2c6dce276' ,  580              'title' :  're:(?i)^Death by dogma versus assembling agile . Sander Hoogendoorn' ,  581              'uploader' :  'DevWeek Events' ,  583              'thumbnail' :  're:^https?://.*\.jpg$' ,  587      def  _real_extract ( self
,  url
):  588          mobj 
=  re
. match ( self
._ VALID
_U RL
,  url
)  589          video_id 
=  mobj
. group ( 'id' )  590          player_url 
=  'https://player.vimeo.com/player/'  +  video_id
 591          return  self
. url_result ( player_url
,  'Vimeo' ,  video_id
)  594  class  VimeoWatchLaterIE ( VimeoBaseInfoExtractor
,  VimeoChannelIE
):  595      IE_NAME 
=  'vimeo:watchlater'  596      IE_DESC 
=  'Vimeo watch later list, "vimeowatchlater" keyword (requires authentication)'  597      _VALID_URL 
=  r
'https://vimeo\.com/home/watchlater|:vimeowatchlater'  598      _LOGIN_REQUIRED 
=  True  599      _TITLE_RE 
=  r
'href="/home/watchlater".*?>(.*?)<'  601          'url' :  'https://vimeo.com/home/watchlater' ,  602          'only_matching' :  True ,  605      def  _real_initialize ( self
):  608      def  _page_url ( self
,  base_url
,  pagenum
):  609          url 
=  ' %s /page: %d /'  % ( base_url
,  pagenum
)  610          request 
=  compat_urllib_request
. Request ( url
)  611          # Set the header to get a partial html page with the ids,  612          # the normal page doesn't contain them.  613          request
. add_header ( 'X-Requested-With' ,  'XMLHttpRequest' )  616      def  _real_extract ( self
,  url
):  617          return  self
._ extract
_ videos
( 'watchlater' ,  'https://vimeo.com/home/watchlater' )  620  class  VimeoLikesIE ( InfoExtractor
):  621      _VALID_URL 
=  r
'https://(?:www\.)?vimeo\.com/user(?P<id>[0-9]+)/likes/?(?:$|[?#]|sort:)'  622      IE_NAME 
=  'vimeo:likes'  623      IE_DESC 
=  'Vimeo user likes'  625          'url' :  'https://vimeo.com/user755559/likes/' ,  626          'playlist_mincount' :  293 ,  628              'id' :  'user755559_likes' ,  629              "description" :  "See all the videos urza likes" ,  630              "title" :  'Videos urza likes' ,  634      def  _real_extract ( self
,  url
):  635          user_id 
=  self
._ match
_ id
( url
)  636          webpage 
=  self
._ download
_ webpage
( url
,  user_id
)  637          page_count 
=  self
._ int
(  639                  r
'''(?x)<li><a\s+href="[^"]+"\s+data-page="([0-9]+)">  640                      .*?</a></li>\s*<li\s+class="pagination_next">  641                  ''' ,  webpage
,  'page count' ),  642              'page count' ,  fatal
= True )  644          title 
=  self
._ html
_ search
_ regex
(  645              r
'(?s)<h1>(.+?)</h1>' ,  webpage
,  'title' ,  fatal
= False )  646          description 
=  self
._ html
_ search
_ meta
( 'description' ,  webpage
)  649              page_url 
=  'https://vimeo.com/user %s /likes/page: %d /sort:date'  % (  651              webpage 
=  self
._ download
_ webpage
(  653                  note
= 'Downloading page  %d / %d '  % ( idx 
+  1 ,  page_count
))  654              video_list 
=  self
._ search
_ regex
(  655                  r
'(?s)<ol class="js-browse_list[^"]+"[^>]*>(.*?)</ol>' ,  656                  webpage
,  'video content' )  658                  r
'<li[^>]*>\s*<a\s+href="([^"]+)"' ,  video_list
)  662                      'url' :  compat_urlparse
. urljoin ( page_url
,  path
),  665          pl 
=  InAdvancePagedList ( _get_page
,  page_count
,  PAGE_SIZE
)  669              'id' :  'user %s _likes'  %  user_id
,  671              'description' :  description
,