From b7697e97c388120d456a93fedd81d3db77fa3f47 Mon Sep 17 00:00:00 2001 From: Alex DevTech Date: Thu, 30 Jul 2026 18:06:12 -0700 Subject: [PATCH] feat: make hashtags in video titles clickable (#442) Adds parse_hashtags_in_text() to utils.cr that wraps #hashtag patterns in anchor links pointing to /hashtag/:tag. Applied to video titles on: - Watch page (watch.ecr) - main title and related video titles - Search/feed items (item.ecr) - all video result titles The function uses a regex to find #word patterns and converts them to #TAG links, preserving the original text appearance while making hashtags navigable to their search pages. --- src/invidious/helpers/utils.cr | 10 ++++++++++ src/invidious/views/components/item.ecr | 4 ++-- src/invidious/views/watch.ecr | 4 ++-- 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/src/invidious/helpers/utils.cr b/src/invidious/helpers/utils.cr index 7a262f849..ad4b65324 100644 --- a/src/invidious/helpers/utils.cr +++ b/src/invidious/helpers/utils.cr @@ -404,3 +404,13 @@ def invidious_companion_encrypt(data) encrypted_data = encrypt_ecb_without_salt("#{timestamp}|#{data}", CONFIG.invidious_companion_key) return Base64.urlsafe_encode(encrypted_data) end + +# Parses hashtags (#tag) in a text string and wraps them in clickable links. +# The input text should already be HTML-escaped. The hashtag text is kept as-is +# (including the # symbol) within the link, while the URL uses the encoded tag name. +def parse_hashtags_in_text(text : String) : String + text.gsub(/#([\w-]+)/) do |_match| + hashtag = $1 + %(##{hashtag}) + end +end diff --git a/src/invidious/views/components/item.ecr b/src/invidious/views/components/item.ecr index ece2efe8a..61545e593 100644 --- a/src/invidious/views/components/item.ecr +++ b/src/invidious/views/components/item.ecr @@ -78,7 +78,7 @@
-

<%= HTML.escape(item.title) %>

+

<%= parse_hashtags_in_text(HTML.escape(item.title)) %>

@@ -176,7 +176,7 @@
-

<%= HTML.escape(item.title) %>

+

<%= parse_hashtags_in_text(HTML.escape(item.title)) %>

diff --git a/src/invidious/views/watch.ecr b/src/invidious/views/watch.ecr index 796c0c914..12ae4ba52 100644 --- a/src/invidious/views/watch.ecr +++ b/src/invidious/views/watch.ecr @@ -1,5 +1,5 @@ <% ucid = video.ucid %> -<% title = HTML.escape(video.title) %> +<% title = parse_hashtags_in_text(HTML.escape(video.title)) %> <% author = HTML.escape(video.author) %> @@ -341,7 +341,7 @@ we're going to need to do it here in order to allow for translations.
- &listen=<%= params.listen %>">

<%= HTML.escape(rv["title"]) %>

+ &listen=<%= params.listen %>">

<%= parse_hashtags_in_text(HTML.escape(rv["title"])) %>