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 .= '
' . nl2br($summaryShort) . ' ' . htmlspecialchars($expandText) . ' ↓
'; } 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 .= '

🎬 我的全部已看电影

已记录 ' . $total . ' 部电影,继续探索更多精彩影片

'; // 页码信息 - 调整深色模式 $html .= '
显示范围: ' . $startIndex . '-' . $endIndex . ' / 总计: ' . $total . '
当前页码: 第 ' . $currentPage . ' 页 / 总页数: ' . $totalPages . ' 页
'; // 计算倒序序号 $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 = '
'; // 首页 if ($currentPage > 1) { $html .= '«'; } else { $html .= '«'; } // 上一页 if ($currentPage > 1) { $html .= ''; } else { $html .= ''; } // 页码显示 $startPage = max(1, $currentPage - 2); $endPage = min($totalPages, $currentPage + 2); for ($i = $startPage; $i <= $endPage; $i++) { if ($i == $currentPage) { $html .= '' . $i . ''; } else { $html .= '' . $i . ''; } } // 下一页 if ($currentPage < $totalPages) { $html .= ''; } else { $html .= ''; } // 末页 if ($currentPage < $totalPages) { $html .= '»'; } else { $html .= '»'; } $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; } }