From 01a96bbc43806c1c8da7b4f8a189e6b37b2072b1 Mon Sep 17 00:00:00 2001 From: DariusIII Date: Thu, 26 Feb 2026 14:26:07 +0100 Subject: [PATCH] Declare strict types in classes --- app/Console/Commands/BackfillGroup.php | 2 + .../Commands/BackfillUserActivityStats.php | 2 + app/Console/Commands/CaptchaStatus.php | 2 + app/Console/Commands/CleanNZB.php | 2 + app/Console/Commands/CollectStats.php | 2 + app/Console/Commands/CollectSystemMetrics.php | 2 + .../Commands/CreateManticoreIndexes.php | 2 + app/Console/Commands/CreateMediaIndexes.php | 2 + app/Console/Commands/DeleteReleases.php | 2 + .../Commands/DisableExpiredPromotions.php | 2 + app/Console/Commands/FetchMovieByImdb.php | 2 + app/Console/Commands/FetchSamples.php | 2 + .../Commands/FindSizeMismatchedReleases.php | 10 +- app/Console/Commands/FixReleaseNames.php | 2 + app/Console/Commands/GetArticleRange.php | 2 + app/Console/Commands/GroupsUpdate.php | 2 + app/Console/Commands/ImportNzbs.php | 2 + app/Console/Commands/InstallNntmux.php | 2 + app/Console/Commands/IrcScraperCommand.php | 2 + app/Console/Commands/ManageRoleStacking.php | 2 + app/Console/Commands/MatchPrefiles.php | 2 + app/Console/Commands/MigrateAnimeCovers.php | 2 + app/Console/Commands/NntmuxCheckIndex.php | 2 + .../Commands/NntmuxCreateESIndexes.php | 2 + .../Commands/NntmuxDeleteUnVerifiedUsers.php | 2 + app/Console/Commands/NntmuxESReindex.php | 2 + app/Console/Commands/NntmuxOffsetPopulate.php | 2 + app/Console/Commands/NntmuxOffsetWorker.php | 2 + app/Console/Commands/NntmuxOptimizeTables.php | 2 + app/Console/Commands/NntmuxPopulateAniDB.php | 2 + .../Commands/NntmuxPopulateSearchIndexes.php | 2 + .../Commands/NntmuxPopulateSteamApps.php | 2 + .../Commands/NntmuxPurgeDeletedAccounts.php | 2 + .../Commands/NntmuxRemoveBadReleases.php | 2 + app/Console/Commands/NntmuxResetDb.php | 7 +- .../Commands/NntmuxResetPostProcessing.php | 2 + app/Console/Commands/NntmuxResetTruncate.php | 2 + .../NntmuxResetTvShowPostProcessing.php | 2 + .../Commands/NntmuxUpdateExpiredRoles.php | 2 + app/Console/Commands/PartRepair.php | 2 + .../Commands/PostProcessTvPipeline.php | 2 + app/Console/Commands/PredbCheck.php | 2 + .../Commands/ProcessAdditionalGuid.php | 2 + app/Console/Commands/ProcessBackfill.php | 2 + app/Console/Commands/ProcessBinaries.php | 2 + app/Console/Commands/ProcessFixRelNames.php | 2 + app/Console/Commands/ProcessPostProcess.php | 2 + app/Console/Commands/ProcessReleasesMulti.php | 2 + app/Console/Commands/ProcessSafe.php | 2 + .../Commands/ProcessUpdatePerGroup.php | 2 + app/Console/Commands/RecategorizeReleases.php | 2 + app/Console/Commands/RedisMonitor.php | 2 + app/Console/Commands/RefreshAnimeData.php | 2 + app/Console/Commands/RemoveCrapReleases.php | 2 + .../Commands/ReprocessMissingEpisodes.php | 2 + app/Console/Commands/ReprocessTvRelease.php | 2 + .../Commands/ReprocessUnmatchedTvReleases.php | 2 + app/Console/Commands/TestCategorization.php | 2 + app/Console/Commands/TmuxAttach.php | 2 + app/Console/Commands/TmuxHealthCheck.php | 2 + app/Console/Commands/TmuxMonitor.php | 2 + app/Console/Commands/TmuxStart.php | 2 + app/Console/Commands/TmuxStop.php | 2 + app/Console/Commands/UpdateBackfill.php | 2 + app/Console/Commands/UpdateGroupHeaders.php | 2 + app/Console/Commands/UpdateNNTmux.php | 2 + app/Console/Commands/UpdateNNTmuxComposer.php | 2 + app/Console/Commands/UpdateNNTmuxDB.php | 2 + app/Console/Commands/UpdateNNTmuxGit.php | 2 + .../Commands/UpdateReleasesIndexSchema.php | 2 + .../Commands/UpdateReleasesIndexSchemaES.php | 2 + app/Events/UserAccessedApi.php | 2 + app/Events/UserLoggedIn.php | 2 + app/Exceptions/Handler.php | 2 + app/Extensions/helper/helpers.php | 62 +-- app/Facades/Categorization.php | 2 + app/Facades/Elasticsearch.php | 2 + app/Facades/Search.php | 2 + app/Facades/TvProcessing.php | 2 + app/Facades/Yenc.php | 2 + .../Controllers/Admin/AdminAjaxController.php | 2 + .../Admin/AdminAnidbController.php | 2 + .../Admin/AdminBlacklistController.php | 2 + .../Controllers/Admin/AdminBookController.php | 2 + .../Admin/AdminCategoryController.php | 2 + .../Admin/AdminCategoryRegexesController.php | 2 + .../AdminCollectionRegexesController.php | 2 + .../Admin/AdminCommentsController.php | 2 + .../Admin/AdminConsoleController.php | 2 + .../Admin/AdminContentController.php | 4 +- .../Admin/AdminFailedReleasesController.php | 2 + .../Controllers/Admin/AdminGameController.php | 2 + .../Admin/AdminGroupController.php | 2 + .../Admin/AdminInvitationController.php | 2 + .../Admin/AdminMovieController.php | 2 + .../Admin/AdminMusicController.php | 2 + .../Controllers/Admin/AdminPageController.php | 2 + .../Admin/AdminPredbController.php | 2 + .../Admin/AdminPromotionController.php | 2 + .../AdminReleaseNamingRegexesController.php | 2 + .../Admin/AdminReleaseReportController.php | 2 + .../Admin/AdminReleasesController.php | 2 + .../Controllers/Admin/AdminRoleController.php | 2 + .../Admin/AdminShowsController.php | 2 + .../Controllers/Admin/AdminSiteController.php | 4 +- .../Controllers/Admin/AdminTmuxController.php | 2 + .../Controllers/Admin/AdminUserController.php | 4 +- .../Admin/AdminUserRoleHistoryController.php | 2 + .../Admin/DeletedUsersController.php | 2 + app/Http/Controllers/AdultController.php | 2 + app/Http/Controllers/AjaxController.php | 2 + app/Http/Controllers/Api/ApiController.php | 8 +- .../Controllers/Api/ApiInformController.php | 2 + app/Http/Controllers/Api/ApiV2Controller.php | 4 +- .../Controllers/Api/FileListApiController.php | 2 + app/Http/Controllers/Api/RSS.php | 2 + app/Http/Controllers/Api/XML_Response.php | 52 +- app/Http/Controllers/ApiHelpController.php | 2 + .../Auth/ForgotPasswordController.php | 2 + app/Http/Controllers/Auth/LoginController.php | 2 + .../Controllers/Auth/RegisterController.php | 2 + .../Auth/ResetPasswordController.php | 2 + app/Http/Controllers/BasePageController.php | 2 + app/Http/Controllers/BooksController.php | 8 +- app/Http/Controllers/BrowseController.php | 2 + .../Controllers/BrowseGroupController.php | 2 + app/Http/Controllers/BtcPaymentController.php | 2 + app/Http/Controllers/CartController.php | 2 + app/Http/Controllers/ConsoleController.php | 8 +- app/Http/Controllers/ContactUsController.php | 2 + app/Http/Controllers/ContentController.php | 2 + app/Http/Controllers/Controller.php | 2 + app/Http/Controllers/CoverController.php | 2 + app/Http/Controllers/DetailsController.php | 2 + .../Controllers/FailedReleasesController.php | 2 + app/Http/Controllers/GamesController.php | 8 +- app/Http/Controllers/GetNzbController.php | 36 +- app/Http/Controllers/InvitationController.php | 2 + app/Http/Controllers/MovieController.php | 8 +- app/Http/Controllers/MusicController.php | 8 +- app/Http/Controllers/MyMoviesController.php | 6 +- app/Http/Controllers/MyShowsController.php | 2 + app/Http/Controllers/NfoController.php | 2 + .../PasswordSecurityController.php | 2 + .../Controllers/PrivacyPolicyController.php | 2 + app/Http/Controllers/ProfileController.php | 2 + .../Controllers/ProfileSecurityController.php | 2 + .../Controllers/ReleaseReportController.php | 2 + app/Http/Controllers/RssController.php | 2 + app/Http/Controllers/SearchController.php | 15 +- .../Controllers/SearchSuggestController.php | 2 + app/Http/Controllers/SeriesController.php | 4 +- app/Http/Controllers/TermsController.php | 2 + app/Http/Middleware/BlockAbusiveServices.php | 2 + .../Middleware/CheckForMaintenanceMode.php | 2 + app/Http/Middleware/ClearanceMiddleware.php | 2 + app/Http/Middleware/ContentSecurityPolicy.php | 2 + app/Http/Middleware/ForceJsonOnAPI.php | 2 + app/Http/Middleware/Google2FAMiddleware.php | 2 + .../NoCacheForAuthenticatedUsers.php | 2 + app/Http/Middleware/SetUserTimezone.php | 2 + .../Middleware/TrustedDevice2FAMiddleware.php | 2 + app/Http/Requests/Auth/LoginLoginRequest.php | 2 + .../Requests/Auth/RegisterRegisterRequest.php | 2 + ...owLinkRequestFormForgotPasswordRequest.php | 2 + app/Http/Requests/ContactContactURequest.php | 2 + .../Disable2faPasswordSecurityRequest.php | 2 + app/Http/Resources/AnidbCollection.php | 2 + app/Http/Resources/AnidbResource.php | 2 + app/Http/Resources/BookCollection.php | 2 + app/Http/Resources/BookResource.php | 2 + app/Http/Resources/MovieCollection.php | 2 + app/Http/Resources/MovieResource.php | 2 + app/Jobs/PurgeDeletedAccounts.php | 2 + app/Jobs/RemoveInactiveAccounts.php | 2 + app/Jobs/SendAccountChangedEmail.php | 2 + app/Jobs/SendAccountDeletedEmail.php | 2 + app/Jobs/SendAccountExpiredEmail.php | 2 + app/Jobs/SendAccountWillExpireEmail.php | 2 + app/Jobs/SendContactUsEmail.php | 2 + app/Jobs/SendInviteEmail.php | 2 + app/Jobs/SendNewRegisteredAccountMail.php | 2 + app/Jobs/SendPasswordForgottenEmail.php | 2 + app/Jobs/SendPasswordResetEmail.php | 2 + app/Jobs/SendWelcomeEmail.php | 2 + app/Listeners/UpdateUserAccessedApi.php | 2 + app/Listeners/UpdateUserLoggedIn.php | 2 + app/Livewire/Actions/Logout.php | 2 + app/Livewire/Forms/LoginForm.php | 2 + app/Mail/AccountChange.php | 2 + app/Mail/AccountDeleted.php | 2 + app/Mail/AccountExpired.php | 2 + app/Mail/AccountWillExpire.php | 2 + app/Mail/ContactUs.php | 2 + app/Mail/ForgottenPassword.php | 2 + app/Mail/InvitationMail.php | 2 + app/Mail/NewAccountCreatedEmail.php | 2 + app/Mail/PasswordReset.php | 2 + app/Mail/SendInvite.php | 2 + app/Mail/WelcomeEmail.php | 2 + app/Models/AnidbEpisode.php | 2 + app/Models/AnidbInfo.php | 2 + app/Models/AnidbTitle.php | 2 + app/Models/AudioData.php | 2 + app/Models/Binary.php | 2 + app/Models/BinaryBlacklist.php | 2 + app/Models/BookInfo.php | 2 + app/Models/Category.php | 18 +- app/Models/CategoryRegex.php | 2 + app/Models/Collection.php | 2 + app/Models/CollectionRegex.php | 2 + app/Models/ConsoleInfo.php | 2 + app/Models/Content.php | 2 + app/Models/Country.php | 2 + app/Models/DnzbFailure.php | 2 + app/Models/DownloadStat.php | 2 + app/Models/Forumpost.php | 2 + app/Models/GamesInfo.php | 2 + app/Models/Genre.php | 2 + app/Models/GrabStat.php | 2 + app/Models/Invitation.php | 2 + app/Models/MediaInfo.php | 2 + app/Models/MissedPart.php | 2 + app/Models/MovieInfo.php | 2 + app/Models/MusicInfo.php | 2 + app/Models/ParHash.php | 2 + app/Models/Part.php | 2 + app/Models/PasswordSecurity.php | 2 + app/Models/Payment.php | 2 + app/Models/Predb.php | 2 + app/Models/PredbHash.php | 2 + app/Models/PredbImport.php | 2 + app/Models/Release.php | 2 + app/Models/ReleaseComment.php | 2 + app/Models/ReleaseFile.php | 2 + app/Models/ReleaseInform.php | 2 + app/Models/ReleaseNamingRegex.php | 2 + app/Models/ReleaseNfo.php | 2 + app/Models/ReleaseRegex.php | 2 + app/Models/ReleaseReport.php | 2 + app/Models/ReleaseSearchData.php | 2 + app/Models/ReleaseStat.php | 2 + app/Models/ReleaseSubtitle.php | 2 + app/Models/ReleaseUnique.php | 2 + app/Models/ReleasesGroups.php | 2 + app/Models/RoleExpirationEmail.php | 2 + app/Models/RolePromotion.php | 2 + app/Models/RolePromotionStat.php | 2 + app/Models/RoleStat.php | 2 + app/Models/RootCategory.php | 2 + app/Models/Settings.php | 4 +- app/Models/ShortGroup.php | 2 + app/Models/SignupStat.php | 2 + app/Models/SiteStat.php | 2 + app/Models/SteamApp.php | 2 + app/Models/SystemMetric.php | 2 + app/Models/TvEpisode.php | 2 + app/Models/TvInfo.php | 2 + app/Models/UsenetGroup.php | 2 + app/Models/User.php | 2 +- app/Models/UserActivity.php | 2 + app/Models/UserActivityStat.php | 2 + app/Models/UserDownload.php | 2 + app/Models/UserExcludedCategory.php | 2 + app/Models/UserMovie.php | 2 + app/Models/UserRequest.php | 2 + app/Models/UserRoleHistory.php | 2 + app/Models/UserSerie.php | 2 + app/Models/UsersRelease.php | 2 + app/Models/Video.php | 2 + app/Models/VideoAlias.php | 2 + app/Models/VideoData.php | 2 + app/Observers/MovieInfoObserver.php | 2 + app/Observers/ReleaseObserver.php | 2 + app/Observers/RolePromotionObserver.php | 2 + app/Observers/UserActivityObserver.php | 2 + app/Observers/UserServiceObserver.php | 2 + app/Observers/VideoObserver.php | 2 + app/Policies/CategoryPolicy.php | 2 + app/Policies/ForumPolicy.php | 2 + app/Policies/PostPolicy.php | 2 + app/Policies/ThreadPolicy.php | 2 + .../AdditionalProcessingServiceProvider.php | 2 + app/Providers/AppServiceProvider.php | 2 + .../CategorizationServiceProvider.php | 2 + app/Providers/ForumServiceProvider.php | 2 + app/Providers/HorizonServiceProvider.php | 2 + app/Providers/RouteServiceProvider.php | 2 + app/Providers/SearchServiceProvider.php | 2 + app/Providers/TelescopeServiceProvider.php | 2 + app/Providers/TvProcessingServiceProvider.php | 2 + app/Providers/UserServiceProvider.php | 2 + app/Providers/VoltServiceProvider.php | 2 + app/Rules/TurnstileRule.php | 2 + app/Rules/ValidEmailDomain.php | 2 + .../AdditionalProcessingOrchestrator.php | 2 + .../ArchiveExtractionService.php | 2 + .../Config/ProcessingConfiguration.php | 2 + .../ConsoleOutputService.php | 2 + .../DTO/ReleaseProcessingContext.php | 2 + .../MediaExtractionService.php | 2 + .../AdditionalProcessing/NzbContentParser.php | 2 + .../ReleaseFileManager.php | 2 + .../UsenetDownloadService.php | 2 + app/Services/AnidbService.php | 2 + app/Services/ArchiveProcessingService.php | 2 + app/Services/Binaries/BinaryHandler.php | 2 +- app/Services/BlacklistService.php | 2 + app/Services/BookService.php | 6 +- app/Services/BooksProcessor.php | 2 + .../Categorization/CategorizationPipeline.php | 2 + .../Categorization/CategorizationResult.php | 2 + .../Categorization/CategorizationService.php | 2 + .../Categorizers/AbstractCategorizer.php | 2 + .../Categorizers/BookCategorizer.php | 2 + .../Categorizers/ConsoleCategorizer.php | 2 + .../Categorizers/GroupNameCategorizer.php | 2 + .../Categorizers/MiscCategorizer.php | 2 + .../Categorizers/MovieCategorizer.php | 2 + .../Categorizers/MusicCategorizer.php | 2 + .../Categorizers/PcCategorizer.php | 2 + .../Categorizers/TvCategorizer.php | 2 + .../Categorizers/XxxCategorizer.php | 2 + .../Contracts/CategorizerInterface.php | 2 + .../Pipes/AbstractCategorizationPipe.php | 2 + .../Categorization/Pipes/BookPipe.php | 2 + .../Pipes/CategorizationPassable.php | 2 + .../Categorization/Pipes/ConsolePipe.php | 2 + .../Categorization/Pipes/GroupNamePipe.php | 2 + .../Categorization/Pipes/MiscPipe.php | 2 + .../Categorization/Pipes/MoviePipe.php | 2 + .../Categorization/Pipes/MusicPipe.php | 2 + app/Services/Categorization/Pipes/PcPipe.php | 2 + app/Services/Categorization/Pipes/TvPipe.php | 2 + app/Services/Categorization/Pipes/XxxPipe.php | 2 + .../Categorization/ReleaseContext.php | 2 + app/Services/CollectionCleanupService.php | 2 + app/Services/ConsoleService.php | 4 +- app/Services/ConsolesProcessor.php | 2 + app/Services/FanartTvService.php | 2 + app/Services/ForkingService.php | 2 + app/Services/GamesProcessor.php | 2 + app/Services/GenreService.php | 2 + app/Services/IRCClient.php | 2 + app/Services/IRCScraper.php | 2 + app/Services/ImdbScraper.php | 2 + app/Services/InvitationService.php | 2 + app/Services/ItunesService.php | 2 + app/Services/MediaProcessingService.php | 2 + app/Services/MovieBrowseService.php | 2 + app/Services/MovieService.php | 85 ++- app/Services/MoviesProcessor.php | 2 + app/Services/MusicProcessor.php | 2 + app/Services/MusicService.php | 6 +- app/Services/NNTP/NNTPService.php | 10 +- app/Services/NfoProcessor.php | 2 + app/Services/Nzb/NzbService.php | 2 +- app/Services/Par2Processor.php | 2 + app/Services/PasswordBreachService.php | 2 + app/Services/RedisMonitorService.php | 2 + app/Services/ReleaseCreationService.php | 2 + app/Services/ReleaseExtraService.php | 2 + app/Services/ReleaseProcessingService.php | 63 ++- .../Releases/ReleaseBrowseService.php | 6 +- .../Releases/ReleaseManagementService.php | 2 + .../Releases/ReleaseSearchService.php | 38 +- app/Services/Runners/BackfillRunner.php | 2 + app/Services/Runners/BaseRunner.php | 2 + app/Services/Runners/BinariesRunner.php | 2 + app/Services/Runners/PostProcessRunner.php | 2 + app/Services/Runners/ReleasesRunner.php | 2 + .../Contracts/SearchDriverInterface.php | 2 + .../Contracts/SearchServiceInterface.php | 2 + .../Search/Drivers/ElasticSearchDriver.php | 2 + .../Search/Drivers/ManticoreSearchDriver.php | 2 + app/Services/Search/MediaSearchService.php | 2 + app/Services/Search/SearchService.php | 2 + app/Services/SystemMetricsService.php | 2 + app/Services/TempWorkspaceService.php | 2 + app/Services/TmdbClient.php | 13 + app/Services/Tmux/Scripts/showsleep.php | 2 + app/Services/Tmux/Tmux.php | 2 + app/Services/Tmux/TmuxLayoutBuilder.php | 2 + app/Services/Tmux/TmuxMonitorService.php | 14 +- app/Services/Tmux/TmuxOutput.php | 58 +- app/Services/Tmux/TmuxPaneManager.php | 2 + app/Services/Tmux/TmuxSessionManager.php | 2 + app/Services/Tmux/TmuxTaskRunner.php | 2 + app/Services/TraktService.php | 2 + app/Services/TurnstileService.php | 2 + .../Pipes/AbstractTvProviderPipe.php | 2 + .../TvProcessing/Pipes/LocalDbPipe.php | 4 +- .../TvProcessing/Pipes/ParseInfoPipe.php | 2 + app/Services/TvProcessing/Pipes/TmdbPipe.php | 8 +- app/Services/TvProcessing/Pipes/TraktPipe.php | 6 +- .../TvProcessing/Pipes/TvMazePipe.php | 8 +- app/Services/TvProcessing/Pipes/TvdbPipe.php | 8 +- .../Providers/AbstractTvProvider.php | 6 +- .../Providers/BaseVideoProvider.php | 8 +- .../Providers/LocalDbProvider.php | 2 + .../TvProcessing/Providers/TmdbProvider.php | 10 +- .../TvProcessing/Providers/TraktProvider.php | 8 +- .../TvProcessing/Providers/TvMazeProvider.php | 10 +- .../TvProcessing/Providers/TvdbProvider.php | 14 +- .../TvProcessing/TvProcessingPassable.php | 2 + .../TvProcessing/TvProcessingPipeline.php | 2 + .../TvProcessing/TvProcessingResult.php | 2 + .../TvProcessing/TvReleaseContext.php | 6 +- app/Services/TvProcessor.php | 2 + app/Services/UserStatsService.php | 2 + app/Services/XrefService.php | 2 + app/Services/YencService.php | 2 + app/Support/CaptchaHelper.php | 2 + app/Support/Google2FAAuthenticator.php | 2 + app/Support/UpdatePerformanceHelper.php | 2 + app/Transformers/ApiTransformer.php | 2 + app/Transformers/CategoryTransformer.php | 2 + app/Transformers/DetailsTransformer.php | 2 + app/View/Components/AppLayout.php | 2 + app/View/Components/GuestLayout.php | 2 + app/View/Components/SortDropdown.php | 2 + app/View/Composers/GlobalDataComposer.php | 2 + phpstan-baseline.neon | 6 - .../admin/site/tmux-edit-content.blade.php | 498 +++++++++++++++++ .../views/admin/site/tmux-edit.blade.php | 500 +----------------- 425 files changed, 1672 insertions(+), 767 deletions(-) create mode 100644 resources/views/admin/site/tmux-edit-content.blade.php diff --git a/app/Console/Commands/BackfillGroup.php b/app/Console/Commands/BackfillGroup.php index 794dee9a3..744ac8fb4 100644 --- a/app/Console/Commands/BackfillGroup.php +++ b/app/Console/Commands/BackfillGroup.php @@ -1,5 +1,7 @@ id, $release->searchname, - number_format($release->release_size, 2).' GiB', - number_format($release->files_total_size, 2).' GiB', - number_format($release->size_diff, 2).' GiB', - number_format($release->diff_percent, 2).'%', + number_format((float) ($release->release_size ?? 0), 2).' GiB', + number_format((float) ($release->files_total_size ?? 0), 2).' GiB', + number_format((float) ($release->size_diff ?? 0), 2).' GiB', + number_format((float) ($release->diff_percent ?? 0), 2).'%', ]; }); diff --git a/app/Console/Commands/FixReleaseNames.php b/app/Console/Commands/FixReleaseNames.php index 2c2f8c397..0819a1601 100644 --- a/app/Console/Commands/FixReleaseNames.php +++ b/app/Console/Commands/FixReleaseNames.php @@ -1,5 +1,7 @@ getPathname(); + if (basename($path) !== '.gitignore' && basename($path) !== 'no-cover.jpg' && basename($path) !== 'no-backdrop.jpg') { + File::delete($path); } } diff --git a/app/Console/Commands/NntmuxResetPostProcessing.php b/app/Console/Commands/NntmuxResetPostProcessing.php index fdc0616e5..b4551f6e7 100644 --- a/app/Console/Commands/NntmuxResetPostProcessing.php +++ b/app/Console/Commands/NntmuxResetPostProcessing.php @@ -1,5 +1,7 @@ |null $postData + * @return array|false|string */ - function getRawHtml(mixed $url, $cookie = false, mixed $postData = null) + function getRawHtml(string $url, bool|string|null $cookie = false, ?array $postData = null): array|false|string { // Check if this is an adult site that needs age verification $adultSites = [ @@ -69,11 +71,11 @@ if (! function_exists('getRawHtml')) { if (! function_exists('makeFieldLinks')) { /** - * @return string + * @param array|object $data Array or model (ArrayAccess) * * @throws Exception */ - function makeFieldLinks(mixed $data, mixed $field, mixed $type) + function makeFieldLinks(array|object $data, string $field, string $type): string { // Check if field exists and is not empty if (! isset($data[$field]) || empty($data[$field])) { @@ -100,10 +102,9 @@ if (! function_exists('makeFieldLinks')) { if (! function_exists('getUserBrowseOrder')) { /** - * @param string $orderBy - * @return array + * @return array{0: string, 1: string} */ - function getUserBrowseOrder($orderBy): array + function getUserBrowseOrder(string $orderBy): array { $order = ($orderBy === '' ? 'username_desc' : $orderBy); $orderArr = explode('_', $order); @@ -159,20 +160,14 @@ if (! function_exists('getUserBrowseOrdering')) { } if (! function_exists('getSimilarName')) { - /** - * @param string $name - */ - function getSimilarName($name): string + function getSimilarName(string $name): string { return implode(' ', \array_slice(str_word_count(str_replace(['.', '_', '-'], ' ', $name), 2), 0, 2)); } } if (! function_exists('human_filesize')) { - /** - * @param int $decimals - */ - function human_filesize(mixed $bytes, $decimals = 0): string + function human_filesize(int|float|string $bytes, int $decimals = 0): string { $size = ['B', 'kB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']; $factor = (int) floor((\strlen((string) $bytes) - 1) / 3); @@ -182,10 +177,7 @@ if (! function_exists('human_filesize')) { } if (! function_exists('bcdechex')) { - /** - * @return string - */ - function bcdechex(mixed $dec) + function bcdechex(string $dec): string { $hex = ''; do { @@ -201,13 +193,8 @@ if (! function_exists('bcdechex')) { if (! function_exists('runCmd')) { /** * Run CLI command. - * - * - * @param string $command - * @param bool $debug - * @return string */ - function runCmd($command, $debug = false) + function runCmd(string $command, bool $debug = false): string { if ($debug) { echo '-Running Command: '.PHP_EOL.' '.$command.PHP_EOL; @@ -231,7 +218,7 @@ if (! function_exists('imdb_id_pad')) { * Normalize IMDB ID to 8 zero-padded digits for storage in releases / join consistency. * Enables index use on releases.imdbid; call when writing to releases.imdbid. */ - function imdb_id_pad(mixed $id): string + function imdb_id_pad(int|string|null $id): string { if ($id === null || $id === '') { return '00000000'; @@ -281,7 +268,7 @@ if (! function_exists('is_it_json')) { if (! function_exists('getStreamingZip')) { /** - * @param array $guids + * @param list $guids * * @throws Exception */ @@ -450,9 +437,8 @@ if (! function_exists('getReleaseCover')) { * Get the cover image URL for a release based on its type and ID * * @param object|array $release The release object or array - * @return string The cover image URL or placeholder if no cover exists */ - function getReleaseCover($release): string + function getReleaseCover(object|array $release): string { $coverType = null; $coverId = null; @@ -545,13 +531,11 @@ if (! function_exists('sanitize')) { if (! function_exists('formatBytes')) { /** * Format bytes into human-readable file size. - * - * @param int|float|null $bytes */ - function formatBytes($bytes = 0): string + function formatBytes(int|float|null $bytes = 0): string { $units = ['B', 'KB', 'MB', 'GB', 'TB']; - $bytes = max((int) $bytes, 0); + $bytes = max((int) ($bytes ?? 0), 0); $pow = floor(($bytes ? log($bytes) : 0) / log(1024)); $pow = min($pow, count($units) - 1); @@ -675,10 +659,8 @@ if (! function_exists('getAvailableTimezones')) { if (! function_exists('countryCode')) { /** * Get a country code for a country name. - * - * @return mixed */ - function countryCode(string $country) + function countryCode(string $country): string { if (\strlen($country) > 2) { $code = CountryModel::whereFullName($country)->orWhere('name', $country)->first(['iso_3166_2']); @@ -694,10 +676,8 @@ if (! function_exists('countryCode')) { if (! function_exists('unzipGzipFile')) { /** * Unzip a gzip file, return the output. Return false on error / empty. - * - * @return bool|string */ - function unzipGzipFile(string $filePath) + function unzipGzipFile(string $filePath): false|string { $string = ''; $gzFile = @gzopen($filePath, 'rb', false); @@ -894,7 +874,7 @@ if (! function_exists('showApiError')) { } if (! function_exists('getRange')) { - function getRange(mixed $tableName): \Illuminate\Contracts\Pagination\LengthAwarePaginator // @phpstan-ignore missingType.generics + function getRange(string $tableName): \Illuminate\Contracts\Pagination\LengthAwarePaginator // @phpstan-ignore missingType.generics { $range = \Illuminate\Support\Facades\DB::table($tableName); diff --git a/app/Facades/Categorization.php b/app/Facades/Categorization.php index d0fce2f16..b2796a022 100644 --- a/app/Facades/Categorization.php +++ b/app/Facades/Categorization.php @@ -1,5 +1,7 @@ has('id')) { $meta_title = 'Content Edit'; - $id = $request->input('id'); + $id = (int) $request->input('id'); $content = $this->getContentById($id); } break; diff --git a/app/Http/Controllers/Admin/AdminFailedReleasesController.php b/app/Http/Controllers/Admin/AdminFailedReleasesController.php index 08d86cf3a..8a66c30ef 100644 --- a/app/Http/Controllers/Admin/AdminFailedReleasesController.php +++ b/app/Http/Controllers/Admin/AdminFailedReleasesController.php @@ -1,5 +1,7 @@ (int) trim($value), array_filter($books_selected)); diff --git a/app/Http/Controllers/Admin/AdminTmuxController.php b/app/Http/Controllers/Admin/AdminTmuxController.php index 0b1d2a045..942cade46 100644 --- a/app/Http/Controllers/Admin/AdminTmuxController.php +++ b/app/Http/Controllers/Admin/AdminTmuxController.php @@ -1,5 +1,7 @@ input('apikey'); // Cache user lookup for 5 minutes to avoid repeated DB hits (same pattern as API v2) - $userCacheKey = 'api_user:'.md5($apiKey); + $userCacheKey = 'api_user:'.md5((string) $apiKey); $res = Cache::remember($userCacheKey, 300, function () use ($apiKey) { return User::query() ->where('api_token', $apiKey) @@ -358,7 +360,7 @@ class ApiController extends BasePageController $nzbFile = $request->file('file'); // Save the file to the server, get the name without the extension. - if (File::isFile($nzbFile)) { + if ($nzbFile !== null) { // We need to check if file is an actual nzb file. if ($nzbFile->getClientOriginalExtension() !== 'nzb') { return response('File is not an NZB file', 400); @@ -379,7 +381,7 @@ class ApiController extends BasePageController Log::channel('nzb_upload')->warning('NZB file uploaded by API failed: '.$nzbFile->getClientOriginalName()); } else { - Log::channel('nzb_upload')->warning('NZB file uploaded by API failed: '.$nzbFile->getClientOriginalName()); + Log::channel('nzb_upload')->warning('NZB file uploaded by API failed: no file provided'); return response('NZB file upload failed', 500); } diff --git a/app/Http/Controllers/Api/ApiInformController.php b/app/Http/Controllers/Api/ApiInformController.php index 8fb3a7143..64fb85920 100644 --- a/app/Http/Controllers/Api/ApiInformController.php +++ b/app/Http/Controllers/Api/ApiInformController.php @@ -1,5 +1,7 @@ input('api_token'); - $userCacheKey = 'api_user:'.md5($apiToken); + $userCacheKey = 'api_user:'.md5((string) $apiToken); return Cache::remember($userCacheKey, 300, function () use ($apiToken) { return User::query() diff --git a/app/Http/Controllers/Api/FileListApiController.php b/app/Http/Controllers/Api/FileListApiController.php index 0875402f9..444a051b5 100644 --- a/app/Http/Controllers/Api/FileListApiController.php +++ b/app/Http/Controllers/Api/FileListApiController.php @@ -1,5 +1,7 @@ $serverUrl.'/details/'.$this->release->guid, 'link' => $serverUrl.'/getnzb?id='.$this->release->guid.'.nzb&r='.$this->parameters['token'].$delParam, 'comments' => $serverUrl.'/details/'.$this->release->guid.'#comments', - 'pubDate' => date(DATE_RSS, strtotime($this->release->adddate)), + 'pubDate' => date(DATE_RSS, strtotime((string) $this->release->adddate)), 'category' => $this->release->category_name, 'description' => $this->release->searchname, ]; @@ -396,9 +398,9 @@ class XML_Response { $this->xml->startDocument('1.0', 'UTF-8'); $this->xml->startElement('register'); - $this->xml->writeAttribute('username', $this->parameters['username']); - $this->xml->writeAttribute('password', $this->parameters['password']); - $this->xml->writeAttribute('apikey', $this->parameters['token']); + $this->xml->writeAttribute('username', (string) ($this->parameters['username'] ?? '')); + $this->xml->writeAttribute('password', (string) ($this->parameters['password'] ?? '')); + $this->xml->writeAttribute('apikey', (string) ($this->parameters['token'] ?? '')); $this->xml->endElement(); $this->xml->endDocument(); @@ -414,7 +416,7 @@ class XML_Response { $this->xml->startElement($element['name']); foreach ($element['data'] as $attr => $val) { - $this->xml->writeAttribute($attr, $val); + $this->xml->writeAttribute($attr, (string) $val); } $this->xml->endElement(); } @@ -443,17 +445,17 @@ class XML_Response $this->xml->startElement('categories'); foreach ($this->server['categories'] as $this->parameters) { $this->xml->startElement('category'); - $this->xml->writeAttribute('id', $this->parameters['id']); - $this->xml->writeAttribute('name', html_entity_decode($this->parameters['title'])); + $this->xml->writeAttribute('id', (string) $this->parameters['id']); + $this->xml->writeAttribute('name', html_entity_decode((string) $this->parameters['title'])); if (! empty($this->parameters['description'])) { - $this->xml->writeAttribute('description', html_entity_decode($this->parameters['description'])); + $this->xml->writeAttribute('description', html_entity_decode((string) $this->parameters['description'])); } foreach ($this->parameters['categories'] as $c) { $this->xml->startElement('subcat'); - $this->xml->writeAttribute('id', $c['id']); - $this->xml->writeAttribute('name', html_entity_decode($c['title'])); + $this->xml->writeAttribute('id', (string) $c['id']); + $this->xml->writeAttribute('name', html_entity_decode((string) $c['title'])); if (! empty($c['description'])) { - $this->xml->writeAttribute('description', html_entity_decode($c['description'])); + $this->xml->writeAttribute('description', html_entity_decode((string) $c['description'])); } $this->xml->endElement(); } @@ -541,23 +543,23 @@ class XML_Response public function includeTotalRows(): void { $this->xml->startElement($this->namespace.':response'); - $this->xml->writeAttribute('offset', $this->offset); - $this->xml->writeAttribute('total', $this->releases[0]->_totalrows ?? 0); + $this->xml->writeAttribute('offset', (string) $this->offset); + $this->xml->writeAttribute('total', (string) ($this->releases[0]->_totalrows ?? 0)); $this->xml->endElement(); } public function includeLimits(): void { $this->xml->startElement($this->namespace.':apilimits'); - $this->xml->writeAttribute('apicurrent', $this->parameters['requests']); - $this->xml->writeAttribute('apimax', $this->parameters['apilimit']); - $this->xml->writeAttribute('grabcurrent', $this->parameters['grabs']); - $this->xml->writeAttribute('grabmax', $this->parameters['downloadlimit']); + $this->xml->writeAttribute('apicurrent', (string) $this->parameters['requests']); + $this->xml->writeAttribute('apimax', (string) $this->parameters['apilimit']); + $this->xml->writeAttribute('grabcurrent', (string) $this->parameters['grabs']); + $this->xml->writeAttribute('grabmax', (string) $this->parameters['downloadlimit']); if (! empty($this->parameters['oldestapi'])) { - $this->xml->writeAttribute('apioldesttime', $this->parameters['oldestapi']); + $this->xml->writeAttribute('apioldesttime', (string) $this->parameters['oldestapi']); } if (! empty($this->parameters['oldestgrab'])) { - $this->xml->writeAttribute('graboldesttime', $this->parameters['oldestgrab']); + $this->xml->writeAttribute('graboldesttime', (string) $this->parameters['oldestgrab']); } $this->xml->endElement(); } @@ -602,7 +604,7 @@ class XML_Response ((int) $this->parameters['del'] === 1 ? '&del=1' : '') ); $this->xml->writeElement('comments', "{$this->server['server']['url']}/details/{$this->release->guid}#comments"); - $this->xml->writeElement('pubDate', date(DATE_RSS, strtotime($this->release->adddate))); + $this->xml->writeElement('pubDate', date(DATE_RSS, strtotime((string) $this->release->adddate))); $this->xml->writeElement('category', $this->release->category_name); if ($this->namespace === 'newznab') { $this->xml->writeElement('description', $this->release->searchname); @@ -617,7 +619,7 @@ class XML_Response "&r={$this->parameters['token']}". ((int) $this->parameters['del'] === 1 ? '&del=1' : '') ); - $this->xml->writeAttribute('length', $this->release->size); + $this->xml->writeAttribute('length', (string) $this->release->size); $this->xml->writeAttribute('type', 'application/x-nzb'); $this->xml->endElement(); } @@ -745,13 +747,13 @@ class XML_Response * Writes individual zed (newznab) type attributes. * * @param string $name The namespaced attribute name tag - * @param string|null $value The namespaced attribute value + * @param mixed $value The namespaced attribute value (int/string/null from DB) */ - protected function writeZedAttr(string $name, ?string $value): void + protected function writeZedAttr(string $name, mixed $value): void { $this->xml->startElement($this->namespace.':attr'); $this->xml->writeAttribute('name', $name); - $this->xml->writeAttribute('value', $value); + $this->xml->writeAttribute('value', $value === null ? '' : (string) $value); $this->xml->endElement(); } @@ -914,7 +916,7 @@ class XML_Response if (! empty($this->release->$info)) { if ($info === 'mu_releasedate') { $ucInfo = 'Released'; - $rDate = date('Y-m-d', strtotime($this->release->$info)); + $rDate = date('Y-m-d', strtotime((string) $this->release->$info)); $cData .= "
  • {$ucInfo}: {$rDate}
  • \n"; } else { $ucInfo = ucfirst(preg_replace('/^[a-z]{2}_/i', '', $info)); diff --git a/app/Http/Controllers/ApiHelpController.php b/app/Http/Controllers/ApiHelpController.php index e5ff97324..bbae48b1d 100644 --- a/app/Http/Controllers/ApiHelpController.php +++ b/app/Http/Controllers/ApiHelpController.php @@ -1,5 +1,7 @@ has('page') && is_numeric($request->input('page')) ? $request->input('page') : 1; - $offset = ($page - 1) * config('nntmux.items_per_cover_page'); - $rslt = $bookService->getBookRange($page, $catarray, $offset, config('nntmux.items_per_cover_page'), $orderby, (array) $this->userdata->categoryexclusions); // @phpstan-ignore argument.type - $results = $this->paginate($rslt, $rslt[0]->_totalcount ?? 0, config('nntmux.items_per_cover_page'), $page, $request->url(), $request->query()); + $offset = ($page - 1) * (int) config('nntmux.items_per_cover_page'); + $rslt = $bookService->getBookRange($page, $catarray, $offset, (int) config('nntmux.items_per_cover_page'), $orderby, (array) $this->userdata->categoryexclusions); + $results = $this->paginate($rslt, $rslt[0]->_totalcount ?? 0, (int) config('nntmux.items_per_cover_page'), $page, $request->url(), $request->query()); $maxwords = 50; foreach ($results as $result) { if (! empty($result->overview)) { diff --git a/app/Http/Controllers/BrowseController.php b/app/Http/Controllers/BrowseController.php index 11687640e..0d675e4c1 100644 --- a/app/Http/Controllers/BrowseController.php +++ b/app/Http/Controllers/BrowseController.php @@ -1,5 +1,7 @@ consoleService->getConsoleOrdering(); $orderby = $request->has('ob') && \in_array($request->input('ob'), $ordering, false) ? $request->input('ob') : ''; $page = $request->has('page') && is_numeric($request->input('page')) ? $request->input('page') : 1; - $offset = ($page - 1) * config('nntmux.items_per_cover_page'); + $offset = ($page - 1) * (int) config('nntmux.items_per_cover_page'); $consoles = []; - $rslt = $this->consoleService->getConsoleRange($page, $catarray, $offset, config('nntmux.items_per_cover_page'), $orderby, (array) $this->userdata->categoryexclusions); // @phpstan-ignore argument.type - $results = $this->paginate($rslt, $rslt[0]->_totalcount ?? 0, config('nntmux.items_per_cover_page'), $page, $request->url(), $request->query()); + $rslt = $this->consoleService->getConsoleRange($page, $catarray, $offset, (int) config('nntmux.items_per_cover_page'), $orderby, (array) $this->userdata->categoryexclusions); + $results = $this->paginate($rslt, $rslt[0]->_totalcount ?? 0, (int) config('nntmux.items_per_cover_page'), $page, $request->url(), $request->query()); $maxwords = 50; foreach ($results as $result) { diff --git a/app/Http/Controllers/ContactUsController.php b/app/Http/Controllers/ContactUsController.php index fa92342dd..e8c88f2f0 100644 --- a/app/Http/Controllers/ContactUsController.php +++ b/app/Http/Controllers/ContactUsController.php @@ -1,5 +1,7 @@ has('page') && is_numeric($request->input('page')) ? $request->input('page') : 1; $ordering = $games->getGamesOrdering(); $orderby = $request->has('ob') && \in_array($request->input('ob'), $ordering, true) ? $request->input('ob') : ''; - $offset = ($page - 1) * config('nntmux.items_per_cover_page'); - $rslt = $games->getGamesRange($page, $catarray, $offset, config('nntmux.items_per_cover_page'), $orderby, '', (array) $this->userdata->categoryexclusions); - $results = $this->paginate($rslt, $rslt[0]->_totalcount ?? 0, config('nntmux.items_per_cover_page'), $page, $request->url(), $request->query()); + $offset = ($page - 1) * (int) config('nntmux.items_per_cover_page'); + $rslt = $games->getGamesRange($page, $catarray, $offset, (int) config('nntmux.items_per_cover_page'), $orderby, '', (array) $this->userdata->categoryexclusions); + $results = $this->paginate($rslt, $rslt[0]->_totalcount ?? 0, (int) config('nntmux.items_per_cover_page'), $page, $request->url(), $request->query()); $title = ($request->has('title') && ! empty($request->input('title'))) ? stripslashes($request->input('title')) : ''; diff --git a/app/Http/Controllers/GetNzbController.php b/app/Http/Controllers/GetNzbController.php index 427859333..a2c462ce4 100644 --- a/app/Http/Controllers/GetNzbController.php +++ b/app/Http/Controllers/GetNzbController.php @@ -1,5 +1,7 @@ normalizeGuidParameter($request, $guid); @@ -80,7 +77,7 @@ class GetNzbController extends BasePageController * * @return array|Response */ - private function authenticateUser(Request $request) + private function authenticateUser(Request $request): array|Response { // Try session authentication first if ($request->user()) { @@ -96,7 +93,7 @@ class GetNzbController extends BasePageController * * @return array|Response */ - private function getUserDataFromSession() + private function getUserDataFromSession(): array|Response { if ($this->userdata->hasRole('Disabled')) { return showApiError(101); @@ -115,7 +112,7 @@ class GetNzbController extends BasePageController * * @return array|Response */ - private function getUserDataFromRssToken(Request $request) + private function getUserDataFromRssToken(Request $request): array|Response { if ($request->missing('r')) { return showApiError(200); @@ -141,11 +138,10 @@ class GetNzbController extends BasePageController /** * Check if user has exceeded download limits * - * @return Response|null * * @throws Exception */ - private function checkDownloadLimit(int $uid, int $maxDownloads): mixed + private function checkDownloadLimit(int $uid, int $maxDownloads): ?Response { $requests = UserDownload::getDownloadRequests($uid); if ($requests > $maxDownloads) { @@ -157,10 +153,8 @@ class GetNzbController extends BasePageController /** * Validate and sanitize the release ID parameter - * - * @return string|Response */ - private function validateAndSanitizeId(Request $request) + private function validateAndSanitizeId(Request $request): string|Response { $id = $request->input('id'); @@ -186,8 +180,6 @@ class GetNzbController extends BasePageController /** * Handle zip download of multiple releases * - * @return JsonResponse|ZipStream|StreamedResponse|\STS\ZipStream\Builder - * * @throws Exception */ private function handleZipDownload( @@ -196,7 +188,7 @@ class GetNzbController extends BasePageController string $userName, int $maxDownloads, string $releaseId - ) { + ): JsonResponse|StreamedResponse|\STS\ZipStream\Builder { $guids = explode(',', $releaseId); $guidCount = \count($guids); @@ -206,13 +198,13 @@ class GetNzbController extends BasePageController return showApiError(501); } - $zip = getStreamingZip($guids); // @phpstan-ignore argument.type + $zip = getStreamingZip($guids); if ($zip === '') { // @phpstan-ignore identical.alwaysFalse return response()->json(['message' => 'Unable to create .zip file'], 404); } // Update statistics - $this->updateZipDownloadStatistics($request, $uid, $guids); // @phpstan-ignore argument.type + $this->updateZipDownloadStatistics($request, $uid, $guids); Log::channel('zipped')->info("User {$userName} downloaded zipped files from site with IP: {$request->ip()}"); @@ -222,7 +214,7 @@ class GetNzbController extends BasePageController /** * Update statistics for zip downloads * - * @param array $guids + * @param list $guids */ private function updateZipDownloadStatistics(Request $request, int $uid, array $guids): void { @@ -243,15 +235,13 @@ class GetNzbController extends BasePageController /** * Handle single NZB file download - * - * @return Response|StreamedResponse */ private function handleSingleNzbDownload( Request $request, int $uid, string $rssToken, string $releaseId - ) { + ): Response|StreamedResponse { // Get NZB file path and validate $nzbPath = app(NzbService::class)->getNzbPath($releaseId); if (! File::exists($nzbPath)) { diff --git a/app/Http/Controllers/InvitationController.php b/app/Http/Controllers/InvitationController.php index c773d4c79..b3e5859d3 100644 --- a/app/Http/Controllers/InvitationController.php +++ b/app/Http/Controllers/InvitationController.php @@ -1,5 +1,7 @@ input('page', 1); - $offset = ($page - 1) * config('nntmux.items_per_cover_page'); + $offset = ($page - 1) * (int) config('nntmux.items_per_cover_page'); $orderby = $request->input('ob', ''); $ordering = $this->movieBrowseService->getMovieOrdering(); @@ -46,9 +48,9 @@ class MovieController extends BasePageController $orderby = ''; } - $rslt = $this->movieBrowseService->getMovieRange($page, $catarray, $offset, config('nntmux.items_per_cover_page'), $orderby, -1, (array) $this->userdata->categoryexclusions); + $rslt = $this->movieBrowseService->getMovieRange($page, $catarray, $offset, (int) config('nntmux.items_per_cover_page'), $orderby, -1, (array) $this->userdata->categoryexclusions); $totalCount = $rslt->isNotEmpty() ? ($rslt[0]->_totalcount ?? 0) : 0; - $results = $this->paginate($rslt ?? [], $totalCount, config('nntmux.items_per_cover_page'), $page, $request->url(), $request->query()); + $results = $this->paginate($rslt ?? [], $totalCount, (int) config('nntmux.items_per_cover_page'), $page, $request->url(), $request->query()); $movies = $results->map(function ($result) { $result->genre = makeFieldLinks($result, 'genre', 'movies'); diff --git a/app/Http/Controllers/MusicController.php b/app/Http/Controllers/MusicController.php index bf726a5ec..9d8574d6e 100644 --- a/app/Http/Controllers/MusicController.php +++ b/app/Http/Controllers/MusicController.php @@ -1,5 +1,7 @@ has('page') && is_numeric($request->input('page')) ? $request->input('page') : 1; - $offset = ($page - 1) * config('nntmux.items_per_cover_page'); + $offset = ($page - 1) * (int) config('nntmux.items_per_cover_page'); $ordering = $music->getMusicOrdering(); $orderby = $request->has('ob') && \in_array($request->input('ob'), $ordering, true) ? $request->input('ob') : ''; $musics = []; - $rslt = $music->getMusicRange($page, $catarray, $offset, config('nntmux.items_per_cover_page'), $orderby, (array) $this->userdata->categoryexclusions); // @phpstan-ignore argument.type - $results = $this->paginate($rslt ?? [], $rslt[0]->_totalcount ?? 0, config('nntmux.items_per_cover_page'), $page, $request->url(), $request->query()); + $rslt = $music->getMusicRange($page, $catarray, $offset, (int) config('nntmux.items_per_cover_page'), $orderby, (array) $this->userdata->categoryexclusions); + $results = $this->paginate($rslt ?? [], $rslt[0]->_totalcount ?? 0, (int) config('nntmux.items_per_cover_page'), $page, $request->url(), $request->query()); $artist = ($request->has('artist') && ! empty($request->input('artist'))) ? stripslashes($request->input('artist')) : ''; diff --git a/app/Http/Controllers/MyMoviesController.php b/app/Http/Controllers/MyMoviesController.php index 7954b01c7..52a973620 100644 --- a/app/Http/Controllers/MyMoviesController.php +++ b/app/Http/Controllers/MyMoviesController.php @@ -1,5 +1,7 @@ has('page') && is_numeric($request->input('page')) ? $request->input('page') : 1; - $offset = ($page - 1) * config('nntmux.items_per_cover_page'); + $offset = ($page - 1) * (int) config('nntmux.items_per_cover_page'); $movies = UserMovie::getMovies($this->userdata->id); /** @var array $categories */ @@ -159,7 +161,7 @@ class MyMoviesController extends BasePageController $page = $request->has('page') && is_numeric($request->input('page')) ? $request->input('page') : 1; - $results = $this->movieBrowseService->getMovieRange($page, [], $offset, config('nntmux.items_per_cover_page'), $ordering, -1, (array) $this->userdata->categoryexclusions); + $results = $this->movieBrowseService->getMovieRange($page, [], $offset, (int) config('nntmux.items_per_cover_page'), $ordering, -1, (array) $this->userdata->categoryexclusions); $this->viewData['covgroup'] = ''; diff --git a/app/Http/Controllers/MyShowsController.php b/app/Http/Controllers/MyShowsController.php index d31a7ca1f..ff8115c97 100644 --- a/app/Http/Controllers/MyShowsController.php +++ b/app/Http/Controllers/MyShowsController.php @@ -1,5 +1,7 @@ has('t')) { - $categoryID = array_map('intval', explode(',', $request->input('t'))); + $t = $request->input('t'); + if ($t !== null && $t !== '') { + $categoryID = array_map('intval', explode(',', (string) $t)); + } } $orderByUrls = []; @@ -89,12 +94,12 @@ class SearchController extends BasePageController -1, -1, $offset, - config('nntmux.items_per_page'), + (int) config('nntmux.items_per_page'), $orderBy, -1, $this->userdata->categoryexclusions ?? [], 'basic', - $categoryID); // @phpstan-ignore argument.type + $categoryID); $results = $this->paginate($rslt ?? [], $rslt[0]->_totalrows ?? 0, config('nntmux.items_per_page'), $page, $request->url(), $request->query()); $category = $categoryID; @@ -194,12 +199,12 @@ class SearchController extends BasePageController ($searchVars['searchadvdaysnew'] === '' ? -1 : $searchVars['searchadvdaysnew']), ($searchVars['searchadvdaysold'] === '' ? -1 : $searchVars['searchadvdaysold']), $offset, - config('nntmux.items_per_page'), + (int) config('nntmux.items_per_page'), $orderBy, -1, $this->userdata->categoryexclusions ?? [], 'advanced', - [$searchVars['searchadvcat'] === '' ? -1 : $searchVars['searchadvcat']] // @phpstan-ignore argument.type + [$searchVars['searchadvcat'] === '' ? -1 : $searchVars['searchadvcat']] ); $results = $this->paginate($rslt ?? [], $rslt[0]->_totalrows ?? 0, config('nntmux.items_per_page'), $page, $request->url(), $request->query()); diff --git a/app/Http/Controllers/SearchSuggestController.php b/app/Http/Controllers/SearchSuggestController.php index 787e79471..7c71a0191 100644 --- a/app/Http/Controllers/SearchSuggestController.php +++ b/app/Http/Controllers/SearchSuggestController.php @@ -1,5 +1,7 @@ 0 ? ($page - 1) * $seriesLimit : 0; - $rel = $this->releaseSearchService->tvSearch(['id' => $id], '', '', '', $offset, $seriesLimit, '', $catarray, -1); // @phpstan-ignore argument.type + $rel = $this->releaseSearchService->tvSearch(['id' => $id], '', '', '', $offset, $seriesLimit, '', $catarray, -1); $show = Video::getByVideoID($id); diff --git a/app/Http/Controllers/TermsController.php b/app/Http/Controllers/TermsController.php index 3c0052887..f5d0332c1 100644 --- a/app/Http/Controllers/TermsController.php +++ b/app/Http/Controllers/TermsController.php @@ -1,5 +1,7 @@ $cat - * @return array + * @param array $cat Category IDs (list or associative) + * @return array|string|null */ public static function getCategorySearch(array $cat = [], ?string $searchType = null, mixed $builder = false): string|array|null { // Generate a cache key based on the input parameters - $cacheKey = 'cat_search_'.md5(serialize($cat).$searchType.($builder ? '1' : '0')); + $cacheKey = 'cat_search_'.md5(serialize($cat).(string) ($searchType ?? '').($builder ? '1' : '0')); $cached = Cache::get($cacheKey); if ($cached !== null) { return $cached; @@ -433,13 +435,14 @@ class Category extends Model public static function getChildren(mixed $categoryId) { $expiresAt = now()->addMinutes(config('nntmux.cache_expiry_long')); - $result = Cache::get(md5($categoryId)); + $cacheKey = md5((string) $categoryId); + $result = Cache::get($cacheKey); if ($result !== null) { return $result; } $result = RootCategory::find($categoryId)->categories; - Cache::put(md5($categoryId), $result, $expiresAt); + Cache::put($cacheKey, $result, $expiresAt); return $result; } @@ -469,13 +472,14 @@ class Category extends Model { if (\count($ids) > 0) { $expiresAt = now()->addMinutes(config('nntmux.cache_expiry_long')); - $result = Cache::get(md5(implode(',', $ids))); + $idsKey = implode(',', array_map('strval', (array) $ids)); + $result = Cache::get(md5($idsKey)); if ($result !== null) { return $result; } $result = self::query()->whereIn('id', $ids)->get(); - Cache::put(md5(md5(implode(',', $ids))), $result, $expiresAt); + Cache::put(md5($idsKey), $result, $expiresAt); return $result; } diff --git a/app/Models/CategoryRegex.php b/app/Models/CategoryRegex.php index 01039d470..eb3f40e97 100644 --- a/app/Models/CategoryRegex.php +++ b/app/Models/CategoryRegex.php @@ -1,5 +1,7 @@ null, 'countryCode' => null]; } - $cacheKey = 'ip_country_lookup_'.md5($ip); + $cacheKey = 'ip_country_lookup_'.md5((string) $ip); return Cache::remember($cacheKey, 86400, function () use ($ip) { return $this->fetchCountryFromApi($ip); diff --git a/app/Models/UserActivity.php b/app/Models/UserActivity.php index 1091a8ec3..9a67b61d9 100644 --- a/app/Models/UserActivity.php +++ b/app/Models/UserActivity.php @@ -1,5 +1,7 @@ $cat + * @param array $cat Category IDs (list or associative) * @param array $excludedCats */ public function getBookRange(int $page, array $cat, int $start, int $num, string $orderBy, array $excludedCats = []): mixed @@ -103,7 +105,7 @@ class BookService $browseby = $this->getBrowseBy(); $catsrch = ''; - if (\count($cat) > 0 && $cat[0] !== -1) { // @phpstan-ignore offsetAccess.notFound + if (\count($cat) > 0 && $cat[0] !== -1) { $catsrch = Category::getCategorySearch($cat); } $exccatlist = ''; diff --git a/app/Services/BooksProcessor.php b/app/Services/BooksProcessor.php index 879a4caad..eade18be8 100644 --- a/app/Services/BooksProcessor.php +++ b/app/Services/BooksProcessor.php @@ -1,5 +1,7 @@ $cat + * @param array $cat Category IDs (list or associative) * @param array $excludedCats * * @throws \Exception @@ -134,7 +134,7 @@ class ConsoleService $browseBy = $this->getBrowseBy(); $catsrch = ''; - if (\count($cat) > 0 && (int) $cat[0] !== -1) { // @phpstan-ignore offsetAccess.notFound + if (\count($cat) > 0 && (int) $cat[0] !== -1) { $catsrch = Category::getCategorySearch($cat); } $exccatlist = ''; diff --git a/app/Services/ConsolesProcessor.php b/app/Services/ConsolesProcessor.php index 4c8a353b2..b675c4d09 100644 --- a/app/Services/ConsolesProcessor.php +++ b/app/Services/ConsolesProcessor.php @@ -1,5 +1,7 @@ $variable5 * @return array */ - protected function setVariables(string|array $variable1, string|array $variable2, string|array $variable3, string|array $variable4, string|array $variable5 = ''): array|string + protected function setVariables(string|array|int|float $variable1, string|array|int|float $variable2, string|array|int|float $variable3, string|array|int|float $variable4, string|array|int|float $variable5 = ''): array|string { if (! empty($variable1)) { - return $variable1; + return is_array($variable1) ? $variable1 : (string) $variable1; } if (! empty($variable2)) { - return $variable2; + return is_array($variable2) ? $variable2 : (string) $variable2; } if (! empty($variable3)) { - return $variable3; + return is_array($variable3) ? $variable3 : (string) $variable3; } if (! empty($variable4)) { - return $variable4; + return is_array($variable4) ? $variable4 : (string) $variable4; } if (! empty($variable5)) { - return $variable5; + return is_array($variable5) ? $variable5 : (string) $variable5; } return ''; @@ -263,7 +268,7 @@ class MovieService return false; } foreach ($query as $key => $value) { - $query[$key] = rtrim($value, ', '); + $query[$key] = rtrim((string) $value, ', '); } MovieInfo::upsert($query, ['imdbid'], $onDuplicateKey); @@ -549,9 +554,25 @@ class MovieService if ($this->currentTitle !== '' && ! empty($title)) { similar_text($this->currentTitle, $title, $percent); if ($percent < self::MATCH_PERCENT) { - Cache::put($cacheKey, false, $expiresAt); + $tmdbId = TmdbClient::getInt($tmdbLookup, 'id'); + $altTitles = $tmdbId > 0 ? $tmdbClient->getMovieAlternativeTitles($tmdbId) : null; + $titles = is_array($altTitles) ? ($altTitles['titles'] ?? []) : []; + $matched = false; + foreach ($titles as $alt) { + $altTitle = is_array($alt) ? ($alt['title'] ?? '') : ''; + if ($altTitle !== '') { + similar_text($this->currentTitle, $altTitle, $altPercent); + if ($altPercent >= self::MATCH_PERCENT) { + $matched = true; + break; + } + } + } + if (! $matched) { + Cache::put($cacheKey, false, $expiresAt); - return false; + return false; + } } } @@ -907,7 +928,7 @@ class MovieService if ($movCheck === null || (isset($movCheck['updated_at']) && - (time() - strtotime($movCheck['updated_at'])) > $thirtyDaysInSeconds)) { + (time() - strtotime((string) $movCheck['updated_at'])) > $thirtyDaysInSeconds)) { $info = $this->updateMovieInfo($imdbId); @@ -980,7 +1001,7 @@ class MovieService continue; } - $this->currentRelID = $arr['id']; + $this->currentRelID = (string) $arr['id']; $movieName = $this->formatMovieName(); if ($this->echooutput) { @@ -1066,14 +1087,17 @@ class MovieService continue; } similar_text($title, $this->currentTitle, $percent); - if ($percent < self::MATCH_PERCENT) { + $yearMatches = empty($this->currentYear) || empty($match['year']); + if (! $yearMatches) { + similar_text($this->currentYear, $match['year'], $yearPercent); + $yearMatches = $yearPercent >= self::YEAR_MATCH_PERCENT; + } + $titleMatchThreshold = $yearMatches ? self::MATCH_PERCENT_ALT_TITLE : self::MATCH_PERCENT; + if ($percent < $titleMatchThreshold) { continue; } - if (! empty($this->currentYear) && ! empty($match['year'])) { - similar_text($this->currentYear, $match['year'], $yearPercent); - if ($yearPercent < self::YEAR_MATCH_PERCENT) { - continue; - } + if (! $yearMatches) { + continue; } $imdbId = $this->doMovieUpdate('tt'.$match['imdbid'], 'IMDb(scrape)', $releaseId); if ($imdbId !== false) { @@ -1100,6 +1124,15 @@ class MovieService ? $this->omdbApi->search($omdbTitle, 'movie', $this->currentYear) : $this->omdbApi->search($omdbTitle, 'movie'); + if ($this->currentYear !== '' && ( + ! is_object($buffer) || + $buffer->message !== 'OK' || + ($buffer->data->Response ?? '') !== 'True' || + empty($buffer->data->Search[0]->imdbID ?? null) + )) { + $buffer = $this->omdbApi->search($omdbTitle, 'movie'); + } + if (! is_object($buffer) || $buffer->message !== 'OK' || Str::contains($buffer->data->Response, 'Error:') || @@ -1153,7 +1186,8 @@ class MovieService return false; } - $data = $tmdbClient->searchMovies($this->currentTitle); + $year = $this->currentYear !== '' ? $this->currentYear : null; + $data = $tmdbClient->searchMovies($this->currentTitle, 1, $year); if ($data === null || empty($data['total_results']) || empty($data['results'])) { return false; @@ -1249,8 +1283,23 @@ class MovieService return false; } + /** + * Normalize release searchname before movie title/year parsing. + * Replaces dots and underscores with spaces so patterns like "Title_2024" or "Title.Year.2024" + * can be matched by the name+year regex (which expects a non-word character before the year). + */ + protected function cleanReleaseNameForMovieLookup(string $searchname): string + { + $s = str_replace(['.', '_'], ' ', $searchname); + $s = trim(preg_replace('/\s{2,}/', ' ', $s)); + + return $s; + } + protected function parseMovieSearchName(string $releaseName): bool { + $releaseName = $this->cleanReleaseNameForMovieLookup($releaseName); + if (empty(trim($releaseName))) { return false; } diff --git a/app/Services/MoviesProcessor.php b/app/Services/MoviesProcessor.php index 9ba7d0420..f5b1a8df7 100644 --- a/app/Services/MoviesProcessor.php +++ b/app/Services/MoviesProcessor.php @@ -1,5 +1,7 @@ $cat + * @param array $cat Category IDs (list or associative) * @param array $excludedCats */ public function getMusicRange(int $page, array $cat, int $start, int $num, string $orderBy, array $excludedCats = []): mixed @@ -100,7 +102,7 @@ class MusicService $browseby = $this->getBrowseBy(); $catsrch = ''; - if (\count($cat) > 0 && (int) $cat[0] !== -1) { // @phpstan-ignore offsetAccess.notFound + if (\count($cat) > 0 && (int) $cat[0] !== -1) { $catsrch = Category::getCategorySearch($cat); } $exccatlist = ''; diff --git a/app/Services/NNTP/NNTPService.php b/app/Services/NNTP/NNTPService.php index bdc2dd0ff..423e61f8c 100644 --- a/app/Services/NNTP/NNTPService.php +++ b/app/Services/NNTP/NNTPService.php @@ -1,5 +1,7 @@ |string On success : (array) The headers. - * On failure : (object) DariusIII\NetNntp\Error. - * On decompress failure: (string) error message + * @return array|string|NntpError On success : (array) The headers. + * On failure : (object) DariusIII\NetNntp\Error. + * On decompress failure: (string) error message */ - protected function &_getXFeatureTextResponse(): array|string + protected function &_getXFeatureTextResponse(): array|string|NntpError { $possibleTerm = false; // Use array accumulation for better performance with large data diff --git a/app/Services/NfoProcessor.php b/app/Services/NfoProcessor.php index 7e061ef30..f48f88de9 100644 --- a/app/Services/NfoProcessor.php +++ b/app/Services/NfoProcessor.php @@ -1,5 +1,7 @@ update(['nzbstatus' => self::NZB_ADDED]); if (! empty($nzb_guid)) { - $release->update(['nzb_guid' => DB::raw('UNHEX( '.escapeString(md5($nzb_guid)).' )')]); + $release->update(['nzb_guid' => DB::raw('UNHEX( '.escapeString(md5((string) $nzb_guid)).' )')]); } // Delete CBP (Collections, Binaries, Parts) for release that has its NZB created. diff --git a/app/Services/Par2Processor.php b/app/Services/Par2Processor.php index 95f4c235a..20cad2589 100644 --- a/app/Services/Par2Processor.php +++ b/app/Services/Par2Processor.php @@ -1,5 +1,7 @@ update(['collections.filecheck' => CollectionFileCheckStatus::ZeroPart->value]); }, 10); - DB::transaction(static function () use ($groupID): void { - $query = Collection::query() - ->where('filecheck', '=', CollectionFileCheckStatus::CompleteCollection->value); + $this->updateCollectionsFilecheckInChunks( + $groupID, + CollectionFileCheckStatus::CompleteCollection->value, + CollectionFileCheckStatus::TempComplete->value + ); + } - if ($groupID !== 0) { - $query->where('groups_id', $groupID); + /** + * Update collections filecheck in small chunks to reduce deadlock risk. + * Retries on deadlock (1213) with exponential backoff. + * + * @throws Throwable + */ + private function updateCollectionsFilecheckInChunks(int $groupID, int $fromStatus, int $toStatus): void + { + $attempt = 0; + $maxAttempts = self::MAX_RETRIES + 1; + + while ($attempt < $maxAttempts) { + try { + $updated = 0; + do { + $ids = Collection::query() + ->where('filecheck', $fromStatus) + ->when($groupID !== 0, static fn ($q) => $q->where('groups_id', $groupID)) + ->orderBy('id') + ->limit(self::BATCH_SIZE) + ->pluck('id') + ->all(); + + if ($ids === []) { + break; + } + + DB::transaction(static function () use ($ids, $toStatus): void { + Collection::query() + ->whereIn('id', $ids) + ->update(['filecheck' => $toStatus]); + }, 10); + + $updated = \count($ids); + } while ($updated === self::BATCH_SIZE); + + return; + } catch (QueryException $e) { + $isDeadlock = ($e->errorInfo[1] ?? 0) === 1213 + || str_contains($e->getMessage(), 'Deadlock'); + + if ($isDeadlock && $attempt < $maxAttempts - 1) { + $attempt++; + usleep(self::RETRY_BASE_DELAY_US * (2 ** $attempt)); + } else { + throw $e; + } } - - $query->update(['filecheck' => CollectionFileCheckStatus::TempComplete->value]); - }, 10); + } } /** diff --git a/app/Services/Releases/ReleaseBrowseService.php b/app/Services/Releases/ReleaseBrowseService.php index 2e1e98bcd..93ab5c6b6 100644 --- a/app/Services/Releases/ReleaseBrowseService.php +++ b/app/Services/Releases/ReleaseBrowseService.php @@ -1,5 +1,7 @@ $cat + * @param array $cat Category IDs (list or associative) * @param array $excludedCats * @param array $orderBy * @param array $searchArr @@ -49,7 +51,7 @@ class ReleaseSearchService int $maxAge = -1, array $excludedCats = [], string $type = 'basic', - array $cat = [-1], // @phpstan-ignore parameter.defaultValue + array $cat = [-1], int $minSize = 0 ): mixed { if (config('app.debug')) { @@ -128,11 +130,11 @@ class ReleaseSearchService /** * Search function for API. * - * @param array $cat + * @param array $cat Category IDs (list or associative) * @param array $excludedCats * @return Collection|mixed */ - public function apiSearch(mixed $searchName, mixed $groupName, int $offset = 0, int $limit = 1000, int $maxAge = -1, array $excludedCats = [], array $cat = [-1], int $minSize = 0): mixed // @phpstan-ignore parameter.defaultValue + public function apiSearch(mixed $searchName, mixed $groupName, int $offset = 0, int $limit = 1000, int $maxAge = -1, array $excludedCats = [], array $cat = [-1], int $minSize = 0): mixed { if (config('app.debug')) { Log::debug('ReleaseSearchService::apiSearch called', [ @@ -253,12 +255,12 @@ class ReleaseSearchService /** * Search for TV shows via API. * - * @param array $cat + * @param array $cat Category IDs (list or associative) * @param array $excludedCategories * @param array $siteIdArr * @return array|\Illuminate\Database\Eloquent\Collection|\Illuminate\Support\Collection|mixed */ - public function tvSearch(array $siteIdArr = [], string $series = '', string $episode = '', string $airDate = '', int $offset = 0, int $limit = 100, string $name = '', array $cat = [-1], int $maxAge = -1, int $minSize = 0, array $excludedCategories = []): mixed // @phpstan-ignore parameter.defaultValue + public function tvSearch(array $siteIdArr = [], string $series = '', string $episode = '', string $airDate = '', int $offset = 0, int $limit = 100, string $name = '', array $cat = [-1], int $maxAge = -1, int $minSize = 0, array $excludedCategories = []): mixed { $shouldCache = ! (isset($siteIdArr['id']) && (int) $siteIdArr['id'] > 0); $rawCacheKey = md5(serialize(func_get_args()).'tvSearch'); @@ -523,12 +525,12 @@ class ReleaseSearchService /** * Search TV Shows via APIv2. * - * @param array $cat + * @param array $cat Category IDs (list or associative) * @param array $excludedCategories * @param array $siteIdArr * @return Collection|mixed */ - public function apiTvSearch(array $siteIdArr = [], string $series = '', string $episode = '', string $airDate = '', int $offset = 0, int $limit = 100, string $name = '', array $cat = [-1], int $maxAge = -1, int $minSize = 0, array $excludedCategories = []): mixed // @phpstan-ignore parameter.defaultValue + public function apiTvSearch(array $siteIdArr = [], string $series = '', string $episode = '', string $airDate = '', int $offset = 0, int $limit = 100, string $name = '', array $cat = [-1], int $maxAge = -1, int $minSize = 0, array $excludedCategories = []): mixed { // OPTIMIZATION: Try to find releases using search index external IDs first $externalIds = []; @@ -669,11 +671,11 @@ class ReleaseSearchService /** * Search anime releases. * - * @param array $cat + * @param array $cat Category IDs (list or associative) * @param array $excludedCategories * @return Collection|mixed */ - public function animeSearch(mixed $aniDbID, int $offset = 0, int $limit = 100, string $name = '', array $cat = [-1], int $maxAge = -1, array $excludedCategories = []): mixed // @phpstan-ignore parameter.defaultValue + public function animeSearch(mixed $aniDbID, int $offset = 0, int $limit = 100, string $name = '', array $cat = [-1], int $maxAge = -1, array $excludedCategories = []): mixed { $searchResult = []; if (! empty($name)) { @@ -739,11 +741,11 @@ class ReleaseSearchService /** * Movies search through API and site. * - * @param array $cat + * @param array $cat Category IDs (list or associative) * @param array $excludedCategories * @return Collection|mixed */ - public function moviesSearch(int $imDbId = -1, int $tmDbId = -1, int $traktId = -1, int $offset = 0, int $limit = 100, string $name = '', array $cat = [-1], int $maxAge = -1, int $minSize = 0, array $excludedCategories = []): mixed // @phpstan-ignore parameter.defaultValue + public function moviesSearch(int $imDbId = -1, int $tmDbId = -1, int $traktId = -1, int $offset = 0, int $limit = 100, string $name = '', array $cat = [-1], int $maxAge = -1, int $minSize = 0, array $excludedCategories = []): mixed { $searchResult = []; @@ -899,7 +901,7 @@ class ReleaseSearchService return $ret; } - $results = $this->search(['searchname' => getSimilarName($name)], -1, '', '', -1, -1, 0, config('nntmux.items_per_page'), '', -1, $excludedCats, 'basic', [$parentCat]); // @phpstan-ignore argument.type + $results = $this->search(['searchname' => getSimilarName($name)], -1, '', '', -1, -1, 0, (int) config('nntmux.items_per_page'), '', -1, $excludedCats, 'basic', [$parentCat]); if (! $results) { return $ret; } @@ -1018,7 +1020,7 @@ class ReleaseSearchService /** * Build WHERE clause for search query * - * @param array $cat + * @param array $cat Category IDs (list or associative) * @param array $excludedCats * @param array $searchResult */ @@ -1123,7 +1125,7 @@ class ReleaseSearchService /** * Build category condition based on search type * - * @param array $cat + * @param array $cat Category IDs (list or associative) */ private function buildCategoryCondition(string $type, array $cat): string { @@ -1136,8 +1138,8 @@ class ReleaseSearchService return ($catSearch === '1=1') ? '' : $catSearch; } - if ($type === 'advanced' && (int) $cat[0] !== -1) { // @phpstan-ignore offsetAccess.notFound - return sprintf('r.categories_id = %d', (int) $cat[0]); // @phpstan-ignore offsetAccess.notFound + if ($type === 'advanced' && (int) $cat[0] !== -1) { + return sprintf('r.categories_id = %d', (int) $cat[0]); } return ''; @@ -1213,7 +1215,7 @@ class ReleaseSearchService private function getCacheVersion(): int { - return Cache::get(self::CACHE_VERSION_KEY, 1); + return (int) Cache::get(self::CACHE_VERSION_KEY, 1); } /** diff --git a/app/Services/Runners/BackfillRunner.php b/app/Services/Runners/BackfillRunner.php index 6ec4487d9..eea900861 100644 --- a/app/Services/Runners/BackfillRunner.php +++ b/app/Services/Runners/BackfillRunner.php @@ -1,5 +1,7 @@ get('/movie/'.$movieId.'/external_ids'); } + /** + * Get alternative titles for a movie (including international / alternate names). + * + * @param int $movieId The TMDB movie ID + * @return array|null Response with 'titles' array (each has 'title', 'iso_3166_1', 'type') or null on failure + */ + public function getMovieAlternativeTitles(int $movieId): ?array + { + return $this->get('/movie/'.$movieId.'/alternative_titles'); + } + // ========================================================================= // TV SHOW METHODS // ========================================================================= diff --git a/app/Services/Tmux/Scripts/showsleep.php b/app/Services/Tmux/Scripts/showsleep.php index 30bcbfe05..da9d771d1 100644 --- a/app/Services/Tmux/Scripts/showsleep.php +++ b/app/Services/Tmux/Scripts/showsleep.php @@ -1,5 +1,7 @@ runVar['settings']['maxsize_pp'] ?? ''; $minSize = $this->runVar['settings']['minsize_pp'] ?? ''; - $proc2Query = $this->tmux->proc_query(2, $bookReqIds, $dbName, $maxSize, $minSize); + $proc2Query = $this->tmux->proc_query(2, $bookReqIds, $dbName, (string) $maxSize, (string) $minSize); $proc2Result = DB::selectOne($proc2Query); if ($proc2Result) { @@ -348,8 +350,8 @@ class TmuxMonitorService // Calculate diffs foreach ($this->runVar['counts']['now'] as $key => $value) { - $startValue = $this->runVar['counts']['start'][$key] ?? 0; - $this->runVar['counts']['diff'][$key] = number_format($value - $startValue); + $startValue = (int) ($this->runVar['counts']['start'][$key] ?? 0); + $this->runVar['counts']['diff'][$key] = number_format((int) $value - $startValue); } // Calculate percentages for category counts (as % of total categorized releases) @@ -358,13 +360,13 @@ class TmuxMonitorService // Sum all category counts to get the true total $totalCategorized = 0; foreach ($categoryKeys as $key) { - $totalCategorized += $this->runVar['counts']['now'][$key] ?? 0; + $totalCategorized += (int) ($this->runVar['counts']['now'][$key] ?? 0); } foreach ($categoryKeys as $key) { - $value = $this->runVar['counts']['now'][$key] ?? 0; + $value = (int) ($this->runVar['counts']['now'][$key] ?? 0); $this->runVar['counts']['percent'][$key] = $totalCategorized > 0 - ? sprintf('%02d', floor(($value / $totalCategorized) * 100)) + ? sprintf('%02d', (int) floor(($value / $totalCategorized) * 100)) : 0; } diff --git a/app/Services/Tmux/TmuxOutput.php b/app/Services/Tmux/TmuxOutput.php index f056b484e..8e0285dc0 100644 --- a/app/Services/Tmux/TmuxOutput.php +++ b/app/Services/Tmux/TmuxOutput.php @@ -1,5 +1,7 @@ tmpMasks[1], 'Parts in Repair:', - number_format($this->runVar['counts']['now']['missed_parts_table']) + number_format((int) ($this->runVar['counts']['now']['missed_parts_table'] ?? 0)) ); if (((int) $this->runVar['settings']['post'] === 1 || (int) $this->runVar['settings']['post'] === 3) && (int) $this->runVar['constants']['sequential'] !== 2) { @@ -213,12 +215,12 @@ class TmuxOutput extends Tmux 'Nfo', sprintf( '%s(%s)', - number_format($this->runVar['counts']['now']['processnfo']), + number_format((int) ($this->runVar['counts']['now']['processnfo'] ?? 0)), $this->runVar['counts']['diff']['processnfo'] ), sprintf( '%s(%d%%)', - number_format($this->runVar['counts']['now']['nfo']), + number_format((int) ($this->runVar['counts']['now']['nfo'] ?? 0)), $this->runVar['counts']['percent']['nfo'] ) ); @@ -227,13 +229,13 @@ class TmuxOutput extends Tmux 'PreDB', sprintf( '%s(%s)', - number_format($this->runVar['counts']['now']['predb'] - - $this->runVar['counts']['now']['distinct_predb_matched']), + number_format((int) ($this->runVar['counts']['now']['predb'] ?? 0) - + (int) ($this->runVar['counts']['now']['distinct_predb_matched'] ?? 0)), $this->runVar['counts']['diff']['distinct_predb_matched'] ), sprintf( '%s(%d%%)', - number_format($this->runVar['counts']['now']['predb_matched']), + number_format((int) ($this->runVar['counts']['now']['predb_matched'] ?? 0)), $this->runVar['counts']['percent']['predb_matched'] ) ); @@ -242,12 +244,12 @@ class TmuxOutput extends Tmux 'Renames', sprintf( '%s(%s)', - number_format($this->runVar['counts']['now']['processrenames']), + number_format((int) ($this->runVar['counts']['now']['processrenames'] ?? 0)), $this->runVar['counts']['diff']['processrenames'] ), sprintf( '%s(%d%%)', - number_format($this->runVar['counts']['now']['renamed']), + number_format((int) ($this->runVar['counts']['now']['renamed'] ?? 0)), $this->runVar['counts']['percent']['renamed'] ) ); @@ -261,12 +263,12 @@ class TmuxOutput extends Tmux 'Audio', sprintf( '%s(%s)', - number_format($this->runVar['counts']['now']['processmusic']), + number_format((int) ($this->runVar['counts']['now']['processmusic'] ?? 0)), $this->runVar['counts']['diff']['processmusic'] ), sprintf( '%s(%d%%)', - number_format($this->runVar['counts']['now']['audio']), + number_format((int) ($this->runVar['counts']['now']['audio'] ?? 0)), $this->runVar['counts']['percent']['audio'] ) ); @@ -275,12 +277,12 @@ class TmuxOutput extends Tmux 'Books', sprintf( '%s(%s)', - number_format($this->runVar['counts']['now']['processbooks']), + number_format((int) ($this->runVar['counts']['now']['processbooks'] ?? 0)), $this->runVar['counts']['diff']['processbooks'] ), sprintf( '%s(%d%%)', - number_format($this->runVar['counts']['now']['books']), + number_format((int) ($this->runVar['counts']['now']['books'] ?? 0)), $this->runVar['counts']['percent']['books'] ) ); @@ -289,12 +291,12 @@ class TmuxOutput extends Tmux 'Console', sprintf( '%s(%s)', - number_format($this->runVar['counts']['now']['processconsole']), + number_format((int) ($this->runVar['counts']['now']['processconsole'] ?? 0)), $this->runVar['counts']['diff']['processconsole'] ), sprintf( '%s(%d%%)', - number_format($this->runVar['counts']['now']['console']), + number_format((int) ($this->runVar['counts']['now']['console'] ?? 0)), $this->runVar['counts']['percent']['console'] ) ); @@ -303,12 +305,12 @@ class TmuxOutput extends Tmux 'Misc', sprintf( '%s(%s)', - number_format($this->runVar['counts']['now']['work']), + number_format((int) ($this->runVar['counts']['now']['work'] ?? 0)), $this->runVar['counts']['diff']['work'] ), sprintf( '%s(%d%%)', - number_format($this->runVar['counts']['now']['misc']), + number_format((int) ($this->runVar['counts']['now']['misc'] ?? 0)), $this->runVar['counts']['percent']['misc'] ) ); @@ -317,12 +319,12 @@ class TmuxOutput extends Tmux 'Movie', sprintf( '%s(%s)', - number_format($this->runVar['counts']['now']['processmovies']), + number_format((int) ($this->runVar['counts']['now']['processmovies'] ?? 0)), $this->runVar['counts']['diff']['processmovies'] ), sprintf( '%s(%d%%)', - number_format($this->runVar['counts']['now']['movies']), + number_format((int) ($this->runVar['counts']['now']['movies'] ?? 0)), $this->runVar['counts']['percent']['movies'] ) ); @@ -331,12 +333,12 @@ class TmuxOutput extends Tmux 'PC', sprintf( '%s(%s)', - number_format($this->runVar['counts']['now']['processgames']), + number_format((int) ($this->runVar['counts']['now']['processgames'] ?? 0)), $this->runVar['counts']['diff']['processgames'] ), sprintf( '%s(%d%%)', - number_format($this->runVar['counts']['now']['pc']), + number_format((int) ($this->runVar['counts']['now']['pc'] ?? 0)), $this->runVar['counts']['percent']['pc'] ) ); @@ -345,12 +347,12 @@ class TmuxOutput extends Tmux 'TV', sprintf( '%s(%s)', - number_format($this->runVar['counts']['now']['processtv']), + number_format((int) ($this->runVar['counts']['now']['processtv'] ?? 0)), $this->runVar['counts']['diff']['processtv'] ), sprintf( '%s(%d%%)', - number_format($this->runVar['counts']['now']['tv']), + number_format((int) ($this->runVar['counts']['now']['tv'] ?? 0)), $this->runVar['counts']['percent']['tv'] ) ); @@ -364,7 +366,7 @@ class TmuxOutput extends Tmux ), sprintf( '%s(%d%%)', - number_format($this->runVar['counts']['now']['xxx'] ?? 0), + number_format((int) ($this->runVar['counts']['now']['xxx'] ?? 0)), $this->runVar['counts']['percent']['xxx'] ?? 0 ) ); @@ -375,12 +377,12 @@ class TmuxOutput extends Tmux 'Total', sprintf( '%s(%s)', - number_format($this->runVar['counts']['now']['total_work']), + number_format((int) ($this->runVar['counts']['now']['total_work'] ?? 0)), $this->runVar['counts']['diff']['total_work'] ), sprintf( '%s(%s)', - number_format($this->runVar['counts']['now']['releases']), + number_format((int) ($this->runVar['counts']['now']['releases'] ?? 0)), $this->runVar['counts']['diff']['releases'] ) ); @@ -507,9 +509,9 @@ class TmuxOutput extends Tmux $buffer .= $this->_getSeparator(); $buffer .= sprintf( $this->tmpMasks[5], - number_format($this->runVar['counts']['now']['collections_table']), - number_format($this->runVar['counts']['now']['binaries_table']), - number_format($this->runVar['counts']['now']['parts_table']) + number_format((int) ($this->runVar['counts']['now']['collections_table'] ?? 0)), + number_format((int) ($this->runVar['counts']['now']['binaries_table'] ?? 0)), + number_format((int) ($this->runVar['counts']['now']['parts_table'] ?? 0)) ); return $buffer; diff --git a/app/Services/Tmux/TmuxPaneManager.php b/app/Services/Tmux/TmuxPaneManager.php index cc04ff5ce..2c5778f33 100644 --- a/app/Services/Tmux/TmuxPaneManager.php +++ b/app/Services/Tmux/TmuxPaneManager.php @@ -1,5 +1,7 @@ getByTitle($cleanName, self::TYPE_TV, 0); // If not found and cleanName contains a year in parentheses, try without the year - if (($videoId === 0 || $videoId === false) && preg_match('/^(.+?)\s*\(\d{4}\)$/', $cleanName, $yearMatch)) { + if (($videoId === 0 || $videoId === false) && preg_match('/^(.+?)\s*\(\d{4}\)$/', (string) $cleanName, $yearMatch)) { $nameWithoutYear = trim($yearMatch[1]); $videoId = $localDb->getByTitle($nameWithoutYear, self::TYPE_TV, 0); } diff --git a/app/Services/TvProcessing/Pipes/ParseInfoPipe.php b/app/Services/TvProcessing/Pipes/ParseInfoPipe.php index 5564412ff..97884bde2 100644 --- a/app/Services/TvProcessing/Pipes/ParseInfoPipe.php +++ b/app/Services/TvProcessing/Pipes/ParseInfoPipe.php @@ -1,5 +1,7 @@ getByTitle($cleanName, self::TYPE_TV, self::SOURCE_TMDB); // If not found and cleanName contains a year in parentheses, try without the year - if ($videoId === 0 && preg_match('/^(.+?)\s*\(\d{4}\)$/', $cleanName, $yearMatch)) { + if ($videoId === 0 && preg_match('/^(.+?)\s*\(\d{4}\)$/', (string) $cleanName, $yearMatch)) { $nameWithoutYear = trim($yearMatch[1]); $videoId = $tmdb->getByTitle($nameWithoutYear, self::TYPE_TV, self::SOURCE_TMDB); } @@ -93,14 +95,14 @@ class TmdbPipe extends AbstractTvProviderPipe $tmdbShow = $tmdb->getShowInfo((string) $cleanName); // If not found and cleanName contains a year in parentheses, try without the year - if ($tmdbShow === false && preg_match('/^(.+?)\s*\(\d{4}\)$/', $cleanName, $yearMatch)) { + if ($tmdbShow === false && preg_match('/^(.+?)\s*\(\d{4}\)$/', (string) $cleanName, $yearMatch)) { $nameWithoutYear = trim($yearMatch[1]); $tmdbShow = $tmdb->getShowInfo($nameWithoutYear); } if (is_array($tmdbShow)) { // Check if we have a valid country - if (isset($parsedInfo['country']) && strlen($parsedInfo['country']) === 2) { + if (isset($parsedInfo['country']) && strlen((string) $parsedInfo['country']) === 2) { $tmdbShow['country'] = $parsedInfo['country']; } $videoId = $tmdb->add($tmdbShow); diff --git a/app/Services/TvProcessing/Pipes/TraktPipe.php b/app/Services/TvProcessing/Pipes/TraktPipe.php index a4a9cafc2..88aefebfb 100644 --- a/app/Services/TvProcessing/Pipes/TraktPipe.php +++ b/app/Services/TvProcessing/Pipes/TraktPipe.php @@ -1,5 +1,7 @@ getByTitle($cleanName, self::TYPE_TV, self::SOURCE_TRAKT); // If not found and cleanName contains a year in parentheses, try without the year - if ($videoId === 0 && preg_match('/^(.+?)\s*\(\d{4}\)$/', $cleanName, $yearMatch)) { + if ($videoId === 0 && preg_match('/^(.+?)\s*\(\d{4}\)$/', (string) $cleanName, $yearMatch)) { $nameWithoutYear = trim($yearMatch[1]); $videoId = $trakt->getByTitle($nameWithoutYear, self::TYPE_TV, self::SOURCE_TRAKT); } @@ -92,7 +94,7 @@ class TraktPipe extends AbstractTvProviderPipe $traktShow = $trakt->getShowInfo((string) $cleanName); // If not found and cleanName contains a year in parentheses, try without the year - if ($traktShow === false && preg_match('/^(.+?)\s*\(\d{4}\)$/', $cleanName, $yearMatch)) { + if ($traktShow === false && preg_match('/^(.+?)\s*\(\d{4}\)$/', (string) $cleanName, $yearMatch)) { $nameWithoutYear = trim($yearMatch[1]); $traktShow = $trakt->getShowInfo($nameWithoutYear); } diff --git a/app/Services/TvProcessing/Pipes/TvMazePipe.php b/app/Services/TvProcessing/Pipes/TvMazePipe.php index a7de4a084..821dccb12 100644 --- a/app/Services/TvProcessing/Pipes/TvMazePipe.php +++ b/app/Services/TvProcessing/Pipes/TvMazePipe.php @@ -1,5 +1,7 @@ getByTitle($cleanName, self::TYPE_TV, self::SOURCE_TVMAZE); // If not found and cleanName contains a year in parentheses, try without the year - if ($videoId === 0 && preg_match('/^(.+?)\s*\(\d{4}\)$/', $cleanName, $yearMatch)) { + if ($videoId === 0 && preg_match('/^(.+?)\s*\(\d{4}\)$/', (string) $cleanName, $yearMatch)) { $nameWithoutYear = trim($yearMatch[1]); $videoId = $tvmaze->getByTitle($nameWithoutYear, self::TYPE_TV, self::SOURCE_TVMAZE); } @@ -92,14 +94,14 @@ class TvMazePipe extends AbstractTvProviderPipe $tvmazeShow = $tvmaze->getShowInfo((string) $cleanName); // If not found and cleanName contains a year in parentheses, try without the year - if ($tvmazeShow === false && preg_match('/^(.+?)\s*\(\d{4}\)$/', $cleanName, $yearMatch)) { + if ($tvmazeShow === false && preg_match('/^(.+?)\s*\(\d{4}\)$/', (string) $cleanName, $yearMatch)) { $nameWithoutYear = trim($yearMatch[1]); $tvmazeShow = $tvmaze->getShowInfo($nameWithoutYear); } if (is_array($tvmazeShow)) { // Check if we have a valid country - if (isset($parsedInfo['country']) && strlen($parsedInfo['country']) === 2) { + if (isset($parsedInfo['country']) && strlen((string) $parsedInfo['country']) === 2) { $tvmazeShow['country'] = $parsedInfo['country']; } $videoId = $tvmaze->add($tvmazeShow); diff --git a/app/Services/TvProcessing/Pipes/TvdbPipe.php b/app/Services/TvProcessing/Pipes/TvdbPipe.php index 2d370167e..8fd412867 100644 --- a/app/Services/TvProcessing/Pipes/TvdbPipe.php +++ b/app/Services/TvProcessing/Pipes/TvdbPipe.php @@ -1,5 +1,7 @@ getByTitle($cleanName, self::TYPE_TV); // If not found and cleanName contains a year in parentheses, try without the year - if ($videoId === 0 && preg_match('/^(.+?)\s*\(\d{4}\)$/', $cleanName, $yearMatch)) { + if ($videoId === 0 && preg_match('/^(.+?)\s*\(\d{4}\)$/', (string) $cleanName, $yearMatch)) { $nameWithoutYear = trim($yearMatch[1]); $videoId = $tvdb->getByTitle($nameWithoutYear, self::TYPE_TV); } @@ -89,7 +91,7 @@ class TvdbPipe extends AbstractTvProviderPipe // Check if we have a valid country $country = ( - isset($parsedInfo['country']) && strlen($parsedInfo['country']) === 2 + isset($parsedInfo['country']) && strlen((string) $parsedInfo['country']) === 2 ? (string) $parsedInfo['country'] : '' ); @@ -101,7 +103,7 @@ class TvdbPipe extends AbstractTvProviderPipe $tvdbShow = $tvdb->getShowInfo((string) $cleanName); // If not found and cleanName contains a year in parentheses, try without the year - if ($tvdbShow === false && preg_match('/^(.+?)\s*\(\d{4}\)$/', $cleanName, $yearMatch)) { + if ($tvdbShow === false && preg_match('/^(.+?)\s*\(\d{4}\)$/', (string) $cleanName, $yearMatch)) { $nameWithoutYear = trim($yearMatch[1]); $tvdbShow = $tvdb->getShowInfo($nameWithoutYear); } diff --git a/app/Services/TvProcessing/Providers/AbstractTvProvider.php b/app/Services/TvProcessing/Providers/AbstractTvProvider.php index fdb17559a..f446eb7eb 100644 --- a/app/Services/TvProcessing/Providers/AbstractTvProvider.php +++ b/app/Services/TvProcessing/Providers/AbstractTvProvider.php @@ -1,5 +1,7 @@ siteColumns as $column) { if ((int) $show[$column] > 0) { - $videoId = $this->getVideoIDFromSiteID($column, $show[$column]); + $videoId = $this->getVideoIDFromSiteID($column, (int) $show[$column]); } if ($videoId !== false) { break; @@ -362,7 +364,7 @@ abstract class AbstractTvProvider extends BaseVideoProvider if ($series > 0 && $episode > 0) { $queryString = sprintf('tve.series = %d AND tve.episode = %d', $series, $episode); } elseif (! empty($airdate)) { - $queryString = sprintf('DATE(tve.firstaired) = %s', escapeString(date('Y-m-d', strtotime($airdate)))); + $queryString = sprintf('DATE(tve.firstaired) = %s', escapeString(date('Y-m-d', strtotime((string) $airdate)))); } else { return false; } diff --git a/app/Services/TvProcessing/Providers/BaseVideoProvider.php b/app/Services/TvProcessing/Providers/BaseVideoProvider.php index d269bb3a8..10250acde 100644 --- a/app/Services/TvProcessing/Providers/BaseVideoProvider.php +++ b/app/Services/TvProcessing/Providers/BaseVideoProvider.php @@ -1,5 +1,7 @@ getTitleExact($titleWithoutYear, $type, $source); if ($res !== 0) { @@ -230,7 +232,7 @@ abstract class BaseVideoProvider if (! empty($title)) { $sql = Video::query() - ->where('title', 'like', rtrim($title, '%')) + ->where('title', 'like', rtrim((string) $title, '%')) ->where('type', $type); if ($source > 0) { $sql->where('source', $source); @@ -244,7 +246,7 @@ abstract class BaseVideoProvider if (empty($return)) { $sql = Video::query() ->join('videos_aliases', 'videos.id', '=', 'videos_aliases.videos_id') - ->where('videos_aliases.title', '=', rtrim($title, '%')) + ->where('videos_aliases.title', '=', rtrim((string) $title, '%')) ->where('type', $type); if ($source > 0) { $sql->where('videos.source', $source); diff --git a/app/Services/TvProcessing/Providers/LocalDbProvider.php b/app/Services/TvProcessing/Providers/LocalDbProvider.php index 9cc1029ba..8a13c7b3b 100644 --- a/app/Services/TvProcessing/Providers/LocalDbProvider.php +++ b/app/Services/TvProcessing/Providers/LocalDbProvider.php @@ -1,5 +1,7 @@ getVideoIDFromSiteID('tvdb', $tmdbShow['tvdb']); + $dupeCheck = $this->getVideoIDFromSiteID('tvdb', (int) $tmdbShow['tvdb']); if ($dupeCheck === false) { $videoId = $this->add($tmdbShow); $siteId = $tmdbShow['tmdb']; @@ -118,8 +120,8 @@ class TmdbProvider extends AbstractTvProvider // Now that we have valid video and tmdb ids, try to get the poster $this->getPoster($videoId); - $seriesNo = preg_replace('/^S0*/i', '', $release['season']); - $episodeNo = preg_replace('/^E0*/i', '', $release['episode']); + $seriesNo = preg_replace('/^S0*/i', '', (string) $release['season']); + $episodeNo = preg_replace('/^E0*/i', '', (string) $release['episode']); $hasAirdate = ! empty($release['airdate']); if ($episodeNo === 'all') { @@ -494,7 +496,7 @@ class TmdbProvider extends AbstractTvProvider $imdbId = 0; $externalIds = TmdbClient::getArray($show, 'external_ids'); if (! empty($externalIds['imdb_id'])) { - preg_match('/tt(?P\d{6,8})$/i', $externalIds['imdb_id'], $imdb); + preg_match('/tt(?P\d{6,8})$/i', (string) $externalIds['imdb_id'], $imdb); $imdbId = $imdb['imdbid'] ?? 0; } diff --git a/app/Services/TvProcessing/Providers/TraktProvider.php b/app/Services/TvProcessing/Providers/TraktProvider.php index 23809a7f1..8a91fe717 100644 --- a/app/Services/TvProcessing/Providers/TraktProvider.php +++ b/app/Services/TvProcessing/Providers/TraktProvider.php @@ -1,5 +1,7 @@ getPoster($videoId, $traktid); - $seasonNo = preg_replace('/^S0*/i', '', $release['season']); - $episodeNo = preg_replace('/^E0*/i', '', $release['episode']); + $seasonNo = preg_replace('/^S0*/i', '', (string) $release['season']); + $episodeNo = preg_replace('/^E0*/i', '', (string) $release['episode']); if ($episodeNo === 'all') { // Set the video ID and leave episode 0 @@ -383,7 +385,7 @@ class TraktProvider extends AbstractTvProvider */ public function formatShowInfo(mixed $show): array { - preg_match('/tt(?P\d{6,8})$/i', $show['ids']['imdb'], $imdb); + preg_match('/tt(?P\d{6,8})$/i', (string) ($show['ids']['imdb'] ?? ''), $imdb); $this->posterUrl = $show['images']['poster']['thumb'] ?? ''; $this->fanartUrl = $show['images']['fanart']['thumb'] ?? ''; $this->localizedTZ = $show['airs']['timezone'] ?? ''; diff --git a/app/Services/TvProcessing/Providers/TvMazeProvider.php b/app/Services/TvProcessing/Providers/TvMazeProvider.php index 5b905627e..fd2fc4b39 100644 --- a/app/Services/TvProcessing/Providers/TvMazeProvider.php +++ b/app/Services/TvProcessing/Providers/TvMazeProvider.php @@ -1,5 +1,7 @@ getVideoIDFromSiteID('tvdb', $tvMazeShow['tvdb']); + $dupeCheck = $this->getVideoIDFromSiteID('tvdb', (int) $tvMazeShow['tvdb']); } if ($dupeCheck === false) { $videoId = $this->add($tvMazeShow); @@ -137,8 +139,8 @@ class TvMazeProvider extends AbstractTvProvider // Now that we have valid video and tvmaze ids, try to get the poster $this->getPoster($videoId); - $seriesNo = preg_replace('/^S0*/i', '', $release['season']); - $episodeNo = preg_replace('/^E0*/i', '', $release['episode']); + $seriesNo = preg_replace('/^S0*/i', '', (string) $release['season']); + $episodeNo = preg_replace('/^E0*/i', '', (string) $release['episode']); if ($episodeNo === 'all') { // Set the video ID and leave episode 0 @@ -444,7 +446,7 @@ class TvMazeProvider extends AbstractTvProvider // Extract IMDB ID if available if (! empty($show->externalIDs['imdb'])) { - preg_match('/tt(?P\d{6,9})$/i', $show->externalIDs['imdb'], $imdb); + preg_match('/tt(?P\d{6,9})$/i', (string) $show->externalIDs['imdb'], $imdb); $imdbId = (int) ($imdb['imdbid'] ?? 0); } diff --git a/app/Services/TvProcessing/Providers/TvdbProvider.php b/app/Services/TvProcessing/Providers/TvdbProvider.php index fddab7ee4..9a867c097 100644 --- a/app/Services/TvProcessing/Providers/TvdbProvider.php +++ b/app/Services/TvProcessing/Providers/TvdbProvider.php @@ -1,5 +1,7 @@ client->series()->artworks($show->tvdb_id); + $poster = $this->client->series()->artworks((int) $show->tvdb_id); $poster = collect($poster)->where('type', 2)->sortByDesc('score')->first(); $this->posterUrl = ! empty($poster->image) ? $poster->image : ''; } catch (ResourceNotFoundException $e) { @@ -402,8 +404,8 @@ class TvdbProvider extends AbstractTvProvider $imdbId = 0; $imdbIdObj = null; try { - $imdbIdObj = $this->client->series()->extended($show->tvdb_id); - preg_match('/tt(?P\d{6,9})$/i', $imdbIdObj->getIMDBId(), $imdb); + $imdbIdObj = $this->client->series()->extended((int) $show->tvdb_id); + preg_match('/tt(?P\d{6,9})$/i', (string) ($imdbIdObj->getIMDBId() ?? ''), $imdb); $imdbId = $imdb['imdbid'] ?? 0; } catch (ResourceNotFoundException $e) { cli()->error('Show ImdbId not found on TVDB'); @@ -412,7 +414,7 @@ class TvdbProvider extends AbstractTvProvider } // Look up TMDB and Trakt IDs using available external IDs - $externalIds = $this->lookupExternalIds($show->tvdb_id, $imdbId); + $externalIds = $this->lookupExternalIds((int) $show->tvdb_id, $imdbId); return [ 'type' => parent::TYPE_TV, diff --git a/app/Services/TvProcessing/TvProcessingPassable.php b/app/Services/TvProcessing/TvProcessingPassable.php index a41e8bca5..8f3703fad 100644 --- a/app/Services/TvProcessing/TvProcessingPassable.php +++ b/app/Services/TvProcessing/TvProcessingPassable.php @@ -1,5 +1,7 @@ searchName); + return (bool) preg_match($pattern, (string) $this->searchName); } } diff --git a/app/Services/TvProcessor.php b/app/Services/TvProcessor.php index 8b316b0c2..33634591b 100644 --- a/app/Services/TvProcessor.php +++ b/app/Services/TvProcessor.php @@ -1,5 +1,7 @@ but returns array\\.$#' - identifier: return.type - count: 1 - path: app/Extensions/helper/helpers.php - - message: '#^Class App\\Facades\\Elasticsearch has PHPDoc tag @method for method bulk\(\) parameter \#1 \$params with no value type specified in iterable type array\.$#' identifier: missingType.iterableValue diff --git a/resources/views/admin/site/tmux-edit-content.blade.php b/resources/views/admin/site/tmux-edit-content.blade.php new file mode 100644 index 000000000..ff543f271 --- /dev/null +++ b/resources/views/admin/site/tmux-edit-content.blade.php @@ -0,0 +1,498 @@ +
    +
    + +
    +

    + {{ $title }} +

    +
    + + + @if(session('success')) +
    +

    + {{ session('success') }} +

    +
    + @endif + + +
    + @csrf + + + +
    + +
    +

    Tmux - How It Works

    +
    +

    Tmux is a screen multiplexer and at least version 1.6 is required. It is used here to allow multiple windows per session and multiple panes per window.

    +

    Each script is run in its own shell environment. It is not looped, but allowed to run once and then exit. This notifies tmux that the pane is dead and can then be respawned with another iteration of the script in a new shell environment.

    +

    This allows for scripts that crash to be restarted without user intervention.

    +
    +

    NOTICE:

    +

    If "Save Tmux Settings" is the last thing you did on this page, refreshing will save the current form values again, not reload from database.

    +
    +
    +
    + + +
    +

    Monitor Settings

    +
    + + + @foreach($yesno_ids as $index => $val) + + @endforeach + + + + +
    + + seconds +
    +
    + + + + + + + + +
    +
    + + +
    +

    Sequential Settings

    +
    + + + @foreach($sequential_ids as $index => $val) + + @endforeach + + + + +
    + + seconds +
    +
    + +
    +

    Sequential mode is not recommended as it's not tested enough.

    +
    +
    +
    + + +
    +

    Update Binaries Settings

    +
    + + + @foreach($binaries_ids as $index => $val) + + @endforeach + + + + +
    + + seconds +
    +
    + + +
    + + minutes +
    +
    +
    +
    + + +
    +

    Backfill Settings

    +
    +
    + + + @foreach($backfill_ids as $index => $val) + + @endforeach + + + + + + @foreach($backfill_group_ids as $index => $val) + + @endforeach + + + + + + @foreach($backfill_days_ids as $index => $val) + + @endforeach + + +
    + + + + + + + + + + +
    + + seconds +
    +
    + + + + @foreach($yesno_ids as $index => $val) + + @endforeach + + +
    +
    + + +
    +

    Update Releases Settings

    +
    + + + @foreach($releases_ids as $index => $val) + + @endforeach + + + + +
    + + seconds +
    +
    +
    +
    + + +
    +

    Postprocessing Settings

    +
    + + + @foreach($post_ids as $index => $val) + + @endforeach + + + + +
    + + seconds +
    +
    + + +
    + + seconds +
    +
    + + + + @foreach($yesno_ids as $index => $val) + + @endforeach + + + + +
    + + seconds +
    +
    + + + + @foreach($yesno_ids as $index => $val) + + @endforeach + + + + +
    + + seconds +
    +
    +
    +
    + + +
    +

    Fix Release Names

    +
    + + + @foreach($yesno_ids as $index => $val) + + @endforeach + + + + +
    + + seconds +
    +
    +
    +
    + + +
    +

    Remove Crap Releases

    +
    + +
    + @foreach($fix_crap_radio_ids as $index => $val) + + @endforeach +
    +
    + +
    + +
    + @php + $selectedTypes = explode(',', $site['fix_crap'] ?? ''); + @endphp + @foreach($fix_crap_check_ids as $index => $val) + + @endforeach +
    +
    +
    + + +
    + + seconds +
    +
    +
    +
    + + +
    +

    Scrapers

    +
    + + + @foreach($yesno_ids as $index => $val) + + @endforeach + + +
    +
    + + + +
    +

    Console & Monitoring Tools

    +
    +

    + + Enable monitoring tools to display system metrics in separate tmux windows. Each tool requires the corresponding package to be installed on your system. +

    +
    + +
    + + + + @foreach($yesno_ids as $index => $val) + + @endforeach + + + + +
    + + + @foreach($yesno_ids as $index => $val) + + @endforeach + + + + + + @foreach($yesno_ids as $index => $val) + + @endforeach + + + + + + @foreach($yesno_ids as $index => $val) + + @endforeach + + +
    + + +
    + + + @foreach($yesno_ids as $index => $val) + + @endforeach + + + + + + + + + + @foreach($yesno_ids as $index => $val) + + @endforeach + + +
    + + + + + + +
    + + + @foreach($yesno_ids as $index => $val) + + @endforeach + + + + + + @foreach($yesno_ids as $index => $val) + + @endforeach + + +
    + + + + + +
    +

    + + Note: Enabling a monitoring tool without having it installed will cause the tmux window to fail. Install required packages before enabling them. +

    +
    +
    +
    + + +
    + + Save Tmux Settings + +
    +
    +
    +
    +
    diff --git a/resources/views/admin/site/tmux-edit.blade.php b/resources/views/admin/site/tmux-edit.blade.php index c4e22e406..987116cd6 100644 --- a/resources/views/admin/site/tmux-edit.blade.php +++ b/resources/views/admin/site/tmux-edit.blade.php @@ -3,503 +3,5 @@ {{-- Styles moved to resources/css/csp-safe.css --}} @section('content') -
    -
    - -
    -

    - {{ $title }} -

    -
    - - - @if(session('success')) -
    -

    - {{ session('success') }} -

    -
    - @endif - - -
    - @csrf - - - -
    - -
    -

    Tmux - How It Works

    -
    -

    Tmux is a screen multiplexer and at least version 1.6 is required. It is used here to allow multiple windows per session and multiple panes per window.

    -

    Each script is run in its own shell environment. It is not looped, but allowed to run once and then exit. This notifies tmux that the pane is dead and can then be respawned with another iteration of the script in a new shell environment.

    -

    This allows for scripts that crash to be restarted without user intervention.

    -
    -

    NOTICE:

    -

    If "Save Tmux Settings" is the last thing you did on this page, refreshing will save the current form values again, not reload from database.

    -
    -
    -
    - - -
    -

    Monitor Settings

    -
    - - - @foreach($yesno_ids as $index => $val) - - @endforeach - - - - -
    - - seconds -
    -
    - - - - - - - - -
    -
    - - -
    -

    Sequential Settings

    -
    - - - @foreach($sequential_ids as $index => $val) - - @endforeach - - - - -
    - - seconds -
    -
    - -
    -

    Sequential mode is not recommended as it's not tested enough.

    -
    -
    -
    - - -
    -

    Update Binaries Settings

    -
    - - - @foreach($binaries_ids as $index => $val) - - @endforeach - - - - -
    - - seconds -
    -
    - - -
    - - minutes -
    -
    -
    -
    - - -
    -

    Backfill Settings

    -
    -
    - - - @foreach($backfill_ids as $index => $val) - - @endforeach - - - - - - @foreach($backfill_group_ids as $index => $val) - - @endforeach - - - - - - @foreach($backfill_days_ids as $index => $val) - - @endforeach - - -
    - - - - - - - - - - -
    - - seconds -
    -
    - - - - @foreach($yesno_ids as $index => $val) - - @endforeach - - -
    -
    - - -
    -

    Update Releases Settings

    -
    - - - @foreach($releases_ids as $index => $val) - - @endforeach - - - - -
    - - seconds -
    -
    -
    -
    - - -
    -

    Postprocessing Settings

    -
    - - - @foreach($post_ids as $index => $val) - - @endforeach - - - - -
    - - seconds -
    -
    - - -
    - - seconds -
    -
    - - - - @foreach($yesno_ids as $index => $val) - - @endforeach - - - - -
    - - seconds -
    -
    - - - - @foreach($yesno_ids as $index => $val) - - @endforeach - - - - -
    - - seconds -
    -
    -
    -
    - - -
    -

    Fix Release Names

    -
    - - - @foreach($yesno_ids as $index => $val) - - @endforeach - - - - -
    - - seconds -
    -
    -
    -
    - - -
    -

    Remove Crap Releases

    -
    - -
    - @foreach($fix_crap_radio_ids as $index => $val) - - @endforeach -
    -
    - -
    - -
    - @php - $selectedTypes = explode(',', $site['fix_crap'] ?? ''); - @endphp - @foreach($fix_crap_check_ids as $index => $val) - - @endforeach -
    -
    -
    - - -
    - - seconds -
    -
    -
    -
    - - -
    -

    Scrapers

    -
    - - - @foreach($yesno_ids as $index => $val) - - @endforeach - - -
    -
    - - - -
    -

    Console & Monitoring Tools

    -
    -

    - - Enable monitoring tools to display system metrics in separate tmux windows. Each tool requires the corresponding package to be installed on your system. -

    -
    - -
    - - - - @foreach($yesno_ids as $index => $val) - - @endforeach - - - - -
    - - - @foreach($yesno_ids as $index => $val) - - @endforeach - - - - - - @foreach($yesno_ids as $index => $val) - - @endforeach - - - - - - @foreach($yesno_ids as $index => $val) - - @endforeach - - -
    - - -
    - - - @foreach($yesno_ids as $index => $val) - - @endforeach - - - - - - - - - - @foreach($yesno_ids as $index => $val) - - @endforeach - - -
    - - - - - - -
    - - - @foreach($yesno_ids as $index => $val) - - @endforeach - - - - - - @foreach($yesno_ids as $index => $val) - - @endforeach - - -
    - - - - - -
    -

    - - Note: Enabling a monitoring tool without having it installed will cause the tmux window to fail. Install required packages before enabling them. -

    -
    -
    -
    - - -
    - - Save Tmux Settings - -
    -
    -
    -
    -
    +@include('admin.site.tmux-edit-content') @endsection -