feat(类型): 添加视频搜索类型定义和工具函数
- 新增 SearchVideoData 类型定义,包含视频标题、链接、时长、作者等字段 - 新增 Message 类型的 searchVideos 属性用于存储视频搜索结果 - 添加 videoUtils.ts 视频工具函数库: - parseVideoUrl: 解析B站/YouTube/抖音视频链接 - formatDuration: 格式化视频时长显示 - getPlatformName/getPlatformColor: 获取平台名称和主题色 - detectPlatform: 检测视频平台类型 - supportsEmbed: 判断是否支持嵌入播放 - getEmbedUrlWithPage: 生成带选集参数的嵌入URL - supportsEpisodes: 判断是否支持选集功能
This commit is contained in:
parent
92ec88e1a3
commit
159009dd56
185
src/lib/videoUtils.ts
Normal file
185
src/lib/videoUtils.ts
Normal file
@ -0,0 +1,185 @@
|
||||
/**
|
||||
* 视频工具函数
|
||||
* 处理视频链接解析、时长格式化等
|
||||
*/
|
||||
|
||||
/**
|
||||
* 视频平台类型
|
||||
*/
|
||||
export type VideoPlatform = 'bilibili' | 'youtube' | 'douyin' | 'other';
|
||||
|
||||
/**
|
||||
* 视频解析信息
|
||||
*/
|
||||
export interface VideoInfo {
|
||||
platform: VideoPlatform;
|
||||
videoId: string;
|
||||
embedUrl: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析视频链接,提取平台和视频ID
|
||||
* @param url 视频链接
|
||||
* @returns 视频信息,解析失败返回 null
|
||||
*/
|
||||
export function parseVideoUrl(url: string): VideoInfo | null {
|
||||
if (!url) return null;
|
||||
|
||||
try {
|
||||
// B站视频解析 - av号格式
|
||||
// 格式: bilibili.com/video/av123456 或 www.bilibili.com/video/av123456
|
||||
const bilibiliAvMatch = url.match(/bilibili\.com\/video\/av(\d+)/i);
|
||||
if (bilibiliAvMatch) {
|
||||
const aid = bilibiliAvMatch[1];
|
||||
return {
|
||||
platform: 'bilibili',
|
||||
videoId: `av${aid}`,
|
||||
embedUrl: `//player.bilibili.com/player.html?aid=${aid}&high_quality=1&danmaku=0`,
|
||||
};
|
||||
}
|
||||
|
||||
// B站视频解析 - BV号格式
|
||||
// 格式: bilibili.com/video/BV1xx411c7mD
|
||||
const bilibiliBvMatch = url.match(/bilibili\.com\/video\/(BV[\w]+)/i);
|
||||
if (bilibiliBvMatch) {
|
||||
const bvid = bilibiliBvMatch[1];
|
||||
return {
|
||||
platform: 'bilibili',
|
||||
videoId: bvid,
|
||||
embedUrl: `//player.bilibili.com/player.html?bvid=${bvid}&high_quality=1&danmaku=0`,
|
||||
};
|
||||
}
|
||||
|
||||
// YouTube 视频解析
|
||||
// 格式: youtube.com/watch?v=xxx 或 youtu.be/xxx
|
||||
const youtubeMatch = url.match(/(?:youtube\.com\/watch\?v=|youtu\.be\/)([\w-]+)/);
|
||||
if (youtubeMatch) {
|
||||
const videoId = youtubeMatch[1];
|
||||
return {
|
||||
platform: 'youtube',
|
||||
videoId,
|
||||
embedUrl: `https://www.youtube.com/embed/${videoId}`,
|
||||
};
|
||||
}
|
||||
|
||||
// 抖音视频解析 (预留)
|
||||
// 格式: douyin.com/video/xxx
|
||||
const douyinMatch = url.match(/douyin\.com\/video\/(\d+)/);
|
||||
if (douyinMatch) {
|
||||
return {
|
||||
platform: 'douyin',
|
||||
videoId: douyinMatch[1],
|
||||
embedUrl: '', // 抖音暂不支持嵌入
|
||||
};
|
||||
}
|
||||
|
||||
return null;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化视频时长
|
||||
* @param seconds 秒数(字符串或数字)
|
||||
* @returns 格式化后的时长 (如 "2:34" 或 "1:02:34")
|
||||
*/
|
||||
export function formatDuration(seconds: string | number): string {
|
||||
const totalSeconds = typeof seconds === 'string' ? parseInt(seconds, 10) : seconds;
|
||||
|
||||
if (isNaN(totalSeconds) || totalSeconds < 0) {
|
||||
return '--:--';
|
||||
}
|
||||
|
||||
const hours = Math.floor(totalSeconds / 3600);
|
||||
const minutes = Math.floor((totalSeconds % 3600) / 60);
|
||||
const secs = totalSeconds % 60;
|
||||
|
||||
if (hours > 0) {
|
||||
return `${hours}:${minutes.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`;
|
||||
}
|
||||
|
||||
return `${minutes}:${secs.toString().padStart(2, '0')}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取视频平台显示名称
|
||||
* @param platform 平台标识
|
||||
* @returns 平台中文名称
|
||||
*/
|
||||
export function getPlatformName(platform: VideoPlatform): string {
|
||||
const names: Record<VideoPlatform, string> = {
|
||||
bilibili: 'B站',
|
||||
youtube: 'YouTube',
|
||||
douyin: '抖音',
|
||||
other: '视频',
|
||||
};
|
||||
return names[platform] || '视频';
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取视频平台主题色
|
||||
* @param platform 平台标识
|
||||
* @returns 平台主题色 (hex)
|
||||
*/
|
||||
export function getPlatformColor(platform: VideoPlatform): string {
|
||||
const colors: Record<VideoPlatform, string> = {
|
||||
bilibili: '#fb7299',
|
||||
youtube: '#ff0000',
|
||||
douyin: '#000000',
|
||||
other: '#6b7280',
|
||||
};
|
||||
return colors[platform] || '#6b7280';
|
||||
}
|
||||
|
||||
/**
|
||||
* 检测视频链接的平台
|
||||
* @param url 视频链接
|
||||
* @returns 平台标识
|
||||
*/
|
||||
export function detectPlatform(url: string): VideoPlatform {
|
||||
if (!url) return 'other';
|
||||
|
||||
if (url.includes('bilibili.com')) return 'bilibili';
|
||||
if (url.includes('youtube.com') || url.includes('youtu.be')) return 'youtube';
|
||||
if (url.includes('douyin.com')) return 'douyin';
|
||||
|
||||
return 'other';
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查视频是否支持嵌入播放
|
||||
* @param platform 平台标识
|
||||
* @returns 是否支持嵌入
|
||||
*/
|
||||
export function supportsEmbed(platform: VideoPlatform): boolean {
|
||||
return platform === 'bilibili' || platform === 'youtube';
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成带选集参数的嵌入 URL
|
||||
* @param url 原始视频链接
|
||||
* @param page 选集编号(从1开始)
|
||||
* @returns 嵌入 URL
|
||||
*/
|
||||
export function getEmbedUrlWithPage(url: string, page: number = 1): string | null {
|
||||
const videoInfo = parseVideoUrl(url);
|
||||
if (!videoInfo) return null;
|
||||
|
||||
if (videoInfo.platform === 'bilibili') {
|
||||
// B站嵌入链接支持 p 参数指定分P
|
||||
return `${videoInfo.embedUrl}&p=${page}`;
|
||||
}
|
||||
|
||||
// 其他平台暂不支持选集
|
||||
return videoInfo.embedUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查平台是否支持选集
|
||||
* @param platform 平台标识
|
||||
* @returns 是否支持选集
|
||||
*/
|
||||
export function supportsEpisodes(platform: VideoPlatform): boolean {
|
||||
return platform === 'bilibili';
|
||||
}
|
||||
@ -36,6 +36,8 @@ export interface Message {
|
||||
usedTools?: string[];
|
||||
/** 搜索到的图片(图片搜索工具结果) */
|
||||
searchImages?: SearchImageData[];
|
||||
/** 搜索到的视频(视频搜索工具结果) */
|
||||
searchVideos?: SearchVideoData[];
|
||||
}
|
||||
|
||||
// 搜索图片数据类型
|
||||
@ -49,6 +51,28 @@ export interface SearchImageData {
|
||||
sourceUrl?: string;
|
||||
}
|
||||
|
||||
// 搜索视频数据类型
|
||||
export interface SearchVideoData {
|
||||
/** 视频标题 */
|
||||
title: string;
|
||||
/** 视频链接 */
|
||||
link: string;
|
||||
/** 视频摘要 */
|
||||
snippet: string;
|
||||
/** 相关度评分 */
|
||||
score: string;
|
||||
/** 排序位置 */
|
||||
position: number;
|
||||
/** 作者列表 */
|
||||
authors: string[];
|
||||
/** 发布日期 */
|
||||
date: string;
|
||||
/** 时长(秒) */
|
||||
duration: string;
|
||||
/** 封面图URL */
|
||||
coverImage: string;
|
||||
}
|
||||
|
||||
// 工具调用记录
|
||||
export interface ToolCall {
|
||||
id: string;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user