Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ MPV2 (MoneyPrinter Version 2) is, as the name suggests, the second version of th
## Features

- [x] Twitter Bot (with CRON Jobs => `scheduler`)
- [x] YouTube Shorts Automator (with CRON Jobs => `scheduler`)
- [x] PostBridge-first Video Publishing (with CRON Jobs => `scheduler`)
- [x] Affiliate Marketing (Amazon + Twitter)
- [x] Find local businesses & cold outreach

Expand Down Expand Up @@ -75,7 +75,7 @@ All relevant documents can be found [here](docs/).

For easier usage, there are some scripts in the `scripts` directory that can be used to directly access the core functionality of MPV2 without the need for user interaction.

All scripts need to be run from the root directory of the project, e.g. `bash scripts/upload_video.sh`.
All scripts need to be run from the root directory of the project, e.g. `bash scripts/publish_video.sh`.

## Contributing

Expand Down
9 changes: 7 additions & 2 deletions config.example.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@
"verbose": true,
"firefox_profile": "",
"headless": false,
"video_publishing": {
"profile_name": "Default Publisher",
"niche": "",
"language": "English"
},
"ollama_base_url": "http://127.0.0.1:11434",
"ollama_model": "",
"twitter_language": "English",
Expand Down Expand Up @@ -35,8 +40,8 @@
"post_bridge": {
"enabled": false,
"api_key": "",
"platforms": ["tiktok", "instagram"],
"platforms": ["youtube", "tiktok", "instagram"],
"account_ids": [],
"auto_crosspost": false
"auto_publish": false
}
}
23 changes: 16 additions & 7 deletions docs/Configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,12 @@ All your configurations will be in a file in the root directory, called `config.
## Values

- `verbose`: `boolean` - If `true`, the application will print out more information.
- `firefox_profile`: `string` - The path to your Firefox profile. This is used to use your Social Media Accounts without having to log in every time you run the application.
- `firefox_profile`: `string` - The path to your Firefox profile. This is only needed for browser-based Twitter automation.
- `headless`: `boolean` - If `true`, the application will run in headless mode. This means that the browser will not be visible.
- `video_publishing`: `object`:
- `profile_name`: `string` - Friendly label for the configured publisher profile.
- `niche`: `string` - The topic or niche used for generated videos.
- `language`: `string` - The language used for generated videos.
- `ollama_base_url`: `string` - Base URL of your local Ollama server (default: `http://127.0.0.1:11434`).
- `ollama_model`: `string` - Ollama model to use for text generation (e.g. `llama3.2:3b`). If empty, the app queries Ollama at startup and lets you pick from the available models interactively.
- `twitter_language`: `string` - The language that will be used to generate & post tweets.
Expand Down Expand Up @@ -39,11 +43,11 @@ All your configurations will be in a file in the root directory, called `config.
- `imagemagick_path`: `string` - The path to the ImageMagick binary. This is used by MoviePy to manipulate images. Install ImageMagick from [here](https://imagemagick.org/script/download.php) and set the path to the `magick.exe` on Windows, or on Linux/MacOS the path to `convert` (usually /usr/bin/convert).
- `script_sentence_length`: `number` - The number of sentences in the generated video script (default: `4`).
- `post_bridge`: `object`:
- `enabled`: `boolean` - Enables Post Bridge cross-posting after successful YouTube uploads.
- `enabled`: `boolean` - Enables Post Bridge as the primary video publishing backend.
- `api_key`: `string` - Your Post Bridge API key. If empty, MPV2 falls back to `POST_BRIDGE_API_KEY`.
- `platforms`: `string[]` - Platforms to target. Supported values in v1 are `tiktok` and `instagram`.
- `account_ids`: `number[]` - Optional fixed Post Bridge account IDs to avoid account-selection prompts.
- `auto_crosspost`: `boolean` - If `true`, cross-post automatically after a successful YouTube upload. If `false`, interactive runs ask and cron runs skip.
- `platforms`: `string[]` - Platforms to target. Supported values include `youtube`, `tiktok`, `instagram`, `facebook`, `twitter`, `threads`, `linkedin`, `bluesky`, and `pinterest`.
- `account_ids`: `number[]` - Fixed Post Bridge account IDs. The setup wizard stores one account ID per selected platform.
- `auto_publish`: `boolean` - If `true`, generated videos are published automatically. If `false`, interactive runs ask and cron runs skip.

## Example

Expand All @@ -52,6 +56,11 @@ All your configurations will be in a file in the root directory, called `config.
"verbose": true,
"firefox_profile": "",
"headless": false,
"video_publishing": {
"profile_name": "Default Publisher",
"niche": "",
"language": "English"
},
"ollama_base_url": "http://127.0.0.1:11434",
"ollama_model": "",
"twitter_language": "English",
Expand Down Expand Up @@ -85,9 +94,9 @@ All your configurations will be in a file in the root directory, called `config.
"post_bridge": {
"enabled": false,
"api_key": "",
"platforms": ["tiktok", "instagram"],
"platforms": ["youtube", "tiktok", "instagram"],
"account_ids": [],
"auto_crosspost": false
"auto_publish": false
}
}
```
Expand Down
92 changes: 49 additions & 43 deletions docs/PostBridge.md
Original file line number Diff line number Diff line change
@@ -1,33 +1,39 @@
# Post Bridge Integration
# Post Bridge Video Publishing

MoneyPrinterV2 can optionally hand off a successfully uploaded YouTube Short to [Post Bridge](https://api.post-bridge.com/reference), which then publishes the same asset to connected TikTok and Instagram accounts.
MoneyPrinterV2 now uses [Post Bridge](https://api.post-bridge.com/reference) as the primary backend for publishing generated videos.

## What Post Bridge Does

Post Bridge is a publishing API for social platforms. In this integration, MoneyPrinterV2 uses it to:
In the current flow, MoneyPrinterV2 still generates the video locally, but Post Bridge handles publishing:

1. Look up your connected social accounts.
2. Request a signed upload URL for the generated video.
3. Upload the video asset to Post Bridge storage.
4. Create a post for the selected TikTok and Instagram accounts.
1. Fetch the connected social accounts that should receive the video.
2. Request a signed upload URL for the generated media.
3. Upload the local video asset to Post Bridge storage.
4. Create a post for the selected platform accounts.
5. Expose publish history and per-platform URLs through the Post Bridge API.

MoneyPrinterV2 still owns video generation and the initial YouTube upload. Post Bridge only starts after YouTube upload succeeds.
This replaces the old browser-driven YouTube upload path for normal usage.

## Setup

1. Create a Post Bridge account.
2. Connect the TikTok and Instagram accounts you want to publish to.
2. Connect the social accounts you want to publish to.
3. Generate an API key from Post Bridge.
4. Add the `post_bridge` block to `config.json`, or set `POST_BRIDGE_API_KEY` in your environment.
4. Run the in-app publisher setup wizard, or configure `video_publishing` and `post_bridge` manually in `config.json`.

```json
{
"video_publishing": {
"profile_name": "Default Publisher",
"niche": "finance",
"language": "English"
},
"post_bridge": {
"enabled": true,
"api_key": "pb_your_api_key_here",
"platforms": ["tiktok", "instagram"],
"account_ids": [],
"auto_crosspost": false
"platforms": ["youtube", "tiktok", "instagram"],
"account_ids": [101, 202, 303],
"auto_publish": false
}
}
```
Expand All @@ -36,45 +42,45 @@ MoneyPrinterV2 still owns video generation and the initial YouTube upload. Post

| Key | Type | Default | Description |
| --- | --- | --- | --- |
| `enabled` | `boolean` | `false` | Enables the Post Bridge integration. |
| `api_key` | `string` | `""` | Post Bridge API key. Falls back to `POST_BRIDGE_API_KEY` when blank. |
| `platforms` | `string[]` | `["tiktok", "instagram"]` when omitted | Platform filters used when looking up connected accounts. Unsupported values inside the list are ignored. |
| `account_ids` | `number[]` | `[]` | Exact Post Bridge account IDs to post to. When provided, MoneyPrinterV2 uses these directly and skips account lookup. |
| `auto_crosspost` | `boolean` | `false` | Automatically cross-post after a successful YouTube upload. |
| `video_publishing.profile_name` | `string` | `"Default Publisher"` | Friendly label shown in the CLI wizard. |
| `video_publishing.niche` | `string` | `""` | Topic or niche used for generated videos. |
| `video_publishing.language` | `string` | `"English"` | Language used for generated videos. |
| `post_bridge.enabled` | `boolean` | `false` | Enables Post Bridge publishing. |
| `post_bridge.api_key` | `string` | `""` | Post Bridge API key. Falls back to `POST_BRIDGE_API_KEY` when blank. |
| `post_bridge.platforms` | `string[]` | `["youtube", "tiktok", "instagram"]` when omitted | Platforms targeted for publishing. |
| `post_bridge.account_ids` | `number[]` | `[]` | Exact Post Bridge account IDs to publish to. The setup wizard stores one account per selected platform. |
| `post_bridge.auto_publish` | `boolean` | `false` | Automatically publish after generation. Interactive runs prompt when disabled; cron runs skip. |

## How The Integration Works
## Publish Flow

### Interactive YouTube flow
### Interactive publishing

- If `enabled` is `false`, nothing happens.
- If `enabled` is `true` and `auto_crosspost` is `false`, MoneyPrinterV2 asks whether to cross-post after a successful YouTube upload.
- If `account_ids` is configured, those IDs are used directly.
- If `account_ids` is empty, MoneyPrinterV2 fetches connected Post Bridge accounts for the configured platforms.
- If there is exactly one connected account for a platform, it is selected automatically.
- If there are multiple connected accounts for a platform, MoneyPrinterV2 prompts you to choose one.
- After interactive selection, the chosen IDs are printed so you can copy them into `config.json`, but the app does not edit your config file for you.
- Use the `Video Publishing` menu in `src/main.py`.
- `Setup Publisher` runs the config-writing wizard.
- `Publish Video` generates a video locally and publishes it through Post Bridge.
- `Show Recent Publishes` fetches recent posts and post results live from the API.

### Cron / scheduled uploads
### Scheduled publishing

- Cron uses the same integration after a successful YouTube upload.
- If `auto_crosspost` is `false`, cron skips Post Bridge and logs why.
- If `auto_crosspost` is `true`, cron cross-posts automatically.
- If `account_ids` is empty and multiple connected accounts exist for a platform, cron skips cross-posting instead of hanging on an interactive prompt.
- Cron now uses `publish` mode instead of `youtube`.
- `scripts/publish_video.sh` runs the publish cron entrypoint directly.
- If `post_bridge.auto_publish` is `false`, cron skips publishing and logs why.

## Current v1 Behavior
## Content Mapping

- The generated YouTube title is used as the default caption.
- TikTok receives the YouTube title as its platform-specific `title` override.
- Post Bridge account lookup follows the API’s pagination.
- Instagram cover-image customization is intentionally not included in this v1 integration.
- Cross-posting only runs after `upload_video()` returns success.
- Global caption: generated description
- `youtube.title`: generated title
- `youtube.caption`: generated description
- `tiktok.title`: generated title

Only configured platform overrides are sent.

## Troubleshooting

| Issue | What to check |
| --- | --- |
| Cross-post prompt never appears | Verify `post_bridge.enabled` is `true`. |
| Cross-post is skipped in cron | Set `auto_crosspost` to `true`. |
| No accounts are found | Make sure the accounts are connected in Post Bridge and that `platforms` matches the accounts you connected. |
| Cron skips because multiple accounts exist | Add the desired `account_ids` to `config.json` so cron does not need to prompt. |
| API key seems ignored | Set `post_bridge.api_key`, or leave it blank and export `POST_BRIDGE_API_KEY`. |
| The publisher wizard cannot continue | Verify `video_publishing.niche` and a Post Bridge API key are set. |
| No accounts are found | Make sure the accounts are connected in Post Bridge and that `post_bridge.platforms` matches the connected accounts. |
| Cron skips publishing | Set `post_bridge.auto_publish` to `true`. |
| Publish history is empty | Confirm that posts exist for the configured platforms and that the API key has access. |
| Old cron commands stopped working | Use `publish` mode instead of `youtube`. |
10 changes: 6 additions & 4 deletions docs/YouTube.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
# YouTube Shorts Automater
# Legacy YouTube Automation

MPV2 uses a similar implementation of V1 (see [MPV1](https://github.com/FujiwaraChoki/MoneyPrinter)), to generate Video-Files and upload them to YouTube Shorts.
This document describes the older browser-driven YouTube workflow.

In contrast to V1, V2 uses AI generated images as the visuals for the video, instead of using stock footage. This makes the videos more unique and less likely to be flagged by YouTube. V2 also supports music right from the get-go.
MoneyPrinterV2 now uses Post Bridge as the primary publishing backend for generated videos. See [PostBridge.md](./PostBridge.md) for the current flow.

The legacy implementation used a Firefox profile plus Selenium to upload videos directly to YouTube Shorts after generation.

## Relevant Configuration

In your `config.json`, you need the following attributes filled out, so that the bot can function correctly.
If you are still experimenting with the legacy code path, you need the following attributes filled out:

```json
{
Expand Down
35 changes: 34 additions & 1 deletion scripts/preflight_local.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,40 @@ def main() -> int:
else:
warn(f"firefox_profile does not exist: {firefox_profile}")
else:
warn("firefox_profile is empty. Twitter/YouTube automation requires this.")
warn("firefox_profile is empty. Twitter automation requires this.")

video_cfg = cfg.get("video_publishing", {})
if isinstance(video_cfg, dict):
niche = str(video_cfg.get("niche", "")).strip()
language = str(video_cfg.get("language", "")).strip()
if niche:
ok(f"video_publishing.niche is set: {niche}")
else:
warn("video_publishing.niche is empty. Video publishing setup is incomplete.")
if language:
ok(f"video_publishing.language is set: {language}")
else:
warn("video_publishing.language is empty. Defaulting to English at runtime.")
else:
warn("video_publishing config block is missing or invalid.")

post_bridge = cfg.get("post_bridge", {})
if isinstance(post_bridge, dict) and post_bridge.get("enabled"):
api_key = str(post_bridge.get("api_key", "")).strip() or os.environ.get(
"POST_BRIDGE_API_KEY",
"",
).strip()
if api_key:
ok("Post Bridge API key is set")
else:
fail("Post Bridge is enabled but no API key is configured")
failures += 1

platforms = post_bridge.get("platforms", [])
if platforms:
ok(f"Post Bridge platforms configured: {', '.join(platforms)}")
else:
warn("Post Bridge is enabled but no platforms are configured.")

# Ollama (LLM)
base = str(cfg.get("ollama_base_url", "http://127.0.0.1:11434")).rstrip("/")
Expand Down
11 changes: 11 additions & 0 deletions scripts/publish_video.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#!/bin/bash

# Script to generate and publish a video via Post Bridge.

if [ -x "$(command -v python3)" ]; then
PYTHON=python3
else
PYTHON=python
fi

"$PYTHON" src/cron.py publish
14 changes: 14 additions & 0 deletions scripts/setup_local.sh
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,19 @@ cfg.setdefault("whisper_model", "base")
cfg.setdefault("whisper_device", "auto")
cfg.setdefault("whisper_compute_type", "int8")

video_publishing = cfg.setdefault("video_publishing", {})
video_publishing.setdefault("profile_name", "Default Publisher")
video_publishing.setdefault("niche", "")
video_publishing.setdefault("language", "English")

post_bridge = cfg.setdefault("post_bridge", {})
post_bridge.setdefault("enabled", False)
post_bridge.setdefault("api_key", "")
post_bridge.setdefault("platforms", ["youtube", "tiktok", "instagram"])
post_bridge.setdefault("account_ids", [])
if "auto_publish" not in post_bridge:
post_bridge["auto_publish"] = bool(post_bridge.pop("auto_crosspost", False))

magick_path = os.environ.get("MAGICK_PATH", "")
if magick_path:
cfg["imagemagick_path"] = magick_path
Expand Down Expand Up @@ -109,6 +122,7 @@ print(f"[setup] Updated {cfg_path}")
print(f"[setup] llm_provider={cfg.get('llm_provider')} model={cfg.get('ollama_model')}")
print(f"[setup] image_provider={cfg.get('image_provider')}")
print(f"[setup] stt_provider={cfg.get('stt_provider')}")
print(f"[setup] video_publishing platforms={post_bridge.get('platforms')}")
PY

echo "[setup] Running local preflight..."
Expand Down
35 changes: 3 additions & 32 deletions scripts/upload_video.sh
100644 → 100755
Original file line number Diff line number Diff line change
@@ -1,34 +1,5 @@
#!/bin/bash

# Script to generate & Upload a video to YT Shorts

# Check which interpreter to use (python)
if [ -x "$(command -v python3)" ]; then
PYTHON=python3
else
PYTHON=python
fi

# Read .mp/youtube.json file, loop through accounts array, get each id and print every id
youtube_ids=$($PYTHON -c "import json; print('\n'.join([account['id'] for account in json.load(open('.mp/youtube.json'))['accounts']]))")

echo "What account do you want to upload the video to?"

# Print the ids
for id in $youtube_ids; do
echo $id
done

# Ask for the id
read -p "Enter the id: " id

# Check if the id is in the list
if [[ " ${youtube_ids[@]} " =~ " ${id} " ]]; then
echo "ID found"
else
echo "ID not found"
exit 1
fi

# Run python script
$PYTHON src/cron.py youtube $id
echo "The legacy YouTube upload helper has been removed."
echo "Use 'bash scripts/publish_video.sh' instead."
exit 1
Loading
Loading