summaryrefslogtreecommitdiff
path: root/omptagger
blob: d6d221e61d13733be0f53104281a4690c3914c65 (plain) (blame)
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
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
#!/usr/bin/env ruby
#
# omptagger
# http://github.com/omp/omptagger
# Modify and display metadata of audio files.
#
# Copyright 2007-2010 David Vazgenovich Shakaryan <dvshakaryan@gmail.com>
# Distributed under the terms of the GNU General Public License v3.
# See http://www.gnu.org/licenses/gpl.txt for the full license text.
#
# Dependencies:
#   Ruby            http://www.ruby-lang.org/
#   RubyGems        http://rubygems.org/
#   rtaglib         http://rubygems.org/gems/rtaglib
#   ruby-filemagic  http://rubygems.org/gems/ruby-filemagic
#   TagLib          http://developer.kde.org/~wheeler/taglib.html

require 'getoptlong'
require 'rubygems'
require 'TagLib'
require 'filemagic'

class Hash
  def longest_key_length
    self.keys.inject(0) do |longest, key|
      key.length > longest ? key.length : longest
    end
  end
end

class String
  def colourise(colour)
    case colour
    when :green
      code = '32'
    when :yellow
      code = '33'
    when :cyan
      code = '36'
    else
      return self
    end

    return "\e[#{code}m#{self}\e[0m"
  end

  def format(type)
    case type
    when :file
      indent = ''
      colour = :yellow
    when :action
      indent = '  '
      colour = :green
    when :tag
      indent = '    '
      colour = :cyan
    when :info
      indent = '    '
      colour = nil
    end

    if @@colour
      return indent + self.colourise(colour)
    else
      return indent + self
    end
  end
end

class Metadata
  def output(tag, val, padding = 0)
    puts tag.format(:tag) + ' ' * (2 + padding) + val
  end

  private :output

  def initialize(file)
    puts (file + ':').format(:file)

    @filename = File.expand_path(file)
    @write = false
  end

  def view
    puts 'Viewing all tags:'.format(:action)

    if @tags.empty?
      puts 'No tags set.'.format(:info)
      throw :next
    end

    @tags.sort.each do |tag, val|
      val.each do |val|
        output(tag, val, @tags.longest_key_length - tag.length)
      end
    end
  end

  def viewtag(tag)
    puts ('Viewing ' + tag + ' tag:').format(:action)

    unless @tags.has_key? tag
      puts 'Tag not set.'.format(:info)
      throw :next
    end

    @tags[tag].each do |val|
      output(tag, val.to_s)
    end
  end
end

class VorbisComment < Metadata
end

class FLAC < VorbisComment
  def initialize(file)
    super

    @file = TagLib::FLAC::File.new(file)
    @tags = @file.xiphComment.fieldListMap.hash
  end
end

class Vorbis < VorbisComment
  def initialize(file)
    super

    @file = TagLib::Vorbis::File.new(file)
    @tags = @file.tag.fieldListMap.hash
  end
end

class MP3 < Metadata
  def initialize(file)
    super

    @file = TagLib::MPEG::File.new(file)
    @tags = @file.ID3v2Tag.frameListMap.hash
  end

  def output(tag, val, padding = 0)
    super(tag, val.to_s, padding)
  end
end

def help
  puts <<-end
Usage: omptagger [actions/options] [files]

Actions:
  --view        -v  View all tags
  --view-tag    -t  View a tag
  --add-tag     -a  Add a tag
  --set-tag     -s  Set a tag
  --remove      -r  Remove all tags
  --remove-tag  -d  Remove a tag
  --generate    -g  Generate tags based on filename
  --rename      -m  Rename file based on tags
  --scheme      -n  Change file naming scheme

Options:
  --no-colour   -c  Disable colourisation of output
  --pretend     -p  Disable finalisation of changes
  --help        -h  Display help information
  --info        -i  Display additional information
  end

  exit
end

def info
  puts <<-end
Actions:
  * Multiple actions can be chained together.
  * Actions are executed in the order they are specified.
  * Example: omptagger --view --set-tag ALBUM='Foobar' --view

Schemes:
  * The default file naming scheme is Track - Artist - Title.
  * Schemes must be specified prior to actions that use them.
  * Example: omptagger --scheme '%a - %t' --generate
  * %a - Artist    %* - Wildcard
    %b - Album     %% - Per cent sign
    %d - Date
    %n - Track
    %t - Title

Supported Tags:
  * Vorbis comments: Any field name allowed by specification.
  * ID3v2 frames: Artist, album, title, track, year.

Tag Generation:
  * Underscores in filenames are converted to spaces in tags.
  * Backslashes in filenames are converted to forward slashes in tags.
  end

  exit
end

actions = Array.new
options = Array.new
scheme  = '%n - %a - %t'

GetoptLong.new(
  ['--view',       '-v', GetoptLong::NO_ARGUMENT],
  ['--view-tag',   '-t', GetoptLong::REQUIRED_ARGUMENT],
  ['--add-tag',    '-a', GetoptLong::REQUIRED_ARGUMENT],
  ['--set-tag',    '-s', GetoptLong::REQUIRED_ARGUMENT],
  ['--remove',     '-r', GetoptLong::NO_ARGUMENT],
  ['--remove-tag', '-d', GetoptLong::REQUIRED_ARGUMENT],
  ['--generate',   '-g', GetoptLong::NO_ARGUMENT],
  ['--rename',     '-m', GetoptLong::NO_ARGUMENT],
  ['--scheme',     '-n', GetoptLong::REQUIRED_ARGUMENT],
  ['--no-colour',  '-c', GetoptLong::NO_ARGUMENT],
  ['--pretend',    '-p', GetoptLong::NO_ARGUMENT],
  ['--help',       '-h', GetoptLong::NO_ARGUMENT],
  ['--info',       '-i', GetoptLong::NO_ARGUMENT]
).each do |option, argument|
  option = option.delete('-').intern

  case option
  when :scheme
    scheme = argument
  when :nocolour, :pretend, :help, :info
    options << option
  when :viewtag, :addtag, :settag, :removetag
    actions << [option, argument]
  when :generate, :rename
    actions << [option, scheme]
  else
    actions << [option]
  end
end

help if options.include?(:help)
info if options.include?(:info)
help if actions.empty?

if ARGV.empty?
  puts 'No files specified.'
  exit
end

if options.include?(:nocolour)
  class String
    @@colour = false
  end
else
  class String
    @@colour = true
  end
end

mime = FileMagic.mime
mime.simplified = true

ARGV.each do |file|
  begin
    raise 'File does not exist.' unless File.exist?(file)

    case mime.file(file)
    when 'audio/x-flac'
      track = FLAC.new(file)
    when 'application/ogg'
      track = Vorbis.new(file)
    when 'audio/mpeg'
      track = MP3.new(file)
    else
      raise 'File extension not recognised.'
    end

    actions.each do |action|
      catch :next do
        track.send(*action)
      end
    end
  rescue RuntimeError => message
    $stderr.puts $0 + ': ' + file + ': ' + message
  end
end