Improve music processing robustness and workflow UX
Unify safe file-move behavior and richer progress semantics across backend tasks, while upgrading traditional-to-simplified conversion and refining the frontend multi-step panels for clearer execution feedback.
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
package com.music.service;
|
||||
|
||||
import com.music.common.FileTransferUtils;
|
||||
import com.music.dto.ProgressMessage;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -29,6 +30,9 @@ public class LibraryMergeService {
|
||||
|
||||
private static final Set<String> LYRICS_EXTENSIONS = new HashSet<>(Arrays.asList("lrc"));
|
||||
private static final Set<String> COVER_NAMES = new HashSet<>(Arrays.asList("cover.jpg", "cover.png", "folder.jpg", "folder.png"));
|
||||
private static final Set<String> EXCLUDED_ROOT_DIRS = new HashSet<>(Arrays.asList(
|
||||
"_Manual_Fix_Required_", "_Reports"
|
||||
));
|
||||
|
||||
private final SimpMessagingTemplate messagingTemplate;
|
||||
private final ProgressStore progressStore;
|
||||
@@ -46,19 +50,19 @@ public class LibraryMergeService {
|
||||
try {
|
||||
// 基本校验
|
||||
if (srcDir == null || srcDir.trim().isEmpty()) {
|
||||
sendProgress(taskId, 0, 0, 0, 0, 0, null, "源目录不能为空", true);
|
||||
sendProgress(taskId, 0, 0, 0, 0, 0, 0, null, "源目录不能为空", true);
|
||||
return;
|
||||
}
|
||||
if (!Files.exists(srcPath) || !Files.isDirectory(srcPath)) {
|
||||
sendProgress(taskId, 0, 0, 0, 0, 0, null, "源目录不存在或不是目录", true);
|
||||
sendProgress(taskId, 0, 0, 0, 0, 0, 0, null, "源目录不存在或不是目录", true);
|
||||
return;
|
||||
}
|
||||
if (dstDir == null || dstDir.trim().isEmpty()) {
|
||||
sendProgress(taskId, 0, 0, 0, 0, 0, null, "目标目录不能为空", true);
|
||||
sendProgress(taskId, 0, 0, 0, 0, 0, 0, null, "目标目录不能为空", true);
|
||||
return;
|
||||
}
|
||||
if (srcPath.normalize().equals(dstPath.normalize())) {
|
||||
sendProgress(taskId, 0, 0, 0, 0, 0, null, "源目录与目标目录不能相同", true);
|
||||
sendProgress(taskId, 0, 0, 0, 0, 0, 0, null, "源目录与目标目录不能相同", true);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -72,6 +76,14 @@ public class LibraryMergeService {
|
||||
Map<Path, Path> coverMap = new HashMap<>(); // 专辑目录 -> 封面文件
|
||||
|
||||
Files.walkFileTree(srcPath, new SimpleFileVisitor<Path>() {
|
||||
@Override
|
||||
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) {
|
||||
if (isExcludedSystemDirectory(srcPath, dir)) {
|
||||
return FileVisitResult.SKIP_SUBTREE;
|
||||
}
|
||||
return FileVisitResult.CONTINUE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
|
||||
String fileName = file.getFileName().toString().toLowerCase();
|
||||
@@ -97,7 +109,7 @@ public class LibraryMergeService {
|
||||
|
||||
int total = audioFiles.size();
|
||||
if (total == 0) {
|
||||
sendProgress(taskId, 0, 0, 0, 0, 0, null, "未在源目录中找到音频文件", true);
|
||||
sendProgress(taskId, 0, 0, 0, 0, 0, 0, null, "未在源目录中找到音频文件", true);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -109,7 +121,7 @@ public class LibraryMergeService {
|
||||
|
||||
Set<String> processedAlbums = new HashSet<>();
|
||||
|
||||
sendProgress(taskId, total, 0, 0, 0, 0, null, "开始合并...", false);
|
||||
sendProgress(taskId, total, 0, 0, 0, 0, 0, null, "开始合并...", false);
|
||||
|
||||
for (Path audioFile : audioFiles) {
|
||||
try {
|
||||
@@ -141,7 +153,7 @@ public class LibraryMergeService {
|
||||
targetPath.getFileName().toString() + ".backup");
|
||||
Files.copy(targetPath, backupPath, StandardCopyOption.REPLACE_EXISTING);
|
||||
}
|
||||
Files.move(audioFile, targetPath, StandardCopyOption.REPLACE_EXISTING);
|
||||
FileTransferUtils.moveWithFallback(audioFile, targetPath);
|
||||
wasUpgraded = true;
|
||||
upgraded.incrementAndGet();
|
||||
} else {
|
||||
@@ -149,7 +161,7 @@ public class LibraryMergeService {
|
||||
int p = processed.incrementAndGet();
|
||||
if (p % 20 == 0 || p == total) {
|
||||
sendProgress(taskId, total, p, albumsMerged.get(), tracksMerged.get(),
|
||||
upgraded.get(), audioFile.getFileName().toString(),
|
||||
upgraded.get(), skipped.get(), audioFile.getFileName().toString(),
|
||||
String.format("已处理 %d/%d (升级: %d)", p, total, upgraded.get()), false);
|
||||
}
|
||||
continue; // 跳过,不升级
|
||||
@@ -160,7 +172,7 @@ public class LibraryMergeService {
|
||||
int p = processed.incrementAndGet();
|
||||
if (p % 20 == 0 || p == total) {
|
||||
sendProgress(taskId, total, p, albumsMerged.get(), tracksMerged.get(),
|
||||
upgraded.get(), audioFile.getFileName().toString(),
|
||||
upgraded.get(), skipped.get(), audioFile.getFileName().toString(),
|
||||
String.format("已处理 %d/%d (升级: %d)", p, total, upgraded.get()), false);
|
||||
}
|
||||
continue;
|
||||
@@ -168,7 +180,7 @@ public class LibraryMergeService {
|
||||
} else {
|
||||
// 新文件,直接移动
|
||||
Files.createDirectories(targetPath.getParent());
|
||||
Files.move(audioFile, targetPath, StandardCopyOption.REPLACE_EXISTING);
|
||||
FileTransferUtils.moveWithFallback(audioFile, targetPath);
|
||||
}
|
||||
|
||||
tracksMerged.incrementAndGet();
|
||||
@@ -178,7 +190,7 @@ public class LibraryMergeService {
|
||||
if (lyricsFile != null && Files.exists(lyricsFile)) {
|
||||
Path lyricsTarget = targetPath.resolveSibling(lyricsFile.getFileName().toString());
|
||||
if (!Files.exists(lyricsTarget) || wasUpgraded) {
|
||||
Files.move(lyricsFile, lyricsTarget, StandardCopyOption.REPLACE_EXISTING);
|
||||
FileTransferUtils.moveWithFallback(lyricsFile, lyricsTarget);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -190,7 +202,7 @@ public class LibraryMergeService {
|
||||
Path coverTarget = albumDir.resolve("cover.jpg");
|
||||
if (!Files.exists(coverTarget)) {
|
||||
// 目标目录没有封面,直接移动
|
||||
Files.move(coverFile, coverTarget, StandardCopyOption.REPLACE_EXISTING);
|
||||
FileTransferUtils.moveWithFallback(coverFile, coverTarget);
|
||||
} else {
|
||||
// 比较封面,保留更好的版本
|
||||
if (isBetterCover(coverFile, coverTarget)) {
|
||||
@@ -198,7 +210,7 @@ public class LibraryMergeService {
|
||||
Path backupPath = coverTarget.resolveSibling("cover.jpg.backup");
|
||||
Files.copy(coverTarget, backupPath, StandardCopyOption.REPLACE_EXISTING);
|
||||
}
|
||||
Files.move(coverFile, coverTarget, StandardCopyOption.REPLACE_EXISTING);
|
||||
FileTransferUtils.moveWithFallback(coverFile, coverTarget);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -212,20 +224,20 @@ public class LibraryMergeService {
|
||||
int p = processed.incrementAndGet();
|
||||
if (p % 20 == 0 || p == total) {
|
||||
sendProgress(taskId, total, p, albumsMerged.get(), tracksMerged.get(),
|
||||
upgraded.get(), audioFile.getFileName().toString(),
|
||||
upgraded.get(), skipped.get(), audioFile.getFileName().toString(),
|
||||
String.format("已处理 %d/%d (升级: %d)", p, total, upgraded.get()), false);
|
||||
}
|
||||
}
|
||||
|
||||
sendProgress(taskId, total, processed.get(), albumsMerged.get(), tracksMerged.get(),
|
||||
upgraded.get(), null,
|
||||
upgraded.get(), skipped.get(), null,
|
||||
String.format("合并完成!专辑: %d, 曲目: %d, 升级: %d, 跳过: %d",
|
||||
albumsMerged.get(), tracksMerged.get(), upgraded.get(), skipped.get()),
|
||||
true);
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("合并任务执行失败", e);
|
||||
sendProgress(taskId, 0, 0, 0, 0, 0, null, "任务执行失败: " + e.getMessage(), true);
|
||||
sendProgress(taskId, 0, 0, 0, 0, 0, 0, null, "任务执行失败: " + e.getMessage(), true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -325,11 +337,29 @@ public class LibraryMergeService {
|
||||
return i > 0 ? fileName.substring(0, i) : fileName;
|
||||
}
|
||||
|
||||
private boolean isExcludedSystemDirectory(Path sourceRoot, Path dir) {
|
||||
if (dir == null || sourceRoot == null) {
|
||||
return false;
|
||||
}
|
||||
if (dir.equals(sourceRoot)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Path relative = sourceRoot.relativize(dir);
|
||||
if (relative.getNameCount() <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
String firstSegment = relative.getName(0).toString();
|
||||
return EXCLUDED_ROOT_DIRS.contains(firstSegment);
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送进度消息
|
||||
*/
|
||||
private void sendProgress(String taskId, int total, int processed, int albumsMerged,
|
||||
int tracksMerged, int upgraded, String currentFile, String message, boolean completed) {
|
||||
int tracksMerged, int upgraded, int skipped,
|
||||
String currentFile, String message, boolean completed) {
|
||||
try {
|
||||
ProgressMessage pm = new ProgressMessage();
|
||||
pm.setTaskId(taskId);
|
||||
@@ -338,6 +368,10 @@ public class LibraryMergeService {
|
||||
pm.setProcessed(processed);
|
||||
pm.setSuccess(albumsMerged); // 使用 success 字段存储专辑数
|
||||
pm.setFailed(tracksMerged); // 使用 failed 字段存储曲目数
|
||||
pm.setAlbumsMerged(albumsMerged);
|
||||
pm.setTracksMerged(tracksMerged);
|
||||
pm.setUpgradedFiles(upgraded);
|
||||
pm.setSkippedFiles(skipped);
|
||||
pm.setCurrentFile(currentFile);
|
||||
pm.setMessage(message);
|
||||
pm.setCompleted(completed);
|
||||
|
||||
Reference in New Issue
Block a user