everydaycats commited on
Commit
eccb28b
Β·
verified Β·
1 Parent(s): f2f082a

Update apps/viralcat.js

Browse files
Files changed (1) hide show
  1. apps/viralcat.js +82 -80
apps/viralcat.js CHANGED
@@ -23,9 +23,7 @@ const getBase64Payload = (req) => {
23
  return null;
24
  };
25
 
26
- const sleep = (ms) => new Promise(r => setTimeout(r, ms));
27
-
28
- const handleMediaProcessing = async (req, num_frames = 15, get_transcript = true) => {
29
  const video_base64 = getBase64Payload(req);
30
  if (!video_base64) throw new Error("No video data found in request");
31
 
@@ -40,78 +38,86 @@ const handleMediaProcessing = async (req, num_frames = 15, get_transcript = true
40
  return mediaData;
41
  };
42
 
43
- // ── SEQUENTIAL MAP-REDUCE WITH AURA EXTRACTION ──
44
  const performDeepAnalysis = async (frames, transcript, targetNiche = null) => {
45
- console.log(`[DEEP ANALYZE] Starting Map-Reduce on ${frames.length} frames...`);
46
 
47
- const cappedFrames = frames.slice(0, 15);
 
48
  const chunks =[];
49
- for (let i = 0; i < cappedFrames.length; i += 3) {
50
- chunks.push(cappedFrames.slice(i, i + 3));
 
 
51
  }
52
 
53
- const sceneDescriptions =[];
54
-
55
- for (let i = 0; i < chunks.length; i++) {
56
- console.log(`-> Processing Batch ${i + 1}/${chunks.length}`);
57
- const prompt = `Analyze this 3-image sequence. Describe the action, camera movement, lighting, and subjects briefly.`;
58
 
59
- const res = await generateCompletion({
60
- model: "qwen",
61
  prompt,
62
- system_prompt: "You are a precise video frame analyzer.",
63
- images: chunks[i]
64
  });
65
-
66
- sceneDescriptions.push(`[Scene Segment ${i + 1}]:\n${res.data}`);
67
- if (i < chunks.length - 1) await sleep(1500);
68
- }
 
 
 
 
 
 
 
 
 
69
 
70
  const combinedVisualData = sceneDescriptions.join("\n\n");
71
  console.log(`[DEEP ANALYZE] Map phase complete. Synthesizing output...`);
72
 
73
  if (targetNiche) {
74
- // 🚨 PROMPT ENG: Force the AI to establish the psychological hooks first!
75
  const finalPrompt = `
76
  TASK: Rewrite an existing viral video script for a NEW NICHE.
77
  [ORIGINAL VISUAL TIMELINE]
78
- ${combinedVisualData}
79
- [ORIGINAL AUDIO TRANSCRIPT]
80
  ${transcript}
81
  [NEW TARGET NICHE]
82
  User's Niche: "${targetNiche}"
83
 
84
  INSTRUCTIONS:
85
- 1. DECONSTRUCT THE AURA: Analyze exactly what made the original video go viral. Look at the total time, pacing, specific psychological triggers, and choice of words in the hook.
86
- 2. Map those EXACT psychological triggers, pacing, and hooks onto the user's new niche.
87
- 3. DO NOT summarize the original video. Write a BRAND NEW script.
88
-
89
- FORMAT EXACTLY LIKE THIS:
90
- ## 🧠 The Viral Aura[Briefly explain the psychology, word choice, and timing that made the original work, and how you applied it to this new script.]
91
 
 
 
 
92
  ## 🎬 The Hook (0-3s)
93
- **Visual:**[What to show]
94
  **Audio:** [What to say]
95
-
96
  ## πŸ“– The Body
97
- **Visual:**[What to show]
98
- **Audio:** [What to say]
99
 
100
- ## πŸ’₯ The Call to Action
101
- **Visual:** [What to show]
102
- **Audio:** [What to say]`;
103
-
104
- return await generateCompletion({ model: "maverick", prompt: finalPrompt, system_prompt: "You are Viral Cat πŸ“ˆπŸˆ." });
105
  } else {
106
- // 🚨 PROMPT ENG: Extract the Viral Factors for the Database Feed
107
  const finalPrompt = `[VISUAL TIMELINE]\n${combinedVisualData}\n[TRANSCRIPT]\n${transcript}\n
108
- Format exactly:
109
- TRANSCRIPT:[Cleaned dialogue]
110
- ENVIRONMENT:[Setting/lighting]
111
- PACING:[Edit style]
112
- VIRAL_FACTORS:[Analyze the psychology, specific word choices, hooks, and timing that made this viral]`;
 
 
113
 
114
- return await generateCompletion({ model: "maverick", prompt: finalPrompt, system_prompt: "Elite metadata extractor." });
115
  }
116
  };
117
 
@@ -145,32 +151,36 @@ router.post('/admin/template', upload.single('video_file'), async (req, res) =>
145
  const { title, video_url, platform, use_deep_analysis } = req.body;
146
  const isDeep = (use_deep_analysis === 'true' || use_deep_analysis === true);
147
 
148
- const framesToExtract = isDeep ? 15 : 3;
 
149
  const mediaData = await handleMediaProcessing(req, framesToExtract, true);
150
 
151
  let aiResult;
152
  if (isDeep) {
153
  aiResult = await performDeepAnalysis(mediaData.frames, mediaData.transcript);
154
  } else {
155
- // Fast extraction includes VIRAL_FACTORS now too
156
- const fastFrames = mediaData.frames.slice(0, 3);
157
- const aiPrompt = `Analyze transcript and frames.\nTRANSCRIPT:\n${mediaData.transcript}\n\nFormat exactly:\nTRANSCRIPT:[dialogue]\nENVIRONMENT:[visuals]\nPACING:[style]\nVIRAL_FACTORS:[Identify psychological hooks and word choices]`;
158
- aiResult = await generateCompletion({ model: "maverick", prompt: aiPrompt, images: fastFrames, system_prompt: "Fast metadata extractor." });
 
 
 
 
 
 
159
  }
160
 
161
- // Parse 4 sections now
162
- const transcriptMatch = aiResult.data.match(/TRANSCRIPT:\s*(.*?)(?=ENVIRONMENT:)/is);
163
- const envMatch = aiResult.data.match(/ENVIRONMENT:\s*(.*?)(?=PACING:)/is);
164
- const pacingMatch = aiResult.data.match(/PACING:\s*(.*?)(?=VIRAL_FACTORS:)/is);
165
- const auraMatch = aiResult.data.match(/VIRAL_FACTORS:\s*(.*)/is);
166
 
167
  const { error } = await supabase.from('viral_cat_trending').insert([{
168
  platform, video_url, title,
169
  thumbnail_url: `data:image/jpeg;base64,${mediaData.thumbnail}`,
170
- transcript: transcriptMatch ? transcriptMatch[1].trim() : mediaData.transcript,
171
- ai_environment_data: envMatch ? envMatch[1].trim() : "N/A",
172
- ai_scene_changes: pacingMatch ? pacingMatch[1].trim() : "N/A",
173
- ai_viral_factors: auraMatch ? auraMatch[1].trim() : "N/A" // 🚨 Saving the Aura!
174
  }]);
175
 
176
  if (error) throw error;
@@ -185,39 +195,31 @@ router.post('/remix', async (req, res) => {
185
  try {
186
  const { user_input, transcript, ai_environment_data, ai_scene_changes, ai_viral_factors } = req.body;
187
 
188
- // Feed Remix also gets the Aura upgrade
189
  const aiPrompt = `TASK: Rewrite script for a NEW NICHE.
190
  [ORIGINAL DATA]
191
  Pacing: ${ai_scene_changes}
192
  Environment: ${ai_environment_data}
193
  Viral Psychology (The Aura): ${ai_viral_factors}
194
- Transcript:
195
- ${transcript}[NEW NICHE]
196
  "${user_input}"
197
 
198
  INSTRUCTIONS: Map the original viral psychology and pacing onto the user's new niche.
199
- Format in Markdown:
200
- ## 🧠 The Viral Aura
201
- [Explain the psychology you used]
202
- ## 🎬 The Hook
203
- **Visual:**
204
- **Audio:**
205
- ## πŸ“– The Body
206
- **Visual:**
207
- **Audio:**
208
- ## πŸ’₯ The Call to Action
209
- **Visual:**
210
- **Audio:**`;
211
-
212
- const result = await generateCompletion({ model: "maverick", prompt: aiPrompt, system_prompt: "You are Viral Cat πŸ“ˆπŸˆ. Adapt viral formats to new niches." });
213
- res.json(result);
214
  } catch (err) { res.status(500).json({ success: false, error: err.message }); }
215
  });
216
 
217
  router.post('/custom_remix', upload.single('video_file'), async (req, res) => {
218
  try {
219
  const { user_niche } = req.body;
220
- const mediaData = await handleMediaProcessing(req, 15, true);
221
  const result = await performDeepAnalysis(mediaData.frames, mediaData.transcript, user_niche);
222
 
223
  res.json({ success: true, data: result.data, thumbnail: mediaData.thumbnail });
@@ -232,7 +234,7 @@ router.delete('/admin/template/:id', async (req, res) => {
232
  res.json({ success: true });
233
  });
234
 
235
- // Admin Dashboard HTML
236
  router.get('/admin', (req, res) => {
237
  res.send(`<!DOCTYPE html><html><head><title>Viral Cat Admin</title><script src="https://cdn.tailwindcss.com"></script></head><body class="bg-[#090A0F] text-white p-8"><h1 class="text-3xl font-bold text-[#12D8C3] mb-8">Viral Cat Admin</h1><div class="grid grid-cols-2 gap-8"><form id="f" class="space-y-4 bg-[#16181F] p-6 rounded-2xl"><input name="title" placeholder="Title" class="w-full p-2 bg-black border border-gray-700 rounded" required/><input name="video_url" placeholder="Embed URL" class="w-full p-2 bg-black border border-gray-700 rounded" required/><select name="platform" class="w-full p-2 bg-black border border-gray-700 rounded"><option value="tiktok">TikTok</option><option value="instagram">Instagram</option><option value="youtube">YouTube</option></select><div class="flex items-center gap-2 mt-2 mb-2"><input type="checkbox" id="deep" name="use_deep_analysis" value="true" checked class="w-5 h-5 accent-[#12D8C3]"><label for="deep" class="text-gray-300 text-sm">Use Map-Reduce Deep Analysis (Slower but 10x more accurate)</label></div><input type="file" id="video_file" accept="video/mp4" class="w-full" required/><button type="submit" id="submitBtn" class="w-full bg-[#12D8C3] text-black p-3 rounded font-bold">Process Video</button></form><div id="list" class="space-y-2"></div></div><script>const toB64 = f => new Promise((res,rej)=>{const r=new FileReader();r.readAsDataURL(f);r.onload=()=>res(r.result.split(',')[1]);});async function load(){const r=await fetch('/api/viralcat/trending');const j=await r.json();document.getElementById('list').innerHTML=j.data.map(t=>\`<div class="p-4 bg-gray-900 flex justify-between rounded-lg"><div><span class="text-xs bg-[#12D8C3] text-black px-2 py-1 rounded font-bold">\${t.platform.toUpperCase()}</span><p class="text-sm mt-1 font-bold">\${t.title}</p></div><button onclick="del('\${t.id}')" class="text-red-500">Delete</button></div>\`).join('');} async function del(id){await fetch('/api/viralcat/admin/template/'+id,{method:'DELETE'});load();} document.getElementById('f').onsubmit=async e=>{e.preventDefault();const b=e.target.querySelector('button'); const isDeep = document.getElementById('deep').checked; b.innerText = isDeep ? "Running Deep Map-Reduce... (Wait ~30s)" : "Processing... (Wait ~10s)"; b.disabled = true; try { const b64 = await toB64(document.getElementById('video_file').files[0]); const payload = { title: e.target.title.value, video_url: e.target.video_url.value, platform: e.target.platform.value, use_deep_analysis: isDeep, video_base64: b64 }; const res = await fetch('/api/viralcat/admin/template', { method: 'POST', headers: {'Content-Type':'application/json'}, body: JSON.stringify(payload) }); if(res.ok) { alert("Done!"); load(); } else { alert("Error: " + await res.text()); } } catch(err) { alert(err.message); } b.innerText="Process Video"; b.disabled=false;}; load();</script></body></html>`);
238
  });
 
23
  return null;
24
  };
25
 
26
+ const handleMediaProcessing = async (req, num_frames = 14, get_transcript = true) => {
 
 
27
  const video_base64 = getBase64Payload(req);
28
  if (!video_base64) throw new Error("No video data found in request");
29
 
 
38
  return mediaData;
39
  };
40
 
41
+ // ── PARALLEL MAP-REDUCE WITH OPENROUTER ──
42
  const performDeepAnalysis = async (frames, transcript, targetNiche = null) => {
43
+ console.log(`[DEEP ANALYZE] Starting Parallel Map-Reduce on ${frames.length} frames...`);
44
 
45
+ // 1. Cap at 14 frames max
46
+ const cappedFrames = frames.slice(0, 14);
47
  const chunks =[];
48
+
49
+ // 2. Chunk into batches of 7 (OpenRouter's max attachment limit)
50
+ for (let i = 0; i < cappedFrames.length; i += 7) {
51
+ chunks.push(cappedFrames.slice(i, i + 7));
52
  }
53
 
54
+ // 3. Process all chunks AT THE SAME TIME (No more sleep delays!)
55
+ console.log(`-> Firing ${chunks.length} parallel worker instances to OpenRouter...`);
56
+ const scenePromises = chunks.map((chunk, index) => {
57
+ const prompt = `Analyze this ${chunk.length}-image sequence. Return ONLY a JSON object with a single key "description" containing a brief summary of the action, camera movement, lighting, and subjects.`;
 
58
 
59
+ return generateCompletion({
60
+ model: "qwen", // Qwen is cheapest and fastest for this vision task
61
  prompt,
62
+ system_prompt: "You are a precise video frame analyzer. Output strictly JSON.",
63
+ images: chunk
64
  });
65
+ });
66
+
67
+ // Wait for all workers to finish simultaneously
68
+ const sceneResults = await Promise.all(scenePromises);
69
+
70
+ const sceneDescriptions = sceneResults.map((res, i) => {
71
+ try {
72
+ const parsed = JSON.parse(res.data);
73
+ return `[Scene Segment ${i + 1}]:\n${parsed.description}`;
74
+ } catch (e) {
75
+ return `[Scene Segment ${i + 1}]:\n${res.data}`; // Fallback if JSON parse fails
76
+ }
77
+ });
78
 
79
  const combinedVisualData = sceneDescriptions.join("\n\n");
80
  console.log(`[DEEP ANALYZE] Map phase complete. Synthesizing output...`);
81
 
82
  if (targetNiche) {
83
+ // 🚨 REDUCE PHASE: Custom Remix
84
  const finalPrompt = `
85
  TASK: Rewrite an existing viral video script for a NEW NICHE.
86
  [ORIGINAL VISUAL TIMELINE]
87
+ ${combinedVisualData}[ORIGINAL AUDIO TRANSCRIPT]
 
88
  ${transcript}
89
  [NEW TARGET NICHE]
90
  User's Niche: "${targetNiche}"
91
 
92
  INSTRUCTIONS:
93
+ 1. DECONSTRUCT THE AURA: Analyze exactly what made the original video go viral.
94
+ 2. Map those EXACT psychological triggers onto the user's new niche.
95
+ 3. Return ONLY a JSON object with a single key "script_markdown" containing the fully formatted markdown script.
 
 
 
96
 
97
+ Markdown format inside the JSON string should be:
98
+ ## 🧠 The Viral Aura
99
+ [Explanation]
100
  ## 🎬 The Hook (0-3s)
101
+ **Visual:** [What to show]
102
  **Audio:** [What to say]
 
103
  ## πŸ“– The Body
104
+ ...etc`;
 
105
 
106
+ const result = await generateCompletion({ model: "maverick", prompt: finalPrompt, system_prompt: "You are Viral Cat πŸ“ˆπŸˆ. Output strictly JSON." });
107
+ return { success: true, data: JSON.parse(result.data).script_markdown };
108
+
 
 
109
  } else {
110
+ // 🚨 REDUCE PHASE: Database Template Extraction
111
  const finalPrompt = `[VISUAL TIMELINE]\n${combinedVisualData}\n[TRANSCRIPT]\n${transcript}\n
112
+ Analyze the data and return ONLY a JSON object with these exact keys:
113
+ {
114
+ "transcript": "Cleaned dialogue",
115
+ "environment": "Setting/lighting description",
116
+ "pacing": "Edit style and camera movement",
117
+ "viral_factors": "Analyze the psychology, specific word choices, hooks, and timing that made this viral"
118
+ }`;
119
 
120
+ return await generateCompletion({ model: "maverick", prompt: finalPrompt, system_prompt: "Elite metadata extractor. Output strictly JSON." });
121
  }
122
  };
123
 
 
151
  const { title, video_url, platform, use_deep_analysis } = req.body;
152
  const isDeep = (use_deep_analysis === 'true' || use_deep_analysis === true);
153
 
154
+ // Max 14 frames for deep, 7 for fast (fits in 1 OpenRouter request)
155
+ const framesToExtract = isDeep ? 14 : 7;
156
  const mediaData = await handleMediaProcessing(req, framesToExtract, true);
157
 
158
  let aiResult;
159
  if (isDeep) {
160
  aiResult = await performDeepAnalysis(mediaData.frames, mediaData.transcript);
161
  } else {
162
+ const fastFrames = mediaData.frames.slice(0, 7);
163
+ const aiPrompt = `Analyze transcript and frames.\nTRANSCRIPT:\n${mediaData.transcript}\n
164
+ Return ONLY a JSON object with these exact keys:
165
+ {
166
+ "transcript": "Cleaned dialogue",
167
+ "environment": "Setting/lighting description",
168
+ "pacing": "Edit style and camera movement",
169
+ "viral_factors": "Identify psychological hooks and word choices"
170
+ }`;
171
+ aiResult = await generateCompletion({ model: "maverick", prompt: aiPrompt, images: fastFrames, system_prompt: "Fast metadata extractor. Output strictly JSON." });
172
  }
173
 
174
+ // 🚨 CLEAN JSON PARSING (No more Regex!)
175
+ const parsedData = JSON.parse(aiResult.data);
 
 
 
176
 
177
  const { error } = await supabase.from('viral_cat_trending').insert([{
178
  platform, video_url, title,
179
  thumbnail_url: `data:image/jpeg;base64,${mediaData.thumbnail}`,
180
+ transcript: parsedData.transcript || mediaData.transcript,
181
+ ai_environment_data: parsedData.environment || "N/A",
182
+ ai_scene_changes: parsedData.pacing || "N/A",
183
+ ai_viral_factors: parsedData.viral_factors || "N/A"
184
  }]);
185
 
186
  if (error) throw error;
 
195
  try {
196
  const { user_input, transcript, ai_environment_data, ai_scene_changes, ai_viral_factors } = req.body;
197
 
 
198
  const aiPrompt = `TASK: Rewrite script for a NEW NICHE.
199
  [ORIGINAL DATA]
200
  Pacing: ${ai_scene_changes}
201
  Environment: ${ai_environment_data}
202
  Viral Psychology (The Aura): ${ai_viral_factors}
203
+ Transcript: ${transcript}
204
+ [NEW NICHE]
205
  "${user_input}"
206
 
207
  INSTRUCTIONS: Map the original viral psychology and pacing onto the user's new niche.
208
+ Return ONLY a JSON object with a single key "script_markdown" containing the fully formatted markdown script.`;
209
+
210
+ const result = await generateCompletion({ model: "maverick", prompt: aiPrompt, system_prompt: "You are Viral Cat πŸ“ˆπŸˆ. Output strictly JSON." });
211
+
212
+ // Extract the markdown string from the JSON response
213
+ const finalMarkdown = JSON.parse(result.data).script_markdown;
214
+ res.json({ success: true, data: finalMarkdown });
215
+
 
 
 
 
 
 
 
216
  } catch (err) { res.status(500).json({ success: false, error: err.message }); }
217
  });
218
 
219
  router.post('/custom_remix', upload.single('video_file'), async (req, res) => {
220
  try {
221
  const { user_niche } = req.body;
222
+ const mediaData = await handleMediaProcessing(req, 14, true);
223
  const result = await performDeepAnalysis(mediaData.frames, mediaData.transcript, user_niche);
224
 
225
  res.json({ success: true, data: result.data, thumbnail: mediaData.thumbnail });
 
234
  res.json({ success: true });
235
  });
236
 
237
+ // Admin Dashboard HTML (Unchanged)
238
  router.get('/admin', (req, res) => {
239
  res.send(`<!DOCTYPE html><html><head><title>Viral Cat Admin</title><script src="https://cdn.tailwindcss.com"></script></head><body class="bg-[#090A0F] text-white p-8"><h1 class="text-3xl font-bold text-[#12D8C3] mb-8">Viral Cat Admin</h1><div class="grid grid-cols-2 gap-8"><form id="f" class="space-y-4 bg-[#16181F] p-6 rounded-2xl"><input name="title" placeholder="Title" class="w-full p-2 bg-black border border-gray-700 rounded" required/><input name="video_url" placeholder="Embed URL" class="w-full p-2 bg-black border border-gray-700 rounded" required/><select name="platform" class="w-full p-2 bg-black border border-gray-700 rounded"><option value="tiktok">TikTok</option><option value="instagram">Instagram</option><option value="youtube">YouTube</option></select><div class="flex items-center gap-2 mt-2 mb-2"><input type="checkbox" id="deep" name="use_deep_analysis" value="true" checked class="w-5 h-5 accent-[#12D8C3]"><label for="deep" class="text-gray-300 text-sm">Use Map-Reduce Deep Analysis (Slower but 10x more accurate)</label></div><input type="file" id="video_file" accept="video/mp4" class="w-full" required/><button type="submit" id="submitBtn" class="w-full bg-[#12D8C3] text-black p-3 rounded font-bold">Process Video</button></form><div id="list" class="space-y-2"></div></div><script>const toB64 = f => new Promise((res,rej)=>{const r=new FileReader();r.readAsDataURL(f);r.onload=()=>res(r.result.split(',')[1]);});async function load(){const r=await fetch('/api/viralcat/trending');const j=await r.json();document.getElementById('list').innerHTML=j.data.map(t=>\`<div class="p-4 bg-gray-900 flex justify-between rounded-lg"><div><span class="text-xs bg-[#12D8C3] text-black px-2 py-1 rounded font-bold">\${t.platform.toUpperCase()}</span><p class="text-sm mt-1 font-bold">\${t.title}</p></div><button onclick="del('\${t.id}')" class="text-red-500">Delete</button></div>\`).join('');} async function del(id){await fetch('/api/viralcat/admin/template/'+id,{method:'DELETE'});load();} document.getElementById('f').onsubmit=async e=>{e.preventDefault();const b=e.target.querySelector('button'); const isDeep = document.getElementById('deep').checked; b.innerText = isDeep ? "Running Deep Map-Reduce... (Wait ~30s)" : "Processing... (Wait ~10s)"; b.disabled = true; try { const b64 = await toB64(document.getElementById('video_file').files[0]); const payload = { title: e.target.title.value, video_url: e.target.video_url.value, platform: e.target.platform.value, use_deep_analysis: isDeep, video_base64: b64 }; const res = await fetch('/api/viralcat/admin/template', { method: 'POST', headers: {'Content-Type':'application/json'}, body: JSON.stringify(payload) }); if(res.ok) { alert("Done!"); load(); } else { alert("Error: " + await res.text()); } } catch(err) { alert(err.message); } b.innerText="Process Video"; b.disabled=false;}; load();</script></body></html>`);
240
  });