(
audioPath: string,
metadata?: SongInfo
)
| 1626 | } |
| 1627 | if (metadata.artist && metadata.artist !== "Unknown Artist") { |
| 1628 | ffmpegArgs.push('-metadata', `artist=${metadata.artist}`); |
| 1629 | console.log(`[music] 添加艺术家: ${metadata.artist}`); |
| 1630 | } |
| 1631 | if (metadata.album) { |
| 1632 | ffmpegArgs.push('-metadata', `album=${metadata.album}`); |
| 1633 | console.log(`[music] 添加专辑: ${metadata.album}`); |
| 1634 | } |
| 1635 | |
| 1636 | // 添加 ID3v2 标签版本 |
| 1637 | ffmpegArgs.push('-id3v2_version', '3'); |
| 1638 | |
| 1639 | // 输出文件 |
| 1640 | ffmpegArgs.push('-y', outputPath); |
| 1641 | |
| 1642 | console.log("[music] 执行 FFmpeg 转换命令..."); |
| 1643 | const { stderr } = await execFileAsync('ffmpeg', ffmpegArgs); |
| 1644 | if (stderr) { |
| 1645 | console.log("[music] FFmpeg 输出:", stderr); |
| 1646 | } |
| 1647 | |
| 1648 | // 验证输出文件 |
| 1649 | if (!fs.existsSync(outputPath) || fs.statSync(outputPath).size === 0) { |
| 1650 | console.error("[music] 转换失败"); |
| 1651 | return audioPath; |
| 1652 | } |
| 1653 | |
| 1654 | // 删除原 OPUS 文件 |
| 1655 | fs.unlinkSync(audioPath); |
| 1656 | |
| 1657 | const newSize = fs.statSync(outputPath).size; |
| 1658 | console.log(`[music] OPUS 转 MP3 成功 (${Utils.formatSize(newSize)})`); |
| 1659 | return outputPath; |
| 1660 | } catch (error) { |
| 1661 | console.error("[music] OPUS 转换错误:", error); |
| 1662 | return audioPath; |
| 1663 | } |
| 1664 | } |
| 1665 | |
| 1666 | private async embedCoverToMp3( |
| 1667 | mp3Path: string, |
| 1668 | metadata?: SongInfo, |
| 1669 | thumbnailPath?: string |
| 1670 | ): Promise<string> { |
| 1671 | // 为 MP3 文件嵌入封面 |
| 1672 | if (!thumbnailPath || !fs.existsSync(thumbnailPath)) { |
| 1673 | return mp3Path; |
| 1674 | } |
| 1675 | |
| 1676 | try { |
| 1677 | const outputPath = mp3Path.replace(".mp3", "_final.mp3"); |
| 1678 | |
| 1679 | // 使用 FFmpeg 嵌入封面 |
| 1680 | const ffmpegArgs: string[] = ['-loglevel', 'error', '-i', mp3Path, '-i', thumbnailPath]; |
| 1681 | ffmpegArgs.push('-map', '0:a', '-map', '1:v'); |
| 1682 | ffmpegArgs.push('-c:a', 'copy', '-c:v', 'mjpeg'); |
| 1683 | ffmpegArgs.push('-disposition:v', 'attached_pic'); |
| 1684 | |
| 1685 | // 保留元数据 |
no test coverage detected