- 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>
134 lines
3.7 KiB
Python
134 lines
3.7 KiB
Python
"""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)
|