苹果cms系统的影视热度榜单一直靠站内真实点击量,对于个人站点或规模较小的网站,热榜数据容易失真。 TMDB官方接口可是按天/周粒度拉全网热度,电影、电视剧、动漫(用 tv 当载体)一把抓,关键是免费层 50 QPS 完全够个人影视站用,就决定把它接到苹果 CMS 上做一个独立页,实现真实的影片热度数据。
一、实现功能
下面这些是这次接入真正落地的能力点,每条都能在 /label/tmdb.html 上直接验证:
- 综合热榜 / 电影热榜 / 电视剧热榜 三种媒体类型切换:type=all|movie|tv 参数控制,对应 TMDB /trending/{all|movie|tv}/{day|week} 三个官方接口。
- 今日榜 / 本周榜 两种时间窗口切换:time=day|week 参数控制,TMDB 官方按这两粒度维护热度。
- 可调返回条数:limit 参数支持 1-50,默认 20,前端要 Top10 直接 ?limit=10。
- 服务端缓存 24 小时:TTL=86400 秒,避免对 TMDB 高频请求撞 QPS 限流。
- 快捷 URL 路由:除主入口外,单独暴露 /api.php/tmdbhot/top10、/top20、/movie、/tv、/all、/week 一堆快捷接口,前端不用拼 query string。
- 一键清缓存:/api.php/tmdbhot/refresh 把所有 key 一次性清掉,TMDB 配置改了想立即生效调一次就行。
- 数据归一化:标题兼容 title/name、日期兼容 release_date/first_air_date、海报与背景图自动拼 image.tmdb.org/t/p/w500、评分/人气/原标题/类型 id/原语言/成人标记全部带回来。
- 类型过滤:media_type=person 在 provider 层直接跳过,名单里不会出现 TMDB 的「人物」条目污染影视榜。
- 混合榜按评分排序:type=all 时把电影和电视剧按 vote_average 降序拼一起,避免某类霸榜。
二、效果预览
![图片[1] - 苹果CMS 接入TMDB API:新增每日热门影视独立页 - 迅维博客网](https://image-1301450403.cos.ap-nanjing.myqcloud.com/2026/07/26/HcQFJBJB.png)
三、完整处理流程
接入链条不长,5 跳就到 TMDB 官方 API,下面按 6 段拆,每段都标注了关键文件 + 行号,方便照着抄。
用户访问 /label/tmdb.html
↓
ThinkPHP 路由 (route.php:671)
'label-<file>' → label/index
↓
Label::__construct() (Label.php)
取 file=douban/tmdb → template/ds3/name/label/tmdb.html
↓
tmdb.html (前端壳子)
jQuery ajax → GET /api.php/tmdbhot
↓
Tmdbhot::index() (api/controller/Tmdbhot.php)
参数校验 → 读 Cache → 未命中则调 provider
↓
TmdbExternalSourceProvider::fetchRecent() (common/util/TmdbExternalSourceProvider.php)
拼 URL → curlPostWithTimeout 调 TMDB
↓
TMDB 官方: /trending/{type}/{time}?language=zh-CN&api_key=***
↓
normalizeList() 归一化 → 写 Cache → return JSON
↓
前端 $.each 渲染排名/海报/标题/简介 → 点击跳站内搜索
3.1 模板与前端渲染
template/ds3/name/label/tmdb.html 是个纯壳子,结构就三块:banner + 容器 + jQuery ajax。
<link rel="stylesheet" href="/static/ds3/css/tmdb.css">
<div class="page-banner tmdb-page-banner">
<h1><i class="iconfont-ylfb yb-paihangbang"></i> TMDB 热榜</h1>
<p>TMDB热门影视,每日更新</p>
</div>
<div class="box-width-small">
<div class="tmdb-hot-list" id="tmdbHotList">
<div class="loading">加载中...</div>
</div>
</div>
<script>
$(function() {
$.ajax({
url: '/api.php/tmdbhot',
type: 'GET',
dataType: 'json',
success: function(res) {
if (res.code == 200 && res.data && res.data.length > 0) {
var html = '';
$.each(res.data, function(i, item) {
var rank = i + 1;
var rankClass = rank <= 3 ? 'top' + rank : '';
html += '<a class="tmdb-item" href="/vodsearch.html?wd='
+ encodeURIComponent(item.item_title) + '">';
html += '<span class="tmdb-rank ' + rankClass + '">' + rank + '</span>';
html += '<img class="lazy lazy1" data-src="' + item.item_cover + '"'
+ ' onerror="..." alt="' + item.item_title + '" />';
// 标题、类型、评分、简介...
});
$('#tmdbHotList').html(html);
}
}
});
});
</script>
3.2 API 控制器 Tmdbhot
application/api/controller/Tmdbhot.php 是这次的核心入口。它本身是 ThinkPHP 的 api 模块控制器,挂在 api.php 这个入口下,路由规则 ThinkPHP 默认就是 /api.php/{controller}/{action}。
主入口 index() 处理流程:
public function index(Request $request)
{
header('Content-Type: application/json; charset=utf-8');
// 1. 取参数 + 白名单校验
$type = isset($_GET['type']) ? trim($_GET['type']) : 'all';
$time = isset($_GET['time']) ? trim($_GET['time']) : 'day';
$limit = isset($_GET['limit']) ? max(1, min(50, intval($_GET['limit']))) : 20;
$refresh= isset($_GET['refresh']) && $_GET['refresh'] === '1';
$type = in_array($type, ['all','movie','tv']) ? $type : 'all';
$time = in_array($time, ['day','week']) ? $time : 'day';
// 2. 缓存 key
$cacheKey = 'tmdb_trending_' . $type . '_' . $time . '_' . $limit;
// 3. 命中缓存直接返回
if (!$refresh) {
$cached = Cache::get($cacheKey);
if (!empty($cached)) {
echo json_encode([
'code'=>200, 'msg'=>'success', 'data'=>$cached,
'cached'=>true, 'type'=>$type, 'time'=>$time, 'limit'=>$limit
], JSON_UNESCAPED_UNICODE);
exit;
}
} else {
Cache::rm($cacheKey); // 强制刷新
}
// 4. 取 TMDB 配置 + 调 provider
$cfg = config('maccms.ai_search');
$tmdbCfg= $cfg['external_sources']['sources']['tmdb'] ?? [];
$provider = new TmdbExternalSourceProvider($tmdbCfg);
$data = $provider->fetchRecent([
'limit' => $limit,
'media_type' => $type,
'time_window'=> $time,
]);
// 5. 写缓存 + 返回
if (!empty($data)) {
Cache::set($cacheKey, $data, 86400); // TTL 1 天
}
echo json_encode([
'code'=>200, 'msg'=>'success', 'data'=>$data,
'cached'=>false, 'type'=>$type, 'time'=>$time, 'limit'=>$limit
], JSON_UNESCAPED_UNICODE);
exit;
}
关键设计点:
- 缓存粒度 = type × time × limit:三个参数决定一份独立缓存。
- TTL = 86400 秒(1 天):和 TMDB 官方 trending/{day} 的更新节奏对齐,超过一天价值也不大。
- refresh=1 主动失效:调试或配置变更时强制重拉,不需要等 24 小时。
支持的接口完整列表:
| URL | 等价参数 | 用途 |
|---|---|---|
/api.php/tmdbhot | — | 主入口,参数自由组合 |
/api.php/tmdbhot/all | type=all | 综合(电影+电视剧) |
/api.php/tmdbhot/movie | type=movie | 仅电影 |
/api.php/tmdbhot/tv | type=tv | 仅电视剧 |
/api.php/tmdbhot/week | time=week | 本周榜 |
/api.php/tmdbhot/top10 | limit=10 | Top10 快捷 |
/api.php/tmdbhot/top20 | limit=20 | Top20 快捷 |
/api.php/tmdbhot/refresh | — | 清掉所有 24 个缓存 key |
/api.php/tmdbhot/categories | — | 返回可选分类元数据 |
3.3 三方 Provider
application/common/util/TmdbExternalSourceProvider.php 负责真正和 TMDB 官方 API 对接。它实现了 ExternalSourceProviderInterface 接口,除了 fetchRecent(拉热榜)还提供了 search(关键字搜影片)方法。
配置读取(application/extra/maccms.php 第 797 行):
'external_sources' => array(
'sources' => array(
'tmdb' => array(
'enabled' => '1',
'base_url' => 'https://api.themoviedb.org/3',
'image_base_url'=> 'https://image.tmdb.org/t/p/w500',
'language' => 'zh-CN',
'region' => 'CN',
'api_key' => 'xxxxxxxx',
),
),
),
fetchRecent() 核心逻辑:
public function fetchRecent(array $options = [])
{
if (!$this->isEnabled()) return [];
$limit = max(1, min(50, intval($options['limit'] ?? 20)));
$mediaType = $options['media_type'] ?? 'all';
$timeWindow = $options['time_window'] ?? 'day';
// 路径: /trending/{all|movie|tv}/{day|week}
$path = '/trending/' . $mediaType . '/' . $timeWindow;
$params = ['language' => $this->getLanguage()];
$rows = $this->request($path, $params);
if (!is_array($rows)) return [];
$data = array_slice($this->normalizeList($rows, $mediaType), 0, $limit);
// 混合榜按评分排序
if ($mediaType === 'all' && !empty($data)) {
usort($data, fn($a, $b) => $b['item_score'] <=> $a['item_score']);
}
return $data;
}
private function request($path, array $params)
{
$params['api_key'] = $this->getApiKey();
$url = $this->getBaseUrl() . $path . '?' . http_build_query($params);
$resp = HttpClient::curlPostWithTimeout($url, '', ['Accept: application/json'], 10, false);
if ($resp === false || $resp === '') return [];
$json = json_decode((string)$resp, true);
if (!is_array($json) || empty($json['results'])) return [];
return $json['results'];
}
normalizeList() 字段归一化——这是整个 provider 的核心,把 TMDB 那套英语字段转成站内统一结构:
$out[] = [
'provider_code' => 'tmdb',
'item_key' => $itemMediaType . '_' . $id,
'item_mid' => $itemMediaType === 'tv' ? 1 : 2, // 1=电视剧 2=电影
'item_title' => $title,
'item_subtitle' => $this->getMediaTypeLabel($itemMediaType), // 电影/电视剧
'item_snippet' => $overview,
'item_url' => 'https://www.themoviedb.org/' . ($itemMediaType === 'tv' ? 'tv' : 'movie') . '/' . $id,
'item_cover' => $cover, // 海报
'item_backdrop' => $backdrop, // 背景图
'item_score' => $vote, // 评分
'item_vote_count' => $voteCount,
'item_popularity' => $popularity, // 人气
'item_release_date' => $releaseDate,
'item_original_title'=> $originalTitle,
'item_language' => $originalLanguage,
'item_adult' => $adult,
'item_genre_ids' => $genreIds,
'item_payload' => json_encode($row, JSON_UNESCAPED_UNICODE),
];
四、操作步骤
下面按 「检查 → 加配置 → 改文件 → 加路由 SEO → 验证」 的顺序写,照着抄就能落地。
4.1 准备 TMDB API Key
去 themoviedb.org/settings/api 申请一个 v3 auth key。
4.2 写配置文件
登录苹果cms系统管理台,配置TMDB信息,新版的苹果cms支持TMDB信息配置。
打开 application/extra/maccms.php,找到 maccms.ai_search.external_sources.sources.tmdb(约第 797 行),看 enabled、api_key、base_url、image_base_url、language、region 五项是否填齐。没有就整段新增:
'tmdb' => array(
'enabled' => '1',
'base_url' => 'https://api.themoviedb.org/3',
'image_base_url' => 'https://image.tmdb.org/t/p/w500',
'language' => 'zh-CN',
'region' => 'CN',
'api_key' => '你的32位hex key',
),
4.3 新增 TmdbExternalSourceProvider
新增 application/common/util/TmdbExternalSourceProvider.php 文件
4.4 新增 Tmdbhot API 控制器
新增application/api/controller/Tmdbhot.php 文件
4.5 新增模板与样式
新建 template/ds3/name/label/tmdb.html文件,用于前端页面显示。
4.7 清理缓存 + 验证
跑 6 个 URL 验证整套链路:
GET /label/tmdb.html → 200 + 渲染热榜
GET /api.php/tmdbhot → 200 + cached:false
GET /api.php/tmdbhot → 200 + cached:true
GET /api.php/tmdbhot?type=movie&time=week&limit=10 → 200 + 仅电影 10 条
GET /api.php/tmdbhot/top20 → 200 + 20 条
GET /api.php/tmdbhot/refresh → 200 + cleared_keys:24
TMDB 数据在第三方,根本没法人为干预,榜单就是真实的全球热度。所以这套改造的本质:用一次性的 4 个文件改动,换取一个零维护、抗污染、独立 SEO、高用户感知的每日热榜页面。
五、核心代码下载
核心php文件下载,前端模板自己基于自己网站风格写吧,或者直接把本地址丢给ai大模型实现。
本文仅用于学习测试使用,请勿用于商业用途。















暂无评论内容