summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorOmar Roth <omarroth@hotmail.com>2018-09-13 17:47:31 -0500
committerOmar Roth <omarroth@hotmail.com>2018-09-13 17:47:31 -0500
commitf7ca81c38413cc9d9e66551c2dc26aa0de1ad68b (patch)
tree23785202e877be737aff091d37050d6a7fd7a994
parentd4ee786cab3ee2e40804f90dca35007cbd850784 (diff)
downloadinvidious-f7ca81c38413cc9d9e66551c2dc26aa0de1ad68b.tar.gz
invidious-f7ca81c38413cc9d9e66551c2dc26aa0de1ad68b.tar.bz2
invidious-f7ca81c38413cc9d9e66551c2dc26aa0de1ad68b.zip
Add support for channel search
-rw-r--r--src/invidious.cr33
-rw-r--r--src/invidious/search.cr69
-rw-r--r--src/invidious/views/search.ecr2
-rw-r--r--src/invidious/views/template.ecr2
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 @@
</div>
<div class="pure-u-1 pure-u-md-3-5"></div>
<div style="text-align:right;" class="pure-u-1 pure-u-md-1-5">
- <% if count == 20 %>
+ <% if count >= 20 %>
<a href="/search?q=<%= query %>&page=<%= page + 1 %>">Next page</a>
<% end %>
</div>
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 @@
<div class="pure-u-1 pure-u-md-12-24 searchbar">
<form class="pure-form" action="/search" method="get">
<fieldset>
- <input type="search" style="width:100%;" name="q" placeholder="search" value="<%= env.params.query["q"]? %>">
+ <input type="search" style="width:100%;" name="q" placeholder="search" value="<%= env.params.query["q"]? || env.get? "search" %>">
</fieldset>
</form>
</div>