diff --git a/app/admin/upload/actions.ts b/app/admin/upload/actions.ts index 868c8ee..18eecb9 100644 --- a/app/admin/upload/actions.ts +++ b/app/admin/upload/actions.ts @@ -1,7 +1,9 @@ "use server"; import { requireAdmin } from "@/lib/auth/auth"; -import { publishVideoJob } from "@/lib/video/video-platforms"; +import { db } from "@/db"; +import { videoUploadJobs, videoUploads } from "@/db/schema"; +import { eq } from "drizzle-orm"; import { revalidatePath } from "next/cache"; export async function createVideoUpload(formData: FormData) { @@ -14,6 +16,17 @@ export async function createVideoUpload(formData: FormData) { export async function retryVideoJob(jobId: string) { await requireAdmin(); - await publishVideoJob(jobId).catch(() => undefined); + const job = await db.query.videoUploadJobs.findFirst({ + where: (table, { eq }) => eq(table.id, jobId), + }); + if (!job) return; + await db + .update(videoUploadJobs) + .set({ status: "pending", error: null }) + .where(eq(videoUploadJobs.id, job.id)); + await db + .update(videoUploads) + .set({ status: "partial", updatedAt: new Date() }) + .where(eq(videoUploads.id, job.uploadId)); revalidatePath("/admin/upload/history"); } diff --git a/lib/video/video-platforms.ts b/lib/video/video-platforms.ts index cacacb5..34c8f7d 100644 --- a/lib/video/video-platforms.ts +++ b/lib/video/video-platforms.ts @@ -243,10 +243,20 @@ async function publishTiktok(upload: typeof videoUploads.$inferSelect) { const settings = upload.tiktokSettings as TiktokSettings; const creatorResponse = await fetch( "https://open.tiktokapis.com/v2/post/publish/creator_info/query/", - { headers: { Authorization: `Bearer ${token}` } }, + { + method: "POST", + headers: { + Authorization: `Bearer ${token}`, + "Content-Type": "application/json; charset=UTF-8", + }, + }, ); - if (!creatorResponse.ok) - throw new Error("TikTok creator information lookup failed."); + if (!creatorResponse.ok) { + const detail = await creatorResponse.text().catch(() => ""); + throw new Error( + `TikTok creator information lookup failed (${creatorResponse.status})${detail ? `: ${detail}` : "."}`, + ); + } const creator = (await creatorResponse.json()) as { data?: { privacy_level_options?: string[] }; }; @@ -367,8 +377,13 @@ export async function processDueVideoUploads() { where: (table, { eq, and, ne }) => and(eq(table.uploadId, upload.id), ne(table.status, "completed")), }); - for (const job of jobs) - await publishVideoJob(job.id).catch(() => undefined); + for (const job of jobs) { + await publishVideoJob(job.id).catch((error) => { + const message = + error instanceof Error ? error.message : "Platform upload failed."; + console.error(`[${job.platform}] ${job.id}: ${message}`); + }); + } const remaining = await db.query.videoUploadJobs.findMany({ where: (table, { eq, and, ne }) => and(eq(table.uploadId, upload.id), ne(table.status, "completed")),