Files
newznab-tmux/app/Services/AdditionalProcessing/State/ReleaseProcessingContext.php
T
2026-08-11 10:43:22 +02:00

298 lines
7.7 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Services\AdditionalProcessing\State;
use App\Models\Release;
use App\Services\AdditionalProcessing\DTO\AdditionalWorkPlan;
use App\Services\AdditionalProcessing\DTO\DownloadedArchive;
/**
* Mutable context object that holds the processing state for a single release.
* Passed between services during processing to share state.
*
* NOTE: Intentionally NOT a `spatie/laravel-data` Data object. This class is
* mutated heavily during the additional-processing pipeline (counters, found
* flags, accumulating message IDs) and holds an Eloquent {@see Release} model,
* neither of which fits the immutable, serialisable design of `Data` DTOs.
*/
class ReleaseProcessingContext
{
private const int MAX_RETAINED_ARCHIVES = 2;
private const int MAX_RETAINED_ARCHIVE_BYTES = 16_777_216;
// The release being processed
public Release $release;
// Processing state flags
public bool $foundVideo = false;
public bool $foundMediaInfo = false;
public bool $foundAudioInfo = false;
public bool $foundAudioSample = false;
public bool $foundJPGSample = false;
public bool $foundSample = false;
public bool $foundPAR2Info = false;
// Password state
public int $passwordStatus = 0;
public bool $releaseHasPassword = false;
// NFO state
public bool $releaseHasNoNFO = false;
// Group state
public string $releaseGroupName = '';
public bool $groupUnavailable = false;
// NZB state
public bool $nzbHasCompressedFile = false;
/**
* @var list<array<string, mixed>>
*/
public array $nzbContents = [];
public ?AdditionalWorkPlan $workPlan = null;
/**
* @var list<DownloadedArchive>
*/
public array $downloadedArchives = [];
private int $retainedArchiveBytes = 0;
// Message IDs for downloading
/**
* @var list<string>
*/
public array $sampleMessageIDs = [];
/**
* @var list<string>
*/
public array $jpgMessageIDs = [];
/**
* @var list<string>|string
*/
public string|array $mediaInfoMessageIDs = [];
/**
* @var list<string>|string
*/
public string|array $audioInfoMessageIDs = [];
/**
* @var list<string>
*/
public array $rarFileMessageIDs = [];
public string $audioInfoExtension = '';
// File info counters
public int $addedFileInfo = 0;
public int $totalFileInfo = 0;
public int $compressedFilesChecked = 0;
/**
* @var array<string, array<string, mixed>>
*/
public array $pendingReleaseFiles = [];
/**
* @var array<string, array<string, mixed>>
*/
public array $pendingParHashes = [];
/**
* @var array<string, true>|null
*/
public ?array $existingReleaseFileNames = null;
public bool $releaseFilesChanged = false;
// Temp path for this release
public string $tmpPath = '';
// Timeout tracking
public float $startTime = 0;
public function __construct(Release $release)
{
$this->release = $release;
$this->startTime = hrtime(true);
}
/**
* Initialize processing state based on configuration flags.
* Sets "found" flags to true if processing is disabled (to skip those steps).
*/
public function initializeFromConfig(
bool $processVideo,
bool $processMediaInfo,
bool $processAudioInfo,
bool $processAudioSample,
bool $processJPGSample,
bool $processThumbnails
): void {
$this->foundVideo = ! $processVideo;
$this->foundMediaInfo = ! $processMediaInfo;
$this->foundAudioInfo = ! $processAudioInfo;
$this->foundAudioSample = ! $processAudioSample;
$this->foundJPGSample = ! $processJPGSample;
$this->foundSample = ! $processThumbnails;
$this->foundPAR2Info = false;
}
/**
* Reset message ID arrays for a fresh processing run.
*/
public function resetMessageIDs(): void
{
$this->sampleMessageIDs = [];
$this->jpgMessageIDs = [];
$this->mediaInfoMessageIDs = [];
$this->audioInfoMessageIDs = [];
$this->rarFileMessageIDs = [];
$this->audioInfoExtension = '';
}
/**
* Reset file counters.
*/
public function resetCounters(): void
{
$this->addedFileInfo = 0;
$this->totalFileInfo = 0;
$this->compressedFilesChecked = 0;
$this->pendingReleaseFiles = [];
$this->pendingParHashes = [];
$this->existingReleaseFileNames = null;
$this->releaseFilesChanged = false;
}
/**
* Full reset for processing a new release.
*/
public function reset(): void
{
$this->passwordStatus = 0;
$this->releaseHasPassword = false;
$this->nzbHasCompressedFile = false;
$this->groupUnavailable = false;
$this->workPlan = null;
$this->releaseDownloadedArchives();
$this->resetMessageIDs();
$this->resetCounters();
}
/**
* Retain only small, immediately reusable archive payloads for this release.
*
* @param array<int, array<string, mixed>> $files
*/
public function rememberDownloadedArchive(string $title, string $data, array $files): bool
{
$byteSize = strlen($data);
if ($byteSize === 0
|| count($this->downloadedArchives) >= self::MAX_RETAINED_ARCHIVES
|| ($this->retainedArchiveBytes + $byteSize) > self::MAX_RETAINED_ARCHIVE_BYTES
) {
return false;
}
$archive = new DownloadedArchive($title, $data, $files);
$this->downloadedArchives[] = $archive;
$this->retainedArchiveBytes += $archive->byteSize();
return true;
}
public function releaseDownloadedArchives(): void
{
$this->downloadedArchives = [];
$this->retainedArchiveBytes = 0;
}
public function duplicateMessageIdCount(): int
{
return $this->workPlan === null ? 0 : $this->workPlan->duplicateMessageIdCount;
}
/**
* @return list<string>
*/
public function unsupportedReasons(): array
{
return $this->workPlan === null ? [] : $this->workPlan->unsupportedReasons;
}
/**
* Mark a processing artifact as found.
*/
public function markFound(string $type): void
{
match ($type) {
'video' => $this->foundVideo = true,
'mediaInfo' => $this->foundMediaInfo = true,
'audioInfo' => $this->foundAudioInfo = true,
'audioSample' => $this->foundAudioSample = true,
'jpgSample' => $this->foundJPGSample = true,
'sample' => $this->foundSample = true,
'par2Info' => $this->foundPAR2Info = true,
default => null,
};
}
/**
* Check if more media processing is needed.
*/
public function needsMediaProcessing(): bool
{
return ! $this->foundVideo
|| ! $this->foundMediaInfo
|| ! $this->foundAudioInfo
|| ! $this->foundAudioSample;
}
/**
* Check if any sample is still needed.
*/
public function needsSample(): bool
{
return ! $this->foundSample || ! $this->foundJPGSample;
}
/**
* Check if the release has exceeded the processing timeout.
*/
public function isTimedOut(int $timeoutSeconds): bool
{
if ($timeoutSeconds <= 0) {
return false;
}
return (hrtime(true) - $this->startTime) / 1e9 > $timeoutSeconds;
}
/**
* Get elapsed processing time in seconds.
*/
public function getElapsedSeconds(): int
{
return (int) ((hrtime(true) - $this->startTime) / 1e9);
}
}