feat: add Minimax Speech as alternative TTS provider for Reels

- Add minimax_config table (migration + schema + storage CRUD)
- Add GET/POST /api/minimax/config routes
- Pass tts_provider + minimax_api_key through ffmpeg service
- Add generate_tts_minimax() in Python using Minimax T2A v2 API
- Fall back to ffsubsync for subtitle sync (no WordBoundary events)
- Add Minimax config card in Settings page
- Add provider toggle (Edge TTS / Minimax) + French voices in new-reel
This commit is contained in:
Michael committed 2026-05-18 11:55:07 +02:00
1 parent 9932561050
commit b7ef4940f7
9 files changed
+364 -29

No files matched your search

+47 -1
View File
@@ -10,7 +10,7 @@ import passport from "./auth";
import { z } from "zod";
import { openRouterService } from "./services/openrouter";
import { minioService as cloudinaryService, buildMinioUrl } from "./services/minio";
import { insertPostSchema, insertScheduledPostSchema, insertSocialPageSchema, insertAiGenerationSchema, insertCloudinaryConfigSchema, updateCloudinaryConfigSchema, insertOpenrouterConfigSchema, updateOpenrouterConfigSchema, insertFreesoundConfigSchema, updateFreesoundConfigSchema, insertUserSchema, postMedia, type SocialPage } from "@shared/schema";
import { insertPostSchema, insertScheduledPostSchema, insertSocialPageSchema, insertAiGenerationSchema, insertCloudinaryConfigSchema, updateCloudinaryConfigSchema, insertOpenrouterConfigSchema, updateOpenrouterConfigSchema, insertFreesoundConfigSchema, updateFreesoundConfigSchema, updateMinimaxConfigSchema, insertMinimaxConfigSchema, insertUserSchema, postMedia, type SocialPage } from "@shared/schema";
import type { User, InsertUser, ScheduledPost, FreesoundConfig } from "@shared/schema";
import { freeSoundService } from "./services/freesound";
import { analyticsRouter } from "./routes/analytics";
@@ -477,6 +477,52 @@ export async function registerRoutes(app: Express): Promise<Server> {
}
});
// Minimax Configuration Routes
app.get("/api/minimax/config", requireAuth, async (req, res) => {
try {
const user = req.user as User;
const config = await storage.getMinimaxConfig(user.id);
if (!config) {
return res.json(null);
}
return res.json({
...config,
apiKey: config.apiKey ? "********" : "",
});
} catch (error) {
console.error("Error fetching Minimax config:", error);
res.status(500).json({ error: "Failed to fetch configuration" });
}
});
app.post("/api/minimax/config", requireAuth, async (req, res) => {
try {
const user = req.user as User;
const data = updateMinimaxConfigSchema.parse(req.body);
let config = await storage.getMinimaxConfig(user.id);
const updateData: any = { ...data };
if (!updateData.apiKey || updateData.apiKey === "********") {
delete updateData.apiKey;
}
if (config) {
config = await storage.updateMinimaxConfig(user.id, updateData);
} else {
if (!data.apiKey) {
return res.status(400).json({ error: "API Key is required" });
}
config = await storage.createMinimaxConfig({ ...data as any, userId: user.id });
}
res.json({ success: true });
} catch (error) {
console.error("Error saving Minimax config:", error);
res.status(500).json({ error: "Failed to save configuration" });
}
});
// Reels & Music Routes
app.use("/api", requireAuth, reelsRouter);
app.use("/api/remotion", requireAuth, remotionRouter);