add video node

This commit is contained in:
pk
2026-03-10 14:16:27 +01:00
parent 854297d3d7
commit 534349298c
3 changed files with 53 additions and 23 deletions
+32 -15
View File
@@ -325,6 +325,7 @@ class AQ_Gemini_acstep15:
"STRING",
"STRING",
"STRING",
"STRING",
)
RETURN_NAMES = (
"response_json",
@@ -342,6 +343,7 @@ class AQ_Gemini_acstep15:
"headline",
"yt_description",
"yt_tags",
"yt_title",
"instruction",
)
FUNCTION = "generate"
@@ -354,7 +356,7 @@ class AQ_Gemini_acstep15:
"- Uses Google Gemini API."
)
if not gemini_api_key:
return ("", "", "", seed, 120, 120.0, "4", "en", "C major", "", "", "", "", "", "", instruction)
return ("", "", "", seed, 120, 120.0, "4", "en", "C major", "", "", "", "", "", "", "", instruction)
json_schema = {
"type": "object",
@@ -373,7 +375,8 @@ class AQ_Gemini_acstep15:
"tagline",
"headline",
"yt_description",
"yt_tags"
"yt_tags",
"yt_title"
],
"properties": {
"description": {"type": "string"},
@@ -390,7 +393,8 @@ class AQ_Gemini_acstep15:
"tagline": {"type": "string", "description": "Catchy subtitle, 5-12 words, for YouTube thumbnail."},
"headline": {"type": "string", "description": "Bold thumbnail primary text, 3-8 words, under 30 chars."},
"yt_description": {"type": "string", "description": "Full YouTube video description: 2-4 paragraphs covering mood, story, genre, and a lyrics/credits section."},
"yt_tags": {"type": "string", "description": "Comma-separated YouTube search tags, 10-20 keywords covering genre, mood, instruments, era, and use-case (e.g. study music, workout)."}
"yt_tags": {"type": "string", "description": "Comma-separated YouTube search tags, 10-20 keywords covering genre, mood, instruments, era, and use-case (e.g. study music, workout)."},
"yt_title": {"type": "string", "description": "SEO-optimised YouTube video title. Use a keyword-rich format with a pipe or colon separator, e.g. 'Focus Music | Minimalist Ambient Drone for Deep Work' or 'Deep Sleep Music | Gentle Soundscapes for Insomnia Relief'. Include the primary use-case, mood, and one or two distinctive sonic elements. 60-100 characters."}
}
}
@@ -498,6 +502,7 @@ class AQ_Gemini_acstep15:
headline = str(json_response.get("headline", ""))
yt_description = str(json_response.get("yt_description", ""))
yt_tags = str(json_response.get("yt_tags", ""))
yt_title = str(json_response.get("yt_title", ""))
response_json = json.dumps({
"description": json_response.get("description", ""),
@@ -514,10 +519,11 @@ class AQ_Gemini_acstep15:
"tagline": tagline,
"headline": headline,
"yt_description": yt_description,
"yt_tags": yt_tags
"yt_tags": yt_tags,
"yt_title": yt_title
})
return (response_json, tags, lyrics, seed_value, bpm, duration, timesignature, language, keyscale, coverImage, title, tagline, headline, yt_description, yt_tags, instruction)
return (response_json, tags, lyrics, seed_value, bpm, duration, timesignature, language, keyscale, coverImage, title, tagline, headline, yt_description, yt_tags, yt_title, instruction)
except Exception as e:
print(f"Error in Gemini API: {str(e)}")
raise e
@@ -627,6 +633,7 @@ class AQ_OpenAI_acstep15:
"STRING",
"STRING",
"STRING",
"STRING",
)
RETURN_NAMES = (
"response_json",
@@ -644,6 +651,7 @@ class AQ_OpenAI_acstep15:
"headline",
"yt_description",
"yt_tags",
"yt_title",
"instruction",
)
FUNCTION = "generate"
@@ -661,7 +669,7 @@ class AQ_OpenAI_acstep15:
"- tags/lyrics/bpm/duration/timesignature/language/keyscale/coverImage/title/tagline/headline/yt_description/yt_tags are parsed from JSON."
)
if not openai_api_key:
return ("", "", "", seed, 120, 120.0, "4", "en", "C major", "", "", "", "", "", "", instruction)
return ("", "", "", seed, 120, 120.0, "4", "en", "C major", "", "", "", "", "", "", "", instruction)
schema = {
"type": "object",
@@ -743,7 +751,8 @@ class AQ_OpenAI_acstep15:
"tagline",
"headline",
"yt_description",
"yt_tags"
"yt_tags",
"yt_title"
],
"additionalProperties": False
}
@@ -860,6 +869,7 @@ class AQ_OpenAI_acstep15:
headline = str(json_response.get("headline", ""))
yt_description = str(json_response.get("yt_description", ""))
yt_tags = str(json_response.get("yt_tags", ""))
yt_title = str(json_response.get("yt_title", ""))
response_json = json.dumps({
"tags": tags,
@@ -875,10 +885,11 @@ class AQ_OpenAI_acstep15:
"tagline": tagline,
"headline": headline,
"yt_description": yt_description,
"yt_tags": yt_tags
"yt_tags": yt_tags,
"yt_title": yt_title
})
return (response_json, tags, lyrics, seed_value, bpm, duration, timesignature, language, keyscale, coverImage, title, tagline, headline, yt_description, yt_tags, instruction)
return (response_json, tags, lyrics, seed_value, bpm, duration, timesignature, language, keyscale, coverImage, title, tagline, headline, yt_description, yt_tags, yt_title, instruction)
except Exception as e:
print(f"Error in OpenAI API: {str(e)}")
raise e
@@ -926,7 +937,7 @@ class AQ_OpenAI_Compatible_acstep15(AQ_OpenAI_acstep15):
"- Allows custom API endpoint."
)
if not openai_api_key:
return ("", "", "", seed, 120, 120.0, "4", "en", "C major", "", "", "", "", "", "", instruction)
return ("", "", "", seed, 120, 120.0, "4", "en", "C major", "", "", "", "", "", "", "", instruction)
model_name = custom_model if model == "custom" and custom_model else model
@@ -975,9 +986,10 @@ class AQ_OpenAI_Compatible_acstep15(AQ_OpenAI_acstep15):
"tagline": {"type": "string", "description": "Catchy subtitle, 5-12 words, for YouTube thumbnail."},
"headline": {"type": "string", "description": "Bold thumbnail primary text, 3-8 words, under 30 chars."},
"yt_description": {"type": "string", "description": "Full YouTube video description: 2-4 paragraphs covering mood, story, genre, and a lyrics/credits section."},
"yt_tags": {"type": "string", "description": "Comma-separated YouTube search tags, 10-20 keywords covering genre, mood, instruments, era, and use-case (e.g. study music, workout)."}
"yt_tags": {"type": "string", "description": "Comma-separated YouTube search tags, 10-20 keywords covering genre, mood, instruments, era, and use-case (e.g. study music, workout)."},
"yt_title": {"type": "string", "description": "SEO-optimised YouTube video title. Use a keyword-rich format with a pipe or colon separator, e.g. 'Focus Music | Minimalist Ambient Drone for Deep Work' or 'Deep Sleep Music | Gentle Soundscapes for Insomnia Relief'. Include the primary use-case, mood, and one or two distinctive sonic elements. 60-100 characters."}
},
"required": ["tags", "lyrics", "seed", "bpm", "keyscale", "durationSeconds", "timesignature", "language", "coverImage", "title", "tagline", "headline", "yt_description", "yt_tags"],
"required": ["tags", "lyrics", "seed", "bpm", "keyscale", "durationSeconds", "timesignature", "language", "coverImage", "title", "tagline", "headline", "yt_description", "yt_tags", "yt_title"],
"additionalProperties": False
}
@@ -1043,6 +1055,7 @@ class AQ_OpenAI_Compatible_acstep15(AQ_OpenAI_acstep15):
headline = str(json_response.get("headline", ""))
yt_description = str(json_response.get("yt_description", ""))
yt_tags = str(json_response.get("yt_tags", ""))
yt_title = str(json_response.get("yt_title", ""))
response_json = json.dumps({
"tags": tags,
@@ -1058,10 +1071,11 @@ class AQ_OpenAI_Compatible_acstep15(AQ_OpenAI_acstep15):
"tagline": tagline,
"headline": headline,
"yt_description": yt_description,
"yt_tags": yt_tags
"yt_tags": yt_tags,
"yt_title": yt_title
})
return (response_json, tags, lyrics, seed_value, bpm, duration, timesignature, language, keyscale, coverImage, title, tagline, headline, yt_description, yt_tags, instruction)
return (response_json, tags, lyrics, seed_value, bpm, duration, timesignature, language, keyscale, coverImage, title, tagline, headline, yt_description, yt_tags, yt_title, instruction)
except Exception as e:
print(f"Error in API: {str(e)}")
raise e
@@ -1091,6 +1105,7 @@ class AQ_Parse_JSON_to_acestep:
"STRING",
"STRING",
"STRING",
"STRING",
)
RETURN_NAMES = (
"response_json",
@@ -1108,6 +1123,7 @@ class AQ_Parse_JSON_to_acestep:
"headline",
"yt_description",
"yt_tags",
"yt_title",
)
FUNCTION = "parse"
CATEGORY = "Aquasite/LLM"
@@ -1144,8 +1160,9 @@ class AQ_Parse_JSON_to_acestep:
headline = str(data.get("headline", ""))
yt_description = str(data.get("yt_description", ""))
yt_tags = str(data.get("yt_tags", ""))
yt_title = str(data.get("yt_title", ""))
return (json_text, tags, lyrics, seed, bpm, duration, timesignature, language, keyscale, coverImage, title, tagline, headline, yt_description, yt_tags)
return (json_text, tags, lyrics, seed, bpm, duration, timesignature, language, keyscale, coverImage, title, tagline, headline, yt_description, yt_tags, yt_title)
except Exception as e:
print(f"Error parsing JSON in AQ_Parse_JSON_to_acestep: {str(e)}")
raise e
+14 -7
View File
@@ -105,7 +105,7 @@ class AQ_StillImageToVideo:
# ------------------------------------------------------------------
def _encode(self, encoder_choice, img_path, wav_path, output_path, fps, meta_args):
"""Run ffmpeg, trying GPU first when encoder is 'auto'."""
"""Run ffmpeg. Named GPU encoders fall back to CPU on failure."""
_GPU_CODECS = {
"nvidia (h264_nvenc)": self._nvenc_args,
@@ -119,22 +119,29 @@ class AQ_StillImageToVideo:
self._run(cmd)
elif encoder_choice in _GPU_CODECS:
codec_args = _GPU_CODECS[encoder_choice]()
# Try the requested GPU encoder; fall back to CPU on failure
cmd = self._build_cmd(img_path, wav_path, output_path, fps,
codec_args, meta_args)
_GPU_CODECS[encoder_choice](), meta_args)
result = subprocess.run(cmd, capture_output=True, text=True, timeout=6000)
if result.returncode == 0:
return
last_line = result.stderr.splitlines()[-1] if result.stderr else ""
print(f"[AQ_StillImageToVideo] {encoder_choice} failed: {last_line}")
print("[AQ_StillImageToVideo] Falling back to CPU (libx264 ultrafast)")
cmd = self._build_cmd(img_path, wav_path, output_path, fps,
self._cpu_args(), meta_args)
self._run(cmd)
else: # auto: try GPU codecs in order, fall back to CPU
else: # auto: try all GPU codecs in order, fall back to CPU
for codec_fn in (self._nvenc_args, self._amf_args, self._qsv_args):
cmd = self._build_cmd(img_path, wav_path, output_path, fps,
codec_fn(), meta_args)
result = subprocess.run(cmd, capture_output=True, text=True, timeout=6000)
if result.returncode == 0:
return
print(f"[AQ_StillImageToVideo] GPU encoder failed, trying next: "
f"{result.stderr.splitlines()[-1] if result.stderr else ''}")
last_line = result.stderr.splitlines()[-1] if result.stderr else ""
print(f"[AQ_StillImageToVideo] GPU encoder failed, trying next: {last_line}")
# CPU fallback
print("[AQ_StillImageToVideo] Falling back to CPU (libx264 ultrafast)")
cmd = self._build_cmd(img_path, wav_path, output_path, fps,
self._cpu_args(), meta_args)
+7 -1
View File
@@ -21,6 +21,11 @@ Return ONLY valid JSON with the following fields:
- **headline** — bold primary text for a YouTube thumbnail, 3–8 words, ideally under 30 characters, all-caps acceptable; this is the dominant visual text
- **yt_description** — full YouTube video description, 2–4 paragraphs: open with the mood/story, describe the sound and instrumentation, note key/BPM/genre for music discovery, close with a call-to-action or credits placeholder
- **yt_tags** — comma-separated YouTube search keywords, 10–20 tags; mix genre, mood, instruments, era, tempo descriptor, and use-case terms (e.g. "study music", "workout playlist", "relaxing beats")
- **yt_title** — SEO-optimised YouTube video title, 60–100 characters. Use a keyword-rich format with `|` or `:` as separator. Lead with the primary use-case or mood category, follow with the specific sonic identity. Examples by style:
- Focus/Study: `Focus Music | Minimalist Ambient Drone for Deep Work (No Distractions)`
- Sleep: `Deep Sleep Music | Gentle Ambient Soundscapes for Insomnia Relief`
- Meditation: `Pure Relaxation | Calming Piano Melodies for Inner Peace`
- General: `[Mood/Use-case] | [Instrument/Style] for [Activity/Feeling]`
## Consistency Guidelines
@@ -47,6 +52,7 @@ Return ONLY valid JSON with the following fields:
"tagline": "When the city lights blur your tears away",
"headline": "NEON RAIN",
"yt_description": "A melancholic synth-pop journey through rain-soaked city streets...\n\nFeaturing lush synthesizers, driving bass, and evocative vocals in G minor at 118 BPM.\n\n🎵 Stream & save | 💬 Share your thoughts below",
"yt_tags": "synth-pop, melancholic music, city vibes, night drive music, rainy day playlist, 80s inspired, synthwave, emotional music, study music, chill beats"
"yt_tags": "synth-pop, melancholic music, city vibes, night drive music, rainy day playlist, 80s inspired, synthwave, emotional music, study music, chill beats",
"yt_title": "Neon Rain | Melancholic Synth-Pop for Late Night City Drives"
}
```