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.');
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 ',
- self::$validSites[$siteId]['rating_average_img'], $siteId, $siteId, $rating
+ '
%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();