mirror of
https://github.com/NNTmux/newznab-tmux.git
synced 2026-08-28 23:01:29 +00:00
298 lines
7.7 KiB
PHP
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);
|
|
}
|
|
}
|