mirror of
https://github.com/NNTmux/newznab-tmux.git
synced 2026-08-29 01:08:56 +00:00
Move tv processing into services
This commit is contained in:
@@ -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');
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
Executable → Regular
+24
-49
@@ -1,6 +1,6 @@
|
||||
<?php
|
||||
|
||||
namespace Blacklight\processing\tv;
|
||||
namespace App\Services\TvProcessing\Providers;
|
||||
|
||||
use App\Models\Category;
|
||||
use App\Models\Release;
|
||||
@@ -9,16 +9,15 @@ use App\Models\TvEpisode;
|
||||
use App\Models\TvInfo;
|
||||
use App\Models\Video;
|
||||
use Blacklight\ColorCLI;
|
||||
use Blacklight\processing\Videos;
|
||||
use Blacklight\Releases;
|
||||
use Blacklight\utility\Country;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
/**
|
||||
* Class TV -- abstract extension of Videos
|
||||
* Class AbstractTvProvider -- abstract extension of BaseVideoProvider
|
||||
* Contains functions suitable for re-use in all TV scrapers.
|
||||
*/
|
||||
abstract class TV extends Videos
|
||||
abstract class AbstractTvProvider extends BaseVideoProvider
|
||||
{
|
||||
// Television Sources
|
||||
protected const SOURCE_NONE = 0; // No Scrape source
|
||||
@@ -71,8 +70,7 @@ abstract class TV extends Videos
|
||||
protected ColorCLI $colorCli;
|
||||
|
||||
/**
|
||||
* TV constructor.
|
||||
*
|
||||
* AbstractTvProvider constructor.
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
@@ -397,17 +395,12 @@ abstract class TV extends Videos
|
||||
$showInfo += $this->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<year>(19|20)\d{2})[.\-\/](?P<month>\d{2})[.\-\/](?P<day>\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<year>(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<name>[\w .-]*?)'.$following.'/i', $relname, $hits)) {
|
||||
$showName = $hits['name'];
|
||||
} elseif (preg_match('/^(?P<name>[\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<airdate>(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<airdate>(\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).'...';
|
||||
}
|
||||
}
|
||||
|
||||
Executable → Regular
+3
-2
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
+3
-3
@@ -1,14 +1,14 @@
|
||||
<?php
|
||||
|
||||
namespace Blacklight\processing\tv;
|
||||
namespace App\Services\TvProcessing\Providers;
|
||||
|
||||
use Blacklight\ReleaseImage;
|
||||
|
||||
/**
|
||||
* Class LocalDB -- performs local database lookups before hitting external APIs.
|
||||
* Class LocalDbProvider -- performs local database lookups before hitting external APIs.
|
||||
* This reduces unnecessary API calls by matching releases against already scraped data.
|
||||
*/
|
||||
class LocalDB extends TV
|
||||
class LocalDbProvider extends AbstractTvProvider
|
||||
{
|
||||
/**
|
||||
* Main processing director function for local database lookups.
|
||||
+3
-3
@@ -1,11 +1,11 @@
|
||||
<?php
|
||||
|
||||
namespace Blacklight\processing\tv;
|
||||
namespace App\Services\TvProcessing\Providers;
|
||||
|
||||
use App\Services\TmdbClient;
|
||||
use Blacklight\ReleaseImage;
|
||||
|
||||
class TMDB extends TV
|
||||
class TmdbProvider extends AbstractTvProvider
|
||||
{
|
||||
protected const MATCH_PROBABILITY = 75;
|
||||
|
||||
@@ -236,7 +236,7 @@ class TMDB extends TV
|
||||
/**
|
||||
* Truncate title for display purposes.
|
||||
*/
|
||||
private function truncateTitle(string $title, int $maxLength = 45): string
|
||||
protected function truncateTitle(string $title, int $maxLength = 45): string
|
||||
{
|
||||
if (mb_strlen($title) <= $maxLength) {
|
||||
return $title;
|
||||
Executable → Regular
+4
-4
@@ -1,17 +1,17 @@
|
||||
<?php
|
||||
|
||||
namespace Blacklight\processing\tv;
|
||||
namespace App\Services\TvProcessing\Providers;
|
||||
|
||||
use App\Services\TraktService;
|
||||
use Blacklight\ReleaseImage;
|
||||
use Illuminate\Support\Carbon;
|
||||
|
||||
/**
|
||||
* Class TraktTv.
|
||||
* Class TraktProvider.
|
||||
*
|
||||
* Process information retrieved from the Trakt API.
|
||||
*/
|
||||
class TraktTv extends TV
|
||||
class TraktProvider extends AbstractTvProvider
|
||||
{
|
||||
private const MATCH_PROBABILITY = 75;
|
||||
|
||||
@@ -219,7 +219,7 @@ class TraktTv extends TV
|
||||
/**
|
||||
* Truncate title for display purposes.
|
||||
*/
|
||||
private function truncateTitle(string $title, int $maxLength = 45): string
|
||||
protected function truncateTitle(string $title, int $maxLength = 45): string
|
||||
{
|
||||
if (mb_strlen($title) <= $maxLength) {
|
||||
return $title;
|
||||
+5
-4
@@ -1,16 +1,16 @@
|
||||
<?php
|
||||
|
||||
namespace Blacklight\processing\tv;
|
||||
namespace App\Services\TvProcessing\Providers;
|
||||
|
||||
use Blacklight\ReleaseImage;
|
||||
use DariusIII\TVMaze\TVMaze as Client;
|
||||
|
||||
/**
|
||||
* Class TVMaze.
|
||||
* Class TvMazeProvider.
|
||||
*
|
||||
* Process information retrieved from the TVMaze API.
|
||||
*/
|
||||
class TVMaze extends TV
|
||||
class TvMazeProvider extends AbstractTvProvider
|
||||
{
|
||||
private const MATCH_PROBABILITY = 75;
|
||||
|
||||
@@ -237,7 +237,7 @@ class TVMaze extends TV
|
||||
/**
|
||||
* Truncate title for display purposes.
|
||||
*/
|
||||
private function truncateTitle(string $title, int $maxLength = 45): string
|
||||
protected function truncateTitle(string $title, int $maxLength = 45): string
|
||||
{
|
||||
if (mb_strlen($title) <= $maxLength) {
|
||||
return $title;
|
||||
@@ -464,3 +464,4 @@ class TVMaze extends TV
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
Executable → Regular
+8
-77
@@ -1,6 +1,6 @@
|
||||
<?php
|
||||
|
||||
namespace Blacklight\processing\tv;
|
||||
namespace App\Services\TvProcessing\Providers;
|
||||
|
||||
use App\Services\FanartTvService;
|
||||
use Blacklight\ReleaseImage;
|
||||
@@ -11,9 +11,9 @@ use CanIHaveSomeCoffee\TheTVDbAPI\TheTVDbAPI;
|
||||
use Symfony\Component\Serializer\Exception\ExceptionInterface;
|
||||
|
||||
/**
|
||||
* Class TVDB -- functions used to post process releases against TVDB.
|
||||
* Class TvdbProvider -- functions used to post process releases against TVDB.
|
||||
*/
|
||||
class TVDB extends TV
|
||||
class TvdbProvider extends AbstractTvProvider
|
||||
{
|
||||
private const MATCH_PROBABILITY = 75;
|
||||
|
||||
@@ -39,8 +39,7 @@ class TVDB extends TV
|
||||
private mixed $fanartapikey;
|
||||
|
||||
/**
|
||||
* TVDB constructor.
|
||||
*
|
||||
* TvdbProvider constructor.
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
@@ -66,7 +65,6 @@ class TVDB extends TV
|
||||
$tvCount = \count($res);
|
||||
|
||||
if ($tvCount === 0) {
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -80,7 +78,6 @@ class TVDB extends TV
|
||||
$siteId = false;
|
||||
$this->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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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],
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user