feat: expose imdb and douban ids in torrent ratings

Signed-off-by: Qi HU <github@spcsky.com>
This commit is contained in:
Qi HU
2025-11-05 21:31:50 +08:00
parent 1430ba68dc
commit 450ff5af34
+142 -16
View File
@@ -304,15 +304,15 @@ HTML;
if (!isset($siteIdAndRating[$site])) { if (!isset($siteIdAndRating[$site])) {
continue; continue;
} }
$rating = $siteIdAndRating[$site]; [$ratingValue, $externalId] = $this->normalizeRatingInfo($site, $siteIdAndRating[$site]);
if (empty($rating)) { if ($ratingValue === '' || $ratingValue === null) {
continue; continue;
} }
if ($count > 2) { if ($count > 2) {
//only show the first two //only show the first two
break; break;
} }
$ratingIcons[] = $this->getRatingIcon($site, $rating); $ratingIcons[] = $this->getRatingIcon($site, $ratingValue, $externalId);
$count++; $count++;
} }
if (empty($ratingIcons)) { if (empty($ratingIcons)) {
@@ -323,14 +323,22 @@ HTML;
return $result; return $result;
} }
public function getRatingIcon($siteId, $rating): string public function getRatingIcon($siteId, $rating, $externalId = null): string
{ {
if (is_numeric($rating)) { if (is_numeric($rating)) {
$rating = number_format($rating, 1); $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( $result = sprintf(
'<div style="display: flex;align-content: center;justify-content: space-between;padding: 2px 0"><img src="%s" alt="%s" title="%s" style="max-width: 16px;max-height: 16px"/><span>%s</span></div>', '<div style="display: flex;align-content: center;justify-content: space-between;padding: 2px 0"><img src="%s" alt="%s" title="%s" style="max-width: 16px;max-height: 16px"/><span%s>%s</span></div>',
self::$validSites[$siteId]['rating_average_img'], $siteId, $siteId, $rating self::$validSites[$siteId]['rating_average_img'], $siteId, $siteId, $spanAttr, $rating
); );
return $result; return $result;
} }
@@ -351,12 +359,17 @@ HTML;
{ {
$results = []; $results = [];
$log = ""; $log = "";
$sharedFallbackLinks = [];
if (!empty($ptGenData['__link']) && is_string($ptGenData['__link'])) {
$sharedFallbackLinks[] = $ptGenData['__link'];
}
//First, get from PTGen //First, get from PTGen
foreach (self::$validSites as $site => $info) { foreach (self::$validSites as $site => $info) {
if (!isset($ptGenData[$site]['data'])) { if (!isset($ptGenData[$site])) {
continue; continue;
} }
$data = $ptGenData[$site]['data']; $siteEntry = $ptGenData[$site];
$data = is_array($siteEntry) && isset($siteEntry['data']) ? $siteEntry['data'] : [];
$log .= ", handling site: $site"; $log .= ", handling site: $site";
$rating = ''; $rating = '';
if (isset($data['__rating'])) { if (isset($data['__rating'])) {
@@ -376,18 +389,39 @@ HTML;
} }
} }
} }
if (!empty($rating)) {
$results[$site] = $rating; $fallbackLinks = $sharedFallbackLinks;
$log .= ", get rating: $rating"; if ($site === self::SITE_IMDB && !empty($imdbLink)) {
} else { $fallbackLinks[] = $imdbLink;
$log .= ", can't get rating";
} }
$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 //Second, imdb can get from imdb api
if (!isset($results[self::SITE_IMDB]) && !empty($imdbLink)) { if (!isset($results[self::SITE_IMDB]) && !empty($imdbLink)) {
$imdb = new Imdb(); $imdb = new Imdb();
$imdbRating = $imdb->getRating($imdbLink); $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"; $log .= ", again 'imdb' from: $imdbLink -> $imdbRating";
} }
//Otherwise, get from desc //Otherwise, get from desc
@@ -403,7 +437,11 @@ HTML;
$log .= ", at last, trying to get '$site' from desc with pattern: $pattern"; $log .= ", at last, trying to get '$site' from desc with pattern: $pattern";
if (preg_match($pattern, $desc, $matches)) { if (preg_match($pattern, $desc, $matches)) {
$log .= ", get " . $matches[1]; $log .= ", get " . $matches[1];
$results[$site] = $matches[1]; $externalId = $this->extractExternalId($site, $ptGenData[$site] ?? []);
$results[$site] = [
'rating' => $matches[1],
'id' => $externalId,
];
} else { } else {
$log .= ", not match"; $log .= ", not match";
} }
@@ -413,6 +451,87 @@ HTML;
return $results; 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 public function updateTorrentPtGen(int $id): bool|array
{ {
$now = Carbon::now(); $now = Carbon::now();
@@ -457,7 +576,14 @@ HTML;
} }
$siteIdAndRating = $this->listRatings($ptGenInfo, $torrent->url, $extra->descr); $siteIdAndRating = $this->listRatings($ptGenInfo, $torrent->url, $extra->descr);
foreach ($siteIdAndRating as $key => $value) { 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['__link'] = $link;
$ptGenInfo['__updated_at'] = $now->toDateTimeString(); $ptGenInfo['__updated_at'] = $now->toDateTimeString();