- 视频分析: MiMo 2.5 大模型直接分析 - 场景检测: AI 视觉模型识别分镜头 - 语音识别: 每个场景单独 ASR - 语音克隆: MiMo TTS 语音克隆 - 下载功能: 单文件/批量 ZIP 下载 - 系统设置: API 密钥配置 - 文件夹管理: 创建/移动/筛选 - 扁平化 UI: 简约按钮样式
420 lines
22 KiB
HTML
420 lines
22 KiB
HTML
<!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>
|