mirror of
https://github.com/complexcaresolutions/cms.c2sgmbh.git
synced 2026-03-17 20:54:11 +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