diff --git a/spec/invidious/utils_spec.cr b/spec/invidious/utils_spec.cr index 7c2c27114..c576d5670 100644 --- a/spec/invidious/utils_spec.cr +++ b/spec/invidious/utils_spec.cr @@ -43,4 +43,17 @@ Spectator.describe "Utils" do expect(decode_date("8 years ago")).to be_close(Time.utc - 8.years, 500.milliseconds) end end + + describe "linkify_hashtags" do + it "escapes apostrophes without creating false hashtag links" do + result = linkify_hashtags("Rock 'n' Roll `#music`") + expect(result).to_not contain("/hashtag/39") + expect(result).to contain("'") + expect(result).to contain("#music") + end + + it "links a plain hashtag" do + expect(linkify_hashtags("hello #music")).to eq("hello #music") + end + end end diff --git a/src/invidious/channels/about.cr b/src/invidious/channels/about.cr index 6213bdea8..765a196bb 100644 --- a/src/invidious/channels/about.cr +++ b/src/invidious/channels/about.cr @@ -75,7 +75,7 @@ def get_about_info(ucid) : AboutChannel author_url = "https://www.youtube.com/channel/#{ucid}" author_thumbnail = phr_vm.try &.dig?("animatedImage", "contentPreviewImageViewModel", "image", "sources", 0, "url").try &.as_s || "" banner = nil - description_node = JSON.parse(%({"simpleText": ""})) + description_node = nil tags = [] of String else author = ithr["title"]["simpleText"].as_s diff --git a/src/invidious/helpers/utils.cr b/src/invidious/helpers/utils.cr index f0efa245f..8c3168619 100644 --- a/src/invidious/helpers/utils.cr +++ b/src/invidious/helpers/utils.cr @@ -386,15 +386,15 @@ def parse_link_endpoint(endpoint : JSON::Any, text : String, video_id : String) return text end -def linkify_hashtags(title : String) : String +def linkify_hashtags(raw_title : String) : String String.build do |str| pos = 0 - title.scan(/#([a-zA-Z0-9_]+)/) do |match| - str << title[pos...match.begin(0)] - str << %(#{match[0]}) + raw_title.scan(/#([a-zA-Z0-9_]+)/) do |match| + str << HTML.escape(raw_title[pos...match.begin(0)]) + str << %(#{HTML.escape(match[0])}) pos = match.end(0) end - str << title[pos..] + str << HTML.escape(raw_title[pos..]) end end diff --git a/src/invidious/videos/parser.cr b/src/invidious/videos/parser.cr index 3fc5e34eb..4dab5a489 100644 --- a/src/invidious/videos/parser.cr +++ b/src/invidious/videos/parser.cr @@ -20,14 +20,16 @@ module Invidious::Videos::Parser end # Both have "short", so the "long" option shouldn't be required - # Use the first run that has a valid browse endpoint. + # Use the first run that has a non-empty string browseId. # For collaboration videos, the first run is a plain text summary # without a link, while subsequent runs contain the channel links. byline_runs = (related["shortBylineText"]? || related["longBylineText"]?) .try &.dig?("runs").try &.as_a channel_info = byline_runs.try &.find do |run| - run.dig?("navigationEndpoint", "browseEndpoint", "browseId") + browse_id = run.dig?("navigationEndpoint", "browseEndpoint", "browseId") + text_id = browse_id.try &.as_s? + !text_id.nil? && !text_id.empty? end || byline_runs.try &.[0]? author = channel_info.try &.dig?("text") diff --git a/src/invidious/views/watch.ecr b/src/invidious/views/watch.ecr index 266065fbc..4788ab31a 100644 --- a/src/invidious/views/watch.ecr +++ b/src/invidious/views/watch.ecr @@ -94,7 +94,7 @@ we're going to need to do it here in order to allow for translations.