2 from __future__
import unicode_literals
4 from .common
import InfoExtractor
5 from ..compat
import compat_str
15 class MindsBaseIE(InfoExtractor
):
16 _VALID_URL_BASE
= r
'https?://(?:www\.)?minds\.com/'
18 def _call_api(self
, path
, video_id
, resource
, query
=None):
19 api_url
= 'https://www.minds.com/api/' + path
20 token
= self
._get
_cookies
(api_url
).get('XSRF-TOKEN')
21 return self
._download
_json
(
22 api_url
, video_id
, 'Downloading %s JSON metadata' % resource
, headers
={
23 'Referer': 'https://www.minds.com/',
24 'X-XSRF-TOKEN': token
.value
if token
else '',
28 class MindsIE(MindsBaseIE
):
30 _VALID_URL
= MindsBaseIE
._VALID
_URL
_BASE
+ r
'(?:media|newsfeed|archive/view)/(?P<id>[0-9]+)'
32 'url': 'https://www.minds.com/media/100000000000086822',
33 'md5': '215a658184a419764852239d4970b045',
35 'id': '100000000000086822',
37 'title': 'Minds intro sequence',
38 'thumbnail': r
're:https?://.+\.png',
39 'uploader_id': 'ottman',
40 'upload_date': '20130524',
41 'timestamp': 1369404826,
42 'uploader': 'Bill Ottman',
46 'tags': ['animation'],
48 'license': 'attribution-cc',
51 # entity.type == 'activity' and empty title
52 'url': 'https://www.minds.com/newsfeed/798025111988506624',
53 'md5': 'b2733a74af78d7fd3f541c4cbbaa5950',
55 'id': '798022190320226304',
57 'title': '798022190320226304',
58 'uploader': 'ColinFlaherty',
59 'upload_date': '20180111',
60 'timestamp': 1515639316,
61 'uploader_id': 'ColinFlaherty',
64 'url': 'https://www.minds.com/archive/view/715172106794442752',
65 'only_matching': True,
68 'url': 'https://www.minds.com/newsfeed/1197131838022602752',
69 'only_matching': True,
72 def _real_extract(self
, url
):
73 entity_id
= self
._match
_id
(url
)
74 entity
= self
._call
_api
(
75 'v1/entities/entity/' + entity_id
, entity_id
, 'entity')['entity']
76 if entity
.get('type') == 'activity':
77 if entity
.get('custom_type') == 'video':
78 video_id
= entity
['entity_guid']
80 return self
.url_result(entity
['perma_url'])
82 assert(entity
['subtype'] == 'video')
84 # 1080p and webm formats available only on the sources array
85 video
= self
._call
_api
(
86 'v2/media/video/' + video_id
, video_id
, 'video')
89 for source
in (video
.get('sources') or []):
90 src
= source
.get('src')
94 'format_id': source
.get('label'),
95 'height': int_or_none(source
.get('size')),
98 self
._sort
_formats
(formats
)
100 entity
= video
.get('entity') or entity
101 owner
= entity
.get('ownerObj') or {}
102 uploader_id
= owner
.get('username')
104 tags
= entity
.get('tags')
105 if tags
and isinstance(tags
, compat_str
):
109 poster
= video
.get('poster') or entity
.get('thumbnail_src')
111 urlh
= self
._request
_webpage
(poster
, video_id
, fatal
=False)
113 thumbnail
= urlh
.geturl()
117 'title': entity
.get('title') or video_id
,
119 'description': clean_html(entity
.get('description')) or None,
120 'license': str_or_none(entity
.get('license')),
121 'timestamp': int_or_none(entity
.get('time_created')),
122 'uploader': strip_or_none(owner
.get('name')),
123 'uploader_id': uploader_id
,
124 'uploader_url': format_field(uploader_id
, template
='https://www.minds.com/%s'),
125 'view_count': int_or_none(entity
.get('play:count')),
126 'like_count': int_or_none(entity
.get('thumbs:up:count')),
127 'dislike_count': int_or_none(entity
.get('thumbs:down:count')),
129 'comment_count': int_or_none(entity
.get('comments:count')),
130 'thumbnail': thumbnail
,
134 class MindsFeedBaseIE(MindsBaseIE
):
137 def _entries(self
, feed_id
):
138 query
= {'limit': self._PAGE_SIZE, 'sync': 1}
141 data
= self
._call
_api
(
142 'v2/feeds/container/%s/videos' % feed_id
,
143 feed_id
, 'page %s' % i
, query
)
144 entities
= data
.get('entities') or []
145 for entity
in entities
:
146 guid
= entity
.get('guid')
149 yield self
.url_result(
150 'https://www.minds.com/newsfeed/' + guid
,
151 MindsIE
.ie_key(), guid
)
152 query
['from_timestamp'] = data
['load-next']
153 if not (query
['from_timestamp'] and len(entities
) == self
._PAGE
_SIZE
):
157 def _real_extract(self
, url
):
158 feed_id
= self
._match
_id
(url
)
159 feed
= self
._call
_api
(
160 'v1/%s/%s' % (self
._FEED
_PATH
, feed_id
),
161 feed_id
, self
._FEED
_TYPE
)[self
._FEED
_TYPE
]
163 return self
.playlist_result(
164 self
._entries
(feed
['guid']), feed_id
,
165 strip_or_none(feed
.get('name')),
166 feed
.get('briefdescription'))
169 class MindsChannelIE(MindsFeedBaseIE
):
170 _FEED_TYPE
= 'channel'
171 IE_NAME
= 'minds:' + _FEED_TYPE
172 _VALID_URL
= MindsBaseIE
._VALID
_URL
_BASE
+ r
'(?!(?:newsfeed|media|api|archive|groups)/)(?P<id>[^/?&#]+)'
173 _FEED_PATH
= 'channel'
175 'url': 'https://www.minds.com/ottman',
178 'title': 'Bill Ottman',
179 'description': 'Co-creator & CEO @minds',
181 'playlist_mincount': 54,
185 class MindsGroupIE(MindsFeedBaseIE
):
187 IE_NAME
= 'minds:' + _FEED_TYPE
188 _VALID_URL
= MindsBaseIE
._VALID
_URL
_BASE
+ r
'groups/profile/(?P<id>[0-9]+)'
189 _FEED_PATH
= 'groups/group'
191 'url': 'https://www.minds.com/groups/profile/785582576369672204/feed/videos',
193 'id': '785582576369672204',
194 'title': 'Cooking Videos',
196 'playlist_mincount': 1,