refactor(scraper): rename /ui/audio*, /ui/chapter-text routes to /api/ namespace

This commit is contained in:
Admin
2026-03-02 22:01:52 +05:00
parent 59b1cfab1d
commit cb90771248
2 changed files with 11 additions and 11 deletions

View File

@@ -130,8 +130,8 @@ func (s *Server) ListenAndServe(ctx context.Context) error {
mux.HandleFunc("POST /ui/scrape/book", s.handleUIScrapeBook) mux.HandleFunc("POST /ui/scrape/book", s.handleUIScrapeBook)
mux.HandleFunc("GET /ui/scrape/status", s.handleUIScrapeStatus) mux.HandleFunc("GET /ui/scrape/status", s.handleUIScrapeStatus)
mux.HandleFunc("GET /ui/ranking/status", s.handleRankingStatus) mux.HandleFunc("GET /ui/ranking/status", s.handleRankingStatus)
// Plain-text chapter content for browser-side TTS // Plain-text chapter content (used server-side for audio generation)
mux.HandleFunc("GET /ui/chapter-text/{slug}/{n}", s.handleChapterText) mux.HandleFunc("GET /api/chapter-text/{slug}/{n}", s.handleChapterText)
// Server-side audio generation via Kokoro /v1/audio/speech. // Server-side audio generation via Kokoro /v1/audio/speech.
// Generation can take several minutes, so wrap in its own timeout handler. // Generation can take several minutes, so wrap in its own timeout handler.
audioGenHandler := http.TimeoutHandler( audioGenHandler := http.TimeoutHandler(
@@ -139,9 +139,9 @@ func (s *Server) ListenAndServe(ctx context.Context) error {
10*time.Minute, 10*time.Minute,
`{"error":"audio generation timed out"}`, `{"error":"audio generation timed out"}`,
) )
mux.Handle("POST /ui/audio/{slug}/{n}", audioGenHandler) mux.Handle("POST /api/audio/{slug}/{n}", audioGenHandler)
// Proxy route: fetches the generated file from Kokoro /v1/download/{filename}. // Proxy route: fetches the generated file from Kokoro /v1/download/{filename}.
mux.HandleFunc("GET /ui/audio-proxy/{slug}/{n}", s.handleAudioProxy) mux.HandleFunc("GET /api/audio-proxy/{slug}/{n}", s.handleAudioProxy)
srv := &http.Server{ srv := &http.Server{
Addr: s.addr, Addr: s.addr,
@@ -289,7 +289,7 @@ func (s *Server) handleDeleteProgress(w http.ResponseWriter, r *http.Request) {
} }
// handleChapterText returns the plain text of a chapter (markdown stripped) // handleChapterText returns the plain text of a chapter (markdown stripped)
// for browser-side TTS. The browser POSTs this directly to Kokoro-FastAPI. // for server-side audio generation. Called by handleAudioGenerate internally.
func (s *Server) handleChapterText(w http.ResponseWriter, r *http.Request) { func (s *Server) handleChapterText(w http.ResponseWriter, r *http.Request) {
slug := r.PathValue("slug") slug := r.PathValue("slug")
n, err := strconv.Atoi(r.PathValue("n")) n, err := strconv.Atoi(r.PathValue("n"))
@@ -309,7 +309,7 @@ func (s *Server) handleChapterText(w http.ResponseWriter, r *http.Request) {
// ─── Audio generation via Kokoro /v1/audio/speech ──────────────────────────── // ─── Audio generation via Kokoro /v1/audio/speech ────────────────────────────
// //
// handleAudioGenerate handles POST /ui/audio/{slug}/{n}. // handleAudioGenerate handles POST /api/audio/{slug}/{n}.
// //
// It calls Kokoro's POST /v1/audio/speech with return_download_link=true. // It calls Kokoro's POST /v1/audio/speech with return_download_link=true.
// Kokoro generates the audio, saves it to its own temp storage, and returns // Kokoro generates the audio, saves it to its own temp storage, and returns
@@ -463,9 +463,9 @@ func (s *Server) generateSpeech(ctx context.Context, text, voice string, speed f
} }
// writeAudioResponse writes the JSON response for a generated audio chapter. // writeAudioResponse writes the JSON response for a generated audio chapter.
// The URL points to our proxy handler which fetches from Kokoro on demand. // The URL points to our proxy handler GET /api/audio-proxy/{slug}/{n}.
func (s *Server) writeAudioResponse(w http.ResponseWriter, slug string, n int, voice string, speed float64, filename string) { func (s *Server) writeAudioResponse(w http.ResponseWriter, slug string, n int, voice string, speed float64, filename string) {
proxyURL := fmt.Sprintf("/ui/audio-proxy/%s/%d?voice=%s&speed=%.1f", slug, n, voice, speed) proxyURL := fmt.Sprintf("/api/audio-proxy/%s/%d?voice=%s&speed=%.1f", slug, n, voice, speed)
w.Header().Set("Content-Type", "application/json") w.Header().Set("Content-Type", "application/json")
_ = json.NewEncoder(w).Encode(map[string]interface{}{ _ = json.NewEncoder(w).Encode(map[string]interface{}{
"url": proxyURL, "url": proxyURL,
@@ -473,7 +473,7 @@ func (s *Server) writeAudioResponse(w http.ResponseWriter, slug string, n int, v
}) })
} }
// handleAudioProxy handles GET /ui/audio-proxy/{slug}/{n}. // handleAudioProxy handles GET /api/audio-proxy/{slug}/{n}.
// It looks up the Kokoro download filename for this chapter (voice/speed) and // It looks up the Kokoro download filename for this chapter (voice/speed) and
// proxies GET /v1/download/{filename} from the Kokoro server back to the browser. // proxies GET /v1/download/{filename} from the Kokoro server back to the browser.
func (s *Server) handleAudioProxy(w http.ResponseWriter, r *http.Request) { func (s *Server) handleAudioProxy(w http.ResponseWriter, r *http.Request) {

View File

@@ -40,8 +40,8 @@ export const POST: RequestHandler = async ({ params, request }) => {
const data = (await scraperRes.json()) as { url: string; filename: string }; const data = (await scraperRes.json()) as { url: string; filename: string };
// Rewrite the proxy URL from the scraper's /ui/audio-proxy/... to our own // The scraper returns a proxy URL pointing to /api/audio-proxy/... — we rewrite
// /api/audio/[slug]/[n]?voice=...&speed=... so the browser never calls the scraper directly. // it to our own /api/audio/[slug]/[n]?... so the browser never calls the scraper directly.
const voice = body.voice ?? ''; const voice = body.voice ?? '';
const speed = body.speed ?? 1.0; const speed = body.speed ?? 1.0;
const qs = new URLSearchParams(); const qs = new URLSearchParams();