如何将本地讲座/课程视频(没有 TMDB/TVDB 收录的)在 Plex 中整理成结构清晰的电视节目,并通过 Plex API 写入自定义描述文字。
示例是 Jonathan Biss 的 Exploring Beethoven’s Piano Sonatas——Curtis 音乐学院与 Coursera 合作的 5 部分课程,存储在 Synology NAS 上。
Plex 默认的 metadata 爬虫依赖 TMDB 或 TVDB。没有收录的本地讲座视频要么显示成一堆无封面的乱列表,要么被错误匹配到不相关的节目。
解决方案:将课程当作**电视节目(TV Show)**处理,使用 Plex 的 Personal Media Shows 代理,再通过 Plex API 推送自定义 metadata。
第一步:按 Season/Episode 结构整理文件
#Plex 的 TV Show 扫描器要求文件名包含 SxxExx:
1
2
3
4
5
6
7
| Exploring Beethoven's Piano Sonatas/
├── Season 01/
│ ├── Exploring Beethoven's Piano Sonatas - S01E01 - Lecture 1, Part 1.mp4
│ ├── Exploring Beethoven's Piano Sonatas - S01E01 - Lecture 1, Part 1.en.srt
│ └── ...
├── Season 02/
└── ...
|
每个"讲"对应一个 Season,每个"Part"对应一集。字幕文件放在视频旁边,命名加 .en.srt 后缀,Plex 会自动识别为英文字幕。
用一个 shell 脚本批量重命名和移动文件,支持 --dry-run 预览:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
| #!/bin/bash
BASE="/volume1/data/media/courses/Exploring Beethoven's Piano Sonatas"
SHOW="Exploring Beethoven's Piano Sonatas"
DRY_RUN=false
[[ "$1" == "--dry-run" ]] && DRY_RUN=true
for file in "$BASE"/*.mp4; do
filename=$(basename "$file")
S=$(echo "$filename" | sed 's/^\([0-9]*\) - .*/\1/')
E=$(echo "$filename" | sed 's/^[0-9]* - \([0-9]*\) - .*/\1/')
TITLE=$(echo "$filename" | sed 's/^[0-9]* - [0-9]* - //' | sed 's/ ([^)]*)\.[^.]*$//')
SS=$(printf "%02d" "$S"); EE=$(printf "%02d" "$E")
mkdir -p "$BASE/Season $SS"
mv "$file" "$BASE/Season $SS/${SHOW} - S${SS}E${EE} - ${TITLE}.mp4"
done
# 将字幕从 srts/ 子文件夹移到对应 Season 目录
for file in "$BASE/srts"/*.srt; do
filename=$(basename "$file")
S=$(echo "$filename" | sed 's/^\([0-9]*\) - .*/\1/')
E=$(echo "$filename" | sed 's/^[0-9]* - \([0-9]*\) - .*/\1/')
TITLE=$(echo "$filename" | sed 's/^[0-9]* - [0-9]* - //' | sed 's/ ([^)]*)\.[^.]*$//')
SS=$(printf "%02d" "$S"); EE=$(printf "%02d" "$E")
mv "$file" "$BASE/Season $SS/${SHOW} - S${SS}E${EE} - ${TITLE}.en.srt"
done
|
第二步:配置 Plex 资料库
#在 Plex 中新建资料库:
- 类型: 电视节目(TV Shows)
- 文件夹: 指向节目根目录(或
courses/ 父目录) - 高级 → 代理: Personal Media Shows
- 高级: 勾选"优先使用本地 metadata"
保存后扫描,Plex 会自动从文件名识别出所有季和集。
第三步:通过 Plex API 写入描述文字
#Personal Media Shows 不会从任何地方拉取描述。可以在 Plex UI 里手动编辑,但有多季时用 API 批量推送更方便。
获取 Plex Token
#Plex Web → 播放任意视频 → “…” → “获取信息” → “查看 XML”,URL 中有 X-Plex-Token=XXXXX。
核心 API 调用
# 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
| import urllib.request, urllib.parse, json
BASE = "http://plex.nas:32400"
TOKEN = "your-token-here"
def plex_get(path, params=None):
url = BASE + path
if params:
url += "?" + urllib.parse.urlencode(params)
req = urllib.request.Request(url)
req.add_header("X-Plex-Token", TOKEN)
req.add_header("Accept", "application/json")
with urllib.request.urlopen(req) as r:
return json.loads(r.read())
# 列出所有资料库
sections = plex_get("/library/sections")
# 列出某个资料库的所有节目(key=7)
shows = plex_get("/library/sections/7/all")
# 更新节目描述(type=2)或季描述(type=3)
def set_summary(section_key, rating_key, item_type, summary):
params = {
"type": item_type,
"id": rating_key,
"summary.value": summary,
"summary.locked": 1,
}
url = f"{BASE}/library/sections/{section_key}/all?" + urllib.parse.urlencode(params)
req = urllib.request.Request(url, method="PUT")
req.add_header("X-Plex-Token", TOKEN)
urllib.request.urlopen(req)
|
summary.locked=1 可以防止 Plex 在下次刷新 metadata 时覆盖你写入的文字。
完整脚本逻辑
#脚本自动发现节目和所有季,然后逐一推送预先写好的描述:
1
2
3
4
5
6
7
| section_key, show_key = find_show(base_url, token, SHOW_TITLE)
seasons = get_seasons(base_url, token, show_key)
set_summary(section_key, show_key, 2, SHOW_SUMMARY) # 节目级描述
for season in seasons:
idx = season["index"]
set_summary(section_key, season["ratingKey"], 3, SEASON_SUMMARIES[idx])
|
注意事项
#- 如果 Plex 自动匹配了错误的节目,可以在 UI 中"修正匹配 → 无",或将资料库代理切换为 Personal Media Shows 后重新扫描。
- Plex 显示的节目标题可能和文件夹名不同(取决于之前匹配到了什么)。在搜索前先通过 API(
/library/sections/{key}/all)确认实际标题。 - 海报图片:在节目根目录放一张
poster.jpg,Plex 会自动读取。