filter_rename 1.0.0 → 1.2.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.
- checksums.yaml +4 -4
- data/.github/FUNDING.yml +16 -0
- data/.github/workflows/gem-push.yml +46 -0
- data/.github/workflows/main.yml +32 -0
- data/.rubocop.yml +84 -0
- data/Gemfile +15 -1
- data/README.md +83 -39
- data/Rakefile +7 -1
- data/exe/filter_rename +1 -1
- data/filter_rename.gemspec +23 -25
- data/lib/filter_rename/cli.rb +121 -61
- data/lib/filter_rename/config.rb +219 -46
- data/lib/filter_rename/filename.rb +59 -35
- data/lib/filter_rename/filename_factory.rb +22 -17
- data/lib/filter_rename/filetype/audio_filename.rb +86 -0
- data/lib/filter_rename/filetype/image_filename.rb +18 -12
- data/lib/filter_rename/filetype/mp3_filename.rb +23 -21
- data/lib/filter_rename/filetype/pdf_filename.rb +19 -12
- data/lib/filter_rename/filter_base.rb +171 -70
- data/lib/filter_rename/filter_pipe.rb +20 -15
- data/lib/filter_rename/filters.rb +737 -269
- data/lib/filter_rename/utils.rb +306 -105
- data/lib/filter_rename/version.rb +3 -1
- data/lib/filter_rename.rb +100 -34
- data/lib/filter_rename.yaml +27 -13
- metadata +34 -44
data/lib/filter_rename/cli.rb
CHANGED
|
@@ -1,82 +1,145 @@
|
|
|
1
|
-
|
|
2
|
-
require 'ostruct'
|
|
3
|
-
require 'filter_rename'
|
|
4
|
-
require 'filter_rename/version'
|
|
1
|
+
# frozen_string_literal: true
|
|
5
2
|
|
|
6
|
-
|
|
3
|
+
require "optparse"
|
|
4
|
+
require "ostruct"
|
|
5
|
+
require "filter_rename"
|
|
6
|
+
require "filter_rename/version"
|
|
7
7
|
|
|
8
|
+
module FilterRename
|
|
9
|
+
#
|
|
10
|
+
# Parsing the input data.
|
|
11
|
+
#
|
|
8
12
|
class OptParseMain
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
options = OpenStruct.new
|
|
13
|
+
def self.parse(_args)
|
|
14
|
+
options = Struct.new(:filters, :files, :global, :operation, :show_macro, :help_config).new
|
|
12
15
|
options.filters = []
|
|
13
16
|
options.files = []
|
|
14
17
|
options.global = {}
|
|
15
|
-
options.operation = :
|
|
16
|
-
options.show_macro =
|
|
18
|
+
options.operation = :diff
|
|
19
|
+
options.show_macro = ""
|
|
20
|
+
options.help_config = ""
|
|
17
21
|
|
|
18
22
|
opt_parser = OptionParser.new do |opt|
|
|
19
|
-
opt.banner =
|
|
23
|
+
opt.banner = "Usage: filter_rename [-g OPTION1[,OPTION2...]] [FILTER1[,FILTER2...]] <file1>" \
|
|
24
|
+
"[ <file2>...] OPERATION"
|
|
20
25
|
|
|
21
|
-
opt.separator
|
|
22
|
-
opt.separator
|
|
26
|
+
opt.separator ""
|
|
27
|
+
opt.separator "Operations:"
|
|
23
28
|
|
|
24
|
-
opt.on(
|
|
29
|
+
opt.on("--apply", "Apply the changes.") do |_c|
|
|
25
30
|
options.operation = :apply
|
|
26
31
|
end
|
|
27
32
|
|
|
28
|
-
opt.on(
|
|
33
|
+
opt.on("-d", "--dry-run", "Don't apply any change but check for errors") do |_c|
|
|
29
34
|
options.operation = :dry_run
|
|
30
35
|
end
|
|
31
36
|
|
|
32
|
-
opt.on(
|
|
33
|
-
options.operation = :
|
|
37
|
+
opt.on("-D", "--diff", "View the changes in a diff format [DEFAULT]") do |_c|
|
|
38
|
+
options.operation = :diff
|
|
34
39
|
end
|
|
35
40
|
|
|
36
|
-
opt.on(
|
|
41
|
+
opt.on("-t", "--targets", "List of targets for each file") do |_c|
|
|
37
42
|
options.operation = :targets
|
|
38
43
|
end
|
|
39
44
|
|
|
40
|
-
opt.on(
|
|
45
|
+
opt.on("-v", "--values", "List of targets values for each file") do |_c|
|
|
46
|
+
options.operation = :values
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
opt.on("-g", "--globals", "List of global variables") do |_c|
|
|
41
50
|
options.operation = :globals
|
|
42
51
|
end
|
|
43
52
|
|
|
44
|
-
opt.on(
|
|
53
|
+
opt.on("-c", "--configs", "List of filter variables") do |_c|
|
|
45
54
|
options.operation = :configs
|
|
46
55
|
end
|
|
47
56
|
|
|
48
|
-
opt.on(
|
|
57
|
+
opt.on("-w", "--words", "List of groups of words available for translation") do |_c|
|
|
49
58
|
options.operation = :words
|
|
50
59
|
end
|
|
51
60
|
|
|
52
|
-
opt.on(
|
|
61
|
+
opt.on("-m", "--macros", "List of available macros") do |_c|
|
|
53
62
|
options.operation = :macros
|
|
54
63
|
end
|
|
55
64
|
|
|
56
|
-
opt.on(
|
|
65
|
+
opt.on("-s", "--show-macro <MACRO>", "List of commands used by MACRO") do |c|
|
|
57
66
|
options.operation = :show_macro
|
|
58
67
|
options.show_macro = c
|
|
59
68
|
end
|
|
60
69
|
|
|
61
|
-
opt.separator
|
|
62
|
-
opt.separator
|
|
70
|
+
opt.separator ""
|
|
71
|
+
opt.separator "Global options:"
|
|
72
|
+
opt.separator ""
|
|
63
73
|
|
|
64
|
-
opt.on(
|
|
65
|
-
|
|
66
|
-
|
|
74
|
+
opt.on("-G", "--global OPTION:VALUE[,OPTION:VALUE]", String,
|
|
75
|
+
'Override global options with: "option:value"') do |v|
|
|
76
|
+
v.parametrize.each do |idx|
|
|
77
|
+
options.global.store(idx.split(":")[0].to_sym, idx.split(":")[1])
|
|
67
78
|
end
|
|
68
79
|
end
|
|
69
80
|
|
|
70
|
-
opt.separator
|
|
71
|
-
opt.separator 'Filters:'
|
|
81
|
+
opt.separator ""
|
|
72
82
|
|
|
73
|
-
opt.on(
|
|
74
|
-
options.
|
|
75
|
-
|
|
83
|
+
opt.on("-V", "--virtual", "Accept a list of file not present in the local drives") do |_c|
|
|
84
|
+
options.global.store(:mimemagic, false)
|
|
85
|
+
options.global.store(:essential_tags, true)
|
|
86
|
+
options.global.store(:check_source, false)
|
|
76
87
|
end
|
|
77
88
|
|
|
78
|
-
|
|
89
|
+
opt.separator ""
|
|
90
|
+
|
|
91
|
+
GlobalConfig::OPTIONS.each do |k, v|
|
|
92
|
+
if v[:args].nil?
|
|
93
|
+
opt.on("--[no-]g-#{k.to_s.gsub("_", "-")}", v[:desc]) do |c|
|
|
94
|
+
options.global.store(k, c)
|
|
95
|
+
end
|
|
96
|
+
else
|
|
97
|
+
opt.on("--g-#{k.to_s.gsub("_", "-")} #{v[:args]}", String, v[:desc]) do |c|
|
|
98
|
+
options.global.store(k, c)
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
opt.separator ""
|
|
104
|
+
opt.separator "Filter options:"
|
|
105
|
+
opt.separator ""
|
|
106
|
+
|
|
107
|
+
opt.on("-X", "--exclude-files PATTERN", String, "Exclude the files matching the PATTERN") do |c|
|
|
108
|
+
klass = Object.const_get("FilterRename::Filters::Config")
|
|
109
|
+
options.filters << { klass => ["grep:#{c}"] }
|
|
110
|
+
options.filters << { klass => ["grep_exclude:true"] }
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
opt.on("-O", "--only-files PATTERN", String, "Select only the files matching the PATTERN") do |c|
|
|
114
|
+
klass = Object.const_get("FilterRename::Filters::Config")
|
|
115
|
+
options.filters << { klass => ["grep:#{c}"] }
|
|
116
|
+
options.filters << { klass => ["grep_exclude:false"] }
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
opt.on("-M", "--macro MACRO1,[MACRO2,...]", Array, "Apply the MACRO") do |v|
|
|
120
|
+
options.filters << MacroConfig.create(v)
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
opt.separator ""
|
|
79
124
|
|
|
125
|
+
FilterConfig::OPTIONS.each do |k, v|
|
|
126
|
+
klass = Object.const_get("FilterRename::Filters::Config")
|
|
127
|
+
|
|
128
|
+
if v[:args].nil?
|
|
129
|
+
opt.on("--[no-]f-#{k.to_s.gsub("_", "-")}", v[:desc]) do |c|
|
|
130
|
+
options.filters << { klass => ["#{k}:#{c}"] }
|
|
131
|
+
end
|
|
132
|
+
else
|
|
133
|
+
opt.on("--f-#{k.to_s.gsub("_", "-")} #{v[:args]}", String, v[:desc]) do |c|
|
|
134
|
+
options.filters << { klass => ["#{k}:#{c}"] }
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
opt.separator ""
|
|
140
|
+
opt.separator "Filters:"
|
|
141
|
+
|
|
142
|
+
Filters.constants.sort.each do |c|
|
|
80
143
|
switch = c.to_s.to_switch
|
|
81
144
|
klass = Object.const_get("FilterRename::Filters::#{c}")
|
|
82
145
|
|
|
@@ -89,57 +152,54 @@ module FilterRename
|
|
|
89
152
|
options.filters << { klass => v }
|
|
90
153
|
end
|
|
91
154
|
else
|
|
92
|
-
opt.on("--#{switch} #{klass.params}",
|
|
155
|
+
opt.on("--#{switch} #{klass.params}", klass.hint.to_s) do |v|
|
|
93
156
|
options.filters << { klass => v.parametrize }
|
|
94
157
|
end
|
|
95
158
|
end
|
|
96
159
|
end
|
|
97
160
|
|
|
98
|
-
opt.separator
|
|
99
|
-
opt.separator
|
|
161
|
+
opt.separator ""
|
|
162
|
+
opt.separator "Other:"
|
|
100
163
|
|
|
101
|
-
opt.on_tail(
|
|
164
|
+
opt.on_tail("-h", "--help", "Show this message") do
|
|
102
165
|
puts opt
|
|
103
166
|
exit
|
|
104
167
|
end
|
|
105
|
-
|
|
168
|
+
|
|
169
|
+
opt.on_tail("-H", "--help-config [G-OPTION|F-OPTION]", String,
|
|
170
|
+
"Extended description for the available options") do |o|
|
|
171
|
+
options.operation = :help_config
|
|
172
|
+
options.help_config = o
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
opt.on_tail("-v", "--version", "Show version") do
|
|
106
176
|
puts VERSION
|
|
107
177
|
exit
|
|
108
178
|
end
|
|
109
179
|
end
|
|
110
180
|
|
|
111
|
-
if (
|
|
112
|
-
|
|
181
|
+
if (!$stdin.tty? && !$stdin.closed?) || !ARGV.empty?
|
|
113
182
|
opt_parser.parse!(ARGV)
|
|
114
|
-
|
|
115
|
-
(ARGV.empty? ? ARGF : ARGV).each do |f|
|
|
116
|
-
f = File.expand_path(f.strip)
|
|
117
|
-
|
|
118
|
-
if File.exists?(f)
|
|
119
|
-
options.files << f
|
|
120
|
-
else
|
|
121
|
-
raise FileNotFound, f
|
|
122
|
-
end
|
|
123
|
-
end if [:apply, :preview, :dry_run, :targets].include? options.operation
|
|
183
|
+
options.files = (ARGV.empty? ? ARGF : ARGV)
|
|
124
184
|
else
|
|
125
|
-
puts opt_parser
|
|
185
|
+
puts opt_parser
|
|
186
|
+
exit
|
|
126
187
|
end
|
|
127
188
|
|
|
128
189
|
options
|
|
129
190
|
end
|
|
130
|
-
|
|
131
191
|
end
|
|
132
192
|
|
|
193
|
+
#
|
|
194
|
+
# Interface class to run the application.
|
|
195
|
+
#
|
|
133
196
|
class CLI
|
|
134
197
|
def self.start
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
exit 1
|
|
141
|
-
end
|
|
198
|
+
options = OptParseMain.parse(ARGV)
|
|
199
|
+
Builder.new(options).send(options.operation)
|
|
200
|
+
rescue StandardError => e
|
|
201
|
+
Messages.error e
|
|
202
|
+
exit 1
|
|
142
203
|
end
|
|
143
204
|
end
|
|
144
|
-
|
|
145
205
|
end
|
data/lib/filter_rename/config.rb
CHANGED
|
@@ -1,100 +1,276 @@
|
|
|
1
|
-
|
|
1
|
+
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
require "yaml"
|
|
4
4
|
|
|
5
|
+
module FilterRename
|
|
6
|
+
#
|
|
7
|
+
# Macro configurations.
|
|
8
|
+
#
|
|
5
9
|
class MacroConfig
|
|
6
|
-
|
|
7
10
|
def initialize(cfg)
|
|
8
11
|
cfg.each do |key, value|
|
|
9
|
-
instance_variable_set(
|
|
12
|
+
instance_variable_set("@#{key}", value)
|
|
10
13
|
end
|
|
11
14
|
end
|
|
12
15
|
|
|
13
16
|
def get_macro(name)
|
|
14
|
-
macro = instance_variable_get(
|
|
17
|
+
macro = instance_variable_get("@#{name.to_s.gsub(/[^a-zA-Z0-9,\-_]/, "")}")
|
|
15
18
|
raise InvalidMacro, name if macro.nil? || macro.to_s.empty?
|
|
19
|
+
|
|
16
20
|
macro
|
|
17
21
|
end
|
|
18
22
|
|
|
19
|
-
def
|
|
20
|
-
instance_variables.map { |
|
|
23
|
+
def macros
|
|
24
|
+
instance_variables.map { |msg| msg.to_s.gsub(/^@/, "") }
|
|
21
25
|
end
|
|
22
26
|
|
|
23
27
|
def self.create(name)
|
|
24
28
|
{ FilterRename::MacroConfig => name }
|
|
25
29
|
end
|
|
26
|
-
|
|
27
30
|
end
|
|
28
31
|
|
|
29
|
-
|
|
32
|
+
#
|
|
33
|
+
# Word filters configurations.
|
|
34
|
+
#
|
|
30
35
|
class WordsConfig
|
|
31
|
-
|
|
32
36
|
def initialize(cfg)
|
|
33
37
|
cfg.each do |key, value|
|
|
34
|
-
instance_variable_set(
|
|
38
|
+
instance_variable_set("@#{key}", value)
|
|
35
39
|
end
|
|
36
40
|
end
|
|
37
41
|
|
|
38
42
|
def get_words(name, section, idx = nil)
|
|
39
|
-
w = instance_variable_get(
|
|
43
|
+
w = instance_variable_get("@#{name}")
|
|
40
44
|
raise InvalidWordsGroup, name if w.nil? || name.to_s.empty?
|
|
41
|
-
raise InvalidWordsSection.new(name, section) unless w.
|
|
45
|
+
raise InvalidWordsSection.new(name, section) unless w.key? section.to_sym
|
|
42
46
|
|
|
43
47
|
if idx.nil?
|
|
44
|
-
|
|
45
|
-
elsif w[section].
|
|
48
|
+
w[section]
|
|
49
|
+
elsif w[section].instance_of?(Array)
|
|
46
50
|
raise InvalidWordsIndex.new(name, section, idx) unless idx < w[section].length
|
|
47
|
-
|
|
51
|
+
|
|
52
|
+
w[section][idx].to_s
|
|
48
53
|
else
|
|
49
|
-
|
|
54
|
+
w[section].to_s
|
|
50
55
|
end
|
|
51
56
|
end
|
|
52
57
|
end
|
|
53
58
|
|
|
54
|
-
|
|
59
|
+
#
|
|
60
|
+
# Global configurations.
|
|
61
|
+
#
|
|
55
62
|
class GlobalConfig
|
|
56
|
-
|
|
57
|
-
|
|
63
|
+
OPTIONS = { date_format: { args: "FORMAT",
|
|
64
|
+
desc: "Describes the date FORMAT for <mtime> and <ctime> targets.",
|
|
65
|
+
long: "This is how the <mtime> and <ctime> targets will be formatted\n" \
|
|
66
|
+
"for each file read, unless the *essential_tags* option is on.\n" \
|
|
67
|
+
"The allowed placeholders are prefixed with a percentage % char\n" \
|
|
68
|
+
"and are the same used by the *date* command.\n" \
|
|
69
|
+
"Use the *date* man page to get more info.",
|
|
70
|
+
default: "%Y-%m-%d" },
|
|
71
|
+
hash_type: { args: "ALGORITHM",
|
|
72
|
+
desc: "Use one of the ALGORITHM among sha1, sha2, md5 to create the <hash> target.",
|
|
73
|
+
long: "It preloads the <hash> target with one of the allowed value\n" \
|
|
74
|
+
"sha1, sha2, or md5.\n" \
|
|
75
|
+
"Just as reminder, md5 is the less safe but the quicker to be\n" \
|
|
76
|
+
"executed, sha2 is the slowest but the safest.",
|
|
77
|
+
default: "md5" },
|
|
78
|
+
hash_on_tags: { args: nil,
|
|
79
|
+
desc: "Enable or not the hash calculation for each file.",
|
|
80
|
+
long: "Hash calculation can be expensive, that's why it is disabled\n" \
|
|
81
|
+
"by default and the <hash> target is not created.",
|
|
82
|
+
default: false },
|
|
83
|
+
hash_if_exists: { args: nil,
|
|
84
|
+
desc: "Prints the hash code in case the file exists.",
|
|
85
|
+
long: "This option shows the hash of the two files when the *dry* or\n" \
|
|
86
|
+
"*apply* operation is running and a conflict is raised.\n" \
|
|
87
|
+
"Looking at the hash code would be easier to understand wheter or\n" \
|
|
88
|
+
"not the renaming failure is caused by two clones or two totally\n" \
|
|
89
|
+
"different files that the chain of filters accidentally transformed\n" \
|
|
90
|
+
"in the same filename.",
|
|
91
|
+
default: true },
|
|
92
|
+
counter_length: { args: "NUMBER",
|
|
93
|
+
desc: "The minimum length of the counter padded by zeros.",
|
|
94
|
+
long: "This is the length of the counter in terms of total NUMBER of\n" \
|
|
95
|
+
"chars rendered.\n" \
|
|
96
|
+
"It means that, when the counter length is less than NUMBER, a certain\n" \
|
|
97
|
+
"number of '0' is prepended to the counter to cover the difference.",
|
|
98
|
+
default: "3" },
|
|
99
|
+
counter_start: { args: "NUMBER",
|
|
100
|
+
desc: "Start the <count> target from NUMBER+1.",
|
|
101
|
+
long: "This is the way to set the internal counter, that affect the <count>\n" \
|
|
102
|
+
"target, to a specific position.\n" \
|
|
103
|
+
"Be aware that, for technical reasons, it will start from NUMBER+1,\n" \
|
|
104
|
+
"so to have 0 for the first element just set NUMBER to -1",
|
|
105
|
+
default: "0" },
|
|
106
|
+
inline_targets: { args: nil,
|
|
107
|
+
desc: "Print the targets' list inline or in a column.",
|
|
108
|
+
long: "This option affects the way the target's list for each file is shown:\n" \
|
|
109
|
+
"a *short* list where the data is in the same line, one for RW targets\n" \
|
|
110
|
+
"and one for RO targets, or a *long* list where the data is in column.",
|
|
111
|
+
default: true },
|
|
112
|
+
pdf_metadata: { args: nil,
|
|
113
|
+
desc: "Create the targets from the PDF files metadata.",
|
|
114
|
+
long: "This option enable the generation of targets from the metadata\n" \
|
|
115
|
+
"embedded in the PDF files.",
|
|
116
|
+
default: true },
|
|
117
|
+
image_metadata: { args: nil,
|
|
118
|
+
desc: "Create the targets from the image files metadata.",
|
|
119
|
+
long: "This option enable the generation of targets from the metadata\n" \
|
|
120
|
+
"embedded in the supported image files.",
|
|
121
|
+
default: true },
|
|
122
|
+
audio_metadata: { args: nil,
|
|
123
|
+
desc: "Create the targets from the audio files metadata.",
|
|
124
|
+
long: "This option enable the generation of targets from the metadata\n" \
|
|
125
|
+
"embedded in the supported audio files.",
|
|
126
|
+
default: true },
|
|
127
|
+
mimemagic: { args: nil,
|
|
128
|
+
desc: "Create the extra targets depending from the file's mimetype.",
|
|
129
|
+
long: "Disabling it, disable all the extra targets' calculation\n" \
|
|
130
|
+
"based on the file's mimetype.",
|
|
131
|
+
default: true },
|
|
132
|
+
essential_tags: { args: nil,
|
|
133
|
+
desc: "Define whether or not to enable only essential targets.",
|
|
134
|
+
long: "Setting it to true the basic targets only which involve\n" \
|
|
135
|
+
"the file name, path, and extension, will be calculated.\n" \
|
|
136
|
+
"This is useful to use less memory or to skip\n" \
|
|
137
|
+
"those data that can't be calculated in case of a file list\n" \
|
|
138
|
+
"is served by the pipeline.",
|
|
139
|
+
default: false },
|
|
140
|
+
check_source: { args: nil,
|
|
141
|
+
desc: "Define whether or not to enable the source files checking.",
|
|
142
|
+
long: "Setting it to false the source files aren't checked out\n" \
|
|
143
|
+
"and a list of files not necessarily in the same machine\n" \
|
|
144
|
+
"could be provided using the pipeline as an input.\n" \
|
|
145
|
+
"Combined with essential_tags to true and mimemagic to false\n" \
|
|
146
|
+
"is useful to avoid errors in that last scenario.",
|
|
147
|
+
default: true } }.freeze
|
|
148
|
+
|
|
149
|
+
attr_reader(*OPTIONS.keys)
|
|
58
150
|
|
|
151
|
+
# rubocop:disable Style/RedundantCondition
|
|
59
152
|
def initialize(cfg)
|
|
60
|
-
@date_format = cfg[:date_format] ||
|
|
61
|
-
@hash_type =
|
|
62
|
-
@
|
|
153
|
+
@date_format = cfg[:date_format] || "%Y-%m-%d"
|
|
154
|
+
@hash_type = cfg[:hash_type].nil? ? :md5 : cfg[:hash_type].to_sym
|
|
155
|
+
@hash_on_tags = cfg[:hash_on_tags] || false
|
|
156
|
+
@hash_if_exists = cfg[:hash_if_exists] || true
|
|
157
|
+
@counter_length = cfg[:counter_length] || 3
|
|
63
158
|
@counter_start = cfg[:counter_start] || 0
|
|
64
|
-
@
|
|
159
|
+
@inline_targets = cfg[:inline_targets].nil? ? true : cfg[:inline_targets].to_boolean
|
|
65
160
|
@pdf_metadata = cfg[:pdf_metadata].nil? ? true : cfg[:pdf_metadata].to_boolean
|
|
66
161
|
@image_metadata = cfg[:image_metadata].nil? ? true : cfg[:image_metadata].to_boolean
|
|
67
|
-
@
|
|
162
|
+
@audio_metadata = cfg[:audio_metadata].nil? ? true : cfg[:audio_metadata].to_boolean
|
|
163
|
+
@mimemagic = cfg[:mimemagic].nil? ? true : cfg[:mimemagic].to_boolean
|
|
164
|
+
@essential_tags = cfg[:essential_tags].nil? ? false : cfg[:essential_tags].to_boolean
|
|
165
|
+
@check_source = cfg[:check_source].nil? ? true : cfg[:check_source].to_boolean
|
|
68
166
|
end
|
|
167
|
+
# rubocop:enable Style/RedundantCondition
|
|
69
168
|
end
|
|
70
169
|
|
|
71
|
-
|
|
170
|
+
#
|
|
171
|
+
# Filter configurations.
|
|
172
|
+
#
|
|
72
173
|
class FilterConfig
|
|
73
|
-
|
|
174
|
+
OPTIONS = { word_separator: { args: "CHARACTER",
|
|
175
|
+
desc: "Define the CHARACTER that separates two words.",
|
|
176
|
+
long: "This options affects the way a string is split in words or how the\n" \
|
|
177
|
+
"words are joined together.\n" \
|
|
178
|
+
"All the filters that operate with words will use this CHARACTER\n" \
|
|
179
|
+
"to define the sequence of the word itself which can contain all\n" \
|
|
180
|
+
"characters except CHARACTER.",
|
|
181
|
+
default: " " },
|
|
182
|
+
number_separator: { args: "CHARACTER",
|
|
183
|
+
desc: "Define the CHARACTER to join two or more numbers.",
|
|
184
|
+
long: "This option configure the CHARACTER that will join two or more\n" \
|
|
185
|
+
"numbers in the swap-number filter when an interval is used as\n" \
|
|
186
|
+
"first parameter.",
|
|
187
|
+
default: "." },
|
|
188
|
+
# This one is unused
|
|
189
|
+
# occurrence_separator: { args: "CHARACTER"
|
|
190
|
+
# default: "-" },
|
|
191
|
+
target: { args: "TARGET",
|
|
192
|
+
desc: "This is the TARGET name where the filters are operating.",
|
|
193
|
+
long: "Switching to one of the available targets allows to alter their content\n" \
|
|
194
|
+
"directly using a chain of filters.\n" \
|
|
195
|
+
"Targets can be read-write or read-only: the former type reflects the data\n" \
|
|
196
|
+
"that will be write back to the file, the latter can be used to generate\n" \
|
|
197
|
+
"other values or be emedded in other targets using the *--template* filter.\n" \
|
|
198
|
+
"Also custom targets can be defined just to hold data that could be manipulated\n" \
|
|
199
|
+
"easier.\n" \
|
|
200
|
+
"This option is the same as using the *--select* filter",
|
|
201
|
+
default: "name" },
|
|
202
|
+
ignore_case: { args: nil,
|
|
203
|
+
desc: "Ignore or not the character case when searching for strings.",
|
|
204
|
+
long: "This option allows to ignore the case during the search and replace\n" \
|
|
205
|
+
"operations, doesn't matter which type of filter is involved.",
|
|
206
|
+
default: true },
|
|
207
|
+
lang: { args: "LANG",
|
|
208
|
+
desc: "Set the default LANG code for the *--replace-date* filter.",
|
|
209
|
+
long: "This option is used by the *--replace-date* filter to translate groups\n" \
|
|
210
|
+
"of words containing the months or day of weeks from a language to another\n" \
|
|
211
|
+
"where it's not specified.",
|
|
212
|
+
default: "en" },
|
|
213
|
+
grep: { args: "REGEXP",
|
|
214
|
+
desc: "Limit the file to be changed to only those matching the REGEXP pattern.",
|
|
215
|
+
long: "This options by default includes from the following filters only those files\n" \
|
|
216
|
+
"matching the REGEXP pattern.\n" \
|
|
217
|
+
"Depending on the *grep_exclude* option the logic can be inverted and the files\n" \
|
|
218
|
+
"matching the REGEXP excluded from the action of the following filters.",
|
|
219
|
+
default: ".*" },
|
|
220
|
+
grep_on_dest: { args: nil,
|
|
221
|
+
desc: "Execute the *grep* option on the destination filename.",
|
|
222
|
+
long: "To limit the files to be involved in the changes you can apply the\n" \
|
|
223
|
+
"grep option on the source or destination filename.\n" \
|
|
224
|
+
"By default it is executed on the source, while this option allows\n" \
|
|
225
|
+
"to select the destination.\n" \
|
|
226
|
+
"Be aware that the destination changes according to all the previous\n" \
|
|
227
|
+
"filters applied.",
|
|
228
|
+
default: false },
|
|
229
|
+
grep_exclude: { args: nil,
|
|
230
|
+
desc: "Invert the logic to exclude the files matching the regular expression.",
|
|
231
|
+
long: "With this option the *grep* options will be used to exclude the files\n" \
|
|
232
|
+
"matching the regular expression, from the following chain of filters.",
|
|
233
|
+
default: false },
|
|
234
|
+
grep_target: { args: "TARGET",
|
|
235
|
+
desc: "Use the specific TARGET to match files through the *grep* option.",
|
|
236
|
+
long: "With this option you can limit the *grep* option to one of the available\n" \
|
|
237
|
+
"TARGETs instead of considering the full filename.\n" \
|
|
238
|
+
"It comes in handy to focus on specific data contained within a TARGET\n" \
|
|
239
|
+
"and the file selection process is exclusively based on that data.",
|
|
240
|
+
default: "full_filename" } }.freeze
|
|
241
|
+
|
|
242
|
+
attr_accessor(*OPTIONS.keys)
|
|
74
243
|
|
|
244
|
+
# rubocop:disable Style/RedundantCondition
|
|
75
245
|
def initialize(cfg)
|
|
76
|
-
@word_separator = cfg[:word_separator] ||
|
|
77
|
-
@
|
|
246
|
+
@word_separator = cfg[:word_separator] || " "
|
|
247
|
+
@number_separator = cfg[:number_separator] || "."
|
|
248
|
+
# Unused property
|
|
249
|
+
@occurrence_separator = cfg[:occurrence_separator] || "-"
|
|
250
|
+
@target = cfg[:target].nil? ? :name : cfg[:target].to_sym
|
|
78
251
|
@ignore_case = cfg[:ignore_case].nil? ? true : cfg[:ignore_case].to_boolean
|
|
79
252
|
@lang = (cfg[:lang] || :en).to_sym
|
|
80
253
|
@macro = cfg[:macro] || {}
|
|
81
|
-
@grep = cfg[:grep] ||
|
|
82
|
-
@
|
|
83
|
-
@grep_exclude = cfg[:grep_exclude].
|
|
84
|
-
@grep_target = cfg[:grep_target]
|
|
254
|
+
@grep = cfg[:grep] || ".*"
|
|
255
|
+
@grep_on_dest = cfg[:grep_on_dest].nil? ? false : cfg[:grep_on_dest].to_boolean
|
|
256
|
+
@grep_exclude = cfg[:grep_exclude].nil? ? false : cfg[:grep_exclude].to_boolean
|
|
257
|
+
@grep_target = (cfg[:grep_target] || :full_filename).to_sym
|
|
85
258
|
end
|
|
259
|
+
# rubocop:enable Style/RedundantCondition
|
|
86
260
|
end
|
|
87
261
|
|
|
88
|
-
|
|
262
|
+
#
|
|
263
|
+
# Proxy class for all configurations.
|
|
264
|
+
#
|
|
89
265
|
class Config
|
|
90
266
|
attr_reader :filter, :global, :macro, :words
|
|
91
267
|
|
|
92
268
|
def initialize(global = {})
|
|
93
|
-
cfg = {filter: {}, global: {}, macro: {}, words: {}}
|
|
269
|
+
cfg = { filter: {}, global: {}, macro: {}, words: {} }
|
|
94
270
|
|
|
95
|
-
load_file(File.expand_path(File.join(File.dirname(__FILE__),
|
|
96
|
-
load_file(File.join(
|
|
97
|
-
load_file(File.join(
|
|
271
|
+
load_file(File.expand_path(File.join(File.dirname(__FILE__), "..", "filter_rename.yaml")), cfg)
|
|
272
|
+
load_file(File.join(Dir.home, ".filter_rename.yaml"), cfg)
|
|
273
|
+
load_file(File.join(Dir.home, ".filter_rename", "config.yaml"), cfg)
|
|
98
274
|
|
|
99
275
|
@filter = FilterConfig.new(cfg[:filter])
|
|
100
276
|
@global = GlobalConfig.new(cfg[:global].merge(global))
|
|
@@ -105,16 +281,13 @@ module FilterRename
|
|
|
105
281
|
private
|
|
106
282
|
|
|
107
283
|
def load_file(filename, cfg = nil)
|
|
284
|
+
return unless File.exist?(filename)
|
|
108
285
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
[
|
|
113
|
-
cfg[s].merge!(yaml[s]) if yaml.has_key?(s)
|
|
114
|
-
end
|
|
286
|
+
@filename = filename
|
|
287
|
+
yaml = YAML.load_file(filename)
|
|
288
|
+
%i[filter global macro words].each do |s|
|
|
289
|
+
cfg[s].merge!(yaml[s]) if yaml.key?(s)
|
|
115
290
|
end
|
|
116
|
-
|
|
117
291
|
end
|
|
118
292
|
end
|
|
119
|
-
|
|
120
293
|
end
|