forked from midou/invidious
Add support for channel search
This commit is contained in:
parent
d4ee786cab
commit
f7ca81c384
@ -432,32 +432,39 @@ get "/search" do |env|
|
|||||||
page = env.params.query["page"]?.try &.to_i?
|
page = env.params.query["page"]?.try &.to_i?
|
||||||
page ||= 1
|
page ||= 1
|
||||||
|
|
||||||
sort = "relevance"
|
channel = nil
|
||||||
date = ""
|
date = ""
|
||||||
duration = ""
|
duration = ""
|
||||||
features = [] of String
|
features = [] of String
|
||||||
|
sort = "relevance"
|
||||||
|
|
||||||
operators = query.split(" ").select { |a| a.match(/\w+:[\w,]+/) }
|
operators = query.split(" ").select { |a| a.match(/\w+:[\w,]+/) }
|
||||||
operators.each do |operator|
|
operators.each do |operator|
|
||||||
key, value = operator.split(":")
|
key, value = operator.split(":")
|
||||||
|
|
||||||
case key
|
case key
|
||||||
when "sort"
|
when "channel", "user"
|
||||||
sort = value
|
channel = value
|
||||||
when "date"
|
when "date"
|
||||||
date = value
|
date = value
|
||||||
when "duration"
|
when "duration"
|
||||||
duration = value
|
duration = value
|
||||||
when "features"
|
when "features"
|
||||||
features = value.split(",")
|
features = value.split(",")
|
||||||
|
when "sort"
|
||||||
|
sort = value
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
search_query = (query.split(" ") - operators).join(" ")
|
search_query = (query.split(" ") - operators).join(" ")
|
||||||
|
|
||||||
search_params = build_search_params(sort: sort, date: date, content_type: "video",
|
if channel
|
||||||
duration: duration, features: features)
|
count, videos = channel_search(search_query, page, channel)
|
||||||
count, videos = search(search_query, page, search_params).as(Tuple)
|
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"
|
templated "search"
|
||||||
end
|
end
|
||||||
@ -1666,6 +1673,14 @@ get "/channel/:ucid" do |env|
|
|||||||
auto_generated = true
|
auto_generated = true
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if !auto_generated
|
||||||
|
if author.includes? " "
|
||||||
|
env.set "search", "channel:#{ucid} "
|
||||||
|
else
|
||||||
|
env.set "search", "channel:#{author.downcase} "
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
videos = [] of SearchVideo
|
videos = [] of SearchVideo
|
||||||
2.times do |i|
|
2.times do |i|
|
||||||
url = produce_channel_videos_url(ucid, page * 2 + (i - 1), auto_generated: auto_generated)
|
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)
|
url = URI.parse(url)
|
||||||
|
|
||||||
if {"m.youtube.com", "www.youtube.com", "youtu.be"}.includes? url.host
|
if {"m.youtube.com", "www.youtube.com", "youtu.be"}.includes? url.host
|
||||||
if url.path == "/redirect"
|
if url.path == "/redirect"
|
||||||
url = HTTP::Params.parse(url.query.not_nil!)["q"]
|
url = HTTP::Params.parse(url.query.not_nil!)["q"]
|
||||||
else
|
else
|
||||||
url = url.full_path
|
url = url.full_path
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
url = run["navigationEndpoint"]["commandMetadata"]?.try &.["webCommandMetadata"]["url"].as_s
|
url = run["navigationEndpoint"]["commandMetadata"]?.try &.["webCommandMetadata"]["url"].as_s
|
||||||
|
@ -12,6 +12,43 @@ class SearchVideo
|
|||||||
})
|
})
|
||||||
end
|
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"))
|
def search(query, page = 1, search_params = build_search_params(content_type: "video"))
|
||||||
client = make_client(YT_URL)
|
client = make_client(YT_URL)
|
||||||
if query.empty?
|
if query.empty?
|
||||||
@ -124,3 +161,35 @@ def build_search_params(sort : String = "relevance", date : String = "", content
|
|||||||
|
|
||||||
return token
|
return token
|
||||||
end
|
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
|
||||||
|
@ -18,7 +18,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="pure-u-1 pure-u-md-3-5"></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">
|
<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>
|
<a href="/search?q=<%= query %>&page=<%= page + 1 %>">Next page</a>
|
||||||
<% end %>
|
<% end %>
|
||||||
</div>
|
</div>
|
||||||
|
@ -28,7 +28,7 @@
|
|||||||
<div class="pure-u-1 pure-u-md-12-24 searchbar">
|
<div class="pure-u-1 pure-u-md-12-24 searchbar">
|
||||||
<form class="pure-form" action="/search" method="get">
|
<form class="pure-form" action="/search" method="get">
|
||||||
<fieldset>
|
<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>
|
</fieldset>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
Loading…
Reference in New Issue
Block a user