上一篇讲了如何解析 YouTube 字幕,那个字幕对应的是 youtuber 自己设置好的字幕文件,字幕是标准的。
在 youtube 上还有另外一种字幕是平台自动生成的。这种字幕文件不是以句子为单位,而是以单词为单位的。那么上一篇中的解析方法就不适用了。为此我又写了一个把不规范的字幕文件转为规范的字幕文件,然后再用上一篇的方法进行解析,最终生成标准的字幕文件。
先来看一下自动生成的字幕是什么样的子的。
可以看到每一个单词都是分开的,我们要的是一个完整的句子,就像下面的图。
直接看代码吧,注释都写了。主要的处理就是用循环的方法把每一个单词拼接成一句话,然后添加到对应的数组中。其中还有一点是字幕显示的时间,自动生成的字幕是两行两行显示的,所以如果直接按照 json 中的时间显示的话,会同时显示两行字幕,看起来很乱,我是忍受不了这样的显示方式,所以我时间又处理了一下,变成一行显示。
function convertJson(resourcePath, outPath) {
pReadFile(resourcePath)
.then(function (data) {
// console.log('data'+ data);
var data = JSON.parse(data);
var sub = data.events;
console.log(sub.length);
var length = sub.length;
for (let i = 0; i < length; i++) {
if (sub[i].segs) {
var utf8 = '';
// console.log(sub[i].segs.length);
for (let j = 0; j < sub[i].segs.length; j++) {
utf8 += sub[i].segs[j].utf8;
}
sub[i].segs = [{ "utf8": utf8 }];
}
}
/**
* arr.splice(index,len,item)
* splice 方法在删除或替换数组里的元素后会改变原来数组的值
* 如果用正常的正序循环删除的话,会由于数组元素的下标发生变化而出错
* 所以可以用倒序的方法来实现循环删除
*
* 用 while 来循环更简单
*/
while (length--) {
if (sub[length].segs) {
if (sub[length].segs[0].utf8 == '\n') {
sub.splice(length, 1);
}
}
}
// 去掉第一项,第一项不是字幕
sub.shift();
// 自动生成的字幕是以两行的形式显示的,有点乱,所以把它改成一行显示
// 两行显示的原因是字幕持续时间是两行的时间,改为一行的时间就可以了
for (let i = 0; i < sub.length; i++) {
if (i < sub.length - 1) {
sub[i].dDurationMs = sub[i + 1].tStartMs - sub[i].tStartMs;
}
}
data.events = sub;
if (fs.existsSync('../subtitle/subtitle.json')) {
fs.writeFileSync('../subtitle/subtitle.json', '');
}
fs.writeFileSync('../subtitle/subtitle.json', JSON.stringify(data));
}, function (err) {
});
}
另外在代码中使用了 promise 。封装了一个读取文件的 promise 方法,这个方法是从别的项目里拿过来的。这里当然也可以不用,直接就用传统的方法读取也没有关系。
function pReadFile(filePath) {
return new Promise(function (resolved, rejected) {
fs.readFile(filePath, 'utf8', function (err, data) {
if (err) {
rejected(err)
} else {
resolved(data)
}
})
})
}
把不规则的 json 文件处理成规则的之后,再用上一篇的方法,进行解析就可以了。
PS:关注公众号 windymiao,回复『字幕2』获取完整源码