Metadata-Version: 2.4
Name: publishsafety
Version: 0.3.0
Summary: Pre-flight safety gate for auto-published short videos (TikTok/Shorts/Reels). Catches policy, copyright, and spam risks before upload.
License: MIT
Keywords: tiktok,youtube-shorts,instagram-reels,moneyprinterturbo,content-policy,pre-flight
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: Console
Classifier: Intended Audience :: End Users/Desktop
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Multimedia :: Video
Requires-Python: >=3.11
Description-Content-Type: text/markdown
License-File: LICENSE
Dynamic: license-file

# publishsafety

[![ci](https://github.com/marouanedaouyny-prog/publish-safety-gate/actions/workflows/ci.yml/badge.svg)](https://github.com/marouanedaouyny-prog/publish-safety-gate/actions/workflows/ci.yml)
![python](https://img.shields.io/badge/python-3.11%2B-blue)
![license](https://img.shields.io/badge/license-MIT-green)
![tests](https://img.shields.io/badge/tests-74-brightgreen)

Pre-flight safety gate for auto-published short videos. Sits between the
generate step (e.g. MoneyPrinterTurbo) and the publish step (Upload-Post,
platform APIs): check the task/video before it ships so batch channels don't
get muted, demonetized, or banned.

Zero-cost core, stdlib-only (Python 3.11+). Deterministic rules, JSON
envelope, exit-code gates.

## Install

```bash
pip install publishsafety                     # PyPI (when published)
pip install git+https://github.com/marouanedaouyny-prog/publish-safety-gate.git   # from source
```

## Why

MoneyPrinterTurbo makes batch faceless-shorts pipelines trivial: topic ->
script -> free stock -> free TTS -> compose -> **auto-publish** to TikTok /
Instagram / YouTube Shorts. Nothing checks what gets published. The failure
modes are real and expensive:

- MPT's default `bgm_type = "random"` picks from bundled songs that the
  author himself flags as possibly infringing (`resource/songs`)
- financial-guarantee scripts ("get rich quick") trip platform policy filters
- CapCut-template watermarks get cross-posted videos downranked
- 12 near-duplicate uploads/day is textbook spam suppression
- landscape video letterboxed into a vertical feed loses reach

## Quick start

```powershell
cd projects/publish-safety-gate

# check hand-written manifests
python -m publishsafety check video.json --fail-on-severity high

# check MoneyPrinterTurbo task configs directly (single task or whole directory)
python -m publishsafety check-mpt storage\tasks\task.json --platform tiktok
python -m publishsafety check-mpt storage\tasks\ --meta social_metadata.json

# channel-level markdown report over all tasks
python -m publishsafety report storage\tasks\ --out channel_report.md

# user blocklists
python -m publishsafety check video.json --blocklist my_terms.txt

python -m unittest discover -s tests   # 58 tests
```

Install as a CLI tool: `pip install .` -> `publishsafety check-mpt ...`

## MoneyPrinterTurbo integration

`check-mpt` reads real MPT task configs (`{"task_id":..., "params":{...}}` or
bare params) and maps them onto checks:

| MPT field | Mapping |
|-----------|---------|
| `bgm_type: "random"` | **COP-001 high** - bundled unverified music |
| `bgm_type: "custom"` + `bgm_file` | COP-001 unless marked licensed:/royalty-free |
| `voice_name` containing `Neural` | Edge TTS -> COP-004 low |
| `custom_audio_file` | treated as owned audio |
| `video_source: pexels/pixabay` | clean; `local`/unknown URLs -> COP-002 |
| `video_aspect` | resolution fallback when no MP4 present |
| `final-*.mp4` next to the config | duration/resolution read from the MP4 itself (stdlib atom parsing) |

With `--meta social_metadata.json` it also scans the generated title/caption/
hashtags (from MPT's social-metadata endpoint) per target platform.

## Manifest schema (v1.1)

All fields optional; type-checked when present. Rules skip absent fields.

```json
{
  "file": "output/final.mp4",
  "title": "...", "description": "...", "script": "...",
  "tags": ["#money"],
  "duration_s": 42,
  "resolution": [1080, 1920],
  "watermark": "none | capcut | ...",
  "sources": {"stock": ["pexels"], "bgm": null, "tts": "elevenlabs"},
  "batch": {"size": 40, "same_template_count": 12, "posted_last_24h": 18}
}
```

## Rules

| ID | Rule | Severity | Fires when |
|----|------|----------|------------|
| POL-* | policy_word | high/medium | title/description/script/tags match a platform policy category (financial guarantees, health claims, weight-loss schemes, adult, profanity, shock) or a `--blocklist` term |
| COP-001 | unverified_music | high | BGM not marked none/original/licensed:/owned:/royalty-free: |
| COP-002 | unknown_stock_source | medium | stock source not pexels/pixabay/coverr and not owned:/licensed: |
| COP-003 | template_watermark | medium | watermark from capcut/invideo/canva/tiktok/clipchamp |
| COP-004 | unofficial_tts | low | Edge TTS (unofficial client) |
| TEC-001/002 | duration bounds | high/low | outside platform window (Shorts <=180s, TikTok <=600s, Reels <=180s) |
| TEC-003 | aspect_ratio_mismatch | medium/low | not 9:16 for the feed |
| TEC-004 | caption_too_long | medium | description+tags over platform caption cap |
| SPM-001 | template_flood | medium | >=5 recent same-template videos |
| SPM-002 | posting_cadence | low | >10 uploads in 24h |

Platforms: `tiktok`, `youtube_shorts`, `instagram_reels`
(`publishsafety/platforms.py`). Policy categories are per-platform.

Exit codes: `0` pass, `1` finding at/above threshold, `2` usage/input error.
`--json` prints a `publishsafety.check.v1` envelope.

## Channel report

`report` aggregates every task in a directory into a markdown file: channel
risk score (`100 - 12*high - 4*medium - 1*low`, floor 0), verdict band
(green-light / caution / stop), per-task table, and an actionable fix
checklist derived from the findings.

## Free vs Pro

The safety core is free forever: `check`, `check-mpt`, all rules, JSON
envelope, MP4 verification, single-task workflows.

**Pro** ($9/mo or $79/yr) unlocks the operator workflow:

- custom blocklists (`--blocklist`) - your niche's banned words, brand-safety terms
- channel reports (`report`) - batch scorecards + fix checklists
- priority rule updates when platform policies change

```bash
# after purchase you receive a license key:
export PUBLISHSAFETY_LICENSE="PSP.eyJ..."
publishsafety license          # -> Pro - you@company.com - expires 2027-08-25
python -m publishsafety report storage/tasks/ --out channel_report.md
```

Licenses are validated offline (RSA-2048 signed tokens) - no phone-home,
works in air-gapped CI. Honest note: offline licensing in open source is
deterrent-grade; we price for honesty.

## Agent skill

A `SKILL.md` ships in this repo so Claude / opencode agents can drive the
gate as a hard pre-publish step: exit code 1 means *never call the upload
API*.

## Honest limits

- **Pattern-based, not policy-complete.** Wordlists catch common violations;
  they do not understand context, sarcasm, or novel phrasing. A clean verdict
  is not a guarantee.
- **No built-in slur list.** Hate-speech terms are intentionally not
  enumerated here; supply your own via `--blocklist`.
- **Manifest-driven trust.** The gate trusts what the manifest/task config
  declares except for `final-*.mp4` metadata, which it reads directly. It does
  not transcribe audio, OCR frames, or detect burned-in watermarks.
- **Platform limits drift.** Duration/caption caps encoded early 2026;
  verify before commercial reliance.
- **English-centric patterns.**

## Roadmap

1. ~~Upload-Post pre-publish hook example~~ - shipped: `examples/upload_post_hook.py`
2. Whisper transcription hook -> scan spoken audio, not just declared script
3. GitHub Action / pre-publish webhook wrapper
4. Automated license fulfillment (Lemon Squeezy webhook -> key email)
