mirror of
https://github.com/NNTmux/newznab-tmux.git
synced 2026-08-29 01:08:56 +00:00
Improve categorization
This commit is contained in:
@@ -0,0 +1,113 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Facades\Search;
|
||||
use App\Models\Release;
|
||||
use App\Services\Categorization\CategorizationService;
|
||||
use App\Services\NameFixing\NzbSplitUnwrapper;
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
class FixNzbSplitNames extends Command
|
||||
{
|
||||
protected $signature = 'releases:fix-nzbsplit
|
||||
{--dry-run : Preview changes without updating the database}';
|
||||
|
||||
protected $description = 'Fix NZBSPLIT-wrapped release names and recategorize the affected releases.';
|
||||
|
||||
public function handle(CategorizationService $categorizationService, NzbSplitUnwrapper $nzbSplitUnwrapper): int
|
||||
{
|
||||
$query = Release::query()
|
||||
->select(['id', 'name', 'searchname', 'fromname', 'groups_id', 'categories_id'])
|
||||
->where(function ($builder): void {
|
||||
$builder
|
||||
->where('name', 'like', '%__NZBSPLIT__%')
|
||||
->orWhere('searchname', 'like', '%__NZBSPLIT__%');
|
||||
})
|
||||
->orderBy('id');
|
||||
|
||||
$count = (clone $query)->count();
|
||||
|
||||
if ($count === 0) {
|
||||
$this->info('No NZBSPLIT-wrapped releases found.');
|
||||
|
||||
return self::SUCCESS;
|
||||
}
|
||||
|
||||
$dryRun = (bool) $this->option('dry-run');
|
||||
$updated = 0;
|
||||
$bar = $this->output->createProgressBar($count);
|
||||
$bar->start();
|
||||
|
||||
$query->chunkById(250, function ($releases) use ($categorizationService, $nzbSplitUnwrapper, $dryRun, &$updated, $bar): void {
|
||||
foreach ($releases as $release) {
|
||||
$bar->advance();
|
||||
|
||||
$newTitle = $nzbSplitUnwrapper->unwrap((string) $release->searchname)
|
||||
?? $nzbSplitUnwrapper->unwrap((string) $release->name);
|
||||
|
||||
if ($newTitle === null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$category = $categorizationService->determineCategory(
|
||||
$release->groups_id,
|
||||
$newTitle,
|
||||
(string) ($release->fromname ?? '')
|
||||
);
|
||||
|
||||
$newCategoryId = (int) ($category['categories_id'] ?? $release->categories_id);
|
||||
|
||||
if ($dryRun) {
|
||||
$this->newLine();
|
||||
$this->line(sprintf(
|
||||
'[dry-run] #%d %s -> %s (%d -> %d)',
|
||||
$release->id,
|
||||
(string) $release->searchname,
|
||||
$newTitle,
|
||||
(int) $release->categories_id,
|
||||
$newCategoryId
|
||||
));
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
Release::query()
|
||||
->where('id', $release->id)
|
||||
->update([
|
||||
'name' => $newTitle,
|
||||
'searchname' => $newTitle,
|
||||
'categories_id' => $newCategoryId,
|
||||
'isrenamed' => 1,
|
||||
'iscategorized' => 1,
|
||||
'videos_id' => 0,
|
||||
'tv_episodes_id' => 0,
|
||||
'imdbid' => null,
|
||||
'musicinfo_id' => null,
|
||||
'consoleinfo_id' => null,
|
||||
'gamesinfo_id' => 0,
|
||||
'bookinfo_id' => 0,
|
||||
'anidbid' => null,
|
||||
]);
|
||||
|
||||
Search::updateRelease((int) $release->id);
|
||||
$updated++;
|
||||
}
|
||||
});
|
||||
|
||||
$bar->finish();
|
||||
$this->newLine(2);
|
||||
|
||||
if ($dryRun) {
|
||||
$this->info(sprintf('Dry run complete. %d NZBSPLIT-wrapped releases inspected.', $count));
|
||||
|
||||
return self::SUCCESS;
|
||||
}
|
||||
|
||||
$this->info(sprintf('Updated %d NZBSPLIT-wrapped releases.', $updated));
|
||||
|
||||
return self::SUCCESS;
|
||||
}
|
||||
}
|
||||
@@ -12,6 +12,7 @@ use App\Models\Release;
|
||||
use App\Models\ReleaseFile;
|
||||
use App\Services\AdditionalProcessing\Config\ProcessingConfiguration;
|
||||
use App\Services\AdditionalProcessing\DTO\ReleaseProcessingContext;
|
||||
use App\Services\NameFixing\FileNameCleaner;
|
||||
use App\Services\NameFixing\NameFixingService;
|
||||
use App\Services\NameFixing\ReleaseUpdateService;
|
||||
use App\Services\NfoService;
|
||||
@@ -31,13 +32,22 @@ use Illuminate\Support\Facades\Log;
|
||||
*/
|
||||
class ReleaseFileManager
|
||||
{
|
||||
private readonly ReleaseUpdateService $releaseUpdateService;
|
||||
|
||||
private readonly FileNameCleaner $fileNameCleaner;
|
||||
|
||||
public function __construct(
|
||||
private readonly ProcessingConfiguration $config,
|
||||
private readonly ReleaseImageService $releaseImage,
|
||||
private readonly NfoService $nfo,
|
||||
private readonly NzbService $nzb,
|
||||
private readonly NameFixingService $nameFixingService
|
||||
) {}
|
||||
private readonly NameFixingService $nameFixingService,
|
||||
?ReleaseUpdateService $releaseUpdateService = null,
|
||||
?FileNameCleaner $fileNameCleaner = null
|
||||
) {
|
||||
$this->releaseUpdateService = $releaseUpdateService ?? new ReleaseUpdateService;
|
||||
$this->fileNameCleaner = $fileNameCleaner ?? new FileNameCleaner;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add file information to the database.
|
||||
@@ -152,6 +162,50 @@ class ReleaseFileManager
|
||||
Search::updateRelease($releaseId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Rename NZBSPLIT-wrapped releases directly from parsed NZB titles.
|
||||
*
|
||||
* @param list<array<string, mixed>> $nzbContents
|
||||
*/
|
||||
public function processReleaseNameFromNzbContents(array $nzbContents, ReleaseProcessingContext $context): bool
|
||||
{
|
||||
foreach ($nzbContents as $nzbFile) {
|
||||
$title = (string) ($nzbFile['title'] ?? '');
|
||||
|
||||
if ($title === '' || ! str_contains($title, '__NZBSPLIT__')) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$candidate = $this->fileNameCleaner->extractNzbSplitName($title);
|
||||
|
||||
if ($candidate === null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$candidate = $this->fileNameCleaner->normalizeCandidateTitle($candidate);
|
||||
|
||||
if (! $this->fileNameCleaner->isPlausibleReleaseTitle($candidate)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->releaseUpdateService->updateRelease(
|
||||
$context->release,
|
||||
$candidate,
|
||||
'NZBSPLIT wrapper',
|
||||
true,
|
||||
'Filenames, ',
|
||||
true,
|
||||
true
|
||||
);
|
||||
|
||||
$context->release->searchname = $candidate;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finalize release processing with status updates.
|
||||
*/
|
||||
@@ -519,7 +573,7 @@ class ReleaseFileManager
|
||||
$candidate = $this->normalizeCandidateTitle($candidate);
|
||||
|
||||
if ($this->isPlausibleReleaseTitle($candidate)) {
|
||||
(new ReleaseUpdateService)->updateRelease(
|
||||
$this->releaseUpdateService->updateRelease(
|
||||
$context->release,
|
||||
$candidate,
|
||||
'RarInfo FileName Match',
|
||||
@@ -545,7 +599,7 @@ class ReleaseFileManager
|
||||
$candidate = $this->normalizeCandidateTitle($candidate);
|
||||
|
||||
if ($this->isPlausibleReleaseTitle($candidate)) {
|
||||
(new ReleaseUpdateService)->updateRelease(
|
||||
$this->releaseUpdateService->updateRelease(
|
||||
$context->release,
|
||||
$candidate,
|
||||
'RarInfo FileName Match',
|
||||
@@ -566,6 +620,12 @@ class ReleaseFileManager
|
||||
private function extractReleaseNameFromFile(string $filename): ?string
|
||||
{
|
||||
$basename = basename($filename);
|
||||
|
||||
$unwrapped = $this->fileNameCleaner->extractNzbSplitName($basename);
|
||||
if ($unwrapped !== null) {
|
||||
return $unwrapped;
|
||||
}
|
||||
|
||||
$cleaned = preg_replace(
|
||||
'/\.(mkv|avi|mp4|m4v|mpg|mpeg|wmv|flv|mov|ts|vob|iso|divx|par2?|nfo|sfv|nzb|rar|zip|r\d{2,3}|pkg|exe|msi)$/i',
|
||||
'',
|
||||
@@ -588,13 +648,7 @@ class ReleaseFileManager
|
||||
*/
|
||||
private function normalizeCandidateTitle(string $title): string
|
||||
{
|
||||
$t = trim($title);
|
||||
$t = preg_replace('/\.(mkv|avi|mp4|m4v|mpg|mpeg|wmv|flv|mov|ts|vob|iso|divx)$/i', '', $t) ?? $t;
|
||||
$t = preg_replace('/\.(par2?|nfo|sfv|nzb|rar|zip|r\d{2,3}|pkg|exe|msi|jpe?g|png|gif|bmp)$/i', '', $t) ?? $t;
|
||||
$t = preg_replace('/[.\-_ ](?:part|vol|r)\d+(?:\+\d+)?$/i', '', $t) ?? $t;
|
||||
$t = preg_replace('/[\s_]+/', ' ', $t) ?? $t;
|
||||
|
||||
return trim($t, " .-_\t\r\n");
|
||||
return $this->fileNameCleaner->normalizeCandidateTitle($title);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -602,30 +656,6 @@ class ReleaseFileManager
|
||||
*/
|
||||
private function isPlausibleReleaseTitle(string $title): bool
|
||||
{
|
||||
$t = trim($title);
|
||||
if ($t === '' || strlen($t) < 12) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$wordCount = preg_match_all('/[A-Za-z0-9]{3,}/', $t);
|
||||
if ($wordCount < 2) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (preg_match('/(?:^|[.\-_ ])(?:part|vol|r)\d+(?:\+\d+)?$/i', $t)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (preg_match('/^(setup|install|installer|patch|update|crack|keygen)\d*[\s._-]/i', $t)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$hasGroupSuffix = (bool) preg_match('/[-.][A-Za-z0-9]{2,}$/', $t);
|
||||
$hasYear = (bool) preg_match('/\b(19|20)\d{2}\b/', $t);
|
||||
$hasQuality = (bool) preg_match('/\b(480p|720p|1080p|2160p|4k|webrip|web[ .-]?dl|bluray|bdrip|dvdrip|hdtv|hdrip|xvid|x264|x265|hevc|h\.?264|ts|cam|r5|proper|repack)\b/i', $t);
|
||||
$hasTV = (bool) preg_match('/\bS\d{1,2}[Eex]\d{1,3}\b/i', $t);
|
||||
$hasXXX = (bool) preg_match('/\bXXX\b/i', $t);
|
||||
|
||||
return $hasGroupSuffix || ($hasTV && $hasQuality) || ($hasYear && ($hasQuality || $hasTV)) || $hasXXX;
|
||||
return $this->fileNameCleaner->isPlausibleReleaseTitle($title);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,6 +59,7 @@ class ReleaseProcessor
|
||||
}
|
||||
|
||||
$this->initializeContext($context);
|
||||
$this->releaseManager->processReleaseNameFromNzbContents($context->nzbContents, $context);
|
||||
$messageIds = $this->nzbParser->extractMessageIDs($context->nzbContents, $context->releaseGroupName, $this->config);
|
||||
|
||||
$context->nzbHasCompressedFile = $messageIds['hasCompressedFile'];
|
||||
|
||||
@@ -8,6 +8,7 @@ use App\Models\Settings;
|
||||
use App\Models\UsenetGroup;
|
||||
use App\Services\Categorization\Pipes\AbstractCategorizationPipe;
|
||||
use App\Services\Categorization\Pipes\CategorizationPassable;
|
||||
use App\Services\NameFixing\NzbSplitUnwrapper;
|
||||
use Illuminate\Pipeline\Pipeline;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
@@ -31,10 +32,12 @@ class CategorizationPipeline
|
||||
|
||||
protected bool $catWebDL;
|
||||
|
||||
protected NzbSplitUnwrapper $nzbSplitUnwrapper;
|
||||
|
||||
/**
|
||||
* @param iterable<AbstractCategorizationPipe> $pipes
|
||||
*/
|
||||
public function __construct(iterable $pipes = [])
|
||||
public function __construct(iterable $pipes = [], ?NzbSplitUnwrapper $nzbSplitUnwrapper = null)
|
||||
{
|
||||
/** @phpstan-ignore argument.templateType */
|
||||
$this->pipes = collect($pipes)
|
||||
@@ -42,6 +45,7 @@ class CategorizationPipeline
|
||||
|
||||
$this->categorizeForeign = (bool) Settings::settingValue('categorizeforeign');
|
||||
$this->catWebDL = (bool) Settings::settingValue('catwebdl');
|
||||
$this->nzbSplitUnwrapper = $nzbSplitUnwrapper ?? new NzbSplitUnwrapper;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -70,6 +74,8 @@ class CategorizationPipeline
|
||||
?string $poster = '',
|
||||
bool $debug = false
|
||||
): array {
|
||||
$releaseName = $this->nzbSplitUnwrapper->unwrap($releaseName) ?? $releaseName;
|
||||
|
||||
$groupName = UsenetGroup::whereId($groupId)->value('name') ?? '';
|
||||
|
||||
$context = new ReleaseContext(
|
||||
|
||||
@@ -13,6 +13,10 @@ use App\Services\Categorization\ReleaseContext;
|
||||
*/
|
||||
class TvCategorizer extends AbstractCategorizer
|
||||
{
|
||||
private const string SPORTS_MARKER_REGEX = '/\b(NFL|NBA|NHL|MLB|MLS|EPL|UFC|WWE|Boxing|F1|Formula[._ -]?1|NASCAR|PGA|Tennis|Golf|Soccer|Football|Cricket|Rugby|Olympics?|Olympic[._ -]?Games?|Paralympics?)\b/i';
|
||||
|
||||
private const string SPORTS_EVENT_CONTEXT_REGEX = '/\d{4}|\b(Season|Week|Round|Match|Game|vs|Playoffs?|Finals?|Qualifying|Opening|Closing|Ceremony|Championship)\b/i';
|
||||
|
||||
protected int $priority = 20;
|
||||
|
||||
public function getName(): string
|
||||
@@ -97,6 +101,10 @@ class TvCategorizer extends AbstractCategorizer
|
||||
if (preg_match('/[._ -](19|20)\d{2}[._ -]\d{2}[._ -]\d{2}[._ -]/i', $name)) {
|
||||
return true;
|
||||
}
|
||||
// Sports events are TV-like even without SxxExx markers.
|
||||
if ($this->isSportEvent($name)) {
|
||||
return true;
|
||||
}
|
||||
// Known anime release groups (should be treated as TV)
|
||||
if (preg_match('/(?:^|[.\-_ \[])(URANiME|ANiHLS|HaiKU|ANiURL|SkyAnime|Erai-raws|LostYears|Vodes|SubsPlease|Judas|Ember|EMBER|YuiSubs|ASW|Tsundere-Raws|Anime-Raws|Kekkan)(?:[.\-_ \]]|$)/i', $name)) {
|
||||
return true;
|
||||
@@ -148,8 +156,7 @@ class TvCategorizer extends AbstractCategorizer
|
||||
|
||||
protected function checkSport(string $name): ?CategorizationResult
|
||||
{
|
||||
if (preg_match('/\b(NFL|NBA|NHL|MLB|MLS|UFC|WWE|Boxing|F1|Formula[._ -]?1|NASCAR|PGA|Tennis|Golf|Soccer|Football|Cricket|Rugby)\b/i', $name) &&
|
||||
preg_match('/\d{4}|\b(Season|Week|Round|Match|Game)\b/i', $name)) {
|
||||
if ($this->isSportEvent($name)) {
|
||||
return $this->matched(Category::TV_SPORT, 0.85, 'sport');
|
||||
}
|
||||
|
||||
@@ -250,4 +257,10 @@ class TvCategorizer extends AbstractCategorizer
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
protected function isSportEvent(string $name): bool
|
||||
{
|
||||
return preg_match(self::SPORTS_MARKER_REGEX, $name) === 1
|
||||
&& preg_match(self::SPORTS_EVENT_CONTEXT_REGEX, $name) === 1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,6 +37,16 @@ class FileNameExtractor
|
||||
|
||||
// Try each pattern in order of specificity
|
||||
|
||||
if (str_contains($filename, '__NZBSPLIT__')) {
|
||||
$unwrapped = $this->cleaner->extractNzbSplitName($filename);
|
||||
|
||||
if ($unwrapped !== null && $this->cleaner->isPlausibleReleaseTitle($unwrapped)) {
|
||||
return NameFixResult::fromMatch($unwrapped, 'NZBSPLIT wrapper', 'File');
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
// Scene TV release with group suffix
|
||||
if (preg_match('/^(.+?(x264|x265|HEVC|XviD|H\.?264|H\.?265)\-[A-Za-z0-9]+)\\\\/i', $filename, $result)) {
|
||||
return NameFixResult::fromMatch($result[1], 'Scene release with group', 'File');
|
||||
|
||||
@@ -16,6 +16,13 @@ class FileNameCleaner
|
||||
{
|
||||
use DetectsHashedNames;
|
||||
|
||||
protected NzbSplitUnwrapper $nzbSplitUnwrapper;
|
||||
|
||||
public function __construct(?NzbSplitUnwrapper $nzbSplitUnwrapper = null)
|
||||
{
|
||||
$this->nzbSplitUnwrapper = $nzbSplitUnwrapper ?? new NzbSplitUnwrapper;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generic DVD structure filenames and similarly low-information names.
|
||||
*
|
||||
@@ -92,6 +99,8 @@ class FileNameCleaner
|
||||
// Extract filename from path
|
||||
$fileName = $this->extractFilenameFromPath($fileName);
|
||||
|
||||
$fileName = $this->extractNzbSplitName($fileName) ?? $fileName;
|
||||
|
||||
if ($this->isIgnorableForMatching($fileName)) {
|
||||
return false;
|
||||
}
|
||||
@@ -153,6 +162,8 @@ class FileNameCleaner
|
||||
{
|
||||
$t = trim($title);
|
||||
|
||||
$t = $this->extractNzbSplitName($t) ?? $t;
|
||||
|
||||
// Remove common video file extensions
|
||||
$t = preg_replace('/\.(mkv|avi|mp4|m4v|mpg|mpeg|wmv|flv|mov|ts|vob|iso|divx)$/i', '', $t) ?? $t;
|
||||
|
||||
@@ -231,6 +242,8 @@ class FileNameCleaner
|
||||
*/
|
||||
public function cleanForTitleMatch(string $filename): string
|
||||
{
|
||||
$filename = $this->extractNzbSplitName($filename) ?? $filename;
|
||||
|
||||
// Remove file extension
|
||||
$clean = preg_replace('/\.(mkv|avi|mp4|m4v|wmv|mpg|mpeg|mov|ts|m2ts|vob|divx|flv|nfo|sfv|nzb|srr|srs|rar|r\d{2,4}|zip|7z|par2?|vol\d+[\+\-]\d+|\d{3})$/i', '', $filename);
|
||||
|
||||
@@ -271,6 +284,8 @@ class FileNameCleaner
|
||||
*/
|
||||
public function looksLikeSceneRelease(string $filename): bool
|
||||
{
|
||||
$filename = $this->extractNzbSplitName($filename) ?? $filename;
|
||||
|
||||
$baseName = preg_replace('/\.[a-z0-9]{2,4}$/i', '', $filename);
|
||||
|
||||
// Check for group suffix
|
||||
@@ -313,6 +328,11 @@ class FileNameCleaner
|
||||
return false;
|
||||
}
|
||||
|
||||
public function extractNzbSplitName(string $value): ?string
|
||||
{
|
||||
return $this->nzbSplitUnwrapper->unwrap($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ignore junk inputs that should never drive PreDB matches.
|
||||
*/
|
||||
|
||||
@@ -1200,6 +1200,13 @@ class NameFixingService
|
||||
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);
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Services\NameFixing;
|
||||
|
||||
final class NzbSplitUnwrapper
|
||||
{
|
||||
private const string WRAPPER_PATTERN = '/__NZBSPLIT__([a-f0-9]{8,})__NZBSPLIT__(.+)$/i';
|
||||
|
||||
/**
|
||||
* @var list<string>
|
||||
*/
|
||||
private const array TRAILING_ARCHIVE_PATTERNS = [
|
||||
'/\.part\d+\.rar$/i',
|
||||
'/\.r\d{2,4}$/i',
|
||||
'/\.7z\.\d{2,4}$/i',
|
||||
'/\.7z$/i',
|
||||
'/\.rar$/i',
|
||||
'/\.par2?$/i',
|
||||
'/\.vol\d+[+\-]\d+\.par2?$/i',
|
||||
'/\.\d{3}$/',
|
||||
];
|
||||
|
||||
/**
|
||||
* @var list<string>
|
||||
*/
|
||||
private const array TRAILING_SUBJECT_PATTERNS = [
|
||||
'/"\s*yEnc(?:\s*\([^)]*\))?$/i',
|
||||
'/\s+yEnc(?:\s*\([^)]*\))?$/i',
|
||||
'/"\s*yEnc.*$/i',
|
||||
'/\s+yEnc.*$/i',
|
||||
];
|
||||
|
||||
public function unwrap(string $value): ?string
|
||||
{
|
||||
if (! preg_match(self::WRAPPER_PATTERN, $value, $matches)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$title = $matches[2];
|
||||
$didStrip = true;
|
||||
|
||||
while ($didStrip) {
|
||||
$didStrip = false;
|
||||
|
||||
foreach (self::TRAILING_SUBJECT_PATTERNS as $pattern) {
|
||||
$updated = preg_replace($pattern, '', $title);
|
||||
|
||||
if ($updated !== null && $updated !== $title) {
|
||||
$title = rtrim($updated, " \t\n\r\0\x0B\"'");
|
||||
$didStrip = true;
|
||||
}
|
||||
}
|
||||
|
||||
foreach (self::TRAILING_ARCHIVE_PATTERNS as $pattern) {
|
||||
$updated = preg_replace($pattern, '', $title);
|
||||
|
||||
if ($updated !== null && $updated !== $title) {
|
||||
$title = $updated;
|
||||
$didStrip = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$title = str_replace(['_', '+'], ['.', ' '], $title);
|
||||
$title = preg_replace('/\.{2,}/', '.', $title) ?? $title;
|
||||
$title = preg_replace('/\s{2,}/', ' ', $title) ?? $title;
|
||||
$title = preg_replace('/\.\s+/', '.', $title) ?? $title;
|
||||
$title = preg_replace('/\s+\./', '.', $title) ?? $title;
|
||||
$title = trim($title, " \t\n\r\0\x0B.-_");
|
||||
|
||||
return $title !== '' ? $title : null;
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@ declare(strict_types=1);
|
||||
namespace App\Services;
|
||||
|
||||
use App\Models\Predb;
|
||||
use App\Services\NameFixing\NzbSplitUnwrapper;
|
||||
|
||||
/**
|
||||
* Cleans names for releases/imports/namefixer.
|
||||
@@ -48,6 +49,8 @@ class ReleaseCleaningService
|
||||
|
||||
protected RegexService $_regexes;
|
||||
|
||||
protected NzbSplitUnwrapper $nzbSplitUnwrapper;
|
||||
|
||||
/**
|
||||
* ReleaseCleaningService constructor.
|
||||
*
|
||||
@@ -60,6 +63,7 @@ class ReleaseCleaningService
|
||||
$this->e1 = CollectionsCleaningService::REGEX_FILE_EXTENSIONS.CollectionsCleaningService::REGEX_END;
|
||||
$this->e2 = CollectionsCleaningService::REGEX_FILE_EXTENSIONS.CollectionsCleaningService::REGEX_SUBJECT_SIZE.CollectionsCleaningService::REGEX_END;
|
||||
$this->_regexes = new RegexService('release_naming_regexes');
|
||||
$this->nzbSplitUnwrapper = new NzbSplitUnwrapper;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -73,6 +77,19 @@ class ReleaseCleaningService
|
||||
$this->fromName = $fromName;
|
||||
$this->groupName = $groupName;
|
||||
|
||||
$unwrappedSubject = $this->nzbSplitUnwrapper->unwrap($subject);
|
||||
if ($unwrappedSubject !== null) {
|
||||
$this->subject = $unwrappedSubject;
|
||||
|
||||
return [
|
||||
'cleansubject' => $unwrappedSubject,
|
||||
'properlynamed' => true,
|
||||
'increment' => false,
|
||||
'predb' => 0,
|
||||
'requestid' => false,
|
||||
];
|
||||
}
|
||||
|
||||
$hit = $hits = [];
|
||||
// Get pre style name from releases.name
|
||||
if (preg_match_all(
|
||||
@@ -429,7 +446,7 @@ class ReleaseCleaningService
|
||||
*/
|
||||
public function fixerCleaner(string $name)
|
||||
{
|
||||
$cleanerName = $name;
|
||||
$cleanerName = $this->nzbSplitUnwrapper->unwrap($name) ?? $name;
|
||||
|
||||
// Remove sample/proof/thumbs markers from the end
|
||||
$cleanerName = preg_replace('/[.\-_](sample|proof|thumbs?)$/i', '', $cleanerName);
|
||||
|
||||
Reference in New Issue
Block a user