mirror of
https://github.com/R0m1k3/Socialflow.git
synced 2026-10-24 15:38:10 +02:00
feat(reels): montage Remotion unifié, aperçu en direct, tests et CI
Montage (étape 3) : - Nouvelle composition ReelVideo : sous-titres animés mot à mot (3 styles : Impact, Surligné, Épuré), logo, effet de fin et fondu, en React - Le service Python prépare l'image (recadrage, HDR, stabilisation, dernière image figée) et la piste son finale (/prepare-reel) ; Remotion compose - Reels d'images sur les mêmes composants, avec le vrai minutage de la voix - Police Montserrat embarquée (plus de dépendance à Google Fonts) - REEL_RENDERER=ffmpeg conserve le rendu FFmpeg, plus rapide, en secours - Script de pré-bundle réparé (échouait en silence : require en ESM) Interface (étape 4) : - Aperçu en direct avec @remotion/player, identique au rendu final ; la voix testée cale les sous-titres, sinon minutage estimé - Choix du style de sous-titres sur les 4 pages Reel - Vraie progression : étape réelle du rendu, échecs visibles 24 h avec leur cause ; fin de la barre simulée et du faux « publié avec succès » Outillage (étape 5) : - Tests vitest (minutage identique à Python, validation, sécurité) et pytest ; CI GitHub Actions (tsc, tests, build, ruff) - Captures d'écran, out.mp4 et scripts de test retirés de la racine Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018Ze4bs7tpF1KGWUk6ZZSZ4
This commit is contained in:
64 files changed
+2488
-782
No files matched your search
+22
-8
@@ -3,6 +3,7 @@ import {
|
||||
socialPages,
|
||||
media,
|
||||
posts,
|
||||
reelJobs,
|
||||
postMedia,
|
||||
scheduledPosts,
|
||||
aiGenerations,
|
||||
@@ -44,7 +45,7 @@ import {
|
||||
type InsertFacebookConfig,
|
||||
} from "@shared/schema";
|
||||
import { db } from "./db";
|
||||
import { eq, and, gte, lte, desc, asc, isNull, isNotNull, inArray, notInArray, getTableColumns, sql, type SQL } from "drizzle-orm";
|
||||
import { eq, and, or, gte, lte, desc, asc, isNull, isNotNull, inArray, notInArray, getTableColumns, sql, type SQL } from "drizzle-orm";
|
||||
import { encrypt, decrypt, isEncrypted } from "./utils/encryption";
|
||||
|
||||
/**
|
||||
@@ -147,7 +148,7 @@ export interface IStorage {
|
||||
getPosts(userId: string): Promise<Post[]>;
|
||||
getPost(id: string): Promise<Post | undefined>;
|
||||
getPostWithMedia(id: string): Promise<{ post: Post; media: Media[] } | undefined>;
|
||||
getOngoingReelPosts(userId: string): Promise<Post[]>;
|
||||
getOngoingReelPosts(userId: string): Promise<(Post & { generationStep: string | null })[]>;
|
||||
createPost(post: InsertPost): Promise<Post>;
|
||||
updatePost(id: string, post: Partial<InsertPost>): Promise<Post>;
|
||||
updatePostGenerationStatus(id: string, status: string, progress: number, error?: string): Promise<Post>;
|
||||
@@ -551,21 +552,34 @@ export class DatabaseStorage implements IStorage {
|
||||
}
|
||||
|
||||
/**
|
||||
* Reels dont la génération est en cours. Filtré en SQL : la route chargeait
|
||||
* tous les posts de l'utilisateur pour n'en garder qu'une poignée, toutes les
|
||||
* trois secondes.
|
||||
* Reels dont la génération est en cours, plus ceux qui ont échoué dans les
|
||||
* dernières 24 h (pour que l'utilisateur en voie la cause), avec l'étape
|
||||
* courante du rendu. Filtré en SQL : la route est interrogée toutes les
|
||||
* trois secondes pendant un rendu.
|
||||
*/
|
||||
async getOngoingReelPosts(userId: string): Promise<Post[]> {
|
||||
return await db
|
||||
async getOngoingReelPosts(userId: string): Promise<(Post & { generationStep: string | null })[]> {
|
||||
const since = new Date(Date.now() - 24 * 60 * 60 * 1000);
|
||||
const rows = await db
|
||||
.select()
|
||||
.from(posts)
|
||||
.where(
|
||||
and(
|
||||
eq(posts.userId, userId),
|
||||
inArray(posts.generationStatus, ['processing', 'pending'])
|
||||
or(
|
||||
inArray(posts.generationStatus, ['processing', 'pending']),
|
||||
and(eq(posts.generationStatus, 'failed'), gte(posts.updatedAt, since), isNotNull(posts.generationError)),
|
||||
),
|
||||
)
|
||||
)
|
||||
.orderBy(desc(posts.createdAt));
|
||||
if (rows.length === 0) return [];
|
||||
|
||||
const jobs = await db
|
||||
.select({ postId: reelJobs.postId, step: reelJobs.step })
|
||||
.from(reelJobs)
|
||||
.where(inArray(reelJobs.postId, rows.map((p) => p.id)));
|
||||
const stepByPost = new Map(jobs.map((j) => [j.postId, j.step]));
|
||||
return rows.map((post) => ({ ...post, generationStep: stepByPost.get(post.id) ?? null }));
|
||||
}
|
||||
|
||||
// AI Generations
|
||||
|
||||
Reference in new issue
Block a user