Using FFmpeg Merge All MP3 Files To One
2023-03-26
筆記如何使用 FFmpeg 將眾多 mp3 檔案合成為單一 mp3 檔以及將 mp3 檔案加入封面成為 mp4 檔。
說明
foreach ($i in (48..57 + 97..102)) {
$c = [char]$i
$files = Get-ChildItem -Path . -Filter $c*.mp3 | Sort-Object Name | Select-Object -ExpandProperty FullName
.\ffmpeg.exe -i ('concat:' + ($files -join '|')) -acodec copy "$c.mp3"
}
因為 ffmpeg.exe
不支援 wildcard 的方式輸入檔案名稱,而必須使用 |
連結所有的 input 檔案名稱,所以透過 PowerShell 來協助處理,但此方法會受限於最大字串長度,替代的方式是迭代產生檔案,使用 reduce 的演算方式來進行合併。
而本次實作為了簡化操作,就以 group by 的方式,將檔案依照 0 到 f 開頭,進行分類,而 48..57 表示 ascii 的 0 到 9,97..102 則是 a 到 f,用以迭代檔案的名稱。
.\ffmpeg.exe -loop 1 -framerate 2 -i cover.png -i $c.mp3 -c:a copy -preset:v ultrafast -shortest $c-series.mp4
使用 ffmpeg.exe
將 mp3 轉換為 mp4,且只使用靜態封面的做插圖的轉換,轉換的時間比預期的久很多,後來藉由 -preset
以及 framerate
去做調校才有改善,但兩個小時的 mp3 檔案,仍需要約 5 分鐘的轉換時間。
進階分類檔案
當使用檔名第一碼檔案數量太多,但使用第二碼的時候又太少的折衷處理知道,根據第二碼分成兩組的處理方式:
static readonly string FolderPath = @"D:\Files";
static void Main(string[] args)
{
string folderPath = FolderPath;
string[] fileNames = Directory.GetFiles(folderPath).Select(Path.GetFileName).ToArray();
var groups = fileNames.GroupBy(
fileName => $"{fileName[0]}{GetGroupType(fileName[1])}",
(prefix, fileName) => new
{
Prefix = prefix,
FileNames = fileName.ToArray()
});
foreach (var group in groups)
{
Console.WriteLine($"Prefix: {group.Prefix}, Count: {group.FileNames.Length}");
string destination = Path.Combine(FolderPath, group.Prefix);
GenerateFolder(destination);
foreach (var file in group.FileNames)
{
MoveFile(file, destination);
}
}
}
static void GenerateFolder(string destinationFolderPath)
{
if (!Directory.Exists(destinationFolderPath))
{
Directory.CreateDirectory(destinationFolderPath);
}
}
static void MoveFile(string fileSource, string fileDestination)
{
string sourceFilePath = Path.Combine(FolderPath, fileSource);
string destinationFilePath = Path.Combine(fileDestination, fileSource);
File.Move(sourceFilePath, destinationFilePath);
}
static string GetGroupType(char prefix)
{
return prefix >= '0' && prefix <= '7' ? "A" : "B";
}
接著使用 PowerShell 搭配 ffmpeg
開始進行檔案轉換:
for ($i = 0; $i -le 15; $i++) {
for ($j = 0; $j -le 1; $j++) {
$prefix = if ($i -lt 10) {"$i"} else {([char]($i + 87))}
$suffix = if ($j -eq 0) {"A"} else {"B"}
echo $prefix$suffix
cd $prefix$suffix
$files = Get-ChildItem -Path . -Filter *.mp3 | Sort-Object Name | Select-Object -ExpandProperty FullName
.\ffmpeg.exe -i ('concat:' + ($files -join '|')) -acodec copy "$prefix$suffix.mp3"
cd ..
move "$prefix$suffix\$prefix$suffix.mp3" "D:\Files\$prefix$suffix.mp3"
}
}