vine.py 5.2 KB

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