xxx_rename 0.0.1
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 +7 -0
- data/.github/workflows/codeql-analysis.yml +42 -0
- data/.github/workflows/ruby.yml +44 -0
- data/.gitignore +12 -0
- data/.rspec +3 -0
- data/.rubocop.yml +41 -0
- data/.ruby-version +1 -0
- data/Gemfile +6 -0
- data/Gemfile.lock +174 -0
- data/README.md +319 -0
- data/Rakefile +12 -0
- data/bin/console +23 -0
- data/bin/install +22 -0
- data/bin/setup +8 -0
- data/codecov.yml +2 -0
- data/docs/DEVELOPMENT.md +42 -0
- data/exe/xxx_rename +12 -0
- data/lib/xxx_rename/actions/base_action.rb +20 -0
- data/lib/xxx_rename/actions/log_new_filename.rb +40 -0
- data/lib/xxx_rename/actions/resolver.rb +32 -0
- data/lib/xxx_rename/actions/stash_app_post_movie.rb +62 -0
- data/lib/xxx_rename/actors_helper.rb +117 -0
- data/lib/xxx_rename/cli.rb +211 -0
- data/lib/xxx_rename/client.rb +110 -0
- data/lib/xxx_rename/constants.rb +96 -0
- data/lib/xxx_rename/contract/config_contract.rb +241 -0
- data/lib/xxx_rename/contract/config_generator.rb +207 -0
- data/lib/xxx_rename/contract/file_rename_op_contract.rb +54 -0
- data/lib/xxx_rename/contract/types.rb +10 -0
- data/lib/xxx_rename/core_extensions/string.rb +39 -0
- data/lib/xxx_rename/data/base.rb +34 -0
- data/lib/xxx_rename/data/config.rb +97 -0
- data/lib/xxx_rename/data/file_rename_op.rb +42 -0
- data/lib/xxx_rename/data/file_rename_op_datastore.rb +111 -0
- data/lib/xxx_rename/data/naughty_america_database.rb +22 -0
- data/lib/xxx_rename/data/query_interface.rb +78 -0
- data/lib/xxx_rename/data/scene_data.rb +71 -0
- data/lib/xxx_rename/data/scene_datastore.rb +401 -0
- data/lib/xxx_rename/data/site_config.rb +84 -0
- data/lib/xxx_rename/data/types.rb +13 -0
- data/lib/xxx_rename/errors.rb +28 -0
- data/lib/xxx_rename/file_scanner.rb +49 -0
- data/lib/xxx_rename/file_utilities.rb +38 -0
- data/lib/xxx_rename/filename_generator.rb +173 -0
- data/lib/xxx_rename/integrations/base.rb +20 -0
- data/lib/xxx_rename/integrations/stash_app.rb +316 -0
- data/lib/xxx_rename/log.rb +26 -0
- data/lib/xxx_rename/migration_client.rb +139 -0
- data/lib/xxx_rename/processed_file.rb +203 -0
- data/lib/xxx_rename/search.rb +166 -0
- data/lib/xxx_rename/site_client_matcher.rb +299 -0
- data/lib/xxx_rename/site_clients/adult_time.rb +31 -0
- data/lib/xxx_rename/site_clients/algolia_common.rb +48 -0
- data/lib/xxx_rename/site_clients/algolia_v2.rb +181 -0
- data/lib/xxx_rename/site_clients/babes.rb +15 -0
- data/lib/xxx_rename/site_clients/base.rb +61 -0
- data/lib/xxx_rename/site_clients/blacked.rb +12 -0
- data/lib/xxx_rename/site_clients/blacked_raw.rb +12 -0
- data/lib/xxx_rename/site_clients/brazzers.rb +15 -0
- data/lib/xxx_rename/site_clients/configuration.rb +55 -0
- data/lib/xxx_rename/site_clients/digital_playground.rb +15 -0
- data/lib/xxx_rename/site_clients/elegant_angel.rb +168 -0
- data/lib/xxx_rename/site_clients/errors.rb +103 -0
- data/lib/xxx_rename/site_clients/evil_angel.rb +59 -0
- data/lib/xxx_rename/site_clients/goodporn.rb +109 -0
- data/lib/xxx_rename/site_clients/jules_jordan.rb +22 -0
- data/lib/xxx_rename/site_clients/jules_jordan_media.rb +175 -0
- data/lib/xxx_rename/site_clients/manuel_ferrara.rb +24 -0
- data/lib/xxx_rename/site_clients/mg_premium.rb +247 -0
- data/lib/xxx_rename/site_clients/mofos.rb +15 -0
- data/lib/xxx_rename/site_clients/naughty_america.rb +272 -0
- data/lib/xxx_rename/site_clients/nfbusty.rb +84 -0
- data/lib/xxx_rename/site_clients/query_generator/base.rb +89 -0
- data/lib/xxx_rename/site_clients/query_generator/evil_angel.rb +36 -0
- data/lib/xxx_rename/site_clients/query_generator/goodporn.rb +27 -0
- data/lib/xxx_rename/site_clients/query_generator/mg_premium.rb +26 -0
- data/lib/xxx_rename/site_clients/query_generator/naughty_america.rb +24 -0
- data/lib/xxx_rename/site_clients/query_generator/stash_db.rb +21 -0
- data/lib/xxx_rename/site_clients/query_generator/vixen.rb +27 -0
- data/lib/xxx_rename/site_clients/query_generator/whale.rb +39 -0
- data/lib/xxx_rename/site_clients/reality_kings.rb +14 -0
- data/lib/xxx_rename/site_clients/stash_db.rb +257 -0
- data/lib/xxx_rename/site_clients/tushy.rb +12 -0
- data/lib/xxx_rename/site_clients/tushy_raw.rb +12 -0
- data/lib/xxx_rename/site_clients/twistys.rb +15 -0
- data/lib/xxx_rename/site_clients/vixen.rb +12 -0
- data/lib/xxx_rename/site_clients/vixen_media.rb +130 -0
- data/lib/xxx_rename/site_clients/whale.rb +106 -0
- data/lib/xxx_rename/site_clients/wicked.rb +52 -0
- data/lib/xxx_rename/site_clients/x_empire.rb +51 -0
- data/lib/xxx_rename/site_clients/zero_tolerance.rb +36 -0
- data/lib/xxx_rename/utils.rb +81 -0
- data/lib/xxx_rename/version.rb +5 -0
- data/lib/xxx_rename.rb +60 -0
- data/output.png +0 -0
- data/xxx_rename.gemspec +42 -0
- metadata +411 -0
@@ -0,0 +1,401 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "pstore"
|
4
|
+
require "benchmark"
|
5
|
+
require "pathname"
|
6
|
+
|
7
|
+
require "xxx_rename/data/query_interface"
|
8
|
+
|
9
|
+
module XxxRename
|
10
|
+
module Data
|
11
|
+
DEFAULT_STORE_FILE = "xxx_rename_datastore.store"
|
12
|
+
|
13
|
+
METADATA_ROOT = "_m_"
|
14
|
+
REGISTERED_FILE_PATHS_PREFIX = "_sp_"
|
15
|
+
|
16
|
+
RecordStatus = Struct.new(:key, :scene_saved, :missing_keys, :conflicting_indexes, :expected_filename_key, keyword_init: true) do
|
17
|
+
def valid?
|
18
|
+
scene_saved && missing_keys.empty? && conflicting_indexes.empty?
|
19
|
+
end
|
20
|
+
|
21
|
+
def errors
|
22
|
+
self
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
class SceneDatastore
|
27
|
+
attr_reader :store
|
28
|
+
|
29
|
+
def initialize(dir, name = DEFAULT_STORE_FILE)
|
30
|
+
path = File.join(dir, name)
|
31
|
+
XxxRename.logger.info "#{"[DATASTORE INIT]".colorize(:green)} #{path} #{name}"
|
32
|
+
|
33
|
+
@store = PStore.new path
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
class SceneDatastoreQuery < QueryInterface
|
38
|
+
include FileUtilities
|
39
|
+
|
40
|
+
# @param [XxxRename::Data::SceneData] scene_data
|
41
|
+
# @raise [UniqueRecordViolation] is key already exists in the DB
|
42
|
+
def create!(scene_data, force: false)
|
43
|
+
benchmark("create!") do
|
44
|
+
semaphore.synchronize do
|
45
|
+
store.transaction do
|
46
|
+
unless force
|
47
|
+
existing_record = store.fetch(scene_data.key, nil)
|
48
|
+
raise UniqueRecordViolation, existing_record if existing_record
|
49
|
+
end
|
50
|
+
|
51
|
+
store[scene_data.key] = scene_data
|
52
|
+
create_indexes(scene_data.key, scene_data)
|
53
|
+
scene_data.key
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
# Find a scene using one of
|
60
|
+
# 1. collection_tag && id
|
61
|
+
# 2. collection_tag && title
|
62
|
+
# 3. title && actors
|
63
|
+
#
|
64
|
+
# @param [String] title
|
65
|
+
# @param [Array[String]] actors
|
66
|
+
# @param [String] collection_tag
|
67
|
+
# @param [String] id
|
68
|
+
def find(id: nil, collection_tag: nil, title: nil, actors: nil)
|
69
|
+
param = { id: id, collection_tag: collection_tag, title: title, actors: actors }.reject { |_k, v| v.nil? }
|
70
|
+
benchmark("find #{param}") do
|
71
|
+
validate_type_params!(id: id, collection_tag: collection_tag, title: title, actors: actors)
|
72
|
+
semaphore.synchronize do
|
73
|
+
store.transaction(read_only: true) do
|
74
|
+
keys = fetch_keys?(id: id, collection_tag: collection_tag, title: title, actors: actors)
|
75
|
+
keys.map do |key|
|
76
|
+
store[key]
|
77
|
+
end.compact
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def find_by_abs_path?(path)
|
84
|
+
benchmark("find_by_abs_path?") do
|
85
|
+
store.transaction(read_only: true) do
|
86
|
+
index_key = generate_lookup_key(REGISTERED_FILE_PATHS_PREFIX, path)
|
87
|
+
key = store.fetch(index_key, nil)
|
88
|
+
return if key.nil?
|
89
|
+
|
90
|
+
store[key]
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def find_by_key?(key)
|
96
|
+
benchmark("find_by_key? #{key}") do
|
97
|
+
semaphore.synchronize do
|
98
|
+
store.transaction(read_only: true) do
|
99
|
+
store.fetch(key, nil)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
# @param [XxxRename::Data::SceneData] scene_data
|
106
|
+
# @param [String] filename
|
107
|
+
def register_file(scene_data, filename, old_filename: nil)
|
108
|
+
validate_file_paths!(filename, old_filename: old_filename)
|
109
|
+
benchmark("register_file") do
|
110
|
+
semaphore.synchronize do
|
111
|
+
store.transaction do
|
112
|
+
key = scene_data.key
|
113
|
+
new_index_key = generate_lookup_key(REGISTERED_FILE_PATHS_PREFIX, filename)
|
114
|
+
store[new_index_key] = key
|
115
|
+
|
116
|
+
if old_filename
|
117
|
+
old_index_key = generate_lookup_key(REGISTERED_FILE_PATHS_PREFIX, old_filename)
|
118
|
+
store.delete(old_index_key)
|
119
|
+
end
|
120
|
+
|
121
|
+
new_index_key
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
def exists?(key)
|
128
|
+
semaphore.synchronize do
|
129
|
+
store.transaction(true) do
|
130
|
+
store.root?(key)
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
alias exist? exists?
|
136
|
+
|
137
|
+
def destroy(scene_data, *filenames)
|
138
|
+
benchmark("destroy") do
|
139
|
+
semaphore.synchronize do
|
140
|
+
store.transaction do
|
141
|
+
key = scene_data.key
|
142
|
+
store.delete(key)
|
143
|
+
destroy_indexes(key, scene_data, *filenames)
|
144
|
+
key
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
def count
|
151
|
+
benchmark("count") do
|
152
|
+
semaphore.synchronize do
|
153
|
+
store.transaction(true) do
|
154
|
+
md5_regex = Regexp.new("^[a-f0-9]{32}$", Regexp::IGNORECASE)
|
155
|
+
|
156
|
+
store.roots.select { |x| x.match?(md5_regex) }.length
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
def all
|
163
|
+
benchmark("all") do
|
164
|
+
semaphore.synchronize do
|
165
|
+
store.transaction(true) do
|
166
|
+
md5_regex = Regexp.new("^[a-f0-9]{32}$", Regexp::IGNORECASE)
|
167
|
+
|
168
|
+
store.roots.select { |x| x.match?(md5_regex) }.map { |key| store[key] }
|
169
|
+
end
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
def metadata
|
175
|
+
semaphore.synchronize do
|
176
|
+
store.transaction(true) do
|
177
|
+
store.fetch(METADATA_ROOT, {})
|
178
|
+
end
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
def update_metadata(hash)
|
183
|
+
semaphore.synchronize do
|
184
|
+
store.transaction do
|
185
|
+
store[METADATA_ROOT] ||= {}
|
186
|
+
store[METADATA_ROOT] = store[METADATA_ROOT].merge(hash)
|
187
|
+
end
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
#
|
192
|
+
# Internal method for testing. Is of no use for a user
|
193
|
+
#
|
194
|
+
# rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
195
|
+
def valid?(scene_data, filepath: nil)
|
196
|
+
validate_file_paths!(filepath, old_filename: nil)
|
197
|
+
|
198
|
+
errors = {
|
199
|
+
key: scene_data.key,
|
200
|
+
scene_saved: true,
|
201
|
+
missing_keys: [],
|
202
|
+
conflicting_indexes: {},
|
203
|
+
expected_filename_key: nil
|
204
|
+
}
|
205
|
+
|
206
|
+
semaphore.synchronize do
|
207
|
+
store.transaction(true) do
|
208
|
+
key = scene_data.key
|
209
|
+
scene = store[key]
|
210
|
+
errors[:scene_saved] = false if scene.nil?
|
211
|
+
|
212
|
+
if scene_data.id
|
213
|
+
id_index_value = store[generate_lookup_key(scene_data.collection_tag, scene_data.id)]
|
214
|
+
errors[:missing_keys] << :id_index if id_index_value.nil?
|
215
|
+
errors[:conflicting_indexes][:id_index] = id_index_value if !id_index_value.nil? && id_index_value != key
|
216
|
+
end
|
217
|
+
|
218
|
+
# title_index_value = store[generate_lookup_key(scene_data.collection_tag, scene_data.title)]
|
219
|
+
# errors[:missing_keys] << :title_index if title_index_value.nil?
|
220
|
+
# errors[:conflicting_indexes][:title_index] = title_index_value if !title_index_value.nil? && title_index_value != key
|
221
|
+
|
222
|
+
collection_title_index_value = store[generate_lookup_key(scene_data.collection, scene_data.title)]
|
223
|
+
errors[:missing_keys] << :collection_title_index if collection_title_index_value.nil?
|
224
|
+
if !collection_title_index_value.nil? && collection_title_index_value != key
|
225
|
+
errors[:conflicting_indexes][:collection_title_index] = collection_title_index_value
|
226
|
+
end
|
227
|
+
|
228
|
+
# title_actor_index_value = store[generate_lookup_key(scene_data.title, scene_data.actors.sort.join("|"))]
|
229
|
+
# errors[:missing_keys] << :title_actors_index if title_actor_index_value.nil? || title_actor_index_value.empty?
|
230
|
+
# if title_actor_index_value && !title_actor_index_value.empty? && !title_actor_index_value.include?(key)
|
231
|
+
# errors[:conflicting_indexes][:title_actors_index] = title_actor_index_value
|
232
|
+
# end
|
233
|
+
|
234
|
+
if filepath
|
235
|
+
filename_value = store[generate_lookup_key(REGISTERED_FILE_PATHS_PREFIX, filepath)]
|
236
|
+
unless filename_value
|
237
|
+
errors[:missing_keys] << :path
|
238
|
+
errors[:expected_filename_key] = sanitize(filepath)
|
239
|
+
end
|
240
|
+
end
|
241
|
+
|
242
|
+
status = RecordStatus.new(**errors)
|
243
|
+
status.valid? ? true : status.errors
|
244
|
+
end
|
245
|
+
end
|
246
|
+
end
|
247
|
+
# rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
248
|
+
|
249
|
+
private
|
250
|
+
|
251
|
+
def validate_file_paths!(filename, old_filename: nil)
|
252
|
+
raise "non absolute path" unless Pathname.new(filename).absolute?
|
253
|
+
|
254
|
+
raise "non absolute path" if old_filename && !Pathname.new(old_filename).absolute?
|
255
|
+
|
256
|
+
raise "file not exist #{filename}" unless valid_file?(filename)
|
257
|
+
|
258
|
+
true
|
259
|
+
end
|
260
|
+
|
261
|
+
def validate_type_params!(id:, collection_tag:, title:, actors:)
|
262
|
+
raise ArgumentError, "no key provided for lookup" if [id, collection_tag, title, actors].none?
|
263
|
+
|
264
|
+
raise TypeError, "actors: wrong argument type #{actors.class} (expected Array)" if actors && !(actors.is_a? Array)
|
265
|
+
end
|
266
|
+
|
267
|
+
#
|
268
|
+
# This method id thread unsafe!
|
269
|
+
# Always call from a synchronized mutex and within a transaction
|
270
|
+
#
|
271
|
+
# @param [String] id
|
272
|
+
# @param [Array[String]] collection_tag
|
273
|
+
# @param [String] title
|
274
|
+
# @param [String] actors
|
275
|
+
# @return [Array[String]]
|
276
|
+
def fetch_keys?(id:, collection_tag:, title:, actors:)
|
277
|
+
if collection_tag && id
|
278
|
+
[store[generate_lookup_key(collection_tag, id)]]
|
279
|
+
elsif collection_tag && title
|
280
|
+
[store[generate_lookup_key(collection_tag, title)]]
|
281
|
+
elsif title && actors
|
282
|
+
store.fetch(generate_lookup_key(title, actors.sort.join("|")), []).to_a
|
283
|
+
end
|
284
|
+
end
|
285
|
+
|
286
|
+
#
|
287
|
+
# Create indexes for a scene for faster lookups.
|
288
|
+
# Comes at a cost of increased file size
|
289
|
+
# Supported indexes:
|
290
|
+
# 1. collection_tag -> id -> key // id index
|
291
|
+
# ~~2. collection_tag -> title -> key // title index~~
|
292
|
+
# 3. collection -> title -> key // title index
|
293
|
+
# ~~4. title + actors -> [keys] // title,actors index~~
|
294
|
+
# ** collection_tag -> title & title + actors are not created
|
295
|
+
# for performance reasons. They can be supported later if
|
296
|
+
# the need arises
|
297
|
+
# @param [String] key
|
298
|
+
# @param [XxxRename::Data::SceneData] scene_data
|
299
|
+
def create_indexes(key, scene_data)
|
300
|
+
create_id_index(key, scene_data)
|
301
|
+
# create_title_index(key, scene_data)
|
302
|
+
create_collection_title_index(key, scene_data)
|
303
|
+
# create_title_actors_index(key, scene_data)
|
304
|
+
end
|
305
|
+
|
306
|
+
# collection_tag -> id -> key // id index
|
307
|
+
# @param [String] key
|
308
|
+
# @param [XxxRename::Data::SceneData] scene_data
|
309
|
+
def create_id_index(key, scene_data)
|
310
|
+
return unless scene_data.id
|
311
|
+
|
312
|
+
index_key = generate_lookup_key(scene_data.collection_tag, scene_data.id)
|
313
|
+
store[index_key] = key
|
314
|
+
end
|
315
|
+
|
316
|
+
# # collection_tag -> title -> key // title index
|
317
|
+
# # @param [String] key
|
318
|
+
# # @param [XxxRename::Data::SceneData] scene_data
|
319
|
+
# def create_title_index(key, scene_data)
|
320
|
+
# index_key = generate_lookup_key(scene_data.collection_tag, scene_data.title)
|
321
|
+
# store[index_key] = key
|
322
|
+
# end
|
323
|
+
|
324
|
+
def create_collection_title_index(key, scene_data)
|
325
|
+
index_key = generate_lookup_key(scene_data.collection, scene_data.title)
|
326
|
+
store[index_key] = key
|
327
|
+
end
|
328
|
+
|
329
|
+
# # title + actors -> [keys] // title,actors index
|
330
|
+
# # @param [String] key
|
331
|
+
# # @param [XxxRename::Data::SceneData] scene_data
|
332
|
+
# def create_title_actors_index(key, scene_data)
|
333
|
+
# index_key = generate_lookup_key(scene_data.title, scene_data.actors.sort.join("|"))
|
334
|
+
#
|
335
|
+
# store[index_key] ||= Set.new
|
336
|
+
# store[index_key].add(key)
|
337
|
+
# end
|
338
|
+
|
339
|
+
# @param [String] key
|
340
|
+
# @param [XxxRename::Data::SceneData] scene_data
|
341
|
+
# @param [Array[String]] filenames
|
342
|
+
def destroy_indexes(key, scene_data, *filenames)
|
343
|
+
destroy_id_index(scene_data)
|
344
|
+
# destroy_title_index(scene_data)
|
345
|
+
destroy_collection_title_index(key, scene_data)
|
346
|
+
# destroy_title_actors_index(key, scene_data)
|
347
|
+
destroy_registered_filenames(filenames)
|
348
|
+
end
|
349
|
+
|
350
|
+
def destroy_id_index(scene_data)
|
351
|
+
return unless scene_data.id
|
352
|
+
|
353
|
+
index_key = generate_lookup_key(scene_data.collection_tag, scene_data.id)
|
354
|
+
store.delete(index_key)
|
355
|
+
end
|
356
|
+
|
357
|
+
# def destroy_title_index(scene_data)
|
358
|
+
# index_key = generate_lookup_key(scene_data.collection_tag, scene_data.title)
|
359
|
+
# store.delete(index_key)
|
360
|
+
# end
|
361
|
+
|
362
|
+
# def destroy_title_actors_index(key, scene_data)
|
363
|
+
# index_key = generate_lookup_key(scene_data.title, scene_data.actors.sort.join("|"))
|
364
|
+
#
|
365
|
+
# index_value = store[index_key]
|
366
|
+
# if index_value && index_value.length > 1
|
367
|
+
# index_value.delete(key)
|
368
|
+
# else
|
369
|
+
# store.delete(index_key)
|
370
|
+
# end
|
371
|
+
# end
|
372
|
+
|
373
|
+
def destroy_collection_title_index(_key, scene_data)
|
374
|
+
index_key = generate_lookup_key(scene_data.collection, scene_data.title)
|
375
|
+
store.delete(index_key)
|
376
|
+
end
|
377
|
+
|
378
|
+
def destroy_registered_filenames(filenames)
|
379
|
+
filenames.map do |file|
|
380
|
+
index_key = generate_lookup_key(REGISTERED_FILE_PATHS_PREFIX, file)
|
381
|
+
store.delete(index_key)
|
382
|
+
end
|
383
|
+
end
|
384
|
+
|
385
|
+
def title_actors_index_key(title, actors)
|
386
|
+
"<#{sanitize(title)}" \
|
387
|
+
"$#{sanitize(actors.sort.join("|"))}" \
|
388
|
+
">"
|
389
|
+
end
|
390
|
+
|
391
|
+
def benchmark(opr = "unnamed")
|
392
|
+
raise "#benchmark called without block" unless block_given?
|
393
|
+
|
394
|
+
resp = nil
|
395
|
+
time = Benchmark.measure { resp = yield }
|
396
|
+
XxxRename.logger.debug "#{"[BENCHMARK]".colorize(:cyan)} #{self.class.name}##{opr}: #{time.real.round(3)}s"
|
397
|
+
resp
|
398
|
+
end
|
399
|
+
end
|
400
|
+
end
|
401
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module XxxRename
|
4
|
+
module Data
|
5
|
+
class SimpleSiteConfig < Base
|
6
|
+
attribute :output_format, Types::Array.of(Types::String)
|
7
|
+
attribute :file_source_format, Types::Array.of(Types::String)
|
8
|
+
attribute :collection_tag, Types::String
|
9
|
+
end
|
10
|
+
|
11
|
+
class CredentialsConfig < Base
|
12
|
+
attribute? :username, Types::String.optional
|
13
|
+
attribute? :password, Types::String.optional
|
14
|
+
attribute? :cookie_file, Types::String.optional
|
15
|
+
attribute? :api_token, Types::String.optional
|
16
|
+
end
|
17
|
+
|
18
|
+
class DatabaseConfig < Base
|
19
|
+
attribute? :database, Types::String.optional
|
20
|
+
end
|
21
|
+
|
22
|
+
class AdultTimeConfig < Base
|
23
|
+
attributes_from Data::SimpleSiteConfig
|
24
|
+
end
|
25
|
+
|
26
|
+
class EvilAngelConfig < Base
|
27
|
+
attributes_from Data::SimpleSiteConfig
|
28
|
+
end
|
29
|
+
|
30
|
+
class ElegantAngelConfig < Base
|
31
|
+
attributes_from Data::SimpleSiteConfig
|
32
|
+
attributes_from Data::DatabaseConfig
|
33
|
+
end
|
34
|
+
|
35
|
+
class GoodpornConfig < Base
|
36
|
+
attributes_from Data::SimpleSiteConfig
|
37
|
+
end
|
38
|
+
|
39
|
+
class JulesJordanMediaConfig < Base
|
40
|
+
attributes_from Data::SimpleSiteConfig
|
41
|
+
attributes_from Data::CredentialsConfig
|
42
|
+
attributes_from Data::DatabaseConfig
|
43
|
+
end
|
44
|
+
|
45
|
+
class MgPremiumConfig < Base
|
46
|
+
attributes_from Data::SimpleSiteConfig
|
47
|
+
end
|
48
|
+
|
49
|
+
class NaughtyAmericaConfig < Base
|
50
|
+
attributes_from Data::SimpleSiteConfig
|
51
|
+
attributes_from Data::DatabaseConfig
|
52
|
+
end
|
53
|
+
|
54
|
+
class NfBustyConfig < Base
|
55
|
+
attributes_from Data::SimpleSiteConfig
|
56
|
+
attributes_from Data::DatabaseConfig
|
57
|
+
end
|
58
|
+
|
59
|
+
class StashDBConfig < Base
|
60
|
+
attributes_from Data::SimpleSiteConfig
|
61
|
+
attributes_from Data::CredentialsConfig
|
62
|
+
end
|
63
|
+
|
64
|
+
class VixenMediaConfig < Base
|
65
|
+
attributes_from Data::SimpleSiteConfig
|
66
|
+
end
|
67
|
+
|
68
|
+
class WhaleMediaConfig < Base
|
69
|
+
attributes_from Data::SimpleSiteConfig
|
70
|
+
end
|
71
|
+
|
72
|
+
class WickedConfig < Base
|
73
|
+
attributes_from Data::SimpleSiteConfig
|
74
|
+
end
|
75
|
+
|
76
|
+
class XEmpireConfig < Base
|
77
|
+
attributes_from Data::SimpleSiteConfig
|
78
|
+
end
|
79
|
+
|
80
|
+
class ZeroToleranceConfig < Base
|
81
|
+
attributes_from Data::SimpleSiteConfig
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module XxxRename
|
4
|
+
module Errors
|
5
|
+
class SafeExit < StandardError; end
|
6
|
+
|
7
|
+
class ConfigValidationError < StandardError
|
8
|
+
attr_reader :errors
|
9
|
+
|
10
|
+
def initialize(errors)
|
11
|
+
@errors = errors
|
12
|
+
super(message)
|
13
|
+
end
|
14
|
+
|
15
|
+
def message
|
16
|
+
messages = []
|
17
|
+
errors.messages.each do |key|
|
18
|
+
messages << "#{key.path.join(".")}: #{key.text}"
|
19
|
+
end
|
20
|
+
messages.join(", ")
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
class ParsingError < StandardError; end
|
25
|
+
class FatalError < StandardError; end
|
26
|
+
class UnprocessedEntity < StandardError; end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module XxxRename
|
4
|
+
class FileScanner
|
5
|
+
#
|
6
|
+
# Create a new Scanner
|
7
|
+
#
|
8
|
+
# @param [String] dir Directory to be processed
|
9
|
+
# @param [Hash] options
|
10
|
+
# @option options [Boolean] :nested Specify if nested directories should be scanned
|
11
|
+
# @option options [String] extension Extension of filenames to be selected
|
12
|
+
def initialize(dir, **options)
|
13
|
+
@nested = options[:nested]
|
14
|
+
@extension = options[:extension].nil? ? "*.{mp4,f4v,mkv,avi,wmv,flv}" : options[:extension]
|
15
|
+
@dir = dir
|
16
|
+
end
|
17
|
+
|
18
|
+
# @param [Proc] block Block of code to be executed for each file
|
19
|
+
def each(&block)
|
20
|
+
@block = block
|
21
|
+
process_directory(@dir)
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
#
|
27
|
+
# Executes a block of code for all files in a given directory. Will scan files
|
28
|
+
# in sub-directories if @nested is passed as true
|
29
|
+
#
|
30
|
+
# @param [String] dir Directory path to scan
|
31
|
+
def process_directory(dir)
|
32
|
+
Dir.chdir(dir) do
|
33
|
+
# Process files in a given directory
|
34
|
+
XxxRename.logger.info "#{"[DIRECTORY SCAN]".colorize(:blue)} #{Dir.pwd}"
|
35
|
+
|
36
|
+
Dir.glob(@extension).sort.each { |file| @block.call(file) }
|
37
|
+
|
38
|
+
# Return unless we want to scan the directories inside
|
39
|
+
# the directory `dir`
|
40
|
+
return unless @nested
|
41
|
+
|
42
|
+
nested_dir = Dir["*"].select { |o| File.directory?(o) }
|
43
|
+
nested_dir.sort.each do |each|
|
44
|
+
process_directory(each)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module XxxRename
|
4
|
+
module FileUtilities
|
5
|
+
MAX_FILENAME_LEN = 255
|
6
|
+
|
7
|
+
def valid_file?(file)
|
8
|
+
file && File.exist?(file) && File.file?(file)
|
9
|
+
end
|
10
|
+
|
11
|
+
def valid_dir?(dir)
|
12
|
+
dir && File.exist?(dir) && File.directory?(dir)
|
13
|
+
end
|
14
|
+
|
15
|
+
def read_file!(file)
|
16
|
+
return File.read(file).strip if valid_file?(file)
|
17
|
+
|
18
|
+
raise Errors::FatalError, "Unable to read file #{file}"
|
19
|
+
end
|
20
|
+
|
21
|
+
def read_yaml(file, default = {}, validate_type = nil)
|
22
|
+
read_yaml!(file, validate_type)
|
23
|
+
rescue Errors::FatalError
|
24
|
+
default
|
25
|
+
end
|
26
|
+
|
27
|
+
def read_yaml!(file, validate_type)
|
28
|
+
raise Errors::FatalError, "Unable to read yaml file #{file}" unless file && File.file?(file) && File.exist?(file)
|
29
|
+
|
30
|
+
yaml = YAML.load_file(file)
|
31
|
+
return yaml unless validate_type
|
32
|
+
|
33
|
+
return yaml if yaml.is_a?(validate_type)
|
34
|
+
|
35
|
+
raise Errors::FatalError, "#{file}: Invalid YAML contents. Was expecting #{validate_type}, but received #{yaml.class}"
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|