option('threshold'); // Percentage difference threshold $limit = $this->option('limit'); $checkSeasonPack = $this->option('season-pack'); $direction = $this->option('direction'); $shouldRename = $this->option('rename'); $nameFixingService = new NameFixingService; $query = Release::query() ->select([ 'releases.id', 'releases.searchname', 'releases.name', 'releases.groups_id', 'releases.categories_id', DB::raw('releases.size / POW(1024, 3) as release_size'), DB::raw('SUM(release_files.size) / POW(1024, 3) as files_total_size'), DB::raw('(releases.size - SUM(release_files.size)) / POW(1024, 3) as size_diff'), DB::raw('((releases.size - SUM(release_files.size)) / releases.size * 100) as diff_percent'), ]) ->join('release_files', 'releases.id', '=', 'release_files.releases_id') ->where('releases.searchname', 'REGEXP', 'S[0-9]{1,3}E[0-9]{1,3}') ->groupBy('releases.id'); // Apply direction filter if ($direction === 'bigger') { $query->having('size_diff', '>', 0) ->having('diff_percent', '>', $threshold); } elseif ($direction === 'smaller') { $query->having('size_diff', '<', 0) ->having('diff_percent', '<', -$threshold); } else { $query->having(DB::raw('ABS(diff_percent)'), '>', $threshold); } // Order by ID if renaming, otherwise by diff_percent $query->orderBy($shouldRename ? 'releases.id' : 'diff_percent', $shouldRename ? 'asc' : 'desc'); if ($limit > 0) { $query->limit((int) $limit); } $mismatches = $query->get(); if ($checkSeasonPack) { $mismatches = $mismatches->filter(function ($release) use ($nameFixingService) { return $nameFixingService->isSeasonPack($release->name); }); } if ($mismatches->isEmpty()) { $this->info('No releases found with size mismatches above '.$threshold.'%' .($checkSeasonPack ? ' that are season packs' : '')); return; } if ($shouldRename) { $this->info("\nAttempting to rename ".$mismatches->count()." releases...\n"); foreach ($mismatches as $release) { $this->attemptRename($release, $nameFixingService); } $this->outputReleaseIdsAsCsv($mismatches); return; } // Regular table output for non-rename mode $headers = ['Release ID', 'Searchname', 'Release Size', 'Files Total', 'Difference', 'Diff %']; $rows = $mismatches->map(function ($release) { return [ $release->id, $release->searchname, 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).'%', ]; }); $this->table($headers, $rows); $this->info("\nFound ".$mismatches->count().' releases with size mismatches above '.$threshold.'%'); $this->outputReleaseIdsAsCsv($mismatches); } private function attemptRename(Release $release, NameFixingService $nameFixingService): ?string { if (preg_match(ReleaseUpdateService::PREDB_REGEX, $this->stripDomainFromString($release->name), $matches)) { $newName = $matches[1]; $nameFixingService->getUpdateService()->updateRelease( release: $release, name: $newName, method: 'size-mismatch / season pack', echo: true, type: '', nameStatus: true, show: true, preId: 0 ); return $newName; } return null; } private function outputReleaseIdsAsCsv(mixed $mismatches): void { $releaseIds = $mismatches->pluck('id')->join(','); $this->line("\nRelease IDs in CSV format:"); $this->line($releaseIds); } private function stripDomainFromString(string $str): string { return preg_replace("/www\.[^\s]+\.[a-z]{2,4}/i", '', $str); } }