summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorOmar Roth <omarroth@hotmail.com>2018-03-24 22:38:35 -0500
committerOmar Roth <omarroth@hotmail.com>2018-03-24 22:38:35 -0500
commit076eaa7635cc320518a2e96775ff50e3712a7e77 (patch)
tree50e0881ba9e33d5674e94194b518935f5d52322c
parent0ed3e5d54703b73739083c5804a4304e3e24bc32 (diff)
downloadinvidious-076eaa7635cc320518a2e96775ff50e3712a7e77.tar.gz
invidious-076eaa7635cc320518a2e96775ff50e3712a7e77.tar.bz2
invidious-076eaa7635cc320518a2e96775ff50e3712a7e77.zip
Add subscriptions
-rw-r--r--src/helpers.cr50
-rw-r--r--src/invidious.cr65
-rw-r--r--src/views/layout.ecr13
-rw-r--r--src/views/subscriptions.ecr38
4 files changed, 161 insertions, 5 deletions
diff --git a/src/helpers.cr b/src/helpers.cr
index a8de6b0a..cd11baab 100644
--- a/src/helpers.cr
+++ b/src/helpers.cr
@@ -54,6 +54,26 @@ class Video
})
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
JSON.mapping({
data: RedditSubmitData,
@@ -464,3 +484,33 @@ def login_req(login_form, f_req)
return HTTP::Params.encode(data)
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
diff --git a/src/invidious.cr b/src/invidious.cr
index 969d2247..c9486e63 100644
--- a/src/invidious.cr
+++ b/src/invidious.cr
@@ -419,21 +419,24 @@ post "/login" do |env|
headers = login.cookies.add_request_headers(headers)
# We are now logged in
+ host = URI.parse(env.request.headers["Host"]).host
+
login.cookies.each do |cookie|
- host = URI.parse(env.request.headers["Host"]).host
+ cookie.secure = false
cookie.extension = cookie.extension.not_nil!.gsub(".youtube.com", host)
+ cookie.extension = cookie.extension.not_nil!.gsub("Secure; ", "")
end
login.cookies.add_response_headers(env.response.headers)
- env.redirect "/"
+ env.redirect "/feed/subscriptions"
rescue ex
error_message = "Login failed"
next templated "error"
end
end
-get "/logout" do |env|
+get "/signout" do |env|
env.request.cookies.each do |cookie|
cookie.expires = Time.new(1990, 1, 1)
end
@@ -546,6 +549,62 @@ get "/api/manifest/dash/id/:id" do |env|
manifest
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_message = "404 Page not found"
templated "error"
diff --git a/src/views/layout.ecr b/src/views/layout.ecr
index f0ce5a3f..872f9a05 100644
--- a/src/views/layout.ecr
+++ b/src/views/layout.ecr
@@ -26,9 +26,18 @@
</div>
<div class="pure-u-1 pure-u-md-1-5">
<% 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 %>
- <a href="/login" class="pure-menu-heading">Login</a>
+ <center><a href="/login" class="pure-menu-heading">Login</a></center>
<% end %>
</div>
</div>
diff --git a/src/views/subscriptions.ecr b/src/views/subscriptions.ecr
new file mode 100644
index 00000000..3d97a8f4
--- /dev/null
+++ b/src/views/subscriptions.ecr
@@ -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> \ No newline at end of file