aboutsummaryrefslogtreecommitdiffstats
path: root/playlist_generator/frame_generator.py
blob: 6bee037c2dc960e9780b4b45f8caf662695b8f85 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
import random
import subprocess
import yt_dlp
import cv2
import os


def get_video_info(url):
    ydl_opts = {"quiet": True}
    with yt_dlp.YoutubeDL(ydl_opts) as ydl:
        return ydl.extract_info(url, download=False)


def pick_best_video_url(info):
    formats = [
        f for f in info["formats"]
        if f.get("url") and f.get("vcodec") != "none"
    ]
    formats.sort(
        key=lambda f: (f.get("height") or 0, f.get("tbr") or 0),
        reverse=True
    )
    return formats[0]["url"]


def is_low_energy(img_path, size=64, threshold=500):
    img = cv2.imread(img_path)
    if img is None:
        raise RuntimeError("Could not read image")
    img = cv2.resize(img, (size, size), interpolation=cv2.INTER_AREA)
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    return gray.var() < threshold


def download_random_frame(url, name_prefix="frame", selected_timestamps=None, max_tries=10):
    if selected_timestamps is None:
        selected_timestamps = []

    info = get_video_info(url)
    duration = info.get("duration")
    if not duration:
        raise RuntimeError("No duration found")

    video_url = pick_best_video_url(info)

    for _ in range(max_tries):
        t = random.uniform(0, duration)
        while any(abs(t - ts) < 1 for ts in selected_timestamps):
            t = random.uniform(0, duration)
        filename = f"{name_prefix}-{t:.2f}.jpg"

        cmd = [
            "ffmpeg",
            "-ss", str(t),
            "-i", video_url,
            "-frames:v", "1",
            "-q:v", "2",
            "-y",
            filename,
        ]
        subprocess.run(cmd, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
        if not os.path.exists(filename):
            continue
        try:
            if is_low_energy(filename):
                os.remove(filename)
                continue
        except Exception:
            pass
        return t

    raise RuntimeError("Failed to generate a valid frame after retries")


def generate_daily_random_frames(url, output_dir, count=3):
    selected_timestamps = []
    os.makedirs(output_dir, exist_ok=True)
    for i in range(count):
        ts = download_random_frame(
            url,
            f"{output_dir}/frame",
            selected_timestamps
        )
        selected_timestamps.append(ts)
        print(f"Downloaded frame {i} at {ts:.2f}s")
send patches to the email below
yukais@pinapelz.com
include the subject [PATCH repo_name]
pinapelz.com
homepage