diff --git a/haruhi_dl/extractor/peertube.py b/haruhi_dl/extractor/peertube.py index 5888662d0..d064c776f 100644 --- a/haruhi_dl/extractor/peertube.py +++ b/haruhi_dl/extractor/peertube.py @@ -1,6 +1,8 @@ # coding: utf-8 from __future__ import unicode_literals +import datetime +from urllib.parse import urlencode import re from .common import SelfhostedInfoExtractor @@ -14,6 +16,7 @@ from ..utils import ( unified_timestamp, url_or_none, urljoin, + ExtractorError, ) @@ -27,10 +30,55 @@ class PeerTubeBaseExtractor(SelfhostedInfoExtractor): '>We are sorry but it seems that PeerTube is not compatible with your web browser.<', '= ts + 5: + return True + + username, password = self._get_login_info() + if not username: + return None + + # the instance domain (the one where user has an account) must be separated from the user e-mail + mobj = re.match(r'^(?P[^@]+(?:@[^@]+)?)@(?P.+)$', username) + if not mobj: + self.report_warning( + 'Invalid login format - must be in format [username or email]@[instance]') + username, instance = mobj.group('username', 'instance') + + oauth_keys = self._downloader.cache.load('peertube-oauth', instance) + if not oauth_keys: + oauth_keys = self._download_json(f'https://{instance}/api/v1/oauth-clients/local', instance, 'Downloading OAuth keys') + self._downloader.cache.store('peertube-oauth', instance, oauth_keys) + client_id, client_secret = oauth_keys['client_id'], oauth_keys['client_secret'] + + auth_res = self._download_json(f'https://{instance}/api/v1/users/token', instance, 'Logging in', data=bytes(urlencode({ + 'client_id': client_id, + 'client_secret': client_secret, + 'response_type': 'code', + 'grant_type': 'password', + 'scope': 'user', + 'username': username, + 'password': password, + }).encode('utf-8'))) + + ts = datetime.datetime.now().timestamp() + auth_res['instance'] = instance + auth_res['expires_on'] = ts + auth_res['expires_in'] + auth_res['refresh_token_expires_on'] = ts + auth_res['refresh_token_expires_in'] + # not using self to set the details to expose it to all peertube extractors + PeerTubeBaseExtractor._LOGIN_INFO = auth_res def _call_api(self, host, resource, resource_id, path, note=None, errnote=None, fatal=True): return self._download_json( self._API_BASE % (host, resource, resource_id, path), resource_id, + headers={ + 'Authorization': f'Bearer {self._LOGIN_INFO["access_token"]}', + } if self._LOGIN_INFO and self._LOGIN_INFO['instance'] == host else {}, note=note, errnote=errnote, fatal=fatal) def _parse_video(self, video, url): @@ -221,6 +269,17 @@ class PeerTubeSHIE(PeerTubeBaseExtractor): def _selfhosted_extract(self, url, webpage=None): host, video_id = self._match_id_and_host(url) + self._login() + + if self._LOGIN_INFO and self._LOGIN_INFO['instance'] != host: + video_search = self._call_api( + self._LOGIN_INFO['instance'], 'search', 'videos', '?' + urlencode({ + 'search': f'https://{host}/videos/watch/{video_id}', + }), note='Searching for remote video') + if len(video_search) == 0: + raise ExtractorError('Remote video not found') + host, video_id = self._LOGIN_INFO['instance'], video_search['data'][0]['uuid'] + video = self._call_api( host, 'videos', video_id, '', note='Downloading video JSON') @@ -262,6 +321,8 @@ class PeerTubePlaylistSHIE(PeerTubeBaseExtractor): def _selfhosted_extract(self, url, webpage=None): host, display_id = self._match_id_and_host(url) + self._login() + playlist_data = self._call_api(host, 'video-playlists', display_id, '', 'Downloading playlist metadata') entries = [] i = 0 @@ -308,6 +369,8 @@ class PeerTubeChannelSHIE(PeerTubeBaseExtractor): def _selfhosted_extract(self, url, webpage=None): host, display_id = self._match_id_and_host(url) + self._login() + channel_data = self._call_api(host, 'video-channels', display_id, '', 'Downloading channel metadata') entries = [] i = 0 @@ -355,6 +418,8 @@ class PeerTubeAccountSHIE(PeerTubeBaseExtractor): def _selfhosted_extract(self, url, webpage=None): host, display_id = self._match_id_and_host(url) + self._login() + account_data = self._call_api(host, 'accounts', display_id, '', 'Downloading account metadata') entries = [] i = 0