Add backtraces to errors (#1498)

Error handling has been reworked to always go through the new `error_template`,
`error_json` and `error_atom` macros.
They all accept a status code followed by a string message or an exception
object. `error_json` accepts a hash with additional fields as third argument.

If the second argument is an exception a backtrace will be printed, if it is a
string only the string is printed. Since up till now only the exception message
was printed a new `InfoException` class was added for situations where no
backtrace is intended but a string cannot be used.

`error_template` with a string message automatically localizes the message.
Missing error translations have been collected in https://github.com/iv-org/invidious/issues/1497
`error_json` with a string message does not localize the message. This is the
same as previous behavior. If translations are desired for `error_json` they
can be added easily but those error messages have not been collected yet.

Uncaught exceptions previously only printed a generic message ("Looks like
you've found a bug in Invidious. [...]"). They still print that message
but now also include a backtrace.
This commit is contained in:
saltycrys
2020-11-30 10:59:21 +01:00
committed by GitHub
parent fe73eccb90
commit 3dac33ffba
11 changed files with 250 additions and 378 deletions

View File

@@ -208,7 +208,7 @@ def fetch_channel(ucid, db, pull_all_videos = true, locale = nil)
author = rss.xpath_node(%q(//feed/title))
if !author
raise translate(locale, "Deleted or invalid channel")
raise InfoException.new("Deleted or invalid channel")
end
author = author.content
@@ -226,13 +226,14 @@ def fetch_channel(ucid, db, pull_all_videos = true, locale = nil)
videos = [] of SearchVideo
begin
initial_data = JSON.parse(response.body).as_a.find &.["response"]?
raise "Could not extract JSON" if !initial_data
raise InfoException.new("Could not extract channel JSON") if !initial_data
videos = extract_videos(initial_data.as_h, author, ucid)
rescue ex
if response.body.includes?("To continue with your YouTube experience, please fill out the form below.") ||
response.body.includes?("https://www.google.com/sorry/index")
raise "Could not extract channel info. Instance is likely blocked."
raise InfoException.new("Could not extract channel info. Instance is likely blocked.")
end
raise ex
end
rss.xpath_nodes("//feed/entry").each do |entry|
@@ -287,7 +288,7 @@ def fetch_channel(ucid, db, pull_all_videos = true, locale = nil)
loop do
response = get_channel_videos_response(ucid, page, auto_generated: auto_generated)
initial_data = JSON.parse(response.body).as_a.find &.["response"]?
raise "Could not extract JSON" if !initial_data
raise InfoException.new("Could not extract channel JSON") if !initial_data
videos = extract_videos(initial_data.as_h, author, ucid)
count = videos.size
@@ -507,8 +508,7 @@ def fetch_channel_community(ucid, continuation, locale, format, thin_mode)
end
if response.status_code != 200
error_message = translate(locale, "This channel does not exist.")
raise error_message
raise InfoException.new("This channel does not exist.")
end
ucid = response.body.match(/https:\/\/www.youtube.com\/channel\/(?<ucid>UC[a-zA-Z0-9_-]{22})/).not_nil!["ucid"]
@@ -518,7 +518,7 @@ def fetch_channel_community(ucid, continuation, locale, format, thin_mode)
body = initial_data["contents"]?.try &.["twoColumnBrowseResultsRenderer"]["tabs"].as_a.select { |tab| tab["tabRenderer"]?.try &.["selected"].as_bool.== true }[0]?
if !body
raise "Could not extract community tab."
raise InfoException.new("Could not extract community tab.")
end
body = body["tabRenderer"]["content"]["sectionListRenderer"]["contents"][0]["itemSectionRenderer"]
@@ -540,7 +540,7 @@ def fetch_channel_community(ucid, continuation, locale, format, thin_mode)
body["response"]["continuationContents"]["backstageCommentsContinuation"]?
if !body
raise "Could not extract continuation."
raise InfoException.new("Could not extract continuation.")
end
end
@@ -551,7 +551,7 @@ def fetch_channel_community(ucid, continuation, locale, format, thin_mode)
error_message = (message["text"]["simpleText"]? ||
message["text"]["runs"]?.try &.[0]?.try &.["text"]?)
.try &.as_s || ""
raise error_message
raise InfoException.new(error_message)
end
response = JSON.build do |json|
@@ -786,21 +786,19 @@ def get_about_info(ucid, locale)
end
if result.status_code != 200
error_message = translate(locale, "This channel does not exist.")
raise error_message
raise InfoException.new("This channel does not exist.")
end
about = XML.parse_html(result.body)
if about.xpath_node(%q(//div[contains(@class, "channel-empty-message")]))
error_message = translate(locale, "This channel does not exist.")
raise error_message
raise InfoException.new("This channel does not exist.")
end
initdata = extract_initial_data(result.body)
if initdata.empty?
error_message = about.xpath_node(%q(//div[@class="yt-alert-content"])).try &.content.strip
error_message ||= translate(locale, "Could not get channel info.")
raise error_message
raise InfoException.new(error_message)
end
author = about.xpath_node(%q(//meta[@name="title"])).not_nil!["content"]