yt / src /asset_manager.py
ayush77uh's picture
ass
69b3c30
Raw
History Blame Contribute Delete
8.27 kB
"""
Asset Manager — Fetches background clips from Pexels API.
Downloads landscape-oriented clips relevant to the script topic.
Falls back to local files if Pexels fails.
"""
import os
import random
import glob
import requests
import time
import re
PEXELS_API_KEY = os.environ.get("PEXELS_API_KEY", "")
PEXELS_VIDEO_URL = "https://api.pexels.com/videos/search"
PEXELS_PER_PAGE = min(int(os.environ.get("PEXELS_PER_PAGE", "80")), 80)
# Anti-repetition
_used_ids = set()
# Self-improvement/psychology keywords for varied backgrounds
FALLBACK_SEARCHES = [
"dark room",
"alone",
"silhouette",
"shadow face",
"thinking",
"stress",
"therapy",
"city night",
"argument",
"conversation",
"sad man",
"sad woman",
"anxiety",
"depression",
"lonely",
"mystery",
"dramatic light",
"relationship",
"breakup",
"office stress",
"student studying dark",
"focused work desk",
"city night cinematic",
"person thinking alone",
"gym discipline training",
"office work focus",
"library study",
"walking alone city",
"mindset success",
"psychology human behavior",
]
TOPIC_QUERY_MAP = {
"gaslighting": ["argument", "confused woman", "relationship conflict", "stress"],
"manipulation": ["conversation", "argument", "shadow face", "relationship conflict"],
"dark psychology": ["dark room", "shadow face", "mystery", "thinking"],
"narciss": ["argument", "relationship conflict", "angry man", "couple arguing"],
"silent treatment": ["lonely", "sad woman", "alone", "relationship"],
"guilt": ["stress", "sad man", "anxiety", "alone"],
"love bombing": ["couple", "relationship", "romantic", "dating"],
"toxic": ["argument", "relationship conflict", "stress", "breakup"],
"emotional": ["stress", "crying", "sad woman", "anxiety"],
"victim": ["sad man", "alone", "stress", "depression"],
"triangulation": ["jealousy", "couple", "relationship", "argument"],
"push-pull": ["relationship", "breakup", "alone", "couple"],
"confusion": ["confused", "thinking", "stress", "anxiety"],
"over-explaining": ["conversation", "office talk", "stress", "argument"],
"intermittent": ["phone waiting", "alone", "relationship", "sad woman"],
"fake nice": ["smiling", "office conversation", "handshake", "people talking"],
"power": ["business meeting", "confidence", "office", "leadership"],
"walking away": ["walking away", "alone walking", "city night", "breakup"],
}
def build_search_queries(topic, count):
normalized = re.sub(r"[-_:]", " ", topic.lower())
normalized = re.sub(r"[^a-z0-9 ]+", " ", normalized)
queries = []
for key, mapped_queries in TOPIC_QUERY_MAP.items():
if key in normalized:
queries.extend(mapped_queries)
words = [word for word in normalized.split() if len(word) > 3]
if words:
queries.append(" ".join(words[:2]))
queries.extend(random.sample(FALLBACK_SEARCHES, min(len(FALLBACK_SEARCHES), count * 3)))
deduped = []
for query in queries:
if query and query not in deduped:
deduped.append(query)
return deduped
class AssetManager:
def __init__(self, assets_dir="assets"):
self.assets_dir = assets_dir
self.backgrounds_dir = os.path.join(assets_dir, "backgrounds")
os.makedirs(self.backgrounds_dir, exist_ok=True)
self._search_index = 0
def get_random_background_video(self, topic="self improvement psychology"):
"""Gets a landscape background clip from Pexels or local files."""
# Try Pexels first
clip = self._fetch_from_pexels(topic)
if clip:
return clip
# Fallback: try generic search
fallback_kw = FALLBACK_SEARCHES[self._search_index % len(FALLBACK_SEARCHES)]
self._search_index += 1
clip = self._fetch_from_pexels(fallback_kw)
if clip:
return clip
# Fallback: local files
print("⚠️ Pexels failed, trying local files...", flush=True)
videos = glob.glob(os.path.join(self.backgrounds_dir, "*.mp4"))
if not videos:
videos = glob.glob(os.path.join(self.assets_dir, "*.mp4"))
if videos:
return random.choice(videos)
print("❌ No background videos found!", flush=True)
return None
def get_background_videos(self, topic="self improvement psychology", count=6):
"""Gets multiple landscape background clips for one video."""
clips = []
queries = build_search_queries(topic, count)
for query in queries:
if len(clips) >= count:
break
clip = self._fetch_from_pexels(query)
if clip and clip not in clips:
clips.append(clip)
if len(clips) < count:
local_videos = glob.glob(os.path.join(self.backgrounds_dir, "*.mp4"))
if not local_videos:
local_videos = glob.glob(os.path.join(self.assets_dir, "*.mp4"))
random.shuffle(local_videos)
for clip in local_videos:
if len(clips) >= count:
break
if clip not in clips:
clips.append(clip)
print(f"🎞️ Background clips selected: {len(clips)}/{count}", flush=True)
return clips
def _fetch_from_pexels(self, query):
"""Search and download a landscape clip from Pexels."""
if not PEXELS_API_KEY:
print("⚠️ PEXELS_API_KEY not set!", flush=True)
return None
try:
headers = {"Authorization": PEXELS_API_KEY}
params = {
"query": query,
"orientation": "landscape",
"per_page": PEXELS_PER_PAGE,
"page": random.randint(1, 25), # Random page = different clips each time
"size": "medium",
}
print(f"🔍 Pexels: searching '{query}' ({PEXELS_PER_PAGE} clips/page)...", flush=True)
resp = requests.get(PEXELS_VIDEO_URL, headers=headers, params=params, timeout=15)
if resp.status_code != 200:
print(f"⚠️ Pexels API error: {resp.status_code}", flush=True)
return None
videos = resp.json().get("videos", [])
if not videos:
print(f"⚠️ No results for '{query}'", flush=True)
return None
# Filter out already-used clips
available = [v for v in videos if v["id"] not in _used_ids]
if not available:
_used_ids.clear() # Reset if all used
available = videos
# Pick random clip
video = random.choice(available)
_used_ids.add(video["id"])
# Find best quality file (prefer HD, landscape)
download_url = None
for vf in video.get("video_files", []):
w = vf.get("width", 0)
h = vf.get("height", 0)
if w >= 1920 and w >= h:
download_url = vf["link"]
break
# Fallback: any file
if not download_url and video.get("video_files"):
download_url = video["video_files"][0]["link"]
if not download_url:
return None
# Download
filename = f"pexels_{video['id']}_{int(time.time())}.mp4"
local_path = os.path.join(self.backgrounds_dir, filename)
print(f"⬇️ Downloading: {filename}...", flush=True)
vid_resp = requests.get(download_url, timeout=60)
if vid_resp.status_code == 200:
with open(local_path, "wb") as f:
f.write(vid_resp.content)
size_mb = len(vid_resp.content) / (1024 * 1024)
print(f"✅ Downloaded: {filename} ({size_mb:.1f} MB)", flush=True)
return local_path
except Exception as e:
print(f"⚠️ Pexels error: {e}", flush=True)
return None
if __name__ == "__main__":
am = AssetManager()
bg = am.get_random_background_video("self improvement psychology")
print(f"Background: {bg}")