From 5632e58636d09d56a12006b8879771aef90ae504 Mon Sep 17 00:00:00 2001 From: Omar Roth Date: Tue, 4 Sep 2018 21:04:40 -0500 Subject: [PATCH] Add support for genre channels --- src/invidious.cr | 104 +++++++++++++++++++++++--------- src/invidious/channels.cr | 19 +++++- src/invidious/views/channel.ecr | 2 +- 3 files changed, 93 insertions(+), 32 deletions(-) diff --git a/src/invidious.cr b/src/invidious.cr index fa28b26f..f182d97c 100644 --- a/src/invidious.cr +++ b/src/invidious.cr @@ -1382,24 +1382,40 @@ get "/feed/channel/:ucid" do |env| end ucid = ucid.content + author = rss.xpath_node("//author/name").not_nil!.content next env.redirect "/feed/channel/#{ucid}" + else + rss = client.get("/feeds/videos.xml?channel_id=#{ucid}") + rss = XML.parse_html(rss.body) + + ucid = rss.xpath_node("//feed/channelid") + if !ucid + error_message = "User does not exist." + next templated "error" + end + + ucid = ucid.content + author = rss.xpath_node("//author/name").not_nil!.content end - url = produce_channel_videos_url(ucid) + # Auto-generated channels + # https://support.google.com/youtube/answer/2579942 + if author.ends_with? " - Topic" + auto_generated = true + end + + url = produce_channel_videos_url(ucid, auto_generated: auto_generated) response = client.get(url) json = JSON.parse(response.body) - if json["content_html"].as_s.empty? - if response.status_code == 500 - error_message = "This channel does not exist." - halt env, status_code: 404, response: error_message - else - next "" - end - end + 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")])) - content_html = json["content_html"].as_s - document = XML.parse_html(content_html) + videos = extract_videos(nodeset, ucid) + else + videos = [] of SearchVideo + end channel = get_channel(ucid, client, PG_DB, pull_all_videos: false) @@ -1420,8 +1436,7 @@ get "/feed/channel/:ucid" do |env| xml.element("uri") { xml.text "#{host_url}/channel/#{ucid}" } end - nodeset = document.xpath_nodes(%q(//li[contains(@class, "feed-item-container")])) - extract_videos(nodeset, ucid).each do |video| + videos.each do |video| xml.element("entry") do xml.element("id") { xml.text "yt:video:#{video.id}" } xml.element("yt:videoId") { xml.text video.id } @@ -1605,24 +1620,39 @@ get "/channel/:ucid" do |env| end ucid = ucid.content + author = rss.xpath_node("//author/name").not_nil!.content next env.redirect "/channel/#{ucid}" + else + rss = client.get("/feeds/videos.xml?channel_id=#{ucid}") + rss = XML.parse_html(rss.body) + + ucid = rss.xpath_node("//feed/channelid") + if !ucid + error_message = "User does not exist." + next templated "error" + end + + ucid = ucid.content + author = rss.xpath_node("//author/name").not_nil!.content end - rss = client.get("/feeds/videos.xml?channel_id=#{ucid}") - if rss.status_code == 404 - error_message = "This channel does not exist." - next templated "error" + # Auto-generated channels + # https://support.google.com/youtube/answer/2579942 + if author.ends_with? " - Topic" + auto_generated = true end - rss = XML.parse_html(rss.body) - author = rss.xpath_node("//feed/author/name").not_nil!.content + url = produce_channel_videos_url(ucid, page, auto_generated: auto_generated) + response = client.get(url) + json = JSON.parse(response.body) - begin - videos = extract_playlist(ucid, page) - videos.each { |a| a.playlists.clear } - rescue ex - error_message = ex.message - next templated "error" + 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")])) + + videos = extract_videos(nodeset, ucid) + else + videos = [] of SearchVideo end templated "channel" @@ -2236,11 +2266,29 @@ get "/api/v1/channels/:ucid" do |env| end ucid = ucid.content - url = "/api/v1/channels/#{ucid}" - next env.redirect url + author = rss.xpath_node("//author/name").not_nil!.content + next env.redirect "/api/v1/channels/#{ucid}" + else + rss = client.get("/feeds/videos.xml?channel_id=#{ucid}") + rss = XML.parse_html(rss.body) + + ucid = rss.xpath_node("//feed/channelid") + if !ucid + error_message = "User does not exist." + next templated "error" + end + + ucid = ucid.content + author = rss.xpath_node("//author/name").not_nil!.content end - url = produce_channel_videos_url(ucid, 1) + # Auto-generated channels + # https://support.google.com/youtube/answer/2579942 + if author.ends_with? " - Topic" + auto_generated = true + end + + url = produce_channel_videos_url(ucid, 1, auto_generated) response = client.get(url) json = JSON.parse(response.body) diff --git a/src/invidious/channels.cr b/src/invidious/channels.cr index 493d7379..329e67d3 100644 --- a/src/invidious/channels.cr +++ b/src/invidious/channels.cr @@ -131,10 +131,23 @@ def fetch_channel(ucid, client, db, pull_all_videos = true) return channel end -def produce_channel_videos_url(ucid, page = 1) - page = "#{page}" +def produce_channel_videos_url(ucid, page = 1, auto_generated = nil) + if auto_generated + seed = Time.epoch(1525757349) - meta = "\x12\x06videos \x00\x30\x02\x38\x01\x60\x01\x6a\x00\x7a" + until seed >= Time.now + seed += 1.month + end + timestamp = seed - (page - 1).months + + page = "#{timestamp.epoch}" + switch = "\x36" + else + page = "#{page}" + switch = "\x00" + end + + meta = "\x12\x06videos #{switch}\x30\x02\x38\x01\x60\x01\x6a\x00\x7a" meta += page.size.to_u8.unsafe_chr meta += page meta += "\xb8\x01\x00" diff --git a/src/invidious/views/channel.ecr b/src/invidious/views/channel.ecr index 00817076..3c0d607b 100644 --- a/src/invidious/views/channel.ecr +++ b/src/invidious/views/channel.ecr @@ -51,7 +51,7 @@
- <% if videos.size == 100 %> + <% if videos.size == 100 || auto_generated %> Next page <% end %>