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

推荐订阅源

F
Full Disclosure
WordPress大学
WordPress大学
小众软件
小众软件
Cloudbric
Cloudbric
AWS News Blog
AWS News Blog
腾讯CDC
量子位
人人都是产品经理
人人都是产品经理
大猫的无限游戏
大猫的无限游戏
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
V
Vulnerabilities – Threatpost
Scott Helme
Scott Helme
Hugging Face - Blog
Hugging Face - Blog
博客园_首页
C
CXSECURITY Database RSS Feed - CXSecurity.com
The Hacker News
The Hacker News
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
IT之家
IT之家
Jina AI
Jina AI
Attack and Defense Labs
Attack and Defense Labs
S
SegmentFault 最新的问题
Simon Willison's Weblog
Simon Willison's Weblog
The Cloudflare Blog
阮一峰的网络日志
阮一峰的网络日志
T
Tailwind CSS Blog
Last Week in AI
Last Week in AI
博客园 - 【当耐特】
Google Online Security Blog
Google Online Security Blog
美团技术团队
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
V
Visual Studio Blog
罗磊的独立博客
L
LINUX DO - 最新话题
博客园 - Franky
博客园 - 叶小钗
Apple Machine Learning Research
Apple Machine Learning Research
The Last Watchdog
The Last Watchdog
J
Java Code Geeks
AI
AI
C
Cisco Blogs
酷 壳 – CoolShell
酷 壳 – CoolShell
C
Cyber Attacks, Cyber Crime and Cyber Security
Cisco Talos Blog
Cisco Talos Blog
博客园 - 三生石上(FineUI控件)
雷峰网
雷峰网
Help Net Security
Help Net Security
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
云风的 BLOG
云风的 BLOG
I
Intezer
S
Securelist

翰林的小站

Bilibili 自建视频云生态观察 2025:拿着过去的碎片,到远方去 年轻人的第一个包:不太严谨的 AOSC OS 贡献指南 2024:在变与不变之中做一颗不动的石头 关于浏览器通知推送(Web Push API)的那些事 HTP 笑传:扔掉 UDP,试试并不特殊的低精度时间同步 切割,与世界上的另一个我 时间线之外的 Kench BF-0505 官方驱动无法在高 DPI 下扫描 TIFF 文件的解决方案 博客现已支持手动切换深浅色模式 Migrate Dream! It's MyHexo!!!!! 验证:所幸,七牛云还是能「防护」住 XFF 的(附后续) 从山西联通到组播IP:七牛云的奇怪视角(附分析和后日谈) 消失的 2023 B站百大报告 2023:走在参差的影子之下 Hackergame Writeup 2023 : A Newbie Perspective 连夜分析,探寻B站2022年度百大的版本答案 2022:不为所动,做最业余的年度报告 对于谷歌翻译大陆版「引导页」行为的观测(2022年9月底)
Bad Apple:ALAC 音乐完整性验证与速查指南
abc1763613206 · 2025-09-13 · via 翰林的小站

Update on 2026-03-17: 使用某下载器对 TOGENASHI TOGEARI - Fragile Violet - Single 进行了复测,发现解码失败的问题仍然存在,而且元信息里的 Encoded date 仍然是 2025-06-23,证明苹果并没有重新编码。鉴于内录测试已经证明了下载器的可靠性,故此次并没有重新使用内录法测试,有兴趣的可自行验证。

TL;DR for non-Chinese readers:
Since around May 2025, We’ve discovered that many lossless ALAC files sourced from Apple Music are fundamentally corrupted—an issue that remains unfixed as of March 2026. Through rigorous internal recording tests, where We captured raw audio directly from the official Windows client (shout out to @TheM14) and compared it with pristine tracks from platforms like MORA and Tidal, We’ve proven that this is not a third-party downloader or DRM decryption glitch, but a defect originating directly from Apple’s own source files or encoders.
Because these corrupted files consistently fail standard integrity checks in software like foobar2000 and throw specific processing errors in FFmpeg, I’ve included automated batch scripts (for Windows, Bash, and PowerShell) below to help you easily scan your own local library.
Ultimately, Apple Music’s lossless tracks can no longer be blindly trusted for high-fidelity archiving; I strongly advise you to verify your files, be cautious of converted FLACs that might mask these underlying errors, and always cross-reference your music with known good sources.

背景

本文是对频道一段时间前的发送的这条推文的整理与补充,方便追踪、讨论与评论区拍砖:

在大约今年5月份之后,Apple Music 某个节点上的无损音乐编码器似乎坏掉了,导致他们提供的音乐源文件就是爆的,相比于其他来源会出现丢失采样的问题。根据社区的实验,内录官方客户端并分析采样,会发现问题依然存在,因此该问题似乎与社区中热门的第三方下载实现无关

叠甲在先:

  1. 尽管 Apple Music 的下载方案已从去年 DRM 被公开破拆之后成为马奇诺防线,并可在多个国内平台看到教程,但本文并不提供任何下载指导,也不鼓励任何形式的侵权行为。
  2. 本文的方法论建立在对几张典型专辑的实际测试之上,我并非 Apple Music 的利益相关者,我甚至并不知道当前的情况是 Apple Music 官方的编码器压坏了,还是 Apple 故意为之,推出了某个很新的妙妙 DRM 方案。众所周知,Apple Music 的编码器会对音频进行一些特殊处理(比如将部分数字母带™的响度调高),如果这次发现的音频问题在内录后依然存在,那么我认为它就不应被归类为 DRM,而是一种事实上的音频水印

现象

正文开始,以下是编写时发现仍有问题的专辑,你可以用来进行测试:

基于如上的叠甲,本文不会讨论更多的原理,只说对速查有用的现象:

  1. 出问题的音乐大多数是在今年(即 2025 年)之后被 Apple 重编码,通过一个现代的播放器(如 Potplayer)你可以在音频文件的 Encoded date 里找到 2025 年之后的日期。

  1. 如果使用带有校验功能的下载工具,会发现这些文件无法通过测试。使用 foobar2000 等第三方软件进行验证完整性,也无法通过:

使用 foobar2000 内置的 FLAC 转换也会报错:

  1. 使用 ffmpeg 进行转码时会报错,根据你不同的 ffmpeg 版本,报错通常会是如下的其中一种或多种:
  • Invalid data found when processing input
  • Not yet implemented in FFmpeg
  • Syntax element
  • patch welcome / patches welcome

其中,对于一大组文件,我认为使用 ffmpeg 进行检测是最为方便的方法,尤其是当你需要批量处理时。你可以使用以下命令来模拟:

1
ffmpeg -i "m4a文件" -f wav -hide_banner -loglevel info NUL 2>&1

以如上的 TOGENASHI TOGEARI - Fragile Violet - Single 为例,输出会是这样的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Stream 
Metadata:
encoder : Lavc62.13.101 pcm_s16le
creation_time : 2025-06-23T04:06:21.000000Z
handler_name : Fragile Violet
vendor_id : [0][0][0][0]
[alac @ 00000289a4f48580] invalid element channel count
[aist#0:0/alac @ 00000289a2cc8fc0] [dec:alac @ 00000289a2cc9180] Error submitting packet to decoder: Invalid data found when processing input
[alac @ 00000289a2cee6c0] invalid element channel count
[alac @ 00000289a4f48580] invalid element channel count
[aist#0:0/alac @ 00000289a2cc8fc0] [dec:alac @ 00000289a2cc9180] Error submitting packet to decoder: Invalid data found when processing input
Last message repeated 1 times
[alac @ 00000289a4f79a40] Syntax element 4 is not implemented. Update your FFmpeg version to the newest one from Git. If the problem still occurs, it means that your file has a feature which has not been implemented.
[aist#0:0/alac @ 00000289a2cc8fc0] [dec:alac @ 00000289a2cc9180] Error submitting packet to decoder: Not yet implemented in FFmpeg, patches welcome
[out#0/wav @ 00000289a2c23080] video:0KiB audio:32168KiB subtitle:0KiB other streams:0KiB global headers:0KiB muxing overhead: 0.000929%
size= 32168KiB time=00:03:07.10 bitrate=1408.4kbits/s speed=3.54e+03x elapsed=0:00:00.05

速查工具

出于验证的需求,我找 Gemini 生了个批处理脚本并改了改,可以用于批量校验这些文件。 你可以直接下载使用,或是在附录找到源代码,自己看一眼再跑。在运行脚本时请确保您的环境中已安装并配置好 ffmpeg,并且可以在命令行中直接调用。

验证

此前的发现在社区内引发了广泛讨论,其中不乏有观点认为这是 Apple Music 的一种防盗版机制。为了厘清这一问题,本段将提供一份客户端内录的验证记录,让每位读者都能亲手检验——这些音频瑕疵究竟是源于 DRM 保护,还是 Apple 自身的编码器缺陷。
在动手前,请务必再次阅读以下声明:首先,出于版权考量,本文不会涉及任何下载教学,我们假设您已合法拥有需要验证的音乐。其次,我们判断的逻辑很简单,也已在上文中叙述:如果所谓的“问题”在内录后仍然存在,那它就和 DRM 无关了,而是一种被编码进音频里的“事实水印”,是该和国内的流媒体坐一桌的。

此处非常感谢 @TheM14 老师进行了严谨的测试,为本文提供了非常重要的参考依据。

准备工作

首先,我们需要证明内录的结果是正确的,这里需要提前进行以下处理:

  1. 寻找一个内录通道,这里使用的是 Minifuse 声卡驱动自带的内录通道(主要是方便),像 VoiceMeeter 的虚拟内录通道也是可以的。
  2. 将输出的采样率和位深设置成和原曲相同。
  3. 干掉 Windows 自带的 CAudioLimiter。 这是 Windows 操作系统音频引擎中一个内置的音频处理组件。它的主要功能是作为一个峰值限制器(limiter),防止音频信号在输出时超过最大电平(0 dBFS),从而避免产生削波(clipping)失真。但为了忠实地还原音频,我们需要确保录制的音频不经过任何额外处理。你可以参考 B 站上的 BV1GPNezjEsM 来干掉它。(在进行任何有关注册表的操作前,请手动备份注册表)

内录与验证

接下来进行内录,此处以「結束バンド」的「僕と三原色」这首歌为例。

  1. 先在 Apple Music 的 Windows 客户端中寻找对应的歌曲并进行下载(以免网络不良的情况下播放成 lossy 的 AAC).
  2. 使用内录通道配合 Reaper 内录(录制的时候要设置好工程的采样率),之后导出 48/24 (与已知采样率一致)的 WAV .

在进行后续对比之前,我们必须首先确保录音能够精确还原 Apple Music 输出的原始音频流。我们可以使用 foobar2000 的 Binary Comparator 组件(下称 bitcompare)来进行对比,你可以在 foobar2000 的组件库 中找到它并安装,请注意一下这个组件只有 32 位格式。
此处,我们手里有三份音源,一份是通过工具下载解密 DRM 获得的 ALAC(decrypted.m4a),一份是内录的 WAV(recorded.wav),另一份是通过 MORA 自购下载的无损 FLAC(mora.flac)。

首先将 decrypted.m4amora.flac 进行比较:

此处 bitcompare 没有找到差别,证明了下载解密工具本身没有问题,两个来源的音源是一致的。

接下来对比 mora.flacrecorded.wav

注意到 bitcompare 的提示:
Differences found in compared tracks; the tracks became identical after applying offset and truncating first/last samples.
Extra leading/trailing sections contained only null samples.

我们显然不能在内录的时候做到和原曲完全同步,因此 bitcompare 通过对齐和裁剪的方式来忽略掉了前后多余的采样点(事实上全是空的),然后发现中间的部分是完全一致的
通过如上的对比,我们可以得出结论:内录是没问题的,解密也是没问题的。因为可以和 MORA 自购下载的文件对齐每个采样点的数值,如果不是完美解密这根本不可能。

问题专辑

接下来,我们再来对比一下已知有问题的蔡依林「Pleasure」专辑里的 Safari,首先我们使用解密工具下载一版,然后尝试使用 foobar2000 来进行完整性验证:

为了排除发行方对不同渠道的影响,此处从 Tidal 和 Amazon Music HD 也分别购买下载了同一首歌的同音质音频,并进行 bitcompare 测试:

结果是一模一样且无错误的,可以证明并非发行商的问题。

然后参照上述的步骤对这首歌进行内录,然后直接使用 Adobe Audition 与来自 Amazon/Tidal 的音源进行反相相消比较,可以发现右声道似乎出现了一个孤零零的音频信号

这里就是 Apple 经过内录和 Amazon/Tidal 的音源不同的地方了,此处出现了一个采样值的错误,这个错误很有可能由于解码器的不支持,导致直接丢弃这个数据块,从而从一个采样点的错误衍生到 4096 个采样点的错误(一个 block)。

验证就到这里,我们可以得出结论:Apple Music 的无损音源确实存在问题,这个问题在内录后依然存在。出于版权考量,本文不会提供相关的源文件下载,读者可以参考上述的步骤自行实验。

结语

所以从音乐收藏和发布的角度来说,我们可以得出什么结论?

  1. Apple Music 的无损音源现在已经不再能和 Amazon 和 Qobuz 一样可信(这应该会给各路喜欢用 Apple Music 搞视听的 HiFi 爱好者一记闷棍),从各路来源获取的时候应该格外谨慎。(实际上真正应该得到的教训是从多个来源交叉验证)
  2. 无论来源如何,任何 ALAC 文件,尤其是已被转码为 FLAC 的文件,都应被视为潜在的污染源尤其是已被转码过的 FLAC 文件,因为在转码完毕后他们的编码错误被掩盖了。
  3. 务必优先采用亲手获取、来源清晰的音频。切勿信赖第三方的链接解析站或下载机器人,因为它们分发的文件缺乏来源保证(有的甚至在静默转码到 FLAC 的时候忽略了上述问题),可能继承了原始文件存在的所有风险。
  4. 如果你正在使用社区里比较流行的下载实现,请务必把他们提供的完整性验证功能打开(原理实际上和本文是类似的)。在排除了网络、账户等个人因素后,若校验持续失败,则应判定 Apple Music 的官方音源本身已损坏。此时,请果断放弃该来源,以避免收藏或传播有问题的音频。

本文提供的方法与结论仅供参考。此方法可有效识别并排除部分存在问题的专辑,但无法保证通过验证的专辑绝对无误。若您获取了 Apple Music 的 ALAC 音频文件,最可靠的验证方式仍是与其他无损且已知无音频水印与后处理的音源(如通过对比未压缩音轨的 MD5 哈希值)进行交叉比对。在撰写本文时,我对一些文件进行了此类交叉验证,目前尚未发现响度匹配而 MD5 值不同的案例,欢迎评论区补充拍砖。  

附录

以下是速查脚本的源代码,理论上与可以直接下载的版本一致,你可以自行验证与修改:

Windows 批处理脚本

请注意:因为批处理中使用 CHCP 65001 设置了 UTF-8 代码页,请保证你在保存时选择使用 UTF-8 编码。如果你不知道这是什么且在运行的时候遇见了编码错误(比如乱码),请把下列代码中的 CHCP 65001 >NUL 这一行删除。

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
@ECHO OFF
SETLOCAL ENABLEDELAYEDEXPANSION




CHCP 65001 >NUL


FOR /F "tokens=2 delims==" %%I IN ('wmic os get LocalDateTime /value') DO SET "dt=%%I"
SET "CURRENT_DATE=%dt:~0,4%-%dt:~4,2%-%dt:~6,2%"
SET "CURRENT_TIME=%dt:~8,2%-%dt:~10,2%-%dt:~12,2%"


SET "report_basename=ALAC_Scan_Report_%CURRENT_DATE%_%CURRENT_TIME%.txt"
SET "report_file=%~dp0%report_basename%"
SET "temp_log=%TEMP%\ffmpeg_scan_error.log"


SET /A problem_count=0
SET /A total_files_scanned=0

ECHO.
ECHO =======================================================
ECHO == ALAC 编码问题扫描工具 20250911 ==
ECHO =======================================================
ECHO.


WHERE ffmpeg >NUL 2>NUL
IF %ERRORLEVEL% NEQ 0 (
ECHO [错误] 未在您的 PATH 中找到 FFMPEG。
ECHO 请先安装 FFMPEG 并确保可以从命令行调用它。
PAUSE
GOTO :EOF
)


IF EXIST "%report_file%" DEL "%report_file%"
ECHO ALAC 文件扫描报告 > "%report_file%"
ECHO 生成时间: %DATE% %TIME% >> "%report_file%"
ECHO ====================================================== >> "%report_file%"


IF "%~1"=="" (
ECHO [模式] 未拖放文件或文件夹,将递归扫描当前目录。
ECHO.
CALL :ScanDirectory "."
) ELSE (
ECHO [模式] 检测到拖放操作,将处理指定的文件或文件夹。
ECHO.
FOR %%A IN (%*) DO (
IF EXIST "%%~A\" (
CALL :ScanDirectory "%%~A"
) ELSE (
IF /I "%%~xA"==".m4a" (
CALL :ProcessFile "%%~A"
) ELSE (
ECHO [跳过] "%%~nxA" 不是 M4A 文件。
)
)
)
)


ECHO.
ECHO --------------------------------------------------
ECHO.
ECHO 扫描完成!

ECHO 总共扫描了 !total_files_scanned! 个文件。

SET "summary="
IF %problem_count% == 0 (
SET "summary=结果: 未发现任何存在编码问题的文件。"
ECHO !summary!
) ELSE (
SET "summary=结果: 共发现 !problem_count! 个文件存在编码问题。"
ECHO !summary!
)


ECHO. >> "%report_file%"
ECHO ====================================================== >> "%report_file%"
ECHO 扫描总结: >> "%report_file%"
ECHO 总共扫描了 %total_files_scanned% 个 M4A 文件。 >> "%report_file%"
ECHO %summary% >> "%report_file%"
ECHO ====================================================== >> "%report_file%"

:AfterChecksum
ECHO 详细的综合报告已生成:
ECHO "%report_file%"
IF %problem_count% GTR 0 (
ECHO.
ECHO 因为发现了问题文件,正在为您自动打开报告...
START "" "%report_file%"
)

ECHO.
PAUSE
GOTO :EOF



:ScanDirectory
SET "target_dir=%~1"
ECHO --- 正在递归扫描文件夹: "%target_dir%" ---
FOR /R "%target_dir%" %%F IN (*.m4a) DO (
CALL :ProcessFile "%%F"
)
GOTO :EOF



:ProcessFile
SET "current_file=%~1"
SET /A total_files_scanned+=1
ECHO.
ECHO [!total_files_scanned!] 正在检查: "!current_file!"


ffmpeg -i "!current_file!" -f wav -hide_banner -loglevel info NUL 2> "%temp_log%"


findstr /C:"Invalid data found when processing input" /C:"Not yet implemented in FFmpeg" /C:"Syntax element" /C:"patch welcome" /C:"patches welcome" "%temp_log%" >NUL

ECHO. >> "%report_file%"
ECHO ---------------------------------------- >> "%report_file%"
ECHO [文件] "!current_file!" >> "%report_file%"

IF !ERRORLEVEL! == 0 (

SET /A problem_count+=1
ECHO [状态] 发现问题!


ECHO [状态] 发现问题! >> "%report_file%"
ECHO [详情] FFmpeg 完整输出: >> "%report_file%"
TYPE "%temp_log%" >> "%report_file%"

) ELSE (

ECHO [状态] 正常
ECHO [状态] 正常 >> "%report_file%"


FOR /F "tokens=*" %%L IN ('findstr " Audio:" "%temp_log%"') DO (

ECHO [信息] %%L

ECHO [信息] %%L >> "%report_file%"
)
)


IF EXIST "%temp_log%" DEL "%temp_log%"
GOTO :EOF

Bash 脚本

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
#!/usr/bin/env bash


set -o pipefail
export LC_ALL=${LC_ALL:-C.UTF-8}
export LANG=${LANG:-C.UTF-8}
CURRENT_DATE=$(date +%Y-%m-%d)
CURRENT_TIME=$(date +%H-%M-%S)
script_dir=$(cd "$(dirname "$0")" && pwd)
report_basename="ALAC_Scan_Report_${CURRENT_DATE}_${CURRENT_TIME}.txt"
report_file="${script_dir}/${report_basename}"
temp_log=$(mktemp -t ffmpeg_scan_error.XXXXXX.log)

problem_count=0
total_files_scanned=0

echo
echo "======================================================="
echo "== ALAC 编码问题扫描工具 20250911 =="
echo "======================================================="
echo

if ! command -v ffmpeg >/dev/null 2>&1; then
echo "[错误] 未在 PATH 中找到 ffmpeg"
exit 1
fi

: > "$report_file"
{
echo "ALAC 文件扫描报告"
echo "生成时间: $(date '+%Y-%m-%d %H:%M:%S')"
echo "======================================================"
} >> "$report_file"

append_report() {
printf '%s\n' "$@" >> "$report_file"
}

process_file() {
local file="$1"
total_files_scanned=$((total_files_scanned + 1))
echo
echo "[$total_files_scanned] 正在检查: \"$file\""
: > "$temp_log"
ffmpeg -i "$file" -f wav -hide_banner -loglevel info -y /dev/null 2> "$temp_log"

if grep -F -e "Invalid data found when processing input" \
-e "Not yet implemented in FFmpeg" \
-e "patch welcome" \
-e "patches welcome" \
-e "Syntax element" "$temp_log" >/dev/null 2>&1; then
problem=true
else
problem=false
fi

append_report ""
append_report "----------------------------------------"
append_report "[文件] \"$file\""

if $problem; then
problem_count=$((problem_count + 1))
echo " [状态] 发现问题!"
append_report "[状态] 发现问题!"
append_report "[详情] FFmpeg 完整输出:"
cat "$temp_log" >> "$report_file"
else
echo " [状态] 正常"
append_report "[状态] 正常"
if grep -F " Audio:" "$temp_log" >/dev/null 2>&1; then
while IFS= read -r line; do
echo " [信息] $line"
append_report "[信息] $line"
done < <(grep -F " Audio:" "$temp_log")
fi
fi
}

scan_directory() {
local target_dir="$1"
echo "--- 正在递归扫描文件夹: \"$target_dir\" ---"
while IFS= read -r -d '' f; do
process_file "$f"
done < <(find "$target_dir" -type f -iname '*.m4a' -print0)
}

if [[ $# -eq 0 ]]; then
echo "[模式] 未提供参数,将递归扫描当前目录。"
echo
scan_directory "."
else
echo "[模式] 检测到传入参数,将处理指定的文件或文件夹。"
echo
for path in "$@"; do
if [[ -d "$path" ]]; then
scan_directory "$path"
elif [[ -f "$path" ]]; then
ext="${path##*.}"
ext="${ext,,}"
if [[ "$ext" == "m4a" ]]; then
process_file "$path"
else
echo "[跳过] \"$(basename "$path")\" 不是 M4A 文件。"
fi
else
echo "[跳过] \"$path\" 无法访问。"
fi
done
fi

echo
echo "--------------------------------------------------"
echo
echo "扫描完成!"
echo "总共扫描了 $total_files_scanned 个文件。"

if [[ $problem_count -eq 0 ]]; then
summary="结果: 未发现任何存在编码问题的文件。"
else
summary="结果: 共发现 $problem_count 个文件存在编码问题。"
fi
echo "$summary"

append_report ""
append_report "======================================================"
append_report "扫描总结:"
append_report "总共扫描了 ${total_files_scanned} 个 M4A 文件。"
append_report "$summary"
append_report "======================================================"

echo "详细的综合报告已生成:"
echo "\"$report_file\""

if [[ $problem_count -gt 0 ]]; then
if command -v xdg-open >/dev/null 2>&1; then
xdg-open "$report_file" >/dev/null 2>&1 &
elif command -v open >/dev/null 2>&1; then
open "$report_file" >/dev/null 2>&1 &
fi
fi

rm -f "$temp_log"
echo

PowerShell 7 脚本

Nullpointer 提供,支持多线程处理,适合大批量文件的扫描。

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

param(
[Parameter(Position = 0)]
[string[]]$Path
)

Set-StrictMode -Version Latest
$ErrorActionPreference = 'Stop'

if (-not (Get-Command ffmpeg -ErrorAction SilentlyContinue)) {
Write-Host "[错误] 未在 PATH 中找到 ffmpeg,请先安装。" -ForegroundColor Red
exit 1
}

function Get-M4AFiles {
param([string[]]$Paths)

if (-not $Paths -or @($Paths).Count -eq 0) {
Write-Host "[模式] 未提供参数,将递归扫描当前目录。"
return Get-ChildItem -Path '.' -Filter '*.m4a' -File -Recurse -ErrorAction SilentlyContinue
}

Write-Host "[模式] 处理指定的文件/文件夹。"
$list = New-Object System.Collections.Generic.List[System.IO.FileInfo]
foreach ($p in $Paths) {
$resolved = (Resolve-Path -LiteralPath $p -ErrorAction SilentlyContinue)?.Path
if (-not $resolved) { Write-Host "[跳过] 找不到路径:$p"; continue }
if (Test-Path -LiteralPath $resolved -PathType Container) {
Get-ChildItem -LiteralPath $resolved -Filter '*.m4a' -File -Recurse -ErrorAction SilentlyContinue |
ForEach-Object { [void]$list.Add($_) }
}
elseif (Test-Path -LiteralPath $resolved -PathType Leaf) {
if ([IO.Path]::GetExtension($resolved).ToLowerInvariant() -eq '.m4a') {
[void]$list.Add([IO.FileInfo]$resolved)
}
else {
Write-Host "[跳过] `"$([IO.Path]::GetFileName($resolved))`" 不是 M4A 文件。"
}
}
}
return $list
}

$targets = @( Get-M4AFiles -Paths $Path | Sort-Object FullName )
if ($targets.Count -eq 0) {
Write-Host "未找到任何 .m4a 文件。"
exit 0
}

$stamp = (Get-Date).ToString('yyyy-MM-dd_HH-mm-ss')
$report = Join-Path $PSScriptRoot "ALAC_Scan_Report_$stamp.txt"

$ErrorPatterns = @(
'Invalid data found when processing input',
'Not yet implemented in FFmpeg',
'Syntax element',
'patch welcome',
'patches welcome'
)

"ALAC 文件扫描报告" | Out-File -LiteralPath $report -Encoding UTF8
"生成时间: $(Get-Date -Format 'yyyy/MM/dd HH:mm:ss')" | Out-File -LiteralPath $report -Encoding UTF8 -Append
"=====================================================" | Out-File -LiteralPath $report -Encoding UTF8 -Append

$maxParallel = [Math]::Max(2, [Environment]::ProcessorCount)
$total = $targets.Count
$problemCount = 0

Write-Host ""
Write-Host "======================================================="
Write-Host "== ALAC 编码问题扫描工具"
Write-Host "======================================================="
Write-Host ""

$sw = [System.Diagnostics.Stopwatch]::StartNew()

$results = $targets | ForEach-Object -Parallel {
$filePath = $_.FullName
$ErrorPatterns = $using:ErrorPatterns

$log = [System.IO.Path]::GetTempFileName()
try {
& ffmpeg -v info -hide_banner -i $filePath -f null - 1>$null 2> $log
$text = if (Test-Path $log) { Get-Content $log -Raw -Encoding UTF8 } else { "" }
$hasProblem = $false
foreach ($p in $ErrorPatterns) {
if ($text -match [Regex]::Escape($p)) { $hasProblem = $true; break }
}
$audio = @()
if ($text) {
$audio = ($text -split "`r?`n") | Where-Object { $_ -match '\bAudio:' }
}

$fileName = [System.IO.Path]::GetFileName($filePath)
Write-Host "处理完成: $fileName" -ForegroundColor Green

[pscustomobject]@{
File = $filePath
Problem = $hasProblem
Log = $text
Audio = $audio
}
}
finally {
if (Test-Path $log) { Remove-Item $log -Force -ErrorAction SilentlyContinue }
}
} -ThrottleLimit $maxParallel

$sw.Stop()

foreach ($r in $results) {
Add-Content $report ""
Add-Content $report "----------------------------------------"
Add-Content $report "[文件] `"$($r.File)`""
if ($r.Problem) {
$problemCount++
Add-Content $report "[状态] 发现问题!"
Add-Content $report "[详情] FFmpeg 完整输出:"
Add-Content $report $r.Log
}
else {
Add-Content $report "[状态] 正常"
foreach ($line in $r.Audio) {
Add-Content $report "[信息] $line"
}
}
}

Add-Content $report ""
Add-Content $report "====================================================="
Add-Content $report "扫描总结:"
Add-Content $report "总共扫描了 $total 个 M4A 文件。"
Add-Content $report ("耗时: {0:N1} 秒" -f $sw.Elapsed.TotalSeconds)
if ($problemCount -eq 0) {
Add-Content $report "结果: 未发现任何存在编码问题的文件。"
}
else {
Add-Content $report "结果: 共发现 $problemCount 个文件存在编码问题。"
}

Write-Host ""
Write-Host ("扫描完成!共 {0} 个文件,{1} 个有问题。" -f $total, $problemCount) `
-ForegroundColor ($problemCount -eq 0 ? 'Green' : 'Yellow')
Write-Host "报告位置:$report"
if ($problemCount -gt 0) {
Start-Process -FilePath $report
}