Kling Transitions — Resumen

Documentos en ai-videos/ · generación de video con Kling API (image2video, modelo kling-v1-6)

inicio.jpeg (brazo normal) paso2 - agua.png (líquido azul) paso3 - fuego.png (fuego intenso) paso4 - normal.jpeg (brazo normal)

Arm to Blue Liquid generado

Doc: arm-liquid-transition.md · Script: generate_arm_transition.py
inicio
inicio.jpeg
paso2 - agua
paso2 - agua.png
arm_to_liquid.mp4 (3.5s, recortado)
model: kling-v1-6 mode: pro cfg_scale: 0.8 duration: 5s → recorte 3.5s (ffmpeg)
Prompt
the person's arm and hand gradually transform into flowing blue liquid,
skin morphs into smooth glossy electric blue liquid with water-like
ripples, droplets and surface highlights, photorealistic, same lighting,
same tiled floor background, same camera angle and framing,
only the arm changes, fluid dynamics, cinematic, 8K hyperrealistic
Output: ~/Movies/arm-liquid-transition/arm_to_liquid.mp4 (full: arm_to_liquid_full.mp4)

Arm to Intense Fire generado

Doc: arm-fire-transition.md · Script: generate_arm_fire_transition.py
paso2 - agua
paso2 - agua.png
paso3 - fuego
paso3 - fuego.png
arm_to_fire.mp4 (5s, completo)
model: kling-v1-6 mode: pro cfg_scale: 0.8 duration: 5s (sin recorte)
Prompt
the person's arm and hand gradually transform into intense
hyperrealistic fire, the blue liquid skin ignites and turns into
bright orange and yellow flames with a white-hot core, glowing
embers, heat distortion, sparks flying, smoke rising,
photorealistic, same lighting, same tiled floor background,
same camera angle and framing, only the arm changes, cinematic,
8K hyperrealistic fire physics
Output: ~/Movies/arm-fire-transition/arm_to_fire.mp4

Arm Fire to Normal generado

Doc: arm-fire-to-normal-transition.md · Script: generate_arm_fire_to_normal_transition.py
paso3 - fuego
paso3 - fuego.png
paso4 - normal
paso4 - normal.jpeg
arm_fire_to_normal.mp4 (5s, completo)
model: kling-v1-6 mode: pro cfg_scale: 0.8 duration: 5s (sin recorte)
Prompt
the intense fire engulfing the person's arm and hand gradually
extinguishes hyperrealistically, flames shrink and die out,
embers cooling and fading, smoke dissipating, the burning arm
slowly reveals normal human skin underneath, the hand rotates
and opens from a closed fist to a relaxed open hand,
photorealistic, same lighting, same tiled floor background,
same camera angle and framing, cinematic, 8K hyperrealistic
Output: ~/Movies/arm-fire-to-normal-transition/arm_fire_to_normal.mp4

Código: Normal a Líquido

Doc: arm-liquid-transition.md · Script: generate_arm_transition.py
import os
import time
import base64
import subprocess
import requests
import jwt  # pip install PyJWT

ACCESS_KEY = os.getenv("KLING_ACCESS_KEY")
SECRET_KEY = os.getenv("KLING_SECRET_KEY")
BASE_URL = "https://api.klingai.com"

START_IMAGE = "inicio.jpeg"
END_IMAGE = "paso2 - agua.png"
OUTPUT_DIR = os.path.expanduser("~/Movies/arm-liquid-transition")

TARGET_DURATION = 3.5  # segundos, recorte final con ffmpeg


def get_token() -> str:
    payload = {
        "iss": ACCESS_KEY,
        "exp": int(time.time()) + 1800,
        "nbf": int(time.time()) - 5,
    }
    return jwt.encode(payload, SECRET_KEY, algorithm="HS256")


def to_base64(path: str) -> str:
    with open(path, "rb") as f:
        return base64.b64encode(f.read()).decode()


def submit(start: str, end: str, prompt: str) -> str:
    headers = {
        "Authorization": f"Bearer {get_token()}",
        "Content-Type": "application/json",
    }
    payload = {
        "model_name": "kling-v1-6",
        "image": to_base64(start),
        "image_tail": to_base64(end),
        "prompt": prompt,
        "negative_prompt": (
            "blurry, low quality, artifacts, unnatural, body distortion outside "
            "the arm, face changes, background changes, camera movement"
        ),
        "cfg_scale": 0.8,
        "mode": "pro",
        "duration": "5",
    }
    resp = requests.post(f"{BASE_URL}/v1/videos/image2video", json=payload, headers=headers)
    resp.raise_for_status()
    task_id = resp.json()["data"]["task_id"]
    print(f"Task: {task_id}")
    return task_id


def poll(task_id: str, timeout: int = 300) -> str:
    headers = {"Authorization": f"Bearer {get_token()}"}
    deadline = time.time() + timeout
    while time.time() < deadline:
        resp = requests.get(
            f"{BASE_URL}/v1/videos/image2video/{task_id}",
            headers=headers,
        )
        resp.raise_for_status()
        result = resp.json()["data"]
        status = result["task_status"]
        print(f"  {status}...")
        if status == "succeed":
            return result["task_result"]["videos"][0]["url"]
        if status == "failed":
            raise RuntimeError(f"Failed: {result}")
        time.sleep(10)
    raise TimeoutError("Timed out")


def download(url: str, path: str):
    r = requests.get(url, stream=True)
    r.raise_for_status()
    with open(path, "wb") as f:
        for chunk in r.iter_content(8192):
            f.write(chunk)
    print(f"Saved: {path}")


def trim(input_path: str, output_path: str, duration: float):
    subprocess.run(
        [
            "ffmpeg", "-y", "-i", input_path,
            "-t", str(duration),
            "-c:v", "libx264", "-c:a", "aac",
            output_path,
        ],
        check=True,
    )
    print(f"Trimmed: {output_path}")


if __name__ == "__main__":
    os.makedirs(OUTPUT_DIR, exist_ok=True)

    prompt = (
        "the person's arm and hand gradually transform into flowing blue liquid, "
        "skin morphs into smooth glossy electric blue liquid with water-like "
        "ripples, droplets and surface highlights, photorealistic, same lighting, "
        "same tiled floor background, same camera angle and framing, "
        "only the arm changes, fluid dynamics, cinematic, 8K hyperrealistic"
    )

    task_id = submit(START_IMAGE, END_IMAGE, prompt)
    video_url = poll(task_id)

    full_output = os.path.join(OUTPUT_DIR, "arm_to_liquid_full.mp4")
    download(video_url, full_output)

    trimmed_output = os.path.join(OUTPUT_DIR, "arm_to_liquid.mp4")
    trim(full_output, trimmed_output, TARGET_DURATION)

    print(f"\nDone: {trimmed_output}")