From 899d2078deb21448b4573e015572d4ddfba22504 Mon Sep 17 00:00:00 2001 From: aidewoode Date: Thu, 6 Jun 2024 15:19:00 +0800 Subject: [PATCH] Parse Ogg duration and bitrate lazily Co-authored-by: obskyr --- README.md | 10 +++++++--- lib/wahwah/helper.rb | 8 ++------ lib/wahwah/ogg_tag.rb | 16 ++++++++++++++-- lib/wahwah/tag.rb | 4 ++-- test/wahwah/ogg_tag_test.rb | 22 ++++++++++++++++++++++ 5 files changed, 47 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 15b2fa5..b66a84b 100644 --- a/README.md +++ b/README.md @@ -36,10 +36,15 @@ WahWah is so easy to use. ```ruby require "wahwah" -# Get metadata from an audio file - +# Get metadata from an audio file path tag = WahWah.open("/files/example.wav") +# Or from IO like object +File.open("/files/example.wav") do |file| + tag = WahWah.open(file) +end + +# Supported attributes tag.title # => "song title" tag.artist # => "artist name" tag.album # => "album name" @@ -62,6 +67,5 @@ tag.images # => [{ :type => :cover_front, :mime_type => 'image/jpeg', :data # Get all support formats - WahWah.support_formats # => ["mp3", "ogg", "oga", "opus", "wav", "flac", "wma", "m4a"] ``` diff --git a/lib/wahwah/helper.rb b/lib/wahwah/helper.rb index d2f0aac..98392d4 100644 --- a/lib/wahwah/helper.rb +++ b/lib/wahwah/helper.rb @@ -28,14 +28,10 @@ def self.split_with_terminator(string, terminator_size) def self.file_format(io) if io.respond_to?(:path) && io.path && !io.path.empty? file_format = file_format_from_extension io.path - # Using OpenURI, the file *has* a path, but it's without an extension. - # To support that case, we fall back on signature detection. - file_format = file_format_from_signature io if file_format.empty? - else - file_format = file_format_from_signature io + return file_format unless file_format.nil? || file_format.empty? end - file_format + file_format_from_signature io end def self.byte_string_to_guid(byte_string) diff --git a/lib/wahwah/ogg_tag.rb b/lib/wahwah/ogg_tag.rb index 6e993fd..c8b5151 100644 --- a/lib/wahwah/ogg_tag.rb +++ b/lib/wahwah/ogg_tag.rb @@ -17,6 +17,20 @@ class OggTag < Tag :sample_rate, :lyrics + # Oggs require reading to the end of the file in order to calculate their + # duration (as it's not stored in any header or metadata). Thus, if the + # IO like object supplied to WahWah is a streaming download, getting the + # duration would download the entire audio file, which may not be + # desirable. Making it lazy in this manner allows the user to avoid that. + def duration + @duration ||= parse_duration + end + + # Requires duration to calculate, so lazy as well. + def bitrate + @bitrate ||= parse_bitrate + end + private def packets @@ -41,8 +55,6 @@ def parse Ogg::FlacTag.new(identification_packet, comment_packet) end - @duration = parse_duration - @bitrate = parse_bitrate @bit_depth = parse_bit_depth end diff --git a/lib/wahwah/tag.rb b/lib/wahwah/tag.rb index 3e95151..b89ad44 100644 --- a/lib/wahwah/tag.rb +++ b/lib/wahwah/tag.rb @@ -3,7 +3,7 @@ module WahWah class Tag INTEGER_ATTRIBUTES = %i[disc disc_total track track_total] - INSPECT_ATTRIBUTES = %i[title artist album albumartist composer track track_total genre year disc disc_total duration bitrate sample_rate bit_depth] + INSPECT_ATTRIBUTES = %i[title artist album albumartist composer track track_total genre year disc disc_total] attr_reader( :title, @@ -51,7 +51,7 @@ def inspect def images return @images_data if @images_data.empty? - @images_data.map do |data| + @images ||= @images_data.map do |data| parse_image_data(data) end end diff --git a/test/wahwah/ogg_tag_test.rb b/test/wahwah/ogg_tag_test.rb index f8887d4..fa2b686 100644 --- a/test/wahwah/ogg_tag_test.rb +++ b/test/wahwah/ogg_tag_test.rb @@ -68,4 +68,26 @@ def test_flac_tag_file assert_equal "I'm feeling tragic like I'm Marlon Brando", tag.lyrics end end + + def test_lazy_duration + File.open("test/files/vorbis_tag.ogg", "rb") do |file| + tag = WahWah::OggTag.new(file) + assert tag.instance_variable_get(:@file_io).pos < file.size + assert_nil tag.instance_variable_get(:@duration) + + tag.duration + assert_equal 8.0, tag.instance_variable_get(:@duration) + end + end + + def test_lazy_bitrate + File.open("test/files/vorbis_tag.ogg", "rb") do |file| + tag = WahWah::OggTag.new(file) + assert tag.instance_variable_get(:@file_io).pos < file.size + assert_nil tag.instance_variable_get(:@bitrate) + + tag.bitrate + assert_equal 192, tag.instance_variable_get(:@bitrate) + end + end end