mirror of
https://github.com/R0m1k3/Socialflow.git
synced 2026-10-11 17:26:45 +02:00
fix cam to 4k et ffmpeg TTS
This commit is contained in:
1 parent
77a6dd7188
commit
074de54c8b
5 files changed
+90
-25
No files matched your search
@@ -1,6 +1,6 @@
|
||||
|
||||
import React, { useRef, useState, useEffect, useCallback } from 'react';
|
||||
import { Camera, StopCircle, RefreshCw, CheckCircle, X, RotateCcw } from 'lucide-react';
|
||||
import { Camera, StopCircle, RefreshCw, CheckCircle, X, RotateCcw, Settings2 } from 'lucide-react';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Card } from '@/components/ui/card';
|
||||
import { useToast } from '@/hooks/use-toast';
|
||||
@@ -10,6 +10,22 @@ interface CameraRecorderProps {
|
||||
onCancel: () => void;
|
||||
}
|
||||
|
||||
// Quality configurations
|
||||
type VideoQuality = "4k" | "1080p" | "720p";
|
||||
|
||||
interface QualityConfig {
|
||||
label: string;
|
||||
width: number;
|
||||
height: number;
|
||||
bitrate: number;
|
||||
}
|
||||
|
||||
const QUALITY_CONFIGS: Record<VideoQuality, QualityConfig> = {
|
||||
"4k": { label: "4K Ultra HD", width: 3840, height: 2160, bitrate: 25000000 },
|
||||
"1080p": { label: "Full HD", width: 1920, height: 1080, bitrate: 8000000 },
|
||||
"720p": { label: "HD 720p", width: 1280, height: 720, bitrate: 5000000 },
|
||||
};
|
||||
|
||||
export function CameraRecorder({ onCapture, onCancel }: CameraRecorderProps) {
|
||||
const videoRef = useRef<HTMLVideoElement>(null);
|
||||
const mediaRecorderRef = useRef<MediaRecorder | null>(null);
|
||||
@@ -23,6 +39,8 @@ export function CameraRecorder({ onCapture, onCancel }: CameraRecorderProps) {
|
||||
const [timer, setTimer] = useState(0);
|
||||
const [facingMode, setFacingMode] = useState<'user' | 'environment'>('environment');
|
||||
const [resolutionInfo, setResolutionInfo] = useState<string>('');
|
||||
const [quality, setQuality] = useState<VideoQuality>('1080p');
|
||||
const [showSettings, setShowSettings] = useState(false);
|
||||
|
||||
// Initialisation de la caméra
|
||||
const startCamera = useCallback(async () => {
|
||||
@@ -31,15 +49,15 @@ export function CameraRecorder({ onCapture, onCancel }: CameraRecorderProps) {
|
||||
stream.getTracks().forEach(track => track.stop());
|
||||
}
|
||||
|
||||
console.log('📸 Starting camera with facing mode:', facingMode);
|
||||
const config = QUALITY_CONFIGS[quality];
|
||||
console.log(`📸 Starting camera: ${config.label}, facing: ${facingMode}`);
|
||||
|
||||
// Constraints pour forcer la meilleure qualité possible (4K ou 1080p)
|
||||
const constraints: MediaStreamConstraints = {
|
||||
audio: true,
|
||||
video: {
|
||||
facingMode: facingMode,
|
||||
width: { ideal: 3840 }, // Try 4K
|
||||
height: { ideal: 2160 },
|
||||
width: { ideal: config.width, min: 640 },
|
||||
height: { ideal: config.height, min: 480 },
|
||||
}
|
||||
};
|
||||
|
||||
@@ -48,7 +66,7 @@ export function CameraRecorder({ onCapture, onCancel }: CameraRecorderProps) {
|
||||
|
||||
if (videoRef.current) {
|
||||
videoRef.current.srcObject = newStream;
|
||||
videoRef.current.volume = 0; // Mute preview to avoid feedback loop
|
||||
videoRef.current.volume = 0;
|
||||
}
|
||||
|
||||
// Check actual resolution
|
||||
@@ -69,7 +87,7 @@ export function CameraRecorder({ onCapture, onCancel }: CameraRecorderProps) {
|
||||
});
|
||||
onCancel();
|
||||
}
|
||||
}, [facingMode, onCancel, toast]);
|
||||
}, [facingMode, quality, onCancel, toast]);
|
||||
|
||||
useEffect(() => {
|
||||
startCamera();
|
||||
@@ -221,14 +239,53 @@ export function CameraRecorder({ onCapture, onCancel }: CameraRecorderProps) {
|
||||
<Button variant="ghost" size="icon" onClick={onCancel} className="text-white">
|
||||
<X className="h-6 w-6" />
|
||||
</Button>
|
||||
<div className="bg-black/30 backdrop-blur-md px-3 py-1 rounded-full text-white font-mono text-sm">
|
||||
{isRecording ? formatTime(timer) : (resolutionInfo || 'Camera')}
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="bg-black/30 backdrop-blur-md px-3 py-1 rounded-full text-white font-mono text-sm">
|
||||
{isRecording ? formatTime(timer) : (resolutionInfo || 'Camera')}
|
||||
</div>
|
||||
{!isRecording && (
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
onClick={() => setShowSettings(!showSettings)}
|
||||
className="text-white"
|
||||
>
|
||||
<Settings2 className="h-5 w-5" />
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
<Button variant="ghost" size="icon" onClick={switchCamera} className="text-white">
|
||||
<RefreshCw className="h-6 w-6" />
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{/* Quality Settings Panel */}
|
||||
{showSettings && !isRecording && (
|
||||
<div className="absolute top-16 left-1/2 -translate-x-1/2 z-20 bg-black/80 backdrop-blur-md rounded-xl p-4 min-w-[200px]">
|
||||
<p className="text-white text-sm font-medium mb-3 text-center">Qualité Vidéo</p>
|
||||
<div className="flex gap-2">
|
||||
{(Object.keys(QUALITY_CONFIGS) as VideoQuality[]).map((q) => (
|
||||
<button
|
||||
key={q}
|
||||
onClick={() => {
|
||||
setQuality(q);
|
||||
setShowSettings(false);
|
||||
}}
|
||||
className={`px-3 py-2 rounded-lg text-sm font-medium transition-all ${quality === q
|
||||
? 'bg-white text-black'
|
||||
: 'bg-white/20 text-white hover:bg-white/30'
|
||||
}`}
|
||||
>
|
||||
{q.toUpperCase()}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
<p className="text-white/60 text-xs mt-2 text-center">
|
||||
{QUALITY_CONFIGS[quality].label}
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Viewfinder */}
|
||||
<div className="relative flex-1 bg-black flex items-center justify-center overflow-hidden">
|
||||
<video
|
||||
|
||||
@@ -28,6 +28,7 @@ import { useToast } from "@/hooks/use-toast";
|
||||
import { apiRequest, queryClient } from "@/lib/queryClient";
|
||||
import type { SocialPage, Media } from "@shared/schema";
|
||||
import { DateTimePicker } from "@/components/datetime-picker";
|
||||
import { CameraRecorder } from "@/components/camera-recorder";
|
||||
|
||||
interface MusicTrack {
|
||||
id: string;
|
||||
@@ -331,6 +332,17 @@ export default function NewReel() {
|
||||
|
||||
return (
|
||||
<div className="flex h-screen overflow-hidden bg-background">
|
||||
{/* Camera Recorder Modal */}
|
||||
{showCamera && (
|
||||
<CameraRecorder
|
||||
onCapture={(file) => {
|
||||
uploadMutation.mutate(file);
|
||||
setShowCamera(false);
|
||||
}}
|
||||
onCancel={() => setShowCamera(false)}
|
||||
/>
|
||||
)}
|
||||
|
||||
{sidebarOpen && (
|
||||
<div
|
||||
className="fixed inset-0 bg-black/50 z-40 lg:hidden"
|
||||
|
||||
@@ -60,7 +60,7 @@ async def generate_tts_with_subs(
|
||||
submaker.feed(chunk)
|
||||
|
||||
with open(vtt_path, "w", encoding="utf-8") as file:
|
||||
file.write(submaker.generate_subs())
|
||||
file.write(submaker.get_subs())
|
||||
|
||||
|
||||
class ReelResponse(BaseModel):
|
||||
@@ -228,11 +228,8 @@ async def process_reel(request: ReelRequest, x_api_key: str = Header(None)):
|
||||
cmd.extend(["-i", str(tts_audio_path)])
|
||||
|
||||
# Prepare inputs for mixing
|
||||
if has_original_audio:
|
||||
# Original audio usually needs to be lowered if there is music/voice
|
||||
vol = 0.6 if (has_music or has_tts) else 1.0
|
||||
filter_complex_parts.append(f"[0:a]volume={vol}[a0]")
|
||||
mix_inputs += 1
|
||||
# ALWAYS ignore original video audio - only use music and/or TTS
|
||||
# (Original audio is never used per user request)
|
||||
|
||||
if has_music:
|
||||
# Music volume
|
||||
@@ -249,8 +246,7 @@ async def process_reel(request: ReelRequest, x_api_key: str = Header(None)):
|
||||
# Build mix command
|
||||
if mix_inputs > 0:
|
||||
inputs_str = ""
|
||||
if has_original_audio:
|
||||
inputs_str += "[a0]"
|
||||
# Note: Original audio is never mixed in (always muted)
|
||||
if has_music:
|
||||
inputs_str += "[a1]"
|
||||
if has_tts:
|
||||
|
||||
@@ -3,6 +3,5 @@ uvicorn==0.27.0
|
||||
python-multipart==0.0.6
|
||||
requests==2.31.0
|
||||
pydantic==2.6.0
|
||||
pydantic==2.6.0
|
||||
edge-tts
|
||||
emoji
|
||||
@@ -48,16 +48,17 @@ Ajout d'une fonctionnalité complète de création de Reels Facebook permettant
|
||||
- [x] Ajouter la voix off (TTS) gratuite via Edge-TTS
|
||||
- [x] FFmpeg Service: Installer edge-tts et implémenter le mixage audio
|
||||
- [x] Backend: Supporter les options TTS (enable, voice)
|
||||
- [ ] Améliorer le TTS (Genre, Sync, Cleanup)
|
||||
- [ ] FFmpeg: Nettoyer le texte (No hashtags/emojis) avant TTS
|
||||
- [ ] FFmpeg: Générer fichier VTT pour synchro sous-titres
|
||||
- [x] Améliorer le TTS (Genre, Sync, Cleanup)
|
||||
- [x] FFmpeg: Nettoyer le texte (No hashtags/emojis) avant TTS
|
||||
- [x] FFmpeg: Générer fichier VTT pour synchro sous-titres
|
||||
- [x] FFmpeg: Enlever le son original quand TTS activé
|
||||
- [ ] Frontend: Sélecteur de voix (Homme/Femme)
|
||||
- [/] Ajouter la valeur 'reel' à l'enum post_type en base de données
|
||||
- [x] Amélioration UI Musique (Pagination, Preview Audio)
|
||||
- [ ] **Improve Camera Capture Quality** (User Request)
|
||||
- [ ] Create `CameraRecorder` component with 4K/1080p constraints
|
||||
- [ ] Integrate CameraRecorder into `new-reel.tsx`
|
||||
- [ ] Test camera permissions and recording flow
|
||||
- [x] **Improve Camera Capture Quality** (User Request)
|
||||
- [x] Create `CameraRecorder` component with 4K/1080p/720p quality selector
|
||||
- [x] Integrate CameraRecorder into `new-reel.tsx`
|
||||
- [x] Add quality settings panel in camera UI
|
||||
|
||||
## Progress Log
|
||||
|
||||
|
||||
Reference in new issue
Block a user