From b1dc81595436d5ed2fdcfbd986dd40562ce0ef4b Mon Sep 17 00:00:00 2001 From: michaelschal <35957947-michaelschal@users.noreply.replit.com> Date: Mon, 13 Oct 2025 12:05:51 +0000 Subject: [PATCH] Add ability to post videos to Facebook feeds and stories Adds support for publishing video content to Facebook feeds and stories via new API endpoints and service methods, addressing issues with video previews and direct video uploads. Replit-Commit-Author: Agent Replit-Commit-Session-Id: ae4037a0-2a6f-4530-9bac-79b543286bda Replit-Commit-Checkpoint-Type: intermediate_checkpoint Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/397bca8c-984f-43ff-841a-10897aeb8140/ae4037a0-2a6f-4530-9bac-79b543286bda/s8IyLQK --- server/services/facebook.ts | 69 +++++++++++++++++++++++++++++++++---- 1 file changed, 63 insertions(+), 6 deletions(-) diff --git a/server/services/facebook.ts b/server/services/facebook.ts index c9bd254..207c64c 100644 --- a/server/services/facebook.ts +++ b/server/services/facebook.ts @@ -10,6 +10,11 @@ interface FacebookFeedResponse { id: string; } +interface FacebookVideoResponse { + id: string; + post_id?: string; +} + interface FacebookError { error: { message: string; @@ -54,8 +59,8 @@ export class FacebookService { return storyIds.join(','); } else { // Default to feed - // Count images only (videos not supported in multi-photo carousel) const imageMedia = mediaList.filter(m => m.type === 'image'); + const videoMedia = mediaList.filter(m => m.type === 'video'); if (imageMedia.length > 1) { // Multi-photo carousel (images only) @@ -63,11 +68,9 @@ export class FacebookService { } else if (imageMedia.length === 1) { // Single image (even if mixed with videos, prioritize the image) return await this.publishPhotoPost(post, page, imageMedia[0]); - } else if (mediaList.length >= 1) { - // No images, but has media (likely video) - publish first media - // Note: Facebook /photos endpoint doesn't support videos well, this may fail - console.warn(`Publishing non-image media to Facebook feed - this may fail`); - return await this.publishPhotoPost(post, page, mediaList[0]); + } else if (videoMedia.length >= 1) { + // Video post - use video endpoint + return await this.publishVideoPost(post, page, videoMedia[0]); } else { // Text only return await this.publishTextPost(post, page); @@ -122,6 +125,32 @@ export class FacebookService { return data.post_id || data.id; } + private async publishVideoPost(post: Post, page: SocialPage, media: Media): Promise { + // Use original URL for video + const videoUrl = media.originalUrl; + + const params = new URLSearchParams({ + access_token: page.accessToken!, + description: post.content, + file_url: videoUrl, + }); + + const url = `${this.baseUrl}/${page.pageId}/videos?${params.toString()}`; + + const response = await fetch(url, { + method: 'POST', + }); + + if (!response.ok) { + const error = await response.json() as FacebookError; + throw new Error(`Facebook API error publishing video: ${error.error.message} (code: ${error.error.code})`); + } + + const data = await response.json() as FacebookVideoResponse; + // Return the post_id or id + return data.post_id || data.id; + } + private async publishMultiPhotoPost(post: Post, page: SocialPage, mediaList: Media[]): Promise { // Validate that all media are images const imageMedia = mediaList.filter(m => m.type === 'image'); @@ -279,6 +308,10 @@ export class FacebookService { } private async publishStory(post: Post, page: SocialPage, media: Media): Promise { + if (media.type === 'video') { + return this.publishVideoStory(post, page, media); + } + // Stories require a 2-step process: // Step 1: Upload photo as unpublished to get photo_id // Step 2: Publish the photo as a story using the photo_id @@ -326,6 +359,30 @@ export class FacebookService { const storyData = await storyResponse.json() as { success: boolean; post_id: string }; return storyData.post_id; } + + private async publishVideoStory(post: Post, page: SocialPage, media: Media): Promise { + // Video stories can be published directly to the video_stories endpoint + const videoUrl = media.originalUrl; + + const params = new URLSearchParams({ + access_token: page.accessToken!, + file_url: videoUrl, + }); + + const url = `${this.baseUrl}/${page.pageId}/video_stories?${params.toString()}`; + + const response = await fetch(url, { + method: 'POST', + }); + + if (!response.ok) { + const error = await response.json() as FacebookError; + throw new Error(`Facebook API error publishing video story: ${error.error.message} (code: ${error.error.code})`); + } + + const data = await response.json() as { success: boolean; post_id: string }; + return data.post_id; + } } export const facebookService = new FacebookService();