diff options
| author | Omar Roth <omarroth@hotmail.com> | 2018-03-24 22:38:35 -0500 |
|---|---|---|
| committer | Omar Roth <omarroth@hotmail.com> | 2018-03-24 22:38:35 -0500 |
| commit | 076eaa7635cc320518a2e96775ff50e3712a7e77 (patch) | |
| tree | 50e0881ba9e33d5674e94194b518935f5d52322c | |
| parent | 0ed3e5d54703b73739083c5804a4304e3e24bc32 (diff) | |
| download | invidious-076eaa7635cc320518a2e96775ff50e3712a7e77.tar.gz invidious-076eaa7635cc320518a2e96775ff50e3712a7e77.tar.bz2 invidious-076eaa7635cc320518a2e96775ff50e3712a7e77.zip | |
Add subscriptions
| -rw-r--r-- | src/helpers.cr | 50 | ||||
| -rw-r--r-- | src/invidious.cr | 65 | ||||
| -rw-r--r-- | src/views/layout.ecr | 13 | ||||
| -rw-r--r-- | src/views/subscriptions.ecr | 38 |
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 |
