吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 2922|回复: 37
收起左侧

[其他原创] 音频剪切处理器

[复制链接]
llb618 发表于 2025-11-11 17:19
本帖最后由 llb618 于 2025-11-11 19:30 编辑

image.png image.png image.png
image.png 目前支持裁剪合并等,我也就是裁点小音频生成对口型视频用的比如最近有点火的西游人物歌曲等。要求太高的我也不会弄,就网页版的便捷简单。浏览器打开就可以用了。对于不会用网页代码的直接下载吧。 index.zip (22.78 KB, 下载次数: 296)

[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>
  <!-- 外部资源 -->
  <script src="https://cdn.tailwindcss.com"></script>
  <link href="https://cdn.jsdelivr.net/npm/font-awesome@4.7.0/css/font-awesome.min.css" rel="stylesheet">
  <script src="https://cdn.jsdelivr.net/npm/lamejs@1.2.1/lame.min.js"></script>
  <script src="https://cdn.jsdelivr.net/npm/wavesurfer.js@6.6.3/dist/wavesurfer.min.js"></script>
  <script src="https://cdn.jsdelivr.net/npm/wavesurfer.js@6.6.3/dist/plugin/wavesurfer.regions.min.js"></script>
  <!-- ffmpeg.js 暂时移除,以避免ORB安全机制阻止 -->
<!-- <script src="https://cdn.jsdelivr.net/npm/ffmpeg.js@4.2.9003/ffmpeg.min.js"></script> -->
  <script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.8/dist/chart.umd.min.js"></script>

  <!-- Tailwind 配置 -->
  <script>
    tailwind.config = {
      theme: {
        extend: {
          colors: {
            primary: '#4F46E5',
            secondary: '#10B981',
            danger: '#EF4444',
            dark: '#1E293B',
            light: '#F8FAFC'
          },
          fontFamily: {
            inter: ['Inter', 'system-ui', 'sans-serif'],
          },
          animation: {
            'pulse-slow': 'pulse 3s cubic-bezier(0.4, 0, 0.6, 1) infinite',
          }
        },
      }
    }
  </script>
  
  <!-- 自定义样式 -->
  <style type="text/tailwindcss">
    @layer utilities {
      .content-auto { content-visibility: auto; }
      .scrollbar-hide {
        scrollbar-width: none;
        -ms-overflow-style: none;
      }
      .scrollbar-hide::-webkit-scrollbar { display: none; }
      .timeline-marker {
        width: 2px;
        height: 100%;
        background-color: #4F46E5;
        position: absolute;
        top: 0;
        transform: translateX(-50%);
        z-index: 10;
      }
      .timeline-selection {
        position: absolute;
        height: 100%;
        background-color: rgba(79,70,229,0.2);
        z-index: 5;
      }
      .timeline-handle {
        width: 8px;
        height: 24px;
        background-color: #4F46E5;
        position: absolute;
        top: 50%;
        transform: translate(-50%, -50%);
        cursor: ew-resize;
        z-index: 15;
        box-shadow: 0 0 0 2px white;
        transition: all 0.2s ease;
      }
      .timeline-handle:hover {
        height: 32px;
        background-color: #3B34A9;
      }
      .waveform-container {
        position: relative;
        height: 140px;
        background: #f8fafc;
        border-radius: 0.5rem;
        overflow: hidden;
      }
      .tool-btn {
        @apply px-3 py-2 rounded-md text-sm transition-all duration-200 flex items-center gap-1 hover:shadow-md;
      }
      .tool-btn.active {
        @apply bg-primary/10 border-primary;
      }
      .panel {
        @apply bg-white rounded-xl shadow-md p-4 md:p-6 transition-all duration-300 hover:shadow-lg;
      }
      .tab-active {
        @apply border-b-2 border-primary text-primary font-medium;
      }
      .clip-item {
        @apply bg-white rounded-lg p-3 shadow-sm flex justify-between items-center cursor-move transition-all duration-200 hover:shadow-md;
      }
      .audio-visualizer {
        @apply h-16 w-full bg-gray-50 rounded-lg overflow-hidden;
      }
      .notification {
        @apply fixed bottom-4 right-4 px-4 py-3 rounded-lg shadow-lg z-50 transform transition-all duration-300 translate-y-20 opacity-0;
      }
      .notification.show {
        @apply translate-y-0 opacity-100;
      }
      .notification.success {
        @apply bg-secondary text-white;
      }
      .notification.error {
        @apply bg-danger text-white;
      }
      .notification.info {
        @apply bg-primary text-white;
      }
      .dark-mode {
        @apply bg-gray-900 text-white;
      }
      .dark-mode .panel,
      .dark-mode header,
      .dark-mode #mobileMenu,
      .dark-mode footer {
        @apply bg-gray-800;
      }
      .dark-mode .waveform-container,
      .dark-mode .bg-gray-50 {
        @apply bg-gray-700;
      }
      .dark-mode .text-gray-500,
      .dark-mode .text-gray-600 {
        @apply text-gray-300;
      }
      .dark-mode .border-gray-200,
      .dark-mode .border-gray-300 {
        @apply border-gray-600;
      }
      .processing-spinner {
        @apply animate-spin h-5 w-5 border-2 border-white border-t-primary rounded-full inline-block;
      }
    }
  </style>
</head>

<body class="font-inter bg-gray-50 text-dark min-h-screen flex flex-col transition-colors duration-300">
  <!-- 通知组件 -->
  <div id="notification" class="notification"></div>
  
  <!-- 顶部导航栏 -->
  <header class="bg-white shadow-sm sticky top-0 z-50 transition-all duration-300">
    <div class="container mx-auto px-4 py-4 flex justify-between items-center">
      <div class="flex items-center space-x-2">
        <i class="fa fa-music text-primary text-2xl"></i>
        <h1 class="text-xl md:text-2xl font-bold text-primary">全能音频处理器</h1>
      </div>
      <div class="hidden md:flex items-center space-x-6">
        <button id="helpBtn" class="text-gray-600 hover:text-primary transition-colors">
          <i class="fa fa-question-circle mr-1"></i>帮助
        </button>
        <button id="presetBtn" class="text-gray-600 hover:text-primary transition-colors">
          <i class="fa fa-bookmark mr-1"></i>预设
        </button>
        <button id="themeToggle" class="text-gray-600 hover:text-primary transition-colors">
          <i class="fa fa-moon-o mr-1"></i>夜间模式
        </button>
      </div>
      <button class="md:hidden text-gray-600" id="mobileMenuBtn">
        <i class="fa fa-bars text-xl"></i>
      </button>
    </div>
    
    <!-- 移动端菜单 -->
    <div id="mobileMenu" class="md:hidden hidden bg-white border-t border-gray-200 py-2 px-4">
      <button id="mobileHelpBtn" class="w-full text-left py-2 text-gray-600 hover:text-primary transition-colors">
        <i class="fa fa-question-circle mr-1"></i>帮助
      </button>
      <button id="mobilePresetBtn" class="w-full text-left py-2 text-gray-600 hover:text-primary transition-colors">
        <i class="fa fa-bookmark mr-1"></i>预设
      </button>
      <button id="mobileThemeToggle" class="w-full text-left py-2 text-gray-600 hover:text-primary transition-colors">
        <i class="fa fa-moon-o mr-1"></i>夜间模式
      </button>
    </div>
  </header>

  <!-- 主要内容区 -->
  <main class="flex-grow container mx-auto px-4 py-6">
    <!-- 功能标签页 -->
    <div class="flex border-b border-gray-200 mb-6 overflow-x-auto scrollbar-hide">
      <button id="tab-clip" class="tab-active px-4 py-3 text-sm md:text-base whitespace-nowrap transition-all">
        <i class="fa fa-scissors mr-1"></i>音频裁剪
      </button>
      <button id="tab-edit" class="px-4 py-3 text-sm md:text-base text-gray-600 hover:text-primary whitespace-nowrap transition-all">
        <i class="fa fa-sliders mr-1"></i>音频编辑
      </button>
      <button id="tab-convert" class="px-4 py-3 text-sm md:text-base text-gray-600 hover:text-primary whitespace-nowrap transition-all">
        <i class="fa fa-exchange mr-1"></i>格式转换
      </button>
      <button id="tab-batch" class="px-4 py-3 text-sm md:text-base text-gray-600 hover:text-primary whitespace-nowrap transition-all">
        <i class="fa fa-files-o mr-1"></i>批量处理
      </button>
    </div>

    <!-- 上传区域 -->
    <section id="uploadSection" class="mb-6">
      <div class="panel">
        <h2 class="text-xl font-semibold mb-4 flex items-center">
          <i class="fa fa-upload text-primary mr-2"></i>上传音频/视频文件
        </h2>
        
        <div id="dropArea" class="border-2 border-dashed border-gray-300 rounded-lg p-8 text-center transition-all duration-300 hover:border-primary hover:bg-primary/5 group">
          <i class="fa fa-file-audio-o text-5xl text-gray-400 mb-4 group-hover:text-primary transition-colors"></i>
          <p class="text-gray-600 mb-4">拖放文件到此处,或点击选择文件</p>
          <p class="text-sm text-gray-500 mb-6">支持格式:MP3, WAV, OGG, WebM, MP4, MOV(可提取音频)</p>
          <label class="inline-block bg-primary hover:bg-primary/90 text-white font-medium py-3 px-6 rounded-lg cursor-pointer transition-all duration-300 transform hover:scale-105">
            <i class="fa fa-folder-open mr-2"></i>选择文件
            <input type="file" id="audioInput" accept="audio/*,video/*" class="hidden" multiple>
          </label>
        </div>
        
        <!-- 上传进度条 (默认隐藏) -->
        <div id="progressContainer" class="hidden mt-6">
          <div class="flex justify-between text-sm mb-1">
            <span id="fileName" class="text-gray-600 truncate max-w-[70%]"></span>
            <span id="progressPercent" class="text-primary font-medium">0%</span>
          </div>
          <div class="w-full bg-gray-200 rounded-full h-2.5 overflow-hidden">
            <div id="progressBar" class="bg-primary h-2.5 rounded-full transition-all duration-300 ease-out" style="width: 0%"></div>
          </div>
        </div>

        <!-- 文件列表 (多文件上传) -->
        <div id="fileList" class="hidden mt-6 space-y-2 max-h-40 overflow-y-auto scrollbar-hide">
          <div class="flex justify-between items-center">
            <h3 class="font-medium">已上传文件</h3>
            <span class="text-sm text-gray-500">共 <span id="totalFilesCount">0</span> 个文件</span>
          </div>
          <div id="fileItems" class="space-y-1"></div>
        </div>
      </div>
    </section>
    
    <!-- 音频处理区域 (默认隐藏) -->
    <section id="audioProcessingSection" class="hidden space-y-6">
      <!-- 1. 音频裁剪标签页 -->
      <div id="panel-clip" class="space-y-6">
        <!-- 音频信息和播放器 -->
        <div class="panel">
          <h2 class="text-xl font-semibold mb-4 flex items-center">
            <i class="fa fa-headphones text-primary mr-2"></i>音频预览与裁剪
          </h2>
          
          <div class="flex flex-col lg:flex-row gap-6">
            <!-- 左侧:波形与时间轴 -->
            <div class="w-full lg:w-2/3">
              <audio id="audioPlayer" controls class="w-full mb-6"></audio>
              
              <!-- 波形可视化 -->
              <div class="waveform-container mb-6 relative" id="waveform">
                <!-- 由wavesurfer.js动态生成波形 -->
                <div class="absolute inset-0 flex items-center justify-center text-gray-400" id="waveform-placeholder">
                  <i class="fa fa-spinner fa-spin text-2xl mr-2"></i>
                  <span>正在生成波形...</span>
                </div>
                <div id="timelineSelection" class="timeline-selection hidden"></div>
                <div id="startHandle" class="timeline-handle hidden"></div>
                <div id="endHandle" class="timeline-handle hidden"></div>
                <div id="playhead" class="timeline-marker" style="left: 0%"></div>
                
                <!-- 音频频谱可视化 -->
                <div class="audio-visualizer absolute bottom-0 left-0 right-0 opacity-0 transition-opacity duration-300" id="audioVisualizer">
                  <canvas id="visualizerCanvas" class="w-full h-full"></canvas>
                </div>
              </div>
              
              <!-- 音频信息 -->
              <div class="flex flex-wrap gap-4 mb-6">
                <div class="flex items-center bg-gray-50 px-4 py-2 rounded-lg">
                  <i class="fa fa-clock-o text-primary mr-2"></i>
                  <span>总时长: <span id="totalDuration" class="font-medium">00:00</span></span>
                </div>
                <div class="flex items-center bg-gray-50 px-4 py-2 rounded-lg">
                  <i class="fa fa-file-size text-primary mr-2"></i>
                  <span>文件大小: <span id="fileSize" class="font-medium">0 MB</span></span>
                </div>
                <div class="flex items-center bg-gray-50 px-4 py-2 rounded-lg">
                  <i class="fa fa-music text-primary mr-2"></i>
                  <span>格式: <span id="fileFormat" class="font-medium">未知</span></span>
                </div>
                <div class="flex items-center bg-gray-50 px-4 py-2 rounded-lg">
                  <i class="fa fa-signal text-primary mr-2"></i>
                  <span>采样率: <span id="sampleRate" class="font-medium">0 Hz</span></span>
                </div>
                <div class="flex items-center bg-gray-50 px-4 py-2 rounded-lg">
                  <i class="fa fa-area-chart text-primary mr-2"></i>
                  <span>声道: <span id="channels" class="font-medium">0</span></span>
                </div>
              </div>
              
              <!-- 时间输入控制 -->
              <div class="flex flex-wrap gap-4 items-center">
                <div class="flex items-center gap-2">
                  <label for="startTime" class="text-gray-700">开始时间:</label>
                  <input type="time" id="startTime" step="0.001" class="border border-gray-300 rounded-md px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-primary/50" value="00:00:00.000">
                </div>
                <div class="flex items-center gap-2">
                  <label for="endTime" class="text-gray-700">结束时间:</label>
                  <input type="time" id="endTime" step="0.001" class="border border-gray-300 rounded-md px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-primary/50" value="00:00:00.000">
                </div>
                <button id="addClipBtn" class="bg-secondary hover:bg-secondary/90 text-white font-medium py-2 px-4 rounded-lg transition-all duration-300 ml-auto transform hover:scale-105">
                  <i class="fa fa-plus mr-1"></i>添加剪切片段
                </button>
              </div>
            </div>
            
            <!-- 右侧:智能裁剪与片段列表 -->
            <div class="w-full lg:w-1/3 space-y-4">
              <!-- 智能裁剪设置 -->
              <div class="panel">
                <h3 class="font-semibold mb-3 flex items-center">
                  <i class="fa fa-magic text-primary mr-2"></i>智能裁剪
                </h3>
                <div class="space-y-4">
                  <div>
                    <label class="block text-gray-700 text-sm mb-1">目标片段时长</label>
                    <div class="flex gap-2">
                      <input type="range" id="targetDurationSlider" min="5" max="180" value="30" 
                        class="w-full h-2 bg-gray-200 rounded-lg appearance-none cursor-pointer accent-primary">
                      <input type="number" id="targetDuration" value="30" min="5" max="180" 
                        class="border border-gray-300 rounded-md px-3 py-2 text-sm w-16">
                      <span class="flex items-center text-gray-500">秒</span>
                    </div>
                  </div>
                  <div>
                    <label class="block text-gray-700 text-sm mb-1">裁剪策略</label>
                    <select id="clipStrategy" class="w-full border border-gray-300 rounded-md px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-primary/50">
                      <option value="lyric">歌词完整性优先(歌曲专用)</option>
                      <option value="silence">静音间隔优先(通用)</option>
                      <option value="equal">均等分割(不考虑内容)</option>
                    </select>
                  </div>
                  <button id="smartClipBtn" class="w-full bg-primary hover:bg-primary/90 text-white font-medium py-2 px-4 rounded-lg transition-all duration-300 transform hover:scale-[1.02]">
                    <i class="fa fa-bolt mr-1"></i>智能生成片段
                  </button>
                </div>
              </div>
              
              <!-- 剪切片段列表 -->
              <div class="panel">
                <div class="flex justify-between items-center mb-4">
                  <h3 class="font-semibold text-lg">剪切片段</h3>
                  <span id="clipCount" class="bg-primary/10 text-primary px-2 py-1 rounded-full text-sm">0 段</span>
                </div>
                
                <!-- 导出设置 -->
                <div class="bg-gray-50 rounded-lg p-3 mb-4">
                  <h4 class="font-medium mb-2 text-sm">导出设置</h4>
                  <div class="flex items-center gap-2 mb-2">
                    <label for="exportPrefix" class="text-gray-700 text-sm">文件名前缀:</label>
                    <input type="text" id="exportPrefix" value="clip" class="border border-gray-300 rounded-md px-3 py-1.5 text-sm flex-grow focus:outline-none focus:ring-2 focus:ring-primary/50">
                  </div>
                  <div class="flex items-center gap-2">
                    <label for="exportFormat" class="text-gray-700 text-sm">导出格式:</label>
                    <select id="exportFormat" class="border border-gray-300 rounded-md px-3 py-1.5 text-sm focus:outline-none focus:ring-2 focus:ring-primary/50">
                      <option value="wav">WAV (无损)</option>
                      <option value="mp3">MP3 (压缩)</option>
                      <option value="aac">AAC (高效)</option>
                      <option value="flac">FLAC (无损压缩)</option>
                    </select>
                  </div>
                  <div class="flex items-center gap-2 mt-2">
                    <label for="bitrate" class="text-gray-700 text-sm">比特率:</label>
                    <select id="bitrate" class="border border-gray-300 rounded-md px-3 py-1.5 text-sm focus:outline-none focus:ring-2 focus:ring-primary/50">
                      <option value="64">64kbps (低)</option>
                      <option value="128" selected>128kbps (中)</option>
                      <option value="192">192kbps (高)</option>
                      <option value="320">320kbps (最高)</option>
                    </select>
                  </div>
                </div>
                
                <div id="clipsList" class="space-y-3 max-h-64 overflow-y-auto scrollbar-hide">
                  <div class="text-center text-gray-500 py-8">
                    <i class="fa fa-film text-2xl mb-2"></i>
                    <p>还没有添加剪切片段</p>
                  </div>
                </div>
                
                <div class="mt-4 pt-4 border-t border-gray-200">
                  <button id="clearClipsBtn" class="w-full bg-gray-200 hover:bg-gray-300 text-gray-700 font-medium py-2 px-4 rounded-lg transition-all duration-300 mb-3">
                    <i class="fa fa-trash mr-2"></i>清空片段
                  </button>
                  <button id="exportBtn" class="w-full bg-primary hover:bg-primary/90 text-white font-medium py-3 px-4 rounded-lg transition-all duration-300 disabled:opacity-50 disabled:cursor-not-allowed transform hover:scale-[1.02]" disabled>
                    <i class="fa fa-download mr-2"></i>导出所有片段
                  </button>
                </div>
              </div>
            </div>
          </div>
        </div>
      </div>
      
      <!-- 2. 音频编辑标签页 (默认隐藏) -->
      <div id="panel-edit" class="hidden space-y-6">
        <div class="panel">
          <h2 class="text-xl font-semibold mb-4 flex items-center">
            <i class="fa fa-sliders text-primary mr-2"></i>音频编辑
          </h2>
          
          <div class="flex flex-col lg:flex-row gap-6">
            <!-- 左侧:编辑控件 -->
            <div class="w-full lg:w-1/3 space-y-4">
              <!-- 音量调节 -->
              <div>
                <h3 class="font-medium mb-3">音量调节</h3>
                <div class="flex items-center gap-3">
                  <i class="fa fa-volume-down text-gray-500"></i>
                  <input type="range" id="volumeControl" min="0" max="200" value="100" class="w-full h-2 bg-gray-200 rounded-lg appearance-none cursor-pointer accent-primary">
                  <i class="fa fa-volume-up text-gray-500"></i>
                </div>
                <div class="flex justify-between text-sm text-gray-500 mt-1">
                  <span>0%</span>
                  <span id="volumeValue">100%</span>
                  <span>200%</span>
                </div>
              </div>
              
              <!-- 音效处理 -->
              <div class="pt-4 border-t border-gray-200">
                <h3 class="font-medium mb-3">音效处理</h3>
                <div class="grid grid-cols-2 gap-2">
                  <button class="tool-btn bg-white border border-gray-300 hover:border-primary" data-effect="fadein">
                    <i class="fa fa-arrow-up"></i>淡入
                  </button>
                  <button class="tool-btn bg-white border border-gray-300 hover:border-primary" data-effect="fadeout">
                    <i class="fa fa-arrow-down"></i>淡出
                  </button>
                  <button class="tool-btn bg-white border border-gray-300 hover:border-primary" data-effect="reverse">
                    <i class="fa fa-rotate-right"></i>反转
                  </button>
                  <button class="tool-btn bg-white border border-gray-300 hover:border-primary" data-effect="noise">
                    <i class="fa fa-ban"></i>降噪
                  </button>
                  <button class="tool-btn bg-white border border-gray-300 hover:border-primary" data-effect="echo">
                    <i class="fa fa-repeat"></i>回声
                  </button>
                  <button class="tool-btn bg-white border border-gray-300 hover:border-primary" data-effect="reverb">
                    <i class="fa fa-area-chart"></i>混响
                  </button>
                </div>
              </div>
              
              <!-- 均衡器 -->
              <div class="pt-4 border-t border-gray-200">
                <h3 class="font-medium mb-3">均衡器</h3>
                <div class="space-y-3">
                  <div>
                    <div class="flex justify-between text-xs text-gray-500 mb-1">
                      <span>60Hz</span>
                      <span id="eq60Value">0dB</span>
                    </div>
                    <input type="range" min="-12" max="12" value="0" class="w-full h-1.5 bg-gray-200 rounded-lg appearance-none cursor-pointer accent-primary" data-eq="60">
                  </div>
                  <div>
                    <div class="flex justify-between text-xs text-gray-500 mb-1">
                      <span>250Hz</span>
                      <span id="eq250Value">0dB</span>
                    </div>
                    <input type="range" min="-12" max="12" value="0" class="w-full h-1.5 bg-gray-200 rounded-lg appearance-none cursor-pointer accent-primary" data-eq="250">
                  </div>
                  <div>
                    <div class="flex justify-between text-xs text-gray-500 mb-1">
                      <span>1kHz</span>
                      <span id="eq1kValue">0dB</span>
                    </div>
                    <input type="range" min="-12" max="12" value="0" class="w-full h-1.5 bg-gray-200 rounded-lg appearance-none cursor-pointer accent-primary" data-eq="1000">
                  </div>
                  <div>
                    <div class="flex justify-between text-xs text-gray-500 mb-1">
                      <span>4kHz</span>
                      <span id="eq4kValue">0dB</span>
                    </div>
                    <input type="range" min="-12" max="12" value="0" class="w-full h-1.5 bg-gray-200 rounded-lg appearance-none cursor-pointer accent-primary" data-eq="4000">
                  </div>
                  <div>
                    <div class="flex justify-between text-xs text-gray-500 mb-1">
                      <span>16kHz</span>
                      <span id="eq16kValue">0dB</span>
                    </div>
                    <input type="range" min="-12" max="12" value="0" class="w-full h-1.5 bg-gray-200 rounded-lg appearance-none cursor-pointer accent-primary" data-eq="16000">
                  </div>
                </div>
              </div>
              
              <!-- 速度与音调 -->
              <div class="pt-4 border-t border-gray-200">
                <h3 class="font-medium mb-3">速度与音调</h3>
                <div class="space-y-3">
                  <div>
                    <label class="block text-sm text-gray-600 mb-1">播放速度</label>
                    <input type="range" id="speedControl" min="0.5" max="2" step="0.1" value="1" 
                      class="w-full h-2 bg-gray-200 rounded-lg appearance-none cursor-pointer accent-primary">
                    <div class="flex justify-between text-sm text-gray-500 mt-1">
                      <span>0.5x</span>
                      <span id="speedValue">1.0x</span>
                      <span>2.0x</span>
                    </div>
                  </div>
                  <div>
                    <label class="block text-sm text-gray-600 mb-1">音调调整</label>
                    <input type="range" id="pitchControl" min="-12" max="12" step="1" value="0" 
                      class="w-full h-2 bg-gray-200 rounded-lg appearance-none cursor-pointer accent-primary">
                    <div class="flex justify-between text-sm text-gray-500 mt-1">
                      <span>-12半音</span>
                      <span id="pitchValue">0</span>
                      <span>+12半音</span>
                    </div>
                  </div>
                </div>
              </div>
              
              <!-- 应用按钮 -->
              <div class="pt-4 border-t border-gray-200">
                <button id="applyEffectsBtn" class="w-full bg-secondary hover:bg-secondary/90 text-white font-medium py-3 px-4 rounded-lg transition-all duration-300 transform hover:scale-[1.02]">
                  <i class="fa fa-check mr-1"></i>应用所有效果
                </button>
                <button id="resetEffectsBtn" class="w-full mt-2 bg-gray-200 hover:bg-gray-300 text-gray-700 font-medium py-2 px-4 rounded-lg transition-all duration-300">
                  <i class="fa fa-refresh mr-1"></i>重置效果
                </button>
              </div>
            </div>
            
            <!-- 右侧:预览与片段拼接 -->
            <div class="w-full lg:w-2/3">
              <audio id="editAudioPlayer" controls class="w-full mb-6"></audio>
              
              <div class="mb-6">
                <h3 class="font-medium mb-3">片段拼接</h3>
                <div id="mergeList" class="bg-gray-50 rounded-lg p-3 min-h-32 mb-3 border border-dashed border-gray-300">
                  <div class="text-center text-gray-500 py-6">
                    <i class="fa fa-arrows-h text-xl mb-2"></i>
                    <p>将剪切片段拖到此处排序拼接</p>
                  </div>
                </div>
                <button id="mergeClipsBtn" class="bg-primary hover:bg-primary/90 text-white font-medium py-2 px-4 rounded-lg transition-all duration-300 transform hover:scale-105">
                  <i class="fa fa-object-group mr-1"></i>合并选中片段
                </button>
              </div>
              
              <!-- 音频标签编辑 -->
              <div class="bg-gray-50 rounded-lg p-4">
                <h3 class="font-medium mb-3">音频标签</h3>
                <div class="grid grid-cols-1 md:grid-cols-2 gap-3">
                  <div>
                    <label class="block text-sm text-gray-600 mb-1">标题</label>
                    <input type="text" id="audioTitle" class="w-full border border-gray-300 rounded-md px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-primary/50">
                  </div>
                  <div>
                    <label class="block text-sm text-gray-600 mb-1">艺术家</label>
                    <input type="text" id="audioArtist" class="w-full border border-gray-300 rounded-md px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-primary/50">
                  </div>
                  <div>
                    <label class="block text-sm text-gray-600 mb-1">专辑</label>
                    <input type="text" id="audioAlbum" class="w-full border border-gray-300 rounded-md px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-primary/50">
                  </div>
                  <div>
                    <label class="block text-sm text-gray-600 mb-1">年份</label>
                    <input type="text" id="audioYear" class="w-full border border-gray-300 rounded-md px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-primary/50">
                  </div>
                  <div class="md:col-span-2">
                    <label class="block text-sm text-gray-600 mb-1">备注</label>
                    <textarea id="audioComment" rows="2" class="w-full border border-gray-300 rounded-md px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-primary/50"></textarea>
                  </div>
                </div>
                <button id="saveTagsBtn" class="mt-3 bg-gray-200 hover:bg-gray-300 text-gray-700 font-medium py-2 px-4 rounded-lg transition-all duration-300">
                  <i class="fa fa-save mr-1"></i>保存标签
                </button>
              </div>
            </div>
          </div>
        </div>
      </div>
      
      <!-- 3. 格式转换标签页 (默认隐藏) -->
      <div id="panel-convert" class="hidden">
        <div class="panel">
          <h2 class="text-xl font-semibold mb-4 flex items-center">
            <i class="fa fa-exchange text-primary mr-2"></i>格式转换
          </h2>
          
          <div class="flex flex-col lg:flex-row gap-6">
            <div class="w-full lg:w-1/2">
              <h3 class="font-medium mb-3">源文件</h3>
              <div id="convertFileList" class="bg-gray-50 rounded-lg p-4 max-h-40 overflow-y-auto scrollbar-hide mb-6">
                <div class="text-center text-gray-500 py-6">
                  <p>请先上传需要转换的文件</p>
                </div>
              </div>
              
              <!-- 格式对比 -->
              <div class="bg-gray-50 rounded-lg p-4">
                <h3 class="font-medium mb-3 text-sm">格式特性对比</h3>
                <div class="overflow-x-auto">
                  <table class="min-w-full text-sm">
                    <thead>
                      <tr class="border-b border-gray-200">
                        <th class="py-2 text-left">格式</th>
                        <th class="py-2 text-left">特点</th>
                        <th class="py-2 text-left">适用场景</th>
                      </tr>
                    </thead>
                    <tbody>
                      <tr class="border-b border-gray-200">
                        <td class="py-2 font-medium">MP3</td>
                        <td class="py-2">压缩率高,兼容性好</td>
                        <td class="py-2">日常播放、分享</td>
                      </tr>
                      <tr class="border-b border-gray-200">
                        <td class="py-2 font-medium">WAV</td>
                        <td class="py-2">无损,文件大</td>
                        <td class="py-2">专业编辑、保存</td>
                      </tr>
                      <tr class="border-b border-gray-200">
                        <td class="py-2 font-medium">FLAC</td>
                        <td class="py-2">无损压缩,音质好</td>
                        <td class="py-2">高品质音乐收藏</td>
                      </tr>
                      <tr>
                        <td class="py-2 font-medium">AAC</td>
                        <td class="py-2">高效压缩,音质优于MP3</td>
                        <td class="py-2">移动设备、流媒体</td>
                      </tr>
                    </tbody>
                  </table>
                </div>
              </div>
            </div>
            
            <div class="w-full lg:w-1/2">
              <h3 class="font-medium mb-3">转换设置</h3>
              <div class="space-y-4">
                <div>
                  <label class="block text-gray-700 mb-1">目标格式</label>
                  <select id="convertFormat" class="w-full border border-gray-300 rounded-md px-3 py-2 focus:outline-none focus:ring-2 focus:ring-primary/50">
                    <option value="mp3">MP3</option>
                    <option value="wav">WAV</option>
                    <option value="aac">AAC</option>
                    <option value="flac">FLAC</option>
                    <option value="ogg">OGG</option>
                  </select>
                </div>
                
                <div>
                  <label class="block text-gray-700 mb-1">采样率</label>
                  <select id="convertSampleRate" class="w-full border border-gray-300 rounded-md px-3 py-2 focus:outline-none focus:ring-2 focus:ring-primary/50">
                    <option value="8000">8kHz (低)</option>
                    <option value="22050">22.05kHz</option>
                    <option value="44100" selected>44.1kHz (标准)</option>
                    <option value="48000">48kHz (高清)</option>
                  </select>
                </div>
                
                <div>
                  <label class="block text-gray-700 mb-1">比特率</label>
                  <select id="convertBitrate" class="w-full border border-gray-300 rounded-md px-3 py-2 focus:outline-none focus:ring-2 focus:ring-primary/50">
                    <option value="64">64kbps</option>
                    <option value="128">128kbps</option>
                    <option value="192" selected>192kbps</option>
                    <option value="320">320kbps</option>
                  </select>
                </div>
                
                <div>
                  <label class="block text-gray-700 mb-1">输出文件夹</label>
                  <div class="flex">
                    <input type="text" id="outputFolder" value="转换输出" class="flex-grow border border-gray-300 rounded-l-md px-3 py-2 focus:outline-none focus:ring-2 focus:ring-primary/50">
                    <button id="browseFolderBtn" class="bg-gray-200 hover:bg-gray-300 px-3 py-2 rounded-r-md border border-l-0 border-gray-300 transition-colors">
                      <i class="fa fa-folder-open"></i>
                    </button>
                  </div>
                </div>
                
                <div>
                  <label class="block text-gray-700 mb-1">文件名格式</label>
                  <select id="fileNameFormat" class="w-full border border-gray-300 rounded-md px-3 py-2 focus:outline-none focus:ring-2 focus:ring-primary/50">
                    <option value="original">保留原文件名</option>
                    <option value="prefix">前缀+序号</option>
                    <option value="custom">自定义格式</option>
                  </select>
                </div>
                
                <div id="customFileName" class="hidden">
                  <label class="block text-sm text-gray-600 mb-1">自定义文件名前缀</label>
                  <input type="text" id="customFileNamePrefix" value="converted_" class="w-full border border-gray-300 rounded-md px-3 py-1.5 text-sm focus:outline-none focus:ring-2 focus:ring-primary/50">
                </div>
                
                <div class="pt-2">
                  <label class="flex items-center gap-2 cursor-pointer">
                    <input type="checkbox" id="deleteOriginal" class="accent-primary">
                    <span class="text-gray-700">转换后删除源文件</span>
                  </label>
                </div>
                
                <button id="startConvertBtn" class="w-full bg-primary hover:bg-primary/90 text-white font-medium py-3 px-4 rounded-lg transition-all duration-300 disabled:opacity-50 disabled:cursor-not-allowed transform hover:scale-[1.02]" disabled>
                  <i class="fa fa-cog mr-1"></i>开始转换
                </button>
              </div>
              
              <!-- 转换进度 -->
              <div id="conversionProgress" class="hidden mt-6">
                <div class="flex justify-between text-sm mb-1">
                  <span id="convertingFileName" class="text-gray-600"></span>
                  <span id="conversionPercent" class="text-primary font-medium">0%</span>
                </div>
                <div class="w-full bg-gray-200 rounded-full h-2.5 overflow-hidden">
                  <div id="conversionBar" class="bg-primary h-2.5 rounded-full transition-all duration-300 ease-out" style="width: 0%"></div>
                </div>
              </div>
            </div>
          </div>
        </div>
      </div>
      
      <!-- 4. 批量处理标签页 -->
      <div id="panel-batch" class="hidden">
        <div class="panel">
          <h2 class="text-xl font-semibold mb-4 flex items-center">
            <i class="fa fa-files-o text-primary mr-2"></i>批量处理
          </h2>
          
          <div class="space-y-6">
            <div>
              <h3 class="font-medium mb-3">待处理文件</h3>
              <div id="batchFileList" class="bg-gray-50 rounded-lg p-4 min-h-40 border border-dashed border-gray-300">
                <div class="text-center text-gray-500 py-6">
                  <i class="fa fa-upload text-xl mb-2"></i>
                  <p>拖放多个文件到此处,或点击上方"选择文件"按钮</p>
                </div>
              </div>
            </div>
            
            <div class="grid grid-cols-1 md:grid-cols-2 gap-6">
              <div>
                <h3 class="font-medium mb-3">批量操作</h3>
                <div class="space-y-3">
                  <label class="flex items-center gap-2 p-3 border border-gray-200 rounded-lg cursor-pointer hover:bg-gray-50 transition-colors">
                    <input type="checkbox" id="batchTrim" class="accent-primary">
                    <div>
                      <span class="block font-medium">统一裁剪</span>
                      <span class="text-sm text-gray-500">对所有文件进行相同时间段的裁剪</span>
                    </div>
                  </label>
                  
                  <div id="batchTrimSettings" class="hidden pl-6 pr-3 pb-3 space-y-3">
                    <div class="flex items-center gap-2">
                      <label for="batchStartTime" class="text-gray-700 text-sm">开始时间:</label>
                      <input type="time" id="batchStartTime" step="0.001" class="border border-gray-300 rounded-md px-3 py-1.5 text-sm focus:outline-none focus:ring-2 focus:ring-primary/50" value="00:00:00.000">
                    </div>
                    <div class="flex items-center gap-2">
                      <label for="batchEndTime" class="text-gray-700 text-sm">结束时间:</label>
                      <input type="time" id="batchEndTime" step="0.001" class="border border-gray-300 rounded-md px-3 py-1.5 text-sm focus:outline-none focus:ring-2 focus:ring-primary/50" value="00:00:10.000">
                    </div>
                  </div>
                  
                  <label class="flex items-center gap-2 p-3 border border-gray-200 rounded-lg cursor-pointer hover:bg-gray-50 transition-colors">
                    <input type="checkbox" id="batchFormat" class="accent-primary">
                    <div>
                      <span class="block font-medium">格式转换</span>
                      <span class="text-sm text-gray-500">将所有文件转换为指定格式</span>
                    </div>
                  </label>
                  
                  <div id="batchFormatSettings" class="hidden pl-6 pr-3 pb-3 space-y-3">
                    <div>
                      <label class="block text-gray-700 text-sm mb-1">目标格式</label>
                      <select id="batchTargetFormat" class="w-full border border-gray-300 rounded-md px-3 py-1.5 text-sm focus:outline-none focus:ring-2 focus:ring-primary/50">
                        <option value="mp3">MP3</option>
                        <option value="wav">WAV</option>
                        <option value="aac">AAC</option>
                        <option value="flac">FLAC</option>
                      </select>
                    </div>
                  </div>
                  
                  <label class="flex items-center gap-2 p-3 border border-gray-200 rounded-lg cursor-pointer hover:bg-gray-50 transition-colors">
                    <input type="checkbox" id="batchVolume" class="accent-primary">
                    <div>
                      <span class="block font-medium">音量调整</span>
                      <span class="text-sm text-gray-500">统一调整所有文件的音量</span>
                    </div>
                  </label>
                  
                  <div id="batchVolumeSettings" class="hidden pl-6 pr-3 pb-3">
                    <div class="flex items-center gap-3">
                      <i class="fa fa-volume-down text-gray-500"></i>
                      <input type="range" id="batchVolumeControl" min="0" max="200" value="100" class="w-full h-2 bg-gray-200 rounded-lg appearance-none cursor-pointer accent-primary">
                      <i class="fa fa-volume-up text-gray-500"></i>
                    </div>
                    <div class="flex justify-between text-sm text-gray-500 mt-1">
                      <span>0%</span>
                      <span id="batchVolumeValue">100%</span>
                      <span>200%</span>
                    </div>
                  </div>
                  
                  <label class="flex items-center gap-2 p-3 border border-gray-200 rounded-lg cursor-pointer hover:bg-gray-50 transition-colors">
                    <input type="checkbox" id="batchTags" class="accent-primary">
                    <div>
                      <span class="block font-medium">统一标签</span>
                      <span class="text-sm text-gray-500">为所有文件添加相同的元数据标签</span>
                    </div>
                  </label>
                  
                  <div id="batchTagsSettings" class="hidden pl-6 pr-3 pb-3 space-y-3">
                    <div>
                      <label class="block text-sm text-gray-600 mb-1">专辑名称</label>
                      <input type="text" id="batchAlbumName" class="w-full border border-gray-300 rounded-md px-3 py-1.5 text-sm focus:outline-none focus:ring-2 focus:ring-primary/50">
                    </div>
                    <div>
                      <label class="block text-sm text-gray-600 mb-1">艺术家</label>
                      <input type="text" id="batchArtistName" class="w-full border border-gray-300 rounded-md px-3 py-1.5 text-sm focus:outline-none focus:ring-2 focus:ring-primary/50">
                    </div>
                  </div>
                </div>
              </div>
              
              <div>
                <h3 class="font-medium mb-3">处理设置</h3>
                <div class="space-y-4">
                  <div>
                    <label class="block text-gray-700 mb-1">输出路径</label>
                    <div class="flex">
                      <input type="text" id="batchOutputFolder" value="批量处理输出" class="flex-grow border border-gray-300 rounded-l-md px-3 py-2 focus:outline-none focus:ring-2 focus:ring-primary/50">
                      <button id="batchBrowseFolderBtn" class="bg-gray-200 hover:bg-gray-300 px-3 py-2 rounded-r-md border border-l-0 border-gray-300 transition-colors">
                        <i class="fa fa-folder-open"></i>
                      </button>
                    </div>
                  </div>
                  
                  <div>
                    <label class="block text-gray-700 mb-1">文件名格式</label>
                    <select id="batchFileNameFormat" class="w-full border border-gray-300 rounded-md px-3 py-2 focus:outline-none focus:ring-2 focus:ring-primary/50">
                      <option value="original">保留原文件名</option>
                      <option value="prefix">前缀+序号 (例如: audio_001)</option>
                      <option value="custom">自定义格式</option>
                    </select>
                  </div>
                  
                  <div id="batchCustomFileName" class="hidden">
                    <label class="block text-gray-700 text-sm mb-1">自定义文件名前缀</label>
                    <input type="text" id="batchFileNamePrefix" value="audio_" class="w-full border border-gray-300 rounded-md px-3 py-1.5 text-sm focus:outline-none focus:ring-2 focus:ring-primary/50">
                  </div>
                  
                  <div class="pt-2">
                    <label class="flex items-center gap-2 cursor-pointer">
                      <input type="checkbox" id="batchOverwrite" class="accent-primary" checked>
                      <span class="text-gray-700">覆盖已存在的文件</span>
                    </label>
                  </div>
                  
                  <div class="pt-2">
                    <label class="flex items-center gap-2 cursor-pointer">
                      <input type="checkbox" id="batchDeleteOriginal" class="accent-primary">
                      <span class="text-gray-700">处理后删除源文件</span>
                    </label>
                  </div>
                  
                  <button id="startBatchBtn" class="w-full bg-primary hover:bg-primary/90 text-white font-medium py-3 px-4 rounded-lg transition-all duration-300 disabled:opacity-50 disabled:cursor-not-allowed transform hover:scale-[1.02]" disabled>
                    <i class="fa fa-play mr-1"></i>开始批量处理
                  </button>
                </div>
                
                <!-- 批量处理进度 -->
                <div id="batchProgress" class="hidden mt-6">
                  <div class="flex justify-between text-sm mb-1">
                    <span id="batchProgressText" class="text-gray-600">准备中...</span>
                    <span id="batchProgressPercent" class="text-primary font-medium">0%</span>
                  </div>
                  <div class="w-full bg-gray-200 rounded-full h-2.5 overflow-hidden">
                    <div id="batchProgressBar" class="bg-primary h-2.5 rounded-full transition-all duration-300 ease-out" style="width: 0%"></div>
                  </div>
                </div>
              </div>
            </div>
          </div>
        </div>
      </div>
    </section>
  </main>

  <!-- 页脚 -->
  <footer class="bg-white shadow-inner py-6 transition-all duration-300">
    <div class="container mx-auto px-4 text-center text-gray-500 text-sm">
      <p>全能音频处理器 &#169; 2023 | 一个功能强大的在线音频处理工具</p>
      <div class="flex justify-center space-x-4 mt-2">
        <a href="#" class="hover:text-primary transition-colors"><i class="fa fa-github"></i> 源码</a>
        <a href="#" class="hover:text-primary transition-colors"><i class="fa fa-question-circle"></i> 使用帮助</a>
        <a href="#" class="hover:text-primary transition-colors"><i class="fa fa-shield"></i> 隐私政策</a>
      </div>
    </div>
  </footer>

  <!-- 帮助模态框 -->
  <div id="helpModal" class="fixed inset-0 bg-black/50 z-50 hidden items-center justify-center p-4">
    <div class="bg-white rounded-xl shadow-xl max-w-2xl w-full max-h-[90vh] overflow-y-auto">
      <div class="p-6 border-b border-gray-200">
        <div class="flex justify-between items-center">
          <h3 class="text-xl font-bold text-primary">使用帮助</h3>
          <button id="closeHelpBtn" class="text-gray-500 hover:text-gray-700">
            <i class="fa fa-times text-xl"></i>
          </button>
        </div>
      </div>
      <div class="p-6 space-y-4">
        <div>
          <h4 class="font-semibold text-lg mb-2">如何上传音频文件?</h4>
          <p class="text-gray-600">您可以点击"选择文件"按钮浏览并选择文件,或直接将音频/视频文件拖放到上传区域。</p>
        </div>
        <div>
          <h4 class="font-semibold text-lg mb-2">如何裁剪音频?</h4>
          <p class="text-gray-600">1. 上传音频后,在波形图上拖动滑块选择需要保留的部分<br>
          2. 或直接输入开始和结束时间<br>
          3. 点击"添加剪切片段"按钮保存片段<br>
          4. 设置导出格式后点击"导出所有片段"</p>
        </div>
        <div>
          <h4 class="font-semibold text-lg mb-2">支持哪些音频格式?</h4>
          <p class="text-gray-600">支持MP3、WAV、OGG、FLAC、AAC等常见音频格式,也可以从MP4、MOV等视频文件中提取音频。</p>
        </div>
        <div>
          <h4 class="font-semibold text-lg mb-2">处理后的文件保存在哪里?</h4>
          <p class="text-gray-600">处理完成的文件会直接下载到您浏览器的默认下载文件夹中。</p>
        </div>
      </div>
    </div>
  </div>

  <!-- 预设模态框 -->
  <div id="presetModal" class="fixed inset-0 bg-black/50 z-50 hidden items-center justify-center p-4">
    <div class="bg-white rounded-xl shadow-xl max-w-md w-full">
      <div class="p-6 border-b border-gray-200">
        <div class="flex justify-between items-center">
          <h3 class="text-xl font-bold text-primary">处理预设</h3>
          <button id="closePresetBtn" class="text-gray-500 hover:text-gray-700">
            <i class="fa fa-times text-xl"></i>
          </button>
        </div>
      </div>
      <div class="p-6">
        <h4 class="font-medium mb-3">常用预设</h4>
        <div class="space-y-2">
          <button class="w-full text-left p-3 border border-gray-200 rounded-lg hover:bg-primary/5 transition-colors" data-preset="podcast">
            <span class="font-medium">播客优化</span>
            <p class="text-sm text-gray-500">提高人声清晰度,降低背景噪音</p>
          </button>
          <button class="w-full text-left p-3 border border-gray-200 rounded-lg hover:bg-primary/5 transition-colors" data-preset="music">
            <span class="font-medium">音乐增强</span>
            <p class="text-sm text-gray-500">优化音质,增强立体声效果</p>
          </button>
          <button class="w-full text-left p-3 border border-gray-200 rounded-lg hover:bg-primary/5 transition-colors" data-preset="ringtone">
            <span class="font-medium">铃声制作</span>
            <p class="text-sm text-gray-500">适合手机铃声的音量和格式设置</p>
          </button>
          <button class="w-full text-left p-3 border border-gray-200 rounded-lg hover:bg-primary/5 transition-colors" data-preset="voice">
            <span class="font-medium">语音笔记</span>
            <p class="text-sm text-gray-500">优化语音录制,提高可懂度</p>
          </button>
        </div>
        
        <div class="mt-6 pt-6 border-t border-gray-200">
          <h4 class="font-medium mb-3">我的预设</h4>
          <div class="text-center text-gray-500 py-4">
            <p>您还没有保存任何预设</p>
          </div>
          <button id="saveCurrentPresetBtn" class="w-full mt-2 bg-gray-200 hover:bg-gray-300 text-gray-700 font-medium py-2 px-4 rounded-lg transition-all duration-300">
            <i class="fa fa-save mr-1"></i>保存当前设置为预设
          </button>
        </div>
      </div>
    </div>
  </div>

  <script>
    // 全局变量
    let wavesurfer;
    let audioContext;
    let analyser;
    let canvas, ctx;
    let clips = [];
    let currentAudioFile = null;
    let audioFiles = [];
    let isDarkMode = false;
    let animationId = null;
    
    // DOM 元素
    const elements = {
      // 上传相关
      dropArea: document.getElementById('dropArea'),
      audioInput: document.getElementById('audioInput'),
      progressContainer: document.getElementById('progressContainer'),
      fileName: document.getElementById('fileName'),
      progressBar: document.getElementById('progressBar'),
      progressPercent: document.getElementById('progressPercent'),
      fileList: document.getElementById('fileList'),
      fileItems: document.getElementById('fileItems'),
      totalFilesCount: document.getElementById('totalFilesCount'),
      audioProcessingSection: document.getElementById('audioProcessingSection'),
      
      // 标签页相关
      tabs: {
        clip: document.getElementById('tab-clip'),
        edit: document.getElementById('tab-edit'),
        convert: document.getElementById('tab-convert'),
        batch: document.getElementById('tab-batch')
      },
      panels: {
        clip: document.getElementById('panel-clip'),
        edit: document.getElementById('panel-edit'),
        convert: document.getElementById('panel-convert'),
        batch: document.getElementById('panel-batch')
      },
      
      // 音频裁剪相关
      audioPlayer: document.getElementById('audioPlayer'),
      waveform: document.getElementById('waveform'),
      waveformPlaceholder: document.getElementById('waveform-placeholder'),
      playhead: document.getElementById('playhead'),
      timelineSelection: document.getElementById('timelineSelection'),
      startHandle: document.getElementById('startHandle'),
      endHandle: document.getElementById('endHandle'),
      startTime: document.getElementById('startTime'),
      endTime: document.getElementById('endTime'),
      addClipBtn: document.getElementById('addClipBtn'),
      totalDuration: document.getElementById('totalDuration'),
      fileSize: document.getElementById('fileSize'),
      fileFormat: document.getElementById('fileFormat'),
      sampleRate: document.getElementById('sampleRate'),
      channels: document.getElementById('channels'),
      targetDurationSlider: document.getElementById('targetDurationSlider'),
      targetDuration: document.getElementById('targetDuration'),
      clipStrategy: document.getElementById('clipStrategy'),
      smartClipBtn: document.getElementById('smartClipBtn'),
      clipsList: document.getElementById('clipsList'),
      clipCount: document.getElementById('clipCount'),
      exportPrefix: document.getElementById('exportPrefix'),
      exportFormat: document.getElementById('exportFormat'),
      bitrate: document.getElementById('bitrate'),
      clearClipsBtn: document.getElementById('clearClipsBtn'),
      exportBtn: document.getElementById('exportBtn'),
      
      // 音频编辑相关
      editAudioPlayer: document.getElementById('editAudioPlayer'),
      volumeControl: document.getElementById('volumeControl'),
      volumeValue: document.getElementById('volumeValue'),
      eqControls: document.querySelectorAll('[data-eq]'),
      eqValues: {
        60: document.getElementById('eq60Value'),
        250: document.getElementById('eq250Value'),
        1000: document.getElementById('eq1kValue'),
        4000: document.getElementById('eq4kValue'),
        16000: document.getElementById('eq16kValue')
      },
      speedControl: document.getElementById('speedControl'),
      speedValue: document.getElementById('speedValue'),
      pitchControl: document.getElementById('pitchControl'),
      pitchValue: document.getElementById('pitchValue'),
      effectButtons: document.querySelectorAll('[data-effect]'),
      applyEffectsBtn: document.getElementById('applyEffectsBtn'),
      resetEffectsBtn: document.getElementById('resetEffectsBtn'),
      mergeList: document.getElementById('mergeList'),
      mergeClipsBtn: document.getElementById('mergeClipsBtn'),
      audioTitle: document.getElementById('audioTitle'),
      audioArtist: document.getElementById('audioArtist'),
      audioAlbum: document.getElementById('audioAlbum'),
      audioYear: document.getElementById('audioYear'),
      audioComment: document.getElementById('audioComment'),
      saveTagsBtn: document.getElementById('saveTagsBtn'),
      
      // 格式转换相关
      convertFileList: document.getElementById('convertFileList'),
      convertFormat: document.getElementById('convertFormat'),
      convertSampleRate: document.getElementById('convertSampleRate'),
      convertBitrate: document.getElementById('convertBitrate'),
      outputFolder: document.getElementById('outputFolder'),
      browseFolderBtn: document.getElementById('browseFolderBtn'),
      deleteOriginal: document.getElementById('deleteOriginal'),
      fileNameFormat: document.getElementById('fileNameFormat'),
      customFileName: document.getElementById('customFileName'),
      customFileNamePrefix: document.getElementById('customFileNamePrefix'),
      startConvertBtn: document.getElementById('startConvertBtn'),
      conversionProgress: document.getElementById('conversionProgress'),
      convertingFileName: document.getElementById('convertingFileName'),
      conversionBar: document.getElementById('conversionBar'),
      conversionPercent: document.getElementById('conversionPercent'),
      
      // 批量处理相关
      batchFileList: document.getElementById('batchFileList'),
      batchTrim: document.getElementById('batchTrim'),
      batchTrimSettings: document.getElementById('batchTrimSettings'),
      batchStartTime: document.getElementById('batchStartTime'),
      batchEndTime: document.getElementById('batchEndTime'),
      batchFormat: document.getElementById('batchFormat'),
      batchFormatSettings: document.getElementById('batchFormatSettings'),
      batchTargetFormat: document.getElementById('batchTargetFormat'),
      batchVolume: document.getElementById('batchVolume'),
      batchVolumeSettings: document.getElementById('batchVolumeSettings'),
      batchVolumeControl: document.getElementById('batchVolumeControl'),
      batchVolumeValue: document.getElementById('batchVolumeValue'),
      batchTags: document.getElementById('batchTags'),
      batchTagsSettings: document.getElementById('batchTagsSettings'),
      batchAlbumName: document.getElementById('batchAlbumName'),
      batchArtistName: document.getElementById('batchArtistName'),
      batchOutputFolder: document.getElementById('batchOutputFolder'),
      batchBrowseFolderBtn: document.getElementById('batchBrowseFolderBtn'),
      batchFileNameFormat: document.getElementById('batchFileNameFormat'),
      batchCustomFileName: document.getElementById('batchCustomFileName'),
      batchFileNamePrefix: document.getElementById('batchFileNamePrefix'),
      batchOverwrite: document.getElementById('batchOverwrite'),
      batchDeleteOriginal: document.getElementById('batchDeleteOriginal'),
      startBatchBtn: document.getElementById('startBatchBtn'),
      batchProgress: document.getElementById('batchProgress'),
      batchProgressText: document.getElementById('batchProgressText'),
      batchProgressBar: document.getElementById('batchProgressBar'),
      batchProgressPercent: document.getElementById('batchProgressPercent'),
      
      // 其他UI元素
      notification: document.getElementById('notification'),
      themeToggle: document.getElementById('themeToggle'),
      mobileThemeToggle: document.getElementById('mobileThemeToggle'),
      mobileMenuBtn: document.getElementById('mobileMenuBtn'),
      mobileMenu: document.getElementById('mobileMenu'),
      helpBtn: document.getElementById('helpBtn'),
      mobileHelpBtn: document.getElementById('mobileHelpBtn'),
      helpModal: document.getElementById('helpModal'),
      closeHelpBtn: document.getElementById('closeHelpBtn'),
      presetBtn: document.getElementById('presetBtn'),
      mobilePresetBtn: document.getElementById('mobilePresetBtn'),
      presetModal: document.getElementById('presetModal'),
      closePresetBtn: document.getElementById('closePresetBtn'),
      saveCurrentPresetBtn: document.getElementById('saveCurrentPresetBtn')
    };

    // 初始化应用
    function initApp() {
      initEventListeners();
      initWaveSurfer();
      loadDarkModePreference();
    }

    // 初始化事件监听器
    function initEventListeners() {
      // 上传相关事件
      elements.dropArea.addEventListener('dragover', handleDragOver);
      elements.dropArea.addEventListener('drop', handleDrop);
      elements.audioInput.addEventListener('change', handleFileSelect);
      
      // 标签页切换事件
      Object.keys(elements.tabs).forEach(tab => {
        elements.tabs[tab].addEventListener('click', () => switchTab(tab));
      });
      
      // 音频裁剪相关事件
      elements.audioPlayer.addEventListener('timeupdate', updatePlayhead);
      elements.startTime.addEventListener('change', updateSelectionFromInputs);
      elements.endTime.addEventListener('change', updateSelectionFromInputs);
      elements.addClipBtn.addEventListener('click', addClip);
      elements.targetDurationSlider.addEventListener('input', syncTargetDuration);
      elements.targetDuration.addEventListener('input', syncTargetDuration);
      elements.smartClipBtn.addEventListener('click', generateSmartClips);
      elements.clearClipsBtn.addEventListener('click', clearClips);
      elements.exportBtn.addEventListener('click', exportClips);
      
      // 音频编辑相关事件
      elements.volumeControl.addEventListener('input', updateVolume);
      elements.speedControl.addEventListener('input', updateSpeed);
      elements.pitchControl.addEventListener('input', updatePitch);
      elements.eqControls.forEach(control => {
        control.addEventListener('input', updateEQ);
      });
      elements.effectButtons.forEach(btn => {
        btn.addEventListener('click', toggleEffect);
      });
      elements.applyEffectsBtn.addEventListener('click', applyEffects);
      elements.resetEffectsBtn.addEventListener('click', resetEffects);
      elements.mergeClipsBtn.addEventListener('click', mergeSelectedClips);
      elements.saveTagsBtn.addEventListener('click', saveAudioTags);
      
      // 格式转换相关事件
      elements.startConvertBtn.addEventListener('click', startConversion);
      elements.fileNameFormat.addEventListener('change', toggleCustomFileName);
      elements.browseFolderBtn.addEventListener('click', () => showNotification('浏览器环境下无法直接选择文件夹,文件将保存到默认下载路径', 'info'));
      
      // 批量处理相关事件
      elements.batchTrim.addEventListener('change', toggleBatchTrimSettings);
      elements.batchFormat.addEventListener('change', toggleBatchFormatSettings);
      elements.batchVolume.addEventListener('change', toggleBatchVolumeSettings);
      elements.batchTags.addEventListener('change', toggleBatchTagsSettings);
      elements.batchVolumeControl.addEventListener('input', updateBatchVolume);
      elements.batchFileNameFormat.addEventListener('change', toggleBatchCustomFileName);
      elements.batchBrowseFolderBtn.addEventListener('click', () => showNotification('浏览器环境下无法直接选择文件夹,文件将保存到默认下载路径', 'info'));
      elements.startBatchBtn.addEventListener('click', startBatchProcessing);
      
      // 其他UI事件
      elements.themeToggle.addEventListener('click', toggleDarkMode);
      elements.mobileThemeToggle.addEventListener('click', toggleDarkMode);
      elements.mobileMenuBtn.addEventListener('click', toggleMobileMenu);
      elements.helpBtn.addEventListener('click', openHelpModal);
      elements.mobileHelpBtn.addEventListener('click', openHelpModal);
      elements.closeHelpBtn.addEventListener('click', closeHelpModal);
      elements.presetBtn.addEventListener('click', openPresetModal);
      elements.mobilePresetBtn.addEventListener('click', openPresetModal);
      elements.closePresetBtn.addEventListener('click', closePresetModal);
      elements.saveCurrentPresetBtn.addEventListener('click', saveCurrentPreset);
      
      // 拖动处理
      setupDragHandlers();
    }

    // 初始化波形可视化
    function initWaveSurfer() {
      wavesurfer = WaveSurfer.create({
        container: '#waveform',
        waveColor: '#e2e8f0',
        progressColor: '#4F46E5',
        cursorColor: '#4F46E5',
        barWidth: 2,
        barRadius: 3,
        height: 140,
        responsive: true,
        normalize: true,
        plugins: [
          WaveSurfer.regions.create({
            dragSelection: true
          })
        ]
      });
      
      wavesurfer.on('ready', () => {
        elements.waveformPlaceholder.classList.add('hidden');
        const duration = wavesurfer.getDuration();
        elements.totalDuration.textContent = formatTime(duration);
        elements.endTime.value = formatTimeForInput(duration);
        
        // 初始化音频可视化
        initAudioVisualizer();
        
        // 启用裁剪功能
        setupWaveformSelection();
      });
      
      wavesurfer.on('audioprocess', () => {
        updateVisualizer();
      });
      
      wavesurfer.on('finish', () => {
        stopVisualizer();
      });
    }

    // 初始化音频可视化
    function initAudioVisualizer() {
      canvas = document.getElementById('visualizerCanvas');
      ctx = canvas.getContext('2d');
      
      // 设置canvas尺寸
      resizeCanvas();
      window.addEventListener('resize', resizeCanvas);
      
      // 创建音频上下文
      try {
        audioContext = new (window.AudioContext || window.webkitAudioContext)();
        const source = audioContext.createMediaElementSource(elements.audioPlayer);
        analyser = audioContext.createAnalyser();
        analyser.fftSize = 256;
        source.connect(analyser);
        analyser.connect(audioContext.destination);
      } catch (e) {
        console.error('音频可视化初始化失败:', e);
      }
    }

    // 调整canvas尺寸
    function resizeCanvas() {
      if (canvas) {
        const container = document.getElementById('audioVisualizer');
        canvas.width = container.clientWidth;
        canvas.height = container.clientHeight;
      }
    }

    // 更新音频可视化
    function updateVisualizer() {
      if (!analyser || !canvas) return;
      
      const bufferLength = analyser.frequencyBinCount;
      const dataArray = new Uint8Array(bufferLength);
      analyser.getByteFrequencyData(dataArray);
      
      const width = canvas.width;
      const height = canvas.height;
      
      ctx.clearRect(0, 0, width, height);
      
      const barWidth = (width / bufferLength) * 2.5;
      let x = 0;
      
      for (let i = 0; i < bufferLength; i++) {
        const barHeight = (dataArray[i] / 255) * height;
        
        // 渐变颜色
        const gradient = ctx.createLinearGradient(0, height, 0, height - barHeight);
        gradient.addColorStop(0, '#4F46E5');
        gradient.addColorStop(1, '#818CF8');
        
        ctx.fillStyle = gradient;
        ctx.fillRect(x, height - barHeight, barWidth, barHeight);
        
        x += barWidth + 1;
      }
      
      // 显示可视化区域
      document.getElementById('audioVisualizer').style.opacity = 1;
      
      // 继续动画
      animationId = requestAnimationFrame(updateVisualizer);
    }

    // 停止音频可视化
    function stopVisualizer() {
      if (animationId) {
        cancelAnimationFrame(animationId);
        animationId = null;
      }
    }

    // 设置波形选择功能
    function setupWaveformSelection() {
      let isSelecting = false;
      let startPercent = 0;
      
      elements.waveform.addEventListener('mousedown', (e) => {
        if (!wavesurfer) return;
        
        isSelecting = true;
        const rect = elements.waveform.getBoundingClientRect();
        startPercent = ((e.clientX - rect.left) / rect.width) * 100;
        
        updateSelectionUI(startPercent, startPercent);
      });
      
      document.addEventListener('mousemove', (e) => {
        if (!isSelecting || !wavesurfer) return;
        
        const rect = elements.waveform.getBoundingClientRect();
        let endPercent = ((e.clientX - rect.left) / rect.width) * 100;
        endPercent = Math.max(0, Math.min(100, endPercent));
        
        updateSelectionUI(startPercent, endPercent);
      });
      
      document.addEventListener('mouseup', () => {
        if (isSelecting) {
          isSelecting = false;
          updateSelectionInputs();
        }
      });
      
      // 初始化选择手柄拖动
      setupHandleDrag('startHandle', (percent) => {
        const endPercent = parseFloat(elements.endHandle.style.left);
        if (percent < endPercent - 1) { // 确保有最小选择宽度
          startPercent = percent;
          updateSelectionUI(startPercent, endPercent);
          updateSelectionInputs();
        }
      });
      
      setupHandleDrag('endHandle', (percent) => {
        const startPercent = parseFloat(elements.startHandle.style.left);
        if (percent > startPercent + 1) { // 确保有最小选择宽度
          updateSelectionUI(startPercent, percent);
          updateSelectionInputs();
        }
      });
    }

    // 设置选择手柄拖动功能
    function setupHandleDrag(handleId, onDrag) {
      const handle = document.getElementById(handleId);
      let isDragging = false;
      
      handle.addEventListener('mousedown', (e) => {
        isDragging = true;
        e.preventDefault();
      });
      
      document.addEventListener('mousemove', (e) => {
        if (!isDragging || !wavesurfer) return;
        
        const rect = elements.waveform.getBoundingClientRect();
        let percent = ((e.clientX - rect.left) / rect.width) * 100;
        percent = Math.max(0, Math.min(100, percent));
        
        onDrag(percent);
      });
      
      document.addEventListener('mouseup', () => {
        isDragging = false;
      });
    }

    // 更新选择UI
    function updateSelectionUI(startPercent, endPercent) {
      const start = Math.min(startPercent, endPercent);
      const end = Math.max(startPercent, endPercent);
      
      elements.timelineSelection.style.left = `${start}%`;
      elements.timelineSelection.style.width = `${end - start}%`;
      elements.timelineSelection.classList.remove('hidden');
      
      elements.startHandle.style.left = `${start}%`;
      elements.startHandle.classList.remove('hidden');
      
      elements.endHandle.style.left = `${end}%`;
      elements.endHandle.classList.remove('hidden');
    }

    // 从输入更新选择
    function updateSelectionFromInputs() {
      if (!wavesurfer) return;
      
      const duration = wavesurfer.getDuration();
      const startSeconds = timeInputToSeconds(elements.startTime.value);
      const endSeconds = timeInputToSeconds(elements.endTime.value);
      
      const startPercent = (startSeconds / duration) * 100;
      const endPercent = (endSeconds / duration) * 100;
      
      updateSelectionUI(startPercent, endPercent);
    }

    // 更新选择输入框
    function updateSelectionInputs() {
      if (!wavesurfer) return;
      
      const duration = wavesurfer.getDuration();
      const startPercent = parseFloat(elements.startHandle.style.left) || 0;
      const endPercent = parseFloat(elements.endHandle.style.left) || 0;
      
      const startSeconds = (startPercent / 100) * duration;
      const endSeconds = (endPercent / 100) * duration;
      
      elements.startTime.value = formatTimeForInput(startSeconds);
      elements.endTime.value = formatTimeForInput(endSeconds);
    }

    // 更新播放头位置
    function updatePlayhead() {
      if (!wavesurfer) return;
      
      const currentTime = elements.audioPlayer.currentTime;
      const duration = wavesurfer.getDuration();
      const percent = (currentTime / duration) * 100;
      
      elements.playhead.style.left = `${percent}%`;
    }

    // 处理拖放事件
    function handleDragOver(e) {
      e.preventDefault();
      elements.dropArea.classList.add('border-primary', 'bg-primary/5');
    }

    // 处理文件拖放
    function handleDrop(e) {
      e.preventDefault();
      elements.dropArea.classList.remove('border-primary', 'bg-primary/5');
      
      if (e.dataTransfer.files.length) {
        handleFiles(e.dataTransfer.files);
      }
    }

    // 处理文件选择
    function handleFileSelect(e) {
      if (e.target.files.length) {
        handleFiles(e.target.files);
      }
    }

    // 处理文件
    function handleFiles(files) {
      // 过滤音频和视频文件
      const validFiles = Array.from(files).filter(file => 
        file.type.startsWith('audio/') || file.type.startsWith('video/')
      );
      
      if (validFiles.length === 0) {
        showNotification('请选择音频或视频文件', 'error');
        return;
      }
      
      // 添加到文件列表
      validFiles.forEach(file => {
        // 检查是否已存在相同文件
        if (!audioFiles.some(f => f.name === file.name && f.size === file.size)) {
          audioFiles.push(file);
        }
      });
      
      // 更新文件列表UI
      updateFileList();
      
      // 处理第一个文件
      processFile(validFiles[0]);
      
      // 显示处理区域
      elements.audioProcessingSection.classList.remove('hidden');
      
      // 更新其他面板的文件列表
      updateConvertFileList();
      updateBatchFileList();
      
      // 启用转换按钮
      elements.startConvertBtn.disabled = false;
      elements.startBatchBtn.disabled = false;
    }

    // 处理单个文件
    function processFile(file) {
      currentAudioFile = file;
      
      // 显示文件名和进度
      elements.fileName.textContent = file.name;
      elements.progressContainer.classList.remove('hidden');
      elements.progressBar.style.width = '0%';
      elements.progressPercent.textContent = '0%';
      
      // 读取文件并加载到波形
      const reader = new FileReader();
      
      reader.onload = (e) => {
        // 加载音频到播放器
        elements.audioPlayer.src = e.target.result;
        elements.editAudioPlayer.src = e.target.result;
        
        // 加载波形
        wavesurfer.loadBlob(file);
        
        // 更新文件信息
        elements.fileSize.textContent = formatFileSize(file.size);
        elements.fileFormat.textContent = getFileExtension(file.name).toUpperCase();
        
        // 模拟进度
        let progress = 0;
        const interval = setInterval(() => {
          progress += 5;
          elements.progressBar.style.width = `${progress}%`;
          elements.progressPercent.textContent = `${progress}%`;
          
          if (progress >= 100) {
            clearInterval(interval);
          }
        }, 50);
      };
      
      reader.onerror = () => {
        showNotification('文件读取失败', 'error');
      };
      
      reader.readAsDataURL(file);
    }

    // 更新文件列表
    function updateFileList() {
      elements.fileItems.innerHTML = '';
      elements.totalFilesCount.textContent = audioFiles.length;
      
      audioFiles.forEach((file, index) => {
        const fileItem = document.createElement('div');
        fileItem.className = 'flex justify-between items-center bg-gray-50 p-2 rounded-lg';
        fileItem.innerHTML = `
          <div class="flex items-center">
            <i class="fa fa-file-audio-o text-primary mr-2"></i>
            <span class="truncate max-w-[70%]">${file.name}</span>
          </div>
          <div class="flex items-center gap-2">
            <span class="text-xs text-gray-500">${formatFileSize(file.size)}</span>
            <button class="play-file-btn text-primary hover:text-primary/80" data-index="${index}">
              <i class="fa fa-play"></i>
            </button>
            <button class="remove-file-btn text-gray-500 hover:text-danger" data-index="${index}">
              <i class="fa fa-times"></i>
            </button>
          </div>
        `;
        
        elements.fileItems.appendChild(fileItem);
      });
      
      // 添加播放和删除事件
      document.querySelectorAll('.play-file-btn').forEach(btn => {
        btn.addEventListener('click', (e) => {
          const index = parseInt(e.currentTarget.dataset.index);
          processFile(audioFiles[index]);
        });
      });
      
      document.querySelectorAll('.remove-file-btn').forEach(btn => {
        btn.addEventListener('click', (e) => {
          const index = parseInt(e.currentTarget.dataset.index);
          audioFiles.splice(index, 1);
          updateFileList();
          updateConvertFileList();
          updateBatchFileList();
          
          if (audioFiles.length === 0) {
            elements.audioProcessingSection.classList.add('hidden');
            elements.startConvertBtn.disabled = true;
            elements.startBatchBtn.disabled = true;
          }
        });
      });
      
      elements.fileList.classList.remove('hidden');
    }

    // 更新转换文件列表
    function updateConvertFileList() {
      if (audioFiles.length === 0) {
        elements.convertFileList.innerHTML = `
          <div class="text-center text-gray-500 py-6">
            <p>请先上传需要转换的文件</p>
          </div>
        `;
        return;
      }
      
      elements.convertFileList.innerHTML = '';
      audioFiles.forEach((file, index) => {
        const fileItem = document.createElement('div');
        fileItem.className = 'flex justify-between items-center p-2 border-b border-gray-100 last:border-0';
        fileItem.innerHTML = `
          <div class="flex items-center">
            <i class="fa fa-file-audio-o text-primary mr-2"></i>
            <span class="truncate">${file.name}</span>
          </div>
          <span class="text-xs text-gray-500">${formatFileSize(file.size)}</span>
        `;
        
        elements.convertFileList.appendChild(fileItem);
      });
    }

    // 更新批量处理文件列表
    function updateBatchFileList() {
      if (audioFiles.length === 0) {
        elements.batchFileList.innerHTML = `
          <div class="text-center text-gray-500 py-6">
            <i class="fa fa-upload text-xl mb-2"></i>
            <p>拖放多个文件到此处,或点击上方"选择文件"按钮</p>
          </div>
        `;
        return;
      }
      
      elements.batchFileList.innerHTML = '';
      audioFiles.forEach((file) => {
        const fileItem = document.createElement('div');
        fileItem.className = 'flex justify-between items-center p-2 border-b border-gray-100 last:border-0';
        fileItem.innerHTML = `
          <div class="flex items-center">
            <i class="fa fa-file-audio-o text-primary mr-2"></i>
            <span class="truncate">${file.name}</span>
          </div>
          <span class="text-xs text-gray-500">${formatFileSize(file.size)}</span>
        `;
        
        elements.batchFileList.appendChild(fileItem);
      });
    }

    // 切换标签页
    function switchTab(tabName) {
      // 更新标签样式
      Object.keys(elements.tabs).forEach(tab => {
        if (tab === tabName) {
          elements.tabs[tab].classList.add('tab-active');
          elements.tabs[tab].classList.remove('text-gray-600');
          elements.panels[tab].classList.remove('hidden');
        } else {
          elements.tabs[tab].classList.remove('tab-active');
          elements.tabs[tab].classList.add('text-gray-600');
          elements.panels[tab].classList.add('hidden');
        }
      });
    }

    // 添加剪切片段
    function addClip() {
      if (!wavesurfer) return;
      
      const duration = wavesurfer.getDuration();
      const startSeconds = timeInputToSeconds(elements.startTime.value);
      const endSeconds = timeInputToSeconds(elements.endTime.value);
      
      // 验证时间
      if (startSeconds >= endSeconds) {
        showNotification('开始时间必须早于结束时间', 'error');
        return;
      }
      
      if (endSeconds > duration) {
        showNotification('结束时间不能超过音频总时长', 'error');
        return;
      }
      
      // 创建新片段
      const clip = {
        id: Date.now(),
        start: startSeconds,
        end: endSeconds,
        duration: endSeconds - startSeconds,
        type: 'manual'
      };
      
      clips.push(clip);
      updateClipsList();
      
      // 显示通知
      showNotification(`已添加片段 (${formatTime(clip.duration)})`, 'success');
    }
    
    // 播放指定片段
    function playClip(clip) {
      if (!wavesurfer) return;
      
      // 暂停当前播放
      wavesurfer.pause();
      
      // 设置播放范围
      wavesurfer.setCurrentTime(clip.start);
      
      // 开始播放
      wavesurfer.play();
      
      // 高亮显示当前播放的片段
      const currentClipElement = document.querySelector(`.clip-item[data-id="${clip.id}"]`);
      if (currentClipElement) {
        currentClipElement.classList.add('ring-2', 'ring-blue-400');
      }
      
      // 设置定时器,在到达片段结束时停止播放
      const playDuration = clip.end - clip.start;
      setTimeout(() => {
        wavesurfer.pause();
        
        // 移除高亮
        if (currentClipElement) {
          currentClipElement.classList.remove('ring-2', 'ring-blue-400');
        }
      }, playDuration * 1000);
      
      // 显示正在播放的通知
      showNotification(`正在播放片段 (${formatTime(clip.duration)})`, 'info');
    }

    // 更新片段列表
    function updateClipsList() {
      if (clips.length === 0) {
        elements.clipsList.innerHTML = `
          <div class="text-center text-gray-500 py-8">
            <i class="fa fa-film text-2xl mb-2"></i>
            <p>还没有添加剪切片段</p>
          </div>
        `;
        elements.clipCount.textContent = '0 段';
        elements.exportBtn.disabled = true;
        return;
      }
      
      elements.clipsList.innerHTML = '';
      clips.forEach((clip, index) => {
        // 获取片段类型信息
        const typeInfo = getClipTypeInfo(clip);
        
        const clipItem = document.createElement('div');
        clipItem.className = `clip-item p-3 rounded-lg shadow-sm mb-2 transition-all hover:shadow ${typeInfo.bgClass}`;
        clipItem.draggable = true;
        clipItem.dataset.id = clip.id;
        clipItem.dataset.type = clip.type || 'manual';
        
        clipItem.innerHTML = `
          <div class="flex justify-between items-start">
            <div class="flex-1">
              <div class="font-medium flex items-center">
                片段 ${index + 1}
                <span class="ml-2 px-2 py-0.5 text-xs font-medium rounded-full ${typeInfo.badgeClass}">${typeInfo.label}</span>
              </div>
              <div class="text-sm text-gray-500 mt-1">
                ${formatTime(clip.start)} - ${formatTime(clip.end)} 
                (${formatTime(clip.duration)})
              </div>
              ${clip.lyricInfo ? `
                <div class="text-xs text-gray-400 mt-1 italic">${clip.lyricInfo}</div>
              ` : ''}
            </div>
            <div class="flex gap-2 ml-3">
              <button class="play-clip-btn p-1 text-gray-500 hover:text-green-600" title="播放片段">
                <i class="fa fa-play"></i>
              </button>
              <button class="delete-clip-btn p-1 text-gray-400 hover:text-danger" data-id="${clip.id}" title="删除片段">
                <i class="fa fa-trash"></i>
              </button>
            </div>
          </div>
        `;
        
        // 添加事件监听器
        if (clipItem.querySelector('.play-clip-btn')) {
          clipItem.querySelector('.play-clip-btn').addEventListener('click', () => {
            playClip(clip);
          });
        }
        
        elements.clipsList.appendChild(clipItem);
      });
      
      // 添加删除事件
      document.querySelectorAll('.delete-clip-btn').forEach(btn => {
        btn.addEventListener('click', (e) => {
          const id = parseInt(e.currentTarget.dataset.id);
          clips = clips.filter(clip => clip.id !== id);
          updateClipsList();
          showNotification('片段已删除', 'info');
        });
      });
      
      elements.clipCount.textContent = `${clips.length} 段`;
      elements.exportBtn.disabled = false;
    }
    
    // 获取片段类型信息
    function getClipTypeInfo(clip) {
      const type = clip.type || 'manual';
      
      const typeConfig = {
        'equal': {
          label: '均等分割',
          badgeClass: 'bg-blue-100 text-blue-800',
          bgClass: 'bg-white'
        },
        'silence': {
          label: '静音检测',
          badgeClass: 'bg-green-100 text-green-800',
          bgClass: 'bg-green-50'
        },
        'lyric': {
          label: '歌词优先',
          badgeClass: 'bg-purple-100 text-purple-800',
          bgClass: 'bg-purple-50'
        },
        'fallback': {
          label: '自动调整',
          badgeClass: 'bg-yellow-100 text-yellow-800',
          bgClass: 'bg-yellow-50'
        },
        'final': {
          label: '结尾片段',
          badgeClass: 'bg-orange-100 text-orange-800',
          bgClass: 'bg-orange-50'
        },
        'manual': {
          label: '手动添加',
          badgeClass: 'bg-gray-100 text-gray-800',
          bgClass: 'bg-white'
        }
      };
      
      return typeConfig[type] || typeConfig.manual;
    }

    // 清空所有片段
    function clearClips() {
      if (clips.length === 0) return;
      
      if (confirm('确定要清空所有剪切片段吗?')) {
        clips = [];
        updateClipsList();
        showNotification('所有片段已清空', 'info');
      }
    }

    // 生成智能片段
    function generateSmartClips() {
      if (!wavesurfer) return;
      
      const duration = wavesurfer.getDuration();
      const targetDuration = parseInt(elements.targetDuration.value);
      const strategy = elements.clipStrategy.value;
      
      // 验证目标时长
      if (targetDuration <= 0 || targetDuration >= duration) {
        showNotification('目标片段时长设置不合理', 'error');
        return;
      }
      
      showNotification(`正在使用 ${getStrategyName(strategy)} 策略分析音频...`, 'info');
      
      // 清空现有片段
      clips = [];
      
      // 根据策略生成片段
      if (strategy === 'equal') {
        // 均等分割
        const clipCount = Math.floor(duration / targetDuration);
        for (let i = 0; i < clipCount; i++) {
          const start = i * targetDuration;
          const end = Math.min((i + 1) * targetDuration, duration);
          
          clips.push({
            id: Date.now() + i,
            start,
            end,
            duration: end - start,
            type: 'equal'
          });
        }
        
        // 处理剩余部分
        if (clips.length * targetDuration < duration) {
          clips.push({
            id: Date.now() + clipCount,
            start: clipCount * targetDuration,
            end: duration,
            duration: duration - clipCount * targetDuration,
            type: 'equal'
          });
        }
      } else if (strategy === 'silence') {
        // 静音检测策略(实际实现)
        detectSilencePoints().then(silencePoints => {
          // 如果没有找到足够的静音点,使用默认分割
          if (silencePoints.length < 2) {
            fallbackToEqualDivision(duration, targetDuration);
            return;
          }
          
          let start = 0;
          let clipIndex = 0;
          let currentSilenceIndex = 0;
          
          while (start < duration - targetDuration * 0.5) {
            // 寻找最接近目标时长的静音点
            const targetEnd = start + targetDuration;
            let bestSilencePoint = null;
            let minDiff = Infinity;
            
            // 在合理范围内查找最佳静音点
            for (let i = currentSilenceIndex; i < silencePoints.length; i++) {
              const point = silencePoints[i];
              if (point < start) continue;
              if (point > start + targetDuration * 1.5) break;
              
              const diff = Math.abs(point - targetEnd);
              if (diff < minDiff && point > start + targetDuration * 0.7) {
                minDiff = diff;
                bestSilencePoint = point;
                currentSilenceIndex = i;
              }
            }
            
            // 确定结束点
            const end = bestSilencePoint || Math.min(start + targetDuration, duration);
            
            clips.push({
              id: Date.now() + clipIndex,
              start,
              end,
              duration: end - start,
              type: bestSilencePoint ? 'silence' : 'fallback'
            });
            
            start = end;
            clipIndex++;
          }
          
          // 处理最后一个片段
          if (start < duration) {
            clips.push({
              id: Date.now() + clipIndex,
              start,
              end: duration,
              duration: duration - start,
              type: 'final'
            });
          }
          
          updateClipsList();
          showNotification(`已生成 ${clips.length} 个基于静音检测的片段`, 'success');
        }).catch(error => {
          console.error('静音检测失败:', error);
          fallbackToEqualDivision(duration, targetDuration);
        });
        
        return; // 异步处理,提前返回
      } else if (strategy === 'lyric') {
        // 歌词优先策略(改进实现)
        extractLyricsAndTimestamps().then(lyricData => {
          // 如果没有找到歌词数据,使用默认分割
          if (!lyricData || lyricData.length === 0) {
            showNotification('无法提取歌词数据,切换到均等分割模式', 'warning');
            fallbackToEqualDivision(duration, targetDuration);
            return;
          }
          
          processLyricsForClips(lyricData, duration, targetDuration);
          
          updateClipsList();
          showNotification(`已生成 ${clips.length} 个基于歌词完整性的片段`, 'success');
        }).catch(error => {
          console.error('歌词分析失败:', error);
          fallbackToEqualDivision(duration, targetDuration);
        });
        
        return; // 异步处理,提前返回
      }
      
      updateClipsList();
      showNotification(`已生成 ${clips.length} 个智能片段`, 'success');
    }
    
    // 获取策略名称
    function getStrategyName(strategy) {
      const names = {
        'equal': '均等分割',
        'silence': '静音检测',
        'lyric': '歌词完整性优先'
      };
      return names[strategy] || strategy;
    }
    
    // 回退到均等分割
    function fallbackToEqualDivision(duration, targetDuration) {
      clips = [];
      const clipCount = Math.ceil(duration / targetDuration);
      
      for (let i = 0; i < clipCount; i++) {
        const start = i * targetDuration;
        const end = Math.min((i + 1) * targetDuration, duration);
        
        clips.push({
          id: Date.now() + i,
          start,
          end,
          duration: end - start,
          type: 'fallback'
        });
      }
      
      updateClipsList();
    }
    
    // 检测静音点
    async function detectSilencePoints() {
      return new Promise((resolve, reject) => {
        try {
          // 在实际应用中,这里应该使用Web Audio API分析音频波形
          // 获取音频数据,计算振幅,检测低于阈值的静音区间
          
          // 模拟实现:使用随机生成的静音点(实际应用中应替换为真实分析)
          const duration = wavesurfer.getDuration();
          const silencePoints = [];
          
          // 确保包含开始和结束点
          silencePoints.push(0);
          silencePoints.push(duration);
          
          // 在实际应用中,这里应该:
          // 1. 获取音频波形数据
          // 2. 设置适当的阈值(如0.05的振幅)
          // 3. 扫描波形找出静音区间
          // 4. 在静音区间的中间位置添加剪切点
          
          // 模拟的静音点分布
          const minInterval = 10; // 最小间隔10秒
          const maxPoints = Math.floor(duration / minInterval) - 1;
          const actualPoints = Math.min(maxPoints, Math.floor(Math.random() * 3) + 2); // 2-4个随机静音点
          
          for (let i = 0; i < actualPoints; i++) {
            // 生成随机静音点,但确保不重叠且分布均匀
            const position = minInterval + (i * (duration - 2 * minInterval)) / (actualPoints + 1) + 
                            (Math.random() * minInterval * 0.5 - minInterval * 0.25);
            silencePoints.push(position);
          }
          
          // 排序并去重
          const uniqueSortedPoints = [...new Set(silencePoints)].sort((a, b) => a - b);
          
          // 模拟处理延迟
          setTimeout(() => {
            resolve(uniqueSortedPoints);
          }, 800);
        } catch (error) {
          reject(error);
        }
      });
    }
    
    // 提取歌词和时间戳
    async function extractLyricsAndTimestamps() {
      return new Promise((resolve, reject) => {
        try {
          // 在实际应用中,这里应该:
          // 1. 从音频文件中提取内嵌的歌词(如MP3的ID3标签)
          // 2. 或者使用语音识别API(如Web Speech API)识别歌词
          // 3. 分析歌词结构,确定句子边界和段落
          
          // 模拟实现:生成示例歌词数据
          const lyricData = [
            { time: 2.5, text: '这是第一句歌词', isSentenceEnd: true },
            { time: 7.8, text: '第二句歌词内容', isSentenceEnd: true },
            { time: 12.3, text: '这是第三句歌词', isSentenceEnd: true },
            { time: 17.1, text: '第四句歌词', isSentenceEnd: true },
            { time: 22.5, text: '这是第五句歌词', isSentenceEnd: true },
            { time: 27.2, text: '第六句歌词内容', isSentenceEnd: true },
            { time: 32.8, text: '这是副歌部分的第一句', isSentenceEnd: false },
            { time: 35.1, text: '副歌的第二部分', isSentenceEnd: true },
            { time: 40.5, text: '这是第七句歌词', isSentenceEnd: true },
            { time: 45.2, text: '第八句歌词内容', isSentenceEnd: true },
            { time: 50.8, text: '这是第九句歌词', isSentenceEnd: true },
            { time: 55.3, text: '第十句歌词', isSentenceEnd: true }
          ];
          
          // 模拟处理延迟
          setTimeout(() => {
            // 在实际应用中,这里应该根据音频的实际时长和内容调整歌词数据
            resolve(lyricData);
          }, 1200);
        } catch (error) {
          reject(error);
        }
      });
    }
    
    // 根据歌词处理裁剪片段
    function processLyricsForClips(lyricData, duration, targetDuration) {
      let start = 0;
      let clipIndex = 0;
      let currentLyricIndex = 0;
      
      while (start < duration - targetDuration * 0.5) {
        // 寻找下一个合适的歌词边界
        const targetEnd = start + targetDuration;
        let bestLyricEnd = null;
        let bestPosition = start + targetDuration;
        
        // 寻找最佳歌词结束点
        while (currentLyricIndex < lyricData.length && 
               lyricData[currentLyricIndex].time <= targetEnd * 1.3) {
          const lyric = lyricData[currentLyricIndex];
          
          // 优先考虑句子结束的位置
          if (lyric.isSentenceEnd) {
            // 计算与目标时长的接近程度
            const diff = Math.abs(lyric.time - start - targetDuration);
            const positionScore = 1 / (1 + diff); // 距离目标越近分数越高
            
            // 确保片段不会太短(至少达到目标时长的70%)
            if (lyric.time - start >= targetDuration * 0.7) {
              // 找到更好的结束点
              if (!bestLyricEnd || positionScore > bestLyricEnd.score) {
                bestLyricEnd = {
                  time: lyric.time,
                  score: positionScore
                };
                bestPosition = lyric.time;
              }
            }
          }
          
          currentLyricIndex++;
        }
        
        // 如果没有找到合适的歌词结束点,使用目标时长
        const end = Math.min(bestPosition, duration);
        
        clips.push({
          id: Date.now() + clipIndex,
          start,
          end,
          duration: end - start,
          type: bestLyricEnd ? 'lyric' : 'fallback',
          lyricInfo: bestLyricEnd ? `基于歌词完整性` : '无法找到合适的歌词边界'
        });
        
        start = end;
        clipIndex++;
      }
      
      // 处理最后一个片段
      if (start < duration) {
        clips.push({
          id: Date.now() + clipIndex,
          start,
          end: duration,
          duration: duration - start,
          type: 'final'
        });
      }
    }

    // 导出所有片段
    function exportClips() {
      if (clips.length === 0 || !currentAudioFile) return;
      
      showNotification('开始导出片段,请稍候...', 'info');
      
      // 模拟导出过程
      setTimeout(() => {
        clips.forEach((clip, index) => {
          // 实际应用中应该使用ffmpeg.js或其他库处理音频
          const prefix = elements.exportPrefix.value || 'clip';
          const format = elements.exportFormat.value;
          const fileName = `${prefix}_${index + 1}.${format}`;
          
          // 创建一个模拟的下载链接
          const a = document.createElement('a');
          a.href = elements.audioPlayer.src; // 实际应用中应该是处理后的音频
          a.download = fileName;
          document.body.appendChild(a);
          a.click();
          document.body.removeChild(a);
        });
        
        showNotification(`成功导出 ${clips.length} 个片段`, 'success');
      }, 1500);
    }

    // 同步目标时长
    function syncTargetDuration(e) {
      const value = e.target.value;
      elements.targetDurationSlider.value = value;
      elements.targetDuration.value = value;
    }

    // 更新音量
    function updateVolume() {
      const volume = elements.volumeControl.value;
      elements.volumeValue.textContent = `${volume}%`;
      elements.audioPlayer.volume = volume / 100;
      elements.editAudioPlayer.volume = volume / 100;
    }

    // 更新速度
    function updateSpeed() {
      const speed = elements.speedControl.value;
      elements.speedValue.textContent = `${speed}x`;
      elements.audioPlayer.playbackRate = speed;
      elements.editAudioPlayer.playbackRate = speed;
    }

    // 更新音调(简化实现)
    function updatePitch() {
      const pitch = elements.pitchControl.value;
      elements.pitchValue.textContent = pitch;
      // 实际应用中需要使用音频处理库来改变音调
      showNotification('音调调整将在应用效果后生效', 'info');
    }

    // 更新均衡器
    function updateEQ(e) {
      const freq = e.target.dataset.eq;
      const value = e.target.value;
      elements.eqValues[freq].textContent = `${value}dB`;
      // 实际应用中需要使用Web Audio API来应用均衡效果
    }

    // 切换效果按钮状态
    function toggleEffect(e) {
      const btn = e.currentTarget;
      btn.classList.toggle('active');
    }

    // 应用所有效果
    function applyEffects() {
      showNotification('正在应用效果,请稍候...', 'info');
      
      // 模拟效果应用
      setTimeout(() => {
        showNotification('所有效果已应用', 'success');
      }, 1500);
    }

    // 重置效果
    function resetEffects() {
      // 重置音量
      elements.volumeControl.value = 100;
      elements.volumeValue.textContent = '100%';
      elements.audioPlayer.volume = 1;
      elements.editAudioPlayer.volume = 1;
      
      // 重置均衡器
      elements.eqControls.forEach(control => {
        control.value = 0;
        const freq = control.dataset.eq;
        elements.eqValues[freq].textContent = '0dB';
      });
      
      // 重置速度和音调
      elements.speedControl.value = 1;
      elements.speedValue.textContent = '1.0x';
      elements.audioPlayer.playbackRate = 1;
      elements.editAudioPlayer.playbackRate = 1;
      
      elements.pitchControl.value = 0;
      elements.pitchValue.textContent = '0';
      
      // 重置效果按钮
      elements.effectButtons.forEach(btn => {
        btn.classList.remove('active');
      });
      
      showNotification('效果已重置', 'info');
    }

    // 合并选中片段
    function mergeSelectedClips() {
      const mergeItems = elements.mergeList.querySelectorAll('.clip-item');
      if (mergeItems.length < 2) {
        showNotification('至少需要2个片段才能合并', 'error');
        return;
      }
      
      showNotification('正在合并片段,请稍候...', 'info');
      
      // 模拟合并过程
      setTimeout(() => {
        // 清空合并列表
        elements.mergeList.innerHTML = `
          <div class="text-center text-gray-500 py-6">
            <i class="fa fa-arrows-h text-xl mb-2"></i>
            <p>将剪切片段拖到此处排序拼接</p>
          </div>
        `;
        
        showNotification('片段合并成功', 'success');
      }, 2000);
    }

    // 保存音频标签
    function saveAudioTags() {
      // 在实际应用中,应该使用库来写入音频元数据
      showNotification('音频标签已保存', 'success');
    }

    // 切换自定义文件名显示
    function toggleCustomFileName() {
      elements.customFileName.classList.toggle('hidden', 
        elements.fileNameFormat.value !== 'custom');
    }

    // 开始格式转换
    function startConversion() {
      if (audioFiles.length === 0) return;
      
      elements.conversionProgress.classList.remove('hidden');
      elements.conversionBar.style.width = '0%';
      elements.conversionPercent.textContent = '0%';
      
      // 模拟转换过程
      let progress = 0;
      const totalFiles = audioFiles.length;
      let currentFile = 0;
      
      const interval = setInterval(() => {
        progress += 1;
        elements.conversionBar.style.width = `${progress}%`;
        elements.conversionPercent.textContent = `${progress}%`;
        elements.convertingFileName.textContent = `正在转换: ${audioFiles[currentFile].name}`;
        
        if (progress >= 100 / totalFiles * (currentFile + 1)) {
          currentFile++;
          
          if (currentFile >= totalFiles) {
            clearInterval(interval);
            elements.conversionProgress.classList.add('hidden');
            showNotification(`所有 ${totalFiles} 个文件转换完成`, 'success');
          }
        }
      }, 50);
    }

    // 切换批量处理设置显示
    function toggleBatchTrimSettings() {
      elements.batchTrimSettings.classList.toggle('hidden', !elements.batchTrim.checked);
    }
    
    function toggleBatchFormatSettings() {
      elements.batchFormatSettings.classList.toggle('hidden', !elements.batchFormat.checked);
    }
    
    function toggleBatchVolumeSettings() {
      elements.batchVolumeSettings.classList.toggle('hidden', !elements.batchVolume.checked);
    }
    
    function toggleBatchTagsSettings() {
      elements.batchTagsSettings.classList.toggle('hidden', !elements.batchTags.checked);
    }
    
    function updateBatchVolume() {
      const volume = elements.batchVolumeControl.value;
      elements.batchVolumeValue.textContent = `${volume}%`;
    }
    
    function toggleBatchCustomFileName() {
      elements.batchCustomFileName.classList.toggle('hidden', 
        elements.batchFileNameFormat.value !== 'custom');
    }

    // 开始批量处理
    function startBatchProcessing() {
      if (audioFiles.length === 0) return;
      
      // 检查是否选择了至少一项操作
      if (!elements.batchTrim.checked && !elements.batchFormat.checked && 
          !elements.batchVolume.checked && !elements.batchTags.checked) {
        showNotification('请至少选择一项批量操作', 'error');
        return;
      }
      
      elements.batchProgress.classList.remove('hidden');
      elements.batchProgressBar.style.width = '0%';
      elements.batchProgressPercent.textContent = '0%';
      
      // 模拟批量处理过程
      let progress = 0;
      const totalFiles = audioFiles.length;
      
      const interval = setInterval(() => {
        progress += 1;
        elements.batchProgressBar.style.width = `${progress}%`;
        elements.batchProgressPercent.textContent = `${progress}%`;
        elements.batchProgressText.textContent = `正在处理 ${Math.ceil(progress / 100 * totalFiles)}/${totalFiles} 个文件`;
        
        if (progress >= 100) {
          clearInterval(interval);
          elements.batchProgress.classList.add('hidden');
          showNotification(`批量处理完成,共处理 ${totalFiles} 个文件`, 'success');
        }
      }, 50);
    }

    // 切换移动端菜单
    function toggleMobileMenu() {
      elements.mobileMenu.classList.toggle('hidden');
    }

    // 打开帮助模态框
    function openHelpModal() {
      elements.helpModal.classList.remove('hidden');
      elements.helpModal.classList.add('flex');
    }

    // 关闭帮助模态框
    function closeHelpModal() {
      elements.helpModal.classList.add('hidden');
      elements.helpModal.classList.remove('flex');
    }

    // 打开预设模态框
    function openPresetModal() {
      elements.presetModal.classList.remove('hidden');
      elements.presetModal.classList.add('flex');
    }

    // 关闭预设模态框
    function closePresetModal() {
      elements.presetModal.classList.add('hidden');
      elements.presetModal.classList.remove('flex');
    }

    // 保存当前设置为预设
    function saveCurrentPreset() {
      showNotification('预设功能开发中', 'info');
    }

    // 显示通知
    function showNotification(message, type = 'info') {
      const notification = elements.notification;
      notification.textContent = message;
      notification.className = 'notification';
      notification.classList.add(type);
      
      // 显示通知
      setTimeout(() => {
        notification.classList.add('show');
      }, 10);
      
      // 自动隐藏
      setTimeout(() => {
        notification.classList.remove('show');
      }, 3000);
    }

    // 格式化时间
    function formatTime(seconds) {
      const hrs = Math.floor(seconds / 3600);
      const mins = Math.floor((seconds % 3600) / 60);
      const secs = Math.floor(seconds % 60);
      
      if (hrs > 0) {
        return `${hrs.toString().padStart(2, '0')}:${mins.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`;
      }
      return `${mins.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`;
    }

    // 格式化时间用于输入框
    function formatTimeForInput(seconds) {
      const hrs = Math.floor(seconds / 3600);
      const mins = Math.floor((seconds % 3600) / 60);
      const secs = seconds % 60;
      
      return `${hrs.toString().padStart(2, '0')}:${mins.toString().padStart(2, '0')}:${secs.toFixed(3).padStart(6, '0')}`;
    }

    // 将时间输入转换为秒数
    function timeInputToSeconds(timeStr) {
      if (!timeStr) return 0;
      
      const parts = timeStr.split(':');
      if (parts.length !== 3) return 0;
      
      const [hrs, mins, secs] = parts.map(p => parseFloat(p));
      return hrs * 3600 + mins * 60 + secs;
    }

    // 格式化文件大小
    function formatFileSize(bytes) {
      if (bytes === 0) return '0 B';
      
      const k = 1024;
      const sizes = ['B', 'KB', 'MB', 'GB'];
      const i = Math.floor(Math.log(bytes) / Math.log(k));
      
      return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
    }

    // 获取文件扩展名
    function getFileExtension(filename) {
      return filename.slice((filename.lastIndexOf('.') - 1 >>> 0) + 2);
    }

    // 加载暗黑模式偏好
    function loadDarkModePreference() {
      const savedTheme = localStorage.getItem('darkMode');
      if (savedTheme === 'true') {
        document.body.classList.add('dark-mode');
        isDarkMode = true;
        elements.themeToggle.innerHTML = '<i class="fa fa-sun-o mr-1"></i>日间模式';
        elements.mobileThemeToggle.innerHTML = '<i class="fa fa-sun-o mr-1"></i>日间模式';
      }
    }

    // 切换暗黑模式
    function toggleDarkMode() {
      isDarkMode = !isDarkMode;
      document.body.classList.toggle('dark-mode', isDarkMode);
      
      // 保存偏好
      localStorage.setItem('darkMode', isDarkMode);
      
      // 更新按钮文本
      if (isDarkMode) {
        elements.themeToggle.innerHTML = '<i class="fa fa-sun-o mr-1"></i>日间模式';
        elements.mobileThemeToggle.innerHTML = '<i class="fa fa-sun-o mr-1"></i>日间模式';
      } else {
        elements.themeToggle.innerHTML = '<i class="fa fa-moon-o mr-1"></i>夜间模式';
        elements.mobileThemeToggle.innerHTML = '<i class="fa fa-moon-o mr-1"></i>夜间模式';
      }
    }

    // 设置拖动处理
    function setupDragHandlers() {
      // 为剪切片段添加拖动功能
      elements.clipsList.addEventListener('dragstart', (e) => {
        if (e.target.classList.contains('clip-item')) {
          e.dataTransfer.setData('clipId', e.target.querySelector('.delete-clip-btn').dataset.id);
        }
      });
      
      // 合并区域的拖动处理
      elements.mergeList.addEventListener('dragover', (e) => {
        e.preventDefault();
        elements.mergeList.classList.add('border-primary');
      });
      
      elements.mergeList.addEventListener('dragleave', () => {
        elements.mergeList.classList.remove('border-primary');
      });
      
      elements.mergeList.addEventListener('drop', (e) => {
        e.preventDefault();
        elements.mergeList.classList.remove('border-primary');
        
        const clipId = e.dataTransfer.getData('clipId');
        if (clipId) {
          const clip = clips.find(c => c.id === parseInt(clipId));
          if (clip) {
            // 清除占位符
            if (elements.mergeList.querySelector('.text-center')) {
              elements.mergeList.innerHTML = '';
            }
            
            // 添加到合并列表
            const clipItem = document.createElement('div');
            clipItem.className = 'clip-item';
            clipItem.innerHTML = `
              <div>
                <div class="font-medium">${clip.id}</div>
                <div class="text-sm text-gray-500">
                  ${formatTime(clip.start)} - ${formatTime(clip.end)}
                </div>
              </div>
            `;
            
            elements.mergeList.appendChild(clipItem);
          }
        }
      });
    }

    // 初始化应用
    document.addEventListener('DOMContentLoaded', initApp);
  </script>
</body>
</html>

免费评分

参与人数 11吾爱币 +15 热心值 +8 收起 理由
Fr1day + 1 + 1 我很赞同!
swz7852151 + 1 + 1 热心回复!
xxmhy + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
xingshizhuyi + 1 + 1 我很赞同!
chenwei7362 + 1 + 1 我很赞同!
bicheng9951951 + 1 热心回复!
dajiadouhao1983 + 1 我很赞同!
、神通广大 + 1 + 1 我很赞同!
wuqi9277 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
zl52 + 1 热心回复!
hrh123 + 7 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

jiesen2017 发表于 2026-2-4 11:40
你好,我用的格式转换这个功能,视频转mp3,有提示转换完成,但是我在默认的下载文件夹里找不到转换后的文件,我更改了浏览器的默认下载目录,还是没有转换后的文件,我把默认下载的文件夹路径复制到输出文件夹也不管用,这是我操作的问题吗
饮食协会 发表于 2025-11-12 09:17
前几天正好剪辑了一些音频 测试了不少软件,感觉真正好用的是论坛里大神优化的的这个  

MP3剪切合并大师v13.8 去广告优化版

https://www.52pojie.cn/thread-1279623-1-1.html

但作者好像四五年没来论坛了
andyzgs 发表于 2025-11-12 00:59
测试了没有效果呢,分成的几段,还是原来的文件,相当于原来的文件复制了几份
sktao 发表于 2025-11-12 07:11
貌似还需要改进啊 ,大家交流一下。
大宁散人 发表于 2025-11-12 07:13
用AU不香吗?小白操作也不复杂哦点
zjtzjt 发表于 2025-11-12 07:22
处理音频很方便
NZTX007 发表于 2025-11-12 08:08
大宁散人 发表于 2025-11-12 07:13
用AU不香吗?小白操作也不复杂哦点

AU是啥啊
52soft 发表于 2025-11-12 08:29
都支持哪些格式
硬梆梆的漢堡包 发表于 2025-11-12 08:45
这是一个跳转网址?不是实体小工具啊。。。
zlqhysy 发表于 2025-11-12 09:07
有消除和提取人声功能吗?
bicheng9951951 发表于 2025-11-12 09:16
不知道是否好用,感谢分享
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - 52pojie.cn ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2026-6-4 06:35

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表