[vine:user] Fix extraction (closes #15514)
[youtube-dl] / youtube_dl / extractor / vine.py
1 # coding: utf-8
2 from __future__ import unicode_literals
3
4 import re
5
6 from .common import InfoExtractor
7 from ..utils import (
8     determine_ext,
9     int_or_none,
10     unified_timestamp,
11 )
12
13
14 class VineIE(InfoExtractor):
15     _VALID_URL = r'https?://(?:www\.)?vine\.co/(?:v|oembed)/(?P<id>\w+)'
16     _TESTS = [{
17         'url': 'https://vine.co/v/b9KOOWX7HUx',
18         'md5': '2f36fed6235b16da96ce9b4dc890940d',
19         'info_dict': {
20             'id': 'b9KOOWX7HUx',
21             'ext': 'mp4',
22             'title': 'Chicken.',
23             'alt_title': 'Vine by Jack',
24             'timestamp': 1368997951,
25             'upload_date': '20130519',
26             'uploader': 'Jack',
27             'uploader_id': '76',
28             'view_count': int,
29             'like_count': int,
30             'comment_count': int,
31             'repost_count': int,
32         },
33     }, {
34         'url': 'https://vine.co/v/e192BnZnZ9V',
35         'info_dict': {
36             'id': 'e192BnZnZ9V',
37             'ext': 'mp4',
38             'title': 'ยิ้ม~ เขิน~ อาย~ น่าร้ากอ้ะ >//< @n_whitewo @orlameena #lovesicktheseries  #lovesickseason2',
39             'alt_title': 'Vine by Pimry_zaa',
40             'timestamp': 1436057405,
41             'upload_date': '20150705',
42             'uploader': 'Pimry_zaa',
43             'uploader_id': '1135760698325307392',
44             'view_count': int,
45             'like_count': int,
46             'comment_count': int,
47             'repost_count': int,
48         },
49         'params': {
50             'skip_download': True,
51         },
52     }, {
53         'url': 'https://vine.co/v/MYxVapFvz2z',
54         'only_matching': True,
55     }, {
56         'url': 'https://vine.co/v/bxVjBbZlPUH',
57         'only_matching': True,
58     }, {
59         'url': 'https://vine.co/oembed/MYxVapFvz2z.json',
60         'only_matching': True,
61     }]
62
63     def _real_extract(self, url):
64         video_id = self._match_id(url)
65
66         data = self._download_json(
67             'https://archive.vine.co/posts/%s.json' % video_id, video_id)
68
69         def video_url(kind):
70             for url_suffix in ('Url', 'URL'):
71                 format_url = data.get('video%s%s' % (kind, url_suffix))
72                 if format_url:
73                     return format_url
74
75         formats = []
76         for quality, format_id in enumerate(('low', '', 'dash')):
77             format_url = video_url(format_id.capitalize())
78             if not format_url:
79                 continue
80             # DASH link returns plain mp4
81             if format_id == 'dash' and determine_ext(format_url) == 'mpd':
82                 formats.extend(self._extract_mpd_formats(
83                     format_url, video_id, mpd_id='dash', fatal=False))
84             else:
85                 formats.append({
86                     'url': format_url,
87                     'format_id': format_id or 'standard',
88                     'quality': quality,
89                 })
90         self._sort_formats(formats)
91
92         username = data.get('username')
93
94         alt_title = 'Vine by %s' % username if username else None
95
96         return {
97             'id': video_id,
98             'title': data.get('description') or alt_title or 'Vine video',
99             'alt_title': alt_title,
100             'thumbnail': data.get('thumbnailUrl'),
101             'timestamp': unified_timestamp(data.get('created')),
102             'uploader': username,
103             'uploader_id': data.get('userIdStr'),
104             'view_count': int_or_none(data.get('loops')),
105             'like_count': int_or_none(data.get('likes')),
106             'comment_count': int_or_none(data.get('comments')),
107             'repost_count': int_or_none(data.get('reposts')),
108             'formats': formats,
109         }
110
111
112 class VineUserIE(InfoExtractor):
113     IE_NAME = 'vine:user'
114     _VALID_URL = r'(?:https?://)?vine\.co/(?P<u>u/)?(?P<user>[^/]+)/?(\?.*)?$'
115     _VINE_BASE_URL = 'https://vine.co/'
116     _TESTS = [
117         {
118             'url': 'https://vine.co/itsruthb',
119             'info_dict': {
120                 'id': 'itsruthb',
121             },
122             'playlist_mincount': 611,
123         },
124         {
125             'url': 'https://vine.co/u/942914934646415360',
126             'only_matching': True,
127         },
128     ]
129
130     def _real_extract(self, url):
131         mobj = re.match(self._VALID_URL, url)
132         user = mobj.group('user')
133         u = mobj.group('u')
134
135         profile_url = '%sapi/users/profiles/%s%s' % (
136             self._VINE_BASE_URL, 'vanity/' if not u else '', user)
137         profile_data = self._download_json(
138             profile_url, user, note='Downloading user profile data')
139
140         user_id = profile_data['data']['userId']
141         user_archive = self._download_json(
142             'https://archive.vine.co/profiles/%s.json' % user_id, user_id)
143         posts = user_archive['posts']
144         entries = [
145             self.url_result('https://vine.co/v/%s' % post_id, 'Vine')
146             for post_id in posts]
147         return self.playlist_result(entries, user)