Auto Publisher is a Python automation tool that streamlines the workflow for e-commerce and product content creators. It automates the entire process from raw images and video files to SEO-optimised YouTube uploads, WordPress blog posts, and Facebook Reels using multimodal LLMs via OpenRouter.
- Smart Parsing: Reads structured product data from
input_data.txtusing a simple[SECTION]format. - Image Optimisation: Automatically renames files to SEO-friendly slugs, compresses images to under 2 MB, and resizes if needed.
- AI Vision Alt Text: Uses a vision model to generate accurate, SEO-optimised alt text per image based on product features.
- YouTube Automation: Generates SEO-optimised titles and descriptions, then uploads the video via YouTube Data API v3.
- WordPress Integration:
- Uploads images to the Media Library with AI-generated alt text and captions.
- Generates a complete HTML blog post with naturally embedded images and a YouTube embed block.
- Creates a scheduled draft post, with the publish date derived from the folder name (
YYYY-MM-DD).
- Facebook Reels: Generates an AI caption with hashtags and uploads the video as a Facebook Reel via the Graph API (3-step binary upload).
- Model Flexibility: Every LLM call uses a configurable primary + fallback model via OpenRouter. Override per-function in
.env.
- Python 3.10+
- OpenRouter API Key
- YouTube Data API v3 OAuth2 credentials (
client_secret.json) - WordPress Application Password (Settings → Users → Application Passwords)
- Facebook Page Access Token with
pages_manage_posts+pages_read_engagementscopes
git clone https://github.com/your-username/auto-publisher.git
cd auto-publisherpython -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install -r requirements.txtThe project uses two separate files to keep settings apart from secrets:
.env — copy from .env.example, fill in your WordPress URL and model preferences:
WP_WEBSITE_URL=https://your-site.com
YOUTUBE_CLIENT_SECRET_FILE=client_secret.json
# Optional: override AI models (see .env.example for all options)
MODEL_ALT_TEXT=google/gemini-2.0-flash
MODEL_BLOG_POST=openai/gpt-4o-mini
MODEL_FB_CAPTION=openai/gpt-4o-minisecrets.env — copy from secrets.env.example, fill in your credentials:
WP_USERNAME=your_wp_username
WP_APP_PASSWORD=xxxx xxxx xxxx xxxx xxxx xxxx
OPENROUTER_API_KEY=sk-or-...
FB_PAGE_ID=your_page_id
FB_PAGE_ACCESS_TOKEN=your_page_access_tokenYouTube: Place client_secret.json in the root folder. A youtube_token.pickle will be generated automatically on first run (browser OAuth popup).
Facebook: No interactive auth flow — the token in secrets.env is used directly. Get a long-lived Page Access Token from the Graph API Explorer or generate a non-expiring token via Facebook Business Manager → System Users.
Each product batch lives in its own date-named folder (YYYY-MM-DD). Inside, create an input_data.txt using the format in input_data.txt.example:
[PRODUCT]
Name: Your Product Name Here
[KEY_FEATURES]
- Key feature or spec 1
- Key feature or spec 2
- Available sizes, colours, or variants
[HASHTAGS]
#YourBrand #Category #Keyword1 #Keyword2
Place all product images (JPG/PNG/WEBP) and one video (MP4/MOV) in the same folder. Files are renamed automatically to SEO slugs on first run.
Example folder layout:
2026-03-15/
├── input_data.txt
├── product photo 1.jpg
├── product photo 2.jpg
└── product video.mp4
| Mode | Command | What it does |
|---|---|---|
all (default) |
python run.py ./2026-03-15 |
Full pipeline: prepare + publish |
prepare |
python run.py ./2026-03-15 --mode prepare |
Rename, compress, all LLM generation → llm_cache.json |
publish |
python run.py ./2026-03-15 --mode publish |
YouTube + WordPress + Facebook Reel |
youtube |
python run.py ./2026-03-15 --mode youtube |
YouTube upload only |
wordpress |
python run.py ./2026-03-15 --mode wordpress |
WordPress only |
facebook |
python run.py ./2026-03-15 --mode facebook |
Facebook Reel only |
Optional flags:
python run.py ./2026-03-15 --verbose # show DEBUG logs
python run.py ./2026-03-15 --quiet # errors and final summary only
python run.py ./2026-03-15 --force # bypass done-guard (cache is still used)Run prepare first to review all AI-generated content before uploading:
# Phase 1 — generate all LLM content, save to llm_cache.json
python run.py ./2026-03-15 --mode prepare
# Edit llm_cache.json to adjust any title, description, or caption
# Phase 2 — upload everything
python run.py ./2026-03-15 --mode publish| Step | Action |
|---|---|
| 1 | Parse input_data.txt, rename files to SEO slugs |
| 2 | Compress and resize images |
| 3 | Generate AI alt text per image (vision model) |
| 4 | Generate WordPress title |
| 5 | Generate YouTube title + description |
| 6 | Generate Facebook Reel caption with hashtags |
| 7 | Upload video to YouTube |
| 8 | Upload images to WordPress Media Library |
| 9 | Generate HTML blog post, create scheduled WordPress draft |
| 10 | Upload video as Facebook Reel (3-step binary upload) |
| 11 | Save results.json to the folder |
Facebook uses a static Page Access Token — no browser popup or OAuth flow. The token is read directly from secrets.env.
| YouTube | ||
|---|---|---|
| Auth type | OAuth2 (browser popup) | Page Access Token |
| First run | Opens browser, you log in | Nothing — token in secrets.env |
| Token storage | youtube_token.pickle (auto-refreshed) |
secrets.env (manual update when expired) |
| Token expiry | Handled automatically | Short-lived ~1h / Long-lived ~60 days / System User = never |
When a token expires the upload fails at Step 1 with Invalid OAuth access token. Paste a fresh token into secrets.env and re-run.
auto-publisher/
├── run.py # Entry point
├── auto_publisher/
│ ├── __main__.py # Pipeline orchestrator
│ ├── config.py # Env loading, model routing, constants
│ ├── parser.py # input_data.txt parsing + file renaming
│ ├── images.py # Image compression + base64 encoding
│ ├── llm.py # OpenRouter LLM calls (alt text, content generation)
│ ├── youtube.py # YouTube Data API upload
│ ├── wordpress.py # WordPress REST API (media + draft)
│ ├── facebook.py # Facebook Graph API — Reels upload (3-step)
│ ├── cache.py # LLM cache (llm_cache.json) read/write
│ ├── output.py # Summary printing + results.json
│ ├── utils.py # Slugify, date parsing helpers
│ └── prompts/ # Prompt templates (.txt)
│ ├── alt_text.txt
│ ├── blog_post.txt
│ ├── wp_title.txt
│ ├── yt_description.txt
│ ├── yt_title.txt
│ └── fb_caption.txt
├── tests/
│ ├── test_alt_text.py # Vision model comparison across images
│ └── test_facebook_reel.py # Facebook Reel 3-step upload test
├── .env.example # Settings template (model IDs, URLs)
├── secrets.env.example # Secrets template (API keys, passwords)
├── requirements.txt
└── .gitignore
- Rate limiting: Alt text generation inserts a 3.5-second delay between images to stay within the OpenRouter 20 req/min limit.
- Re-runs: The script is safe to re-run on the same folder. A done-guard checks
results.jsonandllm_cache.jsonto avoid duplicate uploads. Use--forceto override. - LLM cache: All generated text (titles, descriptions, captions) is cached in
llm_cache.json. Edit this file before running--mode publishto fine-tune any content. - Imunify360: WordPress uploads use full Chromium browser headers (
sec-ch-ua,sec-fetch-*) to pass bot-protection checks on shared hosting. - Date scheduling: The WordPress draft publish date is parsed from the folder name (e.g.,
2026-03-15→ scheduled for2026-03-15T12:00:00). - Facebook Reels video requirements: min 540×960 resolution, 9:16 aspect ratio, 4–60 seconds duration, MP4/MOV format.
This project is for automation purposes. Ensure you comply with the YouTube Terms of Service, WordPress API usage limits, and Facebook Platform Policy. Never commit secrets.env, .env, or client_secret.json to version control.