圣诞快乐!
这是今年最后一篇文章了
Github: Masterain98/Anime-MKV-Plex-Packager
前言
最近在维护NAS上的Plex媒体服务器时发现在刮削内容完成情况下,Plex依然对日本动画资源的读取有困难,故根据实际遇上的情况写了个Python脚本将所有需要包含的多媒体资源重新混流以达到让Plex顺利读取的目的。将整个过程记录下来供众人参考,也为以后debug做个文档准备。
遇到的问题
字幕文件信息不全
- 许多来自字幕组的中文字幕都包含简体中文
chs
和繁体中文cht
两种语言格式。MVK的语言编码使用的是ISO-639-2
,Plex额外采用了ISO-639-1
`,但它们都没有区分简繁体 - 在一部分字幕组自行压制的番剧系列中,可以观察到Plex读取到了简繁体信息,这是因为字幕组在MKV混流时为字幕所对应的轨道以简繁体设置了名称。而在压制组和字幕组合作的番剧中,
ass
字幕文件并没有被和正片一起混流所以不会被Plex识别,或者被识别成错误的语言。
- 以鬼灭之刃为例,字幕为随包外挂的
ass
文件,在文件名相同的情况下Plex能成功读取但没有正确的语言信息 - 而像 FSN HF3这样的剧场版,蓝光盘中的原生字幕在压制组混流时已经被添加,但字幕组外挂的ass字幕仍然无法被正确识别
- 许多来自字幕组的中文字幕都包含简体中文
内封字幕的字体
- 在一些字幕组的高端操作下
ass
字幕可以做出很好的特效,但这可能需要配合相对应的字幕。而字幕组一般会将字体打包以减少番剧主文件占用过多储存空间,当Plex系统不包含对应的字体文件就会出现问题,Plex将采用其默认样式来渲染字幕。因此需要将字体文件以附件的形式和MVK一起混流,经过测试以附件形式混流的字体文件将会被Plex识别并启用。
- 在一些字幕组的高端操作下
附加音轨不会被识别
- 和字幕文件类似的问题,附带的MKA音轨(可能是5.1音轨或评论轨)不会被Plex识别,需要将MKA文件随MKV一起混流
脚本
思路很简单,列目录读MKV文件,读取相同名字的外挂音轨和字幕
- 这得益于字幕组对文件命名的标准化
解压字体压缩包并以附件的形式混流
- 在写码的时候发现一些字体压缩包使用了
GBK
编码而不是UTF-8
,可能是字幕组在打包时使用了百度网盘的自动压缩工具
- 在写码的时候发现一些字体压缩包使用了
- 主要使用的库为
pymkv
,核心是随MKVToolNix
一起的mkvmerge
程序 - 将原始媒体文件和字幕自动重命名或删除,防止Plex重复采用
import os
import shutil
import zipfile
from pymkv import MKVFile, MKVTrack
from pathlib import Path
# 设置项
DELETE_FONTS = True
DELETE_ORIGINAL_MKV = False
RENAME_ORIGINAL_MKV = True
DELETE_ORIGINAL_MKA = False
RENAME_ORIGINAL_MKA = True
DELETE_CHS_SUB = False
RENAME_CHS_SUB = True
DELETE_CHT_SUB = False
RENAME_CHT_SUB = True
SUFFIX_NAME = "_Plex"
# https://gist.github.com/hideaki-t/c42a16189dd5f88a955d
# 网上抄来的```GBK```解压代码,进行了部分修改以适应本脚本功能
def unzip(f, encoding):
font_list = []
with zipfile.ZipFile(f) as z:
for i in z.namelist():
font_list.append("Fonts/" + i.encode('cp437').decode(encoding))
n = Path("Fonts/" + i.encode('cp437').decode(encoding))
if i[-1] == '/':
if not n.exists():
n.mkdir()
else:
with n.open('wb') as w:
w.write(z.read(i))
return font_list
if __name__ == '__main__':
if DELETE_ORIGINAL_MKV and RENAME_ORIGINAL_MKV:
print("Rename MKV instead")
DELETE_ORIGINAL_MKV = False
if DELETE_ORIGINAL_MKA and RENAME_ORIGINAL_MKA:
print("Rename MKA instead")
DELETE_ORIGINAL_MKA = False
if DELETE_CHS_SUB and RENAME_CHS_SUB:
print("Rename CHS instead")
DELETE_ORIGINAL_MKA = False
if DELETE_CHT_SUB and RENAME_CHT_SUB:
print("Rename CHT instead")
DELETE_ORIGINAL_MKA = False
delete_list = []
rename_list = []
# 解压字体包
# 方法是找包含 Font 和 .zip 的关键词
folder_list = os.listdir()
font_list = []
for file_name in folder_list:
if "Font" in file_name and ".zip" in file_name:
print("Find font package file: " + file_name)
if not os.path.exists("Fonts"):
os.makedirs("Fonts")
print("Fonts sub-directory created")
font_list = unzip(file_name, "GBK")
print("Unzipped to /Fonts: " + str(font_list))
print("=" * 20)
# 主任务
for file_name in folder_list:
# .mkv 为番剧主文件
if ".mkv" in file_name:
episode_name = file_name.replace(".mkv", "")
this_task = MKVFile(file_name)
for item in folder_list:
if episode_name in item:
if ".mkv" in item:
print("Find main video: " + item)
if DELETE_ORIGINAL_MKV:
delete_list.append(item)
if RENAME_ORIGINAL_MKV:
rename_list.append(item)
# 混流字幕文件
if ".ass" in item:
# 以 sc 和 chs 作为识别简中字幕的关键词
if "chs" in item or "sc" in item:
this_chs = MKVTrack(item, track_name="chs", default_track=True, language="chi")
this_task.add_track(this_chs)
print("Find associated CHS subtitle: " + item)
if DELETE_CHS_SUB:
delete_list.append(item)
if RENAME_CHS_SUB:
rename_list.append(item)
# 以 tc 和 cht 作为识别简中字幕的关键词
if "cht" in item or "tc" in item:
this_cht = MKVTrack(item, track_name="cht", default_track=False, language="chi")
this_task.add_track(this_cht)
print("Find associated CHT subtitle: " + item)
if DELETE_CHT_SUB:
delete_list.append(item)
if RENAME_CHT_SUB:
rename_list.append(item)
# 混流外挂音轨
if ".mka" in item:
this_task.add_track(item)
print("Find associated audio: " + item)
if DELETE_ORIGINAL_MKA:
delete_list.append(item)
if RENAME_ORIGINAL_MKA:
rename_list.append(item)
for font in font_list:
this_task.add_attachment(font)
# 为新的混流文件设置一个包含专属后缀的文件名
newMKV_name = episode_name + SUFFIX_NAME + ".mkv"
this_task.mux(newMKV_name)
print("=" * 20)
# Clean up
# 删除解压的字体目录和其它原始文件
try:
shutil.rmtree("Fonts/")
print("Remove Fonts Folder Successfully")
except:
print("Remove Fonts Folder Error")
for file in delete_list:
try:
os.remove(file)
except:
print("Failed to delete " + file)
for file in rename_list:
os.rename(file, file + ".bak")
input("Task finished. Press any key to exit...")
运行示例
- 以鬼灭之刃为例,脚本运行截图如下
以FSN HF3为例,以下是在字体文件是否以附件混流情况下Plex播放时的效果
- 当字体文件没有随媒体文件混流时
- 当字体文件随媒体文件混流时
转载请标注来源