diff options
| -rw-r--r-- | scripts/deploy-database.sh | 4 | ||||
| -rw-r--r-- | scripts/install-dependencies.sh | 2 | ||||
| -rw-r--r-- | spec/invidious/search/query_spec.cr | 42 | ||||
| -rw-r--r-- | src/invidious/channels/about.cr | 13 | ||||
| -rw-r--r-- | src/invidious/routes/search.cr | 6 | ||||
| -rw-r--r-- | src/invidious/search/query.cr | 12 | ||||
| -rw-r--r-- | src/invidious/videos.cr | 52 | ||||
| -rw-r--r-- | src/invidious/views/components/item.ecr | 33 | ||||
| -rw-r--r-- | src/invidious/views/feeds/history.ecr | 4 | ||||
| -rw-r--r-- | src/invidious/views/search.ecr | 10 | ||||
| -rw-r--r-- | src/invidious/views/user/subscription_manager.ecr | 4 | ||||
| -rw-r--r-- | src/invidious/views/user/token_manager.ecr | 4 | ||||
| -rw-r--r-- | src/invidious/views/watch.ecr | 8 |
13 files changed, 100 insertions, 94 deletions
diff --git a/scripts/deploy-database.sh b/scripts/deploy-database.sh index ed9464e6..fa24b8f0 100644 --- a/scripts/deploy-database.sh +++ b/scripts/deploy-database.sh @@ -6,7 +6,7 @@ interactive=true -if [ "$1" == "--no-interactive" ]; then +if [ "$1" = "--no-interactive" ]; then interactive=false fi @@ -21,7 +21,7 @@ sudo systemctl enable postgresql.service # Create databse and user # -if [ "$interactive" == "true" ]; then +if [ "$interactive" = "true" ]; then sudo -u postgres -- createuser -P kemal sudo -u postgres -- createdb -O kemal invidious else diff --git a/scripts/install-dependencies.sh b/scripts/install-dependencies.sh index 27e0bf15..1e67bdaf 100644 --- a/scripts/install-dependencies.sh +++ b/scripts/install-dependencies.sh @@ -74,7 +74,7 @@ install_apt() { sudo apt-get install --yes --no-install-recommends \ libssl-dev libxml2-dev libyaml-dev libgmp-dev libevent-dev \ libpcre3-dev libreadline-dev libsqlite3-dev zlib1g-dev \ - crystal postgres git librsvg2-bin make + crystal postgresql-13 git librsvg2-bin make } install_yum() { diff --git a/spec/invidious/search/query_spec.cr b/spec/invidious/search/query_spec.cr index 4853e9e9..063b69f1 100644 --- a/spec/invidious/search/query_spec.cr +++ b/spec/invidious/search/query_spec.cr @@ -197,4 +197,46 @@ Spectator.describe Invidious::Search::Query do ) end end + + describe "#to_http_params" do + it "formats regular search" do + query = described_class.new( + HTTP::Params.parse("q=The+Simpsons+hiding+in+bush&duration=short"), + Invidious::Search::Query::Type::Regular, nil + ) + + params = query.to_http_params + + expect(params).to have_key("duration") + expect(params["duration"]?).to eq("short") + + expect(params).to have_key("q") + expect(params["q"]?).to eq("The Simpsons hiding in bush") + + # Check if there aren't other parameters + params.delete("duration") + params.delete("q") + expect(params).to be_empty + end + + it "formats channel search" do + query = described_class.new( + HTTP::Params.parse("q=channel:UC2DjFE7Xf11URZqWBigcVOQ%20multimeter"), + Invidious::Search::Query::Type::Regular, nil + ) + + params = query.to_http_params + + expect(params).to have_key("channel") + expect(params["channel"]?).to eq("UC2DjFE7Xf11URZqWBigcVOQ") + + expect(params).to have_key("q") + expect(params["q"]?).to eq("multimeter") + + # Check if there aren't other parameters + params.delete("channel") + params.delete("q") + expect(params).to be_empty + end + end end diff --git a/src/invidious/channels/about.cr b/src/invidious/channels/about.cr index c2027f90..f60ee7af 100644 --- a/src/invidious/channels/about.cr +++ b/src/invidious/channels/about.cr @@ -59,9 +59,6 @@ def get_about_info(ucid, locale) : AboutChannel banner = banners.try &.[-1]?.try &.["url"].as_s? description_node = initdata["header"]["interactiveTabbedHeaderRenderer"]["description"] - - is_family_friendly = initdata["microformat"]["microformatDataRenderer"]["familySafe"].as_bool - allowed_regions = initdata["microformat"]["microformatDataRenderer"]["availableCountries"].as_a.map(&.as_s) else author = initdata["metadata"]["channelMetadataRenderer"]["title"].as_s author_url = initdata["metadata"]["channelMetadataRenderer"]["channelUrl"].as_s @@ -79,13 +76,17 @@ def get_about_info(ucid, locale) : AboutChannel # end description_node = initdata["metadata"]["channelMetadataRenderer"]?.try &.["description"]? - - is_family_friendly = initdata["microformat"]["microformatDataRenderer"]["familySafe"].as_bool - allowed_regions = initdata["microformat"]["microformatDataRenderer"]["availableCountries"].as_a.map(&.as_s) end + is_family_friendly = initdata["microformat"]["microformatDataRenderer"]["familySafe"].as_bool + + allowed_regions = initdata + .dig?("microformat", "microformatDataRenderer", "availableCountries") + .try &.as_a.map(&.as_s) || [] of String + description = !description_node.nil? ? description_node.as_s : "" description_html = HTML.escape(description) + if !description_node.nil? if description_node.as_h?.nil? description_node = text_to_parsed_content(description_node.as_s) diff --git a/src/invidious/routes/search.cr b/src/invidious/routes/search.cr index 6f8bffea..2a9705cf 100644 --- a/src/invidious/routes/search.cr +++ b/src/invidious/routes/search.cr @@ -59,6 +59,12 @@ module Invidious::Routes::Search return error_template(500, ex) end + params = query.to_http_params + url_prev_page = "/search?#{params}&page=#{query.page - 1}" + url_next_page = "/search?#{params}&page=#{query.page + 1}" + + redirect_url = Invidious::Frontend::Misc.redirect_url(env) + env.set "search", query.text templated "search" end diff --git a/src/invidious/search/query.cr b/src/invidious/search/query.cr index 34b36b1d..24e79609 100644 --- a/src/invidious/search/query.cr +++ b/src/invidious/search/query.cr @@ -57,7 +57,7 @@ module Invidious::Search # Get the page number (also common to all search types) @page = params["page"]?.try &.to_i? || 1 - # Stop here is raw query in empty + # Stop here if raw query is empty # NOTE: maybe raise in the future? return if self.empty_raw_query? @@ -127,6 +127,16 @@ module Invidious::Search return items end + # Return the HTTP::Params corresponding to this Query (invidious format) + def to_http_params : HTTP::Params + params = @filters.to_iv_params + + params["q"] = @query + params["channel"] = @channel if !@channel.empty? + + return params + end + # TODO: clean code private def unnest_items(all_items) : Array(SearchItem) items = [] of SearchItem diff --git a/src/invidious/videos.cr b/src/invidious/videos.cr index 1a547ee0..19ee064c 100644 --- a/src/invidious/videos.cr +++ b/src/invidious/videos.cr @@ -323,7 +323,7 @@ struct Video json.field "viewCount", self.views json.field "likeCount", self.likes - json.field "dislikeCount", self.dislikes + json.field "dislikeCount", 0_i64 json.field "paid", self.paid json.field "premium", self.premium @@ -354,7 +354,7 @@ struct Video json.field "lengthSeconds", self.length_seconds json.field "allowRatings", self.allow_ratings - json.field "rating", self.average_rating + json.field "rating", 0_i64 json.field "isListed", self.is_listed json.field "liveNow", self.live_now json.field "isUpcoming", self.is_upcoming @@ -556,11 +556,6 @@ struct Video info["dislikes"]?.try &.as_i64 || 0_i64 end - def average_rating : Float64 - # (likes / (likes + dislikes) * 4 + 1) - info["videoDetails"]["averageRating"]?.try { |t| t.as_f? || t.as_i64?.try &.to_f64 }.try &.round(4) || 0.0 - end - def published : Time info .dig?("microformat", "playerMicroformatRenderer", "publishDate") @@ -813,14 +808,6 @@ struct Video return info.dig?("streamingData", "adaptiveFormats", 0, "projectionType").try &.as_s end - def wilson_score : Float64 - ci_lower_bound(likes, likes + dislikes).round(4) - end - - def engagement : Float64 - (((likes + dislikes) / views) * 100).round(4) - end - def reason : String? info["reason"]?.try &.as_s end @@ -908,13 +895,20 @@ def extract_video_info(video_id : String, proxy_region : String? = nil, context_ player_response = YoutubeAPI.player(video_id: video_id, params: "", client_config: client_config) - if player_response.dig?("playabilityStatus", "status").try &.as_s != "OK" + playability_status = player_response.dig?("playabilityStatus", "status").try &.as_s + + if playability_status != "OK" subreason = player_response.dig?("playabilityStatus", "errorScreen", "playerErrorMessageRenderer", "subreason") reason = subreason.try &.[]?("simpleText").try &.as_s reason ||= subreason.try &.[]("runs").as_a.map(&.[]("text")).join("") reason ||= player_response.dig("playabilityStatus", "reason").as_s + params["reason"] = JSON::Any.new(reason) - return params + + # Stop here if video is not a scheduled livestream + if playability_status != "LIVE_STREAM_OFFLINE" + return params + end end params["shortDescription"] = player_response.dig?("videoDetails", "shortDescription") || JSON::Any.new(nil) @@ -1005,7 +999,7 @@ def extract_video_info(video_id : String, proxy_region : String? = nil, context_ params["relatedVideos"] = JSON::Any.new(related) - # Likes/dislikes + # Likes toplevel_buttons = video_primary_renderer .try &.dig?("videoActions", "menuRenderer", "topLevelButtons") @@ -1023,30 +1017,10 @@ def extract_video_info(video_id : String, proxy_region : String? = nil, context_ LOGGER.trace("extract_video_info: Found \"likes\" button. Button text is \"#{likes_txt}\"") LOGGER.debug("extract_video_info: Likes count is #{likes}") if likes end - - dislikes_button = toplevel_buttons.as_a - .find(&.dig("toggleButtonRenderer", "defaultIcon", "iconType").as_s.== "DISLIKE") - .try &.["toggleButtonRenderer"] - - if dislikes_button - dislikes_txt = (dislikes_button["defaultText"]? || dislikes_button["toggledText"]?) - .try &.dig?("accessibility", "accessibilityData", "label") - dislikes = dislikes_txt.as_s.gsub(/\D/, "").to_i64? if dislikes_txt - - LOGGER.trace("extract_video_info: Found \"dislikes\" button. Button text is \"#{dislikes_txt}\"") - LOGGER.debug("extract_video_info: Dislikes count is #{dislikes}") if dislikes - end - end - - if likes && likes != 0_i64 && (!dislikes || dislikes == 0_i64) - if rating = player_response.dig?("videoDetails", "averageRating").try { |x| x.as_i64? || x.as_f? } - dislikes = (likes * ((5 - rating)/(rating - 1))).round.to_i64 - LOGGER.debug("extract_video_info: Dislikes count (using fallback method) is #{dislikes}") - end end params["likes"] = JSON::Any.new(likes || 0_i64) - params["dislikes"] = JSON::Any.new(dislikes || 0_i64) + params["dislikes"] = JSON::Any.new(0_i64) # Description diff --git a/src/invidious/views/components/item.ecr b/src/invidious/views/components/item.ecr index fb7ad1dc..0e959ff2 100644 --- a/src/invidious/views/components/item.ecr +++ b/src/invidious/views/components/item.ecr @@ -5,7 +5,7 @@ <a href="/channel/<%= item.ucid %>"> <% if !env.get("preferences").as(Preferences).thin_mode %> <center> - <img loading="lazy" style="width:56.25%" src="/ggpht<%= URI.parse(item.author_thumbnail).request_target.gsub(/=s\d+/, "=s176") %>"/> + <img loading="lazy" tabindex="-1" style="width:56.25%" src="/ggpht<%= URI.parse(item.author_thumbnail).request_target.gsub(/=s\d+/, "=s176") %>"/> </center> <% end %> <p dir="auto"><%= HTML.escape(item.author) %><% if !item.author_verified.nil? && item.author_verified %> <i class="icon ion ion-md-checkmark-circle"></i><% end %></p> @@ -23,7 +23,7 @@ <a style="width:100%" href="<%= url %>"> <% if !env.get("preferences").as(Preferences).thin_mode %> <div class="thumbnail"> - <img loading="lazy" class="thumbnail" src="<%= URI.parse(item.thumbnail || "/").request_target %>"/> + <img loading="lazy" tabindex="-1" class="thumbnail" src="<%= URI.parse(item.thumbnail || "/").request_target %>"/> <p class="length"><%= translate_count(locale, "generic_videos_count", item.video_count, NumberFormatting::Separator) %></p> </div> <% end %> @@ -36,7 +36,7 @@ <a href="/watch?v=<%= item.id %>&list=<%= item.rdid %>"> <% if !env.get("preferences").as(Preferences).thin_mode %> <div class="thumbnail"> - <img loading="lazy" class="thumbnail" src="/vi/<%= item.id %>/mqdefault.jpg"/> + <img loading="lazy" tabindex="-1" class="thumbnail" src="/vi/<%= item.id %>/mqdefault.jpg"/> <% if item.length_seconds != 0 %> <p class="length"><%= recode_length_seconds(item.length_seconds) %></p> <% end %> @@ -51,16 +51,13 @@ <a style="width:100%" href="/watch?v=<%= item.id %>&list=<%= item.plid %>&index=<%= item.index %>"> <% if !env.get("preferences").as(Preferences).thin_mode %> <div class="thumbnail"> - <img loading="lazy" class="thumbnail" src="/vi/<%= item.id %>/mqdefault.jpg"/> + <img loading="lazy" tabindex="-1" class="thumbnail" src="/vi/<%= item.id %>/mqdefault.jpg"/> + <% if plid_form = env.get?("remove_playlist_items") %> <form data-onsubmit="return_false" action="/playlist_ajax?action_remove_video=1&set_video_id=<%= item.index %>&playlist_id=<%= plid_form %>&referer=<%= env.get("current_page") %>" method="post"> <input type="hidden" name="csrf_token" value="<%= HTML.escape(env.get?("csrf_token").try &.as(String) || "") %>"> <p class="watched"> - <a data-onclick="remove_playlist_item" data-index="<%= item.index %>" data-plid="<%= plid_form %>" href="javascript:void(0)"> - <button type="submit" style="all:unset"> - <i class="icon ion-md-trash"></i> - </button> - </a> + <button type="submit" style="all:unset" data-onclick="remove_playlist_item" data-index="<%= item.index %>" data-plid="<%= plid_form %>"><i class="icon ion-md-trash"></i></button> </p> </form> <% end %> @@ -103,29 +100,21 @@ <a style="width:100%" href="/watch?v=<%= item.id %>"> <% if !env.get("preferences").as(Preferences).thin_mode %> <div class="thumbnail"> - <img loading="lazy" class="thumbnail" src="/vi/<%= item.id %>/mqdefault.jpg"/> + <img loading="lazy" tabindex="-1" class="thumbnail" src="/vi/<%= item.id %>/mqdefault.jpg"/> <% if env.get? "show_watched" %> <form data-onsubmit="return_false" action="/watch_ajax?action_mark_watched=1&id=<%= item.id %>&referer=<%= env.get("current_page") %>" method="post"> <input type="hidden" name="csrf_token" value="<%= HTML.escape(env.get?("csrf_token").try &.as(String) || "") %>"> <p class="watched"> - <a data-onclick="mark_watched" data-id="<%= item.id %>" href="javascript:void(0)"> - <button type="submit" style="all:unset"> - <i data-mouse="switch_classes" data-switch-classes="ion-ios-eye-off,ion-ios-eye" - class="icon ion-ios-eye"> - </i> - </button> - </a> + <button type="submit" style="all:unset" data-onclick="mark_watched" data-id="<%= item.id %>"> + <i data-mouse="switch_classes" data-switch-classes="ion-ios-eye-off,ion-ios-eye" class="icon ion-ios-eye"></i> + </button> </p> </form> <% elsif plid_form = env.get? "add_playlist_items" %> <form data-onsubmit="return_false" action="/playlist_ajax?action_add_video=1&video_id=<%= item.id %>&playlist_id=<%= plid_form %>&referer=<%= env.get("current_page") %>" method="post"> <input type="hidden" name="csrf_token" value="<%= HTML.escape(env.get?("csrf_token").try &.as(String) || "") %>"> <p class="watched"> - <a data-onclick="add_playlist_item" data-id="<%= item.id %>" data-plid="<%= plid_form %>" href="javascript:void(0)"> - <button type="submit" style="all:unset"> - <i class="icon ion-md-add"></i> - </button> - </a> + <button type="submit" style="all:unset" data-onclick="add_playlist_item" data-id="<%= item.id %>" data-plid="<%= plid_form %>"><i class="icon ion-md-add"></i></button> </p> </form> <% end %> diff --git a/src/invidious/views/feeds/history.ecr b/src/invidious/views/feeds/history.ecr index 6c1243c5..471d21db 100644 --- a/src/invidious/views/feeds/history.ecr +++ b/src/invidious/views/feeds/history.ecr @@ -38,9 +38,7 @@ <form data-onsubmit="return_false" action="/watch_ajax?action_mark_unwatched=1&id=<%= item %>&referer=<%= env.get("current_page") %>" method="post"> <input type="hidden" name="csrf_token" value="<%= URI.encode_www_form(env.get?("csrf_token").try &.as(String) || "") %>"> <p class="watched"> - <a data-onclick="mark_unwatched" data-id="<%= item %>" href="javascript:void(0)"> - <button type="submit" style="all:unset"><i class="icon ion-md-trash"></i></button> - </a> + <button type="submit" style="all:unset" data-onclick="mark_unwatched" data-id="<%= item %>"><i class="icon ion-md-trash"></i></button> </p> </form> </div> diff --git a/src/invidious/views/search.ecr b/src/invidious/views/search.ecr index 7110703e..254449a1 100644 --- a/src/invidious/views/search.ecr +++ b/src/invidious/views/search.ecr @@ -3,16 +3,6 @@ <link rel="stylesheet" href="/css/search.css?v=<%= ASSET_COMMIT %>"> <% end %> -<%- - search_query_encoded = URI.encode_www_form(query.text, space_to_plus: true) - filter_params = query.filters.to_iv_params - - url_prev_page = "/search?q=#{search_query_encoded}&#{filter_params}&page=#{query.page - 1}" - url_next_page = "/search?q=#{search_query_encoded}&#{filter_params}&page=#{query.page + 1}" - - redirect_url = Invidious::Frontend::Misc.redirect_url(env) --%> - <!-- Search redirection and filtering UI --> <%= Invidious::Frontend::SearchFilters.generate(query.filters, query.text, query.page, locale) %> <hr/> diff --git a/src/invidious/views/user/subscription_manager.ecr b/src/invidious/views/user/subscription_manager.ecr index c2a89ca2..c9801f09 100644 --- a/src/invidious/views/user/subscription_manager.ecr +++ b/src/invidious/views/user/subscription_manager.ecr @@ -39,9 +39,7 @@ <h3 style="padding-right:0.5em"> <form data-onsubmit="return_false" action="/subscription_ajax?action_remove_subscriptions=1&c=<%= channel.id %>&referer=<%= env.get("current_page") %>" method="post"> <input type="hidden" name="csrf_token" value="<%= HTML.escape(env.get?("csrf_token").try &.as(String) || "") %>"> - <a data-onclick="remove_subscription" data-ucid="<%= channel.id %>" href="#"> - <input style="all:unset" type="submit" value="<%= translate(locale, "unsubscribe") %>"> - </a> + <input style="all:unset" type="submit" data-onclick="remove_subscription" data-ucid="<%= channel.id %>" value="<%= translate(locale, "unsubscribe") %>"> </form> </h3> </div> diff --git a/src/invidious/views/user/token_manager.ecr b/src/invidious/views/user/token_manager.ecr index 79f905a1..a73fa048 100644 --- a/src/invidious/views/user/token_manager.ecr +++ b/src/invidious/views/user/token_manager.ecr @@ -31,9 +31,7 @@ <h3 style="padding-right:0.5em"> <form data-onsubmit="return_false" action="/token_ajax?action_revoke_token=1&session=<%= token[:session] %>&referer=<%= env.get("current_page") %>" method="post"> <input type="hidden" name="csrf_token" value="<%= HTML.escape(env.get?("csrf_token").try &.as(String) || "") %>"> - <a data-onclick="revoke_token" data-session="<%= token[:session] %>" href="#"> - <input style="all:unset" type="submit" value="<%= translate(locale, "revoke") %>"> - </a> + <input style="all:unset" type="submit" data-onclick="revoke_token" data-session="<%= token[:session] %>" value="<%= translate(locale, "revoke") %>"> </form> </h3> </div> diff --git a/src/invidious/views/watch.ecr b/src/invidious/views/watch.ecr index d1fdcce2..50c63d21 100644 --- a/src/invidious/views/watch.ecr +++ b/src/invidious/views/watch.ecr @@ -173,7 +173,7 @@ we're going to need to do it here in order to allow for translations. <p id="views"><i class="icon ion-ios-eye"></i> <%= number_with_separator(video.views) %></p> <p id="likes"><i class="icon ion-ios-thumbs-up"></i> <%= number_with_separator(video.likes) %></p> - <p id="dislikes"></p> + <p id="dislikes" style="display: none; visibility: hidden;"></p> <p id="genre"><%= translate(locale, "Genre: ") %> <% if !video.genre_url %> <%= video.genre %> @@ -185,9 +185,9 @@ we're going to need to do it here in order to allow for translations. <p id="license"><%= translate(locale, "License: ") %><%= video.license %></p> <% end %> <p id="family_friendly"><%= translate(locale, "Family friendly? ") %><%= translate_bool(locale, video.is_family_friendly) %></p> - <p id="wilson"><%= translate(locale, "Wilson score: ") %><%= video.wilson_score %></p> - <p id="rating"></p> - <p id="engagement"><%= translate(locale, "Engagement: ") %><%= video.engagement %>%</p> + <p id="wilson" style="display: none; visibility: hidden;"></p> + <p id="rating" style="display: none; visibility: hidden;"></p> + <p id="engagement" style="display: none; visibility: hidden;"></p> <% if video.allowed_regions.size != REGIONS.size %> <p id="allowed_regions"> <% if video.allowed_regions.size < REGIONS.size // 2 %> |
