278 lines
7.2 KiB
Plaintext
278 lines
7.2 KiB
Plaintext
import { tt } from '@/utils/i18nfun.uts'
|
|
|
|
// 视频内容类型扩展
|
|
export type VideoContent = {
|
|
id: string
|
|
title: string
|
|
summary: string | null
|
|
content: string
|
|
author: string
|
|
published_at: string
|
|
quality_score: number
|
|
category_id: string
|
|
original_language: string
|
|
source_url: string | null
|
|
tags: string[] | null
|
|
created_at: string
|
|
updated_at: string
|
|
|
|
// 视频特有字段
|
|
content_type: 'video'
|
|
video_url: string
|
|
video_duration: number
|
|
video_poster: string
|
|
video_width: number
|
|
video_height: number
|
|
video_size: number
|
|
video_format: string
|
|
video_quality: string
|
|
allow_danmu: boolean
|
|
allow_download: boolean
|
|
|
|
// 统计数据
|
|
view_count: number
|
|
like_count: number
|
|
favorite_count: number
|
|
share_count: number
|
|
comment_count: number
|
|
danmu_count: number
|
|
play_completion_rate: number
|
|
average_play_duration: number
|
|
|
|
// 用户状态
|
|
is_liked?: boolean
|
|
is_favorited?: boolean
|
|
}
|
|
|
|
// 弹幕类型
|
|
export type DanmuData = {
|
|
id: string
|
|
content_id: string
|
|
user_id: string
|
|
user_name: string
|
|
text: string
|
|
time_point: number
|
|
color: string
|
|
font_size: number
|
|
position_type: 'scroll' | 'top' | 'bottom'
|
|
speed: number
|
|
is_visible: boolean
|
|
status: string
|
|
created_at: string
|
|
}
|
|
|
|
// 弹幕发送数据
|
|
export type DanmuSendData = {
|
|
text: string
|
|
time_point: number
|
|
color?: string
|
|
font_size?: number
|
|
position_type?: 'scroll' | 'top' | 'bottom'
|
|
speed?: number
|
|
}
|
|
|
|
// 用户交互类型
|
|
export type UserInteraction = {
|
|
id: string
|
|
user_id: string
|
|
content_id: string
|
|
interaction_type: 'like' | 'favorite' | 'share' | 'view' | 'download'
|
|
interaction_data?: any
|
|
created_at: string
|
|
}
|
|
|
|
// 视频评论类型
|
|
export type VideoComment = {
|
|
id: string
|
|
content_id: string
|
|
user_id: string
|
|
user_name: string
|
|
parent_id: string | null
|
|
reply_to_user_id: string | null
|
|
reply_to_user_name: string | null
|
|
content: string
|
|
like_count: number
|
|
reply_count: number
|
|
status: string
|
|
is_pinned: boolean
|
|
level: number
|
|
created_at: string
|
|
updated_at: string
|
|
is_liked_by_user?: boolean
|
|
}
|
|
|
|
// 播放记录类型
|
|
export type PlayRecord = {
|
|
id: string
|
|
content_id: string
|
|
user_id: string
|
|
play_position: number
|
|
play_duration: number
|
|
play_percentage: number
|
|
is_completed: boolean
|
|
device_type: string
|
|
resolution: string
|
|
play_speed: number
|
|
created_at: string
|
|
updated_at: string
|
|
}
|
|
|
|
// 视频页面状态
|
|
export type VideoPageState = {
|
|
loading: boolean
|
|
error: string | null
|
|
danmu_loading: boolean
|
|
comment_loading: boolean
|
|
sending_danmu: boolean
|
|
posting_comment: boolean
|
|
}
|
|
|
|
// 弹幕配置
|
|
export type DanmuConfig = {
|
|
enabled: boolean
|
|
opacity: number
|
|
font_size: number
|
|
speed: number
|
|
show_area: number // 显示区域百分比
|
|
max_count: number // 同时显示最大数量
|
|
filter_enabled: boolean // 是否开启弹幕过滤
|
|
filter_keywords: string[] // 过滤关键词
|
|
}
|
|
|
|
// 视频播放器状态
|
|
export type VideoPlayerState = {
|
|
playing: boolean
|
|
current_time: number
|
|
duration: number
|
|
volume: number
|
|
playback_rate: number
|
|
fullscreen: boolean
|
|
quality: string
|
|
loading: boolean
|
|
error: string | null
|
|
}
|
|
|
|
// 分享选项
|
|
export type ShareOption = {
|
|
platform: string
|
|
name: string
|
|
icon: string
|
|
color: string
|
|
}
|
|
|
|
// 视频质量选项
|
|
export const VIDEO_QUALITY_OPTIONS = [
|
|
{ value: '360p', text: 'mt.video.quality.360p' },
|
|
{ value: '480p', text: 'mt.video.quality.480p' },
|
|
{ value: '720p', text: 'mt.video.quality.720p' },
|
|
{ value: '1080p', text: 'mt.video.quality.1080p' },
|
|
{ value: '4k', text: 'mt.video.quality.4k' }
|
|
]
|
|
|
|
// 播放速度选项
|
|
export const PLAYBACK_RATE_OPTIONS = [
|
|
{ value: 0.5, text: '0.5x' },
|
|
{ value: 0.75, text: '0.75x' },
|
|
{ value: 1.0, text: 'mt.video.speed.normal' },
|
|
{ value: 1.25, text: '1.25x' },
|
|
{ value: 1.5, text: '1.5x' },
|
|
{ value: 2.0, text: '2.0x' }
|
|
]
|
|
|
|
// 弹幕位置选项
|
|
export const DANMU_POSITION_OPTIONS = [
|
|
{ value: 'scroll', text: 'mt.video.danmu.position.scroll' },
|
|
{ value: 'top', text: 'mt.video.danmu.position.top' },
|
|
{ value: 'bottom', text: 'mt.video.danmu.position.bottom' }
|
|
]
|
|
|
|
// 弹幕颜色选项
|
|
export const DANMU_COLOR_OPTIONS = [
|
|
'#FFFFFF', '#FF0000', '#00FF00', '#0000FF', '#FFFF00',
|
|
'#FF00FF', '#00FFFF', '#FFA500', '#FFC0CB', '#800080'
|
|
]
|
|
|
|
// 分享平台选项
|
|
export const SHARE_PLATFORM_OPTIONS: ShareOption[] = [
|
|
{ platform: 'wechat', name: 'mt.share.wechat', icon: '💬', color: '#07C160' },
|
|
{ platform: 'weibo', name: 'mt.share.weibo', icon: '📱', color: '#E6162D' },
|
|
{ platform: 'qq', name: 'mt.share.qq', icon: '🐧', color: '#12B7F5' },
|
|
{ platform: 'link', name: 'mt.share.copyLink', icon: '🔗', color: '#666666' }
|
|
]
|
|
|
|
// 工具函数
|
|
|
|
// 格式化视频时长
|
|
export const formatVideoDuration = (seconds: number): string => {
|
|
const hours = Math.floor(seconds / 3600)
|
|
const minutes = Math.floor((seconds % 3600) / 60)
|
|
const secs = Math.floor(seconds % 60)
|
|
|
|
if (hours > 0) {
|
|
return `${hours}:${minutes.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`
|
|
} else {
|
|
return `${minutes}:${secs.toString().padStart(2, '0')}`
|
|
}
|
|
}
|
|
|
|
// 格式化文件大小
|
|
export const formatFileSize = (bytes: number): string => {
|
|
if (bytes === 0) return '0 B'
|
|
const k = 1024
|
|
const sizes = ['B', 'KB', 'MB', 'GB', 'TB']
|
|
const i = Math.floor(Math.log(bytes) / Math.log(k))
|
|
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]
|
|
}
|
|
|
|
// 格式化播放次数
|
|
export const formatViewCount = (count: number): string => {
|
|
if (count < 1000) return count.toString()
|
|
if (count < 10000) return (count / 1000).toFixed(1) + 'K'
|
|
if (count < 1000000) return Math.floor(count / 10000) + tt('mt.video.unit.wan')
|
|
return (count / 1000000).toFixed(1) + 'M'
|
|
}
|
|
|
|
// 获取视频质量显示文本
|
|
export const getVideoQualityText = (quality: string): string => {
|
|
const option = VIDEO_QUALITY_OPTIONS.find(opt => opt.value === quality)
|
|
return option ? tt(option.text) : quality
|
|
}
|
|
|
|
// 获取弹幕位置显示文本
|
|
export const getDanmuPositionText = (position: string): string => {
|
|
const option = DANMU_POSITION_OPTIONS.find(opt => opt.value === position)
|
|
return option ? tt(option.text) : position
|
|
}
|
|
|
|
// 获取分享平台显示文本
|
|
export const getSharePlatformText = (platform: string): string => {
|
|
const option = SHARE_PLATFORM_OPTIONS.find(opt => opt.platform === platform)
|
|
return option ? tt(option.name) : platform
|
|
}
|
|
|
|
// 弹幕校验结果类型
|
|
export type DanmuValidateResult = { valid: boolean; error?: string };
|
|
|
|
// 验证弹幕内容
|
|
export function validateDanmuText(text: string): DanmuValidateResult {
|
|
if ( text.trim().length === 0) {
|
|
return { valid: false, error: tt('mt.video.danmu.error.empty') };
|
|
}
|
|
if (text.length > 100) {
|
|
return { valid: false, error: tt('mt.video.danmu.error.tooLong') };
|
|
}
|
|
// 检查是否包含敏感词
|
|
const sensitiveWords = ['spam', 'advertisement']; // 简化示例
|
|
const hasSensitive = sensitiveWords.some(word => text.toLowerCase().includes(word));
|
|
if (hasSensitive) {
|
|
return { valid: false, error: tt('mt.video.danmu.error.sensitive') };
|
|
}
|
|
return { valid: true };
|
|
}
|
|
|
|
// 计算弹幕显示时间
|
|
export const calculateDanmuDisplayTime = (textLength: number, speed: number): number => {
|
|
// 基础显示时间 + 文本长度影响 / 速度
|
|
return Math.max(3, (5 + textLength * 0.1) / speed)
|
|
}
|