好友
阅读权限 10
听众
最后登录 1970-1-1
本帖最后由 testqkl 于 2024-9-13 14:31 编辑
工具说明
cue和wav音乐文件的歌曲分割提取
ai辅助快速开发
通过百度的文小言辅助,自己修修改改的cue和wav音乐文件,需要先本地安装ffmpeg,如果不在path环境,可以自己写ffmpeg的全路径地址。
tikinter版本截图
Tkinter版本代码
[Python] 纯文本查看 复制代码
# mac brew install tk-devel
$ brew install tk-devel
[Python] 纯文本查看 复制代码
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from tkinter import (
Tk,
filedialog,
messagebox,
Label,
Text,
Button,
END,
Scrollbar,
RIGHT,
Y,
)
from functools import partial
import time
import os
import re
import subprocess
import json
class MUSIC_CUE_CUTE_GUI:
def __init__(self, init_window_name):
self.init_window_name = init_window_name
self.cue_file_path = ""
self.wav_file_path = ""
self.wav_result_path = ""
self.tracks = []
self.ffmpeg = "ffmpeg"
def reset_params(self):
self.cue_file_path = ""
self.wav_file_path = ""
self.wav_result_path = ""
self.tracks = []
# 设置窗口
def set_init_window(self):
self.init_window_name.title("CUE音频提取器_v1.0.0") # 窗口名
# self.init_window_name.geometry('320x160+10+10') #290 160为窗口大小,+10 +10 定义窗口弹出时的默认展示位置
self.init_window_name.geometry("681x681+10+10")
# self.init_window_name["bg"] = "pink" #窗口背景色,其他背景色见:blog.csdn.net/chl0000/article/details/7657887
# self.init_window_name.attributes("-alpha",0.9) #虚化,值越小虚化程度越高
# 标签
# 按钮
self.cue_file_button = Button(
self.init_window_name,
text="打开CUE文件",
bg="lightblue",
width=16,
command=lambda: self.open_file("cue"),
) # 调用内部方法 加()为直接调用
self.cue_file_button.grid(row=0, column=0)
self.wav_file_button = Button(
self.init_window_name,
text="打开WAV文件",
bg="lightblue",
width=16,
command=lambda: self.open_file("wav"),
) # 调用内部方法 加()为直接调用
self.wav_file_button.grid(row=0, column=1)
self.detect_ffmpeg_button = Button(
self.init_window_name,
text="检测ffmpeg环境",
bg="lightblue",
width=16,
command=lambda: self.detect_ffmpeg(),
) # 调用内部方法 加()为直接调用
self.detect_ffmpeg_button.grid(row=0, column=2)
self.parse_cue_button = Button(
self.init_window_name,
text="读取cue歌曲信息",
bg="lightblue",
width=16,
command=lambda: self.parse_cue(),
) # 调用内部方法 加()为直接调用
self.parse_cue_button.grid(row=1, column=0)
self.execute_button = Button(
self.init_window_name,
text=">> 提取音乐",
bg="lightblue",
width=16,
command=lambda: self.split_audio(),
) # 调用内部方法 加()为直接调用
self.execute_button.grid(row=1, column=1)
self.result_data_label = Label(self.init_window_name, text="")
self.result_data_label.grid(
row=2, column=0, rowspan=1, columnspan=5, sticky="W"
)
self.result_data_Text = Text(self.init_window_name) # 处理结果展示
self.result_data_Text.grid(row=3, column=0, rowspan=5, columnspan=5)
self.readme_text = Text(self.init_window_name, height=10)
self.readme_text.grid(row=9, column=0, rowspan=2, columnspan=5, sticky="W")
self.readme_text.insert(END, "使用说明:\n")
self.readme_text.insert(END, "1. 请先检测ffmpeg环境\n")
self.readme_text.insert(
END, "2. 选择cue文件,如果wav和cue在同目录且同名,可不用操作wav文件\n"
)
self.readme_text.insert(END, "3. 尝试先读取cue文件的歌曲清单,做确认提取音乐\n")
self.readme_text.insert(END, "4. 执行提取音乐\n")
# self.readme_text.config(state="disable")
# # 创建滚动条
# scrollbar = Scrollbar(self.init_window_name)
# scrollbar.pack(side=RIGHT, fill=Y)
# # 将滚动条与Text组件关联
# self.readme_text.config(yscrollcommand=scrollbar.set)
# scrollbar.config(command=self.readme_text.yview)
def detect_ffmpeg(self):
try:
subprocess.run([self.ffmpeg, "-version", ">", "/dev/null", "2>&1"])
messagebox.showinfo("ffmpeg检测", "你已正确安装")
except FileNotFoundError:
messagebox.showwarning(
"ffmpeg检测", "你未正确安装,请先安装ffmpeg到系统环境"
)
def open_file(self, t="cue"):
filetypes = [("All files", "*.*")]
if t == "cue":
filetypes = [("Cue files", "*.cue")]
elif t == "wav":
filetypes = [("Wav files", "*.wav")]
print(filetypes, t, self)
file_path = filedialog.askopenfilename(
title="选择" + t + "文件", filetypes=filetypes
)
if t == "cue":
self.cue_file_path = file_path
if self.wav_file_path == "":
self.wav_file_path = self.replace_file_suffix(file_path, ".wav")
self.wav_result_path = os.path.dirname(self.wav_file_path)
elif t == "wav":
if self.cue_file_path == "":
self.cue_file_path = self.replace_file_suffix(file_path, ".cue")
self.wav_file_path = file_path
self.wav_result_path = os.path.dirname(self.wav_file_path)
self.print_log("你选择了" + t + "文件: " + self.cue_file_path + "\n")
self.print_log(
"系统默认给你选择了"
+ ("wav" if t == "cue" else "cue")
+ "文件: "
+ self.wav_file_path
+ "\n",
)
print(self.cue_file_path, self.wav_file_path)
def replace_file_suffix(self, file_path, new_suffix):
# 使用os.path.splitext()分割文件路径为基本部分和扩展名
base_name, ext = os.path.splitext(file_path)
# 确保替换的是扩展名(去掉点号)
if ext and ext[0] == ".":
# 使用新的后缀替换旧的后缀,确保新后缀前有点号
new_file_path = base_name + new_suffix
else:
# 如果没有找到扩展名,直接添加新后缀
new_file_path = file_path + new_suffix
return new_file_path
def parse_cue(self):
tracks = []
current_track = None
if self.cue_file_path == "":
messagebox.showwarning("警告", "请先选择cue文件再来操作")
with open(self.cue_file_path, "r", encoding="GB2312") as file:
for line in file:
line = line.strip()
# 检查是否为新的音轨开始
if line.startswith("TRACK"):
if current_track:
tracks.append(current_track)
match = re.match(r"TRACK (\d+) AUDIO", line)
if match:
track_number = int(match.group(1))
current_track = {
"number": track_number,
"title": "",
"start": None,
}
# 提取歌曲名
elif line.startswith("TITLE"):
title = line.split('TITLE "')[1].split('"')[0]
if current_track:
current_track["title"] = title
# 提取起始时间
elif line.startswith("INDEX 01"):
match = re.match(r"INDEX 01 ((\d+):(\d+):(\d+))", line)
if match:
# minutes, seconds, frames = map(int, match.groups())
# 将时间转换为毫秒
# total_seconds = minutes * 60 + seconds + frames / 75.0
# start_time = int(total_seconds * 1000)
start_time, minutes, seconds, ms = match.groups()
if current_track:
current_track["start"] = minutes + ":" + seconds + "." + ms
# 注意:.cue文件通常不直接包含结束时间,但可以通过下一个音轨的起始时间推断
# 添加最后一个音轨(如果有)
if current_track:
tracks.append(current_track)
self.tracks = tracks
self.print_log("提取到{}首音乐".format(len(self.tracks)))
self.print_log(
"\n".join(
list(map(lambda x: json.dumps(x, ensure_ascii=False), self.tracks))
)
)
def split_audio(self):
if len(self.tracks) == 0:
self.parse_cue()
if len(self.tracks) == 0:
self.print_log("cue音频提起失败")
"""根据 CUE 文件中的时间信息分割 WAV 文件"""
for i, track in enumerate(self.tracks):
# print(i, track)
start_time = track["start"]
# 使用 ffmpeg 剪切音频
output_file = (
self.wav_result_path + os.sep + track["title"].strip() + ".wav"
)
# 最后一个
if len(self.tracks) == i + 1:
# 如果是到文件末尾,则不需要指定结束时间
cuteCmd = [
self.ffmpeg,
"-i",
self.wav_file_path,
"-y",
"-ss",
start_time,
"-vn",
"-acodec",
"copy",
output_file,
]
else:
# 如果是第一个音轨,没有前一个音轨的结束时间,所以从头开始
next_track = self.tracks[i + 1] # 获取前一个音轨的信息(注意索引)
cuteCmd = [
self.ffmpeg,
"-i",
self.wav_file_path,
"-y",
"-ss",
start_time,
"-to",
next_track["start"],
"-vn",
"-acodec",
"copy",
output_file,
]
self.print_log(i + 1 + ": " + " ".join(cuteCmd))
subprocess.run(cuteCmd)
self.print_log("cue音频提起完成")
self.reset_params()
# 获取当前时间
def get_current_time(self):
current_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(time.time()))
return current_time
def print_log(self, text):
self.result_data_Text.insert(END, self.get_current_time() + ": " + text + "\n")
def gui_start():
init_window = Tk() # 实例化出一个父窗口
MUSIC_CUE_CUTE_GUI(init_window).set_init_window()
init_window.mainloop() # 父窗口进入事件循环,可以理解为保持窗口运行,否则界面不展示
if __name__ == "__main__":
gui_start()
旧代码:
[Python] 纯文本查看 复制代码
import re
import subprocess
def parse_cue(cue_file_path):
tracks = []
current_track = None
with open(cue_file_path, 'r', encoding='GB2312') as file:
for line in file:
line = line.strip()
# 检查是否为新的音轨开始
if line.startswith('TRACK'):
if current_track:
tracks.append(current_track)
match = re.match(r'TRACK (\d+) AUDIO', line)
if match:
track_number = int(match.group(1))
current_track = {'number': track_number, 'title': '', 'start': None, 'end': None}
# 提取歌曲名
elif line.startswith('TITLE'):
title = line.split('TITLE "')[1].split('"')[0]
if current_track:
current_track['title'] = title
# 提取起始时间
elif line.startswith('INDEX 01'):
match = re.match(r'INDEX 01 ((\d+):(\d+):(\d+))', line)
if match:
# minutes, seconds, frames = map(int, match.groups())
# 将时间转换为毫秒
# total_seconds = minutes * 60 + seconds + frames / 75.0
# start_time = int(total_seconds * 1000)
start_time,minutes,seconds,ms = match.groups()
if current_track:
current_track['start'] = minutes+":"+ seconds+"."+ms
# 注意:.cue文件通常不直接包含结束时间,但可以通过下一个音轨的起始时间推断
# 添加最后一个音轨(如果有)
if current_track:
tracks.append(current_track)
return tracks
def split_audio(wav_file, tracks):
""" 根据 CUE 文件中的时间信息分割 WAV 文件 """
for i, track in enumerate(tracks):
print(i, track)
start_time = track['start']
if (len(tracks) == i+1):
# 如果是到文件末尾,则不需要指定结束时间
end_cmd = ''
end_time = ''
else:
# 如果是第一个音轨,没有前一个音轨的结束时间,所以从头开始
next_track = tracks[i+1] # 获取前一个音轨的信息(注意索引)
end_cmd = '-to'
end_time = next_track['start']
# 使用 ffmpeg 剪切音频
output_file = track['title'].strip()+".wav"
print(' '.join(['ffmpeg', '-ss', start_time, end_cmd, end_time, '-i', wav_file, '-y', '-vn', '-acodec', 'copy', output_file]))
subprocess.run([
'ffmpeg', '-ss', start_time, end_cmd, end_time, '-i', wav_file, '-y', '-vn', '-acodec', 'copy', output_file
])
# 使用示例
cue_file_path = './1.cue'
wav_file = './1.wav'
tracks = parse_cue(cue_file_path)
for track in tracks:
print(f"Track {track['number']}: {track['title']}, Start: {track['start']}")
# 注意:这里没有打印结束时间,因为它通常需要从下一个音轨的起始时间推断
confirm = input('确认分割音频:')
if confirm.lower() == 'y' or confirm.lower() == 'yes':
split_audio(wav_file, tracks)
else:
print("你已取消cue音频提取执行")
免费评分
查看全部评分