rutaci 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.
- 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
|