tv-recommender-mcp-server
Version:
MCP server for TV show recommendations
307 lines (306 loc) • 11.5 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.findPersonId = findPersonId;
exports.convertCastToIds = convertCastToIds;
exports.findKeywordId = findKeywordId;
exports.discoverShows = discoverShows;
exports.getPersonTvCredits = getPersonTvCredits;
exports.findShowsByPersonName = findShowsByPersonName;
exports.getRecommendationsByActor = getRecommendationsByActor;
const tmdbClient_1 = require("../services/tmdbClient");
const genreMap_1 = require("../utils/genreMap");
const errorHandler_1 = require("../utils/errorHandler");
/**
* 将字符串类型数组转换为TMDb API所需的ID数组
* @param genres 类型名称数组
* @returns 类型ID数组字符串
*/
async function convertGenresToIds(genres) {
if (!genres || !genres.length)
return '';
const genreIds = genres.map(genre => (0, genreMap_1.mapGenreToId)(genre)).filter(id => id !== null);
if (genreIds.length === 0) {
throw new errorHandler_1.ApiError(`无法识别提供的类型: ${genres.join(', ')}`, 400);
}
return genreIds.join(',');
}
/**
* 查找人物ID
* @param personName 人物名称
* @returns 人物ID
*/
async function findPersonId(personName) {
try {
const response = await tmdbClient_1.tmdbClient.searchPerson(personName);
if (response.results && response.results.length > 0) {
return response.results[0].id;
}
return null;
}
catch (error) {
return null;
}
}
/**
* 将演员名称数组转换为ID数组
* @param cast 演员名称数组
* @returns 演员ID数组字符串
*/
async function convertCastToIds(cast) {
if (!cast || !cast.length)
return '';
const castPromises = cast.map(name => findPersonId(name));
const castIds = await Promise.all(castPromises);
const validIds = castIds.filter(id => id !== null);
if (validIds.length === 0) {
throw new errorHandler_1.ApiError(`无法找到演员: ${cast.join(', ')}`, 400);
}
return validIds.join(',');
}
/**
* 查找关键词ID
* @param keyword 关键词
* @returns 关键词ID
*/
async function findKeywordId(keyword) {
try {
const response = await tmdbClient_1.tmdbClient.searchKeyword(keyword);
if (response.results && response.results.length > 0) {
return response.results[0].id;
}
return null;
}
catch (error) {
// console.error(`查找关键词"${keyword}"失败:`, error);
return null;
}
}
/**
* 将关键词数组转换为ID数组
* @param keywords 关键词数组
* @returns 关键词ID数组字符串
*/
async function convertKeywordsToIds(keywords) {
if (!keywords || !keywords.length)
return '';
const keywordPromises = keywords.map(keyword => findKeywordId(keyword));
const keywordIds = await Promise.all(keywordPromises);
const validIds = keywordIds.filter(id => id !== null);
if (validIds.length === 0) {
throw new errorHandler_1.ApiError(`无法找到关键词: ${keywords.join(', ')}`, 400);
}
return validIds.join(',');
}
/**
* 格式化发现剧集结果
* @param results TMDb API返回的结果
* @returns 格式化后的结果
*/
function formatDiscoverResults(results) {
return results.map(show => ({
id: show.id,
name: show.name,
overview: show.overview || '暂无简介',
poster_path: show.poster_path || null,
vote_average: show.vote_average || 0,
first_air_date: show.first_air_date
}));
}
/**
* 高级剧集发现工具实现
* @param params 多个可选筛选条件
* @returns 发现结果
*/
async function discoverShows(params) {
try {
// 构建API请求参数
const apiParams = {};
// 处理类型参数
if (params.with_genres?.length) {
apiParams.with_genres = await convertGenresToIds(params.with_genres);
}
// 注释: TMDB API的discover/tv不支持按演员或人物筛选
// 相关代码已移除
// 存储请求的年份以便后续过滤
const requestedYear = params.first_air_date_year;
// 处理年份参数 - 修正参数名称为带点号格式
if (requestedYear) {
apiParams['first_air_date.gte'] = `${requestedYear}-01-01`;
apiParams['first_air_date.lte'] = `${requestedYear}-12-31`;
}
// 处理评分参数 - 修正参数名称为带点号格式
if (params.vote_average_gte) {
apiParams['vote_average.gte'] = params.vote_average_gte;
}
// 处理网络参数
if (params.with_networks?.length) {
apiParams.with_networks = params.with_networks.join(',');
}
// 处理关键词参数
if (params.with_keywords?.length) {
apiParams.with_keywords = await convertKeywordsToIds(params.with_keywords);
}
// 处理排序参数
if (params.sort_by) {
apiParams.sort_by = params.sort_by;
}
else {
apiParams.sort_by = 'popularity.desc';
}
// 处理分页参数
apiParams.page = params.page || 1;
// 处理语言参数
if (params.with_original_language) {
apiParams.with_original_language = params.with_original_language;
}
// 处理成人内容参数
if (params.include_adult !== undefined) {
apiParams.include_adult = params.include_adult;
}
// 处理无首播日期内容参数
if (params.include_null_first_air_dates !== undefined) {
apiParams.include_null_first_air_dates = params.include_null_first_air_dates;
}
// 处理原产国参数
if (params.with_origin_country) {
apiParams.with_origin_country = params.with_origin_country;
}
// 处理状态参数
if (params.with_status) {
apiParams.with_status = params.with_status;
}
// 处理院线放映参数
if (params.screened_theatrically !== undefined) {
apiParams.screened_theatrically = params.screened_theatrically;
}
// 处理时区参数
if (params.timezone) {
apiParams.timezone = params.timezone;
}
// 处理观看区域参数
if (params.watch_region) {
apiParams.watch_region = params.watch_region;
}
// 处理制作公司参数
if (params.with_companies) {
apiParams.with_companies = params.with_companies;
}
// 处理观看提供商参数
if (params.with_watch_providers) {
apiParams.with_watch_providers = params.with_watch_providers;
}
// 处理观看货币化类型参数
if (params.with_watch_monetization_types) {
apiParams.with_watch_monetization_types = params.with_watch_monetization_types;
}
// 调用TMDb API
const response = await tmdbClient_1.tmdbClient.discoverTvShows(apiParams);
// 如果有指定年份,进行额外的过滤确保日期准确性
let filteredResults = response.results || [];
if (requestedYear) {
console.log(`过滤前剧集数量: ${filteredResults.length}`);
filteredResults = filteredResults.filter((show) => {
// 确保有首播日期
if (!show.first_air_date) {
console.log(`剧集 "${show.name}" (ID: ${show.id}) 缺少首播日期,已过滤`);
return false;
}
// 解析首播年份并与请求的年份比较
const airDateYear = new Date(show.first_air_date).getFullYear();
const matches = airDateYear === requestedYear;
if (!matches) {
console.log(`剧集 "${show.name}" (ID: ${show.id}) 首播日期 ${show.first_air_date} (${airDateYear}年) 与请求年份 ${requestedYear} 不匹配,已过滤`);
}
return matches;
});
console.log(`过滤后剧集数量: ${filteredResults.length}`);
}
// 格式化结果
const results = formatDiscoverResults(filteredResults);
// 构建响应
return {
page: response.page,
results,
total_pages: response.total_pages,
total_results: filteredResults.length // 更新为过滤后的总数
};
}
catch (error) {
// console.error('高级剧集发现失败:', error);
throw new errorHandler_1.ApiError(`高级剧集发现失败: ${(0, errorHandler_1.formatErrorMessage)(error)}`, 500);
}
}
/**
* 获取人物的电视作品
* 这是一个替代方案,用于在discover/tv不支持按演员筛选的情况下使用
* @param personId 人物ID
* @returns 该人物参与的电视剧列表
*/
async function getPersonTvCredits(personId) {
try {
// 调用person/{person_id}/tv_credits API获取此人参与的所有电视剧
const response = await tmdbClient_1.tmdbClient.getPersonTvCredits(personId);
// 格式化结果以匹配DiscoverShowsResponse格式
const results = response.cast || [];
const formattedResults = formatDiscoverResults(results);
return {
page: 1,
results: formattedResults,
total_pages: 1,
total_results: formattedResults.length
};
}
catch (error) {
throw new errorHandler_1.ApiError(`获取人物电视作品失败: ${(0, errorHandler_1.formatErrorMessage)(error)}`, 500);
}
}
/**
* 通过人物名称查找其参与的电视剧
* @param personName 人物名称
* @returns 该人物参与的电视剧列表
*/
async function findShowsByPersonName(personName) {
try {
// 先查找人物ID
const personId = await findPersonId(personName);
if (!personId) {
throw new errorHandler_1.ApiError(`未找到人物: ${personName}`, 404);
}
// 然后获取其电视作品
return getPersonTvCredits(personId);
}
catch (error) {
throw new errorHandler_1.ApiError(`查找人物电视作品失败: ${(0, errorHandler_1.formatErrorMessage)(error)}`, 500);
}
}
/**
* 根据演员名称查找推荐电视剧
* 此函数提供了一种替代方法来弥补discover/tv接口不支持按演员筛选的限制
* @param actorName 演员名称
* @param limit 返回结果数量限制
* @returns 包含该演员的电视剧列表
*/
async function getRecommendationsByActor(actorName, limit = 10) {
try {
// 获取演员ID
const actorId = await findPersonId(actorName);
if (!actorId) {
throw new errorHandler_1.ApiError(`未找到演员: ${actorName}`, 404);
}
// 获取演员的电视剧作品
const response = await getPersonTvCredits(actorId);
// 按流行度排序并限制结果数量
let sortedResults = [...response.results];
sortedResults.sort((a, b) => (b.vote_average || 0) - (a.vote_average || 0));
sortedResults = sortedResults.slice(0, limit);
return {
page: 1,
results: sortedResults,
total_pages: 1,
total_results: sortedResults.length
};
}
catch (error) {
throw new errorHandler_1.ApiError(`获取演员推荐电视剧失败: ${(0, errorHandler_1.formatErrorMessage)(error)}`, 500);
}
}