summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorOmar Roth <omarroth@hotmail.com>2019-04-03 18:42:12 -0500
committerOmar Roth <omarroth@hotmail.com>2019-04-03 18:42:12 -0500
commit5ef288b840b0308ada72f625b09d15565fe5b0d0 (patch)
treeff8f1ad751fa64424b560fe62158fdf99e27b66f
parentf6615a490d830b69e3ead47e188e175912d1775d (diff)
downloadinvidious-5ef288b840b0308ada72f625b09d15565fe5b0d0.tar.gz
invidious-5ef288b840b0308ada72f625b09d15565fe5b0d0.tar.bz2
invidious-5ef288b840b0308ada72f625b09d15565fe5b0d0.zip
Add 'sort_by' to /api/v1/comments
-rw-r--r--spec/helpers_spec.cr28
-rw-r--r--src/invidious.cr5
-rw-r--r--src/invidious/comments.cr121
-rw-r--r--src/invidious/videos.cr8
4 files changed, 143 insertions, 19 deletions
diff --git a/spec/helpers_spec.cr b/spec/helpers_spec.cr
index 6f33babd..307e52d2 100644
--- a/spec/helpers_spec.cr
+++ b/spec/helpers_spec.cr
@@ -4,8 +4,10 @@ require "spec"
require "yaml"
require "../src/invidious/helpers/*"
require "../src/invidious/channels"
+require "../src/invidious/comments"
require "../src/invidious/playlists"
require "../src/invidious/search"
+require "../src/invidious/users"
describe "Helpers" do
describe "#produce_channel_videos_url" do
@@ -16,9 +18,7 @@ describe "Helpers" do
produce_channel_videos_url(ucid: "UCXuqSBlHAE6Xw-yeJA0Tunw", page: 20).should eq("/browse_ajax?continuation=4qmFsgJEEhhVQ1h1cVNCbEhBRTZYdy15ZUpBMFR1bncaKEVnWjJhV1JsYjNNd0FqZ0JZQUZxQUxnQkFDQUFlZ0l5TUElM0QlM0Q%3D&gl=US&hl=en")
- produce_channel_videos_url(ucid: "UC-9-kyTW8ZkZNDHQJ6FgpwQ", auto_generated: true).should eq("/browse_ajax?continuation=4qmFsgJIEhhVQy05LWt5VFc4WmtaTkRIUUo2Rmdwd1EaLEVnWjJhV1JsYjNNd0FqZ0JZQUZxQUxnQkFDQTJlZ294TlRVeU1ESXlPVFE1&gl=US&hl=en")
-
- produce_channel_videos_url(ucid: "UC-9-kyTW8ZkZNDHQJ6FgpwQ", page: 20, auto_generated: true, sort_by: "popular").should eq("/browse_ajax?continuation=4qmFsgJOEhhVQy05LWt5VFc4WmtaTkRIUUo2Rmdwd1EaMkVnWjJhV1JsYjNNd0FqZ0JZQUZxQUxnQkFDQTJlZ294TlRBeU1UY3dNVFE1R0FFJTNE&gl=US&hl=en")
+ produce_channel_videos_url(ucid: "UC-9-kyTW8ZkZNDHQJ6FgpwQ", page: 20, sort_by: "popular").should eq("/browse_ajax?continuation=4qmFsgJAEhhVQy05LWt5VFc4WmtaTkRIUUo2Rmdwd1EaJEVnWjJhV1JsYjNNd0FqZ0JZQUZxQUxnQkFDQUFlZ0l5TUJnQg%3D%3D&gl=US&hl=en")
end
end
@@ -59,4 +59,26 @@ describe "Helpers" do
produce_search_params(content_type: "channel").should eq("CAASAhAC")
end
end
+
+ describe "#produce_comment_continuation" do
+ it "correctly produces a continuation token for comments" do
+ produce_comment_continuation("_cE8xSu6swE", "ADSJ_i2qvJeFtL0htmS5_K5Ctj3eGFVBMWL9Wd42o3kmUL6_mAzdLp85-liQZL0mYr_16BhaggUqX652Sv9JqV6VXinShSP-ZT6rL4NolPBaPXVtJsO5_rA_qE3GubAuLFw9uzIIXU2-HnpXbdgPLWTFavfX206hqWmmpHwUOrmxQV_OX6tYkM3ux3rPAKCDrT8eWL7MU3bLiNcnbgkW8o0h8KYLL_8BPa8LcHbTv8pAoNkjerlX1x7K4pqxaXPoyz89qNlnh6rRx6AXgAzzoHH1dmcyQ8CIBeOHg-m4i8ZxdX4dP88XWrIFg-jJGhpGP8JUMDgZgavxVx225hUEYZMyrLGler5em4FgbG62YWC51moLDLeYEA").should eq("EiYSC19jRTh4U3U2c3dFwAEByAEB4AEBogINKP___________wFAABgGMowDCvYCQURTSl9pMnF2SmVGdEwwaHRtUzVfSzVDdGozZUdGVkJNV0w5V2Q0Mm8za21VTDZfbUF6ZExwODUtbGlRWkwwbVlyXzE2QmhhZ2dVcVg2NTJTdjlKcVY2VlhpblNoU1AtWlQ2ckw0Tm9sUEJhUFhWdEpzTzVfckFfcUUzR3ViQXVMRnc5dXpJSVhVMi1IbnBYYmRnUExXVEZhdmZYMjA2aHFXbW1wSHdVT3JteFFWX09YNnRZa00zdXgzclBBS0NEclQ4ZVdMN01VM2JMaU5jbmJna1c4bzBoOEtZTExfOEJQYThMY0hiVHY4cEFvTmtqZXJsWDF4N0s0cHF4YVhQb3l6ODlxTmxuaDZyUng2QVhnQXp6b0hIMWRtY3lROENJQmVPSGctbTRpOFp4ZFg0ZFA4OFhXcklGZy1qSkdocEdQOEpVTURnWmdhdnhWeDIyNWhVRVlaTXlyTEdsZXI1ZW00RmdiRzYyWVdDNTFtb0xETGVZRUEiDyILX2NFOHhTdTZzd0UwACgU")
+
+ produce_comment_continuation("_cE8xSu6swE", "ADSJ_i1yz21HI4xrtsYXVC-2_kfZ6kx1yjYQumXAAxqH3CAd7ZxKxfLdZS1__fqhCtOASRbbpSBGH_tH1J96Dxux-Qfjk-lUbupMqv08Q3aHzGu7p70VoUMHhI2-GoJpnbpmcOxkGzeIuenRS_ym2Y8fkDowhqLPFgsS0n4djnZ2UmC17F3Ch3N1S1UYf1ZVOc991qOC1iW9kJDzyvRQTWCPsJUPneSaAKW-Rr97pdesOkR4i8cNvHZRnQKe2HEfsvlJOb2C3lF1dJBfJeNfnQYeh5hv6_fZN7bt3-JL1Xk3Qc9NXNxmmbDpwAC_yFR8dthFfUJdyIO9Nu1D79MLYeR-H5HxqUJokkJiGIz4lTE_CXXbhAI").should eq("EiYSC19jRTh4U3U2c3dFwAEByAEB4AEBogINKP___________wFAABgGMokDCvMCQURTSl9pMXl6MjFISTR4cnRzWVhWQy0yX2tmWjZreDF5allRdW1YQUF4cUgzQ0FkN1p4S3hmTGRaUzFfX2ZxaEN0T0FTUmJicFNCR0hfdEgxSjk2RHh1eC1RZmprLWxVYnVwTXF2MDhRM2FIekd1N3A3MFZvVU1IaEkyLUdvSnBuYnBtY094a0d6ZUl1ZW5SU195bTJZOGZrRG93aHFMUEZnc1MwbjRkam5aMlVtQzE3RjNDaDNOMVMxVVlmMVpWT2M5OTFxT0MxaVc5a0pEenl2UlFUV0NQc0pVUG5lU2FBS1ctUnI5N3BkZXNPa1I0aThjTnZIWlJuUUtlMkhFZnN2bEpPYjJDM2xGMWRKQmZKZU5mblFZZWg1aHY2X2ZaTjdidDMtSkwxWGszUWM5TlhOeG1tYkRwd0FDX3lGUjhkdGhGZlVKZHlJTzlOdTFENzlNTFllUi1INUh4cVVKb2trSmlHSXo0bFRFX0NYWGJoQUkiDyILX2NFOHhTdTZzd0UwACgU")
+
+ produce_comment_continuation("29-q7YnyUmY", "").should eq("EiYSCzI5LXE3WW55VW1ZwAEByAEB4AEBogINKP___________wFAABgGMhMiDyILMjktcTdZbnlVbVkwAHgC")
+
+ produce_comment_continuation("CvFH_6DNRCY", "").should eq("EiYSC0N2RkhfNkROUkNZwAEByAEB4AEBogINKP___________wFAABgGMhMiDyILQ3ZGSF82RE5SQ1kwAHgC")
+ end
+ end
+
+ describe "#produce_comment_reply_continuation" do
+ it "correctly produces a continuation token for replies to a given comment" do
+ produce_comment_reply_continuation("cIHQWOoJeag", "UCq6VFHwMzcMXbuKyG7SQYIg", "Ugx1IP_wGVv3WtGWcdV4AaABAg").should eq("EiYSC2NJSFFXT29KZWFnwAEByAEB4AEBogINKP___________wFAABgGMk0aSxIaVWd4MUlQX3dHVnYzV3RHV2NkVjRBYUFCQWciAggAKhhVQ3E2VkZId016Y01YYnVLeUc3U1FZSWcyC2NJSFFXT29KZWFnQAFICg%3D%3D")
+
+ produce_comment_reply_continuation("cIHQWOoJeag", "UCq6VFHwMzcMXbuKyG7SQYIg", "Ugza62y_TlmTu9o2RfF4AaABAg").should eq("EiYSC2NJSFFXT29KZWFnwAEByAEB4AEBogINKP___________wFAABgGMk0aSxIaVWd6YTYyeV9UbG1UdTlvMlJmRjRBYUFCQWciAggAKhhVQ3E2VkZId016Y01YYnVLeUc3U1FZSWcyC2NJSFFXT29KZWFnQAFICg%3D%3D")
+
+ produce_comment_reply_continuation("_cE8xSu6swE", "UC1AZY74-dGVPe6bfxFwwEMg", "UgyBUaRGHB9Jmt1dsUZ4AaABAg").should eq("EiYSC19jRTh4U3U2c3dFwAEByAEB4AEBogINKP___________wFAABgGMk0aSxIaVWd5QlVhUkdIQjlKbXQxZHNVWjRBYUFCQWciAggAKhhVQzFBWlk3NC1kR1ZQZTZiZnhGd3dFTWcyC19jRTh4U3U2c3dFQAFICg%3D%3D")
+ end
+ end
end
diff --git a/src/invidious.cr b/src/invidious.cr
index e97198d8..8494f9d9 100644
--- a/src/invidious.cr
+++ b/src/invidious.cr
@@ -2691,11 +2691,14 @@ get "/api/v1/comments/:id" do |env|
format = env.params.query["format"]?
format ||= "json"
+ sort_by = env.params.query["sort_by"]?.try &.downcase
+ sort_by ||= "top"
+
continuation = env.params.query["continuation"]?
if source == "youtube"
begin
- comments = fetch_youtube_comments(id, PG_DB, continuation, proxies, format, locale, thin_mode, region)
+ comments = fetch_youtube_comments(id, PG_DB, continuation, proxies, format, locale, thin_mode, region, sort_by: sort_by)
rescue ex
error_message = {"error" => ex.message}.to_json
env.response.status_code = 500
diff --git a/src/invidious/comments.cr b/src/invidious/comments.cr
index b5133cf2..aa3233d4 100644
--- a/src/invidious/comments.cr
+++ b/src/invidious/comments.cr
@@ -56,15 +56,14 @@ class RedditListing
})
end
-def fetch_youtube_comments(id, db, continuation, proxies, format, locale, thin_mode, region)
+def fetch_youtube_comments(id, db, continuation, proxies, format, locale, thin_mode, region, sort_by = "top")
video = get_video(id, db, proxies, region: region)
-
session_token = video.info["session_token"]?
- itct = video.info["itct"]?
- ctoken = video.info["ctoken"]?
+
+ ctoken = produce_comment_continuation(id, cursor: "", sort_by: sort_by)
continuation ||= ctoken
- if !continuation || !itct || !session_token
+ if !continuation || !session_token
if format == "json"
return {"comments" => [] of String}.to_json
else
@@ -73,7 +72,7 @@ def fetch_youtube_comments(id, db, continuation, proxies, format, locale, thin_m
end
post_req = {
- "session_token" => session_token.not_nil!,
+ "session_token" => session_token,
}
post_req = HTTP::Params.encode(post_req)
@@ -90,7 +89,7 @@ def fetch_youtube_comments(id, db, continuation, proxies, format, locale, thin_m
headers["x-youtube-client-name"] = "1"
headers["x-youtube-client-version"] = "2.20180719"
- response = client.post("/comment_service_ajax?action_get_comments=1&pbj=1&ctoken=#{continuation}&continuation=#{continuation}&itct=#{itct}&hl=en&gl=US", headers, post_req)
+ response = client.post("/comment_service_ajax?action_get_comments=1&ctoken=#{continuation}&continuation=#{continuation}&hl=en&gl=US", headers, post_req)
response = JSON.parse(response.body)
if !response["response"]["continuationContents"]?
@@ -516,3 +515,111 @@ def content_to_comment_html(content)
return comment_html
end
+
+def produce_comment_continuation(video_id, cursor = "", sort_by = "top")
+ continuation = IO::Memory.new
+
+ continuation.write(Bytes[0x12, 0x26])
+
+ continuation.write(Bytes[0x12, video_id.size])
+ continuation.print(video_id)
+
+ continuation.write(Bytes[0xc0, 0x01, 0x01])
+ continuation.write(Bytes[0xc8, 0x01, 0x01])
+ continuation.write(Bytes[0xe0, 0x01, 0x01])
+
+ continuation.write(Bytes[0xa2, 0x02, 0x0d])
+ continuation.write(Bytes[0x28, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01])
+
+ continuation.write(Bytes[0x40, 0x00])
+ continuation.write(Bytes[0x18, 0x06])
+
+ if cursor.empty?
+ continuation.write(Bytes[0x32])
+ continuation.write(write_var_int(video_id.size + 8))
+
+ continuation.write(Bytes[0x22, video_id.size + 4])
+ continuation.write(Bytes[0x22, video_id.size])
+ continuation.print(video_id)
+
+ case sort_by
+ when "top"
+ continuation.write(Bytes[0x30, 0x00])
+ when "new", "newest"
+ continuation.write(Bytes[0x30, 0x01])
+ end
+
+ continuation.write(Bytes[0x78, 0x02])
+ else
+ continuation.write(Bytes[0x32])
+ continuation.write(write_var_int(cursor.size + video_id.size + 11))
+
+ continuation.write(Bytes[0x0a])
+ continuation.write(write_var_int(cursor.size))
+ continuation.print(cursor)
+
+ continuation.write(Bytes[0x22, video_id.size + 4])
+ continuation.write(Bytes[0x22, video_id.size])
+ continuation.print(video_id)
+
+ case sort_by
+ when "top"
+ continuation.write(Bytes[0x30, 0x00])
+ when "new", "newest"
+ continuation.write(Bytes[0x30, 0x01])
+ end
+
+ continuation.write(Bytes[0x28, 0x14])
+ end
+
+ continuation.rewind
+ continuation = continuation.gets_to_end
+
+ continuation = Base64.urlsafe_encode(continuation.to_slice)
+ continuation = URI.escape(continuation)
+
+ return continuation
+end
+
+def produce_comment_reply_continuation(video_id, ucid, comment_id)
+ continuation = IO::Memory.new
+
+ continuation.write(Bytes[0x12, 0x26])
+
+ continuation.write(Bytes[0x12, video_id.size])
+ continuation.print(video_id)
+
+ continuation.write(Bytes[0xc0, 0x01, 0x01])
+ continuation.write(Bytes[0xc8, 0x01, 0x01])
+ continuation.write(Bytes[0xe0, 0x01, 0x01])
+
+ continuation.write(Bytes[0xa2, 0x02, 0x0d])
+ continuation.write(Bytes[0x28, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01])
+
+ continuation.write(Bytes[0x40, 0x00])
+ continuation.write(Bytes[0x18, 0x06])
+
+ continuation.write(Bytes[0x32, ucid.size + video_id.size + comment_id.size + 16])
+ continuation.write(Bytes[0x1a, ucid.size + video_id.size + comment_id.size + 14])
+
+ continuation.write(Bytes[0x12, comment_id.size])
+ continuation.print(comment_id)
+
+ continuation.write(Bytes[0x22, 0x02, 0x08, 0x00]) # ??
+
+ continuation.write(Bytes[ucid.size + video_id.size + 7])
+ continuation.write(Bytes[ucid.size])
+ continuation.print(ucid)
+ continuation.write(Bytes[0x32, video_id.size])
+ continuation.print(video_id)
+ continuation.write(Bytes[0x40, 0x01])
+ continuation.write(Bytes[0x48, 0x0a])
+
+ continuation.rewind
+ continuation = continuation.gets_to_end
+
+ continuation = Base64.urlsafe_encode(continuation.to_slice)
+ continuation = URI.escape(continuation)
+
+ return continuation
+end
diff --git a/src/invidious/videos.cr b/src/invidious/videos.cr
index 856b6447..3887277a 100644
--- a/src/invidious/videos.cr
+++ b/src/invidious/videos.cr
@@ -608,14 +608,6 @@ def extract_player_config(body, html)
params["session_token"] = md["session_token"]
end
- if md = body.match(/itct=(?<itct>[^"]+)"/)
- params["itct"] = md["itct"]
- end
-
- if md = body.match(/'COMMENTS_TOKEN': "(?<ctoken>[^"]+)"/)
- params["ctoken"] = md["ctoken"]
- end
-
if md = body.match(/'RELATED_PLAYER_ARGS': (?<rvs>{"rvs":"[^"]+"})/)
params["rvs"] = JSON.parse(md["rvs"])["rvs"].as_s
end