Skip to content

Commit c285a7b

Browse files
Miriadresearch
andcommitted
feat: download NotebookLM infographics and upload to Sanity assets in check-research
- Import writeClient from sanity-write-client - Download PNGs with authenticated fetch using NotebookLM cookies - Upload to Sanity via writeClient.assets.upload() - Store image refs in infographics array field on doc - Keep backward-compat CDN URLs in researchData.infographicUrls - Graceful error handling per infographic (try/catch) Co-authored-by: research <research@miriad.systems>
1 parent 53e673a commit c285a7b

File tree

1 file changed

+77
-15
lines changed

1 file changed

+77
-15
lines changed

app/api/cron/check-research/route.ts

Lines changed: 77 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import { ArtifactTypeCode, ArtifactStatus } from '@/lib/services/notebooklm/type
1010
import { generateWithGemini, stripCodeFences } from '@/lib/gemini';
1111
import { getConfigValue } from '@/lib/config';
1212
import type { ResearchPayload } from '@/lib/services/research';
13+
import { writeClient } from '@/lib/sanity-write-client';
1314

1415
// ---------------------------------------------------------------------------
1516
// Types
@@ -351,20 +352,77 @@ async function stepInfographicsGenerating(
351352
return { id: doc._id, title: doc.title, step: 'infographics_generating', outcome: 'still_generating' };
352353
}
353354

354-
// Collect infographic URLs from completed artifacts
355+
// Download and upload infographics to Sanity
356+
interface SanityImageRef {
357+
_type: 'image';
358+
_key: string;
359+
alt?: string;
360+
asset: { _type: 'reference'; _ref: string };
361+
}
362+
363+
const infographicRefs: SanityImageRef[] = [];
355364
const infographicUrls: string[] = [];
356-
for (const artifactId of artifactIds) {
365+
366+
for (let i = 0; i < artifactIds.length; i++) {
367+
const artifactId = artifactIds[i];
357368
try {
358-
const url = await nbClient.getInfographicUrl(notebookId, artifactId);
359-
if (url) {
360-
infographicUrls.push(url);
369+
// Step 1: Get the auth-gated URL
370+
const authUrl = await nbClient.getInfographicUrl(notebookId, artifactId);
371+
if (!authUrl) {
372+
console.warn(`[check-research] No URL for artifact ${artifactId}`);
373+
continue;
374+
}
375+
376+
// Step 2: Download PNG with NotebookLM auth cookies
377+
const cookies = nbClient.getCookieHeader();
378+
const imageResponse = await fetch(authUrl, {
379+
headers: { Cookie: cookies },
380+
redirect: 'follow',
381+
});
382+
383+
if (!imageResponse.ok) {
384+
console.warn(`[check-research] Failed to download infographic ${artifactId}: ${imageResponse.status}`);
385+
continue;
361386
}
387+
388+
const contentType = imageResponse.headers.get('content-type') || '';
389+
if (!contentType.includes('image')) {
390+
console.warn(`[check-research] Infographic ${artifactId} returned non-image: ${contentType}`);
391+
continue;
392+
}
393+
394+
const arrayBuffer = await imageResponse.arrayBuffer();
395+
const buffer = Buffer.from(arrayBuffer);
396+
console.log(`[check-research] Downloaded infographic ${i + 1}: ${buffer.length} bytes`);
397+
398+
// Step 3: Upload to Sanity assets
399+
const filename = `infographic-${doc._id}-${i}.png`;
400+
const asset = await writeClient.assets.upload('image', buffer, {
401+
filename,
402+
contentType: 'image/png',
403+
});
404+
405+
console.log(`[check-research] Uploaded to Sanity: ${asset._id}`);
406+
407+
// Step 4: Build image reference for the array field
408+
const artifact = ourArtifacts.find(a => a.id === artifactId);
409+
infographicRefs.push({
410+
_type: 'image',
411+
_key: artifactId.slice(0, 8),
412+
alt: artifact?.title || `Research infographic ${i + 1}`,
413+
asset: { _type: 'reference', _ref: asset._id },
414+
});
415+
416+
// Also store the Sanity CDN URL for researchData backward compat
417+
const cdnUrl = `https://cdn.sanity.io/images/${process.env.NEXT_PUBLIC_SANITY_PROJECT_ID}/${process.env.NEXT_PUBLIC_SANITY_DATASET}/${asset._id.replace('image-', '').replace('-png', '.png').replace('-jpg', '.jpg')}`;
418+
infographicUrls.push(cdnUrl);
419+
362420
} catch (err) {
363-
console.warn(`[check-research] Failed to get infographic URL for ${artifactId}:`, err instanceof Error ? err.message : err);
421+
console.warn(`[check-research] Failed to process infographic ${artifactId}:`, err instanceof Error ? err.message : err);
364422
}
365423
}
366424

367-
console.log(`[check-research] Collected ${infographicUrls.length} infographic URLs`);
425+
console.log(`[check-research] Processed ${infographicRefs.length} infographics (${infographicUrls.length} URLs)`);
368426

369427
// Parse existing research data and add infographic URLs
370428
let researchData: Record<string, unknown> = {};
@@ -377,15 +435,19 @@ async function stepInfographicsGenerating(
377435
}
378436
researchData.infographicUrls = infographicUrls;
379437

380-
await sanity
381-
.patch(doc._id)
382-
.set({
383-
status: 'enriching',
384-
researchData: JSON.stringify(researchData),
385-
})
386-
.commit();
438+
const patchData: Record<string, unknown> = {
439+
status: 'enriching',
440+
researchData: JSON.stringify(researchData),
441+
};
442+
443+
// Add infographic image refs if we have any
444+
if (infographicRefs.length > 0) {
445+
patchData.infographics = infographicRefs;
446+
}
447+
448+
await sanity.patch(doc._id).set(patchData).commit();
387449

388-
console.log(`[check-research] "${doc.title}" → enriching (${infographicUrls.length} infographic URLs)`);
450+
console.log(`[check-research] "${doc.title}" → enriching (${infographicRefs.length} infographics, ${infographicUrls.length} URLs)`);
389451
return { id: doc._id, title: doc.title, step: 'infographics_generating', outcome: 'enriching' };
390452
}
391453

0 commit comments

Comments
 (0)