Skip to content

Commit 24a6940

Browse files
authored
Merge pull request #6661 from avalonmediasystem/linear_dropbox
Only request magic bytes for mimetype verification
2 parents 1140e86 + b6f4ee3 commit 24a6940

3 files changed

Lines changed: 74 additions & 7 deletions

File tree

app/services/file_locator.rb

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,12 +134,25 @@ def reader
134134
when 's3'
135135
S3File.new(uri).object.get.body
136136
when 'file'
137-
File.open(location,'r')
137+
File.open(location, 'r')
138138
else
139139
open_uri
140140
end
141141
end
142142

143+
def magic_bytes
144+
# Magic bytes for relevant file types should max out at 16, so only request
145+
# that range when checking mimetype.
146+
case uri.scheme
147+
when 's3'
148+
S3File.new(uri).object.get(range: 'bytes=0-15').body
149+
when 'file'
150+
File.read(location, 16)
151+
else
152+
open_uri.read(16)
153+
end
154+
end
155+
143156
# Ruby 3.0 removed URI#open from being called by Kernel#open.
144157
# Prioritize using URI#open, attempt to fallback to Kernel#open
145158
# if URI fails.

lib/avalon/ffprobe.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ def content_type media_file
8080
# Remove S3 credentials or other params from extension output
8181
extension = File.extname(media_file.location)&.gsub(/[\?#].*/, '')
8282
# Fall back on file extension if magic bytes fail to identify file
83-
Marcel::MimeType.for media_file.reader, extension: extension
83+
@content_type ||= Marcel::MimeType.for media_file.magic_bytes, extension: extension
8484
end
8585

8686
def valid_content_type? media_file

spec/services/file_locator_spec.rb

Lines changed: 59 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@
4545
allow(ENV).to receive(:[]).with('AWS_REGION').and_return('us-east-2')
4646
expect(subject.host).to eq 'mybucket.domain'
4747
expect(subject.path).to eq '/mykey.mp4'
48-
expect(subject.query).to include('response-content-disposition=attachment%3B%20filename%3Dmykey.mp4', 'X-Amz-Algorithm',
48+
expect(subject.query).to include('response-content-disposition=attachment%3B%20filename%3Dmykey.mp4', 'X-Amz-Algorithm',
4949
'X-Amz-Credential', 'X-Amz-Expires', 'X-Amz-SignedHeaders', 'X-Amz-Signature')
5050
end
5151

@@ -60,7 +60,7 @@
6060
allow(ENV).to receive(:[]).with('AWS_REGION').and_return('us-east-2')
6161
expect(subject.host).to eq 'mybucket.s3.us-stubbed-1.amazonaws.com'
6262
expect(subject.path).to eq '/mykey.mp4'
63-
expect(subject.query).to include('response-content-disposition=attachment%3B%20filename%3Dmykey.mp4', 'X-Amz-Algorithm',
63+
expect(subject.query).to include('response-content-disposition=attachment%3B%20filename%3Dmykey.mp4', 'X-Amz-Algorithm',
6464
'X-Amz-Credential', 'X-Amz-Expires', 'X-Amz-SignedHeaders', 'X-Amz-Signature')
6565
end
6666
end
@@ -168,7 +168,7 @@
168168
describe '#remove_fs_dir' do
169169
let(:file_path) { "/tmp/dropbox/test/mykey.mp4" }
170170
let(:locator) { FileLocator.new(file_path) }
171-
171+
172172
it 'deletes fs dir' do
173173
allow(File).to receive(:exist?).with(file_path) { true }
174174
expect(locator.exist?).to be_truthy
@@ -177,14 +177,14 @@
177177
expect(locator.exist?).to be_falsey
178178
end
179179
end
180-
180+
181181
describe '#remove_s3_dir' do
182182
let(:old_bucket) { Settings.encoding.masterfile_bucket }
183183
let(:old_path) { Settings.dropbox.path }
184184

185185
let(:dropbox_path) { "s3://#{test_bucket}/dropbox/test_collection" }
186186
let(:test_bucket) { "test_bucket" }
187-
let(:dropbox_prefix) { "/dropbox/test_collection/"}
187+
let(:dropbox_prefix) { "/dropbox/test_collection/" }
188188
let(:s3_res) { Aws::S3::Resource.new }
189189
let(:s3_bucket) { Aws::S3::Bucket.new(test_bucket) }
190190

@@ -225,4 +225,58 @@
225225
end
226226
end
227227
end
228+
229+
describe "#magic_bytes" do
230+
let(:locator) { FileLocator.new(source) }
231+
let(:source) { "file://#{Rails.root.join('spec', 'fixtures', 'videoshort.mp4')}" }
232+
233+
context "local file" do
234+
it "returns the first 16 bytes" do
235+
expect(locator.magic_bytes.length).to eq 16
236+
end
237+
238+
it 'successfully returns correct mimetype' do
239+
expect(Marcel::MimeType.for(locator.magic_bytes)).to eq 'video/mp4'
240+
end
241+
end
242+
243+
context "s3 file" do
244+
let(:client) { Aws::S3::Client.new(stub_responses: true) }
245+
let(:bucket) { "mybucket" }
246+
let(:key) { "meow.wav" }
247+
let(:source) { "s3://#{bucket}/#{key}" }
248+
let(:object_body) { File.read(Rails.root.join('spec', 'fixtures', 'videoshort.mp4')) }
249+
250+
before do
251+
client.stub_responses(:get_object, lambda { |context|
252+
range = context.params[:range]
253+
from, to = range.match(/bytes=(\d+)-(\d+)/)[1..2].map(&:to_i)
254+
# Return the specific byte range of the content
255+
{ body: object_body[from..to], content_range: "bytes #{from}-#{to}/#{object_body.length}" }
256+
})
257+
258+
allow(Aws::S3::Client).to receive(:new).and_return(client)
259+
end
260+
261+
it "returns the first 16 bytes" do
262+
expect(locator.magic_bytes.length).to eq 16
263+
end
264+
265+
it 'successfully returns correct mimetype' do
266+
expect(Marcel::MimeType.for(locator.magic_bytes)).to eq 'video/mp4'
267+
end
268+
end
269+
270+
context "other file" do
271+
let(:file_path) { "bogus://#{source}" }
272+
it "returns the first 16 bytes" do
273+
allow(URI).to receive(:open).with(file_path).and_return(URI.open(Rails.root.join('spec', 'fixtures', 'videoshort.mp4').to_s))
274+
expect(locator.magic_bytes.length).to eq 16
275+
end
276+
277+
it 'successfully returns correct mimetype' do
278+
expect(Marcel::MimeType.for(locator.magic_bytes)).to eq 'video/mp4'
279+
end
280+
end
281+
end
228282
end

0 commit comments

Comments
 (0)