From 0723dfa3d9d3b671ee4a53c13568822cb3efdc58 Mon Sep 17 00:00:00 2001 From: DariusIII Date: Tue, 12 May 2026 15:38:01 +0200 Subject: [PATCH] Fix naming on import --- app/Services/Nzb/NzbImportService.php | 26 +++++++++++++++++- tests/Unit/NzbImportServiceTest.php | 38 +++++++++++++++++++++++++++ 2 files changed, 63 insertions(+), 1 deletion(-) diff --git a/app/Services/Nzb/NzbImportService.php b/app/Services/Nzb/NzbImportService.php index 426e80046..2ed3b3282 100644 --- a/app/Services/Nzb/NzbImportService.php +++ b/app/Services/Nzb/NzbImportService.php @@ -152,7 +152,7 @@ class NzbImportService } // Try to insert the NZB details into the DB. - $nzbFileName = $useNzbName === true ? str_ireplace('.nzb', '', basename($nzbFilePath)) : ''; + $nzbFileName = $useNzbName === true ? $this->deriveReleaseNameFromNzbPath($nzbFilePath) : ''; try { $importStatus = $this->scanNZBFile($nzbXML, $nzbFileName, $source); } catch (\Exception $e) { @@ -232,6 +232,30 @@ class NzbImportService return true; } + /** + * Derive a clean release name from an NZB file path by stripping the + * trailing ".nzb"/".nzb.gz" wrapper and a known trailing media/container + * extension (e.g. ".mkv", ".mp4"). Example: + * "Show - S01E07 [1080p].mkv.nzb.gz" => "Show - S01E07 [1080p]". + */ + protected function deriveReleaseNameFromNzbPath(string $path): string + { + $name = basename($path); + + // Strip trailing .nzb or .nzb.gz (case-insensitive). + $name = preg_replace('/\.nzb(\.gz)?$/i', '', $name) ?? $name; + + // Strip one trailing known media/container extension from the allow-list. + $name = preg_replace( + '/\.(mkv|mp4|avi|mov|wmv|flv|m4v|ts|webm|mpg|mpeg|iso|m2ts|ogm|divx|vob)$/i', + '', + $name + ) ?? $name; + + // Tidy up any trailing dots/whitespace left behind. + return rtrim($name, ". \t\n\r\0\x0B"); + } + /** * Scan and process an NZB file. * diff --git a/tests/Unit/NzbImportServiceTest.php b/tests/Unit/NzbImportServiceTest.php index e8840bd1f..31538a4e2 100644 --- a/tests/Unit/NzbImportServiceTest.php +++ b/tests/Unit/NzbImportServiceTest.php @@ -10,6 +10,7 @@ use Illuminate\Contracts\Console\Kernel; use Illuminate\Support\Facades\Cache; use Illuminate\Support\Facades\DB; use PDO; +use PHPUnit\Framework\Attributes\DataProvider; use Tests\TestCase; final class NzbImportServiceTest extends TestCase @@ -190,6 +191,43 @@ final class NzbImportServiceTest extends TestCase $this->assertFileDoesNotExist($noGroupFile); } + /** + * @return array + */ + public static function nzbFilenameProvider(): array + { + return [ + 'plain .nzb' => ['foo.nzb', 'foo'], + 'plain .nzb.gz' => ['foo.nzb.gz', 'foo'], + 'mkv wrapper' => ['foo.mkv.nzb.gz', 'foo'], + 'uppercase wrapper' => ['bar.MP4.NZB.GZ', 'bar'], + 'release with brackets' => [ + '[DKB] Kami-tachi ni Hirowareta Otoko - S01E07 [1080p][H.265 10bit].mkv.nzb.gz', + '[DKB] Kami-tachi ni Hirowareta Otoko - S01E07 [1080p][H.265 10bit]', + ], + 'non-media inner ext stays' => ['release.name.nzb.gz', 'release.name'], + 'no trailing media ext' => ['something.nzb', 'something'], + 'full path input' => ['/tmp/nested/path/Show - 01.mp4.nzb.gz', 'Show - 01'], + ]; + } + + /** + * @dataProvider nzbFilenameProvider + */ + #[DataProvider('nzbFilenameProvider')] + public function test_derive_release_name_strips_wrapper_and_media_extension(string $input, string $expected): void + { + $service = new class(['Browser' => true]) extends NzbImportService + { + public function deriveForTest(string $path): string + { + return $this->deriveReleaseNameFromNzbPath($path); + } + }; + + $this->assertSame($expected, $service->deriveForTest($input)); + } + private function makeNzbFile(string $suffix): string { $path = sys_get_temp_dir().'/'.$suffix.'-'.bin2hex(random_bytes(5)).'.nzb';