gfycat.py 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146
  1. from .common import InfoExtractor
  2. from ..utils import (
  3. int_or_none,
  4. float_or_none,
  5. qualities,
  6. ExtractorError,
  7. )
  8. class GfycatIE(InfoExtractor):
  9. _VALID_URL = r'https?://(?:(?:www|giant|thumbs)\.)?gfycat\.com/(?i:ru/|ifr/|gifs/detail/)?(?P<id>[^-/?#\."\']+)'
  10. _EMBED_REGEX = [rf'<(?:iframe|source)[^>]+\bsrc=["\'](?P<url>{_VALID_URL})']
  11. _TESTS = [{
  12. 'url': 'http://gfycat.com/DeadlyDecisiveGermanpinscher',
  13. 'info_dict': {
  14. 'id': 'DeadlyDecisiveGermanpinscher',
  15. 'ext': 'mp4',
  16. 'title': 'Ghost in the Shell',
  17. 'timestamp': 1410656006,
  18. 'upload_date': '20140914',
  19. 'uploader': 'anonymous',
  20. 'duration': 10.4,
  21. 'view_count': int,
  22. 'like_count': int,
  23. 'categories': list,
  24. 'age_limit': 0,
  25. 'uploader_id': 'anonymous',
  26. 'description': '',
  27. }
  28. }, {
  29. 'url': 'http://gfycat.com/ifr/JauntyTimelyAmazontreeboa',
  30. 'info_dict': {
  31. 'id': 'JauntyTimelyAmazontreeboa',
  32. 'ext': 'mp4',
  33. 'title': 'JauntyTimelyAmazontreeboa',
  34. 'timestamp': 1411720126,
  35. 'upload_date': '20140926',
  36. 'uploader': 'anonymous',
  37. 'duration': 3.52,
  38. 'view_count': int,
  39. 'like_count': int,
  40. 'categories': list,
  41. 'age_limit': 0,
  42. 'uploader_id': 'anonymous',
  43. 'description': '',
  44. }
  45. }, {
  46. 'url': 'https://gfycat.com/alienatedsolidgreathornedowl',
  47. 'info_dict': {
  48. 'id': 'alienatedsolidgreathornedowl',
  49. 'ext': 'mp4',
  50. 'upload_date': '20211226',
  51. 'uploader_id': 'reactions',
  52. 'timestamp': 1640536930,
  53. 'like_count': int,
  54. 'description': '',
  55. 'title': 'Ingrid Michaelson, Zooey Deschanel - Merry Christmas Happy New Year',
  56. 'categories': list,
  57. 'age_limit': 0,
  58. 'duration': 2.9583333333333335,
  59. 'uploader': 'Reaction GIFs',
  60. 'view_count': int,
  61. }
  62. }, {
  63. 'url': 'https://gfycat.com/ru/RemarkableDrearyAmurstarfish',
  64. 'only_matching': True
  65. }, {
  66. 'url': 'https://gfycat.com/gifs/detail/UnconsciousLankyIvorygull',
  67. 'only_matching': True
  68. }, {
  69. 'url': 'https://gfycat.com/acceptablehappygoluckyharborporpoise-baseball',
  70. 'only_matching': True
  71. }, {
  72. 'url': 'https://thumbs.gfycat.com/acceptablehappygoluckyharborporpoise-size_restricted.gif',
  73. 'only_matching': True
  74. }, {
  75. 'url': 'https://giant.gfycat.com/acceptablehappygoluckyharborporpoise.mp4',
  76. 'only_matching': True
  77. }, {
  78. 'url': 'http://gfycat.com/IFR/JauntyTimelyAmazontreeboa',
  79. 'only_matching': True
  80. }]
  81. def _real_extract(self, url):
  82. video_id = self._match_id(url)
  83. gfy = self._download_json(
  84. 'https://api.gfycat.com/v1/gfycats/%s' % video_id,
  85. video_id, 'Downloading video info')
  86. if 'error' in gfy:
  87. raise ExtractorError('Gfycat said: ' + gfy['error'], expected=True)
  88. gfy = gfy['gfyItem']
  89. title = gfy.get('title') or gfy['gfyName']
  90. description = gfy.get('description')
  91. timestamp = int_or_none(gfy.get('createDate'))
  92. uploader = gfy.get('userName') or gfy.get('username')
  93. view_count = int_or_none(gfy.get('views'))
  94. like_count = int_or_none(gfy.get('likes'))
  95. dislike_count = int_or_none(gfy.get('dislikes'))
  96. age_limit = 18 if gfy.get('nsfw') == '1' else 0
  97. width = int_or_none(gfy.get('width'))
  98. height = int_or_none(gfy.get('height'))
  99. fps = int_or_none(gfy.get('frameRate'))
  100. num_frames = int_or_none(gfy.get('numFrames'))
  101. duration = float_or_none(num_frames, fps) if num_frames and fps else None
  102. categories = gfy.get('tags') or gfy.get('extraLemmas') or []
  103. FORMATS = ('gif', 'webm', 'mp4')
  104. quality = qualities(FORMATS)
  105. formats = []
  106. for format_id in FORMATS:
  107. video_url = gfy.get('%sUrl' % format_id)
  108. if not video_url:
  109. continue
  110. filesize = int_or_none(gfy.get('%sSize' % format_id))
  111. formats.append({
  112. 'url': video_url,
  113. 'format_id': format_id,
  114. 'width': width,
  115. 'height': height,
  116. 'fps': fps,
  117. 'filesize': filesize,
  118. 'quality': quality(format_id),
  119. })
  120. return {
  121. 'id': video_id,
  122. 'title': title,
  123. 'description': description,
  124. 'timestamp': timestamp,
  125. 'uploader': gfy.get('userDisplayName') or uploader,
  126. 'uploader_id': uploader,
  127. 'duration': duration,
  128. 'view_count': view_count,
  129. 'like_count': like_count,
  130. 'dislike_count': dislike_count,
  131. 'categories': categories,
  132. 'age_limit': age_limit,
  133. 'formats': formats,
  134. }