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()
|