mirror of
https://github.com/NNTmux/newznab-tmux.git
synced 2026-08-28 17:01:16 +00:00
Fix phpstan discovered issues
This commit is contained in:
@@ -40,9 +40,9 @@ class NntmuxPopulateSteamApps extends Command
|
||||
|
||||
$this->info(sprintf(
|
||||
'Added %d new steam app(s), %d skipped, %d errors',
|
||||
$stats['inserted'], // @phpstan-ignore offsetAccess.notFound
|
||||
$stats['skipped'], // @phpstan-ignore offsetAccess.notFound
|
||||
$stats['errors'] // @phpstan-ignore offsetAccess.notFound
|
||||
$stats['inserted'],
|
||||
$stats['skipped'],
|
||||
$stats['errors']
|
||||
));
|
||||
} catch (\Exception $e) {
|
||||
$this->error($e->getMessage());
|
||||
|
||||
@@ -125,7 +125,7 @@ class NntmuxSearchDiag extends Command
|
||||
// ['data' => [...]] shape is retained as a fallback.
|
||||
$rows = [];
|
||||
foreach ($response as $key => $value) {
|
||||
if (\is_int($key) || (\is_string($key) && ctype_digit($key))) {
|
||||
if (\is_int($key) || ctype_digit($key)) {
|
||||
if (\is_array($value)) {
|
||||
$row = $value;
|
||||
if (! isset($row['id'])) {
|
||||
|
||||
@@ -172,7 +172,7 @@ class NntmuxSearchReconcile extends Command
|
||||
// The old `$response['data']` shape only exists for non-raw mode or pre-4.x clients.
|
||||
foreach ($response as $key => $value) {
|
||||
// Top-level numeric keys mirror row ids (single-column id projection).
|
||||
if (\is_int($key) || (\is_string($key) && ctype_digit($key))) {
|
||||
if (\is_int($key) || ctype_digit($key)) {
|
||||
$id = (int) $key;
|
||||
if (isset($requested[$id])) {
|
||||
$out[$id] = $id;
|
||||
|
||||
@@ -68,8 +68,8 @@ class AdminPaymentController extends BasePageController
|
||||
|
||||
$summaryTotals = [
|
||||
'tx_count' => (int) $summary->sum('tx_count'),
|
||||
'invoice_total' => (float) $summary->sum(fn ($r) => (float) $r->invoice_total),
|
||||
'value_total' => (float) $summary->sum(fn ($r) => (float) $r->value_total),
|
||||
'invoice_total' => (float) $summary->sum(fn (Payment $payment) => (float) $payment->getAttribute('invoice_total')),
|
||||
'value_total' => (float) $summary->sum(fn (Payment $payment) => (float) $payment->getAttribute('value_total')),
|
||||
];
|
||||
|
||||
$paymentStatuses = BtcPaymentController::paymentStatusesForAdminFilter();
|
||||
|
||||
@@ -101,7 +101,7 @@ class RegisterController extends Controller
|
||||
'email',
|
||||
'max:255',
|
||||
Rule::unique('users', 'email')->where(fn ($query) => $query->whereNull('deleted_at')),
|
||||
new ValidEmailDomain,
|
||||
app(ValidEmailDomain::class),
|
||||
],
|
||||
'password' => ['required', 'confirmed', Password::min(8)->letters()->mixedCase()->numbers()->symbols()->uncompromised()],
|
||||
], [
|
||||
|
||||
@@ -20,7 +20,7 @@ class BasePageController extends Controller
|
||||
/**
|
||||
* @var Collection<int, mixed>
|
||||
*/
|
||||
public Collection $settings; // @phpstan-ignore property.phpDocType, class.notFound, missingType.generics
|
||||
public Collection $settings;
|
||||
|
||||
public string $title = '';
|
||||
|
||||
|
||||
@@ -87,9 +87,9 @@ class ContentController extends BasePageController
|
||||
*
|
||||
* @return Collection<int, mixed>
|
||||
*/
|
||||
protected function getActiveContent(): Collection // @phpstan-ignore class.notFound, missingType.generics, return.phpDocType
|
||||
protected function getActiveContent(): Collection
|
||||
{
|
||||
return Content::active()->ordered()->get(); // @phpstan-ignore method.notFound
|
||||
return Content::active()->ordered()->get();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -97,11 +97,11 @@ class ContentController extends BasePageController
|
||||
*
|
||||
* @return Collection<int, mixed>
|
||||
*/
|
||||
protected function getAllButFront(): Collection // @phpstan-ignore class.notFound, missingType.generics, return.phpDocType
|
||||
protected function getAllButFront(): Collection
|
||||
{
|
||||
return Content::query()
|
||||
->where('id', '<>', 1)
|
||||
->ordered() // @phpstan-ignore method.notFound
|
||||
->ordered()
|
||||
->get();
|
||||
}
|
||||
|
||||
@@ -121,7 +121,7 @@ class ContentController extends BasePageController
|
||||
*
|
||||
* @return Collection<int, mixed>
|
||||
*/
|
||||
protected function getFrontPageContent(): Collection // @phpstan-ignore class.notFound, missingType.generics, return.phpDocType
|
||||
protected function getFrontPageContent(): Collection
|
||||
{
|
||||
return Content::frontPage()->get();
|
||||
}
|
||||
@@ -133,7 +133,7 @@ class ContentController extends BasePageController
|
||||
{
|
||||
return Content::active()
|
||||
->ofType(Content::TYPE_INDEX)
|
||||
->ordered() // @phpstan-ignore method.notFound
|
||||
->ordered()
|
||||
->first();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -142,6 +142,18 @@ class AnidbInfo extends Model
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get AniDB URL if anidbid exists.
|
||||
*/
|
||||
public function getAnidbUrl(): ?string
|
||||
{
|
||||
if (empty($this->anidbid)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return 'https://anidb.net/anime/'.$this->anidbid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get AniList URL if anilist_id exists.
|
||||
*/
|
||||
@@ -165,4 +177,34 @@ class AnidbInfo extends Model
|
||||
|
||||
return 'https://myanimelist.net/anime/'.$this->mal_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get MyAnimeList URL if mal_id exists.
|
||||
*/
|
||||
public function getMyAnimeListUrl(): ?string
|
||||
{
|
||||
return $this->getMalUrl();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get available external links keyed by provider.
|
||||
*
|
||||
* @return array<string, string>
|
||||
*/
|
||||
public function getExternalLinks(): array
|
||||
{
|
||||
return array_filter([
|
||||
'anidb' => $this->getAnidbUrl(),
|
||||
'anilist' => $this->getAnilistUrl(),
|
||||
'myanimelist' => $this->getMyAnimeListUrl(),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether at least one external link exists.
|
||||
*/
|
||||
public function hasExternalLinks(): bool
|
||||
{
|
||||
return $this->getExternalLinks() !== [];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -143,7 +143,7 @@ class Content extends Model
|
||||
{
|
||||
return $query->active() // @phpstan-ignore method.notFound
|
||||
->ofType(self::TYPE_INDEX)
|
||||
->ordered(); // @phpstan-ignore method.notFound
|
||||
->ordered();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -65,9 +65,9 @@ class ReleaseFile extends Model
|
||||
* @param Builder<self> $query
|
||||
* @return Builder<self>
|
||||
*/
|
||||
public function scopeNfoFiles(Builder $query): Builder // @phpstan-ignore missingType.generics, return.phpDocType
|
||||
public function scopeNfoFiles(Builder $query): Builder
|
||||
{
|
||||
return $query->where(function (Builder $q) { // @phpstan-ignore missingType.generics
|
||||
return $query->where(function (Builder $q) {
|
||||
$q->where('name', 'like', '%.nfo')
|
||||
->orWhere('name', 'like', '%.diz')
|
||||
->orWhere('name', 'like', '%.inf')
|
||||
@@ -82,7 +82,7 @@ class ReleaseFile extends Model
|
||||
* @param Builder<self> $query
|
||||
* @return Builder<self>
|
||||
*/
|
||||
public function scopeNfoFilesWithContent(Builder $query): Builder // @phpstan-ignore missingType.generics, return.phpDocType
|
||||
public function scopeNfoFilesWithContent(Builder $query): Builder
|
||||
{
|
||||
return $query->nfoFiles()->where('size', '>', 0);
|
||||
}
|
||||
|
||||
@@ -182,7 +182,7 @@ class ValidEmailDomain implements ValidationRule
|
||||
/**
|
||||
* Validate that the domain has proper DNS records
|
||||
*/
|
||||
private function validateDnsRecords(string $domain): bool
|
||||
protected function validateDnsRecords(string $domain): bool
|
||||
{
|
||||
// Check for MX records (primary email validation)
|
||||
if (@checkdnsrr($domain, 'MX')) {
|
||||
|
||||
@@ -63,7 +63,7 @@ class BinariesService
|
||||
private int $headersBlackListed = 0;
|
||||
|
||||
/**
|
||||
* @var array<string, mixed>
|
||||
* @var array<int, int|string>
|
||||
*/
|
||||
private array $headersReceived = [];
|
||||
|
||||
@@ -257,7 +257,7 @@ class BinariesService
|
||||
* @param int $first The oldest wanted header.
|
||||
* @param int $last The newest wanted header.
|
||||
* @param string $type Is this part repair or update or backfill?
|
||||
* @param array<string, mixed>|null $missingParts If we are running in part repair, the list of missing article numbers.
|
||||
* @param array<int, mixed>|null $missingParts If we are running in part repair, the list of missing article numbers.
|
||||
* @return array<string, mixed> Empty on failure.
|
||||
*
|
||||
* @throws \Exception
|
||||
@@ -381,7 +381,7 @@ class BinariesService
|
||||
}
|
||||
|
||||
// Calculate parts repaired
|
||||
$lastPartNumber = $missingParts[$missingCount - 1]->numberid; // @phpstan-ignore offsetAccess.notFound
|
||||
$lastPartNumber = $missingParts[$missingCount - 1]->numberid;
|
||||
$remainingCount = $this->missedPartHandler->getCount($groupArr['id'], $lastPartNumber);
|
||||
$partsRepaired = $missingCount - $remainingCount;
|
||||
|
||||
@@ -693,7 +693,7 @@ class BinariesService
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, mixed>
|
||||
* @return array<int, array<string, mixed>>
|
||||
*/
|
||||
private function downloadHeaders(bool $partRepair): ?array
|
||||
{
|
||||
@@ -736,8 +736,8 @@ class BinariesService
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $headersNotInserted
|
||||
* @param array<string, mixed> $parsedHeaders
|
||||
* @param array<int, int|string> $headersNotInserted
|
||||
* @param array<int, array<string, mixed>> $parsedHeaders
|
||||
*/
|
||||
private function handlePartRepairTracking(array $headersNotInserted, array $parsedHeaders): void
|
||||
{
|
||||
@@ -750,11 +750,11 @@ class BinariesService
|
||||
// Check for missing headers in range
|
||||
$expectedCount = $this->last - $this->first - $this->notYEnc - $this->headersBlackListed + 1;
|
||||
if ($expectedCount > \count($this->headersReceived)) {
|
||||
$rangeNotReceived = array_diff(range($this->first, $this->last), $this->headersReceived);
|
||||
$rangeNotReceived = array_values(array_diff(range($this->first, $this->last), $this->headersReceived));
|
||||
$notReceivedCount = \count($rangeNotReceived);
|
||||
|
||||
if ($notReceivedCount > 0) {
|
||||
$this->missedPartHandler->addMissingParts($rangeNotReceived, $this->groupMySQL['id']); // @phpstan-ignore argument.type
|
||||
$this->missedPartHandler->addMissingParts($rangeNotReceived, $this->groupMySQL['id']);
|
||||
|
||||
if ($this->config->echoCli) {
|
||||
cli()->alternate(
|
||||
@@ -766,14 +766,14 @@ class BinariesService
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $missingParts
|
||||
* @return array<string, mixed>
|
||||
* @param array<int, \stdClass> $missingParts
|
||||
* @return list<array{partfrom: mixed, partto: mixed, partlist: list<mixed>}>
|
||||
*/
|
||||
private function groupMissingPartsIntoRanges(array $missingParts): array
|
||||
{
|
||||
$ranges = [];
|
||||
$partList = [];
|
||||
$firstPart = $lastNum = $missingParts[0]->numberid; // @phpstan-ignore offsetAccess.notFound
|
||||
$firstPart = $lastNum = $missingParts[0]->numberid;
|
||||
|
||||
foreach ($missingParts as $part) {
|
||||
if (($part->numberid - $firstPart) > ($this->config->messageBuffer / 4)) {
|
||||
|
||||
@@ -140,6 +140,7 @@ final class CollectionHandler
|
||||
* Resolve collections for a chunk of headers with one bulk insert and one id lookup.
|
||||
*
|
||||
* @param array<int, array<string, mixed>> $headers
|
||||
* @param array<int, int> $totalFilesByIndex
|
||||
* @return array<int, int> Collection ids keyed by header index
|
||||
*/
|
||||
public function getOrCreateCollections(
|
||||
|
||||
@@ -34,10 +34,10 @@ final class HeaderParser
|
||||
/**
|
||||
* Parse and filter raw headers from NNTP.
|
||||
*
|
||||
* @param array<string, mixed> $headers Raw headers from NNTP
|
||||
* @param array<int, array<string, mixed>> $headers Raw headers from NNTP
|
||||
* @param string $groupName The newsgroup name
|
||||
* @param bool $partRepair Whether this is a part repair scan
|
||||
* @param array<string, mixed>|null $missingParts Missing part numbers if part repair
|
||||
* @param array<int, mixed>|null $missingParts Missing part numbers if part repair
|
||||
* @return array<string, mixed> Filtered and parsed headers with article info
|
||||
*/
|
||||
public function parse(
|
||||
@@ -137,7 +137,7 @@ final class HeaderParser
|
||||
/**
|
||||
* Extract highest and lowest article info from headers.
|
||||
*
|
||||
* @param array<string, mixed> $headers
|
||||
* @param array<int, array<string, mixed>> $headers
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function getArticleRange(array $headers): array
|
||||
|
||||
@@ -20,7 +20,7 @@ final class HeaderStorageService
|
||||
|
||||
private BinariesConfig $config;
|
||||
|
||||
/** @var array<int> Article numbers that failed to insert */
|
||||
/** @var array<int, int|string> Article numbers that failed to insert */
|
||||
private array $failedInserts = [];
|
||||
|
||||
public function __construct(
|
||||
@@ -41,10 +41,10 @@ final class HeaderStorageService
|
||||
/**
|
||||
* Store parsed headers to the database.
|
||||
*
|
||||
* @param array<string, mixed> $headers Parsed headers with 'matches' already populated
|
||||
* @param array<int, array<string, mixed>> $headers Parsed headers with 'matches' already populated
|
||||
* @param array<string, mixed> $groupMySQL Group info from database
|
||||
* @param bool $addToPartRepair Whether to track failed inserts
|
||||
* @return array<string, mixed> Article numbers that failed to insert
|
||||
* @return array<int, int|string> Article numbers that failed to insert
|
||||
*/
|
||||
public function store(array $headers, array $groupMySQL, bool $addToPartRepair = true): array
|
||||
{
|
||||
@@ -77,7 +77,7 @@ final class HeaderStorageService
|
||||
/**
|
||||
* Store one bounded header chunk inside its own transaction.
|
||||
*
|
||||
* @param array<string, mixed> $headers
|
||||
* @param array<int, array<string, mixed>> $headers
|
||||
* @param array<string, mixed> $groupMySQL
|
||||
*/
|
||||
private function storeChunk(array $headers, array $groupMySQL, bool $addToPartRepair): void
|
||||
@@ -87,16 +87,17 @@ final class HeaderStorageService
|
||||
$this->partHandler->reset();
|
||||
$this->partHandler->setAddToPartRepair($addToPartRepair);
|
||||
|
||||
$chunkNumbers = array_values(array_filter(array_map(
|
||||
static fn (array $header): mixed => $header['Number'] ?? null,
|
||||
$headers
|
||||
)));
|
||||
$chunkNumbers = [];
|
||||
foreach ($headers as $header) {
|
||||
if (isset($header['Number']) && (\is_int($header['Number']) || \is_string($header['Number']))) {
|
||||
$chunkNumbers[] = $header['Number'];
|
||||
}
|
||||
}
|
||||
|
||||
// Create transaction
|
||||
$transaction = new HeaderStorageTransaction(
|
||||
$this->collectionHandler,
|
||||
$this->binaryHandler,
|
||||
$this->partHandler
|
||||
$this->binaryHandler
|
||||
);
|
||||
|
||||
$transaction->begin();
|
||||
@@ -208,65 +209,11 @@ final class HeaderStorageService
|
||||
private function markHeaderFailed(array $header, HeaderStorageTransaction $transaction, bool $addToPartRepair): void
|
||||
{
|
||||
$transaction->markError();
|
||||
if ($addToPartRepair && isset($header['Number'])) {
|
||||
if ($addToPartRepair && isset($header['Number']) && (\is_int($header['Number']) || \is_string($header['Number']))) {
|
||||
$this->failedInserts[] = $header['Number'];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $groupMySQL
|
||||
* @param array<string, mixed> $header
|
||||
*/
|
||||
private function processHeader(array $header, array $groupMySQL, HeaderStorageTransaction $transaction): bool
|
||||
{
|
||||
// Get file count from subject
|
||||
$fileCount = $this->getFileCount($header['matches'][1]);
|
||||
if ($fileCount[1] === 0 && $fileCount[3] === 0) {
|
||||
$fileCount = $this->getFileCount($header['matches'][0]);
|
||||
}
|
||||
|
||||
$totalFiles = (int) $fileCount[3];
|
||||
$fileNumber = (int) $fileCount[1];
|
||||
|
||||
// Get or create collection
|
||||
$collectionId = $this->collectionHandler->getOrCreateCollection(
|
||||
$header,
|
||||
$groupMySQL['id'],
|
||||
$groupMySQL['name'],
|
||||
$totalFiles,
|
||||
$transaction->getBatchNoise()
|
||||
);
|
||||
|
||||
if ($collectionId === null) {
|
||||
$transaction->markError();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get or create binary
|
||||
$binaryId = $this->binaryHandler->getOrCreateBinary(
|
||||
$header,
|
||||
$collectionId,
|
||||
$groupMySQL['id'],
|
||||
$fileNumber
|
||||
);
|
||||
|
||||
if ($binaryId === null) {
|
||||
$transaction->markError();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Add part
|
||||
if (! $this->partHandler->addPart($binaryId, $header)) {
|
||||
$transaction->markError();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, int|string>
|
||||
*/
|
||||
|
||||
@@ -24,8 +24,7 @@ final class HeaderStorageTransaction
|
||||
|
||||
public function __construct(
|
||||
CollectionHandler $collectionHandler,
|
||||
BinaryHandler $binaryHandler,
|
||||
PartHandler $partHandler
|
||||
BinaryHandler $binaryHandler
|
||||
) {
|
||||
$this->collectionHandler = $collectionHandler;
|
||||
$this->binaryHandler = $binaryHandler;
|
||||
|
||||
@@ -29,7 +29,7 @@ final class MissedPartHandler
|
||||
/**
|
||||
* Add missing article numbers to the repair queue.
|
||||
*
|
||||
* @param array<string, mixed> $numbers
|
||||
* @param array<int, int|string> $numbers
|
||||
*/
|
||||
public function addMissingParts(array $numbers, int $groupId): void
|
||||
{
|
||||
@@ -49,7 +49,7 @@ final class MissedPartHandler
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $numbers
|
||||
* @param array<int, int|string> $numbers
|
||||
*/
|
||||
private function addMissingPartsSqlite(array $numbers, int $groupId): void
|
||||
{
|
||||
@@ -71,7 +71,7 @@ final class MissedPartHandler
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $numbers
|
||||
* @param array<int, int|string> $numbers
|
||||
*/
|
||||
private function addMissingPartsMysql(array $numbers, int $groupId): void
|
||||
{
|
||||
@@ -95,7 +95,7 @@ final class MissedPartHandler
|
||||
/**
|
||||
* Remove successfully repaired parts from the queue.
|
||||
*
|
||||
* @param array<string, mixed> $numbers
|
||||
* @param array<int, int|string> $numbers
|
||||
*/
|
||||
public function removeRepairedParts(array $numbers, int $groupId): void
|
||||
{
|
||||
@@ -120,7 +120,7 @@ final class MissedPartHandler
|
||||
/**
|
||||
* Get parts that need repair for a group.
|
||||
*
|
||||
* @return array<string, mixed> Array of missed parts
|
||||
* @return array<int, \stdClass> Array of missed parts
|
||||
*/
|
||||
public function getMissingParts(int $groupId): array
|
||||
{
|
||||
|
||||
@@ -45,7 +45,6 @@ class CategorizationPipeline
|
||||
?NzbSplitUnwrapper $nzbSplitUnwrapper = null,
|
||||
?ObfuscatedSubjectExtractor $obfuscatedSubjectExtractor = null
|
||||
) {
|
||||
/** @phpstan-ignore argument.templateType */
|
||||
$this->pipes = collect($pipes)
|
||||
->sortBy(fn (AbstractCategorizationPipe $p) => $p->getPriority());
|
||||
|
||||
|
||||
@@ -442,13 +442,13 @@ class IGDBService
|
||||
if ($isPublisher === true && $companyId) {
|
||||
$companyData = Company::find($companyId);
|
||||
if ($companyData) {
|
||||
$publishers[] = $companyData->name; // @phpstan-ignore property.notFound
|
||||
$publishers[] = $companyData->name;
|
||||
}
|
||||
}
|
||||
if ($isDeveloper === true && $companyId) {
|
||||
$companyData = Company::find($companyId);
|
||||
if ($companyData) {
|
||||
$developers[] = $companyData->name; // @phpstan-ignore property.notFound
|
||||
$developers[] = $companyData->name;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -119,7 +119,7 @@ class ReleaseBrowseService
|
||||
'groups_id' => $groupId,
|
||||
'password_allow_rar' => $this->passwordAllowRar(),
|
||||
'sort_field' => $indexSort,
|
||||
'sort_dir' => $orderBy[1] ?? 'desc', // @phpstan-ignore offsetAccess.notFound
|
||||
'sort_dir' => $orderBy[1] ?? 'desc',
|
||||
'try_fuzzy' => true,
|
||||
], (int) $num, (int) $start);
|
||||
$searchIndexIds = $filtered['ids'];
|
||||
|
||||
@@ -312,7 +312,7 @@ class SteamService
|
||||
/**
|
||||
* Populate the steam_apps table with the full app list from Steam.
|
||||
*
|
||||
* @return list<array<string, mixed>>
|
||||
* @return array{inserted: int<0, max>, updated: int<0, max>, skipped: int<0, max>, errors: int<0, max>}
|
||||
*/
|
||||
public function populateSteamAppsTable(?callable $progressCallback = null): array
|
||||
{
|
||||
@@ -397,13 +397,13 @@ class SteamService
|
||||
/**
|
||||
* Get the full list of Steam apps.
|
||||
*
|
||||
* @return array{inserted: int<0, max>, updated: 0, skipped: int<0, max>, errors: int<0, max>}
|
||||
* @return list<array<string, mixed>>
|
||||
*/
|
||||
public function getFullAppList(): array
|
||||
{
|
||||
$cacheKey = 'steam_full_app_list';
|
||||
$cached = Cache::get($cacheKey);
|
||||
if ($cached !== null) {
|
||||
if (is_array($cached)) {
|
||||
return $cached;
|
||||
}
|
||||
|
||||
|
||||
@@ -275,7 +275,7 @@ class TmdbClient
|
||||
* Get TV show details by ID
|
||||
*
|
||||
* @param int|string $id The TMDB TV show ID
|
||||
* @param array<string, mixed> $appendToResponse Additional data to append
|
||||
* @param list<string> $appendToResponse Additional data to append
|
||||
* @return array<string, mixed>|null TV show data or null on failure
|
||||
*/
|
||||
public function getTvShow(int|string $id, array $appendToResponse = []): ?array
|
||||
|
||||
@@ -77,7 +77,7 @@ class TmdbPipe extends AbstractTvProviderPipe
|
||||
if ($videoId !== 0) {
|
||||
$siteId = $tmdb->getSiteByID('tmdb', (int) $videoId);
|
||||
// If show exists in local DB with a TMDB ID, use it directly
|
||||
if ($siteId !== false && $siteId !== 0) { // @phpstan-ignore notIdentical.alwaysTrue
|
||||
if ($siteId !== false && $siteId !== 0) {
|
||||
$this->outputFoundInDb($cleanName);
|
||||
} else {
|
||||
// Show exists in local DB but without TMDB ID (from another source)
|
||||
|
||||
@@ -78,7 +78,7 @@ class TvMazePipe extends AbstractTvProviderPipe
|
||||
$siteId = $tvmaze->getSiteByID('tvmaze', (int) $videoId);
|
||||
// If show exists in local DB but doesn't have a TVMaze ID, use the existing video
|
||||
// and process episode matching without trying to search TVMaze API
|
||||
if ($siteId === false || $siteId === 0) { // @phpstan-ignore identical.alwaysFalse
|
||||
if ($siteId === false || $siteId === 0) {
|
||||
// Show exists in our DB (likely from another source like TMDB)
|
||||
// Skip TVMaze API search and proceed to episode matching
|
||||
$this->outputFoundInDb($cleanName);
|
||||
|
||||
@@ -80,7 +80,7 @@ class TvdbPipe extends AbstractTvProviderPipe
|
||||
$siteId = $tvdb->getSiteByID('tvdb', (int) $videoId);
|
||||
// If show exists in local DB but doesn't have a TVDB ID, use the existing video
|
||||
// and process episode matching without trying to search TVDB API
|
||||
if ($siteId === false || $siteId === 0) { // @phpstan-ignore identical.alwaysFalse
|
||||
if ($siteId === false || $siteId === 0) {
|
||||
// Show exists in our DB (likely from another source like TMDB)
|
||||
// Skip TVDB API search and proceed to episode matching
|
||||
$this->outputFoundInDb($cleanName);
|
||||
|
||||
@@ -51,7 +51,6 @@ class TvProcessingPipeline
|
||||
*/
|
||||
public function __construct(iterable $pipes = [], bool $echoOutput = true)
|
||||
{
|
||||
/** @phpstan-ignore argument.templateType */
|
||||
$this->pipes = collect($pipes)
|
||||
->sortBy(fn (AbstractTvProviderPipe $p) => $p->getPriority());
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ namespace App\Services\TvProcessing;
|
||||
use App\Models\Video;
|
||||
use App\Services\TmdbClient;
|
||||
use App\Services\TraktService;
|
||||
use App\Services\TvProcessing\Providers\AbstractTvProvider;
|
||||
use App\Services\TvProcessing\Providers\TmdbProvider;
|
||||
use App\Services\TvProcessing\Providers\TraktProvider;
|
||||
use App\Services\TvProcessing\Providers\TvdbProvider;
|
||||
@@ -139,10 +140,6 @@ class TvShowAdder
|
||||
return null;
|
||||
}
|
||||
|
||||
if (! is_object($extended)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// TvdbProvider::formatShowInfo() expects fields shaped like SearchResult
|
||||
// (tvdb_id, name, overview, first_air_time, aliases). The extended
|
||||
// endpoint returns SeriesExtendedRecord (camelCase). Adapt it.
|
||||
@@ -166,6 +163,9 @@ class TvShowAdder
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array{videoId: int, existed: bool, source: string, externalId: string, title: ?string}
|
||||
*/
|
||||
private function addViaTvdb(int $tvdbId, int $type): array
|
||||
{
|
||||
$provider = new TvdbProvider;
|
||||
@@ -206,6 +206,9 @@ class TvShowAdder
|
||||
return is_array($show) ? $show : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array{videoId: int, existed: bool, source: string, externalId: string, title: ?string}
|
||||
*/
|
||||
private function addViaTvMaze(int $tvmazeId, int $type): array
|
||||
{
|
||||
$provider = new TvMazeProvider;
|
||||
@@ -251,12 +254,16 @@ class TvShowAdder
|
||||
}
|
||||
$show['alternative_titles'] = $alternativeTitles;
|
||||
|
||||
$networks = TmdbClient::getArray($show, 'networks');
|
||||
$show['network'] = ! empty($networks[0]['name']) ? (string) $networks[0]['name'] : '';
|
||||
$networks = array_values(TmdbClient::getArray($show, 'networks'));
|
||||
$firstNetwork = $networks[0] ?? null;
|
||||
$show['network'] = is_array($firstNetwork) && ! empty($firstNetwork['name']) ? (string) $firstNetwork['name'] : '';
|
||||
|
||||
return $show;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array{videoId: int, existed: bool, source: string, externalId: string, title: ?string}
|
||||
*/
|
||||
private function addViaTmdb(int $tmdbId, int $type): array
|
||||
{
|
||||
$provider = new TmdbProvider;
|
||||
@@ -290,6 +297,9 @@ class TvShowAdder
|
||||
return is_array($show) && ! empty($show['ids']) ? $show : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array{videoId: int, existed: bool, source: string, externalId: string, title: ?string}
|
||||
*/
|
||||
private function addViaTrakt(int|string $traktId, int $type): array
|
||||
{
|
||||
$provider = new TraktProvider;
|
||||
@@ -338,8 +348,10 @@ class TvShowAdder
|
||||
$trakt = app(TraktService::class);
|
||||
if ($trakt->isConfigured()) {
|
||||
$results = $trakt->searchById($imdbId, 'imdb', 'show');
|
||||
if (is_array($results) && ! empty($results[0]['show']['ids']['trakt'])) {
|
||||
$traktId = (int) $results[0]['show']['ids']['trakt'];
|
||||
$resultRows = is_array($results) ? array_values($results) : [];
|
||||
$firstResult = $resultRows[0] ?? null;
|
||||
if (is_array($firstResult) && ! empty($firstResult['show']['ids']['trakt'])) {
|
||||
$traktId = (int) $firstResult['show']['ids']['trakt'];
|
||||
$provider = new TraktProvider;
|
||||
$show = $this->fetchTrakt($traktId);
|
||||
if ($show !== null) {
|
||||
@@ -365,6 +377,9 @@ class TvShowAdder
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array{videoId: int, existed: bool, source: string, externalId: string, title: ?string}
|
||||
*/
|
||||
private function addViaImdb(string $imdbId, int $type): array
|
||||
{
|
||||
// TMDB
|
||||
@@ -388,8 +403,10 @@ class TvShowAdder
|
||||
$trakt = app(TraktService::class);
|
||||
if ($trakt->isConfigured()) {
|
||||
$results = $trakt->searchById($imdbId, 'imdb', 'show');
|
||||
if (is_array($results) && ! empty($results[0]['show']['ids']['trakt'])) {
|
||||
return $this->addViaTrakt((int) $results[0]['show']['ids']['trakt'], $type);
|
||||
$resultRows = is_array($results) ? array_values($results) : [];
|
||||
$firstResult = $resultRows[0] ?? null;
|
||||
if (is_array($firstResult) && ! empty($firstResult['show']['ids']['trakt'])) {
|
||||
return $this->addViaTrakt((int) $firstResult['show']['ids']['trakt'], $type);
|
||||
}
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
@@ -417,7 +434,7 @@ class TvShowAdder
|
||||
* @param array<string, mixed> $data
|
||||
* @return array{videoId: int, existed: bool, source: string, externalId: string, title: ?string}
|
||||
*/
|
||||
private function persist(object $provider, array $data, string $source, string $externalId): array
|
||||
private function persist(AbstractTvProvider $provider, array $data, string $source, string $externalId): array
|
||||
{
|
||||
// Ensure required keys formatShowInfo callers might omit
|
||||
$data += [
|
||||
@@ -442,10 +459,9 @@ class TvShowAdder
|
||||
throw new RuntimeException('Provider returned a show without a title.');
|
||||
}
|
||||
|
||||
/** @var int $videoId */
|
||||
$videoId = $provider->add($data); // @phpstan-ignore-line - AbstractTvProvider::add()
|
||||
$videoId = $provider->add($data);
|
||||
|
||||
if ($videoId > 0 && method_exists($provider, 'getPoster')) {
|
||||
if ($videoId > 0) {
|
||||
try {
|
||||
$provider->getPoster($videoId);
|
||||
} catch (\Throwable $e) {
|
||||
|
||||
@@ -65,7 +65,7 @@ class GlobalDataComposer
|
||||
$viewData['usefulLinks'] = $this->rememberWithCacheFallback('content_useful_links', self::CACHE_TTL, function () {
|
||||
return Content::active()
|
||||
->ofType(Content::TYPE_USEFUL)
|
||||
->ordered() // @phpstan-ignore method.notFound
|
||||
->ordered()
|
||||
->get();
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user