Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor sending audio in TracksController by switching back to Rack::Files #682

Merged
merged 1 commit into from
Nov 13, 2024
Merged
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
56 changes: 11 additions & 45 deletions app/controllers/tracks_controller.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
class TracksController < ApplicationController
include ActionController::Live

before_action :set_track, only: %i[show update destroy audio download merge]

has_scope :by_filter, as: 'filter'
Expand Down Expand Up @@ -65,9 +63,9 @@ def audio
CreateTranscodedItemJob.perform_now(audio_file, conversion)
transcoded_item = TranscodedItem.find_by(audio_file:, codec_conversion: conversion)
end
audio_with_file(transcoded_item.path, transcoded_item.mimetype)
send_file_with_range(transcoded_item.path, transcoded_item.mimetype)
else
audio_with_file(audio_file.full_path, audio_file.codec.mimetype)
send_file_with_range(audio_file.full_path, audio_file.codec.mimetype)
end
end

Expand All @@ -86,48 +84,16 @@ def merge

private

def audio_with_file(path, mimetype)
file = File.open(path, 'rb')
audio_with_stream(file, mimetype, file.size)
end

def audio_with_stream(stream, mimetype, total_size)
first_byte = 0
last_byte = total_size - 1
if request.headers['range'].present?
response.status = 206
# This is technically not a correct parsing of the header; a user agent
# can request multiple byte ranges. In practice this doesn't happen
# (especially not for web audio).
match = request.headers['range'].match(/bytes=(\d+)-(\d*)/)
first_byte = match[1].to_i
last_byte = match[2].to_i if match[2].present?
response.headers['content-range'] = "bytes #{first_byte}-#{last_byte}/#{total_size}"
else
response.status = 200
end
response.content_type = mimetype
response.headers['accept-ranges'] = 'bytes'
response.headers['content-length'] = (last_byte - first_byte + 1).to_s
# Unfortunately, if we don't commit the headers, ActionController::Live will
# delete the "content-length" header set above. No other headers need to be
# set, so we just freeze them here.
response.commit!

to_skip = first_byte
while to_skip.positive?
read_bytes = stream.read([to_skip, 16.kilobytes].min)
to_skip -= read_bytes.length
end
to_send = last_byte - first_byte + 1
while to_send.positive?
read_bytes = stream.read([to_send, 16.kilobytes].min)
to_send -= read_bytes.length
response.stream.write read_bytes
def send_file_with_range(path, mimetype)
Rack::Files.new(nil).serving(request, path).tap do |(status, headers, body)|
self.status = status
self.response_body = body
headers.each do |name, value|
response.headers[name] = value
end
response.headers['accept-ranges'] = 'bytes'
response.headers['content-type'] = mimetype
end
ensure
stream.close
response.stream.close
end

def set_track
Expand Down