#!/usr/bin/env php 0 && posix_kill($pid, 0)) { return false; // another instance alive } @unlink($lockFile); // stale lock } file_put_contents($lockFile, (string) getmypid()); return true; } function releaseLock(string $lockFile): void { @unlink($lockFile); } function run(string $root, string $cmd, string $logFile, string $tag): int { logMsg($logFile, ">>> [$tag] $cmd"); $output = []; $code = 0; exec('cd ' . escapeshellarg($root) . ' && ' . $cmd . ' 2>&1', $output, $code); foreach (array_slice($output, -6) as $line) { logMsg($logFile, " $line"); } logMsg($logFile, "<<< [$tag] exit $code"); return $code; } logMsg($logFile, "Scheduler loop started — interval {$intervalMin}min, batch {$batchSize}"); while (true) { if (! acquireLock($lockFile)) { logMsg($logFile, 'Another crawler instance is running — exiting this loop.'); exit(1); } try { // 1. refresh prices from direct sources run($root, 'php spark gg:crawl steam', $logFile, 'steam'); run($root, 'php spark gg:crawl gog', $logFile, 'gog'); run($root, 'php spark gg:crawl epic', $logFile, 'epic'); run($root, 'php spark gg:crawl finalize', $logFile, 'finalize'); // 2. expand catalog via FlareSolverr session run($root, "php spark gg:ggdeals --batch={$batchSize}", $logFile, 'ggdeals-expand'); } catch (\Throwable $e) { logMsg($logFile, 'ERROR in loop: ' . $e->getMessage()); } finally { releaseLock($lockFile); } logMsg($logFile, "Sleeping {$intervalMin}min until next cycle…"); sleep($intervalMin * 60); }