release: Multi-Hoster-Upload 2.1.7

This commit is contained in:
Sucukdeluxe
2026-08-11 16:10:14 +02:00
parent f6b20a8bc8
commit 622460d844
28 changed files with 2368 additions and 80 deletions
+223
View File
@@ -0,0 +1,223 @@
(function (root) {
'use strict';
const pathApi = typeof require === 'function' ? require('path') : null;
const protectedStatuses = new Set(['done', 'error', 'aborted', 'skipped']);
function normalizeFile(file, platform) {
const value = typeof file === 'string' ? file.trim() : '';
if (!value) return '';
if (platform === 'win32') {
if (pathApi) return pathApi.win32.resolve(value);
return value.replace(/\//g, '\\');
}
if (pathApi) return pathApi.resolve(value);
return value;
}
function normalizeHoster(hoster) {
return typeof hoster === 'string' ? hoster.trim().toLowerCase() : '';
}
function uniqueHosters(values) {
const hosters = [];
const seen = new Set();
for (const value of values) {
const hoster = normalizeHoster(value);
if (!hoster || seen.has(hoster)) continue;
seen.add(hoster);
hosters.push(hoster);
}
return hosters;
}
function cloneFingerprint(fingerprint) {
if (!fingerprint || typeof fingerprint !== 'object' || Array.isArray(fingerprint)) return null;
return { ...fingerprint };
}
function relatedJobs(queueJobs, job, platform) {
if (!Array.isArray(queueJobs) || !job) return [];
const file = normalizeFile(job.file, platform);
const token = typeof job.sourceCleanupToken === 'string' && job.sourceCleanupToken
? job.sourceCleanupToken
: null;
return queueJobs.filter((candidate) => {
if (!candidate) return false;
if (file && normalizeFile(candidate.file, platform) === file) return true;
return token !== null && candidate.sourceCleanupToken === token;
});
}
function storedRequiredHosters(jobs) {
const values = [];
for (const job of jobs) {
if (Array.isArray(job.sourceCleanupRequiredHosters)) {
values.push(...job.sourceCleanupRequiredHosters);
}
}
return uniqueHosters(values);
}
function completedHosters(jobs, requiredHosters) {
const values = [];
for (const job of jobs) {
if (Array.isArray(job.sourceCleanupCompletedHosters)) {
values.push(...job.sourceCleanupCompletedHosters);
}
}
for (const job of jobs) {
if (job.status === 'done') values.push(job.hoster);
}
const completed = new Set(uniqueHosters(values));
return requiredHosters.filter((hoster) => completed.has(hoster));
}
function storedToken(jobs) {
for (const job of jobs) {
if (typeof job.sourceCleanupToken === 'string' && job.sourceCleanupToken) {
return job.sourceCleanupToken;
}
}
return null;
}
function storedFingerprint(jobs) {
for (const job of jobs) {
const fingerprint = cloneFingerprint(job.sourceCleanupFingerprint);
if (fingerprint) return fingerprint;
}
return null;
}
function assignMetadata(jobs, token, requiredHosters, completed, fingerprint, touchedJobs, touchedSet) {
for (const job of jobs) {
job.sourceCleanupToken = token;
job.sourceCleanupRequiredHosters = [...requiredHosters];
job.sourceCleanupCompletedHosters = [...completed];
job.sourceCleanupFingerprint = cloneFingerprint(fingerprint);
if (!touchedSet.has(job)) {
touchedSet.add(job);
touchedJobs.push(job);
}
}
}
function prepareGroups(queueJobs, jobsToStart, createToken, platform) {
const groups = [];
const touchedJobs = [];
const touchedSet = new Set();
const preparedFiles = new Set();
if (!Array.isArray(queueJobs) || !Array.isArray(jobsToStart)) return { groups, touchedJobs };
for (const selectedJob of jobsToStart) {
const file = normalizeFile(selectedJob && selectedJob.file, platform);
if (!file || preparedFiles.has(file)) continue;
const siblings = relatedJobs(queueJobs, selectedJob, platform);
if (siblings.length === 0) continue;
preparedFiles.add(file);
const token = storedToken(siblings) || (typeof createToken === 'function' ? createToken(file) : null);
if (typeof token !== 'string' || !token) continue;
const persistedRequired = storedRequiredHosters(siblings);
const requiredHosters = uniqueHosters([
...persistedRequired,
...siblings.map((job) => job.hoster)
]);
const completed = completedHosters(siblings, requiredHosters);
const fingerprint = storedFingerprint(siblings);
assignMetadata(
siblings,
token,
requiredHosters,
completed,
fingerprint,
touchedJobs,
touchedSet
);
groups.push({
token,
file: selectedJob.file,
requiredHosters: [...requiredHosters],
completedHosters: [...completed],
fingerprint: cloneFingerprint(fingerprint),
jobs: siblings.map((job) => ({
jobId: job.id,
hoster: normalizeHoster(job.hoster),
status: job.status
}))
});
}
return { groups, touchedJobs };
}
function markCompleted(queueJobs, job, platform) {
const siblings = relatedJobs(queueJobs, job, platform);
if (siblings.length === 0) return [];
const requiredHosters = storedRequiredHosters(siblings);
const completed = new Set(completedHosters(siblings, requiredHosters));
const hoster = normalizeHoster(job.hoster);
if (requiredHosters.includes(hoster)) completed.add(hoster);
const orderedCompleted = requiredHosters.filter((required) => completed.has(required));
for (const sibling of siblings) {
sibling.sourceCleanupCompletedHosters = [...orderedCompleted];
}
return siblings;
}
function removeRequirement(queueJobs, job, platform) {
if (!job || protectedStatuses.has(job.status)) return [];
const siblings = relatedJobs(queueJobs, job, platform);
const removedHoster = normalizeHoster(job.hoster);
for (const sibling of siblings) {
const required = Array.isArray(sibling.sourceCleanupRequiredHosters)
? sibling.sourceCleanupRequiredHosters
: [];
const completed = Array.isArray(sibling.sourceCleanupCompletedHosters)
? sibling.sourceCleanupCompletedHosters
: [];
sibling.sourceCleanupRequiredHosters = uniqueHosters(required)
.filter((hoster) => hoster !== removedHoster);
sibling.sourceCleanupCompletedHosters = uniqueHosters(completed)
.filter((hoster) => hoster !== removedHoster);
}
return siblings;
}
function fingerprintFor(fingerprints, token) {
if (fingerprints instanceof Map) return fingerprints.get(token);
if (!fingerprints || typeof fingerprints !== 'object') return undefined;
return Object.prototype.hasOwnProperty.call(fingerprints, token)
? fingerprints[token]
: undefined;
}
function applyFingerprints(queueJobs, fingerprints) {
if (!Array.isArray(queueJobs)) return [];
const touchedJobs = [];
for (const job of queueJobs) {
if (!job || typeof job.sourceCleanupToken !== 'string' || !job.sourceCleanupToken) continue;
const fingerprint = fingerprintFor(fingerprints, job.sourceCleanupToken);
if (fingerprint === undefined) continue;
job.sourceCleanupFingerprint = cloneFingerprint(fingerprint);
touchedJobs.push(job);
}
return touchedJobs;
}
const api = {
prepareGroups,
markCompleted,
removeRequirement,
applyFingerprints
};
if (typeof module !== 'undefined' && module.exports) {
module.exports = api;
} else if (root) {
root.SourceCleanupPolicy = api;
}
})(typeof window !== 'undefined' ? window : this);