pixivflow 2.19.4 → 2.20.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.
Files changed (44) hide show
  1. package/dist/commands/SchedulerCommand.js +36 -0
  2. package/dist/commands/SchedulerRunOnceCommand.d.ts +2 -3
  3. package/dist/commands/SchedulerRunOnceCommand.js +2 -3
  4. package/dist/commands/scheduler-runtime.js +17 -1
  5. package/dist/config/defaults.d.ts +1 -0
  6. package/dist/config/defaults.js +4 -0
  7. package/dist/config/environment.js +19 -0
  8. package/dist/config/types.d.ts +28 -0
  9. package/dist/config/validation.js +25 -0
  10. package/dist/delivery/DeliveryService.d.ts +30 -1
  11. package/dist/delivery/DeliveryService.js +13 -2
  12. package/dist/delivery/HttpMultipartDelivery.js +29 -8
  13. package/dist/delivery/OutboxWorker.d.ts +2 -0
  14. package/dist/delivery/OutboxWorker.js +3 -0
  15. package/dist/delivery/types.d.ts +18 -0
  16. package/dist/download/DownloadManager.js +12 -1
  17. package/dist/download/handlers/IllustrationTargetHandler.d.ts +42 -2
  18. package/dist/download/handlers/IllustrationTargetHandler.js +240 -50
  19. package/dist/download/handlers/NovelTargetHandler.d.ts +47 -0
  20. package/dist/download/handlers/NovelTargetHandler.js +213 -27
  21. package/dist/download/handlers/deliveryContext.d.ts +16 -0
  22. package/dist/download/handlers/deliveryContext.js +32 -0
  23. package/dist/download/pipeline/DownloadPipeline.d.ts +22 -3
  24. package/dist/download/pipeline/DownloadPipeline.js +109 -92
  25. package/dist/download/plan/DownloadPlanner.d.ts +47 -1
  26. package/dist/download/plan/DownloadPlanner.js +105 -15
  27. package/dist/notification/NotificationPolicy.d.ts +12 -0
  28. package/dist/notification/NotificationPolicy.js +86 -0
  29. package/dist/package.json +1 -1
  30. package/dist/scheduler/MultiScheduleManager.js +2 -0
  31. package/dist/scheduler/ScheduleTriggerServer.d.ts +58 -1
  32. package/dist/scheduler/ScheduleTriggerServer.js +207 -40
  33. package/dist/scheduler/SlotCoordinator.d.ts +80 -2
  34. package/dist/scheduler/SlotCoordinator.js +145 -3
  35. package/dist/scheduler/TargetOutcome.d.ts +195 -2
  36. package/dist/scheduler/TargetOutcome.js +202 -3
  37. package/dist/storage/DatabaseMigration.js +11 -0
  38. package/dist/storage/repositories/DeliveryRepository.d.ts +12 -0
  39. package/dist/storage/repositories/DeliveryRepository.js +18 -1
  40. package/dist/storage/repositories/SlotRepository.d.ts +8 -0
  41. package/dist/storage/repositories/SlotRepository.js +6 -2
  42. package/dist/version.js +1 -1
  43. package/dist/webui/package.json +1 -1
  44. package/package.json +1 -1
@@ -99,6 +99,15 @@ class DatabaseMigration {
99
99
  lease_owner TEXT,
100
100
  lease_until INTEGER,
101
101
  heartbeat_at INTEGER,
102
+ -- Request UUID of a remote manual replacement ("重抓"). NULL for a
103
+ -- scheduled occurrence. Persisted in the SAME transaction that
104
+ -- opens the manual slot, so the acceptance ACK and the caller's
105
+ -- retry converge on one row instead of racing a second slot.
106
+ manual_request_id TEXT,
107
+ -- Opaque caller correlation (review chain / review id). Recorded so
108
+ -- the terminal outcome can be reported back to the requester
109
+ -- without this service learning anything about the review.
110
+ correlation_id TEXT,
102
111
  created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
103
112
  started_at DATETIME,
104
113
  completed_at DATETIME,
@@ -227,6 +236,8 @@ class DatabaseMigration {
227
236
  lease_owner: 'ALTER TABLE schedule_slots ADD COLUMN lease_owner TEXT',
228
237
  lease_until: 'ALTER TABLE schedule_slots ADD COLUMN lease_until INTEGER',
229
238
  heartbeat_at: 'ALTER TABLE schedule_slots ADD COLUMN heartbeat_at INTEGER',
239
+ manual_request_id: 'ALTER TABLE schedule_slots ADD COLUMN manual_request_id TEXT',
240
+ correlation_id: 'ALTER TABLE schedule_slots ADD COLUMN correlation_id TEXT',
230
241
  };
231
242
  const columnAlters = [];
232
243
  for (const [col, sql] of Object.entries(slotColumnMigrations)) {
@@ -60,6 +60,18 @@ export declare class DeliveryRepository extends BaseRepository {
60
60
  isDelivered(deliveryTarget: string, workType: string, pixivId: string): boolean;
61
61
  /** Batch form for candidate-pipeline pre-lock dedupe. */
62
62
  deliveredIds(deliveryTarget: string, workType: string, pixivIds: string[]): Set<string>;
63
+ /**
64
+ * Batch pre-lock dedupe for CANDIDATE SELECTION: works already delivered, or
65
+ * whose review submission is still PENDING.
66
+ *
67
+ * Deliberately broader than `deliveredIds`: a pending intent is a work already
68
+ * submitted for human review that has not been answered yet. Re-selecting it
69
+ * would submit the same work a second time, which is the user-visible defect
70
+ * this scan exists to prevent. Within-slot RESUME keeps using `isDelivered`:
71
+ * a cell resuming its OWN pending work is continuing it, not duplicating it.
72
+ */
73
+ submittedIds(deliveryTarget: string, workType: string, pixivIds: string[]): Set<string>;
74
+ private idsByStatus;
63
75
  recordAck(id: string, ack: {
64
76
  status: DeliveryStatus;
65
77
  remoteId?: string;
@@ -71,16 +71,33 @@ class DeliveryRepository extends BaseRepository_1.BaseRepository {
71
71
  }
72
72
  /** Batch form for candidate-pipeline pre-lock dedupe. */
73
73
  deliveredIds(deliveryTarget, workType, pixivIds) {
74
+ return this.idsByStatus(deliveryTarget, workType, pixivIds, ['delivered', 'duplicate']);
75
+ }
76
+ /**
77
+ * Batch pre-lock dedupe for CANDIDATE SELECTION: works already delivered, or
78
+ * whose review submission is still PENDING.
79
+ *
80
+ * Deliberately broader than `deliveredIds`: a pending intent is a work already
81
+ * submitted for human review that has not been answered yet. Re-selecting it
82
+ * would submit the same work a second time, which is the user-visible defect
83
+ * this scan exists to prevent. Within-slot RESUME keeps using `isDelivered`:
84
+ * a cell resuming its OWN pending work is continuing it, not duplicating it.
85
+ */
86
+ submittedIds(deliveryTarget, workType, pixivIds) {
87
+ return this.idsByStatus(deliveryTarget, workType, pixivIds, ['pending', 'delivered', 'duplicate']);
88
+ }
89
+ idsByStatus(deliveryTarget, workType, pixivIds, statuses) {
74
90
  const out = new Set();
75
91
  if (pixivIds.length === 0)
76
92
  return out;
93
+ const statusList = statuses.map((status) => `'${status}'`).join(',');
77
94
  const CHUNK = 400;
78
95
  for (let i = 0; i < pixivIds.length; i += CHUNK) {
79
96
  const slice = pixivIds.slice(i, i + CHUNK);
80
97
  const placeholders = slice.map(() => '?').join(',');
81
98
  const rows = this.db
82
99
  .prepare(`SELECT DISTINCT pixiv_id FROM deliveries
83
- WHERE delivery_target = ? AND work_type = ? AND status IN ('delivered','duplicate')
100
+ WHERE delivery_target = ? AND work_type = ? AND status IN (${statusList})
84
101
  AND pixiv_id IN (${placeholders})`)
85
102
  .all(deliveryTarget, workType, ...slice);
86
103
  for (const r of rows)
@@ -25,6 +25,10 @@ export interface SlotRecord {
25
25
  leaseOwner: string | null;
26
26
  leaseUntil: number | null;
27
27
  heartbeatAt: number | null;
28
+ /** Request UUID of a remote manual replacement; null for scheduled slots. */
29
+ manualRequestId: string | null;
30
+ /** Opaque caller correlation (review chain / review id); null unless manual. */
31
+ correlationId: string | null;
28
32
  }
29
33
  export interface SlotItemRecord {
30
34
  id: number;
@@ -65,6 +69,10 @@ export declare class SlotRepository extends BaseRepository {
65
69
  triggerSource?: string;
66
70
  slotDate?: string;
67
71
  slotName?: string;
72
+ /** Remote manual replacement request UUID (opens a `manual-` slot). */
73
+ manualRequestId?: string | null;
74
+ /** Opaque caller correlation recorded with a manual slot. */
75
+ correlationId?: string | null;
68
76
  }): {
69
77
  slot: SlotRecord;
70
78
  created: boolean;
@@ -21,10 +21,10 @@ class SlotRepository extends BaseRepository_1.BaseRepository {
21
21
  getOrCreateSlot(id, data) {
22
22
  const insert = this.db.prepare(`INSERT INTO schedule_slots
23
23
  (id, schedule_id, occurrence_at, occurrence_date, occurrence_label, timezone, target_ids,
24
- status, trigger_source, slot_date, slot_name)
24
+ status, trigger_source, slot_date, slot_name, manual_request_id, correlation_id)
25
25
  VALUES
26
26
  (@id, @scheduleId, @occurrenceAt, @occurrenceDate, @occurrenceLabel, @timezone, @targetIds,
27
- 'pending', @triggerSource, @slotDate, @slotName)
27
+ 'pending', @triggerSource, @slotDate, @slotName, @manualRequestId, @correlationId)
28
28
  ON CONFLICT(id) DO NOTHING`);
29
29
  const info = insert.run({
30
30
  id,
@@ -37,6 +37,8 @@ class SlotRepository extends BaseRepository_1.BaseRepository {
37
37
  triggerSource: data.triggerSource ?? null,
38
38
  slotDate: data.slotDate ?? data.occurrenceDate ?? '',
39
39
  slotName: data.slotName ?? '',
40
+ manualRequestId: data.manualRequestId ?? null,
41
+ correlationId: data.correlationId ?? null,
40
42
  });
41
43
  const created = info.changes > 0;
42
44
  return { slot: this.getSlot(id), created };
@@ -359,6 +361,8 @@ class SlotRepository extends BaseRepository_1.BaseRepository {
359
361
  leaseOwner: row.lease_owner ?? null,
360
362
  leaseUntil: row.lease_until ?? null,
361
363
  heartbeatAt: row.heartbeat_at ?? null,
364
+ manualRequestId: row.manual_request_id ?? null,
365
+ correlationId: row.correlation_id ?? null,
362
366
  };
363
367
  }
364
368
  toItem(row) {
package/dist/version.js CHANGED
@@ -2,5 +2,5 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.BUILD = void 0;
4
4
  // GENERATED by scripts/write-version.js — do not edit manually.
5
- exports.BUILD = { version: '2.19.4', commit: 'f331cd4bb164' };
5
+ exports.BUILD = { version: '2.20.0', commit: '2229fb7b45a6' };
6
6
  //# sourceMappingURL=version.js.map
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "type": "commonjs",
3
3
  "name": "pixivflow-webui-backend",
4
- "version": "2.19.4",
4
+ "version": "2.20.0",
5
5
  "description": "PixivFlow WebUI Backend - CommonJS module"
6
6
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pixivflow",
3
- "version": "2.19.4",
3
+ "version": "2.20.0",
4
4
  "description": "🎨 智能的 Pixiv 自动化下载工具 - 支持批量下载插画和小说、定时任务、Docker部署 | Intelligent Pixiv Automation Downloader with batch download, scheduler, and Docker support",
5
5
  "repository": {
6
6
  "type": "git",