|
|
|
---
|
|
|
|
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="/html/assets/element-ui.css">
|
|
|
|
<!-- import Vue before Element -->
|
|
|
|
<script src="/html/assets/vue.min.js"></script>
|
|
|
|
<!-- import JavaScript -->
|
|
|
|
<script src="/html/assets/element-ui.js"></script>
|
|
|
|
</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""></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&bvid=BV16G4y1S7Hp&cid=985575449&page=1&as_wide=1&high_quality=1&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 %}
|