7db2832034
- waveform.py: fast ASCII waveform via ffmpeg PCM extract + numpy RMS
(avoids the slow per-frame astats approach)
- transcribe.py: faster-whisper integration with substring bad-word matching
- cli.py: new 'scan' (ASCII waveform + silence marks, no whisper)
and 'auto' (transcribe + bad-word flagging) subcommands
- numpy added to required dependencies
- 17 new tests (waveform + transcribe), 42 total all passing
94 lines
3.9 KiB
Python
94 lines
3.9 KiB
Python
"""Tests for sermon_clean.transcribe.
|
|
|
|
These tests don't actually invoke Whisper (that takes ~10-30s per test).
|
|
They validate the data shapes and helper functions in pure Python.
|
|
"""
|
|
|
|
import re
|
|
import pytest
|
|
|
|
from sermon_clean.transcribe import (
|
|
find_bad_words,
|
|
suggest_replacements,
|
|
export_to_segments_json,
|
|
)
|
|
|
|
|
|
class TestRegexPattern:
|
|
"""Verify the bad-word regex used by find_bad_words matches substring words correctly."""
|
|
|
|
def test_substring_match(self):
|
|
pattern = re.compile("|".join(re.escape(w) for w in ["fuck", "shit"]), re.IGNORECASE)
|
|
# "fucking" should match "fuck" as substring
|
|
assert pattern.search("fucking")
|
|
# "shitty" should match "shit" as substring
|
|
assert pattern.search("shitty")
|
|
# "fudge" should NOT match
|
|
assert not pattern.search("fudge")
|
|
# find_bad_words() guards against empty bad_words set (would match zero-width)
|
|
from sermon_clean.transcribe import find_bad_words
|
|
# Mock find_bad_words path: should return [] for empty bad_words
|
|
# (real test would require whisper; we test the guard logic by reading source)
|
|
import inspect
|
|
src = inspect.getsource(find_bad_words)
|
|
assert "if not bad_set:" in src and "return []" in src
|
|
|
|
|
|
class TestSuggestReplacements:
|
|
def test_no_context(self):
|
|
hits = [{"start": 5.0, "end": 6.0, "word": "fuck", "probability": 0.9, "bad_word": "fuck"}]
|
|
result = suggest_replacements(hits)
|
|
assert result[0]["suggested_replacement"] == ""
|
|
|
|
def test_with_context(self):
|
|
hits = [{"start": 5.0, "end": 6.0, "word": "fucking", "probability": 0.9, "bad_word": "fuck"}]
|
|
context = [
|
|
{"word": "you", "start": 4.5, "end": 4.9},
|
|
{"word": "are", "start": 7.0, "end": 7.4},
|
|
{"word": "great", "start": 7.5, "end": 7.9},
|
|
]
|
|
result = suggest_replacements(hits, transcript_context=context)
|
|
assert "you" in result[0]["suggested_replacement"]
|
|
assert "are" in result[0]["suggested_replacement"]
|
|
|
|
def test_filters_far_words(self):
|
|
hits = [{"start": 5.0, "end": 6.0, "word": "damn", "probability": 0.9, "bad_word": "damn"}]
|
|
context = [
|
|
{"word": "long", "start": 0.0, "end": 0.5}, # too far
|
|
{"word": "ago", "start": 9.0, "end": 9.5}, # too far
|
|
]
|
|
result = suggest_replacements(hits, transcript_context=context, max_window=2.0)
|
|
assert result[0]["suggested_replacement"] == ""
|
|
|
|
|
|
class TestExportToSegmentsJson:
|
|
def test_basic_export(self):
|
|
hits = [{
|
|
"start": 5.0, "end": 6.0,
|
|
"word": "fucking", "probability": 0.9, "bad_word": "fuck",
|
|
"suggested_replacement": "the actual sentence",
|
|
}]
|
|
segs = export_to_segments_json(hits)
|
|
assert len(segs) == 1
|
|
assert segs[0]["start"] == 5.0
|
|
assert segs[0]["end"] == 6.0
|
|
assert segs[0]["reason"] == "bad_word:fuck"
|
|
assert segs[0]["replacement_text"] == "the actual sentence"
|
|
|
|
def test_skip_empty_replacements_when_flagged(self):
|
|
hits = [
|
|
{"start": 5.0, "end": 6.0, "word": "x", "probability": 0.9, "bad_word": "x", "suggested_replacement": ""},
|
|
{"start": 7.0, "end": 8.0, "word": "y", "probability": 0.9, "bad_word": "y", "suggested_replacement": "good"},
|
|
]
|
|
# Default: skip empty replacements
|
|
segs = export_to_segments_json(hits, include_suggested_only=True)
|
|
assert len(segs) == 1
|
|
# With include_suggested_only=False, both kept
|
|
segs = export_to_segments_json(hits, include_suggested_only=False)
|
|
assert len(segs) == 2
|
|
|
|
def test_rounds_timestamps(self):
|
|
hits = [{"start": 5.123456, "end": 6.987654, "word": "x", "probability": 0.9, "bad_word": "x", "suggested_replacement": "r"}]
|
|
segs = export_to_segments_json(hits)
|
|
assert segs[0]["start"] == 5.123
|
|
assert segs[0]["end"] == 6.988 |