Hexo个人博客
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

208 lines
7.8 KiB

7 months ago
---
title: B站字幕转换
date: 2022-02-22 10:34:50
toc: false
---
{% raw %}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="Pragma" content="no-cache">
<meta http-equiv="Cache-Control" content="no-cache">
<meta http-equiv="Expires" content="0">
<link rel="stylesheet" href="/assets/element-ui.css">
7 months ago
<!-- import Vue before Element -->
<script src="/assets/vue.min.js"></script>
7 months ago
<!-- import JavaScript -->
<script src="/assets/element-ui.js"></script>
7 months ago
</head>
<body>
<div id="app">
<div class="tools-upload">
<el-upload action="#" :http-request="requestUpload" :show-file-list="false">
<el-button size="mini" type="primary">上传</el-button>
</el-upload>
<div class="tools-right">
<div class="tool-item">
文件编码:
<el-select size="mini" v-model="fileEncoder" placeholder="文件编码">
<el-option label="GBK" value="GBK"></el-option>
<el-option label="UTF-8" value="UTF-8"></el-option>
<el-option label="GB2312" value="GB2312&quot;"></el-option>
</el-select>
</div>
<div class="tool-item">
换行符:
<el-select size="mini" v-model="newline" placeholder="换行符">
<el-option label="\r\n (Windows)" value="windows"></el-option>
<el-option label="\n (Linux)" value="linux"></el-option>
</el-select>
</div>
</div>
</div>
<el-input type="textarea" placeholder="请上传或输入字幕文件" v-model="originText" rows="15" show-word-limit="">
</el-input>
<div class="tools-btn">
<el-button size="mini" @click="generateSrt">生成SRT文件</el-button>
<el-button size="mini" @click="generateTXT">生成TXT文件</el-button>
<el-button size="mini" @click="clear">清空</el-button>
</div>
<el-input type="textarea" placeholder="等待生成SRT文件" v-model="srtText" rows="15" show-word-limit="">
</el-input>
<el-button type="text" @click="saveSrt">保存到本地</el-button>
<h3>字幕提取方法参考下面的视频</h3>
<div style="position: relative; padding: 30% 50%;">
<iframe src="//player.bilibili.com/player.html?aid=863543343&amp;bvid=BV16G4y1S7Hp&amp;cid=985575449&amp;page=1&amp;as_wide=1&amp;high_quality=1&amp;danmaku=1"
scrolling="no" border="0" frameborder="no" framespacing="0" allowfullscreen="true"
style="position: absolute; width: 100%; height: 100%; left: 0; top: 0;"></iframe>
</div>
</div>
</body>
<script>
new Vue({
el: '#app',
data: function () {
return {
fileEncoder: "UTF-8",
newline: "windows",
originText: "",
srtText: "",
fileName: ""
}
},
methods: {
// 覆盖默认的上传行为
requestUpload(content) {
let file = content.file
let index = file.name.lastIndexOf('.')
this.fileName = file.name.substring(0, index)
// 新建一个FileReader
const reader = new FileReader()
// 读取文件
reader.readAsText(file, this.fileEncoder)
// 读取完文件之后会回来这里
reader.onload = (e) => {
// 读取文件内容
const fileString = e.target.result
// 接下来可对文件内容进行处理
this.originText = fileString
}
},
generateSrt() {
this.srtText = ""
let originObj = JSON.parse(this.originText)
let body = originObj.body
let lastTo = null
for (let index = 0; index < body.length; index++) {
let line = body[index]
this.srtText += (index + (this.newline == 'windows' ? '\r\n' : '\n'))
let from = line.from
if (lastTo) {
let timeA = (from + '').split(".");
let timeB = (lastTo + '').split("."); //上次的时间
if (parseInt(timeB[0]) >= parseInt(timeA[0])) {
timeA[0] = timeB[0];
if (timeB[1]) {
if (parseInt(timeB[1]) >= parseInt(timeA[1])) {
timeA[1] = timeB[1];
}
}
from = (timeA[1] ? (timeA[0] + '.' + timeA[1]) : timeA[0])
}
}
this.srtText += (this.deelTime(from) + " --> ")
let to = line.to
lastTo = to
this.srtText += (this.deelTime(to) + (this.newline == 'windows' ? '\r\n' : '\n'))
this.srtText += line.content
this.srtText += (this.newline == 'windows' ? '\r\n' : '\n')
this.srtText += (this.newline == 'windows' ? '\r\n' : '\n')
}
},
generateTXT() {
this.srtText = ""
let originObj = JSON.parse(this.originText)
let body = originObj.body
let lastTo = null
for (let index = 0; index < body.length; index++) {
let line = body[index]
let content = line.content
this.srtText += content
this.srtText += (this.newline == 'windows' ? '\r\n' : '\n')
}
},
deelTime(origin) {
let timeA = (origin + '').split(".");
let tranTime = this.sumSecondToTime(timeA[0]);
return tranTime + "," + (timeA.length < 2 ? '000' : timeA[1].padStart(3, '0'));
},
sumSecondToTime(sumSecond) {
if (sumSecond <= 0) {
return "00:00:00";
}
let h = parseInt(sumSecond / 3600);
let m = parseInt((sumSecond - h * 3600) / 60);
let s = sumSecond - h * 3600 - m * 60;
let time = (h + '').padStart(2, '0') + ':' + (m + '').padStart(2, '0') + ':' + (s + '').padStart(2,
'0');
return time;
},
clear() {
this.originText = ""
},
saveSrt() {
// const file = new Blob(this.srtText, {
// type: 'text/plain'
// });
// a.href = URL.createObjectURL(file);
// a.download = name;
var element = document.createElement('a');
element.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(this.srtText));
if (this.fileName != '') {
element.setAttribute('download', this.fileName + '.srt');
} else {
element.setAttribute('download', 'translated.srt');
}
element.style.display = 'none';
document.body.appendChild(element);
element.click();
document.body.removeChild(element);
}
}
})
</script>
<style>
.tools-upload {
margin: 10px 0;
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
}
.tools-right {
display: flex;
flex-direction: row;
align-items: center;
font-size: 15px;
}
.tool-item {
margin-left: 10px;
}
.tools-btn {
margin: 10px 0;
}
</style>
</html>
{% endraw %}