rutaci 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +339 -0
- data/Manifest +18 -0
- data/README +152 -0
- data/Rakefile +13 -0
- data/TODO +44 -0
- data/bin/rutaci +25 -0
- data/lib/rutaci/application.rb +232 -0
- data/lib/rutaci/filename_source.rb +59 -0
- data/lib/rutaci/formater.rb +81 -0
- data/lib/rutaci/getter.rb +79 -0
- data/lib/rutaci/interaction.rb +57 -0
- data/lib/rutaci/renamer.rb +63 -0
- data/lib/rutaci/setter.rb +71 -0
- data/lib/rutaci/source.rb +55 -0
- data/lib/rutaci/taglib_source.rb +38 -0
- data/lib/rutaci.rb +26 -0
- data/rutaci.gemspec +111 -0
- data/templates/rubyfile.rb +20 -0
- metadata +110 -0
@@ -0,0 +1,232 @@
|
|
1
|
+
# This file is part of rutaci, see the README for more information
|
2
|
+
# Copyright (C) 2008 Jonas Bähr <jonas.baehr@fs.ei.tum.de>
|
3
|
+
#
|
4
|
+
# This program is free software; you can redistribute it and/or
|
5
|
+
# modify it under the terms of the GNU General Public License
|
6
|
+
# as published by the Free Software Foundation; either version 2
|
7
|
+
# of the License, or (at your option) any later version.
|
8
|
+
#
|
9
|
+
# This program is distributed in the hope that it will be useful,
|
10
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
11
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
12
|
+
# GNU General Public License for more details.
|
13
|
+
#
|
14
|
+
# You should have received a copy of the GNU General Public License
|
15
|
+
# along with this program; if not, write to the Free Software
|
16
|
+
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
17
|
+
|
18
|
+
require 'optparse'
|
19
|
+
require 'ostruct'
|
20
|
+
|
21
|
+
module Rutaci
|
22
|
+
class Application
|
23
|
+
attr_reader :options
|
24
|
+
|
25
|
+
def initialize
|
26
|
+
@options = OpenStruct.new(
|
27
|
+
# default values
|
28
|
+
:command => :get,
|
29
|
+
:fields => [:track, :title, :artist, :album, :year, :genre, :comment], # FIXME: patch tagalib to provilde a list of all available fields
|
30
|
+
:destination => ".",
|
31
|
+
:files => []
|
32
|
+
)
|
33
|
+
#puts "default options: #{@options}"
|
34
|
+
end
|
35
|
+
|
36
|
+
def parse_options
|
37
|
+
# The general form of the parameters to parse is:
|
38
|
+
# "<general options> <command> <field otions> <filenames...>"
|
39
|
+
# Since GetoptLong and ParseOptions don't know handle this structure (options os a
|
40
|
+
# command in the middle) but I don't want to do all by hand, ARGV is split at the command
|
41
|
+
# position in two, the command is analysed manualy and the two parts are passed to GetoptLong
|
42
|
+
# seperately.
|
43
|
+
command_position = (ARGV.index('set') or ARGV.index('get') or ARGV.index('rename'))
|
44
|
+
if command_position
|
45
|
+
field_options = ARGV.slice! command_position..ARGV.length # splitt ARGV at the command_position
|
46
|
+
@options.fields = nil # don't use the default files when a command is given
|
47
|
+
@options.command = field_options.shift.to_sym
|
48
|
+
else
|
49
|
+
field_options = []
|
50
|
+
end
|
51
|
+
|
52
|
+
get_parser = OptionParser.new do |opts|
|
53
|
+
opts.banner = "fields to use with the get command (or set with --format/--muscibrains):"
|
54
|
+
opts.on("--title", "-t", "get the title") do
|
55
|
+
@options.fields ||= Array.new
|
56
|
+
@options.fields.push :title
|
57
|
+
end
|
58
|
+
opts.on("--artist", "-i", "get the artist") do
|
59
|
+
@options.fields ||= Array.new
|
60
|
+
@options.fields.push :artist
|
61
|
+
end
|
62
|
+
opts.on("--album", "-a", "get the album") do
|
63
|
+
@options.fields ||= Array.new
|
64
|
+
@options.fields.push :album
|
65
|
+
end
|
66
|
+
opts.on("--track", "-n", "get the tracknumer") do
|
67
|
+
@options.fields ||= Array.new
|
68
|
+
@options.fields.push :track
|
69
|
+
end
|
70
|
+
opts.on("--year", "-y", "get the year") do
|
71
|
+
@options.fields ||= Array.new
|
72
|
+
@options.fields.push :year
|
73
|
+
end
|
74
|
+
opts.on("--genre", "-g", "get the genre") do
|
75
|
+
@options.fields ||= Array.new
|
76
|
+
@options.fields.push :genre
|
77
|
+
end
|
78
|
+
opts.on("--comment", "-c", "get the comment") do
|
79
|
+
@options.fields ||= Array.new
|
80
|
+
@options.fields.push :comment
|
81
|
+
end
|
82
|
+
end # get_parser
|
83
|
+
|
84
|
+
set_parser = OptionParser.new do |opts|
|
85
|
+
opts.banner = "fields to use with the set command:"
|
86
|
+
opts.on("--title TITLE", "-t", "set the title to TITLE") do |title|
|
87
|
+
@options.fields ||= Hash.new
|
88
|
+
@options.fields[:title] = title
|
89
|
+
end
|
90
|
+
opts.on("--artist ARTIST", "-i", "set the artist to ARTIST") do |artist|
|
91
|
+
@options.fields ||= Hash.new
|
92
|
+
@options.fields[:artist] = artist
|
93
|
+
end
|
94
|
+
opts.on("--album ALBUM", "-a", "set the album to ALBUM") do |album|
|
95
|
+
@options.fields ||= Hash.new
|
96
|
+
@options.fields[:album] = album
|
97
|
+
end
|
98
|
+
opts.on("--track N", "-n", "set the tracknumer to N") do |track|
|
99
|
+
@options.fields ||= Hash.new
|
100
|
+
@options.fields[:track] = track
|
101
|
+
end
|
102
|
+
opts.on("--year YEAR", "-y", "set the year to YEAR") do |year|
|
103
|
+
@options.fields ||= Hash.new
|
104
|
+
@options.fields[:year] = year
|
105
|
+
end
|
106
|
+
opts.on("--genre GENRE", "-g", "set the genre to GENRE") do |genre|
|
107
|
+
@options.fields ||= Hash.new
|
108
|
+
@options.fields[:genre] = genre
|
109
|
+
end
|
110
|
+
opts.on("--comment COMMENT", "-c", "set the comment to COMMENT") do |comment|
|
111
|
+
@options.fields ||= Hash.new
|
112
|
+
@options.fields[:comment] = comment
|
113
|
+
end
|
114
|
+
|
115
|
+
opts.separator ""
|
116
|
+
opts.separator "Note: When using the --format option, the set parameters take no argument"
|
117
|
+
opts.separator " (like when used with get) The value is extracted via FORMAT from the"
|
118
|
+
opts.separator " the path/filename. Without parameters, every field appearing in FORMAT"
|
119
|
+
opts.separator " is used."
|
120
|
+
# opts.separator ""
|
121
|
+
# opts.separator "Note: When using the --musicbrainz option, the set parameters take no argument"
|
122
|
+
# opts.separator " (like when used with get) The value are looked up at musicbrains.org"
|
123
|
+
# opts.separator " using an acoustic fingerprint of the music file. Without parameters,"
|
124
|
+
# opts.separator " every field provided by musicbrains.org is used."
|
125
|
+
end # set_parser
|
126
|
+
|
127
|
+
general_parser = OptionParser.new do |opts|
|
128
|
+
opts.banner = "general options:"
|
129
|
+
opts.on("--dry-run", "-d", "Don't really execute the command, just show what would happen",
|
130
|
+
"Has no effect with the get command") do
|
131
|
+
@options.dry_run = true
|
132
|
+
end
|
133
|
+
opts.on("--interactive", "-i", "Give the user the chance to edit any value/filename",
|
134
|
+
"Has no effect with the get command; requires GNU readline") do
|
135
|
+
@options.interactive = true
|
136
|
+
end
|
137
|
+
opts.on("--keep-empty-fields", "-k", "Keep empty fields instead of removeing them when",
|
138
|
+
"displaying or setting an empty value.",
|
139
|
+
"Don't affects the rename command, we will never set an empty filename") do
|
140
|
+
@options.keep_empty_fields = true
|
141
|
+
end
|
142
|
+
opts.on("--format FORMAT", "-f", "Use FORMAT to parse input or format output and filenames",
|
143
|
+
"Used with a get command, it will format the output",
|
144
|
+
"Used with a set command, it's the pattern for parsing the filename",
|
145
|
+
"Used with a rename command, it will give the new filename",
|
146
|
+
"An example is '%n - %t.mp3', see the manual for details") do |format|
|
147
|
+
@options.format = format
|
148
|
+
end
|
149
|
+
# opts.on("--musicbrainz", "-m", "use musicbrainz.org to find the right taggs for you music",
|
150
|
+
# "This is just an other source for the metadata") do
|
151
|
+
# @options.musicbrainz = true
|
152
|
+
# end
|
153
|
+
|
154
|
+
opts.separator ""
|
155
|
+
opts.separator "options to use with the get command:"
|
156
|
+
opts.on("--values-only", "-o", "Print out only the values themselfs (one per line)",
|
157
|
+
"Fieldnames and the normaly displayed filename are ommited.") do
|
158
|
+
@options.values_only = true
|
159
|
+
end
|
160
|
+
|
161
|
+
opts.separator ""
|
162
|
+
opts.separator "options to use with the rename command:"
|
163
|
+
opts.on("--destination DESTINATION", "-D", "Set the destination directory to DESTINATION",
|
164
|
+
"default '#{@options.destination}'") do |destination|
|
165
|
+
@options.destination = destination
|
166
|
+
end
|
167
|
+
|
168
|
+
opts.separator ""
|
169
|
+
opts.separator "generic options:"
|
170
|
+
|
171
|
+
opts.on("--help", "-h", "-?", "Shows this message and quit") do
|
172
|
+
puts "rutaci is a cli tool for manipulating music file's metadata"
|
173
|
+
puts
|
174
|
+
puts "Useage: #{$0} [<options>] [get [<fields>]] <filenames...>"
|
175
|
+
puts " #{$0} [<options>] set <fields and values> <filenames...>"
|
176
|
+
puts " #{$0} [<options>] --format FORMAT rename <filenames...>"
|
177
|
+
puts
|
178
|
+
puts general_parser
|
179
|
+
puts
|
180
|
+
puts get_parser
|
181
|
+
puts
|
182
|
+
puts set_parser
|
183
|
+
exit
|
184
|
+
end
|
185
|
+
opts.on("--version", "Shows version and exit") do
|
186
|
+
puts VERSION_STRING
|
187
|
+
exit
|
188
|
+
end
|
189
|
+
end #general_parser
|
190
|
+
|
191
|
+
general_parser.parse! ARGV
|
192
|
+
@options.files.push *ARGV # the remaining args are filenames, when no command is given
|
193
|
+
|
194
|
+
if @options.command == :get or (@options.command == :set and (@options.format or @options.musicbrainz))
|
195
|
+
# note: when a format is given, we parse get-style,
|
196
|
+
# even with set since the values come from the filename or muscibrains (not the command line)
|
197
|
+
get_parser.parse! field_options
|
198
|
+
else
|
199
|
+
set_parser.parse! field_options if @options.command == :set
|
200
|
+
end
|
201
|
+
@options.files.push *field_options # the remaining args are filenames
|
202
|
+
|
203
|
+
if @options.files.empty?
|
204
|
+
puts "sorry, I need at least one filename to work with!"
|
205
|
+
puts "try the manpage or #{$0} --help"
|
206
|
+
exit 1
|
207
|
+
end
|
208
|
+
|
209
|
+
if @options.command == :rename and not @options.format
|
210
|
+
puts "sorry, I need a format for the rename command!"
|
211
|
+
puts "try the manpage or #{$0} --help"
|
212
|
+
exit 1
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
def run
|
217
|
+
parse_options
|
218
|
+
executor = case @options.command
|
219
|
+
when :get
|
220
|
+
require 'rutaci/getter'
|
221
|
+
Getter.new @options
|
222
|
+
when :set
|
223
|
+
require 'rutaci/setter'
|
224
|
+
Setter.new @options
|
225
|
+
when :rename
|
226
|
+
require 'rutaci/renamer'
|
227
|
+
Renamer.new @options
|
228
|
+
end
|
229
|
+
executor.run @options.files
|
230
|
+
end
|
231
|
+
end # class Application
|
232
|
+
end # module Rutaci
|
@@ -0,0 +1,59 @@
|
|
1
|
+
# rutaci is a ruby tagger with a command line interface
|
2
|
+
# Copyright (C) 2008 Jonas Bähr <jonas.baehr@fs.ei.tum.de>
|
3
|
+
#
|
4
|
+
# This program is free software; you can redistribute it and/or
|
5
|
+
# modify it under the terms of the GNU General Public License
|
6
|
+
# as published by the Free Software Foundation; either version 2
|
7
|
+
# of the License, or (at your option) any later version.
|
8
|
+
#
|
9
|
+
# This program is distributed in the hope that it will be useful,
|
10
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
11
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
12
|
+
# GNU General Public License for more details.
|
13
|
+
#
|
14
|
+
# You should have received a copy of the GNU General Public License
|
15
|
+
# along with this program; if not, write to the Free Software
|
16
|
+
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
17
|
+
|
18
|
+
require 'rutaci/formater'
|
19
|
+
|
20
|
+
module Rutaci
|
21
|
+
# This class collects the taginfo from various sources.
|
22
|
+
class FilenameSource < Source
|
23
|
+
attr_reader :info
|
24
|
+
def initialize(filename, options)
|
25
|
+
raise ArgumentError, "options.format doesn't exist" unless options.format
|
26
|
+
@filename = File.expand_path filename # we may need the full path
|
27
|
+
@wanted_fields = options.fields # if this is not nil it should contain a positive-list of fileds
|
28
|
+
@regexp, @fieldlist = self.create_regexp options.format
|
29
|
+
@info = self.get_fields
|
30
|
+
end
|
31
|
+
|
32
|
+
def get_fields
|
33
|
+
match = @regexp.match @filename
|
34
|
+
return nil unless match
|
35
|
+
groups = match.to_a
|
36
|
+
groups.shift # get rid of the first field, since it's the whole match (we need only the groups)
|
37
|
+
info = Hash.new
|
38
|
+
@fieldlist.each_with_index do |field, index|
|
39
|
+
next if @wanted_fields and not @wanted_fields.include? field
|
40
|
+
info[field] = groups[index]
|
41
|
+
end
|
42
|
+
return info
|
43
|
+
end
|
44
|
+
|
45
|
+
def create_regexp(format)
|
46
|
+
str = String.new
|
47
|
+
fieldlist = Array.new
|
48
|
+
Formater.tokenize format do |token, is_placeholder|
|
49
|
+
if is_placeholder
|
50
|
+
str += "([^#{File::SEPARATOR}]*)" # match any char appart File::SEPARATOR into a group
|
51
|
+
fieldlist.push Formater::PLACEHOLDERS[token]
|
52
|
+
else
|
53
|
+
str += Regexp.escape token
|
54
|
+
end
|
55
|
+
end # tokenize
|
56
|
+
return Regexp.new(str + "$"), fieldlist
|
57
|
+
end
|
58
|
+
end # class FilenameSource
|
59
|
+
end # module Rutaci
|
@@ -0,0 +1,81 @@
|
|
1
|
+
# rutaci is a ruby tagger with a command line interface
|
2
|
+
# Copyright (C) 2008 Jonas Bähr <jonas.baehr@fs.ei.tum.de>
|
3
|
+
#
|
4
|
+
# This program is free software; you can redistribute it and/or
|
5
|
+
# modify it under the terms of the GNU General Public License
|
6
|
+
# as published by the Free Software Foundation; either version 2
|
7
|
+
# of the License, or (at your option) any later version.
|
8
|
+
#
|
9
|
+
# This program is distributed in the hope that it will be useful,
|
10
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
11
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
12
|
+
# GNU General Public License for more details.
|
13
|
+
#
|
14
|
+
# You should have received a copy of the GNU General Public License
|
15
|
+
# along with this program; if not, write to the Free Software
|
16
|
+
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
17
|
+
|
18
|
+
module Rutaci
|
19
|
+
class Formater
|
20
|
+
# translation of format string tokens to info field names
|
21
|
+
PLACEHOLDERS = {
|
22
|
+
"t" => :title,
|
23
|
+
"i" => :artist,
|
24
|
+
"a" => :album,
|
25
|
+
"n" => :track,
|
26
|
+
"y" => :year,
|
27
|
+
"g" => :genre,
|
28
|
+
"c" => :comment
|
29
|
+
}
|
30
|
+
|
31
|
+
# performs a lexical analysis for strings like "%n - %t"
|
32
|
+
# and yields every token found. A token may be a normal char or a placeholder
|
33
|
+
# the argumetns passed to the block is the current token and if it's a placeholder or not
|
34
|
+
# Note that a placeholder token is passed without the leading '%'
|
35
|
+
# A placeholder token always ends with a lower case letter and may have some leading flags
|
36
|
+
# A flag is everything which isn't a lower case letter or the '%' sign
|
37
|
+
def self.tokenize(format)
|
38
|
+
raise ArgumentError, "need a block to handle the tokens" unless block_given?
|
39
|
+
is_placeholder = false # true if the previous char was a '%'
|
40
|
+
multichar_token = "" # here the chars for multichar tokens are acculumated before the comleate token is send out
|
41
|
+
format.each_byte do |c|
|
42
|
+
if is_placeholder
|
43
|
+
if c == ?%
|
44
|
+
yield c.chr, false # '%%' is not the placeholder '%' but a normal '%'
|
45
|
+
else
|
46
|
+
# look out for multichar tokens
|
47
|
+
multichar_token += c.chr
|
48
|
+
if /[a-z]/.match multichar_token
|
49
|
+
yield multichar_token, true
|
50
|
+
multichar_token = ""
|
51
|
+
else
|
52
|
+
next
|
53
|
+
end
|
54
|
+
end
|
55
|
+
is_placeholder = false
|
56
|
+
else
|
57
|
+
if c == ?%
|
58
|
+
is_placeholder = true
|
59
|
+
else
|
60
|
+
yield c.chr, false
|
61
|
+
end
|
62
|
+
end # is_placeholder?
|
63
|
+
end # each_byte
|
64
|
+
end # def self.tokenize
|
65
|
+
|
66
|
+
def self.string_for_token(token, info)
|
67
|
+
placeholder = token[-1].chr # the placeholder is the last letter
|
68
|
+
flags = token[0..-2] # the rest are flags
|
69
|
+
value = info[PLACEHOLDERS[placeholder]]
|
70
|
+
case value
|
71
|
+
when Fixnum, Bignum
|
72
|
+
value = sprintf "%#{flags}d", value
|
73
|
+
when String
|
74
|
+
value = sprintf "%#{flags}s", value
|
75
|
+
else # for other classes no flags are defined (yet ;-)
|
76
|
+
value = value.to_s
|
77
|
+
end
|
78
|
+
return value
|
79
|
+
end
|
80
|
+
end # class Formater
|
81
|
+
end # module Rutaci
|
@@ -0,0 +1,79 @@
|
|
1
|
+
# rutaci is a ruby tagger with a command line interface
|
2
|
+
# Copyright (C) 2008 Jonas Bähr <jonas.baehr@fs.ei.tum.de>
|
3
|
+
#
|
4
|
+
# This program is free software; you can redistribute it and/or
|
5
|
+
# modify it under the terms of the GNU General Public License
|
6
|
+
# as published by the Free Software Foundation; either version 2
|
7
|
+
# of the License, or (at your option) any later version.
|
8
|
+
#
|
9
|
+
# This program is distributed in the hope that it will be useful,
|
10
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
11
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
12
|
+
# GNU General Public License for more details.
|
13
|
+
#
|
14
|
+
# You should have received a copy of the GNU General Public License
|
15
|
+
# along with this program; if not, write to the Free Software
|
16
|
+
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
17
|
+
|
18
|
+
require 'rutaci/source'
|
19
|
+
|
20
|
+
module Rutaci
|
21
|
+
# gets info from a source and display it
|
22
|
+
# it is a command executor, so it has tp provide an initializer which takes the options
|
23
|
+
# and a method 'run' which takes an array of filenames
|
24
|
+
class Getter
|
25
|
+
def initialize(options)
|
26
|
+
@options = options
|
27
|
+
if @options.format
|
28
|
+
require 'rutaci/formater'
|
29
|
+
# make sure we get all fields nessesary for the expanding the format string
|
30
|
+
@options.fields = Array.new
|
31
|
+
Formater::PLACEHOLDERS.each_pair do |token, field|
|
32
|
+
@options.fields.push field if /%[^a-z]*#{token}/.match @options.format
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
# invoke this executor for an array of filenames
|
38
|
+
def run(files)
|
39
|
+
output = Array.new
|
40
|
+
files.each do |file|
|
41
|
+
output.push self.get(file) + "\n"
|
42
|
+
end
|
43
|
+
puts output.join("\n") # a blank line between the items
|
44
|
+
end
|
45
|
+
|
46
|
+
# get and return the actual info for a specific file
|
47
|
+
def get(file)
|
48
|
+
source = Source.factory file, @options
|
49
|
+
# use the format string if available
|
50
|
+
return self.format(source.info, @options.format) if @options.format
|
51
|
+
# esle create the standard format
|
52
|
+
output = Array.new
|
53
|
+
output.push "Filename: #{file}" unless @options.values_only
|
54
|
+
source.info.each_pair do |key, value|
|
55
|
+
value = nil if value == 0 # HACK: rtaglib seems to return non-existing numeric values as 0 instead of nil
|
56
|
+
value = value.to_s
|
57
|
+
next if value.empty? unless @options.keep_empty_fields
|
58
|
+
element = String.new
|
59
|
+
element += key.to_s.capitalize + ": " unless @options.values_only
|
60
|
+
element += value
|
61
|
+
output.push element
|
62
|
+
end
|
63
|
+
return output.join("\n")
|
64
|
+
end
|
65
|
+
|
66
|
+
# return the info formated, format may be something like "%n - %t"
|
67
|
+
def format(info, format)
|
68
|
+
str = String.new
|
69
|
+
Formater.tokenize format do |token, is_placeholder|
|
70
|
+
if is_placeholder
|
71
|
+
str += Formater.string_for_token token, info
|
72
|
+
else
|
73
|
+
str += token
|
74
|
+
end
|
75
|
+
end
|
76
|
+
return str
|
77
|
+
end
|
78
|
+
end # class Getter
|
79
|
+
end # module Rutaci
|
@@ -0,0 +1,57 @@
|
|
1
|
+
# rutaci is a ruby tagger with a command line interface
|
2
|
+
# Copyright (C) 2008 Jonas Bähr <jonas.baehr@fs.ei.tum.de>
|
3
|
+
#
|
4
|
+
# This program is free software; you can redistribute it and/or
|
5
|
+
# modify it under the terms of the GNU General Public License
|
6
|
+
# as published by the Free Software Foundation; either version 2
|
7
|
+
# of the License, or (at your option) any later version.
|
8
|
+
#
|
9
|
+
# This program is distributed in the hope that it will be useful,
|
10
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
11
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
12
|
+
# GNU General Public License for more details.
|
13
|
+
#
|
14
|
+
# You should have received a copy of the GNU General Public License
|
15
|
+
# along with this program; if not, write to the Free Software
|
16
|
+
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
17
|
+
|
18
|
+
require 'readline'
|
19
|
+
|
20
|
+
module Rutaci
|
21
|
+
# writes info from a source into the files
|
22
|
+
# it is a command executor, so it has tp provide an initializer which takes the options
|
23
|
+
# and a method 'run' which takes an array of filenames
|
24
|
+
module Interaction
|
25
|
+
def self.print_lineedit_help
|
26
|
+
puts "Use <TAB> on an empty line will insert the default value, else completion is done"
|
27
|
+
puts "Use <Ctrl-C> to discard your changes and use the default value"
|
28
|
+
end
|
29
|
+
|
30
|
+
# use GNU readline for the interactive input
|
31
|
+
def self.lineedit(promt, default_value, completion_strings = [])
|
32
|
+
# FIXME: display the default value and don't wait for the user to press TAB
|
33
|
+
# it seems that this requires hacking the readline module
|
34
|
+
add_to_history = true
|
35
|
+
Readline.completion_proc = Proc.new do |current_line|
|
36
|
+
if current_line.empty?
|
37
|
+
# if we haven't typed anything, the completion is the default value
|
38
|
+
candidates = [default_value]
|
39
|
+
else
|
40
|
+
# completion: look which completion string starts with what we've typed
|
41
|
+
# TODO: don't search for the whole current_line, but also substrings.
|
42
|
+
# example: current_file = "some foob" and completion_strings = ["foobar"]
|
43
|
+
candidates = completion_strings.select {|str| str.index(current_line) == 0}
|
44
|
+
end
|
45
|
+
candidates
|
46
|
+
end
|
47
|
+
Readline.completion_append_character = "" # per default a space will be append
|
48
|
+
begin
|
49
|
+
value = Readline.readline promt, add_to_history
|
50
|
+
rescue Interrupt
|
51
|
+
puts # just to have the linebreak
|
52
|
+
value = default_value
|
53
|
+
end
|
54
|
+
return value
|
55
|
+
end
|
56
|
+
end # module Interaction
|
57
|
+
end # module Rutaci
|
@@ -0,0 +1,63 @@
|
|
1
|
+
# rutaci is a ruby tagger with a command line interface
|
2
|
+
# Copyright (C) 2008 Jonas Bähr <jonas.baehr@fs.ei.tum.de>
|
3
|
+
#
|
4
|
+
# This program is free software; you can redistribute it and/or
|
5
|
+
# modify it under the terms of the GNU General Public License
|
6
|
+
# as published by the Free Software Foundation; either version 2
|
7
|
+
# of the License, or (at your option) any later version.
|
8
|
+
#
|
9
|
+
# This program is distributed in the hope that it will be useful,
|
10
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
11
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
12
|
+
# GNU General Public License for more details.
|
13
|
+
#
|
14
|
+
# You should have received a copy of the GNU General Public License
|
15
|
+
# along with this program; if not, write to the Free Software
|
16
|
+
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
17
|
+
|
18
|
+
require 'ftools'
|
19
|
+
|
20
|
+
require 'rutaci/getter'
|
21
|
+
|
22
|
+
module Rutaci
|
23
|
+
# renames files using a format string, uses the Getter
|
24
|
+
# it is a command executor, so it has tp provide an initializer which takes the options
|
25
|
+
# and a method 'run' which takes an array of filenames
|
26
|
+
class Renamer
|
27
|
+
def initialize(options)
|
28
|
+
@options = options
|
29
|
+
@getter = Getter.new options
|
30
|
+
if @options.interactive
|
31
|
+
require 'rutaci/interaction'
|
32
|
+
puts "in interactive mode you can edit the new filename before renaming"
|
33
|
+
Interaction::print_lineedit_help
|
34
|
+
puts
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
# invoke this executor for an array of filenames
|
39
|
+
def run(files)
|
40
|
+
files.each do |file|
|
41
|
+
self.rename(file)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
# get and return the actual info for a specific file
|
46
|
+
def rename(file)
|
47
|
+
puts "current file: \"#{file}\""
|
48
|
+
new_name = File.join @options.destination, @getter.get(file).strip
|
49
|
+
new_name = Interaction::lineedit " new name: ", new_name if @options.interactive
|
50
|
+
if new_name.empty?
|
51
|
+
puts " can't rename to an empty filename!"
|
52
|
+
return
|
53
|
+
end
|
54
|
+
if @options.dry_run
|
55
|
+
puts " renaming to: \"#{new_name}\" (if we where not doing a dry-run)"
|
56
|
+
else
|
57
|
+
puts " renaming to: \"#{new_name}\""
|
58
|
+
File.makedirs File.dirname(new_name) # create missing directories
|
59
|
+
File.move file, new_name
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end # class Renamer
|
63
|
+
end # module Rutaci
|
@@ -0,0 +1,71 @@
|
|
1
|
+
# rutaci is a ruby tagger with a command line interface
|
2
|
+
# Copyright (C) 2008 Jonas Bähr <jonas.baehr@fs.ei.tum.de>
|
3
|
+
#
|
4
|
+
# This program is free software; you can redistribute it and/or
|
5
|
+
# modify it under the terms of the GNU General Public License
|
6
|
+
# as published by the Free Software Foundation; either version 2
|
7
|
+
# of the License, or (at your option) any later version.
|
8
|
+
#
|
9
|
+
# This program is distributed in the hope that it will be useful,
|
10
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
11
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
12
|
+
# GNU General Public License for more details.
|
13
|
+
#
|
14
|
+
# You should have received a copy of the GNU General Public License
|
15
|
+
# along with this program; if not, write to the Free Software
|
16
|
+
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
17
|
+
|
18
|
+
require 'rtaglib'
|
19
|
+
|
20
|
+
require 'rutaci/source'
|
21
|
+
|
22
|
+
module Rutaci
|
23
|
+
# writes info from a source into the files
|
24
|
+
# it is a command executor, so it has tp provide an initializer which takes the options
|
25
|
+
# and a method 'run' which takes an array of filenames
|
26
|
+
class Setter
|
27
|
+
def initialize(options)
|
28
|
+
@options = options
|
29
|
+
if @options.interactive
|
30
|
+
require 'rutaci/interaction'
|
31
|
+
puts "Using interactive mode you can specify the value for the current field."
|
32
|
+
Interaction::print_lineedit_help
|
33
|
+
puts
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
# invoke this executor for an array of filenames
|
38
|
+
def run(files)
|
39
|
+
files.each do |file|
|
40
|
+
if @options.dry_run
|
41
|
+
puts "dry run for #{file}:"
|
42
|
+
else
|
43
|
+
puts "processing #{file}:"
|
44
|
+
end
|
45
|
+
self.set(file)
|
46
|
+
puts unless file == files.last # seperator between the files
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
# gets writes the actual info into a specific file
|
51
|
+
def set(file)
|
52
|
+
source = Source.factory file, @options
|
53
|
+
tagfile = TagFile::File.new file
|
54
|
+
source.info.each_pair do |key, value|
|
55
|
+
value = Interaction::lineedit " #{key.to_s.capitalize}: ", value if @options.interactive
|
56
|
+
if value.empty? and not @options.keep_empty_fields
|
57
|
+
puts " ignoring empty value"
|
58
|
+
next
|
59
|
+
end
|
60
|
+
puts " setting #{key} to \"#{value}\""
|
61
|
+
tagfile.send key.to_s+"=", value unless @options.dry_run
|
62
|
+
end
|
63
|
+
if @options.dry_run
|
64
|
+
puts " writing the metadata (if it was not a dry run ;-)"
|
65
|
+
else
|
66
|
+
puts " writing the metadata"
|
67
|
+
tagfile.save
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end # class Getter
|
71
|
+
end # module Rutaci
|