Refresh the GUI templates around the desktop-first neo-brutalist direction so the library, create, background, and settings screens share one visual system.
Tested: rtk docker compose run --rm test
Tested: Playwright desktop screenshots for /, select mode, /backgrounds, and /settings
Preserve masked secrets on settings save, tolerate malformed background add requests, escape background catalog values, and skip terminal clearing when TERM is unset.
Tested: rtk docker compose run --rm test
- Enable spaCy 3.8.13 (3.8.14 was not on PyPI)
- Remove `pip cache purge` — conflicts with PIP_NO_CACHE_DIR=1
- Model en_core_web_sm auto-downloads at runtime
Co-Authored-By: RuFlo <ruv@ruv.net>
Bump base image from 3.10 to 3.14 to match host Python version.
Add pip cache purge after install to reduce image size.
Note: spaCy remains commented out (no 3.14 wheel yet).
All other dependencies verified working on Python 3.14.4.
Co-Authored-By: RuFlo <ruv@ruv.net>
Replace eval() with safe type-coercion dicts in console/settings/gui_utils.
Replace os.system() with subprocess.run() in TTS engine_wrapper.
Remove shell=True from all subprocess/Popen calls in main + ffmpeg_install.
Redact credentials from error logs and settings page HTML.
Fix 6 bare except clauses across the codebase.
Bug fixes:
- Config overwrite crash: set config={} after writing empty file
- Playwright TimeoutError: import correct exception class
- Lambda closure: default arg captures loop variable value
- Redundant ffmpeg: single concat run after all segments generated
- Audio IndexError: explicit check before accessing clips_durations[0]
- NSFW selector: use generic role-based button instead of hardcoded post ID
- Dead macOS branch: sys.platform == "darwin" instead of os.name == "mac"
Hardening:
- Flask secret_key from env var, rotate per startup
- Docker non-root user (appuser)
- CSRF check via Origin header on mutating requests
- Security headers: X-Content-Type-Options, X-Frame-Options
- Citation path traversal sanitization
- Temp file cleanup in ProgressFfmpeg.__exit__
Co-Authored-By: RuFlo <ruv@ruv.net>
- Reply screenshots now target by comment_id instead of .first (was capturing main post)
- TTS engine returns actual count (idx+1) instead of last index
- Background chop uses ffmpeg stream-copy instead of moviepy re-encode
- Merged prepare_background crop+scale into overlay filter graph (single encode pass)
- Added -preset veryfast -crf 23 to overlay renders
- Platform-conditional title image (no Reddit template on Threads)
Co-Authored-By: RuFlo <ruv@ruv.net>
Add keywords input to Create page to override search_queries per run.
Split /create into two panels: left controls + progress, right
real-time scraper activity feed with stage diagram and typed event
cards. Emit structured events from scraper and auth modules. Add
blocked_words fields to Settings page Content tab.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- Web scraper (platforms/threads/scraper.py) with div-based card parsing
- Multi-source discovery: For You feed + configurable search queries
- Engagement filtering (min_engagement) and post age filter (max_post_age)
- Shared Playwright auth module (platforms/threads/auth.py)
- Migrated ffmpeg-python to av (PyAV) for in-process media probing
- Video composition uses subprocess ffmpeg (av filter graph segfault workaround)
- Updated CLAUDE.md with Threads scraping and macOS-specific notes
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Keep the third-party bot in a dedicated submodule so its history and update cadence stay isolated from the main repo while still making it available in-tree for local workflows.
Constraint: The upstream code should remain separately updatable without copying its files into this repository.
Rejected: Copy the project into the tree directly | duplicated history and harder future syncing.
Confidence: high
Scope-risk: narrow
Directive: Update the submodule by moving it deliberately; do not edit its contents from the parent repo without planning that workflow.
Tested: git submodule add; git submodule status; inspected .gitmodules
Not-tested: Upstream repo build/runtime inside this checkout.
Document the Docker Compose workflow, persistent runtime paths, and container-specific GUI binding so future work on this branch follows the implemented setup rather than the old direct-Python assumptions.
Constraint: The repo now supports both host and container execution paths, and the agent guidance needs to reflect the new operational defaults.
Rejected: Leave AGENTS.md untouched | it would continue pointing contributors at stale runtime behavior.
Confidence: high
Scope-risk: narrow
Directive: Treat the Docker Compose commands as the default local workflow for GUI/CLI work on this branch.
Tested: Reviewed AGENTS.md against the implemented Docker files and runtime bootstrap.
Not-tested: No code-path changes; documentation-only update.
Build one shared container image for the Flask GUI and CLI pipeline, with Playwright, FFmpeg, and spaCy preinstalled so first runs are reliable. Add bootstrap logic for missing runtime files, bind the GUI to 0.0.0.0 in containers, and preserve state through a repo mount.
Constraint: Local development needs a single image that supports both entrypoints without introducing extra services or dependencies.
Rejected: Separate GUI and CLI images | duplicated maintenance and no runtime benefit for this repo.
Confidence: high
Scope-risk: moderate
Directive: Keep runtime state creation in the container bootstrap layer; do not reintroduce host-specific assumptions into GUI startup.
Tested: docker compose build; docker compose run --rm gui python -c '...'; docker compose run --rm cli python -c 'import main'; docker compose up -d gui; curl -I http://localhost:4000
Not-tested: Full end-to-end video generation with live credentials in this environment.
## Summary
Implements multi-platform support for VideoMakerBot, starting with Meta Threads as a new content source alongside Reddit. Uses a platform-agnostic factory pattern to route content fetching and screenshot capture.
## Changes
### New Files
- platforms/__init__.py: Factory dispatch for platform selection
- platforms/threads/__init__.py: Threads package marker
- platforms/threads/fetcher.py: Threads Graph API integration
- platforms/threads/screenshot.py: Playwright-based Threads screenshotter
- CLAUDE.md: Comprehensive development guide
- AGENT.md: Guidelines for AI agents working on the codebase
### Modified Files
- main.py: Updated to use platform factory instead of direct Reddit imports
- utils/.config.template.toml: Added [settings].platform, [settings].post_lang, [threads.*] sections
- utils/videos.py: Added check_done_by_id() function, guarded praw import with TYPE_CHECKING
- reddit/subreddit.py: Added thread_category field to content dict
- TTS/engine_wrapper.py: Fixed post_lang to use fallback chain
- video_creation/final_video.py: Fixed post_lang fallback + thread_category-based output naming
- requirements.txt: Fixed yt-dlp version to 2025.10.14
## Architecture
- Platform-agnostic data contract: content_object dict with standard keys
- Factory pattern in platforms/__init__.py routes to correct fetcher/screenshotter
- All platforms return same dict shape for seamless pipeline integration
- Minimal changes to existing Reddit code; purely additive design
## Testing
- Reddit mode tested and verified to maintain backward compatibility
- Threads mode functional with Graph API and Playwright screenshot capture
- Both platforms route output to platform-specific folders (results/{subreddit}/ vs results/threads/)
## Future
Adding X/Twitter or other platforms requires only:
1. New platform module (fetcher + screenshot)
2. Config section in .config.template.toml
3. Two elif branches in platforms/__init__.py
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>