feat(类型): 添加视频搜索类型定义和工具函数

- 新增 SearchVideoData 类型定义,包含视频标题、链接、时长、作者等字段
- 新增 Message 类型的 searchVideos 属性用于存储视频搜索结果
- 添加 videoUtils.ts 视频工具函数库:
  - parseVideoUrl: 解析B站/YouTube/抖音视频链接
  - formatDuration: 格式化视频时长显示
  - getPlatformName/getPlatformColor: 获取平台名称和主题色
  - detectPlatform: 检测视频平台类型
  - supportsEmbed: 判断是否支持嵌入播放
  - getEmbedUrlWithPage: 生成带选集参数的嵌入URL
  - supportsEpisodes: 判断是否支持选集功能
This commit is contained in:
gaoziman 2025-12-22 21:56:16 +08:00
parent 92ec88e1a3
commit 159009dd56
2 changed files with 209 additions and 0 deletions

185
src/lib/videoUtils.ts Normal file
View 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';
}

View File

@ -36,6 +36,8 @@ export interface Message {
usedTools?: string[]; usedTools?: string[];
/** 搜索到的图片(图片搜索工具结果) */ /** 搜索到的图片(图片搜索工具结果) */
searchImages?: SearchImageData[]; searchImages?: SearchImageData[];
/** 搜索到的视频(视频搜索工具结果) */
searchVideos?: SearchVideoData[];
} }
// 搜索图片数据类型 // 搜索图片数据类型
@ -49,6 +51,28 @@ export interface SearchImageData {
sourceUrl?: string; 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 { export interface ToolCall {
id: string; id: string;