惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Engineering at Meta
Engineering at Meta
人人都是产品经理
人人都是产品经理
aimingoo的专栏
aimingoo的专栏
M
MIT News - Artificial intelligence
Recent Announcements
Recent Announcements
V
Visual Studio Blog
酷 壳 – CoolShell
酷 壳 – CoolShell
MyScale Blog
MyScale Blog
Hugging Face - Blog
Hugging Face - Blog
宝玉的分享
宝玉的分享
H
Hackread – Cybersecurity News, Data Breaches, AI and More
博客园 - 叶小钗
博客园 - 聂微东
U
Unit 42
F
Fortinet All Blogs
Microsoft Security Blog
Microsoft Security Blog
GbyAI
GbyAI
IT之家
IT之家
The GitHub Blog
The GitHub Blog
Stack Overflow Blog
Stack Overflow Blog
MongoDB | Blog
MongoDB | Blog
Y
Y Combinator Blog
A
About on SuperTechFans
博客园 - 三生石上(FineUI控件)

Cyberia

复活吧微软边缘 - Cyberia 从零开始的EAC抓轨教程 - Cyberia Apophenia, 新时代补全工具 - Cyberia 浅谈博客设计 - Cyberia 爱软TV之GitHub - Cyberia 排印演进 - Cyberia EAC相关工具集合 - Cyberia 《C(金钱掌控)》观后感 - Cyberia C#在Windows UI开发中的衰落 - Cyberia 存储退潮 字体padding计算器 开源已死 如何写出优雅完整的音频元数据 起夜级家庭网络正在变得越来越重要 优化Powershell的启动时间 无JavaScript其实没有很重要 你不需要一个电纸书阅读器 文件分享的难题 为什么我不敢看都市 某科学的 SSH 算号指南 时代的辉夜姬 为什么 Pixi 是神 关于神椿的过去,当下以及未来,我的一点看法 Tahoe 的图标令人难评 The EVE of PUSHING LIMITS 在 Word 中通过制表符实现居中对齐等操作 编码之殇:前 Unicode 时代的 WAV 处理 sHELL 即地狱 WR1800K AX NAND 路由器刷入 Immortalwrt 一个 PWM 的世界
自动化EAC音频抓取
Nullpinter · 2026-05-06 · via Cyberia

; Copyright (c) 2026 Nullpinter

; This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.

; If a copy of the MPL was not distributed with this file, You can obtain one at https://mozilla.org/MPL/2.0/.

748 collapsed lines

Persistent

SetTitleMatchMode 2

DetectHiddenWindows false

global EAC_WIN := "ahk_exe EAC.exe"

global current_step := 1

global is_running := false

global abort_flag := false

global ui := 0

global title_text := 0

global status_text := 0

global next_text := 0

global detail_text := 0

global progress_bar := 0

global next_btn := 0

global auto_btn := 0

global skip_btn := 0

global reset_btn := 0

global exit_btn := 0

InitUi()

InitUi() {

global ui, title_text, status_text, next_text, detail_text

global progress_bar, next_btn, auto_btn, skip_btn, reset_btn, exit_btn

ui := Gui("+AlwaysOnTop +ToolWindow", "EAC 自动化控制")

ui.MarginX := 16

ui.MarginY := 14

ui.SetFont("s10", "Microsoft YaHei UI")

title_text := ui.AddText("w620 h28", "EAC 自动化控制")

status_text := ui.AddText("w620 h32", "")

next_text := ui.AddText("w620 h32", "")

progress_bar := ui.AddProgress("w620 h16 Range0-100", 0)

detail_text := ui.AddEdit("w620 h150 ReadOnly -Wrap", "")

next_btn := ui.AddButton("xm w120 h34", "执行下一步")

auto_btn := ui.AddButton("x+8 w120 h34", "全自动模式")

skip_btn := ui.AddButton("x+8 w110 h34", "跳过当前步")

reset_btn := ui.AddButton("x+8 w100 h34", "重置流程")

exit_btn := ui.AddButton("x+8 w80 h34", "退出")

next_btn.OnEvent("Click", (*) => RunNextStep())

auto_btn.OnEvent("Click", (*) => RunAutoMode())

skip_btn.OnEvent("Click", (*) => SkipCurrentStep())

reset_btn.OnEvent("Click", (*) => ResetFlow())

exit_btn.OnEvent("Click", (*) => ExitApp())

ui.OnEvent("Close", (*) => ExitApp())

RefreshUi("等待操作", "请点击“执行下一步”,或点击“全自动模式”。", 0)

ui.Show("AutoSize Center")

}

RunNextStep() {

global current_step, is_running, abort_flag

global next_btn, auto_btn, skip_btn, reset_btn

if is_running

return

if current_step > 5 {

ExitApp()

return

}

if !WinExist(EAC_WIN) {

RefreshUi("错误", "未找到 EAC 进程。请先启动 Exact Audio Copy。", 0)

return

}

is_running := true

abort_flag := false

next_btn.Enabled := false

auto_btn.Enabled := false

skip_btn.Enabled := false

reset_btn.Enabled := false

step_name := StepName(current_step)

RefreshUi("正在执行第 " current_step " 步", step_name, StepStartPercent(current_step))

try {

DoStep(current_step)

finished_step := current_step

current_step += 1

if current_step <= 5 {

RefreshUi(

"第 " finished_step " 步完成",

"已暂停。下一步:" StepName(current_step),

StepDonePercent(finished_step)

)

} else {

RefreshUi(

"全部步骤完成",

"EAC 自动化流程已完成。",

100

)

}

} catch as e {

RefreshUi(

"第 " current_step " 步失败或中止",

e.Message,

StepStartPercent(current_step)

)

}

is_running := false

reset_btn.Enabled := true

auto_btn.Enabled := true

if current_step <= 5 {

next_btn.Text := "执行下一步"

next_btn.Enabled := true

skip_btn.Enabled := true

} else {

next_btn.Text := "关闭"

next_btn.Enabled := true

skip_btn.Enabled := false

}

}

RunAutoMode() {

global current_step, is_running, abort_flag

global next_btn, auto_btn, skip_btn, reset_btn

if is_running

return

if !WinExist(EAC_WIN) {

RefreshUi("错误", "未找到 EAC 进程。请先启动 Exact Audio Copy。", 0)

return

}

is_running := true

abort_flag := false

next_btn.Enabled := false

auto_btn.Enabled := false

skip_btn.Enabled := false

reset_btn.Enabled := false

current_step := 1

try {

RefreshUi("全自动模式:第 1 步", StepName(1), StepStartPercent(1))

AutoStep1()

current_step := 2

RefreshUi("全自动模式:第 2 步", StepName(2), StepStartPercent(2))

AutoStep2()

current_step := 3

RefreshUi("全自动模式:第 3 步", StepName(3), StepStartPercent(3))

AutoStep3()

current_step := 4

RefreshUi("全自动模式:第 4 步", StepName(4), StepStartPercent(4))

AutoStep4()

current_step := 6

RefreshUi(

"全自动模式完成",

"已完成第 1 到第 4 步。`r`n第 5 步不执行任何实际操作。",

100

)

} catch as e {

RefreshUi(

"全自动模式失败或中止",

e.Message,

StepStartPercent(current_step)

)

}

is_running := false

reset_btn.Enabled := true

auto_btn.Enabled := true

if current_step <= 5 {

next_btn.Text := "执行下一步"

next_btn.Enabled := true

skip_btn.Enabled := true

} else {

next_btn.Text := "关闭"

next_btn.Enabled := true

skip_btn.Enabled := false

}

}

DoStep(step) {

switch step {

case 1:

StepDetail("发送菜单命令:操作 > 检测间隙")

SendStepMenu(1)

StepDetail("已发送“检测间隙”命令。`r`n按钮将保持禁用 3 秒。")

Sleep 3000

case 2:

StepDetail("发送菜单命令:操作 > 检测静音间隙")

SendStepMenu(2)

StepDetail("已发送“检测静音间隙”命令。`r`n按钮将保持禁用 3 秒。")

Sleep 3000

case 3:

StepDetail("发送菜单命令:操作 > 创建 CUE 目录文件 > 多个带间隙的 WAV 文件...(非规则)")

SendStepMenu(3)

StepDetail("已发送“创建多个带间隙的 WAV 文件”命令。`r`n按钮将保持禁用 3 秒。")

Sleep 3000

case 4:

StepDetail("发送菜单命令:操作 > 测试并抓取所选音轨 > 已压缩...")

SendStepMenu(4)

StepDetail("已发送“测试并抓取,已压缩”命令。`r`n脚本不会等待抓取完成。`r`n按钮将保持禁用 3 秒。")

Sleep 3000

case 5:

StepDetail("第 5 步不执行任何实际操作。`r`n流程到此结束。")

Sleep 500

}

}

AutoStep1() {

StepDetail("全自动第 1 步:发送“检测间隙”命令。")

SendStepMenu(1)

StepDetail("全自动第 1 步:等待 EAC 的“正在分析”弹窗出现。")

WaitAnalyzeDialogAppearThenDisappear("检测间隙")

StepDetail("全自动第 1 步完成:“正在分析”弹窗已消失。")

}

AutoStep2() {

StepDetail("全自动第 2 步:发送“检测静音间隙”命令。")

SendStepMenu(2)

StepDetail("全自动第 2 步:等待标题为“正在分析”且文本包含“测试静音中的间隙”的弹窗。")

WaitSilentGapDialogAndClickOk()

StepDetail("全自动第 2 步完成:已在按钮变为“确定”后点击。")

}

AutoStep3() {

StepDetail("全自动第 3 步:发送“创建多个带间隙的 WAV 文件(非规则)”命令。")

SendStepMenu(3)

StepDetail("全自动第 3 步:等待 EAC 的“正在分析”弹窗出现。")

WaitAnalyzeDialogAppearThenDisappear("创建多个带间隙的 WAV 文件,非规则")

StepDetail("全自动第 3 步完成:“正在分析”弹窗已消失。")

}

AutoStep4() {

StepDetail("全自动第 4 步:发送“测试并抓取所选音轨,已压缩”命令。")

SendStepMenu(4)

StepDetail("全自动第 4 步:等待 EAC 抓取或压缩相关窗口出现后消失。`r`n本步骤不设置等待超时。")

WaitAnyEacDialogAppearThenDisappearNoTimeout("测试并抓取,已压缩")

StepDetail("全自动第 4 步完成:EAC 相关窗口已消失。")

}

SendStepMenu(step) {

switch step {

case 1:

SelectEacMenuWithRetry(60000,

["操作(&A)", "检测间隙(&D)"],

["操作(A)", "检测间隙(D)"],

["操作", "检测间隙"]

)

case 2:

SelectEacMenuWithRetry(60000,

["操作(&A)", "检测静音间隙(&G)"],

["操作(A)", "检测静音间隙(G)"],

["操作", "检测静音间隙"]

)

case 3:

SelectEacMenuWithRetry(60000,

["操作(&A)", "创建 CUE 目录文件(&S)", "4&"],

["操作(A)", "创建 CUE 目录文件(S)", "4&"],

["操作", "创建 CUE 目录文件", "4&"],

)

case 4:

SelectEacMenuWithRetry(60000,

["操作(&A)", "测试并抓取所选音轨(&O)", "已压缩..."],

["操作(A)", "测试并抓取所选音轨(O)", "已压缩..."],

["操作", "测试并抓取所选音轨", "已压缩..."]

)

default:

throw Error("未知步骤:" step)

}

}

SelectEacMenuWithRetry(timeout_ms, menu_paths*) {

global EAC_WIN

deadline := A_TickCount + timeout_ms

last_error := ""

while A_TickCount < deadline {

CheckAbort()

for path in menu_paths {

try {

WinActivate(EAC_WIN)

WinWaitActive(EAC_WIN, , 2)

MenuSelect(EAC_WIN, "", path*)

Sleep 500

return true

} catch as e {

last_error := e.Message

}

}

Sleep 800

}

throw Error("菜单项执行失败。最后错误:" last_error)

}

WaitAnalyzeDialogAppearThenDisappear(label) {

hwnd := WaitEacDialog("正在分析", "", 60000, label)

StepDetail(label ":已检测到“正在分析”弹窗。`r`n正在等待该弹窗消失。")

WaitWindowGone(hwnd, 0, label)

}

WaitSilentGapDialogAndClickOk() {

seen_target := false

last_info := ""

StepDetail("检测静音间隙:等待 EAC 的“正在分析”弹窗。`r`n不再要求先检测到“取消”。")

loop {

CheckAbort()

hwnds := []

try hwnds := WinGetList("ahk_class #32770 ahk_exe EAC.exe")

catch {

hwnds := []

}

target_hwnd := 0

target_title := ""

target_text := ""

target_btn_text := ""

for hwnd in hwnds {

title := ""

text := ""

btn_text := ""

try title := WinGetTitle("ahk_id " hwnd)

catch {

continue

}

if !InStr(title, "正在分析")

continue

try text := WinGetText("ahk_id " hwnd)

catch {

text := ""

}

if !InStr(text, "测试静音中的间隙")

continue

try btn_text := ControlGetText("Button1", "ahk_id " hwnd)

catch {

btn_text := ""

}

target_hwnd := hwnd

target_title := title

target_text := text

target_btn_text := btn_text

break

}

if target_hwnd {

seen_target := true

btn_norm := NormalizeButtonText(target_btn_text)

last_info := "窗口标题:" target_title "`r`n"

. "Button1 文本:" target_btn_text "`r`n"

. "正在等待 Button1 变为“确定”。"

StepDetail("检测静音间隙进行中。`r`n" last_info)

if btn_norm = "确定" || btn_norm = "OK" {

Sleep 500

try {

ControlClick("Button1", "ahk_id " target_hwnd)

} catch as e {

try {

WinActivate("ahk_id " target_hwnd)

WinWaitActive("ahk_id " target_hwnd, , 2)

Send "{Enter}"

} catch as e2 {

throw Error("点击 Button1 失败:" e.Message ";发送 Enter 也失败:" e2.Message)

}

}

WaitWindowGone(target_hwnd, 0, "检测静音间隙确认弹窗")

return true

}

} else {

if seen_target {

StepDetail(

"检测静音间隙:目标弹窗暂时未找到。`r`n"

"可能正在刷新窗口状态,继续等待。`r`n`r`n"

last_info

)

} else {

StepDetail("检测静音间隙:尚未检测到目标“正在分析”弹窗。")

}

}

Sleep 100

}

}

WaitAnyEacDialogAppearThenDisappearNoTimeout(label) {

hwnd := WaitAnyEacRelatedWindow(0, label)

title := ""

text := ""

try title := WinGetTitle("ahk_id " hwnd)

try text := WinGetText("ahk_id " hwnd)

text := CleanText(text)

if StrLen(text) > 500

text := SubStr(text, 1, 500) "..."

StepDetail(

label ":已检测到 EAC 相关窗口。`r`n"

"窗口标题:" title "`r`n`r`n"

text "`r`n`r`n"

"正在等待该窗口消失。`r`n"

"本步骤不设置等待超时。"

)

WaitWindowGone(hwnd, 0, label)

}

WaitEacDialog(title_contains, text_contains := "", timeout_ms := 60000, label := "") {

deadline := timeout_ms > 0 ? A_TickCount + timeout_ms : 0

loop {

CheckAbort()

if timeout_ms > 0 && A_TickCount > deadline {

if text_contains != ""

throw Error("等待弹窗超时:" label "。标题包含:" title_contains ",文本包含:" text_contains)

else

throw Error("等待弹窗超时:" label "。标题包含:" title_contains)

}

hwnd := FindEacDialog(title_contains, text_contains)

if hwnd

return hwnd

Sleep 250

}

}

FindEacDialog(title_contains := "", text_contains := "") {

hwnds := []

try hwnds := WinGetList("ahk_class #32770 ahk_exe EAC.exe")

catch {

return 0

}

for hwnd in hwnds {

title := ""

text := ""

try title := WinGetTitle("ahk_id " hwnd)

catch {

continue

}

if title_contains != "" && !InStr(title, title_contains)

continue

if text_contains != "" {

try text := WinGetText("ahk_id " hwnd)

catch {

continue

}

if !InStr(text, text_contains)

continue

}

return hwnd

}

return 0

}

WaitAnyEacRelatedWindow(timeout_ms := 0, label := "") {

deadline := timeout_ms > 0 ? A_TickCount + timeout_ms : 0

loop {

CheckAbort()

if timeout_ms > 0 && A_TickCount > deadline

throw Error("等待 EAC 相关弹窗超时:" label)

hwnds := []

try hwnds := WinGetList("ahk_class #32770 ahk_exe EAC.exe")

catch {

hwnds := []

}

for hwnd in hwnds {

title := ""

text := ""

try title := WinGetTitle("ahk_id " hwnd)

try text := WinGetText("ahk_id " hwnd)

if title != "" || text != ""

return hwnd

}

Sleep 500

}

}

WaitWindowGone(hwnd, timeout_ms := 0, label := "") {

deadline := timeout_ms > 0 ? A_TickCount + timeout_ms : 0

loop {

CheckAbort()

if !WinExist("ahk_id " hwnd)

return true

if timeout_ms > 0 && A_TickCount > deadline

throw Error("等待窗口消失超时:" label)

Sleep 250

}

}

NormalizeButtonText(s) {

s := StrReplace(s, "&", "")

s := RegExReplace(s, "\([A-Za-z]\)", "")

s := RegExReplace(s, "([A-Za-z])", "")

return Trim(s)

}

CleanText(s) {

s := StrReplace(s, "`r", "")

s := RegExReplace(s, "`n{3,}", "`n`n")

return Trim(s)

}

SkipCurrentStep() {

global current_step, is_running

global next_btn, auto_btn, skip_btn, reset_btn

if is_running

return

if current_step > 5 {

ExitApp()

return

}

skipped_step := current_step

skipped_name := StepName(skipped_step)

current_step += 1

if current_step <= 5 {

RefreshUi(

"已跳过第 " skipped_step " 步",

"未执行:" skipped_name "`r`n下一步:" StepName(current_step),

StepDonePercent(skipped_step)

)

next_btn.Text := "执行下一步"

next_btn.Enabled := true

auto_btn.Enabled := true

skip_btn.Enabled := true

reset_btn.Enabled := true

} else {

RefreshUi(

"全部步骤完成",

"已跳过第 " skipped_step " 步:" skipped_name "`r`nEAC 自动化流程已完成。",

100

)

next_btn.Text := "关闭"

next_btn.Enabled := true

auto_btn.Enabled := true

skip_btn.Enabled := false

reset_btn.Enabled := true

}

}

ResetFlow() {

global current_step, is_running

global next_btn, auto_btn, skip_btn, reset_btn

if is_running

return

current_step := 1

next_btn.Text := "执行下一步"

next_btn.Enabled := true

auto_btn.Enabled := true

skip_btn.Enabled := true

reset_btn.Enabled := true

RefreshUi("已重置", "请点击“执行下一步”,或点击“全自动模式”。", 0)

}

CheckAbort() {

global abort_flag

if abort_flag

throw Error("用户通过 UI 中止。")

}

RefreshUi(status, detail, percent) {

global current_step

global title_text, status_text, next_text, detail_text, progress_bar, next_btn

title_text.Text := "EAC 自动化控制"

status_text.Text := "状态:" status

detail_text.Value := detail

progress_bar.Value := percent

if current_step <= 5 {

next_text.Text := "下一步:" current_step "/5," StepName(current_step)

next_btn.Text := "执行下一步"

} else {

next_text.Text := "下一步:无,流程已完成"

next_btn.Text := "关闭"

}

}

StepDetail(text) {

global detail_text

if IsObject(detail_text)

detail_text.Value := text

}

StepName(step) {

switch step {

case 1:

return "检测间隙"

case 2:

return "检测静音间隙"

case 3:

return "创建多个带间隙的 WAV 文件,非规则"

case 4:

return "测试并抓取,已压缩"

case 5:

return "抓取完成后确定"

default:

return "完成"

}

}

StepStartPercent(step) {

switch step {

case 1:

return 0

case 2:

return 20

case 3:

return 40

case 4:

return 60

case 5:

return 80

default:

return 100

}

}

StepDonePercent(step) {

switch step {

case 1:

return 20

case 2:

return 40

case 3:

return 60

case 4:

return 80

case 5:

return 100

default:

return 100

}

}