Phase 4: Export to SRT, WebVTT, ASS, plain text, and Markdown
- Implement ExportService using pysubs2 for caption formats (SRT, VTT, ASS) and custom formatters for plain text and Markdown - SRT exports with [Speaker]: prefix, WebVTT with <v Speaker> voice tags, ASS with color-coded speaker styles - Plain text groups by speaker with labels, Markdown adds timestamps - Add export.start IPC handler and export_transcript Tauri command - Add export dropdown menu in header (appears after transcription) - Uses native save dialog for output file selection - Add pysubs2 dependency - Tests: 30 Python (6 export tests), 6 Rust, 0 Svelte errors Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
133
python/tests/test_export.py
Normal file
133
python/tests/test_export.py
Normal file
@@ -0,0 +1,133 @@
|
||||
"""Tests for the export service."""
|
||||
|
||||
import os
|
||||
import tempfile
|
||||
|
||||
from voice_to_notes.services.export import (
|
||||
ExportRequest,
|
||||
ExportSegment,
|
||||
ExportService,
|
||||
make_export_request,
|
||||
)
|
||||
|
||||
|
||||
def _make_segments():
|
||||
return [
|
||||
ExportSegment(text="Hello there", start_ms=0, end_ms=2000, speaker="SPEAKER_00"),
|
||||
ExportSegment(text="How are you?", start_ms=2500, end_ms=4500, speaker="SPEAKER_01"),
|
||||
ExportSegment(text="I'm fine, thanks", start_ms=5000, end_ms=7500, speaker="SPEAKER_00"),
|
||||
]
|
||||
|
||||
|
||||
def _speaker_map():
|
||||
return {"SPEAKER_00": "Alice", "SPEAKER_01": "Bob"}
|
||||
|
||||
|
||||
def test_export_srt():
|
||||
service = ExportService()
|
||||
with tempfile.NamedTemporaryFile(suffix=".srt", delete=False) as f:
|
||||
path = f.name
|
||||
try:
|
||||
req = ExportRequest(
|
||||
segments=_make_segments(),
|
||||
speakers=_speaker_map(),
|
||||
format="srt",
|
||||
output_path=path,
|
||||
)
|
||||
result = service.export(req)
|
||||
assert result == path
|
||||
content = open(path, encoding="utf-8").read()
|
||||
assert "[Alice]:" in content
|
||||
assert "[Bob]:" in content
|
||||
assert "Hello there" in content
|
||||
finally:
|
||||
os.unlink(path)
|
||||
|
||||
|
||||
def test_export_vtt():
|
||||
service = ExportService()
|
||||
with tempfile.NamedTemporaryFile(suffix=".vtt", delete=False) as f:
|
||||
path = f.name
|
||||
try:
|
||||
req = ExportRequest(
|
||||
segments=_make_segments(),
|
||||
speakers=_speaker_map(),
|
||||
format="vtt",
|
||||
output_path=path,
|
||||
)
|
||||
result = service.export(req)
|
||||
content = open(path, encoding="utf-8").read()
|
||||
assert "<v Alice>" in content
|
||||
assert "<v Bob>" in content
|
||||
finally:
|
||||
os.unlink(path)
|
||||
|
||||
|
||||
def test_export_txt():
|
||||
service = ExportService()
|
||||
with tempfile.NamedTemporaryFile(suffix=".txt", delete=False) as f:
|
||||
path = f.name
|
||||
try:
|
||||
req = ExportRequest(
|
||||
segments=_make_segments(),
|
||||
speakers=_speaker_map(),
|
||||
format="txt",
|
||||
output_path=path,
|
||||
title="Test Transcript",
|
||||
)
|
||||
result = service.export(req)
|
||||
content = open(path, encoding="utf-8").read()
|
||||
assert "Test Transcript" in content
|
||||
assert "Alice:" in content
|
||||
assert "Bob:" in content
|
||||
assert "Hello there" in content
|
||||
finally:
|
||||
os.unlink(path)
|
||||
|
||||
|
||||
def test_export_md():
|
||||
service = ExportService()
|
||||
with tempfile.NamedTemporaryFile(suffix=".md", delete=False) as f:
|
||||
path = f.name
|
||||
try:
|
||||
req = ExportRequest(
|
||||
segments=_make_segments(),
|
||||
speakers=_speaker_map(),
|
||||
format="md",
|
||||
output_path=path,
|
||||
title="Test Transcript",
|
||||
)
|
||||
result = service.export(req)
|
||||
content = open(path, encoding="utf-8").read()
|
||||
assert "# Test Transcript" in content
|
||||
assert "**Alice**" in content
|
||||
assert "**Bob**" in content
|
||||
finally:
|
||||
os.unlink(path)
|
||||
|
||||
|
||||
def test_make_export_request():
|
||||
payload = {
|
||||
"segments": [
|
||||
{"text": "Hello", "start_ms": 0, "end_ms": 1000, "speaker": "SPK_0"},
|
||||
],
|
||||
"speakers": {"SPK_0": "Alice"},
|
||||
"format": "srt",
|
||||
"output_path": "/tmp/test.srt",
|
||||
"title": "Test",
|
||||
}
|
||||
req = make_export_request(payload)
|
||||
assert len(req.segments) == 1
|
||||
assert req.segments[0].speaker == "SPK_0"
|
||||
assert req.speakers["SPK_0"] == "Alice"
|
||||
assert req.format == "srt"
|
||||
|
||||
|
||||
def test_export_unsupported_format():
|
||||
service = ExportService()
|
||||
req = ExportRequest(format="xyz")
|
||||
try:
|
||||
service.export(req)
|
||||
assert False, "Should have raised ValueError"
|
||||
except ValueError as e:
|
||||
assert "Unsupported" in str(e)
|
||||
Reference in New Issue
Block a user