summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/invidious.cr65
-rw-r--r--src/invidious/videos.cr86
2 files changed, 148 insertions, 3 deletions
diff --git a/src/invidious.cr b/src/invidious.cr
index 83b389a7..f393e2d6 100644
--- a/src/invidious.cr
+++ b/src/invidious.cr
@@ -2973,6 +2973,9 @@ get "/api/v1/videos/:id" do |env|
json.field "videoThumbnails" do
generate_thumbnails(json, video.id, config, Kemal.config)
end
+ json.field "storyboards" do
+ generate_storyboards(json, video.storyboards, config, Kemal.config)
+ end
video.description, description = html_to_content(video.description)
@@ -4348,7 +4351,7 @@ get "/videoplayback" do |env|
url = "/videoplayback?#{query_params.to_s}"
headers = HTTP::Headers.new
- {"Accept", "Accept-Encoding", "Connection", "Range"}.each do |header|
+ {"Accept", "Accept-Encoding", "Cache-Control", "Connection", "If-None-Match", "Range"}.each do |header|
if env.request.headers[header]?
headers[header] = env.request.headers[header]
end
@@ -4445,7 +4448,63 @@ get "/ggpht/*" do |env|
url = env.request.path.lchop("/ggpht")
headers = HTTP::Headers.new
- {"Range", "Accept", "Accept-Encoding"}.each do |header|
+ {"Accept", "Accept-Encoding", "Cache-Control", "Connection", "If-None-Match", "Range"}.each do |header|
+ if env.request.headers[header]?
+ headers[header] = env.request.headers[header]
+ end
+ end
+
+ client.get(url, headers) do |response|
+ env.response.status_code = response.status_code
+ response.headers.each do |key, value|
+ env.response.headers[key] = value
+ end
+
+ if response.status_code == 304
+ break
+ end
+
+ chunk_size = 4096
+ size = 1
+ if response.headers.includes_word?("Content-Encoding", "gzip")
+ Gzip::Writer.open(env.response) do |deflate|
+ until size == 0
+ size = IO.copy(response.body_io, deflate)
+ env.response.flush
+ end
+ end
+ elsif response.headers.includes_word?("Content-Encoding", "deflate")
+ Flate::Writer.open(env.response) do |deflate|
+ until size == 0
+ size = IO.copy(response.body_io, deflate)
+ env.response.flush
+ end
+ end
+ else
+ until size == 0
+ size = IO.copy(response.body_io, env.response, chunk_size)
+ env.response.flush
+ end
+ end
+ end
+end
+
+get "/sb/:id/:storyboard/:index" do |env|
+ id = env.params.url["id"]
+ storyboard = env.params.url["storyboard"]
+ index = env.params.url["index"]
+
+ if storyboard.starts_with? "storyboard_live"
+ host = "https://i.ytimg.com"
+ else
+ host = "https://i9.ytimg.com"
+ end
+ client = make_client(URI.parse(host))
+
+ url = "/sb/#{id}/#{storyboard}/#{index}?#{env.params.query}"
+
+ headers = HTTP::Headers.new
+ {"Accept", "Accept-Encoding", "Cache-Control", "Connection", "If-None-Match", "Range"}.each do |header|
if env.request.headers[header]?
headers[header] = env.request.headers[header]
end
@@ -4504,7 +4563,7 @@ get "/vi/:id/:name" do |env|
url = "/vi/#{id}/#{name}"
headers = HTTP::Headers.new
- {"Range", "Accept", "Accept-Encoding"}.each do |header|
+ {"Accept", "Accept-Encoding", "Cache-Control", "Connection", "If-None-Match", "Range"}.each do |header|
if env.request.headers[header]?
headers[header] = env.request.headers[header]
end
diff --git a/src/invidious/videos.cr b/src/invidious/videos.cr
index beaa1330..9e273a96 100644
--- a/src/invidious/videos.cr
+++ b/src/invidious/videos.cr
@@ -473,6 +473,75 @@ struct Video
return @player_json.not_nil!
end
+ def storyboards
+ storyboards = self.player_response["storyboards"]?
+ .try &.as_h
+ .try &.["playerStoryboardSpecRenderer"]?
+
+ if !storyboards
+ storyboards = self.player_response["storyboards"]?
+ .try &.as_h
+ .try &.["playerLiveStoryboardSpecRenderer"]?
+
+ if storyboard = storyboards.try &.["spec"]?
+ .try &.as_s
+ return [{
+ url: storyboard.split("#")[0].sub("M$M", "$N"),
+ width: 106,
+ height: 60,
+ count: -1,
+ interval: 5000,
+ storyboard_width: 3,
+ storyboard_height: 3,
+ storyboard_count: -1,
+ }]
+ end
+ end
+
+ storyboards = storyboards.try &.["spec"]?
+ .try &.as_s.split("|")
+
+ items = [] of NamedTuple(
+ url: String,
+ width: Int32,
+ height: Int32,
+ count: Int32,
+ interval: Int32,
+ storyboard_width: Int32,
+ storyboard_height: Int32,
+ storyboard_count: Int32)
+
+ if !storyboards
+ return items
+ end
+
+ url = storyboards.shift
+
+ storyboards.each_with_index do |storyboard, i|
+ width, height, count, storyboard_width, storyboard_height, interval, _, sigh = storyboard.split("#")
+
+ width = width.to_i
+ height = height.to_i
+ count = count.to_i
+ interval = interval.to_i
+ storyboard_width = storyboard_width.to_i
+ storyboard_height = storyboard_height.to_i
+
+ items << {
+ url: "#{url}&sigh=#{sigh}".sub("$L", i),
+ width: width,
+ height: height,
+ count: count,
+ interval: interval,
+ storyboard_width: storyboard_width,
+ storyboard_height: storyboard_height,
+ storyboard_count: (count.to_f / (storyboard_width.to_f * storyboard_height.to_f)).ceil.to_i,
+ }
+ end
+
+ items
+ end
+
def paid
reason = self.player_response["playabilityStatus"]?.try &.["reason"]?
@@ -1062,3 +1131,20 @@ def generate_thumbnails(json, id, config, kemal_config)
end
end
end
+
+def generate_storyboards(json, storyboards, config, kemal_config)
+ json.array do
+ storyboards.each do |storyboard|
+ json.object do
+ json.field "url", storyboard[:url]
+ json.field "width", storyboard[:width]
+ json.field "height", storyboard[:height]
+ json.field "count", storyboard[:count]
+ json.field "interval", storyboard[:interval]
+ json.field "storyboardWidth", storyboard[:storyboard_width]
+ json.field "storyboardHeight", storyboard[:storyboard_height]
+ json.field "storyboardCount", storyboard[:storyboard_count]
+ end
+ end
+ end
+end