Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save johnlindquist/ce8f2040f17fceb5f0714a51d3ab9c48 to your computer and use it in GitHub Desktop.
Save johnlindquist/ce8f2040f17fceb5f0714a51d3ab9c48 to your computer and use it in GitHub Desktop.
// Name: summarize-latest-claude-print-video
import "@johnlindquist/kit"
import { GoogleGenAI, createUserContent, createPartFromUri } from '@google/genai';
const GEMINI_API_KEY = await env("GEMINI_API_KEY")
const ai = new GoogleGenAI({ apiKey: GEMINI_API_KEY });
const basicMemoryPath = home("Documents", "screenflow", "claude-code", "print", "*.mp4")
const videoFiles = await globby(basicMemoryPath)
const videoPath = await arg("Pick a video", videoFiles.map(v => {
return {
name: path.parse(v).base,
description: v,
value: v
}
}))
await editor(videoPath)
if (!videoPath) {
await div(`<div class="p-4 text-red-500">No video files found in ${basicMemoryPath}</div>`)
}
if (!videoPath.endsWith(".mp4")) {
await div(`<div class="p-4 text-red-500">Please select a video file.</div>`)
exit()
}
// Upload video
console.log("Uploading video...")
let file = await ai.files.upload({
file: videoPath,
config: { mimeType: 'video/mp4' }
});
await hide()
let attempts = 0;
const maxAttempts = 60; // 2 minutes max wait
while (file.state !== 'ACTIVE' && attempts < maxAttempts) {
await new Promise(resolve => setTimeout(resolve, 2000)); // Wait 2 seconds
file = await ai.files.get({ name: file.name });
attempts++;
console.log(`Checking file status (${attempts}/${maxAttempts}): ${file.state}`);
// Check for failure states
if (file.state === 'FAILED') {
throw new Error(`File processing failed: ${file.error?.message || 'Unknown error'}`);
}
}
if (file.state !== 'ACTIVE') {
throw new Error(`File did not become active after ${maxAttempts * 2} seconds`);
}
console.log("File is ready for use!");
let prompt = await readFile(kenvPath("snippets", "summarizer.txt"), "utf8")
const lessonExample = await readFile(kenvPath("snippets", "lesson-example.txt"), "utf8")
prompt = `${prompt}
<CRITICAL>
Follow the <EXAMPLE/> template precisely. It is required that you only output the markdown according to the <EXAMPLE/>.
</CRITICAL>
<EXAMPLE>
${lessonExample}
</EXAMPLE>
`
console.log("Generating content...")
const response = await ai.models.generateContent({
model: 'gemini-2.5-pro',
contents: createUserContent([
createPartFromUri(file.uri, file.mimeType),
prompt
])
});
log(JSON.stringify(response, null, 2))
const videoPathParsed = path.parse(videoPath)
const summaryPath = path.join(videoPathParsed.dir, videoPathParsed.name + ".md")
let summary = response.text
// File the line that starts with "title:", insert a line beneath it that starts with "video: ${videoPath}"
summary = summary.replace(/^title:.*$/m, (match) => `${match}\nvideo: ${videoPath}`)
await writeFile(summaryPath, summary)
await edit(summaryPath)
/* ─── Constants & Types ──────────────────────────────────────────────────── */
/* ─── API Layer – CourseBuilderClient ────────────────────────────────────── */
/* ─── VideoUploader ──────────────────────────────────────────────────────── */
/* ─── Domain Helpers ─────────────────────────────────────────────────────── */
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment