id3tag 0.0.0 → 0.1.0

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.
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