factreel 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- factreel-0.1.0/.github/ISSUE_TEMPLATE/bug_report.yml +27 -0
- factreel-0.1.0/.github/ISSUE_TEMPLATE/config.yml +1 -0
- factreel-0.1.0/.github/ISSUE_TEMPLATE/feature_request.yml +16 -0
- factreel-0.1.0/.github/pull_request_template.md +6 -0
- factreel-0.1.0/.github/workflows/ci.yml +40 -0
- factreel-0.1.0/.github/workflows/publish.yml +19 -0
- factreel-0.1.0/.gitignore +13 -0
- factreel-0.1.0/CHANGELOG.md +18 -0
- factreel-0.1.0/CODE_OF_CONDUCT.md +7 -0
- factreel-0.1.0/CONTRIBUTING.md +39 -0
- factreel-0.1.0/LICENSE +21 -0
- factreel-0.1.0/PKG-INFO +178 -0
- factreel-0.1.0/README.es.md +120 -0
- factreel-0.1.0/README.md +142 -0
- factreel-0.1.0/ROADMAP.md +25 -0
- factreel-0.1.0/SECURITY.md +8 -0
- factreel-0.1.0/docs/configuration.md +156 -0
- factreel-0.1.0/docs/demo.gif +0 -0
- factreel-0.1.0/docs/providers.md +46 -0
- factreel-0.1.0/docs/scheduling.md +34 -0
- factreel-0.1.0/docs/youtube.md +21 -0
- factreel-0.1.0/pyproject.toml +67 -0
- factreel-0.1.0/src/factreel/__init__.py +2 -0
- factreel-0.1.0/src/factreel/__main__.py +5 -0
- factreel-0.1.0/src/factreel/cli.py +185 -0
- factreel-0.1.0/src/factreel/cloudflare.py +70 -0
- factreel-0.1.0/src/factreel/config.py +209 -0
- factreel-0.1.0/src/factreel/doctor.py +123 -0
- factreel-0.1.0/src/factreel/facts.py +128 -0
- factreel-0.1.0/src/factreel/images/__init__.py +86 -0
- factreel-0.1.0/src/factreel/images/common.py +59 -0
- factreel-0.1.0/src/factreel/images/providers.py +194 -0
- factreel-0.1.0/src/factreel/lang.py +116 -0
- factreel-0.1.0/src/factreel/llm.py +81 -0
- factreel-0.1.0/src/factreel/log.py +43 -0
- factreel-0.1.0/src/factreel/metadata.py +92 -0
- factreel-0.1.0/src/factreel/pipeline.py +95 -0
- factreel-0.1.0/src/factreel/render.py +169 -0
- factreel-0.1.0/src/factreel/script.py +204 -0
- factreel-0.1.0/src/factreel/subtitles.py +108 -0
- factreel-0.1.0/src/factreel/templates/en/channel.toml +69 -0
- factreel-0.1.0/src/factreel/templates/en/env.example +6 -0
- factreel-0.1.0/src/factreel/templates/en/facts.json +61 -0
- factreel-0.1.0/src/factreel/templates/en/gitignore +5 -0
- factreel-0.1.0/src/factreel/templates/en/music/credits.json +3 -0
- factreel-0.1.0/src/factreel/templates/en/queue.txt +2 -0
- factreel-0.1.0/src/factreel/templates/es/channel.toml +72 -0
- factreel-0.1.0/src/factreel/templates/es/env.example +6 -0
- factreel-0.1.0/src/factreel/templates/es/facts.json +61 -0
- factreel-0.1.0/src/factreel/templates/es/gitignore +5 -0
- factreel-0.1.0/src/factreel/templates/es/music/credits.json +3 -0
- factreel-0.1.0/src/factreel/templates/es/queue.txt +2 -0
- factreel-0.1.0/src/factreel/tts/__init__.py +67 -0
- factreel-0.1.0/src/factreel/tts/providers.py +195 -0
- factreel-0.1.0/src/factreel/tts/timing.py +91 -0
- factreel-0.1.0/src/factreel/youtube.py +120 -0
- factreel-0.1.0/tests/__init__.py +0 -0
- factreel-0.1.0/tests/conftest.py +33 -0
- factreel-0.1.0/tests/test_facts.py +47 -0
- factreel-0.1.0/tests/test_media.py +86 -0
- factreel-0.1.0/tests/test_pipeline.py +39 -0
- factreel-0.1.0/tests/test_script.py +120 -0
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
name: Bug report / Error
|
|
2
|
+
description: Something doesn't work / Algo no funciona
|
|
3
|
+
labels: [bug]
|
|
4
|
+
body:
|
|
5
|
+
- type: textarea
|
|
6
|
+
id: what
|
|
7
|
+
attributes:
|
|
8
|
+
label: What happened? / ¿Qué pasó?
|
|
9
|
+
description: Command you ran and what you expected.
|
|
10
|
+
validations:
|
|
11
|
+
required: true
|
|
12
|
+
- type: textarea
|
|
13
|
+
id: doctor
|
|
14
|
+
attributes:
|
|
15
|
+
label: Output of `factreel doctor`
|
|
16
|
+
render: text
|
|
17
|
+
- type: textarea
|
|
18
|
+
id: log
|
|
19
|
+
attributes:
|
|
20
|
+
label: Log (logs/factreel_YYYY-MM.log), without API keys
|
|
21
|
+
render: text
|
|
22
|
+
- type: input
|
|
23
|
+
id: version
|
|
24
|
+
attributes:
|
|
25
|
+
label: factreel --version, OS and Python version
|
|
26
|
+
validations:
|
|
27
|
+
required: true
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
blank_issues_enabled: true
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
name: Feature or provider request / Propuesta
|
|
2
|
+
description: Idea, new language, new voice/image provider or uploader
|
|
3
|
+
labels: [enhancement]
|
|
4
|
+
body:
|
|
5
|
+
- type: textarea
|
|
6
|
+
id: idea
|
|
7
|
+
attributes:
|
|
8
|
+
label: What would you like? / ¿Qué te gustaría?
|
|
9
|
+
validations:
|
|
10
|
+
required: true
|
|
11
|
+
- type: checkboxes
|
|
12
|
+
id: help
|
|
13
|
+
attributes:
|
|
14
|
+
label: Contribution
|
|
15
|
+
options:
|
|
16
|
+
- label: I'd like to implement it myself / Me gustaría implementarlo yo
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
test:
|
|
10
|
+
strategy:
|
|
11
|
+
fail-fast: false
|
|
12
|
+
matrix:
|
|
13
|
+
os: [ubuntu-latest, windows-latest, macos-latest]
|
|
14
|
+
python: ["3.10", "3.13"]
|
|
15
|
+
runs-on: ${{ matrix.os }}
|
|
16
|
+
steps:
|
|
17
|
+
- uses: actions/checkout@v4
|
|
18
|
+
- uses: actions/setup-python@v5
|
|
19
|
+
with:
|
|
20
|
+
python-version: ${{ matrix.python }}
|
|
21
|
+
- name: Install FFmpeg (Linux)
|
|
22
|
+
if: runner.os == 'Linux'
|
|
23
|
+
run: sudo apt-get update && sudo apt-get install -y ffmpeg
|
|
24
|
+
- name: Install FFmpeg (macOS)
|
|
25
|
+
if: runner.os == 'macOS'
|
|
26
|
+
run: brew install ffmpeg
|
|
27
|
+
- name: Install FFmpeg (Windows)
|
|
28
|
+
if: runner.os == 'Windows'
|
|
29
|
+
uses: FedericoCarboni/setup-ffmpeg@v3
|
|
30
|
+
- run: pip install -e ".[dev]"
|
|
31
|
+
- run: ruff check .
|
|
32
|
+
- name: Tests
|
|
33
|
+
shell: bash
|
|
34
|
+
run: |
|
|
35
|
+
set -o pipefail
|
|
36
|
+
pytest -q -rf --tb=line 2>&1 | tee pytest.log || { grep -E "^(FAILED|E |/|.*Error)" pytest.log | head -15 | sed 's/^/::error::/'; ffmpeg -hide_banner -filters 2>/dev/null | grep -E " (ass|xfade|loudnorm) " | sed 's/^/::warning::filter /'; exit 1; }
|
|
37
|
+
- name: End-to-end preview video
|
|
38
|
+
run: |
|
|
39
|
+
factreel init demo --lang es
|
|
40
|
+
factreel make -c demo --offline --voice dummy --no-history
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
name: Publish to PyPI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
release:
|
|
5
|
+
types: [published]
|
|
6
|
+
|
|
7
|
+
jobs:
|
|
8
|
+
publish:
|
|
9
|
+
runs-on: ubuntu-latest
|
|
10
|
+
environment: pypi
|
|
11
|
+
permissions:
|
|
12
|
+
id-token: write # trusted publishing, no API token needed
|
|
13
|
+
steps:
|
|
14
|
+
- uses: actions/checkout@v4
|
|
15
|
+
- uses: actions/setup-python@v5
|
|
16
|
+
with:
|
|
17
|
+
python-version: "3.12"
|
|
18
|
+
- run: pip install build && python -m build
|
|
19
|
+
- uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project are documented here. The format follows
|
|
4
|
+
[Keep a Changelog](https://keepachangelog.com/) and the project uses [Semantic Versioning](https://semver.org/).
|
|
5
|
+
|
|
6
|
+
## [0.1.0] - 2026-10-20
|
|
7
|
+
|
|
8
|
+
### Added
|
|
9
|
+
- First public release, extracted from a channel that publishes a Short every day.
|
|
10
|
+
- Fact-grounded script writer for any OpenAI-compatible LLM, with number, repetition and rumour guards,
|
|
11
|
+
a fact-check pass and a facts-only fallback.
|
|
12
|
+
- Voice chain: Edge, ElevenLabs, Cloudflare Aura, system voice, dummy; health tracking.
|
|
13
|
+
- Image chain: Cloudflare FLUX, NVIDIA FLUX, custom command, archive, procedural gradient.
|
|
14
|
+
- Word-by-word ASS subtitles, jitter-free zoom, cross-fades, loudness normalisation, music with credits.
|
|
15
|
+
- Spanish and English templates; `init`, `make`, `topics`, `doctor`, `upload`, `run` commands.
|
|
16
|
+
- Optional YouTube upload with synthetic-media declaration.
|
|
17
|
+
- Works with FFmpeg builds without libass (e.g. Homebrew's `ffmpeg`): the video is rendered without burned
|
|
18
|
+
subtitles and a warning explains how to get a full build.
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
# Code of Conduct
|
|
2
|
+
|
|
3
|
+
This project follows the [Contributor Covenant, version 2.1](https://www.contributor-covenant.org/version/2/1/code_of_conduct/).
|
|
4
|
+
|
|
5
|
+
In short: be kind, assume good intent, welcome newcomers and people of every background and language,
|
|
6
|
+
and keep discussions about the work. Report unacceptable behaviour privately to the maintainer through
|
|
7
|
+
[GitHub](https://github.com/DeadY2J) (or a private security advisory); reports are handled confidentially.
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# Contributing to FactReel
|
|
2
|
+
|
|
3
|
+
Thanks for helping! Issues and pull requests are welcome **in English or Spanish** (también en español).
|
|
4
|
+
|
|
5
|
+
## Setup
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
git clone https://github.com/DeadY2J/factreel
|
|
9
|
+
cd factreel
|
|
10
|
+
python -m venv .venv && source .venv/bin/activate # Windows: .venv\Scripts\activate
|
|
11
|
+
pip install -e ".[dev,youtube]"
|
|
12
|
+
pytest # needs ffmpeg for the end-to-end test (it is skipped otherwise)
|
|
13
|
+
ruff check .
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
Try your change on a real channel without spending API credits:
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
factreel init /tmp/test-channel --lang es
|
|
20
|
+
factreel make -c /tmp/test-channel --offline --voice dummy --no-history
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Good first contributions
|
|
24
|
+
|
|
25
|
+
- **A new language**: add an entry to `LANGS` in `src/factreel/lang.py` (prompts, month names, call-to-action
|
|
26
|
+
regex, default Edge voice), a template in `src/factreel/templates/<lang>/` and a test.
|
|
27
|
+
- **A new voice or image provider**: see [docs/providers.md](docs/providers.md).
|
|
28
|
+
- **A new uploader** (TikTok, Instagram Reels…): mirror `src/factreel/youtube.py`.
|
|
29
|
+
- Anything in the [roadmap](ROADMAP.md) or labelled `good first issue`.
|
|
30
|
+
|
|
31
|
+
## Guidelines
|
|
32
|
+
|
|
33
|
+
- Keep the guarantees: never let the script state something that isn't in the facts, never block a video
|
|
34
|
+
because one optional service is down, never put secrets in config files.
|
|
35
|
+
- New behaviour needs a test. Network calls must be mockable; tests must pass offline.
|
|
36
|
+
- Keep dependencies light: optional features go in an extra (`[project.optional-dependencies]`).
|
|
37
|
+
- Format with `ruff`. One topic per pull request, with a short description of the why.
|
|
38
|
+
|
|
39
|
+
By contributing you agree that your contributions are licensed under the MIT license.
|
factreel-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Jorge Lamelas
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
factreel-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: factreel
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Fact-checked faceless short videos (YouTube Shorts, TikTok, Reels) from a list of verified facts: AI script with anti-hallucination guards, voice fallback chain, karaoke subtitles and FFmpeg render.
|
|
5
|
+
Project-URL: Homepage, https://github.com/DeadY2J/factreel
|
|
6
|
+
Project-URL: Issues, https://github.com/DeadY2J/factreel/issues
|
|
7
|
+
Project-URL: Changelog, https://github.com/DeadY2J/factreel/blob/main/CHANGELOG.md
|
|
8
|
+
Author-email: Jorge Lamelas <205297977+DeadY2J@users.noreply.github.com>
|
|
9
|
+
License-Expression: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Keywords: edge-tts,faceless,fact-checking,ffmpeg,llm,reels,spanish,subtitles,text-to-speech,tiktok,video-automation,youtube-shorts
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Environment :: Console
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: Intended Audience :: End Users/Desktop
|
|
16
|
+
Classifier: Natural Language :: English
|
|
17
|
+
Classifier: Natural Language :: Spanish
|
|
18
|
+
Classifier: Operating System :: OS Independent
|
|
19
|
+
Classifier: Programming Language :: Python :: 3
|
|
20
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
21
|
+
Classifier: Topic :: Multimedia :: Sound/Audio :: Speech
|
|
22
|
+
Classifier: Topic :: Multimedia :: Video
|
|
23
|
+
Requires-Python: >=3.10
|
|
24
|
+
Requires-Dist: edge-tts>=7.3.1
|
|
25
|
+
Requires-Dist: pillow>=10.0
|
|
26
|
+
Requires-Dist: requests>=2.31
|
|
27
|
+
Requires-Dist: tomli>=2.0; python_version < '3.11'
|
|
28
|
+
Provides-Extra: dev
|
|
29
|
+
Requires-Dist: pytest>=8; extra == 'dev'
|
|
30
|
+
Requires-Dist: ruff>=0.6; extra == 'dev'
|
|
31
|
+
Provides-Extra: youtube
|
|
32
|
+
Requires-Dist: google-api-python-client>=2.100; extra == 'youtube'
|
|
33
|
+
Requires-Dist: google-auth-oauthlib>=1.1; extra == 'youtube'
|
|
34
|
+
Requires-Dist: google-auth>=2.23; extra == 'youtube'
|
|
35
|
+
Description-Content-Type: text/markdown
|
|
36
|
+
|
|
37
|
+
<div align="center">
|
|
38
|
+
|
|
39
|
+
# FactReel 🎬✅
|
|
40
|
+
|
|
41
|
+
**Fact-checked faceless short videos, on autopilot.**
|
|
42
|
+
YouTube Shorts · TikTok · Reels, from a list of verified facts, with free AI services and a fallback for everything.
|
|
43
|
+
|
|
44
|
+
[](https://github.com/DeadY2J/factreel/actions/workflows/ci.yml)
|
|
45
|
+
[](https://pypi.org/project/factreel/)
|
|
46
|
+
[](https://pypi.org/project/factreel/)
|
|
47
|
+
[](LICENSE)
|
|
48
|
+
|
|
49
|
+
[English](README.md) · [Español](README.es.md)
|
|
50
|
+
|
|
51
|
+
<img src="docs/demo.gif" width="260" alt="FactReel demo: vertical video with word-by-word subtitles">
|
|
52
|
+
|
|
53
|
+
</div>
|
|
54
|
+
|
|
55
|
+
---
|
|
56
|
+
|
|
57
|
+
Most "AI shorts generators" let a language model make things up. **FactReel doesn't.** You write the facts
|
|
58
|
+
(with sources) once; the AI only turns them into a punchy script, and every script goes through guards that
|
|
59
|
+
reject invented numbers, repeated ideas, unlabelled rumours and anything a second fact-checking pass can't
|
|
60
|
+
find in your facts. Then it narrates it, draws the images, burns karaoke subtitles and renders a vertical
|
|
61
|
+
MP4 ready to upload.
|
|
62
|
+
|
|
63
|
+
It grew out of a real channel that publishes a Short every day unattended, so it is built to **never stop
|
|
64
|
+
on a bad day**: if the voice service, the image API or the script AI is down, the next option in the chain
|
|
65
|
+
takes over.
|
|
66
|
+
|
|
67
|
+
## Features
|
|
68
|
+
|
|
69
|
+
- 🛡️ **Anti-hallucination script writer**: number whitelist, repetition check, rumour labelling, a
|
|
70
|
+
temperature-0 fact-check pass, and a facts-only fallback that is always correct.
|
|
71
|
+
- 🗣️ **Voice fallback chain**: Microsoft Edge neural voices (free, no key) → ElevenLabs → Cloudflare
|
|
72
|
+
Aura → your OS's offline voice. `factreel doctor` tells you when a service is down and when it is back.
|
|
73
|
+
- 🖼️ **Image fallback chain**: Cloudflare Workers AI FLUX → NVIDIA NIM FLUX → your own generator
|
|
74
|
+
(local GPU, ComfyUI…) → archive of previous images → procedural background that always works offline.
|
|
75
|
+
- 💬 **Word-by-word subtitles** with the spoken word highlighted, synced from the TTS engine or Whisper.
|
|
76
|
+
- 🎞️ **Smooth Ken Burns zoom without jitter** (sub-pixel rendering), cross-fades on scene changes,
|
|
77
|
+
voice normalised to -14 LUFS, optional background music with automatic credits.
|
|
78
|
+
- 🌍 **Spanish and English first-class** (prompts, month names, countdowns). More languages = one dict.
|
|
79
|
+
- 💸 **Free by default**: runs with zero API keys; every paid service is optional and budget-capped.
|
|
80
|
+
- 🔌 **Any OpenAI-compatible LLM**: NVIDIA NIM, OpenRouter, Groq, OpenAI, Ollama, LM Studio, OmniRoute…
|
|
81
|
+
- 📤 **Optional YouTube upload** with the "altered or synthetic content" flag set for you.
|
|
82
|
+
- ✅ **Verifies every render** (resolution, audio, no black screen) before it reaches the upload queue.
|
|
83
|
+
|
|
84
|
+
## Quick start
|
|
85
|
+
|
|
86
|
+
You need **Python 3.10+** and **[FFmpeg](https://ffmpeg.org/download.html)** on your PATH.
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
pip install factreel
|
|
90
|
+
factreel init my-channel --lang en # or --lang es
|
|
91
|
+
cd my-channel
|
|
92
|
+
factreel doctor # checks FFmpeg, keys and every voice
|
|
93
|
+
factreel make # -> output/20261020_180000_venus_day.mp4 (+ .json/.txt metadata)
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
That's it: with no keys at all you get a video with the free Edge voice, the facts read as they are and
|
|
97
|
+
offline backgrounds. Add free keys to `.env` to unlock the AI script and AI images:
|
|
98
|
+
|
|
99
|
+
| Key | What it unlocks | Cost |
|
|
100
|
+
| --- | --- | --- |
|
|
101
|
+
| `NVIDIA_API_KEY` ([build.nvidia.com](https://build.nvidia.com)) | AI script + fact check, FLUX images | free credits |
|
|
102
|
+
| `CLOUDFLARE_ACCOUNT_ID` + `CLOUDFLARE_API_TOKEN` | FLUX images, backup voice, exact subtitle timing (Whisper) | 10,000 free neurons/day, capped |
|
|
103
|
+
| `ELEVENLABS_API_KEY` | premium voice | optional |
|
|
104
|
+
|
|
105
|
+
## How it works
|
|
106
|
+
|
|
107
|
+
```mermaid
|
|
108
|
+
flowchart LR
|
|
109
|
+
F[facts.json<br/>verified facts + sources] --> T[topic rotation<br/>queue.txt / least used]
|
|
110
|
+
T --> S[AI script]
|
|
111
|
+
S --> G{guards<br/>numbers · repeats<br/>rumours · fact check}
|
|
112
|
+
G -- rejected --> S
|
|
113
|
+
G -- gives up --> FB[facts-only script]
|
|
114
|
+
G -- ok --> V[voice chain]
|
|
115
|
+
FB --> V
|
|
116
|
+
V --> SUB[karaoke subtitles]
|
|
117
|
+
V --> I[image chain]
|
|
118
|
+
SUB --> R[FFmpeg render + verify]
|
|
119
|
+
I --> R
|
|
120
|
+
R --> O[output/*.mp4 + metadata] --> U[YouTube upload]
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
## Your facts
|
|
124
|
+
|
|
125
|
+
```json
|
|
126
|
+
{
|
|
127
|
+
"topics": [
|
|
128
|
+
{
|
|
129
|
+
"id": "venus_day",
|
|
130
|
+
"angle": "a day on Venus is longer than its year",
|
|
131
|
+
"facts": [
|
|
132
|
+
"Venus takes about 243 Earth days to spin once on its axis.",
|
|
133
|
+
"Venus takes about 225 Earth days to orbit the Sun."
|
|
134
|
+
],
|
|
135
|
+
"sources": ["https://science.nasa.gov/venus/venus-facts/"],
|
|
136
|
+
"hashtags": ["#venus"],
|
|
137
|
+
"weight": 2
|
|
138
|
+
}
|
|
139
|
+
]
|
|
140
|
+
}
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
Prefix unconfirmed news with `RUMOR:` and the script will be forced to say so. Put topic ids in
|
|
144
|
+
`queue.txt` to choose what goes out next; otherwise the least used topic is picked (`weight` > 1 = more often).
|
|
145
|
+
|
|
146
|
+
## Commands
|
|
147
|
+
|
|
148
|
+
| Command | What it does |
|
|
149
|
+
| --- | --- |
|
|
150
|
+
| `factreel init DIR --lang es` | new channel folder from a template |
|
|
151
|
+
| `factreel make [-t TOPIC] [-n 3]` | generate videos into `output/` |
|
|
152
|
+
| `factreel make --offline --voice dummy` | instant preview: no AI, silent voice, offline images |
|
|
153
|
+
| `factreel topics` | topics and how many times each was used |
|
|
154
|
+
| `factreel doctor [--voices --log]` | check everything; `--log` is perfect for a scheduled task |
|
|
155
|
+
| `factreel upload [--privacy unlisted]` | upload pending videos to YouTube (`pip install "factreel[youtube]"`) |
|
|
156
|
+
| `factreel run` | make + upload, for a daily scheduled task |
|
|
157
|
+
|
|
158
|
+
## Documentation
|
|
159
|
+
|
|
160
|
+
- [Configuration reference](docs/configuration.md): every `channel.toml` option
|
|
161
|
+
- [Providers](docs/providers.md): voices, images, LLMs and how to add your own
|
|
162
|
+
- [YouTube upload](docs/youtube.md): OAuth setup in 5 minutes
|
|
163
|
+
- [Scheduling](docs/scheduling.md): Windows Task Scheduler, cron, GitHub Actions
|
|
164
|
+
|
|
165
|
+
## Responsible use
|
|
166
|
+
|
|
167
|
+
FactReel declares AI-generated media when uploading, never invents facts on its own, and labels rumours.
|
|
168
|
+
Please keep it that way: cite your sources, respect trademarks and music licences, and don't use it to
|
|
169
|
+
mass-produce misleading content. Platforms penalise low-effort spam; good facts make good videos.
|
|
170
|
+
|
|
171
|
+
## Contributing
|
|
172
|
+
|
|
173
|
+
Contributions are very welcome, especially new languages, voice/image providers and platform uploaders.
|
|
174
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md) and the [roadmap](ROADMAP.md) for good first issues.
|
|
175
|
+
|
|
176
|
+
## License
|
|
177
|
+
|
|
178
|
+
[MIT](LICENSE) © Jorge Lamelas
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
|
|
3
|
+
# FactReel 🎬✅
|
|
4
|
+
|
|
5
|
+
**Vídeos cortos sin cara, con datos verificados y en piloto automático.**
|
|
6
|
+
YouTube Shorts · TikTok · Reels a partir de una lista de hechos verificados, con servicios de IA gratuitos y un plan B para todo.
|
|
7
|
+
|
|
8
|
+
[](https://github.com/DeadY2J/factreel/actions/workflows/ci.yml)
|
|
9
|
+
[](https://pypi.org/project/factreel/)
|
|
10
|
+
[](LICENSE)
|
|
11
|
+
|
|
12
|
+
[English](README.md) · [Español](README.es.md)
|
|
13
|
+
|
|
14
|
+
<img src="docs/demo.gif" width="260" alt="Demo de FactReel: vídeo vertical con subtítulos palabra a palabra">
|
|
15
|
+
|
|
16
|
+
</div>
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
La mayoría de los generadores de Shorts con IA dejan que el modelo se invente cosas. **FactReel no.** Tú
|
|
21
|
+
escribes los hechos (con sus fuentes) una vez; la IA solo los convierte en un guion con gancho, y cada guion
|
|
22
|
+
pasa por controles que rechazan cifras inventadas, ideas repetidas, rumores sin avisar y cualquier cosa que
|
|
23
|
+
una segunda pasada de verificación no encuentre en tus hechos. Después lo narra, genera las imágenes, pone
|
|
24
|
+
subtítulos tipo karaoke y renderiza un MP4 vertical listo para subir.
|
|
25
|
+
|
|
26
|
+
Nace de un canal real que publica un Short cada día sin intervención, así que está hecho para **no pararse
|
|
27
|
+
nunca por un mal día**: si se cae la voz, la API de imágenes o la IA del guion, entra la siguiente opción.
|
|
28
|
+
|
|
29
|
+
## Qué incluye
|
|
30
|
+
|
|
31
|
+
- 🛡️ **Guionista con controles anti-invenciones**: lista blanca de cifras, detección de repeticiones,
|
|
32
|
+
rumores etiquetados, verificación a temperatura 0 y un guion de reserva solo con los hechos, siempre correcto.
|
|
33
|
+
- 🗣️ **Cadena de voces**: voces neuronales de Microsoft Edge (gratis, sin clave) → ElevenLabs → Cloudflare
|
|
34
|
+
Aura → la voz sin conexión de tu sistema. `factreel doctor` te avisa cuando un servicio cae y cuando vuelve.
|
|
35
|
+
- 🖼️ **Cadena de imágenes**: FLUX en Cloudflare Workers AI → FLUX en NVIDIA NIM → tu propio generador
|
|
36
|
+
(GPU local, ComfyUI…) → archivo de imágenes anteriores → fondo procedural que siempre funciona sin internet.
|
|
37
|
+
- 💬 **Subtítulos palabra a palabra** con la palabra que suena resaltada, sincronizados con la voz o con Whisper.
|
|
38
|
+
- 🎞️ **Zoom Ken Burns suave y sin temblor** (render subpíxel), fundidos entre escenas, voz normalizada a
|
|
39
|
+
-14 LUFS y música de fondo opcional con créditos automáticos.
|
|
40
|
+
- 🌍 **Español e inglés de serie** (prompts, meses, cuentas atrás). Otro idioma = un diccionario más.
|
|
41
|
+
- 💸 **Gratis por defecto**: funciona sin ninguna clave; todo lo de pago es opcional y con tope de gasto.
|
|
42
|
+
- 🔌 **Cualquier LLM compatible con OpenAI**: NVIDIA NIM, OpenRouter, Groq, OpenAI, Ollama, LM Studio, OmniRoute…
|
|
43
|
+
- 📤 **Subida a YouTube opcional** marcando el contenido como "alterado o sintético", como exige YouTube.
|
|
44
|
+
- ✅ **Comprueba cada vídeo** (resolución, audio, sin pantalla negra) antes de dejarlo en la cola de subida.
|
|
45
|
+
|
|
46
|
+
## Empezar
|
|
47
|
+
|
|
48
|
+
Necesitas **Python 3.10+** y **[FFmpeg](https://ffmpeg.org/download.html)** en el PATH.
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
pip install factreel
|
|
52
|
+
factreel init mi-canal --lang es
|
|
53
|
+
cd mi-canal
|
|
54
|
+
factreel doctor # comprueba FFmpeg, claves y cada voz
|
|
55
|
+
factreel make # -> output/20261020_180000_dia_venus.mp4 (+ metadatos .json/.txt)
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Sin ninguna clave ya sale un vídeo con la voz gratuita de Edge, los hechos leídos tal cual y fondos sin
|
|
59
|
+
conexión. Añade claves gratuitas en `.env` para tener guion e imágenes con IA:
|
|
60
|
+
|
|
61
|
+
| Clave | Qué activa | Coste |
|
|
62
|
+
| --- | --- | --- |
|
|
63
|
+
| `NVIDIA_API_KEY` ([build.nvidia.com](https://build.nvidia.com)) | guion con IA + verificación, imágenes FLUX | créditos gratis |
|
|
64
|
+
| `CLOUDFLARE_ACCOUNT_ID` + `CLOUDFLARE_API_TOKEN` | imágenes FLUX, voz de reserva, tiempos exactos de subtítulos (Whisper) | 10.000 neurons gratis al día, con tope |
|
|
65
|
+
| `ELEVENLABS_API_KEY` | voz premium | opcional |
|
|
66
|
+
|
|
67
|
+
## Tus hechos
|
|
68
|
+
|
|
69
|
+
```json
|
|
70
|
+
{
|
|
71
|
+
"topics": [
|
|
72
|
+
{
|
|
73
|
+
"id": "dia_venus",
|
|
74
|
+
"angle": "en Venus un día dura más que un año",
|
|
75
|
+
"facts": [
|
|
76
|
+
"Venus tarda unos 243 días terrestres en dar una vuelta sobre sí mismo.",
|
|
77
|
+
"Venus tarda unos 225 días terrestres en dar una vuelta al Sol."
|
|
78
|
+
],
|
|
79
|
+
"sources": ["https://science.nasa.gov/venus/venus-facts/"],
|
|
80
|
+
"hashtags": ["#venus"],
|
|
81
|
+
"weight": 2
|
|
82
|
+
}
|
|
83
|
+
]
|
|
84
|
+
}
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Empieza por `RUMOR:` las noticias sin confirmar y el guion tendrá que decirlo. Pon ids de temas en
|
|
88
|
+
`queue.txt` para elegir qué sale después; si no, se elige el tema menos usado (`weight` > 1 = más a menudo).
|
|
89
|
+
|
|
90
|
+
## Comandos
|
|
91
|
+
|
|
92
|
+
| Comando | Qué hace |
|
|
93
|
+
| --- | --- |
|
|
94
|
+
| `factreel init CARPETA --lang es` | crea un canal desde una plantilla |
|
|
95
|
+
| `factreel make [-t TEMA] [-n 3]` | genera vídeos en `output/` |
|
|
96
|
+
| `factreel make --offline --voice dummy` | vista previa al instante: sin IA, voz muda, imágenes sin conexión |
|
|
97
|
+
| `factreel topics` | temas y cuántas veces se ha usado cada uno |
|
|
98
|
+
| `factreel doctor [--voices --log]` | lo comprueba todo; `--log` es ideal para una tarea programada |
|
|
99
|
+
| `factreel upload [--privacy unlisted]` | sube los vídeos pendientes a YouTube (`pip install "factreel[youtube]"`) |
|
|
100
|
+
| `factreel run` | genera y sube, para la tarea programada diaria |
|
|
101
|
+
|
|
102
|
+
## Documentación
|
|
103
|
+
|
|
104
|
+
- [Configuración](docs/configuration.md) · [Proveedores](docs/providers.md) · [Subida a YouTube](docs/youtube.md) · [Programar tareas](docs/scheduling.md) (en inglés)
|
|
105
|
+
|
|
106
|
+
## Uso responsable
|
|
107
|
+
|
|
108
|
+
FactReel declara el contenido generado con IA al subirlo, no se inventa datos y etiqueta los rumores.
|
|
109
|
+
Mantenlo así: cita tus fuentes, respeta marcas y licencias de música y no lo uses para producir en masa
|
|
110
|
+
contenido engañoso. Las plataformas penalizan el spam; buenos datos hacen buenos vídeos.
|
|
111
|
+
|
|
112
|
+
## Contribuir
|
|
113
|
+
|
|
114
|
+
¡Las contribuciones son muy bienvenidas!, sobre todo idiomas nuevos, proveedores de voz e imagen y
|
|
115
|
+
subidas a otras plataformas. Mira [CONTRIBUTING.md](CONTRIBUTING.md) y la [hoja de ruta](ROADMAP.md).
|
|
116
|
+
Puedes escribir las issues y los PR en español o en inglés.
|
|
117
|
+
|
|
118
|
+
## Licencia
|
|
119
|
+
|
|
120
|
+
[MIT](LICENSE) © Jorge Lamelas
|
factreel-0.1.0/README.md
ADDED
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
|
|
3
|
+
# FactReel 🎬✅
|
|
4
|
+
|
|
5
|
+
**Fact-checked faceless short videos, on autopilot.**
|
|
6
|
+
YouTube Shorts · TikTok · Reels, from a list of verified facts, with free AI services and a fallback for everything.
|
|
7
|
+
|
|
8
|
+
[](https://github.com/DeadY2J/factreel/actions/workflows/ci.yml)
|
|
9
|
+
[](https://pypi.org/project/factreel/)
|
|
10
|
+
[](https://pypi.org/project/factreel/)
|
|
11
|
+
[](LICENSE)
|
|
12
|
+
|
|
13
|
+
[English](README.md) · [Español](README.es.md)
|
|
14
|
+
|
|
15
|
+
<img src="docs/demo.gif" width="260" alt="FactReel demo: vertical video with word-by-word subtitles">
|
|
16
|
+
|
|
17
|
+
</div>
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
Most "AI shorts generators" let a language model make things up. **FactReel doesn't.** You write the facts
|
|
22
|
+
(with sources) once; the AI only turns them into a punchy script, and every script goes through guards that
|
|
23
|
+
reject invented numbers, repeated ideas, unlabelled rumours and anything a second fact-checking pass can't
|
|
24
|
+
find in your facts. Then it narrates it, draws the images, burns karaoke subtitles and renders a vertical
|
|
25
|
+
MP4 ready to upload.
|
|
26
|
+
|
|
27
|
+
It grew out of a real channel that publishes a Short every day unattended, so it is built to **never stop
|
|
28
|
+
on a bad day**: if the voice service, the image API or the script AI is down, the next option in the chain
|
|
29
|
+
takes over.
|
|
30
|
+
|
|
31
|
+
## Features
|
|
32
|
+
|
|
33
|
+
- 🛡️ **Anti-hallucination script writer**: number whitelist, repetition check, rumour labelling, a
|
|
34
|
+
temperature-0 fact-check pass, and a facts-only fallback that is always correct.
|
|
35
|
+
- 🗣️ **Voice fallback chain**: Microsoft Edge neural voices (free, no key) → ElevenLabs → Cloudflare
|
|
36
|
+
Aura → your OS's offline voice. `factreel doctor` tells you when a service is down and when it is back.
|
|
37
|
+
- 🖼️ **Image fallback chain**: Cloudflare Workers AI FLUX → NVIDIA NIM FLUX → your own generator
|
|
38
|
+
(local GPU, ComfyUI…) → archive of previous images → procedural background that always works offline.
|
|
39
|
+
- 💬 **Word-by-word subtitles** with the spoken word highlighted, synced from the TTS engine or Whisper.
|
|
40
|
+
- 🎞️ **Smooth Ken Burns zoom without jitter** (sub-pixel rendering), cross-fades on scene changes,
|
|
41
|
+
voice normalised to -14 LUFS, optional background music with automatic credits.
|
|
42
|
+
- 🌍 **Spanish and English first-class** (prompts, month names, countdowns). More languages = one dict.
|
|
43
|
+
- 💸 **Free by default**: runs with zero API keys; every paid service is optional and budget-capped.
|
|
44
|
+
- 🔌 **Any OpenAI-compatible LLM**: NVIDIA NIM, OpenRouter, Groq, OpenAI, Ollama, LM Studio, OmniRoute…
|
|
45
|
+
- 📤 **Optional YouTube upload** with the "altered or synthetic content" flag set for you.
|
|
46
|
+
- ✅ **Verifies every render** (resolution, audio, no black screen) before it reaches the upload queue.
|
|
47
|
+
|
|
48
|
+
## Quick start
|
|
49
|
+
|
|
50
|
+
You need **Python 3.10+** and **[FFmpeg](https://ffmpeg.org/download.html)** on your PATH.
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
pip install factreel
|
|
54
|
+
factreel init my-channel --lang en # or --lang es
|
|
55
|
+
cd my-channel
|
|
56
|
+
factreel doctor # checks FFmpeg, keys and every voice
|
|
57
|
+
factreel make # -> output/20261020_180000_venus_day.mp4 (+ .json/.txt metadata)
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
That's it: with no keys at all you get a video with the free Edge voice, the facts read as they are and
|
|
61
|
+
offline backgrounds. Add free keys to `.env` to unlock the AI script and AI images:
|
|
62
|
+
|
|
63
|
+
| Key | What it unlocks | Cost |
|
|
64
|
+
| --- | --- | --- |
|
|
65
|
+
| `NVIDIA_API_KEY` ([build.nvidia.com](https://build.nvidia.com)) | AI script + fact check, FLUX images | free credits |
|
|
66
|
+
| `CLOUDFLARE_ACCOUNT_ID` + `CLOUDFLARE_API_TOKEN` | FLUX images, backup voice, exact subtitle timing (Whisper) | 10,000 free neurons/day, capped |
|
|
67
|
+
| `ELEVENLABS_API_KEY` | premium voice | optional |
|
|
68
|
+
|
|
69
|
+
## How it works
|
|
70
|
+
|
|
71
|
+
```mermaid
|
|
72
|
+
flowchart LR
|
|
73
|
+
F[facts.json<br/>verified facts + sources] --> T[topic rotation<br/>queue.txt / least used]
|
|
74
|
+
T --> S[AI script]
|
|
75
|
+
S --> G{guards<br/>numbers · repeats<br/>rumours · fact check}
|
|
76
|
+
G -- rejected --> S
|
|
77
|
+
G -- gives up --> FB[facts-only script]
|
|
78
|
+
G -- ok --> V[voice chain]
|
|
79
|
+
FB --> V
|
|
80
|
+
V --> SUB[karaoke subtitles]
|
|
81
|
+
V --> I[image chain]
|
|
82
|
+
SUB --> R[FFmpeg render + verify]
|
|
83
|
+
I --> R
|
|
84
|
+
R --> O[output/*.mp4 + metadata] --> U[YouTube upload]
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## Your facts
|
|
88
|
+
|
|
89
|
+
```json
|
|
90
|
+
{
|
|
91
|
+
"topics": [
|
|
92
|
+
{
|
|
93
|
+
"id": "venus_day",
|
|
94
|
+
"angle": "a day on Venus is longer than its year",
|
|
95
|
+
"facts": [
|
|
96
|
+
"Venus takes about 243 Earth days to spin once on its axis.",
|
|
97
|
+
"Venus takes about 225 Earth days to orbit the Sun."
|
|
98
|
+
],
|
|
99
|
+
"sources": ["https://science.nasa.gov/venus/venus-facts/"],
|
|
100
|
+
"hashtags": ["#venus"],
|
|
101
|
+
"weight": 2
|
|
102
|
+
}
|
|
103
|
+
]
|
|
104
|
+
}
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
Prefix unconfirmed news with `RUMOR:` and the script will be forced to say so. Put topic ids in
|
|
108
|
+
`queue.txt` to choose what goes out next; otherwise the least used topic is picked (`weight` > 1 = more often).
|
|
109
|
+
|
|
110
|
+
## Commands
|
|
111
|
+
|
|
112
|
+
| Command | What it does |
|
|
113
|
+
| --- | --- |
|
|
114
|
+
| `factreel init DIR --lang es` | new channel folder from a template |
|
|
115
|
+
| `factreel make [-t TOPIC] [-n 3]` | generate videos into `output/` |
|
|
116
|
+
| `factreel make --offline --voice dummy` | instant preview: no AI, silent voice, offline images |
|
|
117
|
+
| `factreel topics` | topics and how many times each was used |
|
|
118
|
+
| `factreel doctor [--voices --log]` | check everything; `--log` is perfect for a scheduled task |
|
|
119
|
+
| `factreel upload [--privacy unlisted]` | upload pending videos to YouTube (`pip install "factreel[youtube]"`) |
|
|
120
|
+
| `factreel run` | make + upload, for a daily scheduled task |
|
|
121
|
+
|
|
122
|
+
## Documentation
|
|
123
|
+
|
|
124
|
+
- [Configuration reference](docs/configuration.md): every `channel.toml` option
|
|
125
|
+
- [Providers](docs/providers.md): voices, images, LLMs and how to add your own
|
|
126
|
+
- [YouTube upload](docs/youtube.md): OAuth setup in 5 minutes
|
|
127
|
+
- [Scheduling](docs/scheduling.md): Windows Task Scheduler, cron, GitHub Actions
|
|
128
|
+
|
|
129
|
+
## Responsible use
|
|
130
|
+
|
|
131
|
+
FactReel declares AI-generated media when uploading, never invents facts on its own, and labels rumours.
|
|
132
|
+
Please keep it that way: cite your sources, respect trademarks and music licences, and don't use it to
|
|
133
|
+
mass-produce misleading content. Platforms penalise low-effort spam; good facts make good videos.
|
|
134
|
+
|
|
135
|
+
## Contributing
|
|
136
|
+
|
|
137
|
+
Contributions are very welcome, especially new languages, voice/image providers and platform uploaders.
|
|
138
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md) and the [roadmap](ROADMAP.md) for good first issues.
|
|
139
|
+
|
|
140
|
+
## License
|
|
141
|
+
|
|
142
|
+
[MIT](LICENSE) © Jorge Lamelas
|