cross-seed 5.9.2 → 6.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.
- package/README.md +7 -1
- package/dist/Result.js +2 -0
- package/dist/Result.js.map +1 -1
- package/dist/action.js +127 -62
- package/dist/action.js.map +1 -1
- package/dist/auth.js +5 -5
- package/dist/auth.js.map +1 -1
- package/dist/clients/Deluge.js +104 -51
- package/dist/clients/Deluge.js.map +1 -1
- package/dist/clients/QBittorrent.js +205 -122
- package/dist/clients/QBittorrent.js.map +1 -1
- package/dist/clients/RTorrent.js +57 -23
- package/dist/clients/RTorrent.js.map +1 -1
- package/dist/clients/TorrentClient.js.map +1 -1
- package/dist/clients/Transmission.js +17 -9
- package/dist/clients/Transmission.js.map +1 -1
- package/dist/cmd.js +89 -77
- package/dist/cmd.js.map +1 -1
- package/dist/config.template.cjs +237 -144
- package/dist/config.template.cjs.map +1 -1
- package/dist/configSchema.js +177 -0
- package/dist/configSchema.js.map +1 -0
- package/dist/configuration.js +35 -9
- package/dist/configuration.js.map +1 -1
- package/dist/constants.js +28 -12
- package/dist/constants.js.map +1 -1
- package/dist/dataFiles.js +17 -8
- package/dist/dataFiles.js.map +1 -1
- package/dist/decide.js +118 -28
- package/dist/decide.js.map +1 -1
- package/dist/diff.js.map +1 -1
- package/dist/errors.js.map +1 -1
- package/dist/indexers.js +3 -3
- package/dist/indexers.js.map +1 -1
- package/dist/jobs.js +15 -7
- package/dist/jobs.js.map +1 -1
- package/dist/logger.js +9 -9
- package/dist/logger.js.map +1 -1
- package/dist/migrations/00-initialSchema.js.map +1 -1
- package/dist/migrations/02-timestamps.js +3 -1
- package/dist/migrations/02-timestamps.js.map +1 -1
- package/dist/parseTorrent.js +11 -2
- package/dist/parseTorrent.js.map +1 -1
- package/dist/pipeline.js +34 -25
- package/dist/pipeline.js.map +1 -1
- package/dist/preFilter.js +17 -6
- package/dist/preFilter.js.map +1 -1
- package/dist/pushNotifier.js +29 -10
- package/dist/pushNotifier.js.map +1 -1
- package/dist/searchee.js +8 -1
- package/dist/searchee.js.map +1 -1
- package/dist/server.js.map +1 -1
- package/dist/startup.js +62 -23
- package/dist/startup.js.map +1 -1
- package/dist/torrent.js +14 -29
- package/dist/torrent.js.map +1 -1
- package/dist/torznab.js +56 -32
- package/dist/torznab.js.map +1 -1
- package/dist/utils.js +42 -5
- package/dist/utils.js.map +1 -1
- package/package.json +12 -12
- package/dist/config.template.docker.cjs +0 -236
- package/dist/config.template.docker.cjs.map +0 -1
package/dist/config.template.cjs
CHANGED
@@ -1,231 +1,324 @@
|
|
1
1
|
"use strict";
|
2
|
-
// If you find yourself always using the same command-line flag, you can set
|
3
|
-
//
|
2
|
+
// If you find yourself always using the same command-line flag, you can set it
|
3
|
+
// here as a default.
|
4
4
|
Object.defineProperty(exports, "__esModule", { value: true });
|
5
5
|
module.exports = {
|
6
6
|
/**
|
7
|
-
*
|
8
|
-
*
|
7
|
+
* WARNING! WARNING! WARNING! WARNING! WARNING! WARNING! WARNING!
|
8
|
+
*
|
9
|
+
* THE NEXT 8 OPTIONS CONTAIN POTENTIALLY SENSITIVE INFORMATION
|
10
|
+
* THERE IS A NOTE WHERE YOU WILL WANT TO START COPYING FROM
|
11
|
+
* IF YOU ARE TRYING TO SHARE YOUR CONFIGURATION SETTINGS!
|
12
|
+
*
|
13
|
+
* WARNING! WARNING! WARNING! WARNING! WARNING! WARNING! WARNING!
|
14
|
+
**/
|
15
|
+
/**
|
16
|
+
* Provide your own API key here to override the autogenerated one.
|
17
|
+
* Not recommended - prefer using the autogenerated API key via
|
18
|
+
* `cross-seed api-key`.
|
19
|
+
* Must be 24+ characters.
|
9
20
|
*/
|
10
|
-
|
21
|
+
apiKey: undefined,
|
11
22
|
/**
|
12
23
|
* List of Torznab URLs.
|
13
|
-
* For Jackett, click "Copy RSS feed"
|
14
|
-
* For Prowlarr, click on the indexer name and copy the Torznab Url, then
|
15
|
-
* Wrap each URL in quotation marks
|
24
|
+
* For Jackett, click "Copy RSS feed".
|
25
|
+
* For Prowlarr, click on the indexer name and copy the Torznab Url, then
|
26
|
+
* append "?apikey=YOUR_PROWLARR_API_KEY". Wrap each URL in quotation marks
|
27
|
+
* and separate them with commas, and surround the entire set in brackets.
|
16
28
|
*/
|
17
29
|
torznab: [],
|
18
30
|
/**
|
19
|
-
*
|
20
|
-
*
|
21
|
-
*
|
22
|
-
* If enabled, this needs to be surrounded by brackets. Windows users will need to use
|
23
|
-
* double backslash in all paths in this config.
|
24
|
-
* e.g.
|
25
|
-
* dataDirs: ["/path/here"],
|
26
|
-
* dataDirs: ["/path/here", "/other/path/here"],
|
27
|
-
* dataDirs: ["C:\\My Data\\Downloads"]
|
31
|
+
* Bind to a specific host address.
|
32
|
+
* Example: "127.0.0.1"
|
33
|
+
* Default is "0.0.0.0"
|
28
34
|
*/
|
29
|
-
|
35
|
+
host: undefined,
|
30
36
|
/**
|
31
|
-
*
|
32
|
-
* using the standard matching algorithm. "risky" uses filesize as its only comparison point.
|
33
|
-
* Options: "safe", "risky"
|
37
|
+
* The port you wish to listen on for daemon mode.
|
34
38
|
*/
|
35
|
-
|
39
|
+
port: 2468,
|
36
40
|
/**
|
37
|
-
*
|
38
|
-
*
|
41
|
+
* cross-seed will send POST requests to this url with a JSON payload of
|
42
|
+
* { title, body }. Conforms to the caronc/apprise REST API.
|
39
43
|
*/
|
40
|
-
|
44
|
+
notificationWebhookUrl: undefined,
|
41
45
|
/**
|
42
|
-
*
|
43
|
-
*
|
46
|
+
* The url of your rtorrent XMLRPC interface.
|
47
|
+
* Only relevant with action: "inject".
|
48
|
+
* Could be something like "http://username:password@localhost:1234/RPC2
|
44
49
|
*/
|
45
|
-
|
50
|
+
rtorrentRpcUrl: undefined,
|
46
51
|
/**
|
47
|
-
*
|
48
|
-
* Only relevant
|
49
|
-
*
|
52
|
+
* The url of your qBittorrent webui.
|
53
|
+
* Only relevant with action: "inject".
|
54
|
+
* Supply your username and password inside the url like so:
|
55
|
+
* "http://username:password@localhost:8080"
|
50
56
|
*/
|
51
|
-
|
57
|
+
qbittorrentUrl: undefined,
|
52
58
|
/**
|
53
|
-
*
|
54
|
-
*
|
55
|
-
* Only
|
59
|
+
* The url of your Transmission RPC interface.
|
60
|
+
* Usually ends with "/transmission/rpc".
|
61
|
+
* Only relevant with action: "inject".
|
62
|
+
* Supply your username and password inside the url like so:
|
63
|
+
* "http://username:password@localhost:9091/transmission/rpc"
|
56
64
|
*/
|
57
|
-
|
65
|
+
transmissionRpcUrl: undefined,
|
58
66
|
/**
|
59
|
-
*
|
60
|
-
*
|
61
|
-
*
|
67
|
+
* The url of your Deluge JSON-RPC interface.
|
68
|
+
* Usually ends with "/json".
|
69
|
+
* Only relevant with action: "inject".
|
70
|
+
* Supply your WebUI password as well like so:
|
71
|
+
* "http://:password@localhost:8112/json"
|
62
72
|
*/
|
63
|
-
|
73
|
+
delugeRpcUrl: undefined,
|
64
74
|
/**
|
65
|
-
*
|
66
|
-
* For qBittorrent, this is BT_Backup
|
67
|
-
* For rtorrent, this is your session directory
|
68
|
-
* as configured in your .rtorrent.rc file.
|
69
|
-
* For Deluge, this is ~/.config/deluge/state.
|
70
|
-
* For Transmission, this would be ~/.config/transmission/torrents
|
75
|
+
* END OF POTENTIALLY SENSITIVE CONFIGURATION OPTIONS
|
71
76
|
*/
|
72
|
-
torrentDir: "/path/to/torrent/file/dir",
|
73
77
|
/**
|
74
|
-
*
|
78
|
+
* Pause at least this many seconds in between each search. Higher is safer
|
79
|
+
* for you and friendlier for trackers.
|
80
|
+
* Minimum value of 10.
|
75
81
|
*/
|
76
|
-
|
82
|
+
delay: 30,
|
77
83
|
/**
|
78
|
-
*
|
84
|
+
* To search with already downloaded data, you can enter in the directories
|
85
|
+
* to your downloaded torrent data to find matches, rather than relying
|
86
|
+
* entirely on the .torrent files themselves for matching.
|
87
|
+
*
|
88
|
+
* If directories are entered, they must all be on the one line and they
|
89
|
+
* need to be surrounded by brackets.
|
90
|
+
* Windows users will need to use double backslash in all paths in this
|
91
|
+
* config.
|
92
|
+
*
|
93
|
+
* example:
|
94
|
+
* dataDirs: ["/downloads/movies", "/downloads/packs"],
|
95
|
+
* or for windows users
|
96
|
+
* dataDirs: ["C:\\My Data\\Downloads\\Movies"],
|
79
97
|
*/
|
80
|
-
|
98
|
+
dataDirs: [],
|
81
99
|
/**
|
82
|
-
*
|
83
|
-
*
|
100
|
+
* Defines what qBittorrent or Deluge category to set on linked torrents
|
101
|
+
* Default is "cross-seed-link".
|
102
|
+
*
|
103
|
+
* qBittorrent: If you have linking enabled, all torrents will be injected
|
104
|
+
* to this category.
|
105
|
+
*
|
106
|
+
* Default is "cross-seed-data".
|
84
107
|
*/
|
85
|
-
|
108
|
+
linkCategory: "cross-seed-link",
|
86
109
|
/**
|
87
|
-
*
|
88
|
-
*
|
110
|
+
* If this is specified, cross-seed will create links to matched files in
|
111
|
+
* the specified directory.
|
112
|
+
* It will create a different link for every changed file name or directory
|
113
|
+
* structure.
|
114
|
+
*
|
115
|
+
* Unlike dataDirs, this is just a quoted string WITHOUT []'s around it.
|
89
116
|
*
|
90
|
-
*
|
91
|
-
*
|
92
|
-
*
|
93
|
-
*
|
117
|
+
* If you are a Windows user you need to put double '\' (e.g. "C:\\links")
|
118
|
+
*
|
119
|
+
* IF YOU ARE USING HARDLINKS, THIS MUST BE UNDER THE SAME VOLUME AS YOUR
|
120
|
+
* DATADIRS. THIS PATH MUST ALSO BE ACCESSIBLE VIA YOUR TORRENT CLIENT
|
121
|
+
* USING THE SAME PATH.
|
94
122
|
*/
|
95
|
-
|
123
|
+
linkDir: undefined,
|
96
124
|
/**
|
97
|
-
*
|
98
|
-
*
|
125
|
+
* cross-seed will use links of this type to inject data-based matches into
|
126
|
+
* your client.
|
127
|
+
* https://www.cross-seed.org/docs/basics/faq-troubleshooting#what-linktype-should-i-use
|
128
|
+
* Options: "symlink", "hardlink".
|
99
129
|
*/
|
100
|
-
|
130
|
+
linkType: "hardlink",
|
101
131
|
/**
|
102
|
-
*
|
103
|
-
*
|
104
|
-
*
|
105
|
-
*
|
106
|
-
*
|
107
|
-
*
|
132
|
+
* Enabling this will link files using v5's flat folder style.
|
133
|
+
*
|
134
|
+
* Each individual Torznab tracker's cross-seeds, otherwise, will have it's
|
135
|
+
* own folder with the tracker's name and it's links within it.
|
136
|
+
*
|
137
|
+
* qBittorent: If you are using qBittorrent with Auto Torrent Management in any capacity
|
138
|
+
* it is recommended to enable this to avoid issues.
|
139
|
+
*
|
140
|
+
* Default: false.
|
108
141
|
*/
|
109
|
-
|
142
|
+
flatLinking: false,
|
110
143
|
/**
|
111
|
-
*
|
112
|
-
*
|
113
|
-
*
|
114
|
-
* "
|
115
|
-
* "
|
116
|
-
*
|
144
|
+
* Determines flexibility of naming during matching.
|
145
|
+
* "safe" will allow only perfect name/size matches using the standard
|
146
|
+
* matching algorithm.
|
147
|
+
* "risky" uses filesize as its only comparison point.
|
148
|
+
* "partial" is like risky but allows matches if they are missing small
|
149
|
+
* files like .nfo/.srt.
|
150
|
+
* Options: "safe", "risky", "partial".
|
117
151
|
*/
|
118
|
-
|
152
|
+
matchMode: "safe",
|
119
153
|
/**
|
120
|
-
*
|
121
|
-
*
|
154
|
+
* Determines how deep into the specified dataDirs to go to generate new
|
155
|
+
* searchees. Setting this to higher values will result in more searchees
|
156
|
+
* and more API hits to your indexers.
|
122
157
|
*/
|
123
|
-
|
158
|
+
maxDataDepth: 1,
|
124
159
|
/**
|
125
|
-
*
|
126
|
-
*
|
127
|
-
*
|
160
|
+
* Directory containing .torrent files.
|
161
|
+
* For qBittorrent, this is BT_Backup.
|
162
|
+
* For rtorrent, this is your session directory as configured in your
|
163
|
+
* .rtorrent.rc file.
|
164
|
+
* For Deluge, this is ~/.config/deluge/state.
|
165
|
+
* For Transmission, this would be ~/.config/transmission/torrents.
|
166
|
+
*
|
167
|
+
* If you are a Windows user you need to put double '\' (e.g. "C:\\torrents")
|
128
168
|
*/
|
129
|
-
|
169
|
+
torrentDir: "/path/to/torrent/file/dir",
|
130
170
|
/**
|
131
|
-
*
|
132
|
-
*
|
133
|
-
*
|
134
|
-
* "http://username:password@localhost:8080"
|
171
|
+
* Where to save the torrent files that cross-seed finds for you.
|
172
|
+
*
|
173
|
+
* If you are a Windows user you need to put double '\' (e.g. "C:\\output")
|
135
174
|
*/
|
136
|
-
|
175
|
+
outputDir: ".",
|
137
176
|
/**
|
138
|
-
*
|
139
|
-
*
|
140
|
-
*
|
141
|
-
* Supply your username and password inside the url like so:
|
142
|
-
* "http://username:password@localhost:9091/transmission/rpc"
|
177
|
+
* Whether to search for all episode torrents, including those from season
|
178
|
+
* packs.
|
179
|
+
* This option overrides includeSingleEpisodes when set to true.
|
143
180
|
*/
|
144
|
-
|
181
|
+
includeEpisodes: false,
|
145
182
|
/**
|
146
|
-
*
|
147
|
-
*
|
148
|
-
*
|
149
|
-
* Supply your WebUI password as well
|
150
|
-
* "http://:password@localhost:8112/json"
|
183
|
+
* Whether to include single episode torrents in the search (not those from
|
184
|
+
* season packs).
|
185
|
+
* Like `includeEpisodes` but slightly more restrictive.
|
151
186
|
*/
|
152
|
-
|
187
|
+
includeSingleEpisodes: false,
|
188
|
+
/**
|
189
|
+
* Include torrents which contain non-video files.
|
190
|
+
* This option does not override includeEpisodes or includeSingleEpisodes.
|
191
|
+
*
|
192
|
+
* If this option is set to false, any folders or torrents containing ANY
|
193
|
+
* non-video files will automatically be excluded from cross-seed searches.
|
194
|
+
*
|
195
|
+
* For example, if you have .srt or .nfo files inside your folders/torrents
|
196
|
+
* you would set this as true.
|
197
|
+
* For full disc based folders (not .ISO) you may wish to set this as true.
|
198
|
+
* You may also want to set this as false to exclude things like music,
|
199
|
+
* games, or books.
|
200
|
+
*
|
201
|
+
* To search for everything except episodes, use:
|
202
|
+
*
|
203
|
+
* includeEpisodes: false
|
204
|
+
* includeSingleEpisodes: false
|
205
|
+
* includeNonVideos: true
|
206
|
+
*
|
207
|
+
* To search for everything including episodes, use:
|
208
|
+
*
|
209
|
+
* includeEpisodes: true
|
210
|
+
* includeNonVideos: true
|
211
|
+
*
|
212
|
+
* To search for everything except season pack episodes (data-based) use:
|
213
|
+
*
|
214
|
+
* includeEpisodes: false
|
215
|
+
* includeSingleEpisodes: true
|
216
|
+
* includeNonVideos: true
|
217
|
+
*
|
218
|
+
*/
|
219
|
+
includeNonVideos: false,
|
153
220
|
/**
|
154
|
-
*
|
155
|
-
*
|
156
|
-
*
|
157
|
-
*
|
158
|
-
*
|
221
|
+
* You should NOT modify this unless you have good reason.
|
222
|
+
* The following option is the preliminary value to compare sizes of
|
223
|
+
* releases for further comparison.
|
224
|
+
*
|
225
|
+
* decimal value (0.02 = 2%)
|
159
226
|
*/
|
160
|
-
|
227
|
+
fuzzySizeThreshold: 0.02,
|
161
228
|
/**
|
162
|
-
*
|
163
|
-
*
|
164
|
-
* Conforms to the caronc/apprise REST API.
|
229
|
+
* Time based options below use the following format:
|
230
|
+
* https://github.com/vercel/ms
|
165
231
|
*/
|
166
|
-
notificationWebhookUrl: undefined,
|
167
232
|
/**
|
168
|
-
*
|
233
|
+
* Exclude torrents first seen by cross-seed more than this long ago.
|
234
|
+
* Examples:
|
235
|
+
* "10 minutes"
|
236
|
+
* "1 day"
|
237
|
+
* "0" - this will search everything exactly once, never more.
|
169
238
|
*/
|
170
|
-
|
239
|
+
excludeOlder: "2 weeks",
|
171
240
|
/**
|
172
|
-
*
|
173
|
-
*
|
174
|
-
*
|
241
|
+
* Exclude torrents which have been searched more recently than this long
|
242
|
+
* ago.
|
243
|
+
* Doesn't exclude previously failed searches.
|
244
|
+
* Examples:
|
245
|
+
* "2 days"
|
246
|
+
* "1 year"
|
175
247
|
*/
|
176
|
-
|
248
|
+
excludeRecentSearch: "3 days",
|
249
|
+
/**
|
250
|
+
* What action to take upon a match being found.
|
251
|
+
* Options: "save", "inject".
|
252
|
+
*/
|
253
|
+
action: "inject",
|
177
254
|
/**
|
178
|
-
*
|
179
|
-
*
|
180
|
-
*
|
255
|
+
* qBittorrent and Deluge specific.
|
256
|
+
* Whether to inject using the same labels/categories as the original
|
257
|
+
* torrent.
|
258
|
+
*
|
259
|
+
* qBittorrent: This will apply the category's original category as a tag.
|
260
|
+
*
|
261
|
+
* Example: if you have a original label/category called "Movies", this will
|
262
|
+
* automatically inject cross-seeds to "Movies.cross-seed".
|
181
263
|
*/
|
182
|
-
|
264
|
+
duplicateCategories: false,
|
183
265
|
/**
|
184
|
-
* Run rss scans on a schedule.
|
266
|
+
* Run rss scans on a schedule.
|
185
267
|
* Set to undefined or null to disable. Minimum of 10 minutes.
|
186
268
|
* Examples:
|
187
|
-
* "
|
188
|
-
* "
|
189
|
-
* "3 days"
|
269
|
+
* "10 minutes"
|
270
|
+
* "1 hour"
|
190
271
|
*/
|
191
|
-
rssCadence:
|
272
|
+
rssCadence: "30 minutes",
|
192
273
|
/**
|
193
|
-
* Run searches on a schedule.
|
274
|
+
* Run searches on a schedule.
|
194
275
|
* Set to undefined or null to disable. Minimum of 1 day.
|
195
|
-
* If you have RSS enabled, you won't need this to run often (2+ weeks recommended)
|
196
276
|
* Examples:
|
197
|
-
* "
|
198
|
-
* "2w"
|
277
|
+
* "2 weeks"
|
199
278
|
* "3 days"
|
200
279
|
*/
|
201
|
-
searchCadence:
|
280
|
+
searchCadence: "1 day",
|
202
281
|
/**
|
203
282
|
* Fail snatch requests that haven't responded after this long.
|
204
283
|
* Set to null for an infinite timeout.
|
205
|
-
* Format: https://github.com/vercel/ms
|
206
284
|
* Examples:
|
207
|
-
* "
|
208
|
-
* "10s"
|
209
|
-
* "1min"
|
285
|
+
* "30 seconds"
|
210
286
|
* null
|
211
287
|
*/
|
212
288
|
snatchTimeout: undefined,
|
213
289
|
/**
|
214
290
|
* Fail search requests that haven't responded after this long.
|
215
291
|
* Set to null for an infinite timeout.
|
216
|
-
* Format: https://github.com/vercel/ms
|
217
292
|
* Examples:
|
218
|
-
* "
|
219
|
-
* "10s"
|
220
|
-
* "1min"
|
293
|
+
* "30 seconds"
|
221
294
|
* null
|
222
295
|
*/
|
223
296
|
searchTimeout: undefined,
|
224
297
|
/**
|
225
|
-
* The number of searches to
|
226
|
-
*
|
227
|
-
*
|
298
|
+
* The number of searches to make in one run/batch.
|
299
|
+
* If more than this many searches are queued,
|
300
|
+
* "searchCadence" will determine how long until the next batch.
|
301
|
+
*
|
302
|
+
* Combine this with "excludeRecentSearch" and "searchCadence" to smooth
|
303
|
+
* long-term API usage patterns.
|
304
|
+
*
|
305
|
+
* Set to null for no limit.
|
306
|
+
*/
|
307
|
+
searchLimit: 100,
|
308
|
+
/**
|
309
|
+
* The list of infohashes or strings which are contained in torrents that
|
310
|
+
* you want to be excluded from cross-seed. This is the same format as
|
311
|
+
* torznab, surround the entire set of quoted strings in square brackets
|
312
|
+
* You can use any combination which must be entered on the one line.
|
313
|
+
* Leave as undefined to disable.
|
314
|
+
*
|
315
|
+
* examples:
|
316
|
+
*
|
317
|
+
* blockList: ["-excludedGroup", "-excludedGroup2"],
|
318
|
+
* blocklist: ["x265"],
|
319
|
+
* blocklist: ["Release.Name"],
|
320
|
+
* blocklist: ["3317e6485454354751555555366a8308c1e92093"],
|
228
321
|
*/
|
229
|
-
|
322
|
+
blockList: undefined,
|
230
323
|
};
|
231
324
|
//# sourceMappingURL=config.template.cjs.map
|
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"file":"config.template.cjs","sourceRoot":"","sources":["../src/config.template.cjs"],"names":[],"mappings":";AAAA
|
1
|
+
{"version":3,"file":"config.template.cjs","sourceRoot":"","sources":["../src/config.template.cjs"],"names":[],"mappings":";AAAA,+EAA+E;AAC/E,qBAAqB;;AAErB,MAAM,CAAC,OAAO,GAAG;IAChB;;;;;;;;QAQI;IAEJ;;;;;OAKG;IACH,MAAM,EAAE,SAAS;IAEjB;;;;;;OAMG;IACH,OAAO,EAAE,EAAE;IAEX;;;;OAIG;IACH,IAAI,EAAE,SAAS;IAEf;;OAEG;IACH,IAAI,EAAE,IAAI;IAEV;;;OAGG;IACH,sBAAsB,EAAE,SAAS;IAEjC;;;;OAIG;IACH,cAAc,EAAE,SAAS;IAEzB;;;;;OAKG;IACH,cAAc,EAAE,SAAS;IAEzB;;;;;;OAMG;IACH,kBAAkB,EAAE,SAAS;IAE7B;;;;;;OAMG;IACH,YAAY,EAAE,SAAS;IAEvB;;OAEG;IAEH;;;;OAIG;IAEH,KAAK,EAAE,EAAE;IAET;;;;;;;;;;;;;;OAcG;IAEH,QAAQ,EAAE,EAAE;IAEZ;;;;;;;;OAQG;IACH,YAAY,EAAE,iBAAiB;IAE/B;;;;;;;;;;;;;OAaG;IACH,OAAO,EAAE,SAAS;IAElB;;;;;OAKG;IACH,QAAQ,EAAE,UAAU;IAEpB;;;;;;;;;;OAUG;IACH,WAAW,EAAE,KAAK;IAElB;;;;;;;;OAQG;IACH,SAAS,EAAE,MAAM;IAEjB;;;;OAIG;IACH,YAAY,EAAE,CAAC;IAEf;;;;;;;;;OASG;IACH,UAAU,EAAE,2BAA2B;IAEvC;;;;OAIG;IACH,SAAS,EAAE,GAAG;IAEd;;;;OAIG;IACH,eAAe,EAAE,KAAK;IAEtB;;;;OAIG;IACH,qBAAqB,EAAE,KAAK;IAE5B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA8BG;IACH,gBAAgB,EAAE,KAAK;IAEvB;;;;;;OAMG;IACH,kBAAkB,EAAE,IAAI;IAExB;;;OAGG;IAEH;;;;;;OAMG;IACH,YAAY,EAAE,SAAS;IAEvB;;;;;;;OAOG;IACH,mBAAmB,EAAE,QAAQ;IAE7B;;;OAGG;IACH,MAAM,EAAE,QAAQ;IAEhB;;;;;;;;;OASG;IACH,mBAAmB,EAAE,KAAK;IAE1B;;;;;;OAMG;IACH,UAAU,EAAE,YAAY;IAExB;;;;;;OAMG;IACH,aAAa,EAAE,OAAO;IAEtB;;;;;;OAMG;IACH,aAAa,EAAE,SAAS;IAExB;;;;;;OAMG;IACH,aAAa,EAAE,SAAS;IAExB;;;;;;;;;OASG;IACH,WAAW,EAAE,GAAG;IAEhB;;;;;;;;;;;;;OAaG;IACH,SAAS,EAAE,SAAS;CACpB,CAAC"}
|
@@ -0,0 +1,177 @@
|
|
1
|
+
import ms from "ms";
|
2
|
+
import { sep } from "path";
|
3
|
+
import { z } from "zod";
|
4
|
+
import { Action, LinkType, MatchMode } from "./constants.js";
|
5
|
+
import { logger } from "./logger.js";
|
6
|
+
/**
|
7
|
+
* error messages and map returned upon Zod validation failure
|
8
|
+
*/
|
9
|
+
const ZodErrorMessages = {
|
10
|
+
vercel: "format does not follow vercel's `ms` style ( https://github.com/vercel/ms#examples )",
|
11
|
+
emptyString: "cannot have an empty string. If you want to unset it, use null or undefined.",
|
12
|
+
delay: "delay is in seconds, you can't travel back in time.",
|
13
|
+
fuzzySizeThreshold: "fuzzySizeThreshold must be between 0 and 1.",
|
14
|
+
injectUrl: "You need to specify rtorrentRpcUrl, transmissionRpcUrl, qbittorrentUrl, or delugeRpcUrl when using 'inject'",
|
15
|
+
windowsPath: `\t\t\tYour path is not formatted properly for Windows. \n\t\t\t\tPlease use "\\\\" or "/" for directory separators.`,
|
16
|
+
qBitAutoTMM: "Using Automatic Torrent Management in qBittorrent without flatLinking enabled can result in unintended behavior.",
|
17
|
+
needsLinkDir: "You need to set a linkDir (and have your data accessible) for risky or partial matching to work.",
|
18
|
+
};
|
19
|
+
/**
|
20
|
+
* custom zod error map for logging
|
21
|
+
* @param error ZodIssue messaging object
|
22
|
+
* @param ctx ZodError map
|
23
|
+
* @returns (the custom error for config display)
|
24
|
+
*/
|
25
|
+
export function customizeErrorMessage(error, ctx) {
|
26
|
+
switch (error.code) {
|
27
|
+
case z.ZodIssueCode.invalid_union:
|
28
|
+
return {
|
29
|
+
message: error.unionErrors
|
30
|
+
.reduce((acc, error) => {
|
31
|
+
error.errors.forEach((x) => acc.push(x.message));
|
32
|
+
return acc;
|
33
|
+
}, [])
|
34
|
+
.join("; "),
|
35
|
+
};
|
36
|
+
}
|
37
|
+
return { message: ctx.defaultError };
|
38
|
+
}
|
39
|
+
/**
|
40
|
+
* adds an issue in Zod's error mapped formatting
|
41
|
+
* @param setting the value of the setting
|
42
|
+
* @param errorMessage the error message to append on a newline
|
43
|
+
* @param ctx ZodError map
|
44
|
+
*/
|
45
|
+
function addZodIssue(setting, errorMessage, ctx) {
|
46
|
+
ctx.addIssue({
|
47
|
+
code: "custom",
|
48
|
+
message: `Setting: "${setting}"\n\t${errorMessage}`,
|
49
|
+
});
|
50
|
+
}
|
51
|
+
/**
|
52
|
+
* helper function for ms time validation
|
53
|
+
* @return transformed duration (string -> milliseconds)
|
54
|
+
*/
|
55
|
+
function transformDurationString(durationStr, ctx) {
|
56
|
+
const duration = ms(durationStr);
|
57
|
+
if (isNaN(duration)) {
|
58
|
+
// adds the error to the Zod Issues
|
59
|
+
addZodIssue(durationStr, ZodErrorMessages.vercel, ctx);
|
60
|
+
}
|
61
|
+
return duration;
|
62
|
+
}
|
63
|
+
/**
|
64
|
+
* helper function for directory validation
|
65
|
+
* @return path if valid formatting
|
66
|
+
*/
|
67
|
+
function checkValidPathFormat(path, ctx) {
|
68
|
+
if ((sep === "\\" && !path.includes(`\\`) && !path.includes("/")) ||
|
69
|
+
path === ".") {
|
70
|
+
addZodIssue(path, ZodErrorMessages.windowsPath, ctx);
|
71
|
+
}
|
72
|
+
return path;
|
73
|
+
}
|
74
|
+
/**
|
75
|
+
* an object of the zod schema
|
76
|
+
* each are named after what they are intended to validate
|
77
|
+
*/
|
78
|
+
export const VALIDATION_SCHEMA = z
|
79
|
+
.object({
|
80
|
+
delay: z.number().nonnegative({
|
81
|
+
message: ZodErrorMessages.delay,
|
82
|
+
}),
|
83
|
+
torznab: z.array(z.string().url()),
|
84
|
+
dataDirs: z
|
85
|
+
.array(z
|
86
|
+
.string()
|
87
|
+
.transform((value, ctx) => value && value.length > 0
|
88
|
+
? checkValidPathFormat(value, ctx)
|
89
|
+
: null))
|
90
|
+
.nullish(),
|
91
|
+
matchMode: z.nativeEnum(MatchMode),
|
92
|
+
linkCategory: z.string().nullish(),
|
93
|
+
linkDir: z.string().transform(checkValidPathFormat).nullish(),
|
94
|
+
linkType: z.nativeEnum(LinkType),
|
95
|
+
flatLinking: z
|
96
|
+
.boolean()
|
97
|
+
.nullish()
|
98
|
+
.transform((value) => (typeof value === "boolean" ? value : false)),
|
99
|
+
maxDataDepth: z.number().gte(1),
|
100
|
+
torrentDir: z.string().transform(checkValidPathFormat).nullable(),
|
101
|
+
outputDir: z.string().transform(checkValidPathFormat),
|
102
|
+
includeEpisodes: z.boolean(),
|
103
|
+
includeSingleEpisodes: z.boolean(),
|
104
|
+
includeNonVideos: z.boolean(),
|
105
|
+
fuzzySizeThreshold: z.number().positive().lte(1, {
|
106
|
+
message: ZodErrorMessages.fuzzySizeThreshold,
|
107
|
+
}),
|
108
|
+
excludeOlder: z
|
109
|
+
.string()
|
110
|
+
.min(1, { message: ZodErrorMessages.emptyString })
|
111
|
+
.transform(transformDurationString)
|
112
|
+
.nullish(),
|
113
|
+
excludeRecentSearch: z
|
114
|
+
.string()
|
115
|
+
.min(1, { message: ZodErrorMessages.emptyString })
|
116
|
+
.transform(transformDurationString)
|
117
|
+
.nullish(),
|
118
|
+
action: z.nativeEnum(Action),
|
119
|
+
qbittorrentUrl: z.string().url().nullish(),
|
120
|
+
rtorrentRpcUrl: z.string().url().nullish(),
|
121
|
+
transmissionRpcUrl: z.string().url().nullish(),
|
122
|
+
delugeRpcUrl: z.string().url().nullish(),
|
123
|
+
duplicateCategories: z.boolean(),
|
124
|
+
notificationWebhookUrl: z.string().url().nullish(),
|
125
|
+
port: z
|
126
|
+
.number()
|
127
|
+
.positive()
|
128
|
+
.lte(65535)
|
129
|
+
.or(z.literal(false).transform(() => null))
|
130
|
+
.nullish(),
|
131
|
+
host: z.string().ip().nullish(),
|
132
|
+
rssCadence: z
|
133
|
+
.string()
|
134
|
+
.min(1, { message: ZodErrorMessages.emptyString })
|
135
|
+
.transform(transformDurationString)
|
136
|
+
.nullish(),
|
137
|
+
searchCadence: z
|
138
|
+
.string()
|
139
|
+
.min(1, { message: ZodErrorMessages.emptyString })
|
140
|
+
.transform(transformDurationString)
|
141
|
+
.nullish(),
|
142
|
+
snatchTimeout: z
|
143
|
+
.string()
|
144
|
+
.min(1, { message: ZodErrorMessages.emptyString })
|
145
|
+
.transform(transformDurationString)
|
146
|
+
.nullish(),
|
147
|
+
searchTimeout: z
|
148
|
+
.string()
|
149
|
+
.min(1, { message: ZodErrorMessages.emptyString })
|
150
|
+
.transform(transformDurationString)
|
151
|
+
.nullish(),
|
152
|
+
searchLimit: z.number().nonnegative().nullish(),
|
153
|
+
verbose: z.boolean(),
|
154
|
+
torrents: z.array(z.string()).optional(),
|
155
|
+
blockList: z
|
156
|
+
.array(z.string())
|
157
|
+
.nullish()
|
158
|
+
.transform((value) => (Array.isArray(value) ? value : [])),
|
159
|
+
apiKey: z.string().min(24).nullish(),
|
160
|
+
})
|
161
|
+
.strict()
|
162
|
+
.refine((config) => {
|
163
|
+
if (config.action === Action.INJECT &&
|
164
|
+
config.qbittorrentUrl &&
|
165
|
+
!config.flatLinking &&
|
166
|
+
config.linkDir) {
|
167
|
+
logger.warn(ZodErrorMessages.qBitAutoTMM);
|
168
|
+
}
|
169
|
+
return true;
|
170
|
+
})
|
171
|
+
.refine((config) => !(config.action === Action.INJECT &&
|
172
|
+
!config.rtorrentRpcUrl &&
|
173
|
+
!config.qbittorrentUrl &&
|
174
|
+
!config.transmissionRpcUrl &&
|
175
|
+
!config.delugeRpcUrl), ZodErrorMessages.injectUrl)
|
176
|
+
.refine((config) => config.matchMode === MatchMode.SAFE || config.linkDir, ZodErrorMessages.needsLinkDir);
|
177
|
+
//# sourceMappingURL=configSchema.js.map
|