colorCli = new ColorCLI; $this->dnr_path = PHP_BINARY.' misc/update/multiprocessing/.do_not_run/switch.php "php '; $this->maxSize = (int) Settings::settingValue('maxsizetoprocessnfo'); $this->minSize = (int) Settings::settingValue('minsizetoprocessnfo'); $this->maxRetries = (int) Settings::settingValue('maxnforetries') >= 0 ? -((int) Settings::settingValue('maxnforetries') + 1) : Nfo::NFO_UNPROC; $this->maxRetries = max($this->maxRetries, -8); // init runners $this->backfillRunner = new BackfillRunner($this->colorCli); $this->binariesRunner = new BinariesRunner($this->colorCli); $this->releasesRunner = new ReleasesRunner($this->colorCli); $this->postProcessRunner = new PostProcessRunner($this->colorCli); } /** * Setup the class to work on a type of work, then process the work. * Valid work types:. * * @param string $type The type of multiProcessing to do : backfill, binaries, releases, postprocess * @param array $options Array containing arguments for the type of work. * * @throws \Exception */ public function processWorkType(string $type, array $options = []): void { // Set/reset some variables. $startTime = now()->timestamp; $this->workType = $type; $this->workTypeOptions = $options; $this->processAdditional = $this->processNFO = $this->processTV = $this->processMovies = $this->ppRenamedOnly = false; $this->work = []; // Get work to fork via runners $this->getWork(); // Process extra work that should not be forked and done after. $this->processEndWork(); if (config('nntmux.echocli')) { $this->colorCli->header( 'Multi-processing for '.$this->workType.' finished in '.(now()->timestamp - $startTime). ' seconds at '.now()->toRfc2822String().'.'.PHP_EOL ); } } /** * Only post process renamed movie / tv releases? */ private bool $ppRenamedOnly; /** * Helper to ensure concurrency is at least 1 and with a common timeout. */ private function createPool(int $concurrency): Pool { $concurrency = max(1, $concurrency); return Pool::create() ->concurrency($concurrency) ->timeout(config('nntmux.multiprocessing_max_child_time')); } /** * Helper to build a DNR switch command. */ private function buildDnrCommand(string $args): string { return $this->dnr_path.$args.'"'; } /** * Get work for our workers to work on, set the max child processes here. * * @throws \Exception */ private function getWork(): void { $this->maxProcesses = 0; switch ($this->workType) { case 'backfill': $this->backfillRunner->backfill($this->workTypeOptions); break; case 'binaries': $maxPerGroup = (int) ($this->workTypeOptions[0] ?? 0); $this->binariesRunner->binaries($maxPerGroup); break; case 'fixRelNames_standard': $this->releasesRunner->fixRelNames('standard', (int) Settings::settingValue('fixnamesperrun'), (int) Settings::settingValue('fixnamethreads')); break; case 'fixRelNames_predbft': $this->releasesRunner->fixRelNames('predbft', (int) Settings::settingValue('fixnamesperrun'), (int) Settings::settingValue('fixnamethreads')); break; case 'releases': $this->releasesRunner->releases(); break; case 'postProcess_ama': $this->processSingle(); break; case 'postProcess_add': $this->postProcessRunner->processAdditional(); break; case 'postProcess_mov': $renamedOnly = (isset($this->workTypeOptions[0]) && $this->workTypeOptions[0] === true); $this->postProcessRunner->processMovies($renamedOnly); break; case 'postProcess_nfo': $this->postProcessRunner->processNfo(); break; case 'postProcess_tv': $renamedOnly = (isset($this->workTypeOptions[0]) && $this->workTypeOptions[0] === true); if ($this->postProcessRunner->hasTvWork($renamedOnly)) { $this->postProcessRunner->processTv($renamedOnly); } else { $this->logger('No TV work to do.'); } break; case 'safe_backfill': $this->backfillRunner->safeBackfill(); break; case 'safe_binaries': $this->binariesRunner->safeBinaries(); break; case 'update_per_group': $this->releasesRunner->updatePerGroup(); break; } } /** * Process any work that does not need to be forked, but needs to run at the end. */ private function processEndWork(): void { switch ($this->workType) { case 'update_per_group': case 'releases': $count = $this->getReleaseWorkCount(); $this->_executeCommand($this->buildDnrCommand('releases '.$count.'_')); break; } } /** * Compute count of groups which currently have collections pending (used for DNR signalling). */ private function getReleaseWorkCount(): int { $groups = DB::select('SELECT id FROM usenet_groups WHERE (active = 1 OR backfill = 1)'); $count = 0; foreach ($groups as $g) { try { $q = DB::select(sprintf('SELECT id FROM collections WHERE groups_id = %d LIMIT 1', $g->id)); if (! empty($q)) { $count++; } } catch (\PDOException $e) { if (config('app.debug') === true) { \Log::debug($e->getMessage()); } } } return $count; } // The remaining methods are kept for BC and potential direct usage within the codebase, // but are not used when processWorkType delegates to runner classes. private function backfill(): void {} private function safeBackfill(): void {} private function binaries(): void {} private function safeBinaries(): void {} private function fixRelNames(): void {} private function releases(): void {} public function postProcess(array $releases, int $maxProcess): void {} private function postProcessAdd(): void {} private function postProcessNfo(): void {} private function postProcessMov(): void {} private function postProcessTv(): void {} private function processSingle(): void { $postProcess = new PostProcess; $postProcess->processBooks(); $postProcess->processConsoles(); $postProcess->processGames(); $postProcess->processMusic(); $postProcess->processXXX(); } protected function _executeCommand(string $command): string { $process = Process::fromShellCommandline($command); $process->setTimeout(1800); $process->run(function ($type, $buffer) { if ($type === Process::ERR) { echo $buffer; } }); return $process->getOutput(); } public function logger(string $message): void { if (config('nntmux.echocli')) { echo $message.PHP_EOL; } } public function exit(string $pid): void { if (config('nntmux.echocli')) { $this->colorCli->header( 'Process ID #'.$pid.' has completed.'.PHP_EOL. 'There are '.(max(1, $this->maxProcesses) - 1).' process(es) still active with '. (--$this->_workCount).' job(s) left in the queue.', true ); } } }