]> jfr.im git - yt-dlp.git/blob - yt_dlp/extractor/storyfire.py
[cleanup] Use format_field where applicable
[yt-dlp.git] / yt_dlp / extractor / storyfire.py
1 # coding: utf-8
2 from __future__ import unicode_literals
3
4 import functools
5
6 from .common import InfoExtractor
7 from ..utils import (
8 format_field,
9 int_or_none,
10 OnDemandPagedList,
11 smuggle_url,
12 )
13
14
15 class StoryFireBaseIE(InfoExtractor):
16 _VALID_URL_BASE = r'https?://(?:www\.)?storyfire\.com/'
17
18 def _call_api(self, path, video_id, resource, query=None):
19 return self._download_json(
20 'https://storyfire.com/app/%s/%s' % (path, video_id), video_id,
21 'Downloading %s JSON metadata' % resource, query=query)
22
23 def _parse_video(self, video):
24 title = video['title']
25 vimeo_id = self._search_regex(
26 r'https?://player\.vimeo\.com/external/(\d+)',
27 video['vimeoVideoURL'], 'vimeo id')
28
29 uploader_id = video.get('hostID')
30
31 return {
32 '_type': 'url_transparent',
33 'id': vimeo_id,
34 'title': title,
35 'description': video.get('description'),
36 'url': smuggle_url(
37 'https://player.vimeo.com/video/' + vimeo_id, {
38 'http_headers': {
39 'Referer': 'https://storyfire.com/',
40 }
41 }),
42 'thumbnail': video.get('storyImage'),
43 'view_count': int_or_none(video.get('views')),
44 'like_count': int_or_none(video.get('likesCount')),
45 'comment_count': int_or_none(video.get('commentsCount')),
46 'duration': int_or_none(video.get('videoDuration')),
47 'timestamp': int_or_none(video.get('publishDate')),
48 'uploader': video.get('username'),
49 'uploader_id': uploader_id,
50 'uploader_url': format_field(uploader_id, template='https://storyfire.com/user/%s/video'),
51 'episode_number': int_or_none(video.get('episodeNumber') or video.get('episode_number')),
52 }
53
54
55 class StoryFireIE(StoryFireBaseIE):
56 _VALID_URL = StoryFireBaseIE._VALID_URL_BASE + r'video-details/(?P<id>[0-9a-f]{24})'
57 _TEST = {
58 'url': 'https://storyfire.com/video-details/5df1d132b6378700117f9181',
59 'md5': 'caec54b9e4621186d6079c7ec100c1eb',
60 'info_dict': {
61 'id': '378954662',
62 'ext': 'mp4',
63 'title': 'Buzzfeed Teaches You About Memes',
64 'uploader_id': 'ntZAJFECERSgqHSxzonV5K2E89s1',
65 'timestamp': 1576129028,
66 'description': 'md5:0b4e28021548e144bed69bb7539e62ea',
67 'uploader': 'whang!',
68 'upload_date': '20191212',
69 'duration': 418,
70 'view_count': int,
71 'like_count': int,
72 'comment_count': int,
73 },
74 'params': {
75 'skip_download': True,
76 },
77 'expected_warnings': ['Unable to download JSON metadata']
78 }
79
80 def _real_extract(self, url):
81 video_id = self._match_id(url)
82 video = self._call_api(
83 'generic/video-detail', video_id, 'video')['video']
84 return self._parse_video(video)
85
86
87 class StoryFireUserIE(StoryFireBaseIE):
88 _VALID_URL = StoryFireBaseIE._VALID_URL_BASE + r'user/(?P<id>[^/]+)/video'
89 _TEST = {
90 'url': 'https://storyfire.com/user/UQ986nFxmAWIgnkZQ0ftVhq4nOk2/video',
91 'info_dict': {
92 'id': 'UQ986nFxmAWIgnkZQ0ftVhq4nOk2',
93 },
94 'playlist_mincount': 151,
95 }
96 _PAGE_SIZE = 20
97
98 def _fetch_page(self, user_id, page):
99 videos = self._call_api(
100 'publicVideos', user_id, 'page %d' % (page + 1), {
101 'skip': page * self._PAGE_SIZE,
102 })['videos']
103 for video in videos:
104 yield self._parse_video(video)
105
106 def _real_extract(self, url):
107 user_id = self._match_id(url)
108 entries = OnDemandPagedList(functools.partial(
109 self._fetch_page, user_id), self._PAGE_SIZE)
110 return self.playlist_result(entries, user_id)
111
112
113 class StoryFireSeriesIE(StoryFireBaseIE):
114 _VALID_URL = StoryFireBaseIE._VALID_URL_BASE + r'write/series/stories/(?P<id>[^/?&#]+)'
115 _TESTS = [{
116 'url': 'https://storyfire.com/write/series/stories/-Lq6MsuIHLODO6d2dDkr/',
117 'info_dict': {
118 'id': '-Lq6MsuIHLODO6d2dDkr',
119 },
120 'playlist_mincount': 13,
121 }, {
122 'url': 'https://storyfire.com/write/series/stories/the_mortal_one/',
123 'info_dict': {
124 'id': 'the_mortal_one',
125 },
126 'playlist_count': 0,
127 }]
128
129 def _extract_videos(self, stories):
130 for story in stories.values():
131 if story.get('hasVideo'):
132 yield self._parse_video(story)
133
134 def _real_extract(self, url):
135 series_id = self._match_id(url)
136 stories = self._call_api(
137 'seriesStories', series_id, 'series stories')
138 return self.playlist_result(self._extract_videos(stories), series_id)