Template
Fix M4A fallback ingestion
This commit is contained in:
@@ -10,6 +10,7 @@ import java.io.InputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
@@ -436,6 +437,115 @@ class AudioValidationServiceTest {
|
||||
}
|
||||
}
|
||||
|
||||
// ========== FFprobe 后备元数据读取 ==========
|
||||
|
||||
@Test
|
||||
void getMetadataCommandArgs_usesStructuredJson() {
|
||||
Path f = Paths.get("/tmp/song.m4a");
|
||||
List<String> args = service.getMetadataCommandArgs(f);
|
||||
assertTrue(args.contains("-print_format"));
|
||||
assertTrue(args.contains("json"));
|
||||
assertTrue(args.contains("-show_format"));
|
||||
assertTrue(args.contains("-show_streams"));
|
||||
// 绝不使用任何人类可读文本或文件名猜测选项
|
||||
assertFalse(args.contains("-of"));
|
||||
assertEquals(f.toAbsolutePath().toString(), args.get(args.size() - 1));
|
||||
}
|
||||
|
||||
@Test
|
||||
void readMetadata_returnsFullTags() throws Exception {
|
||||
assumeFfmpegFfprobe();
|
||||
Path tmpDir = Files.createTempDirectory("probe-meta-");
|
||||
try {
|
||||
Path m4a = createTaggedM4a(tmpDir, "song.m4a",
|
||||
"ProbeTitle", "ProbeArtist", "ProbeAlbum");
|
||||
|
||||
AudioValidationService.ProbeMetadata meta = service.readMetadata(m4a);
|
||||
assertTrue(meta.isAvailable(), "应成功读取元数据");
|
||||
assertEquals("ProbeTitle", meta.getFirst("title"));
|
||||
assertEquals("ProbeArtist", meta.getFirst("artist"));
|
||||
assertEquals("ProbeAlbum", meta.getFirst("album"));
|
||||
} finally {
|
||||
deleteDirectory(tmpDir);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void readMetadata_missingAlbumReturnsEmpty() throws Exception {
|
||||
assumeFfmpegFfprobe();
|
||||
Path tmpDir = Files.createTempDirectory("probe-meta-");
|
||||
try {
|
||||
// 仅写 title/artist,故意不写 album
|
||||
Path m4a = tmpDir.resolve("noalbum.m4a");
|
||||
ProcessBuilder pb = new ProcessBuilder(
|
||||
"ffmpeg", "-y", "-f", "lavfi", "-i", "anullsrc=r=44100:cl=mono",
|
||||
"-t", "0.3", "-c:a", "aac",
|
||||
"-metadata", "title=T", "-metadata", "artist=A",
|
||||
m4a.toAbsolutePath().toString());
|
||||
pb.redirectErrorStream(true);
|
||||
Process p = pb.start();
|
||||
readAllBytes(p.getInputStream());
|
||||
p.waitFor(15, TimeUnit.SECONDS);
|
||||
|
||||
AudioValidationService.ProbeMetadata meta = service.readMetadata(m4a);
|
||||
assertTrue(meta.isAvailable());
|
||||
assertEquals("", meta.getFirst("album"), "缺失的 album 应返回空字符串");
|
||||
} finally {
|
||||
deleteDirectory(tmpDir);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void readMetadata_nonAudioFileUnavailable() throws Exception {
|
||||
assumeFfmpegFfprobe();
|
||||
Path tmpDir = Files.createTempDirectory("probe-meta-");
|
||||
try {
|
||||
Path bogus = tmpDir.resolve("bogus.m4a");
|
||||
Files.write(bogus, "not an audio file".getBytes(StandardCharsets.UTF_8));
|
||||
|
||||
AudioValidationService.ProbeMetadata meta = service.readMetadata(bogus);
|
||||
assertFalse(meta.isAvailable(), "无法识别的文件应返回 unavailable");
|
||||
} finally {
|
||||
deleteDirectory(tmpDir);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void readMetadata_ffprobeFailureUnavailable() {
|
||||
// 强制 ffprobe 指向失败命令
|
||||
System.setProperty(AudioValidationService.FFPROBE_BIN_PROPERTY, "/bin/false");
|
||||
AudioValidationService.ProbeMetadata meta =
|
||||
service.readMetadata(Paths.get("/tmp/whatever.m4a"));
|
||||
assertFalse(meta.isAvailable(), "ffprobe 失败应返回 unavailable");
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用 FFmpeg 创建一个带标签的有效短 M4A(AAC)文件。
|
||||
*/
|
||||
private static Path createTaggedM4a(Path dir, String fileName,
|
||||
String title, String artist, String album) throws Exception {
|
||||
Path out = dir.resolve(fileName);
|
||||
ProcessBuilder pb = new ProcessBuilder(
|
||||
"ffmpeg", "-y", "-f", "lavfi", "-i", "anullsrc=r=44100:cl=mono",
|
||||
"-t", "0.3", "-c:a", "aac",
|
||||
"-metadata", "title=" + title,
|
||||
"-metadata", "artist=" + artist,
|
||||
"-metadata", "album=" + album,
|
||||
out.toAbsolutePath().toString());
|
||||
pb.redirectErrorStream(true);
|
||||
Process p = pb.start();
|
||||
readAllBytes(p.getInputStream());
|
||||
boolean finished = p.waitFor(15, TimeUnit.SECONDS);
|
||||
if (!finished) {
|
||||
p.destroyForcibly();
|
||||
throw new RuntimeException("ffmpeg 创建 M4A 超时");
|
||||
}
|
||||
if (p.exitValue() != 0) {
|
||||
throw new RuntimeException("ffmpeg 创建 M4A 失败 (exit=" + p.exitValue() + ")");
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
// ========== 帮助方法 ==========
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user