Live subtitle editing

This commit is contained in:
Luke Hubmayer-Werner 2024-12-19 17:16:00 +10:30
parent 0b05f0ce7e
commit 5927496413
4 changed files with 41 additions and 84 deletions

View File

@ -10,6 +10,9 @@ const arrangement_output_element = document.querySelector('#arrangement_output')
const lyrics_output_show_tl_element = document.querySelector('#lyrics_output_show_tl');
const lyrics_output_show_romaji_element = document.querySelector('#lyrics_output_show_romaji');
const lyrics_output_show_kanji_element = document.querySelector('#lyrics_output_show_kanji');
const subtitle_editor_textarea = document.getElementById('subtitle_editor_textarea');
const generate_subtitles = document.getElementById('generate_subtitles');
const download_subtitles = document.getElementById('download_subtitles');
const req_tokenization_url = './tokenize';
// let has_lyrics_changed = false
let tokenized_lyric_lines = [];
@ -171,6 +174,14 @@ update_output_styles();
document.getElementById('btn_syllable').addEventListener('click', () => {console.log(`Syllable button clicked! ${performance.now()}ms`)});
document.getElementById('btn_metronome').addEventListener('click', () => {console.log(`Metronome button clicked! ${performance.now()}ms`)});
function save_text_file(filename, data, mime_type="text/plain") {
const a = document.createElement("a");
a.href = URL.createObjectURL(new Blob([data], {type: mime_type}));
a.setAttribute("download", filename);
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
}
function save_song() {
const data = {
song_title: document.getElementById('song_title').value,
@ -185,15 +196,7 @@ function save_song() {
lyrics_translation: lyrics_tl_input_element.value,
arrangement: arrangement_input_element.value,
};
const a = document.createElement("a");
a.href = URL.createObjectURL(new Blob([JSON.stringify(data, null, 2)], {
type: "application/json", //"text/plain"
}));
a.setAttribute("download", `${data.song_title_en}.json`);
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
save_text_file(`${data.song_title_en}.json`, JSON.stringify(data, null, 2), "application/json");
}
document.getElementById('btn_save_song').addEventListener('click', save_song);
@ -209,7 +212,8 @@ function load_song(data) {
lyrics_input_element.value = data.lyrics ?? '';
lyrics_tl_input_element.value = data.lyrics_translation ?? '';
arrangement_input_element.value = data.arrangement ?? '';
lyrics_input_updated()
// lyrics_input_updated()
lyrics_input_element.dispatchEvent(new Event('change', {}));
}
document.getElementById('load_song').addEventListener('change', event => {
console.log('Attempting to load json file');
@ -218,3 +222,13 @@ document.getElementById('load_song').addEventListener('change', event => {
reader.onload = () => {load_song(JSON.parse(reader.result))};
reader.readAsText(file);
}, false)
download_subtitles.addEventListener('click', () => {
const song_title_en = document.getElementById('song_title_en').value;
save_text_file(`${song_title_en}.ass`, subtitle_editor_textarea.value);
});
generate_subtitles.addEventListener('click', () => {
// TODO: Do something to generate subtitles
subtitle_editor_textarea.dispatchEvent(new Event('change', {})); // This triggers a reload in the player
});

View File

@ -33,9 +33,15 @@ import JASSUB from '../jass/jassub.es.js'
// import font from '../jass/DroidSansJapanese.ttf'
const renderer = new JASSUB({
video: document.querySelector('#local_video'),
subUrl: '../test.ass',
subUrl: '../test.ass', // TODO: debug why it won't load future tracks if the initial one is empty or fails
fonts: [],
// fallbackFont: 'liberation sans',
// fallbackFont: 'Droid Sans Japanese',
// availableFonts: {'DroidSansJapanese': './DroidSansJapanese.ttf'}
})
const subtitle_editor_textarea = document.getElementById('subtitle_editor_textarea');
subtitle_editor_textarea.addEventListener('change', () => {
renderer.setTrack(subtitle_editor_textarea.value);
console.log('Updated subtitles');
});

View File

@ -16,73 +16,3 @@ Style: Translation,Droid Sans,36,&H00FFFFFF,&H000019FF,&H00000000,&H00000000,0,1
[Events]
Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text
Dialogue: 0,00:01:08.00,00:01:20.50,Translation,,,,,,Rain and snow fall from the heavens. Moisten the earth and make it sprout
Dialogue: 0,00:01:08.00,00:01:20.50,Romaji,,,,,,{\k0}{\K50}a{\K50}me{\k0} {\K50}ya{\k0} {\K50}yu{\K50}ki{\k0} {\K50}ga{\k0} {\K50}ten{\k0} {\K50}ka{\K50}ra{\k0} {\K50}fu{\K50}tte{\k0} {\K50}chi{\k0} {\K50}wo{\k0} {\K50}u{\K50}ru{\K50}o{\K50}shi{\k0} {\K50}me{\k0} {\K50}wo{\k0} {\K50}da{\K50}sa{\k0} {\K50}se{\K50}ru
Dialogue: 0,00:01:08.00,00:01:20.50,Kanji,,,,,,{\k0}{\K100}雨{\K50}や{\K100}雪{\K50}が{\K50}天{\K50}か{\K50}ら{\K50}降{\K50}って{\K50}地{\K50}を{\K150}潤{\K50}し{\k0} {\K50}芽{\K50}を{\K50}出{\K50}さ{\K50}せ{\K50}る
Dialogue: 1,00:01:08.00,00:01:20.50,Furigana,,90,1190,,,{\k0}{\K50}あ{\K50}め
Dialogue: 1,00:01:08.00,00:01:20.50,Furigana,,200,1080,,,{\k150}{\K50}ゆ{\K50}き
Dialogue: 1,00:01:08.00,00:01:20.50,Furigana,,310,970,,,{\k300}{\K50}てん
Dialogue: 1,00:01:08.00,00:01:20.50,Furigana,,475,805,,,{\k450}{\K50}ふ
Dialogue: 1,00:01:08.00,00:01:20.50,Furigana,,640,640,,,{\k550}{\K50}ち
Dialogue: 1,00:01:08.00,00:01:20.50,Furigana,,750,530,,,{\k650}{\K50}う{\K50}る{\K50}お
Dialogue: 1,00:01:08.00,00:01:20.50,Furigana,,915,365,,,{\k850}{\K50}め
Dialogue: 1,00:01:08.00,00:01:20.50,Furigana,,1025,255,,,{\k950}{\K50}だ
Dialogue: 0,00:01:20.50,00:01:33.50,Translation,,,,,,Seed to those who sow. And bread to those who eat.
Dialogue: 0,00:01:20.50,00:01:33.50,Romaji,,,,,,{\k0}{\K50}ta{\K50}ne{\K50}ma{\K50}ki{\k0} {\K50}ku{\k0} {\K50}nin{\k0} {\K50}ni{\k0} {\K50}shu{\k0} {\K50}wo{\k0} {\K50}a{\K50}ta{\K50}e{\k0} {\K50}ta{\K50}be{\K50}ru{\k0} {\K50}hi{\K50}to{\k0} {\K50}ni{\k0} {\K50}pan{\k0} {\K50}wo{\k0} {\K50}a{\K50}ta{\K50}e{\K50}ru
Dialogue: 0,00:01:20.50,00:01:33.50,Kanji,,,,,,{\k0}{\K200}種蒔{\K50}く{\K50}人{\K50}に{\k0} {\K50}種{\K50}を{\K100}与{\K50}え{\k0} {\K50}食{\K50}べ{\K50}る{\K100}人{\K50}に{\k0} {\K50}パン{\K50}を{\K100}与{\K50}え{\K50}る
Dialogue: 1,00:01:20.50,00:01:33.50,Furigana,,62,1217,,,{\k0}{\K50}た{\K50}ね{\K50}ま{\K50}き
Dialogue: 1,00:01:20.50,00:01:33.50,Furigana,,200,1080,,,{\k250}{\K50}にん
Dialogue: 1,00:01:20.50,00:01:33.50,Furigana,,365,915,,,{\k350}{\K50}しゅ
Dialogue: 1,00:01:20.50,00:01:33.50,Furigana,,475,805,,,{\k450}{\K50}あ{\K50}た
Dialogue: 1,00:01:20.50,00:01:33.50,Furigana,,640,640,,,{\k600}{\K50}た
Dialogue: 1,00:01:20.50,00:01:33.50,Furigana,,805,475,,,{\k750}{\K50}ひ{\K50}と
Dialogue: 1,00:01:20.50,00:01:33.50,Furigana,,1135,145,,,{\k1000}{\K50}あ{\K50}た
Dialogue: 0,00:01:33.50,00:01:46.00,Translation,,,,,,The Lord's thoughts always go beyond my thoughts
Dialogue: 0,00:01:33.50,00:01:46.00,Romaji,,,,,,{\k0}{\K50}shu{\k0} {\K50}no{\k0} {\K50}o{\K50}mo{\K50}i{\k0} {\K50}ha{\k0} {\K50}i{\K50}tsu{\k0} {\K50}de{\k0} {\K50}mo{\k0} {\K50}wa{\K50}ta{\K50}shi{\k0} {\K50}no{\k0} {\K50}o{\K50}mo{\K50}i{\k0} {\K50}wo{\k0} {\K50}ko{\K50}e{\k0} {\K50}te{\k0} {\K50}i{\K50}ku
Dialogue: 0,00:01:33.50,00:01:46.00,Kanji,,,,,,{\k0}{\K50}主{\K50}の{\K100}思{\K50}い{\K50}は{\K50}い{\K50}つ{\K50}で{\K50}も{\k0} {\K150}私{\K50}の{\K100}思{\K50}い{\K50}を{\K50}超{\K50}え{\K50}て{\K50}い{\K50}く
Dialogue: 1,00:01:33.50,00:01:46.00,Furigana,,117,1162,,,{\k0}{\K50}しゅ
Dialogue: 1,00:01:33.50,00:01:46.00,Furigana,,227,1052,,,{\k100}{\K50}お{\K50}も
Dialogue: 1,00:01:33.50,00:01:46.00,Furigana,,667,612,,,{\k500}{\K50}わ{\K50}た{\K50}し
Dialogue: 1,00:01:33.50,00:01:46.00,Furigana,,777,502,,,{\k700}{\K50}お{\K50}も
Dialogue: 1,00:01:33.50,00:01:46.00,Furigana,,942,337,,,{\k900}{\K50}こ
Dialogue: 0,00:01:46.00,00:01:58.50,Translation,,,,,,The words you have spoken Will not return in vain. And I will see it done.
Dialogue: 0,00:01:46.00,00:01:58.50,Romaji,,,,,,{\k0}{\K50}a{\K50}na{\K50}ta{\k0} {\K50}ga{\k0} {\K50}tsu{\K50}ge{\k0} {\K50}ta{\k0} {\K50}ko{\K50}to{\K50}ba{\k0} {\K50}ha{\k0} {\K50}mu{\K50}na{\K50}shi{\K50}ku{\k0} {\K50}ka{\K50}e{\K50}ru{\k0} {\K50}ko{\K50}to{\k0} {\K50}ha{\k0} {\K50}na{\K50}i
Dialogue: 0,00:01:46.00,00:01:58.50,Kanji,,,,,,{\k0}{\K50}あ{\K50}な{\K50}た{\K50}が{\K50}告{\K50}げ{\K50}た{\K150}言葉{\K50}は{\k0} {\K100}空{\K50}し{\K50}く{\K100}帰{\K50}る{\K50}こ{\K50}と{\K50}は{\K50}な{\K50}い
Dialogue: 1,00:01:46.00,00:01:58.50,Furigana,,310,970,,,{\k200}{\K50}つ
Dialogue: 1,00:01:46.00,00:01:58.50,Furigana,,502,777,,,{\k350}{\K50}こ{\K50}と{\K50}ば
Dialogue: 1,00:01:46.00,00:01:58.50,Furigana,,695,585,,,{\k550}{\K50}む{\K50}な
Dialogue: 1,00:01:46.00,00:01:58.50,Furigana,,860,420,,,{\k750}{\K50}か{\K50}え
Dialogue: 0,00:01:58.50,00:02:12.00,Translation,,,,,,You will succeed in the mission you have given.
Dialogue: 0,00:01:58.50,00:02:12.00,Romaji,,,,,,{\k0}{\K50}ka{\K50}na{\K50}ra{\K50}zu{\k0} {\K50}so{\K50}re{\k0} {\K50}wo{\k0} {\K50}na{\K50}shi{\K50}to{\K50}ge{\k0} {\K50}a{\K50}ta{\K50}e{\k0} {\K50}ta{\k0} {\K50}shi{\K50}me{\K50}i{\k0} {\K50}se{\K50}i{\K50}ko{\K50}u{\k0} {\K50}sa{\k0} {\K50}se{\K50}ru
Dialogue: 0,00:01:58.50,00:02:12.00,Kanji,,,,,,{\k0}{\K150}必{\K50}ず{\K50}そ{\K50}れ{\K50}を{\k0} {\K50}成{\K50}し{\K50}遂{\K50}げ{\k0} {\K100}与{\K50}え{\K50}た{\K150}使命{\k0} {\K200}成功{\K50}さ{\K50}せ{\K50}る
Dialogue: 1,00:01:58.50,00:02:12.00,Furigana,,62,1217,,,{\k0}{\K50}か{\K50}な{\K50}ら
Dialogue: 1,00:01:58.50,00:02:12.00,Furigana,,392,887,,,{\k350}{\K50}な
Dialogue: 1,00:01:58.50,00:02:12.00,Furigana,,502,777,,,{\k450}{\K50}と
Dialogue: 1,00:01:58.50,00:02:12.00,Furigana,,667,612,,,{\k550}{\K50}あ{\K50}た
Dialogue: 1,00:01:58.50,00:02:12.00,Furigana,,860,420,,,{\k750}{\K50}し{\K50}め{\K50}い
Dialogue: 1,00:01:58.50,00:02:12.00,Furigana,,1025,255,,,{\k900}{\K50}せ{\K50}い{\K50}こ{\K50}う
Dialogue: 0,00:02:12.00,00:02:26.50,Translation,,,,,,Praise the Lord, my soul. Rejoice and be glad, and see what the Lord has done.
Dialogue: 0,00:02:12.00,00:02:26.50,Romaji,,,,,,{\k0}{\K50}ho{\K50}me{\K50}ta{\K50}ta{\K50}e{\K50}yo{\k0} {\K50}wa{\K50}ga{\k0} {\K50}ta{\K50}ma{\K50}shi{\K50}i{\k0} {\K50}yo{\k0} {\K50}yo{\K50}ro{\K50}ko{\K50}bi{\k0} {\K50}mo{\K50}te{\k0} {\K50}shu{\k0} {\K50}no{\k0} {\K50}mi{\k0} {\K50}wa{\K50}sa{\k0} {\K50}wo{\k0} {\K50}mi{\K50}yo
Dialogue: 0,00:02:12.00,00:02:26.50,Kanji,,,,,,{\k0}{\K50}褒{\K50}め{\K50}た{\K50}た{\K50}え{\K50}よ{\k0} {\K50}我{\K50}が{\K200}魂{\K50}よ{\k0} {\K150}喜{\K50}び{\K50}持{\K50}て{\k0} {\K50}主{\K50}の{\K50}み{\K50}わ{\K50}さ{\K50}を{\K50}見{\K50}よ
Dialogue: 1,00:02:12.00,00:02:26.50,Furigana,,-20,1300,,,{\k0}{\K50}ほ
Dialogue: 1,00:02:12.00,00:02:26.50,Furigana,,365,915,,,{\k300}{\K50}わ
Dialogue: 1,00:02:12.00,00:02:26.50,Furigana,,475,805,,,{\k400}{\K50}た{\K50}ま{\K50}し{\K50}い
Dialogue: 1,00:02:12.00,00:02:26.50,Furigana,,640,640,,,{\k650}{\K50}よ{\K50}ろ{\K50}こ
Dialogue: 1,00:02:12.00,00:02:26.50,Furigana,,750,530,,,{\k850}{\K50}も
Dialogue: 1,00:02:12.00,00:02:26.50,Furigana,,915,365,,,{\k950}{\K50}しゅ
Dialogue: 1,00:02:12.00,00:02:26.50,Furigana,,1245,35,,,{\k1250}{\K50}み
Dialogue: 0,00:02:26.50,00:02:40.00,Translation,,,,,,Hallelujah. Glory to the Lord.
Dialogue: 0,00:02:26.50,00:02:40.00,Romaji,,,,,,{\k0}{\K50}ha{\K50}re{\K50}ru{\K50}ya{\k0} {\K50}shu{\k0} {\K50}ni{\k0} {\K50}e{\K50}i{\K50}ko{\K50}u{\k0} {\K50}a{\K50}re{\k0} {\K50}i{\K50}ke{\K50}ru{\k0} {\K50}ka{\K50}mi{\k0} {\K50}ni{\k0} {\K50}yu{\K50}da{\K50}ne{\k0} {\K50}te{\k0} {\K50}i{\K50}ki{\K50}ru
Dialogue: 0,00:02:26.50,00:02:40.00,Kanji,,,,,,{\k0}{\K50}ハ{\K50}レ{\K50}ル{\K50}ヤ{\k0} {\K50}主{\K50}に{\K200}栄光{\K50}あ{\K50}れ{\k0} {\K50}生{\K50}け{\K50}る{\K100}神{\K50}に{\k0} {\K100}委{\K50}ね{\K50}て{\K50}生{\K50}き{\K50}る
Dialogue: 1,00:02:26.50,00:02:40.00,Furigana,,282,997,,,{\k200}{\K50}しゅ
Dialogue: 1,00:02:26.50,00:02:40.00,Furigana,,420,860,,,{\k300}{\K50}え{\K50}い{\K50}こ{\K50}う
Dialogue: 1,00:02:26.50,00:02:40.00,Furigana,,667,612,,,{\k600}{\K50}い
Dialogue: 1,00:02:26.50,00:02:40.00,Furigana,,832,447,,,{\k750}{\K50}か{\K50}み
Dialogue: 1,00:02:26.50,00:02:40.00,Furigana,,997,282,,,{\k900}{\K50}ゆ{\K50}だ
Dialogue: 1,00:02:26.50,00:02:40.00,Furigana,,1162,117,,,{\k1100}{\K50}い
Dialogue: 0,00:02:40.00,00:02:47.50,Translation,,,,,,I entrust my life to the living God. The living God is with us.
Dialogue: 0,00:02:40.00,00:02:47.50,Romaji,,,,,,{\k0}{\K50}i{\K50}ke{\K50}ru{\k0} {\K50}ka{\K50}mi{\k0} {\K50}ga{\k0} {\K50}to{\K50}mo{\k0} {\K50}ni{\k0} {\K50}o{\K50}sa{\k0} {\K50}re{\K50}ru
Dialogue: 0,00:02:40.00,00:02:47.50,Kanji,,,,,,{\k0}{\K50}生{\K50}け{\K50}る{\K100}神{\K50}が{\k0} {\K100}共{\K50}に{\K50}お{\K50}さ{\K50}れ{\K50}る
Dialogue: 1,00:02:40.00,00:02:47.50,Furigana,,337,942,,,{\k0}{\K50}い
Dialogue: 1,00:02:40.00,00:02:47.50,Furigana,,502,777,,,{\k150}{\K50}か{\K50}み
Dialogue: 1,00:02:40.00,00:02:47.50,Furigana,,667,612,,,{\k300}{\K50}と{\K50}も

View File

@ -19,19 +19,19 @@
<tr>
<td><label for="song_title">曲名</label></td>
<td><input type="text" id="song_title" placeholder="曲名"></td>
<td><label for="song_title_en">Song title:</label></td>
<td><label for="song_title_en">Song Title:</label></td>
<td><input type="text" id="song_title_en" placeholder="Song title"></td>
</tr>
<tr>
<td><label for="song_lyricist">作詞</label></td>
<td><input type="text" id="song_lyricist" placeholder="作詞"></td>
<td><label for="song_lyricist_en">Song lyricist:</label></td>
<td><label for="song_lyricist_en">Song Lyricist:</label></td>
<td><input type="text" id="song_lyricist_en" placeholder="Song lyricist"></td>
</tr>
<tr>
<td><label for="song_composer">作曲</label></td>
<td><input type="text" id="song_composer" placeholder="作曲"></td>
<td><label for="song_composer_en">Song composer:</label></td>
<td><label for="song_composer_en">Song Composer:</label></td>
<td><input type="text" id="song_composer_en" placeholder="Song composer"></td>
</tr>
</table>
@ -133,6 +133,13 @@
<input id="video_selector_input" type="file" accept="video/*"/>
<video id="local_video" controls autoplay width="640"></video>
</div>
<!-- Manual Subtitle Editor -->
<div id="subtitle_editor">
<h2>Subtitle Editor</h2>
<button id="generate_subtitles">Generate Subtitles</button>
<button id="download_subtitles">Download Subtitles</button>
<br><textarea id="subtitle_editor_textarea" rows="20" cols="80"></textarea>
</div>
</div>
</body>
</html>