diff --git a/src/helpers.cr b/src/helpers.cr index e7a1e688..040f09fe 100644 --- a/src/helpers.cr +++ b/src/helpers.cr @@ -670,3 +670,82 @@ def decode_time(string) return time end + +def produce_playlist_url(ucid, index) + ucid = ucid.lstrip("UC") + ucid = "VLUU" + ucid + + continuation = write_var_int(index) + continuation.unshift(0x08_u8) + slice = continuation.to_unsafe.to_slice(continuation.size) + + continuation = Base64.urlsafe_encode(slice, false) + continuation = "PT:" + continuation + continuation = continuation.bytes + continuation.unshift(0x7a_u8, continuation.size.to_u8) + + slice = continuation.to_unsafe.to_slice(continuation.size) + continuation = Base64.urlsafe_encode(slice) + continuation = URI.escape(continuation) + continuation = continuation.bytes + continuation.unshift(continuation.size.to_u8) + + continuation.unshift(ucid.size.to_u8) + continuation = ucid.bytes + continuation + continuation.unshift(0x12.to_u8, ucid.size.to_u8) + continuation.unshift(0xe2_u8, 0xa9_u8, 0x85_u8, 0xb2_u8, 2_u8, continuation.size.to_u8) + + slice = continuation.to_unsafe.to_slice(continuation.size) + continuation = Base64.urlsafe_encode(slice) + continuation = URI.escape(continuation) + + url = "/browse_ajax?action_continuation=1&continuation=#{continuation}" + + return url +end + +def read_var_int(bytes) + numRead = 0 + result = 0 + + read = bytes[numRead] + + if bytes.size == 1 + result = bytes[0].to_i32 + else + while ((read & 0b10000000) != 0) + read = bytes[numRead].to_u64 + value = (read & 0b01111111) + result |= (value << (7 * numRead)) + + numRead += 1 + if numRead > 5 + raise "VarInt is too big" + end + end + end + + return result +end + +def write_var_int(value : Int) + bytes = [] of UInt8 + value = value.to_u32 + + if value == 0 + bytes = [0_u8] + else + while value != 0 + temp = (value & 0b01111111).to_u8 + value = value >> 7 + + if value != 0 + temp |= 0b10000000 + end + + bytes << temp + end + end + + return bytes +end diff --git a/src/invidious.cr b/src/invidious.cr index f86d9331..679fd748 100644 --- a/src/invidious.cr +++ b/src/invidious.cr @@ -908,6 +908,41 @@ get "/videoplayback" do |env| end end +get "/channel/:ucid" do |env| + ucid = env.params.url["ucid"] + + page = env.params.query["page"]?.try &.to_i + page ||= 1 + + client = make_client(YT_URL) + + if !ucid.starts_with? "UC" + rss = client.get("/feeds/videos.xml?user=#{ucid}").body + rss = XML.parse_html(rss) + + ucid = rss.xpath_node("//feed/channelid").not_nil!.content + env.redirect "/channel/#{ucid}" + end + + url = produce_playlist_url(ucid, (page - 1) * 100) + response = client.get(url) + + json = JSON.parse(response.body) + document = XML.parse_html(json["content_html"].as_s) + author = document.xpath_node(%q(//div[@class="pl-video-owner"]/a)).not_nil!.content + + videos = [] of ChannelVideo + document.xpath_nodes(%q(//a[contains(@class,"pl-video-title-link")])).each do |item| + href = URI.parse(item["href"]) + id = HTTP::Params.parse(href.query.not_nil!)["v"] + title = item.content + + videos << ChannelVideo.new(id, title, Time.now, Time.now, ucid, author) + end + + templated "channel" +end + options "/videoplayback" do |env| env.response.headers["Access-Control-Allow-Origin"] = "*" env.response.headers["Access-Control-Allow-Methods"] = "GET" diff --git a/src/views/channel.ecr b/src/views/channel.ecr new file mode 100644 index 00000000..857ad57a --- /dev/null +++ b/src/views/channel.ecr @@ -0,0 +1,26 @@ +<% content_for "header" do %> +<%= author %> - Invidious +<% end %> + +

<%= author %>

+<% videos.each_slice(4) do |slice| %> +
+ <% slice.each do |video| %> + <%= rendered "components/video" %> + <% end %> +
+<% end %> + +
+
+ <% if page > 2 %> + Previous page + <% else %> + Previous page + <% end %> +
+
+
+ Next page +
+
\ No newline at end of file