1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 |
import tkinter as tk from tkinter import filedialog, messagebox, ttk import os import subprocess
class MP3SplitterApp: def __init__(self, root): self.root = root self.root.title("MP3 分割工具") self.root.geometry("600x400")
# 文件路径变量 self.file_path = tk.StringVar()
# 创建界面元素 self.create_widgets()
# 预设分割时长为 4:30 self.minutes_entry.insert(0, "4") self.seconds_entry.insert(0, "30")
# 存储分割点的列表 self.split_points = []
def create_widgets(self): # 文件选择框 file_frame = ttk.Frame(self.root) file_frame.pack(pady=10, padx=10, fill=tk.X)
ttk.Label(file_frame, text="选择文件:").pack(side=tk.LEFT) ttk.Entry(file_frame, textvariable=self.file_path, width=50).pack(side=tk.LEFT, padx=5) ttk.Button(file_frame, text="选择文件", command=self.select_file).pack(side=tk.LEFT)
# 分割时长输入区域 split_frame = ttk.Frame(self.root) split_frame.pack(pady=10, padx=10, fill=tk.BOTH)
ttk.Label(split_frame, text="设置分割时长(格式:分:秒):").pack()
input_frame = ttk.Frame(split_frame) input_frame.pack(pady=5)
self.minutes_entry = ttk.Entry(input_frame, width=5) self.minutes_entry.pack(side=tk.LEFT) ttk.Label(input_frame, text=":").pack(side=tk.LEFT) self.seconds_entry = ttk.Entry(input_frame, width=5) self.seconds_entry.pack(side=tk.LEFT)
# 控制按钮 button_frame = ttk.Frame(self.root) button_frame.pack(pady=10, padx=10)
ttk.Button(button_frame, text="MP4转MP3", command=self.convert_mp4_to_mp3).pack(side=tk.LEFT, padx=5) ttk.Button(button_frame, text="按时长分割", command=self.split_mp3).pack(side=tk.LEFT, padx=5) ttk.Button(button_frame, text="按歌曲分割", command=self.detect_songs).pack(side=tk.LEFT, padx=5)
def select_file(self): file_path = filedialog.askopenfilename(filetypes=[("视频/音频文件", "*.mp4 *.mp3")]) if file_path: self.file_path.set(file_path)
def detect_songs(self): if not self.file_path.get(): messagebox.showerror("错误", "请选择MP3文件") return
try: # 检查FFmpeg路径 possible_paths = [ r"ffmpeg\bin\ffmpeg.exe", r".\ffmpeg\bin\ffmpeg.exe", r"C:\ffmpeg\bin\ffmpeg.exe", "ffmpeg" ]
ffmpeg_path = None for path in possible_paths: if os.path.exists(path): ffmpeg_path = path break
if ffmpeg_path is None: messagebox.showerror("错误", "找不到FFmpeg,请确保已正确安装FFmpeg") return
input_file = self.file_path.get()
# 使用FFmpeg的silencedetect过滤器检测静音部分 cmd = [ ffmpeg_path, '-i', input_file, '-af', 'silencedetect=noise=-35dB:d=1', # 更宽松的参数 '-f', 'null', '-' ]
result = subprocess.run(cmd, capture_output=True, encoding='utf-8', errors='ignore')
# 解析静音检测结果 silence_starts = [] silence_ends = []
if result.stderr: # 确保有输出 for line in result.stderr.split('\n'): if line: # 确保行不为空 if 'silence_start:' in line: try: parts = line.split('silence_start:') if len(parts) > 1: time = float(parts[1].strip().split()[0]) silence_starts.append(time) except: continue elif 'silence_end:' in line: try: parts = line.split('silence_end:') if len(parts) > 1: time = float(parts[1].strip().split()[0]) silence_ends.append(time) except: continue
# 使用检测到的静音点进行分割 if silence_starts and silence_ends: output_dir = os.path.splitext(input_file)[0] + "_songs" os.makedirs(output_dir, exist_ok=True)
# 获取总时长 probe = subprocess.run([ ffmpeg_path, '-i', input_file ], capture_output=True, encoding='utf-8', errors='ignore')
duration = None if probe.stderr: for line in probe.stderr.split('\n'): if "Duration:" in line: try: time_str = line.split("Duration:")[1].split(",")[0].strip() h, m, s = map(float, time_str.split(":")) duration = h * 3600 + m * 60 + s break except: continue
if duration is None: duration = max(silence_ends[-1] if silence_ends else 0, 3600)
# 构建分割点列表 split_points = [0] # 添加开始点 for start, end in zip(silence_starts, silence_ends): # 使用静音段的中点作为分割点 split_point = (start + end) / 2 # 只有当两个分割点间隔超过20秒时才考虑这是一首新歌 if split_point - split_points[-1] > 20: split_points.append(split_point)
split_points.append(duration) # 添加结束点
# 分割文件 for i in range(len(split_points) - 1): start_time = split_points[i] end_time = split_points[i + 1]
if end_time - start_time < 20: # 如果片段小于20秒则跳过 continue
output_path = os.path.join(output_dir, f"song_{i+1:03d}.mp3")
subprocess.run([ ffmpeg_path, '-i', input_file, '-ss', str(start_time), '-t', str(end_time - start_time), '-acodec', 'copy', '-y', output_path ], capture_output=True)
messagebox.showinfo("成功", f"文件已按歌曲分割完成,保存在:{output_dir}") else: messagebox.showerror("错误", "未能检测到有效的歌曲分隔点")
except Exception as e: messagebox.showerror("错误", f"分割过程中出现错误:{str(e)}")
def convert_mp4_to_mp3(self): if not self.file_path.get(): messagebox.showerror("错误", "请选择MP4文件") return
if not self.file_path.get().lower().endswith('.mp4'): messagebox.showerror("错误", "请选择MP4文件") return
try: # 检查FFmpeg路径 possible_paths = [ r"ffmpeg\bin\ffmpeg.exe", r".\ffmpeg\bin\ffmpeg.exe", r"C:\ffmpeg\bin\ffmpeg.exe", "ffmpeg" ]
ffmpeg_path = None for path in possible_paths: if os.path.exists(path): ffmpeg_path = path break
if ffmpeg_path is None: messagebox.showerror("错误", "找不到FFmpeg,请确保已正确安装FFmpeg") return
input_file = self.file_path.get() output_file = os.path.splitext(input_file)[0] + ".mp3"
# 显示转换中的消息 messagebox.showinfo("提示", "开始转换,请稍候...")
# 执行转换 subprocess.run([ ffmpeg_path, '-i', input_file, '-vn', '-acodec', 'libmp3lame', '-q:a', '2', '-y', output_file ], check=True)
# 更新文件路径为新生成的MP3文件 self.file_path.set(output_file) messagebox.showinfo("成功", f"MP4已转换为MP3:\n{output_file}")
except Exception as e: messagebox.showerror("错误", f"转换过程中出现错误:{str(e)}")
def split_mp3(self): if not self.file_path.get(): messagebox.showerror("错误", "请选择MP3文件") return
try: # 获取用户输入的分割时长 try: minutes = int(self.minutes_entry.get() or "0") seconds = int(self.seconds_entry.get() or "0") if minutes < 0 or seconds < 0 or seconds >= 60: raise ValueError segment_duration = minutes * 60 + seconds if segment_duration <= 0: raise ValueError except ValueError: messagebox.showerror("错误", "请输入有效的分割时长") return
# 检查FFmpeg路径 possible_paths = [ r"ffmpeg\bin\ffmpeg.exe", r".\ffmpeg\bin\ffmpeg.exe", r"C:\ffmpeg\bin\ffmpeg.exe", "ffmpeg" ]
ffmpeg_path = None for path in possible_paths: if os.path.exists(path): ffmpeg_path = path break
if ffmpeg_path is None: messagebox.showerror("错误", "找不到FFmpeg,请确保已正确安装FFmpeg") return
input_file = self.file_path.get() output_dir = os.path.splitext(input_file)[0] + "_split" os.makedirs(output_dir, exist_ok=True)
# 获取音频总时长 result = subprocess.run([ffmpeg_path, '-i', input_file], capture_output=True, encoding='utf-8', errors='ignore')
# 从输出中提取持续时间 duration = None for line in result.stderr.split('\n'): if "Duration:" in line: try: time_str = line.split("Duration:")[1].split(",")[0].strip() h, m, s = map(float, time_str.split(":")) duration = h * 3600 + m * 60 + s break except: continue
if duration is None: duration = 3600 messagebox.showwarning("警告", "无法获取音频时长,将使用默认时长进行分割")
# 计算分割点 num_segments = int(duration // segment_duration) + 1
# 分割文件 for i in range(num_segments): start_time = i * segment_duration end_time = min((i + 1) * segment_duration, duration)
if end_time - start_time < 1: # 如果片段小于1秒则跳过 continue
output_path = os.path.join(output_dir, f"segment_{i+1:03d}.mp3")
subprocess.run([ ffmpeg_path, '-i', input_file, '-ss', str(start_time), '-t', str(end_time - start_time), '-acodec', 'copy', '-y', output_path ], capture_output=True)
messagebox.showinfo("成功", f"文件已分割完成,保存在:{output_dir}\n共分割成 {num_segments} 个文件")
except Exception as e: messagebox.showerror("错误", f"分割过程中出现错误:{str(e)}")
if __name__ == "__main__": root = tk.Tk() app = MP3SplitterApp(root) root.mainloop() |
这是一个使用 Python 和 FFmpeg 开发的 MP3 分割工具,支持 MP4 转 MP3、按时长分割和按歌曲自动分割等功能。
1.MP4 转 MP3
2.按时长分割
3.按歌曲分割
Python 3.x
FFmpeg
必需的 Python 库
1 |
pip install tkinter |
FFmpeg 安装
下载 FFmpeg:
访问:Releases · BtbN/FFmpeg-Builds
下载 "ffmpeg-master-latest-win64-gpl-shared.zip"
安装步骤:
目录结构应如下:
你的程序目录/
├── mp3_splitter.py
└── ffmpeg/
└── bin/
├── ffmpeg.exe
├── ffplay.exe
└── ffprobe.exe
1.MP4 转 MP3:
2.按时长分割:
3.按歌曲分割:
1.MP4 转 MP3:
2.按时长分割:
3.按歌曲分割:
确保已正确安装 FFmpeg 并放置在正确位置
按歌曲分割功能的效果取决于音频特征,可能并非所有音频都能准确分割
建议在分割大文件前先进行测试
程序会自动跳过过短的片段以避免产生无效文件
使用 Tkinter 构建图形界面
使用 subprocess 调用 FFmpeg 进行音频处理
代码采用面向对象方式组织,主要类为 MP3SplitterApp
所有文件操作都有错误处理机制