From 743f03876856dfbc5b4b835de0320e60e91a78a1 Mon Sep 17 00:00:00 2001 From: Max <674778+01max@users.noreply.github.com> Date: Wed, 5 Sep 2018 00:54:33 +0200 Subject: [PATCH] Fix & improve MediaInfo::Tracks::Attributes.standardize_to_milliseconds. (#30) --- README.md | 68 +++++++++++++++++++++-------------------- lib/mediainfo/tracks.rb | 36 ++++++++++++---------- spec/mediainfo_spec.rb | 4 +-- 3 files changed, 57 insertions(+), 51 deletions(-) diff --git a/README.md b/README.md index 08f3ba3..a187f15 100644 --- a/README.md +++ b/README.md @@ -3,9 +3,9 @@ MediaInfo is a class wrapping [the mediainfo CLI](http://mediainfo.sourceforge.net). ## Installation - + $ gem install mediainfo - + ## Usage #### Parsing raw XML @@ -16,28 +16,28 @@ MediaInfo is a class wrapping [the mediainfo CLI](http://mediainfo.sourceforge.n media_info = MediaInfo.from('http://techslides.com/demos/sample-videos/small.mp4') You can specify an alternate path for the MediaInfo Binary: - + ENV['MEDIAINFO_PATH'] = "/opt/local/bin/mediainfo" - + Once you have an MediaInfo object, you can start inspecting tracks: - + media_info.track_types => ['general','video','audio'] media_info.track_types.count => 3 media_info.video? => true media_info.image? => nil media_info.image.filesize => MethodNotFound exception - -When inspecting specific types of tracks, you have a couple general API options. The -first approach assumes one track of a given type, a common scenario in many video files, + +When inspecting specific types of tracks, you have a couple general API options. The +first approach assumes one track of a given type, a common scenario in many video files, for example: - + media_info.video.count => 1 media_info.video.duration => 120 (seconds) - + Sometimes you'll have more than one track of a given type: - The first track type name, or any track type with 1 will not contain '1' - - + + media_info.track_types => ['general','video','video2','audio','other','other2'] media_info.track_types.count => 5 media_info.video? => true @@ -54,14 +54,14 @@ Sometimes you'll have more than one track of a given type: media_info.video.encoded_date.class => Time media_info.video2.duration.class => Integer media_info.video.display_aspect_ratio.class => Float - + - Any track attribute name with "date" and matching /\d-/ will be converted using Time.parse: - + media_info.video.encoded_date => 2018-03-30 12:12:08 -0400 media_info.video.customdate => 2016-02-10 01:00:00 -0600 - -- .duration and .overall_duration will be returned as milliseconds AS LONG AS the Duration and Overall_Duration match one of the expected units: + +- .duration and .overall_duration will be returned as milliseconds AS LONG AS the Duration and Overall_Duration match one of the expected units (each separated by a space or not): - h (\15h\) (hour) - hour (\15hour\) - mn (\15hour 6mn\) (minute) @@ -70,25 +70,27 @@ Sometimes you'll have more than one track of a given type: - s (\15hour 6min 59s\) (second) - sec (\15hour 6min 59sec\) (second) - ms (\15hour 6min 59sec 301ms\) (milliseconds) + - in the form of a Float (as for iphone mov files) - [Submit an issue to add more!](https://github.com/greatseth/mediainfo/issues) - - - media_info.video.duration => 9855000 (\15s 164ms\) - media_info.video.duration => 17196000 (\36s 286ms\) + + + media_info.video.duration => 15164 (\15s 164ms\) + media_info.video.duration => 36286 (\36s 286ms\) + media_info.video.duration => 123456 (\123.456\) - We standardize the naming of several Attributes: - You can review lib/attribute_standardization_rules.yml to see them all - + media_info.video.bit_rate => nil (\41.2 Mbps\) media_info.video.bitrate => "41.2 Mbps" (\41.2 Mbps\) media_info.general.filesize => "11.5 MiB" (\11.5 MiB\ - + In order to support all possible MediaInfo variations, you may see the following situation: media_info.track_types => ['general','video','video5','audio','other','other2'] - + The track type media_info.video5 is available, but no video2, 3, and 4. This is because the MediaInfo from the video has: @@ -99,7 +101,7 @@ The track type media_info.video5 is available, but no video2, 3, and 4. This is ... *The ID will take priority for labeling.* Else if no ID exists, you'll see consecutive numbering for duplicate tracks in the Media. - + Any second level attributes are also available: MediaInfo.from('~/Desktop/test.mov').general.extra @@ -110,17 +112,17 @@ Any second level attributes are also available: @com_apple_quicktime_model=0, @com_apple_quicktime_software=11.2> -REXML is used as the XML parser by default. If you'd like, you can +REXML is used as the XML parser by default. If you'd like, you can configure Mediainfo to use Nokogiri instead: - * define the `MEDIAINFO_XML_PARSER` environment variable to be the - name of the parser as you'd pass to a :gem or :require call. - + * define the `MEDIAINFO_XML_PARSER` environment variable to be the + name of the parser as you'd pass to a :gem or :require call. + e.g. `export MEDIAINFO_XML_PARSER=nokogiri` - -Once you've got an instance setup, you can call numerous methods to get -a variety of information about a file. Some attributes may be present -for some files where others are not, but any supported attribute + +Once you've got an instance setup, you can call numerous methods to get +a variety of information about a file. Some attributes may be present +for some files where others are not, but any supported attribute should at least return `nil`. ## Requirements @@ -136,4 +138,4 @@ should at least return `nil`. * Ned Campion - [http://github.com/nedcampion](http://github.com/nedcampion) * Daniel Jagszent - [http://github.com/d--j](http://github.com/d--j) * Robert Mrasek - [http://github.com/derobo](http://github.com/derobo) -* Nathan Pierce - [http://github.com/NorseGaud](http://github.com/NorseGaud) \ No newline at end of file +* Nathan Pierce - [http://github.com/NorseGaud](http://github.com/NorseGaud) diff --git a/lib/mediainfo/tracks.rb b/lib/mediainfo/tracks.rb index 636fbf8..be4963a 100644 --- a/lib/mediainfo/tracks.rb +++ b/lib/mediainfo/tracks.rb @@ -119,23 +119,27 @@ def self.sanitize_element_value(param) end def self.standardize_to_milliseconds(value) - # TODO iphone video has a float as the duration - # UPDATE THE README IF YOU'RE CHANGING THIS - milliseconds = 0 - value.scan(/\d+\s?\w+/).each do |chunk| - case chunk - when /\d+\s?h/ then milliseconds += chunk.to_i * 60 * 60 * 1000 - when /\d+\s?hour/ then milliseconds += chunk.to_i * 60 * 60 * 1000 - when /\d+\s?m/ then milliseconds += chunk.to_i * 60 * 1000 - when /\d+\s?mn/ then milliseconds += chunk.to_i * 60 * 1000 - when /\d+\s?min/ then milliseconds += chunk.to_i * 60 * 1000 - when /\d+\s?s/ then milliseconds += chunk.to_i * 1000 - when /\d+\s?sec/ then milliseconds += chunk.to_i * 1000 - when /\d+\s?ms/ then milliseconds += chunk.to_i - end + return standardize_float_to_milliseconds(value.to_f) if value.to_f.to_s == value.to_s + return standardize_string_to_milliseconds(value) + value + end + + def self.standardize_string_to_milliseconds(v, base_msec = 0) + v.scan(/\d+\s?\D+/).each do |chunk| + base_msec += case chunk + when /\d+\s?ms/ then chunk.to_i + when /\d+\s?s(ec)?/ then chunk.to_i * 1000 + when /\d+\s?m(i?n)?/ then chunk.to_i * 60 * 1000 + when /\d+\s?h(our)?/ then chunk.to_i * 60 * 60 * 1000 + end.to_i end - milliseconds = value if milliseconds == 0 # We don't raise anymore. It's much better for the gem to work, returning the original MediaInfo attribute, than raise. - return milliseconds + # We don't raise anymore. It's much better for the gem to work, + # returning the original MediaInfo attribute, than raise. + base_msec == 0 ? v : base_msec + end + + def self.standardize_float_to_milliseconds(v) + (v*1000).to_i end end diff --git a/spec/mediainfo_spec.rb b/spec/mediainfo_spec.rb index 5f73d61..1980c4c 100644 --- a/spec/mediainfo_spec.rb +++ b/spec/mediainfo_spec.rb @@ -167,11 +167,11 @@ # REXML # TODO (see tracks.rb under standardize_to_milliseconds) expect(MediaInfo.obtain(::File.open('./spec/fixtures/xml/iphone6+_video.mov.xml').read).video.duration).to be_a(Integer) expect(MediaInfo.from(::File.open('./spec/fixtures/xml/AwayWeGo_24fps.mov.xml').read).video.duration).to be_a(Integer) - expect(MediaInfo.from(::File.open('./spec/fixtures/xml/multiple_streams_no_stream_id_three_video.xml').read).video100.duration).to eq(10204000) + expect(MediaInfo.from(::File.open('./spec/fixtures/xml/multiple_streams_no_stream_id_three_video.xml').read).video100.duration).to eq(4170) # NOKOGIRI ENV['MEDIAINFO_XML_PARSER'] = 'nokogiri' expect(MediaInfo.from(::File.open('./spec/fixtures/xml/hats.3gp.xml').read).video.duration).to be_a(Integer) - expect(MediaInfo.from(::File.open('./spec/fixtures/xml/vimeo.57652.avi.xml').read).video.duration).to eq(9855000) + expect(MediaInfo.from(::File.open('./spec/fixtures/xml/vimeo.57652.avi.xml').read).video.duration).to eq(15164) ENV['MEDIAINFO_XML_PARSER'] = nil end