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