id3tag 0.0.0 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (62) hide show
  1. checksums.yaml +15 -0
  2. data/.travis.yml +7 -0
  3. data/Gemfile +9 -9
  4. data/Gemfile.lock +36 -14
  5. data/README.md +73 -0
  6. data/Rakefile +3 -2
  7. data/VERSION +1 -1
  8. data/id3tag.gemspec +75 -19
  9. data/lib/id3tag.rb +34 -0
  10. data/lib/id3tag/audio_file.rb +90 -0
  11. data/lib/id3tag/frame_id_advisor.rb +50 -0
  12. data/lib/id3tag/frames/util/genre_names.rb +140 -0
  13. data/lib/id3tag/frames/v1/comments_frame.rb +24 -0
  14. data/lib/id3tag/frames/v1/genre_frame.rb +19 -0
  15. data/lib/id3tag/frames/v1/text_frame.rb +27 -0
  16. data/lib/id3tag/frames/v1/track_nr_frame.rb +18 -0
  17. data/lib/id3tag/frames/v2/basic_frame.rb +26 -0
  18. data/lib/id3tag/frames/v2/comments_frame.rb +44 -0
  19. data/lib/id3tag/frames/v2/frame_fabricator.rb +36 -0
  20. data/lib/id3tag/frames/v2/genre_frame.rb +34 -0
  21. data/lib/id3tag/frames/v2/genre_frame/genre_parser.rb +36 -0
  22. data/lib/id3tag/frames/v2/genre_frame/genre_parser_24.rb +15 -0
  23. data/lib/id3tag/frames/v2/genre_frame/genre_parser_pre_24.rb +39 -0
  24. data/lib/id3tag/frames/v2/text_frame.rb +37 -0
  25. data/lib/id3tag/frames/v2/unique_file_id_frame.rb +25 -0
  26. data/lib/id3tag/id3_v1_frame_parser.rb +87 -0
  27. data/lib/id3tag/id3_v2_frame_parser.rb +72 -0
  28. data/lib/id3tag/id3_v2_tag_header.rb +61 -0
  29. data/lib/id3tag/number_util.rb +14 -0
  30. data/lib/id3tag/string_util.rb +7 -0
  31. data/lib/id3tag/synchsafe_integer.rb +28 -0
  32. data/lib/id3tag/tag.rb +121 -0
  33. data/spec/fixtures/id3v1_and_v2.mp3 +0 -0
  34. data/spec/fixtures/id3v1_with_track_nr.mp3 +0 -0
  35. data/spec/fixtures/id3v1_without_track_nr.mp3 +0 -0
  36. data/spec/fixtures/id3v2.mp3 +0 -0
  37. data/spec/lib/id3tag/audio_file_spec.rb +40 -0
  38. data/spec/lib/id3tag/frames/util/genre_name_by_id_finder_spec.rb +18 -0
  39. data/spec/lib/id3tag/frames/v1/comments_frame_spec.rb +31 -0
  40. data/spec/lib/id3tag/frames/v1/genre_frame_spec.rb +20 -0
  41. data/spec/lib/id3tag/frames/v1/text_frame_spec.rb +14 -0
  42. data/spec/lib/id3tag/frames/v1/track_nr_frame_spec.rb +19 -0
  43. data/spec/lib/id3tag/frames/v2/basic_frame_spec.rb +25 -0
  44. data/spec/lib/id3tag/frames/v2/comments_frame_spec.rb +45 -0
  45. data/spec/lib/id3tag/frames/v2/genre_frame/genre_parser_24_spec.rb +26 -0
  46. data/spec/lib/id3tag/frames/v2/genre_frame/genre_parser_pre_24_spec.rb +48 -0
  47. data/spec/lib/id3tag/frames/v2/genre_frame_spec.rb +44 -0
  48. data/spec/lib/id3tag/frames/v2/text_frame_spec.rb +59 -0
  49. data/spec/lib/id3tag/frames/v2/unique_file_id_frame_spec.rb +31 -0
  50. data/spec/lib/id3tag/id3_v1_frame_parser_spec.rb +67 -0
  51. data/spec/lib/id3tag/id3_v2_frame_parser_spec.rb +18 -0
  52. data/spec/lib/id3tag/id3_v2_tag_header_spec.rb +60 -0
  53. data/spec/lib/id3tag/id3tag_spec.rb +17 -0
  54. data/spec/lib/id3tag/number_util_spec.rb +24 -0
  55. data/spec/lib/id3tag/string_util_spec.rb +21 -0
  56. data/spec/lib/id3tag/synchsafe_integer_spec.rb +14 -0
  57. data/spec/lib/id3tag/tag_spec.rb +84 -0
  58. data/spec/spec_helper.rb +9 -1
  59. data/spec/support/mp3_fixtures.rb +4 -0
  60. metadata +102 -38
  61. data/README.rdoc +0 -19
  62. data/spec/id3tag_spec.rb +0 -7
Binary file
Binary file
@@ -0,0 +1,40 @@
1
+ require "spec_helper"
2
+ describe ID3Tag::AudioFile do
3
+ let(:mp3_with_v1_tag) { mp3_fixture('id3v1_without_track_nr.mp3') }
4
+ let(:mp3_with_v2_tag) { mp3_fixture('id3v2.mp3') }
5
+ let(:mp3_with_v1_and_v2_tags) { mp3_fixture('id3v1_and_v2.mp3') }
6
+
7
+ describe "Tag presence checking methods" do
8
+ context "reading file only with ID3v1 tag" do
9
+ subject { described_class.new(mp3_with_v1_tag) }
10
+ its(:v1_tag_present?) { should be_true }
11
+ its(:v2_tag_present?) { should be_false }
12
+ end
13
+
14
+ context "reading file only with ID3v2 tag" do
15
+ subject { described_class.new(mp3_with_v2_tag) }
16
+ its(:v1_tag_present?) { should be_false }
17
+ its(:v2_tag_present?) { should be_true }
18
+ end
19
+
20
+ context "reading file with ID3v1 and ID3v2 tags" do
21
+ subject { described_class.new(mp3_with_v1_and_v2_tags) }
22
+ its(:v1_tag_present?) { should be_true }
23
+ its(:v2_tag_present?) { should be_true }
24
+ end
25
+ end
26
+
27
+ describe "#v1_tag_body" do
28
+ subject { described_class.new(mp3_with_v1_tag) }
29
+ it "should return last 125 bytes of audio file" do
30
+ subject.v1_tag_body.should == File.read(mp3_with_v1_tag, 125, 579)
31
+ end
32
+ end
33
+
34
+ describe "#v2_tag_body" do
35
+ subject { described_class.new(mp3_with_v2_tag) }
36
+ it "should return frame and padding bytes" do
37
+ subject.v2_tag_body.should == File.read(mp3_with_v2_tag, 246, 10)
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,18 @@
1
+ require "spec_helper"
2
+
3
+ describe ID3Tag::Frames::Util::GenreNames do
4
+
5
+ describe 'self#find_by_id' do
6
+ subject { described_class.find_by_id(id) }
7
+
8
+ context "when calling with id 0 what represents Blues" do
9
+ let(:id) { 0 }
10
+ it { should eq('Blues') }
11
+ end
12
+
13
+ context "when calling with id 17 what represents Rock" do
14
+ let(:id) { 17 }
15
+ it { should eq('Rock') }
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,31 @@
1
+ require 'spec_helper'
2
+ describe ID3Tag::Frames::V1::CommentsFrame do
3
+ describe '#id' do
4
+ subject { described_class.new('comments', nil).id }
5
+ it { should == 'comments' }
6
+ end
7
+
8
+ describe '#content' do
9
+ context 'when comment is present' do
10
+ subject { described_class.new('comments', 'some comment about the song').content }
11
+ it { should == 'some comment about the song' }
12
+ end
13
+
14
+ context 'when comment is not present' do
15
+ subject { described_class.new('comments', '').content }
16
+ it { should == '' }
17
+ end
18
+ end
19
+
20
+ describe '#language' do
21
+ it 'should be unknown' do
22
+ described_class.new('comments', '').language.should eq('unknown')
23
+ end
24
+ end
25
+
26
+ describe '#desciption' do
27
+ it 'should be unknown' do
28
+ described_class.new('comments', '').desciption.should eq('unknown')
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,20 @@
1
+ require 'spec_helper'
2
+ describe ID3Tag::Frames::V1::GenreFrame do
3
+ describe '#id' do
4
+ subject { described_class.new('genre', nil).id }
5
+ it { should == 'genre' }
6
+ end
7
+
8
+ describe '#content' do
9
+ context 'when genre is blues' do
10
+ subject { described_class.new('genre', [0].pack('c')).content }
11
+ it { should == 'Blues' }
12
+ end
13
+
14
+ context 'when genre is metal' do
15
+ subject { described_class.new('genre', [9].pack('c')).content }
16
+ it { should == 'Metal' }
17
+ end
18
+ end
19
+ end
20
+
@@ -0,0 +1,14 @@
1
+ require 'spec_helper'
2
+ describe ID3Tag::Frames::V1::TextFrame do
3
+ describe '#id' do
4
+ subject { described_class.new('album', nil).id }
5
+ it { should == 'album' }
6
+ end
7
+
8
+ describe '#content' do
9
+ context 'when containing track artist' do
10
+ subject { described_class.new('artist', 'some fancy artist').content }
11
+ it { should == 'some fancy artist' }
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,19 @@
1
+ require 'spec_helper'
2
+ describe ID3Tag::Frames::V1::TrackNrFrame do
3
+ describe '#id' do
4
+ subject { described_class.new('track_nr', nil).id }
5
+ it { should == 'track_nr' }
6
+ end
7
+
8
+ describe '#content' do
9
+ context 'when nr is 3' do
10
+ subject { described_class.new('track_nr', [3].pack('c')).content }
11
+ it { should == '3' }
12
+ end
13
+
14
+ context 'when nr is 11' do
15
+ subject { described_class.new('track_nr', [11].pack('c')).content }
16
+ it { should == '11' }
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,25 @@
1
+ require 'spec_helper'
2
+
3
+ describe ID3Tag::Frames::V2::BasicFrame do
4
+ let(:id) { 'foo' }
5
+ let(:raw_content) { 'bar' }
6
+ let(:flags) { nil }
7
+ let(:major_version_number) { 4 }
8
+ let(:frame) { described_class.new(id, raw_content, flags, major_version_number) }
9
+
10
+ describe '#id' do
11
+ subject { frame.id }
12
+ it { should == :foo }
13
+ end
14
+
15
+ describe '#content' do
16
+ subject { frame.content }
17
+ it { should == 'bar' }
18
+ end
19
+
20
+ describe '#inspect' do
21
+ it 'should be pretty inspectable' do
22
+ frame.inspect.should eq('<ID3Tag::Frames::V2::BasicFrame foo: bar>')
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,45 @@
1
+ # encoding: utf-8
2
+ require 'spec_helper'
3
+
4
+ describe ID3Tag::Frames::V2::CommentsFrame do
5
+ let(:id) { "COMM" }
6
+ let(:raw_content) do
7
+ text.encode(target_encoding).prepend(separator).prepend(short_desc).prepend(lang).prepend(encoding)
8
+ end
9
+ let(:separator) { "\x00".force_encoding(target_encoding) }
10
+ let(:short_desc) { 'bob once said'.force_encoding(target_encoding) }
11
+ let(:lang) { 'lav'.force_encoding(target_encoding) }
12
+ let(:encoding) { encoding_byte.force_encoding(target_encoding) }
13
+ let(:flags) { nil }
14
+ let(:major_version_number) { 4 }
15
+ let(:target_encoding) { Encoding::UTF_8 }
16
+ let(:encoding_byte) { "\x03" }
17
+ let(:text) { "Glāzšķūņrūķīši" }
18
+ let(:frame) { described_class.new(id, raw_content, flags, major_version_number) }
19
+
20
+ describe '#id' do
21
+ subject { frame.id }
22
+ it { should == :COMM }
23
+ end
24
+
25
+ describe '#content' do
26
+ subject { frame.content }
27
+ it { should == 'Glāzšķūņrūķīši' }
28
+ end
29
+
30
+ describe '#language' do
31
+ subject { frame.language }
32
+ it { should == 'lav' }
33
+ end
34
+
35
+ describe '#description' do
36
+ subject { frame.description }
37
+ it { should == 'bob once said' }
38
+ end
39
+
40
+ describe '#inspect' do
41
+ it 'should be pretty inspectable' do
42
+ frame.inspect.should eq('<ID3Tag::Frames::V2::CommentsFrame COMM: Glāzšķūņrūķīši>')
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,26 @@
1
+ # encoding: utf-8
2
+ require 'spec_helper'
3
+
4
+ describe ID3Tag::Frames::V2::GenreFrame::GenreParser24 do
5
+ describe '#genres' do
6
+
7
+ subject { described_class.new(genre_string).genres }
8
+
9
+ context "when version is 2.4" do
10
+ context "with remix and cover" do
11
+ let(:genre_string) { "RX\x00CR" }
12
+ it { should == ['Remix', 'Cover'] }
13
+ end
14
+ context "with refinement and number" do
15
+ let(:genre_string) { "ABC\x0017" }
16
+ it { should == ['ABC', 'Rock'] }
17
+ end
18
+ context "with one genre" do
19
+ let(:genre_string) { "17" }
20
+ it { should == ['Rock'] }
21
+ end
22
+ end
23
+
24
+ end
25
+ end
26
+
@@ -0,0 +1,48 @@
1
+ # encoding: utf-8
2
+ require 'spec_helper'
3
+
4
+ describe ID3Tag::Frames::V2::GenreFrame::GenreParserPre24 do
5
+ describe '#genres' do
6
+
7
+ subject { described_class.new(genre_string).genres }
8
+
9
+ context "without refinements" do
10
+ context "with one genre" do
11
+ let(:genre_string) { '(17)' }
12
+ it { should == ['Rock'] }
13
+ end
14
+ context "with multiple genres" do
15
+ let(:genre_string) { '(17)(18)' }
16
+ it { should == ['Rock', 'Techno'] }
17
+ end
18
+ context "with remix and cover" do
19
+ let(:genre_string) { '(RX)(CR)' }
20
+ it { should == ['Remix', 'Cover'] }
21
+ end
22
+ end
23
+ context "with refinements" do
24
+ context "with one genre" do
25
+ let(:genre_string) { '(17)qwerty' }
26
+ it { should == ['qwerty'] }
27
+ end
28
+ context "with miltiple genres but only 1 refinement" do
29
+ let(:genre_string) { '(0)(16)(17)qwerty' }
30
+ it { should == ['Blues', 'Reggae', 'qwerty'] }
31
+ end
32
+ context "with multiple genres" do
33
+ let(:genre_string) { '(17)qwerty(18)abcdef' }
34
+ it { should == ['qwerty', 'abcdef'] }
35
+ end
36
+ end
37
+ context "with refinement containing '('" do
38
+ context "with one genre" do
39
+ let(:genre_string) { '(55)((I think...)' }
40
+ it { should == ['(I think...)'] }
41
+ end
42
+ context "with multiple genres" do
43
+ let(:genre_string) { '(55)((I think...)(17)' }
44
+ it { should == ['(I think...)', 'Rock'] }
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,44 @@
1
+ # encoding: utf-8
2
+ require 'spec_helper'
3
+
4
+ describe ID3Tag::Frames::V2::GenreFrame do
5
+ let(:id) { "TCON" }
6
+ let(:raw_content) { "(17)" }
7
+ let(:flags) { nil }
8
+ let(:major_version_number) { 3 }
9
+ let(:frame) { described_class.new(id, raw_content, flags, major_version_number) }
10
+
11
+ describe '#id' do
12
+ subject { frame.id }
13
+ it { should == :TCON }
14
+ end
15
+
16
+ describe '#content' do
17
+ subject { frame.content }
18
+
19
+ context "when one genre" do
20
+ before do
21
+ ID3Tag::Frames::V2::GenreFrame::GenreParserPre24.any_instance.stub(:genres) { ['A'] }
22
+ end
23
+
24
+ it { should eq('A') }
25
+ end
26
+ context "when two genres" do
27
+ before do
28
+ ID3Tag::Frames::V2::GenreFrame::GenreParserPre24.any_instance.stub(:genres) { ['A', 'B'] }
29
+ end
30
+
31
+ it { should eq('A, B') }
32
+ end
33
+ end
34
+
35
+ describe '#inspect' do
36
+ before do
37
+ frame.stub(:content) { 'Rock' }
38
+ end
39
+
40
+ it 'should be pretty inspectable' do
41
+ frame.inspect.should eq('<ID3Tag::Frames::V2::GenreFrame TCON: Rock>')
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,59 @@
1
+ # encoding: utf-8
2
+ require 'spec_helper'
3
+
4
+ describe ID3Tag::Frames::V2::TextFrame do
5
+ let(:id) { "artist" }
6
+ let(:raw_content) { text.encode(target_encoding).prepend(encoding_byte.force_encoding(target_encoding)) }
7
+ let(:flags) { nil }
8
+ let(:major_version_number) { 4 }
9
+
10
+ let(:frame) { described_class.new(id, raw_content, flags, major_version_number) }
11
+ let(:target_encoding) { Encoding::UTF_8 }
12
+ let(:encoding_byte) { "\x03" }
13
+ let(:text) { "Glāzšķūņrūķīši" }
14
+
15
+ describe '#id' do
16
+ subject { frame.id }
17
+ it { should == :artist }
18
+ end
19
+
20
+ describe '#content' do
21
+ subject { frame.content }
22
+
23
+ context "when encoding byte is not present" do
24
+ let(:encoding_byte) { "" }
25
+ it { expect { subject }.to raise_error(ID3Tag::Frames::V2::TextFrame::UnsupportedTextEncoding) }
26
+ end
27
+
28
+ context "when encoding is ISO08859_1" do
29
+ let(:target_encoding) { Encoding::ISO8859_1 }
30
+ let(:encoding_byte) { "\x00" }
31
+ let(:text) { "some fancy artist" }
32
+ it { should == 'some fancy artist' }
33
+ end
34
+
35
+ context "when encoding is UTF_16" do
36
+ let(:target_encoding) { Encoding::UTF_16 }
37
+ let(:encoding_byte) { "\x01" }
38
+ it { should == 'Glāzšķūņrūķīši' }
39
+ end
40
+
41
+ context "when encoding is UTF_16BE" do
42
+ let(:target_encoding) { Encoding::UTF_16BE }
43
+ let(:encoding_byte) { "\x02" }
44
+ it { should == 'Glāzšķūņrūķīši' }
45
+ end
46
+
47
+ context "when encoding is UTF_8" do
48
+ let(:target_encoding) { Encoding::UTF_8 }
49
+ let(:encoding_byte) { "\x03" }
50
+ it { should == 'Glāzšķūņrūķīši' }
51
+ end
52
+ end
53
+
54
+ describe '#inspect' do
55
+ it 'should be pretty inspectable' do
56
+ frame.inspect.should eq('<ID3Tag::Frames::V2::TextFrame artist: Glāzšķūņrūķīši>')
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,31 @@
1
+ # encoding: utf-8
2
+ require 'spec_helper'
3
+
4
+ describe ID3Tag::Frames::V2::UniqueFileIdFrame do
5
+ let(:id) { "UFID" }
6
+ let(:raw_content) { "ZXC\x00foobar" }
7
+ let(:flags) { nil }
8
+ let(:major_version_number) { 4 }
9
+ let(:frame) { described_class.new(id, raw_content, flags, major_version_number) }
10
+
11
+ describe '#id' do
12
+ subject { frame.id }
13
+ it { should == :UFID }
14
+ end
15
+
16
+ describe '#owner_identifier' do
17
+ subject { frame.owner_identifier }
18
+ it { should == 'ZXC' }
19
+ end
20
+
21
+ describe '#content' do
22
+ subject { frame.content }
23
+ it { should == 'foobar' }
24
+ end
25
+
26
+ describe '#inspect' do
27
+ it 'should be pretty inspectable' do
28
+ frame.inspect.should eq('<ID3Tag::Frames::V2::UniqueFileIdFrame UFID: ZXC>')
29
+ end
30
+ end
31
+ end