portrait-queue-worker.mjs 2.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475
  1. import path from 'node:path';
  2. import { createRequire } from 'node:module';
  3. import { PACKAGE_ROOT } from './config-loader.mjs';
  4. const require = createRequire(import.meta.url);
  5. const { processPortraitQueue } = require(path.join(
  6. PACKAGE_ROOT,
  7. 'mcp',
  8. 'src',
  9. 'tools',
  10. 'qiwei-portrait-tags-run.js',
  11. ));
  12. export class PortraitQueueWorker {
  13. constructor({ intervalMs = 60_000, limit = 5, onState = () => {}, processor = processPortraitQueue } = {}) {
  14. this.intervalMs = intervalMs;
  15. this.limit = limit;
  16. this.onState = onState;
  17. this.processor = processor;
  18. this.running = false;
  19. this.timer = null;
  20. this.inFlight = null;
  21. }
  22. start() {
  23. if (this.running) return;
  24. this.running = true;
  25. this.onState({ portraitQueue: { status: 'running', lastError: '' } });
  26. void this.runOnce();
  27. this.timer = setInterval(() => void this.runOnce(), this.intervalMs);
  28. this.timer.unref?.();
  29. }
  30. async runOnce() {
  31. if (!this.running) return { skipped: true, reason: 'stopped' };
  32. if (this.inFlight) return { skipped: true, reason: 'in-flight' };
  33. this.inFlight = (async () => {
  34. try {
  35. const result = await this.processor(this.limit);
  36. this.onState({
  37. portraitQueue: {
  38. status: 'running',
  39. lastRunAt: new Date().toISOString(),
  40. processed: Number(result?.processed || 0),
  41. remaining: Number(result?.remaining || 0),
  42. lastError: '',
  43. },
  44. });
  45. return result;
  46. } catch (error) {
  47. this.onState({
  48. portraitQueue: {
  49. status: 'error',
  50. lastRunAt: new Date().toISOString(),
  51. lastError: error.message,
  52. },
  53. });
  54. return { processed: 0, error: error.message };
  55. } finally {
  56. this.inFlight = null;
  57. }
  58. })();
  59. return this.inFlight;
  60. }
  61. async stop() {
  62. this.running = false;
  63. if (this.timer) clearInterval(this.timer);
  64. this.timer = null;
  65. if (this.inFlight) await this.inFlight;
  66. this.onState({ portraitQueue: { status: 'stopped', stoppedAt: new Date().toISOString() } });
  67. }
  68. }