aboutsummaryrefslogtreecommitdiffstats
path: root/youtube
diff options
context:
space:
mode:
authorJames Taylor <user234683@users.noreply.github.com>2019-11-22 14:56:53 -0800
committerJames Taylor <user234683@users.noreply.github.com>2019-11-22 14:56:53 -0800
commit79d9a18f815a03498e21dd5769a2e70c7ae7afa5 (patch)
tree693376dc5e091f94b5348f1fe51063b1ddc1fc82 /youtube
parent70b56d6eef4fd9d6c46c8fbf48dfec3ae7a2937e (diff)
downloadyt-local-79d9a18f815a03498e21dd5769a2e70c7ae7afa5.tar.lz
yt-local-79d9a18f815a03498e21dd5769a2e70c7ae7afa5.tar.xz
yt-local-79d9a18f815a03498e21dd5769a2e70c7ae7afa5.zip
Extraction: return and display any errors preventing video playback
Diffstat (limited to 'youtube')
-rw-r--r--youtube/templates/watch.html41
-rw-r--r--youtube/watch.py3
-rw-r--r--youtube/yt_data_extract.py17
3 files changed, 40 insertions, 21 deletions
diff --git a/youtube/templates/watch.html b/youtube/templates/watch.html
index e97b638..da3b336 100644
--- a/youtube/templates/watch.html
+++ b/youtube/templates/watch.html
@@ -14,6 +14,19 @@
text-decoration: underline;
}
+ .playability-error{
+ height: 360px;
+ width: 640px;
+ grid-column: 2;
+ background-color: var(--video-background-color);
+ text-align:center;
+ }
+ .playability-error span{
+ position: relative;
+ top: 50%;
+ transform: translate(-50%, -50%);
+ }
+
{% if theater_mode %}
video{
grid-column: 1 / span 5;
@@ -202,20 +215,24 @@
{% endblock style %}
{% block main %}
- <video controls autofocus>
- {% for video_source in video_sources %}
- <source src="{{ video_source['src'] }}" type="{{ video_source['type'] }}">
- {% endfor %}
+ {% if playability_error %}
+ <div class="playability-error"><span>{{ 'Error: ' + playability_error }}</span></div>
+ {% else %}
+ <video controls autofocus class="video">
+ {% for video_source in video_sources %}
+ <source src="{{ video_source['src'] }}" type="{{ video_source['type'] }}">
+ {% endfor %}
- {% for source in subtitle_sources %}
- {% if source['on'] %}
- <track label="{{ source['label'] }}" src="{{ source['url'] }}" kind="subtitles" srclang="{{ source['srclang'] }}" default>
- {% else %}
- <track label="{{ source['label'] }}" src="{{ source['url'] }}" kind="subtitles" srclang="{{ source['srclang'] }}">
- {% endif %}
- {% endfor %}
+ {% for source in subtitle_sources %}
+ {% if source['on'] %}
+ <track label="{{ source['label'] }}" src="{{ source['url'] }}" kind="subtitles" srclang="{{ source['srclang'] }}" default>
+ {% else %}
+ <track label="{{ source['label'] }}" src="{{ source['url'] }}" kind="subtitles" srclang="{{ source['srclang'] }}">
+ {% endif %}
+ {% endfor %}
- </video>
+ </video>
+ {% endif %}
<div class="video-info">
<h2 class="title">{{ title }}</h2>
diff --git a/youtube/watch.py b/youtube/watch.py
index 959dca2..8a396a7 100644
--- a/youtube/watch.py
+++ b/youtube/watch.py
@@ -123,7 +123,7 @@ decrypt_function_re = re.compile(r'function\(a\)\{(a=a\.split\(""\)[^\}]+)\}')
op_with_arg_re = re.compile(r'[^\.]+\.([^\(]+)\(a,(\d+)\)')
def decrypt_signatures(info):
'''return error string, or False if no errors'''
- if not info['formats'] or not info['formats'][0]['s']:
+ if ('formats' not in info) or (not info['formats']) or (not info['formats'][0]['s']):
return False # No decryption needed
if not info['base_js']:
return 'Failed to find base.js'
@@ -356,6 +356,7 @@ def get_watch_page():
uploader = info['author'],
description = info['description'],
unlisted = info['unlisted'],
+ playability_error = info['playability_error'],
)
diff --git a/youtube/yt_data_extract.py b/youtube/yt_data_extract.py
index 6bfec59..1a5f21c 100644
--- a/youtube/yt_data_extract.py
+++ b/youtube/yt_data_extract.py
@@ -824,7 +824,7 @@ def check_missing_keys(object, *key_sequences):
_object = object
try:
for key in key_sequence:
- _object = object[key]
+ _object = _object[key]
except (KeyError, IndexError, TypeError):
return 'Could not find ' + key
@@ -1028,21 +1028,20 @@ def extract_watch_info(polymer_json):
return {'error': 'Invalid top level polymer data'}
error = check_missing_keys(top_level,
- ['playerResponse'],
- )
- if error:
- return {'error': error}
-
- error = check_missing_keys(top_level,
['player', 'args'],
['player', 'assets', 'js'],
+ ['playerResponse'],
)
if error:
info['playability_error'] = error
-
player_args = default_multi_get(top_level, 'player', 'args', default={})
player_response = json.loads(player_args['player_response']) if 'player_response' in player_args else {}
+ playability_status = default_multi_get(player_response, 'playabilityStatus', 'status', default=None)
+ playability_reason = default_multi_get(player_response, 'playabilityStatus', 'reason', default='Unknown error')
+ if playability_status not in (None, 'OK'):
+ info['playability_error'] = playability_reason
+
streaming_data = player_response.get('streamingData', {})
yt_formats = streaming_data.get('formats', []) + streaming_data.get('adaptiveFormats', [])
@@ -1069,6 +1068,8 @@ def extract_watch_info(polymer_json):
fmt.update(_formats.get(str(yt_fmt.get('itag')), {}))
info['formats'].append(fmt)
+ if info['formats']:
+ info['playability_error'] = None # in case they lie
info['base_js'] = default_multi_get(top_level, 'player', 'assets', 'js')
if info['base_js']: