diff --git a/Audio-Transcription-Chrome/content.js b/Audio-Transcription-Chrome/content.js index c09987d..acf1038 100644 --- a/Audio-Transcription-Chrome/content.js +++ b/Audio-Transcription-Chrome/content.js @@ -96,11 +96,11 @@ function init_element(lines = 3) { elem_container = document.createElement('div'); 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++) { 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_container.appendChild(elem_text); diff --git a/Audio-Transcription-Firefox/content.js b/Audio-Transcription-Firefox/content.js index 4481927..4891b4c 100644 --- a/Audio-Transcription-Firefox/content.js +++ b/Audio-Transcription-Firefox/content.js @@ -218,11 +218,11 @@ function init_element(lines = 3) { elem_container = document.createElement('div'); 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++) { 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_container.appendChild(elem_text); diff --git a/tests/test_client.py b/tests/test_client.py index 9838668..c1af148 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -4,9 +4,10 @@ import scipy import websocket import copy import unittest +from io import StringIO from unittest.mock import patch, MagicMock 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 @@ -116,6 +117,33 @@ class TestAudioResampling(unittest.TestCase): 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): def test_send_packet(self): self.client.send_packet_to_server(self.mock_audio_packet) diff --git a/whisper_live/utils.py b/whisper_live/utils.py index c7f85ec..4adf269 100644 --- a/whisper_live/utils.py +++ b/whisper_live/utils.py @@ -1,3 +1,5 @@ +import os +import shutil import textwrap import scipy import numpy as np @@ -11,15 +13,26 @@ def clear_screen(): 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: + lines = [] 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: - wrapper = textwrap.TextWrapper(width=60) - text=" ".join(text) if translated else "".join(text) - for line in wrapper.wrap(text=text): - print(line) + wrapper = textwrap.TextWrapper(width=wrap_width) + transcript = " ".join(text) if translated else "".join(text) + lines = wrapper.wrap(text=transcript) + + for line in lines[-3:]: + print(line.center(terminal_width)) def format_time(s):