mirror of
https://github.com/NNTmux/newznab-tmux.git
synced 2026-08-28 17:01:16 +00:00
Use manticore for mediainfo data
This commit is contained in:
@@ -4,8 +4,10 @@ declare(strict_types=1);
|
||||
|
||||
namespace Tests\Unit;
|
||||
|
||||
use App\Services\Search\Drivers\ElasticSearchDriver;
|
||||
use PHPUnit\Framework\Attributes\Test;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use ReflectionClass;
|
||||
|
||||
final class ElasticSearchQueryTest extends TestCase
|
||||
{
|
||||
@@ -67,4 +69,40 @@ final class ElasticSearchQueryTest extends TestCase
|
||||
$this->assertStringContainsString("'_source' => false", $methodSource);
|
||||
$this->assertStringContainsString("'track_total_hits' => (bool)", $methodSource);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function filtered_release_search_supports_media_info_text_and_numeric_filters(): void
|
||||
{
|
||||
$driverSource = file_get_contents(__DIR__.'/../../app/Services/Search/Drivers/ElasticSearchDriver.php');
|
||||
|
||||
$this->assertIsString($driverSource);
|
||||
$this->assertStringContainsString("'media_video_codec' => ['media_video_codec']", $driverSource);
|
||||
$this->assertStringContainsString("'term' => ['media_unique_id' => \$mediaUniqueId]", $driverSource);
|
||||
$this->assertStringContainsString("'term' => ['has_media_info' =>", $driverSource);
|
||||
$this->assertStringContainsString("'range' => ['media_video_width' => \$range]", $driverSource);
|
||||
$this->assertStringContainsString("'range' => ['media_video_height' => \$range]", $driverSource);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function media_info_unique_id_uses_an_exact_keyword_filter(): void
|
||||
{
|
||||
$reflection = new ReflectionClass(ElasticSearchDriver::class);
|
||||
$driver = $reflection->newInstanceWithoutConstructor();
|
||||
$method = $reflection->getMethod('buildElasticsearchReleaseFilters');
|
||||
|
||||
$filters = $method->invoke($driver, ['media_unique_id' => ' 0x123ABC ']);
|
||||
|
||||
$this->assertContains(['term' => ['media_unique_id' => '0x123ABC']], $filters);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function incremental_release_updates_use_the_canonical_projection_and_document(): void
|
||||
{
|
||||
$driverSource = file_get_contents(__DIR__.'/../../app/Services/Search/Drivers/ElasticSearchDriver.php');
|
||||
|
||||
$this->assertIsString($driverSource);
|
||||
$this->assertStringContainsString('ReleaseIndexProjection::forId((int) $releaseID)', $driverSource);
|
||||
$this->assertStringContainsString('ReleaseSearchIndexDocument::normalizeForBulk($parameters)', $driverSource);
|
||||
$this->assertStringContainsString("'body' => \$document", $driverSource);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,6 +29,11 @@ final class ManticoreIndexRegistryTest extends TestCase
|
||||
|
||||
self::assertSame('bigint', $definitions['releases']['columns']['passwordstatus']['type']);
|
||||
self::assertSame('bigint', $definitions['releases']['columns']['haspreview']['type']);
|
||||
self::assertSame('text', $definitions['releases']['columns']['media_movie_name']['type']);
|
||||
self::assertSame('string', $definitions['releases']['columns']['media_unique_id']['type']);
|
||||
self::assertSame('integer', $definitions['releases']['columns']['media_video_width']['type']);
|
||||
self::assertSame('text', $definitions['releases']['columns']['media_audio_language']['type']);
|
||||
self::assertSame('integer', $definitions['releases']['columns']['has_media_info']['type']);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
namespace Tests\Unit;
|
||||
|
||||
use App\Services\Search\Drivers\ManticoreSearchDriver;
|
||||
use App\Services\Search\DTO\ReleaseSearchQuery;
|
||||
use PHPUnit\Framework\Attributes\DataProvider;
|
||||
use PHPUnit\Framework\Attributes\Test;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
@@ -263,6 +264,46 @@ class ManticoreSearchQueryTest extends TestCase
|
||||
$this->assertStringContainsString("\$query->sort('id', \$order);", $driverSource);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function release_search_query_normalizes_media_info_filters(): void
|
||||
{
|
||||
$criteria = ReleaseSearchQuery::fromCriteria([
|
||||
'has_media_info' => true,
|
||||
'media_unique_id' => ' 0x123ABC ',
|
||||
'min_video_width' => -1,
|
||||
'max_video_width' => 3840,
|
||||
'min_video_height' => 720,
|
||||
'max_video_height' => 2160,
|
||||
], 25)->criteria();
|
||||
|
||||
$this->assertTrue($criteria['has_media_info']);
|
||||
$this->assertSame('0x123ABC', $criteria['media_unique_id']);
|
||||
$this->assertSame(0, $criteria['min_video_width']);
|
||||
$this->assertSame(3840, $criteria['max_video_width']);
|
||||
$this->assertSame(720, $criteria['min_video_height']);
|
||||
$this->assertSame(2160, $criteria['max_video_height']);
|
||||
|
||||
$invalidCriteria = ReleaseSearchQuery::fromCriteria(['media_unique_id' => '-1'], 25)->criteria();
|
||||
$this->assertNull($invalidCriteria['media_unique_id']);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function filtered_release_search_allowlists_media_info_fields_and_filters(): void
|
||||
{
|
||||
$driverSource = file_get_contents(__DIR__.'/../../app/Services/Search/Drivers/ManticoreSearchDriver.php');
|
||||
|
||||
$this->assertIsString($driverSource);
|
||||
$this->assertStringContainsString("'media_video_codec',", $driverSource);
|
||||
$this->assertStringContainsString('in_array($key, self::RELEASE_FIELD_SEARCH_FIELDS, true)', $driverSource);
|
||||
$this->assertStringContainsString("\$query->filter('has_media_info', '='", $driverSource);
|
||||
$this->assertStringContainsString("\$query->filter('media_unique_id', '='", $driverSource);
|
||||
$this->assertStringContainsString("\$query->filter('media_video_width', 'gte'", $driverSource);
|
||||
$this->assertStringContainsString("\$query->filter('media_video_height', 'lte'", $driverSource);
|
||||
|
||||
$reflection = new ReflectionClass(ManticoreSearchDriver::class);
|
||||
$this->assertNotContains('media_unique_id', $reflection->getConstant('RELEASE_FIELD_SEARCH_FIELDS'));
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function search_releases_filtered_scopes_field_queries_with_grouping_parentheses(): void
|
||||
{
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Tests\Unit;
|
||||
|
||||
use App\Facades\Search;
|
||||
use App\Models\MediaInfo as MediaInfoRecord;
|
||||
use App\Services\ReleaseExtraService;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Mhor\MediaInfo\Container\MediaInfoContainer;
|
||||
use Mhor\MediaInfo\Type\General;
|
||||
use PHPUnit\Framework\Attributes\Test;
|
||||
use Tests\TestCase;
|
||||
|
||||
final class ReleaseExtraServiceTest extends TestCase
|
||||
{
|
||||
#[Test]
|
||||
public function it_reindexes_the_release_once_after_media_info_persistence(): void
|
||||
{
|
||||
Search::shouldReceive('updateRelease')
|
||||
->once()
|
||||
->with(42);
|
||||
|
||||
(new ReleaseExtraService)->addFromXml(42, new MediaInfoContainer);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function it_does_not_persist_invalid_media_unique_id_sentinels(): void
|
||||
{
|
||||
Schema::create('media_infos', function (Blueprint $table): void {
|
||||
$table->id();
|
||||
$table->unsignedBigInteger('releases_id');
|
||||
$table->string('movie_name')->nullable();
|
||||
$table->string('file_name')->nullable();
|
||||
$table->string('unique_id')->nullable();
|
||||
$table->timestamps();
|
||||
});
|
||||
|
||||
$general = new General;
|
||||
$general->set('movie_name', 'Example Movie');
|
||||
$general->set('unique_id', '0x0');
|
||||
|
||||
$mediaInfo = new MediaInfoContainer;
|
||||
$mediaInfo->setGeneral($general);
|
||||
|
||||
try {
|
||||
MediaInfoRecord::addData(43, $mediaInfo);
|
||||
$record = MediaInfoRecord::query()->where('releases_id', 43)->firstOrFail();
|
||||
} finally {
|
||||
Schema::drop('media_infos');
|
||||
}
|
||||
|
||||
$this->assertSame('Example Movie', $record->movie_name);
|
||||
$this->assertNull($record->unique_id);
|
||||
}
|
||||
}
|
||||
@@ -30,6 +30,11 @@ final class ReleaseSearchIndexDocumentTest extends TestCase
|
||||
'tv_episodes_id' => '456',
|
||||
'passwordstatus' => '-1',
|
||||
'comments' => '3',
|
||||
'media_movie_name' => 'Example Movie',
|
||||
'media_video_width' => '1920',
|
||||
'media_video_height' => '1080',
|
||||
'media_audio_language' => 'English French',
|
||||
'has_media_info' => '1',
|
||||
'postdate' => '2026-01-02 03:04:05',
|
||||
'adddate' => '2026-01-03 04:05:06',
|
||||
'nzb_password' => 'must-not-be-indexed',
|
||||
@@ -40,6 +45,11 @@ final class ReleaseSearchIndexDocumentTest extends TestCase
|
||||
self::assertSame(123, $document['tmdbid']);
|
||||
self::assertSame(456, $document['tv_episodes_id']);
|
||||
self::assertSame(-1, $document['passwordstatus']);
|
||||
self::assertSame('Example Movie', $document['media_movie_name']);
|
||||
self::assertSame(1920, $document['media_video_width']);
|
||||
self::assertSame(1080, $document['media_video_height']);
|
||||
self::assertSame('English French', $document['media_audio_language']);
|
||||
self::assertSame(1, $document['has_media_info']);
|
||||
self::assertGreaterThan(0, $document['postdate_ts']);
|
||||
self::assertGreaterThan(0, $document['adddate_ts']);
|
||||
self::assertArrayNotHasKey('nzb_password', $document);
|
||||
|
||||
@@ -5,7 +5,12 @@ declare(strict_types=1);
|
||||
namespace Tests\Unit\Services\Search;
|
||||
|
||||
use App\Services\Search\Support\ReleaseIndexProjection;
|
||||
use Illuminate\Database\Query\Builder;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use PHPUnit\Framework\Attributes\Test;
|
||||
use ReflectionClass;
|
||||
use Tests\TestCase;
|
||||
|
||||
final class ReleaseIndexProjectionTest extends TestCase
|
||||
@@ -17,8 +22,68 @@ final class ReleaseIndexProjectionTest extends TestCase
|
||||
|
||||
self::assertStringContainsString('select group_concat(rf.name', $sql);
|
||||
self::assertStringContainsString('where rf.releases_id = r.id', $sql);
|
||||
self::assertStringContainsString('select group_concat(ad.audioformat', $sql);
|
||||
self::assertStringContainsString('from release_subtitles rs', $sql);
|
||||
self::assertStringContainsString('left join (select "selected_media_info"."releases_id"', $sql);
|
||||
self::assertStringContainsString('select min(candidate_media_info.id)', $sql);
|
||||
self::assertStringContainsString('left join "video_data" as "vd"', $sql);
|
||||
self::assertStringNotContainsString('join "release_files"', $sql);
|
||||
self::assertStringNotContainsString('join "audio_data"', $sql);
|
||||
self::assertStringNotContainsString('join "release_subtitles"', $sql);
|
||||
self::assertStringNotContainsString(' group by ', $sql);
|
||||
self::assertStringContainsString('order by "r"."id" asc limit 5000', $sql);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function media_info_projection_selects_one_deterministic_row_per_release(): void
|
||||
{
|
||||
Schema::create('media_infos', static function (Blueprint $table): void {
|
||||
$table->id();
|
||||
$table->unsignedBigInteger('releases_id');
|
||||
$table->string('movie_name')->nullable();
|
||||
$table->string('file_name')->nullable();
|
||||
$table->string('unique_id')->nullable();
|
||||
});
|
||||
|
||||
try {
|
||||
DB::table('media_infos')->insert([
|
||||
[
|
||||
'id' => 10,
|
||||
'releases_id' => 1,
|
||||
'movie_name' => 'First movie',
|
||||
'file_name' => 'first.mkv',
|
||||
'unique_id' => 'first-id',
|
||||
],
|
||||
[
|
||||
'id' => 11,
|
||||
'releases_id' => 1,
|
||||
'movie_name' => 'Duplicate movie',
|
||||
'file_name' => 'duplicate.mkv',
|
||||
'unique_id' => 'duplicate-id',
|
||||
],
|
||||
]);
|
||||
|
||||
$method = (new ReflectionClass(ReleaseIndexProjection::class))->getMethod('mediaInfoQuery');
|
||||
$query = $method->invoke(null);
|
||||
self::assertInstanceOf(Builder::class, $query);
|
||||
$rows = $query->get();
|
||||
$row = (array) $rows->first();
|
||||
|
||||
self::assertCount(1, $rows);
|
||||
self::assertSame('First movie', $row['movie_name']);
|
||||
self::assertSame('first-id', $row['unique_id']);
|
||||
} finally {
|
||||
Schema::drop('media_infos');
|
||||
}
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function both_search_engines_bulk_populate_from_the_canonical_projection(): void
|
||||
{
|
||||
$commandSource = file_get_contents(__DIR__.'/../../../../app/Console/Commands/NntmuxPopulateSearchIndexes.php');
|
||||
|
||||
self::assertIsString($commandSource);
|
||||
self::assertGreaterThanOrEqual(2, substr_count($commandSource, 'ReleaseIndexProjection::query()->orderBy(\'r.id\')'));
|
||||
self::assertGreaterThanOrEqual(2, substr_count($commandSource, 'ReleaseSearchIndexDocument::normalize((array) $item)'));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user