Add video reverse-prompt support and keep original media

This commit is contained in:
wenjian
2025-12-13 00:14:29 +08:00
parent 4cd29f007e
commit 26e18d1a76
2 changed files with 97 additions and 15 deletions
+93 -15
View File
@@ -4,26 +4,83 @@ from PIL import Image
import numpy as np
import base64
import os
import io
def encode_image_b64(ref_image):
i = 255. * ref_image.cpu().numpy()[0]
"""
Encode ComfyUI IMAGE tensor to base64 JPEG without resizing.
Notes:
- Keep original resolution (no resize).
- Avoid temporary files (in-memory encoding).
"""
i = 255.0 * ref_image.cpu().numpy()[0]
img = Image.fromarray(np.clip(i, 0, 255).astype(np.uint8))
lsize = np.max(img.size)
factor = 1
while lsize / factor > 2048:
factor *= 2
img = img.resize((img.size[0] // factor, img.size[1] // factor))
buf = io.BytesIO()
# Use JPEG to match the existing OpenAI-compatible payload mime label.
img.save(buf, format="JPEG", quality=95, optimize=True)
return base64.b64encode(buf.getvalue()).decode("utf-8")
image_path = f'{time.time()}.webp'
img.save(image_path, 'WEBP')
with open(image_path, "rb") as image_file:
base64_image = base64.b64encode(image_file.read()).decode('utf-8')
def _get_video_file_path(video):
"""
Try to extract a filesystem path from a ComfyUI VIDEO object.
Returns None if it cannot be resolved.
"""
# VideoFromFile type (private attribute)
if hasattr(video, "_VideoFromFile__file"):
path = getattr(video, "_VideoFromFile__file", None)
if isinstance(path, str) and os.path.exists(path):
return path
# print(img_base64)
os.remove(image_path)
return base64_image
# Stream-like sources
if hasattr(video, "get_stream_source"):
try:
stream_source = video.get_stream_source()
if isinstance(stream_source, str) and os.path.exists(stream_source):
return stream_source
except Exception:
pass
# Common attributes
for attr in ("path", "file"):
if hasattr(video, attr):
path = getattr(video, attr, None)
if isinstance(path, str) and os.path.exists(path):
return path
return None
def encode_video_b64(video):
"""
Encode ComfyUI VIDEO object to base64 MP4 bytes.
Notes:
- No ffmpeg processing, no compression, no resizing.
- If a file path is available, read it directly.
- Otherwise, try saving via save_to() to a temp mp4 and read back.
"""
video_path = _get_video_file_path(video)
if video_path:
with open(video_path, "rb") as f:
return base64.b64encode(f.read()).decode("utf-8")
if hasattr(video, "save_to"):
temp_path = f"temp_video_{time.time()}.mp4"
try:
video.save_to(temp_path)
with open(temp_path, "rb") as f:
return base64.b64encode(f.read()).decode("utf-8")
finally:
try:
if os.path.exists(temp_path):
os.remove(temp_path)
except Exception:
pass
raise ValueError(f"Unable to read video data from object type: {type(video)}")
class RH_LLMAPI_Node():
@@ -44,6 +101,7 @@ class RH_LLMAPI_Node():
},
"optional": {
"ref_image": ("IMAGE",),
"video": ("VIDEO",),
}
}
@@ -52,10 +110,30 @@ class RH_LLMAPI_Node():
FUNCTION = "rh_run_llmapi"
CATEGORY = "Runninghub"
def rh_run_llmapi(self, api_baseurl, api_key, model, role, prompt, temperature, seed, ref_image=None):
def rh_run_llmapi(self, api_baseurl, api_key, model, role, prompt, temperature, seed, ref_image=None, video=None):
client = OpenAI(api_key=api_key, base_url=api_baseurl)
if ref_image is None:
# Priority: video > image > text (align with reference node behavior)
if video is not None:
base64_video = encode_video_b64(video)
messages = [
{'role': 'system', 'content': f'{role}'},
{'role': 'user',
'content': [
{
"type": "text",
"text": f"{prompt}"
},
{
"type": "video_url",
"video_url": {
"url": f"data:video/mp4;base64,{base64_video}"
}
},
]},
]
elif ref_image is None:
messages = [
{'role': 'system', 'content': f'{role}'},
{'role': 'user', 'content': f'{prompt}'},