> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/darkzOGx/youtube-automation-agent/llms.txt
> Use this file to discover all available pages before exploring further.

# Publishing & Scheduling Agent

> Learn how the Publishing & Scheduling Agent automates video uploads and optimizes publishing times

## Overview

The **Publishing & Scheduling Agent** is your automated publishing manager that handles the entire video upload process, optimizes publishing times based on channel analytics, and manages your content calendar. It ensures your videos are published at optimal times for maximum reach and engagement.

<Card title="What It Does" icon="cloud-upload">
  The Publishing & Scheduling Agent manages the publish queue, uploads videos to YouTube with all metadata, schedules content for optimal times, and provides publishing analytics and performance tracking.
</Card>

## Key Features

<CardGroup cols={2}>
  <Card title="Automated Uploads" icon="upload">
    Handles complete video upload including metadata, thumbnails, and captions
  </Card>

  <Card title="Smart Scheduling" icon="calendar-check">
    Optimizes publish times based on channel analytics
  </Card>

  <Card title="Queue Management" icon="list">
    Manages publishing queue with priority handling
  </Card>

  <Card title="Publishing Reports" icon="chart-line">
    Tracks publishing performance and schedule accuracy
  </Card>
</CardGroup>

## Core Methods

### scheduleContent()

Adds content to the publishing queue with optimal timing.

```javascript publishing-scheduling-agent.js theme={null}
async scheduleContent(productionData) {
  this.logger.info(`Scheduling content: ${productionData.id}`);
  
  const scheduleEntry = {
    productionId: productionData.id,
    title: productionData.script.title,
    publishTime: productionData.scheduledPublishTime,
    status: 'scheduled',
    priority: productionData.priority,
    metadata: {
      seo: productionData.seo,
      thumbnail: productionData.assets.thumbnail,
      video: productionData.assets.finalVideo,
      captions: productionData.assets.captions
    },
    createdAt: new Date().toISOString()
  };
  
  this.publishQueue.push(scheduleEntry);
  this.publishQueue.sort((a, b) => 
    new Date(a.publishTime) - new Date(b.publishTime)
  );
  
  await this.db.saveScheduleEntry(scheduleEntry);
  
  this.logger.info(`Content scheduled for: ${scheduleEntry.publishTime}`);
  return scheduleEntry;
}
```

### publishContent()

Handles the complete video upload process to YouTube.

```javascript publishing-scheduling-agent.js theme={null}
async publishContent(contentId) {
  this.logger.info(`Publishing content: ${contentId}`);
  
  const scheduleEntry = this.publishQueue.find(entry => 
    entry.productionId === contentId || entry.id === contentId
  );
  
  if (!scheduleEntry) {
    throw new Error(`Content not found in queue: ${contentId}`);
  }
  
  // Upload video to YouTube
  const uploadResult = await this.uploadToYouTube(scheduleEntry);
  
  // Update database
  scheduleEntry.status = 'published';
  scheduleEntry.publishedAt = new Date().toISOString();
  scheduleEntry.youtubeId = uploadResult.id;
  scheduleEntry.youtubeUrl = `https://www.youtube.com/watch?v=${uploadResult.id}`;
  
  await this.db.updateScheduleEntry(scheduleEntry);
  
  // Remove from queue
  this.publishQueue = this.publishQueue.filter(entry => 
    entry.id !== scheduleEntry.id
  );
  
  this.logger.success(`Content published: ${scheduleEntry.youtubeUrl}`);
  return scheduleEntry;
}
```

### uploadToYouTube()

Handles the YouTube API upload with all metadata.

```javascript publishing-scheduling-agent.js theme={null}
async uploadToYouTube(scheduleEntry) {
  const { metadata } = scheduleEntry;
  
  // Prepare video metadata
  const videoMetadata = {
    snippet: {
      title: metadata.seo.title,
      description: metadata.seo.description,
      tags: metadata.seo.tags,
      categoryId: metadata.seo.metadata.category.toString(),
      defaultLanguage: metadata.seo.metadata.language,
      defaultAudioLanguage: metadata.seo.metadata.language
    },
    status: {
      privacyStatus: process.env.DEFAULT_PRIVACY_STATUS || 'public',
      publishAt: scheduleEntry.publishTime,
      selfDeclaredMadeForKids: false
    }
  };
  
  // Upload video file
  const videoUpload = await this.youtube.videos.insert({
    part: 'snippet,status',
    requestBody: videoMetadata,
    media: {
      body: await this.getVideoStream(metadata.video.path)
    }
  });
  
  const videoId = videoUpload.data.id;
  this.logger.info(`Video uploaded with ID: ${videoId}`);
  
  // Upload thumbnail
  if (metadata.thumbnail && metadata.thumbnail.path) {
    await this.uploadThumbnail(videoId, metadata.thumbnail.path);
  }
  
  // Upload captions
  if (metadata.captions && metadata.captions.path) {
    await this.uploadCaptions(videoId, metadata.captions.path);
  }
  
  return videoUpload.data;
}
```

## Publishing Workflow

<Steps>
  <Step title="Content Enters Queue">
    Production agent adds completed content to publish queue
  </Step>

  <Step title="Time Optimization">
    Agent analyzes optimal publish time based on channel analytics
  </Step>

  <Step title="Priority Assignment">
    Content is prioritized based on trend sensitivity and estimated impact
  </Step>

  <Step title="Automatic Publishing">
    At scheduled time, agent uploads video with all metadata
  </Step>

  <Step title="Thumbnail Upload">
    Custom thumbnail is uploaded after video processing
  </Step>

  <Step title="Caption Upload">
    Generated captions are added for accessibility
  </Step>

  <Step title="Status Tracking">
    Publishing success is recorded and analytics tracking begins
  </Step>
</Steps>

## Publish Time Optimization

The agent analyzes channel data to find optimal publishing times:

### optimizePublishTimes()

```javascript publishing-scheduling-agent.js theme={null}
async optimizePublishTimes() {
  // Analyze channel analytics to find optimal publish times
  const analytics = await this.getChannelAnalytics();
  const optimalTimes = this.calculateOptimalTimes(analytics);
  
  // Update scheduled content with better times
  for (const entry of this.publishQueue) {
    if (entry.status === 'scheduled') {
      const currentTime = new Date(entry.publishTime);
      const betterTime = this.findBetterTime(currentTime, optimalTimes);
      
      if (betterTime && betterTime.getTime() !== currentTime.getTime()) {
        entry.publishTime = betterTime.toISOString();
        await this.db.updateScheduleEntry(entry);
        this.logger.info(`Optimized publish time for: ${entry.title}`);
      }
    }
  }
}
```

### findBetterTime()

```javascript publishing-scheduling-agent.js theme={null}
findBetterTime(currentTime, optimalTimes) {
  const currentDay = currentTime.toLocaleDateString('en-US', { weekday: 'long' });
  const currentHour = currentTime.getHours();
  
  // If current time is already optimal, return null
  if (optimalTimes.bestDays.includes(currentDay) && 
      optimalTimes.bestHours.includes(currentHour)) {
    return null;
  }
  
  // Find the next optimal time
  const nextOptimalTime = new Date(currentTime);
  
  // Try to find an optimal hour on the same day
  for (const hour of optimalTimes.bestHours) {
    if (hour > currentHour) {
      nextOptimalTime.setHours(hour, 0, 0, 0);
      if (optimalTimes.bestDays.includes(currentDay)) {
        return nextOptimalTime;
      }
    }
  }
  
  // Find next optimal day
  for (let i = 1; i <= 7; i++) {
    const testDate = new Date(currentTime.getTime() + (i * 24 * 60 * 60 * 1000));
    const testDay = testDate.toLocaleDateString('en-US', { weekday: 'long' });
    
    if (optimalTimes.bestDays.includes(testDay)) {
      testDate.setHours(optimalTimes.bestHours[0], 0, 0, 0);
      return testDate;
    }
  }
  
  return null;
}
```

<Note>
  The agent analyzes your channel's historical performance to identify when your audience is most active and engaged.
</Note>

## Optimal Publishing Times

Based on YouTube analytics research:

<Tabs>
  <Tab title="Best Days">
    **Tuesday - Thursday**: Peak engagement days

    **Saturday - Sunday**: Strong weekend traffic

    ```javascript theme={null}
    optimalDays: ['Tuesday', 'Wednesday', 'Thursday']
    ```
  </Tab>

  <Tab title="Best Hours">
    **2-4 PM**: Afternoon peak (14:00-16:00)

    **8-10 PM**: Evening peak (20:00-22:00)

    ```javascript theme={null}
    optimalHours: [14, 15, 16, 20, 21]
    ```
  </Tab>

  <Tab title="Worst Times">
    **Monday mornings**: Low engagement

    **Late night/early morning**: Minimal traffic

    ```javascript theme={null}
    worstHours: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
    ```
  </Tab>
</Tabs>

## Queue Management

The publishing queue supports advanced management:

### processPublishQueue()

Automatically processes scheduled content:

```javascript publishing-scheduling-agent.js theme={null}
async processPublishQueue() {
  this.logger.info('Processing publish queue...');
  
  const now = new Date();
  const readyToPublish = this.publishQueue.filter(entry => {
    const publishTime = new Date(entry.publishTime);
    return publishTime <= now && entry.status === 'scheduled';
  });
  
  for (const entry of readyToPublish) {
    try {
      await this.publishContent(entry.productionId);
      this.logger.info(`Auto-published: ${entry.title}`);
    } catch (error) {
      this.logger.error(`Failed to auto-publish ${entry.title}:`, error);
      // Mark as failed but don't stop processing
      entry.status = 'failed';
      entry.error = error.message;
      await this.db.updateScheduleEntry(entry);
    }
  }
  
  return readyToPublish.length;
}
```

### Priority System

```javascript publishing-scheduling-agent.js theme={null}
calculatePriority(strategy) {
  let priority = 50; // Base priority
  
  // Adjust based on estimated views
  if (strategy.estimatedViews > 100000) priority += 30;
  else if (strategy.estimatedViews > 50000) priority += 20;
  else if (strategy.estimatedViews > 10000) priority += 10;
  
  // Adjust based on trend score
  if (strategy.competitorAnalysis && strategy.competitorAnalysis.length > 0) {
    priority += 10;
  }
  
  // Time sensitivity
  const hoursUntilPublish = (new Date(strategy.bestPublishTime) - new Date()) / (1000 * 60 * 60);
  if (hoursUntilPublish < 24) priority += 20;
  else if (hoursUntilPublish < 48) priority += 10;
  
  return Math.min(100, priority);
}
```

<AccordionGroup>
  <Accordion title="High Priority (80-100)">
    Time-sensitive trending topics with high estimated views
  </Accordion>

  <Accordion title="Medium Priority (50-79)">
    Standard content with moderate estimated engagement
  </Accordion>

  <Accordion title="Low Priority (0-49)">
    Evergreen content that can be published anytime
  </Accordion>
</AccordionGroup>

## Upload Components

### uploadThumbnail()

Uploads custom thumbnail after video processing:

```javascript publishing-scheduling-agent.js theme={null}
async uploadThumbnail(videoId, thumbnailPath) {
  try {
    const thumbnailBuffer = await fs.readFile(thumbnailPath);
    
    await this.youtube.thumbnails.set({
      videoId: videoId,
      media: {
        body: thumbnailBuffer
      }
    });
    
    this.logger.info(`Thumbnail uploaded for video: ${videoId}`);
  } catch (error) {
    this.logger.error(`Failed to upload thumbnail: ${error.message}`);
  }
}
```

### uploadCaptions()

Adds caption files for accessibility:

```javascript publishing-scheduling-agent.js theme={null}
async uploadCaptions(videoId, captionsPath) {
  try {
    const captionsContent = await fs.readFile(captionsPath, 'utf8');
    
    await this.youtube.captions.insert({
      part: 'snippet',
      requestBody: {
        snippet: {
          videoId: videoId,
          language: 'en',
          name: 'English Captions',
          isDraft: false
        }
      },
      media: {
        body: captionsContent
      }
    });
    
    this.logger.info(`Captions uploaded for video: ${videoId}`);
  } catch (error) {
    this.logger.error(`Failed to upload captions: ${error.message}`);
  }
}
```

## Emergency Publishing

For urgent content needs:

### emergencyPublish()

```javascript publishing-scheduling-agent.js theme={null}
async emergencyPublish(contentId, delayMinutes = 0) {
  this.logger.info(`Emergency publish requested: ${contentId}`);
  
  const entry = this.publishQueue.find(e => 
    e.productionId === contentId || e.id === contentId
  );
  
  if (!entry) {
    throw new Error(`Content not found: ${contentId}`);
  }
  
  if (delayMinutes > 0) {
    const newPublishTime = new Date(Date.now() + (delayMinutes * 60 * 1000));
    entry.publishTime = newPublishTime.toISOString();
    await this.db.updateScheduleEntry(entry);
    this.logger.info(`Emergency scheduled for: ${entry.publishTime}`);
    return entry;
  } else {
    return await this.publishContent(contentId);
  }
}
```

### Pause & Resume

```javascript publishing-scheduling-agent.js theme={null}
async pauseScheduledContent(contentId) {
  const entry = this.publishQueue.find(e => 
    e.productionId === contentId || e.id === contentId
  );
  
  if (!entry) throw new Error(`Content not found: ${contentId}`);
  
  entry.status = 'paused';
  await this.db.updateScheduleEntry(entry);
  
  this.logger.info(`Content paused: ${entry.title}`);
  return entry;
}

async resumeScheduledContent(contentId, newPublishTime = null) {
  const entry = this.publishQueue.find(e => 
    e.productionId === contentId || e.id === contentId
  );
  
  if (!entry) throw new Error(`Content not found: ${contentId}`);
  
  entry.status = 'scheduled';
  if (newPublishTime) {
    entry.publishTime = new Date(newPublishTime).toISOString();
  }
  
  await this.db.updateScheduleEntry(entry);
  
  this.logger.info(`Content resumed: ${entry.title}`);
  return entry;
}
```

## Publishing Reports

The agent generates comprehensive publishing analytics:

### createPublishingReport()

```javascript publishing-scheduling-agent.js theme={null}
async createPublishingReport() {
  const report = {
    queueStatus: {
      total: this.publishQueue.length,
      scheduled: this.publishQueue.filter(e => e.status === 'scheduled').length,
      published: this.publishQueue.filter(e => e.status === 'published').length,
      failed: this.publishQueue.filter(e => e.status === 'failed').length
    },
    upcomingPublications: await this.getUpcomingSchedule(7),
    recentPublications: this.publishQueue
      .filter(e => e.status === 'published' && 
              new Date(e.publishedAt) > new Date(Date.now() - 7 * 24 * 60 * 60 * 1000))
      .sort((a, b) => new Date(b.publishedAt) - new Date(a.publishedAt)),
    performance: await this.getPublishingPerformance(),
    generatedAt: new Date().toISOString()
  };
  
  return report;
}
```

### Publishing Performance Metrics

```javascript publishing-scheduling-agent.js theme={null}
async getPublishingPerformance() {
  const published = this.publishQueue.filter(e => e.status === 'published');
  
  if (published.length === 0) {
    return {
      totalPublished: 0,
      averageScheduleAccuracy: 0,
      publishingFrequency: 0
    };
  }
  
  // Calculate schedule accuracy
  let totalDelay = 0;
  let accuratePublishes = 0;
  
  published.forEach(entry => {
    const scheduledTime = new Date(entry.publishTime);
    const actualTime = new Date(entry.publishedAt);
    const delay = Math.abs(actualTime - scheduledTime) / (1000 * 60);
    
    totalDelay += delay;
    if (delay <= 5) accuratePublishes++;
  });
  
  const averageDelay = totalDelay / published.length;
  const accuracyRate = (accuratePublishes / published.length) * 100;
  
  return {
    totalPublished: published.length,
    averageScheduleAccuracy: `${accuracyRate.toFixed(1)}%`,
    averageDelay: `${averageDelay.toFixed(1)} minutes`,
    publishingFrequency: this.calculatePublishingFrequency(published)
  };
}
```

## Configuration

```bash .env theme={null}
# Default privacy status (public|unlisted|private)
DEFAULT_PRIVACY_STATUS=public

# Auto-publish enabled
AUTO_PUBLISH_ENABLED=true

# Queue processing interval (minutes)
QUEUE_CHECK_INTERVAL=5

# Maximum retries for failed uploads
MAX_UPLOAD_RETRIES=3

# Upload timeout (minutes)
UPLOAD_TIMEOUT=30
```

## Example Schedule Entry

```json theme={null}
{
  "productionId": "prod_1234567890_abc123",
  "title": "Ultimate Guide to AI Technology Trends (2026)",
  "publishTime": "2026-03-07T14:00:00.000Z",
  "status": "scheduled",
  "priority": 85,
  "metadata": {
    "seo": { /* SEO data */ },
    "thumbnail": { /* Thumbnail data */ },
    "video": { /* Video file data */ },
    "captions": { /* Caption file data */ }
  },
  "youtubeId": "dQw4w9WgXcQ",
  "youtubeUrl": "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
  "publishedAt": "2026-03-07T14:00:23.000Z"
}
```

## Best Practices

<AccordionGroup>
  <Accordion title="Schedule in Advance">
    Queue content at least 24 hours before intended publish time for optimal scheduling
  </Accordion>

  <Accordion title="Monitor Queue Status">
    Regularly check the publishing queue to identify any failed uploads
  </Accordion>

  <Accordion title="Test Publish Times">
    Use analytics data to continuously refine optimal publishing times for your specific audience
  </Accordion>

  <Accordion title="Maintain Consistency">
    Publish on a consistent schedule to build audience expectations
  </Accordion>
</AccordionGroup>

## Performance Metrics

<CardGroup cols={3}>
  <Card title="Upload Time" icon="clock">
    5-15 minutes
  </Card>

  <Card title="Schedule Accuracy" icon="bullseye">
    ±5 minutes
  </Card>

  <Card title="Success Rate" icon="check">
    98%+
  </Card>
</CardGroup>

## Next Steps

<CardGroup cols={2}>
  <Card title="Analytics Agent" icon="chart-bar" href="/features/analytics-optimization">
    Track performance after publishing
  </Card>

  <Card title="Production Agent" icon="film">
    Learn about the video production pipeline
  </Card>

  <Card title="Configuration" icon="gear" href="/configuration/getting-api-keys">
    Configure publishing settings
  </Card>

  <Card title="API Reference" icon="code" href="/api/agents/publishing-scheduling">
    View complete API documentation
  </Card>
</CardGroup>
