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
@@ -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
+