cross-seed 6.0.0-3 → 6.0.0-30
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/dist/Result.js +17 -11
- package/dist/Result.js.map +1 -1
- package/dist/action.js +158 -47
- package/dist/action.js.map +1 -1
- package/dist/arr.js +200 -0
- package/dist/arr.js.map +1 -0
- package/dist/clients/Deluge.js +129 -71
- package/dist/clients/Deluge.js.map +1 -1
- package/dist/clients/QBittorrent.js +74 -58
- package/dist/clients/QBittorrent.js.map +1 -1
- package/dist/clients/RTorrent.js +12 -10
- package/dist/clients/RTorrent.js.map +1 -1
- package/dist/clients/TorrentClient.js.map +1 -1
- package/dist/clients/Transmission.js +10 -10
- package/dist/clients/Transmission.js.map +1 -1
- package/dist/cmd.js +16 -16
- package/dist/cmd.js.map +1 -1
- package/dist/config.template.cjs +83 -46
- package/dist/config.template.cjs.map +1 -1
- package/dist/configSchema.js +97 -13
- package/dist/configSchema.js.map +1 -1
- package/dist/configuration.js +3 -0
- package/dist/configuration.js.map +1 -1
- package/dist/constants.js +100 -6
- package/dist/constants.js.map +1 -1
- package/dist/dataFiles.js +4 -5
- package/dist/dataFiles.js.map +1 -1
- package/dist/decide.js +263 -158
- package/dist/decide.js.map +1 -1
- package/dist/diff.js +12 -2
- package/dist/diff.js.map +1 -1
- package/dist/errors.js +5 -2
- package/dist/errors.js.map +1 -1
- package/dist/indexers.js +31 -4
- package/dist/indexers.js.map +1 -1
- package/dist/jobs.js +2 -0
- package/dist/jobs.js.map +1 -1
- package/dist/logger.js +19 -5
- package/dist/logger.js.map +1 -1
- package/dist/migrations/05-caps.js +16 -0
- package/dist/migrations/05-caps.js.map +1 -0
- package/dist/migrations/06-uniqueDecisions.js +29 -0
- package/dist/migrations/06-uniqueDecisions.js.map +1 -0
- package/dist/migrations/migrations.js +11 -1
- package/dist/migrations/migrations.js.map +1 -1
- package/dist/parseTorrent.js +5 -0
- package/dist/parseTorrent.js.map +1 -1
- package/dist/pipeline.js +181 -85
- package/dist/pipeline.js.map +1 -1
- package/dist/preFilter.js +130 -52
- package/dist/preFilter.js.map +1 -1
- package/dist/pushNotifier.js +4 -2
- package/dist/pushNotifier.js.map +1 -1
- package/dist/runtimeConfig.js.map +1 -1
- package/dist/searchee.js +203 -13
- package/dist/searchee.js.map +1 -1
- package/dist/server.js +22 -15
- package/dist/server.js.map +1 -1
- package/dist/startup.js +2 -0
- package/dist/startup.js.map +1 -1
- package/dist/torrent.js +84 -39
- package/dist/torrent.js.map +1 -1
- package/dist/torznab.js +285 -95
- package/dist/torznab.js.map +1 -1
- package/dist/utils.js +155 -31
- package/dist/utils.js.map +1 -1
- package/package.json +3 -1
package/dist/constants.js.map
CHANGED
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"file":"constants.js","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,QAAQ,CAAC;AACvC,MAAM,OAAO,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAC/C,MAAM,cAAc,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAAC;AAElD,MAAM,CAAC,MAAM,YAAY,GAAG,cAAc,CAAC,IAAI,CAAC;AAChD,MAAM,CAAC,MAAM,eAAe,GAAG,cAAc,CAAC,OAAO,CAAC;AACtD,MAAM,CAAC,MAAM,UAAU,GAAG,aAAa,eAAe,EAAE,CAAC;AACzD,MAAM,CAAC,MAAM,WAAW,GAAG,YAAY,CAAC;AACxC,MAAM,CAAC,MAAM,uBAAuB,GAAG,aAAa,CAAC;
|
1
|
+
{"version":3,"file":"constants.js","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,QAAQ,CAAC;AACvC,MAAM,OAAO,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAC/C,MAAM,cAAc,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAAC;AAElD,MAAM,CAAC,MAAM,YAAY,GAAG,cAAc,CAAC,IAAI,CAAC;AAChD,MAAM,CAAC,MAAM,eAAe,GAAG,cAAc,CAAC,OAAO,CAAC;AACtD,MAAM,CAAC,MAAM,UAAU,GAAG,aAAa,eAAe,EAAE,CAAC;AACzD,MAAM,CAAC,MAAM,WAAW,GAAG,YAAY,CAAC;AACxC,MAAM,CAAC,MAAM,uBAAuB,GAAG,aAAa,CAAC;AACrD,MAAM,CAAC,MAAM,cAAc,GAAG,YAAY,CAAC;AAE3C,MAAM,CAAC,MAAM,QAAQ,GACpB,sPAAsP,CAAC;AACxP,MAAM,CAAC,MAAM,iBAAiB,GAAG,uCAAuC,CAAC;AACzE,MAAM,CAAC,MAAM,YAAY,GACxB,yFAAyF,CAAC;AAC3F,MAAM,CAAC,MAAM,WAAW,GACvB,uEAAuE,CAAC;AACzE,MAAM,CAAC,MAAM,WAAW,GACvB,iUAAiU,CAAC;AACnU,MAAM,CAAC,MAAM,mBAAmB,GAC/B,yGAAyG,CAAC;AAC3G,MAAM,CAAC,MAAM,iBAAiB,GAAG,wBAAwB,CAAC;AAC1D,MAAM,CAAC,MAAM,gBAAgB,GAAG,4CAA4C,CAAC;AAC7E,MAAM,CAAC,MAAM,gBAAgB,GAAG,+BAA+B,CAAC;AAChE,MAAM,CAAC,MAAM,WAAW,GAAG,oCAAoC,CAAC;AAChE,MAAM,CAAC,MAAM,mBAAmB,GAC/B,+CAA+C,CAAC;AACjD,MAAM,CAAC,MAAM,gBAAgB,GAAG,oCAAoC,CAAC;AACrE,MAAM,CAAC,MAAM,iBAAiB,GAAG,kCAAkC,CAAC;AACpE,MAAM,CAAC,MAAM,aAAa,GACzB,6KAA6K,CAAC;AAC/K,MAAM,CAAC,MAAM,uBAAuB,GACnC,0CAA0C,CAAC;AAC5C,MAAM,CAAC,MAAM,0BAA0B,GAAG,mBAAmB,CAAC;AAE9D,0EAA0E;AAC1E,MAAM,cAAc,GAAG;IACtB,IAAI,EAAE,oDAAoD;IAC1D,IAAI,EAAE,yBAAyB;IAC/B,EAAE,EAAE,0BAA0B;IAC9B,IAAI,EAAE,aAAa;IACnB,IAAI,EAAE,kBAAkB;IACxB,GAAG,EAAE,sDAAsD;IAC3D,IAAI,EAAE,aAAa;IACnB,IAAI,EAAE,4BAA4B;CAClC,CAAC;AACF,MAAM,UAAU,WAAW,CAAC,KAAa;IACxC,KAAK,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,cAAc,CAAC,EAAE,CAAC;QAC9D,IAAI,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC;YAAE,OAAO,MAAM,CAAC;IACtC,CAAC;IACD,OAAO,IAAI,CAAC;AACb,CAAC;AACD,MAAM,UAAU,iBAAiB,CAAC,KAAa;IAC9C,MAAM,cAAc,GAAG,KAAK,CAAC,MAAM,CAAC;IACpC,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,MAAM,CAAC,cAAc,CAAC,EAAE,CAAC;QACnD,MAAM,QAAQ,GAAG,KAAK,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;QAC1C,IAAI,QAAQ,CAAC,MAAM,KAAK,cAAc;YAAE,OAAO,QAAQ,CAAC;IACzD,CAAC;IACD,OAAO,KAAK,CAAC;AACd,CAAC;AAED,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,CAAC,CAAC;AAChE,MAAM,CAAC,MAAM,qBAAqB,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;AACvE,MAAM,CAAC,MAAM,gBAAgB,GAAG;IAC/B,MAAM;IACN,OAAO;IACP,OAAO;IACP,OAAO;IACP,MAAM;IACN,MAAM;IACN,MAAM;IACN,MAAM;IACN,MAAM;IACN,MAAM;IACN,MAAM;IACN,MAAM;IACN,KAAK;IACL,MAAM;CACN,CAAC;AACF,MAAM,CAAC,MAAM,eAAe,GAAG;IAC9B,OAAO;IACP,OAAO;IACP,MAAM;IACN,OAAO;IACP,OAAO;IACP,MAAM;IACN,OAAO;IACP,OAAO;IACP,MAAM;IACN,MAAM;IACN,MAAM;IACN,MAAM;IACN,MAAM;IACN,MAAM;CACN,CAAC;AACF,MAAM,CAAC,MAAM,cAAc,GAAG;IAC7B,GAAG,gBAAgB;IACnB,GAAG,gBAAgB;IACnB,GAAG,eAAe;IAClB,GAAG,qBAAqB;CACxB,CAAC;AAEF,MAAM,CAAC,MAAM,oBAAoB,GAAG,eAAe,CAAC;AACpD,MAAM,CAAC,MAAM,eAAe,GAAG,gBAAgB,CAAC;AAEhD,MAAM,CAAN,IAAY,MAGX;AAHD,WAAY,MAAM;IACjB,uBAAa,CAAA;IACb,2BAAiB,CAAA;AAClB,CAAC,EAHW,MAAM,KAAN,MAAM,QAGjB;AAED,MAAM,CAAN,IAAY,eAKX;AALD,WAAY,eAAe;IAC1B,uCAAoB,CAAA;IACpB,sCAAmB,CAAA;IACnB,oDAAiC,CAAA;IACjC,gEAA6C,CAAA;AAC9C,CAAC,EALW,eAAe,KAAf,eAAe,QAK1B;AAED,MAAM,CAAN,IAAY,UAEX;AAFD,WAAY,UAAU;IACrB,6BAAe,CAAA;AAChB,CAAC,EAFW,UAAU,KAAV,UAAU,QAErB;AAID,MAAM,CAAN,IAAY,QAkBX;AAlBD,WAAY,QAAQ;IACnB,2BAAe,CAAA;IACf,+CAAmC,CAAA;IACnC,2CAA+B,CAAA;IAC/B,uDAA2C,CAAA;IAC3C,2CAA+B,CAAA;IAC/B,2DAA+C,CAAA;IAC/C,iDAAqC,CAAA;IACrC,+CAAmC,CAAA;IACnC,uCAA2B,CAAA;IAC3B,yCAA6B,CAAA;IAC7B,iEAAqD,CAAA;IACrD,qDAAyC,CAAA;IACzC,6DAAiD,CAAA;IACjD,+CAAmC,CAAA;IACnC,6DAAiD,CAAA;IACjD,uDAA2C,CAAA;IAC3C,+CAAmC,CAAA;AACpC,CAAC,EAlBW,QAAQ,KAAR,QAAQ,QAkBnB;AAKD,MAAM,UAAU,oBAAoB,CACnC,QAAkB;IAElB,OAAO,CACN,QAAQ,KAAK,QAAQ,CAAC,KAAK;QAC3B,QAAQ,KAAK,QAAQ,CAAC,eAAe;QACrC,QAAQ,KAAK,QAAQ,CAAC,aAAa,CACnC,CAAC;AACH,CAAC;AACD,MAAM,UAAU,gBAAgB,CAAC,QAAkB;IAClD,OAAO,CACN,QAAQ,KAAK,QAAQ,CAAC,sBAAsB;QAC5C,QAAQ,KAAK,QAAQ,CAAC,mBAAmB;QACzC,QAAQ,KAAK,QAAQ,CAAC,eAAe;QACrC,QAAQ,KAAK,QAAQ,CAAC,sBAAsB;QAC5C,QAAQ,KAAK,QAAQ,CAAC,WAAW,CACjC,CAAC;AACH,CAAC;AAED,MAAM,CAAN,IAAY,SAIX;AAJD,WAAY,SAAS;IACpB,0BAAa,CAAA;IACb,4BAAe,CAAA;IACf,gCAAmB,CAAA;AACpB,CAAC,EAJW,SAAS,KAAT,SAAS,QAIpB;AAED,MAAM,CAAN,IAAY,QAGX;AAHD,WAAY,QAAQ;IACnB,+BAAmB,CAAA;IACnB,iCAAqB,CAAA;AACtB,CAAC,EAHW,QAAQ,KAAR,QAAQ,QAGnB;AAED,MAAM,CAAC,MAAM,0BAA0B,GAAG;IACzC,QAAQ;IACR,OAAO;IACP,MAAM;IACN,OAAO;IACP,aAAa;IACb,UAAU;CACV,CAAC"}
|
package/dist/dataFiles.js
CHANGED
@@ -1,15 +1,14 @@
|
|
1
1
|
import { readdirSync, statSync } from "fs";
|
2
2
|
import { basename, extname, join } from "path";
|
3
|
-
import { IGNORED_FOLDERS_SUBSTRINGS,
|
3
|
+
import { IGNORED_FOLDERS_SUBSTRINGS, VIDEO_EXTENSIONS } from "./constants.js";
|
4
4
|
import { getRuntimeConfig } from "./runtimeConfig.js";
|
5
5
|
function shouldIgnorePathHeuristically(root, isDir) {
|
6
|
-
const
|
6
|
+
const searchBasename = basename(root);
|
7
7
|
if (isDir) {
|
8
|
-
return
|
9
|
-
IGNORED_FOLDERS_REGEX.test(folderBaseName));
|
8
|
+
return IGNORED_FOLDERS_SUBSTRINGS.includes(searchBasename.toLowerCase());
|
10
9
|
}
|
11
10
|
else {
|
12
|
-
return !VIDEO_EXTENSIONS.includes(extname(
|
11
|
+
return !VIDEO_EXTENSIONS.includes(extname(searchBasename));
|
13
12
|
}
|
14
13
|
}
|
15
14
|
export function findPotentialNestedRoots(root, depth, isDirHint) {
|
package/dist/dataFiles.js.map
CHANGED
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"file":"dataFiles.js","sourceRoot":"","sources":["../src/dataFiles.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,IAAI,CAAC;AAC3C,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC/C,OAAO,
|
1
|
+
{"version":3,"file":"dataFiles.js","sourceRoot":"","sources":["../src/dataFiles.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,IAAI,CAAC;AAC3C,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC/C,OAAO,EAAE,0BAA0B,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAC9E,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAEtD,SAAS,6BAA6B,CAAC,IAAY,EAAE,KAAc;IAClE,MAAM,cAAc,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC;IACtC,IAAI,KAAK,EAAE,CAAC;QACX,OAAO,0BAA0B,CAAC,QAAQ,CACzC,cAAc,CAAC,WAAW,EAAE,CAC5B,CAAC;IACH,CAAC;SAAM,CAAC;QACP,OAAO,CAAC,gBAAgB,CAAC,QAAQ,CAAC,OAAO,CAAC,cAAc,CAAC,CAAC,CAAC;IAC5D,CAAC;AACF,CAAC;AACD,MAAM,UAAU,wBAAwB,CACvC,IAAY,EACZ,KAAa,EACb,SAAmB;IAEnB,MAAM,KAAK,GACV,SAAS,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,WAAW,EAAE,CAAC;IACpE,IAAI,KAAK,IAAI,CAAC,IAAI,6BAA6B,CAAC,IAAI,EAAE,KAAK,CAAC,EAAE,CAAC;QAC9D,OAAO,EAAE,CAAC;IACX,CAAC;IACD,wCAAwC;SACnC,IAAI,KAAK,GAAG,CAAC,IAAI,KAAK,EAAE,CAAC;QAC7B,MAAM,cAAc,GAAG,WAAW,CAAC,IAAI,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;QAClE,MAAM,cAAc,GAAG,cAAc,CAAC,OAAO,CAAC,CAAC,MAAM,EAAE,EAAE,CACxD,wBAAwB,CACvB,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC,EACvB,KAAK,GAAG,CAAC,EACT,MAAM,CAAC,WAAW,EAAE,CACpB,CACD,CAAC;QACF,OAAO,CAAC,IAAI,EAAE,GAAG,cAAc,CAAC,CAAC;IAClC,CAAC;SAAM,CAAC;QACP,OAAO,CAAC,IAAI,CAAC,CAAC;IACf,CAAC;AACF,CAAC;AAED,MAAM,UAAU,4BAA4B;IAC3C,MAAM,EAAE,QAAQ,EAAE,YAAY,EAAE,GAAG,gBAAgB,EAAE,CAAC;IACtD,OAAO,QAAQ,CAAC,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CACnC,WAAW,CAAC,OAAO,CAAC;SAClB,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;SACtC,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,wBAAwB,CAAC,IAAI,EAAE,YAAY,CAAC,CAAC,CACjE,CAAC;AACH,CAAC"}
|
package/dist/decide.js
CHANGED
@@ -1,20 +1,17 @@
|
|
1
|
-
import { existsSync, writeFileSync } from "fs";
|
1
|
+
import { existsSync, statSync, utimesSync, writeFileSync } from "fs";
|
2
2
|
import path from "path";
|
3
3
|
import { appDir } from "./configuration.js";
|
4
|
-
import { Decision, MatchMode, RELEASE_GROUP_REGEX, REPACK_PROPER_REGEX, TORRENT_CACHE_FOLDER, } from "./constants.js";
|
4
|
+
import { Decision, isAnyMatchedDecision, isStaticDecision, MatchMode, RELEASE_GROUP_REGEX, REPACK_PROPER_REGEX, RES_STRICT_REGEX, parseSource, TORRENT_CACHE_FOLDER, ANIME_GROUP_REGEX, SEASON_REGEX, } from "./constants.js";
|
5
5
|
import { db } from "./db.js";
|
6
6
|
import { Label, logger } from "./logger.js";
|
7
|
-
import {
|
7
|
+
import { Metafile } from "./parseTorrent.js";
|
8
|
+
import { findBlockedStringInReleaseMaybe, isSingleEpisode, } from "./preFilter.js";
|
8
9
|
import { getRuntimeConfig } from "./runtimeConfig.js";
|
9
|
-
import { parseTorrentFromFilename,
|
10
|
-
import {
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
label: Label.DECIDE,
|
15
|
-
message: `${name} - no match for ${tracker} torrent ${Title} - ${reason}`,
|
16
|
-
});
|
17
|
-
}
|
10
|
+
import { parseTorrentFromFilename, snatch, SnatchError } from "./torrent.js";
|
11
|
+
import { extractInt, getFuzzySizeFactor, getLogString, getMediaType, getMinSizeRatio, sanitizeInfoHash, stripExtension, wait, } from "./utils.js";
|
12
|
+
import ms from "ms";
|
13
|
+
function logDecision(searchee, candidate, decision, metafile, tracker) {
|
14
|
+
const { matchMode } = getRuntimeConfig();
|
18
15
|
let reason;
|
19
16
|
switch (decision) {
|
20
17
|
case Decision.MATCH_PARTIAL:
|
@@ -23,8 +20,17 @@ const createReasonLogger = (Title, tracker, name) => (decision, cached, searchee
|
|
23
20
|
return;
|
24
21
|
case Decision.MATCH:
|
25
22
|
return;
|
23
|
+
case Decision.FUZZY_SIZE_MISMATCH:
|
24
|
+
reason = `the total sizes are outside of the fuzzySizeThreshold range: ${Math.abs((candidate.size - searchee.length) / searchee.length).toFixed(3)} > ${getFuzzySizeFactor()}`;
|
25
|
+
break;
|
26
26
|
case Decision.SIZE_MISMATCH:
|
27
|
-
reason = `
|
27
|
+
reason = `some files are missing or have different sizes${compareFileTreesPartial(metafile, searchee) ? ` (will match in partial match mode)` : ""}`;
|
28
|
+
break;
|
29
|
+
case Decision.PARTIAL_SIZE_MISMATCH:
|
30
|
+
reason = `too many files are missing or have different sizes: torrent progress would be ${(getPartialSizeRatio(metafile, searchee) * 100).toFixed(3)}%`;
|
31
|
+
break;
|
32
|
+
case Decision.RESOLUTION_MISMATCH:
|
33
|
+
reason = `its resolution does not match: ${searchee.title.match(RES_STRICT_REGEX)?.groups?.res} -> ${candidate.name.match(RES_STRICT_REGEX)?.groups?.res}`;
|
28
34
|
break;
|
29
35
|
case Decision.NO_DOWNLOAD_LINK:
|
30
36
|
reason = "it doesn't have a download link";
|
@@ -35,34 +41,41 @@ const createReasonLogger = (Title, tracker, name) => (decision, cached, searchee
|
|
35
41
|
case Decision.DOWNLOAD_FAILED:
|
36
42
|
reason = "the torrent file failed to download";
|
37
43
|
break;
|
44
|
+
case Decision.MAGNET_LINK:
|
45
|
+
reason = "the torrent is a magnet link";
|
46
|
+
break;
|
38
47
|
case Decision.INFO_HASH_ALREADY_EXISTS:
|
39
48
|
reason = "the info hash matches a torrent you already have";
|
40
49
|
break;
|
41
50
|
case Decision.FILE_TREE_MISMATCH:
|
42
|
-
reason =
|
51
|
+
reason = `it has a different file tree${matchMode === MatchMode.SAFE ? " (will match in risky or partial match mode)" : ""}`;
|
43
52
|
break;
|
44
53
|
case Decision.RELEASE_GROUP_MISMATCH:
|
45
|
-
reason = `it has a different release group
|
46
|
-
.match(RELEASE_GROUP_REGEX)
|
47
|
-
?.trim()} -> ${candidate.name
|
48
|
-
.match(RELEASE_GROUP_REGEX)
|
49
|
-
?.trim()}
|
54
|
+
reason = `it has a different release group: ${stripExtension(searchee.title)
|
55
|
+
.match(RELEASE_GROUP_REGEX)
|
56
|
+
?.groups?.group?.trim()} -> ${stripExtension(candidate.name)
|
57
|
+
.match(RELEASE_GROUP_REGEX)
|
58
|
+
?.groups?.group?.trim()}`;
|
50
59
|
break;
|
51
60
|
case Decision.PROPER_REPACK_MISMATCH:
|
52
|
-
reason = `one is a different subsequent release
|
53
|
-
"INITIAL"} -> ${candidate.name.match(REPACK_PROPER_REGEX)?.groups?.type ?? "INITIAL"}
|
61
|
+
reason = `one is a different subsequent release: ${searchee.title.match(REPACK_PROPER_REGEX)?.groups?.type ??
|
62
|
+
"INITIAL"} -> ${candidate.name.match(REPACK_PROPER_REGEX)?.groups?.type ?? "INITIAL"}`;
|
63
|
+
break;
|
64
|
+
case Decision.SOURCE_MISMATCH:
|
65
|
+
reason = `it has a different source: ${parseSource(searchee.title)} -> ${parseSource(candidate.name)}`;
|
54
66
|
break;
|
55
67
|
case Decision.BLOCKED_RELEASE:
|
56
|
-
reason = `it matches the blocklist
|
68
|
+
reason = `it matches the blocklist: "${findBlockedStringInReleaseMaybe(searchee, getRuntimeConfig().blockList)}"`;
|
57
69
|
break;
|
58
70
|
default:
|
59
71
|
reason = decision;
|
60
72
|
break;
|
61
73
|
}
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
};
|
74
|
+
logger.verbose({
|
75
|
+
label: Label.DECIDE,
|
76
|
+
message: `${getLogString(searchee)} - no match for ${tracker} torrent ${candidate.name}${metafile ? ` [${sanitizeInfoHash(metafile.infoHash)}]` : ""} - ${reason}`,
|
77
|
+
});
|
78
|
+
}
|
66
79
|
export function compareFileTrees(candidate, searchee) {
|
67
80
|
const cmp = (elOfA, elOfB) => {
|
68
81
|
const lengthsAreEqual = elOfB.length === elOfA.length;
|
@@ -72,13 +85,18 @@ export function compareFileTrees(candidate, searchee) {
|
|
72
85
|
return candidate.files.every((elOfA) => searchee.files.some((elOfB) => cmp(elOfA, elOfB)));
|
73
86
|
}
|
74
87
|
export function compareFileTreesIgnoringNames(candidate, searchee) {
|
75
|
-
const
|
76
|
-
|
77
|
-
|
78
|
-
|
88
|
+
for (const candidateFile of candidate.files) {
|
89
|
+
let matchedSearcheeFiles = searchee.files.filter((searcheeFile) => searcheeFile.length === candidateFile.length);
|
90
|
+
if (matchedSearcheeFiles.length > 1) {
|
91
|
+
matchedSearcheeFiles = matchedSearcheeFiles.filter((searcheeFile) => searcheeFile.name === candidateFile.name);
|
92
|
+
}
|
93
|
+
if (matchedSearcheeFiles.length === 0) {
|
94
|
+
return false;
|
95
|
+
}
|
96
|
+
}
|
97
|
+
return true;
|
79
98
|
}
|
80
|
-
export function
|
81
|
-
const { fuzzySizeThreshold } = getRuntimeConfig();
|
99
|
+
export function getPartialSizeRatio(candidate, searchee) {
|
82
100
|
let matchedSizes = 0;
|
83
101
|
for (const candidateFile of candidate.files) {
|
84
102
|
const searcheeHasFileSize = searchee.files.some((searcheeFile) => searcheeFile.length === candidateFile.length);
|
@@ -86,10 +104,9 @@ export function compareFileTreesPartialIgnoringNames(candidate, searchee) {
|
|
86
104
|
matchedSizes += candidateFile.length;
|
87
105
|
}
|
88
106
|
}
|
89
|
-
return matchedSizes / candidate.length
|
107
|
+
return matchedSizes / candidate.length;
|
90
108
|
}
|
91
109
|
export function compareFileTreesPartial(candidate, searchee) {
|
92
|
-
const { fuzzySizeThreshold } = getRuntimeConfig();
|
93
110
|
let matchedSizes = 0;
|
94
111
|
for (const candidateFile of candidate.files) {
|
95
112
|
let matchedSearcheeFiles = searchee.files.filter((searcheeFile) => searcheeFile.length === candidateFile.length);
|
@@ -102,193 +119,281 @@ export function compareFileTreesPartial(candidate, searchee) {
|
|
102
119
|
}
|
103
120
|
const totalPieces = Math.ceil(candidate.length / candidate.pieceLength);
|
104
121
|
const availablePieces = Math.floor(matchedSizes / candidate.pieceLength);
|
105
|
-
return availablePieces / totalPieces >=
|
122
|
+
return availablePieces / totalPieces >= getMinSizeRatio();
|
106
123
|
}
|
107
|
-
function
|
108
|
-
const
|
124
|
+
function fuzzySizeDoesMatch(resultSize, searchee) {
|
125
|
+
const fuzzySizeFactor = getFuzzySizeFactor();
|
109
126
|
const { length } = searchee;
|
110
|
-
const lowerBound = length -
|
111
|
-
const upperBound = length +
|
127
|
+
const lowerBound = length - fuzzySizeFactor * length;
|
128
|
+
const upperBound = length + fuzzySizeFactor * length;
|
112
129
|
return resultSize >= lowerBound && resultSize <= upperBound;
|
113
130
|
}
|
114
|
-
function
|
115
|
-
const
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
(
|
121
|
-
|
122
|
-
|
123
|
-
|
131
|
+
function resolutionDoesMatch(searcheeTitle, candidateName) {
|
132
|
+
const searcheeRes = searcheeTitle
|
133
|
+
.match(RES_STRICT_REGEX)
|
134
|
+
?.groups?.res?.trim()
|
135
|
+
?.toLowerCase();
|
136
|
+
const candidateRes = candidateName
|
137
|
+
.match(RES_STRICT_REGEX)
|
138
|
+
?.groups?.res?.trim()
|
139
|
+
?.toLowerCase();
|
140
|
+
if (!searcheeRes || !candidateRes)
|
141
|
+
return true;
|
142
|
+
return extractInt(searcheeRes) === extractInt(candidateRes);
|
124
143
|
}
|
125
|
-
function releaseGroupDoesMatch(
|
126
|
-
const searcheeReleaseGroup =
|
127
|
-
.match(RELEASE_GROUP_REGEX)
|
128
|
-
?.trim()
|
144
|
+
function releaseGroupDoesMatch(searcheeTitle, candidateName) {
|
145
|
+
const searcheeReleaseGroup = stripExtension(searcheeTitle)
|
146
|
+
.match(RELEASE_GROUP_REGEX)
|
147
|
+
?.groups?.group?.trim()
|
148
|
+
?.toLowerCase();
|
149
|
+
const candidateReleaseGroup = stripExtension(candidateName)
|
150
|
+
.match(RELEASE_GROUP_REGEX)
|
151
|
+
?.groups?.group?.trim()
|
152
|
+
?.toLowerCase();
|
153
|
+
if (!searcheeReleaseGroup || !candidateReleaseGroup) {
|
154
|
+
return true; // Pass if missing -GRP
|
155
|
+
}
|
156
|
+
if (searcheeReleaseGroup.startsWith(candidateReleaseGroup) ||
|
157
|
+
candidateReleaseGroup.startsWith(searcheeReleaseGroup)) {
|
158
|
+
return true; // -GRP matches
|
159
|
+
}
|
160
|
+
// Anime naming can cause weird things to match as release groups
|
161
|
+
const searcheeAnimeGroup = searcheeTitle
|
162
|
+
.match(ANIME_GROUP_REGEX)
|
163
|
+
?.groups?.group?.trim()
|
129
164
|
?.toLowerCase();
|
130
|
-
const
|
131
|
-
.match(
|
132
|
-
?.trim()
|
165
|
+
const candidateAnimeGroup = candidateName
|
166
|
+
.match(ANIME_GROUP_REGEX)
|
167
|
+
?.groups?.group?.trim()
|
133
168
|
?.toLowerCase();
|
134
|
-
if (
|
169
|
+
if (!searcheeAnimeGroup && !candidateAnimeGroup) {
|
170
|
+
return false;
|
171
|
+
}
|
172
|
+
// Most checks will never get here, below are rare edge cases
|
173
|
+
if (searcheeAnimeGroup === candidateAnimeGroup) {
|
135
174
|
return true;
|
136
175
|
}
|
137
|
-
|
138
|
-
|
139
|
-
|
176
|
+
if (searcheeAnimeGroup && searcheeAnimeGroup === candidateReleaseGroup) {
|
177
|
+
return true;
|
178
|
+
}
|
179
|
+
if (candidateAnimeGroup && searcheeReleaseGroup === candidateAnimeGroup) {
|
180
|
+
return true;
|
140
181
|
}
|
141
|
-
return
|
182
|
+
return false;
|
183
|
+
}
|
184
|
+
function sourceDoesMatch(searcheeTitle, candidateName) {
|
185
|
+
const searcheeSource = parseSource(searcheeTitle);
|
186
|
+
const candidateSource = parseSource(candidateName);
|
187
|
+
if (!searcheeSource || !candidateSource)
|
188
|
+
return true;
|
189
|
+
return searcheeSource === candidateSource;
|
142
190
|
}
|
143
|
-
async function assessCandidateHelper(
|
144
|
-
const {
|
191
|
+
async function assessCandidateHelper(metaOrCandidate, searchee, hashesToExclude) {
|
192
|
+
const { blockList, includeSingleEpisodes, matchMode } = getRuntimeConfig();
|
193
|
+
// When metaOrCandidate is a Metafile, skip straight to the
|
194
|
+
// main matching algorithms as we don't need pre-download filtering.
|
195
|
+
const isCandidate = !(metaOrCandidate instanceof Metafile);
|
196
|
+
const name = metaOrCandidate.name;
|
197
|
+
const size = isCandidate ? metaOrCandidate.size : metaOrCandidate.length;
|
198
|
+
if (isCandidate) {
|
199
|
+
if (!releaseGroupDoesMatch(searchee.title, name)) {
|
200
|
+
return { decision: Decision.RELEASE_GROUP_MISMATCH };
|
201
|
+
}
|
202
|
+
if (!resolutionDoesMatch(searchee.title, name)) {
|
203
|
+
return { decision: Decision.RESOLUTION_MISMATCH };
|
204
|
+
}
|
205
|
+
if (!sourceDoesMatch(searchee.title, name)) {
|
206
|
+
return { decision: Decision.SOURCE_MISMATCH };
|
207
|
+
}
|
208
|
+
if (size && !fuzzySizeDoesMatch(size, searchee)) {
|
209
|
+
return { decision: Decision.FUZZY_SIZE_MISMATCH };
|
210
|
+
}
|
211
|
+
if (!metaOrCandidate.link) {
|
212
|
+
return { decision: Decision.NO_DOWNLOAD_LINK };
|
213
|
+
}
|
214
|
+
}
|
145
215
|
if (findBlockedStringInReleaseMaybe(searchee, blockList)) {
|
146
216
|
return { decision: Decision.BLOCKED_RELEASE };
|
147
217
|
}
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
218
|
+
let metafile;
|
219
|
+
if (isCandidate) {
|
220
|
+
let res = await snatch(metaOrCandidate);
|
221
|
+
if (res.isErr()) {
|
222
|
+
const e = res.unwrapErr();
|
223
|
+
if ([Label.ANNOUNCE, Label.RSS].includes(searchee.label) &&
|
224
|
+
![SnatchError.RATE_LIMITED, SnatchError.MAGNET_LINK].includes(e)) {
|
225
|
+
await wait(ms("30 seconds"));
|
226
|
+
res = await snatch(metaOrCandidate);
|
227
|
+
}
|
228
|
+
if (res.isErr()) {
|
229
|
+
const err = res.unwrapErr();
|
230
|
+
return err === SnatchError.MAGNET_LINK
|
231
|
+
? { decision: Decision.MAGNET_LINK }
|
232
|
+
: err === SnatchError.RATE_LIMITED
|
233
|
+
? { decision: Decision.RATE_LIMITED }
|
234
|
+
: { decision: Decision.DOWNLOAD_FAILED };
|
235
|
+
}
|
236
|
+
}
|
237
|
+
metafile = res.unwrap();
|
238
|
+
cacheTorrentFile(metafile);
|
239
|
+
metaOrCandidate.size = metafile.length; // Trackers can be wrong
|
155
240
|
}
|
156
|
-
|
157
|
-
|
241
|
+
else {
|
242
|
+
metafile = metaOrCandidate;
|
158
243
|
}
|
159
|
-
|
160
|
-
|
161
|
-
return result.unwrapErrOrThrow() === SnatchError.RATE_LIMITED
|
162
|
-
? { decision: Decision.RATE_LIMITED }
|
163
|
-
: { decision: Decision.DOWNLOAD_FAILED };
|
244
|
+
if (hashesToExclude.includes(metafile.infoHash)) {
|
245
|
+
return { decision: Decision.INFO_HASH_ALREADY_EXISTS, metafile };
|
164
246
|
}
|
165
|
-
|
166
|
-
if (
|
167
|
-
|
247
|
+
// Prevent candidate episodes from matching searchee season packs
|
248
|
+
if (!includeSingleEpisodes &&
|
249
|
+
SEASON_REGEX.test(searchee.title) &&
|
250
|
+
isSingleEpisode(metafile, getMediaType(metafile))) {
|
251
|
+
return { decision: Decision.FILE_TREE_MISMATCH, metafile };
|
168
252
|
}
|
169
|
-
const
|
170
|
-
const perfectMatch = compareFileTrees(candidateMeta, searchee);
|
253
|
+
const perfectMatch = compareFileTrees(metafile, searchee);
|
171
254
|
if (perfectMatch) {
|
172
|
-
return { decision: Decision.MATCH, metafile
|
255
|
+
return { decision: Decision.MATCH, metafile };
|
173
256
|
}
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
return {
|
178
|
-
decision: Decision.MATCH_SIZE_ONLY,
|
179
|
-
metafile: candidateMeta,
|
180
|
-
};
|
257
|
+
const sizeMatch = compareFileTreesIgnoringNames(metafile, searchee);
|
258
|
+
if (sizeMatch && matchMode !== MatchMode.SAFE) {
|
259
|
+
return { decision: Decision.MATCH_SIZE_ONLY, metafile };
|
181
260
|
}
|
182
261
|
if (matchMode === MatchMode.PARTIAL) {
|
183
|
-
const partialSizeMatch =
|
262
|
+
const partialSizeMatch = getPartialSizeRatio(metafile, searchee) >= getMinSizeRatio();
|
184
263
|
if (!partialSizeMatch) {
|
185
|
-
return { decision: Decision.
|
264
|
+
return { decision: Decision.PARTIAL_SIZE_MISMATCH, metafile };
|
186
265
|
}
|
187
|
-
const partialMatch = compareFileTreesPartial(
|
266
|
+
const partialMatch = compareFileTreesPartial(metafile, searchee);
|
188
267
|
if (partialMatch) {
|
189
|
-
return {
|
190
|
-
decision: Decision.MATCH_PARTIAL,
|
191
|
-
metafile: candidateMeta,
|
192
|
-
};
|
268
|
+
return { decision: Decision.MATCH_PARTIAL, metafile };
|
193
269
|
}
|
194
270
|
}
|
195
271
|
else if (!sizeMatch) {
|
196
|
-
return { decision: Decision.SIZE_MISMATCH };
|
272
|
+
return { decision: Decision.SIZE_MISMATCH, metafile };
|
197
273
|
}
|
198
|
-
return { decision: Decision.FILE_TREE_MISMATCH };
|
274
|
+
return { decision: Decision.FILE_TREE_MISMATCH, metafile };
|
199
275
|
}
|
200
276
|
function existsInTorrentCache(infoHash) {
|
201
|
-
|
277
|
+
const torrentPath = path.join(appDir(), TORRENT_CACHE_FOLDER, `${infoHash}.cached.torrent`);
|
278
|
+
if (!existsSync(torrentPath))
|
279
|
+
return false;
|
280
|
+
utimesSync(torrentPath, new Date(), statSync(torrentPath).mtime);
|
281
|
+
return true;
|
202
282
|
}
|
203
283
|
async function getCachedTorrentFile(infoHash) {
|
204
284
|
return parseTorrentFromFilename(path.join(appDir(), TORRENT_CACHE_FOLDER, `${infoHash}.cached.torrent`));
|
205
285
|
}
|
206
286
|
function cacheTorrentFile(meta) {
|
207
|
-
|
287
|
+
const torrentPath = path.join(appDir(), TORRENT_CACHE_FOLDER, `${meta.infoHash}.cached.torrent`);
|
288
|
+
if (existsInTorrentCache(meta.infoHash))
|
289
|
+
return;
|
290
|
+
writeFileSync(torrentPath, meta.encode());
|
208
291
|
}
|
209
|
-
async function assessAndSaveResults(
|
210
|
-
const assessment = await assessCandidateHelper(
|
211
|
-
if (assessment.decision === Decision.MATCH ||
|
212
|
-
assessment.decision === Decision.MATCH_SIZE_ONLY ||
|
213
|
-
assessment.decision === Decision.MATCH_PARTIAL) {
|
214
|
-
cacheTorrentFile(assessment.metafile);
|
215
|
-
}
|
292
|
+
async function assessAndSaveResults(metaOrCandidate, searchee, guid, infoHashesToExclude, firstSeen) {
|
293
|
+
const assessment = await assessCandidateHelper(metaOrCandidate, searchee, infoHashesToExclude);
|
216
294
|
await db.transaction(async (trx) => {
|
217
|
-
const now = Date.now();
|
218
295
|
const { id } = await trx("searchee")
|
219
296
|
.select("id")
|
220
|
-
.where({ name: searchee.
|
297
|
+
.where({ name: searchee.title })
|
221
298
|
.first();
|
222
|
-
await trx("decision")
|
299
|
+
await trx("decision")
|
300
|
+
.insert({
|
223
301
|
searchee_id: id,
|
224
302
|
guid: guid,
|
303
|
+
info_hash: assessment.metafile?.infoHash ?? null,
|
225
304
|
decision: assessment.decision,
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
first_seen: now,
|
233
|
-
});
|
305
|
+
first_seen: firstSeen,
|
306
|
+
last_seen: Date.now(),
|
307
|
+
fuzzy_size_factor: getFuzzySizeFactor(),
|
308
|
+
})
|
309
|
+
.onConflict(["searchee_id", "guid"])
|
310
|
+
.merge();
|
234
311
|
});
|
235
312
|
return assessment;
|
236
313
|
}
|
314
|
+
/**
|
315
|
+
* Some trackers have alt titles which get their own guid but resolve to same torrent
|
316
|
+
* @param guid The guid of the candidate
|
317
|
+
* @returns The info hash of the torrent if found
|
318
|
+
*/
|
319
|
+
async function fuzzyGuidLookup(guid) {
|
320
|
+
if (!guid.includes(".tv/torrent/"))
|
321
|
+
return;
|
322
|
+
const torrentIdStr = guid.match(/\.tv\/torrent\/(\d+)\/group/)?.[1];
|
323
|
+
if (!torrentIdStr)
|
324
|
+
return;
|
325
|
+
return (await db("decision")
|
326
|
+
.select({ infoHash: "info_hash" })
|
327
|
+
.where("guid", "like", `%.tv/torrent/${torrentIdStr}/group%`)
|
328
|
+
.whereNotNull("info_hash")
|
329
|
+
.first())?.infoHash;
|
330
|
+
}
|
237
331
|
async function assessCandidateCaching(candidate, searchee, infoHashesToExclude) {
|
238
332
|
const { guid, name, tracker } = candidate;
|
239
|
-
const logReason = createReasonLogger(name, tracker, searchee.name);
|
240
333
|
const cacheEntry = await db("decision")
|
241
334
|
.select({
|
242
|
-
decision: "decision.decision",
|
243
|
-
infoHash: "decision.info_hash",
|
244
335
|
id: "decision.id",
|
336
|
+
infoHash: "decision.info_hash",
|
337
|
+
decision: "decision.decision",
|
338
|
+
firstSeen: "decision.first_seen",
|
339
|
+
fuzzySizeFactor: "decision.fuzzy_size_factor",
|
245
340
|
})
|
246
341
|
.join("searchee", "decision.searchee_id", "searchee.id")
|
247
|
-
.where({ name: searchee.
|
342
|
+
.where({ name: searchee.title, guid })
|
248
343
|
.first();
|
344
|
+
const metaInfoHash = (await db("decision")
|
345
|
+
.select({ infoHash: "info_hash" })
|
346
|
+
.where({ guid })
|
347
|
+
.whereNotNull("info_hash")
|
348
|
+
.first())?.infoHash ?? (await fuzzyGuidLookup(guid));
|
349
|
+
const metaOrCandidate = metaInfoHash
|
350
|
+
? existsInTorrentCache(metaInfoHash)
|
351
|
+
? await getCachedTorrentFile(metaInfoHash)
|
352
|
+
: candidate
|
353
|
+
: candidate;
|
354
|
+
if (metaOrCandidate instanceof Metafile) {
|
355
|
+
logger.verbose({
|
356
|
+
label: Label.DECIDE,
|
357
|
+
message: `Using cached torrent ${sanitizeInfoHash(metaInfoHash)} for ${tracker} assessment ${name}`,
|
358
|
+
});
|
359
|
+
candidate.size = metaOrCandidate.length; // Trackers can be wrong
|
360
|
+
}
|
249
361
|
let assessment;
|
250
|
-
if (!cacheEntry?.decision
|
251
|
-
|
252
|
-
|
253
|
-
assessment = await assessAndSaveResults(candidate, searchee, guid, infoHashesToExclude);
|
254
|
-
logReason(assessment.decision, false, searchee, candidate);
|
362
|
+
if (!cacheEntry?.decision) {
|
363
|
+
// New candiate, could be metafile from cache
|
364
|
+
assessment = await assessAndSaveResults(metaOrCandidate, searchee, guid, infoHashesToExclude, Date.now());
|
255
365
|
}
|
256
|
-
else if ((cacheEntry.decision
|
257
|
-
cacheEntry.decision === Decision.MATCH_SIZE_ONLY ||
|
258
|
-
cacheEntry.decision === Decision.MATCH_PARTIAL) &&
|
366
|
+
else if (isAnyMatchedDecision(cacheEntry.decision) &&
|
259
367
|
infoHashesToExclude.includes(cacheEntry.infoHash)) {
|
260
|
-
//
|
368
|
+
// Already injected fast path, preserve match decision
|
261
369
|
assessment = { decision: Decision.INFO_HASH_ALREADY_EXISTS };
|
262
|
-
await db("decision")
|
263
|
-
|
264
|
-
|
265
|
-
}
|
266
|
-
else if ((cacheEntry.decision === Decision.MATCH ||
|
267
|
-
cacheEntry.decision === Decision.MATCH_SIZE_ONLY ||
|
268
|
-
cacheEntry.decision === Decision.MATCH_PARTIAL) &&
|
269
|
-
existsInTorrentCache(cacheEntry.infoHash)) {
|
270
|
-
// cached match
|
271
|
-
assessment = {
|
272
|
-
decision: cacheEntry.decision,
|
273
|
-
metafile: await getCachedTorrentFile(cacheEntry.infoHash),
|
274
|
-
};
|
370
|
+
await db("decision").where({ id: cacheEntry.id }).update({
|
371
|
+
last_seen: Date.now(),
|
372
|
+
});
|
275
373
|
}
|
276
|
-
else if (cacheEntry.decision
|
277
|
-
|
278
|
-
cacheEntry.decision
|
279
|
-
|
280
|
-
|
374
|
+
else if (isStaticDecision(cacheEntry.decision)) {
|
375
|
+
// These decisions will never change unless we update their logic
|
376
|
+
assessment = { decision: cacheEntry.decision };
|
377
|
+
await db("decision").where({ id: cacheEntry.id }).update({
|
378
|
+
last_seen: Date.now(),
|
379
|
+
});
|
281
380
|
}
|
282
381
|
else {
|
283
|
-
// cached
|
284
|
-
|
285
|
-
|
382
|
+
// Re-assess decisions using Metafile if cached
|
383
|
+
if (cacheEntry.decision !== Decision.FUZZY_SIZE_MISMATCH ||
|
384
|
+
cacheEntry.fuzzySizeFactor < getFuzzySizeFactor()) {
|
385
|
+
assessment = await assessAndSaveResults(metaOrCandidate, searchee, guid, infoHashesToExclude, cacheEntry.firstSeen);
|
386
|
+
}
|
387
|
+
else {
|
388
|
+
assessment = { decision: Decision.FUZZY_SIZE_MISMATCH };
|
389
|
+
await db("decision").where({ id: cacheEntry.id }).update({
|
390
|
+
last_seen: Date.now(),
|
391
|
+
});
|
392
|
+
}
|
286
393
|
}
|
287
|
-
|
288
|
-
if (
|
289
|
-
|
290
|
-
.where({ id: cacheEntry.id })
|
291
|
-
.update({ last_seen: Date.now() });
|
394
|
+
const wasCached = cacheEntry?.decision === assessment.decision;
|
395
|
+
if (!wasCached) {
|
396
|
+
logDecision(searchee, candidate, assessment.decision, assessment.metafile, tracker);
|
292
397
|
}
|
293
398
|
return assessment;
|
294
399
|
}
|