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.
207 lines
7.9 KiB
207 lines
7.9 KiB
--- |
|
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="https://unpkg.com/element-ui/lib/theme-chalk/index.css"> |
|
<!-- import Vue before Element --> |
|
<script src="https://unpkg.com/vue@2/dist/vue.js"></script> |
|
<!-- import JavaScript --> |
|
<script src="https://unpkg.com/element-ui/lib/index.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 %}
|
|
|