contentEx = array('MovieInfo_Plugin', 'parse');
Typecho_Plugin::factory('Widget_Abstract_Contents')->excerptEx = array('MovieInfo_Plugin', 'parse');
Typecho_Plugin::factory('admin/write-post.php')->bottom = array('MovieInfo_Plugin', 'renderButton');
Typecho_Plugin::factory('admin/write-page.php')->bottom = array('MovieInfo_Plugin', 'renderButton');
$cacheDir = dirname(__FILE__) . '/cache/';
if (!file_exists($cacheDir)) mkdir($cacheDir, 0755, true);
return 'TMDB电影插件激活成功!';
}
/**
* 禁用插件
*/
public static function deactivate()
{
return '插件已禁用';
}
/**
* 配置面板
*/
public static function config(Typecho_Widget_Helper_Form $form)
{
// TMDB API 配置
$tmdbApiKey = new Typecho_Widget_Helper_Form_Element_Text('tmdbApiKey', NULL, '',
'TMDB API Key', '在TMDB网站申请的API密钥');
$form->addInput($tmdbApiKey);
$language = new Typecho_Widget_Helper_Form_Element_Select('language',
array(
'zh-CN' => '中文',
'en-US' => '英文',
'ja-JP' => '日文',
'ko-KR' => '韩文',
'fr-FR' => '法文'
), 'zh-CN', '显示语言', '电影信息的显示语言');
$form->addInput($language);
$cacheEnable = new Typecho_Widget_Helper_Form_Element_Radio('cacheEnable',
array('1' => '启用', '0' => '禁用'),
'1', '启用缓存', '缓存电影信息,提升访问速度');
$form->addInput($cacheEnable);
$cacheTime = new Typecho_Widget_Helper_Form_Element_Text('cacheTime', NULL, '30',
'缓存时间(天)', '电影信息缓存保留天数');
$cacheTime->addRule('isInteger', '请输入整数');
$form->addInput($cacheTime);
$imageProxy = new Typecho_Widget_Helper_Form_Element_Text('imageProxy', NULL,
'https://images.weserv.nl/?url=', '图片代理', '用于加载TMDB图片');
$form->addInput($imageProxy);
$defaultPoster = new Typecho_Widget_Helper_Form_Element_Text('defaultPoster', NULL,
'https://via.placeholder.com/300x450/CCCCCC/666666?text=No+Poster',
'默认海报', '当无法获取海报时显示的图片');
$form->addInput($defaultPoster);
$summaryLength = new Typecho_Widget_Helper_Form_Element_Text('summaryLength', NULL, '200',
'简介显示长度', '简介默认显示的最大字符数,超出部分可展开查看');
$summaryLength->addRule('isInteger', '请输入整数');
$form->addInput($summaryLength);
$expandText = new Typecho_Widget_Helper_Form_Element_Text('expandText', NULL, '展开',
'"展开"文字', '点击展开完整简介的文字');
$form->addInput($expandText);
$collapseText = new Typecho_Widget_Helper_Form_Element_Text('collapseText', NULL, '收起',
'"收起"文字', '点击收起简介的文字');
$form->addInput($collapseText);
$expandColor = new Typecho_Widget_Helper_Form_Element_Text('expandColor', NULL, '#0073aa',
'展开按钮颜色', '展开/收起按钮的文字颜色');
$form->addInput($expandColor);
// 独立页面每页显示条数设置
$pageSize = new Typecho_Widget_Helper_Form_Element_Text('pageSize', NULL, '10',
'独立页面每页显示条数', '在独立页面中每页显示的电影数量(1-50)');
$pageSize->addRule('isInteger', '请输入整数');
$pageSize->addRule(array(new MovieInfo_Plugin, 'validatePageSize'), '请输入1-50之间的整数');
$form->addInput($pageSize);
}
/**
* 验证页面显示条数
*/
public static function validatePageSize($value)
{
$value = intval($value);
if ($value < 1 || $value > 50) {
throw new Typecho_Widget_Exception('请输入1-50之间的整数');
}
return true;
}
/**
* 个人配置面板
*/
public static function personalConfig(Typecho_Widget_Helper_Form $form)
{
}
/**
* 解析短代码
*/
public static function parse($content, $widget, $lastResult)
{
$content = empty($lastResult) ? $content : $lastResult;
// 如果是独立页面,且内容中包含[all_movies]标记,则显示所有电影
if ($widget instanceof Widget_Archive && $widget->is('page')) {
$pattern = '/\[all_movies(?::page=(\d+))?\]/i';
if (preg_match_all($pattern, $content, $matches)) {
foreach ($matches[0] as $key => $match) {
$page = isset($matches[1][$key]) ? intval($matches[1][$key]) : 1;
$allMoviesHtml = self::renderAllMovies($page);
$content = str_replace($match, $allMoviesHtml, $content);
}
}
}
// 如果是单篇文章,解析电影短代码
if ($widget instanceof Widget_Archive && $widget->is('single')) {
// 匹配 [movie:数字] 或 [movie:数字:短评] 格式
$pattern = '/\[movie:(\d+)(?::([^\]]+))?\]/i';
if (preg_match_all($pattern, $content, $matches)) {
foreach ($matches[0] as $key => $match) {
$movieId = $matches[1][$key];
$reviewWithCustom = isset($matches[2][$key]) ? trim($matches[2][$key]) : '';
$review = '';
$customData = array();
// 解码短评和自定义数据
if (!empty($reviewWithCustom)) {
// 分离短评和自定义数据
if (strpos($reviewWithCustom, '|CUSTOM:') !== false) {
list($review, $customJson) = explode('|CUSTOM:', $reviewWithCustom, 2);
// 解码自定义数据
if (!empty($customJson)) {
$decodedJson = urldecode($customJson);
$customData = json_decode($decodedJson, true);
if (!is_array($customData)) {
$customData = array();
}
}
} else {
// 检查旧格式的自定义数据
if (preg_match('/^(自定义:(.*))$/u', $reviewWithCustom, $customMatch)) {
$review = '';
$customData = self::parseLegacyCustomData($customMatch[1]);
} else {
// 检查中文括号格式
if (preg_match('/^(.*?)(自定义:(.*))$/u', $reviewWithCustom, $customMatch)) {
$review = trim($customMatch[1]);
$customData = self::parseLegacyCustomData($customMatch[2]);
} else {
$review = $reviewWithCustom;
}
}
}
// 解码短评
if (!empty($review)) {
$review = str_replace(array('[', ']'), array('[', ']'), $review);
}
}
// 获取电影数据
$movieHtml = self::renderMovie($movieId, $review, $customData);
$content = str_replace($match, $movieHtml, $content);
}
}
}
return $content;
}
/**
* 解析旧格式的自定义数据
*/
private static function parseLegacyCustomData($customText)
{
$customData = array();
$pairs = explode(',', $customText);
foreach ($pairs as $pair) {
if (strpos($pair, ':') !== false) {
list($key, $value) = explode(':', $pair, 2);
switch (trim($key)) {
case '开始观看':
$customData['startDate'] = trim($value);
break;
case '结束观看':
$customData['watchDate'] = trim($value);
break;
case '观看方式':
$customData['watchMethod'] = trim($value);
break;
case '电影分类':
$customData['movieCategory'] = trim($value);
break;
case '推荐指数':
$starCount = substr_count($value, '★');
$customData['recommendation'] = $starCount;
break;
case '我的评分':
$customData['myRating'] = trim($value);
break;
}
}
}
return $customData;
}
/**
* 渲染单部电影信息
*/
private static function renderMovie($movieId, $review = '', $customData = array())
{
// 获取电影数据
$movieData = self::getMovieData($movieId, $review, $customData);
if (!$movieData || empty($movieData['title'])) {
return '
获取电影信息失败,ID:' . htmlspecialchars($movieId) . '
';
}
$options = Typecho_Widget::widget('Widget_Options')->plugin('MovieInfo');
$imageProxy = isset($options->imageProxy) ? $options->imageProxy : 'https://images.weserv.nl/?url=';
$defaultPoster = isset($options->defaultPoster) ? $options->defaultPoster :
'https://via.placeholder.com/300x450/CCCCCC/666666?text=No+Poster';
$summaryLength = isset($options->summaryLength) ? intval($options->summaryLength) : 200;
$expandText = isset($options->expandText) ? $options->expandText : '展开';
$collapseText = isset($options->collapseText) ? $options->collapseText : '收起';
$expandColor = isset($options->expandColor) ? $options->expandColor : '#0073aa';
$title = htmlspecialchars($movieData['title']);
$originalTitle = isset($movieData['original_title']) ? htmlspecialchars($movieData['original_title']) : '';
$releaseYear = isset($movieData['release_year']) ? htmlspecialchars($movieData['release_year']) : '未知';
$director = isset($movieData['director']) ? htmlspecialchars($movieData['director']) : '未知';
$genres = isset($movieData['genres']) ? $movieData['genres'] : array('未知');
$runtime = isset($movieData['runtime']) ? htmlspecialchars($movieData['runtime']) . '分钟' : '未知';
$summary = isset($movieData['overview']) ? $movieData['overview'] : '';
$rating = isset($movieData['vote_average']) ? floatval($movieData['vote_average']) : 0;
$ratingCount = isset($movieData['vote_count']) ? intval($movieData['vote_count']) : 0;
$productionCountries = isset($movieData['production_countries']) ? $movieData['production_countries'] : array('未知');
$review = htmlspecialchars($review);
// 自定义字段
$startDate = isset($customData['startDate']) ? htmlspecialchars($customData['startDate']) : '';
$watchDate = isset($customData['watchDate']) ? htmlspecialchars($customData['watchDate']) : '';
$watchMethod = isset($customData['watchMethod']) ? htmlspecialchars($customData['watchMethod']) : '';
$movieCategory = isset($customData['movieCategory']) ? htmlspecialchars($customData['movieCategory']) : '';
$recommendation = isset($customData['recommendation']) ? intval($customData['recommendation']) : 0;
$myRating = isset($customData['myRating']) ? htmlspecialchars($customData['myRating']) : '';
// 如果customData中没有,尝试从movieData中获取
if (empty($startDate) && isset($movieData['custom_start_date']) && !empty($movieData['custom_start_date'])) {
$startDate = htmlspecialchars($movieData['custom_start_date']);
}
if (empty($watchDate) && isset($movieData['custom_watch_date']) && !empty($movieData['custom_watch_date'])) {
$watchDate = htmlspecialchars($movieData['custom_watch_date']);
}
if (empty($watchMethod) && isset($movieData['custom_watch_method']) && !empty($movieData['custom_watch_method'])) {
$watchMethod = htmlspecialchars($movieData['custom_watch_method']);
}
if (empty($movieCategory) && isset($movieData['custom_movie_category']) && !empty($movieData['custom_movie_category'])) {
$movieCategory = htmlspecialchars($movieData['custom_movie_category']);
}
if ($recommendation == 0 && isset($movieData['custom_recommendation'])) {
$recommendation = intval($movieData['custom_recommendation']);
}
if (empty($myRating) && isset($movieData['custom_my_rating'])) {
$myRating = htmlspecialchars($movieData['custom_my_rating']);
}
// 检查简介长度
$isSummaryLong = false;
$summaryShort = $summary;
$plainSummary = strip_tags($summary);
if (mb_strlen($plainSummary, 'UTF-8') > $summaryLength) {
$isSummaryLong = true;
$plainShort = mb_substr($plainSummary, 0, $summaryLength, 'UTF-8');
$summaryShort = $plainShort . '...';
}
// 海报URL
$posterPath = isset($movieData['poster_path']) ? $movieData['poster_path'] : '';
if ($posterPath) {
$posterUrl = 'https://image.tmdb.org/t/p/w300' . $posterPath;
$posterSrc = $imageProxy . urlencode($posterUrl);
} else {
$posterSrc = $defaultPoster;
}
// 类型显示 - 确保不换行
$genresHtml = '';
if (is_array($genres)) {
$genresHtml = implode(' / ', $genres);
}
// 地区显示
$countriesHtml = '';
if (is_array($productionCountries)) {
$countriesHtml = implode(' / ', $productionCountries);
}
// TMDB评分显示 - 移除括号和评价人数
$ratingHtml = '';
if ($rating > 0) {
$ratingHtml = '
评分:
' . number_format($rating, 1) . '
';
}
// 我的评分显示
$myRatingHtml = '';
if (!empty($myRating)) {
$myRatingHtml = '
我的评分:
' . $myRating . '
';
}
// 推荐指数星星
$recommendationHtml = '';
if ($recommendation > 0) {
$recommendationHtml = '
推荐指数:';
for ($i = 1; $i <= 5; $i++) {
if ($i <= $recommendation) {
$recommendationHtml .= '★';
} else {
$recommendationHtml .= '★';
}
}
$recommendationHtml .= '
';
}
// 自定义信息 - 按图书插件逻辑处理开始和结束观看
$customInfoHtml = '';
$hasCustomInfo = false;
// 判断开始和结束观看日期是否相同
$hasStartDate = !empty($startDate);
$hasWatchDate = !empty($watchDate);
$datesEqual = $hasStartDate && $hasWatchDate && $startDate === $watchDate;
if ($hasStartDate || $hasWatchDate || $watchMethod || $movieCategory) {
$hasCustomInfo = true;
if ($datesEqual) {
// 开始和结束观看日期相同,显示"观看日期"
$customInfoHtml .= '
观看日期:
' . $startDate . '
';
} else {
// 日期不同或只有一个日期,分开显示
if ($startDate) {
$customInfoHtml .= '
开始观看:
' . $startDate . '
';
}
if ($watchDate) {
$customInfoHtml .= '
结束观看:
' . $watchDate . '
';
}
}
if ($watchMethod) {
$customInfoHtml .= '
观看方式:
' . $watchMethod . '
';
}
if ($movieCategory) {
$customInfoHtml .= '
电影分类:
' . $movieCategory . '
';
}
}
// 右侧栏完整HTML
$rightColumnHtml = '';
if ($myRatingHtml) {
$rightColumnHtml .= $myRatingHtml;
}
if ($recommendationHtml) {
$rightColumnHtml .= $recommendationHtml;
}
if ($customInfoHtml) {
$rightColumnHtml .= $customInfoHtml;
} else {
$rightColumnHtml .= '
观看记录:
暂无记录
';
}
// 简介部分HTML
$summaryHtml = '';
if ($isSummaryLong) {
$summaryHtml .= '
';
} else {
$summaryHtml .= '
' . $summary . '
';
}
$summaryHtml .= '
';
// 短评HTML
$reviewHtml = '';
if (!empty($review)) {
$reviewHtml = '
💭 我的影评
' . nl2br($review) . '
';
}
// 原始标题显示 - 仅作为鼠标悬停提示
$titleTooltip = '';
if ($originalTitle && $originalTitle != $title) {
$titleTooltip = ' title="原名:' . $originalTitle . '"';
}
$html = <<
:root {
--movie-bg-primary: #ffffff;
--movie-bg-secondary: #f9fafb;
--movie-bg-tertiary: #fafafa;
--movie-bg-card: #ffffff;
--movie-text-primary: #1a1a1a;
--movie-text-secondary: #666666;
--movie-text-tertiary: #999999;
--movie-primary-color: #0073aa;
--movie-primary-hover: #0056b3;
--movie-accent-color: #01d277;
--movie-accent-hover: #01b36b;
--movie-border-color: #e8e8e8;
--movie-border-light: #f0f0f0;
--movie-shadow-color: rgba(0,0,0,0.08);
--movie-rating-color: #ffac2d;
--movie-my-rating-color: #ff6b35;
--movie-star-color: #ff6b35;
--movie-star-empty-color: #dddddd;
--movie-button-bg: #f8f9fa;
--movie-button-hover-bg: #e9ecef;
--movie-review-bg: #f9f9f9;
}
@media (prefers-color-scheme: dark) {
:root {
--movie-bg-primary: #1a1a1a;
--movie-bg-secondary: #2d2d2d;
--movie-bg-tertiary: #2a2a2a;
--movie-bg-card: #2d2d2d;
--movie-text-primary: #e0e0e0;
--movie-text-secondary: #aaaaaa;
--movie-text-tertiary: #888888;
--movie-primary-color: #4dabf7;
--movie-primary-hover: #74c0fc;
--movie-accent-color: #20c997;
--movie-accent-hover: #12b886;
--movie-border-color: #404040;
--movie-border-light: #333333;
--movie-shadow-color: rgba(0,0,0,0.2);
--movie-rating-color: #ffc107;
--movie-my-rating-color: #ff922b;
--movie-star-color: #ff922b;
--movie-star-empty-color: #555555;
--movie-button-bg: #3a3a3a;
--movie-button-hover-bg: #4a4a4a;
--movie-review-bg: #3a3a3a;
}
}
.dark .markdown-body h2{border-bottom:0px!important;margin-top:0px!important;padding-bottom:0px!important;margin-bottom:0px!important;}
.movie-card-content {
background: var(--movie-bg-card);
border-radius: 12px;
box-shadow: 0 4px 12px var(--movie-shadow-color);
overflow: hidden;
border: 1px solid var(--movie-border-color);
transition: transform 0.3s ease, box-shadow 0.3s ease;
}
.movie-card-content:hover {
transform: translateY(-2px);
box-shadow: 0 6px 16px var(--movie-shadow-color);
}
.movie-header {
padding: 20px 25px;
background: linear-gradient(135deg, var(--movie-bg-secondary) 0%, var(--movie-bg-tertiary) 100%);
border-bottom: 1px solid var(--movie-border-light);
text-align: center;
}
.movie-title {
margin: 0;
font-size: 22px;
line-height: 1.3;
color: var(--movie-text-primary);
font-weight: 700;
}
.movie-title-link {
color: var(--movie-primary-color);
text-decoration: none;
transition: color 0.2s ease;
}
.movie-title-link:hover {
color: var(--movie-primary-hover);
text-decoration: underline;
}
.movie-content {
padding: 25px;
border-bottom: 1px solid var(--movie-border-light);
background: var(--movie-bg-card);
}
.movie-grid {
display: grid;
grid-template-columns: 120px 1fr 1fr;
gap: 25px;
align-items: stretch;
}
@media (max-width: 768px) {
.movie-grid {
grid-template-columns: 1fr;
gap: 20px;
}
.movie-grid > div:nth-child(2) {
border-right: none;
padding-right: 0;
}
.movie-grid > div:nth-child(3) {
padding-left: 0;
}
}
.movie-poster-container {
display: flex;
align-items: center;
justify-content: center;
}
.movie-poster-link {
display: block;
text-decoration: none;
width: 100%;
}
.movie-poster-wrapper {
position: relative;
width: 100%;
}
.movie-poster-frame {
width: 100%;
height: 0;
padding-bottom: 150%;
position: relative;
overflow: hidden;
border-radius: 8px;
border: 3px solid var(--movie-bg-card);
box-shadow: 0 4px 12px rgba(0,0,0,0.12);
background: var(--movie-bg-tertiary);
}
.movie-poster {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
object-fit: cover;
transition: transform 0.3s ease;
}
.movie-poster:hover {
transform: scale(1.05);
}
.movie-poster-badge {
position: absolute;
top: -5px;
right: -5px;
background: var(--movie-accent-color);
color: white;
width: 24px;
height: 24px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 12px;
font-weight: bold;
box-shadow: 0 2px 4px rgba(1,210,119,0.3);
}
.movie-info-left {
padding-right: 15px;
border-right: 1px solid var(--movie-border-color);
}
.movie-info-item {
margin: 0 0 10px 0;
font-size: 14px;
color: var(--movie-text-secondary);
line-height: 1.5;
}
.movie-info-label {
font-weight: 600;
color: var(--movie-text-primary);
display: inline-block;
width: 70px;
flex-shrink: 0;
}
.movie-info-value {
color: var(--movie-text-primary);
}
.movie-genres {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
display: inline;
}
.movie-info-right {
padding-left: 15px;
}
.movie-footer {
padding: 20px 25px 25px 25px;
}
.movie-summary-section {
margin-bottom: 20px;
}
.movie-summary-header {
margin: 0 0 12px 0;
font-size: 16px;
color: var(--movie-text-primary);
font-weight: 600;
display: flex;
align-items: center;
gap: 8px;
padding-bottom: 8px;
border-bottom: 1px solid var(--movie-border-color);
}
.movie-summary-icon {
background: var(--movie-accent-color);
color: white;
width: 28px;
height: 28px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 14px;
}
.movie-summary-content {
background: var(--movie-bg-tertiary);
padding: 20px;
border-radius: 8px;
border: 1px solid var(--movie-border-color);
box-shadow: 0 1px 3px rgba(0,0,0,0.05);
}
导演:
{$director}
类型:
{$genresHtml}
年份:
{$releaseYear}
时长:
{$runtime}
地区:
{$countriesHtml}
{$ratingHtml}
{$rightColumnHtml}
HTML;
// 添加JavaScript切换函数
$html .= '
';
return $html;
}
/**
* 获取所有电影数据(支持分页)
*/
private static function getAllMoviesData($page = 1, $pageSize = 10)
{
$cacheDir = dirname(__FILE__) . '/cache/';
$allMovies = array();
// 扫描缓存目录
if (file_exists($cacheDir)) {
$files = scandir($cacheDir);
foreach ($files as $file) {
if (pathinfo($file, PATHINFO_EXTENSION) === 'json') {
$filePath = $cacheDir . $file;
$content = file_get_contents($filePath);
if ($content) {
$data = json_decode($content, true);
if ($data && isset($data['fetched_at'])) {
$movieId = pathinfo($file, PATHINFO_FILENAME);
$data['movieId'] = $movieId;
if (file_exists($filePath)) {
$data['added_time'] = filemtime($filePath);
} else {
$data['added_time'] = isset($data['fetched_at']) ? $data['fetched_at'] : time();
}
$allMovies[] = $data;
}
}
}
}
}
// 按added_time倒序排序
usort($allMovies, function($a, $b) {
return $b['added_time'] - $a['added_time'];
});
$total = count($allMovies);
$totalPages = ceil($total / $pageSize);
// 限制页码范围
$page = max(1, min($page, $totalPages));
// 分页处理
$startIndex = ($page - 1) * $pageSize;
$paginatedMovies = array_slice($allMovies, $startIndex, $pageSize);
return array(
'movies' => $paginatedMovies,
'total' => $total,
'page' => $page,
'pageSize' => $pageSize,
'totalPages' => $totalPages,
'startIndex' => $startIndex + 1,
'endIndex' => min($startIndex + $pageSize, $total)
);
}
/**
* 渲染所有电影列表(简单文本格式)- 支持分页
*/
public static function renderAllMovies($page = 1)
{
// 从GET参数获取页码
if (isset($_GET['page']) && is_numeric($_GET['page'])) {
$page = intval($_GET['page']);
}
$options = Typecho_Widget::widget('Widget_Options')->plugin('MovieInfo');
$pageSize = isset($options->pageSize) ? intval($options->pageSize) : 10;
$imageProxy = isset($options->imageProxy) ? $options->imageProxy : 'https://images.weserv.nl/?url=';
$defaultPoster = isset($options->defaultPoster) ? $options->defaultPoster :
'https://via.placeholder.com/300x450/CCCCCC/666666?text=No+Poster';
// 获取分页数据
$paginationData = self::getAllMoviesData($page, $pageSize);
$allMovies = $paginationData['movies'];
$total = $paginationData['total'];
$currentPage = $paginationData['page'];
$totalPages = $paginationData['totalPages'];
$startIndex = $paginationData['startIndex'];
$endIndex = $paginationData['endIndex'];
if (empty($allMovies)) {
return '
暂无电影数据
请先在文章中使用[movie:ID]短代码添加电影
';
}
$html = '';
$html .= '';
// 标题模块 - 调整深色模式
$html .= '';
// 页码信息 - 调整深色模式
$html .= '';
// 计算倒序序号
$totalCount = $total;
$currentIndex = $totalCount - (($currentPage - 1) * $pageSize);
foreach ($allMovies as $movie) {
$movieId = $movie['movieId'];
$title = isset($movie['title']) ? htmlspecialchars($movie['title']) : '未知电影';
$originalTitle = isset($movie['original_title']) ? htmlspecialchars($movie['original_title']) : '';
// 海报URL
$posterPath = isset($movie['poster_path']) ? $movie['poster_path'] : '';
if ($posterPath) {
$posterUrl = 'https://image.tmdb.org/t/p/w200' . $posterPath;
$posterSrc = $imageProxy . urlencode($posterUrl);
} else {
$posterSrc = $defaultPoster;
}
// 自定义字段
$startDate = isset($movie['custom_start_date']) ? htmlspecialchars($movie['custom_start_date']) : '';
$watchDate = isset($movie['custom_watch_date']) ? htmlspecialchars($movie['custom_watch_date']) : '';
$watchMethod = isset($movie['custom_watch_method']) ? htmlspecialchars($movie['custom_watch_method']) : '';
$movieCategory = isset($movie['custom_movie_category']) ? htmlspecialchars($movie['custom_movie_category']) : '';
$myRating = isset($movie['custom_my_rating']) ? htmlspecialchars($movie['custom_my_rating']) : '';
// 电影信息
$releaseYear = isset($movie['release_year']) ? htmlspecialchars($movie['release_year']) : '未知';
$director = isset($movie['director']) ? htmlspecialchars($movie['director']) : '未知';
// 类型 - 使用·分隔符
$genres = '未知';
if (isset($movie['genres']) && is_array($movie['genres'])) {
$genres = implode('
·', $movie['genres']);
}
// 时长
$runtime = isset($movie['runtime']) ? htmlspecialchars($movie['runtime']) . '分钟' : '未知';
// 评分
$rating = isset($movie['vote_average']) ? floatval($movie['vote_average']) : 0;
$ratingDisplay = $rating > 0 ? number_format($rating, 1) . '分' : '暂无评分';
// 短评
$review = isset($movie['review']) ? htmlspecialchars($movie['review']) : '';
// 原始标题作为工具提示
$titleTooltip = '';
if ($originalTitle && $originalTitle != $title) {
$titleTooltip = ' title="原名:' . $originalTitle . '"';
}
// 获取序号颜色
$indexColors = [
'#01d277, #01b36b', // TMDB绿色
'#0073aa, #0056b3', // 蓝色
'#e67e22, #d35400', // 橙色
'#9b59b6, #8e44ad', // 紫色
'#e74c3c, #c0392b', // 红色
];
$indexColor = $indexColors[($currentIndex - 1) % count($indexColors)];
$html .= '
《' . $title . '》';
if ($movieCategory) {
$html .= '
/' . $movieCategory . '';
}
// 处理观看日期显示
if ($startDate && $watchDate) {
if ($startDate === $watchDate) {
$html .= '
/' . $startDate . '';
} else {
$html .= '
/' . $startDate . '-' . $watchDate . '';
}
} elseif ($startDate) {
$html .= '
/' . $startDate . '-至今';
} elseif ($watchDate) {
$html .= '
/未知-' . $watchDate . '';
}
if ($watchMethod) {
$html .= '
/' . $watchMethod . '';
}
$html .= '
';
// 第二行:导演/类型/年份/时长/评分 - 修改:导演前面加"导演:"
$html .= '
';
$html .= '导演:' . $director . '';
$html .= '/';
// 类型显示 - 使用·分隔符
$html .= '' . $genres . '';
$html .= '/';
$html .= '' . $releaseYear . '年';
$html .= '/';
$html .= '' . $runtime . '';
$html .= '/';
if ($rating > 0) {
$html .= '评分:' . $ratingDisplay . '';
} else {
$html .= '' . $ratingDisplay . '';
}
if ($myRating) {
$html .= '/我的评分:' . $myRating . '';
}
$html .= '
';
// 第三行:短评
if ($review) {
$html .= '
📝 影评
' . nl2br($review) . '
';
}
$html .= '
';
$currentIndex--;
}
// 分页导航
if ($totalPages > 1) {
$html .= self::generatePagination($currentPage, $totalPages);
}
$html .= '
';
return $html;
}
/**
* 生成分页HTML
*/
private static function generatePagination($currentPage, $totalPages, $baseUrl = '')
{
if ($totalPages <= 1) {
return '';
}
$html = '';
return $html;
}
/**
* 获取页面URL
*/
private static function getPageUrl($page, $baseUrl = '')
{
if (empty($baseUrl)) {
$currentUrl = isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : '';
$currentUrl = preg_replace('/[&?]page=\d+/', '', $currentUrl);
$currentUrl = rtrim($currentUrl, '?&');
$separator = strpos($currentUrl, '?') === false ? '?' : '&';
return $currentUrl . $separator . 'page=' . $page;
}
return $baseUrl . (strpos($baseUrl, '?') === false ? '?' : '&') . 'page=' . $page;
}
/**
* 获取序号颜色
*/
private static function getIndexColor($index)
{
$colors = [
'#01d277, #01b36b', // TMDB绿色
'#0073aa, #0056b3', // 蓝色
'#e67e22, #d35400', // 橙色
'#9b59b6, #8e44ad', // 紫色
'#e74c3c, #c0392b', // 红色
];
return $colors[($index - 1) % count($colors)];
}
/**
* 获取电影数据(包含影评和自定义信息)
*/
private static function getMovieData($movieId, $review = '', $customData = array())
{
if (!is_numeric($movieId)) return null;
$cacheFile = dirname(__FILE__) . '/cache/' . $movieId . '.json';
$options = Typecho_Widget::widget('Widget_Options')->plugin('MovieInfo');
$cacheEnable = isset($options->cacheEnable) ? $options->cacheEnable : '1';
$cacheTime = isset($options->cacheTime) ? intval($options->cacheTime) : 7;
$apiKey = isset($options->tmdbApiKey) ? $options->tmdbApiKey : '';
$language = isset($options->language) ? $options->language : 'zh-CN';
if (empty($apiKey)) {
return array(
'title' => '请配置TMDB API Key',
'original_title' => 'Please configure TMDB API Key',
'error' => 'API_KEY_MISSING'
);
}
$data = null;
$needUpdate = false;
// 检查缓存
if ($cacheEnable == '1' && file_exists($cacheFile)) {
$fileTime = filemtime($cacheFile);
$expireTime = $cacheTime * 24 * 3600;
if (time() - $fileTime < $expireTime) {
$cacheContent = file_get_contents($cacheFile);
if ($cacheContent) {
$data = json_decode($cacheContent, true);
}
}
}
// 如果缓存不存在或已过期,从TMDB API获取
if (!$data || empty($data['title'])) {
$data = self::fetchFromTMDB($movieId, $apiKey, $language);
$needUpdate = true;
}
// 更新影评
if (!empty($review) && (!isset($data['review']) || $data['review'] !== $review)) {
$data['review'] = $review;
$data['review_updated'] = time();
$needUpdate = true;
}
// 更新自定义数据
if (!empty($customData)) {
if (isset($customData['startDate']) && (!isset($data['custom_start_date']) || $data['custom_start_date'] !== $customData['startDate'])) {
$data['custom_start_date'] = $customData['startDate'];
$needUpdate = true;
}
if (isset($customData['watchDate']) && (!isset($data['custom_watch_date']) || $data['custom_watch_date'] !== $customData['watchDate'])) {
$data['custom_watch_date'] = $customData['watchDate'];
$needUpdate = true;
}
if (isset($customData['watchMethod']) && (!isset($data['custom_watch_method']) || $data['custom_watch_method'] !== $customData['watchMethod'])) {
$data['custom_watch_method'] = $customData['watchMethod'];
$needUpdate = true;
}
if (isset($customData['movieCategory']) && (!isset($data['custom_movie_category']) || $data['custom_movie_category'] !== $customData['movieCategory'])) {
$data['custom_movie_category'] = $customData['movieCategory'];
$needUpdate = true;
}
if (isset($customData['recommendation']) && (!isset($data['custom_recommendation']) || $data['custom_recommendation'] != $customData['recommendation'])) {
$data['custom_recommendation'] = intval($customData['recommendation']);
$needUpdate = true;
}
if (isset($customData['myRating']) && (!isset($data['custom_my_rating']) || $data['custom_my_rating'] !== $customData['myRating'])) {
$data['custom_my_rating'] = $customData['myRating'];
$needUpdate = true;
}
}
// 确保自定义字段存在
$customFields = array(
'custom_start_date' => '',
'custom_watch_date' => '',
'custom_watch_method' => '',
'custom_movie_category' => '',
'custom_recommendation' => 0,
'custom_my_rating' => ''
);
foreach ($customFields as $field => $default) {
if (!isset($data[$field])) {
$data[$field] = $default;
}
}
// 如果需要更新缓存,保存到文件
if ($needUpdate && $data && !empty($data['title'])) {
file_put_contents($cacheFile, json_encode($data, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT));
}
return $data;
}
/**
* 从TMDB API获取数据
*/
private static function fetchFromTMDB($movieId, $apiKey, $language)
{
// TMDB API URL
$url = "https://api.themoviedb.org/3/movie/{$movieId}?api_key={$apiKey}&language={$language}&append_to_response=credits";
$ch = curl_init();
curl_setopt_array($ch, array(
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_TIMEOUT => 20,
CURLOPT_SSL_VERIFYPEER => false,
CURLOPT_USERAGENT => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
CURLOPT_ENCODING => 'gzip, deflate',
CURLOPT_HTTPHEADER => array(
'Accept: application/json',
'Cache-Control: no-cache'
)
));
$response = curl_exec($ch);
if (curl_errno($ch)) {
curl_close($ch);
return null;
}
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($httpCode != 200 || empty($response)) {
return null;
}
$movieData = json_decode($response, true);
if (!$movieData) {
return null;
}
$data = array();
// 1. 基本信息
$data['title'] = isset($movieData['title']) ? trim($movieData['title']) : '未知电影';
$data['original_title'] = isset($movieData['original_title']) ? trim($movieData['original_title']) : '';
// 2. 海报
$data['poster_path'] = isset($movieData['poster_path']) ? $movieData['poster_path'] : '';
// 3. 简介
$data['overview'] = isset($movieData['overview']) ? trim($movieData['overview']) : '暂无简介';
// 4. 上映年份
if (isset($movieData['release_date']) && !empty($movieData['release_date'])) {
$releaseDate = $movieData['release_date'];
$data['release_year'] = substr($releaseDate, 0, 4);
} else {
$data['release_year'] = '未知';
}
// 5. 导演
$director = '未知';
if (isset($movieData['credits']['crew'])) {
foreach ($movieData['credits']['crew'] as $crew) {
if (isset($crew['job']) && $crew['job'] === 'Director') {
$director = $crew['name'];
break;
}
}
}
$data['director'] = $director;
// 6. 类型
$genres = array();
if (isset($movieData['genres'])) {
foreach ($movieData['genres'] as $genre) {
if (isset($genre['name'])) {
$genres[] = $genre['name'];
}
}
}
$data['genres'] = !empty($genres) ? $genres : array('未知');
// 7. 时长
$data['runtime'] = isset($movieData['runtime']) ? $movieData['runtime'] : 0;
// 8. 评分
$data['vote_average'] = isset($movieData['vote_average']) ? floatval($movieData['vote_average']) : 0;
$data['vote_count'] = isset($movieData['vote_count']) ? intval($movieData['vote_count']) : 0;
// 9. 生产国家
$countries = array();
if (isset($movieData['production_countries'])) {
foreach ($movieData['production_countries'] as $country) {
if (isset($country['name'])) {
$countries[] = $country['name'];
}
}
}
$data['production_countries'] = !empty($countries) ? $countries : array('未知');
// 10. 初始化影评和自定义字段
$data['review'] = '';
$data['review_updated'] = 0;
// 初始化自定义字段
$data['custom_start_date'] = '';
$data['custom_watch_date'] = '';
$data['custom_watch_method'] = '';
$data['custom_movie_category'] = '';
$data['custom_recommendation'] = 0;
$data['custom_my_rating'] = '';
// 11. 添加抓取时间
$data['fetched_at'] = time();
return $data;
}
/**
* 渲染编辑器按钮 - 增加关键词联想搜索
*/
public static function renderButton()
{
// 获取插件配置
$options = Typecho_Widget::widget('Widget_Options')->plugin('MovieInfo');
$apiKey = isset($options->tmdbApiKey) ? $options->tmdbApiKey : '';
$language = isset($options->language) ? $options->language : 'zh-CN';
// 生成nonce或token用于验证请求
$nonce = md5(uniqid());
echo <<
#movieinfo-button {
padding: 5px!important;
background: #fff;
cursor: pointer;
color: white;
border: none;
border-radius: 4px;
font-size: 16px;
line-height: 1;
display: inline-flex;
align-items: center;
justify-content: center;
min-width: 26px;
min-height: 26px;
}
#movieinfo-button:hover { background: #E9E9E6 }
.dark #movieinfo-button { background: rgb(16, 25, 40); }
.dark #movieinfo-button:hover { background: #375d85; }
/* 搜索建议下拉框样式 */
.movie-search-suggestions {
position: absolute;
background: white;
border: 1px solid #ddd;
border-radius: 4px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
max-height: 300px;
overflow-y: auto;
z-index: 10001;
width: 300px;
margin-top: 2px;
}
.movie-search-suggestion-item {
padding: 8px 12px;
cursor: pointer;
border-bottom: 1px solid #f0f0f0;
font-size: 14px;
color: #333;
display: flex;
align-items: center;
gap: 10px;
}
.movie-search-suggestion-item:hover {
background: #f5f5f5;
}
.movie-search-suggestion-item.selected {
background: #0073aa;
color: white;
}
.movie-search-suggestion-poster {
width: 30px;
height: 45px;
object-fit: cover;
border-radius: 3px;
flex-shrink: 0;
}
.movie-search-suggestion-info {
flex-grow: 1;
overflow: hidden;
}
.movie-search-suggestion-title {
font-weight: 600;
margin-bottom: 2px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.movie-search-suggestion-year {
font-size: 12px;
color: #666;
}
.movie-search-suggestion-item:hover .movie-search-suggestion-year {
color: #ccc;
}
.movie-search-suggestion-item.selected .movie-search-suggestion-year {
color: #ccc;
}
.movie-search-loading {
padding: 10px;
text-align: center;
color: #666;
font-size: 14px;
}
.movie-search-no-results {
padding: 10px;
text-align: center;
color: #999;
font-size: 14px;
}
.movie-search-input-container {
position: relative;
margin-bottom: 10px;
}
.movie-search-results {
position: relative;
}
.movie-id-section {
margin-top: 15px;
margin-bottom: 10px;
padding-top: 15px;
border-top: 1px solid #eee;
}
.dark .movie-search-suggestions {
background: #2d2d2d;
border-color: #404040;
color: #e0e0e0;
}
.dark .movie-search-suggestion-item {
color: #e0e0e0;
border-bottom-color: #404040;
}
.dark .movie-search-suggestion-item:hover {
background: #3a3a3a;
}
.dark .movie-search-suggestion-item.selected {
background: #0073aa;
}
.dark .movie-search-suggestion-year {
color: #aaa;
}
.dark .movie-search-loading,
.dark .movie-search-no-results {
color: #aaa;
}
HTML;
}
}