mirror of
https://github.com/complexcaresolutions/cms.c2sgmbh.git
synced 2026-03-17 17:24:12 +00:00
feat: add bulk YouTube thumbnail download endpoint
POST /api/youtube/thumbnails/bulk (Super-Admin only) Supports ?dryRun=true for preview. Downloads sequentially with 500ms delay. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
2872f32635
commit
6ffe6e756c
1 changed files with 94 additions and 0 deletions
94
src/app/(payload)/api/youtube/thumbnails/bulk/route.ts
Normal file
94
src/app/(payload)/api/youtube/thumbnails/bulk/route.ts
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
import { NextResponse, type NextRequest } from 'next/server'
|
||||
import { getPayload } from 'payload'
|
||||
import config from '@payload-config'
|
||||
import { downloadAndUploadImage } from '@/lib/utils/media-download'
|
||||
import { getYouTubeThumbnail } from '@/lib/utils/youtube'
|
||||
|
||||
/**
|
||||
* POST /api/youtube/thumbnails/bulk
|
||||
*
|
||||
* Bulk-Download von YouTube-Thumbnails für alle YouTubeContent-Einträge
|
||||
* die noch kein Thumbnail haben. Erfordert Super-Admin-Rechte.
|
||||
*
|
||||
* Query-Parameter:
|
||||
* - dryRun=true: Zeigt nur was passieren würde, ohne tatsächlich herunterzuladen
|
||||
*/
|
||||
export async function POST(req: NextRequest) {
|
||||
try {
|
||||
const payload = await getPayload({ config })
|
||||
|
||||
const { user } = await payload.auth({ headers: req.headers })
|
||||
if (!user) {
|
||||
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
|
||||
}
|
||||
|
||||
const typedUser = user as { isSuperAdmin?: boolean }
|
||||
if (!typedUser.isSuperAdmin) {
|
||||
return NextResponse.json({ error: 'Super-Admin erforderlich' }, { status: 403 })
|
||||
}
|
||||
|
||||
const dryRun = req.nextUrl.searchParams.get('dryRun') === 'true'
|
||||
|
||||
// Find all YouTubeContent without thumbnail that have a videoId
|
||||
const contentWithoutThumbnail = await payload.find({
|
||||
collection: 'youtube-content',
|
||||
where: {
|
||||
thumbnail: { exists: false },
|
||||
'youtube.videoId': { exists: true },
|
||||
},
|
||||
limit: 500,
|
||||
depth: 0,
|
||||
})
|
||||
|
||||
const results = { processed: 0, downloaded: 0, skipped: 0, errors: 0, dryRun }
|
||||
|
||||
for (const doc of contentWithoutThumbnail.docs) {
|
||||
results.processed++
|
||||
const videoId = doc.youtube?.videoId
|
||||
if (!videoId) {
|
||||
results.skipped++
|
||||
continue
|
||||
}
|
||||
|
||||
if (dryRun) {
|
||||
results.downloaded++
|
||||
continue
|
||||
}
|
||||
|
||||
try {
|
||||
const thumbnailUrl = getYouTubeThumbnail(videoId, 'hq')
|
||||
const filename = `yt-thumb-${videoId}.jpg`
|
||||
const tenantId = typeof doc.tenant === 'object' ? doc.tenant?.id : doc.tenant
|
||||
|
||||
const mediaId = await downloadAndUploadImage(payload, {
|
||||
url: thumbnailUrl,
|
||||
filename,
|
||||
alt: `Thumbnail: ${typeof doc.title === 'string' ? doc.title : doc.title?.de || videoId}`,
|
||||
tenantId: tenantId || undefined,
|
||||
})
|
||||
|
||||
if (mediaId) {
|
||||
await payload.update({
|
||||
collection: 'youtube-content',
|
||||
id: doc.id,
|
||||
data: { thumbnail: mediaId },
|
||||
depth: 0,
|
||||
})
|
||||
results.downloaded++
|
||||
} else {
|
||||
results.errors++
|
||||
}
|
||||
} catch {
|
||||
results.errors++
|
||||
}
|
||||
|
||||
// Rate-limit delay (500ms between downloads)
|
||||
await new Promise((resolve) => setTimeout(resolve, 500))
|
||||
}
|
||||
|
||||
return NextResponse.json(results)
|
||||
} catch (error) {
|
||||
console.error('[yt-thumbnail-bulk] Error:', error)
|
||||
return NextResponse.json({ error: 'Internal Server Error' }, { status: 500 })
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue