Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

trying to only use next endpoint for the major data #5003

Draft
wants to merge 3 commits into
base: master
Choose a base branch
from

Conversation

unixfox
Copy link
Member

@unixfox unixfox commented Oct 13, 2024

Description

This PR aims to avoid throwing a raw error and instead display all the data that we can extract from the /next endpoint but without displaying the "player" (video.js) element.

This is one big step towards #4985. Invidious will have to be able to display the watch page even when YouTube is blocking the IP.

This will give the same result as on www.youtube.com:

This way the user can still view the title of the video, accessing the channel page, viewing the comments and much more which is not yet blocked.

What changed?

  • The watch page is displayed properly but without the player if an error is found.
  • All the important data like title, viewcount is fetched from /next endpoint is unable from the /player endpoint.
  • The error message now throws the reason AND the subreason. Makes it clearer to the user what's really happening.

TODO

  • Integrate the embed page with the changes
  • Display the subreason on the watch page when there is an error
  • Fix description
  • Add mocks for a video without videoDetails

Try these videos ID for testing - on both a blocked IP and a not blocked one

  • 98TyXNPdMRo (private video)
  • 7KSfDyainYU (georestricted video)
  • KIuDBxqz__k (familySafe no)
  • yqaS07znJWY (unlisted video)
  • aaaaaaaaaaaa (notfound video)

Fixes

@unixfox unixfox force-pushed the use-only-next-for-extra-info branch from 60a5c1c to 9438a6d Compare October 13, 2024 22:08
@ic-scm
Copy link

ic-scm commented Oct 17, 2024

Thank you very much for this. Looking at instance yewtu.be which uses invidious-custom, it seems to be working just as expected, exactly as described in my linked issue, and with a 100% success rate.

Thank you for this great work - this is a huge step towards making Invidious convenient to use again. For me personally, it solves the entire problem.

The only thing I can potentially suggest is for the video thumbnail to be loaded and displayed on the page, separately from the broken video player. But that would be just a nice-to-have feature, and I'm guessing it may not be trivial to implement, when the official Youtube website just shows a black error screen, with the thumbnail nowhere to be seen.

if reason = info["reason"]?
if info["reason"]? && info["subreason"]?
reason = info["reason"].as_s
puts info
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please remove the debug puts here

Comment on lines -90 to +91
next_steps = error_redirect_helper(env)
error_message = <<-END_HTML
<div class="error_message">
<h2>#{translate(locale, "error_processing_data_youtube")}</h2>
<p>#{translate(locale, message)}</p>
#{error_redirect_helper(env)}
</div>
END_HTML
Copy link
Member

@syeopite syeopite Oct 18, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your changes here will show the Error while processing the data sent by YouTube message on any InfoException while also removing the next steps message on all InfoExceptions

Example:

info exception

And InfoExceptions are raised in places unrelated to YouTube as well such as when a user inserts the wrong password.

Related: #4651

@@ -4,5 +4,4 @@

<div class="h-box">
<%= error_message %>
<%= next_steps %>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The error page is used for everything and not just the watch page, so it must contain at least the "refresh" and "switch instance" buttons. Alternatively, we could split the error page logic for the watch page from the rest.

<%= translate(locale, "error_from_youtube_unplayable") %> <%= video.reason %>
</h3>
<h3>
<%= translate(locale, "next_steps_error_message") %>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You probably want to use error_redirect_helper(). This error message alone (without any actions listed underneath) will be very confusing.

Comment on lines +120 to +122
<span id="refresh-page">
<a href="<% env.request.resource %>"><%= translate(locale, "refresh_page") %></a>
</span>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why did you add this link here? All browsers allow to refresh the page natively, and the close procimity with the "watch on youtube" link is prone to miss-clicks.

Comment on lines +207 to +209
if !(video_details = player_response.dig?("videoDetails"))
video_details = {} of String => JSON::Any
end
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here you can use the more compact form:

Suggested change
if !(video_details = player_response.dig?("videoDetails"))
video_details = {} of String => JSON::Any
end
video_details = player_response.dig?("videoDetails")
video_details ||= {} of String => JSON::Any

Comment on lines +240 to +249
published_txt = video_primary_renderer
.try &.dig?("dateText", "simpleText")

if published_txt.try &.as_s.includes?("ago") && !published_txt.nil?
published = decode_date(published_txt.as_s.lchop("Started streaming "))
elsif published_txt && published_txt.try &.as_s.matches?(/(\w{3} \d{1,2}, \d{4})$/)
published = Time.parse(published_txt.as_s.match!(/(\w{3} \d{1,2}, \d{4})$/)[0], "%b %-d, %Y", Time::Location::UTC)
else
published = Time.utc
end
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here I'd recommend using "scheduledStartTime" (see below) in the player response, as it's more accurate and doesn't need complex parsing logic.

{
	"playabilityStatus": {
		"status": "LIVE_STREAM_OFFLINE",
		"reason": "This live event will begin in 32 hours.",
		"playableInEmbed": true,
		"liveStreamability": {
			"liveStreamabilityRenderer": {
				"videoId": "hUoukmX_BRQ",
				"offlineSlate": {
					"liveStreamOfflineSlateRenderer": {
						"scheduledStartTime": "1729468800",
						"mainText": {
							"runs": [
								{"text": "Live in "},
                                {"text": "32 hours"}
							]
						},
						"subtitleText": {
							"simpleText": "October 21 at 12:00 AM"
						}
					}
				}
			}
		}
	}
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note: if you plan to fallback to the "subtitleText" value, there is a non-breaking space before AM/PM:
image

@@ -96,7 +98,10 @@ we're going to need to do it here in order to allow for translations.

<% if video.reason %>
<h3>
<%= video.reason %>
<%= translate(locale, "error_from_youtube_unplayable") %> <%= video.reason %>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ditto: keeping the error message inside a player placeholder.

# dont error when it's a premiere.
# we already parsed most of the data and display the premiere date
raise InfoException.new(reason.as_s || "")
raise NotFoundException.new(reason + ": Video not found" || "")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That will expand to "Video unavailable: Video not found", which is not very explicit, imo.

Comment on lines +332 to +336
if info.dig?("subreason").nil?
subreason = info["subreason"].as_s
else
subreason = "No additional reason"
end
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This bloc of code can be summarized to the following, as you're already checking info["subreason"]? falsiness in the parent if

Suggested change
if info.dig?("subreason").nil?
subreason = info["subreason"].as_s
else
subreason = "No additional reason"
end
subreason = info["subreason"].as_s

@@ -313,7 +313,7 @@ def get_video(id, refresh = true, region = nil, force_refresh = false)
end
else
video = fetch_video(id, region)
Invidious::Database::Videos.insert(video) if !region
Invidious::Database::Videos.insert(video) if !region && !video.info.dig?("reason")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should make a getter dunction inside the Video class for reason/subreason.

"reason" => JSON::Any.new(reason),
"version" => JSON::Any.new(Video::SCHEMA_VERSION.to_i64),
"reason" => JSON::Any.new(reason),
"subreason" => JSON::Any.new(subreason),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't forget to bump SCHEMA_VERSION in the Video class!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
4 participants