mirror of
https://github.com/iv-org/invidious.git
synced 2026-09-06 00:52:45 -05:00
fix: use UTF-16 offsets for description links
This commit is contained in:
parent
ad4b1c69ce
commit
ed68874dc6
56
spec/invidious/videos/description_spec.cr
Normal file
56
spec/invidious/videos/description_spec.cr
Normal file
@ -0,0 +1,56 @@
|
||||
require "../../spec_helper"
|
||||
require "../../../src/invidious/videos/description"
|
||||
|
||||
Spectator.describe "Video description parser" do
|
||||
describe "#parse_description" do
|
||||
it "uses UTF-16 offsets when an emoji precedes a command" do
|
||||
description = JSON.parse(<<-JSON)
|
||||
{
|
||||
"content": "🚀 Visit example now",
|
||||
"commandRuns": [{
|
||||
"startIndex": 3,
|
||||
"length": 5,
|
||||
"onTap": {
|
||||
"innertubeCommand": {
|
||||
"urlEndpoint": {"url": "https://example.com"}
|
||||
}
|
||||
}
|
||||
}]
|
||||
}
|
||||
JSON
|
||||
|
||||
expect(parse_description(description, "video-id")).to eq(
|
||||
%(🚀 <a href="https://example.com">Visit</a> example now)
|
||||
)
|
||||
end
|
||||
|
||||
it "uses UTF-16 lengths when an emoji is inside a command" do
|
||||
description = JSON.parse(<<-JSON)
|
||||
{
|
||||
"content": "Open 😀 docs",
|
||||
"commandRuns": [{
|
||||
"startIndex": 0,
|
||||
"length": 7,
|
||||
"onTap": {
|
||||
"innertubeCommand": {
|
||||
"urlEndpoint": {"url": "https://example.com"}
|
||||
}
|
||||
}
|
||||
}]
|
||||
}
|
||||
JSON
|
||||
|
||||
expect(parse_description(description, "video-id")).to eq(
|
||||
%(<a href="https://example.com">Open 😀</a> docs)
|
||||
)
|
||||
end
|
||||
|
||||
it "escapes complete descriptions without command runs" do
|
||||
description = JSON.parse(%({"content":"🚀 <safe> & complete"}))
|
||||
|
||||
expect(parse_description(description, "video-id")).to eq(
|
||||
%(🚀 <safe> & complete)
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -1,9 +1,9 @@
|
||||
require "json"
|
||||
require "uri"
|
||||
|
||||
private def copy_string(str : String::Builder, iter : Iterator, count : Int) : Int
|
||||
private def copy_string(str : String::Builder, iter : Iterator, count : Int? = nil) : Int
|
||||
copied = 0
|
||||
while copied < count
|
||||
while count.nil? || copied < count
|
||||
cp = iter.next
|
||||
break if cp.is_a?(Iterator::Stop)
|
||||
|
||||
@ -21,7 +21,9 @@ private def copy_string(str : String::Builder, iter : Iterator, count : Int) : I
|
||||
str << cp.chr
|
||||
end
|
||||
|
||||
copied += 1
|
||||
# YouTube expresses command offsets in UTF-16 code units. Astral
|
||||
# codepoints therefore count as two units instead of one.
|
||||
copied += cp > 0xFFFF ? 2 : 1
|
||||
end
|
||||
|
||||
return copied
|
||||
@ -38,7 +40,7 @@ def parse_description(desc, video_id : String) : String?
|
||||
# Slightly faster than HTML.escape, as we're only doing one pass on
|
||||
# the string instead of five for the standard library
|
||||
return String.build do |str|
|
||||
copy_string(str, content.each_codepoint, content.size)
|
||||
copy_string(str, content.each_codepoint)
|
||||
end
|
||||
end
|
||||
|
||||
@ -70,7 +72,6 @@ def parse_description(desc, video_id : String) : String?
|
||||
end
|
||||
|
||||
# Copy the end of the string (past the last command).
|
||||
remaining_length = content.size - index
|
||||
copy_string(str, iter, remaining_length) if remaining_length > 0
|
||||
copy_string(str, iter)
|
||||
end
|
||||
end
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user