mirror of https://github.com/iv-org/invidious.git
Add subscriptions
This commit is contained in:
parent
0ed3e5d547
commit
076eaa7635
|
@ -54,6 +54,26 @@ class Video
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
|
class InvidiousChannel
|
||||||
|
module XMLConverter
|
||||||
|
def self.from_rs(rs)
|
||||||
|
XML.parse_html(rs.read(String))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
add_mapping({
|
||||||
|
id: String,
|
||||||
|
rss: {
|
||||||
|
type: XML::Node,
|
||||||
|
default: XML.parse_html(""),
|
||||||
|
converter: InvidiousChannel::XMLConverter,
|
||||||
|
|
||||||
|
},
|
||||||
|
updated: Time,
|
||||||
|
author: String,
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
class RedditSubmit
|
class RedditSubmit
|
||||||
JSON.mapping({
|
JSON.mapping({
|
||||||
data: RedditSubmitData,
|
data: RedditSubmitData,
|
||||||
|
@ -464,3 +484,33 @@ def login_req(login_form, f_req)
|
||||||
|
|
||||||
return HTTP::Params.encode(data)
|
return HTTP::Params.encode(data)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def get_channel(id, client, db)
|
||||||
|
if db.query_one?("SELECT EXISTS (SELECT true FROM channels WHERE id = $1)", id, as: Bool)
|
||||||
|
channel = db.query_one("SELECT * FROM channels WHERE id = $1", id, as: InvidiousChannel)
|
||||||
|
|
||||||
|
if Time.now - channel.updated > 1.hours
|
||||||
|
db.exec("DELETE FROM channels * WHERE id = $1", id)
|
||||||
|
channel = fetch_channel(id, client)
|
||||||
|
args = arg_array(channel.to_a)
|
||||||
|
db.exec("INSERT INTO channels VALUES (#{args})", channel.to_a)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
channel = fetch_channel(id, client)
|
||||||
|
args = arg_array(channel.to_a)
|
||||||
|
db.exec("INSERT INTO channels VALUES (#{args})", channel.to_a)
|
||||||
|
end
|
||||||
|
|
||||||
|
return channel
|
||||||
|
end
|
||||||
|
|
||||||
|
def fetch_channel(id, client)
|
||||||
|
rss = client.get("/feeds/videos.xml?channel_id=#{id}").body
|
||||||
|
rss = XML.parse_html(rss)
|
||||||
|
|
||||||
|
author = rss.xpath_node("//feed/author/name").not_nil!.content
|
||||||
|
|
||||||
|
channel = InvidiousChannel.new(id, rss, Time.now, author)
|
||||||
|
|
||||||
|
return channel
|
||||||
|
end
|
||||||
|
|
|
@ -419,21 +419,24 @@ post "/login" do |env|
|
||||||
headers = login.cookies.add_request_headers(headers)
|
headers = login.cookies.add_request_headers(headers)
|
||||||
# We are now logged in
|
# We are now logged in
|
||||||
|
|
||||||
login.cookies.each do |cookie|
|
|
||||||
host = URI.parse(env.request.headers["Host"]).host
|
host = URI.parse(env.request.headers["Host"]).host
|
||||||
|
|
||||||
|
login.cookies.each do |cookie|
|
||||||
|
cookie.secure = false
|
||||||
cookie.extension = cookie.extension.not_nil!.gsub(".youtube.com", host)
|
cookie.extension = cookie.extension.not_nil!.gsub(".youtube.com", host)
|
||||||
|
cookie.extension = cookie.extension.not_nil!.gsub("Secure; ", "")
|
||||||
end
|
end
|
||||||
|
|
||||||
login.cookies.add_response_headers(env.response.headers)
|
login.cookies.add_response_headers(env.response.headers)
|
||||||
|
|
||||||
env.redirect "/"
|
env.redirect "/feed/subscriptions"
|
||||||
rescue ex
|
rescue ex
|
||||||
error_message = "Login failed"
|
error_message = "Login failed"
|
||||||
next templated "error"
|
next templated "error"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
get "/logout" do |env|
|
get "/signout" do |env|
|
||||||
env.request.cookies.each do |cookie|
|
env.request.cookies.each do |cookie|
|
||||||
cookie.expires = Time.new(1990, 1, 1)
|
cookie.expires = Time.new(1990, 1, 1)
|
||||||
end
|
end
|
||||||
|
@ -546,6 +549,62 @@ get "/api/manifest/dash/id/:id" do |env|
|
||||||
manifest
|
manifest
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Get subscriptions for authorized user
|
||||||
|
get "/feed/subscriptions" do |env|
|
||||||
|
authorized = env.get "authorized"
|
||||||
|
|
||||||
|
if authorized
|
||||||
|
max_results = env.params.query["maxResults"]?.try &.to_i
|
||||||
|
max_results ||= 40
|
||||||
|
|
||||||
|
page = env.params.query["page"]?.try &.to_i
|
||||||
|
page ||= 1
|
||||||
|
|
||||||
|
client = get_client(youtube_pool)
|
||||||
|
|
||||||
|
headers = HTTP::Headers.new
|
||||||
|
headers["Cookie"] = env.request.headers["Cookie"]
|
||||||
|
|
||||||
|
feed = client.get("/subscription_manager?action_takeout=1", headers).body
|
||||||
|
|
||||||
|
videos = Array(Hash(String, String | Time)).new
|
||||||
|
|
||||||
|
feed = XML.parse_html(feed)
|
||||||
|
feed.xpath_nodes("//opml/outline/outline").each do |channel|
|
||||||
|
id = channel["xmlurl"][-24..-1]
|
||||||
|
rss = get_channel(id, client, PG_DB).rss
|
||||||
|
|
||||||
|
rss.xpath_nodes("//feed/entry").each do |entry|
|
||||||
|
video = {} of String => String | Time
|
||||||
|
|
||||||
|
video["id"] = entry.xpath_node("videoid").not_nil!.content
|
||||||
|
video["title"] = entry.xpath_node("title").not_nil!.content
|
||||||
|
video["published"] = Time.parse(entry.xpath_node("published").not_nil!.content, "%FT%X%z")
|
||||||
|
video["author"] = entry.xpath_node("author/name").not_nil!.content
|
||||||
|
video["ucid"] = entry.xpath_node("channelid").not_nil!.content
|
||||||
|
video["thumbnail"] = entry.xpath_node("group/thumbnail").not_nil!["url"].gsub(/hqdefault\.jpg$/, "mqdefault.jpg")
|
||||||
|
# video["thumbnail"] = video["thumbnail"].rstrip("hqdefault.jpg")
|
||||||
|
# video["thumbnail"] += "mqdefault.jpg"
|
||||||
|
|
||||||
|
videos << video
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
youtube_pool << client
|
||||||
|
|
||||||
|
videos.sort_by! { |video| video["published"].as(Time).epoch }
|
||||||
|
videos.reverse!
|
||||||
|
|
||||||
|
start = (page - 1)*max_results
|
||||||
|
stop = start + max_results - 1
|
||||||
|
videos = videos[start..stop]
|
||||||
|
|
||||||
|
templated "subscriptions"
|
||||||
|
else
|
||||||
|
env.redirect "/"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
error 404 do |env|
|
error 404 do |env|
|
||||||
error_message = "404 Page not found"
|
error_message = "404 Page not found"
|
||||||
templated "error"
|
templated "error"
|
||||||
|
|
|
@ -26,9 +26,18 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="pure-u-1 pure-u-md-1-5">
|
<div class="pure-u-1 pure-u-md-1-5">
|
||||||
<% if env.get "authorized" %>
|
<% if env.get "authorized" %>
|
||||||
<a href="/logout" class="pure-menu-heading">Logout</a>
|
<div class="pure-g">
|
||||||
|
<div class="pure-u-1 pure-u-md-1-3">
|
||||||
|
<a href="/feed/subscriptions" class="pure-menu-heading">
|
||||||
|
<center><i class="far fa-bell"></i></center>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div class="pure-u-1 pure-u-md-2-3">
|
||||||
|
<a href="/signout" class="pure-menu-heading">Sign out</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<% else %>
|
<% else %>
|
||||||
<a href="/login" class="pure-menu-heading">Login</a>
|
<center><a href="/login" class="pure-menu-heading">Login</a></center>
|
||||||
<% end %>
|
<% end %>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -0,0 +1,38 @@
|
||||||
|
<% content_for "header" do %>
|
||||||
|
<title>Subscriptions - Invidious</title>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
|
<% videos.each_slice(4) do |slice| %>
|
||||||
|
<div class="pure-g">
|
||||||
|
<% slice.each do |video| %>
|
||||||
|
<div class="pure-u-1 pure-u-md-1-4">
|
||||||
|
<div style="overflow-wrap:break-word; word-wrap:break-word;" class="h-box">
|
||||||
|
<a style="width:100%;" href="/watch?v=<%= video["id"] %>">
|
||||||
|
<img style="width:100%;" src="<%= video["thumbnail"] %>"/>
|
||||||
|
<p style="height:100%"><%= video["title"] %></p>
|
||||||
|
</a>
|
||||||
|
<p>
|
||||||
|
<b><a style="width:100%;" href="https://youtube.com/channel/<%= video["ucid"] %>"><%= video["author"] %></a></b>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<h5>Shared <%= video["published"].as(Time).to_s("%B %-d, %Y at %r") %></h5>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
|
<div class="pure-g">
|
||||||
|
<div class="pure-u-1 pure-u-md-1-5">
|
||||||
|
<% if page > 1 %>
|
||||||
|
<a href="/feed/subscriptions?maxResults=<%= max_results %>&page=<%= page - 1 %>">Previous page</a>
|
||||||
|
<% else %>
|
||||||
|
<a href="/feed/subscriptions?maxResults=<%= max_results %>">Previous page</a>
|
||||||
|
<% end %>
|
||||||
|
</div>
|
||||||
|
<div class="pure-u-1 pure-u-md-3-5"></div>
|
||||||
|
<div class="pure-u-1 pure-u-md-1-5">
|
||||||
|
<a href="/feed/subscriptions?maxResults=<%= max_results %>&page=<%= page + 1 %>">Next page</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
Loading…
Reference in New Issue