File: mp3tag.rb

package info (click to toggle)
libmp3tag-ruby 1.0-1
  • links: PTS
  • area: main
  • in suites: sarge
  • size: 76 kB
  • ctags: 49
  • sloc: ruby: 352; makefile: 34
file content (238 lines) | stat: -rw-r--r-- 6,629 bytes parent folder | download | duplicates (4)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
=begin
= Synopsis

Read the tag from a file or create and Mp3Tag instance for saving tag to
mp3 file later:

   tag = Mp3Tag.new(filename)

Examining tags:

   tag.songname
   tag.artist
   tag.album
   tag.year
   tag.comment
   tag.tracknum
   tag.genre_id
   tag.genre

Setting tags:

   tag.songname = "My Song"
   tag.artist = "Me"
   tag.album = "My Album"
   tag.year = "2001"
   tag.comment = "No Comment"
   tag.tracknum = 3

   tag.genre_id = 23
   tag.genre = "Drum Solo"

genre_id's should exist in Mp3Tag::Genres. Elements in Mp3Tag::Genres
can be assigned using tag.genre= and the id will be looked up
automatically.

Saving tag to mp3:

   tag.commit

Checking if a file has a tag:

   Mp3Tag.hastag?(filename)

=end

# '

class Mp3Tag

  VERSION = '1.0'

  Genres = [ 'Blues', 'Classic Rock', 'Country', 'Dance',
    'Disco', 'Funk', 'Grunge', 'Hip-Hop', 'Jazz', 'Metal', 'New Age',
    'Oldies', 'Other', 'Pop', 'R&B', 'Rap', 'Reggae', 'Rock', 'Techno',
    'Industrial', 'Alternative', 'Ska', 'Death Metal', 'Pranks',
    'Soundtrack', 'Euro-Techno', 'Ambient', 'Trip-Hop', 'Vocal',
    'Jazz+Funk', 'Fusion', 'Trance', 'Classical', 'Instrumental',
    'Acid', 'House', 'Game', 'Sound Clip', 'Gospel', 'Noise',
    'Alternative Rock', 'Bass', 'Soul', 'Punk', 'Space', 'Meditative',
    'Instrumental Pop', 'Instrumental Rock', 'Ethnic', 'Gothic',
    'Darkwave', 'Techno-Industrial', 'Electronic', 'Pop-Folk',
    'Eurodance', 'Dream', 'Southern Rock', 'Comedy', 'Cult', 'Gangsta',
    'Top 40', 'Christian Rap', 'Pop/Funk', 'Jungle', 'Native US',
    'Cabaret', 'New Wave', 'Psychadelic', 'Rave', 'Showtunes',
    'Trailer', 'Lo-Fi', 'Tribal', 'Acid Punk', 'Acid Jazz', 'Polka',
    'Retro', 'Musical', 'Rock & Roll', 'Hard Rock', 'Folk', 'Folk-Rock',
    'National Folk', 'Swing', 'Fast Fusion', 'Bebob', 'Latin',
    'Revival', 'Celtic', 'Bluegrass', 'Avantgarde', 'Gothic Rock',
    'Progressive Rock', 'Psychedelic Rock', 'Symphonic Rock',
    'Slow Rock', 'Big Band', 'Chorus', 'Easy Listening', 'Acoustic', 'Humour',
    'Speech', 'Chanson', 'Opera', 'Chamber Music', 'Sonata', 'Symphony',
    'Booty Bass', 'Primus', 'Porn Groove', 'Satire', 'Slow Jam', 'Club',
    'Tango', 'Samba', 'Folklore', 'Ballad', 'Power Ballad', 'Rhytmic Soul',
    'Freestyle', 'Duet', 'Punk Rock', 'Drum Solo', 'Acapella',
    'Euro-House', 'Dance Hall', 'Goa', 'Drum & Bass', 'Club-House',
    'Hardcore', 'Terror', 'Indie', 'BritPop', 'Negerpunk', 'Polsk Punk',
    'Beat', 'Christian Gangsta Rap', 'Heavy Metal', 'Black Metal',
    'Crossover', 'Contemporary Christian', 'Christian Rock', 'Merengue',
    'Salsa', 'Trash Metal', 'Anime', 'Jpop', 'Synthpop' ]

  attr_accessor :songname, :artist, :album, :comment
  attr_reader :path, :year, :tracknum, :genre_id

  TagSize = 128
  TAGFORMAT_WRITE = 'Z30Z30Z30Z4Z28CCC'# write zero padded
  TAGFORMAT_READ = 'A30A30A30A4A30C'# accept zero/space padded input

=begin
= Class Methods
--- Mp3Tag.new(path)
      Creates a new Mp3Tag object for the file give by ((|path|)).
=end

  def initialize(path)
    @path = path

    @genre_id = 255
    @songname = ''
    @artist = ''
    @album = ''
    @comment = ''
    @tracknum = 0
    @year = 0

    readtag
  end

=begin
--- Mp3Tag.hastag?(filename)
      Tests if ((|filename|)) has a ID3V1.0 or ID3V1.1 tag. Returns a boolean values giving the result of the test.
=end

  def Mp3Tag.hastag?(file)
    hastag = nil
    File.open(file, 'r') { |f|
      f.seek(-TagSize, IO::SEEK_END)
      tag = f.read(3)
      hastag = (tag == 'TAG')
    }
    return hastag
  end

=begin
--- Mp3Tag.removetag(filename)
      Removes an ID3v1 tag from the MP3 file ((|filename|))
=end

  def Mp3Tag.removetag(filename)
    if Mp3Tag.hastag?(filename)
      newsize = File.size(filename) - TagSize
      File.open(filename, 'r+') {
	|f|
	f.truncate(newsize)
      }
    end
  end

=begin
= Instance Methods
--- songname
--- artist
--- album
--- comment
      Return the song name, artist, album, or comment from the tag as a String object. Will return empty strings if file did not have a tag.
--- tracknum
      Returns the track number from the tag. Will return 0 if the track number was not set in the tag when loaded, or if the file had no tag.
--- year
      Returns the year from the tag. Will return 0 if the file had no tag.
--- genre_id
      Return the id number of the genre from the tag. Will return 255 if the file had no tag.
--- genre
      Returns the genre name. Will return "Unknown" if the file had no tag or the genre id was not in Mp3Tag::Genres
--- path
      Returns the full path name of the MP3 file.
--- filename
      Returns the filename without the directory part of the MP3 File.
--- songname=(txt)
--- artist=(txt)
--- album=(txt)
--- comment=(txt)
      Sets the song name, atist, album, or comment for the tag to ((|txt|)). ((|txt|)) should be a String object.
--- tracknum=(num)
      Sets the track number for the tag. Only values in the range (0..255) are allowed.
--- year=(num)
      Sets the year for the tag. Should be a four digit number.
--- genre_id=(num)
      Sets the the genre id for the tag. Only values in the range (0..255) are allowed.
--- genre=(txt)
      Sets the genre id for the tag to the index of ((|txt|)) in Mp3Tag::Genres. The genre id will be set to 255 if ((|txt|)) is not found in the list.

=end

  def tracknum=(num)
    @tracknum = num.to_i
  end

  def year=(year)
    @year = year.to_i
  end

  def genre=(genre)
    @genre_id = Genres.index(genre) || 255
  end

  def genre_id=(id)
    @genre_id = id.to_i
  end
 
  def genre
    Genres[@genre_id] || 'Unknown'
  end

  def filename
    File.basename(@path)
  end

  def readtag
    return if File.size(@path) < TagSize
    File.open(@path, 'r') {
      |f|
      f.seek(-TagSize, IO::SEEK_END)
      tag = f.read(3)
      if (tag == 'TAG')
	rest = f.read(TagSize-3)
	# Read id3v1.0 tag
	@songname, @artist, @album, @year_s, @comment, @genre_id = rest.unpack TAGFORMAT_READ
	@year = @year_s.to_i
	# Handle id3v1.1 tracknums
	if @comment[28] == 0
	  @tracknum = @comment[29]
	  @comment.sub!(/\000+.$/, '')# remove nulls and tracknum from comment
	end
      end
    }
  end
  private :readtag

=begin
--- commit
      Saves the tag to the MP3 file. Will overwrite any existing tag.
=end

  def commit
    File.open(@path, 'r+') {
      |f|
      f.seek(-TagSize, IO::SEEK_END)
      tag = f.read(3)
      if tag != 'TAG'
	# Append new tag
	f.seek(0, IO::SEEK_END)
	f.write('TAG')
      end
      f.write([@songname,@artist,@album, ("%04d" % @year), @comment, 0, @tracknum, @genre_id].pack(TAGFORMAT_WRITE))
    }
  end

end