cameo creature pfps to chardesigns
This commit is contained in:
+57
-3
@@ -8,6 +8,7 @@ Usage:
|
||||
Defaults:
|
||||
chat-root: MIXER_TWITCH_CHAT env, else Windows Synology path used in this project.
|
||||
Writes JPEGs under Story/pfp/<slug>/ and inserts a marked section into each roster .md.
|
||||
Also fills Story/Cameo-Creatures.md from CAMEO_ROSTER (see tools/pfp_from_chat.py).
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
@@ -53,6 +54,21 @@ MD_FILES: dict[str, str] = {
|
||||
"raincloud": "RaincloudTheDragon.md",
|
||||
}
|
||||
|
||||
# Grouped cameo handles (see Story/Cameo-Creatures.md). Slug = folder under Story/pfp/<slug>/.
|
||||
CAMEO_ROSTER: dict[str, list[str]] = {
|
||||
"cameo-branndon": ["branndongames", "experimenta1ic3"],
|
||||
"cameo-noncritical": ["noncriticalmother", "noncriticalgamingttv"],
|
||||
"cameo-pirate": ["pirate_protogen"],
|
||||
"cameo-foxy": ["foxy_fnaf5_ucn"],
|
||||
"cameo-gymrat": ["basedgymrat"],
|
||||
"cameo-miclbero": ["miclbero"],
|
||||
"cameo-queen": ["cameoqueen86"],
|
||||
"cameo-rayne": ["rayne8856"],
|
||||
"cameo-bd": ["bd_cum_lube"],
|
||||
}
|
||||
|
||||
CAMEO_MD = "Cameo-Creatures.md"
|
||||
|
||||
DEFAULT_CHAT_ROOT = os.environ.get(
|
||||
"MIXER_TWITCH_CHAT",
|
||||
r"C:\Users\Nathan\SynologyDrive\YouTube\Streams\MixerTwitch",
|
||||
@@ -113,12 +129,14 @@ class SeenAvatar:
|
||||
url: str
|
||||
|
||||
|
||||
def collect_avatars(chat_root: Path) -> dict[tuple[str, str], dict[str, SeenAvatar]]:
|
||||
def collect_avatars(
|
||||
chat_root: Path, roster: dict[str, list[str]]
|
||||
) -> dict[tuple[str, str], dict[str, SeenAvatar]]:
|
||||
"""
|
||||
Returns map (slug, login) -> fingerprint -> SeenAvatar (earliest message wins per fp).
|
||||
"""
|
||||
login_to_slug: dict[str, str] = {}
|
||||
for slug, logins in ROSTER.items():
|
||||
for slug, logins in roster.items():
|
||||
for lg in logins:
|
||||
login_to_slug[lg.lower()] = slug
|
||||
|
||||
@@ -236,7 +254,8 @@ def main() -> int:
|
||||
print("Set MIXER_TWITCH_CHAT or pass --chat-root.", file=sys.stderr)
|
||||
return 1
|
||||
|
||||
acc = collect_avatars(args.chat_root)
|
||||
full_roster: dict[str, list[str]] = {**ROSTER, **CAMEO_ROSTER}
|
||||
acc = collect_avatars(args.chat_root, full_roster)
|
||||
pfp_root = args.story_root / "pfp"
|
||||
|
||||
# Flatten per slug for multi-login: still per-login files
|
||||
@@ -294,6 +313,41 @@ def main() -> int:
|
||||
md_path.write_text(inject_section(text, section_md), encoding="utf-8")
|
||||
print(f"updated {md_path}")
|
||||
|
||||
# Cameo creatures: one combined section in Cameo-Creatures.md
|
||||
cameo_images: list[tuple[str, str]] = []
|
||||
for slug in CAMEO_ROSTER:
|
||||
logins = CAMEO_ROSTER[slug]
|
||||
story_rel = f"pfp/{slug}"
|
||||
for login in logins:
|
||||
key = (slug, login)
|
||||
avs = acc.get(key, {})
|
||||
ordered = sorted(avs.values(), key=lambda x: x.first_at)
|
||||
if not ordered:
|
||||
print(f"No avatars in exports for cameo `{login}` ({slug})", file=sys.stderr)
|
||||
continue
|
||||
for i, av in enumerate(ordered):
|
||||
fn = f"{login}_{i}.jpg"
|
||||
rel = f"{story_rel}/{fn}"
|
||||
dest = pfp_root / slug / fn
|
||||
if not download_as_jpeg(av.url, dest):
|
||||
continue
|
||||
cap = f"`{login}`"
|
||||
if len(ordered) > 1:
|
||||
cap = f"{cap} — {i + 1}"
|
||||
cameo_images.append((cap, rel))
|
||||
|
||||
if cameo_images:
|
||||
cameo_md = build_section_md(cameo_images)
|
||||
cameo_path = args.story_root / CAMEO_MD
|
||||
if not args.no_write_md and cameo_path.is_file():
|
||||
cameo_path.write_text(
|
||||
inject_section(cameo_path.read_text(encoding="utf-8"), cameo_md),
|
||||
encoding="utf-8",
|
||||
)
|
||||
print(f"updated {cameo_path}")
|
||||
elif not args.no_write_md:
|
||||
print("No cameo avatars downloaded (check exports and logins).", file=sys.stderr)
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user