mirror of
https://github.com/NNTmux/newznab-tmux.git
synced 2026-08-29 09:18:54 +00:00
1399 lines
49 KiB
PHP
1399 lines
49 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Services\NameFixing;
|
|
|
|
use App\Facades\Search;
|
|
use App\Models\Category;
|
|
use App\Models\Release;
|
|
use App\Services\NameFixing\Extractors\FileNameExtractor;
|
|
use App\Services\NameFixing\Extractors\NfoNameExtractor;
|
|
use App\Services\NNTP\NNTPService;
|
|
use App\Services\Nzb\NzbContentsService;
|
|
|
|
/**
|
|
* Main service for name fixing operations.
|
|
*
|
|
* Orchestrates the various name fixing sources (NFO, Files, CRC, SRR, etc.)
|
|
* and handles the overall processing flow.
|
|
*/
|
|
class NameFixingService
|
|
{
|
|
// Constants for name fixing status
|
|
public const PROC_NFO_NONE = 0;
|
|
|
|
public const PROC_NFO_DONE = 1;
|
|
|
|
public const PROC_FILES_NONE = 0;
|
|
|
|
public const PROC_FILES_DONE = 1;
|
|
|
|
public const PROC_PAR2_NONE = 0;
|
|
|
|
public const PROC_PAR2_DONE = 1;
|
|
|
|
public const PROC_UID_NONE = 0;
|
|
|
|
public const PROC_UID_DONE = 1;
|
|
|
|
public const PROC_HASH16K_NONE = 0;
|
|
|
|
public const PROC_HASH16K_DONE = 1;
|
|
|
|
public const PROC_SRR_NONE = 0;
|
|
|
|
public const PROC_SRR_DONE = 1;
|
|
|
|
public const PROC_CRC_NONE = 0;
|
|
|
|
public const PROC_CRC_DONE = 1;
|
|
|
|
// Constants for overall rename status
|
|
public const IS_RENAMED_NONE = 0;
|
|
|
|
public const IS_RENAMED_DONE = 1;
|
|
|
|
protected ReleaseUpdateService $updateService;
|
|
|
|
protected NameCheckerService $checkerService;
|
|
|
|
protected NfoNameExtractor $nfoExtractor;
|
|
|
|
protected FileNameExtractor $fileExtractor;
|
|
|
|
protected FileNameCleaner $fileNameCleaner;
|
|
|
|
protected FilePrioritizer $filePrioritizer;
|
|
|
|
protected PredbMatchSelector $predbMatchSelector;
|
|
|
|
protected bool $echoOutput;
|
|
|
|
protected string $othercats;
|
|
|
|
protected string $timeother;
|
|
|
|
protected string $timeall;
|
|
|
|
protected string $fullother;
|
|
|
|
protected string $fullall;
|
|
|
|
protected int $_totalReleases = 0;
|
|
|
|
public function __construct(
|
|
?ReleaseUpdateService $updateService = null,
|
|
?NameCheckerService $checkerService = null,
|
|
?NfoNameExtractor $nfoExtractor = null,
|
|
?FileNameExtractor $fileExtractor = null,
|
|
?FileNameCleaner $fileNameCleaner = null,
|
|
?FilePrioritizer $filePrioritizer = null,
|
|
?PredbMatchSelector $predbMatchSelector = null
|
|
) {
|
|
$this->updateService = $updateService ?? new ReleaseUpdateService;
|
|
$this->checkerService = $checkerService ?? new NameCheckerService;
|
|
$this->nfoExtractor = $nfoExtractor ?? new NfoNameExtractor;
|
|
$this->fileExtractor = $fileExtractor ?? new FileNameExtractor;
|
|
$this->fileNameCleaner = $fileNameCleaner ?? new FileNameCleaner;
|
|
$this->filePrioritizer = $filePrioritizer ?? new FilePrioritizer;
|
|
$this->predbMatchSelector = $predbMatchSelector ?? new PredbMatchSelector($this->fileNameCleaner);
|
|
$this->echoOutput = config('nntmux.echocli');
|
|
|
|
$this->othercats = implode(',', Category::OTHERS_GROUP);
|
|
$this->timeother = sprintf(' AND rel.adddate > (NOW() - INTERVAL 6 HOUR) AND rel.categories_id IN (%s) GROUP BY rel.id ORDER BY postdate DESC', $this->othercats);
|
|
$this->timeall = ' AND rel.adddate > (NOW() - INTERVAL 6 HOUR) GROUP BY rel.id ORDER BY postdate DESC';
|
|
$this->fullother = sprintf(' AND rel.categories_id IN (%s) GROUP BY rel.id', $this->othercats);
|
|
$this->fullall = '';
|
|
}
|
|
|
|
/**
|
|
* Fix names using NFO content.
|
|
*/
|
|
public function fixNamesWithNfo(int $time, bool $echo, int $cats, bool $nameStatus, bool $show): void
|
|
{
|
|
$this->echoStartMessage($time, '.nfo files');
|
|
$type = 'NFO, ';
|
|
|
|
$preId = false;
|
|
if ($cats === 3) {
|
|
$query = sprintf(
|
|
'SELECT rel.id AS releases_id, rel.fromname
|
|
FROM releases rel
|
|
INNER JOIN release_nfos nfo ON (nfo.releases_id = rel.id)
|
|
WHERE rel.predb_id = 0'
|
|
);
|
|
$cats = 2;
|
|
$preId = true;
|
|
} else {
|
|
$query = sprintf(
|
|
'SELECT rel.id AS releases_id, rel.fromname
|
|
FROM releases rel
|
|
INNER JOIN release_nfos nfo ON (nfo.releases_id = rel.id)
|
|
WHERE (rel.isrenamed = %d OR rel.categories_id IN (%d, %d))
|
|
AND rel.predb_id = 0
|
|
AND rel.proc_nfo = %d',
|
|
self::IS_RENAMED_NONE,
|
|
Category::OTHER_MISC,
|
|
Category::OTHER_HASHED,
|
|
self::PROC_NFO_NONE
|
|
);
|
|
}
|
|
|
|
$releases = $this->getReleases($time, $cats, $query);
|
|
$total = $releases->count();
|
|
|
|
if ($total > 0) {
|
|
$this->_totalReleases = $total;
|
|
cli()->info(number_format($total).' releases to process.');
|
|
|
|
foreach ($releases as $rel) {
|
|
/** @var Release $rel */
|
|
$releaseRow = Release::fromQuery(
|
|
sprintf(
|
|
'SELECT nfo.releases_id AS nfoid, rel.groups_id, rel.fromname, rel.categories_id, rel.name, rel.searchname,
|
|
UNCOMPRESS(nfo) AS textstring, rel.id AS releases_id
|
|
FROM releases rel
|
|
INNER JOIN release_nfos nfo ON (nfo.releases_id = rel.id)
|
|
WHERE rel.id = %d LIMIT 1',
|
|
$rel->releases_id
|
|
)
|
|
);
|
|
|
|
$this->updateService->incrementChecked();
|
|
|
|
// Ignore encrypted NFOs
|
|
if (preg_match('/^=newz\[NZB\]=\w+/', $releaseRow[0]->textstring)) {
|
|
$this->updateService->updateSingleColumn('proc_nfo', self::PROC_NFO_DONE, $rel->releases_id);
|
|
|
|
continue;
|
|
}
|
|
|
|
$this->updateService->reset();
|
|
|
|
// Try NFO extraction
|
|
$nfoResult = $this->nfoExtractor->extractFromNfo($releaseRow[0]->textstring);
|
|
if ($nfoResult !== null) {
|
|
$this->updateService->updateRelease(
|
|
$releaseRow[0],
|
|
$nfoResult->newName,
|
|
'nfoCheck: '.$nfoResult->method,
|
|
$echo,
|
|
$type,
|
|
$nameStatus,
|
|
$show
|
|
);
|
|
}
|
|
|
|
// If NFO extraction didn't work, try pattern checkers
|
|
if (! $this->updateService->matched) {
|
|
$this->checkWithPatternMatchers($releaseRow[0], $echo, $type, $nameStatus, $show, $preId);
|
|
}
|
|
|
|
$this->echoRenamed($show);
|
|
}
|
|
$this->echoFoundCount($echo, ' NFO\'s');
|
|
} else {
|
|
cli()->info('Nothing to fix.');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Fix names using file names.
|
|
*/
|
|
public function fixNamesWithFiles(int $time, bool $echo, int $cats, bool $nameStatus, bool $show): void
|
|
{
|
|
$this->echoStartMessage($time, 'file names');
|
|
$type = 'Filenames, ';
|
|
|
|
$preId = false;
|
|
if ($cats === 3) {
|
|
$query = sprintf(
|
|
'SELECT rf.name AS textstring, rel.categories_id, rel.name, rel.searchname, rel.fromname, rel.groups_id,
|
|
rf.releases_id AS fileid, rel.id AS releases_id
|
|
FROM releases rel
|
|
INNER JOIN release_files rf ON rf.releases_id = rel.id
|
|
WHERE predb_id = 0'
|
|
);
|
|
$cats = 2;
|
|
$preId = true;
|
|
} else {
|
|
$query = sprintf(
|
|
'SELECT rf.name AS textstring, rel.categories_id, rel.name, rel.searchname, rel.fromname, rel.groups_id,
|
|
rf.releases_id AS fileid, rel.id AS releases_id
|
|
FROM releases rel
|
|
INNER JOIN release_files rf ON rf.releases_id = rel.id
|
|
WHERE (rel.isrenamed = %d OR rel.categories_id IN (%d, %d))
|
|
AND rel.predb_id = 0
|
|
AND proc_files = %d',
|
|
self::IS_RENAMED_NONE,
|
|
Category::OTHER_MISC,
|
|
Category::OTHER_HASHED,
|
|
self::PROC_FILES_NONE
|
|
);
|
|
}
|
|
|
|
$releases = $this->getReleases($time, $cats, $query);
|
|
$total = $releases->count();
|
|
|
|
if ($total > 0) {
|
|
$this->_totalReleases = $total;
|
|
cli()->info(number_format($total).' file names to process.');
|
|
|
|
// Group files by release
|
|
$releaseFiles = [];
|
|
foreach ($releases as $release) {
|
|
/** @var Release $release */
|
|
$releaseId = $release->releases_id;
|
|
if (! isset($releaseFiles[$releaseId])) {
|
|
$releaseFiles[$releaseId] = [
|
|
'release' => $release,
|
|
'files' => [],
|
|
];
|
|
}
|
|
$releaseFiles[$releaseId]['files'][] = $release->textstring;
|
|
}
|
|
|
|
foreach ($releaseFiles as $releaseId => $data) {
|
|
$this->updateService->reset();
|
|
$this->updateService->incrementChecked();
|
|
|
|
// Prioritize files for matching
|
|
$prioritizedFiles = $this->filePrioritizer->prioritizeForMatching($data['files']); // @phpstan-ignore argument.type
|
|
|
|
foreach ($prioritizedFiles as $filename) {
|
|
/** @var Release $release */
|
|
$release = clone $data['release'];
|
|
$release->textstring = $filename;
|
|
|
|
// Try file name extraction
|
|
$fileResult = $this->fileExtractor->extractFromFile($filename);
|
|
if ($fileResult !== null) {
|
|
$this->updateService->updateRelease(
|
|
$release,
|
|
$fileResult->newName,
|
|
'fileCheck: '.$fileResult->method,
|
|
$echo,
|
|
$type,
|
|
$nameStatus,
|
|
$show
|
|
);
|
|
}
|
|
|
|
// If not matched, try PreDB search
|
|
if (! $this->updateService->matched) {
|
|
$this->preDbFileCheck($release, $echo, $type, $nameStatus, $show);
|
|
}
|
|
|
|
if (! $this->updateService->matched) {
|
|
$this->preDbTitleCheck($release, $echo, $type, $nameStatus, $show);
|
|
}
|
|
|
|
if ($this->updateService->matched) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
$this->echoRenamed($show);
|
|
}
|
|
|
|
$this->echoFoundCount($echo, ' files');
|
|
} else {
|
|
cli()->info('Nothing to fix.');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Fix names using SRR files.
|
|
*/
|
|
public function fixNamesWithSrr(int $time, bool $echo, int $cats, bool $nameStatus, bool $show): void
|
|
{
|
|
$this->echoStartMessage($time, 'SRR file names');
|
|
$type = 'SRR, ';
|
|
|
|
if ($cats === 3) {
|
|
$query = sprintf(
|
|
'SELECT rf.name AS textstring, rel.categories_id, rel.name, rel.searchname, rel.fromname, rel.groups_id,
|
|
rf.releases_id AS fileid, rel.id AS releases_id
|
|
FROM releases rel
|
|
INNER JOIN release_files rf ON (rf.releases_id = rel.id)
|
|
WHERE predb_id = 0
|
|
AND (rf.name LIKE %s OR rf.name LIKE %s)',
|
|
escapeString('%.srr'),
|
|
escapeString('%.srs')
|
|
);
|
|
$cats = 2;
|
|
} else {
|
|
$query = sprintf(
|
|
'SELECT rf.name AS textstring, rel.categories_id, rel.name, rel.searchname, rel.fromname, rel.groups_id,
|
|
rf.releases_id AS fileid, rel.id AS releases_id
|
|
FROM releases rel
|
|
INNER JOIN release_files rf ON (rf.releases_id = rel.id)
|
|
WHERE (rel.isrenamed = %d OR rel.categories_id IN (%d, %d))
|
|
AND rel.predb_id = 0
|
|
AND (rf.name LIKE %s OR rf.name LIKE %s)
|
|
AND rel.proc_srr = %d',
|
|
self::IS_RENAMED_NONE,
|
|
Category::OTHER_MISC,
|
|
Category::OTHER_HASHED,
|
|
escapeString('%.srr'),
|
|
escapeString('%.srs'),
|
|
self::PROC_SRR_NONE
|
|
);
|
|
}
|
|
|
|
$releases = $this->getReleases($time, $cats, $query);
|
|
$total = $releases->count();
|
|
|
|
if ($total > 0) {
|
|
$this->_totalReleases = $total;
|
|
cli()->info(number_format($total).' srr file extensions to process.');
|
|
|
|
foreach ($releases as $release) {
|
|
$this->updateService->reset();
|
|
$this->updateService->incrementChecked();
|
|
|
|
$this->srrNameCheck($release, $echo, $type, $nameStatus, $show);
|
|
$this->echoRenamed($show);
|
|
}
|
|
|
|
$this->echoFoundCount($echo, ' files');
|
|
} else {
|
|
cli()->info('Nothing to fix.');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Fix names using CRC32 hashes.
|
|
*/
|
|
public function fixNamesWithCrc(int $time, bool $echo, int $cats, bool $nameStatus, bool $show): void
|
|
{
|
|
$this->echoStartMessage($time, 'CRC32');
|
|
$type = 'CRC32, ';
|
|
|
|
$preId = false;
|
|
if ($cats === 3) {
|
|
$query = sprintf(
|
|
'SELECT rf.crc32 AS textstring, rf.name AS filename, rel.categories_id, rel.name, rel.searchname, rel.fromname, rel.groups_id, rel.size as relsize,
|
|
rf.releases_id AS fileid, rel.id AS releases_id
|
|
FROM releases rel
|
|
INNER JOIN release_files rf ON rf.releases_id = rel.id
|
|
WHERE predb_id = 0
|
|
AND rf.crc32 != \'\'
|
|
AND rf.crc32 IS NOT NULL'
|
|
);
|
|
$cats = 2;
|
|
$preId = true;
|
|
} else {
|
|
$query = sprintf(
|
|
'SELECT rf.crc32 AS textstring, rf.name AS filename, rel.categories_id, rel.name, rel.searchname, rel.fromname, rel.groups_id, rel.size as relsize,
|
|
rf.releases_id AS fileid, rel.id AS releases_id
|
|
FROM releases rel
|
|
INNER JOIN release_files rf ON rf.releases_id = rel.id
|
|
WHERE (rel.isrenamed = %d OR rel.categories_id IN (%d, %d))
|
|
AND rel.predb_id = 0
|
|
AND rel.proc_crc32 = %d
|
|
AND rf.crc32 != \'\'
|
|
AND rf.crc32 IS NOT NULL',
|
|
self::IS_RENAMED_NONE,
|
|
Category::OTHER_MISC,
|
|
Category::OTHER_HASHED,
|
|
self::PROC_CRC_NONE
|
|
);
|
|
}
|
|
|
|
$releases = $this->getReleases($time, $cats, $query);
|
|
$total = $releases->count();
|
|
|
|
if ($total > 0) {
|
|
$this->_totalReleases = $total;
|
|
cli()->info(number_format($total).' CRC32\'s to process.');
|
|
|
|
// Group by release
|
|
$releasesCrc = [];
|
|
foreach ($releases as $release) {
|
|
/** @var Release $release */
|
|
$releaseId = $release->releases_id;
|
|
if (! isset($releasesCrc[$releaseId])) {
|
|
$releasesCrc[$releaseId] = [
|
|
'release' => $release,
|
|
'crcs' => [],
|
|
];
|
|
}
|
|
if (! empty($release->textstring)) {
|
|
$priority = $this->filePrioritizer->getCrcPriority($release->filename ?? '');
|
|
$releasesCrc[$releaseId]['crcs'][$priority][] = $release->textstring;
|
|
}
|
|
}
|
|
|
|
foreach ($releasesCrc as $releaseId => $data) {
|
|
$this->updateService->reset();
|
|
$this->updateService->incrementChecked();
|
|
|
|
ksort($data['crcs']);
|
|
foreach ($data['crcs'] as $crcs) {
|
|
foreach ($crcs as $crc) {
|
|
/** @var Release $release */
|
|
$release = clone $data['release'];
|
|
$release->textstring = $crc;
|
|
|
|
$this->crcCheck($release, $echo, $type, $nameStatus, $show, $preId);
|
|
|
|
if ($this->updateService->matched) {
|
|
break 2;
|
|
}
|
|
}
|
|
}
|
|
|
|
$this->echoRenamed($show);
|
|
}
|
|
|
|
$this->echoFoundCount($echo, ' crc32\'s');
|
|
} else {
|
|
cli()->info('Nothing to fix.');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Fix names using Media info unique IDs.
|
|
*/
|
|
public function fixNamesWithMedia(int $time, bool $echo, int $cats, bool $nameStatus, bool $show): void
|
|
{
|
|
$type = 'UID, ';
|
|
$this->echoStartMessage($time, 'mediainfo Unique_IDs');
|
|
|
|
if ($cats === 3) {
|
|
$query = sprintf(
|
|
'SELECT rel.id AS releases_id, rel.size AS relsize, rel.groups_id, rel.fromname, rel.categories_id,
|
|
rel.name, rel.name AS textstring, rel.predb_id, rel.searchname,
|
|
ru.unique_id AS uid
|
|
FROM releases rel
|
|
LEFT JOIN media_infos ru ON ru.releases_id = rel.id
|
|
WHERE ru.releases_id IS NOT NULL
|
|
AND rel.predb_id = 0'
|
|
);
|
|
$cats = 2;
|
|
} else {
|
|
$query = sprintf(
|
|
'SELECT rel.id AS releases_id, rel.size AS relsize, rel.groups_id, rel.fromname, rel.categories_id,
|
|
rel.name, rel.name AS textstring, rel.predb_id, rel.searchname,
|
|
ru.unique_id AS uid
|
|
FROM releases rel
|
|
LEFT JOIN media_infos ru ON ru.releases_id = rel.id
|
|
WHERE ru.releases_id IS NOT NULL
|
|
AND (rel.isrenamed = %d OR rel.categories_id IN (%d, %d))
|
|
AND rel.predb_id = 0
|
|
AND rel.proc_uid = %d',
|
|
self::IS_RENAMED_NONE,
|
|
Category::OTHER_MISC,
|
|
Category::OTHER_HASHED,
|
|
self::PROC_UID_NONE
|
|
);
|
|
}
|
|
|
|
$releases = $this->getReleases($time, $cats, $query);
|
|
$total = $releases->count();
|
|
|
|
if ($total > 0) {
|
|
$this->_totalReleases = $total;
|
|
cli()->info(number_format($total).' unique ids to process.');
|
|
|
|
foreach ($releases as $rel) {
|
|
$this->updateService->reset();
|
|
$this->updateService->incrementChecked();
|
|
$this->uidCheck($rel, $echo, $type, $nameStatus, $show);
|
|
$this->echoRenamed($show);
|
|
}
|
|
|
|
$this->echoFoundCount($echo, ' UID\'s');
|
|
} else {
|
|
cli()->info('Nothing to fix.');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Fix names using PAR2 hash_16K.
|
|
*/
|
|
public function fixNamesWithParHash(int $time, bool $echo, int $cats, bool $nameStatus, bool $show): void
|
|
{
|
|
$type = 'PAR2 hash, ';
|
|
$this->echoStartMessage($time, 'PAR2 hash_16K');
|
|
|
|
if ($cats === 3) {
|
|
$query = sprintf(
|
|
'SELECT rel.id AS releases_id, rel.size AS relsize, rel.groups_id, rel.fromname, rel.categories_id,
|
|
rel.name, rel.name AS textstring, rel.predb_id, rel.searchname,
|
|
IFNULL(ph.hash, \'\') AS hash
|
|
FROM releases rel
|
|
LEFT JOIN par_hashes ph ON ph.releases_id = rel.id
|
|
WHERE ph.hash != \'\'
|
|
AND rel.predb_id = 0'
|
|
);
|
|
$cats = 2;
|
|
} else {
|
|
$query = sprintf(
|
|
'SELECT rel.id AS releases_id, rel.size AS relsize, rel.groups_id, rel.fromname, rel.categories_id,
|
|
rel.name, rel.name AS textstring, rel.predb_id, rel.searchname,
|
|
IFNULL(ph.hash, \'\') AS hash
|
|
FROM releases rel
|
|
LEFT JOIN par_hashes ph ON ph.releases_id = rel.id
|
|
WHERE (rel.isrenamed = %d OR rel.categories_id IN (%d, %d))
|
|
AND rel.predb_id = 0
|
|
AND ph.hash != \'\'
|
|
AND rel.proc_hash16k = %d',
|
|
self::IS_RENAMED_NONE,
|
|
Category::OTHER_MISC,
|
|
Category::OTHER_HASHED,
|
|
self::PROC_HASH16K_NONE
|
|
);
|
|
}
|
|
|
|
$releases = $this->getReleases($time, $cats, $query);
|
|
$total = $releases->count();
|
|
|
|
if ($total > 0) {
|
|
$this->_totalReleases = $total;
|
|
cli()->info(number_format($total).' hash_16K to process.');
|
|
|
|
foreach ($releases as $rel) {
|
|
$this->updateService->reset();
|
|
$this->updateService->incrementChecked();
|
|
$this->hashCheck($rel, $echo, $type, $nameStatus, $show);
|
|
$this->echoRenamed($show);
|
|
}
|
|
|
|
$this->echoFoundCount($echo, ' hashes');
|
|
} else {
|
|
cli()->info('Nothing to fix.');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Check with pattern matchers (TV, Movie, Game, App).
|
|
*/
|
|
protected function checkWithPatternMatchers(object $release, bool $echo, string $type, bool $nameStatus, bool $show, bool $preId): void
|
|
{
|
|
// Check for PreDB match first
|
|
$preDbMatch = $this->updateService->checkPreDbMatch($release, $release->textstring);
|
|
if ($preDbMatch !== null) {
|
|
$this->updateService->updateRelease(
|
|
$release,
|
|
$preDbMatch['title'],
|
|
'preDB: Match',
|
|
$echo,
|
|
$type,
|
|
$nameStatus,
|
|
$show,
|
|
$preDbMatch['id']
|
|
);
|
|
|
|
return;
|
|
}
|
|
|
|
if ($preId) {
|
|
return;
|
|
}
|
|
|
|
// Try pattern checkers
|
|
$result = $this->checkerService->check($release, $release->textstring);
|
|
if ($result !== null) {
|
|
$this->updateService->updateRelease(
|
|
$release,
|
|
$result->newName,
|
|
$result->getFormattedMethod(),
|
|
$echo,
|
|
$type,
|
|
$nameStatus,
|
|
$show
|
|
);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Check SRR file for release name.
|
|
*/
|
|
protected function srrNameCheck(object $release, bool $echo, string $type, bool $nameStatus, bool $show): bool
|
|
{
|
|
$extractedName = null;
|
|
|
|
if (preg_match('/^(.+)\.srr$/i', $release->textstring, $hit)) {
|
|
$extractedName = $hit[1];
|
|
} elseif (preg_match('/^(.+)\.srs$/i', $release->textstring, $hit)) {
|
|
$extractedName = $hit[1];
|
|
}
|
|
|
|
if ($extractedName !== null) {
|
|
if (preg_match('/[\\\\\/]([^\\\\\/]+)$/', $extractedName, $pathMatch)) {
|
|
$extractedName = $pathMatch[1];
|
|
}
|
|
|
|
if (preg_match(ReleaseUpdateService::PREDB_REGEX, $extractedName)) {
|
|
$this->updateService->updateRelease(
|
|
$release,
|
|
$extractedName,
|
|
'fileCheck: SRR extension',
|
|
$echo,
|
|
$type,
|
|
$nameStatus,
|
|
$show
|
|
);
|
|
|
|
return true;
|
|
}
|
|
}
|
|
|
|
$this->updateService->updateSingleColumn('proc_srr', self::PROC_SRR_DONE, $release->releases_id);
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Check CRC32 for matches.
|
|
*/
|
|
protected function crcCheck(object $release, bool $echo, string $type, bool $nameStatus, bool $show, bool $preId): bool
|
|
{
|
|
if ($release->textstring === '') {
|
|
$this->updateService->updateSingleColumn('proc_crc32', self::PROC_CRC_DONE, $release->releases_id);
|
|
|
|
return false;
|
|
}
|
|
|
|
$result = Release::fromQuery(
|
|
sprintf(
|
|
'SELECT rf.crc32, rel.categories_id, rel.name, rel.searchname, rel.fromname, rel.groups_id, rel.size as relsize, rel.predb_id as predb_id,
|
|
rf.releases_id AS fileid, rel.id AS releases_id
|
|
FROM releases rel
|
|
LEFT JOIN release_files rf ON rf.releases_id = rel.id
|
|
WHERE rel.predb_id > 0
|
|
AND rf.crc32 = %s',
|
|
escapeString($release->textstring)
|
|
)
|
|
);
|
|
|
|
foreach ($result as $res) {
|
|
/** @var Release $res */
|
|
$floor = round(($res->relsize - $release->relsize) / $res->relsize * 100, 1);
|
|
if ($floor >= -5 && $floor <= 5) {
|
|
$this->updateService->updateRelease(
|
|
$release,
|
|
$res->searchname,
|
|
'crcCheck: CRC32',
|
|
$echo,
|
|
$type,
|
|
$nameStatus,
|
|
$show,
|
|
$res->predb_id
|
|
);
|
|
|
|
return true;
|
|
}
|
|
}
|
|
|
|
$this->updateService->updateSingleColumn('proc_crc32', self::PROC_CRC_DONE, $release->releases_id);
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Check UID for matches.
|
|
*/
|
|
protected function uidCheck(object $release, bool $echo, string $type, bool $nameStatus, bool $show): bool
|
|
{
|
|
if (empty($release->uid)) {
|
|
$this->updateService->updateSingleColumn('proc_uid', self::PROC_UID_DONE, $release->releases_id);
|
|
|
|
return false;
|
|
}
|
|
|
|
$result = Release::fromQuery(sprintf(
|
|
'SELECT r.id AS releases_id, r.size AS relsize, r.name AS textstring, r.searchname, r.fromname, r.predb_id
|
|
FROM releases r
|
|
LEFT JOIN media_infos ru ON ru.releases_id = r.id
|
|
WHERE ru.releases_id IS NOT NULL
|
|
AND ru.unique_id = %s
|
|
AND ru.releases_id != %d
|
|
AND (r.predb_id > 0 OR r.anidbid > 0 OR r.fromname = %s)',
|
|
escapeString($release->uid),
|
|
$release->releases_id,
|
|
escapeString('nonscene@Ef.net (EF)')
|
|
));
|
|
|
|
foreach ($result as $res) {
|
|
/** @var Release $res */
|
|
$floor = round(($res->relsize - $release->relsize) / $res->relsize * 100, 1);
|
|
if ($floor >= -10 && $floor <= 10) {
|
|
$this->updateService->updateRelease(
|
|
$release,
|
|
$res->searchname,
|
|
'uidCheck: Unique_ID',
|
|
$echo,
|
|
$type,
|
|
$nameStatus,
|
|
$show,
|
|
$res->predb_id
|
|
);
|
|
|
|
return true;
|
|
}
|
|
}
|
|
|
|
$this->updateService->updateSingleColumn('proc_uid', self::PROC_UID_DONE, $release->releases_id);
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Check PAR2 hash for matches.
|
|
*/
|
|
protected function hashCheck(object $release, bool $echo, string $type, bool $nameStatus, bool $show): bool
|
|
{
|
|
$result = Release::fromQuery(sprintf(
|
|
'SELECT r.id AS releases_id, r.size AS relsize, r.name AS textstring, r.searchname, r.fromname, r.predb_id
|
|
FROM releases r
|
|
LEFT JOIN par_hashes ph ON ph.releases_id = r.id
|
|
WHERE ph.hash = %s
|
|
AND ph.releases_id != %d
|
|
AND (r.predb_id > 0 OR r.anidbid > 0)',
|
|
escapeString($release->hash),
|
|
$release->releases_id
|
|
));
|
|
|
|
foreach ($result as $res) {
|
|
/** @var Release $res */
|
|
$floor = round(($res->relsize - $release->relsize) / $res->relsize * 100, 1);
|
|
if ($floor >= -5 && $floor <= 5) {
|
|
$this->updateService->updateRelease(
|
|
$release,
|
|
$res->searchname,
|
|
'hashCheck: PAR2 hash_16K',
|
|
$echo,
|
|
$type,
|
|
$nameStatus,
|
|
$show,
|
|
$res->predb_id
|
|
);
|
|
|
|
return true;
|
|
}
|
|
}
|
|
|
|
$this->updateService->updateSingleColumn('proc_hash16k', self::PROC_HASH16K_DONE, $release->releases_id);
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Check PreDB for filename matches.
|
|
*/
|
|
protected function preDbFileCheck(object $release, bool $echo, string $type, bool $nameStatus, bool $show): bool
|
|
{
|
|
$fileName = $this->fileNameCleaner->cleanForMatching($release->textstring);
|
|
|
|
if (empty($fileName)) {
|
|
return false;
|
|
}
|
|
|
|
$bestMatch = $this->findBestPredbMatch($fileName);
|
|
if ($bestMatch !== null) {
|
|
$this->updateService->updateRelease(
|
|
$release,
|
|
$bestMatch['title'] ?? '',
|
|
'PreDb: Filename match',
|
|
$echo,
|
|
$type,
|
|
$nameStatus,
|
|
$show,
|
|
$bestMatch['id'] ?? null
|
|
);
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Check PreDB for title matches.
|
|
*/
|
|
protected function preDbTitleCheck(object $release, bool $echo, string $type, bool $nameStatus, bool $show): bool
|
|
{
|
|
$fileName = $this->fileNameCleaner->cleanForMatching($release->textstring);
|
|
|
|
if (empty($fileName)) {
|
|
return false;
|
|
}
|
|
|
|
$bestMatch = $this->findBestPredbMatch($fileName);
|
|
if ($bestMatch !== null) {
|
|
$this->updateService->updateRelease(
|
|
$release,
|
|
$bestMatch['title'] ?? '',
|
|
'PreDb: Title match',
|
|
$echo,
|
|
$type,
|
|
$nameStatus,
|
|
$show,
|
|
$bestMatch['id'] ?? null
|
|
);
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* @return array<string, mixed>|null
|
|
*/
|
|
protected function findBestPredbMatch(string $fileName): ?array
|
|
{
|
|
$results = Search::searchPredb($fileName);
|
|
|
|
return $this->predbMatchSelector->selectBestMatch($fileName, $results);
|
|
}
|
|
|
|
/**
|
|
* Get releases based on time and category parameters.
|
|
*
|
|
* @return Collection<int, mixed>
|
|
*/
|
|
protected function getReleases(int $time, int $cats, string $query, int $limit = 0): \Illuminate\Database\Eloquent\Collection|bool // @phpstan-ignore class.notFound, missingType.generics, return.phpDocType
|
|
{
|
|
$releases = false;
|
|
$queryLimit = ($limit === 0) ? '' : ' LIMIT '.$limit;
|
|
|
|
if ($time === 1 && $cats === 1) {
|
|
$releases = Release::fromQuery($query.$this->timeother.$queryLimit);
|
|
}
|
|
if ($time === 1 && $cats === 2) {
|
|
$releases = Release::fromQuery($query.$this->timeall.$queryLimit);
|
|
}
|
|
if ($time === 2 && $cats === 1) {
|
|
$releases = Release::fromQuery($query.$this->fullother.$queryLimit);
|
|
}
|
|
if ($time === 2 && $cats === 2) {
|
|
$releases = Release::fromQuery($query.$this->fullall.$queryLimit);
|
|
}
|
|
|
|
return $releases;
|
|
}
|
|
|
|
/**
|
|
* Echo start message.
|
|
*/
|
|
protected function echoStartMessage(int $time, string $type): void
|
|
{
|
|
cli()->info(
|
|
sprintf(
|
|
'Fixing search names %s using %s.',
|
|
($time === 1 ? 'in the past 6 hours' : 'since the beginning'),
|
|
$type
|
|
)
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Echo found count.
|
|
*/
|
|
protected function echoFoundCount(bool $echo, string $type): void
|
|
{
|
|
$stats = $this->updateService->getStats();
|
|
if ($echo === true) {
|
|
cli()->info(
|
|
PHP_EOL.
|
|
number_format($stats['fixed']).
|
|
' releases have had their names changed out of: '.
|
|
number_format($stats['checked']).
|
|
$type.'.'
|
|
);
|
|
} else {
|
|
cli()->info(
|
|
PHP_EOL.
|
|
number_format($stats['fixed']).
|
|
' releases could have their names changed. '.
|
|
number_format($stats['checked']).
|
|
$type.' were checked.'
|
|
);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Echo renamed progress.
|
|
*/
|
|
protected function echoRenamed(bool $show): void
|
|
{
|
|
$stats = $this->updateService->getStats();
|
|
|
|
// Show milestone message every 500 releases
|
|
if ($stats['checked'] % 500 === 0 && $stats['checked'] > 0) {
|
|
cli()->alternate(PHP_EOL.number_format($stats['checked']).' files processed.'.PHP_EOL);
|
|
}
|
|
|
|
// Show progress at meaningful intervals to reduce tmux pane clutter
|
|
if ($show === true) {
|
|
$percent = $this->_totalReleases > 0
|
|
? round(($stats['checked'] / $this->_totalReleases) * 100, 1)
|
|
: 0;
|
|
|
|
// Calculate progress interval - show update every 10% or at completion
|
|
$progressInterval = max(1, (int) ($this->_totalReleases / 10));
|
|
$isLastItem = $stats['checked'] === $this->_totalReleases;
|
|
$isIntervalPoint = $stats['checked'] % $progressInterval === 0;
|
|
|
|
// Only output at intervals or completion to keep tmux pane clean
|
|
if ($isIntervalPoint || $isLastItem) {
|
|
cli()->info(
|
|
'Renamed: '.number_format($stats['fixed']).
|
|
' | Processed: '.number_format($stats['checked']).
|
|
'/'.number_format($this->_totalReleases).
|
|
' ('.$percent.'%)'
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get the update service.
|
|
*/
|
|
public function getUpdateService(): ReleaseUpdateService
|
|
{
|
|
return $this->updateService;
|
|
}
|
|
|
|
/**
|
|
* Get the checker service.
|
|
*/
|
|
public function getCheckerService(): NameCheckerService
|
|
{
|
|
return $this->checkerService;
|
|
}
|
|
|
|
/**
|
|
* Fix names using PAR2 files (requires NNTP connection).
|
|
*/
|
|
public function fixNamesWithPar2(int $time, bool $echo, int $cats, bool $nameStatus, bool $show, NNTPService $nntp): void
|
|
{
|
|
$this->echoStartMessage($time, 'par2 files');
|
|
|
|
if ($cats === 3) {
|
|
$query = sprintf(
|
|
'SELECT rel.id AS releases_id, rel.guid, rel.groups_id, rel.fromname
|
|
FROM releases rel
|
|
WHERE rel.predb_id = 0'
|
|
);
|
|
$cats = 2;
|
|
} else {
|
|
$query = sprintf(
|
|
'SELECT rel.id AS releases_id, rel.guid, rel.groups_id, rel.fromname
|
|
FROM releases rel
|
|
WHERE (rel.isrenamed = %d OR rel.categories_id IN (%d, %d))
|
|
AND rel.predb_id = 0
|
|
AND rel.proc_par2 = %d',
|
|
self::IS_RENAMED_NONE,
|
|
Category::OTHER_MISC,
|
|
Category::OTHER_HASHED,
|
|
self::PROC_PAR2_NONE
|
|
);
|
|
}
|
|
|
|
$releases = $this->getReleases($time, $cats, $query);
|
|
$total = $releases ? $releases->count() : 0;
|
|
|
|
if ($total > 0) {
|
|
$this->_totalReleases = $total;
|
|
cli()->info(number_format($total).' releases to process.');
|
|
$nzbContentsService = app(NzbContentsService::class);
|
|
|
|
foreach ($releases as $release) {
|
|
/** @var Release $release */
|
|
if ($nzbContentsService->checkPar2($release->guid, $release->releases_id, $release->groups_id, (int) $nameStatus, (int) $show)) {
|
|
$this->updateService->fixed++;
|
|
}
|
|
|
|
$this->updateService->incrementChecked();
|
|
$this->echoRenamed($show);
|
|
}
|
|
$this->echoFoundCount($echo, ' files');
|
|
} else {
|
|
cli()->info('Nothing to fix.');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Fix XXX release names using specific file names.
|
|
*/
|
|
public function fixXXXNamesWithFiles(int $time, bool $echo, int $cats, bool $nameStatus, bool $show): void
|
|
{
|
|
$this->echoStartMessage($time, 'file names');
|
|
$type = 'Filenames, ';
|
|
|
|
if ($cats === 3) {
|
|
$query = sprintf(
|
|
'SELECT rf.name AS textstring, rel.categories_id, rel.name, rel.searchname, rel.fromname, rel.groups_id,
|
|
rf.releases_id AS fileid, rel.id AS releases_id
|
|
FROM releases rel
|
|
INNER JOIN release_files rf ON rf.releases_id = rel.id
|
|
WHERE predb_id = 0'
|
|
);
|
|
$cats = 2;
|
|
} else {
|
|
$query = sprintf(
|
|
'SELECT rf.name AS textstring, rel.categories_id, rel.name, rel.searchname, rel.fromname, rel.groups_id,
|
|
rf.releases_id AS fileid, rel.id AS releases_id
|
|
FROM releases rel
|
|
INNER JOIN release_files rf ON rf.releases_id = rel.id
|
|
WHERE (rel.isrenamed = %d OR rel.categories_id IN (%d, %d))
|
|
AND rel.predb_id = 0
|
|
AND rf.name LIKE %s',
|
|
self::IS_RENAMED_NONE,
|
|
Category::OTHER_MISC,
|
|
Category::OTHER_HASHED,
|
|
escapeString('%SDPORN%')
|
|
);
|
|
}
|
|
|
|
$releases = $this->getReleases($time, $cats, $query);
|
|
$total = $releases ? $releases->count() : 0;
|
|
|
|
if ($total > 0) {
|
|
$this->_totalReleases = $total;
|
|
cli()->info(number_format($total).' xxx file names to process.');
|
|
|
|
foreach ($releases as $release) {
|
|
$this->updateService->reset();
|
|
$this->xxxNameCheck($release, $echo, $type, $nameStatus, $show);
|
|
$this->updateService->incrementChecked();
|
|
$this->echoRenamed($show);
|
|
}
|
|
$this->echoFoundCount($echo, ' files');
|
|
} else {
|
|
cli()->info('Nothing to fix.');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Fix release names using mediainfo movie_name.
|
|
*/
|
|
public function fixNamesWithMediaMovieName(int $time, bool $echo, int $cats, bool $nameStatus, bool $show): void
|
|
{
|
|
$type = 'Mediainfo, ';
|
|
$this->echoStartMessage($time, 'Mediainfo movie_name');
|
|
|
|
if ($cats === 3) {
|
|
$query = sprintf(
|
|
'SELECT rel.id AS releases_id, rel.name, rel.name AS textstring, rel.predb_id, rel.searchname, rel.fromname, rel.groups_id, rel.categories_id, rel.id AS releases_id, rf.movie_name as movie_name
|
|
FROM releases rel
|
|
INNER JOIN media_infos rf ON rf.releases_id = rel.id
|
|
WHERE rel.predb_id = 0'
|
|
);
|
|
$cats = 2;
|
|
} else {
|
|
$query = sprintf(
|
|
'SELECT rel.id AS releases_id, rel.name, rel.name AS textstring, rel.predb_id, rel.searchname, rel.fromname, rel.groups_id, rel.categories_id, rel.id AS releases_id, rf.movie_name as movie_name, rf.file_name as file_name
|
|
FROM releases rel
|
|
INNER JOIN media_infos rf ON rf.releases_id = rel.id
|
|
WHERE rel.isrenamed = %d
|
|
AND rel.predb_id = 0',
|
|
self::IS_RENAMED_NONE
|
|
);
|
|
if ($cats === 2) {
|
|
$query .= PHP_EOL.'AND rel.categories_id IN ('.Category::OTHER_MISC.','.Category::OTHER_HASHED.')';
|
|
}
|
|
}
|
|
|
|
$releases = $this->getReleases($time, $cats, $query);
|
|
$total = $releases ? $releases->count() : 0;
|
|
|
|
if ($total > 0) {
|
|
$this->_totalReleases = $total;
|
|
cli()->info(number_format($total).' mediainfo movie names to process.');
|
|
|
|
foreach ($releases as $rel) {
|
|
$this->updateService->incrementChecked();
|
|
$this->updateService->reset();
|
|
$this->mediaMovieNameCheck($rel, $echo, $type, $nameStatus, $show);
|
|
$this->echoRenamed($show);
|
|
}
|
|
$this->echoFoundCount($echo, ' MediaInfo\'s');
|
|
} else {
|
|
cli()->info('Nothing to fix.');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Check for XXX release name.
|
|
*/
|
|
protected function xxxNameCheck(object $release, bool $echo, string $type, bool $nameStatus, bool $show): bool
|
|
{
|
|
if (preg_match('/^.+?SDPORN/i', $release->textstring, $hit)) {
|
|
$this->updateService->updateRelease($release, $hit[0], 'fileCheck: XXX SDPORN', $echo, $type, $nameStatus, $show);
|
|
|
|
return true;
|
|
}
|
|
|
|
$this->updateService->updateSingleColumn('proc_files', self::PROC_FILES_DONE, $release->releases_id);
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Check mediainfo movie_name for release name.
|
|
*/
|
|
protected function mediaMovieNameCheck(object $release, bool $echo, string $type, bool $nameStatus, bool $show): bool
|
|
{
|
|
$newName = '';
|
|
|
|
if (! empty($release->movie_name)) {
|
|
if (preg_match(ReleaseUpdateService::PREDB_REGEX, $release->movie_name, $hit)) {
|
|
$newName = $hit[1];
|
|
} elseif (preg_match('/(.+),(\sRMZ\.cr)?$/i', $release->movie_name, $hit)) {
|
|
$newName = $hit[1];
|
|
} else {
|
|
$newName = $release->movie_name;
|
|
}
|
|
}
|
|
|
|
if ($newName !== '') {
|
|
$this->updateService->updateRelease($release, $newName, 'MediaInfo: Movie Name', $echo, $type, $nameStatus, $show, $release->predb_id ?? 0);
|
|
|
|
return true;
|
|
}
|
|
|
|
$this->updateService->updateSingleColumn('proc_uid', self::PROC_UID_DONE, $release->releases_id);
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Check the array using regex for a clean name.
|
|
*
|
|
* @throws \Exception
|
|
*/
|
|
public function checkName(object $release, bool $echo, string $type, bool $nameStatus, bool $show, bool $preId = false): bool
|
|
{
|
|
// Check PreDB first
|
|
$preDbMatch = $this->updateService->checkPreDbMatch($release, $release->textstring);
|
|
if ($preDbMatch !== null) {
|
|
$this->updateService->updateRelease($release, $preDbMatch['title'], 'preDB: Match', $echo, $type, $nameStatus, $show, $preDbMatch['id']);
|
|
|
|
return true;
|
|
}
|
|
|
|
if ($preId) {
|
|
return $this->updateService->matched;
|
|
}
|
|
|
|
// Route to appropriate checker based on type
|
|
switch ($type) {
|
|
case 'PAR2, ':
|
|
$result = $this->fileExtractor->extractFromFile($release->textstring);
|
|
if ($result !== null) {
|
|
$this->updateService->updateRelease($release, $result->newName, 'fileCheck: '.$result->method, $echo, $type, $nameStatus, $show);
|
|
}
|
|
break;
|
|
|
|
case 'NFO, ':
|
|
$result = $this->nfoExtractor->extractFromNfo($release->textstring);
|
|
if ($result !== null) {
|
|
$this->updateService->updateRelease($release, $result->newName, 'nfoCheck: '.$result->method, $echo, $type, $nameStatus, $show);
|
|
}
|
|
break;
|
|
|
|
case 'Filenames, ':
|
|
// Try direct file name extraction first (handles NZBSPLIT wrappers)
|
|
if (! $this->updateService->matched) {
|
|
$result = $this->fileExtractor->extractFromFile($release->textstring);
|
|
if ($result !== null) {
|
|
$this->updateService->updateRelease($release, $result->newName, 'fileCheck: '.$result->method, $echo, $type, $nameStatus, $show);
|
|
}
|
|
}
|
|
// Try PreDB file check
|
|
if (! $this->updateService->matched) {
|
|
$this->preDbFileCheck($release, $echo, $type, $nameStatus, $show);
|
|
}
|
|
// Try PreDB title check
|
|
if (! $this->updateService->matched) {
|
|
$this->preDbTitleCheck($release, $echo, $type, $nameStatus, $show);
|
|
}
|
|
// Try file name extraction
|
|
if (! $this->updateService->matched) {
|
|
$result = $this->fileExtractor->extractFromFile($release->textstring);
|
|
if ($result !== null) {
|
|
$this->updateService->updateRelease($release, $result->newName, 'fileCheck: '.$result->method, $echo, $type, $nameStatus, $show);
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
// Use pattern checker service
|
|
$result = $this->checkerService->check($release, $release->textstring);
|
|
if ($result !== null) {
|
|
$this->updateService->updateRelease($release, $result->newName, $result->getFormattedMethod(), $echo, $type, $nameStatus, $show);
|
|
}
|
|
}
|
|
|
|
// Update processing flags if not matched
|
|
if ($nameStatus === true && ! $this->updateService->matched) {
|
|
$this->updateProcessingFlags($type, $release->releases_id);
|
|
}
|
|
|
|
return $this->updateService->matched;
|
|
}
|
|
|
|
/**
|
|
* Update processing flags based on type.
|
|
*/
|
|
protected function updateProcessingFlags(string $type, int $releaseId): void
|
|
{
|
|
switch ($type) {
|
|
case 'NFO, ':
|
|
$this->updateService->updateSingleColumn('proc_nfo', self::PROC_NFO_DONE, $releaseId);
|
|
break;
|
|
case 'Filenames, ':
|
|
$this->updateService->updateSingleColumn('proc_files', self::PROC_FILES_DONE, $releaseId);
|
|
break;
|
|
case 'PAR2, ':
|
|
$this->updateService->updateSingleColumn('proc_par2', self::PROC_PAR2_DONE, $releaseId);
|
|
break;
|
|
case 'PAR2 hash, ':
|
|
$this->updateService->updateSingleColumn('proc_hash16k', self::PROC_HASH16K_DONE, $releaseId);
|
|
break;
|
|
case 'SRR, ':
|
|
$this->updateService->updateSingleColumn('proc_srr', self::PROC_SRR_DONE, $releaseId);
|
|
break;
|
|
case 'UID, ':
|
|
case 'Mediainfo, ':
|
|
$this->updateService->updateSingleColumn('proc_uid', self::PROC_UID_DONE, $releaseId);
|
|
break;
|
|
case 'CRC32, ':
|
|
$this->updateService->updateSingleColumn('proc_crc32', self::PROC_CRC_DONE, $releaseId);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Match a release filename to a PreDB filename or title.
|
|
*
|
|
* @throws \Exception
|
|
*/
|
|
public function matchPreDbFiles(object $release, bool $echo, bool $nameStatus, bool $show): int
|
|
{
|
|
$matching = 0;
|
|
|
|
$files = explode('||', $release->filename ?? '');
|
|
$prioritizedFiles = $this->filePrioritizer->prioritizeForPreDb($files); // @phpstan-ignore argument.type
|
|
|
|
foreach ($prioritizedFiles as $fileName) {
|
|
$cleanedFileName = $this->fileNameCleaner->cleanForMatching($fileName);
|
|
|
|
if (empty($cleanedFileName) || strlen($cleanedFileName) < 8) {
|
|
continue;
|
|
}
|
|
|
|
$results = Search::searchPredb($cleanedFileName);
|
|
|
|
if (! empty($results)) {
|
|
$bestMatch = $this->predbMatchSelector->selectBestMatch($cleanedFileName, $results);
|
|
|
|
if ($bestMatch !== null) {
|
|
if ($bestMatch['title'] !== $release->searchname) {
|
|
$this->updateService->updateRelease($release, $bestMatch['title'], 'file matched source: '.($bestMatch['source'] ?? ''), $echo, 'PreDB file match, ', $nameStatus, $show);
|
|
} else {
|
|
$this->updateService->updateSingleColumn('predb_id', $bestMatch['id'] ?? 0, $release->releases_id);
|
|
}
|
|
$matching++;
|
|
|
|
return $matching;
|
|
}
|
|
}
|
|
}
|
|
|
|
return $matching;
|
|
}
|
|
|
|
/**
|
|
* Check if a release name looks like a season pack.
|
|
* Season packs have S01/S02 etc. without an episode (E01) suffix.
|
|
* Uses atomic group so "S02E07" matches "S02" then fails the (?!E\d+) lookahead
|
|
* instead of backtracking to "S0" and incorrectly matching.
|
|
*/
|
|
public function isSeasonPack(string $name): bool
|
|
{
|
|
return (bool) preg_match('/S(?>\d{1,2})(?!E\d+)/i', $name);
|
|
}
|
|
|
|
/**
|
|
* Reset the update service state.
|
|
*/
|
|
public function reset(): void
|
|
{
|
|
$this->updateService->reset();
|
|
}
|
|
|
|
/**
|
|
* Retrieves releases and their file names to attempt PreDB matches.
|
|
*
|
|
* @param array<string, mixed> $args
|
|
*
|
|
* @throws \Exception
|
|
*/
|
|
public function getPreFileNames(array $args = []): void
|
|
{
|
|
$show = isset($args[2]) && $args[2] === 'show';
|
|
|
|
if (isset($args[1]) && is_numeric($args[1])) {
|
|
$limit = 'LIMIT '.$args[1];
|
|
$orderBy = 'ORDER BY r.id DESC';
|
|
} else {
|
|
$orderBy = 'ORDER BY r.id ASC';
|
|
$limit = 'LIMIT 1000000';
|
|
}
|
|
|
|
cli()->info(PHP_EOL.'Match PreFiles '.($args[1] ?? 'all').' Started at '.now());
|
|
cli()->info('Matching predb filename to cleaned release_files.name.');
|
|
|
|
$counter = $counted = 0;
|
|
$timeStart = now();
|
|
|
|
$query = Release::fromQuery(
|
|
sprintf(
|
|
"SELECT r.id AS releases_id, r.name, r.searchname,
|
|
r.fromname, r.groups_id, r.categories_id,
|
|
GROUP_CONCAT(rf.name ORDER BY LENGTH(rf.name) DESC SEPARATOR '||') AS filename
|
|
FROM releases r
|
|
INNER JOIN release_files rf ON r.id = rf.releases_id
|
|
WHERE rf.name IS NOT NULL
|
|
AND r.predb_id = 0
|
|
AND r.categories_id IN (%s)
|
|
AND r.isrenamed = 0
|
|
GROUP BY r.id
|
|
%s %s",
|
|
implode(',', Category::OTHERS_GROUP),
|
|
$orderBy,
|
|
$limit
|
|
)
|
|
);
|
|
|
|
if ($query->isNotEmpty()) {
|
|
$total = $query->count();
|
|
|
|
if ($total > 0) {
|
|
cli()->info(PHP_EOL.number_format($total).' releases to process.');
|
|
|
|
foreach ($query as $row) {
|
|
$success = $this->matchPreDbFiles($row, true, true, $show);
|
|
if ($success === 1) {
|
|
$counted++;
|
|
}
|
|
if ($show === false) {
|
|
cli()->info('Renamed Releases: ['.number_format($counted).'] '.cli()->percentString(++$counter, $total));
|
|
}
|
|
}
|
|
cli()->info(PHP_EOL.'Renamed '.number_format($counted).' releases in '.now()->diffInSeconds($timeStart, true).' seconds.');
|
|
} else {
|
|
cli()->info('Nothing to do.');
|
|
}
|
|
}
|
|
}
|
|
}
|