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.
Files changed (63) hide show
  1. package/README.md +7 -1
  2. package/dist/Result.js +2 -0
  3. package/dist/Result.js.map +1 -1
  4. package/dist/action.js +127 -62
  5. package/dist/action.js.map +1 -1
  6. package/dist/auth.js +5 -5
  7. package/dist/auth.js.map +1 -1
  8. package/dist/clients/Deluge.js +104 -51
  9. package/dist/clients/Deluge.js.map +1 -1
  10. package/dist/clients/QBittorrent.js +205 -122
  11. package/dist/clients/QBittorrent.js.map +1 -1
  12. package/dist/clients/RTorrent.js +57 -23
  13. package/dist/clients/RTorrent.js.map +1 -1
  14. package/dist/clients/TorrentClient.js.map +1 -1
  15. package/dist/clients/Transmission.js +17 -9
  16. package/dist/clients/Transmission.js.map +1 -1
  17. package/dist/cmd.js +89 -77
  18. package/dist/cmd.js.map +1 -1
  19. package/dist/config.template.cjs +237 -144
  20. package/dist/config.template.cjs.map +1 -1
  21. package/dist/configSchema.js +177 -0
  22. package/dist/configSchema.js.map +1 -0
  23. package/dist/configuration.js +35 -9
  24. package/dist/configuration.js.map +1 -1
  25. package/dist/constants.js +28 -12
  26. package/dist/constants.js.map +1 -1
  27. package/dist/dataFiles.js +17 -8
  28. package/dist/dataFiles.js.map +1 -1
  29. package/dist/decide.js +118 -28
  30. package/dist/decide.js.map +1 -1
  31. package/dist/diff.js.map +1 -1
  32. package/dist/errors.js.map +1 -1
  33. package/dist/indexers.js +3 -3
  34. package/dist/indexers.js.map +1 -1
  35. package/dist/jobs.js +15 -7
  36. package/dist/jobs.js.map +1 -1
  37. package/dist/logger.js +9 -9
  38. package/dist/logger.js.map +1 -1
  39. package/dist/migrations/00-initialSchema.js.map +1 -1
  40. package/dist/migrations/02-timestamps.js +3 -1
  41. package/dist/migrations/02-timestamps.js.map +1 -1
  42. package/dist/parseTorrent.js +11 -2
  43. package/dist/parseTorrent.js.map +1 -1
  44. package/dist/pipeline.js +34 -25
  45. package/dist/pipeline.js.map +1 -1
  46. package/dist/preFilter.js +17 -6
  47. package/dist/preFilter.js.map +1 -1
  48. package/dist/pushNotifier.js +29 -10
  49. package/dist/pushNotifier.js.map +1 -1
  50. package/dist/searchee.js +8 -1
  51. package/dist/searchee.js.map +1 -1
  52. package/dist/server.js.map +1 -1
  53. package/dist/startup.js +62 -23
  54. package/dist/startup.js.map +1 -1
  55. package/dist/torrent.js +14 -29
  56. package/dist/torrent.js.map +1 -1
  57. package/dist/torznab.js +56 -32
  58. package/dist/torznab.js.map +1 -1
  59. package/dist/utils.js +42 -5
  60. package/dist/utils.js.map +1 -1
  61. package/package.json +12 -12
  62. package/dist/config.template.docker.cjs +0 -236
  63. package/dist/config.template.docker.cjs.map +0 -1
@@ -1,231 +1,324 @@
1
1
  "use strict";
2
- // If you find yourself always using the same command-line flag, you can set
3
- // it here as a default.
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
- * Pause at least this much in between each search. Higher is safer.
8
- * It is not recommended to set this to less than 2 seconds.
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
- delay: 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 append "?apikey=YOUR_PROWLARR_API_KEY"
15
- * Wrap each URL in quotation marks, and separate them with commas, and surround the entire set in brackets.
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
- * To search with downloaded data, you can pass in directories to your downloaded torrent
20
- * data to find matches rather using the torrent files themselves for matching.
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
- dataDirs: undefined,
35
+ host: undefined,
30
36
  /**
31
- * Determines flexibility of naming during matching. "safe" will allow only perfect name matches
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
- matchMode: "safe",
39
+ port: 2468,
36
40
  /**
37
- * Defines what category torrents injected by data-based matching should use.
38
- * Default is "cross-seed-data"
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
- dataCategory: undefined,
44
+ notificationWebhookUrl: undefined,
41
45
  /**
42
- * If this is specified, cross-seed will create links to scanned files in the specified directory.
43
- * It will create a different link for every changed file name or directory structure.
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
- linkDir: undefined,
50
+ rtorrentRpcUrl: undefined,
46
51
  /**
47
- * cross-seed will use links of this type to inject data-based matches into your client.
48
- * Only relevant if dataDirs is specified.
49
- * Options: "symlink", "hardlink"
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
- linkType: "symlink",
57
+ qbittorrentUrl: undefined,
52
58
  /**
53
- * Whether to skip recheck in Qbittorrent. If using "risky" matchMode it is HIGHLY
54
- * recommended to set this to false.
55
- * Only applies to data based matches.
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
- skipRecheck: false,
65
+ transmissionRpcUrl: undefined,
58
66
  /**
59
- * Determines how deep into the specified dataDirs to go to generate new searchees.
60
- * Setting this to higher values will result in more searchees and more API hits to
61
- * your indexers.
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
- maxDataDepth: 2,
73
+ delugeRpcUrl: undefined,
64
74
  /**
65
- * Directory containing .torrent files.
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
- * Where to put the torrent files that cross-seed finds for you.
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
- outputDir: ".",
82
+ delay: 30,
77
83
  /**
78
- * Whether to search for all episode torrents, including those from season packs. This option overrides includeSingleEpisodes.
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
- includeEpisodes: false,
98
+ dataDirs: [],
81
99
  /**
82
- * Whether to include single episode torrents in the search (not from season packs).
83
- * Like `includeEpisodes` but slightly more restrictive.
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
- includeSingleEpisodes: false,
108
+ linkCategory: "cross-seed-link",
86
109
  /**
87
- * Include torrents which contain non-video files
88
- * This option does not override includeEpisodes or includeSingleEpisodes.
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
- * To search for everything except episodes, use (includeEpisodes: false, includeSingleEpisodes: false, includeNonVideos: true)
91
- * To search for everything including episodes, use (includeEpisodes: true, includeNonVideos: true)
92
- * To search for everything except season pack episodes (data-based)
93
- * use (includeEpisodes: false, includeSingleEpisodes: true, includeNonVideos: true)
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
- includeNonVideos: false,
123
+ linkDir: undefined,
96
124
  /**
97
- * fuzzy size match threshold
98
- * decimal value (0.02 = 2%)
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
- fuzzySizeThreshold: 0.02,
130
+ linkType: "hardlink",
101
131
  /**
102
- * Exclude torrents first seen more than this long ago.
103
- * Format: https://github.com/vercel/ms
104
- * Examples:
105
- * "10min"
106
- * "2w"
107
- * "3 days"
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
- excludeOlder: undefined,
142
+ flatLinking: false,
110
143
  /**
111
- * Exclude torrents which have been searched
112
- * more recently than this long ago.
113
- * Examples:
114
- * "10min"
115
- * "2w"
116
- * "3 days"
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
- excludeRecentSearch: undefined,
152
+ matchMode: "safe",
119
153
  /**
120
- * With "inject" you need to set up one of the below clients.
121
- * Options: "save", "inject"
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
- action: "save",
158
+ maxDataDepth: 1,
124
159
  /**
125
- * The url of your rtorrent XMLRPC interface.
126
- * Only relevant with action: "inject".
127
- * Could be something like "http://username:password@localhost:1234/RPC2
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
- rtorrentRpcUrl: undefined,
169
+ torrentDir: "/path/to/torrent/file/dir",
130
170
  /**
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"
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
- qbittorrentUrl: undefined,
175
+ outputDir: ".",
137
176
  /**
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"
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
- transmissionRpcUrl: undefined,
181
+ includeEpisodes: false,
145
182
  /**
146
- * The url of your Deluge JSON-RPC interface.
147
- * Usually ends with "/json".
148
- * Only relevant with action: "inject".
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
- delugeRpcUrl: undefined,
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
- * qBittorrent and Deluge specific
155
- * Whether to inject using the same labels/categories as the original torrent.
156
- * qBittorrent: This will apply the category's save path
157
- * Example: if you have a label/category called "Movies",
158
- * this will automatically inject cross-seeds to "Movies.cross-seed"
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
- duplicateCategories: false,
227
+ fuzzySizeThreshold: 0.02,
161
228
  /**
162
- * cross-seed will send POST requests to this url
163
- * with a JSON payload of { title, body }.
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
- * Listen on a custom port.
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
- port: 2468,
239
+ excludeOlder: "2 weeks",
171
240
  /**
172
- * Bind to a specific host address.
173
- * Example: "127.0.0.1"
174
- * Default is "0.0.0.0"
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
- host: undefined,
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
- * 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.
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
- apiAuth: true,
264
+ duplicateCategories: false,
183
265
  /**
184
- * Run rss scans on a schedule. Format: https://github.com/vercel/ms
266
+ * Run rss scans on a schedule.
185
267
  * Set to undefined or null to disable. Minimum of 10 minutes.
186
268
  * Examples:
187
- * "10min"
188
- * "2w"
189
- * "3 days"
269
+ * "10 minutes"
270
+ * "1 hour"
190
271
  */
191
- rssCadence: undefined,
272
+ rssCadence: "30 minutes",
192
273
  /**
193
- * Run searches on a schedule. Format: https://github.com/vercel/ms
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
- * "10min"
198
- * "2w"
277
+ * "2 weeks"
199
278
  * "3 days"
200
279
  */
201
- searchCadence: undefined,
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
- * "30sec"
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
- * "30sec"
219
- * "10s"
220
- * "1min"
293
+ * "30 seconds"
221
294
  * null
222
295
  */
223
296
  searchTimeout: undefined,
224
297
  /**
225
- * The number of searches to be done before it stops.
226
- * Combine this with "excludeRecentSearch" and "searchCadence" to smooth long-term API usage patterns.
227
- * Default is no limit.
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
- searchLimit: undefined,
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,4EAA4E;AAC5E,wBAAwB;;AAExB,MAAM,CAAC,OAAO,GAAG;IAChB;;;OAGG;IACH,KAAK,EAAE,EAAE;IAET;;;;;OAKG;IACH,OAAO,EAAE,EAAE;IAEX;;;;;;;;;;OAUG;IACH,QAAQ,EAAE,SAAS;IAEnB;;;;OAIG;IACH,SAAS,EAAE,MAAM;IAEjB;;;OAGG;IACH,YAAY,EAAE,SAAS;IAEvB;;;OAGG;IACH,OAAO,EAAE,SAAS;IAElB;;;;OAIG;IACH,QAAQ,EAAE,SAAS;IAEnB;;;;OAIG;IACH,WAAW,EAAE,KAAK;IAElB;;;;OAIG;IACH,YAAY,EAAE,CAAC;IAEf;;;;;;;OAOG;IACH,UAAU,EAAE,2BAA2B;IAEvC;;OAEG;IACH,SAAS,EAAE,GAAG;IAEd;;OAEG;IACH,eAAe,EAAE,KAAK;IAEtB;;;OAGG;IACH,qBAAqB,EAAE,KAAK;IAE5B;;;;;;;;OAQG;IACH,gBAAgB,EAAE,KAAK;IAEvB;;;OAGG;IACH,kBAAkB,EAAE,IAAI;IAExB;;;;;;;OAOG;IACH,YAAY,EAAE,SAAS;IAEvB;;;;;;;OAOG;IACH,mBAAmB,EAAE,SAAS;IAE9B;;;OAGG;IACH,MAAM,EAAE,MAAM;IAEd;;;;OAIG;IACH,cAAc,EAAE,SAAS;IAEzB;;;;;OAKG;IACH,cAAc,EAAE,SAAS;IAEzB;;;;;;OAMG;IACH,kBAAkB,EAAE,SAAS;IAE7B;;;;;;OAMG;IACH,YAAY,EAAE,SAAS;IAEvB;;;;;;OAMG;IACH,mBAAmB,EAAE,KAAK;IAE1B;;;;OAIG;IACH,sBAAsB,EAAE,SAAS;IAEjC;;OAEG;IACH,IAAI,EAAE,IAAI;IAEV;;;;OAIG;IACH,IAAI,EAAE,SAAS;IAEf;;;;OAIG;IACH,OAAO,EAAE,IAAI;IAEb;;;;;;;OAOG;IACH,UAAU,EAAE,SAAS;IAErB;;;;;;;;OAQG;IACH,aAAa,EAAE,SAAS;IAExB;;;;;;;;;OASG;IACH,aAAa,EAAE,SAAS;IAExB;;;;;;;;;OASG;IACH,aAAa,EAAE,SAAS;IAExB;;;;OAIG;IACH,WAAW,EAAE,SAAS;CACtB,CAAC"}
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