wsl2 挂载NAS网盘指南 + transcode music 文档
This commit is contained in:
885
Project/transcode_music/transcode_music.md
Normal file
885
Project/transcode_music/transcode_music.md
Normal file
@@ -0,0 +1,885 @@
|
||||
# `transcode_music.py` 使用手册
|
||||
|
||||
批量将无损音乐(FLAC / WAV / DSF / DFF / APE)转码为 MP3 320k CBR 的 Python 脚本,支持断点续跑、错误恢复、CUE 整轨检测、DSD 转码。
|
||||
|
||||
---
|
||||
|
||||
## 目录
|
||||
|
||||
- [1. 简介](#1-简介)
|
||||
- [2. 系统要求](#2-系统要求)
|
||||
- [3. 快速上手](#3-快速上手)
|
||||
- [4. 命令行参数详解](#4-命令行参数详解)
|
||||
- [5. 常用使用场景](#5-常用使用场景)
|
||||
- [6. Manifest 文件说明](#6-manifest-文件说明)
|
||||
- [7. 文件分类规则](#7-文件分类规则)
|
||||
- [8. CUE 整轨检测逻辑](#8-cue-整轨检测逻辑)
|
||||
- [9. 采样率与 DSD 处理](#9-采样率与-dsd-处理)
|
||||
- [10. 错误处理与恢复](#10-错误处理与恢复)
|
||||
- [11. 已知局限](#11-已知局限)
|
||||
- [12. FAQ](#12-faq)
|
||||
|
||||
---
|
||||
|
||||
## 1. 简介
|
||||
|
||||
**这个工具解决什么问题?**
|
||||
|
||||
你有一大堆无损音乐(比如 `/mnt/z/` 网盘上几百 GB 的 FLAC / WAV / DSF),想批量转成体积更小的 MP3 320k 存到手机/车载/移动设备,同时**保持原有的目录结构和文件名**,并且要能**中途暂停、下次继续**。
|
||||
|
||||
**核心特性**:
|
||||
|
||||
| 特性 | 说明 |
|
||||
|---|---|
|
||||
| 保持目录结构 | 目标目录树与源目录一一对应,多碟专辑 (CD1/CD2) 分开处理 |
|
||||
| 保留所有资源 | 封面图片 (jpg/png/…)、说明 TXT、EAC log、CUE 都会复制过去 |
|
||||
| 断点续跑 | 转码状态记录在源目录的 `.transcode_manifest.json`,任意时刻中断都能从上次位置继续 |
|
||||
| CUE 整轨检测 | 分析阶段自动识别 "CUE + 单个大 flac 未分轨" 的专辑,报警并跳过等你手动 split |
|
||||
| CUE 已分轨识别 | 若 CUE 引用的是整轨但同时存在分轨文件,自动跳过冗余的整轨文件 |
|
||||
| 高采样率处理 | 24bit / 96kHz / 192kHz 输入自动重采样到 44.1kHz (符合 MP3 规范) |
|
||||
| DSD 支持 | DSF/DFF (DSD64 / DSD128) 自动解码降采样后编码 MP3 |
|
||||
| 保留封面 | 内嵌于 FLAC/DSF 的封面会写入 MP3 的 ID3v2 tag |
|
||||
| 错误隔离 | 单个文件失败不中断整体流程,错误写入 manifest 供后续排查 |
|
||||
| 幂等 | 已完成的文件/专辑,再次运行时秒跳过 |
|
||||
|
||||
---
|
||||
|
||||
## 2. 系统要求
|
||||
|
||||
| 组件 | 版本 | 说明 |
|
||||
|---|---|---|
|
||||
| Python | 3.10+ | 只用标准库,无需 pip install 任何依赖 |
|
||||
| ffmpeg | 6.0+ | 需支持 `libmp3lame` 编码器,`dsd_lsbf/msbf` 解码器 (DSD 用) |
|
||||
| ffprobe | 6.0+ | 通常随 ffmpeg 一起安装 |
|
||||
|
||||
**检查环境**:
|
||||
|
||||
```bash
|
||||
python3 --version
|
||||
ffmpeg -version | head -1
|
||||
ffprobe -version | head -1
|
||||
# 确认支持 mp3 编码
|
||||
ffmpeg -codecs 2>/dev/null | grep libmp3lame
|
||||
# 确认支持 DSD 解码 (如果你有 DSF 文件)
|
||||
ffmpeg -codecs 2>/dev/null | grep -i dsd
|
||||
```
|
||||
|
||||
**安装 ffmpeg**(如果没有):
|
||||
|
||||
```bash
|
||||
# Ubuntu/Debian/WSL
|
||||
sudo apt install ffmpeg
|
||||
|
||||
# macOS
|
||||
brew install ffmpeg
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. 快速上手
|
||||
|
||||
**最常用的三个命令**:
|
||||
|
||||
```bash
|
||||
# 1. 先分析看看有没有问题 (不实际转码)
|
||||
python3 transcode_music.py /mnt/z/我的音乐 /mnt/x/mp3输出 --analyze-only
|
||||
|
||||
# 2. 确认没问题后, 正式转码 (中断可再跑, 自动 resume)
|
||||
python3 transcode_music.py /mnt/z/我的音乐 /mnt/x/mp3输出
|
||||
|
||||
# 3. 后来加了新专辑, 再跑一次 (已完成的会自动跳过, 只处理新加的)
|
||||
python3 transcode_music.py /mnt/z/我的音乐 /mnt/x/mp3输出
|
||||
```
|
||||
|
||||
就这么简单。下面是完整的参数说明和更多用法。
|
||||
|
||||
---
|
||||
|
||||
## 4. 命令行参数详解
|
||||
|
||||
### 完整语法
|
||||
|
||||
```
|
||||
transcode_music.py [SOURCE] [TARGET] [选项...]
|
||||
```
|
||||
|
||||
或
|
||||
|
||||
```
|
||||
transcode_music.py --source SOURCE --target TARGET [选项...]
|
||||
```
|
||||
|
||||
### 参数列表
|
||||
|
||||
| 参数 | 短选项 | 类型 | 是否必需 | 默认 | 说明 |
|
||||
|---|---|---|---|---|---|
|
||||
| `SOURCE` | — | 位置参数 | 必需¹ | — | 源目录,无损音乐所在位置 |
|
||||
| `TARGET` | — | 位置参数 | 必需¹ | — | 目标目录,MP3 输出位置 |
|
||||
| `--source` | `-s` | 字符串 | 必需¹ | — | 源目录(与位置参数 SOURCE 等价) |
|
||||
| `--target` | `-t` | 字符串 | 必需¹ | — | 目标目录(与位置参数 TARGET 等价) |
|
||||
| `--bitrate` | `-b` | 字符串 | 可选 | `320k` | MP3 CBR 比特率。可选 `320k` / `256k` / `192k` / `128k` 等 |
|
||||
| `--analyze-only` | — | 开关 | 可选 | 关 | 只分析生成 manifest,不实际转码 |
|
||||
| `--force` | — | 开关 | 可选 | 关 | 忽略现有 manifest,全部重新分析并转码 |
|
||||
| `--verbose` | `-v` | 开关 | 可选 | 关 | 显示 debug 级别日志(包括跳过的项) |
|
||||
| `--ffmpeg` | — | 字符串 | 可选 | `ffmpeg` | ffmpeg 可执行文件路径(默认从 PATH 查找) |
|
||||
| `--ffprobe` | — | 字符串 | 可选 | `ffprobe` | ffprobe 可执行文件路径 |
|
||||
| `--timeout` | — | 整数 | 可选 | `3600` | 单文件转码超时秒数(超时算失败) |
|
||||
| `--sample-rate-when-high` | — | 整数 | 可选 | `44100` | 输入采样率 > 48kHz 时的输出采样率,可选 `44100` / `48000` |
|
||||
| `--help` | `-h` | 开关 | — | — | 显示帮助并退出 |
|
||||
|
||||
¹ **源目录 和 目标目录 必须提供,但可以用位置参数或 flag 两种形式之一**:
|
||||
|
||||
```bash
|
||||
# 位置参数 (更简洁, 推荐)
|
||||
transcode_music.py /mnt/z/music /mnt/x/mp3
|
||||
|
||||
# flag 长形式
|
||||
transcode_music.py --source /mnt/z/music --target /mnt/x/mp3
|
||||
|
||||
# flag 短形式
|
||||
transcode_music.py -s /mnt/z/music -t /mnt/x/mp3
|
||||
|
||||
# 混合也可以
|
||||
transcode_music.py /mnt/z/music -t /mnt/x/mp3
|
||||
```
|
||||
|
||||
### 参数详细说明
|
||||
|
||||
#### `SOURCE` / `--source` / `-s`
|
||||
|
||||
**源目录**。脚本会递归扫描此目录下所有子目录,对每个包含无损音频的子目录识别为一个"专辑单元"处理。
|
||||
|
||||
- 支持含空格、中文、特殊字符的路径
|
||||
- 网盘挂载(`/mnt/z/`、`/mnt/x/` 等)都可以
|
||||
- Manifest 文件 `.transcode_manifest.json` 会写入此目录
|
||||
|
||||
#### `TARGET` / `--target` / `-t`
|
||||
|
||||
**目标目录**。MP3 输出位置。目录结构会与源目录保持一致。
|
||||
|
||||
- 目录会自动创建(含中间层)
|
||||
- 已存在的目标文件如果 manifest 记录 `completed` 会自动跳过
|
||||
|
||||
#### `--bitrate` / `-b`
|
||||
|
||||
MP3 输出比特率(CBR,Constant Bit Rate)。默认 `320k`。
|
||||
|
||||
**推荐取值**:
|
||||
|
||||
| 值 | 说明 |
|
||||
|---|---|
|
||||
| `320k` | 最高质量,几乎无损感知,推荐(默认) |
|
||||
| `256k` | 高质量,体积减小约 20% |
|
||||
| `192k` | 良好质量,体积减小约 40% |
|
||||
| `128k` | 一般质量,仅在存储极度紧张时使用 |
|
||||
|
||||
**注意**: 一旦确定比特率跑过一次,配置写入 manifest。**中途改比特率**不会导致已完成的文件重新转码(脚本以文件 completed 状态为准)。想改需配合 `--force`。
|
||||
|
||||
#### `--analyze-only`
|
||||
|
||||
**只分析,不转码**。用途:
|
||||
|
||||
- 首次跑一个新的大目录时,先看看总专辑数、CUE 整轨的有没有
|
||||
- 探测有没有解析错误的文件
|
||||
- 生成 manifest 后可以手动查看/编辑
|
||||
|
||||
**推荐**每次对新目录都先跑一次 `--analyze-only`。
|
||||
|
||||
#### `--force`
|
||||
|
||||
**强制重跑**。忽略源目录现有的 manifest,重新分析并转码所有文件。
|
||||
|
||||
**什么时候用**:
|
||||
- 换了比特率想全部重新转
|
||||
- 怀疑之前的输出有问题
|
||||
- 想清理并重新开始
|
||||
|
||||
**警告**:会覆盖已存在的 MP3。之前的 manifest 不会删除但会被覆盖。
|
||||
|
||||
#### `--verbose` / `-v`
|
||||
|
||||
打开 DEBUG 级别日志。默认 INFO 级别只显示正在处理和错误。加上 `-v` 会额外显示:
|
||||
|
||||
- `[SKIP (已完成): ...]` 每个跳过的专辑
|
||||
- `[SKIP (空目录): ...]` 跳过的空目录
|
||||
- ffprobe/ffmpeg 的调试信息
|
||||
|
||||
大量输出,通常只在排查问题时用。
|
||||
|
||||
#### `--ffmpeg` / `--ffprobe`
|
||||
|
||||
指定 ffmpeg / ffprobe 的路径。默认从 `PATH` 查找。
|
||||
|
||||
**什么时候用**:
|
||||
- 系统装了多个 ffmpeg 版本,想指定用哪个
|
||||
- ffmpeg 不在标准 PATH 里
|
||||
|
||||
示例:
|
||||
|
||||
```bash
|
||||
transcode_music.py /src /dst --ffmpeg /opt/ffmpeg-6.1/bin/ffmpeg
|
||||
```
|
||||
|
||||
#### `--timeout`
|
||||
|
||||
单个文件转码的超时时间(秒),默认 3600(1 小时)。超时算失败,写入 manifest 后继续处理下一个。
|
||||
|
||||
**通常不需要调整**。除非你有超长的音频文件(比如整轨 3 小时的 wav)在慢网盘上转码,可以调大。
|
||||
|
||||
#### `--sample-rate-when-high`
|
||||
|
||||
高采样率输入(> 48kHz)降到多少 kHz。默认 `44100`(CD 标准)。
|
||||
|
||||
- `44100`:CD 标准,最兼容
|
||||
- `48000`:视频/DVD 标准
|
||||
|
||||
对于 24bit/96kHz、24bit/192kHz、DSD(352.8k / 705.6k)等高采样率源,MP3 编码器不支持 > 48kHz,必须降采样。默认 44.1kHz 是 CD 标准,兼容性最好。
|
||||
|
||||
如果你的音乐主要来源是从视频/电影音轨提取的,用 48000 可能更保留原有采样率。
|
||||
|
||||
---
|
||||
|
||||
## 5. 常用使用场景
|
||||
|
||||
### 场景 1: 第一次处理新目录(推荐流程)
|
||||
|
||||
假设你刚下载了一大堆音乐到 `/mnt/z/新收藏/`,想转到 `/mnt/x/music/新收藏/`。
|
||||
|
||||
**第一步:先分析**
|
||||
|
||||
```bash
|
||||
python3 transcode_music.py /mnt/z/新收藏 /mnt/x/music/新收藏 --analyze-only
|
||||
```
|
||||
|
||||
输出会告诉你:
|
||||
- 总共多少个专辑
|
||||
- 多少个音频文件、多少资源
|
||||
- **有没有需要 CUE split 的专辑(`needs_cue_split`)**
|
||||
|
||||
**第二步:处理 needs_cue_split 的专辑**(如果有)
|
||||
|
||||
对于报警的整轨专辑,你需要用别的工具(比如 `shntool`、`CUETools`、`XLD`)手动 split 成分轨,然后再回来跑脚本。
|
||||
|
||||
**第三步:正式转码**
|
||||
|
||||
```bash
|
||||
python3 transcode_music.py /mnt/z/新收藏 /mnt/x/music/新收藏
|
||||
```
|
||||
|
||||
已经 `analyzed` 的专辑会开始转码,`needs_cue_split` 的会自动跳过(不影响其他)。
|
||||
|
||||
### 场景 2: 中断后恢复
|
||||
|
||||
假设转到一半按 Ctrl+C 中断了(或者断电、脚本崩溃了)。
|
||||
|
||||
只需要**再跑同样的命令**:
|
||||
|
||||
```bash
|
||||
python3 transcode_music.py /mnt/z/新收藏 /mnt/x/music/新收藏
|
||||
```
|
||||
|
||||
脚本会读取 manifest,跳过所有 `completed` 状态的文件和专辑,从上次中断的地方继续。**不需要任何特殊参数**。
|
||||
|
||||
### 场景 3: 增量同步(新增了专辑)
|
||||
|
||||
以后你又下载了几个新专辑到 `/mnt/z/新收藏/`,直接跑:
|
||||
|
||||
```bash
|
||||
python3 transcode_music.py /mnt/z/新收藏 /mnt/x/music/新收藏
|
||||
```
|
||||
|
||||
已完成的专辑秒跳过,只有新目录会被分析和转码。这是最舒服的用法。
|
||||
|
||||
### 场景 4: 修复失败的文件
|
||||
|
||||
如果某个专辑因为源文件损坏等原因失败了(`status: failed`),你修复源文件后:
|
||||
|
||||
```bash
|
||||
# 直接再跑一次, failed 的会被自动重试
|
||||
python3 transcode_music.py /mnt/z/新收藏 /mnt/x/music/新收藏
|
||||
```
|
||||
|
||||
已成功的不会重跑,只有失败的会重试。
|
||||
|
||||
### 场景 5: 换比特率重新转码
|
||||
|
||||
想把之前的 320k 全部改成 256k:
|
||||
|
||||
```bash
|
||||
# 加 --force 强制重跑
|
||||
python3 transcode_music.py /mnt/z/新收藏 /mnt/x/music/新收藏 -b 256k --force
|
||||
```
|
||||
|
||||
### 场景 6: 只转一小部分做质量测试
|
||||
|
||||
比如你想先转一个专辑试听下音质,别一下转几百 GB。
|
||||
|
||||
**方法一**:直接指定那个专辑的父目录
|
||||
|
||||
```bash
|
||||
# 只处理 "李宗盛精选" 这一个专辑
|
||||
python3 transcode_music.py "/mnt/z/新收藏/李宗盛精选" /mnt/x/music/李宗盛精选
|
||||
```
|
||||
|
||||
**方法二**:跑 `--analyze-only` 看总数,觉得数量能接受再跑正式的。
|
||||
|
||||
### 场景 7: 只想复制资源不想转码
|
||||
|
||||
不太常见。可以用位置参数让源和目标一致(虽然实际上 target 是别的,但 --analyze-only 后手动处理):
|
||||
|
||||
```bash
|
||||
# 只分析生成 manifest, 不转码
|
||||
python3 transcode_music.py /mnt/z/music /mnt/x/mp3 --analyze-only
|
||||
```
|
||||
|
||||
然后手动编辑 manifest 把所有音频状态改成 `skipped` 再跑。**不推荐**,通常你就是想转码。
|
||||
|
||||
### 场景 8: 排查某个专辑的问题
|
||||
|
||||
某个专辑失败了或者行为奇怪,用 verbose 模式跑:
|
||||
|
||||
```bash
|
||||
python3 transcode_music.py "/mnt/z/新收藏/有问题的专辑" /mnt/x/music/test -v
|
||||
```
|
||||
|
||||
输出会更详细,包括 ffprobe 探测细节、跳过原因等。
|
||||
|
||||
### 场景 9: 用不同 ffmpeg 版本
|
||||
|
||||
```bash
|
||||
python3 transcode_music.py /mnt/z/music /mnt/x/mp3 \
|
||||
--ffmpeg /opt/ffmpeg-7.0/bin/ffmpeg \
|
||||
--ffprobe /opt/ffmpeg-7.0/bin/ffprobe
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. Manifest 文件说明
|
||||
|
||||
### 位置
|
||||
|
||||
Manifest 保存在**源目录**下,文件名 `.transcode_manifest.json`(隐藏文件,前缀有点)。
|
||||
|
||||
### 顶层结构
|
||||
|
||||
```json
|
||||
{
|
||||
"manifest_version": 1,
|
||||
"created_at": "2026-08-16T15:05:03+08:00",
|
||||
"updated_at": "2026-08-16T15:19:06+08:00",
|
||||
"source_root": "/mnt/z/test",
|
||||
"target_root": "/mnt/x/music/test",
|
||||
"config": {
|
||||
"bitrate": "320k",
|
||||
"id3v2_version": "3",
|
||||
"output_sample_rate_when_high": 44100
|
||||
},
|
||||
"stats": {
|
||||
"total_albums": 5,
|
||||
"by_album_status": {"completed": 5},
|
||||
"by_audio_status": {"completed": 50},
|
||||
"by_asset_status": {"completed": 7},
|
||||
"total_audio_files": 50,
|
||||
"total_asset_files": 7
|
||||
},
|
||||
"albums": {
|
||||
"专辑相对路径1": { ... },
|
||||
"专辑相对路径2": { ... }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 单个专辑 (album) 结构
|
||||
|
||||
```json
|
||||
{
|
||||
"status": "completed",
|
||||
"issues": [],
|
||||
"audio_files": {
|
||||
"01 - 北风.flac": {
|
||||
"type": "flac",
|
||||
"size": 29671831,
|
||||
"sample_rate": 44100,
|
||||
"channels": 2,
|
||||
"duration_sec": 297.36,
|
||||
"bits_per_sample": 16,
|
||||
"codec": "flac",
|
||||
"status": "completed",
|
||||
"target_rel": "01 - 北风.mp3",
|
||||
"error": null,
|
||||
"converted_at": "2026-08-16T15:18:04+08:00"
|
||||
}
|
||||
},
|
||||
"asset_files": {
|
||||
"cover.jpg": {
|
||||
"type": "image",
|
||||
"size": 234567,
|
||||
"status": "completed",
|
||||
"target_rel": "cover.jpg",
|
||||
"error": null,
|
||||
"copied_at": "2026-08-16T15:18:05+08:00"
|
||||
}
|
||||
},
|
||||
"ignored_files": ["Thumbs.db"],
|
||||
"has_audio": true,
|
||||
"started_at": "2026-08-16T15:18:04+08:00",
|
||||
"completed_at": "2026-08-16T15:18:06+08:00",
|
||||
"error": null
|
||||
}
|
||||
```
|
||||
|
||||
### 状态值 (status)
|
||||
|
||||
**专辑级状态**:
|
||||
|
||||
| status | 说明 | 下次运行时行为 |
|
||||
|---|---|---|
|
||||
| `pending` | 初始状态(一般用不到) | 会重新分析 |
|
||||
| `analyzed` | 已分析,等待转码 | 继续转码 |
|
||||
| `processing` | 正在处理(若看到,说明上次中断了) | 会重新处理未完成部分 |
|
||||
| `needs_cue_split` | 是 CUE + 整轨大文件,需先手动 split | **跳过**,不处理 |
|
||||
| `completed` | 所有文件都成功了 | **秒跳过** |
|
||||
| `failed` | 有文件失败 | 会重试失败的部分 |
|
||||
| `skipped` | 空目录,无音频且无资源 | 跳过 |
|
||||
|
||||
**文件级状态**(audio_files 和 asset_files 里):
|
||||
|
||||
| status | 说明 |
|
||||
|---|---|
|
||||
| `pending` | 待处理 |
|
||||
| `completed` | 已完成 |
|
||||
| `failed` | 失败(`error` 字段有原因) |
|
||||
| `skipped` | 主动跳过(如 CUE 引用的整轨已有分轨) |
|
||||
|
||||
### 手动干预 manifest
|
||||
|
||||
Manifest 是纯 JSON,你可以用文本编辑器直接改:
|
||||
|
||||
**例子 1:让某个专辑重新处理**
|
||||
```json
|
||||
"status": "completed" → "status": "analyzed"
|
||||
```
|
||||
把该专辑内的音频文件也改成:
|
||||
```json
|
||||
"status": "completed" → "status": "pending"
|
||||
```
|
||||
|
||||
**例子 2:跳过某个损坏的文件**
|
||||
```json
|
||||
"01 - 有问题.flac": {
|
||||
...
|
||||
"status": "skipped",
|
||||
"error": "手动跳过,源文件有损坏"
|
||||
}
|
||||
```
|
||||
|
||||
**例子 3:删除某个专辑的记录,让它从头分析**
|
||||
删除对应的 album 条目,下次运行会自动重新扫描添加。
|
||||
|
||||
**改完保存直接跑**:
|
||||
|
||||
```bash
|
||||
python3 transcode_music.py /mnt/z/music /mnt/x/mp3
|
||||
```
|
||||
|
||||
### Manifest 损坏怎么办?
|
||||
|
||||
如果 JSON 损坏(编辑错了、盘故障),脚本启动时会自动检测,将坏 manifest 重命名为 `.transcode_manifest.json.corrupt-<时间戳>`,然后**重新生成新的 manifest**。已完成的 MP3 文件不会丢,但脚本会重新扫描所有文件(对已有 MP3 的处理由目标文件是否存在来判断,见"文件级幂等")。
|
||||
|
||||
**手动恢复方法**:如果损坏的 manifest 里有大量已完成状态,可以试着修复 JSON 语法后重命名回去。
|
||||
|
||||
---
|
||||
|
||||
## 7. 文件分类规则
|
||||
|
||||
脚本会根据扩展名把文件分到几类:
|
||||
|
||||
### 无损音频(会转码为 MP3)
|
||||
|
||||
`.flac` `.wav` `.dsf` `.dff` `.ape`
|
||||
|
||||
### 有损音频(当作资源直接复制,不转码)
|
||||
|
||||
`.mp3` `.m4a` `.aac` `.ogg` `.opus` `.wma` `.mp2`
|
||||
|
||||
**为什么不再压?** 因为它们已经是有损,再压一次损失更多。
|
||||
|
||||
### 图片(复制)
|
||||
|
||||
`.jpg` `.jpeg` `.png` `.bmp` `.gif` `.webp` `.tif` `.tiff`
|
||||
|
||||
### 文档(复制)
|
||||
|
||||
`.txt` `.log` `.pdf` `.md` `.nfo` `.doc` `.docx` `.rtf`
|
||||
|
||||
### CUE(复制)
|
||||
|
||||
`.cue`
|
||||
|
||||
### 忽略的文件(不处理)
|
||||
|
||||
- `.ds_store`、`thumbs.db`、`desktop.ini`、`.directory`(系统文件)
|
||||
- 以 `._` 开头的(macOS resource fork)
|
||||
- `.transcode_manifest.json` 及其 tmp 中间文件
|
||||
|
||||
### 其他扩展名
|
||||
|
||||
未列出的扩展名会被当作"other"资源直接复制,不会丢。
|
||||
|
||||
---
|
||||
|
||||
## 8. CUE 整轨检测逻辑
|
||||
|
||||
CUE 文件常见有三种搭配:
|
||||
|
||||
### 情况 A:CUE + 单个整轨大文件(需先 split)
|
||||
|
||||
```
|
||||
album.cue (指向 album.flac, 里面 12 个 TRACK 条目)
|
||||
album.flac (整张 CD 打包成一个大文件)
|
||||
```
|
||||
|
||||
**处理**:脚本标记专辑状态为 `needs_cue_split`,跳过转码,在报告里警告:
|
||||
|
||||
```
|
||||
[!] 1 个专辑需先手动 CUE split (已跳过转码):
|
||||
• album名
|
||||
album.cue: 整轨型 (单文件 'album.flac', 12 轨) — 需先手动 CUE split 再转码
|
||||
```
|
||||
|
||||
**处理方式**:用其他工具(`shntool split`、`CUETools`、`XLD`、`foobar2000`)split 成分轨 flac 后,删除大整轨文件(或保留但用 CUE 引用改指向分轨),然后再跑脚本。
|
||||
|
||||
### 情况 B:CUE + 分轨文件(已 split,直接转码)
|
||||
|
||||
```
|
||||
album.cue (指向 track01.flac, track02.flac...)
|
||||
track01.flac
|
||||
track02.flac
|
||||
...
|
||||
```
|
||||
|
||||
**处理**:脚本正常转码所有分轨 flac,CUE 文件也作为资源复制过去。
|
||||
|
||||
### 情况 C:CUE 引用整轨 + 但同时存在分轨(跳过冗余)
|
||||
|
||||
```
|
||||
CDImage.cue (指向 CDImage.flac)
|
||||
CDImage.flac (整轨大文件, 冗余)
|
||||
(01) 曲名.flac (已经手动 split 出来的分轨)
|
||||
(02) 曲名.flac
|
||||
...
|
||||
```
|
||||
|
||||
**处理**:脚本自动跳过 `CDImage.flac`(避免重复转码),只转分轨,同时复制 CUE。报告里会说明:
|
||||
|
||||
```
|
||||
CDImage.cue: 引用 'CDImage.flac' 但存在 20 个分轨 → 跳过 'CDImage.flac' 只转分轨
|
||||
```
|
||||
|
||||
### 检测算法(技术细节)
|
||||
|
||||
对每个 CUE 文件:
|
||||
1. 解析出所有 `FILE "xxx" WAVE` 条目(可能有多个)
|
||||
2. 解析出所有 `TRACK NN AUDIO` 条目
|
||||
3. 判断:
|
||||
- 若 CUE 只引用 1 个文件,且这个文件是目录里唯一的音频 → 情况 A(整轨)
|
||||
- 若 CUE 只引用 1 个文件,但目录里还有其他分轨音频 → 情况 C(跳过整轨)
|
||||
- 若 CUE 引用多个文件(>= 2)→ 情况 B(分轨)
|
||||
|
||||
CUE 文件的编码会尝试多种(`utf-8-sig` / `utf-8` / `gbk` / `big5` / `shift_jis` / `cp936` / `latin1`),中日韩音源基本都能识别。
|
||||
|
||||
---
|
||||
|
||||
## 9. 采样率与 DSD 处理
|
||||
|
||||
### MP3 采样率约束
|
||||
|
||||
MP3 规范只允许这些采样率:8 / 11.025 / 12 / 16 / 22.05 / 24 / 32 / 44.1 / 48 kHz。
|
||||
|
||||
超过 48kHz 的输入**必须重采样**,脚本自动处理。
|
||||
|
||||
### 具体规则
|
||||
|
||||
| 输入采样率 | 输出采样率 | 备注 |
|
||||
|---|---|---|
|
||||
| 44100 Hz | 44100 Hz | 透传,CD 标准 |
|
||||
| 48000 Hz | 48000 Hz | 透传 |
|
||||
| 32000 Hz 及其他 MP3 支持值 | 保持不变 | 透传 |
|
||||
| 88200 / 96000 Hz | 44100 Hz | 降采样(默认,可通过 `--sample-rate-when-high` 改为 48000) |
|
||||
| 176400 / 192000 Hz | 44100 Hz | 降采样 |
|
||||
| 352800 Hz (DSD64 解码后) | 44100 Hz | 精确 8:1 降采样(DSD64 特性)|
|
||||
| 705600 Hz (DSD128 解码后) | 44100 Hz | 精确 16:1 降采样 |
|
||||
|
||||
### DSD (DSF / DFF) 处理
|
||||
|
||||
DSD 是 1-bit 高频(2.8 或 5.6 MHz)采样格式,与传统 PCM 不同。脚本流程:
|
||||
|
||||
1. **ffprobe** 探测:报告 `codec_name=dsd_lsbf_planar` 或 `dsd_msbf_planar`,`sample_rate=352800` 或 `705600`
|
||||
2. **ffmpeg 解码**:DSD → 高采样率 PCM(自带低通滤波器,96 tap,通带平坦到 48kHz,阻带抑制 ~160dB)
|
||||
3. **重采样**:降到 44100 Hz
|
||||
4. **编码**:libmp3lame 320k CBR
|
||||
|
||||
DSD 内嵌的封面(通常是 mjpeg)会被识别为视频流并保留到 MP3 的 ID3v2 tag。
|
||||
|
||||
### 位深处理
|
||||
|
||||
- 16-bit 输入:直接编码
|
||||
- 24-bit 输入:libmp3lame 内部用 32-bit float 处理,等效于 24-bit 精度参与编码
|
||||
- DSD (1-bit):解码为 24-bit PCM 后编码
|
||||
|
||||
---
|
||||
|
||||
## 10. 错误处理与恢复
|
||||
|
||||
### 错误捕获原则
|
||||
|
||||
**单个文件失败不影响其他**。转码或复制某个文件出错时:
|
||||
|
||||
1. 该文件状态设为 `failed`,错误消息写入 `error` 字段
|
||||
2. 该专辑状态设为 `failed`
|
||||
3. **继续处理**下一个文件、下一个专辑
|
||||
4. 最后报告里列出所有失败项
|
||||
|
||||
### 再次运行时
|
||||
|
||||
`failed` 状态的文件会**自动重试**。已经 `completed` 的不会重跑。
|
||||
|
||||
```bash
|
||||
# 修好源文件后, 直接再跑
|
||||
python3 transcode_music.py /mnt/z/music /mnt/x/mp3
|
||||
```
|
||||
|
||||
### 常见错误
|
||||
|
||||
**"Error opening output files: Invalid argument"**
|
||||
|
||||
通常是 ffmpeg 无法识别输出格式或权限问题。脚本内部已加 `-f mp3` 强制指定 muxer 避免此问题。若仍出现:
|
||||
- 检查目标目录是否可写
|
||||
- 检查文件名是否有极端特殊字符
|
||||
|
||||
**"Operation not permitted"(复制时)**
|
||||
|
||||
网盘挂载(9p / SMB / NFS)不允许改文件权限。脚本已经改用 `copyfile` + best-effort `utime`,通常应该没问题。若出现:
|
||||
- 检查目标目录挂载参数
|
||||
- 尝试 `mount -o rw,noatime ...` 之类的选项
|
||||
|
||||
**ffprobe 失败 / 无法探测采样率**
|
||||
|
||||
源文件可能损坏。检查 `manifest.audio_files.<文件>.error` 里的具体消息。
|
||||
|
||||
**ffmpeg 超时**
|
||||
|
||||
默认 3600 秒。若真有超大文件慢网盘的组合,用 `--timeout 7200` 调大。
|
||||
|
||||
### 中断(Ctrl+C)
|
||||
|
||||
脚本捕获 SIGINT,会保存 manifest 后退出。**中断后当前正在转的那个文件的 `.mp3.tmp` 中间文件会被清理**,不会留下坏的 MP3。
|
||||
|
||||
### 用什么方法查看失败列表
|
||||
|
||||
```bash
|
||||
# 用 jq 快速查看失败的专辑
|
||||
jq -r '.albums | to_entries[] | select(.value.status=="failed") | .key' \
|
||||
/mnt/z/music/.transcode_manifest.json
|
||||
|
||||
# 查看某个专辑里失败的文件
|
||||
jq '.albums["专辑名"].audio_files | with_entries(select(.value.status=="failed"))' \
|
||||
/mnt/z/music/.transcode_manifest.json
|
||||
```
|
||||
|
||||
### 关于 needs_cue_split
|
||||
|
||||
`needs_cue_split` 状态的专辑**不算失败**,是主动跳过等你手动处理。若不需要转这些,可以:
|
||||
|
||||
**选项 1:修 manifest 忽略它们**
|
||||
|
||||
把状态改成 `skipped`:
|
||||
|
||||
```json
|
||||
"status": "needs_cue_split" → "status": "skipped"
|
||||
```
|
||||
|
||||
**选项 2:手动 CUE split**
|
||||
|
||||
用其他工具 split 成分轨,然后:
|
||||
|
||||
```bash
|
||||
# 让脚本重新分析
|
||||
rm /mnt/z/music/.transcode_manifest.json
|
||||
python3 transcode_music.py /mnt/z/music /mnt/x/mp3 --analyze-only
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 11. 已知局限
|
||||
|
||||
- **不支持并行转码**。是单进程顺序处理。CPU 密集型(LAME 编码)加网盘 IO 是主要瓶颈。多进程可以显著加速,但为保证 manifest 状态一致性没有实现。若性能是问题,可以对不同子目录同时跑多个脚本实例。
|
||||
- **不支持 VBR MP3**。只做 CBR。原因:CBR 兼容性更好,且 320k CBR 与 V0 VBR 音质差异对绝大多数人不可闻。
|
||||
- **不做 replaygain / ID3v1 补齐 / 章节标记**。只做基本的 ID3v2.3 元数据映射。
|
||||
- **CUE split 不会自动做**。检测出整轨专辑后只是报警跳过,不代替你调用外部工具去 split。设计如此以避免不可控的副作用。
|
||||
- **不支持 ALAC (.m4a) 转 MP3**。因为 `.m4a` 容器里既可能是 ALAC 无损也可能是 AAC 有损,无法从扩展名区分。这些文件会被当作有损直接复制。若你有确定是 ALAC 的 `.m4a`,需要先用 ffmpeg 手动转成 flac。
|
||||
- **网盘 mtime 不能保留**。9p / SMB 挂载不允许改 mtime,所以复制后的资源文件时间戳是当下时间(内容完全一致)。转码后的 MP3 也是同样情况。
|
||||
- **不做重复检测**。若源目录有两个不同路径下的同名同内容文件,会各自转码两份。
|
||||
|
||||
---
|
||||
|
||||
## 12. FAQ
|
||||
|
||||
**Q: 我可以中途改比特率吗?**
|
||||
|
||||
A: 已经 `completed` 的文件不会重转,即使你改了 `-b`。想全部按新比特率重跑就加 `--force`。
|
||||
|
||||
**Q: 手机不支持 320k 会怎样?**
|
||||
|
||||
A: 所有正常的 MP3 播放器都支持 320k CBR,几十年的老设备也没问题。
|
||||
|
||||
**Q: 转码后 MP3 音质怎么样?**
|
||||
|
||||
A: LAME 320k CBR 是"透明比特率"级别,即绝大多数听者在双盲测试中无法区分 320k MP3 和原始 FLAC。除非你有非常好的耳机和听音训练,几乎听不出差别。
|
||||
|
||||
**Q: DSD 转 MP3 有没有意义?**
|
||||
|
||||
A: DSD 主要意义在于极高频响应(超过人耳范围)和低失真。转成 MP3 后这些优势基本没了。但 MP3 320k 仍能保留 DSD 音源的音乐性和大部分动态。若你就是想要个手机能播的版本,DSD → MP3 是合理的。想要"无损感"应该保留 FLAC。
|
||||
|
||||
**Q: 支持 Windows 直接跑吗?**
|
||||
|
||||
A: 脚本用了 POSIX 的 `os.fsync(dir_fd)` 做原子写,Windows 上会 best-effort(fsync dir 会 fail 但不影响正确性)。理论上可以跑,但没实测。**推荐 Linux / WSL2 / macOS**。
|
||||
|
||||
**Q: 网盘断连了怎么办?**
|
||||
|
||||
A: 单文件失败会记录到 manifest 并继续。重新连上后再跑一次,失败的会自动重试。
|
||||
|
||||
**Q: 生成的 manifest 会不会很大?**
|
||||
|
||||
A: 一般不大。50 个专辑(我的测试目录)的 manifest 约 26 KB。上千个专辑估计几 MB。不影响使用。
|
||||
|
||||
**Q: 我想只转某几个专辑怎么办?**
|
||||
|
||||
A: 最简单的办法是让 SOURCE 指向那个专辑的父目录:
|
||||
|
||||
```bash
|
||||
python3 transcode_music.py "/mnt/z/music/张学友精选" /mnt/x/mp3/张学友精选
|
||||
```
|
||||
|
||||
或者临时把想转的目录复制/软链接到一个专用目录,跑完再删。
|
||||
|
||||
**Q: 能不能顺便做元数据整理(比如加艺人、专辑年份)?**
|
||||
|
||||
A: 目前不做。脚本只做 `-map_metadata 0` 把源文件的 tag 原样映射过去。想批量整理元数据推荐 `beets`、`Picard`。
|
||||
|
||||
**Q: 转码完想验证质量怎么办?**
|
||||
|
||||
A: 抽几首用 `ffprobe` 看输出的 `bit_rate` 应该是 320000 左右,`sample_rate` 是 44100 或 48000。
|
||||
|
||||
```bash
|
||||
ffprobe -v error -show_entries "stream=codec_name,sample_rate,bit_rate" \
|
||||
-show_entries "format=bit_rate,duration" \
|
||||
"/mnt/x/mp3/xxx.mp3"
|
||||
```
|
||||
|
||||
或用播放器听一下确认。
|
||||
|
||||
---
|
||||
|
||||
## 附录 A:完整命令示例集
|
||||
|
||||
```bash
|
||||
# 分析(不转码)
|
||||
python3 transcode_music.py /mnt/z/music /mnt/x/mp3 --analyze-only
|
||||
|
||||
# 正式转码(自动 resume)
|
||||
python3 transcode_music.py /mnt/z/music /mnt/x/mp3
|
||||
|
||||
# 详细日志
|
||||
python3 transcode_music.py /mnt/z/music /mnt/x/mp3 -v
|
||||
|
||||
# 使用 256k 比特率
|
||||
python3 transcode_music.py /mnt/z/music /mnt/x/mp3 -b 256k
|
||||
|
||||
# 强制重跑
|
||||
python3 transcode_music.py /mnt/z/music /mnt/x/mp3 --force
|
||||
|
||||
# 使用 48kHz 输出(视频 / DVD 场景)
|
||||
python3 transcode_music.py /mnt/z/music /mnt/x/mp3 --sample-rate-when-high 48000
|
||||
|
||||
# 使用长参数
|
||||
python3 transcode_music.py --source /mnt/z/music --target /mnt/x/mp3
|
||||
|
||||
# 使用短参数
|
||||
python3 transcode_music.py -s /mnt/z/music -t /mnt/x/mp3
|
||||
|
||||
# 组合:位置 + 选项
|
||||
python3 transcode_music.py /mnt/z/music /mnt/x/mp3 -b 256k -v --force
|
||||
|
||||
# 单独指定 ffmpeg 路径
|
||||
python3 transcode_music.py /mnt/z/music /mnt/x/mp3 \
|
||||
--ffmpeg /opt/ffmpeg-7/bin/ffmpeg \
|
||||
--ffprobe /opt/ffmpeg-7/bin/ffprobe
|
||||
|
||||
# 单文件超长的场景,加大超时
|
||||
python3 transcode_music.py /mnt/z/music /mnt/x/mp3 --timeout 7200
|
||||
```
|
||||
|
||||
## 附录 B:文件结构示例
|
||||
|
||||
**源目录**(真实的音乐盘结构示例):
|
||||
|
||||
```
|
||||
/mnt/z/新收藏/
|
||||
├── 张学友精选/
|
||||
│ ├── 01 - 吻别.flac ← 转成 mp3
|
||||
│ ├── 02 - 一路上有你.flac ← 转成 mp3
|
||||
│ ├── cover.jpg ← 复制
|
||||
│ └── 专辑说明.txt ← 复制
|
||||
├── 王菲 - 天空 (24bit-192kHz)/
|
||||
│ ├── 01. 天空.flac ← 24/192 → 降采样后转 mp3
|
||||
│ ├── 02. 誓言.flac
|
||||
│ └── booklet.jpg
|
||||
├── 蔡琴 2CD 精选/
|
||||
│ ├── back.jpg ← 顶层封面, 复制
|
||||
│ ├── cover.jpg
|
||||
│ ├── 目录.txt
|
||||
│ ├── CD1/ ← 独立处理
|
||||
│ │ ├── 01 恰似你的温柔.wav ← 转成 mp3
|
||||
│ │ └── 02 你的眼神.wav
|
||||
│ └── CD2/
|
||||
│ └── 01 出塞曲.wav
|
||||
├── DSD 专辑/
|
||||
│ └── 谭咏麟-一生中最爱.dsf ← DSD 解码后转 mp3
|
||||
└── 整轨专辑 [FLAC+CUE]/ ← ⚠ needs_cue_split
|
||||
├── CDImage.flac ← 整个 CD 一个大文件
|
||||
└── CDImage.cue ← CUE 索引
|
||||
```
|
||||
|
||||
**目标目录**(脚本处理后):
|
||||
|
||||
```
|
||||
/mnt/x/mp3/新收藏/
|
||||
├── 张学友精选/
|
||||
│ ├── 01 - 吻别.mp3 ← 320k CBR
|
||||
│ ├── 02 - 一路上有你.mp3
|
||||
│ ├── cover.jpg
|
||||
│ └── 专辑说明.txt
|
||||
├── 王菲 - 天空 (24bit-192kHz)/
|
||||
│ ├── 01. 天空.mp3 ← 44.1kHz 320k
|
||||
│ ├── 02. 誓言.mp3
|
||||
│ └── booklet.jpg
|
||||
├── 蔡琴 2CD 精选/
|
||||
│ ├── back.jpg
|
||||
│ ├── cover.jpg
|
||||
│ ├── 目录.txt
|
||||
│ ├── CD1/
|
||||
│ │ ├── 01 恰似你的温柔.mp3
|
||||
│ │ └── 02 你的眼神.mp3
|
||||
│ └── CD2/
|
||||
│ └── 01 出塞曲.mp3
|
||||
├── DSD 专辑/
|
||||
│ └── 谭咏麟-一生中最爱.mp3 ← 320k
|
||||
└── 整轨专辑 [FLAC+CUE]/ ← 空(因为 needs_cue_split 被跳过)
|
||||
```
|
||||
|
||||
**Manifest**(在源目录):
|
||||
|
||||
```
|
||||
/mnt/z/新收藏/.transcode_manifest.json
|
||||
```
|
||||
16
Project/transcode_music/项目需求.md
Normal file
16
Project/transcode_music/项目需求.md
Normal file
@@ -0,0 +1,16 @@
|
||||
这个项目主要是针对一个现有的目录,遍历这个目录下面所有的文件夹,找到相应的无损音乐文件。并把这些无损音乐文件转码成高质量的mp3(320K)并保存到另外一个目录下,保持目录和原始音乐的文件名不变。
|
||||
|
||||
现在的无损音乐文件的格式有 flac, wav, dsf 等。要求用python脚本来实现这个批量转换的过程。
|
||||
|
||||
1. 指定的目是我的一个网盘,并不是在我的 laptop 上面,我要通过这样的方式去访问 比如: /mnt/z/test/
|
||||
2. 转换好的 MP3 需要保存到另一个网盘目录下,然后保持目录结构以及文件名和原始文件相同,后缀名为 MP3。比如 /mnt/x/music/test
|
||||
|
||||
3. 整个转化的过程是可追溯的,也允许这个脚本可以运行多次。我需要记录每个专辑目录转换的一个状态,因此我需要一个 manifest 文件来记录状态。
|
||||
|
||||
4. 原始的目录里面包含了一些专辑的图片,以及一些 TXT 的说明文档。这个需要在转化过程当中一并复制到对应的新的目录里面。
|
||||
|
||||
5. 我已经在环境里面安装了 FFmpeg 这个工具。我希望在这个脚本里面可以针对转化的质量进行配置。
|
||||
6. 开始之前需要对指定的目录进行一次遍历,并进行文件的分析。分析一下这个目录下面所有的专辑目录下面的文件是否都是可转换的状态。如果发现有一些专辑文件,比如说专辑文件里面会包含一些还没有分割的原始文件,比如说 CUE + APE/WAV/FLAC 的这样的一个文件,这些文件是需要先进行 CUE 的 split。以后把专辑里面的音乐分割好以后再进行转换。这个需要先进行分析并提供一些提示。
|
||||
7. 在开始转换之前,需要先创建一个 manifest 文件,把指定目录下面的所有的文件进行一遍分析,并记录好状态。这个 manifest 需要是一个 JSON 格式的一个文件,保存到指定的这个原始目录里。
|
||||
8. 当中如果遇到一些意外的情况,需要能够跳过这个错误,能够正常地去执行后面的文件,然后把错误的信息记录到 manifest 这个 JSON 文件里面。这样的话,我可以后续再检查状态并重新执行这个脚本。
|
||||
9. 执行成功的专辑目录在再次执行脚本的过程当中就可以直接跳过。只针对有问题的进行调整后的专辑目录进行再次执行,而不需要重新再执行已经完成的操作。
|
||||
265
knowledgebase/WSL2 Ubuntu 中挂载 NAS 网盘完全指南.md
Normal file
265
knowledgebase/WSL2 Ubuntu 中挂载 NAS 网盘完全指南.md
Normal file
@@ -0,0 +1,265 @@
|
||||
#wsl #ubuntu #nas #mount
|
||||
## 背景
|
||||
|
||||
- **环境**: Windows 11 + WSL2 (Ubuntu 24)
|
||||
- **硬件**: Lenovo ThinkBook 16, Synology DS718 NAS
|
||||
- **目标**: 在 Ubuntu 中访问 NAS 上的 `/volume1/music` 共享文件夹
|
||||
- **Windows 路径**: `\\192.168.3.17\music`
|
||||
|
||||
## 踩过的坑
|
||||
|
||||
### 1. CIFS 方案失败 ❌
|
||||
|
||||
#### 尝试方法
|
||||
|
||||
```bash
|
||||
sudo apt install cifs-utils
|
||||
sudo mkdir -p /mnt/nas-music
|
||||
sudo mount -t cifs //192.168.3.17/music /mnt/nas-music -o username=shenwei,password=your_password
|
||||
```
|
||||
|
||||
#### 错误信息
|
||||
|
||||
```
|
||||
mount error(95): Operation not supported
|
||||
```
|
||||
|
||||
#### 原因分析
|
||||
|
||||
- 虽然内核支持 CIFS (`grep -i cifs /proc/filesystems` 有输出)
|
||||
- 但 WSL2 内核被配置为禁止旧 SMB 版本
|
||||
- dmesg 显示: `vers=2.0 mount not permitted when legacy dialects disabled`
|
||||
- Synology NAS 只支持 SMB 2.0 及更低版本,与 WSL2 不兼容
|
||||
|
||||
#### 相关错误日志
|
||||
|
||||
```
|
||||
CIFS: VFS: \\192.168.3.17 Dialect not supported by server
|
||||
CIFS: VFS: vers=2.0 mount not permitted when legacy dialects disabled
|
||||
```
|
||||
|
||||
### 2. SSHFS 方案问题 ⚠️
|
||||
|
||||
#### 尝试方法
|
||||
|
||||
```bash
|
||||
sudo apt install sshfs
|
||||
sudo sshfs shenwei@192.168.3.17:/volume1/music /mnt/nas-music -o allow_other
|
||||
```
|
||||
|
||||
#### 错误信息
|
||||
|
||||
```
|
||||
read: Connection reset by peer
|
||||
```
|
||||
|
||||
#### 原因
|
||||
|
||||
可能是 Synology SSH 配置的限制或 sshfs 参数兼容性问题
|
||||
|
||||
#### 建议
|
||||
|
||||
如果 SSH 能连接但 sshfs 有问题,可尝试:
|
||||
|
||||
- 用密钥认证而不是密码
|
||||
- 调整参数: `-o reconnect,ServerAliveInterval=15`
|
||||
- 但不如直接用 drvfs 稳定
|
||||
|
||||
---
|
||||
|
||||
## ✅ 最终成功方案:在 Windows 挂载后,WSL2 中访问
|
||||
|
||||
### 第 1 步:在 Windows 中挂载 NAS(已完成)
|
||||
|
||||
PowerShell (管理员):
|
||||
|
||||
```powershell
|
||||
net use Z: \\192.168.3.17\music /user:shenwei your_password
|
||||
```
|
||||
|
||||
验证:
|
||||
|
||||
```powershell
|
||||
dir Z:\
|
||||
```
|
||||
|
||||
### 第 2 步:在 WSL2 Ubuntu 中挂载 Windows 驱动器
|
||||
|
||||
创建挂载点:
|
||||
|
||||
```bash
|
||||
sudo mkdir -p /mnt/z
|
||||
```
|
||||
|
||||
挂载 Z: 驱动器:
|
||||
|
||||
```bash
|
||||
sudo mount -t drvfs Z: /mnt/z
|
||||
```
|
||||
|
||||
验证:
|
||||
|
||||
```bash
|
||||
ls /mnt/z/test
|
||||
```
|
||||
|
||||
输出示例:
|
||||
|
||||
```
|
||||
20260815
|
||||
@eaDir
|
||||
albums
|
||||
new
|
||||
test
|
||||
另外处理
|
||||
张国荣音乐合集1978-2007 140CD FLAC
|
||||
英文经典
|
||||
```
|
||||
|
||||
### 第 3 步:永久自动挂载(可选)
|
||||
|
||||
编辑 `/etc/fstab`:
|
||||
|
||||
```bash
|
||||
sudo nano /etc/fstab
|
||||
```
|
||||
|
||||
在末尾添加:
|
||||
|
||||
```
|
||||
Z: /mnt/z drvfs defaults 0 0
|
||||
```
|
||||
|
||||
保存 (Ctrl+O → Enter → Ctrl+X)
|
||||
|
||||
测试:
|
||||
|
||||
```bash
|
||||
sudo mount -a
|
||||
```
|
||||
|
||||
以后每次启动 WSL2,Z: 驱动器会自动挂载到 `/mnt/z`
|
||||
|
||||
---
|
||||
|
||||
## 使用示例
|
||||
|
||||
### 在 Python 中访问 NAS 文件
|
||||
|
||||
```python
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
# NAS 路径
|
||||
nas_music_path = Path("/mnt/z")
|
||||
|
||||
# 列出所有目录
|
||||
for item in nas_music_path.iterdir():
|
||||
print(item.name)
|
||||
|
||||
# 处理音乐文件
|
||||
for flac_file in nas_music_path.glob("**/*.flac"):
|
||||
print(f"处理: {flac_file}")
|
||||
```
|
||||
|
||||
### 在 Bash 中访问 NAS 文件
|
||||
|
||||
```bash
|
||||
# 列出文件
|
||||
ls /mnt/z/albums
|
||||
|
||||
# 复制文件
|
||||
cp /mnt/z/albums/artist/album.flac /home/user/music/
|
||||
|
||||
# 批量处理
|
||||
for file in /mnt/z/**/*.flac; do
|
||||
echo "处理: $file"
|
||||
done
|
||||
```
|
||||
|
||||
### 在 Docker 容器中使用
|
||||
|
||||
在 `docker-compose.yml` 中挂载:
|
||||
|
||||
```yaml
|
||||
volumes:
|
||||
- /mnt/z:/nas-music:ro
|
||||
```
|
||||
|
||||
容器内访问:
|
||||
|
||||
```bash
|
||||
ls /nas-music/albums
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 性能和稳定性对比
|
||||
|
||||
|方案|稳定性|性能|易用性|备注|
|
||||
|---|---|---|---|---|
|
||||
|CIFS|❌|-|-|WSL2 不支持低版本 SMB|
|
||||
|SSHFS|⚠️|中|中|可能有兼容性问题|
|
||||
|**drvfs**|✅|快|✅|**推荐方案**|
|
||||
|
||||
---
|
||||
|
||||
## 故障排查
|
||||
|
||||
### 问题 1: 挂载点不存在
|
||||
|
||||
```bash
|
||||
# 解决
|
||||
sudo mkdir -p /mnt/z
|
||||
```
|
||||
|
||||
### 问题 2: 权限不足
|
||||
|
||||
```bash
|
||||
# 加 sudo
|
||||
sudo mount -t drvfs Z: /mnt/z
|
||||
```
|
||||
|
||||
### 问题 3: Z: 驱动器在 Windows 中不可用
|
||||
|
||||
```powershell
|
||||
# Windows PowerShell (管理员) 中重新挂载
|
||||
net use Z: \\192.168.3.17\music /user:shenwei your_password
|
||||
```
|
||||
|
||||
### 问题 4: WSL2 中 Z: 驱动器挂载失败
|
||||
|
||||
```bash
|
||||
# 查看 dmesg 日志
|
||||
sudo dmesg | tail -20
|
||||
|
||||
# 尝试卸载后重新挂载
|
||||
sudo umount /mnt/z
|
||||
sudo mount -t drvfs Z: /mnt/z
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 总结
|
||||
|
||||
**最简单和最稳定的方案**:
|
||||
|
||||
1. **Windows 中**: `net use Z: \\192.168.3.17\music`
|
||||
2. **WSL2 中**: `sudo mount -t drvfs Z: /mnt/z`
|
||||
3. **Ubuntu 中访问**: `/mnt/z/test`
|
||||
|
||||
不需要配置复杂的 CIFS 或 SSHFS,直接利用 Windows 已有的网络共享,通过 WSL2 的 drvfs 驱动访问,**稳定、快速、可靠**。
|
||||
|
||||
---
|
||||
|
||||
## 相关文件路径
|
||||
|
||||
- Windows 网络路径: `\\192.168.3.17\music`
|
||||
- Windows 驱动器: `Z:\`
|
||||
- WSL2 挂载点: `/mnt/z`
|
||||
- 音乐文件实际位置: `/mnt/z/albums/...`
|
||||
|
||||
---
|
||||
|
||||
**更新时间**: 2026-08-16
|
||||
**验证环境**: WSL2 Ubuntu 24, Synology DS718
|
||||
Reference in New Issue
Block a user