vidpipe 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +243 -0
- package/assets/fonts/Montserrat-Bold.ttf +0 -0
- package/assets/fonts/Montserrat-Regular.ttf +0 -0
- package/assets/fonts/OFL.txt +93 -0
- package/dist/__tests__/agents.test.d.ts +2 -0
- package/dist/__tests__/agents.test.d.ts.map +1 -0
- package/dist/__tests__/agents.test.js +434 -0
- package/dist/__tests__/agents.test.js.map +1 -0
- package/dist/__tests__/aspectRatio.test.d.ts +2 -0
- package/dist/__tests__/aspectRatio.test.d.ts.map +1 -0
- package/dist/__tests__/aspectRatio.test.js +406 -0
- package/dist/__tests__/aspectRatio.test.js.map +1 -0
- package/dist/__tests__/captionGenerator.test.d.ts +2 -0
- package/dist/__tests__/captionGenerator.test.d.ts.map +1 -0
- package/dist/__tests__/captionGenerator.test.js +435 -0
- package/dist/__tests__/captionGenerator.test.js.map +1 -0
- package/dist/__tests__/config.test.d.ts +2 -0
- package/dist/__tests__/config.test.d.ts.map +1 -0
- package/dist/__tests__/config.test.js +81 -0
- package/dist/__tests__/config.test.js.map +1 -0
- package/dist/__tests__/faceDetection.test.d.ts +2 -0
- package/dist/__tests__/faceDetection.test.d.ts.map +1 -0
- package/dist/__tests__/faceDetection.test.js +372 -0
- package/dist/__tests__/faceDetection.test.js.map +1 -0
- package/dist/__tests__/ffmpegTools.test.d.ts +2 -0
- package/dist/__tests__/ffmpegTools.test.d.ts.map +1 -0
- package/dist/__tests__/ffmpegTools.test.js +464 -0
- package/dist/__tests__/ffmpegTools.test.js.map +1 -0
- package/dist/__tests__/integration/captionBurn.test.d.ts +2 -0
- package/dist/__tests__/integration/captionBurn.test.d.ts.map +1 -0
- package/dist/__tests__/integration/captionBurn.test.js +103 -0
- package/dist/__tests__/integration/captionBurn.test.js.map +1 -0
- package/dist/__tests__/integration/clipComposite.test.d.ts +2 -0
- package/dist/__tests__/integration/clipComposite.test.d.ts.map +1 -0
- package/dist/__tests__/integration/clipComposite.test.js +56 -0
- package/dist/__tests__/integration/clipComposite.test.js.map +1 -0
- package/dist/__tests__/integration/faceDetection.test.d.ts +2 -0
- package/dist/__tests__/integration/faceDetection.test.d.ts.map +1 -0
- package/dist/__tests__/integration/faceDetection.test.js +85 -0
- package/dist/__tests__/integration/faceDetection.test.js.map +1 -0
- package/dist/__tests__/integration/ffmpegPipeline.test.d.ts +2 -0
- package/dist/__tests__/integration/ffmpegPipeline.test.d.ts.map +1 -0
- package/dist/__tests__/integration/ffmpegPipeline.test.js +88 -0
- package/dist/__tests__/integration/ffmpegPipeline.test.js.map +1 -0
- package/dist/__tests__/integration/fixture.d.ts +19 -0
- package/dist/__tests__/integration/fixture.d.ts.map +1 -0
- package/dist/__tests__/integration/fixture.js +112 -0
- package/dist/__tests__/integration/fixture.js.map +1 -0
- package/dist/__tests__/integration/fixture.test.d.ts +2 -0
- package/dist/__tests__/integration/fixture.test.d.ts.map +1 -0
- package/dist/__tests__/integration/fixture.test.js +27 -0
- package/dist/__tests__/integration/fixture.test.js.map +1 -0
- package/dist/__tests__/integration/realCaptions.test.d.ts +2 -0
- package/dist/__tests__/integration/realCaptions.test.d.ts.map +1 -0
- package/dist/__tests__/integration/realCaptions.test.js +226 -0
- package/dist/__tests__/integration/realCaptions.test.js.map +1 -0
- package/dist/__tests__/integration/realPipeline.test.d.ts +2 -0
- package/dist/__tests__/integration/realPipeline.test.d.ts.map +1 -0
- package/dist/__tests__/integration/realPipeline.test.js +210 -0
- package/dist/__tests__/integration/realPipeline.test.js.map +1 -0
- package/dist/__tests__/integration/silenceRemoval.test.d.ts +2 -0
- package/dist/__tests__/integration/silenceRemoval.test.d.ts.map +1 -0
- package/dist/__tests__/integration/silenceRemoval.test.js +93 -0
- package/dist/__tests__/integration/silenceRemoval.test.js.map +1 -0
- package/dist/__tests__/pipeline.test.d.ts +2 -0
- package/dist/__tests__/pipeline.test.d.ts.map +1 -0
- package/dist/__tests__/pipeline.test.js +434 -0
- package/dist/__tests__/pipeline.test.js.map +1 -0
- package/dist/__tests__/services.test.d.ts +2 -0
- package/dist/__tests__/services.test.d.ts.map +1 -0
- package/dist/__tests__/services.test.js +655 -0
- package/dist/__tests__/services.test.js.map +1 -0
- package/dist/__tests__/silenceRemoval.test.d.ts +2 -0
- package/dist/__tests__/silenceRemoval.test.d.ts.map +1 -0
- package/dist/__tests__/silenceRemoval.test.js +266 -0
- package/dist/__tests__/silenceRemoval.test.js.map +1 -0
- package/dist/__tests__/singlePassEdit.test.d.ts +2 -0
- package/dist/__tests__/singlePassEdit.test.d.ts.map +1 -0
- package/dist/__tests__/singlePassEdit.test.js +321 -0
- package/dist/__tests__/singlePassEdit.test.js.map +1 -0
- package/dist/__tests__/smoke.test.d.ts +2 -0
- package/dist/__tests__/smoke.test.d.ts.map +1 -0
- package/dist/__tests__/smoke.test.js +8 -0
- package/dist/__tests__/smoke.test.js.map +1 -0
- package/dist/__tests__/utilities.test.d.ts +2 -0
- package/dist/__tests__/utilities.test.d.ts.map +1 -0
- package/dist/__tests__/utilities.test.js +268 -0
- package/dist/__tests__/utilities.test.js.map +1 -0
- package/dist/agents/BaseAgent.d.ts +52 -0
- package/dist/agents/BaseAgent.d.ts.map +1 -0
- package/dist/agents/BaseAgent.js +108 -0
- package/dist/agents/BaseAgent.js.map +1 -0
- package/dist/agents/BlogAgent.d.ts +3 -0
- package/dist/agents/BlogAgent.d.ts.map +1 -0
- package/dist/agents/BlogAgent.js +163 -0
- package/dist/agents/BlogAgent.js.map +1 -0
- package/dist/agents/ChapterAgent.d.ts +11 -0
- package/dist/agents/ChapterAgent.d.ts.map +1 -0
- package/dist/agents/ChapterAgent.js +191 -0
- package/dist/agents/ChapterAgent.js.map +1 -0
- package/dist/agents/MediumVideoAgent.d.ts +3 -0
- package/dist/agents/MediumVideoAgent.d.ts.map +1 -0
- package/dist/agents/MediumVideoAgent.js +219 -0
- package/dist/agents/MediumVideoAgent.js.map +1 -0
- package/dist/agents/ShortsAgent.d.ts +3 -0
- package/dist/agents/ShortsAgent.d.ts.map +1 -0
- package/dist/agents/ShortsAgent.js +243 -0
- package/dist/agents/ShortsAgent.js.map +1 -0
- package/dist/agents/SilenceRemovalAgent.d.ts +9 -0
- package/dist/agents/SilenceRemovalAgent.d.ts.map +1 -0
- package/dist/agents/SilenceRemovalAgent.js +208 -0
- package/dist/agents/SilenceRemovalAgent.js.map +1 -0
- package/dist/agents/SocialMediaAgent.d.ts +4 -0
- package/dist/agents/SocialMediaAgent.d.ts.map +1 -0
- package/dist/agents/SocialMediaAgent.js +248 -0
- package/dist/agents/SocialMediaAgent.js.map +1 -0
- package/dist/agents/SummaryAgent.d.ts +11 -0
- package/dist/agents/SummaryAgent.d.ts.map +1 -0
- package/dist/agents/SummaryAgent.js +333 -0
- package/dist/agents/SummaryAgent.js.map +1 -0
- package/dist/config/brand.d.ts +29 -0
- package/dist/config/brand.d.ts.map +1 -0
- package/dist/config/brand.js +83 -0
- package/dist/config/brand.js.map +1 -0
- package/dist/config/environment.d.ts +36 -0
- package/dist/config/environment.d.ts.map +1 -0
- package/dist/config/environment.js +44 -0
- package/dist/config/environment.js.map +1 -0
- package/dist/config/logger.d.ts +5 -0
- package/dist/config/logger.d.ts.map +1 -0
- package/dist/config/logger.js +13 -0
- package/dist/config/logger.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +135 -0
- package/dist/index.js.map +1 -0
- package/dist/pipeline.d.ts +57 -0
- package/dist/pipeline.d.ts.map +1 -0
- package/dist/pipeline.js +287 -0
- package/dist/pipeline.js.map +1 -0
- package/dist/services/captionGeneration.d.ts +7 -0
- package/dist/services/captionGeneration.d.ts.map +1 -0
- package/dist/services/captionGeneration.js +29 -0
- package/dist/services/captionGeneration.js.map +1 -0
- package/dist/services/fileWatcher.d.ts +19 -0
- package/dist/services/fileWatcher.d.ts.map +1 -0
- package/dist/services/fileWatcher.js +120 -0
- package/dist/services/fileWatcher.js.map +1 -0
- package/dist/services/gitOperations.d.ts +3 -0
- package/dist/services/gitOperations.d.ts.map +1 -0
- package/dist/services/gitOperations.js +43 -0
- package/dist/services/gitOperations.js.map +1 -0
- package/dist/services/socialPosting.d.ts +38 -0
- package/dist/services/socialPosting.d.ts.map +1 -0
- package/dist/services/socialPosting.js +102 -0
- package/dist/services/socialPosting.js.map +1 -0
- package/dist/services/transcription.d.ts +3 -0
- package/dist/services/transcription.d.ts.map +1 -0
- package/dist/services/transcription.js +100 -0
- package/dist/services/transcription.js.map +1 -0
- package/dist/services/videoIngestion.d.ts +3 -0
- package/dist/services/videoIngestion.d.ts.map +1 -0
- package/dist/services/videoIngestion.js +103 -0
- package/dist/services/videoIngestion.js.map +1 -0
- package/dist/tools/captions/captionGenerator.d.ts +84 -0
- package/dist/tools/captions/captionGenerator.d.ts.map +1 -0
- package/dist/tools/captions/captionGenerator.js +390 -0
- package/dist/tools/captions/captionGenerator.js.map +1 -0
- package/dist/tools/ffmpeg/aspectRatio.d.ts +101 -0
- package/dist/tools/ffmpeg/aspectRatio.d.ts.map +1 -0
- package/dist/tools/ffmpeg/aspectRatio.js +338 -0
- package/dist/tools/ffmpeg/aspectRatio.js.map +1 -0
- package/dist/tools/ffmpeg/audioExtraction.d.ts +16 -0
- package/dist/tools/ffmpeg/audioExtraction.d.ts.map +1 -0
- package/dist/tools/ffmpeg/audioExtraction.js +86 -0
- package/dist/tools/ffmpeg/audioExtraction.js.map +1 -0
- package/dist/tools/ffmpeg/captionBurning.d.ts +8 -0
- package/dist/tools/ffmpeg/captionBurning.d.ts.map +1 -0
- package/dist/tools/ffmpeg/captionBurning.js +71 -0
- package/dist/tools/ffmpeg/captionBurning.js.map +1 -0
- package/dist/tools/ffmpeg/clipExtraction.d.ts +23 -0
- package/dist/tools/ffmpeg/clipExtraction.d.ts.map +1 -0
- package/dist/tools/ffmpeg/clipExtraction.js +178 -0
- package/dist/tools/ffmpeg/clipExtraction.js.map +1 -0
- package/dist/tools/ffmpeg/faceDetection.d.ts +127 -0
- package/dist/tools/ffmpeg/faceDetection.d.ts.map +1 -0
- package/dist/tools/ffmpeg/faceDetection.js +500 -0
- package/dist/tools/ffmpeg/faceDetection.js.map +1 -0
- package/dist/tools/ffmpeg/frameCapture.d.ts +10 -0
- package/dist/tools/ffmpeg/frameCapture.d.ts.map +1 -0
- package/dist/tools/ffmpeg/frameCapture.js +48 -0
- package/dist/tools/ffmpeg/frameCapture.js.map +1 -0
- package/dist/tools/ffmpeg/silenceDetection.d.ts +10 -0
- package/dist/tools/ffmpeg/silenceDetection.d.ts.map +1 -0
- package/dist/tools/ffmpeg/silenceDetection.js +55 -0
- package/dist/tools/ffmpeg/silenceDetection.js.map +1 -0
- package/dist/tools/ffmpeg/singlePassEdit.d.ts +25 -0
- package/dist/tools/ffmpeg/singlePassEdit.d.ts.map +1 -0
- package/dist/tools/ffmpeg/singlePassEdit.js +123 -0
- package/dist/tools/ffmpeg/singlePassEdit.js.map +1 -0
- package/dist/tools/search/exaClient.d.ts +8 -0
- package/dist/tools/search/exaClient.d.ts.map +1 -0
- package/dist/tools/search/exaClient.js +38 -0
- package/dist/tools/search/exaClient.js.map +1 -0
- package/dist/tools/whisper/whisperClient.d.ts +3 -0
- package/dist/tools/whisper/whisperClient.d.ts.map +1 -0
- package/dist/tools/whisper/whisperClient.js +77 -0
- package/dist/tools/whisper/whisperClient.js.map +1 -0
- package/dist/types/index.d.ts +305 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +44 -0
- package/dist/types/index.js.map +1 -0
- package/package.json +63 -0
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
import { BaseAgent } from './BaseAgent';
|
|
2
|
+
import { extractClip, extractCompositeClipWithTransitions } from '../tools/ffmpeg/clipExtraction';
|
|
3
|
+
import { generateStyledASSForSegment, generateStyledASSForComposite } from '../tools/captions/captionGenerator';
|
|
4
|
+
import { burnCaptions } from '../tools/ffmpeg/captionBurning';
|
|
5
|
+
import { v4 as uuidv4 } from 'uuid';
|
|
6
|
+
import slugify from 'slugify';
|
|
7
|
+
import { promises as fs } from 'fs';
|
|
8
|
+
import path from 'path';
|
|
9
|
+
import logger from '../config/logger';
|
|
10
|
+
// ── System prompt ───────────────────────────────────────────────────────────
|
|
11
|
+
const SYSTEM_PROMPT = `You are a medium-form video content strategist. Your job is to analyze a video transcript with word-level timestamps and identify the best 1–3 minute segments to extract as standalone medium-form clips.
|
|
12
|
+
|
|
13
|
+
## What to look for
|
|
14
|
+
|
|
15
|
+
- **Complete topics** — a subject is introduced, explored, and concluded
|
|
16
|
+
- **Narrative arcs** — problem → solution → result; question → exploration → insight
|
|
17
|
+
- **Educational deep dives** — clear, thorough explanations of complex topics
|
|
18
|
+
- **Compelling stories** — anecdotes with setup, tension, and resolution
|
|
19
|
+
- **Strong arguments** — claim → evidence → implication sequences
|
|
20
|
+
- **Topic compilations** — multiple brief mentions of one theme across the video that can be compiled into a cohesive 1–3 minute segment
|
|
21
|
+
|
|
22
|
+
## Clip types
|
|
23
|
+
|
|
24
|
+
- **Deep Dive** — a single contiguous section (1–3 min) covering one topic in depth
|
|
25
|
+
- **Compilation** — multiple non-contiguous segments stitched together around a single theme or narrative thread (1–3 min total)
|
|
26
|
+
|
|
27
|
+
## Rules
|
|
28
|
+
|
|
29
|
+
1. Each clip must be 60–180 seconds total duration.
|
|
30
|
+
2. Timestamps must align to word boundaries from the transcript.
|
|
31
|
+
3. Prefer natural sentence and paragraph boundaries for clean entry/exit points.
|
|
32
|
+
4. Each clip must be self-contained — a viewer with no other context should understand and get value from the clip.
|
|
33
|
+
5. Aim for 2–4 medium clips per video, depending on length and richness.
|
|
34
|
+
6. Every clip needs a descriptive title (5–12 words) and a topic label.
|
|
35
|
+
7. For compilations, specify segments in the order they should appear in the final clip (which may differ from chronological order).
|
|
36
|
+
8. Tags should be lowercase, no hashes, 3–6 per clip.
|
|
37
|
+
9. A 1-second buffer is automatically added around each segment boundary.
|
|
38
|
+
10. Each clip needs a hook — the opening line or concept that draws viewers in.
|
|
39
|
+
|
|
40
|
+
## Differences from shorts
|
|
41
|
+
|
|
42
|
+
- Shorts capture *moments*; medium clips capture *complete ideas*.
|
|
43
|
+
- Don't just find the most exciting 60 seconds — find where a topic starts and where it naturally concludes.
|
|
44
|
+
- It's OK if a medium clip has slower pacing — depth and coherence matter more than constant high energy.
|
|
45
|
+
- Look for segments that work as standalone mini-tutorials or explanations.
|
|
46
|
+
- Avoid overlap with content that would work better as a short (punchy, viral, single-moment).
|
|
47
|
+
|
|
48
|
+
When you have identified the clips, call the **plan_medium_clips** tool with your complete plan.`;
|
|
49
|
+
// ── JSON Schema for the plan_medium_clips tool ──────────────────────────────
|
|
50
|
+
const PLAN_MEDIUM_CLIPS_SCHEMA = {
|
|
51
|
+
type: 'object',
|
|
52
|
+
properties: {
|
|
53
|
+
clips: {
|
|
54
|
+
type: 'array',
|
|
55
|
+
description: 'Array of planned medium-length clips',
|
|
56
|
+
items: {
|
|
57
|
+
type: 'object',
|
|
58
|
+
properties: {
|
|
59
|
+
title: { type: 'string', description: 'Descriptive clip title (5–12 words)' },
|
|
60
|
+
description: { type: 'string', description: 'Brief description of the clip content' },
|
|
61
|
+
tags: {
|
|
62
|
+
type: 'array',
|
|
63
|
+
items: { type: 'string' },
|
|
64
|
+
description: 'Lowercase tags without hashes, 3–6 per clip',
|
|
65
|
+
},
|
|
66
|
+
segments: {
|
|
67
|
+
type: 'array',
|
|
68
|
+
description: 'One or more time segments that compose this clip',
|
|
69
|
+
items: {
|
|
70
|
+
type: 'object',
|
|
71
|
+
properties: {
|
|
72
|
+
start: { type: 'number', description: 'Start time in seconds' },
|
|
73
|
+
end: { type: 'number', description: 'End time in seconds' },
|
|
74
|
+
description: { type: 'string', description: 'What happens in this segment' },
|
|
75
|
+
},
|
|
76
|
+
required: ['start', 'end', 'description'],
|
|
77
|
+
},
|
|
78
|
+
},
|
|
79
|
+
totalDuration: { type: 'number', description: 'Total clip duration in seconds (60–180)' },
|
|
80
|
+
hook: { type: 'string', description: 'Opening hook for the clip' },
|
|
81
|
+
topic: { type: 'string', description: 'Main topic covered in the clip' },
|
|
82
|
+
},
|
|
83
|
+
required: ['title', 'description', 'tags', 'segments', 'totalDuration', 'hook', 'topic'],
|
|
84
|
+
},
|
|
85
|
+
},
|
|
86
|
+
},
|
|
87
|
+
required: ['clips'],
|
|
88
|
+
};
|
|
89
|
+
// ── Agent ────────────────────────────────────────────────────────────────────
|
|
90
|
+
class MediumVideoAgent extends BaseAgent {
|
|
91
|
+
plannedClips = [];
|
|
92
|
+
constructor() {
|
|
93
|
+
super('MediumVideoAgent', SYSTEM_PROMPT);
|
|
94
|
+
}
|
|
95
|
+
getTools() {
|
|
96
|
+
return [
|
|
97
|
+
{
|
|
98
|
+
name: 'plan_medium_clips',
|
|
99
|
+
description: 'Submit the planned medium-length clips as a structured JSON array. Call this once with all planned clips.',
|
|
100
|
+
parameters: PLAN_MEDIUM_CLIPS_SCHEMA,
|
|
101
|
+
handler: async (args) => {
|
|
102
|
+
return this.handleToolCall('plan_medium_clips', args);
|
|
103
|
+
},
|
|
104
|
+
},
|
|
105
|
+
];
|
|
106
|
+
}
|
|
107
|
+
async handleToolCall(toolName, args) {
|
|
108
|
+
if (toolName === 'plan_medium_clips') {
|
|
109
|
+
this.plannedClips = args.clips;
|
|
110
|
+
logger.info(`[MediumVideoAgent] Planned ${this.plannedClips.length} medium clips`);
|
|
111
|
+
return { success: true, count: this.plannedClips.length };
|
|
112
|
+
}
|
|
113
|
+
throw new Error(`Unknown tool: ${toolName}`);
|
|
114
|
+
}
|
|
115
|
+
getPlannedClips() {
|
|
116
|
+
return this.plannedClips;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
// ── Public API ───────────────────────────────────────────────────────────────
|
|
120
|
+
export async function generateMediumClips(video, transcript) {
|
|
121
|
+
const agent = new MediumVideoAgent();
|
|
122
|
+
// Build prompt with full transcript including word-level timestamps
|
|
123
|
+
const transcriptLines = transcript.segments.map((seg) => {
|
|
124
|
+
const words = seg.words
|
|
125
|
+
.map((w) => `[${w.start.toFixed(2)}-${w.end.toFixed(2)}] ${w.word}`)
|
|
126
|
+
.join(' ');
|
|
127
|
+
return `[${seg.start.toFixed(2)}s – ${seg.end.toFixed(2)}s] ${seg.text}\nWords: ${words}`;
|
|
128
|
+
});
|
|
129
|
+
const prompt = [
|
|
130
|
+
`Analyze the following transcript (${transcript.duration.toFixed(0)}s total) and plan medium-length clips (1–3 minutes each).\n`,
|
|
131
|
+
`Video: ${video.filename}`,
|
|
132
|
+
`Duration: ${transcript.duration.toFixed(1)}s\n`,
|
|
133
|
+
'--- TRANSCRIPT ---\n',
|
|
134
|
+
transcriptLines.join('\n\n'),
|
|
135
|
+
'\n--- END TRANSCRIPT ---',
|
|
136
|
+
].join('\n');
|
|
137
|
+
try {
|
|
138
|
+
await agent.run(prompt);
|
|
139
|
+
const planned = agent.getPlannedClips();
|
|
140
|
+
if (planned.length === 0) {
|
|
141
|
+
logger.warn('[MediumVideoAgent] No medium clips were planned');
|
|
142
|
+
return [];
|
|
143
|
+
}
|
|
144
|
+
const clipsDir = path.join(path.dirname(video.repoPath), 'medium-clips');
|
|
145
|
+
await fs.mkdir(clipsDir, { recursive: true });
|
|
146
|
+
const clips = [];
|
|
147
|
+
for (const plan of planned) {
|
|
148
|
+
const id = uuidv4();
|
|
149
|
+
const clipSlug = slugify(plan.title, { lower: true, strict: true });
|
|
150
|
+
const totalDuration = plan.segments.reduce((sum, s) => sum + (s.end - s.start), 0);
|
|
151
|
+
const outputPath = path.join(clipsDir, `${clipSlug}.mp4`);
|
|
152
|
+
const segments = plan.segments.map((s) => ({
|
|
153
|
+
start: s.start,
|
|
154
|
+
end: s.end,
|
|
155
|
+
description: s.description,
|
|
156
|
+
}));
|
|
157
|
+
// Extract the clip — single segment or composite with crossfade transitions
|
|
158
|
+
if (segments.length === 1) {
|
|
159
|
+
await extractClip(video.repoPath, segments[0].start, segments[0].end, outputPath);
|
|
160
|
+
}
|
|
161
|
+
else {
|
|
162
|
+
await extractCompositeClipWithTransitions(video.repoPath, segments, outputPath);
|
|
163
|
+
}
|
|
164
|
+
// Generate ASS captions with medium style (smaller font, bottom-positioned)
|
|
165
|
+
let captionedPath;
|
|
166
|
+
try {
|
|
167
|
+
const assContent = segments.length === 1
|
|
168
|
+
? generateStyledASSForSegment(transcript, segments[0].start, segments[0].end, 1.0, 'medium')
|
|
169
|
+
: generateStyledASSForComposite(transcript, segments, 1.0, 'medium');
|
|
170
|
+
const assPath = path.join(clipsDir, `${clipSlug}.ass`);
|
|
171
|
+
await fs.writeFile(assPath, assContent);
|
|
172
|
+
captionedPath = path.join(clipsDir, `${clipSlug}-captioned.mp4`);
|
|
173
|
+
await burnCaptions(outputPath, assPath, captionedPath);
|
|
174
|
+
logger.info(`[MediumVideoAgent] Burned captions for clip: ${plan.title}`);
|
|
175
|
+
}
|
|
176
|
+
catch (err) {
|
|
177
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
178
|
+
logger.warn(`[MediumVideoAgent] Caption burning failed for ${plan.title}: ${message}`);
|
|
179
|
+
captionedPath = undefined;
|
|
180
|
+
}
|
|
181
|
+
// Generate description markdown
|
|
182
|
+
const mdPath = path.join(clipsDir, `${clipSlug}.md`);
|
|
183
|
+
const mdContent = [
|
|
184
|
+
`# ${plan.title}\n`,
|
|
185
|
+
`**Topic:** ${plan.topic}\n`,
|
|
186
|
+
`**Hook:** ${plan.hook}\n`,
|
|
187
|
+
plan.description,
|
|
188
|
+
'',
|
|
189
|
+
'## Segments\n',
|
|
190
|
+
...plan.segments.map((s, i) => `${i + 1}. **${s.start.toFixed(2)}s – ${s.end.toFixed(2)}s** — ${s.description}`),
|
|
191
|
+
'',
|
|
192
|
+
'## Tags\n',
|
|
193
|
+
plan.tags.map((t) => `- ${t}`).join('\n'),
|
|
194
|
+
'',
|
|
195
|
+
].join('\n');
|
|
196
|
+
await fs.writeFile(mdPath, mdContent);
|
|
197
|
+
clips.push({
|
|
198
|
+
id,
|
|
199
|
+
title: plan.title,
|
|
200
|
+
slug: clipSlug,
|
|
201
|
+
segments,
|
|
202
|
+
totalDuration,
|
|
203
|
+
outputPath,
|
|
204
|
+
captionedPath,
|
|
205
|
+
description: plan.description,
|
|
206
|
+
tags: plan.tags,
|
|
207
|
+
hook: plan.hook,
|
|
208
|
+
topic: plan.topic,
|
|
209
|
+
});
|
|
210
|
+
logger.info(`[MediumVideoAgent] Created medium clip: ${plan.title} (${totalDuration.toFixed(1)}s)`);
|
|
211
|
+
}
|
|
212
|
+
logger.info(`[MediumVideoAgent] Generated ${clips.length} medium clips`);
|
|
213
|
+
return clips;
|
|
214
|
+
}
|
|
215
|
+
finally {
|
|
216
|
+
await agent.destroy();
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
//# sourceMappingURL=MediumVideoAgent.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MediumVideoAgent.js","sourceRoot":"","sources":["../../src/agents/MediumVideoAgent.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ShortsAgent.d.ts","sourceRoot":"","sources":["../../src/agents/ShortsAgent.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,SAAS,EAAkC,MAAM,UAAU,CAAA;AAsI3F,wBAAsB,cAAc,CAClC,KAAK,EAAE,SAAS,EAChB,UAAU,EAAE,UAAU,GACrB,OAAO,CAAC,SAAS,EAAE,CAAC,CAyJtB"}
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
import { BaseAgent } from './BaseAgent';
|
|
2
|
+
import { extractClip, extractCompositeClip } from '../tools/ffmpeg/clipExtraction';
|
|
3
|
+
import { generateStyledASSForSegment, generateStyledASSForComposite, generatePortraitASSWithHook, generatePortraitASSWithHookComposite } from '../tools/captions/captionGenerator';
|
|
4
|
+
import { burnCaptions } from '../tools/ffmpeg/captionBurning';
|
|
5
|
+
import { generatePlatformVariants } from '../tools/ffmpeg/aspectRatio';
|
|
6
|
+
import { v4 as uuidv4 } from 'uuid';
|
|
7
|
+
import slugify from 'slugify';
|
|
8
|
+
import { promises as fs } from 'fs';
|
|
9
|
+
import path from 'path';
|
|
10
|
+
import logger from '../config/logger';
|
|
11
|
+
// ── System prompt ───────────────────────────────────────────────────────────
|
|
12
|
+
const SYSTEM_PROMPT = `You are a short-form video content strategist. Your job is to analyze a video transcript with word-level timestamps and identify the most compelling moments to extract as shorts (15–60 seconds each).
|
|
13
|
+
|
|
14
|
+
## What to look for
|
|
15
|
+
- **Key insights** — concise, quotable takeaways
|
|
16
|
+
- **Funny moments** — humor, wit, unexpected punchlines
|
|
17
|
+
- **Controversial takes** — bold opinions that spark discussion
|
|
18
|
+
- **Educational nuggets** — clear explanations of complex topics
|
|
19
|
+
- **Emotional peaks** — passion, vulnerability, excitement
|
|
20
|
+
- **Topic compilations** — multiple brief mentions of one theme that can be stitched together
|
|
21
|
+
|
|
22
|
+
## Short types
|
|
23
|
+
- **Single segment** — one contiguous section of the video
|
|
24
|
+
- **Composite** — multiple non-contiguous segments combined into one short (great for topic compilations or building a narrative arc)
|
|
25
|
+
|
|
26
|
+
## Rules
|
|
27
|
+
1. Each short must be 15–60 seconds total duration.
|
|
28
|
+
2. Timestamps must align to word boundaries from the transcript.
|
|
29
|
+
3. Prefer natural sentence boundaries for clean cuts.
|
|
30
|
+
4. Aim for 3–8 shorts per video, depending on length and richness.
|
|
31
|
+
5. Every short needs a catchy, descriptive title (5–10 words).
|
|
32
|
+
6. Tags should be lowercase, no hashes, 3–6 per short.
|
|
33
|
+
7. A 1-second buffer is automatically added before and after each segment boundary during extraction, so plan segments based on content timestamps without worrying about clipping words at the edges.
|
|
34
|
+
|
|
35
|
+
When you have identified the shorts, call the **plan_shorts** tool with your complete plan.`;
|
|
36
|
+
// ── JSON Schema for the plan_shorts tool ────────────────────────────────────
|
|
37
|
+
const PLAN_SHORTS_SCHEMA = {
|
|
38
|
+
type: 'object',
|
|
39
|
+
properties: {
|
|
40
|
+
shorts: {
|
|
41
|
+
type: 'array',
|
|
42
|
+
description: 'Array of planned short clips',
|
|
43
|
+
items: {
|
|
44
|
+
type: 'object',
|
|
45
|
+
properties: {
|
|
46
|
+
title: { type: 'string', description: 'Catchy short title (5–10 words)' },
|
|
47
|
+
description: { type: 'string', description: 'Brief description of the short content' },
|
|
48
|
+
tags: {
|
|
49
|
+
type: 'array',
|
|
50
|
+
items: { type: 'string' },
|
|
51
|
+
description: 'Lowercase tags without hashes, 3–6 per short',
|
|
52
|
+
},
|
|
53
|
+
segments: {
|
|
54
|
+
type: 'array',
|
|
55
|
+
description: 'One or more time segments that compose this short',
|
|
56
|
+
items: {
|
|
57
|
+
type: 'object',
|
|
58
|
+
properties: {
|
|
59
|
+
start: { type: 'number', description: 'Start time in seconds' },
|
|
60
|
+
end: { type: 'number', description: 'End time in seconds' },
|
|
61
|
+
description: { type: 'string', description: 'What happens in this segment' },
|
|
62
|
+
},
|
|
63
|
+
required: ['start', 'end', 'description'],
|
|
64
|
+
},
|
|
65
|
+
},
|
|
66
|
+
},
|
|
67
|
+
required: ['title', 'description', 'tags', 'segments'],
|
|
68
|
+
},
|
|
69
|
+
},
|
|
70
|
+
},
|
|
71
|
+
required: ['shorts'],
|
|
72
|
+
};
|
|
73
|
+
// ── Agent ────────────────────────────────────────────────────────────────────
|
|
74
|
+
class ShortsAgent extends BaseAgent {
|
|
75
|
+
plannedShorts = [];
|
|
76
|
+
constructor() {
|
|
77
|
+
super('ShortsAgent', SYSTEM_PROMPT);
|
|
78
|
+
}
|
|
79
|
+
getTools() {
|
|
80
|
+
return [
|
|
81
|
+
{
|
|
82
|
+
name: 'plan_shorts',
|
|
83
|
+
description: 'Submit the planned shorts as a structured JSON array. Call this once with all planned shorts.',
|
|
84
|
+
parameters: PLAN_SHORTS_SCHEMA,
|
|
85
|
+
handler: async (args) => {
|
|
86
|
+
return this.handleToolCall('plan_shorts', args);
|
|
87
|
+
},
|
|
88
|
+
},
|
|
89
|
+
];
|
|
90
|
+
}
|
|
91
|
+
async handleToolCall(toolName, args) {
|
|
92
|
+
if (toolName === 'plan_shorts') {
|
|
93
|
+
this.plannedShorts = args.shorts;
|
|
94
|
+
logger.info(`[ShortsAgent] Planned ${this.plannedShorts.length} shorts`);
|
|
95
|
+
return { success: true, count: this.plannedShorts.length };
|
|
96
|
+
}
|
|
97
|
+
throw new Error(`Unknown tool: ${toolName}`);
|
|
98
|
+
}
|
|
99
|
+
getPlannedShorts() {
|
|
100
|
+
return this.plannedShorts;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
// ── Public API ───────────────────────────────────────────────────────────────
|
|
104
|
+
export async function generateShorts(video, transcript) {
|
|
105
|
+
const agent = new ShortsAgent();
|
|
106
|
+
// Build prompt with full transcript including word-level timestamps
|
|
107
|
+
const transcriptLines = transcript.segments.map((seg) => {
|
|
108
|
+
const words = seg.words
|
|
109
|
+
.map((w) => `[${w.start.toFixed(2)}-${w.end.toFixed(2)}] ${w.word}`)
|
|
110
|
+
.join(' ');
|
|
111
|
+
return `[${seg.start.toFixed(2)}s – ${seg.end.toFixed(2)}s] ${seg.text}\nWords: ${words}`;
|
|
112
|
+
});
|
|
113
|
+
const prompt = [
|
|
114
|
+
`Analyze the following transcript (${transcript.duration.toFixed(0)}s total) and plan shorts.\n`,
|
|
115
|
+
`Video: ${video.filename}`,
|
|
116
|
+
`Duration: ${transcript.duration.toFixed(1)}s\n`,
|
|
117
|
+
'--- TRANSCRIPT ---\n',
|
|
118
|
+
transcriptLines.join('\n\n'),
|
|
119
|
+
'\n--- END TRANSCRIPT ---',
|
|
120
|
+
].join('\n');
|
|
121
|
+
try {
|
|
122
|
+
await agent.run(prompt);
|
|
123
|
+
const planned = agent.getPlannedShorts();
|
|
124
|
+
if (planned.length === 0) {
|
|
125
|
+
logger.warn('[ShortsAgent] No shorts were planned');
|
|
126
|
+
return [];
|
|
127
|
+
}
|
|
128
|
+
const shortsDir = path.join(path.dirname(video.repoPath), 'shorts');
|
|
129
|
+
await fs.mkdir(shortsDir, { recursive: true });
|
|
130
|
+
const shorts = [];
|
|
131
|
+
for (const plan of planned) {
|
|
132
|
+
const id = uuidv4();
|
|
133
|
+
const shortSlug = slugify(plan.title, { lower: true, strict: true });
|
|
134
|
+
const totalDuration = plan.segments.reduce((sum, s) => sum + (s.end - s.start), 0);
|
|
135
|
+
const outputPath = path.join(shortsDir, `${shortSlug}.mp4`);
|
|
136
|
+
const segments = plan.segments.map((s) => ({
|
|
137
|
+
start: s.start,
|
|
138
|
+
end: s.end,
|
|
139
|
+
description: s.description,
|
|
140
|
+
}));
|
|
141
|
+
// Extract the clip (single or composite)
|
|
142
|
+
if (segments.length === 1) {
|
|
143
|
+
await extractClip(video.repoPath, segments[0].start, segments[0].end, outputPath);
|
|
144
|
+
}
|
|
145
|
+
else {
|
|
146
|
+
await extractCompositeClip(video.repoPath, segments, outputPath);
|
|
147
|
+
}
|
|
148
|
+
// Generate platform-specific aspect ratio variants from UNCAPTIONED video
|
|
149
|
+
// so portrait/square crops are clean before captions are burned per-variant
|
|
150
|
+
let variants;
|
|
151
|
+
try {
|
|
152
|
+
const defaultPlatforms = ['tiktok', 'youtube-shorts', 'instagram-reels', 'instagram-feed', 'linkedin'];
|
|
153
|
+
const results = await generatePlatformVariants(outputPath, shortsDir, shortSlug, defaultPlatforms);
|
|
154
|
+
if (results.length > 0) {
|
|
155
|
+
variants = results.map((v) => ({
|
|
156
|
+
path: v.path,
|
|
157
|
+
aspectRatio: v.aspectRatio,
|
|
158
|
+
platform: v.platform,
|
|
159
|
+
width: v.width,
|
|
160
|
+
height: v.height,
|
|
161
|
+
}));
|
|
162
|
+
logger.info(`[ShortsAgent] Generated ${variants.length} platform variants for: ${plan.title}`);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
catch (err) {
|
|
166
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
167
|
+
logger.warn(`[ShortsAgent] Platform variant generation failed for ${plan.title}: ${message}`);
|
|
168
|
+
}
|
|
169
|
+
// Generate ASS captions for the landscape short and burn them in
|
|
170
|
+
let captionedPath;
|
|
171
|
+
try {
|
|
172
|
+
const assContent = segments.length === 1
|
|
173
|
+
? generateStyledASSForSegment(transcript, segments[0].start, segments[0].end)
|
|
174
|
+
: generateStyledASSForComposite(transcript, segments);
|
|
175
|
+
const assPath = path.join(shortsDir, `${shortSlug}.ass`);
|
|
176
|
+
await fs.writeFile(assPath, assContent);
|
|
177
|
+
captionedPath = path.join(shortsDir, `${shortSlug}-captioned.mp4`);
|
|
178
|
+
await burnCaptions(outputPath, assPath, captionedPath);
|
|
179
|
+
logger.info(`[ShortsAgent] Burned captions for short: ${plan.title}`);
|
|
180
|
+
}
|
|
181
|
+
catch (err) {
|
|
182
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
183
|
+
logger.warn(`[ShortsAgent] Caption burning failed for ${plan.title}: ${message}`);
|
|
184
|
+
captionedPath = undefined;
|
|
185
|
+
}
|
|
186
|
+
// Burn portrait-style captions (green highlight, centered, hook overlay) onto portrait variant
|
|
187
|
+
if (variants) {
|
|
188
|
+
const portraitVariant = variants.find(v => v.aspectRatio === '9:16');
|
|
189
|
+
if (portraitVariant) {
|
|
190
|
+
try {
|
|
191
|
+
const portraitAssContent = segments.length === 1
|
|
192
|
+
? generatePortraitASSWithHook(transcript, plan.title, segments[0].start, segments[0].end)
|
|
193
|
+
: generatePortraitASSWithHookComposite(transcript, segments, plan.title);
|
|
194
|
+
const portraitAssPath = path.join(shortsDir, `${shortSlug}-portrait.ass`);
|
|
195
|
+
await fs.writeFile(portraitAssPath, portraitAssContent);
|
|
196
|
+
const portraitCaptionedPath = portraitVariant.path.replace('.mp4', '-captioned.mp4');
|
|
197
|
+
await burnCaptions(portraitVariant.path, portraitAssPath, portraitCaptionedPath);
|
|
198
|
+
// Update the variant path to point to the captioned version
|
|
199
|
+
portraitVariant.path = portraitCaptionedPath;
|
|
200
|
+
logger.info(`[ShortsAgent] Burned portrait captions with hook for: ${plan.title}`);
|
|
201
|
+
}
|
|
202
|
+
catch (err) {
|
|
203
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
204
|
+
logger.warn(`[ShortsAgent] Portrait caption burning failed for ${plan.title}: ${message}`);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
// Generate description markdown
|
|
209
|
+
const mdPath = path.join(shortsDir, `${shortSlug}.md`);
|
|
210
|
+
const mdContent = [
|
|
211
|
+
`# ${plan.title}\n`,
|
|
212
|
+
plan.description,
|
|
213
|
+
'',
|
|
214
|
+
'## Segments\n',
|
|
215
|
+
...plan.segments.map((s, i) => `${i + 1}. **${s.start.toFixed(2)}s – ${s.end.toFixed(2)}s** — ${s.description}`),
|
|
216
|
+
'',
|
|
217
|
+
'## Tags\n',
|
|
218
|
+
plan.tags.map((t) => `- ${t}`).join('\n'),
|
|
219
|
+
'',
|
|
220
|
+
].join('\n');
|
|
221
|
+
await fs.writeFile(mdPath, mdContent);
|
|
222
|
+
shorts.push({
|
|
223
|
+
id,
|
|
224
|
+
title: plan.title,
|
|
225
|
+
slug: shortSlug,
|
|
226
|
+
segments,
|
|
227
|
+
totalDuration,
|
|
228
|
+
outputPath,
|
|
229
|
+
captionedPath,
|
|
230
|
+
description: plan.description,
|
|
231
|
+
tags: plan.tags,
|
|
232
|
+
variants,
|
|
233
|
+
});
|
|
234
|
+
logger.info(`[ShortsAgent] Created short: ${plan.title} (${totalDuration.toFixed(1)}s)`);
|
|
235
|
+
}
|
|
236
|
+
logger.info(`[ShortsAgent] Generated ${shorts.length} shorts`);
|
|
237
|
+
return shorts;
|
|
238
|
+
}
|
|
239
|
+
finally {
|
|
240
|
+
await agent.destroy();
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
//# sourceMappingURL=ShortsAgent.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ShortsAgent.js","sourceRoot":"","sources":["../../src/agents/ShortsAgent.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { VideoFile, Transcript, SilenceRemovalResult } from '../types';
|
|
2
|
+
/**
|
|
3
|
+
* Detect silence, use the agent to decide context-aware removals, and produce
|
|
4
|
+
* an edited video with dead silence removed.
|
|
5
|
+
*
|
|
6
|
+
* Returns the path to the edited video, or the original path if no edits were needed.
|
|
7
|
+
*/
|
|
8
|
+
export declare function removeDeadSilence(video: VideoFile, transcript: Transcript): Promise<SilenceRemovalResult>;
|
|
9
|
+
//# sourceMappingURL=SilenceRemovalAgent.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SilenceRemovalAgent.d.ts","sourceRoot":"","sources":["../../src/agents/SilenceRemovalAgent.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,oBAAoB,EAAE,MAAM,UAAU,CAAA;AAmH3E;;;;;GAKG;AACH,wBAAsB,iBAAiB,CACrC,KAAK,EAAE,SAAS,EAChB,UAAU,EAAE,UAAU,GACrB,OAAO,CAAC,oBAAoB,CAAC,CAsI/B"}
|