diff --git a/src/invidious/channels/community.cr b/src/invidious/channels/community.cr index 49ffd990..43843b11 100644 --- a/src/invidious/channels/community.cr +++ b/src/invidious/channels/community.cr @@ -3,8 +3,8 @@ private IMAGE_QUALITIES = {320, 560, 640, 1280, 2000} # TODO: Add "sort_by" def fetch_channel_community(ucid, cursor, locale, format, thin_mode) if cursor.nil? - # Egljb21tdW5pdHk%3D is the protobuf object to load "community" - initial_data = YoutubeAPI.browse(ucid, params: "Egljb21tdW5pdHk%3D") + # EgVwb3N0c_IGBAoCSgA%3D is the protobuf object to load "posts" + initial_data = YoutubeAPI.browse(ucid, params: "EgVwb3N0c_IGBAoCSgA%3D") items = [] of JSON::Any extract_items(initial_data) do |item| @@ -24,15 +24,21 @@ def fetch_channel_community(ucid, cursor, locale, format, thin_mode) return extract_channel_community(items, ucid: ucid, locale: locale, format: format, thin_mode: thin_mode) end +def decode_ucid_from_post_protobuf(params) + decoded_protobuf = params.try { |i| URI.decode_www_form(i) } + .try { |i| Base64.decode(i) } + .try { |i| IO::Memory.new(i) } + .try { |i| Protodec::Any.parse(i) } + + return decoded_protobuf.try(&.["56:0:embedded"]["2:0:string"].as_s) +end + def fetch_channel_community_post(ucid, post_id, locale, format, thin_mode) object = { - "2:string" => "community", - "25:embedded" => { - "22:string" => post_id.to_s, - }, - "45:embedded" => { - "2:varint" => 1_i64, - "3:varint" => 1_i64, + "56:embedded" => { + "2:string" => ucid, + "3:string" => post_id.to_s, + "11:string" => ucid, }, } params = object.try { |i| Protodec::Any.cast_json(i) } @@ -40,7 +46,7 @@ def fetch_channel_community_post(ucid, post_id, locale, format, thin_mode) .try { |i| Base64.urlsafe_encode(i) } .try { |i| URI.encode_www_form(i) } - initial_data = YoutubeAPI.browse(ucid, params: params) + initial_data = YoutubeAPI.browse("FEpost_detail", params: params) items = [] of JSON::Any extract_items(initial_data) do |item| diff --git a/src/invidious/comments/youtube.cr b/src/invidious/comments/youtube.cr index 0716fcde..ca0bdc2b 100644 --- a/src/invidious/comments/youtube.cr +++ b/src/invidious/comments/youtube.cr @@ -16,34 +16,40 @@ module Invidious::Comments return parse_youtube(id, response, format, locale, thin_mode, sort_by) end - def fetch_community_post_comments(ucid, post_id) + def fetch_community_post_comments(ucid, post_id, sort_by = "top") object = { - "2:string" => "community", - "25:embedded" => { - "22:string" => post_id, - }, - "45:embedded" => { - "2:varint" => 1_i64, - "3:varint" => 1_i64, - }, + "2:string" => "posts", "53:embedded" => { "4:embedded" => { "6:varint" => 0_i64, - "27:varint" => 1_i64, + "15:varint" => 2_i64, + "25:varint" => 0_i64, "29:string" => post_id, "30:string" => ucid, }, + "7:varint" => 0_i64, "8:string" => "comments-section", }, } + case sort_by + when "top" + sort_by_val = 0_i64 + when "new", "newest" + sort_by_val = 1_i64 + else # top + sort_by_val = 0_i64 + end + + object["53:embedded"].as(Hash)["4:embedded"].as(Hash)["6:varint"] = sort_by_val + object_parsed = object.try { |i| Protodec::Any.cast_json(i) } .try { |i| Protodec::Any.from_json(i) } .try { |i| Base64.urlsafe_encode(i) } object2 = { "80226972:embedded" => { - "2:string" => ucid, + "2:string" => "FEcomment_post_detail_page_web_top_level", "3:string" => object_parsed, }, } diff --git a/src/invidious/routes/api/v1/channels.cr b/src/invidious/routes/api/v1/channels.cr index a940ee68..503b8c05 100644 --- a/src/invidious/routes/api/v1/channels.cr +++ b/src/invidious/routes/api/v1/channels.cr @@ -436,7 +436,7 @@ module Invidious::Routes::API::V1::Channels if ucid.nil? response = YoutubeAPI.resolve_url("https://www.youtube.com/post/#{id}") return error_json(400, "Invalid post ID") if response["error"]? - ucid = response.dig("endpoint", "browseEndpoint", "browseId").as_s + ucid = decode_ucid_from_post_protobuf(response.dig("endpoint", "browseEndpoint", "params").as_s) else ucid = ucid.to_s end @@ -460,13 +460,15 @@ module Invidious::Routes::API::V1::Channels format = env.params.query["format"]? format ||= "json" + sort_by = env.params.query["sort_by"]?.try &.downcase + sort_by ||= "top" continuation = env.params.query["continuation"]? case continuation when nil, "" ucid = env.params.query["ucid"] - comments = Comments.fetch_community_post_comments(ucid, id) + comments = Comments.fetch_community_post_comments(ucid, id, sort_by: sort_by) else comments = YoutubeAPI.browse(continuation: continuation) end diff --git a/src/invidious/routes/api/v1/misc.cr b/src/invidious/routes/api/v1/misc.cr index 4f5b58da..4ae877a8 100644 --- a/src/invidious/routes/api/v1/misc.cr +++ b/src/invidious/routes/api/v1/misc.cr @@ -190,15 +190,30 @@ module Invidious::Routes::API::V1::Misc sub_endpoint = endpoint["watchEndpoint"]? || endpoint["browseEndpoint"]? || endpoint params = sub_endpoint.try &.dig?("params") + + if sub_endpoint["browseId"]?.try &.as_s == "FEpost_detail" + decoded_protobuf = params.try &.as_s.try { |i| URI.decode_www_form(i) } + .try { |i| Base64.decode(i) } + .try { |i| IO::Memory.new(i) } + .try { |i| Protodec::Any.parse(i) } + + ucid = decoded_protobuf.try(&.["56:0:embedded"]["2:0:string"].as_s) + post_id = decoded_protobuf.try(&.["56:0:embedded"]["3:1:string"].as_s) + else + ucid = sub_endpoint["browseId"]? if sub_endpoint["browseId"]? && sub_endpoint["browseId"]?.try &.as_s.starts_with? "UC" + post_id = nil + end rescue ex return error_json(500, ex) end JSON.build do |json| json.object do - json.field "ucid", sub_endpoint["browseId"].as_s if sub_endpoint["browseId"]? + json.field "browseId", sub_endpoint["browseId"].as_s if sub_endpoint["browseId"]? + json.field "ucid", ucid if ucid != nil json.field "videoId", sub_endpoint["videoId"].as_s if sub_endpoint["videoId"]? json.field "playlistId", sub_endpoint["playlistId"].as_s if sub_endpoint["playlistId"]? json.field "startTimeSeconds", sub_endpoint["startTimeSeconds"].as_i if sub_endpoint["startTimeSeconds"]? + json.field "postId", post_id if post_id != nil json.field "params", params.try &.as_s json.field "pageType", page_type end diff --git a/src/invidious/routes/channels.cr b/src/invidious/routes/channels.cr index 508aa3e4..6d2b4465 100644 --- a/src/invidious/routes/channels.cr +++ b/src/invidious/routes/channels.cr @@ -284,7 +284,7 @@ module Invidious::Routes::Channels response = YoutubeAPI.resolve_url("https://www.youtube.com/post/#{id}") return error_template(400, "Invalid post ID") if response["error"]? - ucid = response.dig("endpoint", "browseEndpoint", "browseId").as_s + ucid = decode_ucid_from_post_protobuf(response.dig("endpoint", "browseEndpoint", "params").as_s) post_response = fetch_channel_community_post(ucid, id, locale, "json", thin_mode) end