summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorOmar Roth <omarroth@protonmail.com>2019-07-04 15:30:00 -0500
committerOmar Roth <omarroth@protonmail.com>2019-07-04 23:29:28 -0500
commite5fa5df7be38d60ce9e4835bae170d805482afc0 (patch)
tree262e65fa061b9ca7dcbb1c0f3234df885c365c2c
parentf7dbf2bdd4f38fed72ad823be1bc86b727aafdb0 (diff)
downloadinvidious-e5fa5df7be38d60ce9e4835bae170d805482afc0.tar.gz
invidious-e5fa5df7be38d60ce9e4835bae170d805482afc0.tar.bz2
invidious-e5fa5df7be38d60ce9e4835bae170d805482afc0.zip
Chunk video files to bypass throttling
-rw-r--r--src/invidious.cr95
-rw-r--r--src/invidious/helpers/utils.cr18
2 files changed, 88 insertions, 25 deletions
diff --git a/src/invidious.cr b/src/invidious.cr
index 4817b287..3585d928 100644
--- a/src/invidious.cr
+++ b/src/invidious.cr
@@ -54,6 +54,7 @@ MAX_ITEMS_PER_PAGE = 1500
REQUEST_HEADERS_WHITELIST = {"Accept", "Accept-Encoding", "Cache-Control", "Connection", "Content-Length", "If-None-Match", "Range"}
RESPONSE_HEADERS_BLACKLIST = {"Access-Control-Allow-Origin", "Alt-Svc", "Server"}
+HTTP_CHUNK_SIZE = 10485760 # ~10MB
CURRENT_BRANCH = {{ "#{`git branch | sed -n '/\* /s///p'`.strip}" }}
CURRENT_COMMIT = {{ "#{`git rev-list HEAD --max-count=1 --abbrev-commit`.strip}" }}
@@ -4640,41 +4641,85 @@ get "/videoplayback" do |env|
next
end
- client = make_client(URI.parse(host), region)
- begin
- client.get(url, headers) do |response|
- env.response.status_code = response.status_code
+ content_length = nil
+ first_chunk = true
+ range_start, range_end = parse_range(env.request.headers["Range"]?)
+ chunk_start = range_start
+ chunk_end = range_end
- response.headers.each do |key, value|
- if !RESPONSE_HEADERS_BLACKLIST.includes? key
- env.response.headers[key] = value
- end
- end
+ if !chunk_end || chunk_end - chunk_start > HTTP_CHUNK_SIZE
+ chunk_end = chunk_start + HTTP_CHUNK_SIZE - 1
+ end
- env.response.headers["Access-Control-Allow-Origin"] = "*"
+ while true
+ if !range_end && content_length
+ range_end = content_length
+ end
+
+ if range_end && chunk_start > range_end
+ break
+ end
+
+ if range_end && chunk_end > range_end
+ chunk_end = range_end
+ end
+
+ headers["Range"] = "bytes=#{chunk_start}-#{chunk_end}"
+ client = make_client(URI.parse(host), region)
+ begin
+ client.get(url, headers) do |response|
+ if first_chunk
+ if !env.request.headers["Range"]? && response.status_code == 206
+ env.response.status_code = 200
+ else
+ env.response.status_code = response.status_code
+ end
+
+ response.headers.each do |key, value|
+ if !RESPONSE_HEADERS_BLACKLIST.includes?(key) && key != "Content-Range"
+ env.response.headers[key] = value
+ end
+ end
+
+ env.response.headers["Access-Control-Allow-Origin"] = "*"
- if response.headers["Location"]?
- url = URI.parse(response.headers["Location"])
- host = url.host
+ if location = response.headers["Location"]?
+ location = URI.parse(location)
+ location = "#{location.full_path}&host=#{location.host}"
- url = url.full_path
- url += "&host=#{host}"
+ if region
+ location += "&region=#{region}"
+ end
+
+ env.redirect location
+ break
+ end
+
+ if title = query_params["title"]?
+ # https://blog.fastmail.com/2011/06/24/download-non-english-filenames/
+ env.response.headers["Content-Disposition"] = "attachment; filename=\"#{URI.escape(title)}\"; filename*=UTF-8''#{URI.escape(title)}"
+ end
- if region
- url += "&region=#{region}"
+ content_length = response.headers["Content-Range"].split("/")[-1].to_i64
+ if env.request.headers["Range"]?
+ env.response.headers["Content-Range"] = "bytes #{range_start}-#{range_end || (content_length - 1)}/#{content_length}"
+ env.response.content_length = ((range_end.try &.+ 1) || content_length) - range_start
+ else
+ env.response.content_length = content_length
+ end
end
- next env.redirect url
+ proxy_file(response, env)
end
-
- if title = query_params["title"]?
- # https://blog.fastmail.com/2011/06/24/download-non-english-filenames/
- env.response.headers["Content-Disposition"] = "attachment; filename=\"#{URI.escape(title)}\"; filename*=UTF-8''#{URI.escape(title)}"
+ rescue ex
+ if ex.message != "Error reading socket: Connection reset by peer"
+ break
end
-
- proxy_file(response, env)
end
- rescue ex
+
+ chunk_start = chunk_end + 1
+ chunk_end += HTTP_CHUNK_SIZE
+ first_chunk = false
end
end
diff --git a/src/invidious/helpers/utils.cr b/src/invidious/helpers/utils.cr
index 1eb02b89..9ce8efdb 100644
--- a/src/invidious/helpers/utils.cr
+++ b/src/invidious/helpers/utils.cr
@@ -347,3 +347,21 @@ def subscribe_pubsub(topic, key, config)
return client.post("/subscribe", form: body)
end
+
+def parse_range(range)
+ if !range
+ return 0_i64, nil
+ end
+
+ ranges = range.lchop("bytes=").split(',')
+ ranges.each do |range|
+ start_range, end_range = range.split('-')
+
+ start_range = start_range.to_i64? || 0_i64
+ end_range = end_range.to_i64?
+
+ return start_range, end_range
+ end
+
+ return 0_i64, nil
+end