mirror of
https://github.com/NNTmux/newznab-tmux.git
synced 2026-08-28 17:01:16 +00:00
Update anime releases categorization
This commit is contained in:
@@ -23,6 +23,9 @@ class GroupNameCategorizer extends AbstractCategorizer
|
||||
if (empty($groupName)) {
|
||||
return $this->noMatch();
|
||||
}
|
||||
if (preg_match('/alt\.binaries\..*anime/i', $groupName)) {
|
||||
return $this->matched(Category::TV_ANIME, 0.8, 'group_name_anime');
|
||||
}
|
||||
if (preg_match('/alt\.binaries\..*?(tv|hdtv|tvseries)/i', $groupName)) {
|
||||
return $this->matched(Category::TV_OTHER, 0.6, 'group_name_tv');
|
||||
}
|
||||
|
||||
@@ -17,6 +17,12 @@ class TvCategorizer extends AbstractCategorizer
|
||||
|
||||
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';
|
||||
|
||||
/**
|
||||
* Known anime release/fansub group tags. Matches when the tag appears
|
||||
* as a standalone token in the release name (bracketed, dotted, etc.).
|
||||
*/
|
||||
private const string ANIME_GROUPS_REGEX = '/(?:^|[.\-_ \[])(URANiME|ANiHLS|HaiKU|ANiURL|SkyAnime|Erai-raws|LostYears|Vodes|SubsPlease|Judas|Ember|EMBER|YuiSubs|ASW|Tsundere-Raws|Anime-Raws|Kekkan|SSA|MoyaiSubs|QCE|Nyanpasu|UCCUSS|NS|DKB|MTBB|NanDesuKa|smol|Cleo|ToshY|Kawaiika-Raws|Beatrice-Raws|Reaktor|FLE|Commie|HorribleSubs|Doki|FFF|UTW|Coalgirls|CoalGuys|Mezashite|Vivid|Kaylith|Underwater|gg|Chihiro|Hatsuyuki|Mazui|WhyNot|Migoto|Doutei|GJM|Asenshi|Inka-Subs|Akatsuki)(?:[.\-_ \]]|$)/i';
|
||||
|
||||
protected int $priority = 20;
|
||||
|
||||
public function getName(): string
|
||||
@@ -33,6 +39,13 @@ class TvCategorizer extends AbstractCategorizer
|
||||
{
|
||||
$name = $context->releaseName;
|
||||
|
||||
// High-signal anime checks first. These can match release names that
|
||||
// don't follow standard TV S01E01 naming conventions (e.g. anime
|
||||
// fansub releases like "[Group] Show - 08 [1080p]").
|
||||
if ($result = $this->checkAnimeStrongSignals($name, $context)) {
|
||||
return $result;
|
||||
}
|
||||
|
||||
if (! $this->looksLikeTV($name)) {
|
||||
return $this->noMatch();
|
||||
}
|
||||
@@ -106,24 +119,51 @@ class TvCategorizer extends AbstractCategorizer
|
||||
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)) {
|
||||
if (preg_match(self::ANIME_GROUPS_REGEX, $name)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Strong, gate-bypassing anime signals: Anime Tosho poster, anime usenet
|
||||
* groups, and anime hash patterns. These can match even when the release
|
||||
* name doesn't look like a typical TV release.
|
||||
*/
|
||||
protected function checkAnimeStrongSignals(string $name, ReleaseContext $context): ?CategorizationResult
|
||||
{
|
||||
// Anime Tosho poster (very strong signal)
|
||||
if ($context->poster !== '' && preg_match('/animetosho\.org/i', $context->poster)) {
|
||||
return $this->matched(Category::TV_ANIME, 0.98, 'anime_tosho_poster');
|
||||
}
|
||||
// Usenet group dedicated to anime (e.g. alt.binaries.*anime*)
|
||||
if ($context->groupName !== '' && preg_match('/alt\.binaries\..*anime/i', $context->groupName)) {
|
||||
return $this->matched(Category::TV_ANIME, 0.9, 'anime_group_name');
|
||||
}
|
||||
// Known anime fansub/release group tag
|
||||
if (preg_match(self::ANIME_GROUPS_REGEX, $name)) {
|
||||
return $this->matched(Category::TV_ANIME, 0.92, 'anime_group');
|
||||
}
|
||||
// Anime hash pattern: [GroupName] Title - 01 [ABCD1234]
|
||||
if (preg_match('/^\[.+\].*\d{2,3}.*\[[a-fA-F0-9]{8}\]/i', $name)) {
|
||||
return $this->matched(Category::TV_ANIME, 0.9, 'anime_hash');
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
protected function checkAnime(string $name, ReleaseContext $context): ?CategorizationResult
|
||||
{
|
||||
// Check for Anime Tosho poster
|
||||
if (preg_match('/animetosho\.org|usenet\.bot@animetosho\.org/i', $context->poster)) {
|
||||
if ($context->poster !== '' && preg_match('/animetosho\.org/i', $context->poster)) {
|
||||
return $this->matched(Category::TV_ANIME, 0.98, 'anime_tosho_poster');
|
||||
}
|
||||
if (preg_match('/[._ -]Anime[._ -]/i', $name)) {
|
||||
return $this->matched(Category::TV_ANIME, 0.95, 'anime_pattern');
|
||||
}
|
||||
// Known anime release groups - matches with brackets, dots, dashes, underscores, spaces, or at the start
|
||||
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)) {
|
||||
if (preg_match(self::ANIME_GROUPS_REGEX, $name)) {
|
||||
return $this->matched(Category::TV_ANIME, 0.95, 'anime_group');
|
||||
}
|
||||
// Anime hash pattern: [GroupName] Title - 01 [ABCD1234]
|
||||
|
||||
@@ -0,0 +1,101 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Tests\Unit;
|
||||
|
||||
use App\Models\Category;
|
||||
use App\Services\Categorization\Categorizers\GroupNameCategorizer;
|
||||
use App\Services\Categorization\Categorizers\TvCategorizer;
|
||||
use App\Services\Categorization\ReleaseContext;
|
||||
use PHPUnit\Framework\Attributes\DataProvider;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
final class CategorizeAnimeTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @return array<string, array{0: string, 1: string, 2: string}>
|
||||
*/
|
||||
public static function animeReleaseProvider(): array
|
||||
{
|
||||
return [
|
||||
'animetosho poster catches non-TV-looking release' => [
|
||||
'[SSA] Cestvs - The Roman Fighter - 08 [480p]',
|
||||
'alt.binaries.multimedia.anime.highspeed',
|
||||
'Anime Tosho <usenet.bot@animetosho.org>',
|
||||
],
|
||||
'animetosho poster catches subsplease release with hash' => [
|
||||
'[SubsPlease] Cestvs - The Roman Fighter - 08 (720p) [26BDA3C9]',
|
||||
'alt.binaries.multimedia.anime.highspeed',
|
||||
'Anime Tosho <usenet.bot@animetosho.org>',
|
||||
],
|
||||
'animetosho poster catches yuisubs nvenc release' => [
|
||||
'[YuiSubs] Cestvs - The Roman Fighter - 08 (NVENC H.265 1080p)',
|
||||
'alt.binaries.multimedia.anime.highspeed',
|
||||
'Anime Tosho <usenet.bot@animetosho.org>',
|
||||
],
|
||||
'animetosho poster catches nyanpasu hevc release' => [
|
||||
'[Nyanpasu] Super Cub - 09 [1080p][HEVC][19475939]',
|
||||
'alt.binaries.multimedia.anime.highspeed',
|
||||
'Anime Tosho <usenet.bot@animetosho.org>',
|
||||
],
|
||||
'anime group name alone is enough' => [
|
||||
'[QCE] Kami-tachi ni Hirowareta Otoko - 07',
|
||||
'alt.binaries.multimedia.anime.highspeed',
|
||||
'',
|
||||
],
|
||||
'known anime release group tag (SSA) without S/E' => [
|
||||
'[SSA] Hetalia World Stars - 10 [480p]',
|
||||
'',
|
||||
'',
|
||||
],
|
||||
'known anime release group tag (DKB) with media wrapper' => [
|
||||
'[DKB] Kami-tachi ni Hirowareta Otoko - S01E07 [1080p][H.265 10bit]',
|
||||
'',
|
||||
'',
|
||||
],
|
||||
'anime hash pattern alone' => [
|
||||
'[NS] Nanatsu No Taizai - Fundo No Shinpan - 21 - (1080p 10bit) [2C014CB1]',
|
||||
'',
|
||||
'',
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
#[DataProvider('animeReleaseProvider')]
|
||||
public function test_anime_releases_are_categorized_as_tv_anime(string $name, string $groupName, string $poster): void
|
||||
{
|
||||
$categorizer = new TvCategorizer;
|
||||
|
||||
$context = new ReleaseContext(
|
||||
releaseName: $name,
|
||||
groupId: 0,
|
||||
groupName: $groupName,
|
||||
poster: $poster,
|
||||
);
|
||||
|
||||
$result = $categorizer->categorize($context);
|
||||
|
||||
$this->assertTrue($result->isSuccessful(), "Expected anime match for: {$name}");
|
||||
$this->assertSame(Category::TV_ANIME, $result->categoryId, "Expected TV_ANIME for: {$name} (matched_by={$result->matchedBy})");
|
||||
}
|
||||
|
||||
public function test_group_name_categorizer_classifies_anime_usenet_group(): void
|
||||
{
|
||||
$categorizer = new GroupNameCategorizer;
|
||||
|
||||
$context = new ReleaseContext(
|
||||
releaseName: 'some.random.release.name',
|
||||
groupId: 0,
|
||||
groupName: 'alt.binaries.multimedia.anime.highspeed',
|
||||
poster: '',
|
||||
);
|
||||
|
||||
$result = $categorizer->categorize($context);
|
||||
|
||||
$this->assertTrue($result->isSuccessful());
|
||||
$this->assertSame(Category::TV_ANIME, $result->categoryId);
|
||||
$this->assertSame('group_name_anime', $result->matchedBy);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user