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,110 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "xxx_rename/contract/file_rename_op_contract"
|
4
|
+
require "xxx_rename/data/file_rename_op_datastore"
|
5
|
+
require "xxx_rename/site_client_matcher"
|
6
|
+
require "xxx_rename/actions/resolver"
|
7
|
+
|
8
|
+
module XxxRename
|
9
|
+
class Client
|
10
|
+
#
|
11
|
+
# Rename file(s) in the current directory
|
12
|
+
#
|
13
|
+
# @param [Data::Config] config
|
14
|
+
# @param [Boolean] verbose
|
15
|
+
# @param [Symbol] override_site
|
16
|
+
# @param [Boolean] nested
|
17
|
+
# @param [String, Nil] checkpoint
|
18
|
+
def initialize(config, verbose:, override_site: nil, nested: false, checkpoint: nil)
|
19
|
+
@config = config
|
20
|
+
@override_site = override_site
|
21
|
+
@nested = nested
|
22
|
+
@checkpoint = checkpoint
|
23
|
+
@checkpoint_reached = false
|
24
|
+
XxxRename.logger(verbose: verbose)
|
25
|
+
end
|
26
|
+
|
27
|
+
def generate(object, &block)
|
28
|
+
@object = object
|
29
|
+
@custom_action = block
|
30
|
+
if File.directory?(object)
|
31
|
+
process_directory
|
32
|
+
return
|
33
|
+
end
|
34
|
+
|
35
|
+
if File.file?(object)
|
36
|
+
process_file(object)
|
37
|
+
return
|
38
|
+
end
|
39
|
+
|
40
|
+
raise Errors::FatalError, "[UNKNOWN OBJECT #{object}] pass a valid file or directory"
|
41
|
+
end
|
42
|
+
|
43
|
+
def matcher
|
44
|
+
@matcher ||=
|
45
|
+
begin
|
46
|
+
m = SiteClientMatcher.new(config, override_site: override_site)
|
47
|
+
ActorsHelper.instance.matcher(m)
|
48
|
+
m
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def resolver
|
53
|
+
@resolver ||= Actions::Resolver.new(config)
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
attr_reader :config, :override_site, :nested, :object
|
59
|
+
|
60
|
+
def process_directory
|
61
|
+
scanner.each do |file|
|
62
|
+
if @checkpoint
|
63
|
+
if @checkpoint != file && !@checkpoint_reached
|
64
|
+
XxxRename.logger.debug "[SKIP CHECKPOINT] #{file}"
|
65
|
+
next
|
66
|
+
elsif @checkpoint == file
|
67
|
+
XxxRename.logger.info "[REACHED CHECKPOINT] #{file}"
|
68
|
+
@checkpoint_reached = true
|
69
|
+
end
|
70
|
+
end
|
71
|
+
process_file(file)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def process_file(file)
|
76
|
+
path = relative_path(file)
|
77
|
+
file = File.basename(file)
|
78
|
+
|
79
|
+
Dir.chdir(path) do
|
80
|
+
XxxRename.logger.info "#{"[FILE SCAN]".colorize(:blue)} #{file}"
|
81
|
+
search_engine.search(file) do |search_result|
|
82
|
+
next if search_result&.empty?
|
83
|
+
|
84
|
+
config.actions.each do |action_str|
|
85
|
+
action = resolver.resolve!(action_str)
|
86
|
+
action.perform(Dir.pwd, file, search_result)
|
87
|
+
end
|
88
|
+
|
89
|
+
next unless @custom_action
|
90
|
+
|
91
|
+
@custom_action.call(Dir.pwd, file, search_result)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def search_engine
|
97
|
+
@search_engine ||= Search.new(matcher, config.scene_datastore, config.force_refresh)
|
98
|
+
end
|
99
|
+
|
100
|
+
def scanner
|
101
|
+
@scanner ||= FileScanner.new(@object, nested: @nested)
|
102
|
+
end
|
103
|
+
|
104
|
+
def relative_path(file)
|
105
|
+
return Dir.pwd if File.basename(file) == file
|
106
|
+
|
107
|
+
file.gsub(File.basename(file), "")
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module XxxRename
|
4
|
+
module Constants
|
5
|
+
VIDEO_EXTENSIONS = %w[m4v mp4 mov wmv avi mpg mpeg rmvb rm flv asf mkv webm].to_set.freeze
|
6
|
+
|
7
|
+
DEFAULT_HEADERS = {
|
8
|
+
"User-Agent" => "Mozilla/5.0 (platform; rv:geckoversion) Gecko/geckotrail Firefox/firefoxversion"
|
9
|
+
}.freeze
|
10
|
+
|
11
|
+
EVIL_ANGEL_ORIGINAL_FILE_PATTERN = /
|
12
|
+
^ # Start of filename
|
13
|
+
(?<title>[a-zA-z0-9-]+) # Name of movie
|
14
|
+
_s(?<index>\d{1,2}) # Scene index e.g. s01
|
15
|
+
_ # Underscore
|
16
|
+
(?<actors>[a-zA-Z_]+) # Actors in format ActorName,ActorName
|
17
|
+
_\d{3,4}p # Scene Resolution
|
18
|
+
\.\w+ # File extension
|
19
|
+
$ # End of filename
|
20
|
+
/x.freeze
|
21
|
+
|
22
|
+
GOODPORN_ORIGINAL_FILE_FORMAT =
|
23
|
+
/
|
24
|
+
^ # Start of filename
|
25
|
+
[a-z0-9-]+ # Collection + Title
|
26
|
+
- # Hyphen
|
27
|
+
\d{2}-\d{2}-\d{4} # Release Date
|
28
|
+
(_(480|720|1080)p)? # Scene Resolution
|
29
|
+
\.\w+ # File extension
|
30
|
+
$ # End of filename
|
31
|
+
/x.freeze
|
32
|
+
|
33
|
+
MG_PREMIUM_ORIGINAL_FILE_FORMAT =
|
34
|
+
/
|
35
|
+
^ # Start of filename
|
36
|
+
(?<title>[a-z\-\d]+).*(?<!\d{2}-\d{2}-\d{4}) # Scene Title (should not contain a date format \d{2}-\d{2}-\d{4})
|
37
|
+
_ # Underscore
|
38
|
+
\d{3,4}p # Resolution
|
39
|
+
\.\w{3,4} # File extension
|
40
|
+
$ # End of filename
|
41
|
+
/x.freeze
|
42
|
+
|
43
|
+
NAUGHTY_AMERICA_ORIGINAL_FILE_REGEX =
|
44
|
+
/
|
45
|
+
^ # Start of filename
|
46
|
+
(?<compressed_scene>[a-z1]+) # Scene Title
|
47
|
+
_ # Underscore
|
48
|
+
\d+\w+? # Resolution
|
49
|
+
\.\w{0,4} # File extension
|
50
|
+
$ # End of filename
|
51
|
+
/x.freeze
|
52
|
+
|
53
|
+
NF_BUSTY_ORIGINAL_FILE_REGEX =
|
54
|
+
/
|
55
|
+
^ # Start of filename
|
56
|
+
nfbusty_ # Prefix
|
57
|
+
(?<title>[a-z0-9_]+) # Scene Title
|
58
|
+
_ # Underscore
|
59
|
+
\d{3,4} # Resolution
|
60
|
+
\.\w{0,4} # File extension
|
61
|
+
$ # End of filename
|
62
|
+
/x.freeze
|
63
|
+
|
64
|
+
# These regexes match the files downloaded originally from Vixen Media sites
|
65
|
+
VIXEN_MEDIA_SITES = %w[DEEPER VIXEN BLACKEDRAW BLACKED TUSHYRAW TUSHY CHANNELS SLAYED].join("|")
|
66
|
+
VIXEN_MEDIA_ORIGINAL_FILE_REGEX_1 = /(?<collection>(#{VIXEN_MEDIA_SITES}))_(?<id>\d*)_\d{3,4}P/x.freeze
|
67
|
+
VIXEN_MEDIA_ORIGINAL_FILE_REGEX_2 = /(?<collection>(#{VIXEN_MEDIA_SITES}))_(?<id>\d{6})-.*_\d{3,4}P/x.freeze
|
68
|
+
|
69
|
+
# rubocop:disable Lint/MixedRegexpCaptureTypes
|
70
|
+
WHALE_ORIGINAL_FILE_PATTERN =
|
71
|
+
/
|
72
|
+
^ # Start of filename
|
73
|
+
(?<site>(nannyspy|spyfam|holed|lubed|myveryfirsttime|tiny4k|povd|fantasyhd|castingcouchx|puremature|passionhd|exotic4k)) # Sitename
|
74
|
+
- # Hyphen
|
75
|
+
(?<title>[a-z0-9-]+) # Scene Title (might contain random id strings)
|
76
|
+
- # Hyphen separator for resolution
|
77
|
+
\d{3,4} # Resolution
|
78
|
+
\.\w+ # File extension
|
79
|
+
$ # End of filename
|
80
|
+
/x.freeze
|
81
|
+
# rubocop:enable Lint/MixedRegexpCaptureTypes
|
82
|
+
end
|
83
|
+
|
84
|
+
module SystemConstants
|
85
|
+
def home_dir
|
86
|
+
ENV["HOME"]
|
87
|
+
end
|
88
|
+
|
89
|
+
def config_file_lookup_dirs
|
90
|
+
[
|
91
|
+
File.join(home_dir, ".config", "xxx_rename"),
|
92
|
+
File.join(home_dir, "xxx_rename")
|
93
|
+
]
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
@@ -0,0 +1,241 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module XxxRename
|
4
|
+
module Contract
|
5
|
+
class ConfigContract < Dry::Validation::Contract
|
6
|
+
include FileUtilities
|
7
|
+
option :filename_generator
|
8
|
+
|
9
|
+
SITE_CONFIG = Dry::Schema.JSON do
|
10
|
+
required(:collection_tag).value(Types::SanitizedString)
|
11
|
+
required(:output_format).value(array[Types::SanitizedString])
|
12
|
+
required(:file_source_format).value(array[Types::SanitizedString])
|
13
|
+
end
|
14
|
+
|
15
|
+
JULES_JORDAN_MEDIA_CONFIG = Dry::Schema.JSON do
|
16
|
+
required(:collection_tag).value(Types::SanitizedString)
|
17
|
+
required(:output_format).value(array[Types::SanitizedString])
|
18
|
+
required(:file_source_format).value(array[Types::SanitizedString])
|
19
|
+
optional(:cookie_file).maybe(Types::SanitizedString)
|
20
|
+
end
|
21
|
+
|
22
|
+
STASH_CONFIG = Dry::Schema.JSON do
|
23
|
+
required(:collection_tag).value(Types::SanitizedString)
|
24
|
+
optional(:username).maybe(Types::SanitizedString)
|
25
|
+
optional(:password).maybe(Types::SanitizedString)
|
26
|
+
optional(:api_token).maybe(Types::SanitizedString)
|
27
|
+
required(:output_format).value(array[Types::SanitizedString])
|
28
|
+
required(:file_source_format).value(array[Types::SanitizedString])
|
29
|
+
end
|
30
|
+
|
31
|
+
NAUGHTY_AMERICA_CONFIG = Dry::Schema.JSON do
|
32
|
+
# if database is not provided, it will be created automatically
|
33
|
+
optional(:database).filled(Types::SanitizedString)
|
34
|
+
required(:collection_tag).value(Types::SanitizedString)
|
35
|
+
required(:output_format).value(array[Types::SanitizedString])
|
36
|
+
required(:file_source_format).value(array[Types::SanitizedString])
|
37
|
+
end
|
38
|
+
|
39
|
+
NF_BUSTY_CONFIG = Dry::Schema.JSON do
|
40
|
+
# if database is not provided, it will be created automatically
|
41
|
+
optional(:database).filled(Types::SanitizedString)
|
42
|
+
required(:collection_tag).value(Types::SanitizedString)
|
43
|
+
required(:output_format).value(array[Types::SanitizedString])
|
44
|
+
required(:file_source_format).value(array[Types::SanitizedString])
|
45
|
+
end
|
46
|
+
|
47
|
+
# rubocop:disable Metrics/BlockLength
|
48
|
+
json do
|
49
|
+
required(:generated_files_dir).filled(:string)
|
50
|
+
optional(:force_refresh_datastore).filled(Types::Bool)
|
51
|
+
optional(:force_refresh).filled(Types::Bool)
|
52
|
+
optional(:actions).value(Types::Array.of(Types::String))
|
53
|
+
optional(:override_site).value(Types::String)
|
54
|
+
|
55
|
+
optional(:stash_app).hash do
|
56
|
+
optional(:url).value(Types::SanitizedString)
|
57
|
+
optional(:api_token).maybe(Types::SanitizedString)
|
58
|
+
end
|
59
|
+
|
60
|
+
required(:global).hash do
|
61
|
+
optional(:female_actors_prefix).filled(Types::SanitizedString)
|
62
|
+
optional(:male_actors_prefix).filled(Types::SanitizedString)
|
63
|
+
optional(:actors_prefix).filled(Types::SanitizedString)
|
64
|
+
optional(:title_prefix).filled(Types::SanitizedString)
|
65
|
+
optional(:id_prefix).filled(Types::SanitizedString)
|
66
|
+
optional(:output_format).value(array[Types::SanitizedString])
|
67
|
+
end
|
68
|
+
|
69
|
+
required(:site).hash do
|
70
|
+
optional(:adult_time).hash(SITE_CONFIG)
|
71
|
+
optional(:babes).hash(SITE_CONFIG)
|
72
|
+
optional(:blacked).hash(SITE_CONFIG)
|
73
|
+
optional(:blacked_raw).hash(SITE_CONFIG)
|
74
|
+
optional(:brazzers).hash(SITE_CONFIG)
|
75
|
+
optional(:digital_playground).hash(SITE_CONFIG)
|
76
|
+
optional(:elegant_angel).hash(SITE_CONFIG)
|
77
|
+
optional(:evil_angel).hash(SITE_CONFIG)
|
78
|
+
optional(:goodporn).hash(SITE_CONFIG)
|
79
|
+
optional(:jules_jordan).hash(JULES_JORDAN_MEDIA_CONFIG)
|
80
|
+
optional(:manuel_ferrara).hash(JULES_JORDAN_MEDIA_CONFIG)
|
81
|
+
optional(:mofos).hash(SITE_CONFIG)
|
82
|
+
optional(:naughty_america).hash(NAUGHTY_AMERICA_CONFIG)
|
83
|
+
optional(:nf_busty).hash(NF_BUSTY_CONFIG)
|
84
|
+
optional(:reality_kings).hash(SITE_CONFIG)
|
85
|
+
optional(:stash).hash(STASH_CONFIG)
|
86
|
+
optional(:tushy).hash(SITE_CONFIG)
|
87
|
+
optional(:tushy_raw).hash(SITE_CONFIG)
|
88
|
+
optional(:twistys).hash(SITE_CONFIG)
|
89
|
+
optional(:vixen).hash(SITE_CONFIG)
|
90
|
+
optional(:whale_media).hash(SITE_CONFIG)
|
91
|
+
optional(:wicked).hash(SITE_CONFIG)
|
92
|
+
optional(:x_empire).hash(SITE_CONFIG)
|
93
|
+
optional(:zero_tolerance).hash(SITE_CONFIG)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
# rubocop:enable Metrics/BlockLength
|
97
|
+
|
98
|
+
rule(:generated_files_dir) do
|
99
|
+
key.failure("does not exist or is not readable") unless valid_dir?(value)
|
100
|
+
end
|
101
|
+
|
102
|
+
rule("global.female_actors_prefix") { key.failure(INVALID_PREFIX_MSG) unless prefix_valid?(value) }
|
103
|
+
rule("global.male_actors_prefix") { key.failure(INVALID_PREFIX_MSG) unless prefix_valid?(value) }
|
104
|
+
rule("global.actors_prefix") { key.failure(INVALID_PREFIX_MSG) unless prefix_valid?(value) }
|
105
|
+
rule("global.title_prefix") { key.failure(INVALID_PREFIX_MSG) unless prefix_valid?(value) }
|
106
|
+
rule("global.id_prefix") { key.failure(INVALID_PREFIX_MSG) unless prefix_valid?(value) }
|
107
|
+
|
108
|
+
# Validate output format
|
109
|
+
rule("global.output_format") { validate_format!(key, value) }
|
110
|
+
rule("site.adult_time.output_format") { validate_format!(key, value) }
|
111
|
+
rule("site.babes.output_format") { validate_format!(key, value) }
|
112
|
+
rule("site.blacked.output_format") { validate_format!(key, value) }
|
113
|
+
rule("site.blacked_raw.output_format") { validate_format!(key, value) }
|
114
|
+
rule("site.brazzers.output_format") { validate_format!(key, value) }
|
115
|
+
rule("site.digital_playground.output_format") { validate_format!(key, value) }
|
116
|
+
rule("site.elegant_angel.output_format") { validate_format!(key, value) }
|
117
|
+
rule("site.evil_angel.output_format") { validate_format!(key, value) }
|
118
|
+
rule("site.jules_jordan.output_format") { validate_format!(key, value) }
|
119
|
+
rule("site.manuel_ferrara.output_format") { validate_format!(key, value) }
|
120
|
+
rule("site.mofos.output_format") { validate_format!(key, value) }
|
121
|
+
rule("site.naughty_america.output_format") { validate_format!(key, value) }
|
122
|
+
rule("site.nf_busty.output_format") { validate_format!(key, value) }
|
123
|
+
rule("site.reality_kings.output_format") { validate_format!(key, value) }
|
124
|
+
rule("site.stash.output_format") { validate_format!(key, value) }
|
125
|
+
rule("site.tushy.output_format") { validate_format!(key, value) }
|
126
|
+
rule("site.tushy_raw.output_format") { validate_format!(key, value) }
|
127
|
+
rule("site.twistys.output_format") { validate_format!(key, value) }
|
128
|
+
rule("site.vixen.output_format") { validate_format!(key, value) }
|
129
|
+
rule("site.whale_media.output_format") { validate_format!(key, value) }
|
130
|
+
rule("site.wicked.output_format") { validate_format!(key, value) }
|
131
|
+
rule("site.x_empire.output_format") { validate_format!(key, value) }
|
132
|
+
rule("site.zero_tolerance.output_format") { validate_format!(key, value) }
|
133
|
+
|
134
|
+
rule("site.adult_time.file_source_format") { validate_source_format!(key, value) }
|
135
|
+
rule("site.babes.file_source_format") { validate_source_format!(key, value) }
|
136
|
+
rule("site.blacked.file_source_format") { validate_source_format!(key, value) }
|
137
|
+
rule("site.blacked_raw.file_source_format") { validate_source_format!(key, value) }
|
138
|
+
rule("site.brazzers.file_source_format") { validate_source_format!(key, value) }
|
139
|
+
rule("site.digital_playground.file_source_format") { validate_source_format!(key, value) }
|
140
|
+
rule("site.elegant_angel.file_source_format") { validate_source_format!(key, value) }
|
141
|
+
rule("site.evil_angel.file_source_format") { validate_source_format!(key, value) }
|
142
|
+
rule("site.mofos.file_source_format") { validate_source_format!(key, value) }
|
143
|
+
rule("site.jules_jordan.file_source_format") { validate_source_format!(key, value) }
|
144
|
+
rule("site.manuel_ferrara.file_source_format") { validate_source_format!(key, value) }
|
145
|
+
rule("site.naughty_america.file_source_format") { validate_source_format!(key, value) }
|
146
|
+
rule("site.nf_busty.file_source_format") { validate_source_format!(key, value) }
|
147
|
+
rule("site.reality_kings.file_source_format") { validate_source_format!(key, value) }
|
148
|
+
rule("site.stash.file_source_format") { validate_source_format!(key, value) }
|
149
|
+
rule("site.tushy.file_source_format") { validate_source_format!(key, value) }
|
150
|
+
rule("site.tushy_raw.file_source_format") { validate_source_format!(key, value) }
|
151
|
+
rule("site.twistys.file_source_format") { validate_source_format!(key, value) }
|
152
|
+
rule("site.vixen.file_source_format") { validate_source_format!(key, value) }
|
153
|
+
rule("site.whale_media.file_source_format") { validate_source_format!(key, value) }
|
154
|
+
rule("site.wicked.file_source_format") { validate_source_format!(key, value) }
|
155
|
+
rule("site.x_empire.file_source_format") { validate_source_format!(key, value) }
|
156
|
+
rule("site.zero_tolerance.file_source_format") { validate_source_format!(key, value) }
|
157
|
+
rule("site.adult_time.file_source_format",
|
158
|
+
"site.babes.file_source_format",
|
159
|
+
"site.blacked.file_source_format",
|
160
|
+
"site.blacked_raw.file_source_format",
|
161
|
+
"site.brazzers.file_source_format",
|
162
|
+
"site.digital_playground.file_source_format",
|
163
|
+
"site.elegant_angel.file_source_format",
|
164
|
+
"site.evil_angel.file_source_format",
|
165
|
+
"site.jules_jordan.file_source_format",
|
166
|
+
"site.manuel_ferrara.file_source_format",
|
167
|
+
"site.mofos.file_source_format",
|
168
|
+
"site.naughty_america.file_source_format",
|
169
|
+
"site.nf_busty.file_source_format",
|
170
|
+
"site.reality_kings.file_source_format",
|
171
|
+
"site.stash.file_source_format",
|
172
|
+
"site.tushy.file_source_format",
|
173
|
+
"site.tushy_raw.file_source_format",
|
174
|
+
"site.twistys.file_source_format",
|
175
|
+
"site.vixen.file_source_format",
|
176
|
+
"site.whale_media.file_source_format",
|
177
|
+
"site.wicked.file_source_format",
|
178
|
+
"site.x_empire.file_source_format",
|
179
|
+
"site.zero_tolerance.file_source_format") { validate_uniqueness!(key(:duplicate_source_file_format), values) }
|
180
|
+
|
181
|
+
rule("site.stash.username", "site.stash.password") do
|
182
|
+
next unless key?("site.stash.username") && key?("site.stash.password")
|
183
|
+
|
184
|
+
presence_arr = [values["site.stash.username"], values["site.stash.password"]].map(&:to_s).map(&:presence)
|
185
|
+
if presence_arr.all?(NIL)
|
186
|
+
true
|
187
|
+
elsif presence_arr.none?(NIL)
|
188
|
+
true
|
189
|
+
else
|
190
|
+
key(:stash_credentials).failure("provide both username and password if you want to use login credentials")
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
private
|
195
|
+
|
196
|
+
INVALID_PREFIX_MSG = "should only contain A-Z a-z 0-9 [ ] _ -"
|
197
|
+
INVALID_PREFIX_CHARACTERS = /[^\w\[\]-]/.freeze
|
198
|
+
|
199
|
+
def prefix_valid?(prefix)
|
200
|
+
!INVALID_PREFIX_CHARACTERS.match?(prefix)
|
201
|
+
end
|
202
|
+
|
203
|
+
def validate_format!(key, value)
|
204
|
+
value.each { |format| filename_generator.validate_format!(format) }
|
205
|
+
true
|
206
|
+
rescue InvalidFormatError => e
|
207
|
+
key.failure(e.message)
|
208
|
+
end
|
209
|
+
|
210
|
+
def validate_source_format!(key, value)
|
211
|
+
value.each { |format| filename_generator.validate_input_format!(format) }
|
212
|
+
true
|
213
|
+
rescue InvalidFormatError => e
|
214
|
+
key.failure(e.message)
|
215
|
+
end
|
216
|
+
|
217
|
+
def validate_uniqueness!(key, values)
|
218
|
+
source_file_formats = []
|
219
|
+
values[:site].each_value { |x| source_file_formats.push(*x[:file_source_format]) }
|
220
|
+
duplicates = find_duplicates(source_file_formats)
|
221
|
+
return true if duplicates.empty?
|
222
|
+
|
223
|
+
key.failure(duplicates.map { |x| "'#{x}'" }.join(", "))
|
224
|
+
end
|
225
|
+
|
226
|
+
def find_duplicates(ary)
|
227
|
+
frequency = {}
|
228
|
+
ary.map do |x|
|
229
|
+
frequency[x] = if frequency.key?(x)
|
230
|
+
frequency[x] + 1
|
231
|
+
else
|
232
|
+
1
|
233
|
+
end
|
234
|
+
end
|
235
|
+
resp = []
|
236
|
+
frequency.each_pair { |key, value| resp << key if value > 1 }
|
237
|
+
resp
|
238
|
+
end
|
239
|
+
end
|
240
|
+
end
|
241
|
+
end
|
@@ -0,0 +1,207 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "xxx_rename/constants"
|
4
|
+
require "fileutils"
|
5
|
+
|
6
|
+
module XxxRename
|
7
|
+
module Contract
|
8
|
+
class ConfigGenerator
|
9
|
+
include XxxRename::FileUtilities
|
10
|
+
include SystemConstants
|
11
|
+
|
12
|
+
DEFAULT_CONFIG_FILE = "config.yml"
|
13
|
+
|
14
|
+
def initialize(options)
|
15
|
+
@options = options
|
16
|
+
generated_files_dir
|
17
|
+
end
|
18
|
+
|
19
|
+
# @return [Data::Config]
|
20
|
+
def generate!
|
21
|
+
config_hash = make_config_hash
|
22
|
+
valid_config = validate_download_filters!(config_hash)
|
23
|
+
Data::Config.new(valid_config)
|
24
|
+
end
|
25
|
+
|
26
|
+
# Step 1: Get the base configuration hash with default values
|
27
|
+
def default_config
|
28
|
+
{
|
29
|
+
"generated_files_dir" => generated_files_dir,
|
30
|
+
"stash_app" => {
|
31
|
+
"url" => "",
|
32
|
+
"api_token" => nil
|
33
|
+
},
|
34
|
+
"global" => {
|
35
|
+
"female_actors_prefix" => "[F]",
|
36
|
+
"male_actors_prefix" => "[M]",
|
37
|
+
"actors_prefix" => "[A]",
|
38
|
+
"title_prefix" => "[T]",
|
39
|
+
"id_prefix" => "[ID]",
|
40
|
+
"output_format" => [
|
41
|
+
# disable line length check for readability
|
42
|
+
# rubocop:disable Layout/LineLength
|
43
|
+
"%yyyy_mm_dd %id_prefix %id %title_prefix %title %collection_tag %collection %female_actors_prefix %female_actors %male_actors_prefix %male_actors",
|
44
|
+
"%yyyy_mm_dd %id_prefix %id %title_prefix %collection_tag %collection %actors_prefix %actors",
|
45
|
+
"%yyyy_mm_dd %title_prefix %title %collection_tag %collection %female_actors_prefix %female_actors %male_actors_prefix %male_actors",
|
46
|
+
"%yyyy_mm_dd %title_prefix %collection_tag %collection %actors_prefix %actors"
|
47
|
+
# rubocop:enable Layout/LineLength
|
48
|
+
]
|
49
|
+
},
|
50
|
+
"site" =>
|
51
|
+
{ "adult_time" =>
|
52
|
+
{ "output_format" => [], "file_source_format" => [], "collection_tag" => "AT" },
|
53
|
+
"babes" =>
|
54
|
+
{ "output_format" => [], "file_source_format" => [], "collection_tag" => "BA" },
|
55
|
+
"blacked" =>
|
56
|
+
{ "output_format" => [], "file_source_format" => [], "collection_tag" => "BL" },
|
57
|
+
"blacked_raw" =>
|
58
|
+
{ "output_format" => [], "file_source_format" => [], "collection_tag" => "BLR" },
|
59
|
+
"brazzers" =>
|
60
|
+
{ "output_format" => [], "file_source_format" => [], "collection_tag" => "BZ" },
|
61
|
+
"digital_playground" =>
|
62
|
+
{ "output_format" => [], "file_source_format" => [], "collection_tag" => "DP" },
|
63
|
+
"elegant_angel" =>
|
64
|
+
{ "output_format" => [], "file_source_format" => [], "collection_tag" => "EL" },
|
65
|
+
"evil_angel" =>
|
66
|
+
{ "output_format" => [], "file_source_format" => [], "collection_tag" => "EA" },
|
67
|
+
"goodporn" =>
|
68
|
+
{ "output_format" => [], "file_source_format" => [], "collection_tag" => "GP" },
|
69
|
+
"jules_jordan" =>
|
70
|
+
{ "output_format" => [],
|
71
|
+
"file_source_format" => [],
|
72
|
+
"collection_tag" => "JJ",
|
73
|
+
"cookie_file" => nil },
|
74
|
+
"manuel_ferrara" =>
|
75
|
+
{ "output_format" => [],
|
76
|
+
"file_source_format" => [],
|
77
|
+
"collection_tag" => "MNF",
|
78
|
+
"cookie_file" => nil },
|
79
|
+
"mofos" =>
|
80
|
+
{ "output_format" => [], "file_source_format" => [], "collection_tag" => "MF" },
|
81
|
+
"naughty_america" =>
|
82
|
+
{ "output_format" => [],
|
83
|
+
"file_source_format" => [],
|
84
|
+
"collection_tag" => "NA",
|
85
|
+
"database" => File.join(generated_files_dir, "naughtyamerica.store") },
|
86
|
+
"nf_busty" =>
|
87
|
+
{ "output_format" => [],
|
88
|
+
"file_source_format" => [],
|
89
|
+
"collection_tag" => "NF",
|
90
|
+
"database" => File.join(generated_files_dir, "nf_busty.store") },
|
91
|
+
"reality_kings" =>
|
92
|
+
{ "output_format" => [], "file_source_format" => [], "collection_tag" => "RK" },
|
93
|
+
"stash" =>
|
94
|
+
{ "output_format" => [],
|
95
|
+
"file_source_format" => [],
|
96
|
+
"username" => nil,
|
97
|
+
"password" => nil,
|
98
|
+
"api_token" => nil,
|
99
|
+
"collection_tag" => "ST" },
|
100
|
+
"tushy" =>
|
101
|
+
{ "output_format" => [], "file_source_format" => [], "collection_tag" => "TU" },
|
102
|
+
"tushy_raw" =>
|
103
|
+
{ "output_format" => [], "file_source_format" => [], "collection_tag" => "TUR" },
|
104
|
+
"twistys" =>
|
105
|
+
{ "output_format" => [], "file_source_format" => [], "collection_tag" => "TW" },
|
106
|
+
"vixen" =>
|
107
|
+
{ "output_format" => [], "file_source_format" => [], "collection_tag" => "VX" },
|
108
|
+
"whale_media" =>
|
109
|
+
{ "output_format" => [], "file_source_format" => [], "collection_tag" => "WH" },
|
110
|
+
"wicked" =>
|
111
|
+
{ "output_format" => [], "file_source_format" => [], "collection_tag" => "WI" },
|
112
|
+
"x_empire" =>
|
113
|
+
{ "output_format" => [], "file_source_format" => [], "collection_tag" => "XM" },
|
114
|
+
"zero_tolerance" =>
|
115
|
+
{ "output_format" => [], "file_source_format" => [], "collection_tag" => "WI" } }
|
116
|
+
}
|
117
|
+
end
|
118
|
+
|
119
|
+
private
|
120
|
+
|
121
|
+
attr_reader :options, :yaml
|
122
|
+
|
123
|
+
def make_config_hash
|
124
|
+
config_file = read_config_file_or_abort!
|
125
|
+
override_from_file = config_file.deeper_merge(default_config)
|
126
|
+
override_from_options = override_flags_from_options(override_from_file)
|
127
|
+
override_from_options.deeper_merge(override_from_file)
|
128
|
+
end
|
129
|
+
|
130
|
+
def generated_files_dir
|
131
|
+
@generated_files_dir ||=
|
132
|
+
begin
|
133
|
+
dir = File.join(home_dir, ".config", "xxx_rename", "generated")
|
134
|
+
FileUtils.mkpath(dir)
|
135
|
+
File.expand_path(dir)
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
def save_default_config
|
140
|
+
file = File.join(config_file_lookup_dirs[0], DEFAULT_CONFIG_FILE)
|
141
|
+
|
142
|
+
XxxRename.logger.info "-" * 100
|
143
|
+
XxxRename.logger.info "Config option not passed to app and no config file detected in the current directory."
|
144
|
+
XxxRename.logger.info "Generating a blank configuration file to #{file} This app will now exit."
|
145
|
+
XxxRename.logger.info "Check the contents of the file and run the app again to start downloading."
|
146
|
+
XxxRename.logger.info "-" * 100
|
147
|
+
|
148
|
+
raise Errors::FatalError, "config file already exists" if valid_file?(file)
|
149
|
+
|
150
|
+
File.open(file, "w") do |f|
|
151
|
+
f.write default_config.to_yaml
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
# Step 2: Get the configuration hash from the config file, if present
|
156
|
+
# This method will raise an error if no config file is present,
|
157
|
+
# which is intentional, to allow users to create a config file automatically
|
158
|
+
def read_config_file_or_abort!
|
159
|
+
return read_yaml!(options["config"], nil) if options["config"]
|
160
|
+
|
161
|
+
config_file_lookup_dirs.each do |dir|
|
162
|
+
file = File.join(dir, DEFAULT_CONFIG_FILE)
|
163
|
+
next unless valid_file?(file)
|
164
|
+
|
165
|
+
contents = read_yaml!(file, nil) || {}
|
166
|
+
return contents
|
167
|
+
end
|
168
|
+
|
169
|
+
save_default_config
|
170
|
+
raise XxxRename::Errors::SafeExit, "DEFAULT_FILE_GENERATION"
|
171
|
+
end
|
172
|
+
|
173
|
+
# Step 3: Override any configuration with the options passed to the CLI
|
174
|
+
def override_flags_from_options(config)
|
175
|
+
{}.tap do |h|
|
176
|
+
h["override_site"] = options["override_site"] if options["override_site"]
|
177
|
+
h["global"] = config["global"].merge(**overridden_globals(config["global"]))
|
178
|
+
h["force_refresh_datastore"] = options["force_refresh_datastore"] || false
|
179
|
+
h["actions"] = options["actions"] || []
|
180
|
+
h["force_refresh"] = options["force_refresh"] || false
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
def overridden_globals(hash)
|
185
|
+
{
|
186
|
+
"female_actors_prefix" => override_value(hash["female_actors_prefix"], options["female_actors_prefix"]),
|
187
|
+
"male_actors_prefix" => override_value(hash["male_actors_prefix"], options["male_actors_prefix"]),
|
188
|
+
"actors_prefix" => override_value(hash["actors_prefix"], options["actors_prefix"]),
|
189
|
+
"title_prefix" => override_value(hash["title_prefix"], options["title_prefix"]),
|
190
|
+
"id_prefix" => override_value(hash["id_prefix"], options["id_prefix"]),
|
191
|
+
"collection_prefix" => override_value(hash["collection_prefix"], options["collection_prefix"])
|
192
|
+
}.compact
|
193
|
+
end
|
194
|
+
|
195
|
+
def validate_download_filters!(hash)
|
196
|
+
contract = Contract::ConfigContract.new(filename_generator: FilenameGenerator).call(hash)
|
197
|
+
raise XxxRename::Errors::ConfigValidationError, contract.errors unless contract.errors.empty?
|
198
|
+
|
199
|
+
contract.to_h.transform_keys(&:to_s)
|
200
|
+
end
|
201
|
+
|
202
|
+
def override_value(original, override)
|
203
|
+
override.nil? ? original : override
|
204
|
+
end
|
205
|
+
end
|
206
|
+
end
|
207
|
+
end
|