id3tag 0.1.0 → 0.1.1
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.
- checksums.yaml +8 -8
- data/README.md +10 -2
- data/VERSION +1 -1
- data/id3tag.gemspec +7 -2
- data/lib/id3tag.rb +2 -3
- data/lib/id3tag/audio_file.rb +0 -10
- data/lib/id3tag/frame_id_advisor.rb +7 -1
- data/lib/id3tag/id3_v1_frame_parser.rb +1 -1
- data/lib/id3tag/id3_v2_tag_header.rb +4 -0
- data/lib/id3tag/string_util.rb +12 -0
- data/lib/id3tag/tag.rb +34 -51
- data/lib/id3tag/unsynchronization.rb +45 -0
- data/spec/features/can_read_non_audio_files_spec.rb +15 -0
- data/spec/features/can_read_tag_v1_spec.rb +15 -0
- data/spec/fixtures/id3v1_and_v2.mp3 +0 -0
- data/spec/fixtures/id3v1_with_track_nr.mp3 +0 -0
- data/spec/fixtures/id3v1_without_track_nr.mp3 +0 -0
- data/spec/lib/id3tag/audio_file_spec.rb +18 -3
- data/spec/lib/id3tag/frames/v1/comments_frame_spec.rb +8 -0
- data/spec/lib/id3tag/frames/v2/frame_fabricator_spec.rb +56 -0
- data/spec/lib/id3tag/frames/v2/genre_frame/genre_parser_spec.rb +13 -0
- data/spec/lib/id3tag/frames/v2/genre_frame_spec.rb +26 -8
- data/spec/lib/id3tag/id3_v1_frame_parser_spec.rb +0 -4
- data/spec/lib/id3tag/id3_v2_frame_parser_spec.rb +17 -12
- data/spec/lib/id3tag/id3_v2_tag_header_spec.rb +5 -0
- data/spec/lib/id3tag/string_util_spec.rb +28 -0
- data/spec/lib/id3tag/tag_spec.rb +231 -47
- metadata +7 -2
checksums.yaml
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
---
|
2
2
|
!binary "U0hBMQ==":
|
3
3
|
metadata.gz: !binary |-
|
4
|
-
|
4
|
+
NzNiYTE0YzFlYTA3ZGVhZDVhMDA4ZWUwNTU4ODcxNTc0ODQzMDg4MQ==
|
5
5
|
data.tar.gz: !binary |-
|
6
|
-
|
6
|
+
ODNiYzM1NTUzNDg4YmJiNGExY2M4YjdkZjg4MjZlODYzMDc1YzNlOA==
|
7
7
|
!binary "U0hBNTEy":
|
8
8
|
metadata.gz: !binary |-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
NTc3ZDYzY2FiYWEwNTgzOGU4MjM1NDYxYTU1NDgyNzZlYWVhNGRjOTAzZDk1
|
10
|
+
YmVhMTViOThlYmMxZDRkNzVmMDRkMGViZGU2MWY5ZjViY2IxMTMyOTIwYjUy
|
11
|
+
NmRhZWNkNDliMjA2MDliMGFmNDM3MzI5NWVmZTM0OTNjY2QzMmM=
|
12
12
|
data.tar.gz: !binary |-
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
MGE3NWQwMGM4MmQ3M2E3ZTkxNWMxYzkzODk2MTAxOThjYTc4ODllMGI1Yjgy
|
14
|
+
MGU1ZTdhZTMyYjdlYmFmMzFlMDZiZjIyN2IzMzk0NjYxZTJiNzk3YzdjMzg0
|
15
|
+
OTNmN2EwMWY5NGM3MzZkMmEwZDkwZmI0MmZkYTY2NzNiNjkzNTk=
|
data/README.md
CHANGED
@@ -25,7 +25,8 @@ mp3_file = File.open('/path/to/your/favorite_song.mp3')
|
|
25
25
|
tag = ID3Tag.read(mp3_file)
|
26
26
|
puts "#{tag.artist} - #{tag.title}"
|
27
27
|
```
|
28
|
-
`ID3Tag::Tag` class provides easy accessors to frames like `artist`, `title`, `album`, `year`, `
|
28
|
+
`ID3Tag::Tag` class provides easy accessors to frames like `artist`, `title`, `album`, `year`, `track_nr`, `genre` but you can read any frame by using `get_frame(id)` or `get_frame_content(id)` or browsing all frames by calling `frames`.
|
29
|
+
When using easy accessors to frames like `artist` the reader will look for v.2.x tags artist frame first and if it can not find it artist frame from v1.x will be returned (if v1.x tag exists)
|
29
30
|
|
30
31
|
```ruby
|
31
32
|
mp3s = Dir.entries("/some/dir").select { |filename| filename =~ /\.mp3/i }
|
@@ -37,7 +38,6 @@ mp3s.each do |file|
|
|
37
38
|
puts tag.title
|
38
39
|
puts tag.album
|
39
40
|
puts tag.year
|
40
|
-
puts tag.comments
|
41
41
|
puts tag.track_nr
|
42
42
|
puts tag.genre
|
43
43
|
puts "---"
|
@@ -45,8 +45,16 @@ mp3s.each do |file|
|
|
45
45
|
end
|
46
46
|
end
|
47
47
|
```
|
48
|
+
By default `ID3Tag` reads both v1.x and v2.x tags but it is possible to specify only one of them:
|
49
|
+
```ruby
|
50
|
+
ID3Tag.read(file,:all) # default behaviour
|
51
|
+
ID3Tag.read(file,:v1) # Reads only v1.x tag
|
52
|
+
ID3Tag.read(file,:v2) # Reads only v2.x tag
|
53
|
+
```
|
54
|
+
|
48
55
|
You can inspect tag by calling `frame_ids` to see available frame ids or `frames` to return all frames
|
49
56
|
|
57
|
+
|
50
58
|
## Features
|
51
59
|
|
52
60
|
* Can read v1.x, v2.2.x, v2.3.x, v2.4.x tags
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.1.
|
1
|
+
0.1.1
|
data/id3tag.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = "id3tag"
|
8
|
-
s.version = "0.1.
|
8
|
+
s.version = "0.1.1"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Krists Ozols"]
|
12
|
-
s.date = "2013-05-
|
12
|
+
s.date = "2013-05-13"
|
13
13
|
s.description = "Native Ruby ID3 tag reader that aims for 100% covarage of ID3v2.x and ID3v1.x standards"
|
14
14
|
s.email = "krists@iesals.lv"
|
15
15
|
s.extra_rdoc_files = [
|
@@ -51,6 +51,9 @@ Gem::Specification.new do |s|
|
|
51
51
|
"lib/id3tag/string_util.rb",
|
52
52
|
"lib/id3tag/synchsafe_integer.rb",
|
53
53
|
"lib/id3tag/tag.rb",
|
54
|
+
"lib/id3tag/unsynchronization.rb",
|
55
|
+
"spec/features/can_read_non_audio_files_spec.rb",
|
56
|
+
"spec/features/can_read_tag_v1_spec.rb",
|
54
57
|
"spec/fixtures/id3v1_and_v2.mp3",
|
55
58
|
"spec/fixtures/id3v1_with_track_nr.mp3",
|
56
59
|
"spec/fixtures/id3v1_without_track_nr.mp3",
|
@@ -63,8 +66,10 @@ Gem::Specification.new do |s|
|
|
63
66
|
"spec/lib/id3tag/frames/v1/track_nr_frame_spec.rb",
|
64
67
|
"spec/lib/id3tag/frames/v2/basic_frame_spec.rb",
|
65
68
|
"spec/lib/id3tag/frames/v2/comments_frame_spec.rb",
|
69
|
+
"spec/lib/id3tag/frames/v2/frame_fabricator_spec.rb",
|
66
70
|
"spec/lib/id3tag/frames/v2/genre_frame/genre_parser_24_spec.rb",
|
67
71
|
"spec/lib/id3tag/frames/v2/genre_frame/genre_parser_pre_24_spec.rb",
|
72
|
+
"spec/lib/id3tag/frames/v2/genre_frame/genre_parser_spec.rb",
|
68
73
|
"spec/lib/id3tag/frames/v2/genre_frame_spec.rb",
|
69
74
|
"spec/lib/id3tag/frames/v2/text_frame_spec.rb",
|
70
75
|
"spec/lib/id3tag/frames/v2/unique_file_id_frame_spec.rb",
|
data/lib/id3tag.rb
CHANGED
@@ -1,9 +1,8 @@
|
|
1
|
-
require "set"
|
2
1
|
require "stringio"
|
3
|
-
|
4
2
|
require "id3tag/synchsafe_integer"
|
5
3
|
require "id3tag/audio_file"
|
6
4
|
require "id3tag/id3_v2_tag_header"
|
5
|
+
require "id3tag/unsynchronization"
|
7
6
|
require "id3tag/number_util"
|
8
7
|
require "id3tag/string_util"
|
9
8
|
require "id3tag/id3_v1_frame_parser"
|
@@ -26,7 +25,7 @@ require "id3tag/frames/v2/genre_frame/genre_parser_24"
|
|
26
25
|
require "id3tag/frames/v2/frame_fabricator"
|
27
26
|
|
28
27
|
module ID3Tag
|
29
|
-
def self.read(source, version =
|
28
|
+
def self.read(source, version = :all)
|
30
29
|
tag = Tag.read(source, version)
|
31
30
|
yield tag if block_given?
|
32
31
|
tag
|
data/lib/id3tag/audio_file.rb
CHANGED
@@ -44,7 +44,13 @@ module ID3Tag
|
|
44
44
|
end
|
45
45
|
|
46
46
|
def advise(frame_name)
|
47
|
-
|
47
|
+
version_ids && version_ids[frame_name]
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
def version_ids
|
53
|
+
COMMON_FRAME_IDS_BY_VERSION["v#{@version}.#{@major_version}"]
|
48
54
|
end
|
49
55
|
end
|
50
56
|
end
|
@@ -38,6 +38,10 @@ module ID3Tag
|
|
38
38
|
@tag_size ||= get_tag_size
|
39
39
|
end
|
40
40
|
|
41
|
+
def inspect
|
42
|
+
"<#{self.class.name} version:2.#{version_number} size:#{tag_size} unsync:#{unsynchronisation?} ext.header:#{extended_header?} experimental:#{experimental?} footer:#{footer_present?}>"
|
43
|
+
end
|
44
|
+
|
41
45
|
private
|
42
46
|
|
43
47
|
def flags_byte
|
data/lib/id3tag/string_util.rb
CHANGED
@@ -3,5 +3,17 @@ module ID3Tag
|
|
3
3
|
def self.blank?(string)
|
4
4
|
string !~ /[^[:space:]]/
|
5
5
|
end
|
6
|
+
|
7
|
+
def self.do_unsynchronization(input)
|
8
|
+
unsynch = Unsynchronization.new(input)
|
9
|
+
unsynch.apply
|
10
|
+
unsynch.output
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.undo_unsynchronization(input)
|
14
|
+
unsynch = Unsynchronization.new(input)
|
15
|
+
unsynch.remove
|
16
|
+
unsynch.output
|
17
|
+
end
|
6
18
|
end
|
7
19
|
end
|
data/lib/id3tag/tag.rb
CHANGED
@@ -3,48 +3,37 @@ module ID3Tag
|
|
3
3
|
class MultipleFrameError < StandardError; end
|
4
4
|
|
5
5
|
class << self
|
6
|
-
def read(source, version =
|
6
|
+
def read(source, version = :all)
|
7
7
|
new(source, version)
|
8
8
|
end
|
9
9
|
end
|
10
10
|
|
11
|
-
def initialize(source, version =
|
11
|
+
def initialize(source, version = :all)
|
12
12
|
@source, @version = source, version
|
13
13
|
end
|
14
14
|
|
15
15
|
def artist
|
16
|
-
get_frame_content(
|
16
|
+
get_frame_content(frame_id(:v2, :artist), frame_id(:v1, :artist))
|
17
17
|
end
|
18
18
|
|
19
19
|
def title
|
20
|
-
get_frame_content(
|
20
|
+
get_frame_content(frame_id(:v2, :title), frame_id(:v1, :title))
|
21
21
|
end
|
22
22
|
|
23
23
|
def album
|
24
|
-
get_frame_content(
|
24
|
+
get_frame_content(frame_id(:v2, :album), frame_id(:v1, :album))
|
25
25
|
end
|
26
26
|
|
27
27
|
def year
|
28
|
-
get_frame_content(
|
29
|
-
end
|
30
|
-
|
31
|
-
def comments(language = nil)
|
32
|
-
all_comments_frames = get_frames(frame_id_by_name(:comments))
|
33
|
-
comments_frame = if language
|
34
|
-
all_comments_frames.select { |frame| frame.language == language.to_s.downcase }.first
|
35
|
-
else
|
36
|
-
in_english = all_comments_frames.select { |frame| frame.language == 'eng' }
|
37
|
-
in_english.first || all_comments_frames.first
|
38
|
-
end
|
39
|
-
comments_frame && comments_frame.content
|
28
|
+
get_frame_content(frame_id(:v2, :year), frame_id(:v1, :year))
|
40
29
|
end
|
41
30
|
|
42
31
|
def track_nr
|
43
|
-
get_frame_content(
|
32
|
+
get_frame_content(frame_id(:v2, :track_nr), frame_id(:v1, :track_nr))
|
44
33
|
end
|
45
34
|
|
46
35
|
def genre
|
47
|
-
get_frame_content(
|
36
|
+
get_frame_content(frame_id(:v2, :genre), frame_id(:v1, :genre))
|
48
37
|
end
|
49
38
|
|
50
39
|
def get_frame(frame_id)
|
@@ -65,52 +54,46 @@ module ID3Tag
|
|
65
54
|
end
|
66
55
|
|
67
56
|
def frames
|
68
|
-
@frames ||=
|
69
|
-
end
|
70
|
-
|
71
|
-
def get_frame_content(frame_id)
|
72
|
-
frame = get_frame(frame_id)
|
73
|
-
frame && frame.content
|
74
|
-
end
|
75
|
-
|
76
|
-
def parsable_version
|
77
|
-
@parsable_version ||= calc_parsable_version
|
57
|
+
@frames ||= v2_frames + v1_frames
|
78
58
|
end
|
79
59
|
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
case parsable_version
|
84
|
-
when 2
|
85
|
-
FrameIdAdvisor.new(2, audio_file.v2_tag_major_version_number).advise(name)
|
86
|
-
when 1
|
87
|
-
FrameIdAdvisor.new(1, 'x').advise(name)
|
60
|
+
def v2_frames
|
61
|
+
if audio_file.v2_tag_present? && [:v2, :all].include?(@version)
|
62
|
+
ID3V2FrameParser.new(audio_file.v2_tag_body, audio_file.v2_tag_major_version_number).frames
|
88
63
|
else
|
89
|
-
|
64
|
+
[]
|
90
65
|
end
|
91
66
|
end
|
92
67
|
|
93
|
-
def
|
94
|
-
|
95
|
-
when 2
|
96
|
-
ID3V2FrameParser.new(audio_file.v2_tag_body, audio_file.v2_tag_major_version_number).frames
|
97
|
-
when 1
|
68
|
+
def v1_frames
|
69
|
+
if audio_file.v1_tag_present? && [:v1, :all].include?(@version)
|
98
70
|
ID3V1FrameParser.new(audio_file.v1_tag_body).frames
|
99
71
|
else
|
100
72
|
[]
|
101
73
|
end
|
102
74
|
end
|
103
75
|
|
104
|
-
def
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
76
|
+
def get_frame_content(frame_id, *alt_frame_ids)
|
77
|
+
frame = nil
|
78
|
+
alt_frame_ids.unshift(frame_id).each do |frame_id|
|
79
|
+
frame = get_frame(frame_id)
|
80
|
+
break if frame
|
81
|
+
end
|
82
|
+
frame && frame.content
|
83
|
+
end
|
84
|
+
|
85
|
+
private
|
86
|
+
|
87
|
+
def frame_id(version, name)
|
88
|
+
case version
|
89
|
+
when :v2
|
90
|
+
if audio_file.v2_tag_present?
|
91
|
+
FrameIdAdvisor.new(2, audio_file.v2_tag_major_version_number).advise(name)
|
111
92
|
end
|
93
|
+
when :v1
|
94
|
+
FrameIdAdvisor.new(1, 'x').advise(name)
|
112
95
|
else
|
113
|
-
|
96
|
+
nil
|
114
97
|
end
|
115
98
|
end
|
116
99
|
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module ID3Tag
|
2
|
+
class Unsynchronization
|
3
|
+
def initialize(input)
|
4
|
+
@input = input
|
5
|
+
@encoding = input.encoding
|
6
|
+
end
|
7
|
+
|
8
|
+
def apply
|
9
|
+
loop do
|
10
|
+
current_byte = input_bytes.next
|
11
|
+
output_bytes << current_byte
|
12
|
+
next_byte = input_bytes.peek
|
13
|
+
if (current_byte == 255) && (next_byte > 224)
|
14
|
+
output_bytes << 0
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def remove
|
20
|
+
prev_byte = nil
|
21
|
+
loop do
|
22
|
+
current_byte = input_bytes.next
|
23
|
+
next_byte = input_bytes.peek rescue 0
|
24
|
+
unless (prev_byte == 255) && (current_byte == 0) && (next_byte > 224)
|
25
|
+
output_bytes << current_byte
|
26
|
+
end
|
27
|
+
prev_byte = current_byte
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def output
|
32
|
+
output_bytes.pack("C*").force_encoding(@encoding)
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def input_bytes
|
38
|
+
@input_bytes ||= @input.unpack("C*").to_enum
|
39
|
+
end
|
40
|
+
|
41
|
+
def output_bytes
|
42
|
+
@output_bytes ||= []
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe 'can read any file and does not raise errors if no tag found' do
|
4
|
+
subject { ID3Tag.read(File.open('/dev/null')) }
|
5
|
+
|
6
|
+
it 'should return blanks' do
|
7
|
+
subject.title.should == nil
|
8
|
+
subject.artist.should == nil
|
9
|
+
subject.album.should == nil
|
10
|
+
subject.genre.should == nil
|
11
|
+
subject.year.should == nil
|
12
|
+
subject.track_nr.should == nil
|
13
|
+
subject.frames.should == []
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe 'can read v1 info from file' do
|
4
|
+
let(:mp3_with_v1_1_tag) { mp3_fixture('id3v1_with_track_nr.mp3') }
|
5
|
+
subject { ID3Tag.read(mp3_with_v1_1_tag) }
|
6
|
+
|
7
|
+
it 'reading file with only v1 tag' do
|
8
|
+
subject.title.should == "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaA"
|
9
|
+
subject.artist.should == "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbB"
|
10
|
+
subject.album.should == "cccccccccccccccccccccccccccccC"
|
11
|
+
subject.genre.should == "Blues"
|
12
|
+
subject.year.should == "2003"
|
13
|
+
subject.track_nr.should == "1"
|
14
|
+
end
|
15
|
+
end
|
File without changes
|
File without changes
|
File without changes
|
@@ -32,9 +32,24 @@ describe ID3Tag::AudioFile do
|
|
32
32
|
end
|
33
33
|
|
34
34
|
describe "#v2_tag_body" do
|
35
|
-
|
36
|
-
|
37
|
-
|
35
|
+
context "when extended header is not present" do
|
36
|
+
subject { described_class.new(StringIO.new("ID3\u0003\u0000\u0000\u0000\u0000\u0000\u0003ABC")) }
|
37
|
+
it "should return frame and padding bytes" do
|
38
|
+
subject.v2_tag_body.should == "ABC"
|
39
|
+
end
|
38
40
|
end
|
41
|
+
context "when extended header is present" do
|
42
|
+
subject { described_class.new(StringIO.new("ID3\u0003\u0000\u0040\u0000\u0000\u0000\u0011" + "\u0000\u0000\u0000\u000E" + ("\u0000" * 10) + "ABC")) }
|
43
|
+
it "should return frame and padding bytes" do
|
44
|
+
subject.v2_tag_body.should == "ABC"
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
context "when reading file with v2.3.0 tag" do
|
50
|
+
subject { described_class.new(StringIO.new("ID3\u0003\u0000")) }
|
51
|
+
its(:v2_tag_version) { should eq '3.0' }
|
52
|
+
its(:v2_tag_major_version_number) { should eq 3 }
|
53
|
+
its(:v2_tag_minor_version_number) { should eq 0 }
|
39
54
|
end
|
40
55
|
end
|
@@ -17,6 +17,14 @@ describe ID3Tag::Frames::V1::CommentsFrame do
|
|
17
17
|
end
|
18
18
|
end
|
19
19
|
|
20
|
+
describe "#text" do
|
21
|
+
subject { described_class.new('comments', 'some comment about the song') }
|
22
|
+
it "should be the same as #content" do
|
23
|
+
subject.stub(:content) { "ZXC" }
|
24
|
+
subject.text.should == subject.content
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
20
28
|
describe '#language' do
|
21
29
|
it 'should be unknown' do
|
22
30
|
described_class.new('comments', '').language.should eq('unknown')
|
@@ -0,0 +1,56 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe ID3Tag::Frames::V2::FrameFabricator do
|
4
|
+
let(:id) { nil }
|
5
|
+
let(:content) { 'some content' }
|
6
|
+
let(:flags) { nil }
|
7
|
+
let(:major_version_number) { 4 }
|
8
|
+
|
9
|
+
describe "self#fabricate" do
|
10
|
+
subject { described_class.fabricate(id, content, flags, major_version_number) }
|
11
|
+
context "when frame is a genre frame TCON" do
|
12
|
+
let(:id) { "TCON" }
|
13
|
+
it "fabricates genre frame" do
|
14
|
+
ID3Tag::Frames::V2::GenreFrame.should_receive(:new).with(id, content, flags, major_version_number)
|
15
|
+
subject
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
context "when frame is a genre frame TCO" do
|
20
|
+
let(:id) { "TCO" }
|
21
|
+
it "fabricates genre frame" do
|
22
|
+
ID3Tag::Frames::V2::GenreFrame.should_receive(:new).with(id, content, flags, major_version_number)
|
23
|
+
subject
|
24
|
+
end
|
25
|
+
end
|
26
|
+
context "when frame is a text frame TIT2" do
|
27
|
+
let(:id) { "TIT2" }
|
28
|
+
it "fabricates text frame" do
|
29
|
+
ID3Tag::Frames::V2::TextFrame.should_receive(:new).with(id, content, flags, major_version_number)
|
30
|
+
subject
|
31
|
+
end
|
32
|
+
end
|
33
|
+
context "when frame is a comment frame COM" do
|
34
|
+
let(:id) { "COMM" }
|
35
|
+
it "fabricates comment frame" do
|
36
|
+
ID3Tag::Frames::V2::CommentsFrame.should_receive(:new).with(id, content, flags, major_version_number)
|
37
|
+
subject
|
38
|
+
end
|
39
|
+
end
|
40
|
+
context "when frame is a unique id" do
|
41
|
+
let(:id) { "UFID" }
|
42
|
+
it "fabricates unique id frame" do
|
43
|
+
ID3Tag::Frames::V2::UniqueFileIdFrame.should_receive(:new).with(id, content, flags, major_version_number)
|
44
|
+
subject
|
45
|
+
end
|
46
|
+
end
|
47
|
+
context "when frame is a unknown" do
|
48
|
+
let(:id) { "unknown" }
|
49
|
+
it "fabricates basic frame" do
|
50
|
+
ID3Tag::Frames::V2::BasicFrame.should_receive(:new).with(id, content, flags, major_version_number)
|
51
|
+
subject
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require 'spec_helper'
|
3
|
+
|
4
|
+
describe ID3Tag::Frames::V2::GenreFrame::GenreParser do
|
5
|
+
let(:genre_string) { "17" }
|
6
|
+
subject { described_class.new(genre_string) }
|
7
|
+
|
8
|
+
describe '#genres' do
|
9
|
+
it "should raise not implemented error" do
|
10
|
+
expect { subject.genres }.to raise_error(/genres is not implemented/)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -16,19 +16,37 @@ describe ID3Tag::Frames::V2::GenreFrame do
|
|
16
16
|
describe '#content' do
|
17
17
|
subject { frame.content }
|
18
18
|
|
19
|
-
context "when
|
20
|
-
|
21
|
-
|
19
|
+
context "when tag major version is pre 3" do
|
20
|
+
let(:major_version_number) { 3 }
|
21
|
+
|
22
|
+
context "when one genre" do
|
23
|
+
before do
|
24
|
+
ID3Tag::Frames::V2::GenreFrame::GenreParserPre24.any_instance.stub(:genres) { ['A'] }
|
25
|
+
end
|
26
|
+
|
27
|
+
it { should eq('A') }
|
22
28
|
end
|
29
|
+
context "when two genres" do
|
30
|
+
before do
|
31
|
+
ID3Tag::Frames::V2::GenreFrame::GenreParserPre24.any_instance.stub(:genres) { ['A', 'B'] }
|
32
|
+
end
|
23
33
|
|
24
|
-
|
34
|
+
it { should eq('A, B') }
|
35
|
+
end
|
25
36
|
end
|
26
|
-
|
37
|
+
|
38
|
+
context "when tag major version is 4" do
|
39
|
+
let(:major_version_number) { 4 }
|
27
40
|
before do
|
28
|
-
ID3Tag::Frames::V2::GenreFrame::
|
41
|
+
ID3Tag::Frames::V2::GenreFrame::GenreParser24.any_instance.stub(:genres) { ['B'] }
|
42
|
+
end
|
43
|
+
it { should eq('B') }
|
44
|
+
end
|
45
|
+
context "when tag major version is 232" do
|
46
|
+
let(:major_version_number) { 232 }
|
47
|
+
it "should raise MissingGenreParser error" do
|
48
|
+
expect { subject }.to raise_error(ID3Tag::Frames::V2::GenreFrame::MissingGenreParser)
|
29
49
|
end
|
30
|
-
|
31
|
-
it { should eq('A, B') }
|
32
50
|
end
|
33
51
|
end
|
34
52
|
|
@@ -7,10 +7,6 @@ describe ID3Tag::ID3V1FrameParser do
|
|
7
7
|
describe "#frames" do
|
8
8
|
subject { described_class.new(frame_bytes_v1_0).frames }
|
9
9
|
|
10
|
-
it "should return array or frames" do
|
11
|
-
subject.should be_kind_of(Set)
|
12
|
-
end
|
13
|
-
|
14
10
|
describe "common frames between v1.0 and v.1.1" do
|
15
11
|
|
16
12
|
it "should contain title frame" do
|
@@ -1,18 +1,23 @@
|
|
1
1
|
require "spec_helper"
|
2
2
|
describe ID3Tag::ID3V2FrameParser do
|
3
|
-
|
4
|
-
let(:frame_bytes) { File.read(mp3_with_v2_3_tag, 246, 10) }
|
5
|
-
let(:tag_major_version) { 3 }
|
6
|
-
describe "#frames" do
|
7
|
-
subject { described_class.new(frame_bytes, tag_major_version).frames }
|
8
|
-
it "should have frame TIT2" do
|
9
|
-
subject.select { |x| x.id == :TIT2 }.count.should == 1
|
10
|
-
end
|
3
|
+
subject { described_class.new(frame_bytes, tag_major_version) }
|
11
4
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
5
|
+
context "parsing v2.2.x tag" do
|
6
|
+
let(:tag_major_version) { 2 }
|
7
|
+
let(:frame_bytes) { "TP2\u0000\u0000\u0004\u0000ABC" + "TP1\u0000\u0000\u0004\u0000DEF" }
|
8
|
+
it "should fabricate frames" do
|
9
|
+
ID3Tag::Frames::V2::FrameFabricator.should_receive(:fabricate).with('TP2', "\u0000ABC", nil, 2).once
|
10
|
+
ID3Tag::Frames::V2::FrameFabricator.should_receive(:fabricate).with('TP1', "\u0000DEF", nil, 2).once
|
11
|
+
subject.frames
|
12
|
+
end
|
13
|
+
end
|
14
|
+
context "parsing v2.4.x tag" do
|
15
|
+
let(:tag_major_version) { 4 }
|
16
|
+
let(:frame_bytes) { "TIT2\u0000\u0000\u0000\u0004\u0000\u0000\u0000ABC" + "TIT1\u0000\u0000\u0000\u0004\u0000\u0000\u0000DEF" }
|
17
|
+
it "should fabricate frames" do
|
18
|
+
ID3Tag::Frames::V2::FrameFabricator.should_receive(:fabricate).with('TIT2', "\u0000ABC", "\u0000\u0000", 4).once
|
19
|
+
ID3Tag::Frames::V2::FrameFabricator.should_receive(:fabricate).with('TIT1', "\u0000DEF", "\u0000\u0000", 4).once
|
20
|
+
subject.frames
|
16
21
|
end
|
17
22
|
end
|
18
23
|
end
|
@@ -57,4 +57,9 @@ describe ID3Tag::ID3v2TagHeader do
|
|
57
57
|
let(:header_data) { "ID3abc\x00\x00\x01\x7F" }
|
58
58
|
its(:tag_size) { should == 255 }
|
59
59
|
end
|
60
|
+
|
61
|
+
describe "#inspect" do
|
62
|
+
let(:header_data) { "ID3\u0003\u0000\u0000\u0000\u0000\u0000\u0000" }
|
63
|
+
its(:inspect) { should eq "<ID3Tag::ID3v2TagHeader version:2.3.0 size:0 unsync:false ext.header:false experimental:false footer:false>" }
|
64
|
+
end
|
60
65
|
end
|
@@ -18,4 +18,32 @@ describe ID3Tag::StringUtil do
|
|
18
18
|
it { should be_false }
|
19
19
|
end
|
20
20
|
end
|
21
|
+
|
22
|
+
describe "do_unsynchronization" do
|
23
|
+
let(:input) { }
|
24
|
+
subject { described_class.do_unsynchronization(input) }
|
25
|
+
|
26
|
+
context "when a false synchronization is present" do
|
27
|
+
let(:input) { "\xFF\xEE" }
|
28
|
+
it { should eq("\xFF\x00\xEE") }
|
29
|
+
end
|
30
|
+
context "when a false synchronization is not present" do
|
31
|
+
let(:input) { "\xEE\xEE" }
|
32
|
+
it { should eq("\xEE\xEE") }
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
describe "undo_unsynchronization" do
|
37
|
+
let(:input) { }
|
38
|
+
subject { described_class.undo_unsynchronization(input) }
|
39
|
+
|
40
|
+
context "when unsynchronization in present" do
|
41
|
+
let(:input) { "\xFF\x00\xEE\xEE" }
|
42
|
+
it { should eq("\xFF\xEE\xEE") }
|
43
|
+
end
|
44
|
+
context "when unsynchronization in not present" do
|
45
|
+
let(:input) { "\xEE\xEE" }
|
46
|
+
it { should eq("\xEE\xEE") }
|
47
|
+
end
|
48
|
+
end
|
21
49
|
end
|
data/spec/lib/id3tag/tag_spec.rb
CHANGED
@@ -1,84 +1,268 @@
|
|
1
1
|
require "spec_helper"
|
2
2
|
|
3
3
|
describe ID3Tag::Tag do
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
4
|
+
describe "class method #read" do
|
5
|
+
|
6
|
+
end
|
7
|
+
|
8
|
+
context "when file has v2.4.x tag and v.1.x tag" do
|
9
|
+
subject { described_class.read(nil) }
|
10
|
+
before :each do
|
11
|
+
ID3Tag::AudioFile.any_instance.stub(:v2_tag_present?) { true }
|
12
|
+
ID3Tag::AudioFile.any_instance.stub(:v2_tag_major_version_number) { 4 }
|
13
|
+
end
|
14
|
+
|
15
|
+
describe "#artist" do
|
16
|
+
it "reads TPE1 or v1 artist" do
|
17
|
+
subject.should_receive(:get_frame_content).with(:TPE1, :artist).and_return('A')
|
18
|
+
subject.artist.should eq('A')
|
11
19
|
end
|
12
20
|
end
|
13
21
|
|
14
|
-
describe "
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
22
|
+
describe "#title" do
|
23
|
+
it "reads TIT2 or v1 title" do
|
24
|
+
subject.should_receive(:get_frame_content).with(:TIT2, :title).and_return('A')
|
25
|
+
subject.title.should eq('A')
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
describe "#album" do
|
30
|
+
it "reads TALB or v1 album" do
|
31
|
+
subject.should_receive(:get_frame_content).with(:TALB, :album).and_return('A')
|
32
|
+
subject.album.should eq('A')
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
describe "#year" do
|
37
|
+
it "reads TDRC or v1 year" do
|
38
|
+
subject.should_receive(:get_frame_content).with(:TDRC, :year).and_return('A')
|
39
|
+
subject.year.should eq('A')
|
19
40
|
end
|
20
41
|
end
|
21
42
|
|
22
|
-
describe "#
|
23
|
-
it "
|
24
|
-
subject.
|
25
|
-
subject.
|
43
|
+
describe "#track_nr" do
|
44
|
+
it "reads TRCK or v1 track_nr" do
|
45
|
+
subject.should_receive(:get_frame_content).with(:TRCK, :track_nr).and_return('A')
|
46
|
+
subject.track_nr.should eq('A')
|
26
47
|
end
|
48
|
+
end
|
27
49
|
|
28
|
-
|
29
|
-
|
30
|
-
|
50
|
+
describe "#genre" do
|
51
|
+
it "reads TCON or v1 genre" do
|
52
|
+
subject.should_receive(:get_frame_content).with(:TCON, :genre).and_return('A')
|
53
|
+
subject.genre.should eq('A')
|
31
54
|
end
|
32
55
|
end
|
56
|
+
end
|
33
57
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
58
|
+
describe "#get_frame" do
|
59
|
+
subject { described_class.read(nil) }
|
60
|
+
context "when more that one frame by that ID exists" do
|
61
|
+
before :each do
|
62
|
+
subject.stub(:get_frames) { [:frame, :frame] }
|
63
|
+
end
|
64
|
+
it "should raise MultipleFrameError" do
|
65
|
+
expect { subject.get_frame(:some_unique_frame) }.to raise_error(ID3Tag::Tag::MultipleFrameError)
|
38
66
|
end
|
39
67
|
end
|
40
68
|
|
41
|
-
|
42
|
-
|
69
|
+
context "when only one frame by that ID exists" do
|
70
|
+
before :each do
|
71
|
+
subject.stub(:get_frames) { [:frame] }
|
72
|
+
end
|
73
|
+
it "should return the frame" do
|
74
|
+
subject.get_frame(:some_unique_frame).should eq(:frame)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
describe "#get_frames" do
|
80
|
+
subject { described_class.read(nil) }
|
81
|
+
let(:a1) { ID3Tag::Frames::V1::TextFrame.new(:A, 'a1') }
|
82
|
+
let(:a2) { ID3Tag::Frames::V1::TextFrame.new(:A, 'a2') }
|
83
|
+
let(:b) { ID3Tag::Frames::V1::TextFrame.new(:B, 'b') }
|
84
|
+
before :each do
|
85
|
+
subject.stub(:frames) { [a1, a2, b] }
|
86
|
+
end
|
87
|
+
it "returns frames with specific IDs" do
|
88
|
+
subject.get_frames(:A).should eq([a1, a2])
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
describe "#frames" do
|
93
|
+
subject { described_class.read(nil) }
|
94
|
+
before do
|
95
|
+
subject.stub(:v1_frames) { [:v1_frame1, :v1_frame2] }
|
96
|
+
subject.stub(:v2_frames) { [:v2_frame1, :v2_frame2] }
|
97
|
+
end
|
98
|
+
it "returns v2 frames and v1 frames" do
|
99
|
+
subject.frames.should eq([:v2_frame1, :v2_frame2, :v1_frame1, :v1_frame2])
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
describe "#frame_ids" do
|
104
|
+
subject { described_class.read(nil) }
|
105
|
+
let(:frame_1) { ID3Tag::Frames::V1::TextFrame.new(:AA, 'a1') }
|
106
|
+
let(:frame_2) { ID3Tag::Frames::V1::TextFrame.new(:BB, 'a2') }
|
107
|
+
before do
|
108
|
+
subject.stub(:frames) { [frame_1, frame_2] }
|
109
|
+
end
|
110
|
+
it "returns frames ids" do
|
111
|
+
subject.frame_ids.should eq([:AA, :BB])
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
describe "#v1_frames" do
|
116
|
+
context "when tag reading initialized with v1 tag only" do
|
117
|
+
subject { described_class.read(nil, :v1) }
|
118
|
+
context "when file has v1 tag" do
|
43
119
|
before do
|
44
120
|
ID3Tag::AudioFile.any_instance.stub(:v1_tag_present?) { true }
|
45
|
-
ID3Tag::AudioFile.any_instance.stub(:
|
46
|
-
ID3Tag::
|
121
|
+
ID3Tag::AudioFile.any_instance.stub(:v1_tag_body) { '' }
|
122
|
+
ID3Tag::ID3V1FrameParser.any_instance.stub(:frames) { [:v1_frame] }
|
123
|
+
end
|
124
|
+
it "reads v1 tags" do
|
125
|
+
subject.v1_frames.should eq([:v1_frame])
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
context "when file does not have v1 tag" do
|
130
|
+
before do
|
131
|
+
ID3Tag::AudioFile.any_instance.stub(:v1_tag_present?) { false }
|
132
|
+
ID3Tag::AudioFile.any_instance.stub(:v1_tag_body) { '' }
|
133
|
+
end
|
134
|
+
it "returns empty array" do
|
135
|
+
subject.v1_frames.should eq([])
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
context "when tag reading initialized with v2 tag only" do
|
141
|
+
subject { described_class.read(nil, :v2) }
|
142
|
+
context "when file has v1 tag" do
|
143
|
+
before do
|
144
|
+
ID3Tag::AudioFile.any_instance.stub(:v1_tag_present?) { true }
|
145
|
+
ID3Tag::AudioFile.any_instance.stub(:v1_tag_body) { '' }
|
47
146
|
end
|
48
|
-
it "
|
49
|
-
subject.
|
147
|
+
it "reads v1 tags" do
|
148
|
+
subject.v1_frames.should eq([])
|
50
149
|
end
|
51
150
|
end
|
52
|
-
|
151
|
+
end
|
152
|
+
|
153
|
+
context "when tag reading initialized with all versions flag" do
|
154
|
+
subject { described_class.read(nil, :all) }
|
155
|
+
context "when file has v1 tag" do
|
53
156
|
before do
|
54
157
|
ID3Tag::AudioFile.any_instance.stub(:v1_tag_present?) { true }
|
158
|
+
ID3Tag::AudioFile.any_instance.stub(:v1_tag_body) { '' }
|
159
|
+
ID3Tag::ID3V1FrameParser.any_instance.stub(:frames) { [:v1_frame] }
|
160
|
+
end
|
161
|
+
it "reads v1 tags" do
|
162
|
+
subject.v1_frames.should eq([:v1_frame])
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
describe "#v2_frames" do
|
169
|
+
before do
|
170
|
+
ID3Tag::AudioFile.any_instance.stub(:v2_tag_major_version_number) { 3 }
|
171
|
+
end
|
172
|
+
context "when tag reading initialized with v2 tag only" do
|
173
|
+
subject { described_class.read(nil, :v2) }
|
174
|
+
context "when file has v2 tag" do
|
175
|
+
before do
|
176
|
+
ID3Tag::AudioFile.any_instance.stub(:v2_tag_present?) { true }
|
177
|
+
ID3Tag::AudioFile.any_instance.stub(:v2_tag_body) { '' }
|
178
|
+
ID3Tag::ID3V2FrameParser.any_instance.stub(:frames) { [:v2_frame] }
|
179
|
+
end
|
180
|
+
it "reads v2 tags" do
|
181
|
+
subject.v2_frames.should eq([:v2_frame])
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
context "when file does not have v2 tag" do
|
186
|
+
before do
|
55
187
|
ID3Tag::AudioFile.any_instance.stub(:v2_tag_present?) { false }
|
56
|
-
ID3Tag::
|
188
|
+
ID3Tag::AudioFile.any_instance.stub(:v2_tag_body) { '' }
|
57
189
|
end
|
58
|
-
it "
|
59
|
-
subject.
|
190
|
+
it "returns empty array" do
|
191
|
+
subject.v2_frames.should eq([])
|
60
192
|
end
|
61
193
|
end
|
62
194
|
end
|
63
195
|
|
64
|
-
|
65
|
-
|
66
|
-
|
196
|
+
context "when tag reading initialized with v1 tag only" do
|
197
|
+
subject { described_class.read(nil, :v1) }
|
198
|
+
context "when file has v2 tag" do
|
199
|
+
before do
|
200
|
+
ID3Tag::AudioFile.any_instance.stub(:v2_tag_present?) { true }
|
201
|
+
ID3Tag::AudioFile.any_instance.stub(:v2_tag_body) { '' }
|
202
|
+
end
|
203
|
+
it "returns empty array" do
|
204
|
+
subject.v2_frames.should eq([])
|
205
|
+
end
|
67
206
|
end
|
68
|
-
|
69
|
-
|
207
|
+
end
|
208
|
+
|
209
|
+
context "when tag reading initialized with all versions flag" do
|
210
|
+
subject { described_class.read(nil, :all) }
|
211
|
+
context "when file has v2 tag" do
|
212
|
+
before do
|
213
|
+
ID3Tag::AudioFile.any_instance.stub(:v2_tag_present?) { true }
|
214
|
+
ID3Tag::AudioFile.any_instance.stub(:v2_tag_body) { '' }
|
215
|
+
ID3Tag::ID3V2FrameParser.any_instance.stub(:frames) { [:v2_frame] }
|
216
|
+
end
|
217
|
+
it "reads v2 tags" do
|
218
|
+
subject.v2_frames.should eq([:v2_frame])
|
219
|
+
end
|
70
220
|
end
|
71
221
|
end
|
72
222
|
end
|
73
223
|
|
74
|
-
describe "
|
75
|
-
subject { described_class.
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
224
|
+
describe "#get_frame_content" do
|
225
|
+
subject { described_class.read(nil, :all) }
|
226
|
+
let(:frame_1) { ID3Tag::Frames::V1::TextFrame.new(:some_id, 'some content') }
|
227
|
+
let(:frame_2) { ID3Tag::Frames::V1::TextFrame.new(:some_other_id, 'some other content') }
|
228
|
+
context "with one ID as argument" do
|
229
|
+
context "when frame with ID exists" do
|
230
|
+
before :each do
|
231
|
+
subject.stub(:get_frame).with(:some_id) { frame_1 }
|
232
|
+
end
|
233
|
+
it "should return frame" do
|
234
|
+
subject.get_frame_content(:some_id).should == 'some content'
|
235
|
+
end
|
236
|
+
end
|
237
|
+
context "when frame with ID does not exists" do
|
238
|
+
before :each do
|
239
|
+
subject.stub(:get_frame).with(:some_id) { nil }
|
240
|
+
end
|
241
|
+
it "should return none" do
|
242
|
+
subject.get_frame_content(:some_id).should be_nil
|
243
|
+
end
|
244
|
+
end
|
245
|
+
end
|
246
|
+
|
247
|
+
context "with multiple ID as armguments" do
|
248
|
+
context "when first one does exist" do
|
249
|
+
before :each do
|
250
|
+
subject.stub(:get_frame).with(:some_id) { frame_1 }
|
251
|
+
subject.stub(:get_frame).with(:some_other_id) { frame_2 }
|
252
|
+
end
|
253
|
+
it "should return first existing frame's content" do
|
254
|
+
subject.get_frame_content(:some_id, :some_other_id).should eq 'some content'
|
255
|
+
end
|
256
|
+
end
|
257
|
+
context "when second exists" do
|
258
|
+
before :each do
|
259
|
+
subject.stub(:get_frame).with(:some_id) { nil }
|
260
|
+
subject.stub(:get_frame).with(:some_other_id) { frame_2 }
|
261
|
+
end
|
262
|
+
it "should return first existing frame's content" do
|
263
|
+
subject.get_frame_content(:some_id, :some_other_id).should eq 'some other content'
|
264
|
+
end
|
265
|
+
end
|
266
|
+
end
|
83
267
|
end
|
84
268
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: id3tag
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Krists Ozols
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2013-05-
|
11
|
+
date: 2013-05-13 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: debugger
|
@@ -151,6 +151,9 @@ files:
|
|
151
151
|
- lib/id3tag/string_util.rb
|
152
152
|
- lib/id3tag/synchsafe_integer.rb
|
153
153
|
- lib/id3tag/tag.rb
|
154
|
+
- lib/id3tag/unsynchronization.rb
|
155
|
+
- spec/features/can_read_non_audio_files_spec.rb
|
156
|
+
- spec/features/can_read_tag_v1_spec.rb
|
154
157
|
- spec/fixtures/id3v1_and_v2.mp3
|
155
158
|
- spec/fixtures/id3v1_with_track_nr.mp3
|
156
159
|
- spec/fixtures/id3v1_without_track_nr.mp3
|
@@ -163,8 +166,10 @@ files:
|
|
163
166
|
- spec/lib/id3tag/frames/v1/track_nr_frame_spec.rb
|
164
167
|
- spec/lib/id3tag/frames/v2/basic_frame_spec.rb
|
165
168
|
- spec/lib/id3tag/frames/v2/comments_frame_spec.rb
|
169
|
+
- spec/lib/id3tag/frames/v2/frame_fabricator_spec.rb
|
166
170
|
- spec/lib/id3tag/frames/v2/genre_frame/genre_parser_24_spec.rb
|
167
171
|
- spec/lib/id3tag/frames/v2/genre_frame/genre_parser_pre_24_spec.rb
|
172
|
+
- spec/lib/id3tag/frames/v2/genre_frame/genre_parser_spec.rb
|
168
173
|
- spec/lib/id3tag/frames/v2/genre_frame_spec.rb
|
169
174
|
- spec/lib/id3tag/frames/v2/text_frame_spec.rb
|
170
175
|
- spec/lib/id3tag/frames/v2/unique_file_id_frame_spec.rb
|