Skip to content

Commit 3b761f4

Browse files
committed
Watch view: Better error message when video is unplayable.
1 parent b2b3ba5 commit 3b761f4

File tree

7 files changed

+63
-38
lines changed

7 files changed

+63
-38
lines changed

locales/en-US.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -501,5 +501,8 @@
501501
"toggle_theme": "Toggle Theme",
502502
"carousel_slide": "Slide {{current}} of {{total}}",
503503
"carousel_skip": "Skip the Carousel",
504-
"carousel_go_to": "Go to slide `x`"
504+
"carousel_go_to": "Go to slide `x`",
505+
"error_from_youtube_unplayable": "Video unplayable due to an error from YouTube:",
506+
"error_processing_data_youtube": "Error while processing the data sent by YouTube",
507+
"refresh_page": "Refresh the page"
505508
}

src/invidious/helpers/errors.cr

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -73,10 +73,6 @@ def error_template_helper(env : HTTP::Server::Context, status_code : Int32, exce
7373
</div>
7474
END_HTML
7575

76-
# Don't show the usual "next steps" widget. The same options are
77-
# proposed above the error message, just worded differently.
78-
next_steps = ""
79-
8076
return templated "error"
8177
end
8278

@@ -86,8 +82,13 @@ def error_template_helper(env : HTTP::Server::Context, status_code : Int32, mess
8682

8783
locale = env.get("preferences").as(Preferences).locale
8884

89-
error_message = translate(locale, message)
90-
next_steps = error_redirect_helper(env)
85+
error_message = <<-END_HTML
86+
<div class="error_message">
87+
<h2>#{translate(locale, "error_processing_data_youtube")}</h2>
88+
<p>#{translate(locale, message)}</p>
89+
#{error_redirect_helper(env)}
90+
</div>
91+
END_HTML
9192

9293
return templated "error"
9394
end

src/invidious/videos.cr

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -313,7 +313,7 @@ def get_video(id, refresh = true, region = nil, force_refresh = false)
313313
end
314314
else
315315
video = fetch_video(id, region)
316-
Invidious::Database::Videos.insert(video) if !region
316+
Invidious::Database::Videos.insert(video) if !region && !video.info.dig?("reason")
317317
end
318318

319319
return video
@@ -326,13 +326,13 @@ end
326326
def fetch_video(id, region)
327327
info = extract_video_info(video_id: id)
328328

329-
if reason = info["reason"]?
329+
if info["reason"]? && info["subreason"]?
330+
reason = info["reason"].as_s
331+
subreason = info["subreason"].as_s
330332
if reason == "Video unavailable"
331-
raise NotFoundException.new(reason.as_s || "")
332-
elsif !reason.as_s.starts_with? "Premieres"
333-
# dont error when it's a premiere.
334-
# we already parsed most of the data and display the premiere date
335-
raise InfoException.new(reason.as_s || "")
333+
raise NotFoundException.new(reason + ": Video not found" || "")
334+
elsif {"Private video"}.any?(reason)
335+
raise InfoException.new(reason + ": " + subreason || "")
336336
end
337337
end
338338

src/invidious/videos/parser.cr

Lines changed: 22 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -68,18 +68,20 @@ def extract_video_info(video_id : String)
6868
playability_status = player_response.dig?("playabilityStatus", "status").try &.as_s
6969

7070
if playability_status != "OK"
71-
subreason = player_response.dig?("playabilityStatus", "errorScreen", "playerErrorMessageRenderer", "subreason")
72-
reason = subreason.try &.[]?("simpleText").try &.as_s
73-
reason ||= subreason.try &.[]("runs").as_a.map(&.[]("text")).join("")
74-
reason ||= player_response.dig("playabilityStatus", "reason").as_s
75-
76-
# Stop here if video is not a scheduled livestream or
77-
# for LOGIN_REQUIRED when videoDetails element is not found because retrying won't help
78-
if !{"LIVE_STREAM_OFFLINE", "LOGIN_REQUIRED"}.any?(playability_status) ||
79-
playability_status == "LOGIN_REQUIRED" && !player_response.dig?("videoDetails")
71+
reason = player_response.dig?("playabilityStatus", "reason").try &.as_s
72+
reason ||= player_response.dig("playabilityStatus", "errorScreen", "playerErrorMessageRenderer", "reason", "simpleText").as_s
73+
subreason_main = player_response.dig?("playabilityStatus", "errorScreen", "playerErrorMessageRenderer", "subreason")
74+
subreason = subreason_main.try &.[]?("simpleText").try &.as_s
75+
subreason ||= subreason_main.try &.[]("runs").as_a.map(&.[]("text")).join("")
76+
77+
# Stop if private video or video not found.
78+
# But for video unavailable, only stop if playability_status is ERROR because playability_status UNPLAYABLE
79+
# still gives all the necessary info for displaying the video page (title, description and more)
80+
if {"Private video", "Video unavailable"}.any?(reason) && !{"UNPLAYABLE"}.any?(playability_status)
8081
return {
81-
"version" => JSON::Any.new(Video::SCHEMA_VERSION.to_i64),
82-
"reason" => JSON::Any.new(reason),
82+
"version" => JSON::Any.new(Video::SCHEMA_VERSION.to_i64),
83+
"reason" => JSON::Any.new(reason),
84+
"subreason" => JSON::Any.new(subreason),
8385
}
8486
end
8587
elsif video_id != player_response.dig("videoDetails", "videoId")
@@ -99,11 +101,8 @@ def extract_video_info(video_id : String)
99101
reason = nil
100102
end
101103

102-
# Don't fetch the next endpoint if the video is unavailable.
103-
if {"OK", "LIVE_STREAM_OFFLINE", "LOGIN_REQUIRED"}.any?(playability_status)
104-
next_response = YoutubeAPI.next({"videoId": video_id, "params": ""})
105-
player_response = player_response.merge(next_response)
106-
end
104+
next_response = YoutubeAPI.next({"videoId": video_id, "params": ""})
105+
player_response = player_response.merge(next_response)
107106

108107
params = parse_video_info(video_id, player_response)
109108
params["reason"] = JSON::Any.new(reason) if reason
@@ -197,16 +196,20 @@ def parse_video_info(video_id : String, player_response : Hash(String, JSON::Any
197196
end
198197

199198
video_details = player_response.dig?("videoDetails")
199+
video_details ||= {} of String => JSON::Any
200200
if !(microformat = player_response.dig?("microformat", "playerMicroformatRenderer"))
201201
microformat = {} of String => JSON::Any
202202
end
203203

204-
raise BrokenTubeException.new("videoDetails") if !video_details
205-
206204
# Basic video infos
207205

208206
title = video_details["title"]?.try &.as_s
209207

208+
title ||= extract_text(
209+
video_primary_renderer
210+
.try &.dig?("title")
211+
)
212+
210213
# We have to try to extract viewCount from videoPrimaryInfoRenderer first,
211214
# then from videoDetails, as the latter is "0" for livestreams (we want
212215
# to get the amount of viewers watching).
@@ -483,7 +486,7 @@ def parse_video_info(video_id : String, player_response : Hash(String, JSON::Any
483486
# Description
484487
"description" => JSON::Any.new(description || ""),
485488
"descriptionHtml" => JSON::Any.new(description_html || "<p></p>"),
486-
"shortDescription" => JSON::Any.new(short_description.try &.as_s || nil),
489+
"shortDescription" => JSON::Any.new(short_description.try &.as_s || ""),
487490
# Video metadata
488491
"genre" => JSON::Any.new(genre.try &.as_s || ""),
489492
"genreUcid" => JSON::Any.new(genre_ucid.try &.as_s?),

src/invidious/views/embed.ecr

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,17 @@
3131
%>
3232
</script>
3333
34-
<%= rendered "components/player" %>
34+
<% if video.reason.nil? %>
35+
<div id="player-container" class="h-box">
36+
<%= rendered "components/player" %>
37+
</div>
38+
<% else %>
39+
<div id="player-error-container" class="h-box">
40+
<h3>
41+
<%= video.reason %>
42+
</h3>
43+
</div>
44+
<% end %>
3545
<script src="/js/embed.js?v=<%= ASSET_COMMIT %>"></script>
3646
</body>
3747
</html>

src/invidious/views/error.ecr

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,4 @@
44

55
<div class="h-box">
66
<%= error_message %>
7-
<%= next_steps %>
87
</div>

src/invidious/views/watch.ecr

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -70,9 +70,11 @@ we're going to need to do it here in order to allow for translations.
7070
%>
7171
</script>
7272
73+
<% if video.reason.nil? %>
7374
<div id="player-container" class="h-box">
7475
<%= rendered "components/player" %>
7576
</div>
77+
<% end %>
7678
7779
<div class="h-box">
7880
<h1>
@@ -96,7 +98,10 @@ we're going to need to do it here in order to allow for translations.
9698
9799
<% if video.reason %>
98100
<h3>
99-
<%= video.reason %>
101+
<%= translate(locale, "error_from_youtube_unplayable") %> <%= video.reason %>
102+
</h3>
103+
<h3>
104+
<%= translate(locale, "next_steps_error_message") %>
100105
</h3>
101106
<% elsif video.premiere_timestamp.try &.> Time.utc %>
102107
<h3>
@@ -112,7 +117,7 @@ we're going to need to do it here in order to allow for translations.
112117
<div class="pure-g">
113118
<div class="pure-u-1 pure-u-lg-1-5">
114119
<div class="h-box">
115-
<span id="watch-on-youtube">
120+
<p id="watch-on-youtube">
116121
<%-
117122
link_yt_watch = URI.new(scheme: "https", host: "www.youtube.com", path: "/watch", query: "v=#{video.id}")
118123
link_yt_embed = URI.new(scheme: "https", host: "www.youtube.com", path: "/embed/#{video.id}")
@@ -125,7 +130,7 @@ we're going to need to do it here in order to allow for translations.
125130
-%>
126131
<a id="link-yt-watch" rel="noreferrer noopener" data-base-url="<%= link_yt_watch %>" href="<%= link_yt_watch %>"><%= translate(locale, "videoinfo_watch_on_youTube") %></a>
127132
(<a id="link-yt-embed" rel="noreferrer noopener" data-base-url="<%= link_yt_embed %>" href="<%= link_yt_embed %>"><%= translate(locale, "videoinfo_youTube_embed_link") %></a>)
128-
</span>
133+
</p>
129134
130135
<p id="watch-on-another-invidious-instance">
131136
<%- link_iv_other = IV::Frontend::Misc.redirect_url(env) -%>
@@ -185,18 +190,22 @@ we're going to need to do it here in order to allow for translations.
185190
<% end %>
186191
<% end %>
187192
193+
<% if video_assets %>
188194
<%= Invidious::Frontend::WatchPage.download_widget(locale, video, video_assets) %>
195+
<% end %>
189196
190197
<p id="views"><i class="icon ion-ios-eye"></i> <%= number_with_separator(video.views) %></p>
191198
<p id="likes"><i class="icon ion-ios-thumbs-up"></i> <%= number_with_separator(video.likes) %></p>
192199
<p id="dislikes" style="display: none; visibility: hidden;"></p>
200+
<% if video.genre %>
193201
<p id="genre"><%= translate(locale, "Genre: ") %>
194202
<% if !video.genre_url %>
195203
<%= video.genre %>
196204
<% else %>
197205
<a href="<%= video.genre_url %>"><%= video.genre %></a>
198206
<% end %>
199207
</p>
208+
<% end %>
200209
<% if video.license %>
201210
<% if video.license.empty? %>
202211
<p id="license"><%= translate(locale, "License: ") %><%= translate(locale, "Standard YouTube license") %></p>

0 commit comments

Comments
 (0)