format_parser 0.26.0 → 0.27.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 (45) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +3 -0
  3. data/lib/archive.rb +3 -0
  4. data/lib/audio.rb +3 -0
  5. data/lib/document.rb +1 -0
  6. data/lib/format_parser/version.rb +1 -1
  7. data/lib/image.rb +3 -0
  8. data/lib/parsers/aiff_parser.rb +4 -1
  9. data/lib/parsers/bmp_parser.rb +3 -0
  10. data/lib/parsers/cr2_parser.rb +2 -0
  11. data/lib/parsers/dpx_parser.rb +6 -0
  12. data/lib/parsers/flac_parser.rb +2 -0
  13. data/lib/parsers/gif_parser.rb +2 -0
  14. data/lib/parsers/jpeg_parser.rb +2 -0
  15. data/lib/parsers/m3u_parser.rb +3 -1
  16. data/lib/parsers/moov_parser.rb +10 -1
  17. data/lib/parsers/mp3_parser.rb +3 -2
  18. data/lib/parsers/ogg_parser.rb +3 -2
  19. data/lib/parsers/pdf_parser.rb +2 -2
  20. data/lib/parsers/png_parser.rb +2 -0
  21. data/lib/parsers/psd_parser.rb +2 -0
  22. data/lib/parsers/tiff_parser.rb +10 -2
  23. data/lib/parsers/wav_parser.rb +3 -0
  24. data/lib/parsers/zip_parser.rb +5 -3
  25. data/lib/parsers/zip_parser/office_formats.rb +5 -5
  26. data/lib/text.rb +1 -0
  27. data/lib/video.rb +3 -0
  28. data/spec/parsers/aiff_parser_spec.rb +1 -0
  29. data/spec/parsers/bmp_parser_spec.rb +8 -0
  30. data/spec/parsers/cr2_parser_spec.rb +1 -0
  31. data/spec/parsers/dpx_parser_spec.rb +1 -0
  32. data/spec/parsers/flac_parser_spec.rb +1 -0
  33. data/spec/parsers/gif_parser_spec.rb +1 -0
  34. data/spec/parsers/jpeg_parser_spec.rb +1 -0
  35. data/spec/parsers/m3u_parser_spec.rb +1 -0
  36. data/spec/parsers/moov_parser_spec.rb +4 -1
  37. data/spec/parsers/mp3_parser_spec.rb +1 -0
  38. data/spec/parsers/ogg_parser_spec.rb +1 -0
  39. data/spec/parsers/pdf_parser_spec.rb +1 -0
  40. data/spec/parsers/png_parser_spec.rb +1 -0
  41. data/spec/parsers/psd_parser_spec.rb +1 -0
  42. data/spec/parsers/tiff_parser_spec.rb +1 -0
  43. data/spec/parsers/wav_parser_spec.rb +1 -0
  44. data/spec/parsers/zip_parser_spec.rb +2 -0
  45. metadata +5 -5
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1074a8172a2830a11df0fb7874936c2b8abab8d74fd39985f9c9f7b72d5b348c
4
- data.tar.gz: 5eafd2d610cd30bc85a056bd1c31331bcb49e8ce6b5b538cd13e280b138be7db
3
+ metadata.gz: 1a10ceeaca4d0d6d2336b94f9fc397781ae2ffabdb588cee7ebc59fdcb968082
4
+ data.tar.gz: c28b8b7a0eb1d83e9f93406a4bbbad0699e50248d49d413393d6c3a6d82f7acf
5
5
  SHA512:
6
- metadata.gz: 5ce396a71fedd82b8041bcb6c833e559c7eef74886e73095eaf0b3d21e0c0d49b1620a83aba9796a8134dd5a7fc679156cd967cf82cba95b5941941be73d70c4
7
- data.tar.gz: '0395e5a8fb35e860060e9c3b040b788aaad97eb5883f2d662b418156f1f3986bc4a26a814b9a8552ce7dcbd271da27e977cfb77a5e5b155ebd35db6a49a97719'
6
+ metadata.gz: 6b3780e0f615f8a42aa097b652b675ceba5fb2325c3f2ad7472e178204cef3838967ea56c25e0daa1f5c666df42c5f5d4252a4dd42dae26389c41288d5b56d30
7
+ data.tar.gz: f6f22e664f8603e691795b44902cc5d677f63a02b4950233ea09719b9b2e8ae7d9eea57e406bd4f5b9e252aff8e9c5a684cdb1155846f0eedeceeba7fe7131c0
@@ -1,3 +1,6 @@
1
+ ## 0.27.0
2
+ * Add `#content_type` on `Result` return values which makes sense for the detected filetype
3
+
1
4
  ## 0.26.0
2
5
  * Add support for M3U format files
3
6
 
@@ -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) }
@@ -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) }
@@ -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)
@@ -1,3 +1,3 @@
1
1
  module FormatParser
2
- VERSION = '0.26.0'
2
+ VERSION = '0.27.0'
3
3
  end
@@ -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)
@@ -61,6 +66,7 @@ class FormatParser::DPXParser
61
66
  display_width_px: display_w,
62
67
  display_height_px: display_h,
63
68
  intrinsics: dpx_structure,
69
+ content_type: DPX_MIME_TYPE,
64
70
  )
65
71
  end
66
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
@@ -2,6 +2,7 @@ class FormatParser::M3UParser
2
2
  include FormatParser::IOUtils
3
3
 
4
4
  HEADER = '#EXTM3U'
5
+ M3U8_MIME_TYPE = 'application/vnd.apple.mpegurl' # https://en.wikipedia.org/wiki/M3U#Internet_media_types
5
6
 
6
7
  def likely_match?(filename)
7
8
  filename =~ /\.m3u8?$/i
@@ -14,7 +15,8 @@ class FormatParser::M3UParser
14
15
  return unless HEADER.eql?(header)
15
16
 
16
17
  FormatParser::Text.new(
17
- format: :m3u
18
+ format: :m3u,
19
+ content_type: M3U8_MIME_TYPE,
18
20
  )
19
21
  end
20
22
  FormatParser.register_parser new, natures: :text, formats: :m3u
@@ -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
@@ -32,7 +32,7 @@ class FormatParser::MP3Parser
32
32
  MAGIC_LE = [0x49, 0x49, 0x2A, 0x0].pack('C4')
33
33
  MAGIC_BE = [0x4D, 0x4D, 0x0, 0x2A].pack('C4')
34
34
  TIFF_HEADER_BYTES = [MAGIC_LE, MAGIC_BE]
35
-
35
+ MP3_MIME_TYPE = 'audio/mpeg'
36
36
  # Wraps the Tag object returned by ID3Tag in such
37
37
  # a way that a usable JSON representation gets
38
38
  # returned
@@ -104,7 +104,8 @@ class FormatParser::MP3Parser
104
104
  # do not tell anything of substance
105
105
  num_audio_channels: first_frame.channels,
106
106
  audio_sample_rate_hz: first_frame.sample_rate,
107
- intrinsics: id3tags_hash.merge(id3tags: tags)
107
+ intrinsics: id3tags_hash.merge(id3tags: tags),
108
+ content_type: MP3_MIME_TYPE,
108
109
  )
109
110
 
110
111
  extra_file_attirbutes = fetch_extra_attributes_from_id3_tags(id3tags_hash)
@@ -3,8 +3,8 @@
3
3
  class FormatParser::OggParser
4
4
  include FormatParser::IOUtils
5
5
 
6
- # Maximum size of an Ogg page
7
6
  MAX_POSSIBLE_PAGE_SIZE = 65307
7
+ OGG_MIME_TYPE = 'audio/ogg'
8
8
 
9
9
  def likely_match?(filename)
10
10
  filename =~ /\.ogg$/i
@@ -45,7 +45,8 @@ class FormatParser::OggParser
45
45
  format: :ogg,
46
46
  audio_sample_rate_hz: sample_rate,
47
47
  num_audio_channels: channels,
48
- media_duration_seconds: duration
48
+ media_duration_seconds: duration,
49
+ content_type: OGG_MIME_TYPE,
49
50
  )
50
51
  end
51
52
 
@@ -1,6 +1,5 @@
1
1
  class FormatParser::PDFParser
2
2
  include FormatParser::IOUtils
3
-
4
3
  # First 9 bytes of a PDF should be in this format, according to:
5
4
  #
6
5
  # https://stackoverflow.com/questions/3108201/detect-if-pdf-file-is-correct-header-pdf
@@ -8,6 +7,7 @@ class FormatParser::PDFParser
8
7
  # There are however exceptions, which are left out for now.
9
8
  #
10
9
  PDF_MARKER = /%PDF-1\.[0-8]{1}/
10
+ PDF_CONTENT_TYPE = 'application/pdf'
11
11
 
12
12
  def likely_match?(filename)
13
13
  filename =~ /\.(pdf|ai)$/i
@@ -18,7 +18,7 @@ class FormatParser::PDFParser
18
18
 
19
19
  return unless safe_read(io, 9) =~ PDF_MARKER
20
20
 
21
- FormatParser::Document.new(format: :pdf)
21
+ FormatParser::Document.new(format: :pdf, content_type: PDF_CONTENT_TYPE)
22
22
  end
23
23
 
24
24
  FormatParser.register_parser new, natures: :document, formats: :pdf, priority: 1
@@ -14,6 +14,7 @@ class FormatParser::PNGParser
14
14
  4 => true, # Grayscale with alpha
15
15
  6 => true,
16
16
  }
17
+ PNG_MIME_TYPE = 'image/png'
17
18
 
18
19
  def likely_match?(filename)
19
20
  filename =~ /\.png$/i
@@ -67,6 +68,7 @@ class FormatParser::PNGParser
67
68
  color_mode: color_mode,
68
69
  has_multiple_frames: has_animation,
69
70
  num_animation_or_video_frames: num_frames,
71
+ content_type: PNG_MIME_TYPE,
70
72
  )
71
73
  end
72
74
 
@@ -2,6 +2,7 @@ class FormatParser::PSDParser
2
2
  include FormatParser::IOUtils
3
3
 
4
4
  PSD_HEADER = [0x38, 0x42, 0x50, 0x53]
5
+ PSD_MIME_TYPE = 'application/x-photoshop'
5
6
 
6
7
  def likely_match?(filename)
7
8
  filename =~ /\.psd$/i # Maybe also PSB at some point
@@ -20,6 +21,7 @@ class FormatParser::PSDParser
20
21
  format: :psd,
21
22
  width_px: w,
22
23
  height_px: h,
24
+ content_type: PSD_MIME_TYPE,
23
25
  )
24
26
  end
25
27
 
@@ -5,6 +5,8 @@ class FormatParser::TIFFParser
5
5
  MAGIC_LE = [0x49, 0x49, 0x2A, 0x0].pack('C4')
6
6
  MAGIC_BE = [0x4D, 0x4D, 0x0, 0x2A].pack('C4')
7
7
  HEADER_BYTES = [MAGIC_LE, MAGIC_BE]
8
+ TIFF_MIME_TYPE = 'image/tiff'
9
+ ARW_MIME_TYPE = 'image/x-sony-arw'
8
10
 
9
11
  def likely_match?(filename)
10
12
  filename =~ /\.tiff?$/i
@@ -14,7 +16,10 @@ class FormatParser::TIFFParser
14
16
  io = FormatParser::IOConstraint.new(io)
15
17
 
16
18
  return unless HEADER_BYTES.include?(safe_read(io, 4))
17
- io.seek(io.pos + 2) # Skip over the offset of the IFD, EXIFR will re-read it anyway
19
+
20
+ # Skip over the offset of the IFD,
21
+ # EXIFR will re-read it anyway
22
+ io.seek(io.pos + 2)
18
23
  return if cr2?(io)
19
24
 
20
25
  # The TIFF scanner in EXIFR is plenty good enough,
@@ -26,14 +31,17 @@ class FormatParser::TIFFParser
26
31
  w = exif_data.width || exif_data.pixel_x_dimension
27
32
  h = exif_data.height || exif_data.pixel_y_dimension
28
33
 
34
+ format = arw?(exif_data) ? :arw : :tif
35
+ mime_type = arw?(exif_data) ? ARW_MIME_TYPE : TIFF_MIME_TYPE
29
36
  FormatParser::Image.new(
30
- format: arw?(exif_data) ? :arw : :tif, # Specify format as arw for Sony ARW format images, else tif
37
+ format: format,
31
38
  width_px: w,
32
39
  height_px: h,
33
40
  display_width_px: exif_data.rotated? ? h : w,
34
41
  display_height_px: exif_data.rotated? ? w : h,
35
42
  orientation: exif_data.orientation_sym,
36
43
  intrinsics: {exif: exif_data},
44
+ content_type: mime_type,
37
45
  )
38
46
  rescue EXIFR::MalformedTIFF
39
47
  nil
@@ -1,6 +1,8 @@
1
1
  class FormatParser::WAVParser
2
2
  include FormatParser::IOUtils
3
3
 
4
+ WAV_MIME_TYPE = 'audio/x-wav'
5
+
4
6
  def likely_match?(filename)
5
7
  filename =~ /\.wav$/i
6
8
  end
@@ -96,6 +98,7 @@ class FormatParser::WAVParser
96
98
  audio_sample_rate_hz: fmt_data[:sample_rate],
97
99
  media_duration_frames: sample_frames,
98
100
  media_duration_seconds: duration_in_seconds,
101
+ content_type: WAV_MIME_TYPE,
99
102
  )
100
103
  end
101
104
 
@@ -5,6 +5,8 @@ class FormatParser::ZIPParser
5
5
  include OfficeFormats
6
6
  include FormatParser::IOUtils
7
7
 
8
+ ZIP_MIME_TYPE = 'application/zip'
9
+
8
10
  def likely_match?(filename)
9
11
  filename =~ /\.(zip|docx|keynote|numbers|pptx|xlsx)$/i
10
12
  end
@@ -25,10 +27,10 @@ class FormatParser::ZIPParser
25
27
  end
26
28
 
27
29
  if office_document?(filenames_set)
28
- office_format = office_file_format_from_entry_set(filenames_set)
29
- FormatParser::Archive.new(nature: :document, format: office_format, entries: entries_archive)
30
+ office_format, mime_type = office_file_format_and_mime_type_from_entry_set(filenames_set)
31
+ FormatParser::Archive.new(nature: :document, format: office_format, entries: entries_archive, content_type: mime_type)
30
32
  else
31
- FormatParser::Archive.new(nature: :archive, format: :zip, entries: entries_archive)
33
+ FormatParser::Archive.new(nature: :archive, format: :zip, entries: entries_archive, content_type: ZIP_MIME_TYPE)
32
34
  end
33
35
  rescue FileReader::Error
34
36
  # This is not a ZIP, or a broken ZIP.
@@ -37,15 +37,15 @@ module FormatParser::ZIPParser::OfficeFormats
37
37
  OFFICE_MARKER_FILES.subset?(filenames_set)
38
38
  end
39
39
 
40
- def office_file_format_from_entry_set(filenames_set)
40
+ def office_file_format_and_mime_type_from_entry_set(filenames_set)
41
41
  if filenames_set.include?('word/document.xml')
42
- :docx
42
+ [:docx, 'application/vnd.openxmlformats-officedocument.wordprocessingml.document']
43
43
  elsif filenames_set.include?('xl/workbook.xml')
44
- :xlsx
44
+ [:xlsx, 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet']
45
45
  elsif filenames_set.include?('ppt/presentation.xml')
46
- :pptx
46
+ [:pptx, 'application/vnd.openxmlformats-officedocument.presentationml.presentation']
47
47
  else
48
- :unknown
48
+ [:unknown, 'application/zip']
49
49
  end
50
50
  end
51
51
  end
@@ -5,6 +5,7 @@ module FormatParser
5
5
  NATURE = :text
6
6
 
7
7
  attr_accessor :format
8
+ attr_accessor :content_type
8
9
 
9
10
  # Only permits assignments via defined accessors
10
11
  def initialize(**attributes)
@@ -23,6 +23,9 @@ module FormatParser
23
23
  # it can be placed here
24
24
  attr_accessor :intrinsics
25
25
 
26
+ # The MIME type of the video
27
+ attr_accessor :content_type
28
+
26
29
  # Only permits assignments via defined accessors
27
30
  def initialize(**attributes)
28
31
  attributes.map { |(k, v)| public_send("#{k}=", v) }
@@ -10,6 +10,7 @@ describe FormatParser::AIFFParser do
10
10
  expect(parse_result.num_audio_channels).to eq(2)
11
11
  expect(parse_result.audio_sample_rate_hz).to be_within(0.01).of(44100)
12
12
  expect(parse_result.media_duration_seconds).to be_within(0.01).of(1.05)
13
+ expect(parse_result.content_type).to eq('audio/x-aiff')
13
14
  end
14
15
 
15
16
  it 'parses a Logic Pro created AIFF sample file having a COMT chunk before a COMM chunk' do
@@ -13,6 +13,8 @@ describe FormatParser::BMPParser do
13
13
  expect(parsed.width_px).to eq(40)
14
14
  expect(parsed.height_px).to eq(27)
15
15
 
16
+ expect(parsed.content_type).to eq('image/bmp')
17
+
16
18
  expect(parsed.intrinsics).not_to be_nil
17
19
  expect(parsed.intrinsics[:vertical_resolution]).to eq(2834)
18
20
  expect(parsed.intrinsics[:horizontal_resolution]).to eq(2834)
@@ -32,6 +34,8 @@ describe FormatParser::BMPParser do
32
34
  expect(parsed.width_px).to eq(1920)
33
35
  expect(parsed.height_px).to eq(1080)
34
36
 
37
+ expect(parsed.content_type).to eq('image/bmp')
38
+
35
39
  expect(parsed.intrinsics).not_to be_nil
36
40
  expect(parsed.intrinsics[:vertical_resolution]).to eq(2835)
37
41
  expect(parsed.intrinsics[:horizontal_resolution]).to eq(2835)
@@ -51,6 +55,8 @@ describe FormatParser::BMPParser do
51
55
  expect(parsed.width_px).to eq(200)
52
56
  expect(parsed.height_px).to eq(200)
53
57
 
58
+ expect(parsed.content_type).to eq('image/bmp')
59
+
54
60
  expect(parsed.intrinsics).not_to be_nil
55
61
  end
56
62
 
@@ -64,6 +70,7 @@ describe FormatParser::BMPParser do
64
70
  expect(parsed.color_mode).to eq(:rgb)
65
71
  expect(parsed.width_px).to eq(40)
66
72
  expect(parsed.height_px).to eq(27)
73
+ expect(parsed.content_type).to eq('image/bmp')
67
74
  expect(parsed.intrinsics[:bits_per_pixel]).to eq(24)
68
75
  expect(parsed.intrinsics[:data_order]).to eq(:normal)
69
76
 
@@ -76,6 +83,7 @@ describe FormatParser::BMPParser do
76
83
  expect(parsed.color_mode).to eq(:rgb)
77
84
  expect(parsed.width_px).to eq(40)
78
85
  expect(parsed.height_px).to eq(27)
86
+ expect(parsed.content_type).to eq('image/bmp')
79
87
  expect(parsed.intrinsics[:bits_per_pixel]).to eq(24)
80
88
  expect(parsed.intrinsics[:data_order]).to eq(:normal)
81
89
  end
@@ -17,6 +17,7 @@ describe FormatParser::CR2Parser do
17
17
  expect(parsed.height_px).to be > 0
18
18
 
19
19
  expect(parsed.orientation).not_to be_nil
20
+ expect(parsed.content_type).to eq('image/x-canon-cr2')
20
21
  end
21
22
  end
22
23
  end
@@ -15,6 +15,7 @@ describe FormatParser::DPXParser do
15
15
  expect(parsed.width_px).to be_between(0, 2048)
16
16
  expect(parsed.height_px).to be_kind_of(Integer)
17
17
  expect(parsed.height_px).to be_between(0, 4000)
18
+ expect(parsed.content_type).to eq('image/x-dpx')
18
19
  end
19
20
  end
20
21
 
@@ -14,6 +14,7 @@ describe FormatParser::FLACParser do
14
14
  expect(parsed.intrinsics).not_to be_nil
15
15
  expect(parsed.media_duration_frames).to eq(33810)
16
16
  expect(parsed.media_duration_seconds).to be_within(0.1).of(0.836)
17
+ expect(parsed.content_type).to eq('audio/x-flac')
17
18
  end
18
19
 
19
20
  it 'decodes and estimates duration for the 16bit FLAC File' do
@@ -17,6 +17,7 @@ describe FormatParser::GIFParser do
17
17
 
18
18
  expect(parsed.height_px).to be_kind_of(Integer)
19
19
  expect(parsed.height_px).to be > 0
20
+ expect(parsed.content_type).to eq('image/gif')
20
21
  end
21
22
  end
22
23
  end
@@ -14,6 +14,7 @@ describe FormatParser::JPEGParser do
14
14
 
15
15
  expect(parsed.height_px).to be_kind_of(Integer)
16
16
  expect(parsed.height_px).to be > 0
17
+ expect(parsed.content_type).to eq('image/jpeg')
17
18
  end
18
19
  end
19
20
  end
@@ -25,6 +25,7 @@ describe FormatParser::M3UParser do
25
25
  expect(parsed_m3u).not_to be_nil
26
26
  expect(parsed_m3u.nature).to eq(:text)
27
27
  expect(parsed_m3u.format).to eq(:m3u)
28
+ expect(parsed_m3u.content_type).to eq('application/vnd.apple.mpegurl')
28
29
  end
29
30
  end
30
31
 
@@ -37,7 +37,7 @@ describe FormatParser::MOOVParser do
37
37
  expect(result.nature).to eq(:audio)
38
38
  expect(result.media_duration_seconds).to be_kind_of(Float)
39
39
  expect(result.media_duration_seconds).to be > 0
40
-
40
+ expect(result.content_type).to be_kind_of(String)
41
41
  expect(result.intrinsics).not_to be_nil
42
42
  end
43
43
  end
@@ -52,6 +52,7 @@ describe FormatParser::MOOVParser do
52
52
  expect(result.height_px).to be > 0
53
53
  expect(result.media_duration_seconds).to be_kind_of(Float)
54
54
  expect(result.media_duration_seconds).to be > 0
55
+ expect(result.content_type).to eq('video/mp4')
55
56
 
56
57
  expect(result.intrinsics).not_to be_nil
57
58
  end
@@ -67,6 +68,7 @@ describe FormatParser::MOOVParser do
67
68
  expect(result.height_px).to be > 0
68
69
  expect(result.media_duration_seconds).to be_kind_of(Float)
69
70
  expect(result.media_duration_seconds).to be > 0
71
+ expect(result.content_type).to eq('video/mp4')
70
72
 
71
73
  expect(result.intrinsics).not_to be_nil
72
74
  end
@@ -79,6 +81,7 @@ describe FormatParser::MOOVParser do
79
81
  expect(result).not_to be_nil
80
82
  expect(result.nature).to eq(:audio)
81
83
  expect(result.format).to eq(:m4a)
84
+ expect(result.content_type).to eq('audio/mp4')
82
85
  end
83
86
 
84
87
  it 'parses a MOV file and provides the necessary metadata' do
@@ -23,6 +23,7 @@ describe FormatParser::MP3Parser do
23
23
 
24
24
  expect(parsed.nature).to eq(:audio)
25
25
  expect(parsed.format).to eq(:mp3)
26
+ expect(parsed.content_type).to eq('audio/mpeg')
26
27
  expect(parsed.num_audio_channels).to eq(2)
27
28
  expect(parsed.audio_sample_rate_hz).to eq(48000)
28
29
  expect(parsed.intrinsics).not_to be_nil
@@ -6,6 +6,7 @@ describe FormatParser::OggParser do
6
6
 
7
7
  expect(parse_result.nature).to eq(:audio)
8
8
  expect(parse_result.format).to eq(:ogg)
9
+ expect(parse_result.content_type).to eq('audio/ogg')
9
10
  expect(parse_result.num_audio_channels).to eq(1)
10
11
  expect(parse_result.audio_sample_rate_hz).to eq(16000)
11
12
  expect(parse_result.media_duration_seconds).to be_within(0.01).of(2973.95)
@@ -17,6 +17,7 @@ describe FormatParser::PDFParser do
17
17
  expect(parsed_pdf).not_to be_nil
18
18
  expect(parsed_pdf.nature).to eq(:document)
19
19
  expect(parsed_pdf.format).to eq(:pdf)
20
+ expect(parsed_pdf.content_type).to eq('application/pdf')
20
21
  end
21
22
  end
22
23
 
@@ -15,6 +15,7 @@ describe FormatParser::PNGParser do
15
15
 
16
16
  expect(parsed.height_px).to be_kind_of(Integer)
17
17
  expect(parsed.height_px).to be > 0
18
+ expect(parsed.content_type).to eq('image/png')
18
19
  end
19
20
  end
20
21
  end
@@ -15,6 +15,7 @@ describe FormatParser::PSDParser do
15
15
 
16
16
  expect(parsed.height_px).to be_kind_of(Integer)
17
17
  expect(parsed.height_px).to be > 0
18
+ expect(parsed.content_type).to eq('application/x-photoshop')
18
19
  end
19
20
  end
20
21
  end
@@ -59,6 +59,7 @@ describe FormatParser::TIFFParser do
59
59
  expect(parsed.width_px).to eq(7952)
60
60
  expect(parsed.height_px).to eq(5304)
61
61
  expect(parsed.intrinsics[:exif]).not_to be_nil
62
+ expect(parsed.content_type).to eq('image/x-sony-arw')
62
63
  end
63
64
 
64
65
  describe 'correctly extracts dimensions from various TIFF flavors of the same file' do
@@ -9,6 +9,7 @@ describe FormatParser::WAVParser do
9
9
 
10
10
  expect(parse_result.nature).to eq(:audio)
11
11
  expect(parse_result.format).to eq(:wav)
12
+ expect(parse_result.content_type).to eq('audio/x-wav')
12
13
  end
13
14
  end
14
15
 
@@ -14,6 +14,7 @@ describe FormatParser::ZIPParser do
14
14
  expect(result).not_to be_nil
15
15
 
16
16
  expect(result.format).to eq(:zip)
17
+ expect(result.content_type).to eq('application/zip')
17
18
  expect(result.nature).to eq(:archive)
18
19
  expect(result.entries.length).to eq(0xFFFF + 1)
19
20
 
@@ -58,6 +59,7 @@ describe FormatParser::ZIPParser do
58
59
  result = subject.call(fi_io)
59
60
  expect(result.nature).to eq(:document)
60
61
  expect(result.format).to eq(:docx)
62
+ expect(result.content_type).to eq('application/vnd.openxmlformats-officedocument.wordprocessingml.document')
61
63
 
62
64
  fixture_path = fixtures_dir + '/ZIP/sample-docx.docx'
63
65
  fi_io = File.open(fixture_path, 'rb')
metadata CHANGED
@@ -1,15 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: format_parser
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.26.0
4
+ version: 0.27.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Noah Berman
8
8
  - Julik Tarkhanov
9
- autorequire:
9
+ autorequire:
10
10
  bindir: exe
11
11
  cert_chain: []
12
- date: 2021-01-08 00:00:00.000000000 Z
12
+ date: 2021-01-26 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: ks
@@ -280,7 +280,7 @@ licenses:
280
280
  - MIT (Hippocratic)
281
281
  metadata:
282
282
  allowed_push_host: https://rubygems.org
283
- post_install_message:
283
+ post_install_message:
284
284
  rdoc_options: []
285
285
  require_paths:
286
286
  - lib
@@ -296,7 +296,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
296
296
  version: '0'
297
297
  requirements: []
298
298
  rubygems_version: 3.0.3
299
- signing_key:
299
+ signing_key:
300
300
  specification_version: 4
301
301
  summary: A library for efficient parsing of file metadata
302
302
  test_files: []