From 1430ba68dcc0906eca6fb159168003a1de26cef5 Mon Sep 17 00:00:00 2001 From: Qi HU Date: Sun, 19 Oct 2025 23:13:33 +0800 Subject: [PATCH 1/2] Switch reCAPTCHA endpoints to recaptcha.net Signed-off-by: Qi HU --- app/Services/Captcha/Drivers/RecaptchaV2CaptchaDriver.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/Services/Captcha/Drivers/RecaptchaV2CaptchaDriver.php b/app/Services/Captcha/Drivers/RecaptchaV2CaptchaDriver.php index 04d0a157..7c9a8062 100644 --- a/app/Services/Captcha/Drivers/RecaptchaV2CaptchaDriver.php +++ b/app/Services/Captcha/Drivers/RecaptchaV2CaptchaDriver.php @@ -45,7 +45,7 @@ class RecaptchaV2CaptchaDriver implements CaptchaDriverInterface '%s
%s', htmlspecialchars($label, ENT_QUOTES, 'UTF-8'), $attributes, - '' + '' ); } @@ -74,7 +74,7 @@ class RecaptchaV2CaptchaDriver implements CaptchaDriverInterface $data['remoteip'] = $remoteIp; } - $result = $this->sendVerificationRequest('https://www.google.com/recaptcha/api/siteverify', $data); + $result = $this->sendVerificationRequest('https://www.recaptcha.net/recaptcha/api/siteverify', $data); if (!($result['success'] ?? false)) { throw new CaptchaValidationException('Captcha verification failed.'); From 450ff5af347b23974d2da898129a5e52e9857b9d Mon Sep 17 00:00:00 2001 From: Qi HU Date: Wed, 5 Nov 2025 21:31:50 +0800 Subject: [PATCH 2/2] feat: expose imdb and douban ids in torrent ratings Signed-off-by: Qi HU --- nexus/PTGen/PTGen.php | 158 +++++++++++++++++++++++++++++++++++++----- 1 file changed, 142 insertions(+), 16 deletions(-) diff --git a/nexus/PTGen/PTGen.php b/nexus/PTGen/PTGen.php index 537baffc..8c459456 100644 --- a/nexus/PTGen/PTGen.php +++ b/nexus/PTGen/PTGen.php @@ -304,15 +304,15 @@ HTML; if (!isset($siteIdAndRating[$site])) { continue; } - $rating = $siteIdAndRating[$site]; - if (empty($rating)) { + [$ratingValue, $externalId] = $this->normalizeRatingInfo($site, $siteIdAndRating[$site]); + if ($ratingValue === '' || $ratingValue === null) { continue; } if ($count > 2) { //only show the first two break; } - $ratingIcons[] = $this->getRatingIcon($site, $rating); + $ratingIcons[] = $this->getRatingIcon($site, $ratingValue, $externalId); $count++; } if (empty($ratingIcons)) { @@ -323,14 +323,22 @@ HTML; return $result; } - public function getRatingIcon($siteId, $rating): string + public function getRatingIcon($siteId, $rating, $externalId = null): string { if (is_numeric($rating)) { $rating = number_format($rating, 1); } + $spanAttr = ''; + if (!empty($externalId)) { + if ($siteId === self::SITE_IMDB) { + $spanAttr = sprintf(' data-imdbid="%s"', htmlspecialchars($externalId, ENT_QUOTES)); + } elseif ($siteId === self::SITE_DOUBAN) { + $spanAttr = sprintf(' data-doubanid="%s"', htmlspecialchars($externalId, ENT_QUOTES)); + } + } $result = sprintf( - '
%s%s
', - self::$validSites[$siteId]['rating_average_img'], $siteId, $siteId, $rating + '
%s%s
', + self::$validSites[$siteId]['rating_average_img'], $siteId, $siteId, $spanAttr, $rating ); return $result; } @@ -351,12 +359,17 @@ HTML; { $results = []; $log = ""; + $sharedFallbackLinks = []; + if (!empty($ptGenData['__link']) && is_string($ptGenData['__link'])) { + $sharedFallbackLinks[] = $ptGenData['__link']; + } //First, get from PTGen foreach (self::$validSites as $site => $info) { - if (!isset($ptGenData[$site]['data'])) { + if (!isset($ptGenData[$site])) { continue; } - $data = $ptGenData[$site]['data']; + $siteEntry = $ptGenData[$site]; + $data = is_array($siteEntry) && isset($siteEntry['data']) ? $siteEntry['data'] : []; $log .= ", handling site: $site"; $rating = ''; if (isset($data['__rating'])) { @@ -376,18 +389,39 @@ HTML; } } } - if (!empty($rating)) { - $results[$site] = $rating; - $log .= ", get rating: $rating"; - } else { - $log .= ", can't get rating"; + + $fallbackLinks = $sharedFallbackLinks; + if ($site === self::SITE_IMDB && !empty($imdbLink)) { + $fallbackLinks[] = $imdbLink; } + $externalId = $this->extractExternalId($site, $siteEntry, $fallbackLinks); + + $allowEmptyRatingWithId = in_array($site, [self::SITE_IMDB, self::SITE_DOUBAN], true); + if (($rating === '' || $rating === null) && !($allowEmptyRatingWithId && $externalId)) { + $log .= ", can't get rating"; + continue; + } + if (($rating === '' || $rating === null) && $allowEmptyRatingWithId && $externalId) { + $rating = 'N/A'; + $log .= ", missing rating but found id: $externalId"; + } else { + $log .= ", get rating: $rating"; + } + + $results[$site] = [ + 'rating' => $rating, + 'id' => $externalId, + ]; } //Second, imdb can get from imdb api if (!isset($results[self::SITE_IMDB]) && !empty($imdbLink)) { $imdb = new Imdb(); $imdbRating = $imdb->getRating($imdbLink); - $results[self::SITE_IMDB] = $imdbRating; + $externalId = $this->extractExternalId(self::SITE_IMDB, $imdbLink); + $results[self::SITE_IMDB] = [ + 'rating' => $imdbRating, + 'id' => $externalId, + ]; $log .= ", again 'imdb' from: $imdbLink -> $imdbRating"; } //Otherwise, get from desc @@ -403,7 +437,11 @@ HTML; $log .= ", at last, trying to get '$site' from desc with pattern: $pattern"; if (preg_match($pattern, $desc, $matches)) { $log .= ", get " . $matches[1]; - $results[$site] = $matches[1]; + $externalId = $this->extractExternalId($site, $ptGenData[$site] ?? []); + $results[$site] = [ + 'rating' => $matches[1], + 'id' => $externalId, + ]; } else { $log .= ", not match"; } @@ -413,6 +451,87 @@ HTML; return $results; } + private function normalizeRatingInfo(string $siteId, $value): array + { + $rating = ''; + $externalId = null; + if (is_array($value)) { + $rating = $value['rating'] ?? ''; + $externalId = $value['id'] ?? null; + } else { + $rating = $value; + } + if (($rating === '' || $rating === null) && in_array($siteId, [self::SITE_IMDB, self::SITE_DOUBAN], true) && $externalId) { + $rating = 'N/A'; + } + return [$rating, $externalId]; + } + + private function extractExternalId(string $site, $siteEntry, array $fallbackLinks = []): ?string + { + if (!isset(self::$validSites[$site])) { + return null; + } + $pattern = self::$validSites[$site]['url_pattern'] ?? null; + $candidates = $fallbackLinks; + $candidates = array_merge($candidates, $this->collectStringValues($siteEntry)); + foreach ($candidates as $candidate) { + if (!is_string($candidate) || $candidate === '') { + continue; + } + $candidateId = $this->matchExternalId($site, $pattern, $candidate); + if ($candidateId) { + return $candidateId; + } + } + return null; + } + + private function collectStringValues($data): array + { + $results = []; + $this->appendStringValues($data, $results); + return $results; + } + + private function appendStringValues($value, array &$results): void + { + if (is_string($value)) { + $results[] = $value; + return; + } + if (!is_array($value)) { + return; + } + foreach ($value as $item) { + $this->appendStringValues($item, $results); + } + } + + private function matchExternalId(string $site, ?string $pattern, string $candidate): ?string + { + $candidate = trim($candidate); + if ($candidate === '') { + return null; + } + if ($pattern && preg_match($pattern, $candidate, $matches) && !empty($matches[1])) { + return $matches[1]; + } + if ($site === self::SITE_IMDB) { + if (preg_match('/^tt\d+$/i', $candidate)) { + return strtolower($candidate); + } + if (preg_match('/^\d+$/', $candidate)) { + return 'tt' . str_pad($candidate, 7, '0', STR_PAD_LEFT); + } + } else { + if (preg_match('/^\d+$/', $candidate)) { + return $candidate; + } + } + return null; + } + public function updateTorrentPtGen(int $id): bool|array { $now = Carbon::now(); @@ -457,7 +576,14 @@ HTML; } $siteIdAndRating = $this->listRatings($ptGenInfo, $torrent->url, $extra->descr); foreach ($siteIdAndRating as $key => $value) { - $ptGenInfo[$key]['data']["__rating"] = $value; + if (!isset($ptGenInfo[$key]['data']) || !is_array($ptGenInfo[$key]['data'])) { + continue; + } + $ratingValue = is_array($value) ? ($value['rating'] ?? '') : $value; + $ptGenInfo[$key]['data']["__rating"] = $ratingValue; + if (is_array($value) && isset($value['id']) && $value['id'] !== '') { + $ptGenInfo[$key]['data']["__id"] = $value['id']; + } } $ptGenInfo['__link'] = $link; $ptGenInfo['__updated_at'] = $now->toDateTimeString();