Merge pull request #516 from nightcityblade/fix/issue-324
fix: improve subtitle readability
This commit is contained in:
@@ -96,11 +96,11 @@ function init_element(lines = 3) {
|
|||||||
|
|
||||||
elem_container = document.createElement('div');
|
elem_container = document.createElement('div');
|
||||||
elem_container.id = "transcription";
|
elem_container.id = "transcription";
|
||||||
elem_container.style.cssText = 'padding-top:16px;font-size:18px;position: fixed; top: 85%; left: 50%; transform: translate(-50%, -50%);line-height:18px;width:500px;height:' + (captionLineCount * 30) + 'px;opacity:0.9;z-index:100;background:black;border-radius:10px;color:white;';
|
elem_container.style.cssText = 'padding:0 24px;font-family:Arial,Helvetica,sans-serif;font-size:22px;font-weight:600;line-height:30px;position:fixed;top:85%;left:50%;transform:translate(-50%,-50%);width:min(80vw,900px);min-height:' + (captionLineCount * 30) + 'px;z-index:2147483647;color:white;text-align:center;letter-spacing:0.01em;text-shadow:0 0 2px #000,0 2px 4px rgba(0,0,0,0.95);cursor:move;';
|
||||||
|
|
||||||
for (var i = 0; i <= captionLineCount; i++) {
|
for (var i = 0; i <= captionLineCount; i++) {
|
||||||
elem_text = document.createElement('span');
|
elem_text = document.createElement('span');
|
||||||
elem_text.style.cssText = 'position: absolute;padding-left:16px;padding-right:16px;';
|
elem_text.style.cssText = 'position:absolute;left:50%;transform:translateX(-50%);max-width:100%;padding:2px 14px;background:rgba(0,0,0,0.72);border-radius:6px;box-decoration-break:clone;-webkit-box-decoration-break:clone;';
|
||||||
elem_text.id = "t" + i;
|
elem_text.id = "t" + i;
|
||||||
elem_container.appendChild(elem_text);
|
elem_container.appendChild(elem_text);
|
||||||
|
|
||||||
|
|||||||
@@ -218,11 +218,11 @@ function init_element(lines = 3) {
|
|||||||
|
|
||||||
elem_container = document.createElement('div');
|
elem_container = document.createElement('div');
|
||||||
elem_container.id = "transcription";
|
elem_container.id = "transcription";
|
||||||
elem_container.style.cssText = 'padding-top:16px;font-size:18px;line-height:18px;position:fixed;top:85%;left:50%;transform:translate(-50%,-50%);width:500px;height:' + (captionLineCount * 30) + 'px;opacity:0.9;z-index:100;background:black;border-radius:10px;color:white;';
|
elem_container.style.cssText = 'padding:0 24px;font-family:Arial,Helvetica,sans-serif;font-size:22px;font-weight:600;line-height:30px;position:fixed;top:85%;left:50%;transform:translate(-50%,-50%);width:min(80vw,900px);min-height:' + (captionLineCount * 30) + 'px;z-index:2147483647;color:white;text-align:center;letter-spacing:0.01em;text-shadow:0 0 2px #000,0 2px 4px rgba(0,0,0,0.95);cursor:move;';
|
||||||
|
|
||||||
for (var i = 0; i <= captionLineCount; i++) {
|
for (var i = 0; i <= captionLineCount; i++) {
|
||||||
elem_text = document.createElement('span');
|
elem_text = document.createElement('span');
|
||||||
elem_text.style.cssText = 'position: absolute;padding-left:16px;padding-right:16px;';
|
elem_text.style.cssText = 'position:absolute;left:50%;transform:translateX(-50%);max-width:100%;padding:2px 14px;background:rgba(0,0,0,0.72);border-radius:6px;box-decoration-break:clone;-webkit-box-decoration-break:clone;';
|
||||||
elem_text.id = "t" + i;
|
elem_text.id = "t" + i;
|
||||||
elem_container.appendChild(elem_text);
|
elem_container.appendChild(elem_text);
|
||||||
|
|
||||||
|
|||||||
+29
-1
@@ -4,9 +4,10 @@ import scipy
|
|||||||
import websocket
|
import websocket
|
||||||
import copy
|
import copy
|
||||||
import unittest
|
import unittest
|
||||||
|
from io import StringIO
|
||||||
from unittest.mock import patch, MagicMock
|
from unittest.mock import patch, MagicMock
|
||||||
from whisper_live.client import Client, TranscriptionClient, TranscriptionTeeClient
|
from whisper_live.client import Client, TranscriptionClient, TranscriptionTeeClient
|
||||||
from whisper_live.utils import resample
|
from whisper_live.utils import print_transcript, resample
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
@@ -116,6 +117,33 @@ class TestAudioResampling(unittest.TestCase):
|
|||||||
os.remove(resampled_audio)
|
os.remove(resampled_audio)
|
||||||
|
|
||||||
|
|
||||||
|
class TestPrintTranscript(unittest.TestCase):
|
||||||
|
@patch("whisper_live.utils.shutil.get_terminal_size")
|
||||||
|
@patch("sys.stdout", new_callable=StringIO)
|
||||||
|
def test_print_transcript_respects_narrow_terminals(self, mock_stdout, mock_terminal_size):
|
||||||
|
mock_terminal_size.return_value = os.terminal_size((20, 20))
|
||||||
|
|
||||||
|
print_transcript(["This transcript should still wrap cleanly on a narrow terminal."])
|
||||||
|
|
||||||
|
output_lines = [line for line in mock_stdout.getvalue().splitlines() if line.strip()]
|
||||||
|
self.assertGreater(len(output_lines), 1)
|
||||||
|
self.assertTrue(all(len(line) <= 20 for line in output_lines))
|
||||||
|
|
||||||
|
@patch("whisper_live.utils.shutil.get_terminal_size")
|
||||||
|
@patch("sys.stdout", new_callable=StringIO)
|
||||||
|
def test_print_transcript_indents_timestamp_continuations(self, mock_stdout, mock_terminal_size):
|
||||||
|
mock_terminal_size.return_value = os.terminal_size((32, 20))
|
||||||
|
|
||||||
|
print_transcript(
|
||||||
|
[{"start": "00:00", "end": "00:05", "text": "This line should wrap and keep its timestamp indentation."}],
|
||||||
|
timestamps=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
output_lines = [line.rstrip() for line in mock_stdout.getvalue().splitlines() if line.strip()]
|
||||||
|
self.assertGreater(len(output_lines), 1)
|
||||||
|
self.assertTrue(output_lines[1].startswith(" " * len("[00:00 -> 00:05] ")))
|
||||||
|
|
||||||
|
|
||||||
class TestSendingAudioPacket(BaseTestCase):
|
class TestSendingAudioPacket(BaseTestCase):
|
||||||
def test_send_packet(self):
|
def test_send_packet(self):
|
||||||
self.client.send_packet_to_server(self.mock_audio_packet)
|
self.client.send_packet_to_server(self.mock_audio_packet)
|
||||||
|
|||||||
+19
-6
@@ -1,3 +1,5 @@
|
|||||||
|
import os
|
||||||
|
import shutil
|
||||||
import textwrap
|
import textwrap
|
||||||
import scipy
|
import scipy
|
||||||
import numpy as np
|
import numpy as np
|
||||||
@@ -11,15 +13,26 @@ def clear_screen():
|
|||||||
|
|
||||||
|
|
||||||
def print_transcript(text, translated=False, timestamps=False):
|
def print_transcript(text, translated=False, timestamps=False):
|
||||||
"""Prints formatted transcript text."""
|
"""Prints formatted transcript text in a subtitle-like block."""
|
||||||
|
terminal_width = shutil.get_terminal_size((80, 20)).columns
|
||||||
|
wrap_width = max(10, min(80, terminal_width - 8))
|
||||||
|
|
||||||
if timestamps:
|
if timestamps:
|
||||||
|
lines = []
|
||||||
for t in text:
|
for t in text:
|
||||||
print(f'[{t["start"]} -> {t["end"]}] {t["text"]}')
|
prefix = f'[{t["start"]} -> {t["end"]}] '
|
||||||
|
wrapper = textwrap.TextWrapper(
|
||||||
|
width=wrap_width,
|
||||||
|
subsequent_indent=" " * len(prefix),
|
||||||
|
)
|
||||||
|
lines.extend(wrapper.wrap(f'{prefix}{t["text"]}'))
|
||||||
else:
|
else:
|
||||||
wrapper = textwrap.TextWrapper(width=60)
|
wrapper = textwrap.TextWrapper(width=wrap_width)
|
||||||
text=" ".join(text) if translated else "".join(text)
|
transcript = " ".join(text) if translated else "".join(text)
|
||||||
for line in wrapper.wrap(text=text):
|
lines = wrapper.wrap(text=transcript)
|
||||||
print(line)
|
|
||||||
|
for line in lines[-3:]:
|
||||||
|
print(line.center(terminal_width))
|
||||||
|
|
||||||
|
|
||||||
def format_time(s):
|
def format_time(s):
|
||||||
|
|||||||
Reference in New Issue
Block a user