pixivflow 2.12.3 → 2.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +30 -4
- package/README_EN.md +11 -3
- package/config/fly-two-bots.example.json +3 -1
- package/dist/commands/DoctorCommand.d.ts +20 -0
- package/dist/commands/DoctorCommand.d.ts.map +1 -0
- package/dist/commands/DoctorCommand.js +107 -0
- package/dist/commands/DoctorCommand.js.map +1 -0
- package/dist/commands/OutboxCommand.d.ts +17 -0
- package/dist/commands/OutboxCommand.d.ts.map +1 -0
- package/dist/commands/OutboxCommand.js +102 -0
- package/dist/commands/OutboxCommand.js.map +1 -0
- package/dist/commands/ReconcileCommand.d.ts +24 -0
- package/dist/commands/ReconcileCommand.d.ts.map +1 -0
- package/dist/commands/ReconcileCommand.js +70 -0
- package/dist/commands/ReconcileCommand.js.map +1 -0
- package/dist/commands/SchedulerCommand.d.ts.map +1 -1
- package/dist/commands/SchedulerCommand.js +5 -0
- package/dist/commands/SchedulerCommand.js.map +1 -1
- package/dist/commands/SchedulerRunOnceCommand.d.ts.map +1 -1
- package/dist/commands/SchedulerRunOnceCommand.js +6 -0
- package/dist/commands/SchedulerRunOnceCommand.js.map +1 -1
- package/dist/commands/index.d.ts +4 -1
- package/dist/commands/index.d.ts.map +1 -1
- package/dist/commands/index.js +10 -1
- package/dist/commands/index.js.map +1 -1
- package/dist/commands/scheduler-runtime.d.ts +10 -1
- package/dist/commands/scheduler-runtime.d.ts.map +1 -1
- package/dist/commands/scheduler-runtime.js +157 -47
- package/dist/commands/scheduler-runtime.js.map +1 -1
- package/dist/config/types.d.ts +14 -1
- package/dist/config/types.d.ts.map +1 -1
- package/dist/config/validation.d.ts.map +1 -1
- package/dist/config/validation.js +10 -0
- package/dist/config/validation.js.map +1 -1
- package/dist/delivery/DeliveryAck.d.ts +70 -0
- package/dist/delivery/DeliveryAck.d.ts.map +1 -0
- package/dist/delivery/DeliveryAck.js +72 -0
- package/dist/delivery/DeliveryAck.js.map +1 -0
- package/dist/delivery/DeliveryDispatcher.d.ts +1 -0
- package/dist/delivery/DeliveryDispatcher.d.ts.map +1 -1
- package/dist/delivery/DeliveryDispatcher.js +9 -1
- package/dist/delivery/DeliveryDispatcher.js.map +1 -1
- package/dist/delivery/DeliveryService.d.ts +74 -0
- package/dist/delivery/DeliveryService.d.ts.map +1 -0
- package/dist/delivery/DeliveryService.js +209 -0
- package/dist/delivery/DeliveryService.js.map +1 -0
- package/dist/delivery/HttpMultipartDelivery.d.ts +11 -1
- package/dist/delivery/HttpMultipartDelivery.d.ts.map +1 -1
- package/dist/delivery/HttpMultipartDelivery.js +70 -67
- package/dist/delivery/HttpMultipartDelivery.js.map +1 -1
- package/dist/delivery/LegacyOutboxMigration.d.ts +21 -0
- package/dist/delivery/LegacyOutboxMigration.d.ts.map +1 -0
- package/dist/delivery/LegacyOutboxMigration.js +125 -0
- package/dist/delivery/LegacyOutboxMigration.js.map +1 -0
- package/dist/delivery/OutboxWorker.d.ts +70 -0
- package/dist/delivery/OutboxWorker.d.ts.map +1 -0
- package/dist/delivery/OutboxWorker.js +211 -0
- package/dist/delivery/OutboxWorker.js.map +1 -0
- package/dist/delivery/types.d.ts +15 -0
- package/dist/delivery/types.d.ts.map +1 -1
- package/dist/download/DownloadManager.d.ts +17 -12
- package/dist/download/DownloadManager.d.ts.map +1 -1
- package/dist/download/DownloadManager.js +31 -53
- package/dist/download/DownloadManager.js.map +1 -1
- package/dist/download/IllustrationDownloader.d.ts +1 -0
- package/dist/download/IllustrationDownloader.d.ts.map +1 -1
- package/dist/download/IllustrationDownloader.js +19 -1
- package/dist/download/IllustrationDownloader.js.map +1 -1
- package/dist/download/NovelDownloader.d.ts +3 -1
- package/dist/download/NovelDownloader.d.ts.map +1 -1
- package/dist/download/NovelDownloader.js +30 -2
- package/dist/download/NovelDownloader.js.map +1 -1
- package/dist/download/handlers/IllustrationTargetHandler.d.ts +18 -8
- package/dist/download/handlers/IllustrationTargetHandler.d.ts.map +1 -1
- package/dist/download/handlers/IllustrationTargetHandler.js +101 -74
- package/dist/download/handlers/IllustrationTargetHandler.js.map +1 -1
- package/dist/download/handlers/NovelTargetHandler.d.ts +8 -8
- package/dist/download/handlers/NovelTargetHandler.d.ts.map +1 -1
- package/dist/download/handlers/NovelTargetHandler.js +64 -83
- package/dist/download/handlers/NovelTargetHandler.js.map +1 -1
- package/dist/download/plan/DownloadPlanner.d.ts +6 -1
- package/dist/download/plan/DownloadPlanner.d.ts.map +1 -1
- package/dist/download/plan/DownloadPlanner.js +28 -5
- package/dist/download/plan/DownloadPlanner.js.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/notification/NotificationPolicy.d.ts +38 -0
- package/dist/notification/NotificationPolicy.d.ts.map +1 -0
- package/dist/notification/NotificationPolicy.js +89 -0
- package/dist/notification/NotificationPolicy.js.map +1 -0
- package/dist/package.json +1 -1
- package/dist/pixiv/PixivClient.d.ts +4 -0
- package/dist/pixiv/PixivClient.d.ts.map +1 -1
- package/dist/pixiv/PixivClient.js +15 -2
- package/dist/pixiv/PixivClient.js.map +1 -1
- package/dist/pixiv/RateLimitCoordinator.d.ts +30 -0
- package/dist/pixiv/RateLimitCoordinator.d.ts.map +1 -0
- package/dist/pixiv/RateLimitCoordinator.js +66 -0
- package/dist/pixiv/RateLimitCoordinator.js.map +1 -0
- package/dist/pixiv/client/PixivApiCore.d.ts +5 -0
- package/dist/pixiv/client/PixivApiCore.d.ts.map +1 -1
- package/dist/pixiv/client/PixivApiCore.js +31 -9
- package/dist/pixiv/client/PixivApiCore.js.map +1 -1
- package/dist/scheduler/ScheduleTriggerServer.d.ts +7 -0
- package/dist/scheduler/ScheduleTriggerServer.d.ts.map +1 -1
- package/dist/scheduler/ScheduleTriggerServer.js +17 -0
- package/dist/scheduler/ScheduleTriggerServer.js.map +1 -1
- package/dist/scheduler/SlotCoordinator.d.ts +15 -0
- package/dist/scheduler/SlotCoordinator.d.ts.map +1 -1
- package/dist/scheduler/SlotCoordinator.js +74 -0
- package/dist/scheduler/SlotCoordinator.js.map +1 -1
- package/dist/scheduler/SlotStateMachine.d.ts +32 -0
- package/dist/scheduler/SlotStateMachine.d.ts.map +1 -0
- package/dist/scheduler/SlotStateMachine.js +65 -0
- package/dist/scheduler/SlotStateMachine.js.map +1 -0
- package/dist/scheduler/TargetOutcome.d.ts +63 -0
- package/dist/scheduler/TargetOutcome.d.ts.map +1 -0
- package/dist/scheduler/TargetOutcome.js +28 -0
- package/dist/scheduler/TargetOutcome.js.map +1 -0
- package/dist/storage/Database.d.ts +16 -0
- package/dist/storage/Database.d.ts.map +1 -1
- package/dist/storage/Database.js +29 -0
- package/dist/storage/Database.js.map +1 -1
- package/dist/storage/DatabaseMigration.d.ts.map +1 -1
- package/dist/storage/DatabaseMigration.js +66 -0
- package/dist/storage/DatabaseMigration.js.map +1 -1
- package/dist/storage/repositories/DeliveryRepository.d.ts +73 -0
- package/dist/storage/repositories/DeliveryRepository.d.ts.map +1 -0
- package/dist/storage/repositories/DeliveryRepository.js +148 -0
- package/dist/storage/repositories/DeliveryRepository.js.map +1 -0
- package/dist/storage/repositories/MetadataRepository.d.ts +27 -0
- package/dist/storage/repositories/MetadataRepository.d.ts.map +1 -0
- package/dist/storage/repositories/MetadataRepository.js +58 -0
- package/dist/storage/repositories/MetadataRepository.js.map +1 -0
- package/dist/storage/repositories/OutboxRepository.d.ts +72 -0
- package/dist/storage/repositories/OutboxRepository.d.ts.map +1 -0
- package/dist/storage/repositories/OutboxRepository.js +200 -0
- package/dist/storage/repositories/OutboxRepository.js.map +1 -0
- package/dist/storage/repositories/SlotRepository.d.ts +26 -1
- package/dist/storage/repositories/SlotRepository.d.ts.map +1 -1
- package/dist/storage/repositories/SlotRepository.js +76 -2
- package/dist/storage/repositories/SlotRepository.js.map +1 -1
- package/dist/utils/config-validator-unified.d.ts.map +1 -1
- package/dist/utils/config-validator-unified.js +14 -0
- package/dist/utils/config-validator-unified.js.map +1 -1
- package/dist/webui/package.json +1 -1
- package/package.json +1 -1
- package/dist/delivery/DeliveryOutbox.d.ts +0 -43
- package/dist/delivery/DeliveryOutbox.d.ts.map +0 -1
- package/dist/delivery/DeliveryOutbox.js +0 -293
- package/dist/delivery/DeliveryOutbox.js.map +0 -1
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.OutboxWorker = void 0;
|
|
4
|
+
exports.backoffDelayMs = backoffDelayMs;
|
|
5
|
+
const node_crypto_1 = require("node:crypto");
|
|
6
|
+
const promises_1 = require("node:fs/promises");
|
|
7
|
+
const logger_1 = require("../logger");
|
|
8
|
+
/** Exponential backoff with jitter, capped. */
|
|
9
|
+
function backoffDelayMs(attempt, base, max) {
|
|
10
|
+
const exp = base * 2 ** Math.min(Math.max(0, attempt - 1), 18);
|
|
11
|
+
const capped = Math.min(max, exp);
|
|
12
|
+
// Full jitter: 0.5..1.0 of the delay spreads synchronized workers/retries.
|
|
13
|
+
return Math.round(capped * (0.5 + Math.random() * 0.5));
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Independently pumps the SQLite outbox so deliveries and notifications retry
|
|
17
|
+
* promptly instead of piggy-backing on the next scheduled download run.
|
|
18
|
+
* Survives crashes: an in-flight row keeps an expiring lease; a restart claims
|
|
19
|
+
* it once the lease is stale and retries the SAME idempotent intent.
|
|
20
|
+
*/
|
|
21
|
+
class OutboxWorker {
|
|
22
|
+
database;
|
|
23
|
+
dispatcher;
|
|
24
|
+
timer = null;
|
|
25
|
+
running = false;
|
|
26
|
+
stopped = false;
|
|
27
|
+
owner = `worker-${process.pid}-${(0, node_crypto_1.randomUUID)().slice(0, 8)}`;
|
|
28
|
+
pollIntervalMs;
|
|
29
|
+
leaseMs;
|
|
30
|
+
batchSize;
|
|
31
|
+
retryBaseMs;
|
|
32
|
+
retryMaxMs;
|
|
33
|
+
constructor(database, dispatcher, options = {}) {
|
|
34
|
+
this.database = database;
|
|
35
|
+
this.dispatcher = dispatcher;
|
|
36
|
+
this.pollIntervalMs = options.pollIntervalMs ?? 10_000;
|
|
37
|
+
this.leaseMs = options.leaseMs ?? 120_000;
|
|
38
|
+
this.batchSize = options.batchSize ?? 4;
|
|
39
|
+
this.retryBaseMs = options.retryBaseMs ?? 30_000;
|
|
40
|
+
this.retryMaxMs = options.retryMaxMs ?? 6 * 60 * 60_000;
|
|
41
|
+
if (options.onDeliveryTerminal)
|
|
42
|
+
this.onDeliveryTerminal = options.onDeliveryTerminal;
|
|
43
|
+
if (options.onDead)
|
|
44
|
+
this.onDead = options.onDead;
|
|
45
|
+
}
|
|
46
|
+
onDeliveryTerminal = undefined;
|
|
47
|
+
onDead = undefined;
|
|
48
|
+
start() {
|
|
49
|
+
if (this.timer)
|
|
50
|
+
return;
|
|
51
|
+
logger_1.logger.info('Outbox worker started', { pollMs: this.pollIntervalMs, owner: this.owner });
|
|
52
|
+
// Pump immediately on start so a crash-resume drains without waiting.
|
|
53
|
+
void this.pump();
|
|
54
|
+
this.timer = setInterval(() => void this.pump(), this.pollIntervalMs);
|
|
55
|
+
// Don't keep the event loop alive solely for the pump during shutdown.
|
|
56
|
+
this.timer.unref?.();
|
|
57
|
+
}
|
|
58
|
+
stop() {
|
|
59
|
+
this.stopped = true;
|
|
60
|
+
if (this.timer) {
|
|
61
|
+
clearInterval(this.timer);
|
|
62
|
+
this.timer = null;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
/** Drain all currently-due rows once (used by CLI / watchdog wake). */
|
|
66
|
+
async drainOnce(maxRounds = 50) {
|
|
67
|
+
let processed = 0;
|
|
68
|
+
let done = 0;
|
|
69
|
+
let retried = 0;
|
|
70
|
+
let dead = 0;
|
|
71
|
+
for (let round = 0; round < maxRounds; round++) {
|
|
72
|
+
const rows = this.database.outbox.claimDue(this.owner, this.leaseMs, this.batchSize);
|
|
73
|
+
if (rows.length === 0)
|
|
74
|
+
break;
|
|
75
|
+
let deferred = false;
|
|
76
|
+
for (const row of rows) {
|
|
77
|
+
if (!(await this.dispatcher.isReady(row.deliveryTarget))) {
|
|
78
|
+
this.database.outbox.release(row.id);
|
|
79
|
+
deferred = true;
|
|
80
|
+
continue;
|
|
81
|
+
}
|
|
82
|
+
processed++;
|
|
83
|
+
const result = await this.process(row);
|
|
84
|
+
if (result === 'done')
|
|
85
|
+
done++;
|
|
86
|
+
else if (result === 'dead')
|
|
87
|
+
dead++;
|
|
88
|
+
else
|
|
89
|
+
retried++;
|
|
90
|
+
}
|
|
91
|
+
if (deferred)
|
|
92
|
+
break;
|
|
93
|
+
}
|
|
94
|
+
return { processed, done, retried, dead };
|
|
95
|
+
}
|
|
96
|
+
async pump() {
|
|
97
|
+
if (this.running || this.stopped)
|
|
98
|
+
return;
|
|
99
|
+
this.running = true;
|
|
100
|
+
try {
|
|
101
|
+
const rows = this.database.outbox.claimDue(this.owner, this.leaseMs, this.batchSize);
|
|
102
|
+
for (const row of rows) {
|
|
103
|
+
if (this.stopped) {
|
|
104
|
+
this.database.outbox.release(row.id);
|
|
105
|
+
continue;
|
|
106
|
+
}
|
|
107
|
+
if (!(await this.dispatcher.isReady(row.deliveryTarget))) {
|
|
108
|
+
this.database.outbox.release(row.id);
|
|
109
|
+
continue;
|
|
110
|
+
}
|
|
111
|
+
await this.process(row);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
catch (error) {
|
|
115
|
+
logger_1.logger.warn('Outbox pump failed', { error: error instanceof Error ? error.message : String(error) });
|
|
116
|
+
}
|
|
117
|
+
finally {
|
|
118
|
+
this.running = false;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
async process(row) {
|
|
122
|
+
try {
|
|
123
|
+
if (row.kind === 'notification') {
|
|
124
|
+
const payload = JSON.parse(row.payloadJson);
|
|
125
|
+
await this.dispatcher.notify(row.deliveryTarget, {
|
|
126
|
+
text: payload.text,
|
|
127
|
+
idempotencyKey: row.idempotencyKey ?? row.id,
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
else {
|
|
131
|
+
const payload = JSON.parse(row.payloadJson);
|
|
132
|
+
const result = await this.dispatcher.deliver(row.deliveryTarget, {
|
|
133
|
+
files: payload.files,
|
|
134
|
+
previewFiles: payload.previewFiles,
|
|
135
|
+
fields: payload.fields,
|
|
136
|
+
context: payload.context,
|
|
137
|
+
});
|
|
138
|
+
const ack = result.ack ?? {
|
|
139
|
+
kind: result.status && result.status >= 200 && result.status < 300 ? 'accepted' : 'retryable_failure',
|
|
140
|
+
error: `no ack (HTTP ${result.status})`,
|
|
141
|
+
};
|
|
142
|
+
await this.handleDeliveryAck(row, ack, payload);
|
|
143
|
+
}
|
|
144
|
+
this.database.outbox.markDone(row.id);
|
|
145
|
+
return 'done';
|
|
146
|
+
}
|
|
147
|
+
catch (error) {
|
|
148
|
+
// Transport-level error (fetch threw): retryable.
|
|
149
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
150
|
+
const delay = backoffDelayMs(row.attempts + 1, this.retryBaseMs, this.retryMaxMs);
|
|
151
|
+
const status = this.database.outbox.markRetry(row.id, Date.now() + delay, message);
|
|
152
|
+
if (status === 'dead') {
|
|
153
|
+
logger_1.logger.error('Outbox item dead after max attempts', { outboxId: row.id, kind: row.kind, error: message });
|
|
154
|
+
this.onDead?.(row, message);
|
|
155
|
+
if (row.deliveryId) {
|
|
156
|
+
this.database.deliveries.recordAck(row.deliveryId, { status: 'failed', error: message });
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
else {
|
|
160
|
+
logger_1.logger.warn('Outbox item will retry', { outboxId: row.id, kind: row.kind, attempt: row.attempts + 1, delayMs: delay });
|
|
161
|
+
}
|
|
162
|
+
return status;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
async handleDeliveryAck(row, ack, payload) {
|
|
166
|
+
if (!row.deliveryId)
|
|
167
|
+
return;
|
|
168
|
+
switch (ack.kind) {
|
|
169
|
+
case 'accepted':
|
|
170
|
+
case 'idempotent_replay':
|
|
171
|
+
this.database.deliveries.recordAck(row.deliveryId, {
|
|
172
|
+
status: 'delivered',
|
|
173
|
+
remoteId: ack.remoteId,
|
|
174
|
+
remoteStatus: ack.remoteStatus ?? (ack.kind === 'idempotent_replay' ? 'idempotent_replay' : 'accepted'),
|
|
175
|
+
});
|
|
176
|
+
this.onDeliveryTerminal?.(row.deliveryId, ack, payload);
|
|
177
|
+
await this.cleanup(payload);
|
|
178
|
+
break;
|
|
179
|
+
case 'duplicate_existing':
|
|
180
|
+
this.database.deliveries.recordAck(row.deliveryId, {
|
|
181
|
+
status: 'duplicate',
|
|
182
|
+
remoteId: ack.remoteId,
|
|
183
|
+
remoteStatus: ack.remoteStatus ?? 'duplicate_existing',
|
|
184
|
+
reuseReason: 'historical_duplicate',
|
|
185
|
+
});
|
|
186
|
+
this.onDeliveryTerminal?.(row.deliveryId, ack, payload);
|
|
187
|
+
break;
|
|
188
|
+
case 'permanent_failure':
|
|
189
|
+
// Deterministic rejection: still retry a couple times to survive a
|
|
190
|
+
// misconfigured blip, the outbox max-attempts then dead-letters it.
|
|
191
|
+
throw new Error(`permanent delivery failure: ${ack.error}`);
|
|
192
|
+
case 'retryable_failure':
|
|
193
|
+
throw new Error(`retryable delivery failure: ${ack.error}`);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
async cleanup(payload) {
|
|
197
|
+
if (payload.deleteAfterDelivery === false)
|
|
198
|
+
return;
|
|
199
|
+
const files = [
|
|
200
|
+
...(payload.files ?? []),
|
|
201
|
+
...(payload.previewFiles ?? []),
|
|
202
|
+
...(payload.cleanupFiles ?? []),
|
|
203
|
+
];
|
|
204
|
+
await Promise.all([...new Set(files)].map((file) => (0, promises_1.unlink)(file).catch((error) => {
|
|
205
|
+
if (error.code !== 'ENOENT')
|
|
206
|
+
logger_1.logger.debug('cache cleanup failed', { file, error: error.message });
|
|
207
|
+
})));
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
exports.OutboxWorker = OutboxWorker;
|
|
211
|
+
//# sourceMappingURL=OutboxWorker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"OutboxWorker.js","sourceRoot":"","sources":["../../src/delivery/OutboxWorker.ts"],"names":[],"mappings":";;;AAyCA,wCAKC;AA9CD,6CAAyC;AACzC,+CAA0C;AAM1C,sCAAmC;AAiCnC,+CAA+C;AAC/C,SAAgB,cAAc,CAAC,OAAe,EAAE,IAAY,EAAE,GAAW;IACvE,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,IAAI,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,OAAO,GAAG,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IAC/D,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;IAClC,2EAA2E;IAC3E,OAAO,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,GAAG,GAAG,IAAI,CAAC,MAAM,EAAE,GAAG,GAAG,CAAC,CAAC,CAAC;AAC1D,CAAC;AAED;;;;;GAKG;AACH,MAAa,YAAY;IAYJ;IACA;IAZX,KAAK,GAA0B,IAAI,CAAC;IACpC,OAAO,GAAG,KAAK,CAAC;IAChB,OAAO,GAAG,KAAK,CAAC;IACP,KAAK,GAAG,UAAU,OAAO,CAAC,GAAG,IAAI,IAAA,wBAAU,GAAE,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC;IAC5D,cAAc,CAAS;IACvB,OAAO,CAAS;IAChB,SAAS,CAAS;IAClB,WAAW,CAAS;IACpB,UAAU,CAAS;IAEpC,YACmB,QAAkB,EAClB,UAA8B,EAC/C,UAA+B,EAAE;QAFhB,aAAQ,GAAR,QAAQ,CAAU;QAClB,eAAU,GAAV,UAAU,CAAoB;QAG/C,IAAI,CAAC,cAAc,GAAG,OAAO,CAAC,cAAc,IAAI,MAAM,CAAC;QACvD,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC,OAAO,IAAI,OAAO,CAAC;QAC1C,IAAI,CAAC,SAAS,GAAG,OAAO,CAAC,SAAS,IAAI,CAAC,CAAC;QACxC,IAAI,CAAC,WAAW,GAAG,OAAO,CAAC,WAAW,IAAI,MAAM,CAAC;QACjD,IAAI,CAAC,UAAU,GAAG,OAAO,CAAC,UAAU,IAAI,CAAC,GAAG,EAAE,GAAG,MAAM,CAAC;QACxD,IAAI,OAAO,CAAC,kBAAkB;YAAE,IAAI,CAAC,kBAAkB,GAAG,OAAO,CAAC,kBAAkB,CAAC;QACrF,IAAI,OAAO,CAAC,MAAM;YAAE,IAAI,CAAC,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IACnD,CAAC;IAEO,kBAAkB,GAA8C,SAAS,CAAC;IAC1E,MAAM,GAAkC,SAAS,CAAC;IAE1D,KAAK;QACH,IAAI,IAAI,CAAC,KAAK;YAAE,OAAO;QACvB,eAAM,CAAC,IAAI,CAAC,uBAAuB,EAAE,EAAE,MAAM,EAAE,IAAI,CAAC,cAAc,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC;QACzF,sEAAsE;QACtE,KAAK,IAAI,CAAC,IAAI,EAAE,CAAC;QACjB,IAAI,CAAC,KAAK,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC,KAAK,IAAI,CAAC,IAAI,EAAE,EAAE,IAAI,CAAC,cAAc,CAAC,CAAC;QACtE,uEAAuE;QACvE,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,EAAE,CAAC;IACvB,CAAC;IAED,IAAI;QACF,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;QACpB,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;YACf,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YAC1B,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC;QACpB,CAAC;IACH,CAAC;IAED,uEAAuE;IACvE,KAAK,CAAC,SAAS,CAAC,SAAS,GAAG,EAAE;QAC5B,IAAI,SAAS,GAAG,CAAC,CAAC;QAClB,IAAI,IAAI,GAAG,CAAC,CAAC;QACb,IAAI,OAAO,GAAG,CAAC,CAAC;QAChB,IAAI,IAAI,GAAG,CAAC,CAAC;QACb,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,SAAS,EAAE,KAAK,EAAE,EAAE,CAAC;YAC/C,MAAM,IAAI,GAAG,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC;YACrF,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;gBAAE,MAAM;YAC7B,IAAI,QAAQ,GAAG,KAAK,CAAC;YACrB,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;gBACvB,IAAI,CAAC,CAAC,MAAM,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC,EAAE,CAAC;oBACzD,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;oBACrC,QAAQ,GAAG,IAAI,CAAC;oBAChB,SAAS;gBACX,CAAC;gBACD,SAAS,EAAE,CAAC;gBACZ,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;gBACvC,IAAI,MAAM,KAAK,MAAM;oBAAE,IAAI,EAAE,CAAC;qBACzB,IAAI,MAAM,KAAK,MAAM;oBAAE,IAAI,EAAE,CAAC;;oBAC9B,OAAO,EAAE,CAAC;YACjB,CAAC;YACD,IAAI,QAAQ;gBAAE,MAAM;QACtB,CAAC;QACD,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;IAC5C,CAAC;IAEO,KAAK,CAAC,IAAI;QAChB,IAAI,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC,OAAO;YAAE,OAAO;QACzC,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;QACpB,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC;YACrF,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;gBACvB,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;oBACjB,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;oBACrC,SAAS;gBACX,CAAC;gBACD,IAAI,CAAC,CAAC,MAAM,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC,EAAE,CAAC;oBACzD,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;oBACrC,SAAS;gBACX,CAAC;gBACD,MAAM,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;YAC1B,CAAC;QACH,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,eAAM,CAAC,IAAI,CAAC,oBAAoB,EAAE,EAAE,KAAK,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;QACvG,CAAC;gBAAS,CAAC;YACT,IAAI,CAAC,OAAO,GAAG,KAAK,CAAC;QACvB,CAAC;IACH,CAAC;IAEO,KAAK,CAAC,OAAO,CAAC,GAAc;QAClC,IAAI,CAAC;YACH,IAAI,GAAG,CAAC,IAAI,KAAK,cAAc,EAAE,CAAC;gBAChC,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,WAAW,CAAwB,CAAC;gBACnE,MAAM,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,GAAG,CAAC,cAAc,EAAE;oBAC/C,IAAI,EAAE,OAAO,CAAC,IAAI;oBAClB,cAAc,EAAE,GAAG,CAAC,cAAc,IAAI,GAAG,CAAC,EAAE;iBAC7C,CAAC,CAAC;YACL,CAAC;iBAAM,CAAC;gBACN,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,WAAW,CAAoB,CAAC;gBAC/D,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,GAAG,CAAC,cAAc,EAAE;oBAC/D,KAAK,EAAE,OAAO,CAAC,KAAK;oBACpB,YAAY,EAAE,OAAO,CAAC,YAAY;oBAClC,MAAM,EAAE,OAAO,CAAC,MAAmC;oBACnD,OAAO,EAAE,OAAO,CAAC,OAAgD;iBAClE,CAAC,CAAC;gBACH,MAAM,GAAG,GAAgB,MAAM,CAAC,GAAG,IAAI;oBACrC,IAAI,EAAE,MAAM,CAAC,MAAM,IAAI,MAAM,CAAC,MAAM,IAAI,GAAG,IAAI,MAAM,CAAC,MAAM,GAAG,GAAG,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,mBAAmB;oBACrG,KAAK,EAAE,gBAAgB,MAAM,CAAC,MAAM,GAAG;iBACxC,CAAC;gBACF,MAAM,IAAI,CAAC,iBAAiB,CAAC,GAAG,EAAE,GAAG,EAAE,OAAO,CAAC,CAAC;YAClD,CAAC;YACD,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YACtC,OAAO,MAAM,CAAC;QAChB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,kDAAkD;YAClD,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YACvE,MAAM,KAAK,GAAG,cAAc,CAAC,GAAG,CAAC,QAAQ,GAAG,CAAC,EAAE,IAAI,CAAC,WAAW,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC;YAClF,MAAM,MAAM,GAAG,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,EAAE,OAAO,CAAC,CAAC;YACnF,IAAI,MAAM,KAAK,MAAM,EAAE,CAAC;gBACtB,eAAM,CAAC,KAAK,CAAC,qCAAqC,EAAE,EAAE,QAAQ,EAAE,GAAG,CAAC,EAAE,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,CAAC;gBAC1G,IAAI,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;gBAC5B,IAAI,GAAG,CAAC,UAAU,EAAE,CAAC;oBACnB,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,SAAS,CAAC,GAAG,CAAC,UAAU,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,CAAC;gBAC3F,CAAC;YACH,CAAC;iBAAM,CAAC;gBACN,eAAM,CAAC,IAAI,CAAC,wBAAwB,EAAE,EAAE,QAAQ,EAAE,GAAG,CAAC,EAAE,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,OAAO,EAAE,GAAG,CAAC,QAAQ,GAAG,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,CAAC;YACzH,CAAC;YACD,OAAO,MAAM,CAAC;QAChB,CAAC;IACH,CAAC;IAEO,KAAK,CAAC,iBAAiB,CAAC,GAAc,EAAE,GAAgB,EAAE,OAAwB;QACxF,IAAI,CAAC,GAAG,CAAC,UAAU;YAAE,OAAO;QAC5B,QAAQ,GAAG,CAAC,IAAI,EAAE,CAAC;YACjB,KAAK,UAAU,CAAC;YAChB,KAAK,mBAAmB;gBACtB,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,SAAS,CAAC,GAAG,CAAC,UAAU,EAAE;oBACjD,MAAM,EAAE,WAAW;oBACnB,QAAQ,EAAE,GAAG,CAAC,QAAQ;oBACtB,YAAY,EAAE,GAAG,CAAC,YAAY,IAAI,CAAC,GAAG,CAAC,IAAI,KAAK,mBAAmB,CAAC,CAAC,CAAC,mBAAmB,CAAC,CAAC,CAAC,UAAU,CAAC;iBACxG,CAAC,CAAC;gBACH,IAAI,CAAC,kBAAkB,EAAE,CAAC,GAAG,CAAC,UAAU,EAAE,GAAG,EAAE,OAAO,CAAC,CAAC;gBACxD,MAAM,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;gBAC5B,MAAM;YACR,KAAK,oBAAoB;gBACvB,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,SAAS,CAAC,GAAG,CAAC,UAAU,EAAE;oBACjD,MAAM,EAAE,WAAW;oBACnB,QAAQ,EAAE,GAAG,CAAC,QAAQ;oBACtB,YAAY,EAAE,GAAG,CAAC,YAAY,IAAI,oBAAoB;oBACtD,WAAW,EAAE,sBAAsB;iBACpC,CAAC,CAAC;gBACH,IAAI,CAAC,kBAAkB,EAAE,CAAC,GAAG,CAAC,UAAU,EAAE,GAAG,EAAE,OAAO,CAAC,CAAC;gBACxD,MAAM;YACR,KAAK,mBAAmB;gBACtB,mEAAmE;gBACnE,oEAAoE;gBACpE,MAAM,IAAI,KAAK,CAAC,+BAA+B,GAAG,CAAC,KAAK,EAAE,CAAC,CAAC;YAC9D,KAAK,mBAAmB;gBACtB,MAAM,IAAI,KAAK,CAAC,+BAA+B,GAAG,CAAC,KAAK,EAAE,CAAC,CAAC;QAChE,CAAC;IACH,CAAC;IAEO,KAAK,CAAC,OAAO,CAAC,OAAwB;QAC5C,IAAI,OAAO,CAAC,mBAAmB,KAAK,KAAK;YAAE,OAAO;QAClD,MAAM,KAAK,GAAG;YACZ,GAAG,CAAC,OAAO,CAAC,KAAK,IAAI,EAAE,CAAC;YACxB,GAAG,CAAC,OAAO,CAAC,YAAY,IAAI,EAAE,CAAC;YAC/B,GAAG,CAAC,OAAO,CAAC,YAAY,IAAI,EAAE,CAAC;SAChC,CAAC;QACF,MAAM,OAAO,CAAC,GAAG,CACf,CAAC,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAC/B,IAAA,iBAAM,EAAC,IAAI,CAAC,CAAC,KAAK,CAAC,CAAC,KAA4B,EAAE,EAAE;YAClD,IAAI,KAAK,CAAC,IAAI,KAAK,QAAQ;gBAAE,eAAM,CAAC,KAAK,CAAC,sBAAsB,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;QACpG,CAAC,CAAC,CACH,CACF,CAAC;IACJ,CAAC;CACF;AAxLD,oCAwLC"}
|
package/dist/delivery/types.d.ts
CHANGED
|
@@ -9,6 +9,8 @@ export interface DownloadedArtifact {
|
|
|
9
9
|
tags?: string[];
|
|
10
10
|
/** Files sent to the configured delivery target. */
|
|
11
11
|
files: string[];
|
|
12
|
+
/** Optional per-file lightweight preview sources, aligned with ``files``. */
|
|
13
|
+
previewFiles?: string[];
|
|
12
14
|
/** Local sidecars deleted with cache files after successful delivery. */
|
|
13
15
|
cleanupFiles?: string[];
|
|
14
16
|
/** R-18 work (x_restrict > 0): delivery templates may open Telegram spoiler. */
|
|
@@ -58,6 +60,13 @@ export interface DeliveryContext {
|
|
|
58
60
|
occurrenceAt?: string;
|
|
59
61
|
/** Why the run started: cron | http | manual | catchup. */
|
|
60
62
|
triggerSource?: string;
|
|
63
|
+
/**
|
|
64
|
+
* Occurrence-scoped intent key (e.g. pixivflow:<target>:<type>:<id>:<slot>:<targetId>).
|
|
65
|
+
* Sent downstream as idempotency_key so an ACK-loss retry carrying the SAME
|
|
66
|
+
* key converges to one remote record (idempotent_replay), distinct from a
|
|
67
|
+
* historical duplicate of the same work from a different occurrence.
|
|
68
|
+
*/
|
|
69
|
+
idempotencyKey?: string;
|
|
61
70
|
/** Schedule slot provenance for review-source labelling (external/scheduled runs). */
|
|
62
71
|
slotId?: string;
|
|
63
72
|
slotName?: string;
|
|
@@ -65,12 +74,17 @@ export interface DeliveryContext {
|
|
|
65
74
|
}
|
|
66
75
|
export interface DeliveryRequest {
|
|
67
76
|
files: string[];
|
|
77
|
+
/** Optional per-file preview sources, aligned with ``files``. */
|
|
78
|
+
previewFiles?: string[];
|
|
68
79
|
fields?: Record<string, DeliveryFieldValue>;
|
|
69
80
|
context: DeliveryContext;
|
|
70
81
|
}
|
|
82
|
+
import type { DeliveryAck } from './DeliveryAck';
|
|
71
83
|
export interface DeliveryResult {
|
|
72
84
|
status?: number;
|
|
73
85
|
body?: unknown;
|
|
86
|
+
/** Normalized business acknowledgement (present on delivery attempts). */
|
|
87
|
+
ack?: DeliveryAck;
|
|
74
88
|
}
|
|
75
89
|
export interface DeliveryNotificationRequest {
|
|
76
90
|
text: string;
|
|
@@ -78,6 +92,7 @@ export interface DeliveryNotificationRequest {
|
|
|
78
92
|
}
|
|
79
93
|
export interface DeliveryProvider {
|
|
80
94
|
deliver(request: DeliveryRequest): Promise<DeliveryResult>;
|
|
95
|
+
/** One notification attempt; the durable outbox owns retries. */
|
|
81
96
|
notify?(request: DeliveryNotificationRequest): Promise<DeliveryResult>;
|
|
82
97
|
}
|
|
83
98
|
//# sourceMappingURL=types.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/delivery/types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,WAAW,CAAC;AAE/C,MAAM,MAAM,gBAAgB,GAAG,cAAc,GAAG,OAAO,CAAC;AAExD,2EAA2E;AAC3E,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,gBAAgB,CAAC;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,kFAAkF;IAClF,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,oDAAoD;IACpD,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,yEAAyE;IACzE,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,gFAAgF;IAChF,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,8EAA8E;IAC9E,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,kFAAkF;IAClF,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,4EAA4E;IAC5E,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,gDAAgD;IAChD,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,4CAA4C;IAC5C,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,gBAAgB,CAAC;IACvB,kFAAkF;IAClF,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,iFAAiF;IACjF,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,4EAA4E;IAC5E,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,4DAA4D;IAC5D,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,0CAA0C;IAC1C,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,4DAA4D;IAC5D,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,oDAAoD;IACpD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,8EAA8E;IAC9E,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,2EAA2E;IAC3E,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,2DAA2D;IAC3D,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,sFAAsF;IACtF,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAAC;IAC5C,OAAO,EAAE,eAAe,CAAC;CAC1B;AAED,MAAM,WAAW,cAAc;IAC7B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/delivery/types.ts"],"names":[],"mappings":"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"}
|
|
@@ -3,6 +3,8 @@ import { IDownloadManager } from '../interfaces/IDownloadManager';
|
|
|
3
3
|
import { IPixivClient } from '../interfaces/IPixivClient';
|
|
4
4
|
import { IDatabase } from '../interfaces/IDatabase';
|
|
5
5
|
import { IFileService } from '../interfaces/IFileService';
|
|
6
|
+
import { DeliveryService } from '../delivery/DeliveryService';
|
|
7
|
+
import { TargetOutcome } from '../scheduler/TargetOutcome';
|
|
6
8
|
/**
|
|
7
9
|
* Download Manager with Concurrency Control
|
|
8
10
|
*
|
|
@@ -41,18 +43,22 @@ export declare class DownloadManager implements IDownloadManager {
|
|
|
41
43
|
private readonly novelHandler;
|
|
42
44
|
private cancelled;
|
|
43
45
|
private cancelReason;
|
|
44
|
-
private readonly
|
|
45
|
-
/** Per-target outcome hook (
|
|
46
|
+
private readonly deliveryService;
|
|
47
|
+
/** Per-target TYPED outcome hook (the Slot ledger maps it to cell transitions). */
|
|
46
48
|
private onTargetOutcome;
|
|
47
|
-
/**
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
|
|
51
|
-
/**
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
49
|
+
/** Register a callback fired after each target with its explicit business outcome. */
|
|
50
|
+
setTargetOutcomeHook(fn: (target: TargetConfig, outcome: TargetOutcome) => void): void;
|
|
51
|
+
/** Expose delivery service for handlers (preflight + intent creation). */
|
|
52
|
+
get deliveries(): DeliveryService;
|
|
53
|
+
/** Slot context propagated to delivery templates/outcomes for scheduled runs. */
|
|
54
|
+
slotContext?: {
|
|
55
|
+
slotId: string;
|
|
56
|
+
scheduleId: string;
|
|
57
|
+
occurrenceAtIso: string;
|
|
58
|
+
triggerSource: string;
|
|
59
|
+
slotName: string;
|
|
60
|
+
slotDate: string;
|
|
61
|
+
};
|
|
56
62
|
/**
|
|
57
63
|
* Request cooperative cancellation of the current run. In-flight item
|
|
58
64
|
* finishes; no further targets/items are started. runAllTargets() will
|
|
@@ -66,6 +72,5 @@ export declare class DownloadManager implements IDownloadManager {
|
|
|
66
72
|
runAllTargets(): Promise<void>;
|
|
67
73
|
private dispatchTarget;
|
|
68
74
|
private updateProgress;
|
|
69
|
-
private buildProxyUrl;
|
|
70
75
|
}
|
|
71
76
|
//# sourceMappingURL=DownloadManager.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DownloadManager.d.ts","sourceRoot":"","sources":["../../src/download/DownloadManager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAE3D,OAAO,EAAE,gBAAgB,EAAE,MAAM,gCAAgC,CAAC;AAClE,OAAO,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAC1D,OAAO,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AACpD,OAAO,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;
|
|
1
|
+
{"version":3,"file":"DownloadManager.d.ts","sourceRoot":"","sources":["../../src/download/DownloadManager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAE3D,OAAO,EAAE,gBAAgB,EAAE,MAAM,gCAAgC,CAAC;AAClE,OAAO,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAC1D,OAAO,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AACpD,OAAO,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAU1D,OAAO,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AAC9D,OAAO,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAC;AAK3D;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,qBAAa,eAAgB,YAAW,gBAAgB;IAyDpD,OAAO,CAAC,QAAQ,CAAC,MAAM;IAGvB,OAAO,CAAC,QAAQ,CAAC,WAAW;IA3D9B,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAmB;IACpD,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAiB;IAChD,OAAO,CAAC,QAAQ,CAAC,sBAAsB,CAAyB;IAChE,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAkB;IAClD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAkB;IAC1C,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAmB;IAC5C,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAwB;IACtD,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAmB;IAC5C,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAA4B;IAChE,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAqB;IAGlD,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,YAAY,CAAM;IAC1B,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAmB;IACnD,mFAAmF;IACnF,OAAO,CAAC,eAAe,CAAyE;IAEhG,sFAAsF;IAC/E,oBAAoB,CAAC,EAAE,EAAE,CAAC,MAAM,EAAE,YAAY,EAAE,OAAO,EAAE,aAAa,KAAK,IAAI,GAAG,IAAI;IAI7F,0EAA0E;IAC1E,IAAW,UAAU,IAAI,eAAe,CAEvC;IAED,iFAAiF;IAC1E,WAAW,CAAC,EAAE;QACnB,MAAM,EAAE,MAAM,CAAC;QACf,UAAU,EAAE,MAAM,CAAC;QACnB,eAAe,EAAE,MAAM,CAAC;QACxB,aAAa,EAAE,MAAM,CAAC;QACtB,QAAQ,EAAE,MAAM,CAAC;QACjB,QAAQ,EAAE,MAAM,CAAC;KAClB,CAAC;IAEF;;;;OAIG;IACI,MAAM,CAAC,MAAM,GAAE,MAAoB,GAAG,IAAI;IAQ1C,WAAW,IAAI,OAAO;gBAKV,MAAM,EAAE,gBAAgB,EACzC,MAAM,EAAE,YAAY,EACpB,QAAQ,EAAE,SAAS,EACF,WAAW,EAAE,YAAY;IAoF5C,mBAAmB,CAAC,QAAQ,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,KAAK,IAAI,GAAG,IAAI;IAIlF,UAAU;IAIV,aAAa;YAyDZ,cAAc;IAY5B,OAAO,CAAC,cAAc;CAIvB"}
|
|
@@ -11,9 +11,7 @@ const DownloadExecutor_1 = require("./exec/DownloadExecutor");
|
|
|
11
11
|
const ErrorRecovery_1 = require("./recovery/ErrorRecovery");
|
|
12
12
|
const DownloadPipeline_1 = require("./pipeline/DownloadPipeline");
|
|
13
13
|
const errors_1 = require("../utils/errors");
|
|
14
|
-
const
|
|
15
|
-
const DeliveryOutbox_1 = require("../delivery/DeliveryOutbox");
|
|
16
|
-
const node_path_1 = require("node:path");
|
|
14
|
+
const DeliveryService_1 = require("../delivery/DeliveryService");
|
|
17
15
|
const IllustrationTargetHandler_1 = require("./handlers/IllustrationTargetHandler");
|
|
18
16
|
const NovelTargetHandler_1 = require("./handlers/NovelTargetHandler");
|
|
19
17
|
const createTopicPipeline_1 = require("../topic/createTopicPipeline");
|
|
@@ -56,19 +54,19 @@ class DownloadManager {
|
|
|
56
54
|
// Cooperative cancellation state (see cancel())
|
|
57
55
|
cancelled = false;
|
|
58
56
|
cancelReason = '';
|
|
59
|
-
|
|
60
|
-
/** Per-target outcome hook (
|
|
57
|
+
deliveryService;
|
|
58
|
+
/** Per-target TYPED outcome hook (the Slot ledger maps it to cell transitions). */
|
|
61
59
|
onTargetOutcome = null;
|
|
62
|
-
/**
|
|
63
|
-
onWorkLocked = null;
|
|
64
|
-
/** Register a callback fired after each target with the error if it failed. */
|
|
60
|
+
/** Register a callback fired after each target with its explicit business outcome. */
|
|
65
61
|
setTargetOutcomeHook(fn) {
|
|
66
62
|
this.onTargetOutcome = fn;
|
|
67
63
|
}
|
|
68
|
-
/**
|
|
69
|
-
|
|
70
|
-
this.
|
|
64
|
+
/** Expose delivery service for handlers (preflight + intent creation). */
|
|
65
|
+
get deliveries() {
|
|
66
|
+
return this.deliveryService;
|
|
71
67
|
}
|
|
68
|
+
/** Slot context propagated to delivery templates/outcomes for scheduled runs. */
|
|
69
|
+
slotContext;
|
|
72
70
|
/**
|
|
73
71
|
* Request cooperative cancellation of the current run. In-flight item
|
|
74
72
|
* finishes; no further targets/items are started. runAllTargets() will
|
|
@@ -92,8 +90,10 @@ class DownloadManager {
|
|
|
92
90
|
const downloadConcurrency = config.download?.concurrency || 3;
|
|
93
91
|
const storagePath = config.storage?.illustrationDirectory ?? config.storage?.downloadDirectory ?? './downloads';
|
|
94
92
|
this.illustrationDownloader = new IllustrationDownloader_1.IllustrationDownloader(client, database, fileService, downloadConcurrency, storagePath);
|
|
95
|
-
this.novelDownloader = new NovelDownloader_1.NovelDownloader(client, database, fileService);
|
|
96
|
-
this.planner = new DownloadPlanner_1.DownloadPlanner(database
|
|
93
|
+
this.novelDownloader = new NovelDownloader_1.NovelDownloader(client, database, fileService, database);
|
|
94
|
+
this.planner = new DownloadPlanner_1.DownloadPlanner(database, {
|
|
95
|
+
deliveredIds: (target, type, ids) => this.deliveryService.deliveredIds(target, type, ids),
|
|
96
|
+
});
|
|
97
97
|
this.executor = new DownloadExecutor_1.DownloadExecutor();
|
|
98
98
|
const downloadConfig = config.download ?? {};
|
|
99
99
|
const maxRetries = downloadConfig.maxRetries ?? 3;
|
|
@@ -112,19 +112,16 @@ class DownloadManager {
|
|
|
112
112
|
recovery: this.errorRecovery,
|
|
113
113
|
isCancelled: () => this.cancelled,
|
|
114
114
|
});
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
retryMaxDelayMs: config.delivery?.outboxRetryMaxMs,
|
|
120
|
-
onDeliver: (artifact, target) => this.onWorkLocked?.(artifact, target),
|
|
121
|
-
});
|
|
115
|
+
// Delivery ledger + SQLite outbox. Requires the concrete Database (with the
|
|
116
|
+
// deliveries/outbox repositories). Unit tests pass plain mock databases; in
|
|
117
|
+
// that case delivery dedupe/enqueue is simply inactive.
|
|
118
|
+
this.deliveryService = new DeliveryService_1.DeliveryService(database);
|
|
122
119
|
// One lazily-created topic pipeline (shared resolver/cache) for this run.
|
|
123
120
|
const topicFactory = (0, createTopicPipeline_1.createTopicPipelineFactory)(client, typeof database.getDatabasePath === 'function'
|
|
124
121
|
? database
|
|
125
122
|
: config.storage?.databasePath, config.download?.requestDelay ?? 500);
|
|
126
|
-
this.illustrationHandler = new IllustrationTargetHandler_1.IllustrationTargetHandler(client, database, this.rankingService, this.illustrationDownloader, this.pipeline, this.
|
|
127
|
-
this.novelHandler = new NovelTargetHandler_1.NovelTargetHandler(client, database, this.rankingService, this.pipeline, this.novelDownloader, this.
|
|
123
|
+
this.illustrationHandler = new IllustrationTargetHandler_1.IllustrationTargetHandler(client, database, this.rankingService, this.illustrationDownloader, this.pipeline, topicFactory, this.deliveryService);
|
|
124
|
+
this.novelHandler = new NovelTargetHandler_1.NovelTargetHandler(client, database, this.rankingService, this.pipeline, this.novelDownloader, topicFactory, this.deliveryService);
|
|
128
125
|
}
|
|
129
126
|
setProgressCallback(callback) {
|
|
130
127
|
this.progressReporter.setCallback(callback);
|
|
@@ -133,13 +130,8 @@ class DownloadManager {
|
|
|
133
130
|
await this.fileService.initialise();
|
|
134
131
|
}
|
|
135
132
|
async runAllTargets() {
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
logger_1.logger.info('Processed pending deliveries', { ...pending });
|
|
139
|
-
}
|
|
140
|
-
else if (pending.deferred > 0) {
|
|
141
|
-
logger_1.logger.debug('Pending deliveries remain in backoff', { ...pending });
|
|
142
|
-
}
|
|
133
|
+
// NOTE: pending deliveries are pumped independently by the OutboxWorker,
|
|
134
|
+
// not piggy-backed onto the next download run. No retryPending() here.
|
|
143
135
|
const totalTargets = this.config.targets.length;
|
|
144
136
|
if (totalTargets === 0) {
|
|
145
137
|
this.progressReporter.complete(0, '所有目标处理完成');
|
|
@@ -156,14 +148,18 @@ class DownloadManager {
|
|
|
156
148
|
const targetName = target.filterTag || target.tag || 'unknown';
|
|
157
149
|
this.updateProgress(currentTarget, totalTargets, `处理目标: ${targetName} (${target.type})`);
|
|
158
150
|
try {
|
|
159
|
-
await this.dispatchTarget(target);
|
|
160
|
-
this.onTargetOutcome?.(target);
|
|
151
|
+
const outcome = await this.dispatchTarget(target);
|
|
152
|
+
this.onTargetOutcome?.(target, outcome);
|
|
153
|
+
if (outcome.kind === 'failed' && !outcome.retryable) {
|
|
154
|
+
errors.push({ target: `${targetName} (${target.type})`, error: outcome.error });
|
|
155
|
+
}
|
|
161
156
|
}
|
|
162
157
|
catch (error) {
|
|
158
|
+
// A raw escape means an unexpected/retryable infrastructure failure.
|
|
163
159
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
164
160
|
errors.push({ target: `${targetName} (${target.type})`, error: errorMessage });
|
|
165
161
|
logger_1.logger.error(`Target ${targetName} (${target.type}) failed, continuing with next target`, { error: errorMessage });
|
|
166
|
-
this.onTargetOutcome?.(target, errorMessage);
|
|
162
|
+
this.onTargetOutcome?.(target, { kind: 'failed', retryable: true, error: errorMessage });
|
|
167
163
|
}
|
|
168
164
|
}
|
|
169
165
|
if (this.cancelled) {
|
|
@@ -184,35 +180,17 @@ class DownloadManager {
|
|
|
184
180
|
async dispatchTarget(target) {
|
|
185
181
|
switch (target.type) {
|
|
186
182
|
case 'illustration':
|
|
187
|
-
await this.illustrationHandler.handle(target);
|
|
188
|
-
break;
|
|
183
|
+
return await this.illustrationHandler.handle(target);
|
|
189
184
|
case 'novel':
|
|
190
|
-
await this.novelHandler.handle(target);
|
|
191
|
-
break;
|
|
185
|
+
return await this.novelHandler.handle(target);
|
|
192
186
|
default:
|
|
193
187
|
logger_1.logger.warn(`Unsupported target type ${target.type}`);
|
|
188
|
+
return { kind: 'failed', retryable: false, error: `unsupported target type ${target.type}` };
|
|
194
189
|
}
|
|
195
190
|
}
|
|
196
191
|
updateProgress(current, total, message) {
|
|
197
192
|
this.progressReporter.update(current, total, message);
|
|
198
193
|
}
|
|
199
|
-
buildProxyUrl(network) {
|
|
200
|
-
const proxy = network?.proxy;
|
|
201
|
-
if (!proxy?.enabled) {
|
|
202
|
-
return undefined;
|
|
203
|
-
}
|
|
204
|
-
const protocol = proxy.protocol ?? 'http';
|
|
205
|
-
if (protocol !== 'http' && protocol !== 'https') {
|
|
206
|
-
logger_1.logger.warn(`HTTP multipart delivery does not support ${protocol} proxy through undici; delivering directly`);
|
|
207
|
-
return undefined;
|
|
208
|
-
}
|
|
209
|
-
const url = new URL(`${protocol}://${proxy.host}:${proxy.port}`);
|
|
210
|
-
if (proxy.username)
|
|
211
|
-
url.username = proxy.username;
|
|
212
|
-
if (proxy.password)
|
|
213
|
-
url.password = proxy.password;
|
|
214
|
-
return url.toString();
|
|
215
|
-
}
|
|
216
194
|
}
|
|
217
195
|
exports.DownloadManager = DownloadManager;
|
|
218
196
|
//# sourceMappingURL=DownloadManager.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DownloadManager.js","sourceRoot":"","sources":["../../src/download/DownloadManager.ts"],"names":[],"mappings":";;;AACA,sCAAmC;AAKnC,qDAAkD;AAClD,qEAAkE;AAClE,uDAAoD;AACpD,gEAA6D;AAC7D,4DAAyD;AACzD,8DAA2D;AAC3D,4DAAuF;AACvF,kEAA+D;AAC/D,4CAA0D;AAC1D,
|
|
1
|
+
{"version":3,"file":"DownloadManager.js","sourceRoot":"","sources":["../../src/download/DownloadManager.ts"],"names":[],"mappings":";;;AACA,sCAAmC;AAKnC,qDAAkD;AAClD,qEAAkE;AAClE,uDAAoD;AACpD,gEAA6D;AAC7D,4DAAyD;AACzD,8DAA2D;AAC3D,4DAAuF;AACvF,kEAA+D;AAC/D,4CAA0D;AAC1D,iEAA8D;AAE9D,oFAAiF;AACjF,sEAAmE;AACnE,sEAA0E;AAE1E;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,MAAa,eAAe;IAyDP;IAGA;IA3DF,gBAAgB,CAAmB;IACnC,cAAc,CAAiB;IAC/B,sBAAsB,CAAyB;IAC/C,eAAe,CAAkB;IACjC,OAAO,CAAkB;IACzB,QAAQ,CAAmB;IAC3B,aAAa,CAAwB;IACrC,QAAQ,CAAmB;IAC3B,mBAAmB,CAA4B;IAC/C,YAAY,CAAqB;IAElD,gDAAgD;IACxC,SAAS,GAAG,KAAK,CAAC;IAClB,YAAY,GAAG,EAAE,CAAC;IACT,eAAe,CAAmB;IACnD,mFAAmF;IAC3E,eAAe,GAAoE,IAAI,CAAC;IAEhG,sFAAsF;IAC/E,oBAAoB,CAAC,EAA0D;QACpF,IAAI,CAAC,eAAe,GAAG,EAAE,CAAC;IAC5B,CAAC;IAED,0EAA0E;IAC1E,IAAW,UAAU;QACnB,OAAO,IAAI,CAAC,eAAe,CAAC;IAC9B,CAAC;IAED,iFAAiF;IAC1E,WAAW,CAOhB;IAEF;;;;OAIG;IACI,MAAM,CAAC,SAAiB,WAAW;QACxC,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC;YACpB,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC;YACtB,IAAI,CAAC,YAAY,GAAG,MAAM,CAAC;YAC3B,eAAM,CAAC,IAAI,CAAC,oCAAoC,MAAM,EAAE,CAAC,CAAC;QAC5D,CAAC;IACH,CAAC;IAEM,WAAW;QAChB,OAAO,IAAI,CAAC,SAAS,CAAC;IACxB,CAAC;IAED,YACmB,MAAwB,EACzC,MAAoB,EACpB,QAAmB,EACF,WAAyB;QAHzB,WAAM,GAAN,MAAM,CAAkB;QAGxB,gBAAW,GAAX,WAAW,CAAc;QAE1C,IAAI,CAAC,gBAAgB,GAAG,IAAI,mCAAgB,EAAE,CAAC;QAC/C,IAAI,CAAC,cAAc,GAAG,IAAI,+BAAc,CAAC,MAAM,CAAC,CAAC;QAEjD,MAAM,mBAAmB,GAAG,MAAM,CAAC,QAAQ,EAAE,WAAW,IAAI,CAAC,CAAC;QAC9D,MAAM,WAAW,GAAG,MAAM,CAAC,OAAO,EAAE,qBAAqB,IAAI,MAAM,CAAC,OAAO,EAAE,iBAAiB,IAAI,aAAa,CAAC;QAEhH,IAAI,CAAC,sBAAsB,GAAG,IAAI,+CAAsB,CACtD,MAAM,EACN,QAAQ,EACR,WAAW,EACX,mBAAmB,EACnB,WAAW,CACZ,CAAC;QACF,IAAI,CAAC,eAAe,GAAG,IAAI,iCAAe,CACxC,MAAM,EACN,QAAQ,EACR,WAAW,EACX,QAA6D,CAC9D,CAAC;QACF,IAAI,CAAC,OAAO,GAAG,IAAI,iCAAe,CAAC,QAAQ,EAAE;YAC3C,YAAY,EAAE,CAAC,MAAM,EAAE,IAAI,EAAE,GAAG,EAAE,EAAE,CAClC,IAAI,CAAC,eAAe,CAAC,YAAY,CAAC,MAAM,EAAE,IAAI,EAAE,GAAG,CAAC;SACvD,CAAC,CAAC;QACH,IAAI,CAAC,QAAQ,GAAG,IAAI,mCAAgB,EAAE,CAAC;QAEvC,MAAM,cAAc,GAAG,MAAM,CAAC,QAAQ,IAAI,EAAE,CAAC;QAC7C,MAAM,UAAU,GAAG,cAAc,CAAC,UAAU,IAAI,CAAC,CAAC;QAClD,MAAM,UAAU,GAAG,cAAc,CAAC,UAAU,IAAI,IAAI,CAAC;QACrD,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,UAAU,GAAG,CAAC,EAAE,UAAU,CAAC,CAAC;QAEtD,IAAI,CAAC,aAAa,GAAG,IAAI,oCAAoB,CAAC;YAC5C,WAAW,EAAE,UAAU;YACvB,WAAW,EAAE,UAAU;YACvB,UAAU,EAAE,QAAQ;SACrB,CAAC,CAAC;QAEH,IAAI,CAAC,QAAQ,GAAG,IAAI,mCAAgB,CAAC;YACnC,MAAM;YACN,OAAO,EAAE,IAAI,CAAC,OAAO;YACrB,QAAQ,EAAE,IAAI,CAAC,QAAQ;YACvB,gBAAgB,EAAE,IAAI,CAAC,gBAAgB;YACvC,QAAQ,EAAE,IAAI,CAAC,aAAa;YAC5B,WAAW,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,SAAS;SAClC,CAAC,CAAC;QAEH,4EAA4E;QAC5E,4EAA4E;QAC5E,wDAAwD;QACxD,IAAI,CAAC,eAAe,GAAG,IAAI,iCAAe,CACxC,QAA6D,CAC9D,CAAC;QAEF,0EAA0E;QAC1E,MAAM,YAAY,GAAG,IAAA,gDAA0B,EAC7C,MAAM,EACN,OAAQ,QAA0D,CAAC,eAAe,KAAK,UAAU;YAC/F,CAAC,CAAE,QAAqD;YACxD,CAAC,CAAC,MAAM,CAAC,OAAO,EAAE,YAAY,EAChC,MAAM,CAAC,QAAQ,EAAE,YAAY,IAAI,GAAG,CACrC,CAAC;QAEF,IAAI,CAAC,mBAAmB,GAAG,IAAI,qDAAyB,CACtD,MAAM,EACN,QAAQ,EACR,IAAI,CAAC,cAAc,EACnB,IAAI,CAAC,sBAAsB,EAC3B,IAAI,CAAC,QAAQ,EACb,YAAY,EACZ,IAAI,CAAC,eAAe,CACrB,CAAC;QAEF,IAAI,CAAC,YAAY,GAAG,IAAI,uCAAkB,CACxC,MAAM,EACN,QAAQ,EACR,IAAI,CAAC,cAAc,EACnB,IAAI,CAAC,QAAQ,EACb,IAAI,CAAC,eAAe,EACpB,YAAY,EACZ,IAAI,CAAC,eAAe,CACrB,CAAC;IACJ,CAAC;IAED,mBAAmB,CAAC,QAAoE;QACtF,IAAI,CAAC,gBAAgB,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC;IAC9C,CAAC;IAEM,KAAK,CAAC,UAAU;QACrB,MAAM,IAAI,CAAC,WAAW,CAAC,UAAU,EAAE,CAAC;IACtC,CAAC;IAEM,KAAK,CAAC,aAAa;QACxB,yEAAyE;QACzE,uEAAuE;QACvE,MAAM,YAAY,GAAG,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC;QAEhD,IAAI,YAAY,KAAK,CAAC,EAAE,CAAC;YACvB,IAAI,CAAC,gBAAgB,CAAC,QAAQ,CAAC,CAAC,EAAE,UAAU,CAAC,CAAC;YAC9C,OAAO;QACT,CAAC;QAED,IAAI,aAAa,GAAG,CAAC,CAAC;QACtB,MAAM,MAAM,GAA6C,EAAE,CAAC;QAE5D,KAAK,MAAM,MAAM,IAAI,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;YACzC,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;gBACnB,eAAM,CAAC,IAAI,CAAC,sBAAsB,YAAY,GAAG,aAAa,GAAG,CAAC,eAAe,IAAI,CAAC,YAAY,EAAE,CAAC,CAAC;gBACtG,MAAM;YACR,CAAC;YAED,aAAa,EAAE,CAAC;YAChB,MAAM,UAAU,GAAG,MAAM,CAAC,SAAS,IAAI,MAAM,CAAC,GAAG,IAAI,SAAS,CAAC;YAC/D,IAAI,CAAC,cAAc,CAAC,aAAa,EAAE,YAAY,EAAE,SAAS,UAAU,KAAK,MAAM,CAAC,IAAI,GAAG,CAAC,CAAC;YAEzF,IAAI,CAAC;gBACH,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC;gBAClD,IAAI,CAAC,eAAe,EAAE,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;gBACxC,IAAI,OAAO,CAAC,IAAI,KAAK,QAAQ,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,CAAC;oBACpD,MAAM,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,GAAG,UAAU,KAAK,MAAM,CAAC,IAAI,GAAG,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,CAAC,CAAC;gBAClF,CAAC;YACH,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,qEAAqE;gBACrE,MAAM,YAAY,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;gBAC5E,MAAM,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,GAAG,UAAU,KAAK,MAAM,CAAC,IAAI,GAAG,EAAE,KAAK,EAAE,YAAY,EAAE,CAAC,CAAC;gBAC/E,eAAM,CAAC,KAAK,CAAC,UAAU,UAAU,KAAK,MAAM,CAAC,IAAI,uCAAuC,EAAE,EAAE,KAAK,EAAE,YAAY,EAAE,CAAC,CAAC;gBACnH,IAAI,CAAC,eAAe,EAAE,CAAC,MAAM,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,YAAY,EAAE,CAAC,CAAC;YAC3F,CAAC;QACH,CAAC;QAED,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;YACnB,MAAM,IAAI,gCAAuB,CAAC,UAAU,IAAI,CAAC,YAAY,IAAI,MAAM,EAAE,CAAC,CAAC;QAC7E,CAAC;QAED,IAAI,CAAC,gBAAgB,CAAC,QAAQ,CAAC,YAAY,EAAE,UAAU,CAAC,CAAC;QAEzD,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACtB,eAAM,CAAC,IAAI,CAAC,kBAAkB,MAAM,CAAC,MAAM,mBAAmB,EAAE;gBAC9D,aAAa,EAAE,MAAM,CAAC,MAAM;gBAC5B,YAAY;gBACZ,MAAM,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC;aAChE,CAAC,CAAC;YAEH,IAAI,MAAM,CAAC,MAAM,KAAK,YAAY,EAAE,CAAC;gBACnC,MAAM,IAAI,KAAK,CAAC,OAAO,YAAY,0CAA0C,CAAC,CAAC;YACjF,CAAC;QACH,CAAC;IACH,CAAC;IAEO,KAAK,CAAC,cAAc,CAAC,MAAoB;QAC/C,QAAQ,MAAM,CAAC,IAAI,EAAE,CAAC;YACpB,KAAK,cAAc;gBACjB,OAAO,MAAM,IAAI,CAAC,mBAAmB,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;YACvD,KAAK,OAAO;gBACV,OAAO,MAAM,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;YAChD;gBACE,eAAM,CAAC,IAAI,CAAC,2BAA2B,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC;gBACtD,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,SAAS,EAAE,KAAK,EAAE,KAAK,EAAE,2BAA2B,MAAM,CAAC,IAAI,EAAE,EAAE,CAAC;QACjG,CAAC;IACH,CAAC;IAEO,cAAc,CAAC,OAAe,EAAE,KAAa,EAAE,OAAgB;QACrE,IAAI,CAAC,gBAAgB,CAAC,MAAM,CAAC,OAAO,EAAE,KAAK,EAAE,OAAO,CAAC,CAAC;IACxD,CAAC;CAEF;AAjOD,0CAiOC"}
|
|
@@ -19,6 +19,7 @@ export declare class IllustrationDownloader {
|
|
|
19
19
|
downloadIllustration(illust: PixivIllust, tag: string, options?: {
|
|
20
20
|
aiMetadataCheck?: boolean;
|
|
21
21
|
maxPageCount?: number;
|
|
22
|
+
includeDeliveryPreviews?: boolean;
|
|
22
23
|
}): Promise<DownloadedArtifact | null>;
|
|
23
24
|
/**
|
|
24
25
|
* Download an ugoira (animation) work: fetch metadata, download the frame
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"IllustrationDownloader.d.ts","sourceRoot":"","sources":["../../src/download/IllustrationDownloader.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAC1D,OAAO,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AACpD,OAAO,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAE1D,OAAO,EAAE,WAAW,EAAmB,MAAM,sBAAsB,CAAC;AAOpE,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAWvD;;GAEG;AACH,qBAAa,sBAAsB;IAE/B,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,QAAQ;IACzB,OAAO,CAAC,QAAQ,CAAC,WAAW;IAC5B,OAAO,CAAC,QAAQ,CAAC,mBAAmB;IACpC,OAAO,CAAC,QAAQ,CAAC,WAAW;gBAJX,MAAM,EAAE,YAAY,EACpB,QAAQ,EAAE,SAAS,EACnB,WAAW,EAAE,YAAY,EACzB,mBAAmB,EAAE,MAAM,EAC3B,WAAW,EAAE,MAAM;IAGtC;;OAEG;IACG,oBAAoB,CACxB,MAAM,EAAE,WAAW,EACnB,GAAG,EAAE,MAAM,EACX,OAAO,GAAE;
|
|
1
|
+
{"version":3,"file":"IllustrationDownloader.d.ts","sourceRoot":"","sources":["../../src/download/IllustrationDownloader.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAC1D,OAAO,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AACpD,OAAO,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAE1D,OAAO,EAAE,WAAW,EAAmB,MAAM,sBAAsB,CAAC;AAOpE,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAWvD;;GAEG;AACH,qBAAa,sBAAsB;IAE/B,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,QAAQ;IACzB,OAAO,CAAC,QAAQ,CAAC,WAAW;IAC5B,OAAO,CAAC,QAAQ,CAAC,mBAAmB;IACpC,OAAO,CAAC,QAAQ,CAAC,WAAW;gBAJX,MAAM,EAAE,YAAY,EACpB,QAAQ,EAAE,SAAS,EACnB,WAAW,EAAE,YAAY,EACzB,mBAAmB,EAAE,MAAM,EAC3B,WAAW,EAAE,MAAM;IAGtC;;OAEG;IACG,oBAAoB,CACxB,MAAM,EAAE,WAAW,EACnB,GAAG,EAAE,MAAM,EACX,OAAO,GAAE;QACP,eAAe,CAAC,EAAE,OAAO,CAAC;QAC1B,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,uBAAuB,CAAC,EAAE,OAAO,CAAC;KAC9B,GACL,OAAO,CAAC,kBAAkB,GAAG,IAAI,CAAC;IAgPrC;;;;OAIG;YACW,cAAc;IAyG5B;;OAEG;YACW,6BAA6B;IAgC3C;;OAEG;IACH,OAAO,CAAC,oBAAoB;IAkB5B;;OAEG;IACH,OAAO,CAAC,eAAe;IAiBvB;;OAEG;IACH,OAAO,CAAC,gBAAgB;CAQzB"}
|
|
@@ -127,6 +127,18 @@ class IllustrationDownloader {
|
|
|
127
127
|
// Add timeout protection for image download (2 minutes per image)
|
|
128
128
|
const buffer = await withTimeout(this.client.downloadImage(originalUrl), 120000, `Timeout: Failed to download image for illustration ${detail.id} page ${index + 1} within 120 seconds`);
|
|
129
129
|
const filePath = await this.fileService.saveImage(buffer, fileName, metadata);
|
|
130
|
+
let previewPath;
|
|
131
|
+
const previewUrl = page.image_urls.large ?? page.image_urls.medium;
|
|
132
|
+
if (options.includeDeliveryPreviews && previewUrl && previewUrl !== originalUrl) {
|
|
133
|
+
try {
|
|
134
|
+
const preview = await withTimeout(this.client.downloadImage(previewUrl), 120000, `Timeout: Failed to download preview for illustration ${detail.id} page ${index + 1}`);
|
|
135
|
+
previewPath = `${filePath}.preview.jpg`;
|
|
136
|
+
await node_fs_1.promises.writeFile(previewPath, Buffer.from(preview));
|
|
137
|
+
}
|
|
138
|
+
catch (error) {
|
|
139
|
+
logger_1.logger.warn(`Preview unavailable for illustration ${detail.id} page ${index + 1}: ${(0, errors_1.getErrorMessage)(error)}`);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
130
142
|
// Release the page buffer promptly: ArrayBuffers are external memory,
|
|
131
143
|
// NOT capped by --max-old-space-size. Without explicit GC they linger
|
|
132
144
|
// and accumulate while downloading multi-page works, ballooning node
|
|
@@ -168,12 +180,13 @@ class IllustrationDownloader {
|
|
|
168
180
|
// Log warning but don't fail the download if metadata save fails
|
|
169
181
|
logger_1.logger.warn(`Failed to save metadata for illustration ${detail.id} page ${index + 1}: ${error instanceof Error ? error.message : String(error)}`);
|
|
170
182
|
}
|
|
171
|
-
return { filePath, index: index + 1, metadataPath };
|
|
183
|
+
return { filePath, index: index + 1, metadataPath, previewPath };
|
|
172
184
|
}, concurrency);
|
|
173
185
|
// Insert download records and log results
|
|
174
186
|
let successCount = 0;
|
|
175
187
|
const files = [];
|
|
176
188
|
const cleanupFiles = [];
|
|
189
|
+
const previewFiles = [];
|
|
177
190
|
for (const result of downloadResults) {
|
|
178
191
|
if (result.success) {
|
|
179
192
|
this.database.insertDownload({
|
|
@@ -196,6 +209,10 @@ class IllustrationDownloader {
|
|
|
196
209
|
if (result.result.metadataPath) {
|
|
197
210
|
cleanupFiles.push(result.result.metadataPath);
|
|
198
211
|
}
|
|
212
|
+
if (result.result.previewPath) {
|
|
213
|
+
previewFiles.push(result.result.previewPath);
|
|
214
|
+
cleanupFiles.push(result.result.previewPath);
|
|
215
|
+
}
|
|
199
216
|
}
|
|
200
217
|
else {
|
|
201
218
|
logger_1.logger.warn(`Failed to download page ${result.error.message}`, {
|
|
@@ -241,6 +258,7 @@ class IllustrationDownloader {
|
|
|
241
258
|
title: detail.title,
|
|
242
259
|
tags: tags.map((item) => item.name).filter(Boolean),
|
|
243
260
|
files,
|
|
261
|
+
previewFiles: previewFiles.length === files.length ? previewFiles : undefined,
|
|
244
262
|
cleanupFiles,
|
|
245
263
|
spoiler: (detail.x_restrict ?? 0) > 0,
|
|
246
264
|
xRestrict: detail.x_restrict,
|