From dc9c9e3bb9c8a9081893a68c27df52aa466d2d91 Mon Sep 17 00:00:00 2001 From: DariusIII Date: Fri, 19 Dec 2025 17:06:51 +0100 Subject: [PATCH] Move tv processing into services --- Blacklight/Movie.php | 6 +- .../TvProcessing/Pipes/LocalDbPipe.php | 8 +- .../TvProcessing/Pipes/ParseInfoPipe.php | 8 +- app/Services/TvProcessing/Pipes/TmdbPipe.php | 8 +- app/Services/TvProcessing/Pipes/TraktPipe.php | 8 +- .../TvProcessing/Pipes/TvMazePipe.php | 8 +- app/Services/TvProcessing/Pipes/TvdbPipe.php | 8 +- .../Providers/AbstractTvProvider.php | 73 ++++++---------- .../Providers/BaseVideoProvider.php | 5 +- .../Providers/LocalDbProvider.php | 6 +- .../TvProcessing/Providers/TmdbProvider.php | 6 +- .../TvProcessing/Providers/TraktProvider.php | 8 +- .../TvProcessing/Providers/TvMazeProvider.php | 9 +- .../TvProcessing/Providers/TvdbProvider.php | 85 ++----------------- app/Services/TvProcessor.php | 20 ++--- misc/testing/Tests/test_tvdb_API.php | 4 +- misc/testing/Tests/test_tvmaze_API.php | 4 +- 17 files changed, 91 insertions(+), 183 deletions(-) rename Blacklight/processing/tv/TV.php => app/Services/TvProcessing/Providers/AbstractTvProvider.php (90%) mode change 100755 => 100644 rename Blacklight/processing/Videos.php => app/Services/TvProcessing/Providers/BaseVideoProvider.php (99%) mode change 100755 => 100644 rename Blacklight/processing/tv/LocalDB.php => app/Services/TvProcessing/Providers/LocalDbProvider.php (97%) rename Blacklight/processing/tv/TMDB.php => app/Services/TvProcessing/Providers/TmdbProvider.php (99%) rename Blacklight/processing/tv/TraktTv.php => app/Services/TvProcessing/Providers/TraktProvider.php (98%) mode change 100755 => 100644 rename Blacklight/processing/tv/TVMaze.php => app/Services/TvProcessing/Providers/TvMazeProvider.php (98%) rename Blacklight/processing/tv/TVDB.php => app/Services/TvProcessing/Providers/TvdbProvider.php (84%) mode change 100755 => 100644 diff --git a/Blacklight/Movie.php b/Blacklight/Movie.php index 0b57d960e..ac2da55d9 100755 --- a/Blacklight/Movie.php +++ b/Blacklight/Movie.php @@ -10,7 +10,7 @@ use App\Models\Settings; use App\Services\FanartTvService; use App\Services\ImdbScraper; use App\Services\TmdbClient; -use Blacklight\processing\tv\TraktTv; +use App\Services\TvProcessing\Providers\TraktProvider; use Blacklight\utility\Utility; use GuzzleHttp\Client; use GuzzleHttp\Exception\GuzzleException; @@ -90,7 +90,7 @@ class Movie public string $service; - public TraktTv $traktTv; + public TraktProvider $traktTv; public ?OMDbAPI $omdbApi; @@ -111,7 +111,7 @@ class Movie $this->colorCli = new ColorCLI; $this->traktcheck = config('nntmux_api.trakttv_api_key'); if ($this->traktcheck !== null) { - $this->traktTv = new TraktTv(['Settings' => null]); + $this->traktTv = new TraktProvider(); } $this->client = new Client; $this->fanartapikey = config('nntmux_api.fanarttv_api_key'); diff --git a/app/Services/TvProcessing/Pipes/LocalDbPipe.php b/app/Services/TvProcessing/Pipes/LocalDbPipe.php index 9925512d6..d7b5654c3 100644 --- a/app/Services/TvProcessing/Pipes/LocalDbPipe.php +++ b/app/Services/TvProcessing/Pipes/LocalDbPipe.php @@ -4,7 +4,7 @@ namespace App\Services\TvProcessing\Pipes; use App\Services\TvProcessing\TvProcessingPassable; use App\Services\TvProcessing\TvProcessingResult; -use Blacklight\processing\tv\LocalDB; +use App\Services\TvProcessing\Providers\LocalDbProvider; /** * Pipe for local database lookups. @@ -16,7 +16,7 @@ class LocalDbPipe extends AbstractTvProviderPipe private const TYPE_TV = 0; protected int $priority = 10; - private ?LocalDB $localDb = null; + private ?LocalDbProvider $localDb = null; public function getName(): string { @@ -31,10 +31,10 @@ class LocalDbPipe extends AbstractTvProviderPipe /** * Get or create the LocalDB instance. */ - private function getLocalDb(): LocalDB + private function getLocalDb(): LocalDbProvider { if ($this->localDb === null) { - $this->localDb = new LocalDB(); + $this->localDb = new LocalDbProvider(); } return $this->localDb; } diff --git a/app/Services/TvProcessing/Pipes/ParseInfoPipe.php b/app/Services/TvProcessing/Pipes/ParseInfoPipe.php index 83c1d4955..939d528ef 100644 --- a/app/Services/TvProcessing/Pipes/ParseInfoPipe.php +++ b/app/Services/TvProcessing/Pipes/ParseInfoPipe.php @@ -4,7 +4,7 @@ namespace App\Services\TvProcessing\Pipes; use App\Services\TvProcessing\TvProcessingPassable; use App\Services\TvProcessing\TvProcessingResult; -use Blacklight\processing\tv\LocalDB; +use App\Services\TvProcessing\Providers\LocalDbProvider; use Closure; /** @@ -14,7 +14,7 @@ use Closure; class ParseInfoPipe extends AbstractTvProviderPipe { protected int $priority = 1; - private ?LocalDB $localDb = null; + private ?LocalDbProvider $localDb = null; public function getName(): string { @@ -29,10 +29,10 @@ class ParseInfoPipe extends AbstractTvProviderPipe /** * Get or create the LocalDB instance for parsing. */ - private function getLocalDb(): LocalDB + private function getLocalDb(): LocalDbProvider { if ($this->localDb === null) { - $this->localDb = new LocalDB(); + $this->localDb = new LocalDbProvider(); } return $this->localDb; } diff --git a/app/Services/TvProcessing/Pipes/TmdbPipe.php b/app/Services/TvProcessing/Pipes/TmdbPipe.php index f51374789..3bc7fd294 100644 --- a/app/Services/TvProcessing/Pipes/TmdbPipe.php +++ b/app/Services/TvProcessing/Pipes/TmdbPipe.php @@ -4,7 +4,7 @@ namespace App\Services\TvProcessing\Pipes; use App\Services\TvProcessing\TvProcessingPassable; use App\Services\TvProcessing\TvProcessingResult; -use Blacklight\processing\tv\TMDB; +use App\Services\TvProcessing\Providers\TmdbProvider; /** * Pipe for TMDB API lookups. @@ -16,7 +16,7 @@ class TmdbPipe extends AbstractTvProviderPipe private const SOURCE_TMDB = 2; protected int $priority = 40; - private ?TMDB $tmdb = null; + private ?TmdbProvider $tmdb = null; public function getName(): string { @@ -31,10 +31,10 @@ class TmdbPipe extends AbstractTvProviderPipe /** * Get or create the TMDB instance. */ - private function getTmdb(): TMDB + private function getTmdb(): TmdbProvider { if ($this->tmdb === null) { - $this->tmdb = new TMDB(); + $this->tmdb = new TmdbProvider(); } return $this->tmdb; } diff --git a/app/Services/TvProcessing/Pipes/TraktPipe.php b/app/Services/TvProcessing/Pipes/TraktPipe.php index b053e0f3a..752f8a4c7 100644 --- a/app/Services/TvProcessing/Pipes/TraktPipe.php +++ b/app/Services/TvProcessing/Pipes/TraktPipe.php @@ -4,7 +4,7 @@ namespace App\Services\TvProcessing\Pipes; use App\Services\TvProcessing\TvProcessingPassable; use App\Services\TvProcessing\TvProcessingResult; -use Blacklight\processing\tv\TraktTv; +use App\Services\TvProcessing\Providers\TraktProvider; /** * Pipe for Trakt.tv API lookups. @@ -16,7 +16,7 @@ class TraktPipe extends AbstractTvProviderPipe private const SOURCE_TRAKT = 5; protected int $priority = 50; - private ?TraktTv $trakt = null; + private ?TraktProvider $trakt = null; public function getName(): string { @@ -31,10 +31,10 @@ class TraktPipe extends AbstractTvProviderPipe /** * Get or create the Trakt instance. */ - private function getTrakt(): TraktTv + private function getTrakt(): TraktProvider { if ($this->trakt === null) { - $this->trakt = new TraktTv(); + $this->trakt = new TraktProvider(); } return $this->trakt; } diff --git a/app/Services/TvProcessing/Pipes/TvMazePipe.php b/app/Services/TvProcessing/Pipes/TvMazePipe.php index eb6e2dec5..50f4f62a1 100644 --- a/app/Services/TvProcessing/Pipes/TvMazePipe.php +++ b/app/Services/TvProcessing/Pipes/TvMazePipe.php @@ -4,7 +4,7 @@ namespace App\Services\TvProcessing\Pipes; use App\Services\TvProcessing\TvProcessingPassable; use App\Services\TvProcessing\TvProcessingResult; -use Blacklight\processing\tv\TVMaze; +use App\Services\TvProcessing\Providers\TvMazeProvider; /** * Pipe for TVMaze API lookups. @@ -16,7 +16,7 @@ class TvMazePipe extends AbstractTvProviderPipe private const SOURCE_TVMAZE = 4; protected int $priority = 30; - private ?TVMaze $tvmaze = null; + private ?TvMazeProvider $tvmaze = null; public function getName(): string { @@ -31,10 +31,10 @@ class TvMazePipe extends AbstractTvProviderPipe /** * Get or create the TVMaze instance. */ - private function getTvMaze(): TVMaze + private function getTvMaze(): TvMazeProvider { if ($this->tvmaze === null) { - $this->tvmaze = new TVMaze(); + $this->tvmaze = new TvMazeProvider(); } return $this->tvmaze; } diff --git a/app/Services/TvProcessing/Pipes/TvdbPipe.php b/app/Services/TvProcessing/Pipes/TvdbPipe.php index 2fffee225..a230fc5bf 100644 --- a/app/Services/TvProcessing/Pipes/TvdbPipe.php +++ b/app/Services/TvProcessing/Pipes/TvdbPipe.php @@ -5,7 +5,7 @@ namespace App\Services\TvProcessing\Pipes; use App\Services\FanartTvService; use App\Services\TvProcessing\TvProcessingPassable; use App\Services\TvProcessing\TvProcessingResult; -use Blacklight\processing\tv\TVDB; +use App\Services\TvProcessing\Providers\TvdbProvider; /** * Pipe for TVDB API lookups. @@ -16,7 +16,7 @@ class TvdbPipe extends AbstractTvProviderPipe private const TYPE_TV = 0; protected int $priority = 20; - private ?TVDB $tvdb = null; + private ?TvdbProvider $tvdb = null; private ?FanartTvService $fanart = null; public function getName(): string @@ -32,10 +32,10 @@ class TvdbPipe extends AbstractTvProviderPipe /** * Get or create the TVDB instance. */ - private function getTvdb(): TVDB + private function getTvdb(): TvdbProvider { if ($this->tvdb === null) { - $this->tvdb = new TVDB(); + $this->tvdb = new TvdbProvider(); } return $this->tvdb; } diff --git a/Blacklight/processing/tv/TV.php b/app/Services/TvProcessing/Providers/AbstractTvProvider.php old mode 100755 new mode 100644 similarity index 90% rename from Blacklight/processing/tv/TV.php rename to app/Services/TvProcessing/Providers/AbstractTvProvider.php index bafdeb476..7fad5bee2 --- a/Blacklight/processing/tv/TV.php +++ b/app/Services/TvProcessing/Providers/AbstractTvProvider.php @@ -1,6 +1,6 @@ parseSeasonEp($relname); // --- Post-parse correction for daily talk shows misclassified as Season = Year --- - // Some date-based releases like Show.2025.11.03.* wrongly match the 2009.E01 pattern and set season=2025, episode=11. - // If we detect a full YYYY.MM.DD (or YYYY-MM-DD / YYYY/MM/DD) date in the original release name and season is a 4-digit year - // we convert it to an airdate form (season=0, episode=0, airdate=YYYY-MM-DD) to allow later API season/episode resolution. if (isset($showInfo['season'], $showInfo['episode']) && ! isset($showInfo['airdate'])) { if (is_numeric($showInfo['season']) && (int) $showInfo['season'] >= 1900 && (int) $showInfo['season'] <= (int) date('Y') + 1) { - // Look for full date pattern. if (preg_match('/(?P(19|20)\d{2})[.\-\/](?P\d{2})[.\-\/](?P\d{2})/i', $relname, $dateHits)) { $year = (int) $dateHits['year']; $month = (int) $dateHits['month']; $day = (int) $dateHits['day']; - // Basic sanity checks for month/day ranges. if ($month >= 1 && $month <= 12 && $day >= 1 && $day <= 31) { $showInfo['airdate'] = sprintf('%04d-%02d-%02d', $year, $month, $day); $showInfo['season'] = 0; @@ -416,15 +409,12 @@ abstract class TV extends Videos } } } - // --- End correction --- if (isset($showInfo['season'], $showInfo['episode'])) { if (! isset($showInfo['airdate'])) { - // If year is present in the release name, add it to the cleaned name for title search if (preg_match('/[^a-z0-9](?P(19|20)(\d{2}))[^a-z0-9]/i', $relname, $yearMatch)) { $showInfo['cleanname'] .= ' ('.$yearMatch['year'].')'; } - // Check for multi episode release. if (\is_array($showInfo['episode'])) { $showInfo['episode'] = $showInfo['episode'][0]; } @@ -447,23 +437,15 @@ abstract class TV extends Videos $following = '[^a-z0-9]([(|\[]\w+[)|\]]\s)*?(\d\d-\d\d|\d{1,3}x\d{2,3}|\(?(19|20)\d{2}\)?|(480|720|1080|2160)[ip]|AAC2?|BD-?Rip|Blu-?Ray|D0?\d|DD5|DiVX|DLMux|DTS|DVD(-?Rip)?|E\d{2,3}|[HX][\-_. ]?26[45]|ITA(-ENG)?|HEVC|[HPS]DTV|PROPER|REPACK|Season|Episode|S\d+[^a-z0-9]?((E\d+)[abr]?)*|WEB[\-_. ]?(DL|Rip)|XViD)[^a-z0-9]?'; - // For names that don't start with the title. if (preg_match('/^([^a-z0-9]{2,}|(sample|proof|repost)-)(?P[\w .-]*?)'.$following.'/i', $relname, $hits)) { $showName = $hits['name']; } elseif (preg_match('/^(?P[\w+][\s\w\'._-]*?)'.$following.'/i', $relname, $hits)) { - // For names that start with the title. $showName = $hits['name']; } - // If we still have any of the words in $following, remove them. $showName = preg_replace('/'.$following.'/i', ' ', $showName); - // Remove leading date if present $showName = preg_replace('/^\d{6}/', '', $showName); - // Handle acronyms with dots (e.g., G.R.I.T.S, S.H.I.E.L.D, C.S.I) before removing dots. - // This converts "G.R.I.T.S" to "GRITS" instead of "G R I T S". $showName = $this->convertAcronyms($showName); - // Remove periods, underscored, anything between parenthesis. $showName = preg_replace('/\(.*?\)|[._]/i', ' ', $showName); - // Finally remove multiple spaces and trim leading spaces. $showName = trim(preg_replace('/\s{2,}/', ' ', $showName)); return $showName; @@ -471,17 +453,12 @@ abstract class TV extends Videos /** * Convert acronyms with dots to condensed form. - * E.g., "G.R.I.T.S" becomes "GRITS", "S.H.I.E.L.D" becomes "SHIELD". - * This prevents acronyms from being split into individual letters with spaces. */ private function convertAcronyms(string $str): string { - // Match acronyms: 2 or more single letters separated by dots (optionally ending with a dot) - // Pattern matches things like: G.R.I.T.S, S.H.I.E.L.D., C.S.I, N.C.I.S return preg_replace_callback( '/\b((?:[A-Za-z]\.){2,}[A-Za-z]?\.?)\b/', function ($matches) { - // Remove all dots from the acronym return str_replace('.', '', $matches[1]); }, $str @@ -495,10 +472,8 @@ abstract class TV extends Videos { $normalized = strtolower(trim($cleanName)); $aliases = [ - // Acronym-based show titles 'grits' => 'Girls Raised in the South', 'shield' => 'Agents of S.H.I.E.L.D.', - // Talk shows 'stephen colbert' => 'The Late Show with Stephen Colbert', 'late show with stephen colbert' => 'The Late Show with Stephen Colbert', 'late show stephen colbert' => 'The Late Show with Stephen Colbert', @@ -545,7 +520,7 @@ abstract class TV extends Videos $episodeArr['season'] = (int) $hits[2]; $episodeArr['episode'] = [(int) $hits[3], (int) $hits[4]]; } - // S01E0102 and S01E01E02 - lame no delimit numbering, regex would collide if there was ever 1000 ep season. + // S01E0102 and S01E01E02 elseif (preg_match('/^(.*?)[^a-z0-9]s(\d{2})[^a-z0-9]?e(\d{2})e?(\d{2})[^a-z0-9]?/i', $relname, $hits)) { $episodeArr['season'] = (int) $hits[2]; $episodeArr['episode'] = (int) $hits[3]; @@ -574,22 +549,21 @@ abstract class TV extends Videos elseif (preg_match('/^(.*?)[^a-z0-9](?P(19|20)(\d{2})[.\/-](\d{2})[.\/-](\d{2}))[^a-z0-9]?/i', $relname, $hits)) { $episodeArr['season'] = 0; $episodeArr['episode'] = 0; - $episodeArr['airdate'] = date('Y-m-d', strtotime(preg_replace('/[^0-9]/i', '/', $hits['airdate']))); // yyyy-mm-dd + $episodeArr['airdate'] = date('Y-m-d', strtotime(preg_replace('/[^0-9]/i', '/', $hits['airdate']))); } // 01.01.2009 elseif (preg_match('/^(.*?)[^a-z0-9](?P(\d{2})[^a-z0-9](\d{2})[^a-z0-9](19|20)(\d{2}))[^a-z0-9]?/i', $relname, $hits)) { $episodeArr['season'] = 0; $episodeArr['episode'] = 0; - $episodeArr['airdate'] = date('Y-m-d', strtotime(preg_replace('/[^0-9]/i', '/', $hits['airdate']))); // yyyy-mm-dd + $episodeArr['airdate'] = date('Y-m-d', strtotime(preg_replace('/[^0-9]/i', '/', $hits['airdate']))); } // 01.01.09 elseif (preg_match('/^(.*?)[^a-z0-9](\d{2})[^a-z0-9](\d{2})[^a-z0-9](\d{2})[^a-z0-9]?/i', $relname, $hits)) { - // Add extra logic to capture the proper YYYY year $year = ($hits[4] <= 99 && $hits[4] > 15) ? '19'.$hits[4] : '20'.$hits[4]; $airdate = $year.'/'.$hits[2].'/'.$hits[3]; $episodeArr['season'] = 0; $episodeArr['episode'] = 0; - $episodeArr['airdate'] = date('Y-m-d', strtotime($airdate)); // yyyy-mm-dd + $episodeArr['airdate'] = date('Y-m-d', strtotime($airdate)); } // 2009.E01 elseif (preg_match('/^(.*?)[^a-z0-9]20(\d{2})[^a-z0-9](\d{1,3})[^a-z0-9]?/i', $relname, $hits)) { @@ -606,7 +580,6 @@ abstract class TV extends Videos $episodeArr['season'] = 1; $episodeArr['episode'] = (int) $hits[2]; } - // Band.Of.Brothers.EP06.Bastogne.DVDRiP.XviD-DEiTY elseif (preg_match('/^(.*?)[^a-z0-9]EP?[^a-z0-9]?(\d{1,3})/i', $relname, $hits)) { $episodeArr['season'] = 1; @@ -626,7 +599,6 @@ abstract class TV extends Videos */ private function parseCountry(string $showName): string { - // Country or origin matching. if (preg_match('/[^a-z0-9](US|UK|AU|NZ|CA|NL|Canada|Australia|America|United[^a-z0-9]States|United[^a-z0-9]Kingdom)/i', $showName, $countryMatch)) { $currentCountry = strtolower($countryMatch[1]); if ($currentCountry === 'canada') { @@ -650,7 +622,6 @@ abstract class TV extends Videos /** * Supplementary to parseInfo * Cleans a derived local 'showname' for better matching probability - * Returns the cleaned string. */ public function cleanName(string $str): string { @@ -666,18 +637,17 @@ abstract class TV extends Videos $str = str_replace('&', 'and', $str); $str = preg_replace('/^(history|discovery) channel/i', '', $str); - $str = str_replace(['\'', ':', '!', '"', '#', '*', '’', ',', '(', ')', '?'], '', $str); + $str = str_replace(["'", ':', '!', '"', '#', '*', "'", ',', '(', ')', '?'], '', $str); $str = str_replace('$', 's', $str); $str = preg_replace('/\s{2,}/', ' ', $str); - $str = trim($str, '\"'); + $str = trim($str, '"'); return trim($str); } /** * Simple function that compares two strings of text - * Returns percentage of similarity. */ public function checkMatch($ourName, $scrapeName, $probability): float|int { @@ -690,13 +660,8 @@ abstract class TV extends Videos return 0; } - // - /** - * Convert 2012-24-07 to 2012-07-24, there is probably a better way. - * - * This shouldn't ever happen as I've never heard of a date starting with year being followed by day value. - * Could this be a mistake? i.e. trying to solve the mm-dd-yyyy/dd-mm-yyyy confusion into a yyyy-mm-dd? + * Convert 2012-24-07 to 2012-07-24 */ public function checkDate(bool|string|null $date): string { @@ -715,7 +680,6 @@ abstract class TV extends Videos /** * Checks API response returns have all REQUIRED attributes set - * Returns true or false. */ public function checkRequiredAttr($array, string $type): bool { @@ -735,7 +699,6 @@ abstract class TV extends Videos $required = ['name', 'season', 'number', 'airdate', 'summary']; break; case 'tmdbS': - // TMDB search results use 'name' for the primary title; prefer it over 'original_name' $required = ['id', 'name', 'overview', 'first_air_date', 'origin_country']; break; case 'tmdbE': @@ -745,7 +708,6 @@ abstract class TV extends Videos $required = ['title', 'ids', 'overview', 'first_aired', 'airs', 'country']; break; case 'traktE': - // Trakt uses 'number' for the episode number in episode summaries $required = ['title', 'season', 'number', 'overview', 'first_aired']; break; } @@ -762,4 +724,17 @@ abstract class TV extends Videos return true; } + + /** + * Truncates title for display. + */ + protected function truncateTitle(string $title, int $maxLength = 45): string + { + if (mb_strlen($title) <= $maxLength) { + return $title; + } + + return mb_substr($title, 0, $maxLength - 3).'...'; + } } + diff --git a/Blacklight/processing/Videos.php b/app/Services/TvProcessing/Providers/BaseVideoProvider.php old mode 100755 new mode 100644 similarity index 99% rename from Blacklight/processing/Videos.php rename to app/Services/TvProcessing/Providers/BaseVideoProvider.php index c58ae13fe..d9e7dade3 --- a/Blacklight/processing/Videos.php +++ b/app/Services/TvProcessing/Providers/BaseVideoProvider.php @@ -21,7 +21,7 @@ * @copyright 2015 nZEDb */ -namespace Blacklight\processing; +namespace App\Services\TvProcessing\Providers; use App\Models\TvInfo; use App\Models\Video; @@ -31,7 +31,7 @@ use Illuminate\Support\Facades\Cache; /** * Parent class for TV/Film and any similar classes to inherit from. */ -abstract class Videos +abstract class BaseVideoProvider { // Video Type Identifiers protected const TYPE_TV = 0; // Type of video is a TV Programme/Show @@ -333,3 +333,4 @@ abstract class Videos return $return->isEmpty() ? false : $return; } } + diff --git a/Blacklight/processing/tv/LocalDB.php b/app/Services/TvProcessing/Providers/LocalDbProvider.php similarity index 97% rename from Blacklight/processing/tv/LocalDB.php rename to app/Services/TvProcessing/Providers/LocalDbProvider.php index e98bc9694..5907cc711 100644 --- a/Blacklight/processing/tv/LocalDB.php +++ b/app/Services/TvProcessing/Providers/LocalDbProvider.php @@ -1,14 +1,14 @@ posterUrl = ''; - // Clean the show name for better match probability $release = $this->parseInfo($row['searchname']); if (\is_array($release) && $release['name'] !== '') { if (\in_array($release['cleanname'], $this->titleCache, false)) { @@ -92,25 +89,21 @@ class TVDB extends TV } $this->setVideoNotFound(parent::PROCESS_TVMAZE, $row['id']); $skipped++; - continue; } - // Find the Video ID if it already exists by checking the title. $videoId = $this->getByTitle($release['cleanname'], parent::TYPE_TV); if ($videoId !== 0) { $siteId = $this->getSiteByID('tvdb', $videoId); } - // Force local lookup only $lookupSetting = true; if ($local === true || $this->local) { $lookupSetting = false; } if ($siteId === false && $lookupSetting) { - // If it doesn't exist locally and lookups are allowed lets try to get it. if ($this->echooutput) { $this->colorCli->primaryOver(' → '); $this->colorCli->headerOver($this->truncateTitle($release['cleanname'])); @@ -118,14 +111,12 @@ class TVDB extends TV $this->colorCli->info('Searching TVDB...'); } - // Check if we have a valid country and set it in the array $country = ( isset($release['country']) && \strlen($release['country']) === 2 ? (string) $release['country'] : '' ); - // Get the show from TVDB $tvdbShow = $this->getShowInfo((string) $release['cleanname']); if (\is_array($tvdbShow)) { @@ -141,9 +132,9 @@ class TVDB extends TV } if ((int) $videoId > 0 && (int) $siteId > 0) { - if (! empty($tvdbShow['poster'])) { // Use TVDB poster if available + if (! empty($tvdbShow['poster'])) { $this->getPoster($videoId); - } elseif ($this->fanart->isConfigured()) { // Check Fanart.tv for poster + } elseif ($this->fanart->isConfigured()) { $posterUrl = $this->fanart->getBestTvPoster($siteId); if (! empty($posterUrl)) { $this->posterUrl = $posterUrl; @@ -156,7 +147,6 @@ class TVDB extends TV $hasAirdate = ! empty($release['airdate']); if ($episodeNo === 'all') { - // Set the video ID and leave episode 0 $this->setVideoIdFound($videoId, $row['id'], 0); if ($this->echooutput) { $this->colorCli->primaryOver(' → '); @@ -165,42 +155,30 @@ class TVDB extends TV $this->colorCli->primary('Full Season matched'); } $matched++; - continue; } - // Download all episodes if new show to reduce API/bandwidth usage if (! $this->countEpsByVideoID($videoId)) { $this->getEpisodeInfo($siteId, -1, -1, $videoId); } - // Check if we have the episode for this video ID $episode = $this->getBySeasonEp($videoId, $seriesNo, $episodeNo, $release['airdate']); if ($episode === false && $lookupSetting) { if ($seriesNo !== '' && $episodeNo !== '') { - // Send the request for the episode to TVDB using season/episode numbers - $tvdbEpisode = $this->getEpisodeInfo( - $siteId, - (int) $seriesNo, - (int) $episodeNo, - $videoId - ); - + $tvdbEpisode = $this->getEpisodeInfo($siteId, (int) $seriesNo, (int) $episodeNo, $videoId); if ($tvdbEpisode) { $episode = $this->addEpisode($videoId, $tvdbEpisode); } } if ($episode === false && $hasAirdate) { - // Refresh episode cache and attempt airdate match $this->getEpisodeInfo($siteId, -1, -1, $videoId); $episode = $this->getBySeasonEp($videoId, 0, 0, $release['airdate']); } } if ($episode !== false && is_numeric($episode) && $episode > 0) { - // Mark the releases video and episode IDs $this->setVideoIdFound($videoId, $row['id'], $episode); if ($this->echooutput) { $this->colorCli->primaryOver(' → '); @@ -219,7 +197,6 @@ class TVDB extends TV } $matched++; } else { - // Processing failed, set the episode ID to the next processing group $this->setVideoIdFound($videoId, $row['id'], 0); $this->setVideoNotFound(parent::PROCESS_TVMAZE, $row['id']); if ($this->echooutput) { @@ -234,7 +211,6 @@ class TVDB extends TV } } } else { - // Processing failed, set the episode ID to the next processing group $this->setVideoNotFound(parent::PROCESS_TVMAZE, $row['id']); $this->titleCache[] = $release['cleanname'] ?? null; if ($this->echooutput) { @@ -245,7 +221,6 @@ class TVDB extends TV } } } else { - // Parsing failed, take it out of the queue for examination $this->setVideoNotFound(parent::FAILED_PARSE, $row['id']); $this->titleCache[] = $release['cleanname'] ?? null; if ($this->echooutput) { @@ -259,7 +234,6 @@ class TVDB extends TV } } - // Display summary if ($this->echooutput && $matched > 0) { echo "\n"; $this->colorCli->primaryOver(' ✓ TVDB: '); @@ -267,31 +241,12 @@ class TVDB extends TV } } - /** - * Truncate title for display purposes. - */ - private function truncateTitle(string $title, int $maxLength = 45): string - { - if (mb_strlen($title) <= $maxLength) { - return $title; - } - - return mb_substr($title, 0, $maxLength - 3).'...'; - } - - /** - * Placeholder for Videos getBanner. - */ public function getBanner($videoID, $siteId): bool { return false; } /** - * Calls the API to perform initial show name match to TVDB title - * Returns a formatted array of show data or false if no match. - * - * * @throws UnauthorizedException * @throws ParseException * @throws ExceptionInterface @@ -318,22 +273,18 @@ class TVDB extends TV if (\is_array($response)) { foreach ($response as $show) { if ($this->checkRequiredAttr($show, 'tvdbS')) { - // Check for exact title match first and then terminate if found if (strtolower($show->name) === strtolower($name)) { $highest = $show; break; } - // Check each show title for similarity and then find the highest similar value $matchPercent = $this->checkMatch(strtolower($show->name), strtolower($name), self::MATCH_PROBABILITY); - // If new match has a higher percentage, set as new matched title if ($matchPercent > $highestMatch) { $highestMatch = $matchPercent; $highest = $show; } - // Check for show aliases and try match those too if (! empty($show->aliases)) { foreach ($show->aliases as $akaIndex => $akaName) { $aliasPercent = $this->checkMatch(strtolower($akaName), strtolower($name), self::MATCH_PROBABILITY); @@ -353,21 +304,13 @@ class TVDB extends TV return $return; } - /** - * Retrieves the poster art for the processed show. - * - * @param int $videoId -- the local Video ID - */ public function getPoster(int $videoId): int { $ri = new ReleaseImage; - $hasCover = 0; - // Try to get the Poster only if we have a non-empty URL if (! empty($this->posterUrl)) { $hasCover = $ri->saveImage($videoId, $this->posterUrl, $this->imgSavePath); - // Mark it retrieved if we saved an image if ($hasCover === 1) { $this->setCoverFound($videoId); } @@ -392,7 +335,6 @@ class TVDB extends TV } catch (ResourceNotFoundException $error) { return false; } catch (UnauthorizedException $error) { - try { $this->authorizeTvdb(); } catch (UnauthorizedException $error) { @@ -430,9 +372,6 @@ class TVDB extends TV } /** - * Assigns API show response values to a formatted array for insertion - * Returns the formatted array. - * * @throws ExceptionInterface * @throws ParseException */ @@ -440,13 +379,11 @@ class TVDB extends TV { try { $poster = $this->client->series()->artworks($show->tvdb_id); - // Grab the image with the highest score where type == 2 $poster = collect($poster)->where('type', 2)->sortByDesc('score')->first(); $this->posterUrl = ! empty($poster->image) ? $poster->image : ''; } catch (ResourceNotFoundException $e) { $this->colorCli->error('Poster image not found on TVDB'); } catch (UnauthorizedException $error) { - try { $this->authorizeTvdb(); } catch (UnauthorizedException $error) { @@ -482,10 +419,6 @@ class TVDB extends TV ]; } - /** - * Assigns API episode response values to a formatted array for insertion - * Returns the formatted array. - */ public function formatEpisodeInfo($episode): array { return [ @@ -500,10 +433,7 @@ class TVDB extends TV protected function authorizeTvdb(): void { - // Check if we can get the time for API status - // If we cant then we set local to true $this->token = ''; - // Check if we have the tvdb api key and user pin if (config('tvdb.api_key') === null || config('tvdb.user_pin') === null) { $this->colorCli->warning('TVDB API key or user pin not set. Running in local mode only!', true); $this->local = true; @@ -521,3 +451,4 @@ class TVDB extends TV } } } + diff --git a/app/Services/TvProcessor.php b/app/Services/TvProcessor.php index 93c531339..1aa33b980 100644 --- a/app/Services/TvProcessor.php +++ b/app/Services/TvProcessor.php @@ -4,12 +4,12 @@ namespace App\Services; use App\Models\Release; use App\Models\Settings; +use App\Services\TvProcessing\Providers\LocalDbProvider; +use App\Services\TvProcessing\Providers\TmdbProvider; +use App\Services\TvProcessing\Providers\TraktProvider; +use App\Services\TvProcessing\Providers\TvdbProvider; +use App\Services\TvProcessing\Providers\TvMazeProvider; use Blacklight\ColorCLI; -use Blacklight\processing\tv\LocalDB; -use Blacklight\processing\tv\TMDB; -use Blacklight\processing\tv\TraktTv; -use Blacklight\processing\tv\TVDB; -use Blacklight\processing\tv\TVMaze; class TvProcessor { @@ -179,11 +179,11 @@ class TvProcessor private function buildProviderPipeline(): array { return [ - ['name' => 'Local DB', 'factory' => static fn () => new LocalDB, 'status' => 0], - ['name' => 'TVDB', 'factory' => static fn () => new TVDB, 'status' => 0], - ['name' => 'TVMaze', 'factory' => static fn () => new TVMaze, 'status' => -1], - ['name' => 'TMDB', 'factory' => static fn () => new TMDB, 'status' => -2], - ['name' => 'Trakt', 'factory' => static fn () => new TraktTv, 'status' => -3], + ['name' => 'Local DB', 'factory' => static fn () => new LocalDbProvider, 'status' => 0], + ['name' => 'TVDB', 'factory' => static fn () => new TvdbProvider, 'status' => 0], + ['name' => 'TVMaze', 'factory' => static fn () => new TvMazeProvider, 'status' => -1], + ['name' => 'TMDB', 'factory' => static fn () => new TmdbProvider, 'status' => -2], + ['name' => 'Trakt', 'factory' => static fn () => new TraktProvider, 'status' => -3], ]; } diff --git a/misc/testing/Tests/test_tvdb_API.php b/misc/testing/Tests/test_tvdb_API.php index 5631378aa..53926de5a 100755 --- a/misc/testing/Tests/test_tvdb_API.php +++ b/misc/testing/Tests/test_tvdb_API.php @@ -2,10 +2,10 @@ require_once dirname(__DIR__, 3).DIRECTORY_SEPARATOR.'bootstrap/autoload.php'; -use Blacklight\processing\tv\TVDB; +use App\Services\TvProcessing\Providers\TvdbProvider; $c = new Blacklight\ColorCLI; -$tvDB = new TVDB; +$tvDB = new TvdbProvider; if (! empty($argv[1]) && isset($argv[2], $argv[3]) && is_numeric($argv[2]) && is_numeric($argv[3])) { // Test if your TvDB API key and configuration are working diff --git a/misc/testing/Tests/test_tvmaze_API.php b/misc/testing/Tests/test_tvmaze_API.php index f359fffc7..9679d7a95 100755 --- a/misc/testing/Tests/test_tvmaze_API.php +++ b/misc/testing/Tests/test_tvmaze_API.php @@ -2,10 +2,10 @@ require_once dirname(__DIR__, 3).DIRECTORY_SEPARATOR.'bootstrap/autoload.php'; -use Blacklight\processing\tv\TVMaze; +use App\Services\TvProcessing\Providers\TvMazeProvider; $c = new Blacklight\ColorCLI; -$tvmaze = new TVMaze; +$tvmaze = new TvMazeProvider; if (! empty($argv[1]) && is_numeric($argv[2]) && is_numeric($argv[3])) { // Test if your TVMaze API configuration is working