diff options
| author | Omar Roth <omarroth@protonmail.com> | 2019-10-27 13:50:42 -0400 |
|---|---|---|
| committer | Omar Roth <omarroth@protonmail.com> | 2019-10-27 13:50:42 -0400 |
| commit | 2ebfaf76f29f52293611e966ea1a82736ae7ea93 (patch) | |
| tree | 0d81b68117a52fb885b282fcdb91f8dadef5a340 /src | |
| parent | 0cf187dee73ede397c1ef67a61133d9736738fd5 (diff) | |
| download | invidious-2ebfaf76f29f52293611e966ea1a82736ae7ea93.tar.gz invidious-2ebfaf76f29f52293611e966ea1a82736ae7ea93.tar.bz2 invidious-2ebfaf76f29f52293611e966ea1a82736ae7ea93.zip | |
Refactor continuation token handling
Diffstat (limited to 'src')
| -rw-r--r-- | src/invidious.cr | 1 | ||||
| -rw-r--r-- | src/invidious/channels.cr | 270 | ||||
| -rw-r--r-- | src/invidious/comments.cr | 175 | ||||
| -rw-r--r-- | src/invidious/playlists.cr | 64 | ||||
| -rw-r--r-- | src/invidious/search.cr | 164 |
5 files changed, 235 insertions, 439 deletions
diff --git a/src/invidious.cr b/src/invidious.cr index 58bc35f8..b213eb87 100644 --- a/src/invidious.cr +++ b/src/invidious.cr @@ -24,6 +24,7 @@ require "sqlite3" require "xml" require "yaml" require "zip" +require "protodec/utils" require "./invidious/helpers/*" require "./invidious/*" diff --git a/src/invidious/channels.cr b/src/invidious/channels.cr index c79be352..4cf322b0 100644 --- a/src/invidious/channels.cr +++ b/src/invidious/channels.cr @@ -432,188 +432,108 @@ def fetch_channel_playlists(ucid, author, auto_generated, continuation, sort_by) end def produce_channel_videos_url(ucid, page = 1, auto_generated = nil, sort_by = "newest") + object = { + "80226972:embedded" => { + "2:string" => ucid, + "3:base64" => { + "2:string" => "videos", + "6:varint": 2_i64, + "7:varint": 1_i64, + "12:varint": 1_i64, + "13:string": "", + "23:varint": 0_i64, + }, + }, + } + if auto_generated seed = Time.unix(1525757349) - until seed >= Time.utc seed += 1.month end timestamp = seed - (page - 1).months - page = "#{timestamp.to_unix}" - switch = 0x36 + object["80226972:embedded"]["3:base64"].as(Hash)["4:varint"] = 0x36_i64 + object["80226972:embedded"]["3:base64"].as(Hash)["15:string"] = "#{timestamp.to_unix}" else - page = "#{page}" - switch = 0x00 + object["80226972:embedded"]["3:base64"].as(Hash)["4:varint"] = 0_i64 + object["80226972:embedded"]["3:base64"].as(Hash)["15:string"] = "#{page}" end - data = IO::Memory.new - data.write_byte 0x12 - data.write_byte 0x06 - data.print "videos" - - data.write Bytes[0x30, 0x02] - data.write Bytes[0x38, 0x01] - data.write Bytes[0x60, 0x01] - data.write Bytes[0x6a, 0x00] - data.write Bytes[0xb8, 0x01, 0x00] - - data.write Bytes[0x20, switch] - data.write_byte 0x7a - VarInt.to_io(data, page.bytesize) - data.print page - case sort_by when "newest" - # Empty tags can be omitted - # data.write(Bytes[0x18,0x00]) when "popular" - data.write Bytes[0x18, 0x01] + object["80226972:embedded"]["3:base64"].as(Hash)["3:varint"] = 0x01_i64 when "oldest" - data.write Bytes[0x18, 0x02] + object["80226972:embedded"]["3:base64"].as(Hash)["3:varint"] = 0x02_i64 end - data = Base64.urlsafe_encode(data) - cursor = URI.encode_www_form(data) - - data = IO::Memory.new - - data.write_byte 0x12 - VarInt.to_io(data, ucid.bytesize) - data.print ucid + object["80226972:embedded"]["3:string"] = Base64.urlsafe_encode(Protodec::Any.from_json(Protodec::Any.cast_json(object["80226972:embedded"]["3:base64"]))) + object["80226972:embedded"].delete("3:base64") - data.write_byte 0x1a - VarInt.to_io(data, cursor.bytesize) - data.print cursor + continuation = object.try { |i| Protodec::Any.cast_json(object) } + .try { |i| Protodec::Any.from_json(i) } + .try { |i| Base64.urlsafe_encode(i) } + .try { |i| URI.encode_www_form(i) } - data.rewind - - buffer = IO::Memory.new - buffer.write Bytes[0xe2, 0xa9, 0x85, 0xb2, 0x02] - VarInt.to_io(buffer, data.bytesize) - - IO.copy data, buffer - - continuation = Base64.urlsafe_encode(buffer) - continuation = URI.encode_www_form(continuation) - - url = "/browse_ajax?continuation=#{continuation}&gl=US&hl=en" - - return url + return "/browse_ajax?continuation=#{continuation}&gl=US&hl=en" end def produce_channel_playlists_url(ucid, cursor, sort = "newest", auto_generated = false) + object = { + "80226972:embedded" => { + "2:string" => ucid, + "3:base64" => { + "2:string" => "playlist", + "6:varint": 2_i64, + "7:varint": 1_i64, + "12:varint": 1_i64, + "13:string": "", + "23:varint": 0_i64, + }, + }, + } + if !auto_generated cursor = Base64.urlsafe_encode(cursor, false) end - - data = IO::Memory.new + object["80226972:embedded"]["3:base64"].as(Hash)["15:string"] = cursor if auto_generated - data.write Bytes[0x08, 0x0a] - end - - data.write Bytes[0x12, 0x09] - data.print "playlists" - - if auto_generated - data.write Bytes[0x20, 0x32] + object["80226972:embedded"]["3:base64"].as(Hash)["4:varint"] = 0x32_i64 else - # TODO: Look at 0x01, 0x00 + object["80226972:embedded"]["3:base64"].as(Hash)["4:varint"] = 1_i64 case sort when "oldest", "oldest_created" - data.write Bytes[0x18, 0x02] + object["80226972:embedded"]["3:base64"].as(Hash)["3:varint"] = 2_i64 when "newest", "newest_created" - data.write Bytes[0x18, 0x03] + object["80226972:embedded"]["3:base64"].as(Hash)["3:varint"] = 3_i64 when "last", "last_added" - data.write Bytes[0x18, 0x04] + object["80226972:embedded"]["3:base64"].as(Hash)["3:varint"] = 4_i64 end - - data.write Bytes[0x20, 0x01] end - data.write Bytes[0x30, 0x02] - data.write Bytes[0x38, 0x01] - data.write Bytes[0x60, 0x01] - data.write Bytes[0x6a, 0x00] - - data.write_byte 0x7a - VarInt.to_io(data, cursor.bytesize) - data.print cursor - - data.write Bytes[0xb8, 0x01, 0x00] - - data.rewind - data = Base64.urlsafe_encode(data) - continuation = URI.encode_www_form(data) - - data = IO::Memory.new - - data.write_byte 0x12 - VarInt.to_io(data, ucid.bytesize) - data.print ucid - - data.write_byte 0x1a - VarInt.to_io(data, continuation.bytesize) - data.print continuation - - data.rewind - - buffer = IO::Memory.new - buffer.write Bytes[0xe2, 0xa9, 0x85, 0xb2, 0x02] - VarInt.to_io(buffer, data.bytesize) + object["80226972:embedded"]["3:string"] = Base64.urlsafe_encode(Protodec::Any.from_json(Protodec::Any.cast_json(object["80226972:embedded"]["3:base64"]))) + object["80226972:embedded"].delete("3:base64") - IO.copy data, buffer + continuation = object.try { |i| Protodec::Any.cast_json(object) } + .try { |i| Protodec::Any.from_json(i) } + .try { |i| Base64.urlsafe_encode(i) } + .try { |i| URI.encode_www_form(i) } - continuation = Base64.urlsafe_encode(buffer) - continuation = URI.encode_www_form(continuation) - - url = "/browse_ajax?continuation=#{continuation}&gl=US&hl=en" - - return url + return "/browse_ajax?continuation=#{continuation}&gl=US&hl=en" end def extract_channel_playlists_cursor(url, auto_generated) - continuation = HTTP::Params.parse(URI.parse(url).query.not_nil!)["continuation"] - - continuation = URI.decode_www_form(continuation) - data = IO::Memory.new(Base64.decode(continuation)) - - # 0xe2 0xa9 0x85 0xb2 0x02 - data.pos += 5 - - continuation = Bytes.new(data.read_bytes(VarInt)) - data.read continuation - data = IO::Memory.new(continuation) - - data.read_byte # => 0x12 - ucid = Bytes.new(data.read_bytes(VarInt)) - data.read ucid - - data.read_byte # => 0x1a - inner_continuation = Bytes.new(data.read_bytes(VarInt)) - data.read inner_continuation - - continuation = String.new(inner_continuation) - continuation = URI.decode_www_form(continuation) - data = IO::Memory.new(Base64.decode(continuation)) - - # 0x12 0x09 playlists - data.pos += 11 - - until data.peek[0] == 0x7a - key = data.read_bytes(VarInt) - value = data.read_bytes(VarInt) - end - - data.pos += 1 # => 0x7a - cursor = Bytes.new(data.read_bytes(VarInt)) - data.read cursor - cursor = String.new(cursor) + cursor = URI.parse(url).query_params + .try { |i| Base64.decode(i["continuation"]) } + .try { |i| IO::Memory.new(i) } + .try { |i| Protodec::Any.parse(i) } + .try { |i| i["80226972:0:embedded"]["3:1:base64"]["15:7:string"].as_s } if !auto_generated cursor = URI.decode_www_form(cursor) - cursor = Base64.decode_string(cursor) + .try { |i| Base64.decode_string(i) } end return cursor @@ -621,12 +541,9 @@ end # TODO: Add "sort_by" def fetch_channel_community(ucid, continuation, locale, config, kemal_config, format, thin_mode) - headers = HTTP::Headers.new - headers["User-Agent"] = "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36" - - response = YT_POOL.client &.get("/channel/#{ucid}/community?gl=US&hl=en", headers) + response = YT_POOL.client &.get("/channel/#{ucid}/community?gl=US&hl=en") if response.status_code == 404 - response = YT_POOL.client &.get("/user/#{ucid}/community?gl=US&hl=en", headers) + response = YT_POOL.client &.get("/user/#{ucid}/community?gl=US&hl=en") end if response.status_code == 404 @@ -648,6 +565,7 @@ def fetch_channel_community(ucid, continuation, locale, config, kemal_config, fo else continuation = produce_channel_community_continuation(ucid, continuation) + headers = HTTP::Headers.new headers["cookie"] = response.cookies.add_request_headers(headers)["cookie"] headers["content-type"] = "application/x-www-form-urlencoded" @@ -874,53 +792,31 @@ def fetch_channel_community(ucid, continuation, locale, config, kemal_config, fo end def produce_channel_community_continuation(ucid, cursor) - cursor = URI.encode_www_form(cursor) - - data = IO::Memory.new - - data.write_byte 0x12 - VarInt.to_io(data, ucid.bytesize) - data.print ucid - - data.write_byte 0x1a - VarInt.to_io(data, cursor.bytesize) - data.print cursor - - data.rewind - - buffer = IO::Memory.new - buffer.write Bytes[0xe2, 0xa9, 0x85, 0xb2, 0x02] - VarInt.to_io(buffer, data.size) - - IO.copy data, buffer - - continuation = Base64.urlsafe_encode(buffer) - continuation = URI.encode_www_form(continuation) + object = { + "80226972:embedded" => { + "2:string" => ucid, + "3:string" => cursor, + }, + } + + continuation = object.try { |i| Protodec::Any.cast_json(object) } + .try { |i| Protodec::Any.from_json(i) } + .try { |i| Base64.urlsafe_encode(i) } + .try { |i| URI.encode_www_form(i) } return continuation end def extract_channel_community_cursor(continuation) - continuation = URI.decode_www_form(continuation) - data = IO::Memory.new(Base64.decode(continuation)) - - # 0xe2 0xa9 0x85 0xb2 0x02 - data.pos += 5 - - continuation = Bytes.new(data.read_bytes(VarInt)) - data.read continuation - data = IO::Memory.new(continuation) - - data.read_byte # => 0x12 - ucid = Bytes.new(data.read_bytes(VarInt)) - data.read ucid - - data.read_byte # => 0x1a - until data.peek[0] == 'E'.ord - data.read_byte - end - - return URI.decode_www_form(data.gets_to_end) + cursor = URI.decode_www_form(continuation) + .try { |i| Base64.decode(i) } + .try { |i| IO::Memory.new(i) } + .try { |i| Protodec::Any.parse(i) } + .try { |i| Protodec::Any.cast_json(i["80226972:0:embedded"]["3:1:base64"].as_h) } + .try { |i| Protodec::Any.from_json(i) } + .try { |i| Base64.urlsafe_encode(i) } + + cursor end def get_about_info(ucid, locale) diff --git a/src/invidious/comments.cr b/src/invidious/comments.cr index d4fc2c6f..2d7bc1cf 100644 --- a/src/invidious/comments.cr +++ b/src/invidious/comments.cr @@ -572,129 +572,84 @@ def content_to_comment_html(content) end def extract_comment_cursor(continuation) - continuation = URI.decode_www_form(continuation) - data = IO::Memory.new(Base64.decode(continuation)) + cursor = URI.decode_www_form(continuation) + .try { |i| Base64.decode(i) } + .try { |i| IO::Memory.new(i) } + .try { |i| Protodec::Any.parse(i) } + .try { |i| i["6:2:embedded"]["1:0:string"].as_s } - # 0x12 0x26 - data.pos += 2 - - data.read_byte # => 0x12 - video_id = Bytes.new(data.read_bytes(VarInt)) - data.read video_id - - until data.peek[0] == 0x0a - data.read_byte - end - data.read_byte # 0x0a - data.read_byte if data.peek[0] == 0x0a - - cursor = Bytes.new(data.read_bytes(VarInt)) - data.read cursor - - String.new(cursor) + return cursor end def produce_comment_continuation(video_id, cursor = "", sort_by = "top") - data = IO::Memory.new - - data.write Bytes[0x12, 0x26] - - data.write_byte 0x12 - VarInt.to_io(data, video_id.bytesize) - data.print video_id - - data.write Bytes[0xc0, 0x01, 0x01] - data.write Bytes[0xc8, 0x01, 0x01] - data.write Bytes[0xe0, 0x01, 0x01] - - data.write Bytes[0xa2, 0x02, 0x0d] - data.write Bytes[0x28, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01] - - data.write Bytes[0x40, 0x00] - data.write Bytes[0x18, 0x06] - - if cursor.empty? - data.write Bytes[0x32] - VarInt.to_io(data, cursor.bytesize + video_id.bytesize + 8) - - data.write Bytes[0x22, video_id.bytesize + 4] - data.write Bytes[0x22, video_id.bytesize] - data.print video_id - - case sort_by - when "top" - data.write Bytes[0x30, 0x00] - when "new", "newest" - data.write Bytes[0x30, 0x01] - end - - data.write(Bytes[0x78, 0x02]) - else - data.write Bytes[0x32] - VarInt.to_io(data, cursor.bytesize + video_id.bytesize + 11) - - data.write_byte 0x0a - VarInt.to_io(data, cursor.bytesize) - data.print cursor - - data.write Bytes[0x22, video_id.bytesize + 4] - data.write Bytes[0x22, video_id.bytesize] - data.print video_id - - case sort_by - when "top" - data.write Bytes[0x30, 0x00] - when "new", "newest" - data.write Bytes[0x30, 0x01] - end + object = { + "2:embedded" => { + "2:string" => video_id, + "24:varint" => 1_i64, + "25:varint" => 1_i64, + "28:varint" => 1_i64, + "36:embedded" => { + "5:varint" => -1_i64, + "8:varint" => 0_i64, + }, + }, + "3:varint" => 6_i64, + "6:embedded" => { + "1:string" => cursor, + "4:embedded" => { + "4:string" => video_id, + "6:varint" => 0_i64, + }, + "5:varint" => 20_i64, + }, + } - data.write Bytes[0x28, 0x14] + 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 end - continuation = Base64.urlsafe_encode(data) - continuation = URI.encode_www_form(continuation) + continuation = object.try { |i| Protodec::Any.cast_json(object) } + .try { |i| Protodec::Any.from_json(i) } + .try { |i| Base64.urlsafe_encode(i) } + .try { |i| URI.encode_www_form(i) } return continuation end def produce_comment_reply_continuation(video_id, ucid, comment_id) - data = IO::Memory.new - - data.write Bytes[0x12, 0x26] - - data.write_byte 0x12 - VarInt.to_io(data, video_id.size) - data.print video_id - - data.write Bytes[0xc0, 0x01, 0x01] - data.write Bytes[0xc8, 0x01, 0x01] - data.write Bytes[0xe0, 0x01, 0x01] - - data.write Bytes[0xa2, 0x02, 0x0d] - data.write Bytes[0x28, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01] - - data.write Bytes[0x40, 0x00] - data.write Bytes[0x18, 0x06] - - data.write(Bytes[0x32, ucid.size + video_id.size + comment_id.size + 16]) - data.write(Bytes[0x1a, ucid.size + video_id.size + comment_id.size + 14]) - - data.write_byte 0x12 - VarInt.to_io(data, comment_id.size) - data.print comment_id - - data.write(Bytes[0x22, 0x02, 0x08, 0x00]) # ? - - data.write(Bytes[ucid.size + video_id.size + 7]) - data.write(Bytes[ucid.size]) - data.print(ucid) - data.write(Bytes[0x32, video_id.size]) - data.print(video_id) - data.write(Bytes[0x40, 0x01]) - data.write(Bytes[0x48, 0x0a]) + object = { + "2:embedded" => { + "2:string" => video_id, + "24:varint" => 1_i64, + "25:varint" => 1_i64, + "28:varint" => 1_i64, + "36:embedded" => { + "5:varint" => -1_i64, + "8:varint" => 0_i64, + }, + }, + "3:varint" => 6_i64, + "6:embedded" => { + "3:embedded" => { + "2:string" => comment_id, + "4:embedded" => { + "1:varint" => 0_i64, + }, + "5:string" => ucid, + "6:string" => video_id, + "8:varint" => 1_i64, + "9:varint" => 10_i64, + }, + }, + } - continuation = Base64.urlsafe_encode(data.to_slice) - continuation = URI.encode_www_form(continuation) + continuation = object.try { |i| Protodec::Any.cast_json(object) } + .try { |i| Protodec::Any.from_json(i) } + .try { |i| Base64.urlsafe_encode(i) } + .try { |i| URI.encode_www_form(i) } return continuation end diff --git a/src/invidious/playlists.cr b/src/invidious/playlists.cr index 8d071acc..9c8afd3c 100644 --- a/src/invidious/playlists.cr +++ b/src/invidious/playlists.cr @@ -327,48 +327,28 @@ def produce_playlist_url(id, index) if id.starts_with? "UC" id = "UU" + id.lchop("UC") end - ucid = "VL" + id - - data = IO::Memory.new - data.write_byte 0x08 - VarInt.to_io(data, index) - - data.rewind - data = Base64.urlsafe_encode(data, false) - data = "PT:#{data}" - - continuation = IO::Memory.new - continuation.write_byte 0x7a - VarInt.to_io(continuation, data.bytesize) - continuation.print data - - data = Base64.urlsafe_encode(continuation) - cursor = URI.encode_www_form(data) - - data = IO::Memory.new - - data.write_byte 0x12 - VarInt.to_io(data, ucid.bytesize) - data.print ucid - - data.write_byte 0x1a - VarInt.to_io(data, cursor.bytesize) - data.print cursor - - data.rewind - - buffer = IO::Memory.new - buffer.write Bytes[0xe2, 0xa9, 0x85, 0xb2, 0x02] - VarInt.to_io(buffer, data.bytesize) - - IO.copy data, buffer - - continuation = Base64.urlsafe_encode(buffer) - continuation = URI.encode_www_form(continuation) - - url = "/browse_ajax?continuation=#{continuation}&gl=US&hl=en" - - return url + plid = "VL" + id + + data = {"1:varint" => index.to_i64} + .try { |i| Protodec::Any.cast_json(i) } + .try { |i| Protodec::Any.from_json(i) } + .try { |i| Base64.urlsafe_encode(i, padding: false) } + + object = { + "80226972:embedded" => { + "2:string" => plid, + "3:base64" => { + "15:string" => "PT:#{data}", + }, + }, + } + + continuation = object.try { |i| Protodec::Any.cast_json(object) } + .try { |i| Protodec::Any.from_json(i) } + .try { |i| Base64.urlsafe_encode(i) } + .try { |i| URI.encode_www_form(i) } + + return "/browse_ajax?continuation=#{continuation}&gl=US&hl=en" end def get_playlist(db, plid, locale, refresh = true, force_refresh = false) diff --git a/src/invidious/search.cr b/src/invidious/search.cr index 2acb4057..cc9c9634 100644 --- a/src/invidious/search.cr +++ b/src/invidious/search.cr @@ -281,152 +281,116 @@ end def produce_search_params(sort : String = "relevance", date : String = "", content_type : String = "", duration : String = "", features : Array(String) = [] of String) - header = IO::Memory.new - header.write Bytes[0x08] - header.write case sort + object = { + "1:varint" => 0_i64, + "2:embedded" => {} of String => Int64, + } + + case sort when "relevance" - Bytes[0x00] + object["1:varint"] = 0_i64 when "rating" - Bytes[0x01] + object["1:varint"] = 1_i64 when "upload_date", "date" - Bytes[0x02] + object["1:varint"] = 2_i64 when "view_count", "views" - Bytes[0x03] + object["1:varint"] = 3_i64 else raise "No sort #{sort}" end - body = IO::Memory.new - body.write case date + case date when "hour" - Bytes[0x08, 0x01] + object["2:embedded"].as(Hash)["1:varint"] = 1_i64 when "today" - Bytes[0x08, 0x02] + object["2:embedded"].as(Hash)["1:varint"] = 2_i64 when "week" - Bytes[0x08, 0x03] + object["2:embedded"].as(Hash)["1:varint"] = 3_i64 when "month" - Bytes[0x08, 0x04] + object["2:embedded"].as(Hash)["1:varint"] = 4_i64 when "year" - Bytes[0x08, 0x05] - else - Bytes.new(0) + object["2:embedded"].as(Hash)["1:varint"] = 5_i64 end - body.write case content_type + case content_type when "video" - Bytes[0x10, 0x01] + object["2:embedded"].as(Hash)["2:varint"] = 1_i64 when "channel" - Bytes[0x10, 0x02] + object["2:embedded"].as(Hash)["2:varint"] = 2_i64 when "playlist" - Bytes[0x10, 0x03] + object["2:embedded"].as(Hash)["2:varint"] = 3_i64 when "movie" - Bytes[0x10, 0x04] + object["2:embedded"].as(Hash)["2:varint"] = 4_i64 when "show" - Bytes[0x10, 0x05] + object["2:embedded"].as(Hash)["2:varint"] = 5_i64 when "all" - Bytes.new(0) + # else - Bytes[0x10, 0x01] + object["2:embedded"].as(Hash)["2:varint"] = 1_i64 end - body.write case duration + case duration when "short" - Bytes[0x18, 0x01] + object["2:embedded"].as(Hash)["3:varint"] = 1_i64 when "long" - Bytes[0x18, 0x12] - else - Bytes.new(0) + object["2:embedded"].as(Hash)["3:varint"] = 18_i64 end features.each do |feature| - body.write case feature + case feature when "hd" - Bytes[0x20, 0x01] + object["2:embedded"].as(Hash)["4:varint"] = 1_i64 when "subtitles" - Bytes[0x28, 0x01] + object["2:embedded"].as(Hash)["5:varint"] = 1_i64 when "creative_commons", "cc" - Bytes[0x30, 0x01] + object["2:embedded"].as(Hash)["6:varint"] = 1_i64 when "3d" - Bytes[0x38, 0x01] + object["2:embedded"].as(Hash)["7:varint"] = 1_i64 when "live", "livestream" - Bytes[0x40, 0x01] + object["2:embedded"].as(Hash)["8:varint"] = 1_i64 when "purchased" - Bytes[0x48, 0x01] + object["2:embedded"].as(Hash)["9:varint"] = 1_i64 when "4k" - Bytes[0x70, 0x01] + object["2:embedded"].as(Hash)["14:varint"] = 1_i64 when "360" - Bytes[0x78, 0x01] + object["2:embedded"].as(Hash)["15:varint"] = 1_i64 when "location" - Bytes[0xb8, 0x01, 0x01] + object["2:embedded"].as(Hash)["23:varint"] = 1_i64 when "hdr" - Bytes[0xc8, 0x01, 0x01] - else - Bytes.new(0) + object["2:embedded"].as(Hash)["25:varint"] = 1_i64 end end - token = header - if !body.empty? - token.write Bytes[0x12, body.bytesize] - token.write body.to_slice - end - - token = Base64.urlsafe_encode(token.to_slice) - token = URI.encode_www_form(token) + params = object.try { |i| Protodec::Any.cast_json(object) } + .try { |i| Protodec::Any.from_json(i) } + .try { |i| Base64.urlsafe_encode(i, padding: false) } - return token + return params end def produce_channel_search_url(ucid, query, page) - page = "#{page}" - - data = IO::Memory.new - data.write_byte 0x12 - data.write_byte 0x06 - data.print "search" - - data.write Bytes[0x30, 0x02] - data.write Bytes[0x38, 0x01] - data.write Bytes[0x60, 0x01] - data.write Bytes[0x6a, 0x00] - data.write Bytes[0xb8, 0x01, 0x00] - - data.write_byte 0x7a - VarInt.to_io(data, page.bytesize) - data.print page - - data.rewind - data = Base64.urlsafe_encode(data) - continuation = URI.encode_www_form(data) - - data = IO::Memory.new - - data.write_byte 0x12 - VarInt.to_io(data, ucid.bytesize) - data.print ucid - - data.write_byte 0x1a - VarInt.to_io(data, continuation.bytesize) - data.print continuation - - data.write_byte 0x5a - VarInt.to_io(data, query.bytesize) - data.print query - - data.rewind - - buffer = IO::Memory.new - buffer.write Bytes[0xe2, 0xa9, 0x85, 0xb2, 0x02] - VarInt.to_io(buffer, data.bytesize) - - IO.copy data, buffer - - continuation = Base64.urlsafe_encode(buffer) - continuation = URI.encode_www_form(continuation) - - url = "/browse_ajax?continuation=#{continuation}&gl=US&hl=en" - - return url + object = { + "80226972:embedded" => { + "2:string" => ucid, + "3:base64" => { + "2:string" => "search", + "6:varint" => 2_i64, + "7:varint" => 1_i64, + "12:varint" => 1_i64, + "13:string" => "", + "23:varint" => 0_i64, + "15:string" => "#{page}", + }, + "11:string" => query, + }, + } + + continuation = object.try { |i| Protodec::Any.cast_json(object) } + .try { |i| Protodec::Any.from_json(i) } + .try { |i| Base64.urlsafe_encode(i) } + .try { |i| URI.encode_www_form(i) } + + return "/browse_ajax?continuation=#{continuation}&gl=US&hl=en" end def process_search_query(query, page, user, region) |
