增加自行上传弹幕
This commit is contained in:
@@ -6,6 +6,7 @@ import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { getEpisodes, searchAnime } from '@/lib/danmaku/api';
|
||||
import type {
|
||||
DanmakuAnime,
|
||||
DanmakuComment,
|
||||
DanmakuEpisode,
|
||||
DanmakuSelection,
|
||||
} from '@/lib/danmaku/types';
|
||||
@@ -15,6 +16,7 @@ interface DanmakuPanelProps {
|
||||
currentEpisodeIndex: number;
|
||||
onDanmakuSelect: (selection: DanmakuSelection) => void;
|
||||
currentSelection: DanmakuSelection | null;
|
||||
onUploadDanmaku?: (comments: DanmakuComment[]) => void;
|
||||
}
|
||||
|
||||
export default function DanmakuPanel({
|
||||
@@ -22,6 +24,7 @@ export default function DanmakuPanel({
|
||||
currentEpisodeIndex,
|
||||
onDanmakuSelect,
|
||||
currentSelection,
|
||||
onUploadDanmaku,
|
||||
}: DanmakuPanelProps) {
|
||||
const [searchKeyword, setSearchKeyword] = useState('');
|
||||
const [searchResults, setSearchResults] = useState<DanmakuAnime[]>([]);
|
||||
@@ -31,6 +34,7 @@ export default function DanmakuPanel({
|
||||
const [isLoadingEpisodes, setIsLoadingEpisodes] = useState(false);
|
||||
const [searchError, setSearchError] = useState<string | null>(null);
|
||||
const initializedRef = useRef(false); // 标记是否已初始化过
|
||||
const fileInputRef = useRef<HTMLInputElement>(null);
|
||||
|
||||
// 搜索弹幕
|
||||
const handleSearch = useCallback(async (keyword: string) => {
|
||||
@@ -118,6 +122,38 @@ export default function DanmakuPanel({
|
||||
[currentSelection]
|
||||
);
|
||||
|
||||
// 处理文件上传
|
||||
const handleFileUpload = useCallback(async (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const file = event.target.files?.[0];
|
||||
if (!file) return;
|
||||
|
||||
if (!file.name.endsWith('.xml')) {
|
||||
setSearchError('请上传XML格式的弹幕文件');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const text = await file.text();
|
||||
const { parseXmlDanmaku } = await import('@/lib/danmaku/xml-parser');
|
||||
const comments = parseXmlDanmaku(text);
|
||||
|
||||
if (comments.length === 0) {
|
||||
setSearchError('弹幕文件解析失败或文件为空');
|
||||
return;
|
||||
}
|
||||
|
||||
onUploadDanmaku?.(comments);
|
||||
setSearchError(null);
|
||||
} catch (error) {
|
||||
console.error('上传弹幕失败:', error);
|
||||
setSearchError('弹幕文件解析失败');
|
||||
} finally {
|
||||
if (fileInputRef.current) {
|
||||
fileInputRef.current.value = '';
|
||||
}
|
||||
}
|
||||
}, [onUploadDanmaku]);
|
||||
|
||||
// 当视频标题首次加载时,初始化搜索关键词(仅执行一次)
|
||||
useEffect(() => {
|
||||
if (videoTitle && !initializedRef.current) {
|
||||
@@ -369,6 +405,25 @@ export default function DanmakuPanel({
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 上传弹幕区域 - 固定在底部 */}
|
||||
{onUploadDanmaku && (
|
||||
<div className='mt-3 flex-shrink-0 border-t border-gray-200 pt-3 dark:border-gray-700'>
|
||||
<input
|
||||
ref={fileInputRef}
|
||||
type='file'
|
||||
accept='.xml'
|
||||
onChange={handleFileUpload}
|
||||
className='hidden'
|
||||
/>
|
||||
<button
|
||||
onClick={() => fileInputRef.current?.click()}
|
||||
className='w-full text-center text-xs text-gray-500 dark:text-gray-400 hover:text-green-500 dark:hover:text-green-400 transition-colors py-2'
|
||||
>
|
||||
搜不到想要的弹幕?自行上传
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ import { Settings } from 'lucide-react';
|
||||
|
||||
import DanmakuPanel from '@/components/DanmakuPanel';
|
||||
import EpisodeFilterSettings from '@/components/EpisodeFilterSettings';
|
||||
import type { DanmakuSelection } from '@/lib/danmaku/types';
|
||||
import type { DanmakuSelection, DanmakuComment } from '@/lib/danmaku/types';
|
||||
import { SearchResult, EpisodeFilterConfig } from '@/lib/types';
|
||||
import { getVideoResolutionFromM3u8, processImageUrl } from '@/lib/utils';
|
||||
|
||||
@@ -51,6 +51,7 @@ interface EpisodeSelectorProps {
|
||||
/** 弹幕相关 */
|
||||
onDanmakuSelect?: (selection: DanmakuSelection) => void;
|
||||
currentDanmakuSelection?: DanmakuSelection | null;
|
||||
onUploadDanmaku?: (comments: DanmakuComment[]) => void;
|
||||
/** 观影室房员状态 - 禁用选集和换源,但保留弹幕 */
|
||||
isRoomMember?: boolean;
|
||||
/** 集数过滤配置 */
|
||||
@@ -79,6 +80,7 @@ const EpisodeSelector: React.FC<EpisodeSelectorProps> = ({
|
||||
precomputedVideoInfo,
|
||||
onDanmakuSelect,
|
||||
currentDanmakuSelection,
|
||||
onUploadDanmaku,
|
||||
isRoomMember = false,
|
||||
episodeFilterConfig = null,
|
||||
onFilterConfigUpdate,
|
||||
@@ -576,6 +578,7 @@ const EpisodeSelector: React.FC<EpisodeSelectorProps> = ({
|
||||
currentEpisodeIndex={value - 1}
|
||||
onDanmakuSelect={onDanmakuSelect}
|
||||
currentSelection={currentDanmakuSelection || null}
|
||||
onUploadDanmaku={onUploadDanmaku}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user