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
@@ -0,0 +1,50 @@
1
+ module ID3Tag
2
+ class FrameIdAdvisor
3
+ COMMON_FRAME_IDS_BY_VERSION = {
4
+ 'v1.x' => {
5
+ :artist => :artist,
6
+ :title => :title,
7
+ :album => :album,
8
+ :year => :year,
9
+ :comments => :comments,
10
+ :genre => :genre,
11
+ :track_nr => :track_nr
12
+ },
13
+ 'v2.2' => {
14
+ :artist => :TP1,
15
+ :title => :TT2,
16
+ :album => :TAL,
17
+ :year => :TYE,
18
+ :comments => :COM,
19
+ :genre => :TCO,
20
+ :track_nr => :TRK
21
+ },
22
+ 'v2.3' => {
23
+ :artist => :TPE1,
24
+ :title => :TIT2,
25
+ :album => :TALB,
26
+ :year => :TYER,
27
+ :comments => :COMM,
28
+ :genre => :TCON,
29
+ :track_nr => :TRCK
30
+ },
31
+ 'v2.4' => {
32
+ :artist => :TPE1,
33
+ :title => :TIT2,
34
+ :album => :TALB,
35
+ :year => :TDRC,
36
+ :comments => :COMM,
37
+ :genre => :TCON,
38
+ :track_nr => :TRCK
39
+ }
40
+ }
41
+
42
+ def initialize(version, major_version)
43
+ @version, @major_version = version, major_version
44
+ end
45
+
46
+ def advise(frame_name)
47
+ COMMON_FRAME_IDS_BY_VERSION["v#{@version}.#{@major_version}"][frame_name]
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,140 @@
1
+ module ID3Tag
2
+ module Frames
3
+ module Util
4
+ class GenreNames
5
+ LIST = [
6
+ 'Blues',
7
+ 'Classic Rock',
8
+ 'Country',
9
+ 'Dance',
10
+ 'Disco',
11
+ 'Funk',
12
+ 'Grunge',
13
+ 'Hip-Hop',
14
+ 'Jazz',
15
+ 'Metal',
16
+ 'New Age',
17
+ 'Oldies',
18
+ 'Other',
19
+ 'Pop',
20
+ 'R&B',
21
+ 'Rap',
22
+ 'Reggae',
23
+ 'Rock',
24
+ 'Techno',
25
+ 'Industrial',
26
+ 'Alternative',
27
+ 'Ska',
28
+ 'Death Metal',
29
+ 'Pranks',
30
+ 'Soundtrack',
31
+ 'Euro-Techno',
32
+ 'Ambient',
33
+ 'Trip-Hop',
34
+ 'Vocal',
35
+ 'Jazz+Funk',
36
+ 'Fusion',
37
+ 'Trance',
38
+ 'Classical',
39
+ 'Instrumental',
40
+ 'Acid',
41
+ 'House',
42
+ 'Game',
43
+ 'Sound Clip',
44
+ 'Gospel',
45
+ 'Noise',
46
+ 'AlternRock',
47
+ 'Bass',
48
+ 'Soul',
49
+ 'Punk',
50
+ 'Space',
51
+ 'Meditative',
52
+ 'Instrumental Pop',
53
+ 'Instrumental Rock',
54
+ 'Ethnic',
55
+ 'Gothic',
56
+ 'Darkwave',
57
+ 'Techno-Industrial',
58
+ 'Electronic',
59
+ 'Pop-Folk',
60
+ 'Eurodance',
61
+ 'Dream',
62
+ 'Southern Rock',
63
+ 'Comedy',
64
+ 'Cult',
65
+ 'Gangsta',
66
+ 'Top 40',
67
+ 'Christian Rap',
68
+ 'Pop/Funk',
69
+ 'Jungle',
70
+ 'Native American',
71
+ 'Cabaret',
72
+ 'New Wave',
73
+ 'Psychadelic',
74
+ 'Rave',
75
+ 'Showtunes',
76
+ 'Trailer',
77
+ 'Lo-Fi',
78
+ 'Tribal',
79
+ 'Acid Punk',
80
+ 'Acid Jazz',
81
+ 'Polka',
82
+ 'Retro',
83
+ 'Musical',
84
+ 'Rock & Roll',
85
+ 'Hard Rock',
86
+ 'Folk',
87
+ 'Folk-Rock',
88
+ 'National Folk',
89
+ 'Swing',
90
+ 'Fast Fusion',
91
+ 'Bebob',
92
+ 'Latin',
93
+ 'Revival',
94
+ 'Celtic',
95
+ 'Bluegrass',
96
+ 'Avantgarde',
97
+ 'Gothic Rock',
98
+ 'Progressive Rock',
99
+ 'Psychedelic Rock',
100
+ 'Symphonic Rock',
101
+ 'Slow Rock',
102
+ 'Big Band',
103
+ 'Chorus',
104
+ 'Easy Listening',
105
+ 'Acoustic',
106
+ 'Humour',
107
+ 'Speech',
108
+ 'Chanson',
109
+ 'Opera',
110
+ 'Chamber Music',
111
+ 'Sonata',
112
+ 'Symphony',
113
+ 'Booty Bass',
114
+ 'Primus',
115
+ 'Porn Groove',
116
+ 'Satire',
117
+ 'Slow Jam',
118
+ 'Club',
119
+ 'Tango',
120
+ 'Samba',
121
+ 'Folklore',
122
+ 'Ballad',
123
+ 'Power Ballad',
124
+ 'Rhythmic Soul',
125
+ 'Freestyle',
126
+ 'Duet',
127
+ 'Punk Rock',
128
+ 'Drum Solo',
129
+ 'A capella',
130
+ 'Euro-House',
131
+ 'Dance Hall'
132
+ ]
133
+
134
+ def self.find_by_id(id)
135
+ LIST[id.to_i]
136
+ end
137
+ end
138
+ end
139
+ end
140
+ end
@@ -0,0 +1,24 @@
1
+ module ID3Tag
2
+ module Frames
3
+ module V1
4
+ class CommentsFrame < TextFrame
5
+ def language
6
+ 'unknown'
7
+ end
8
+
9
+ def desciption
10
+ 'unknown'
11
+ end
12
+
13
+ def text
14
+ content
15
+ end
16
+
17
+ def content
18
+ @content.encode(destination_encoding, source_encoding)
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
24
+
@@ -0,0 +1,19 @@
1
+ module ID3Tag
2
+ module Frames
3
+ module V1
4
+ class GenreFrame
5
+ attr_reader :id
6
+
7
+ def initialize(id, content)
8
+ @id, @content = id, content
9
+ end
10
+
11
+ def content
12
+ nr = @content.unpack(NumberUtil::FORMAT_FOR_8_BIT_SIGNED_INTEGER).first
13
+ nr && Util::GenreNames.find_by_id(nr)
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
19
+
@@ -0,0 +1,27 @@
1
+ module ID3Tag
2
+ module Frames
3
+ module V1
4
+ class TextFrame
5
+ attr_reader :id
6
+
7
+ def initialize(id, content)
8
+ @id, @content = id, content
9
+ end
10
+
11
+ def content
12
+ @content.encode(destination_encoding, source_encoding)
13
+ end
14
+
15
+ private
16
+
17
+ def destination_encoding
18
+ Encoding::UTF_8
19
+ end
20
+
21
+ def source_encoding
22
+ Encoding::ISO8859_1
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,18 @@
1
+ module ID3Tag
2
+ module Frames
3
+ module V1
4
+ class TrackNrFrame
5
+ attr_reader :id
6
+
7
+ def initialize(id, content)
8
+ @id, @content = id, content
9
+ end
10
+
11
+ def content
12
+ @content.unpack(NumberUtil::FORMAT_FOR_8_BIT_SIGNED_INTEGER).first.to_s
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
18
+
@@ -0,0 +1,26 @@
1
+ module ID3Tag
2
+ module Frames
3
+ module V2
4
+ class BasicFrame
5
+ attr_reader :id
6
+ def initialize(id, content, flags, major_version_number)
7
+ @id, @raw_content, @flags, @major_version_number = id.to_sym, content, flags, major_version_number
8
+ end
9
+
10
+ def content
11
+ @raw_content
12
+ end
13
+
14
+ def inspect
15
+ "<#{self.class.name} #{id}: #{inspect_content}>"
16
+ end
17
+
18
+ private
19
+
20
+ def inspect_content
21
+ content
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,44 @@
1
+ module ID3Tag
2
+ module Frames
3
+ module V2
4
+ class CommentsFrame < TextFrame
5
+
6
+ # language code according to https://en.wikipedia.org/wiki/List_of_ISO_639-2_codes
7
+ def language
8
+ @language ||= get_language
9
+ end
10
+
11
+ def description
12
+ @desciption ||= encoded_text_and_content_parts.first
13
+ end
14
+
15
+ def text
16
+ @text ||= encoded_text_and_content_parts.last
17
+ end
18
+
19
+ def content
20
+ text
21
+ end
22
+
23
+ private
24
+
25
+ def encoded_text_and_content_parts
26
+ @encoded_text_and_content_parts ||= encoded_text_and_content.split("\00",2)
27
+ end
28
+
29
+ def encoded_text_and_content
30
+ raw_text_and_content.encode(destination_encoding, source_encoding)
31
+ end
32
+
33
+ def raw_text_and_content
34
+ content_without_encoding_byte[3..-1]
35
+ end
36
+
37
+ def get_language
38
+ content_without_encoding_byte[0..2].downcase
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
44
+
@@ -0,0 +1,36 @@
1
+ module ID3Tag
2
+ module Frames
3
+ module V2
4
+ class FrameFabricator
5
+ class << self
6
+ def fabricate(id, content, flags, major_version_number)
7
+ new(id, content, flags, major_version_number).fabricate
8
+ end
9
+ end
10
+
11
+ def initialize(id, content, flags, major_version_number)
12
+ @id, @content, @flags, @major_version_number = id, content, flags, major_version_number
13
+ end
14
+
15
+ def fabricate
16
+ frame_class.new(@id, @content, @flags, @major_version_number)
17
+ end
18
+
19
+ def frame_class
20
+ case @id
21
+ when /^(TCON|TCO)$/
22
+ GenreFrame
23
+ when /^T/
24
+ TextFrame
25
+ when /^(COM|COMM)$/
26
+ CommentsFrame
27
+ when /^UFID$/
28
+ UniqueFileIdFrame
29
+ else
30
+ BasicFrame
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,34 @@
1
+ module ID3Tag
2
+ module Frames
3
+ module V2
4
+ class GenreFrame < BasicFrame
5
+ class MissingGenreParser < StandardError; end
6
+
7
+ def genres
8
+ @genres ||= get_genres
9
+ end
10
+
11
+ def content
12
+ genres.join(", ")
13
+ end
14
+
15
+ private
16
+
17
+ def get_genres
18
+ genre_parser.new(@raw_content).genres
19
+ end
20
+
21
+ def genre_parser
22
+ case @major_version_number
23
+ when 0...4
24
+ GenreParserPre24
25
+ when 4
26
+ GenreParser24
27
+ else
28
+ raise(MissingGenreParser,"Missing genre parser for tag version v.2.#{@major_version_number}")
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,36 @@
1
+ module ID3Tag
2
+ module Frames
3
+ module V2
4
+ class GenreFrame
5
+ class GenreParser
6
+ NUMERIC = /\A\d+\z/
7
+ attr_reader :genre_string
8
+
9
+ def initialize(genre_string)
10
+ @genre_string = genre_string
11
+ end
12
+
13
+ def expand_abbreviation(abbreviation)
14
+ case abbreviation
15
+ when 'CR'
16
+ 'Cover'
17
+ when 'RX'
18
+ 'Remix'
19
+ else
20
+ if abbreviation =~ NUMERIC
21
+ Util::GenreNames.find_by_id(abbreviation.to_i)
22
+ else
23
+ abbreviation
24
+ end
25
+ end
26
+ end
27
+
28
+ def genres
29
+ raise "#genres is not implemented for class #{self.class}"
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
36
+