diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/invidious.cr | 1 | ||||
| -rw-r--r-- | src/invidious/helpers/errors.cr | 35 | ||||
| -rw-r--r-- | src/invidious/helpers/helpers.cr | 1 | ||||
| -rw-r--r-- | src/invidious/helpers/utils.cr | 62 | ||||
| -rw-r--r-- | src/invidious/routes/misc.cr | 11 | ||||
| -rw-r--r-- | src/invidious/routes/preferences.cr | 61 | ||||
| -rw-r--r-- | src/invidious/users.cr | 1 | ||||
| -rw-r--r-- | src/invidious/views/channel.ecr | 3 | ||||
| -rw-r--r-- | src/invidious/views/community.ecr | 3 | ||||
| -rw-r--r-- | src/invidious/views/components/item.ecr | 21 | ||||
| -rw-r--r-- | src/invidious/views/error.ecr | 1 | ||||
| -rw-r--r-- | src/invidious/views/playlist.ecr | 5 | ||||
| -rw-r--r-- | src/invidious/views/playlists.ecr | 5 | ||||
| -rw-r--r-- | src/invidious/views/preferences.ecr | 7 | ||||
| -rw-r--r-- | src/invidious/views/search.ecr | 183 | ||||
| -rw-r--r-- | src/invidious/views/watch.ecr | 3 |
16 files changed, 281 insertions, 122 deletions
diff --git a/src/invidious.cr b/src/invidious.cr index 7037ecfe..b1ee1525 100644 --- a/src/invidious.cr +++ b/src/invidious.cr @@ -314,6 +314,7 @@ Invidious::Routing.get "/shorts/:id", Invidious::Routes::Watch, :redirect Invidious::Routing.get "/w/:id", Invidious::Routes::Watch, :redirect Invidious::Routing.get "/v/:id", Invidious::Routes::Watch, :redirect Invidious::Routing.get "/e/:id", Invidious::Routes::Watch, :redirect +Invidious::Routing.get "/redirect", Invidious::Routes::Misc, :cross_instance_redirect Invidious::Routing.get "/embed/", Invidious::Routes::Embed, :redirect Invidious::Routing.get "/embed/:id", Invidious::Routes::Embed, :show diff --git a/src/invidious/helpers/errors.cr b/src/invidious/helpers/errors.cr index 68ced430..e1d02563 100644 --- a/src/invidious/helpers/errors.cr +++ b/src/invidious/helpers/errors.cr @@ -40,6 +40,9 @@ def error_template_helper(env : HTTP::Server::Context, locale : Hash(String, JSO and include the following text in your message: <pre style="padding: 20px; background: rgba(0, 0, 0, 0.12345);">#{issue_template}</pre> END_HTML + + next_steps = error_redirect_helper(env, locale) + return templated "error" end @@ -47,6 +50,7 @@ def error_template_helper(env : HTTP::Server::Context, locale : Hash(String, JSO env.response.content_type = "text/html" env.response.status_code = status_code error_message = translate(locale, message) + next_steps = error_redirect_helper(env, locale) return templated "error" end @@ -103,3 +107,34 @@ end def error_json_helper(env : HTTP::Server::Context, locale : Hash(String, JSON::Any) | Nil, status_code : Int32, message : String) error_json_helper(env, locale, status_code, message, nil) end + +def error_redirect_helper(env : HTTP::Server::Context, locale : Hash(String, JSON::Any) | Nil) + request_path = env.request.path + + if request_path.starts_with?("/search") || request_path.starts_with?("/watch") || + request_path.starts_with?("/channel") || request_path.starts_with?("/playlist?list=PL") + next_steps_text = translate(locale, "next_steps_error_message") + refresh = translate(locale, "next_steps_error_message_refresh") + go_to_youtube = translate(locale, "next_steps_error_message_go_to_youtube") + switch_instance = translate(locale, "Switch Invidious Instance") + + return <<-END_HTML + <p style="margin-bottom: 4px;">#{next_steps_text}</p> + <ul> + <li> + <a href="#{env.request.resource}">#{refresh}</a> + </li> + <li> + <a href="/redirect?referer=#{env.get("current_page")}">#{switch_instance}</a> + </li> + <li> + <a href="https://youtube.com#{env.request.resource}">#{go_to_youtube}</a> + </li> + </ul> + END_HTML + + return next_step_html + else + return "" + end +end diff --git a/src/invidious/helpers/helpers.cr b/src/invidious/helpers/helpers.cr index 1f92c4ce..0c70cb02 100644 --- a/src/invidious/helpers/helpers.cr +++ b/src/invidious/helpers/helpers.cr @@ -44,6 +44,7 @@ struct ConfigPreferences property quality_dash : String = "auto" property default_home : String? = "Popular" property feed_menu : Array(String) = ["Popular", "Trending", "Subscriptions", "Playlists"] + property automatic_instance_redirect : Bool = false property related_videos : Bool = true property sort : String = "published" property speed : Float32 = 1.0_f32 diff --git a/src/invidious/helpers/utils.cr b/src/invidious/helpers/utils.cr index 66ad6961..6ee07d7a 100644 --- a/src/invidious/helpers/utils.cr +++ b/src/invidious/helpers/utils.cr @@ -409,3 +409,65 @@ def convert_theme(theme) theme end end + +def fetch_random_instance + begin + instance_api_client = HTTP::Client.new(URI.parse("https://api.invidious.io")) + + # Timeouts + instance_api_client.connect_timeout = 10.seconds + instance_api_client.dns_timeout = 10.seconds + + instance_list = JSON.parse(instance_api_client.get("/instances.json").body).as_a + instance_api_client.close + rescue Socket::ConnectError | IO::TimeoutError | JSON::ParseException + instance_list = [] of JSON::Any + end + + filtered_instance_list = [] of String + + instance_list.each do |data| + # TODO Check if current URL is onion instance and use .onion types if so. + if data[1]["type"] == "https" + # Instances can have statisitics disabled, which is an requirement of version validation. + # as_nil? doesn't exist. Thus we'll have to handle the error rasied if as_nil fails. + begin + data[1]["stats"].as_nil + next + rescue TypeCastError + end + + # stats endpoint could also lack the software dict. + next if data[1]["stats"]["software"]?.nil? + + # Makes sure the instance isn't too outdated. + if remote_version = data[1]["stats"]?.try &.["software"]?.try &.["version"] + remote_commit_date = remote_version.as_s.match(/\d{4}\.\d{2}\.\d{2}/) + next if !remote_commit_date + + remote_commit_date = Time.parse(remote_commit_date[0], "%Y.%m.%d", Time::Location::UTC) + local_commit_date = Time.parse(CURRENT_VERSION, "%Y.%m.%d", Time::Location::UTC) + + next if (remote_commit_date - local_commit_date).abs.days > 30 + + begin + data[1]["monitor"].as_nil + health = data[1]["monitor"].as_h["dailyRatios"][0].as_h["ratio"] + filtered_instance_list << data[0].as_s if health.to_s.to_f > 90 + rescue TypeCastError + # We can't check the health if the monitoring is broken. Thus we'll just add it to the list + # and move on. Ideally we'll ignore any instance that has broken health monitoring but due to the fact that + # it's an error that often occurs with all the instances at the same time, we have to just skip the check. + filtered_instance_list << data[0].as_s + end + end + end + end + + # If for some reason no instances managed to get fetched successfully then we'll just redirect to redirect.invidious.io + if filtered_instance_list.size == 0 + return "redirect.invidious.io" + end + + return filtered_instance_list.sample(1)[0] +end diff --git a/src/invidious/routes/misc.cr b/src/invidious/routes/misc.cr index d32ba892..336f7e33 100644 --- a/src/invidious/routes/misc.cr +++ b/src/invidious/routes/misc.cr @@ -35,4 +35,15 @@ class Invidious::Routes::Misc < Invidious::Routes::BaseRoute locale = LOCALES[env.get("preferences").as(Preferences).locale]? rendered "licenses" end + + def cross_instance_redirect(env) + referer = get_referer(env) + + if !env.get("preferences").as(Preferences).automatic_instance_redirect + return env.redirect("https://redirect.invidious.io#{referer}") + end + + instance_url = fetch_random_instance + env.redirect "https://#{instance_url}#{referer}" + end end diff --git a/src/invidious/routes/preferences.cr b/src/invidious/routes/preferences.cr index f98c7a5e..d6002ffd 100644 --- a/src/invidious/routes/preferences.cr +++ b/src/invidious/routes/preferences.cr @@ -92,6 +92,10 @@ class Invidious::Routes::PreferencesRoute < Invidious::Routes::BaseRoute end end + automatic_instance_redirect = env.params.body["automatic_instance_redirect"]?.try &.as(String) + automatic_instance_redirect ||= "off" + automatic_instance_redirect = automatic_instance_redirect == "on" + locale = env.params.body["locale"]?.try &.as(String) locale ||= CONFIG.default_user_preferences.locale @@ -122,34 +126,35 @@ class Invidious::Routes::PreferencesRoute < Invidious::Routes::BaseRoute # Convert to JSON and back again to take advantage of converters used for compatability preferences = Preferences.from_json({ - annotations: annotations, - annotations_subscribed: annotations_subscribed, - autoplay: autoplay, - captions: captions, - comments: comments, - continue: continue, - continue_autoplay: continue_autoplay, - dark_mode: dark_mode, - latest_only: latest_only, - listen: listen, - local: local, - locale: locale, - max_results: max_results, - notifications_only: notifications_only, - player_style: player_style, - quality: quality, - quality_dash: quality_dash, - default_home: default_home, - feed_menu: feed_menu, - related_videos: related_videos, - sort: sort, - speed: speed, - thin_mode: thin_mode, - unseen_only: unseen_only, - video_loop: video_loop, - volume: volume, - extend_desc: extend_desc, - vr_mode: vr_mode, + annotations: annotations, + annotations_subscribed: annotations_subscribed, + autoplay: autoplay, + captions: captions, + comments: comments, + continue: continue, + continue_autoplay: continue_autoplay, + dark_mode: dark_mode, + latest_only: latest_only, + listen: listen, + local: local, + locale: locale, + max_results: max_results, + notifications_only: notifications_only, + player_style: player_style, + quality: quality, + quality_dash: quality_dash, + default_home: default_home, + feed_menu: feed_menu, + automatic_instance_redirect: automatic_instance_redirect, + related_videos: related_videos, + sort: sort, + speed: speed, + thin_mode: thin_mode, + unseen_only: unseen_only, + video_loop: video_loop, + volume: volume, + extend_desc: extend_desc, + vr_mode: vr_mode, }.to_json).to_json if user = env.get? "user" diff --git a/src/invidious/users.cr b/src/invidious/users.cr index d774ee12..98ef8792 100644 --- a/src/invidious/users.cr +++ b/src/invidious/users.cr @@ -36,6 +36,7 @@ struct Preferences property annotations : Bool = CONFIG.default_user_preferences.annotations property annotations_subscribed : Bool = CONFIG.default_user_preferences.annotations_subscribed property autoplay : Bool = CONFIG.default_user_preferences.autoplay + property automatic_instance_redirect : Bool = CONFIG.default_user_preferences.automatic_instance_redirect @[JSON::Field(converter: Preferences::StringToArray)] @[YAML::Field(converter: Preferences::StringToArray)] diff --git a/src/invidious/views/channel.ecr b/src/invidious/views/channel.ecr index 061d7eec..21038394 100644 --- a/src/invidious/views/channel.ecr +++ b/src/invidious/views/channel.ecr @@ -41,6 +41,9 @@ <div class="pure-g h-box"> <div class="pure-u-1-3"> <a href="https://www.youtube.com/channel/<%= channel.ucid %>"><%= translate(locale, "View channel on YouTube") %></a> + <div class="pure-u-1 pure-md-1-3"> + <a href="/redirect?referer=<%= env.get?("current_page") %>"><%= translate(locale, "Switch Invidious Instance") %></a> + </div> <% if !channel.auto_generated %> <div class="pure-u-1 pure-md-1-3"> <b><%= translate(locale, "Videos") %></b> diff --git a/src/invidious/views/community.ecr b/src/invidious/views/community.ecr index 3c4eaabb..b0092e5f 100644 --- a/src/invidious/views/community.ecr +++ b/src/invidious/views/community.ecr @@ -40,6 +40,9 @@ <div class="pure-g h-box"> <div class="pure-u-1-3"> <a href="https://www.youtube.com/channel/<%= channel.ucid %>/community"><%= translate(locale, "View channel on YouTube") %></a> + <div class="pure-u-1 pure-md-1-3"> + <a href="/redirect?referer=<%= env.get?("current_page") %>"><%= translate(locale, "Switch Invidious Instance") %></a> + </div> <% if !channel.auto_generated %> <div class="pure-u-1 pure-md-1-3"> <a href="/channel/<%= channel.ucid %>"><%= translate(locale, "Videos") %></a> diff --git a/src/invidious/views/components/item.ecr b/src/invidious/views/components/item.ecr index 9dfa047e..6f027bee 100644 --- a/src/invidious/views/components/item.ecr +++ b/src/invidious/views/components/item.ecr @@ -137,17 +137,22 @@ </a> <% end %> <p><a href="/watch?v=<%= item.id %>"><%= HTML.escape(item.title) %></a></p> - <p style="display: flex;"> + <div style="display: flex"> <b style="flex: 1;"> <a style="width:100%" href="/channel/<%= item.ucid %>"><%= item.author %></a> </b> - <a title="<%=translate(locale, "Watch on YouTube")%>" href="https://www.youtube.com/watch?v=<%= item.id %>" style="margin-right: 5px;"> - <i class="icon ion-logo-youtube"></i> - </a> - <a title="<%=translate(locale, "Audio mode")%>" href="/watch?v=<%= item.id %>&listen=1"> - <i class="icon ion-md-headset"></i> - </a> - </p> + <div class="icon-buttons"> + <a title="<%=translate(locale, "Watch on YouTube")%>" href="https://www.youtube.com/watch?v=<%= item.id %>"> + <i class="icon ion-logo-youtube"></i> + </a> + <a title="<%=translate(locale, "Audio mode")%>" href="/watch?v=<%= item.id %>&listen=1"> + <i class="icon ion-md-headset"></i> + </a> + <a title="<%=translate(locale, "Switch Invidious Instance")%>" href="/redirect?referer=<%=HTML.escape("watch?v=#{item.id}")%>"> + <i class="icon ion-md-jet"></i> + </a> + </div> + </div> <h5 class="pure-g"> <% if item.responds_to?(:premiere_timestamp) && item.premiere_timestamp.try &.> Time.utc %> diff --git a/src/invidious/views/error.ecr b/src/invidious/views/error.ecr index d0752e5b..04eb74d5 100644 --- a/src/invidious/views/error.ecr +++ b/src/invidious/views/error.ecr @@ -4,4 +4,5 @@ <div class="h-box"> <%= error_message %> + <%= next_steps %> </div> diff --git a/src/invidious/views/playlist.ecr b/src/invidious/views/playlist.ecr index 91156028..a19dd182 100644 --- a/src/invidious/views/playlist.ecr +++ b/src/invidious/views/playlist.ecr @@ -36,6 +36,11 @@ <a href="https://www.youtube.com/playlist?list=<%= playlist.id %>"> <%= translate(locale, "View playlist on YouTube") %> </a> + <span> | </span> + <a href="/redirect?referer=<%= env.get?("current_page") %>"> + <%= translate(locale, "Switch Invidious Instance") %> + </a> + </div> <% end %> </div> diff --git a/src/invidious/views/playlists.ecr b/src/invidious/views/playlists.ecr index 44bdb94d..975ccd6c 100644 --- a/src/invidious/views/playlists.ecr +++ b/src/invidious/views/playlists.ecr @@ -42,6 +42,11 @@ <div class="pure-u-1 pure-md-1-3"> <a href="https://www.youtube.com/channel/<%= channel.ucid %>/playlists"><%= translate(locale, "View channel on YouTube") %></a> </div> + + <div class="pure-u-1 pure-md-1-3"> + <a href="/redirect?referer=<%= env.get?("current_page") %>"><%= translate(locale, "Switch Invidious Instance") %></a> + </div> + <div class="pure-u-1 pure-md-1-3"> <a href="/channel/<%= channel.ucid %>"><%= translate(locale, "Videos") %></a> </div> diff --git a/src/invidious/views/preferences.ecr b/src/invidious/views/preferences.ecr index 1e1e8cae..c5b64ad6 100644 --- a/src/invidious/views/preferences.ecr +++ b/src/invidious/views/preferences.ecr @@ -176,6 +176,13 @@ <% end %> </div> + <legend><%= translate(locale, "Miscellaneous preferences") %></legend> + + <div class="pure-control-group"> + <label for="automatic_instance_redirect"><%= translate(locale, "Automaticatic instance redirection (fallback to redirect.invidious.io): ") %></label> + <input name="automatic_instance_redirect" id="automatic_instance_redirect" type="checkbox" <% if preferences.automatic_instance_redirect %>checked<% end %>> + </div> + <% if env.get? "user" %> <legend><%= translate(locale, "Subscription preferences") %></legend> diff --git a/src/invidious/views/search.ecr b/src/invidious/views/search.ecr index fefc9fbb..15389dce 100644 --- a/src/invidious/views/search.ecr +++ b/src/invidious/views/search.ecr @@ -2,94 +2,105 @@ <title><%= search_query.not_nil!.size > 30 ? HTML.escape(query.not_nil![0,30].rstrip(".") + "...") : HTML.escape(query.not_nil!) %> - Invidious</title> <% end %> -<details id="filters"> - <summary> - <h3 style="display:inline"> <%= translate(locale, "filter") %> </h3> - </summary> - <div id="filters" class="pure-g h-box"> - <div class="pure-u-1-3 pure-u-md-1-5"> - <b><%= translate(locale, "date") %></b> - <hr/> - <% ["hour", "today", "week", "month", "year"].each do |date| %> - <div class="pure-u-1 pure-md-1-5"> - <% if operator_hash.fetch("date", "all") == date %> - <b><%= translate(locale, date) %></b> - <% else %> - <a href="/search?q=<%= HTML.escape(query.not_nil!.gsub(/ ?date:[a-z]+/, "") + " date:" + date) %>&page=<%= page %>"> - <%= translate(locale, date) %> - </a> - <% end %> - </div> - <% end %> +<!-- Search redirection and filtering UI --> +<% if count == 0 %> + <h3 style="text-align: center"> + <a href="/redirect?referer=<%= env.get?("current_page") %>"><%= translate(locale, "Broken? Try another Invidious Instance!") %></a> + </h3> +<% else %> + <details id="filters"> + <summary> + <h3 style="display:inline"> <%= translate(locale, "filter") %> </h3> + </summary> + <div id="filters" class="pure-g h-box"> + <div class="pure-u-1-3 pure-u-md-1-5"> + <b><%= translate(locale, "date") %></b> + <hr/> + <% ["hour", "today", "week", "month", "year"].each do |date| %> + <div class="pure-u-1 pure-md-1-5"> + <% if operator_hash.fetch("date", "all") == date %> + <b><%= translate(locale, date) %></b> + <% else %> + <a href="/search?q=<%= HTML.escape(query.not_nil!.gsub(/ ?date:[a-z]+/, "") + " date:" + date) %>&page=<%= page %>"> + <%= translate(locale, date) %> + </a> + <% end %> + </div> + <% end %> + </div> + <div class="pure-u-1-3 pure-u-md-1-5"> + <b><%= translate(locale, "content_type") %></b> + <hr/> + <% ["video", "channel", "playlist", "movie", "show"].each do |content_type| %> + <div class="pure-u-1 pure-md-1-5"> + <% if operator_hash.fetch("content_type", "all") == content_type %> + <b><%= translate(locale, content_type) %></b> + <% else %> + <a href="/search?q=<%= HTML.escape(query.not_nil!.gsub(/ ?content_type:[a-z]+/, "") + " content_type:" + content_type) %>&page=<%= page %>"> + <%= translate(locale, content_type) %> + </a> + <% end %> + </div> + <% end %> + </div> + <div class="pure-u-1-3 pure-u-md-1-5"> + <b><%= translate(locale, "duration") %></b> + <hr/> + <% ["short", "long"].each do |duration| %> + <div class="pure-u-1 pure-md-1-5"> + <% if operator_hash.fetch("duration", "all") == duration %> + <b><%= translate(locale, duration) %></b> + <% else %> + <a href="/search?q=<%= HTML.escape(query.not_nil!.gsub(/ ?duration:[a-z]+/, "") + " duration:" + duration) %>&page=<%= page %>"> + <%= translate(locale, duration) %> + </a> + <% end %> + </div> + <% end %> + </div> + <div class="pure-u-1-3 pure-u-md-1-5"> + <b><%= translate(locale, "features") %></b> + <hr/> + <% ["hd", "subtitles", "creative_commons", "3d", "live", "purchased", "4k", "360", "location", "hdr"].each do |feature| %> + <div class="pure-u-1 pure-md-1-5"> + <% if operator_hash.fetch("features", "all").includes?(feature) %> + <b><%= translate(locale, feature) %></b> + <% elsif operator_hash.has_key?("features") %> + <a href="/search?q=<%= HTML.escape(query.not_nil!.gsub(/features:/, "features:" + feature + ",")) %>&page=<%= page %>"> + <%= translate(locale, feature) %> + </a> + <% else %> + <a href="/search?q=<%= HTML.escape(query.not_nil! + " features:" + feature) %>&page=<%= page %>"> + <%= translate(locale, feature) %> + </a> + <% end %> + </div> + <% end %> + </div> + <div class="pure-u-1-3 pure-u-md-1-5"> + <b><%= translate(locale, "sort") %></b> + <hr/> + <% ["relevance", "rating", "date", "views"].each do |sort| %> + <div class="pure-u-1 pure-md-1-5"> + <% if operator_hash.fetch("sort", "relevance") == sort %> + <b><%= translate(locale, sort) %></b> + <% else %> + <a href="/search?q=<%= HTML.escape(query.not_nil!.gsub(/ ?sort:[a-z]+/, "") + " sort:" + sort) %>&page=<%= page %>"> + <%= translate(locale, sort) %> + </a> + <% end %> + </div> + <% end %> + </div> </div> - <div class="pure-u-1-3 pure-u-md-1-5"> - <b><%= translate(locale, "content_type") %></b> - <hr/> - <% ["video", "channel", "playlist", "movie", "show"].each do |content_type| %> - <div class="pure-u-1 pure-md-1-5"> - <% if operator_hash.fetch("content_type", "all") == content_type %> - <b><%= translate(locale, content_type) %></b> - <% else %> - <a href="/search?q=<%= HTML.escape(query.not_nil!.gsub(/ ?content_type:[a-z]+/, "") + " content_type:" + content_type) %>&page=<%= page %>"> - <%= translate(locale, content_type) %> - </a> - <% end %> - </div> - <% end %> - </div> - <div class="pure-u-1-3 pure-u-md-1-5"> - <b><%= translate(locale, "duration") %></b> - <hr/> - <% ["short", "long"].each do |duration| %> - <div class="pure-u-1 pure-md-1-5"> - <% if operator_hash.fetch("duration", "all") == duration %> - <b><%= translate(locale, duration) %></b> - <% else %> - <a href="/search?q=<%= HTML.escape(query.not_nil!.gsub(/ ?duration:[a-z]+/, "") + " duration:" + duration) %>&page=<%= page %>"> - <%= translate(locale, duration) %> - </a> - <% end %> - </div> - <% end %> - </div> - <div class="pure-u-1-3 pure-u-md-1-5"> - <b><%= translate(locale, "features") %></b> - <hr/> - <% ["hd", "subtitles", "creative_commons", "3d", "live", "purchased", "4k", "360", "location", "hdr"].each do |feature| %> - <div class="pure-u-1 pure-md-1-5"> - <% if operator_hash.fetch("features", "all").includes?(feature) %> - <b><%= translate(locale, feature) %></b> - <% elsif operator_hash.has_key?("features") %> - <a href="/search?q=<%= HTML.escape(query.not_nil!.gsub(/features:/, "features:" + feature + ",")) %>&page=<%= page %>"> - <%= translate(locale, feature) %> - </a> - <% else %> - <a href="/search?q=<%= HTML.escape(query.not_nil! + " features:" + feature) %>&page=<%= page %>"> - <%= translate(locale, feature) %> - </a> - <% end %> - </div> - <% end %> - </div> - <div class="pure-u-1-3 pure-u-md-1-5"> - <b><%= translate(locale, "sort") %></b> - <hr/> - <% ["relevance", "rating", "date", "views"].each do |sort| %> - <div class="pure-u-1 pure-md-1-5"> - <% if operator_hash.fetch("sort", "relevance") == sort %> - <b><%= translate(locale, sort) %></b> - <% else %> - <a href="/search?q=<%= HTML.escape(query.not_nil!.gsub(/ ?sort:[a-z]+/, "") + " sort:" + sort) %>&page=<%= page %>"> - <%= translate(locale, sort) %> - </a> - <% end %> - </div> - <% end %> - </div> - </div> -</details> + </details> +<% end %> -<hr/> +<% if count == 0 %> + <hr style="margin: 0;"/> +<% else %> + <hr/> +<% end %> <div class="pure-g h-box v-box"> <div class="pure-u-1 pure-u-lg-1-5"> diff --git a/src/invidious/views/watch.ecr b/src/invidious/views/watch.ecr index baffa08b..91e03725 100644 --- a/src/invidious/views/watch.ecr +++ b/src/invidious/views/watch.ecr @@ -109,6 +109,9 @@ we're going to need to do it here in order to allow for translations. <a href="https://www.youtube.com/watch?v=<%= video.id %>"><%= translate(locale, "Watch on YouTube") %></a> (<a href="https://www.youtube.com/embed/<%= video.id %>"><%= translate(locale, "Embed") %></a>) </span> + <p id="watch-on-another-invidious-instance"> + <a href="/redirect?referer=<%= env.get?("current_page") %>"><%= translate(locale, "Switch Invidious Instance") %></a> + </p> <p id="embed-link"> <a href="<%= embed_link %>"><%= translate(locale, "Embed Link") %></a> </p> |
