mcp-demo/server_index.html
GoMrStone 1a7567231a feat: 录音素材管理系统 - 完整功能
- 视频分析: MiMo 2.5 大模型直接分析
- 场景检测: AI 视觉模型识别分镜头
- 语音识别: 每个场景单独 ASR
- 语音克隆: MiMo TTS 语音克隆
- 下载功能: 单文件/批量 ZIP 下载
- 系统设置: API 密钥配置
- 文件夹管理: 创建/移动/筛选
- 扁平化 UI: 简约按钮样式
2026-07-10 17:26:53 +08:00

420 lines
22 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>拍摄素材管理</title>
<style>.nav-icon { width: 16px; height: 16px; object-fit: contain; }</style>
<link rel="stylesheet" href="/static/style.css?v=23">
</head>
<body>
<div class="app-layout">
<!-- Left Sidebar -->
<aside class="sidebar" id="sidebar">
<div class="sidebar-header">
<span class="sidebar-logo">🎬</span>
<span class="sidebar-title">拍摄剪辑</span>
</div>
<nav class="sidebar-nav">
<div class="nav-section">
<div class="nav-item active" data-filter="" onclick="filterByNav(this, 'all')">
<img class="nav-icon" src="/static/icons/Folder.svg" alt="">
<span class="nav-label">全部</span>
<span class="nav-count" id="countAll">0</span>
</div>
<div class="nav-item" data-filter="未分析" onclick="filterByNav(this, 'unanalyzed')">
<img class="nav-icon" src="/static/icons/add.svg" alt="">
<span class="nav-label">未标签</span>
<span class="nav-count" id="countUnanalyzed">0</span>
</div>
<div class="nav-item" data-filter="已分析" onclick="filterByNav(this, 'analyzed')">
<img class="nav-icon" src="/static/icons/check.svg" alt="">
<span class="nav-label">待拉片</span>
<span class="nav-count" id="countAnalyzed">0</span>
</div>
<div class="nav-item" data-filter="已剪辑" onclick="filterByNav(this, 'clipped')">
<img class="nav-icon" src="/static/icons/Play.svg" alt="">
<span class="nav-label">已拉片</span>
<span class="nav-count" id="countClipped">0</span>
</div>
<div class="nav-item" data-filter="recycle" onclick="filterByNav(this, 'recycle')">
<img class="nav-icon" src="/static/icons/Delete.svg" alt="">
<span class="nav-label">回收站</span>
<span class="nav-count" id="countRecycle">0</span>
</div>
</div>
<div class="nav-divider"></div>
<div class="nav-section">
<div class="nav-section-title">任务管理</div>
<div id="taskList">
<div class="nav-item" onclick="showTasks()">
<img class="nav-icon" src="/static/icons/TaskState_running.svg" alt="">
<span class="nav-label">分析任务</span>
<span class="nav-count" id="countRunning">0</span>
</div>
</div>
</div>
<div class="nav-divider"></div>
<div class="nav-section">
<div class="nav-section-title">智能文件夹</div>
<div id="categoryList"></div>
</div>
</nav>
<div class="nav-divider"></div>
<div class="nav-section">
<div class="nav-section-title">AI 工具</div>
<div class="nav-item" id="navVoiceClone" onclick="showVoiceClone()">
<span class="nav-icon">🎤</span>
<span class="nav-label">语音克隆</span>
</div>
</div>
<div class="sidebar-footer">
<button class="btn btn-block btn-upload" onclick="openUploadModal()">📤 上传素材</button>
</div>
</aside>
<!-- Main Area -->
<div class="main-area">
<!-- Top Bar -->
<div class="topbar">
<div class="topbar-left">
<button class="btn btn-icon" id="btnToggleSidebar" onclick="toggleSidebar()"></button>
<div class="breadcrumb">
<span class="breadcrumb-item" id="breadcrumbRoot">拍摄剪辑</span>
<span class="breadcrumb-sep"></span>
<span class="breadcrumb-item active" id="breadcrumbCurrent">全部</span>
</div>
</div>
<div class="topbar-center">
<div class="slider-group">
<span class="slider-label"></span>
<input type="range" id="cardSizeSlider" min="160" max="360" value="240" oninput="setCardSize(this.value)">
<span class="slider-label"></span>
</div>
</div>
<div class="topbar-right">
<div class="view-toggle">
<button class="view-btn active" id="btnGrid" onclick="setView('grid')"></button>
<button class="view-btn" id="btnList" onclick="setView('list')"></button>
</div>
<div class="search-box">
<input type="text" id="search" placeholder="搜索文件名或AI描述..." oninput="applyFilters()">
</div>
<button class="btn btn-analyze" id="btnAnalyze" onclick="startAnalysis()">🔍 分析</button>
</div>
</div>
<!-- Filter Bar -->
<div class="filterbar">
<div class="filter-group">
<label>时长:</label>
<input type="number" id="minDur" placeholder="最小" min="0" step="1" style="width:55px">
<span>-</span>
<input type="number" id="maxDur" placeholder="最大" min="0" step="1" style="width:55px">
<span></span>
</div>
<div class="filter-group">
<label>分辨率:</label>
<select id="resolutionFilter" onchange="applyFilters()">
<option value="">全部</option>
<option value="4k">4K</option>
<option value="1080p">1080P</option>
<option value="vertical">竖屏</option>
</select>
</div>
<div class="filter-results" id="filterResults"></div>
<button class="btn btn-select-all" onclick="selectAllPage()">☑ 全选本页</button>
</div>
<!-- Grid -->
<div class="content-area">
<div class="grid-container">
<div id="analysisProgress" style="display:none"></div>
<div class="grid" id="grid"></div>
<div class="empty-state" id="emptyState" style="display:none">
<p>📭 没有找到匹配的素材</p>
</div>
</div>
<!-- Pagination -->
<div class="pagination" id="pagination"></div>
</div>
<!-- Voice Clone Panel -->
<div id="voiceClonePanel">
<div class="vc-container">
<div class="vc-header">
<h2>🎤 语音克隆</h2>
<p class="vc-subtitle">上传音频/视频样本,使用 AI 克隆声音并合成语音</p>
</div>
<!-- Upload Zone -->
<div class="vc-upload-zone" id="vcUploadZone">
<div class="vc-upload-icon">📁</div>
<div class="vc-upload-text">拖拽音频/视频文件到这里,或点击选择</div>
<div class="vc-upload-hint">支持 WAV, MP3, M4A, AAC, FLAC, MP4, MOV, AVI, MKV</div>
<input type="file" id="vcFileInput" accept="audio/*,video/*" style="position:absolute;width:100%;height:100%;top:0;left:0;opacity:0;cursor:pointer;z-index:10">
</div>
<div class="vc-file-info" id="vcFileInfo" style="display:none">
<span class="vc-file-icon" id="vcFileIcon">🎵</span>
<span class="vc-file-name" id="vcFileName"></span>
<span class="vc-file-type" id="vcFileType"></span>
<button class="btn btn-sm" onclick="vcClearFile()">✕ 移除</button>
</div>
<!-- Presets -->
<div class="vc-section">
<div class="vc-section-title">语音预设</div>
<div class="vc-preset-group">
<button class="vc-preset-btn active" data-preset="natural" onclick="vcSetPreset('natural', this)">🌤 自然</button>
<button class="vc-preset-btn" data-preset="warm" onclick="vcSetPreset('warm', this)">☀️ 温暖</button>
<button class="vc-preset-btn" data-preset="confident" onclick="vcSetPreset('confident', this)">💪 自信</button>
<button class="vc-preset-btn" data-preset="expressive" onclick="vcSetPreset('expressive', this)">🎭 表现力</button>
<button class="vc-preset-btn" data-preset="stable" onclick="vcSetPreset('stable', this)">⚖️ 稳定</button>
<button class="vc-preset-btn" data-preset="creative" onclick="vcSetPreset('creative', this)">🎨 创意</button>
</div>
</div>
<!-- Text Input -->
<div class="vc-section">
<div class="vc-section-title">合成文本</div>
<textarea id="vcTextInput" class="vc-textarea" rows="3" placeholder="输入要合成的文字...">你好,这是一个声音克隆测试。</textarea>
</div>
<!-- Advanced Settings -->
<div class="vc-section">
<div class="vc-section-toggle" onclick="vcToggleAdvanced()">
<span>⚙️ 高级设置</span>
<span id="vcAdvancedArrow"></span>
</div>
<div class="vc-advanced" id="vcAdvanced" style="display:none">
<div class="vc-slider-group">
<label>稳定性</label>
<input type="range" id="vcStability" min="0" max="100" value="70" oninput="vcUpdateSlider(this)">
<span class="vc-slider-value" id="vcStabilityVal">0.7</span>
</div>
<div class="vc-slider-group">
<label>相似度</label>
<input type="range" id="vcSimilarity" min="0" max="100" value="80" oninput="vcUpdateSlider(this)">
<span class="vc-slider-value" id="vcSimilarityVal">0.8</span>
</div>
<div class="vc-slider-group">
<label>表现力</label>
<input type="range" id="vcStyle" min="0" max="100" value="0" oninput="vcUpdateSlider(this)">
<span class="vc-slider-value" id="vcStyleVal">0.0</span>
</div>
<div class="vc-slider-group">
<label>语速</label>
<input type="range" id="vcSpeed" min="50" max="200" value="100" oninput="vcUpdateSlider(this)">
<span class="vc-slider-value" id="vcSpeedVal">1.0</span>
</div>
<div class="vc-slider-group">
<label>音调</label>
<input type="range" id="vcPitch" min="-100" max="100" value="0" oninput="vcUpdateSlider(this)">
<span class="vc-slider-value" id="vcPitchVal">0.0</span>
</div>
<div class="vc-slider-group">
<label>强调</label>
<input type="range" id="vcEmphasis" min="0" max="100" value="0" oninput="vcUpdateSlider(this)">
<span class="vc-slider-value" id="vcEmphasisVal">0.0</span>
</div>
<div class="vc-slider-group">
<label>气息感</label>
<input type="range" id="vcBreathiness" min="0" max="100" value="0" oninput="vcUpdateSlider(this)">
<span class="vc-slider-value" id="vcBreathinessVal">0.0</span>
</div>
<div class="vc-slider-group">
<label>说话人增强</label>
<label class="vc-switch">
<input type="checkbox" id="vcSpeakerBoost" checked>
<span class="vc-switch-slider"></span>
</label>
</div>
</div>
</div>
<!-- Generate Button -->
<div class="vc-section">
<button class="btn btn-vc-generate" id="vcGenerateBtn" onclick="vcGenerate()" disabled>
🔄 生成克隆声音
</button>
</div>
<!-- Loading -->
<div class="vc-loading" id="vcLoading" style="display:none">
<div class="vc-spinner"></div>
<span id="vcLoadingText">正在生成...</span>
</div>
<!-- Error -->
<div class="vc-error" id="vcError" style="display:none"></div>
<!-- Player -->
<div class="vc-player" id="vcPlayer" style="display:none">
<div class="vc-player-header">播放结果</div>
<div class="vc-player-controls">
<button class="btn btn-vc-play" id="vcPlayBtn" onclick="vcTogglePlay()">▶ 播放</button>
<button class="btn btn-vc-stop" onclick="vcStop()">⏹ 停止</button>
<div class="vc-progress-wrapper">
<span class="vc-time" id="vcCurrentTime">00:00</span>
<input type="range" class="vc-progress" id="vcProgress" min="0" max="100" value="0" oninput="vcSeek(this.value)">
<span class="vc-time" id="vcDuration">00:00</span>
</div>
<div class="vc-speed-group">
<button class="vc-speed-btn" onclick="vcSetSpeed(0.5, this)">0.5x</button>
<button class="vc-speed-btn" onclick="vcSetSpeed(0.75, this)">0.75x</button>
<button class="vc-speed-btn active" onclick="vcSetSpeed(1.0, this)">1x</button>
<button class="vc-speed-btn" onclick="vcSetSpeed(1.25, this)">1.25x</button>
<button class="vc-speed-btn" onclick="vcSetSpeed(1.5, this)">1.5x</button>
<button class="vc-speed-btn" onclick="vcSetSpeed(2.0, this)">2x</button>
</div>
<button class="btn btn-vc-save" onclick="vcSaveAudio()">⬇ 保存</button>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Upload Modal -->
<div class="modal" id="uploadModal">
<div class="modal-overlay" onclick="closeUploadModal()"></div>
<div class="modal-content" style="max-width:600px">
<button class="modal-close" onclick="event.stopPropagation();closeUploadModal()"></button>
<div class="upload-header">
<h2>📤 上传视频素材</h2>
<p>支持 MP4、MOV、AVI、MKV 等格式,最大 4GB</p>
</div>
<div class="upload-body">
<div class="upload-dropzone" id="dropzone">
<div class="dropzone-icon">📁</div>
<p>拖拽文件到此处,或点击选择文件</p>
<input type="file" id="fileInput" multiple accept="video/*,.mp4,.mov,.avi,.mkv,.mts,.mxf,.wmv" style="position:absolute;width:100%;height:100%;top:0;left:0;opacity:0;cursor:pointer;z-index:10">
</div>
<div class="upload-category">
<label>分类:</label>
<input type="text" id="uploadCategory" value="上传素材" placeholder="输入分类名称">
</div>
<div class="upload-filelist" id="uploadFilelist"></div>
<div class="upload-progress" id="uploadProgress" style="display:none">
<div class="progress-bar">
<div class="progress-fill" id="uploadProgressFill"></div>
</div>
<span class="progress-text" id="uploadProgressText">上传中...</span>
</div>
<div class="upload-result" id="uploadResult" style="display:none"></div>
<button class="btn btn-upload-submit" id="btnUploadSubmit" onclick="submitUpload()" disabled>开始上传</button>
</div>
</div>
</div>
<!-- Task Panel -->
<div class="task-panel" id="taskPanel" style="display:none">
<div class="task-panel-header">
<span>📋 分析任务</span>
<button class="btn btn-icon" onclick="hideTasks()"></button>
</div>
<div class="task-panel-body" id="taskPanelBody"></div>
</div>
<!-- Analysis Progress -->
<div class="progress-overlay" id="progressBar" style="display:none">
<div class="progress-bar">
<div class="progress-fill" id="progressFill"></div>
</div>
<span class="progress-text" id="progressText">分析中...</span>
</div>
<!-- Video Preview Modal -->
<div class="modal" id="modal">
<div class="modal-overlay" onclick="closeModal()"></div>
<div class="modal-content">
<button class="modal-close" onclick="event.stopPropagation();closeModal()"></button>
<div class="modal-tabs">
<button class="modal-tab active" onclick="switchModalTab('preview', this)">🎬 预览</button>
<button class="modal-tab" onclick="switchModalTab('report', this)">📊 分析报告</button>
</div>
<div class="modal-tab-content" id="tabPreview">
<video id="videoPlayer" controls preload="auto"></video>
<div class="clip-toolbar" id="clipToolbar">
<div class="clip-timeline-wrapper">
<div class="clip-timeline" id="clipTimeline">
<div class="clip-played" id="clipPlayed"></div>
<div class="clip-selection" id="clipSelection"></div>
<div class="clip-marker clip-marker-in" id="clipMarkerIn"></div>
<div class="clip-marker clip-marker-out" id="clipMarkerOut"></div>
<div class="clip-playhead" id="clipPlayhead"></div>
</div>
<div class="clip-timeaxis" id="clipTimeaxis"></div>
</div>
<div class="clip-row">
<button class="btn btn-clip-set" onclick="setClipStart()">📍 入点</button>
<span class="clip-time" id="clipStart">00:00.000</span>
<span class="clip-sep"></span>
<button class="btn btn-clip-set" onclick="setClipEnd()">📍 出点</button>
<span class="clip-time" id="clipEnd">00:00.000</span>
<span class="clip-duration" id="clipDuration">时长: 0s</span>
<button class="btn btn-clip-play" onclick="playClipRange()">▶ 播放选区</button>
<button class="btn btn-clip-split" onclick="splitClip()">✂️ 分割</button>
<button class="btn btn-clip-save" onclick="saveClip()">💾 保存片段</button>
<button class="btn btn-clip-add" onclick="addClipToList()"> 添加到列表</button>
</div>
<div class="clip-list" id="clipList"></div>
</div>
<div class="modal-info" id="modalInfo"></div>
</div>
<div class="modal-tab-content" id="tabReport" style="display:none">
<div class="report-container" id="reportContainer"></div>
<div class="scene-section" id="sceneSection" style="display:none">
<div class="report-section">
<div class="report-section-title">🎤 语音转文字</div>
<div class="report-desc" id="transcriptionResult" style="min-height:40px"></div>
</div>
<div class="report-section">
<div class="report-section-title">📊 废镜头统计</div>
<div id="wasteStats"></div>
</div>
<div class="report-section">
<div class="report-section-title">🎬 逐镜头分析</div>
<div id="sceneResults"></div>
</div>
</div>
</div>
<div class="modal-actions">
<button class="btn btn-analyze-modal" id="modalAnalyzeBtn" onclick="analyzeFromModal()">🔍 分析</button>
<button class="btn btn-prev" onclick="navigateModal(-1)">◀ 上一个</button>
<button class="btn btn-next" onclick="navigateModal(1)">下一个 ▶</button>
</div>
</div>
</div>
<!-- Selected Bar -->
<div class="selected-bar" id="selectedBar" style="display:none">
<span>已选择 <strong id="selectedCount">0</strong> 个素材</span>
<div id="normalActions">
<button class="btn btn-export" onclick="exportSelected()">📥 导出报告</button>
<button class="btn btn-analyze-selected" onclick="analyzeSelected()">🔍 分析选中</button>
<button class="btn btn-delete-selected" onclick="deleteSelected()">🗑 移入回收站</button>
</div>
<div id="recycleActions" style="display:none">
<button class="btn btn-restore" onclick="restoreSelected()">♻️ 恢复选中</button>
<button class="btn btn-permanent-delete" onclick="permanentDeleteSelected()">🔥 彻底删除</button>
</div>
<button class="btn btn-clear" onclick="clearSelection()">✕ 清空</button>
</div>
<script src="/static/app.js?v=1783450100"></script>
</body>
</html>