fix: medium+low issues — version check, URL regex, login wait, path DRY

- M1: version check resilient to network errors, configurable upstream repo
- M2: extract get_output_path() in final_video.py, reuse from main.py
- M3: replace backtracking URL regex with atomic https?://\S+ pattern
- M4: replace fixed 6s delay with page.wait_for_url() post-login
- L1: TOTP secret security warning in config template
- L2-L3: Dockerfile comments for digest pinning + pip version pinning

Co-Authored-By: RuFlo <ruv@ruv.net>
pull/2551/head
Hong Phuc 4 weeks ago
parent 586d5c565b
commit 64e9b6f149

@ -1,3 +1,4 @@
# For production, pin to a digest: FROM python:3.10-slim-bookworm@sha256:...
FROM python:3.10-slim-bookworm
ENV PYTHONDONTWRITEBYTECODE=1 \
@ -14,6 +15,7 @@ RUN apt-get update \
&& rm -rf /var/lib/apt/lists/*
COPY requirements.txt ./
# Pin pip for reproducible builds: --upgrade pip==24.0
RUN pip install --upgrade pip \
&& pip install -r requirements.txt \
&& pip install pytest

@ -58,8 +58,8 @@ class TTSEngine:
self,
): # adds periods to the end of paragraphs (where people often forget to put them) so tts doesn't blend sentences
for comment in self.reddit_object["comments"]:
# remove links
regex_urls = r"((http|https)\:\/\/)?[a-zA-Z0-9\.\/\?\:@\-_=#]+\.([a-zA-Z]){2,6}([a-zA-Z0-9\.\&\/\?\:@\-_=#])*"
# remove links — atomic URL pattern (no backtracking)
regex_urls = r"https?://\S+|www\.\S+\.\S+"
comment["comment_body"] = re.sub(regex_urls, " ", comment["comment_body"])
comment["comment_body"] = comment["comment_body"].replace("\n", ". ")
comment["comment_body"] = re.sub(r"\bAI\b", "A.I", comment["comment_body"])

@ -19,7 +19,7 @@ from video_creation.background import (
download_background_video,
get_background_config,
)
from video_creation.final_video import make_final_video, name_normalize
from video_creation.final_video import get_output_path, make_final_video
from video_creation.voices import save_text_to_mp3
from video_creation.youtube_uploader import upload_to_youtube
@ -80,22 +80,9 @@ def main(POST_ID=None) -> None:
# -- YouTube upload (if enabled in config) ---------------------------
youtube_config = settings.config.get("youtube", {})
if youtube_config.get("enabled", False):
# Compute the video path using the same logic as final_video.py
title_raw = reddit_object.get("thread_title", "video")
filename = f"{name_normalize(title_raw)[:251]}"
platform = settings.config["settings"].get("platform", "reddit")
if platform == "reddit":
subreddit = (
settings.config.get("reddit", {})
.get("thread", {})
.get("subreddit", "unknown")
)
else:
subreddit = reddit_object.get("thread_category", platform)
video_path = f"results/{subreddit}/{filename}.mp4"
video_path = get_output_path(reddit_object)
youtube_url = upload_to_youtube(
video_path, title_raw, settings.config
video_path, reddit_object.get("thread_title", "video"), settings.config
)
if youtube_url:
print_substep(f"YouTube URL: {youtube_url}", "bold green")

@ -40,7 +40,9 @@ def login_to_threads(page: Page, _context: BrowserContext) -> None:
page.locator('input[autocomplete="current-password"]').fill(password)
page.get_by_role("button", name="Log in", exact=True).first.click()
page.wait_for_timeout(6000)
# Wait for navigation to feed (success) or stay on login (failure)
page.wait_for_url("https://www.threads.net/", timeout=15000)
page.wait_for_load_state("networkidle")
cookies = _context.cookies()
Path(THREADS_COOKIE_FILE).parent.mkdir(parents=True, exist_ok=True)

@ -4,7 +4,8 @@ client_secret = { optional = false, nmin = 20, nmax = 40, explanation = "The SEC
username = { optional = false, nmin = 3, nmax = 20, explanation = "The username of your reddit account", example = "JasonLovesDoggo", regex = "^[-_0-9a-zA-Z]+$", oob_error = "A username HAS to be between 3 and 20 characters" }
password = { optional = false, nmin = 8, explanation = "The password of your reddit account", example = "fFAGRNJru1FTz70BzhT3Zg", oob_error = "Password too short" }
2fa = { optional = true, type = "bool", options = [true, false, ], default = false, explanation = "Whether you have Reddit 2FA enabled, Valid options are True and False", example = true }
2fa_secret = { optional = true, default = "", explanation = "TOTP shared secret (base32). If provided, 2FA codes are generated automatically instead of prompting interactively.", example = "JBSWY3DPEHPK3PXP" }
# WARNING: Treat this secret like a password — it allows anyone to generate valid 2FA codes.
2fa_secret = { optional = true, default = "", explanation = "TOTP shared secret (base32). If provided, 2FA codes are generated automatically instead of prompting interactively. SECURITY: store this with the same care as a password.", example = "JBSWY3DPEHPK3PXP" }
[reddit.thread]

@ -2,20 +2,33 @@ import requests
from utils.console import print_step
# Set to the correct GitHub "owner/repo" for this fork, or leave empty to skip check.
_UPSTREAM_REPO = ""
def checkversion(__VERSION__: str):
response = requests.get(
"https://api.github.com/repos/elebumm/RedditVideoMakerBot/releases/latest"
)
latestversion = response.json()["tag_name"]
if not _UPSTREAM_REPO:
return
try:
response = requests.get(
f"https://api.github.com/repos/{_UPSTREAM_REPO}/releases/latest",
timeout=10,
)
response.raise_for_status()
latestversion = response.json()["tag_name"]
except (requests.RequestException, KeyError, ValueError):
return # Network or API error — skip version check silently
if __VERSION__ == latestversion:
print_step(f"You are using the newest version ({__VERSION__}) of the bot")
return True
elif __VERSION__ < latestversion:
print_step(
f"You are using an older version ({__VERSION__}) of the bot. Download the newest version ({latestversion}) from https://github.com/elebumm/RedditVideoMakerBot/releases/latest"
f"You are using an older version ({__VERSION__}) of the bot. "
f"Download the newest version ({latestversion}) from "
f"https://github.com/{_UPSTREAM_REPO}/releases/latest"
)
else:
print_step(
f"Welcome to the test version ({__VERSION__}) of the bot. Thanks for testing and feel free to report any bugs you find."
f"Welcome to the test version ({__VERSION__}) of the bot. "
f"Thanks for testing and feel free to report any bugs you find."
)

@ -27,6 +27,22 @@ from utils.videos import save_data
console = Console()
def get_output_path(reddit_obj: dict) -> str:
"""Compute the output mp4 path from a content object. Shared with main.py."""
title_raw = reddit_obj.get("thread_title", "video")
filename = f"{name_normalize(title_raw)[:251]}"
platform = settings.config["settings"].get("platform", "reddit")
if platform == "reddit":
subreddit = (
settings.config.get("reddit", {})
.get("thread", {})
.get("subreddit", "unknown")
)
else:
subreddit = reddit_obj.get("thread_category", platform)
return f"results/{subreddit}/{filename}.mp4"
def _probe_duration(path: str) -> float:
"""Get media duration in seconds using PyAV."""
with av.open(path) as container:

Loading…
Cancel
Save