format_parser 0.25.4 → 0.28.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/main.yml +104 -0
  3. data/CHANGELOG.md +15 -0
  4. data/README.md +4 -0
  5. data/format_parser.gemspec +1 -0
  6. data/lib/archive.rb +3 -0
  7. data/lib/audio.rb +3 -0
  8. data/lib/document.rb +1 -0
  9. data/lib/format_parser.rb +18 -3
  10. data/lib/format_parser/version.rb +1 -1
  11. data/lib/image.rb +3 -0
  12. data/lib/parsers/aiff_parser.rb +4 -1
  13. data/lib/parsers/bmp_parser.rb +3 -0
  14. data/lib/parsers/cr2_parser.rb +2 -0
  15. data/lib/parsers/dpx_parser.rb +19 -8
  16. data/lib/parsers/flac_parser.rb +2 -0
  17. data/lib/parsers/gif_parser.rb +2 -0
  18. data/lib/parsers/jpeg_parser.rb +2 -0
  19. data/lib/parsers/m3u_parser.rb +23 -0
  20. data/lib/parsers/moov_parser.rb +10 -1
  21. data/lib/parsers/mp3_parser.rb +3 -2
  22. data/lib/parsers/ogg_parser.rb +3 -2
  23. data/lib/parsers/pdf_parser.rb +2 -2
  24. data/lib/parsers/png_parser.rb +2 -0
  25. data/lib/parsers/psd_parser.rb +2 -0
  26. data/lib/parsers/tiff_parser.rb +10 -2
  27. data/lib/parsers/wav_parser.rb +3 -0
  28. data/lib/parsers/zip_parser.rb +5 -3
  29. data/lib/parsers/zip_parser/office_formats.rb +5 -5
  30. data/lib/remote_io.rb +7 -1
  31. data/lib/text.rb +19 -0
  32. data/lib/video.rb +3 -0
  33. data/spec/format_parser_spec.rb +20 -0
  34. data/spec/parsers/aiff_parser_spec.rb +1 -0
  35. data/spec/parsers/bmp_parser_spec.rb +8 -0
  36. data/spec/parsers/cr2_parser_spec.rb +1 -0
  37. data/spec/parsers/dpx_parser_spec.rb +1 -0
  38. data/spec/parsers/flac_parser_spec.rb +1 -0
  39. data/spec/parsers/gif_parser_spec.rb +1 -0
  40. data/spec/parsers/jpeg_parser_spec.rb +1 -0
  41. data/spec/parsers/m3u_parser_spec.rb +41 -0
  42. data/spec/parsers/moov_parser_spec.rb +4 -1
  43. data/spec/parsers/mp3_parser_spec.rb +1 -0
  44. data/spec/parsers/ogg_parser_spec.rb +1 -0
  45. data/spec/parsers/pdf_parser_spec.rb +1 -0
  46. data/spec/parsers/png_parser_spec.rb +1 -0
  47. data/spec/parsers/psd_parser_spec.rb +1 -0
  48. data/spec/parsers/tiff_parser_spec.rb +1 -0
  49. data/spec/parsers/wav_parser_spec.rb +1 -0
  50. data/spec/parsers/zip_parser_spec.rb +2 -0
  51. data/spec/remote_fetching_spec.rb +11 -0
  52. data/spec/remote_io_spec.rb +38 -13
  53. metadata +21 -4
  54. data/.travis.yml +0 -12
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 379af5330f9b9521b278e5fe54821398ea2a5a58d4ac9881bcc5f30021446c2a
4
- data.tar.gz: 71da3110186c0c8fce731fc2adb05fbcbcc829ab9c6fe67b4934dc8d69fa093c
3
+ metadata.gz: d2212d92c3c0ca7aab0e60c9aa6559d0508ec2b8b39e4b2067990d97a7d78327
4
+ data.tar.gz: 27736f9d52ea1e73a147d96d38608e268040648e61d2b913ca38f164f2d2d0b6
5
5
  SHA512:
6
- metadata.gz: 5a9a99547baa58e3e693c8e24cc84af0d1e6598403dbed9473b885531e7c067b51c5aac4cc411ddc3f7d114b7987b470eb81554422553e6b346fc42b98a50789
7
- data.tar.gz: 851b0c4fad434140e641077c01e6d46f463c8aab7b85d35a85ba0729c2913bd978a35e7ab0608d4cb8d5e2e2f13761a00078b98948290c3b3203953a9cba7c03
6
+ metadata.gz: 5f63f74444df32d016fc25dc5c3731671690185b7ecf80ffc06da350b37d82b566f451508091245fec255c816121a115286dd04fb626aa4c1b694836fd645d97
7
+ data.tar.gz: ee1917287bb7c50bd71d9a7756321e30fbc73aa5e3a4f42e4844eb9494dfe77e89a9a26622732772c9b29c4ea673f3401324d2e6bf67e5bdaa5dac991d12f821
@@ -0,0 +1,104 @@
1
+ name: CI
2
+
3
+ on: [push,pull_request]
4
+
5
+ env:
6
+ BUNDLE_PATH: vendor/bundle
7
+
8
+ jobs:
9
+ lint:
10
+ name: Code Style
11
+ runs-on: ubuntu-18.04
12
+ if: github.event_name == 'push' || github.event.pull_request.head.repo.full_name != github.repository
13
+ strategy:
14
+ matrix:
15
+ ruby:
16
+ - 2.7
17
+ - 2.6
18
+ - 2.5
19
+ - 2.4
20
+ - 2.3
21
+ - 2.2
22
+ - jruby
23
+ steps:
24
+ - name: Checkout
25
+ uses: actions/checkout@v2
26
+ - name: Setup Ruby
27
+ uses: ruby/setup-ruby@v1
28
+ with:
29
+ ruby-version: ${{ matrix.ruby }}
30
+ - name: Gemfile Cache
31
+ uses: actions/cache@v2
32
+ with:
33
+ path: Gemfile.lock
34
+ key: ${{ runner.os }}-gemlock-${{ matrix.ruby }}-${{ hashFiles('Gemfile', 'format_parser.gemspec') }}
35
+ restore-keys: |
36
+ ${{ runner.os }}-gemlock-${{ matrix.ruby }}-
37
+ - name: Bundle Cache
38
+ id: cache-gems
39
+ uses: actions/cache@v2
40
+ with:
41
+ path: vendor/bundle
42
+ key: ${{ runner.os }}-gems-${{ matrix.ruby }}-${{ hashFiles('Gemfile', 'Gemfile.lock', 'format_parser.gemspec') }}
43
+ restore-keys: |
44
+ ${{ runner.os }}-gems-${{ matrix.ruby }}-
45
+ ${{ runner.os }}-gems-
46
+ - name: Bundle Install
47
+ if: steps.cache-gems.outputs.cache-hit != 'true'
48
+ run: bundle install --jobs 4 --retry 3
49
+ - name: Rubocop Cache
50
+ uses: actions/cache@v2
51
+ with:
52
+ path: ~/.cache/rubocop_cache
53
+ key: ${{ runner.os }}-rubocop-${{ hashFiles('.rubocop.yml') }}
54
+ restore-keys: |
55
+ ${{ runner.os }}-rubocop-
56
+ - name: Rubocop
57
+ run: bundle exec rubocop
58
+ test:
59
+ name: Specs
60
+ runs-on: ubuntu-18.04
61
+ if: github.event_name == 'push' || github.event.pull_request.head.repo.full_name != github.repository
62
+ strategy:
63
+ matrix:
64
+ ruby:
65
+ - 2.7
66
+ - 2.6
67
+ - 2.5
68
+ - 2.4
69
+ - 2.3
70
+ - 2.2
71
+ - jruby
72
+ experimental: [false]
73
+ include:
74
+ - ruby: 3.0
75
+ experimental: true
76
+ steps:
77
+ - name: Checkout
78
+ uses: actions/checkout@v2
79
+ - name: Setup Ruby
80
+ uses: ruby/setup-ruby@v1
81
+ with:
82
+ ruby-version: ${{ matrix.ruby }}
83
+ - name: Gemfile Cache
84
+ uses: actions/cache@v2
85
+ with:
86
+ path: Gemfile.lock
87
+ key: ${{ runner.os }}-gemlock-${{ matrix.ruby }}-${{ hashFiles('Gemfile', 'format_parser.gemspec') }}
88
+ restore-keys: |
89
+ ${{ runner.os }}-gemlock-${{ matrix.ruby }}-
90
+ - name: Bundle Cache
91
+ id: cache-gems
92
+ uses: actions/cache@v2
93
+ with:
94
+ path: vendor/bundle
95
+ key: ${{ runner.os }}-gems-${{ matrix.ruby }}-${{ hashFiles('Gemfile', 'Gemfile.lock', 'format_parser.gemspec') }}
96
+ restore-keys: |
97
+ ${{ runner.os }}-gems-${{ matrix.ruby }}-
98
+ ${{ runner.os }}-gems-
99
+ - name: Bundle Install
100
+ if: steps.cache-gems.outputs.cache-hit != 'true'
101
+ run: bundle install --jobs 4 --retry 3
102
+ - name: RSpec
103
+ continue-on-error: ${{ matrix.experimental }}
104
+ run: bundle exec rake parallel:spec
data/CHANGELOG.md CHANGED
@@ -1,3 +1,18 @@
1
+ ## 0.28.0
2
+ * Change `FormatParser.parse_http` to follow HTTP redirects
3
+
4
+ ## 0.27.0
5
+ * Add `#content_type` on `Result` return values which makes sense for the detected filetype
6
+
7
+ ## 0.26.0
8
+ * Add support for M3U format files
9
+
10
+ ## 0.25.6
11
+ * Fix FormatParser.parse (with `results: :first`) to be deterministic
12
+
13
+ ## 0.25.5
14
+ * DPX: Fix DPXParser to support images without aspect ratio
15
+
1
16
  ## 0.25.4
2
17
  * MP3: Fix MP3Parser to return nil for TIFF files
3
18
  * Add support to ruby 2.7
data/README.md CHANGED
@@ -32,6 +32,7 @@ and [dimensions,](https://github.com/sstephenson/dimensions) borrowing from them
32
32
  * DOCX, PPTX, XLSX
33
33
  * OGG
34
34
  * MPEG, MPG
35
+ * M3U
35
36
 
36
37
  ...with [more](https://github.com/WeTransfer/format_parser/issues?q=is%3Aissue+is%3Aopen+label%3Aformats) on the way!
37
38
 
@@ -194,6 +195,9 @@ Unless specified otherwise in this section the fixture files are MIT licensed an
194
195
  manipulated using the [https://github.com/recurser/exif-orientation-examples](exif-orientation-examples)
195
196
  script.
196
197
 
198
+ ### M3U
199
+ - The M3U fixture files were created by one of the project maintainers
200
+
197
201
  ### .key
198
202
  - The `keynote_recognized_as_jpeg.key` file was created by the project maintainers
199
203
 
@@ -34,6 +34,7 @@ Gem::Specification.new do |spec|
34
34
  spec.add_dependency 'exifr', '~> 1', '>= 1.3.8'
35
35
  spec.add_dependency 'id3tag', '~> 0.14'
36
36
  spec.add_dependency 'faraday', '~> 0.13'
37
+ spec.add_dependency 'faraday_middleware', '~> 0.14'
37
38
  spec.add_dependency 'measurometer', '~> 1'
38
39
 
39
40
  spec.add_development_dependency 'rspec', '~> 3.0'
data/lib/archive.rb CHANGED
@@ -26,6 +26,9 @@ module FormatParser
26
26
  # it can be placed here
27
27
  attr_accessor :intrinsics
28
28
 
29
+ # The MIME type of the archive
30
+ attr_accessor :content_type
31
+
29
32
  # Only permits assignments via defined accessors
30
33
  def initialize(**attributes)
31
34
  attributes.map { |(k, v)| public_send("#{k}=", v) }
data/lib/audio.rb CHANGED
@@ -35,6 +35,9 @@ module FormatParser
35
35
  # it can be placed here
36
36
  attr_accessor :intrinsics
37
37
 
38
+ # The MIME type of the sound file
39
+ attr_accessor :content_type
40
+
38
41
  # Only permits assignments via defined accessors
39
42
  def initialize(**attributes)
40
43
  attributes.map { |(k, v)| public_send("#{k}=", v) }
data/lib/document.rb CHANGED
@@ -7,6 +7,7 @@ module FormatParser
7
7
  attr_accessor :format
8
8
  attr_accessor :document_type
9
9
  attr_accessor :page_count
10
+ attr_accessor :content_type
10
11
 
11
12
  # Only permits assignments via defined accessors
12
13
  def initialize(**attributes)
data/lib/format_parser.rb CHANGED
@@ -19,6 +19,7 @@ module FormatParser
19
19
  require_relative 'io_constraint'
20
20
  require_relative 'care'
21
21
  require_relative 'active_storage/blob_analyzer'
22
+ require_relative 'text'
22
23
 
23
24
  # Define Measurometer in the internal namespace as well
24
25
  # so that we stay compatible for the applications that use it
@@ -49,8 +50,10 @@ module FormatParser
49
50
  parser_provided_formats = Array(formats)
50
51
  parser_provided_natures = Array(natures)
51
52
  PARSER_MUX.synchronize do
52
- @parsers ||= Set.new
53
- @parsers << callable_parser
53
+ # It can't be a Set because the method `parsers_for` depends on the order
54
+ # that the parsers were added.
55
+ @parsers ||= []
56
+ @parsers << callable_parser unless @parsers.include?(callable_parser)
54
57
  @parsers_per_nature ||= {}
55
58
  parser_provided_natures.each do |provided_nature|
56
59
  @parsers_per_nature[provided_nature] ||= Set.new
@@ -255,7 +258,19 @@ module FormatParser
255
258
  # Order the parsers according to their priority value. The ones having a lower
256
259
  # value will sort higher and will be applied sooner
257
260
  parsers_in_order_of_priority = parsers.to_a.sort do |parser_a, parser_b|
258
- @parser_priorities[parser_a] <=> @parser_priorities[parser_b]
261
+ if @parser_priorities[parser_a] != @parser_priorities[parser_b]
262
+ @parser_priorities[parser_a] <=> @parser_priorities[parser_b]
263
+ else
264
+ # Some parsers have the same priority and we want them to be always sorted
265
+ # in the same way, to not change the result of FormatParser.parse(results: :first).
266
+ # When this changes, it can generate flaky tests or event different
267
+ # results in different environments, which can be hard to understand why.
268
+ # There is also no guarantee in the order that the elements are added in
269
+ # @@parser_priorities
270
+ # So, to have always the same order, we sort by the order that the parsers
271
+ # were registered if the priorities are the same.
272
+ @parsers.index(parser_a) <=> @parsers.index(parser_b)
273
+ end
259
274
  end
260
275
 
261
276
  # If there is one parser that is more likely to match, place it first
@@ -1,3 +1,3 @@
1
1
  module FormatParser
2
- VERSION = '0.25.4'
2
+ VERSION = '0.28.0'
3
3
  end
data/lib/image.rb CHANGED
@@ -64,6 +64,9 @@ module FormatParser
64
64
  # it can be placed here
65
65
  attr_accessor :intrinsics
66
66
 
67
+ # The MIME type of the image file
68
+ attr_accessor :content_type
69
+
67
70
  # Only permits assignments via defined accessors
68
71
  def initialize(**attributes)
69
72
  attributes.map { |(k, v)| public_send("#{k}=", v) }
@@ -1,6 +1,8 @@
1
1
  class FormatParser::AIFFParser
2
2
  include FormatParser::IOUtils
3
3
 
4
+ AIFF_MIME_TYPE = 'audio/x-aiff'
5
+
4
6
  # Known chunk types we can omit when parsing,
5
7
  # grossly lifted from http://www.muratnkonar.com/aiff/
6
8
  KNOWN_CHUNKS = [
@@ -70,7 +72,8 @@ class FormatParser::AIFFParser
70
72
  num_audio_channels: channels,
71
73
  audio_sample_rate_hz: sample_rate.to_i,
72
74
  media_duration_frames: sample_frames,
73
- media_duration_seconds: duration_in_seconds
75
+ media_duration_seconds: duration_in_seconds,
76
+ content_type: AIFF_MIME_TYPE,
74
77
  )
75
78
  end
76
79
 
@@ -5,6 +5,7 @@ class FormatParser::BMPParser
5
5
 
6
6
  VALID_BMP = 'BM'
7
7
  PERMISSIBLE_PIXEL_ARRAY_LOCATIONS = 26..512
8
+ BMP_MIME_TYPE = 'image/bmp'
8
9
 
9
10
  def likely_match?(filename)
10
11
  filename =~ /\.bmp$/i
@@ -42,6 +43,7 @@ class FormatParser::BMPParser
42
43
  width_px: width,
43
44
  height_px: height,
44
45
  color_mode: :rgb,
46
+ content_type: BMP_MIME_TYPE,
45
47
  intrinsics: {
46
48
  data_order: data_order,
47
49
  bits_per_pixel: bit_depth
@@ -63,6 +65,7 @@ class FormatParser::BMPParser
63
65
  width_px: width,
64
66
  height_px: height.abs,
65
67
  color_mode: :rgb,
68
+ content_type: BMP_MIME_TYPE,
66
69
  intrinsics: {
67
70
  vertical_resolution: vertical_res,
68
71
  horizontal_resolution: horizontal_res,
@@ -6,6 +6,7 @@ class FormatParser::CR2Parser
6
6
 
7
7
  TIFF_HEADER = [0x49, 0x49, 0x2a, 0x00]
8
8
  CR2_HEADER = [0x43, 0x52, 0x02, 0x00]
9
+ CR2_MIME_TYPE = 'image/x-canon-cr2'
9
10
 
10
11
  def likely_match?(filename)
11
12
  filename =~ /\.cr2$/i
@@ -39,6 +40,7 @@ class FormatParser::CR2Parser
39
40
  display_height_px: exif_data.rotated? ? w : h,
40
41
  orientation: exif_data.orientation_sym,
41
42
  intrinsics: {exif: exif_data},
43
+ content_type: CR2_MIME_TYPE,
42
44
  )
43
45
  rescue EXIFR::MalformedTIFF
44
46
  nil
@@ -6,6 +6,11 @@ class FormatParser::DPXParser
6
6
  BE_MAGIC = 'SDPX'
7
7
  LE_MAGIC = BE_MAGIC.reverse
8
8
 
9
+ # There is no official MIME type for DPX, so we have
10
+ # to invent something useful. We will prefix it with x-
11
+ # to indicate that it is a vendor subtype
12
+ DPX_MIME_TYPE = 'image/x-dpx'
13
+
9
14
  class ByteOrderHintIO < SimpleDelegator
10
15
  def initialize(io, is_little_endian)
11
16
  super(io)
@@ -35,18 +40,23 @@ class FormatParser::DPXParser
35
40
  w = dpx_structure.fetch(:image).fetch(:pixels_per_line)
36
41
  h = dpx_structure.fetch(:image).fetch(:lines_per_element)
37
42
 
43
+ display_w = w
44
+ display_h = h
45
+
38
46
  pixel_aspect_w = dpx_structure.fetch(:orientation).fetch(:horizontal_pixel_aspect)
39
47
  pixel_aspect_h = dpx_structure.fetch(:orientation).fetch(:vertical_pixel_aspect)
40
- pixel_aspect = pixel_aspect_w / pixel_aspect_h.to_f
41
48
 
42
- image_aspect = w / h.to_f * pixel_aspect
49
+ # Find display height and width based on aspect only if the file structure has pixel aspects
50
+ if pixel_aspect_h != 0 && pixel_aspect_w != 0
51
+ pixel_aspect = pixel_aspect_w / pixel_aspect_h.to_f
43
52
 
44
- display_w = w
45
- display_h = h
46
- if image_aspect > 1
47
- display_h = (display_w / image_aspect).round
48
- else
49
- display_w = (display_h * image_aspect).round
53
+ image_aspect = w / h.to_f * pixel_aspect
54
+
55
+ if image_aspect > 1
56
+ display_h = (display_w / image_aspect).round
57
+ else
58
+ display_w = (display_h * image_aspect).round
59
+ end
50
60
  end
51
61
 
52
62
  FormatParser::Image.new(
@@ -56,6 +66,7 @@ class FormatParser::DPXParser
56
66
  display_width_px: display_w,
57
67
  display_height_px: display_h,
58
68
  intrinsics: dpx_structure,
69
+ content_type: DPX_MIME_TYPE,
59
70
  )
60
71
  end
61
72
 
@@ -4,6 +4,7 @@ class FormatParser::FLACParser
4
4
  MAGIC_BYTES = 4
5
5
  MAGIC_BYTE_STRING = 'fLaC'
6
6
  BLOCK_HEADER_BYTES = 4
7
+ FLAC_MIME_TYPE = 'audio/x-flac'
7
8
 
8
9
  def likely_match?(filename)
9
10
  filename =~ /\.flac$/i
@@ -61,6 +62,7 @@ class FormatParser::FLACParser
61
62
  audio_sample_rate_hz: sample_rate,
62
63
  media_duration_seconds: duration,
63
64
  media_duration_frames: total_samples,
65
+ content_type: FLAC_MIME_TYPE,
64
66
  intrinsics: {
65
67
  bits_per_sample: bits_per_sample,
66
68
  minimum_frame_size: minimum_frame_size,
@@ -3,6 +3,7 @@ class FormatParser::GIFParser
3
3
 
4
4
  HEADERS = ['GIF87a', 'GIF89a'].map(&:b)
5
5
  NETSCAPE_AND_AUTHENTICATION_CODE = 'NETSCAPE2.0'
6
+ GIF_MIME_TYPE = 'image/gif'
6
7
 
7
8
  def likely_match?(filename)
8
9
  filename =~ /\.gif$/i
@@ -45,6 +46,7 @@ class FormatParser::GIFParser
45
46
  height_px: h,
46
47
  has_multiple_frames: is_animated,
47
48
  color_mode: :indexed,
49
+ content_type: GIF_MIME_TYPE
48
50
  )
49
51
  end
50
52
 
@@ -12,6 +12,7 @@ class FormatParser::JPEGParser
12
12
  APP1_MARKER = 0xE1 # maybe EXIF
13
13
  EXIF_MAGIC_STRING = "Exif\0\0".b
14
14
  MUST_FIND_NEXT_MARKER_WITHIN_BYTES = 1024
15
+ JPEG_MIME_TYPE = 'image/jpeg'
15
16
 
16
17
  def self.likely_match?(filename)
17
18
  filename =~ /\.jpe?g$/i
@@ -88,6 +89,7 @@ class FormatParser::JPEGParser
88
89
  display_height_px: dh,
89
90
  orientation: flat_exif.orientation_sym,
90
91
  intrinsics: {exif: flat_exif},
92
+ content_type: JPEG_MIME_TYPE
91
93
  )
92
94
 
93
95
  return result
@@ -0,0 +1,23 @@
1
+ class FormatParser::M3UParser
2
+ include FormatParser::IOUtils
3
+
4
+ HEADER = '#EXTM3U'
5
+ M3U8_MIME_TYPE = 'application/vnd.apple.mpegurl' # https://en.wikipedia.org/wiki/M3U#Internet_media_types
6
+
7
+ def likely_match?(filename)
8
+ filename =~ /\.m3u8?$/i
9
+ end
10
+
11
+ def call(io)
12
+ io = FormatParser::IOConstraint.new(io)
13
+
14
+ header = safe_read(io, 7)
15
+ return unless HEADER.eql?(header)
16
+
17
+ FormatParser::Text.new(
18
+ format: :m3u,
19
+ content_type: M3U8_MIME_TYPE,
20
+ )
21
+ end
22
+ FormatParser.register_parser new, natures: :text, formats: :m3u
23
+ end
@@ -11,6 +11,12 @@ class FormatParser::MOOVParser
11
11
  'm4a ' => :m4a,
12
12
  }
13
13
 
14
+ # https://tools.ietf.org/html/rfc4337#section-2
15
+ # There is also video/quicktime which we should be able to capture
16
+ # here, but there is currently no detection for MOVs versus MP4s
17
+ MP4_AU_MIME_TYPE = 'audio/mp4'
18
+ MP4_MIXED_MIME_TYPE = 'video/mp4'
19
+
14
20
  def likely_match?(filename)
15
21
  filename =~ /\.(mov|m4a|ma4|mp4|aac|m4v)$/i
16
22
  end
@@ -49,10 +55,12 @@ class FormatParser::MOOVParser
49
55
  end
50
56
 
51
57
  # M4A only contains audio, while MP4 and friends can contain video.
52
- if format_from_moov_type(file_type) == :m4a
58
+ fmt = format_from_moov_type(file_type)
59
+ if fmt == :m4a
53
60
  FormatParser::Audio.new(
54
61
  format: format_from_moov_type(file_type),
55
62
  media_duration_seconds: media_duration_s,
63
+ content_type: MP4_AU_MIME_TYPE,
56
64
  intrinsics: atom_tree,
57
65
  )
58
66
  else
@@ -61,6 +69,7 @@ class FormatParser::MOOVParser
61
69
  width_px: width,
62
70
  height_px: height,
63
71
  media_duration_seconds: media_duration_s,
72
+ content_type: MP4_MIXED_MIME_TYPE,
64
73
  intrinsics: atom_tree,
65
74
  )
66
75
  end