UNPKG

@memberjunction/actions-bizapps-social

Version:

Social Media Actions for MemberJunction - Twitter, LinkedIn, Facebook, Instagram, TikTok, YouTube, HootSuite, Buffer

247 lines (222 loc) 9.62 kB
import { RegisterClass } from '@memberjunction/global'; import { YouTubeBaseAction } from '../youtube-base.action'; import { ActionParam, ActionResultSimple, RunActionParams } from '@memberjunction/actions-base'; import { BaseAction } from '@memberjunction/actions'; /** * Action to schedule a YouTube video for publishing */ @RegisterClass(BaseAction, 'YouTubeScheduleVideoAction') export class YouTubeScheduleVideoAction extends YouTubeBaseAction { /** * Schedule a video for publishing on YouTube */ protected async InternalRunAction(params: RunActionParams): Promise<ActionResultSimple> { const { Params, ContextUser } = params; try { // Initialize OAuth const companyIntegrationId = this.getParamValue(Params, 'CompanyIntegrationID'); if (!companyIntegrationId) { throw new Error('CompanyIntegrationID is required'); } const initialized = await this.initializeOAuth(companyIntegrationId); if (!initialized) { throw new Error('Failed to initialize YouTube OAuth connection'); } // Extract parameters const videoId = this.getParamValue(Params, 'VideoID'); const publishAt = this.getParamValue(Params, 'PublishAt'); const notifySubscribers = this.getParamValue(Params, 'NotifySubscribers') ?? true; const premiereTiming = this.getParamValue(Params, 'PremiereTiming'); const enableChat = this.getParamValue(Params, 'EnableChat') ?? false; // Validate required parameters if (!videoId) { throw new Error('VideoID is required'); } if (!publishAt) { throw new Error('PublishAt date/time is required'); } // Validate publish date is in the future const publishDate = new Date(publishAt); if (publishDate <= new Date()) { throw new Error('PublishAt must be a future date/time'); } // Get current video details const currentVideo = await this.makeYouTubeRequest<any>( '/videos', 'GET', undefined, { part: 'snippet,status', id: videoId }, ContextUser ); if (!currentVideo.items || currentVideo.items.length === 0) { throw new Error(`Video not found: ${videoId}`); } const video = currentVideo.items[0]; // Verify video is currently private if (video.status.privacyStatus !== 'private') { throw new Error(`Video must be private to schedule. Current status: ${video.status.privacyStatus}`); } // Prepare update data const updateData: any = { id: videoId, status: { privacyStatus: 'private', // Keep private until publish time publishAt: this.formatDate(publishDate), selfDeclaredMadeForKids: video.status.selfDeclaredMadeForKids, notifySubscribers: notifySubscribers } }; // Handle premiere settings if (premiereTiming === 'premiere') { // Set up as a premiere updateData.status.privacyStatus = 'unlisted'; // Premieres start as unlisted updateData.liveStreamingDetails = { scheduledStartTime: this.formatDate(publishDate), enableLowLatency: false, enableAutoStart: true, enableDvr: true }; if (enableChat) { updateData.liveStreamingDetails.enableClosedCaptions = false; updateData.liveStreamingDetails.enableContentEncryption = false; updateData.liveStreamingDetails.enableEmbed = true; } } // Update the video const updateResponse = await this.makeYouTubeRequest<any>( '/videos', 'PUT', updateData, { part: 'status' + (premiereTiming === 'premiere' ? ',liveStreamingDetails' : '') }, ContextUser ); // Get updated video details const updatedVideo = await this.makeYouTubeRequest<any>( '/videos', 'GET', undefined, { part: 'snippet,status,contentDetails' + (premiereTiming === 'premiere' ? ',liveStreamingDetails' : ''), id: videoId }, ContextUser ); const finalVideo = updatedVideo.items[0]; // Calculate time until publish const timeUntilPublish = publishDate.getTime() - Date.now(); const hoursUntilPublish = Math.floor(timeUntilPublish / (1000 * 60 * 60)); const minutesUntilPublish = Math.floor((timeUntilPublish % (1000 * 60 * 60)) / (1000 * 60)); // Prepare result const result = { videoId: finalVideo.id, videoUrl: `https://www.youtube.com/watch?v=${finalVideo.id}`, title: finalVideo.snippet.title, scheduledFor: finalVideo.status.publishAt, scheduledForLocal: new Date(finalVideo.status.publishAt).toLocaleString(), timeUntilPublish: { hours: hoursUntilPublish, minutes: minutesUntilPublish, formatted: `${hoursUntilPublish}h ${minutesUntilPublish}m` }, privacyStatus: finalVideo.status.privacyStatus, notifySubscribers: notifySubscribers, isPremiere: premiereTiming === 'premiere', premiereDetails: premiereTiming === 'premiere' ? { scheduledStartTime: finalVideo.liveStreamingDetails?.scheduledStartTime, chatEnabled: enableChat } : null, thumbnails: finalVideo.snippet.thumbnails, duration: finalVideo.contentDetails.duration, quotaCost: this.getQuotaCost('videos.update') }; // Update output parameters const outputParams = [...Params]; const scheduleDetailsParam = outputParams.find(p => p.Name === 'ScheduleDetails'); if (scheduleDetailsParam) scheduleDetailsParam.Value = result; const scheduledTimeParam = outputParams.find(p => p.Name === 'ScheduledTime'); if (scheduledTimeParam) scheduledTimeParam.Value = result.scheduledFor; return { Success: true, ResultCode: 'SUCCESS', Message: `Video scheduled successfully for ${result.scheduledForLocal} (${result.timeUntilPublish.formatted} from now)`, Params: outputParams }; } catch (error) { const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred'; return { Success: false, ResultCode: this.getErrorCode(errorMessage), Message: `Failed to schedule video: ${errorMessage}`, Params }; } } /** * Get error code from error message */ private getErrorCode(message: string): string { if (message.includes('quota')) return 'QUOTA_EXCEEDED'; if (message.includes('401') || message.includes('unauthorized')) return 'INVALID_TOKEN'; if (message.includes('403') || message.includes('forbidden')) return 'INSUFFICIENT_PERMISSIONS'; if (message.includes('404')) return 'NOT_FOUND'; if (message.includes('rate limit')) return 'RATE_LIMIT'; if (message.includes('future')) return 'INVALID_DATE'; if (message.includes('private')) return 'INVALID_STATUS'; return 'ERROR'; } /** * Define the parameters this action expects */ public get Params(): ActionParam[] { const baseParams = this.commonSocialParams; const specificParams: ActionParam[] = [ { Name: 'VideoID', Type: 'Input', Value: null }, { Name: 'PublishAt', Type: 'Input', Value: null }, { Name: 'NotifySubscribers', Type: 'Input', Value: true }, { Name: 'PremiereTiming', Type: 'Input', Value: 'standard' }, { Name: 'EnableChat', Type: 'Input', Value: false }, { Name: 'ScheduleDetails', Type: 'Output', Value: null }, { Name: 'ScheduledTime', Type: 'Output', Value: null } ]; return [...baseParams, ...specificParams]; } /** * Metadata about this action */ public get Description(): string { return 'Schedules a private YouTube video to be published at a specific date/time with optional premiere settings'; } }