Skip to content

Commit 2e57ddd

Browse files
committedMar 14, 2020
Implement major functions
1 parent 63c6e8f commit 2e57ddd

File tree

4 files changed

+265
-5
lines changed

4 files changed

+265
-5
lines changed
 

‎index.html

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
<head>
44
<meta charset="utf-8">
55
<meta name="viewport" content="width=device-width,initial-scale=1.0">
6-
<title>fast-srt-subtitle</title>
6+
<title>Fast SRT Subtitle Toolkit</title>
77
</head>
88
<body>
99
<div id="app"></div>

‎src/App.vue

+213-2
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,234 @@
11
<template lang="pug">
22
#app
3-
navbar
3+
navbar
4+
vk-modal(:show.sync="modalShow")
5+
vk-modal-title
6+
| Error
7+
p
8+
| {{modalText}}
9+
p.uk-text-right
10+
vk-button.uk-margin-small-right(@click="modalShow = false")
11+
| Close
12+
.container
13+
.panel
14+
div(v-if="stage === 'prepare'")
15+
input.hidden(type="file", ref="textLoader", accept="text/plain", @change="readText")
16+
vk-button(@click="loadText")
17+
| Load Text File
18+
.uk-margin
19+
textarea.uk-textarea(rows="20", placeholder="Subtitle Preview", v-model="subtitleText")
20+
p.uk-margin
21+
| Lines of Subtitle: {{ subtitleText.split('\n').length }} line(s)
22+
vk-button.uk-margin(type="primary", @click="startEdit")
23+
| Start Editing
24+
div(v-if="stage === 'edit'")
25+
h4
26+
| React Time: {{ reactTime }}
27+
input.uk-range(type="range", min="0.0" max="1.0", step="0.01", v-model="reactTime")
28+
h2
29+
| Current Line
30+
h4.alt-text(v-if="currentLine === null")
31+
| [Empty]
32+
h4(v-else)
33+
| {{ subtitles[currentLine] }}
34+
h2
35+
| Coming Lines
36+
h4.alt-text(v-for="subtitle in nextLines")
37+
| {{ subtitle }}
38+
h4.alt-text(v-if="nextLines.length < 4")
39+
| [End of File]
40+
vk-button.uk-margin(type="primary", @click="startReview")
41+
| Start Reviewing
42+
div(v-if="stage === 'review'")
43+
textarea.uk-textarea(rows="20", placeholder="Subtitle Preview", v-model="subtitleReview")
44+
vk-button.uk-margin(type="primary", @click="saveFile")
45+
| Save File
46+
a.hidden(ref="download", href="")
47+
.panel
48+
input.hidden(
49+
type="file",
50+
ref="videoLoader",
51+
accept="audio/mp4, video/mp4",
52+
@change="readVideo")
53+
vk-button(@click="loadVideo", v-if="stage === 'prepare'")
54+
| Load Video
55+
video.video.uk-margin(
56+
ref="video",
57+
src="http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4",
58+
controls)
59+
shortcut(v-show="stage === 'edit'")
460
</template>
561

662
<script>
763
import Navbar from './components/Navbar';
64+
import Shortcut from './components/Shortcut';
865
966
export default {
1067
name: 'App',
1168
components: {
1269
Navbar,
70+
Shortcut,
1371
},
1472
data() {
1573
return {
74+
modalShow: false,
75+
modalText: '',
1676
stage: 'prepare',
77+
subtitleText: '',
78+
subtitles: [],
79+
subtitleStarts: [],
80+
subtitleEnds: [],
81+
currentLine: null,
82+
nextLine: 0,
83+
previousTiming: 0,
84+
reactTime: 0.3,
85+
subtitleReview: '',
1786
};
1887
},
88+
computed: {
89+
nextLines() {
90+
return this.subtitles.slice(this.nextLine, this.nextLine + 4);
91+
},
92+
},
93+
beforeDestroy() {
94+
// Makesure remove all event handlers
95+
window.removeEventListener('keypress', this.keyHandler);
96+
},
97+
methods: {
98+
loadText() {
99+
this.$refs.textLoader.click();
100+
},
101+
loadVideo() {
102+
this.$refs.videoLoader.click();
103+
},
104+
readText(evt) {
105+
const filename = evt.target.files[0];
106+
this.subtitleText = ''; // Empty the previous results
107+
108+
const reader = new FileReader();
109+
reader.onload = () => {
110+
this.subtitleText = reader.result;
111+
};
112+
113+
reader.readAsText(filename);
114+
},
115+
readVideo(evt) {
116+
const filename = evt.target.files[0];
117+
const url = URL.createObjectURL(filename);
118+
this.$refs.video.src = url;
119+
this.$refs.video.load();
120+
},
121+
startEdit() {
122+
if (this.subtitleText.length === 0) {
123+
this.modalShow = true;
124+
this.modalText = 'Please import the subtitle text first.';
125+
} else {
126+
this.subtitles = this.subtitleText.split('\n');
127+
this.subtitleStarts = new Array(this.subtitles.length).fill(null);
128+
this.subtitleEnds = new Array(this.subtitles.length).fill(null);
129+
this.stage = 'edit';
130+
window.addEventListener('keypress', this.keyHandler);
131+
}
132+
},
133+
startReview() {
134+
window.removeEventListener('keypress', this.keyHandler);
135+
this.stage = 'review';
136+
this.generateSubtitle();
137+
},
138+
keyHandler(e) {
139+
const pressed = String.fromCharCode(e.keyCode).toLowerCase();
140+
141+
switch (pressed) {
142+
case 'k':
143+
// Switch to Next Line
144+
if (this.currentLine !== null) {
145+
this.subtitleEnds[this.currentLine] = this.$refs.video.currentTime - 0.01;
146+
}
147+
this.currentLine = this.nextLine;
148+
this.nextLine += 1;
149+
if (this.currentLine < this.subtitles.length) {
150+
this.subtitleStarts[this.currentLine] = this.$refs.video.currentTime;
151+
} else {
152+
this.currentLine = null;
153+
}
154+
break;
155+
case 'l':
156+
// Stop Current Line
157+
if (this.currentLine !== null) {
158+
this.subtitleEnds[this.currentLine] = this.$refs.video.currentTime - 0.01;
159+
}
160+
this.currentLine = null;
161+
break;
162+
case 'u':
163+
// Prev 3 Secs
164+
this.$refs.video.currentTime -= 3;
165+
break;
166+
case 'p':
167+
// Skip 3 Secs
168+
this.$refs.video.currentTime += 3;
169+
break;
170+
case 'i':
171+
if (this.nextLine > 0) {
172+
this.currentLine = this.nextLine - 2;
173+
this.nextLine = this.nextLine - 1;
174+
}
175+
176+
if (this.currentLine === -1) {
177+
this.currentLine = null;
178+
}
179+
break;
180+
case 'o':
181+
if (this.nextLine < this.subtitles.length) {
182+
this.currentLine = this.nextLine;
183+
this.nextLine = this.nextLine + 1;
184+
}
185+
break;
186+
default:
187+
break;
188+
}
189+
},
190+
timeFormat(secs) {
191+
if (secs === null) {
192+
return '0:0:0,0';
193+
}
194+
const hour = Math.floor(secs / 60 / 60);
195+
const min = Math.floor((secs / 60) % 60);
196+
const sec = Math.floor(secs % 60);
197+
const mil = Math.floor((secs * 1000) % 1000);
198+
return `${hour}:${min}:${sec},${mil}`;
199+
},
200+
generateSubtitle() {
201+
this.subtitleReview = '';
202+
for (let i = 0; i < this.subtitles.length; i += 1) {
203+
this.subtitleReview += `${i + 1}\n`;
204+
this.subtitleReview += `${this.timeFormat(this.subtitleStarts[i])} --> ${this.timeFormat(this.subtitleEnds[i])}\n`;
205+
this.subtitleReview += `${this.subtitles[i]}\n\n`;
206+
}
207+
},
208+
saveFile() {
209+
const a = this.$refs.download;
210+
const file = new Blob([this.subtitleReview], { type: 'text/plain' });
211+
a.href = URL.createObjectURL(file);
212+
a.download = 'result.srt';
213+
a.click();
214+
},
215+
},
19216
};
20217
</script>
21218

22-
<style>
219+
<style lang="stylus" scoped>
220+
.container
221+
display flex
222+
margin 20px
223+
.hidden
224+
display none
225+
.panel
226+
flex 1
227+
margin 10px
228+
.uk-textarea
229+
resize vertical
230+
.video
231+
width 100%
232+
.alt-text
233+
color #aaa
23234
</style>

‎src/components/Navbar.vue

+1-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
<template lang="pug">
2-
#app
3-
vk-navbar
2+
vk-navbar
43
vk-navbar-nav
54
vk-navbar-nav-item(title="Fast SRT Subtitle Toolkit", active)
65
vk-navbar-nav(slot="right")

‎src/components/Shortcut.vue

+50
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
<template lang="pug">
2+
vk-card.shortcuts
3+
vk-card-title
4+
| Shortcuts
5+
p
6+
vk-label.shortcut-label
7+
| K
8+
span.shortcut-text
9+
| Switch to Next Line
10+
p
11+
vk-label.shortcut-label
12+
| L
13+
span.shortcut-text
14+
| Stop Current Line
15+
p
16+
vk-label.shortcut-label
17+
| U
18+
span.shortcut-text
19+
| Prev 3 Secs
20+
p
21+
vk-label.shortcut-label
22+
| P
23+
span.shortcut-text
24+
| Skip 3 Secs
25+
p
26+
vk-label.shortcut-label
27+
| I
28+
span.shortcut-text
29+
| Prev Line
30+
p
31+
vk-label.shortcut-label
32+
| O
33+
span.shortcut-text
34+
| Skip Line
35+
</template>
36+
37+
<script>
38+
39+
export default {
40+
name: 'Shortcut',
41+
};
42+
</script>
43+
44+
<style lang="stylus" scoped>
45+
.shortcut-label
46+
width 30px
47+
text-align center
48+
.shortcut-text
49+
margin-left 10px
50+
</style>

0 commit comments

Comments
 (0)
Please sign in to comment.