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
|
@@ -1,9 +1,13 @@
|
|
|
1
|
-
|
|
1
|
+
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
require "delegate"
|
|
4
|
+
require "keisan"
|
|
4
5
|
|
|
6
|
+
module FilterRename
|
|
7
|
+
#
|
|
5
8
|
# This is the class which handles the list
|
|
6
9
|
# of filters.
|
|
10
|
+
#
|
|
7
11
|
class FilterList
|
|
8
12
|
attr_reader :filters
|
|
9
13
|
|
|
@@ -12,35 +16,34 @@ module FilterRename
|
|
|
12
16
|
end
|
|
13
17
|
|
|
14
18
|
def expand_macros!(macro)
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
@filters.insert(i + z, k.to_s.to_filter => v)
|
|
26
|
-
end
|
|
27
|
-
z += 1
|
|
19
|
+
@filters.each_with_index do |names, idx|
|
|
20
|
+
next unless names.keys[0] == FilterRename::MacroConfig
|
|
21
|
+
|
|
22
|
+
z = 1
|
|
23
|
+
names.values.pop.each do |n|
|
|
24
|
+
macro.get_macro(n).each do |k, v|
|
|
25
|
+
if v.nil? # Array
|
|
26
|
+
@filters.insert(idx + z, k.keys[0].to_s.to_filter => k[k.keys[0]])
|
|
27
|
+
else # Hash
|
|
28
|
+
@filters.insert(idx + z, k.to_s.to_filter => v)
|
|
28
29
|
end
|
|
29
|
-
|
|
30
|
+
z += 1
|
|
30
31
|
end
|
|
32
|
+
@filters[idx] = nil
|
|
31
33
|
end
|
|
32
34
|
end
|
|
33
35
|
|
|
34
36
|
@filters.delete_if(&:nil?)
|
|
35
37
|
end
|
|
36
|
-
|
|
37
38
|
end
|
|
38
39
|
|
|
39
|
-
|
|
40
|
+
#
|
|
41
|
+
# The base class for all type of
|
|
42
|
+
# filters.
|
|
43
|
+
#
|
|
40
44
|
class FilterBase < SimpleDelegator
|
|
41
|
-
|
|
42
45
|
def initialize(obj, options)
|
|
43
|
-
super
|
|
46
|
+
super(obj)
|
|
44
47
|
@dest = obj # useful for macros
|
|
45
48
|
@cfg = options[:cfg]
|
|
46
49
|
@words = options[:words]
|
|
@@ -51,15 +54,15 @@ module FilterRename
|
|
|
51
54
|
end
|
|
52
55
|
|
|
53
56
|
def set_config(name, value)
|
|
54
|
-
raise InvalidFilterSetting, name unless @cfg.instance_variables.include?("@#{name}"
|
|
57
|
+
raise InvalidFilterSetting, name unless @cfg.instance_variables.include?(:"@#{name}")
|
|
55
58
|
|
|
56
|
-
@cfg.instance_variable_set
|
|
59
|
+
@cfg.instance_variable_set "@#{name}", value
|
|
57
60
|
end
|
|
58
61
|
|
|
59
62
|
def get_config(name)
|
|
60
|
-
raise InvalidFilterSetting, name unless @cfg.instance_variables.include?("@#{name}"
|
|
63
|
+
raise InvalidFilterSetting, name unless @cfg.instance_variables.include?(:"@#{name}")
|
|
61
64
|
|
|
62
|
-
@cfg.instance_variable_get
|
|
65
|
+
@cfg.instance_variable_get "@#{name}"
|
|
63
66
|
end
|
|
64
67
|
|
|
65
68
|
def filter(value)
|
|
@@ -67,18 +70,20 @@ module FilterRename
|
|
|
67
70
|
end
|
|
68
71
|
|
|
69
72
|
def set_string(value, target = nil)
|
|
73
|
+
return if value.nil?
|
|
74
|
+
|
|
70
75
|
if target.nil?
|
|
71
|
-
super
|
|
76
|
+
super(@cfg.target, value)
|
|
72
77
|
else
|
|
73
|
-
super
|
|
78
|
+
super(target, value)
|
|
74
79
|
end
|
|
75
80
|
end
|
|
76
81
|
|
|
77
82
|
def get_string(target = nil)
|
|
78
83
|
if target.nil?
|
|
79
|
-
super
|
|
84
|
+
super(@cfg.target)
|
|
80
85
|
else
|
|
81
|
-
super
|
|
86
|
+
super
|
|
82
87
|
end
|
|
83
88
|
end
|
|
84
89
|
|
|
@@ -91,40 +96,80 @@ module FilterRename
|
|
|
91
96
|
str
|
|
92
97
|
end
|
|
93
98
|
|
|
99
|
+
def current_target
|
|
100
|
+
@cfg.target.to_s
|
|
101
|
+
end
|
|
94
102
|
end
|
|
95
103
|
|
|
96
|
-
|
|
104
|
+
#
|
|
105
|
+
# Mixin module for all those filters
|
|
106
|
+
# that make use of indexes.
|
|
107
|
+
#
|
|
97
108
|
module IndexedParams
|
|
109
|
+
attr_reader :params, :params_expanded, :items
|
|
98
110
|
|
|
99
|
-
def
|
|
111
|
+
def normalize_index(idx)
|
|
112
|
+
max_length = items.length
|
|
113
|
+
raise IndexOutOfRange, [idx, max_length] if idx.to_i > max_length || idx.to_i < -max_length
|
|
114
|
+
|
|
115
|
+
if idx.to_i.positive?
|
|
116
|
+
idx = idx.to_i.pred # % max_length
|
|
117
|
+
elsif idx.to_i.negative?
|
|
118
|
+
idx = idx.to_i + max_length # % max_length
|
|
119
|
+
end
|
|
120
|
+
idx.to_i
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
def indexes
|
|
100
124
|
indexes = []
|
|
101
|
-
params_length =
|
|
125
|
+
params_length = indexed_params.zero? ? params.length : indexed_params
|
|
102
126
|
|
|
103
127
|
params[0..params_length.pred].each do |x|
|
|
104
128
|
if x =~ /\.\./
|
|
105
|
-
indexes
|
|
129
|
+
indexes += Range.new(*(x.split("..").map { |y| normalize_index(y) })).map { |idx| idx }
|
|
130
|
+
elsif x =~ /:/
|
|
131
|
+
indexes += x.split(":").map { |y| normalize_index(y) }
|
|
106
132
|
else
|
|
107
|
-
indexes <<
|
|
133
|
+
indexes << normalize_index(x)
|
|
108
134
|
end
|
|
109
|
-
|
|
110
135
|
end
|
|
111
136
|
|
|
112
137
|
indexes
|
|
113
138
|
end
|
|
114
139
|
|
|
140
|
+
def string_to_loop
|
|
141
|
+
get_string
|
|
142
|
+
end
|
|
143
|
+
|
|
115
144
|
def indexed_params
|
|
116
145
|
1
|
|
117
146
|
end
|
|
118
|
-
end
|
|
119
|
-
|
|
120
147
|
|
|
121
|
-
|
|
122
|
-
|
|
148
|
+
def self_targeted?
|
|
149
|
+
false
|
|
150
|
+
end
|
|
123
151
|
|
|
124
152
|
def filter(params)
|
|
125
|
-
|
|
153
|
+
@params = params
|
|
154
|
+
@items = indexed_items
|
|
155
|
+
@params_expanded = indexes
|
|
156
|
+
|
|
157
|
+
res = loop_items
|
|
158
|
+
|
|
159
|
+
if self_targeted?
|
|
160
|
+
super(get_string)
|
|
161
|
+
else
|
|
162
|
+
super(res)
|
|
163
|
+
end
|
|
126
164
|
end
|
|
165
|
+
end
|
|
127
166
|
|
|
167
|
+
#
|
|
168
|
+
# Base class for all the word
|
|
169
|
+
# oriented filters
|
|
170
|
+
#
|
|
171
|
+
class FilterWord < FilterBase
|
|
172
|
+
include IndexedParams
|
|
128
173
|
|
|
129
174
|
private
|
|
130
175
|
|
|
@@ -132,60 +177,116 @@ module FilterRename
|
|
|
132
177
|
get_config(:word_separator)
|
|
133
178
|
end
|
|
134
179
|
|
|
135
|
-
def
|
|
136
|
-
|
|
137
|
-
idx = idx.to_i.pred
|
|
138
|
-
elsif idx.to_i.negative?
|
|
139
|
-
idx = idx.to_i + str.split(ws).length
|
|
140
|
-
end
|
|
141
|
-
idx.to_i
|
|
180
|
+
def indexed_items
|
|
181
|
+
string_to_loop.split(ws)
|
|
142
182
|
end
|
|
143
183
|
|
|
144
|
-
def
|
|
145
|
-
str =
|
|
184
|
+
def loop_items
|
|
185
|
+
str = items.clone
|
|
146
186
|
|
|
147
|
-
|
|
148
|
-
str[idx] = send :filtered_word, str[idx],
|
|
187
|
+
params_expanded.each_with_index do |idx, param_num|
|
|
188
|
+
str[idx] = send :filtered_word, str[idx], param_num.next
|
|
149
189
|
end
|
|
150
190
|
|
|
151
191
|
str.delete_if(&:nil?).join(ws)
|
|
152
192
|
end
|
|
153
|
-
|
|
154
193
|
end
|
|
155
194
|
|
|
195
|
+
#
|
|
196
|
+
# Base class for all the number oriented
|
|
197
|
+
# filters.
|
|
198
|
+
#
|
|
156
199
|
class FilterNumber < FilterBase
|
|
157
|
-
|
|
158
200
|
include IndexedParams
|
|
159
201
|
|
|
160
|
-
def
|
|
161
|
-
|
|
162
|
-
|
|
202
|
+
def self.calculator(num, expr)
|
|
203
|
+
@@calc ||= Keisan::Calculator.new
|
|
204
|
+
begin
|
|
205
|
+
@@calc.evaluate("gt()")
|
|
206
|
+
rescue StandardError
|
|
207
|
+
@@calc.define_function!(:gt, ->(x) { num > x })
|
|
208
|
+
@@calc.define_function!(:lt, ->(x) { num < x })
|
|
209
|
+
@@calc.define_function!(:gte, ->(x) { num >= x })
|
|
210
|
+
@@calc.define_function!(:lte, ->(x) { num <= x })
|
|
211
|
+
@@calc.define_function!(:inc, ->(x) { num + x })
|
|
212
|
+
@@calc.define_function!(:mult, ->(x) { num * x })
|
|
213
|
+
end
|
|
163
214
|
|
|
215
|
+
@@calc.evaluate(expr)
|
|
216
|
+
end
|
|
164
217
|
|
|
165
218
|
private
|
|
166
219
|
|
|
167
|
-
def
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
220
|
+
def ns
|
|
221
|
+
get_config(:number_separator)
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
def indexed_items
|
|
225
|
+
string_to_loop.numbers
|
|
226
|
+
end
|
|
227
|
+
|
|
228
|
+
def loop_items
|
|
229
|
+
str = string_to_loop
|
|
230
|
+
numbers = items.clone
|
|
231
|
+
|
|
232
|
+
params_expanded.each_with_index do |idx, param_idx|
|
|
233
|
+
numbers[idx] = send :filtered_number, numbers[idx], param_idx.next
|
|
234
|
+
end
|
|
235
|
+
str.map_number_with_index do |_num, idx|
|
|
236
|
+
numbers[idx]
|
|
172
237
|
end
|
|
173
|
-
idx.to_i
|
|
174
238
|
end
|
|
239
|
+
end
|
|
175
240
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
241
|
+
#
|
|
242
|
+
# Base class for all the Regexp
|
|
243
|
+
# oriented filters.
|
|
244
|
+
#
|
|
245
|
+
class FilterRegExp < FilterBase
|
|
246
|
+
def filter(params)
|
|
247
|
+
super(loop_regex(get_string, params))
|
|
248
|
+
end
|
|
182
249
|
|
|
183
|
-
|
|
250
|
+
private
|
|
251
|
+
|
|
252
|
+
def loop_regex(str, params)
|
|
253
|
+
str.gsub(Regexp.new(wrap_regex(params[0]), get_config(:ignore_case).to_boolean)) do |_x|
|
|
254
|
+
matches = Regexp.last_match.clone
|
|
255
|
+
send(:filtered_regexp, matches.to_a.delete_if(&:nil?), params).to_s.gsub(/\\([0-9]+)/) do |_y|
|
|
256
|
+
matches[::Regexp.last_match(1).to_i]
|
|
184
257
|
end
|
|
185
258
|
end
|
|
186
|
-
|
|
187
|
-
str
|
|
188
259
|
end
|
|
189
260
|
end
|
|
190
261
|
|
|
262
|
+
#
|
|
263
|
+
# Base class for all the occurence
|
|
264
|
+
# oriented filters.
|
|
265
|
+
#
|
|
266
|
+
class FilterOccurrence < FilterBase
|
|
267
|
+
include IndexedParams
|
|
268
|
+
|
|
269
|
+
private
|
|
270
|
+
|
|
271
|
+
def os
|
|
272
|
+
get_config(:occurrence_separator)
|
|
273
|
+
end
|
|
274
|
+
|
|
275
|
+
def indexed_items
|
|
276
|
+
string_to_loop.scan(Regexp.new(params[indexed_params]))
|
|
277
|
+
end
|
|
278
|
+
|
|
279
|
+
def loop_items
|
|
280
|
+
str = string_to_loop
|
|
281
|
+
regexp = Regexp.new(params[indexed_params])
|
|
282
|
+
occurences = items.clone
|
|
283
|
+
|
|
284
|
+
params_expanded.each_with_index do |idx, param_idx|
|
|
285
|
+
occurences[idx] = send :filtered_occurrence, occurences[idx], param_idx.next
|
|
286
|
+
end
|
|
287
|
+
str.gsub(regexp).with_index do |_idxtem, _i|
|
|
288
|
+
occurences[idx]
|
|
289
|
+
end
|
|
290
|
+
end
|
|
291
|
+
end
|
|
191
292
|
end
|
|
@@ -1,5 +1,10 @@
|
|
|
1
|
-
|
|
1
|
+
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
module FilterRename
|
|
4
|
+
#
|
|
5
|
+
# Class that handles the filters
|
|
6
|
+
# applying them one after one.
|
|
7
|
+
#
|
|
3
8
|
class FilterPipe
|
|
4
9
|
attr_reader :source, :dest
|
|
5
10
|
|
|
@@ -8,27 +13,25 @@ module FilterRename
|
|
|
8
13
|
@cfg = cfg.filter.clone
|
|
9
14
|
@source = FilenameFactory.create(fname, cfg.global)
|
|
10
15
|
@dest = Marshal.load(Marshal.dump(@source))
|
|
11
|
-
@filters =
|
|
16
|
+
@filters = filters.instance_of?(Array) ? filters : filters.filters
|
|
12
17
|
@words = cfg.words
|
|
13
18
|
end
|
|
14
19
|
|
|
15
20
|
def changed?
|
|
16
|
-
!
|
|
21
|
+
!unchanged?
|
|
17
22
|
end
|
|
18
23
|
|
|
19
24
|
def unchanged?
|
|
20
25
|
@source == @dest
|
|
21
26
|
end
|
|
22
|
-
|
|
27
|
+
alias identical? unchanged?
|
|
23
28
|
|
|
24
29
|
def diff
|
|
25
30
|
@source.diff(@dest)
|
|
26
31
|
end
|
|
27
32
|
|
|
28
33
|
def apply
|
|
29
|
-
|
|
30
|
-
@filters.each_with_index do |f, i|
|
|
31
|
-
|
|
34
|
+
@filters.each_with_index do |f, _i|
|
|
32
35
|
filter = f.keys.pop
|
|
33
36
|
params = f.values.pop
|
|
34
37
|
|
|
@@ -37,7 +40,6 @@ module FilterRename
|
|
|
37
40
|
else
|
|
38
41
|
filter.new(@dest, cfg: @cfg, words: @words).filter(params) unless skip?
|
|
39
42
|
end
|
|
40
|
-
|
|
41
43
|
end
|
|
42
44
|
|
|
43
45
|
self
|
|
@@ -50,15 +52,18 @@ module FilterRename
|
|
|
50
52
|
private
|
|
51
53
|
|
|
52
54
|
def skip?
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
55
|
+
grep_on = @cfg.grep_on_dest ? :dest : :source
|
|
56
|
+
unmatched = if %i[full_filename full_path filename].include? @cfg.grep_target.to_sym
|
|
57
|
+
instance_variable_get("@#{grep_on}")
|
|
58
|
+
.send(@cfg.grep_target.to_sym)
|
|
59
|
+
.match(Regexp.new(@cfg.grep)).nil?
|
|
60
|
+
else
|
|
61
|
+
instance_variable_get("@#{grep_on}")
|
|
62
|
+
.get_string(@cfg.grep_target)
|
|
63
|
+
.match(Regexp.new(@cfg.grep)).nil?
|
|
64
|
+
end
|
|
58
65
|
|
|
59
66
|
@cfg.grep_exclude.to_s.to_boolean ? !unmatched : unmatched
|
|
60
67
|
end
|
|
61
|
-
|
|
62
68
|
end
|
|
63
|
-
|
|
64
69
|
end
|