cross-seed 6.13.6 → 7.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 +12 -13
- package/dist/webui/assets/FieldInfo-Bxj_j8SJ.js +1 -0
- package/dist/webui/assets/Page-C3rteCZt.js +1 -0
- package/dist/webui/assets/array-field-DVSC6nHP.js +1 -0
- package/dist/webui/assets/badge-DTZMtS0e.js +1 -0
- package/dist/webui/assets/check-Bu3ldi63.js +1 -0
- package/dist/webui/assets/chevron-down-CRy8M0kJ.js +1 -0
- package/dist/webui/assets/clients-CW8oEZoQ.js +1 -0
- package/dist/webui/assets/connect-YBNsnjWT.js +1 -0
- package/dist/webui/assets/debug-mz8-WYZj.js +1 -0
- package/dist/webui/assets/directories-BSK28RgR.js +1 -0
- package/dist/webui/assets/duration-field-C6xoSlJg.js +1 -0
- package/dist/webui/assets/general-lJJxZhH7.js +1 -0
- package/dist/webui/assets/health-CXbsVrie.js +1 -0
- package/dist/webui/assets/index-Bi48hI2z.js +54 -0
- package/dist/webui/assets/index-C-Ul7GNg.css +1 -0
- package/dist/webui/assets/index-C2cH1Gst.js +1 -0
- package/dist/webui/assets/index-Cc5bDmJr.js +1 -0
- package/dist/webui/assets/jobs-CxmNab9w.js +1 -0
- package/dist/webui/assets/library-vaj2W8sE.js +1 -0
- package/dist/webui/assets/loader-circle-M0gu1gZ-.js +1 -0
- package/dist/webui/assets/logs-Cu9RyKS0.js +1 -0
- package/dist/webui/assets/search-2R5sIdT8.js +1 -0
- package/dist/webui/assets/select-field-BCqNLDrJ.js +1 -0
- package/dist/webui/assets/select-zHgqMzLj.js +1 -0
- package/dist/webui/assets/settings-CMYjpTbZ.js +1 -0
- package/dist/webui/assets/submit-button-BtcnyggQ.js +1 -0
- package/dist/webui/assets/switch-G0W3uJVN.js +1 -0
- package/dist/webui/assets/switch-field-IBd9ORNq.js +1 -0
- package/dist/webui/assets/table-DvgJU7Gh.js +1 -0
- package/dist/webui/assets/test-tube-BIwmoM45.js +1 -0
- package/dist/webui/assets/text-field-DruSbGhy.js +1 -0
- package/dist/webui/assets/time-BSMZjmyW.js +1 -0
- package/dist/webui/assets/trackers-D-OpAe63.js +7 -0
- package/dist/webui/assets/use-form-validation-context-BkAfWAh0.js +1 -0
- package/dist/webui/assets/use-settings-form-submit-CDRh-E9U.js +2 -0
- package/dist/webui/assets/useQuery-A4Hv_4uX.js +1 -0
- package/dist/webui/index.html +13 -0
- package/node_modules/@cross-seed/shared/dist/configSchema.d.ts +261 -0
- package/node_modules/@cross-seed/shared/dist/configSchema.d.ts.map +1 -0
- package/node_modules/@cross-seed/shared/dist/configSchema.js +53 -0
- package/node_modules/@cross-seed/shared/dist/configSchema.js.map +1 -0
- package/node_modules/@cross-seed/shared/dist/constants.d.ts +122 -0
- package/node_modules/@cross-seed/shared/dist/constants.d.ts.map +1 -0
- package/node_modules/@cross-seed/shared/dist/constants.js +127 -0
- package/node_modules/@cross-seed/shared/dist/constants.js.map +1 -0
- package/node_modules/@cross-seed/shared/dist/tsconfig.tsbuildinfo +1 -0
- package/node_modules/@cross-seed/shared/dist/utils.d.ts +6 -0
- package/node_modules/@cross-seed/shared/dist/utils.d.ts.map +1 -0
- package/node_modules/@cross-seed/shared/dist/utils.js +9 -0
- package/node_modules/@cross-seed/shared/dist/utils.js.map +1 -0
- package/node_modules/@cross-seed/shared/package.json +22 -0
- package/package.json +35 -11
- package/dist/Result.js +0 -64
- package/dist/Result.js.map +0 -1
- package/dist/action.js +0 -693
- package/dist/action.js.map +0 -1
- package/dist/arr.js +0 -199
- package/dist/arr.js.map +0 -1
- package/dist/auth.js +0 -25
- package/dist/auth.js.map +0 -1
- package/dist/clients/Deluge.js +0 -698
- package/dist/clients/Deluge.js.map +0 -1
- package/dist/clients/QBittorrent.js +0 -785
- package/dist/clients/QBittorrent.js.map +0 -1
- package/dist/clients/RTorrent.js +0 -654
- package/dist/clients/RTorrent.js.map +0 -1
- package/dist/clients/TorrentClient.js +0 -272
- package/dist/clients/TorrentClient.js.map +0 -1
- package/dist/clients/Transmission.js +0 -404
- package/dist/clients/Transmission.js.map +0 -1
- package/dist/cmd.js +0 -196
- package/dist/cmd.js.map +0 -1
- package/dist/config.template.cjs +0 -353
- package/dist/config.template.cjs.map +0 -1
- package/dist/configSchema.js +0 -667
- package/dist/configSchema.js.map +0 -1
- package/dist/configuration.js +0 -82
- package/dist/configuration.js.map +0 -1
- package/dist/constants.js +0 -281
- package/dist/constants.js.map +0 -1
- package/dist/dataFiles.js +0 -208
- package/dist/dataFiles.js.map +0 -1
- package/dist/db.js +0 -216
- package/dist/db.js.map +0 -1
- package/dist/decide.js +0 -553
- package/dist/decide.js.map +0 -1
- package/dist/diff.js +0 -24
- package/dist/diff.js.map +0 -1
- package/dist/errors.js +0 -16
- package/dist/errors.js.map +0 -1
- package/dist/indexers.js +0 -180
- package/dist/indexers.js.map +0 -1
- package/dist/inject.js +0 -594
- package/dist/inject.js.map +0 -1
- package/dist/jobs.js +0 -146
- package/dist/jobs.js.map +0 -1
- package/dist/logger.js +0 -143
- package/dist/logger.js.map +0 -1
- package/dist/migrations/00-initialSchema.js +0 -30
- package/dist/migrations/00-initialSchema.js.map +0 -1
- package/dist/migrations/01-jobs.js +0 -12
- package/dist/migrations/01-jobs.js.map +0 -1
- package/dist/migrations/02-timestamps.js +0 -21
- package/dist/migrations/02-timestamps.js.map +0 -1
- package/dist/migrations/03-rateLimits.js +0 -14
- package/dist/migrations/03-rateLimits.js.map +0 -1
- package/dist/migrations/04-auth.js +0 -13
- package/dist/migrations/04-auth.js.map +0 -1
- package/dist/migrations/05-caps.js +0 -16
- package/dist/migrations/05-caps.js.map +0 -1
- package/dist/migrations/06-uniqueDecisions.js +0 -29
- package/dist/migrations/06-uniqueDecisions.js.map +0 -1
- package/dist/migrations/07-limits.js +0 -12
- package/dist/migrations/07-limits.js.map +0 -1
- package/dist/migrations/08-rss.js +0 -15
- package/dist/migrations/08-rss.js.map +0 -1
- package/dist/migrations/09-clientAndDataSearchees.js +0 -34
- package/dist/migrations/09-clientAndDataSearchees.js.map +0 -1
- package/dist/migrations/10-indexerNameAudioBookCaps.js +0 -18
- package/dist/migrations/10-indexerNameAudioBookCaps.js.map +0 -1
- package/dist/migrations/11-trackers.js +0 -38
- package/dist/migrations/11-trackers.js.map +0 -1
- package/dist/migrations/migrations.js +0 -31
- package/dist/migrations/migrations.js.map +0 -1
- package/dist/parseTorrent.js +0 -128
- package/dist/parseTorrent.js.map +0 -1
- package/dist/pipeline.js +0 -527
- package/dist/pipeline.js.map +0 -1
- package/dist/preFilter.js +0 -250
- package/dist/preFilter.js.map +0 -1
- package/dist/pushNotifier.js +0 -137
- package/dist/pushNotifier.js.map +0 -1
- package/dist/runtimeConfig.js +0 -11
- package/dist/runtimeConfig.js.map +0 -1
- package/dist/searchee.js +0 -658
- package/dist/searchee.js.map +0 -1
- package/dist/server.js +0 -456
- package/dist/server.js.map +0 -1
- package/dist/startup.js +0 -203
- package/dist/startup.js.map +0 -1
- package/dist/torrent.js +0 -637
- package/dist/torrent.js.map +0 -1
- package/dist/torznab.js +0 -786
- package/dist/torznab.js.map +0 -1
- package/dist/utils.js +0 -637
- package/dist/utils.js.map +0 -1
package/dist/configSchema.js
DELETED
|
@@ -1,667 +0,0 @@
|
|
|
1
|
-
import { existsSync, readdirSync } from "fs";
|
|
2
|
-
import ms from "ms";
|
|
3
|
-
import { isAbsolute, join, relative, resolve } from "path";
|
|
4
|
-
import { z } from "zod";
|
|
5
|
-
import { clientsAreUnique, parseClientEntry } from "./clients/TorrentClient.js";
|
|
6
|
-
import { appDir } from "./configuration.js";
|
|
7
|
-
import { Action, BlocklistType, INFO_HASH_REGEX, LinkType, MatchMode, NEWLINE_INDENT, parseBlocklistEntry, } from "./constants.js";
|
|
8
|
-
import { Label, logger } from "./logger.js";
|
|
9
|
-
import { formatAsList } from "./utils.js";
|
|
10
|
-
/**
|
|
11
|
-
* error messages and map returned upon Zod validation failure
|
|
12
|
-
*/
|
|
13
|
-
const ZodErrorMessages = {
|
|
14
|
-
blocklistType: `Blocklist item does not start with a valid prefix. Must be of ${formatAsList(Object.values(BlocklistType), { sort: false, style: "narrow", type: "unit" })}`,
|
|
15
|
-
blocklistEmptyValue: `Blocklist item must have a value after the colon.`,
|
|
16
|
-
blocklistRegex: `Blocklist regex is not a valid regex.`,
|
|
17
|
-
blocklistFolder: `Blocklist folder must not contain path separators`,
|
|
18
|
-
blocklistTracker: `Blocklist tracker is not a valid URL host. If URL is https://user:pass@tracker.example.com:8080/announce/key, you must use "tracker:tracker.example.com:8080"`,
|
|
19
|
-
blocklistHash: `Blocklist hash must be 40 characters and alphanumeric`,
|
|
20
|
-
blocklistSize: `Blocklist size must be an integer for the number of bytes. You can only have one sizeBelow, one sizeAbove, and sizeBelow <= sizeAbove.`,
|
|
21
|
-
blocklistNeedsClient: `Blocklist ${BlocklistType.CATEGORY}:, ${BlocklistType.TAG}:, and ${BlocklistType.TRACKER}: requires torrentDir or useClientTorrents.`,
|
|
22
|
-
blocklistNeedsDataDirs: `Blocklist ${BlocklistType.FOLDER}: and ${BlocklistType.FOLDER_REGEX}: only applies to searchees from dataDirs.`,
|
|
23
|
-
vercel: "format does not follow vercel's `ms` style ( https://github.com/vercel/ms#examples )",
|
|
24
|
-
emptyString: "cannot have an empty string. If you want to unset it, use null or undefined.",
|
|
25
|
-
delayNegative: "delay is in seconds, you can't travel back in time.",
|
|
26
|
-
delayUnsupported: `delay must be 30 seconds to 1 hour.${NEWLINE_INDENT}To even out search loads please see the following documentation:${NEWLINE_INDENT}(https://www.cross-seed.org/docs/basics/options#delay)`,
|
|
27
|
-
rssCadenceUnsupported: "rssCadence must be 10-120 minutes",
|
|
28
|
-
searchCadenceUnsupported: "searchCadence must be at least 1 day.",
|
|
29
|
-
searchCadenceExcludeRecent: "excludeRecentSearch must be at least 3x searchCadence.",
|
|
30
|
-
excludeRecentOlder: "excludeOlder and excludeRecentSearch must be defined for searching. excludeOlder must be 2-5x excludeRecentSearch.",
|
|
31
|
-
injectNeedsInjectMode: "`cross-seed inject` requires the 'inject' action.",
|
|
32
|
-
autoResumeMaxDownloadUnsupported: "autoResumeMaxDownload must be an integer of bytes between between 0 and 52428800 (50 MiB).",
|
|
33
|
-
numberMustBeRatio: "fuzzySizeThreshold and seasonFromEpisodes must be between 0 and 1.",
|
|
34
|
-
fuzzySizeThresholdMax: "fuzzySizeThreshold cannot be greater than 0.1 when using searchCadence or rssCadence.",
|
|
35
|
-
seasonFromEpisodesMin: "seasonFromEpisodes cannot be less than 0.5 when using searchCadence or rssCadence",
|
|
36
|
-
clientType: `torrentClients type prefix must be one of ${formatAsList([
|
|
37
|
-
Label.QBITTORRENT,
|
|
38
|
-
Label.RTORRENT,
|
|
39
|
-
Label.TRANSMISSION,
|
|
40
|
-
Label.DELUGE,
|
|
41
|
-
].map((l) => `${l}:`), { sort: false, style: "narrow", type: "unit" })} and optionally followed by readonly: (e.g torrentClients: ["qbittorrent:http://username:password@localhost:8080", "rtorrent:readonly:http://username:password@localhost:1234/RPC2"])`,
|
|
42
|
-
clientTypeReadOnly: `You must have at least one non-readonly torrent client when using action: "inject"`,
|
|
43
|
-
multipleClientsTorrentFile: "torrentDir and --torrents arg does not support multiple clients, use useClientTorrents instead.",
|
|
44
|
-
multipleClientsNoLinking: "dataDirs is not supported with multiple clients if not linking. To configure linking, please read: https://www.cross-seed.org/docs/tutorials/linking",
|
|
45
|
-
duplicateClients: "Duplicate torrent client URLs are not allowed.",
|
|
46
|
-
injectNeedsClients: "You need to specify torrentClients when using 'inject'",
|
|
47
|
-
qBitAutoTMM: "If using Automatic Torrent Management in qBittorrent, please read: https://www.cross-seed.org/docs/v6-migration#new-folder-structure-for-links",
|
|
48
|
-
includeSingleEpisodes: "includeSingleEpisodes is not recommended when using announce, please read: https://www.cross-seed.org/docs/v6-migration#updated-includesingleepisodes-behavior",
|
|
49
|
-
invalidOutputDir: "outputDir should only contain .torrent files, cross-seed will populate and manage (https://www.cross-seed.org/docs/basics/options#outputdir)",
|
|
50
|
-
torrentDirAndUseClientTorrents: "You cannot have both torrentDir and useClientTorrents.",
|
|
51
|
-
needsClient: "You need to have a client configured for useClientTorrents.",
|
|
52
|
-
needSearchees: "You need to have torrentDir, useClientTorrents or dataDirs for search/rss/announce matching to work.",
|
|
53
|
-
matchModeInvalid: `matchMode must be one of: ${formatAsList(Object.values(MatchMode).map((m) => `"${m}"`), { sort: false, style: "narrow", type: "unit" })}`,
|
|
54
|
-
matchModeNeedsLinkDirs: `When using action: "inject", you need to set linkDirs for flexible and partial matchMode (https://www.cross-seed.org/docs/tutorials/linking). If you cannot use linking, use matchMode: "strict"`,
|
|
55
|
-
ensembleNeedsClient: "seasonFromEpisodes requires a torrent client to connect to when using torrentDir or useClientTorrents.",
|
|
56
|
-
ensembleNeedsLinkDirs: "When using action 'inject', you need to set linkDirs for seasonFromEpisodes (https://www.cross-seed.org/docs/tutorials/linking). If you cannot use linking, disable this option by using seasonFromEpisodes: null",
|
|
57
|
-
ensembleNeedsPartial: "seasonFromEpisodes requires matchMode partial if enabled and value is below 1.",
|
|
58
|
-
linkDirsInOtherDirs: "You cannot have your linkDirs inside of your torrentDir/dataDirs/outputDir. Please adjust your paths to correct this.",
|
|
59
|
-
dataDirsInOtherDirs: "You cannot have your dataDirs inside of your torrentDir/linkDirs/outputDir. Please adjust your paths to correct this.",
|
|
60
|
-
torrentDirInOtherDirs: "You cannot have your torrentDir inside of your dataDirs/linkDirs/outputDir. Please adjust your paths to correct this.",
|
|
61
|
-
outputDirInOtherDirs: "You cannot have your outputDir inside of your torrentDir/dataDirs/linkDirs. Please adjust your paths to correct this.",
|
|
62
|
-
relativePaths: "Absolute paths for torrentDir, linkDirs, dataDirs, and outputDir are recommended.",
|
|
63
|
-
};
|
|
64
|
-
/**
|
|
65
|
-
* custom zod error map for logging
|
|
66
|
-
* @param error ZodIssue messaging object
|
|
67
|
-
* @param ctx ZodError map
|
|
68
|
-
* @returns (the custom error for config display)
|
|
69
|
-
*/
|
|
70
|
-
export function customizeErrorMessage(error, ctx) {
|
|
71
|
-
switch (error.code) {
|
|
72
|
-
case z.ZodIssueCode.invalid_union:
|
|
73
|
-
return {
|
|
74
|
-
message: error.unionErrors
|
|
75
|
-
.reduce((acc, error) => {
|
|
76
|
-
error.errors.forEach((x) => acc.push(x.message));
|
|
77
|
-
return acc;
|
|
78
|
-
}, [])
|
|
79
|
-
.join("; "),
|
|
80
|
-
};
|
|
81
|
-
}
|
|
82
|
-
return { message: ctx.defaultError };
|
|
83
|
-
}
|
|
84
|
-
/**
|
|
85
|
-
* adds an issue in Zod's error mapped formatting
|
|
86
|
-
* @param setting the value of the setting
|
|
87
|
-
* @param errorMessage the error message to append on a newline
|
|
88
|
-
* @param ctx ZodError map
|
|
89
|
-
*/
|
|
90
|
-
function addZodIssue(setting, errorMessage, ctx) {
|
|
91
|
-
ctx.addIssue({
|
|
92
|
-
code: "custom",
|
|
93
|
-
message: `Setting: "${setting}"\n\t${errorMessage}`,
|
|
94
|
-
});
|
|
95
|
-
}
|
|
96
|
-
/**
|
|
97
|
-
* helper function for ms time validation
|
|
98
|
-
* @param durationStr the duration string to validate
|
|
99
|
-
* @param ctx ZodError map
|
|
100
|
-
* @return transformed duration (string -> milliseconds)
|
|
101
|
-
*/
|
|
102
|
-
function transformDurationString(durationStr, ctx) {
|
|
103
|
-
const duration = ms(durationStr);
|
|
104
|
-
if (isNaN(duration)) {
|
|
105
|
-
// adds the error to the Zod Issues
|
|
106
|
-
addZodIssue(durationStr, ZodErrorMessages.vercel, ctx);
|
|
107
|
-
}
|
|
108
|
-
return duration;
|
|
109
|
-
}
|
|
110
|
-
/**
|
|
111
|
-
* helper function for verifying torrent client types
|
|
112
|
-
* @param torrentClients the torrent clients to validate
|
|
113
|
-
* @param ctx ZodError map
|
|
114
|
-
* @return torrentClients (string array)
|
|
115
|
-
*/
|
|
116
|
-
function transformTorrentClients(torrentClients, ctx) {
|
|
117
|
-
if (!Array.isArray(torrentClients))
|
|
118
|
-
return [];
|
|
119
|
-
for (const clientEntryRaw of torrentClients) {
|
|
120
|
-
const clientEntry = parseClientEntry(clientEntryRaw);
|
|
121
|
-
if (!clientEntry) {
|
|
122
|
-
addZodIssue(clientEntryRaw, ZodErrorMessages.clientType, ctx);
|
|
123
|
-
continue;
|
|
124
|
-
}
|
|
125
|
-
switch (clientEntry.clientType) {
|
|
126
|
-
case Label.QBITTORRENT:
|
|
127
|
-
case Label.RTORRENT:
|
|
128
|
-
case Label.TRANSMISSION:
|
|
129
|
-
case Label.DELUGE:
|
|
130
|
-
break;
|
|
131
|
-
default:
|
|
132
|
-
addZodIssue(clientEntryRaw, ZodErrorMessages.clientType, ctx);
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
return torrentClients;
|
|
136
|
-
}
|
|
137
|
-
/**
|
|
138
|
-
* helper function for verifying blocklist types
|
|
139
|
-
* @param blockList the blocklist to validate
|
|
140
|
-
* @param ctx ZodError map
|
|
141
|
-
* @return blocklist (string array)
|
|
142
|
-
*/
|
|
143
|
-
function transformBlocklist(blockList, ctx) {
|
|
144
|
-
if (!Array.isArray(blockList))
|
|
145
|
-
return [];
|
|
146
|
-
const sizeTest = (value, existing) => {
|
|
147
|
-
if (!existing) {
|
|
148
|
-
const match = value.match(/^\d+$/);
|
|
149
|
-
if (match)
|
|
150
|
-
return parseInt(match[0]);
|
|
151
|
-
}
|
|
152
|
-
addZodIssue(value, ZodErrorMessages.blocklistSize, ctx);
|
|
153
|
-
return existing;
|
|
154
|
-
};
|
|
155
|
-
let sizeBelow = 0;
|
|
156
|
-
let sizeAbove = 0;
|
|
157
|
-
for (const [index, blockRaw] of blockList.entries()) {
|
|
158
|
-
if (!blockRaw.trim().length) {
|
|
159
|
-
addZodIssue(blockRaw, ZodErrorMessages.blocklistType, ctx);
|
|
160
|
-
continue;
|
|
161
|
-
}
|
|
162
|
-
const { blocklistType, blocklistValue } = parseBlocklistEntry(blockRaw);
|
|
163
|
-
switch (blocklistType) {
|
|
164
|
-
case BlocklistType.NAME:
|
|
165
|
-
if (blocklistValue.length === 0) {
|
|
166
|
-
addZodIssue(blockRaw, ZodErrorMessages.blocklistEmptyValue, ctx);
|
|
167
|
-
}
|
|
168
|
-
break;
|
|
169
|
-
case BlocklistType.FOLDER:
|
|
170
|
-
if (blocklistValue.length === 0) {
|
|
171
|
-
addZodIssue(blockRaw, ZodErrorMessages.blocklistEmptyValue, ctx);
|
|
172
|
-
}
|
|
173
|
-
if (/[/\\]/.test(blocklistValue)) {
|
|
174
|
-
addZodIssue(blockRaw, ZodErrorMessages.blocklistFolder, ctx);
|
|
175
|
-
}
|
|
176
|
-
break;
|
|
177
|
-
case BlocklistType.NAME_REGEX:
|
|
178
|
-
case BlocklistType.FOLDER_REGEX:
|
|
179
|
-
if (blocklistValue.length === 0) {
|
|
180
|
-
addZodIssue(blockRaw, ZodErrorMessages.blocklistEmptyValue, ctx);
|
|
181
|
-
}
|
|
182
|
-
try {
|
|
183
|
-
new RegExp(blocklistValue);
|
|
184
|
-
}
|
|
185
|
-
catch (e) {
|
|
186
|
-
addZodIssue(blockRaw, ZodErrorMessages.blocklistRegex, ctx);
|
|
187
|
-
}
|
|
188
|
-
break;
|
|
189
|
-
case BlocklistType.CATEGORY:
|
|
190
|
-
if (blocklistValue.length === 0) {
|
|
191
|
-
logger.info(`Blocklisting all torrents without a category due to empty ${blockRaw}`);
|
|
192
|
-
}
|
|
193
|
-
break;
|
|
194
|
-
case BlocklistType.TAG:
|
|
195
|
-
if (blocklistValue.length === 0) {
|
|
196
|
-
logger.info(`Blocklisting all torrents without a tag due to empty ${blockRaw}`);
|
|
197
|
-
}
|
|
198
|
-
break;
|
|
199
|
-
case BlocklistType.TRACKER:
|
|
200
|
-
if (blocklistValue.length === 0) {
|
|
201
|
-
addZodIssue(blockRaw, ZodErrorMessages.blocklistEmptyValue, ctx);
|
|
202
|
-
}
|
|
203
|
-
if (/[/@]|^[^.]*$/.test(blocklistValue)) {
|
|
204
|
-
addZodIssue(blockRaw, ZodErrorMessages.blocklistTracker, ctx);
|
|
205
|
-
}
|
|
206
|
-
break;
|
|
207
|
-
case BlocklistType.INFOHASH:
|
|
208
|
-
if (!INFO_HASH_REGEX.test(blocklistValue)) {
|
|
209
|
-
addZodIssue(blockRaw, ZodErrorMessages.blocklistHash, ctx);
|
|
210
|
-
}
|
|
211
|
-
blockList[index] =
|
|
212
|
-
`${blocklistType}:${blocklistValue.toLowerCase()}`;
|
|
213
|
-
break;
|
|
214
|
-
case BlocklistType.SIZE_BELOW: {
|
|
215
|
-
sizeBelow = sizeTest(blocklistValue, sizeBelow);
|
|
216
|
-
break;
|
|
217
|
-
}
|
|
218
|
-
case BlocklistType.SIZE_ABOVE: {
|
|
219
|
-
sizeAbove = sizeTest(blocklistValue, sizeAbove);
|
|
220
|
-
break;
|
|
221
|
-
}
|
|
222
|
-
case BlocklistType.LEGACY:
|
|
223
|
-
if (blocklistValue.length === 0) {
|
|
224
|
-
addZodIssue(blockRaw, ZodErrorMessages.blocklistEmptyValue, ctx);
|
|
225
|
-
}
|
|
226
|
-
logger.error(`Legacy style blocklist is deprecated, specify a specific type followed by a colon (e.g infoHash:) for ${blockRaw}`);
|
|
227
|
-
break;
|
|
228
|
-
default:
|
|
229
|
-
addZodIssue(blockRaw, ZodErrorMessages.blocklistType, ctx);
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
|
-
if (sizeBelow && sizeAbove && sizeBelow > sizeAbove) {
|
|
233
|
-
addZodIssue("sizeBelow > sizeAbove", ZodErrorMessages.blocklistSize, ctx);
|
|
234
|
-
}
|
|
235
|
-
return blockList;
|
|
236
|
-
}
|
|
237
|
-
/**
|
|
238
|
-
* check a potential child path being inside a array of parent paths
|
|
239
|
-
* @param childPath path of the potential child (e.g outputDir)
|
|
240
|
-
* @param parentDirs array of parentDir paths (e.g dataDirs)
|
|
241
|
-
* @returns true if `childDir` is inside any `parentDirs` at any nesting level, false otherwise.
|
|
242
|
-
*/
|
|
243
|
-
export function isChildPath(childPath, parentDirs) {
|
|
244
|
-
return parentDirs.some((parentDir) => {
|
|
245
|
-
const resolvedParent = resolve(parentDir);
|
|
246
|
-
const resolvedChild = resolve(childPath);
|
|
247
|
-
const relativePath = relative(resolvedParent, resolvedChild);
|
|
248
|
-
// if the path does not start with '..' and is not absolute
|
|
249
|
-
return !(relativePath.startsWith("..") || isAbsolute(relativePath));
|
|
250
|
-
});
|
|
251
|
-
}
|
|
252
|
-
/**
|
|
253
|
-
* an object of the zod schema
|
|
254
|
-
* each are named after what they are intended to validate
|
|
255
|
-
*/
|
|
256
|
-
export const VALIDATION_SCHEMA = z
|
|
257
|
-
.object({
|
|
258
|
-
delay: z
|
|
259
|
-
.number()
|
|
260
|
-
.nonnegative(ZodErrorMessages.delayNegative)
|
|
261
|
-
.gte(process.env.DEV ? 0 : 30, ZodErrorMessages.delayUnsupported)
|
|
262
|
-
.lte(3600, ZodErrorMessages.delayUnsupported),
|
|
263
|
-
torznab: z.array(z.string().url()),
|
|
264
|
-
useClientTorrents: z.boolean().optional().default(false),
|
|
265
|
-
dataDirs: z
|
|
266
|
-
.array(z.string())
|
|
267
|
-
.nullish()
|
|
268
|
-
.transform((v) => v ?? []),
|
|
269
|
-
matchMode: z.nativeEnum(MatchMode).or(z
|
|
270
|
-
.string()
|
|
271
|
-
.refine((v) => v === "safe" || v === "risky", ZodErrorMessages.matchModeInvalid)
|
|
272
|
-
.transform((v) => v === "safe" ? MatchMode.STRICT : MatchMode.FLEXIBLE)),
|
|
273
|
-
skipRecheck: z.boolean().optional().default(true),
|
|
274
|
-
autoResumeMaxDownload: z
|
|
275
|
-
.number()
|
|
276
|
-
.int()
|
|
277
|
-
.gte(0, ZodErrorMessages.autoResumeMaxDownloadUnsupported)
|
|
278
|
-
.lte(52428800, ZodErrorMessages.autoResumeMaxDownloadUnsupported),
|
|
279
|
-
ignoreNonRelevantFilesToResume: z
|
|
280
|
-
.boolean()
|
|
281
|
-
.nullish()
|
|
282
|
-
.transform((v) => (typeof v === "boolean" ? v : false)),
|
|
283
|
-
linkCategory: z.string().nullish(),
|
|
284
|
-
linkDir: z.string().nullish(),
|
|
285
|
-
linkDirs: z.array(z.string()).optional().default([]),
|
|
286
|
-
linkType: z.nativeEnum(LinkType),
|
|
287
|
-
flatLinking: z
|
|
288
|
-
.boolean()
|
|
289
|
-
.nullish()
|
|
290
|
-
.transform((v) => (typeof v === "boolean" ? v : false)),
|
|
291
|
-
maxDataDepth: z
|
|
292
|
-
.number()
|
|
293
|
-
.gte(1)
|
|
294
|
-
.refine((maxDataDepth) => {
|
|
295
|
-
if (maxDataDepth > 3) {
|
|
296
|
-
logger.warn(`Your maxDataDepth is most likely incorrect, please read: https://www.cross-seed.org/docs/tutorials/data-based-matching#setting-up-data-based-matching`);
|
|
297
|
-
}
|
|
298
|
-
return true;
|
|
299
|
-
}),
|
|
300
|
-
torrentDir: z
|
|
301
|
-
.string()
|
|
302
|
-
.nullish()
|
|
303
|
-
.transform((v) => v ?? null),
|
|
304
|
-
outputDir: z
|
|
305
|
-
.string()
|
|
306
|
-
.nullish()
|
|
307
|
-
.transform((dir) => {
|
|
308
|
-
if (!dir)
|
|
309
|
-
return join(appDir(), "cross-seeds");
|
|
310
|
-
logger.warn(`Set outputDir to null to prevent any issues from occurring (https://www.cross-seed.org/docs/basics/options#outputdir)`);
|
|
311
|
-
return dir;
|
|
312
|
-
})
|
|
313
|
-
.refine((dir) => {
|
|
314
|
-
if (!existsSync(dir))
|
|
315
|
-
return true;
|
|
316
|
-
if (readdirSync(dir).some((f) => !f.endsWith(".torrent"))) {
|
|
317
|
-
logger.warn(ZodErrorMessages.invalidOutputDir);
|
|
318
|
-
}
|
|
319
|
-
return true;
|
|
320
|
-
}),
|
|
321
|
-
injectDir: z.string().nullish(),
|
|
322
|
-
ignoreTitles: z.boolean().optional(),
|
|
323
|
-
includeSingleEpisodes: z.boolean(),
|
|
324
|
-
includeNonVideos: z.boolean(),
|
|
325
|
-
fuzzySizeThreshold: z
|
|
326
|
-
.number()
|
|
327
|
-
.positive()
|
|
328
|
-
.lte(1, ZodErrorMessages.numberMustBeRatio),
|
|
329
|
-
seasonFromEpisodes: z
|
|
330
|
-
.number()
|
|
331
|
-
.positive()
|
|
332
|
-
.lte(1, ZodErrorMessages.numberMustBeRatio)
|
|
333
|
-
.nullish()
|
|
334
|
-
.or(z
|
|
335
|
-
.boolean()
|
|
336
|
-
.refine((value) => value !== true, {
|
|
337
|
-
message: "Expected string, received boolean (true)",
|
|
338
|
-
})
|
|
339
|
-
.transform(() => null)),
|
|
340
|
-
excludeOlder: z
|
|
341
|
-
.string()
|
|
342
|
-
.min(1, ZodErrorMessages.emptyString)
|
|
343
|
-
.transform(transformDurationString)
|
|
344
|
-
.nullish()
|
|
345
|
-
.or(z
|
|
346
|
-
.boolean()
|
|
347
|
-
.refine((value) => value !== true, {
|
|
348
|
-
message: "Expected string, received boolean (true)",
|
|
349
|
-
})
|
|
350
|
-
.transform(() => null)),
|
|
351
|
-
excludeRecentSearch: z
|
|
352
|
-
.string()
|
|
353
|
-
.min(1, ZodErrorMessages.emptyString)
|
|
354
|
-
.transform(transformDurationString)
|
|
355
|
-
.nullish()
|
|
356
|
-
.or(z
|
|
357
|
-
.boolean()
|
|
358
|
-
.refine((value) => value !== true, {
|
|
359
|
-
message: "Expected string, received boolean (true)",
|
|
360
|
-
})
|
|
361
|
-
.transform(() => null)),
|
|
362
|
-
action: z.nativeEnum(Action),
|
|
363
|
-
torrentClients: z
|
|
364
|
-
.array(z.string())
|
|
365
|
-
.optional()
|
|
366
|
-
.transform(transformTorrentClients),
|
|
367
|
-
qbittorrentUrl: z.string().url().nullish(),
|
|
368
|
-
rtorrentRpcUrl: z.string().url().nullish(),
|
|
369
|
-
transmissionRpcUrl: z.string().url().nullish(),
|
|
370
|
-
delugeRpcUrl: z.string().url().nullish(),
|
|
371
|
-
duplicateCategories: z.boolean(),
|
|
372
|
-
notificationWebhookUrls: z
|
|
373
|
-
.array(z.string().url())
|
|
374
|
-
.optional()
|
|
375
|
-
.default([]),
|
|
376
|
-
notificationWebhookUrl: z.string().url().nullish(),
|
|
377
|
-
port: z
|
|
378
|
-
.number()
|
|
379
|
-
.positive()
|
|
380
|
-
.lte(65535)
|
|
381
|
-
.or(z.literal(false).transform(() => null))
|
|
382
|
-
.nullish(),
|
|
383
|
-
host: z.string().ip().nullish(),
|
|
384
|
-
rssCadence: z
|
|
385
|
-
.string()
|
|
386
|
-
.min(1, ZodErrorMessages.emptyString)
|
|
387
|
-
.transform(transformDurationString)
|
|
388
|
-
.nullish()
|
|
389
|
-
.refine((cadence) => process.env.DEV ||
|
|
390
|
-
!cadence ||
|
|
391
|
-
(cadence >= ms("10 minutes") && cadence <= ms("2 hours")), ZodErrorMessages.rssCadenceUnsupported),
|
|
392
|
-
searchCadence: z
|
|
393
|
-
.string()
|
|
394
|
-
.min(1, ZodErrorMessages.emptyString)
|
|
395
|
-
.transform(transformDurationString)
|
|
396
|
-
.nullish()
|
|
397
|
-
.refine((cadence) => process.env.DEV || !cadence || cadence >= ms("1 day"), ZodErrorMessages.searchCadenceUnsupported),
|
|
398
|
-
snatchTimeout: z
|
|
399
|
-
.string()
|
|
400
|
-
.min(1, ZodErrorMessages.emptyString)
|
|
401
|
-
.transform(transformDurationString)
|
|
402
|
-
.nullish(),
|
|
403
|
-
searchTimeout: z
|
|
404
|
-
.string()
|
|
405
|
-
.min(1, ZodErrorMessages.emptyString)
|
|
406
|
-
.transform(transformDurationString)
|
|
407
|
-
.nullish(),
|
|
408
|
-
searchLimit: z.number().nonnegative().nullish(),
|
|
409
|
-
verbose: z.boolean(),
|
|
410
|
-
torrents: z.array(z.string()).optional(),
|
|
411
|
-
blockList: z.array(z.string()).nullish().transform(transformBlocklist),
|
|
412
|
-
apiKey: z.string().min(24).nullish(),
|
|
413
|
-
radarr: z
|
|
414
|
-
.array(z.string().url())
|
|
415
|
-
.nullish()
|
|
416
|
-
.transform((v) => v ?? []),
|
|
417
|
-
sonarr: z
|
|
418
|
-
.array(z.string().url())
|
|
419
|
-
.nullish()
|
|
420
|
-
.transform((v) => v ?? []),
|
|
421
|
-
})
|
|
422
|
-
.strict()
|
|
423
|
-
.refine((config) => {
|
|
424
|
-
if (config.qbittorrentUrl ||
|
|
425
|
-
config.rtorrentRpcUrl ||
|
|
426
|
-
config.transmissionRpcUrl ||
|
|
427
|
-
config.delugeRpcUrl) {
|
|
428
|
-
if (config.torrentClients.length)
|
|
429
|
-
return false;
|
|
430
|
-
logger.warn("qbittorrentUrl, rtorrentRpcUrl, transmissionRpcUrl, and delugeRpcUrl are deprecated, use torrentClients instead.");
|
|
431
|
-
if (config.qbittorrentUrl) {
|
|
432
|
-
config.torrentClients = [
|
|
433
|
-
`${Label.QBITTORRENT}:${config.qbittorrentUrl}`,
|
|
434
|
-
];
|
|
435
|
-
}
|
|
436
|
-
else if (config.rtorrentRpcUrl) {
|
|
437
|
-
config.torrentClients = [
|
|
438
|
-
`${Label.RTORRENT}:${config.rtorrentRpcUrl}`,
|
|
439
|
-
];
|
|
440
|
-
}
|
|
441
|
-
else if (config.transmissionRpcUrl) {
|
|
442
|
-
config.torrentClients = [
|
|
443
|
-
`${Label.TRANSMISSION}:${config.transmissionRpcUrl}`,
|
|
444
|
-
];
|
|
445
|
-
}
|
|
446
|
-
else if (config.delugeRpcUrl) {
|
|
447
|
-
config.torrentClients = [
|
|
448
|
-
`${Label.DELUGE}:${config.delugeRpcUrl}`,
|
|
449
|
-
];
|
|
450
|
-
}
|
|
451
|
-
}
|
|
452
|
-
return true;
|
|
453
|
-
}, "Cannot use qbittorrentUrl, rtorrentRpcUrl, transmissionRpcUrl, or delugeRpcUrl with torrentClients. Only use torrentClients.")
|
|
454
|
-
.refine((config) => {
|
|
455
|
-
if (!config.linkDir)
|
|
456
|
-
return true;
|
|
457
|
-
if (config.linkDirs.length)
|
|
458
|
-
return false;
|
|
459
|
-
logger.warn("linkDir is deprecated, use linkDirs instead.");
|
|
460
|
-
config.linkDirs = [config.linkDir];
|
|
461
|
-
return true;
|
|
462
|
-
}, "You cannot have both linkDir and linkDirs, use linkDirs only.")
|
|
463
|
-
.refine((config) => {
|
|
464
|
-
if (!config.notificationWebhookUrl)
|
|
465
|
-
return true;
|
|
466
|
-
if (config.notificationWebhookUrls.length)
|
|
467
|
-
return false;
|
|
468
|
-
logger.warn("notificationWebhookUrl is deprecated, use notificationWebhookUrls instead.");
|
|
469
|
-
config.notificationWebhookUrls = [config.notificationWebhookUrl];
|
|
470
|
-
return true;
|
|
471
|
-
}, "You cannot have both notificationWebhookUrl and notificationWebhookUrls, use notificationWebhookUrls only.")
|
|
472
|
-
.refine((config) => !config.torrentDir || !config.useClientTorrents, ZodErrorMessages.torrentDirAndUseClientTorrents)
|
|
473
|
-
.refine((config) => !config.useClientTorrents || config.torrentClients.length, ZodErrorMessages.needsClient)
|
|
474
|
-
.refine((config) => {
|
|
475
|
-
if (config.useClientTorrents &&
|
|
476
|
-
config.torrentClients.some((c) => c.startsWith(Label.DELUGE))) {
|
|
477
|
-
logger.warn("Deluge with Docker can have a desyncing issue with the WebUI daemon after prolonged uptime with useClientTorrents, if you notice strange behavior you may need to use torrentDir instead.");
|
|
478
|
-
}
|
|
479
|
-
return true;
|
|
480
|
-
})
|
|
481
|
-
.refine((config) => config.action !== Action.INJECT ||
|
|
482
|
-
config.torrentClients.some((c) => !c.includes(":readonly:")), ZodErrorMessages.clientTypeReadOnly)
|
|
483
|
-
.refine((config) => config.torrentClients.length <= 1 ||
|
|
484
|
-
(!config.torrentDir && !config.torrents?.length), ZodErrorMessages.multipleClientsTorrentFile)
|
|
485
|
-
.refine((config) => config.torrentClients.length <= 1 ||
|
|
486
|
-
config.linkDirs.length ||
|
|
487
|
-
!config.dataDirs.length, ZodErrorMessages.multipleClientsNoLinking)
|
|
488
|
-
.refine((config) => {
|
|
489
|
-
const { uniqueHosts, uniqueWithPathname } = clientsAreUnique(config.torrentClients);
|
|
490
|
-
return uniqueHosts || uniqueWithPathname;
|
|
491
|
-
}, ZodErrorMessages.duplicateClients)
|
|
492
|
-
.refine((config) => !config.searchCadence ||
|
|
493
|
-
!config.excludeRecentSearch ||
|
|
494
|
-
3 * config.searchCadence <= config.excludeRecentSearch, ZodErrorMessages.searchCadenceExcludeRecent)
|
|
495
|
-
.refine((config) => process.env.DEV ||
|
|
496
|
-
!config.searchCadence ||
|
|
497
|
-
(config.excludeOlder &&
|
|
498
|
-
config.excludeRecentSearch &&
|
|
499
|
-
config.excludeOlder >= 2 * config.excludeRecentSearch &&
|
|
500
|
-
config.excludeOlder <= 5 * config.excludeRecentSearch), ZodErrorMessages.excludeRecentOlder)
|
|
501
|
-
.refine((config) => config.fuzzySizeThreshold <= 0.1 ||
|
|
502
|
-
(!config.searchCadence && !config.rssCadence), ZodErrorMessages.fuzzySizeThresholdMax)
|
|
503
|
-
.refine((config) => (!config.searchCadence && !config.rssCadence) ||
|
|
504
|
-
!config.seasonFromEpisodes ||
|
|
505
|
-
config.seasonFromEpisodes >= 0.5, ZodErrorMessages.seasonFromEpisodesMin)
|
|
506
|
-
.refine((config) => {
|
|
507
|
-
if (config.action === Action.SAVE && config.linkDirs.length) {
|
|
508
|
-
logger.error(`You cannot use action 'save' with linkDirs, use 'inject' for cross-seed to perform linking.`);
|
|
509
|
-
}
|
|
510
|
-
return true;
|
|
511
|
-
})
|
|
512
|
-
.refine((config) => {
|
|
513
|
-
if (config.action === Action.INJECT &&
|
|
514
|
-
config.torrentClients.some((c) => c.startsWith(Label.QBITTORRENT)) &&
|
|
515
|
-
!config.flatLinking &&
|
|
516
|
-
config.linkDirs.length) {
|
|
517
|
-
logger.warn(ZodErrorMessages.qBitAutoTMM);
|
|
518
|
-
}
|
|
519
|
-
return true;
|
|
520
|
-
})
|
|
521
|
-
.refine((config) => {
|
|
522
|
-
if (config.includeSingleEpisodes && config.rssCadence) {
|
|
523
|
-
logger.warn(ZodErrorMessages.includeSingleEpisodes);
|
|
524
|
-
}
|
|
525
|
-
return true;
|
|
526
|
-
})
|
|
527
|
-
.refine((config) => config.action === Action.INJECT || config.injectDir === undefined, ZodErrorMessages.injectNeedsInjectMode)
|
|
528
|
-
.refine((config) => config.torrentClients.length || config.action !== Action.INJECT, ZodErrorMessages.injectNeedsClients)
|
|
529
|
-
.refine((config) => {
|
|
530
|
-
if (config.torrentClients.every((c) => c.startsWith(Label.DELUGE)) &&
|
|
531
|
-
config.blockList.some((b) => b.startsWith(`${BlocklistType.TAG}:`))) {
|
|
532
|
-
logger.error(`${BlocklistType.TAG}: blocklisting is deprecated for Deluge, please use ${BlocklistType.CATEGORY}: instead (all ${BlocklistType.TAG}: blocklist items has being automatically converted to ${BlocklistType.CATEGORY}:).`);
|
|
533
|
-
config.blockList = config.blockList.map((b) => {
|
|
534
|
-
if (b.startsWith(`${BlocklistType.TAG}:`)) {
|
|
535
|
-
return b.replace(BlocklistType.TAG, BlocklistType.CATEGORY);
|
|
536
|
-
}
|
|
537
|
-
return b;
|
|
538
|
-
});
|
|
539
|
-
}
|
|
540
|
-
if (config.torrentClients.every((c) => c.startsWith(Label.RTORRENT) ||
|
|
541
|
-
c.startsWith(Label.TRANSMISSION)) &&
|
|
542
|
-
config.blockList.some((b) => b.startsWith(`${BlocklistType.CATEGORY}:`))) {
|
|
543
|
-
logger.error(`Your client does not support ${BlocklistType.CATEGORY}: blocklisting, use ${BlocklistType.TAG}: instead for labels.`);
|
|
544
|
-
return false;
|
|
545
|
-
}
|
|
546
|
-
return true;
|
|
547
|
-
})
|
|
548
|
-
.refine((config) => {
|
|
549
|
-
if (!config.blockList.some((b) => b.startsWith(`${BlocklistType.CATEGORY}:`) ||
|
|
550
|
-
b.startsWith(`${BlocklistType.TAG}:`) ||
|
|
551
|
-
b.startsWith(`${BlocklistType.TRACKER}:`))) {
|
|
552
|
-
return true;
|
|
553
|
-
}
|
|
554
|
-
if (!config.torrentClients.length ||
|
|
555
|
-
!(config.useClientTorrents || config.torrentDir)) {
|
|
556
|
-
logger.error(ZodErrorMessages.blocklistNeedsClient);
|
|
557
|
-
}
|
|
558
|
-
return true;
|
|
559
|
-
}, ZodErrorMessages.blocklistNeedsClient)
|
|
560
|
-
.refine((config) => {
|
|
561
|
-
if (!config.blockList.some((b) => b.startsWith(`${BlocklistType.FOLDER}:`) ||
|
|
562
|
-
b.startsWith(`${BlocklistType.FOLDER_REGEX}:`))) {
|
|
563
|
-
return true;
|
|
564
|
-
}
|
|
565
|
-
if (!config.dataDirs.length) {
|
|
566
|
-
logger.error(ZodErrorMessages.blocklistNeedsDataDirs);
|
|
567
|
-
}
|
|
568
|
-
return true;
|
|
569
|
-
}, ZodErrorMessages.blocklistNeedsDataDirs)
|
|
570
|
-
.refine((config) => config.useClientTorrents ||
|
|
571
|
-
config.torrentDir ||
|
|
572
|
-
config.dataDirs.length ||
|
|
573
|
-
(!config.rssCadence && !config.searchCadence), ZodErrorMessages.needSearchees)
|
|
574
|
-
.refine((config) => config.linkDirs.length ||
|
|
575
|
-
config.matchMode === MatchMode.STRICT ||
|
|
576
|
-
config.action === Action.SAVE, ZodErrorMessages.matchModeNeedsLinkDirs)
|
|
577
|
-
.refine((config) => !config.seasonFromEpisodes ||
|
|
578
|
-
!config.torrentDir ||
|
|
579
|
-
config.torrentClients.length, ZodErrorMessages.ensembleNeedsClient)
|
|
580
|
-
.refine((config) => {
|
|
581
|
-
if (!config.seasonFromEpisodes)
|
|
582
|
-
return true;
|
|
583
|
-
if (config.action === Action.SAVE) {
|
|
584
|
-
logger.warn("Using action 'save' with seasonFromEpisodes is not recommended as these matches are extremely complicated, you will need to run 'cross-seed inject' to add to client.");
|
|
585
|
-
return true;
|
|
586
|
-
}
|
|
587
|
-
return config.linkDirs.length;
|
|
588
|
-
}, ZodErrorMessages.ensembleNeedsLinkDirs)
|
|
589
|
-
.refine((config) => {
|
|
590
|
-
if (config.seasonFromEpisodes && config.seasonFromEpisodes < 1) {
|
|
591
|
-
return config.matchMode === MatchMode.PARTIAL;
|
|
592
|
-
}
|
|
593
|
-
return true;
|
|
594
|
-
}, ZodErrorMessages.ensembleNeedsPartial)
|
|
595
|
-
.refine((config) => {
|
|
596
|
-
if (!config.dataDirs.length)
|
|
597
|
-
return true;
|
|
598
|
-
if (!config.seasonFromEpisodes && !config.includeSingleEpisodes) {
|
|
599
|
-
return true;
|
|
600
|
-
}
|
|
601
|
-
logger.warn(`Using seasonFromEpisodes or includeSingleEpisodes with dataDirs requires a specific data structure${config.maxDataDepth < 3 ? " and likely needs a maxDataDepth of 3" : ""}, please read: https://www.cross-seed.org/docs/tutorials/data-based-matching#setting-up-data-based-matching`);
|
|
602
|
-
return true;
|
|
603
|
-
})
|
|
604
|
-
.refine((config) => {
|
|
605
|
-
for (const linkDir of config.linkDirs) {
|
|
606
|
-
if (isChildPath(linkDir, [config.outputDir]))
|
|
607
|
-
return false;
|
|
608
|
-
if (isChildPath(linkDir, config.dataDirs)) {
|
|
609
|
-
return false;
|
|
610
|
-
}
|
|
611
|
-
if (config.torrentDir &&
|
|
612
|
-
isChildPath(linkDir, [config.torrentDir])) {
|
|
613
|
-
return false;
|
|
614
|
-
}
|
|
615
|
-
}
|
|
616
|
-
return true;
|
|
617
|
-
}, ZodErrorMessages.linkDirsInOtherDirs)
|
|
618
|
-
.refine((config) => {
|
|
619
|
-
for (const dataDir of config.dataDirs) {
|
|
620
|
-
if (isChildPath(dataDir, [config.outputDir]))
|
|
621
|
-
return false;
|
|
622
|
-
if (config.torrentDir &&
|
|
623
|
-
isChildPath(dataDir, [config.torrentDir])) {
|
|
624
|
-
return false;
|
|
625
|
-
}
|
|
626
|
-
if (isChildPath(dataDir, config.linkDirs)) {
|
|
627
|
-
return false;
|
|
628
|
-
}
|
|
629
|
-
}
|
|
630
|
-
return true;
|
|
631
|
-
}, ZodErrorMessages.dataDirsInOtherDirs)
|
|
632
|
-
.refine((config) => {
|
|
633
|
-
if (!config.torrentDir)
|
|
634
|
-
return true;
|
|
635
|
-
if (isChildPath(config.torrentDir, [config.outputDir]))
|
|
636
|
-
return false;
|
|
637
|
-
if (isChildPath(config.torrentDir, config.dataDirs)) {
|
|
638
|
-
return false;
|
|
639
|
-
}
|
|
640
|
-
if (isChildPath(config.torrentDir, config.linkDirs)) {
|
|
641
|
-
return false;
|
|
642
|
-
}
|
|
643
|
-
return true;
|
|
644
|
-
}, ZodErrorMessages.torrentDirInOtherDirs)
|
|
645
|
-
.refine((config) => {
|
|
646
|
-
if (config.torrentDir &&
|
|
647
|
-
isChildPath(config.outputDir, [config.torrentDir])) {
|
|
648
|
-
return false;
|
|
649
|
-
}
|
|
650
|
-
if (isChildPath(config.outputDir, config.dataDirs)) {
|
|
651
|
-
return false;
|
|
652
|
-
}
|
|
653
|
-
if (isChildPath(config.outputDir, config.linkDirs)) {
|
|
654
|
-
return false;
|
|
655
|
-
}
|
|
656
|
-
return true;
|
|
657
|
-
}, ZodErrorMessages.outputDirInOtherDirs)
|
|
658
|
-
.refine((config) => {
|
|
659
|
-
if (!isAbsolute(config.outputDir) ||
|
|
660
|
-
!config.linkDirs.every(isAbsolute) ||
|
|
661
|
-
(config.torrentDir && !isAbsolute(config.torrentDir)) ||
|
|
662
|
-
!config.dataDirs.every(isAbsolute)) {
|
|
663
|
-
logger.warn(ZodErrorMessages.relativePaths);
|
|
664
|
-
}
|
|
665
|
-
return true;
|
|
666
|
-
});
|
|
667
|
-
//# sourceMappingURL=configSchema.js.map
|