id3tag 0.1.0 → 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|