From 6ea23d6f2ee7908898ce84ee7c9a80a2fb28dcab Mon Sep 17 00:00:00 2001 From: kjj6198 Date: Sun, 15 Mar 2020 02:00:59 +0900 Subject: [PATCH 1/2] make it a pure static website --- index.html | 145 +++++++++++++++----------------------------------- main.js | 151 +++++++++++++++++++++++++++++++++++++++++++++++++++++ sketch.js | 111 --------------------------------------- 3 files changed, 193 insertions(+), 214 deletions(-) create mode 100644 main.js delete mode 100644 sketch.js diff --git a/index.html b/index.html index 15ae09d..3c4f785 100644 --- a/index.html +++ b/index.html @@ -1,108 +1,47 @@ -Fast and Dirty Captioner + Fast and Dirty Captioner - - - - - - - - - -

用 Node 開 HTTP Server,影片檔名取 video.mp4,字幕文字檔 subs.txt(每句分行),放在同一資料夾。

-

K: 下一行開始 | L: 這一行提前結束 | I: 前捲一行 | O: 後捲一行 | U: 倒帶 3 秒 | P: 前進 3 秒 | Q: 製作 SRT 檔

-

K: Next Line | L: This Line Ends Early | I: Scroll Back | O: Scroll Forward | U: Rewind | P: Forward | Q: Make SRT File

-

Test Text.

- - + + + +

選取字幕檔以及影片檔案開始製作 SRT!

+ + +

+ K: 下一行開始 | L: 這一行提前結束 | I: 前捲一行 | O: 後捲一行 | U: 倒帶 3 + 秒 | P: 前進 3 秒 | Q: 製作 SRT 檔 +

+

+ K: Next Line | L: This Line Ends Early | I: Scroll Back | O: Scroll + Forward | U: Rewind | P: Forward | Q: Make SRT File +

+

Test Text.

+ + + + diff --git a/main.js b/main.js new file mode 100644 index 0000000..990978a --- /dev/null +++ b/main.js @@ -0,0 +1,151 @@ +const SRT_ID = 'srtFile'; +const VIDEO_ID = 'videoFile'; + +const srtInput = document.querySelector('#srtFile'); +const videoInput = document.querySelector('#videoFile'); +const video = document.querySelector('#video'); +const textArea = document.querySelector('#textArea'); +const status = document.querySelector('#status'); +const reactTime = 0.4; +let subTexts = []; +let currentStamping = 0; +let lines = []; + +function clamp(num) { + return Math.max(num, 0); +} + +const keyMap = { + /* K */ '75': video => { + if (currentStamping >= lines.length) { + return; + } + + lines[currentStamping + 1][0] = clamp(video.currentTime - reactTime); + lines[currentStamping][1] = + lines[currentStamping][1] > video.currentTime - reactTime || + lines[currentStamping][1] === null + ? clamp(video.currentTime - 0.03 - reactTime) + : null; + currentStamping += 1; + }, + /* L */ '76': video => { + lines[currentStamping] = [ + lines[currentStamping][0], + video.currentTime - reactTime + ]; + }, + /* I */ '73': () => { + currentStamping -= 1; + }, + /* O*/ '79': () => { + currentStamping += 1; + }, + /* U*/ '85': () => (video.currentTime -= 3), + /* P */ '80': () => (video.currentTime += 3), + /* Q */ '81': () => makeSRT() +}; + +function getCurrentStatus() { + return `Stamping Line ${currentStamping} | Playhead: ${video.currentTime}`; +} + +function execHotkey(keyMap) { + document.addEventListener('keypress', function(e) { + const execFn = keyMap[e.keyCode]; + if (typeof execFn === 'function') { + execFn(video); + updateContent(); + } + }); +} + +function updateContent() { + const head = '** 目前 ---> '; + + const content = subTexts + .slice(currentStamping, currentStamping + 5) + .map((text, i) => { + const [timeStart, timeEnd] = lines[currentStamping + i]; + return `${i === 0 ? head : ''}${text} | ${timeStart} --> ${timeEnd}`; + }) + .join('\n'); + + textArea.value = content; +} + +function handleFileUpload(e) { + if (e.target.files !== null) { + const reader = new FileReader(); + const file = e.target.files[0]; + + /* + if it's srt file, fill text area with srt content + if it's video, load it into video tag + */ + reader.onload = function() { + if (e.target.id === SRT_ID) { + subTexts = reader.result.split('\n'); + subTexts.forEach((_, i) => (lines[i] = [null, null])); + lines[0][0] = 0; + + updateContent(); + + execHotkey(keyMap); + } + }; + + reader.onerror = function() { + alert('無法讀取檔案!'); + }; + + if (e.target.id === SRT_ID) { + reader.readAsText(file); + } else { + video.src = URL.createObjectURL(file); + } + } +} + +videoInput.addEventListener('change', handleFileUpload); +srtInput.addEventListener('change', handleFileUpload); + +video.addEventListener('timeupdate', function(e) { + status.textContent = getCurrentStatus(); +}); + +function makeSRT() { + srt = ''; + for (let i = 0; i < subTexts.length; i++) { + // line number + srt += i + 1 + '\n'; + // line time + let sh, sm, ss, sms; + let eh, em, es, ems; + const [timeStart, timeEnd] = lines[i]; + const leftPad = str => `${str}`.padStart(2, '0'); + sh = leftPad(Math.floor(timeStart / 3600)); + sm = leftPad(Math.floor((timeStart % 3600) / 60)); + ss = leftPad(Math.floor(timeStart % 60)); + sms = leftPad(Math.floor((timeStart * 1000) % 1000)); + eh = leftPad(Math.floor(timeEnd / 3600)); + em = leftPad(Math.floor((timeEnd % 3600) / 60)); + es = leftPad(Math.floor(timeEnd % 60)); + ems = leftPad(Math.floor((timeEnd * 1000) % 1000)); + + srt += `${sh}:${sm}:${ss},${sms} --> ${eh}:${em}:${es},${ems}\n`; + srt += subTexts[i]; + srt += '\n\n'; + } + console.log(srt); + let blob = new Blob([srt], { + type: 'text/plain;charset=utf-8' + }); + const a = document.createElement('a'); + const file = new Blob([srt], { type: 'text/plain;charset=utf-8' }); + a.href = URL.createObjectURL(file); + a.download = 'srt.txt'; + a.click(); + URL.revokeObjectURL(a.href); + a.remove(); +} diff --git a/sketch.js b/sketch.js deleted file mode 100644 index 50db7ea..0000000 --- a/sketch.js +++ /dev/null @@ -1,111 +0,0 @@ -let curr; // current time -let sta; // status text -let status; // = select("#status"); -let tAreaText; // -let tArea; -let vidFile = "./video.mp4"; -let subFile = "./subs.txt"; -let subText; // load from text file -let lineStartTime = []; -let lineEndTime = []; -let reactTime = 0.4; - -let currentStamping = 0; -let srt = ""; - -function preload() { - vid = createVideo(vidFile); - vid.size(640, 320); - subText = loadStrings(subFile); -} - -function setup() { - noCanvas(); - status = select("#status"); - tArea = select("#textArea"); - - for (let i = 0; i < subText.length; i++) { - lineStartTime[i] = null; - lineEndTime[i] = null; - - //init all timestamps to 0 - } - lineStartTime[0] = 0; - -} - -function draw() { - curr = vid.elt.currentTime; - sta = `Stamping Line ${currentStamping} | Playhead: ${curr}`; - tAreaText = ""; - for (let i = 0; i < 5; i++) { - if (i == 0) { - tAreaText += "** 目前 ---> " - } - tAreaText += `${subText[currentStamping+i]} | ${lineStartTime[currentStamping+i]} --> ${lineEndTime[currentStamping+i]}` + String.fromCharCode(13, 10); - } - status.html(sta); - tArea.html(tAreaText); -} - -function keyPressed() { - if (keyCode === 75) { // K - // 按左鍵 - // set line start time to current time - lineStartTime[currentStamping + 1] = vid.elt.currentTime - reactTime; - if (lineStartTime[currentStamping + 1] < 0){ - lineStartTime[currentStamping + 1] = 0; - } - // set prev line's end time, if prev end time > currentTime; - if (lineEndTime[currentStamping] > vid.elt.currentTime - reactTime || lineEndTime[currentStamping] == null) { - lineEndTime[currentStamping] = vid.elt.currentTime - 0.03 - reactTime; - if (lineEndTime[currentStamping] < 0){ - lineEndTime[currentStamping] = 0; - } - } - currentStamping++; - } else if (keyCode === 76) { // L - lineEndTime[currentStamping] = vid.elt.currentTime - reactTime; - } else if (keyCode === 73) { // I - currentStamping--; - } else if (keyCode === 79) { // O - currentStamping++; - } else if (keyCode === 81) { - // Q : Make SRT - makeSRT(); - } else if (keyCode === 85) { // U - vid.elt.currentTime -= 3; - - } else if (keyCode === 80) { // P - vid.elt.currentTime += 3; - - } -} - -function makeSRT() { - srt = ""; - for (let i = 0; i < subText.length; i++) { - // line number - srt += (i + 1) + "\n"; - // line time - let sh, sm, ss, sms; - let eh, em, es, ems; - sh = floor(lineStartTime[i] / 3600); - sm = floor((lineStartTime[i] % 3600) / 60); - ss = floor(lineStartTime[i] % 60); - sms = floor((lineStartTime[i] * 1000) % 1000); - eh = floor(lineEndTime[i] / 3600); - em = floor((lineEndTime[i] % 3600) / 60); - es = floor(lineEndTime[i] % 60); - ems = floor((lineEndTime[i] * 1000) % 1000); - - srt += `${sh}:${sm}:${ss},${sms} --> ${eh}:${em}:${es},${ems}\n` - srt += subText[i]; - srt += "\n\n" - } - console.log(srt); - let blob = new Blob([srt], { - type: "text/plain;charset=utf-8" - }); - saveAs(blob, 'srt.txt'); -} From fb656f63710400805c828c977f03b5b430a361e2 Mon Sep 17 00:00:00 2001 From: x4base Date: Sun, 14 Feb 2021 19:05:26 +0800 Subject: [PATCH 2/2] Make the hot keys case-insensitive --- main.js | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/main.js b/main.js index 990978a..f0e19e7 100644 --- a/main.js +++ b/main.js @@ -16,7 +16,7 @@ function clamp(num) { } const keyMap = { - /* K */ '75': video => { + 'k': video => { if (currentStamping >= lines.length) { return; } @@ -29,21 +29,21 @@ const keyMap = { : null; currentStamping += 1; }, - /* L */ '76': video => { + 'l': video => { lines[currentStamping] = [ lines[currentStamping][0], video.currentTime - reactTime ]; }, - /* I */ '73': () => { + 'i': () => { currentStamping -= 1; }, - /* O*/ '79': () => { + 'o': () => { currentStamping += 1; }, - /* U*/ '85': () => (video.currentTime -= 3), - /* P */ '80': () => (video.currentTime += 3), - /* Q */ '81': () => makeSRT() + 'u': () => (video.currentTime -= 3), + 'p': () => (video.currentTime += 3), + 'q': () => makeSRT() }; function getCurrentStatus() { @@ -52,7 +52,7 @@ function getCurrentStatus() { function execHotkey(keyMap) { document.addEventListener('keypress', function(e) { - const execFn = keyMap[e.keyCode]; + const execFn = keyMap[e.key.toLowerCase()]; if (typeof execFn === 'function') { execFn(video); updateContent();