139_spanish-voice-trainer/anki_exporter.py

92 lines
No EOL
3.7 KiB
Python

# anki_exporter.py
import os
import tempfile
import subprocess
import shutil
import genanki
def compile_anki_package(records, output_path, deck_name):
"""
Compiles database records into an .apkg package using native macOS TTS.
Uses 'Monica' for Spanish targets and the default premium system voice for English.
"""
# Create a unique random Model ID and Deck ID for genanki
model_id = 1684329011
deck_id = 1684329012
# Define the Anki Card Layout structure with audio fields
anki_model = genanki.Model(
model_id,
'Spanish Voice Trainer Model',
fields=[
{'name': 'EnglishText'},
{'name': 'SpanishText'},
{'name': 'Notes'},
{'name': 'EnglishAudio'},
{'name': 'SpanishAudio'}
],
templates=[
{
'name': 'Card 1',
'qfmt': '<div style="font-family: Arial; font-size: 20px; text-align: center; color: #34495E;">'
'Translate to Spanish:<br><br><b>{{EnglishText}}</b></div><div style="display:none;">{{EnglishAudio}}</div>',
'afmt': '{{FrontSide}}<hr id="answer">'
'<div style="font-family: Arial; font-size: 28px; text-align: center; color: #2980B9; font-weight: bold;">'
'{{SpanishText}}</div><br>'
'<div style="font-family: Arial; font-size: 14px; text-align: center; color: #7F8C8D; font-style: italic;">'
'{{Notes}}</div><br>'
'<div style="text-align: center;">{{SpanishAudio}}</div>',
},
]
)
deck = genanki.Deck(deck_id, deck_name)
media_files = []
# Process all records inside a secure temporary directory workspace
with tempfile.TemporaryDirectory() as tmpdir:
for idx, record in enumerate(records):
en_text = record["en_text"]
es_text = record["es_text"]
notes = f"Context: {record['source_context'] or ''} | {record['notes'] or ''}".strip(" | ")
# Generate unique filenames for the media assets
en_audio_filename = f"en_audio_{idx}.mp3"
es_audio_filename = f"es_audio_{idx}.mp3"
en_audio_path = os.path.join(tmpdir, en_audio_filename)
es_audio_path = os.path.join(tmpdir, es_audio_filename)
try:
# 1. Render English Audio using native macOS text-to-speech engine
subprocess.run(
["say", "-o", en_audio_path, "--data-format=Iface", en_text],
check=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL
)
media_files.append(en_audio_path)
en_audio_field = f"[sound:{en_audio_filename}]"
except Exception:
en_audio_field = ""
try:
# 2. Render Spanish Audio explicitly targeting the Monica voice profile
subprocess.run(
["say", "-v", "Monica", "-o", es_audio_path, "--data-format=Iface", es_text],
check=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL
)
media_files.append(es_audio_path)
es_audio_field = f"[sound:{es_audio_filename}]"
except Exception:
es_audio_field = ""
# Build the card note stack
note = genanki.Note(
model=anki_model,
fields=[en_text, es_text, notes, en_audio_field, es_audio_field]
)
deck.add_note(note)
# Build package collection mapping archive pipelines
package = genanki.Package(deck)
package.media_files = media_files
package.write_to_file(output_path)