format_parser 0.29.1 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 75ee83f55840e3031d4d60d8dc07ca038812188613e2b740079e1c965efb2886
4
- data.tar.gz: 31c3ee84434560c18e6ea74a23160b909e6f880f52b2ed6f0e888e847c557bd9
3
+ metadata.gz: d8bdd6daa59e43bbe6033562b804d3b1b9685c6e2aa5ef3f8d527c74516e1f6c
4
+ data.tar.gz: d6035f0b3c819085ffd23c4988e48145976ba1f98547d16589b2a4b4aa3e7aa7
5
5
  SHA512:
6
- metadata.gz: 536cfb1bac7926f56ba760959d7c5a0905d3b2b0944b16248b6c81be00b722dad894a8d6d5773134fb0471e42d016a20be15e3d5a01c671f0cc65658f2fc05b4
7
- data.tar.gz: cb3f73df051b8612cb6d0e1a4e55c045e461bf2c5e4667dd6e461e779b1f39d01be8d70d084e252e5198a966ac590129286811f5822808a0c980c2ad72a087a1
6
+ metadata.gz: 1a2f6243d295589972c63e45b719f19a5761c872103c32bfaacaef9cee9b85551c18ddaaf3c4095567c358958e7b7bf78800fc3e5a8e6c9f774eab383dc3160c
7
+ data.tar.gz: 73ba4f1099ccb4c490b50a8f531be843679159a6417980b2a61b905724905cf6b7e884127b66096e2237982dc6743e7f057bea0062472c1779e4d754dd5388ff
@@ -16,9 +16,6 @@ jobs:
16
16
  - 2.7
17
17
  - 2.6
18
18
  - 2.5
19
- - 2.4
20
- - 2.3
21
- - 2.2
22
19
  - jruby
23
20
  steps:
24
21
  - name: Checkout
@@ -65,12 +62,11 @@ jobs:
65
62
  - 2.7
66
63
  - 2.6
67
64
  - 2.5
68
- - 2.4
69
- - 2.3
70
- - 2.2
71
65
  - jruby
72
66
  experimental: [false]
73
67
  include:
68
+ - ruby: 3.1
69
+ experimental: true
74
70
  - ruby: 3.0
75
71
  experimental: true
76
72
  steps:
data/CHANGELOG.md CHANGED
@@ -1,3 +1,13 @@
1
+ ## 1.2.0
2
+ * Add support for `codecs` in moov_parser for video metadata
3
+
4
+ ## 1.1.0
5
+ * Add support for `frame_rate` in moov_parser
6
+
7
+ ## 1.0.0
8
+ * Dropping support for Ruby 2.2.X, 2.3.X and 2.4.X
9
+ * MP3: Fix negative length reads in edge cases by bumping `id3tag` version to `v0.14.2`
10
+
1
11
  ## 0.29.1
2
12
  * Fix handling of 200 responses with `parse_http` as well as handling of very small responses which do not need range access
3
13
 
data/Gemfile CHANGED
@@ -1,4 +1,9 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
+ gem 'ruby-debug-ide'
4
+ gem 'debase'
5
+ gem 'solargraph', group: :development
6
+ gem 'pry', group: :development
7
+
3
8
  # Gem dependencies specified in the gemspec
4
9
  gemspec
@@ -32,7 +32,7 @@ Gem::Specification.new do |spec|
32
32
 
33
33
  spec.add_dependency 'ks', '~> 0.0'
34
34
  spec.add_dependency 'exifr', '~> 1', '>= 1.3.8'
35
- spec.add_dependency 'id3tag', '~> 0.14'
35
+ spec.add_dependency 'id3tag', '~> 0.14', '>= 0.14.2'
36
36
  spec.add_dependency 'faraday', '~> 0.13'
37
37
  spec.add_dependency 'faraday_middleware', '~> 0.14'
38
38
  spec.add_dependency 'measurometer', '~> 1'
@@ -1,3 +1,3 @@
1
1
  module FormatParser
2
- VERSION = '0.29.1'
2
+ VERSION = '1.2.0'
3
3
  end
@@ -72,6 +72,7 @@ class FormatParser::MOOVParser::Decoder
72
72
 
73
73
  trak_atoms.find do |trak_atom|
74
74
  hdlr_atom = find_first_atom_by_path([trak_atom], 'trak', 'mdia', 'hdlr')
75
+ next if hdlr_atom.nil?
75
76
  hdlr_atom.atom_fields[:component_type] == 'mhlr' && hdlr_atom.atom_fields[:component_subtype] == 'vide'
76
77
  end
77
78
  end
@@ -111,6 +112,41 @@ class FormatParser::MOOVParser::Decoder
111
112
  }
112
113
  end
113
114
 
115
+ def parse_stts_atom(io, _)
116
+ version = read_byte_value(io)
117
+ is_v1 = version == 1
118
+ stts = {
119
+ version: version,
120
+ flags: read_bytes(io, 3),
121
+ number_of_entries: is_v1 ? read_64bit_uint(io) : read_32bit_uint(io),
122
+ entries: []
123
+ }
124
+ stts[:number_of_entries].times {
125
+ stts[:entries] << {
126
+ sample_count: read_32bit_uint(io),
127
+ sample_duration: read_32bit_uint(io)
128
+ }
129
+ }
130
+ stts
131
+ end
132
+
133
+ def parse_stsd_atom(io, _)
134
+ version = read_byte_value(io)
135
+ is_v1 = version == 1
136
+ stsd = {
137
+ version: version,
138
+ flags: read_bytes(io, 3),
139
+ number_of_entries: is_v1 ? read_64bit_uint(io) : read_32bit_uint(io),
140
+ codecs: []
141
+ }
142
+ stsd[:number_of_entries].times {
143
+ codec_length = read_32bit_uint(io)
144
+ stsd[:codecs] << read_bytes(io, 4)
145
+ io.seek(io.pos + codec_length - 8) # 8 bytes is the header length containing the codec length and the codec name that we just did read
146
+ }
147
+ stsd
148
+ end
149
+
114
150
  def parse_mdhd_atom(io, _)
115
151
  version = read_byte_value(io)
116
152
  is_v1 = version == 1
@@ -48,9 +48,9 @@ class FormatParser::MOOVParser
48
48
  width, height = parse_dimensions(decoder, atom_tree)
49
49
 
50
50
  # Try to find the "topmost" duration (respecting edits)
51
- if mdhd = decoder.find_first_atom_by_path(atom_tree, 'moov', 'mvhd')
52
- timescale = mdhd.field_value(:tscale)
53
- duration = mdhd.field_value(:duration)
51
+ if mvhd = decoder.find_first_atom_by_path(atom_tree, 'moov', 'mvhd')
52
+ timescale = mvhd.field_value(:tscale)
53
+ duration = mvhd.field_value(:duration)
54
54
  media_duration_s = duration / timescale.to_f
55
55
  end
56
56
 
@@ -68,9 +68,11 @@ class FormatParser::MOOVParser
68
68
  format: format_from_moov_type(file_type),
69
69
  width_px: width,
70
70
  height_px: height,
71
+ frame_rate: parse_time_to_sample_atom(decoder, atom_tree)&.truncate(2),
71
72
  media_duration_seconds: media_duration_s,
72
73
  content_type: MP4_MIXED_MIME_TYPE,
73
- intrinsics: atom_tree,
74
+ codecs: parse_sample_description_atom(decoder, atom_tree),
75
+ intrinsics: atom_tree
74
76
  )
75
77
  end
76
78
  end
@@ -115,5 +117,51 @@ class FormatParser::MOOVParser
115
117
  maybe_atom_size >= minimum_ftyp_atom_size && maybe_ftyp_atom_signature == 'ftyp'
116
118
  end
117
119
 
120
+ # Sample information is found in the 'time-to-sample' stts atom.
121
+ # The media atom mdhd is needed too in order to get the movie timescale
122
+ def parse_time_to_sample_atom(decoder, atom_tree)
123
+ video_trak_atom = decoder.find_video_trak_atom(atom_tree)
124
+
125
+ stts = if video_trak_atom
126
+ decoder.find_first_atom_by_path([video_trak_atom], 'trak', 'mdia', 'minf', 'stbl', 'stts')
127
+ else
128
+ decoder.find_first_atom_by_path(atom_tree, 'moov', 'trak', 'mdia', 'minf', 'stbl', 'stts')
129
+ end
130
+
131
+ mdhd = if video_trak_atom
132
+ decoder.find_first_atom_by_path([video_trak_atom], 'trak', 'mdia', 'mdhd')
133
+ else
134
+ decoder.find_first_atom_by_path(atom_tree, 'moov', 'trak', 'mdia', 'mdhd')
135
+ end
136
+
137
+ if stts && mdhd
138
+ timescale = mdhd.atom_fields[:tscale]
139
+ sample_duration = stts.field_value(:entries).first[:sample_duration]
140
+ if timescale.nil? || timescale == 0 || sample_duration.nil? || sample_duration == 0
141
+ nil
142
+ else
143
+ timescale.to_f / sample_duration
144
+ end
145
+ else
146
+ nil
147
+ end
148
+ end
149
+
150
+ def parse_sample_description_atom(decoder, atom_tree)
151
+ video_trak_atom = decoder.find_video_trak_atom(atom_tree)
152
+
153
+ stsd = if video_trak_atom
154
+ decoder.find_first_atom_by_path([video_trak_atom], 'trak', 'mdia', 'minf', 'stbl', 'stsd')
155
+ else
156
+ decoder.find_first_atom_by_path(atom_tree, 'moov', 'trak', 'mdia', 'minf', 'stbl', 'stsd')
157
+ end
158
+
159
+ if stsd
160
+ stsd.field_value(:codecs)
161
+ else
162
+ nil
163
+ end
164
+ end
165
+
118
166
  FormatParser.register_parser new, natures: :video, formats: FTYP_MAP.values, priority: 1
119
167
  end
data/lib/video.rb CHANGED
@@ -8,9 +8,13 @@ module FormatParser
8
8
 
9
9
  attr_accessor :height_px
10
10
 
11
+ attr_accessor :frame_rate
12
+
11
13
  # Type of the file (e.g :mp3)
12
14
  attr_accessor :format
13
15
 
16
+ attr_accessor :codecs
17
+
14
18
  # Duration of the media object (be it audio or video) in seconds,
15
19
  # as a Float
16
20
  attr_accessor :media_duration_seconds
@@ -53,19 +53,22 @@ end
53
53
  require 'minitest/autorun'
54
54
  require 'open-uri'
55
55
 
56
+ fixtures_dir = File.join(File.dirname(__FILE__), '../../fixtures')
57
+
56
58
  describe User do
57
59
  describe "profile_picture's metadatas" do
58
60
  it 'parse metadatas with format_parser' do
61
+ fixture_path = fixtures_dir + '/PNG/cat.png'
59
62
  user = User.create
60
63
  user.profile_picture.attach(
61
64
  filename: 'cat.png',
62
- io: URI.open('https://freesvg.org/img/1416155153.png')
65
+ io: File.open(fixture_path, 'rb')
63
66
  )
64
67
 
65
68
  user.profile_picture.analyze
66
69
 
67
- _(user.profile_picture.metadata[:width_px]).must_equal 500
68
- _(user.profile_picture.metadata[:height_px]).must_equal 296
70
+ _(user.profile_picture.metadata[:width_px]).must_equal 600
71
+ _(user.profile_picture.metadata[:height_px]).must_equal 600
69
72
  _(user.profile_picture.metadata[:color_mode]).must_equal 'rgba'
70
73
  end
71
74
  end
@@ -94,6 +94,7 @@ describe FormatParser::MOOVParser do
94
94
  expect(result.format).to eq(:mov)
95
95
  expect(result.width_px).to eq(1920)
96
96
  expect(result.height_px).to eq(1080)
97
+ expect(result.codecs).to eq(['apcn'])
97
98
  end
98
99
 
99
100
  it 'parses an MP4 video file and provides the necessary metadata' do
@@ -106,6 +107,8 @@ describe FormatParser::MOOVParser do
106
107
  expect(result.format).to eq(:mov)
107
108
  expect(result.width_px).to eq(160)
108
109
  expect(result.height_px).to eq(90)
110
+ expect(result.frame_rate).to eq(14.98)
111
+ expect(result.codecs).to eq(['avc1'])
109
112
  end
110
113
 
111
114
  it 'provides filename hints' do
@@ -122,6 +125,7 @@ describe FormatParser::MOOVParser do
122
125
  expect(result.format).to eq(:mov)
123
126
  expect(result.width_px).to eq(640)
124
127
  expect(result.height_px).to eq(360)
128
+ expect(result.frame_rate).to eq(30)
125
129
  end
126
130
 
127
131
  it 'does not raise error when a meta atom has size 0' do
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.29.1
4
+ version: 1.2.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-09-10 00:00:00.000000000 Z
12
+ date: 2022-04-07 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: ks
@@ -52,6 +52,9 @@ dependencies:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
54
  version: '0.14'
55
+ - - ">="
56
+ - !ruby/object:Gem::Version
57
+ version: 0.14.2
55
58
  type: :runtime
56
59
  prerelease: false
57
60
  version_requirements: !ruby/object:Gem::Requirement
@@ -59,6 +62,9 @@ dependencies:
59
62
  - - "~>"
60
63
  - !ruby/object:Gem::Version
61
64
  version: '0.14'
65
+ - - ">="
66
+ - !ruby/object:Gem::Version
67
+ version: 0.14.2
62
68
  - !ruby/object:Gem::Dependency
63
69
  name: faraday
64
70
  requirement: !ruby/object:Gem::Requirement
@@ -294,7 +300,7 @@ licenses:
294
300
  - MIT (Hippocratic)
295
301
  metadata:
296
302
  allowed_push_host: https://rubygems.org
297
- post_install_message:
303
+ post_install_message:
298
304
  rdoc_options: []
299
305
  require_paths:
300
306
  - lib
@@ -309,8 +315,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
309
315
  - !ruby/object:Gem::Version
310
316
  version: '0'
311
317
  requirements: []
312
- rubygems_version: 3.0.3
313
- signing_key:
318
+ rubygems_version: 3.3.4
319
+ signing_key:
314
320
  specification_version: 4
315
321
  summary: A library for efficient parsing of file metadata
316
322
  test_files: []