53 lines
1.9 KiB
Python
53 lines
1.9 KiB
Python
# core/views_tts.py
|
||
|
||
from django.contrib.auth.decorators import login_required, user_passes_test
|
||
from django.http import HttpResponse, HttpResponseBadRequest, HttpResponseForbidden
|
||
from django.shortcuts import get_object_or_404
|
||
from django.views.decorators.http import require_GET
|
||
from .models import Entry
|
||
|
||
# If you use the OpenAI SDK:
|
||
from openai import OpenAI
|
||
import os
|
||
|
||
client = OpenAI(api_key=os.environ.get("OPENAI_API_KEY", ""))
|
||
|
||
def _is_staff(user):
|
||
return user.is_authenticated and (user.is_staff or user.is_superuser)
|
||
|
||
@login_required
|
||
@user_passes_test(_is_staff)
|
||
@require_GET
|
||
def api_tts_for_entry(request, entry_id):
|
||
"""
|
||
Generate MP3 speech for an entry (staff‑only).
|
||
"""
|
||
entry = get_object_or_404(Entry, pk=entry_id)
|
||
|
||
# ---- Build safe combined text (avoid TypeError) ----
|
||
ill = (entry.illustration or "").strip()
|
||
app = (entry.application or "").strip()
|
||
if ill and not any(ill.endswith(p) for p in ".!?…"):
|
||
ill = ill + "."
|
||
combined = " ".join([t for t in (ill, app) if t]).strip()
|
||
if not combined:
|
||
return HttpResponseBadRequest("No text available for this entry.")
|
||
|
||
# ---- Call OpenAI TTS (MP3) ----
|
||
try:
|
||
# Use the streaming helper for broad SDK compatibility.
|
||
# It yields MP3 audio bytes by default.
|
||
with client.audio.speech.with_streaming_response.create(
|
||
model="gpt-4o-mini-tts",
|
||
voice="alloy", # or another supported voice
|
||
input=combined,
|
||
) as resp:
|
||
audio_bytes = resp.read() # raw MP3 bytes
|
||
except Exception as e:
|
||
return HttpResponse(f"OpenAI TTS failed: {e}", status=500, content_type="text/plain")
|
||
|
||
# ---- Serve as audio/mpeg ----
|
||
resp = HttpResponse(audio_bytes, content_type="audio/mpeg")
|
||
resp["Content-Disposition"] = 'inline; filename="entry-tts.mp3"'
|
||
resp["Cache-Control"] = "no-store"
|
||
return resp |