From f7ca81c38413cc9d9e66551c2dc26aa0de1ad68b Mon Sep 17 00:00:00 2001 From: Omar Roth Date: Thu, 13 Sep 2018 17:47:31 -0500 Subject: [PATCH] Add support for channel search --- src/invidious.cr | 33 ++++++++++----- src/invidious/search.cr | 69 ++++++++++++++++++++++++++++++++ src/invidious/views/search.ecr | 2 +- src/invidious/views/template.ecr | 2 +- 4 files changed, 95 insertions(+), 11 deletions(-) diff --git a/src/invidious.cr b/src/invidious.cr index 51e4d9d4..9cf120c5 100644 --- a/src/invidious.cr +++ b/src/invidious.cr @@ -432,32 +432,39 @@ get "/search" do |env| page = env.params.query["page"]?.try &.to_i? page ||= 1 - sort = "relevance" + channel = nil date = "" duration = "" features = [] of String + sort = "relevance" operators = query.split(" ").select { |a| a.match(/\w+:[\w,]+/) } operators.each do |operator| key, value = operator.split(":") case key - when "sort" - sort = value + when "channel", "user" + channel = value when "date" date = value when "duration" duration = value when "features" features = value.split(",") + when "sort" + sort = value end end search_query = (query.split(" ") - operators).join(" ") - search_params = build_search_params(sort: sort, date: date, content_type: "video", - duration: duration, features: features) - count, videos = search(search_query, page, search_params).as(Tuple) + if channel + count, videos = channel_search(search_query, page, channel) + else + search_params = build_search_params(sort: sort, date: date, content_type: "video", + duration: duration, features: features) + count, videos = search(search_query, page, search_params).as(Tuple) + end templated "search" end @@ -1666,6 +1673,14 @@ get "/channel/:ucid" do |env| auto_generated = true end + if !auto_generated + if author.includes? " " + env.set "search", "channel:#{ucid} " + else + env.set "search", "channel:#{author.downcase} " + end + end + videos = [] of SearchVideo 2.times do |i| url = produce_channel_videos_url(ucid, page * 2 + (i - 1), auto_generated: auto_generated) @@ -1918,11 +1933,11 @@ get "/api/v1/comments/:id" do |env| url = URI.parse(url) if {"m.youtube.com", "www.youtube.com", "youtu.be"}.includes? url.host - if url.path == "/redirect" - url = HTTP::Params.parse(url.query.not_nil!)["q"] + if url.path == "/redirect" + url = HTTP::Params.parse(url.query.not_nil!)["q"] else url = url.full_path - end + end end else url = run["navigationEndpoint"]["commandMetadata"]?.try &.["webCommandMetadata"]["url"].as_s diff --git a/src/invidious/search.cr b/src/invidious/search.cr index 6baeba3e..9e4d277a 100644 --- a/src/invidious/search.cr +++ b/src/invidious/search.cr @@ -12,6 +12,43 @@ class SearchVideo }) end +def channel_search(query, page, channel) + client = make_client(YT_URL) + + response = client.get("/user/#{channel}") + document = XML.parse_html(response.body) + canonical = document.xpath_node(%q(//link[@rel="canonical"])) + + if !canonical + response = client.get("/channel/#{channel}") + document = XML.parse_html(response.body) + canonical = document.xpath_node(%q(//link[@rel="canonical"])) + end + + if !canonical + return 0, [] of SearchVideo + end + + ucid = canonical["href"].split("/")[-1] + + url = produce_channel_search_url(ucid, query, page) + response = client.get(url) + json = JSON.parse(response.body) + + if json["content_html"]? && !json["content_html"].as_s.empty? + document = XML.parse_html(json["content_html"].as_s) + nodeset = document.xpath_nodes(%q(//li[contains(@class, "feed-item-container")])) + + count = nodeset.size + videos = extract_videos(nodeset) + else + count = 0 + videos = [] of SearchVideo + end + + return count, videos +end + def search(query, page = 1, search_params = build_search_params(content_type: "video")) client = make_client(YT_URL) if query.empty? @@ -124,3 +161,35 @@ def build_search_params(sort : String = "relevance", date : String = "", content return token end + +def produce_channel_search_url(ucid, query, page) + page = "#{page}" + + meta = "\x12\x06search0\x02\x38\x01\x60\x01\x6a\x00\x7a" + meta += page.size.to_u8.unsafe_chr + meta += page + meta += "\xb8\x01\x00" + + meta = Base64.urlsafe_encode(meta) + meta = URI.escape(meta) + + continuation = "\x12" + continuation += ucid.size.to_u8.unsafe_chr + continuation += ucid + continuation += "\x1a" + continuation += meta.size.to_u8.unsafe_chr + continuation += meta + continuation += "\x5a" + continuation += query.size.to_u8.unsafe_chr + continuation += query + + continuation = continuation.size.to_u8.unsafe_chr + continuation + continuation = "\xe2\xa9\x85\xb2\x02" + continuation + + continuation = Base64.urlsafe_encode(continuation) + continuation = URI.escape(continuation) + + url = "/browse_ajax?continuation=#{continuation}" + + return url +end diff --git a/src/invidious/views/search.ecr b/src/invidious/views/search.ecr index 6f6df8e0..5ea7345d 100644 --- a/src/invidious/views/search.ecr +++ b/src/invidious/views/search.ecr @@ -18,7 +18,7 @@
- <% if count == 20 %> + <% if count >= 20 %> Next page <% end %>
diff --git a/src/invidious/views/template.ecr b/src/invidious/views/template.ecr index 8a3134de..1086b3d7 100644 --- a/src/invidious/views/template.ecr +++ b/src/invidious/views/template.ecr @@ -28,7 +28,7 @@