$value * MINUTE_IN_SECONDS, 'days' => $value * DAY_IN_SECONDS, default => $value * HOUR_IN_SECONDS, }; } public function start(string $mode, int $user_id, int $first_run_at = 0): void { update_option('aap_auto_enabled', true); update_option('aap_auto_user_id', $user_id); $label = Options::int('aap_auto_interval_value') . ' ' . Options::get('aap_auto_interval_unit', 'hours'); if ($mode === 'now') { update_option('aap_next_auto_run', time() + self::interval_seconds()); Logger::info("Automatic generation enabled (user {$user_id}, interval {$label}). Running now.", 'schedule'); $this->spawn($user_id); return; } $first = ($first_run_at > 0 && $first_run_at > time()) ? $first_run_at : time() + self::interval_seconds(); update_option('aap_next_auto_run', $first); Logger::info( "Automatic generation enabled (user {$user_id}, interval {$label}). First run: " . wp_date('d/m/Y H:i', $first) . '.', 'schedule' ); } public function stop(): void { update_option('aap_auto_enabled', false); delete_option('aap_next_auto_run'); delete_transient('aap_auto_spawn_lock'); wp_clear_scheduled_hook('aap_auto_run'); Logger::info('Automatic generation paused.', 'schedule'); } public function reschedule(): void { if (!Options::bool('aap_auto_enabled')) { return; } update_option('aap_next_auto_run', time() + self::interval_seconds()); Logger::info('Interval changed; next run rescheduled.', 'schedule'); } /** Called on every web request (shutdown) and by the system cron. */ public function tick(): void { if (!Options::bool('aap_auto_enabled')) { return; } if (RunState::is_running() && !RunState::is_stale()) { return; } $next = (int) get_option('aap_next_auto_run', 0); if ($next === 0 || time() < $next) { return; } // Atomic-ish lock: avoid double spawn in concurrent requests. if (get_transient('aap_auto_spawn_lock')) { return; } set_transient('aap_auto_spawn_lock', 1, 60); $next_ts = time() + self::interval_seconds(); update_option('aap_next_auto_run', $next_ts); $author = (int) get_option('aap_auto_user_id', 0); Logger::info('Automatic run triggered by the scheduler. Next: ' . wp_date('d/m/Y H:i', $next_ts) . '.', 'schedule'); $this->spawn($author); } private function spawn(int $user_id): bool { $pipeline = Registry::get('pipeline'); if (!$pipeline instanceof PipelineModule) { Logger::error('Pipeline module unavailable; cannot start a run.', 'schedule'); return false; } return $pipeline->spawn($user_id); } }