DrGitStone/JellyMusicUploader - 安全仓库 · FancyPig · 猪猪安全
DrGitStone/JellyMusicUploader
GitHub: DrGitStone/JellyMusicUploader
这是一个 Jellyfin 插件,通过网页拖放上传音乐文件并自动管理标签和封面,简化音乐库维护。
Stars: 0 | Forks: 0
JellyMusicUploader
一个 Jellyfin 插件,可在网页界面中添加一个**添加音乐**按钮。只需将一个或多个 `Artist/Album/song` 文件夹拖入模态窗口,并可选择为每张专辑设置封面图片,插件即会将文件写入您的音乐媒体库文件夹并触发媒体库扫描。
**现在您终于可以从任何地方为音乐库添加音乐,无需再通过 Jellyfin 的后端操作!**
专为家庭服务器管理员设计——无需 SSH、无需 scp、无需手动导入。
只需将文件夹拖入浏览器即可。
请注意上传格式:`Artist 文件夹 > Album 名称文件夹 > Song 音轨文件`
您还可以在此过程中为专辑选择缩略图(可选),从而获得整洁的元数据体验!
## HTTP API
## 功能特点
- 兼容 **Jellyfin Media Player** 和 **Jellyfin Web UI**
- 从 Jellyfin 网页界面拖放文件夹上传
- 支持一次拖放多个 Artist 文件夹
- 在上传前验证并预览文件夹结构(artist / album / track 数量 / 总大小)
- 每张专辑的**封面图片选择器**——设置一次图片,它将成为该专辑及其内部所有曲目的专辑封面(通过 Jellyfin 标准的 `cover.` 约定)
- **基于文件夹结构的自动标记**——每个音频文件写入后,插件会根据文件夹路径写入 `Artist`、`AlbumArtist`、`Album` 和(若缺失)`Title` 标签。解决了上传文件时 ID3 标签为空或错误的常见问题,否则 Jellyfin 的基于标签的音乐扫描器会显示为“未知艺术家”
- 单文件进度、原子写入(`.part` 临时文件 → 重命名)
- 批量上传完成后自动触发 Jellyfin 媒体库扫描
- 仅限管理员——浮动按钮对非管理员不可见,上传端点会拒绝非管理员令牌
- 路径遍历防护、扩展名白名单、单文件大小限制
- 单个小型 DLL——无需额外运行时依赖
## 系统要求
- **Jellyfin 10.11.x**(基于 10.11.2 构建和测试)
- 在 Jellyfin 中配置一个音乐媒体库,指向一个 Jellyfin 进程可写的目录
- 如需从源代码构建:**.NET 9 SDK**
## 安装说明
### 1. 获取插件 DLL
**选项 A — 下载发行版**(当有可用版本时)
如果此仓库有带标签的发行版,请从 [Releases](../../releases) 页面下载 `JellyMusicUploader.zip` 并解压。您将找到 `JellyMusicUploader.dll`、`TagLibSharp.dll` 和 `meta.json`。
**选项 B — 从源代码构建**
```
git clone https://github.com//jellyfin-music-uploader.git
cd jellyfin-music-uploader
dotnet build -c Release
```
输出位于 `bin/Release/net9.0/`:
- `JellyMusicUploader.dll` —— 插件本体
- `TagLibSharp.dll` —— 标签规范化所需的运行时依赖
如果需要将文件交给他人,可将这两个文件加上 `meta.json` 打包成单个 zip:
```
( cd bin/Release/net9.0 && \
zip -j ../../../JellyMusicUploader.zip \
JellyMusicUploader.dll TagLibSharp.dll ../../../meta.json )
```
### 2. 将插件放入 Jellyfin 的 plugins 文件夹
Jellyfin 要求的文件夹布局是 `_/`。请创建该目录并将 `JellyMusicUploader.dll` + `meta.json` 放入其中。
**原生安装** (Debian/Ubuntu)
```
sudo mkdir -p /var/lib/jellyfin/plugins/JellyMusicUploader_0.1.0.0
sudo cp JellyMusicUploader.dll TagLibSharp.dll meta.json \
/var/lib/jellyfin/plugins/JellyMusicUploader_0.1.0.0/
sudo chown -R jellyfin:jellyfin /var/lib/jellyfin/plugins/JellyMusicUploader_0.1.0.0
sudo systemctl restart jellyfin
```
**Docker (`linuxserver/jellyfin` 及类似)**
插件文件夹位于 `/data/plugins/`。使用 `docker inspect ` 找到您主机侧的挂载点,并查看 `/config` 的绑定源。然后:
```
HOST_CONFIG=/path/to/jellyfin/config # the bind source for /config
mkdir -p "$HOST_CONFIG/data/plugins/JellyMusicUploader_0.1.0.0"
cp JellyMusicUploader.dll TagLibSharp.dll meta.json \
"$HOST_CONFIG/data/plugins/JellyMusicUploader_0.1.0.0/"
docker restart jellyfin
```
### 3. 配置插件
打开 **仪表板 → 插件 → Music Uploader** 并设置:
| 字段 | 设置内容 |
| --- | --- |
| **Library Path** | 您音乐媒体库的绝对服务器端路径。对于 Docker,这是**容器内部**的路径(例如 `/data/music`),而不是主机路径。该路径必须已存在且可被 Jellyfin 写入。 |
| **Max File Size (MB)** | 单个文件大小上限。`0` 表示禁用。默认 `200`。 |
| **Allowed Extensions** | 以逗号分隔、小写、不带前导点号。默认值涵盖了大多数有损和无损音频格式以及封面图片格式。 |
| **Trigger library scan after each batch** | 保持开启,除非有其他程序监视该文件夹。 |
| **Allow overwriting** | 如果关闭,重新上传相同路径将返回 HTTP 409。 |
点击 **保存**。
### 4. 将 JS 集成到 Jellyfin 网页客户端
这是唯一需要仔细操作的步骤。插件提供了一个 JS 文件,通过 `/musicup/frontend/js/uploader.js` 提供服务,但需要让 Jellyfin 加载它。
根据您的安装方式,有两条路径:
#### 4a. 简洁方式:使用自定义 CSS 字段(适用于某些安装)
打开 **仪表板 → 通用 → 品牌 → 自定义 CSS** 并粘贴:
```
```
保存。在浏览器中强制刷新(Cmd-Shift-R / Ctrl-Shift-R)。
#### 4b. 可靠方式:直接编辑 `index.html`
插件的 JS 只需要在提供的 `index.html` 文件中添加一个 `#' /usr/share/jellyfin/web/index.html
```
**Docker**
```
docker exec jellyfin sh -c \
'cp /usr/share/jellyfin/web/index.html /usr/share/jellyfin/web/index.html.bak-musicup && \
sed -i "s###" \
/usr/share/jellyfin/web/index.html'
```
无需重启——该文件在每次页面请求时读取。只需在浏览器中**强制刷新**即可。
## 使用方法
1. 以管理员身份登录 Jellyfin。
2. 右下角会出现 **+ 添加音乐** 按钮。
3. 点击它。模态窗口打开。
4. 从您的文件管理器中,将一个或多个 **Artist 文件夹** 拖放到拖放区域。预期的文件夹结构为 `Artist/Album/song.flac`。
您可以一次拖放多个 Artist 文件夹。
5. 模态窗口会预览它发现的内容——每个 Artist/Album 组合一行,显示曲目数量和总大小。
6. *(可选)* 点击任何行右侧的 **上传缩略图** 来为该专辑设置封面图片。一个 26×26 像素的缩略图预览会显示在旁边,还有一个小 × 用于清除它。
7. 点击 **上传**。文件会逐个上传,并显示进度条。
8. 批量上传完成后,插件会自动触发 Jellyfin 的媒体库扫描。新曲目(和您设置的任何封面)将在约 1 分钟内出现在媒体库中。
### 文件夹结构规则
| 您拖放的内容 | 落地位置 |
| --- | --- |
| `Artist/Album/song.flac` | `/Artist/Album/song.flac` |
| `Artist/song.flac` (无专辑文件夹) | `/Artist/Unknown Album/song.flac` |
| `Artist/Album/Disc 1/song.flac` | `/Artist/Album/Disc 1/song.flac` (原样保留) |
| 为某专辑选择的封面图片 | `/Artist/Album/cover.` |
封面文件遵循 Jellyfin 标准的 `cover.*` 约定——专辑封面也会自动应用到该文件夹中的所有曲目,无需为每个文件嵌入标签。
## 标签工作原理
### 问题所在
Jellyfin 的音乐媒体库是**基于标签的**,而不是基于文件夹的。当它扫描一个音乐文件夹时,它会读取每个文件中的 ID3 (MP3) / Vorbis (FLAC, OGG) / MP4 atom (M4A) 标签,并使用这些标签作为 Artist、AlbumArtist、Album 和 Title。它**不会**像从电影和电视剧的文件名推断那样,从文件夹结构中推断艺术家或专辑。
这很重要,因为拖放的文件通常来自标签不完善的来源:
在所有这些情况下,文件的 `artist` / `album_artist` / `album` 帧为空或错误。磁盘上的文件夹结构是 “TheBand / Ideas and Creations”,但 Jellyfin 读取文件,看到没有标签,就在媒体库界面中显示 **未知艺术家 / 未知专辑**。
您可以在任何文件上使用 `ffprobe` 验证这一点:
```
ffprobe -v error -show_entries format_tags=artist,album_artist,album,title \
-of default=noprint_wrappers=1 "/path/to/file.mp3"
```
如果该命令没有输出,说明文件没有标签——Jellyfin 也将看不到任何内容。
### 插件做了什么
当 **`NormalizeTagsFromFolder`** 开启(默认开启)时,插件会使用 TagLibSharp 打开它刚刚写入的每个音频文件,并设置:
| 标签 | 来源 | 行为 |
| --- | --- | --- |
| Artist | 您拖放的顶层文件夹 (`parts[0]`) | **覆盖写入** |
| AlbumArtist | 与 Artist 相同 | **覆盖写入** |
| Album | 第二级文件夹,如果缺失则为 `"Unknown Album"` | **覆盖写入** |
| Title | 去掉前导音轨号的文件名 | 如果为空则填充 |
因此,一个以 `TheBand/Ideas and Creations/01 You Keep Me Loving You mix # 1.mp3` 路径拖放的文件最终会带有:
```
Artist = TheBand
AlbumArtist = TheBand
Album = Ideas and Creations
Title = You Keep Me Loving You mix # 1 (only if previously blank)
```
前导的 `01 ` 被去掉了。它处理的其他模式包括:`01-`、`01.`、`01_`、`1-02 ` (音轨+碟片),以及任何组合(例如 `001 - `、`1-01. `)。尾部的扩展名会被移除。
### 为什么“覆盖 Artist/Album 但仅填充 Title”
文件夹结构对于艺术家和专辑是**确定性的**——您在组织拖放内容时自己键入了这些文件夹名称,因此文件标签中之前存在的内容几乎肯定是错误或过时的。
而文件名有时是有效的曲目标题(`In your hands.mp3`),有时是垃圾(`track01.mp3`)。如果文件已经有 `Title` 标签,我们信任它——来自特定专辑的标题比从文件名猜测更可靠。如果它是空的,我们使用派生的文件名,而不是留空。
### 何时关闭它
在插件配置页面禁用 `NormalizeTagsFromFolder` 的情况:
- 您正在上传来自正确标记来源的文件(Bandcamp 发行、Discogs 标签的翻录、Beatport 下载),并且不希望任何内容被覆盖。
- 您在 MP3Tag / Picard / Mp3tag 中手动管理标签,磁盘上的文件已经是正确的。
禁用后,插件的行为就像一个纯文件移动器,Jellyfin 读取源文件嵌入的任何标签。
## 配置参考
| 键 | 默认值 | 含义 |
| --- | --- | --- |
| `LibraryPath` | *空* | 绝对服务器端路径。**必填。** |
| `MaxFileSizeMb` | `200` | 每个文件的大小上限(兆字节)。`0` 表示禁用。 |
| `AllowedExtensions` | `mp3,flac,m4a,aac,ogg,opus,wav,wma,alac,aiff,jpg,jpeg,png,webp` | 以逗号分隔、小写、无点号。 |
| `RefreshLibraryOnComplete` | `true` | 每批上传完成后触发 Jellyfin 的 `RefreshLibrary` 计划任务。 |
| `AllowOverwrite` | `false` | 如果为 false,对相同相对路径的二次上传将返回 HTTP 409。 |
| `NormalizeTagsFromFolder` | `true` | 写入每个音频文件后,根据文件夹结构 (`Artist/Album/song.ext`) 覆盖 Artist / AlbumArtist / Album 标签,并根据文件名设置 Title(去掉前导音轨号)。Title 仅在当前为空时设置。如果您的文件标签正确且不希望被修改,请禁用。 |
## API 参考
插件公开了这些端点,都位于 `/musicup` 下。
| 方法 | 路径 | 认证 | 描述 |
| --- | --- | --- | --- |
| GET | `/musicup/frontend/{**path}` | 匿名 | 提供网页客户端使用的嵌入式 JS。 |
| GET | `/musicup/config` | 已认证用户 | 只读配置快照——UI 验证所需。 |
| POST | `/musicup/upload` | 管理员 (`RequiresElevation`) | 多部分表单:`file` + `relativePath`。 |
| POST | `/musicup/refresh` | 管理员 (`RequiresElevation`) | 触发 Jellyfin 的 `RefreshLibrary` 计划任务。 |
通过 `curl` 直接上传示例:
```
curl -X POST \
-H "X-Emby-Token: " \
-F "file=@/tmp/song.flac" \
-F "relativePath=Radiohead/OK Computer/01 Airbag.flac" \
https://your-jellyfin.example.com/musicup/upload
```
## 安全模型
- 上传和刷新需要 `RequiresElevation` 策略(管理员用户或具有管理员作用域的 API 密钥)。
- 相对路径经过清理:没有 `..`、没有绝对路径、没有驱动器字母、没有空字节、没有保留文件名字符、没有空路径段。解析后,通过 `Path.GetFullPath` 前缀比较验证目标位置位于 `LibraryPath` 下——任何试图逃逸根目录的请求都会被 HTTP 400 拒绝。
- 文件先写入 `.part` 临时文件,成功后原子性地重命名。上传中途连接中断只会留下 `.part` 文件,而不会留下半写的 `.flac` 文件。
- 静态 `frontend` 路由仅解析 `JellyMusicUploader.Frontend.` 命名空间下的嵌入资源——无法枚举任意程序集。
## 已知限制
- **全局媒体库扫描,而非仅音乐。** 刷新钩子执行 Jellyfin 的 `RefreshLibrary` 任务,它会扫描所有媒体库。在大型媒体库上,这可能需要几分钟;新曲目通常在扫描完全完成前就会出现,但“运行中任务”指示器会在整个扫描过程中保持显示。
- **自定义 CSS 注入不可靠。** 请参见安装步骤 4——较新的 Jellyfin 版本不会从自定义 CSS 字段注入 `` 这一行,或恢复安装程序留下的 `index.html.bak-musicup` 备份文件。
4. (可选)删除 `/data/plugins/configurations/` 下的 `JellyMusicUploader.xml` 以清除存储的设置。
5. 启动 Jellyfin。
## 许可证
MIT — 详见 [LICENSE](LICENSE)。标签:CMS安全, JavaScript, Jellyfin插件, TCP SYN 扫描, Web API, Web UI, 后端开发, 多人体追踪, 媒体服务器, 媒体服务器插件, 媒体管理, 封面艺术, 库扫描, 拖放上传, 插件开发, 数据可视化, 文件夹管理, 文件管理, 用户界面, 自动标签, 音乐上传, 音乐库, 音乐管理工具, 音频管理