Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 16 additions & 10 deletions src/invidious/channels/community.cr
Original file line number Diff line number Diff line change
Expand Up @@ -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|
Expand All @@ -24,23 +24,29 @@ 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) }
.try { |i| Protodec::Any.from_json(i) }
.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|
Expand Down
48 changes: 26 additions & 22 deletions src/invidious/comments/youtube.cr
Original file line number Diff line number Diff line change
Expand Up @@ -16,23 +16,27 @@ 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")
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 = {
"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,
"6:varint" => sort_by_val,
"15:varint" => 2_i64,
"25:varint" => 0_i64,
"29:string" => post_id,
"30:string" => ucid,
},
"7:varint" => 0_i64,
"8:string" => "comments-section",
},
}
Expand All @@ -43,7 +47,7 @@ module Invidious::Comments

object2 = {
"80226972:embedded" => {
"2:string" => ucid,
"2:string" => "FEcomment_post_detail_page_web_top_level",
"3:string" => object_parsed,
},
}
Expand Down Expand Up @@ -320,6 +324,15 @@ module Invidious::Comments
end

def produce_continuation(video_id, cursor = "", sort_by = "top")
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 = {
"2:embedded" => {
"2:string" => video_id,
Expand All @@ -340,21 +353,12 @@ module Invidious::Comments
"1:string" => cursor,
"4:embedded" => {
"4:string" => video_id,
"6:varint" => 0_i64,
"6:varint" => sort_by_val,
},
"5:varint" => 20_i64,
},
}

case sort_by
when "top"
object["6:embedded"].as(Hash)["4:embedded"].as(Hash)["6:varint"] = 0_i64
when "new", "newest"
object["6:embedded"].as(Hash)["4:embedded"].as(Hash)["6:varint"] = 1_i64
else # top
object["6:embedded"].as(Hash)["4:embedded"].as(Hash)["6:varint"] = 0_i64
end

continuation = object.try { |i| Protodec::Any.cast_json(i) }
.try { |i| Protodec::Any.from_json(i) }
.try { |i| Base64.urlsafe_encode(i) }
Expand Down
6 changes: 4 additions & 2 deletions src/invidious/routes/api/v1/channels.cr
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down
17 changes: 16 additions & 1 deletion src/invidious/routes/api/v1/misc.cr
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion src/invidious/routes/channels.cr
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
Loading