aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--yt_dlp/extractor/nebula.py65
1 files changed, 51 insertions, 14 deletions
diff --git a/yt_dlp/extractor/nebula.py b/yt_dlp/extractor/nebula.py
index 1a0a394f1..4426a8fdc 100644
--- a/yt_dlp/extractor/nebula.py
+++ b/yt_dlp/extractor/nebula.py
@@ -2,9 +2,11 @@
from __future__ import unicode_literals
import json
+import time
+from urllib.error import HTTPError
from .common import InfoExtractor
-from ..compat import compat_str
+from ..compat import compat_str, compat_urllib_parse_unquote, compat_urllib_parse_quote
from ..utils import (
ExtractorError,
parse_iso8601,
@@ -78,7 +80,9 @@ class NebulaIE(InfoExtractor):
]
_NETRC_MACHINE = 'watchnebula'
- def _retrieve_nebula_auth(self, video_id):
+ _nebula_token = None
+
+ def _retrieve_nebula_auth(self):
"""
Log in to Nebula, and returns a Nebula API token
"""
@@ -91,7 +95,7 @@ class NebulaIE(InfoExtractor):
data = json.dumps({'email': username, 'password': password}).encode('utf8')
response = self._download_json(
'https://api.watchnebula.com/api/v1/auth/login/',
- data=data, fatal=False, video_id=video_id,
+ data=data, fatal=False, video_id=None,
headers={
'content-type': 'application/json',
# Submitting the 'sessionid' cookie always causes a 403 on auth endpoint
@@ -101,6 +105,19 @@ class NebulaIE(InfoExtractor):
errnote='Authentication failed or rejected')
if not response or not response.get('key'):
self.raise_login_required()
+
+ # save nebula token as cookie
+ self._set_cookie(
+ 'nebula.app', 'nebula-auth',
+ compat_urllib_parse_quote(
+ json.dumps({
+ "apiToken": response["key"],
+ "isLoggingIn": False,
+ "isLoggingOut": False,
+ }, separators=(",", ":"))),
+ expire_time=int(time.time()) + 86400 * 365,
+ )
+
return response['key']
def _retrieve_zype_api_key(self, page_url, display_id):
@@ -139,8 +156,17 @@ class NebulaIE(InfoExtractor):
'Authorization': 'Token {access_token}'.format(access_token=access_token)
}, note=note)
- def _fetch_zype_access_token(self, video_id, nebula_token):
- user_object = self._call_nebula_api('/auth/user/', video_id, nebula_token, note='Retrieving Zype access token')
+ def _fetch_zype_access_token(self, video_id):
+ try:
+ user_object = self._call_nebula_api('/auth/user/', video_id, self._nebula_token, note='Retrieving Zype access token')
+ except ExtractorError as exc:
+ # if 401, attempt credential auth and retry
+ if exc.cause and isinstance(exc.cause, HTTPError) and exc.cause.code == 401:
+ self._nebula_token = self._retrieve_nebula_auth()
+ user_object = self._call_nebula_api('/auth/user/', video_id, self._nebula_token, note='Retrieving Zype access token')
+ else:
+ raise
+
access_token = try_get(user_object, lambda x: x['zype_auth_info']['access_token'], compat_str)
if not access_token:
if try_get(user_object, lambda x: x['is_subscribed'], bool):
@@ -162,9 +188,21 @@ class NebulaIE(InfoExtractor):
if category.get('value'):
return category['value'][0]
+ def _real_initialize(self):
+ # check cookie jar for valid token
+ nebula_cookies = self._get_cookies('https://nebula.app')
+ nebula_cookie = nebula_cookies.get('nebula-auth')
+ if nebula_cookie:
+ self.to_screen('Authenticating to Nebula with token from cookie jar')
+ nebula_cookie_value = compat_urllib_parse_unquote(nebula_cookie.value)
+ self._nebula_token = self._parse_json(nebula_cookie_value, None).get('apiToken')
+
+ # try to authenticate using credentials if no valid token has been found
+ if not self._nebula_token:
+ self._nebula_token = self._retrieve_nebula_auth()
+
def _real_extract(self, url):
display_id = self._match_id(url)
- nebula_token = self._retrieve_nebula_auth(display_id)
api_key = self._retrieve_zype_api_key(url, display_id)
response = self._call_zype_api('/videos', {'friendly_title': display_id},
@@ -174,7 +212,7 @@ class NebulaIE(InfoExtractor):
video_meta = response['response'][0]
video_id = video_meta['_id']
- zype_access_token = self._fetch_zype_access_token(display_id, nebula_token=nebula_token)
+ zype_access_token = self._fetch_zype_access_token(display_id)
channel_title = self._extract_channel_title(video_meta)
@@ -187,13 +225,12 @@ class NebulaIE(InfoExtractor):
'title': video_meta.get('title'),
'description': video_meta.get('description'),
'timestamp': parse_iso8601(video_meta.get('published_at')),
- 'thumbnails': [
- {
- 'id': tn.get('name'), # this appears to be null
- 'url': tn['url'],
- 'width': tn.get('width'),
- 'height': tn.get('height'),
- } for tn in video_meta.get('thumbnails', [])],
+ 'thumbnails': [{
+ 'id': tn.get('name'), # this appears to be null
+ 'url': tn['url'],
+ 'width': tn.get('width'),
+ 'height': tn.get('height'),
+ } for tn in video_meta.get('thumbnails', [])],
'duration': video_meta.get('duration'),
'channel': channel_title,
'uploader': channel_title, # we chose uploader = channel name