marketing-pipeline-content-automation

Automated AI content pipeline for research, scriptwriting, posting, and video generation using Claude/OpenAI and Remotion

Skill file

Preview skill file
---
name: marketing-pipeline-content-automation
description: Automated AI content pipeline for research, scriptwriting, posting, and video generation using Claude/OpenAI and Remotion
triggers:
  - automate content creation with AI research and video generation
  - set up automated marketing content pipeline
  - generate video content from text using Remotion
  - create content from keyword to published post automatically
  - build AI-powered content workflow with Claude and OpenAI
  - scrape trending news and generate social media content
  - auto-generate multilingual marketing content
  - create content pipeline with research and video rendering
---

# Marketing Pipeline Content Automation

> Skill by [ara.so](https://ara.so) — Marketing Skills collection.

This skill enables AI coding agents to work with the Ultimate AI Content Pipeline - a comprehensive TypeScript-based system that automates the entire content creation workflow from keyword research through news scraping, AI content generation (using Claude/OpenAI), and automatic video rendering with Remotion.

## What This Project Does

The marketing-pipeline-share project is an end-to-end content automation system that:

- **Auto-scans and researches**: Crawls real-time data from TechCrunch, a16z, Twitter/X, LinkedIn for trending topics
- **AI content generation**: Creates articles in multiple formats (Toplist, POV, Case Study, How-to) using Claude 3 or OpenAI
- **Multilingual support**: Generates content simultaneously in English and Vietnamese with customizable tone
- **Video rendering**: Automatically converts text content into videos/infographics using Remotion
- **Multi-platform optimization**: Exports videos optimized for Reels, TikTok, Shorts

## Installation

### Prerequisites

```bash
# Node.js 18+ required
node --version

# Install dependencies
npm install
# or
pnpm install
# or
yarn install
```

### Environment Configuration

Create a `.env.local` file in the project root:

```bash
# AI Provider Keys
ANTHROPIC_API_KEY=your_claude_key_here
OPENAI_API_KEY=your_openai_key_here

# Research/Scraping APIs
RAPIDAPI_KEY=your_rapidapi_key_here

# Database (if applicable)
DATABASE_URL=your_database_connection_string

# Remotion License (for video rendering)
REMOTION_LICENSE_KEY=your_remotion_license

# Optional: Social Media Auto-posting
FACEBOOK_ACCESS_TOKEN=your_fb_token
LINKEDIN_ACCESS_TOKEN=your_linkedin_token
```

### Development Server

```bash
npm run dev
# or
pnpm dev
# or
yarn dev
```

Access the application at `http://localhost:3000`

## Key Architecture

### Project Structure

```
marketing-pipeline-share/
├── src/
│   ├── app/              # Next.js App Router pages
│   ├── components/       # React components
│   ├── lib/
│   │   ├── ai/          # AI service integrations
│   │   ├── scraper/     # News scraping logic
│   │   ├── video/       # Remotion video rendering
│   │   └── utils/       # Helper functions
│   ├── types/           # TypeScript type definitions
│   └── config/          # Configuration files
├── remotion/            # Remotion video templates
├── public/              # Static assets
└── package.json
```

## Core Functionality

### 1. Content Research & Scraping

```typescript
// src/lib/scraper/newsScaper.ts
import { TrendingSources } from '@/types/research';

interface NewsArticle {
  title: string;
  url: string;
  source: string;
  publishedAt: Date;
  summary: string;
}

export async function scrapeNews(
  keyword: string,
  sources: TrendingSources[] = ['techcrunch', 'a16z', 'twitter']
): Promise<NewsArticle[]> {
  const rapidApiKey = process.env.RAPIDAPI_KEY;
  
  const articles: NewsArticle[] = [];
  
  for (const source of sources) {
    const response = await fetch(
      `https://api.rapidapi.com/news/${source}?q=${encodeURIComponent(keyword)}`,
      {
        headers: {
          'X-RapidAPI-Key': rapidApiKey!,
          'X-RapidAPI-Host': 'news-data.p.rapidapi.com'
        }
      }
    );
    
    const data = await response.json();
    articles.push(...data.articles);
  }
  
  return articles.filter(article => 
    isWithinLast24Hours(article.publishedAt)
  );
}

function isWithinLast24Hours(date: Date): boolean {
  const now = new Date();
  const hoursDiff = (now.getTime() - date.getTime()) / (1000 * 60 * 60);
  return hoursDiff <= 24;
}
```

### 2. AI Content Generation

```typescript
// src/lib/ai/contentGenerator.ts
import Anthropic from '@anthropic-ai/sdk';
import OpenAI from 'openai';

type ContentFormat = 'toplist' | 'pov' | 'case-study' | 'how-to';
type Language = 'en' | 'vi';
type Tone = 'expert' | 'friendly' | 'humorous';

interface ContentConfig {
  keyword: string;
  format: ContentFormat;
  language: Language;
  tone: Tone;
  researchData: string; // Scraped news insights
}

// Using Claude
export async function generateWithClaude(config: ContentConfig): Promise<string> {
  const anthropic = new Anthropic({
    apiKey: process.env.ANTHROPIC_API_KEY,
  });

  const systemPrompt = buildSystemPrompt(config.format, config.tone);
  const userPrompt = buildUserPrompt(config.keyword, config.researchData, config.language);

  const message = await anthropic.messages.create({
    model: 'claude-3-5-sonnet-20241022',
    max_tokens: 4096,
    system: systemPrompt,
    messages: [
      {
        role: 'user',
        content: userPrompt,
      },
    ],
  });

  return message.content[0].type === 'text' ? message.content[0].text : '';
}

// Using OpenAI
export async function generateWithOpenAI(config: ContentConfig): Promise<string> {
  const openai = new OpenAI({
    apiKey: process.env.OPENAI_API_KEY,
  });

  const systemPrompt = buildSystemPrompt(config.format, config.tone);
  const userPrompt = buildUserPrompt(config.keyword, config.researchData, config.language);

  const completion = await openai.chat.completions.create({
    model: 'gpt-4-turbo-preview',
    messages: [
      { role: 'system', content: systemPrompt },
      { role: 'user', content: userPrompt },
    ],
    temperature: 0.7,
    max_tokens: 4096,
  });

  return completion.choices[0].message.content || '';
}

function buildSystemPrompt(format: ContentFormat, tone: Tone): string {
  const formatInstructions = {
    'toplist': 'Create a numbered list article with clear rankings and explanations.',
    'pov': 'Write from a unique perspective with personal insights and opinions.',
    'case-study': 'Present a detailed case study with data, analysis, and outcomes.',
    'how-to': 'Write a step-by-step tutorial with actionable instructions.',
  };

  const toneInstructions = {
    'expert': 'Use professional, authoritative language with industry terminology.',
    'friendly': 'Write in a conversational, approachable style.',
    'humorous': 'Add wit and light humor while maintaining informativeness.',
  };

  return `You are an expert content creator. ${formatInstructions[format]} ${toneInstructions[tone]} Use the provided research data to create data-backed, trending content.`;
}

function buildUserPrompt(keyword: string, researchData: string, language: Language): string {
  const langInstruction = language === 'vi' 
    ? 'Write in Vietnamese.' 
    : 'Write in English.';
  
  return `
Keyword: ${keyword}

Recent Research Data:
${researchData}

${langInstruction}

Create a comprehensive, engaging article that incorporates the latest insights from the research data. Include specific examples, statistics, and actionable takeaways.
  `.trim();
}
```

### 3. Video Generation with Remotion

```typescript
// src/lib/video/renderVideo.ts
import { bundle } from '@remotion/bundler';
import { renderMedia, selectComposition } from '@remotion/renderer';
import path from 'path';

interface VideoConfig {
  content: string;
  title: string;
  format: 'reels' | 'tiktok' | 'shorts'; // 9:16 aspect ratio
  duration?: number; // in seconds
}

export async function renderContentVideo(config: VideoConfig): Promise<string> {
  const { content, title, format, duration = 30 } = config;

  // Bundle Remotion project
  const bundleLocation = await bundle({
    entryPoint: path.resolve('./remotion/index.ts'),
    webpackOverride: (config) => config,
  });

  // Select composition
  const composition = await selectComposition({
    serveUrl: bundleLocation,
    id: 'ContentVideo',
    inputProps: {
      title,
      content,
      format,
    },
  });

  // Output path
  const outputLocation = path.join(
    process.cwd(),
    'public',
    'videos',
    `${Date.now()}-${format}.mp4`
  );

  // Render video
  await renderMedia({
    composition,
    serveUrl: bundleLocation,
    codec: 'h264',
    outputLocation,
    inputProps: {
      title,
      content,
      format,
    },
  });

  return outputLocation;
}
```

```typescript
// remotion/ContentVideo.tsx
import { AbsoluteFill, Sequence, useCurrentFrame, useVideoConfig } from 'remotion';
import React from 'react';

interface ContentVideoProps {
  title: string;
  content: string;
  format: 'reels' | 'tiktok' | 'shorts';
}

export const ContentVideo: React.FC<ContentVideoProps> = ({ title, content, format }) => {
  const frame = useCurrentFrame();
  const { fps } = useVideoConfig();

  const opacity = Math.min(1, frame / (fps * 0.5));

  return (
    <AbsoluteFill style={{ backgroundColor: '#1a1a1a' }}>
      <Sequence from={0} durationInFrames={fps * 2}>
        <AbsoluteFill
          style={{
            justifyContent: 'center',
            alignItems: 'center',
            opacity,
          }}
        >
          <h1 style={{ color: 'white', fontSize: 60, textAlign: 'center', padding: 40 }}>
            {title}
          </h1>
        </AbsoluteFill>
      </Sequence>
      
      <Sequence from={fps * 2} durationInFrames={fps * 8}>
        <AbsoluteFill style={{ padding: 60, justifyContent: 'center' }}>
          <p style={{ color: 'white', fontSize: 32, lineHeight: 1.6 }}>
            {content}
          </p>
        </AbsoluteFill>
      </Sequence>
    </AbsoluteFill>
  );
};
```

### 4. Complete Pipeline Workflow

```typescript
// src/lib/pipeline/contentPipeline.ts
import { scrapeNews } from '@/lib/scraper/newsScaper';
import { generateWithClaude } from '@/lib/ai/contentGenerator';
import { renderContentVideo } from '@/lib/video/renderVideo';

interface PipelineConfig {
  keyword: string;
  format: 'toplist' | 'pov' | 'case-study' | 'how-to';
  language: 'en' | 'vi';
  tone: 'expert' | 'friendly' | 'humorous';
  generateVideo: boolean;
  videoFormat?: 'reels' | 'tiktok' | 'shorts';
}

interface PipelineResult {
  content: string;
  videoPath?: string;
  researchSources: number;
}

export async function runContentPipeline(
  config: PipelineConfig
): Promise<PipelineResult> {
  // Step 1: Research & Scraping
  console.log('🔍 Researching trending news...');
  const articles = await scrapeNews(config.keyword);
  
  const researchData = articles
    .map(article => `- ${article.title} (${article.source}): ${article.summary}`)
    .join('\n');

  // Step 2: AI Content Generation
  console.log('✍️ Generating content with AI...');
  const content = await generateWithClaude({
    keyword: config.keyword,
    format: config.format,
    language: config.language,
    tone: config.tone,
    researchData,
  });

  const result: PipelineResult = {
    content,
    researchSources: articles.length,
  };

  // Step 3: Video Generation (optional)
  if (config.generateVideo && config.videoFormat) {
    console.log('🎬 Rendering video...');
    const videoPath = await renderContentVideo({
      content: content.substring(0, 500), // First 500 chars for video
      title: config.keyword,
      format: config.videoFormat,
    });
    
    result.videoPath = videoPath;
  }

  console.log('✅ Pipeline complete!');
  return result;
}
```

### 5. API Route Example (Next.js)

```typescript
// src/app/api/generate/route.ts
import { NextRequest, NextResponse } from 'next/server';
import { runContentPipeline } from '@/lib/pipeline/contentPipeline';

export async function POST(request: NextRequest) {
  try {
    const body = await request.json();
    
    const {
      keyword,
      format = 'toplist',
      language = 'en',
      tone = 'expert',
      generateVideo = false,
      videoFormat = 'reels',
    } = body;

    if (!keyword) {
      return NextResponse.json(
        { error: 'Keyword is required' },
        { status: 400 }
      );
    }

    const result = await runContentPipeline({
      keyword,
      format,
      language,
      tone,
      generateVideo,
      videoFormat,
    });

    return NextResponse.json({
      success: true,
      data: result,
    });
  } catch (error) {
    console.error('Pipeline error:', error);
    return NextResponse.json(
      { error: 'Pipeline execution failed', details: error.message },
      { status: 500 }
    );
  }
}
```

### 6. Frontend Component Example

```typescript
// src/components/ContentGenerator.tsx
'use client';

import { useState } from 'react';

export default function ContentGenerator() {
  const [keyword, setKeyword] = useState('');
  const [loading, setLoading] = useState(false);
  const [result, setResult] = useState<any>(null);

  const handleGenerate = async () => {
    setLoading(true);
    try {
      const response = await fetch('/api/generate', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
          keyword,
          format: 'toplist',
          language: 'en',
          tone: 'expert',
          generateVideo: true,
          videoFormat: 'reels',
        }),
      });

      const data = await response.json();
      setResult(data);
    } catch (error) {
      console.error('Generation failed:', error);
    } finally {
      setLoading(false);
    }
  };

  return (
    <div className="p-8">
      <h1 className="text-3xl font-bold mb-6">AI Content Pipeline</h1>
      
      <div className="space-y-4">
        <input
          type="text"
          value={keyword}
          onChange={(e) => setKeyword(e.target.value)}
          placeholder="Enter keyword (e.g., AI Marketing)"
          className="w-full p-3 border rounded"
        />
        
        <button
          onClick={handleGenerate}
          disabled={loading || !keyword}
          className="bg-blue-600 text-white px-6 py-3 rounded disabled:opacity-50"
        >
          {loading ? 'Generating...' : 'Generate Content'}
        </button>

        {result && (
          <div className="mt-6 p-6 bg-gray-100 rounded">
            <h2 className="text-xl font-semibold mb-4">Results</h2>
            <p className="mb-2">Research Sources: {result.data.researchSources}</p>
            <div className="bg-white p-4 rounded">
              <pre className="whitespace-pre-wrap">{result.data.content}</pre>
            </div>
            {result.data.videoPath && (
              <div className="mt-4">
                <video controls src={result.data.videoPath} className="w-full max-w-md" />
              </div>
            )}
          </div>
        )}
      </div>
    </div>
  );
}
```

## Common Patterns

### Batch Content Generation

```typescript
// Generate multiple pieces of content for different keywords
async function batchGenerate(keywords: string[]) {
  const results = await Promise.allSettled(
    keywords.map(keyword =>
      runContentPipeline({
        keyword,
        format: 'toplist',
        language: 'en',
        tone: 'expert',
        generateVideo: false,
      })
    )
  );

  return results
    .filter(r => r.status === 'fulfilled')
    .map((r: any) => r.value);
}
```

### Scheduled Content Generation

```typescript
// Using node-cron or similar scheduler
import cron from 'node-cron';

// Run every day at 9 AM
cron.schedule('0 9 * * *', async () => {
  const trendingKeywords = await getTrendingKeywords();
  await batchGenerate(trendingKeywords);
});
```

## Troubleshooting

### API Rate Limits

```typescript
// Add rate limiting and retry logic
async function fetchWithRetry(url: string, options: any, maxRetries = 3) {
  for (let i = 0; i < maxRetries; i++) {
    try {
      const response = await fetch(url, options);
      if (response.status === 429) {
        await new Promise(resolve => setTimeout(resolve, 2000 * (i + 1)));
        continue;
      }
      return response;
    } catch (error) {
      if (i === maxRetries - 1) throw error;
    }
  }
}
```

### Video Rendering Memory Issues

```typescript
// For large-scale video rendering, use queue system
import Bull from 'bull';

const videoQueue = new Bull('video-rendering', {
  redis: { host: '127.0.0.1', port: 6379 },
});

videoQueue.process(async (job) => {
  return await renderContentVideo(job.data);
});

// Add job to queue instead of direct rendering
await videoQueue.add({ content, title, format });
```

### Environment Variables Not Loading

Ensure `.env.local` is in the root directory and restart the dev server:

```bash
# Kill all node processes
pkill node

# Restart
npm run dev
```

## Build & Deploy

```bash
# Build for production
npm run build

# Start production server
npm start

# For video rendering in production, ensure ffmpeg is installed
apt-get install ffmpeg
```

This skill provides comprehensive coverage for automating marketing content creation workflows using AI research, generation, and video rendering capabilities.

Source

Creator's repository · aradotso/marketing-skills

View on GitHub

Security

Security checks in progress
Results will appear here once audits complete
What this skill can do
Reads your filesConnects to the internetRuns code on your machine
Checked by 3 independent security firms
Does it try to trick the AI?Not yet checkedPending · Gen Agent Trust Hub
Does it sneak in hidden code?Not yet checkedPending · Socket
Does it have known bugs?Not yet checkedPending · Snyk