Spaces:
Sleeping
Sleeping
File size: 7,132 Bytes
8f46cb8 cb6a767 8f46cb8 8e79ff0 8f46cb8 c4d4bda 8f46cb8 c4d4bda 041a66a 8f46cb8 cb6a767 041a66a 8f46cb8 cb6a767 041a66a 020c59d 041a66a c4d4bda 020c59d 8f46cb8 020c59d 041a66a cb6a767 041a66a cb6a767 041a66a cb6a767 041a66a cb6a767 041a66a 020c59d 041a66a 020c59d cb6a767 041a66a 020c59d 041a66a 8f46cb8 337364d 8f46cb8 acf2787 8f46cb8 cb6a767 337364d 8f46cb8 acf2787 8f46cb8 acf2787 8f46cb8 acf2787 8f46cb8 acf2787 8f46cb8 337364d 8f46cb8 cb6a767 acf2787 8f46cb8 acf2787 8f46cb8 acf2787 8f46cb8 337364d acf2787 8f46cb8 |
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 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 |
import gradio as gr
import os
import requests
import re
GROQ_API_KEY = os.environ.get("GROQ_API_KEY")
GROQ_API_URL = "https://api.groq.com/openai/v1/chat/completions"
MODEL_NAME = "llama-3.1-8b-instant"
SYSTEM_PROMPT = """You are 'ScriptForge AI', a professional YouTube Script Writer.
Your goal is to write highly engaging scripts in the 2nd person (using 'You', 'Your').
FORMATTING RULES (STRICT):
1. Use ONLY these two tags: [VISUAL] for scenes, and [AUDIO] for spoken words.
2. [VISUAL]: Describe the visuals, camera shots, or on-screen text.
3. [AUDIO]: Write ONLY the spoken words. No actor directions, no "Host:", no markdown headers.
4. Do not output any intro text. Start directly with a [VISUAL] or [AUDIO] tag.
5. Example:
[VISUAL]
Wide shot of a clear blue sky.
[AUDIO]
Today is going to be amazing.
"""
def parse_script(full_text):
full_text = re.sub(r'\[?(?:SCENE DESCRIPTION|SCENE|VISUALS)\]?:?', '[VISUAL]', full_text, flags=re.IGNORECASE)
full_text = re.sub(r'\[?(?:SCRIPT|NARRATION|AUDIO)\]?:?', '[AUDIO]', full_text, flags=re.IGNORECASE)
parts = re.split(r'(\[(?:VISUAL|AUDIO)\])', full_text, flags=re.IGNORECASE)
clean_audio = []
clean_visuals = []
current_tag = None
for part in parts:
part = part.strip()
if not part:
continue
if part.upper() == "[AUDIO]":
current_tag = "AUDIO"
elif part.upper() == "[VISUAL]":
current_tag = "VISUAL"
elif current_tag == "AUDIO":
content = re.sub(r'\(.*?\)', '', part, flags=re.DOTALL)
content = re.sub(r'^#+.*$', '', content, flags=re.MULTILINE)
content = re.sub(r'^\w+:\s*', '', content, flags=re.MULTILINE)
content = content.replace("**", "").replace("*", "")
if content.strip():
clean_audio.append(content.strip())
elif current_tag == "VISUAL":
clean_visuals.append(part.strip())
if not clean_audio and not clean_visuals:
lines = full_text.split('\n')
for line in lines:
line = line.strip()
if not line: continue
if line.startswith('(') or line.startswith('[') or "EXT." in line or "INT." in line:
clean_visuals.append(line)
else:
clean_audio.append(line)
return "\n\n".join(clean_audio), "\n\n".join(clean_visuals)
def save_to_file(script_text):
if not script_text:
return None
file_path = "youtube_script.txt"
with open(file_path, "w", encoding="utf-8") as f:
f.write(script_text)
return file_path
def query_groq(topic, tone, duration, hook_strength, chat_history):
if not GROQ_API_KEY:
return "Error: GROQ_API_KEY not found in environment secrets. Please add it in Settings > Secrets.", "", ""
headers = {
"Authorization": f"Bearer {GROQ_API_KEY}",
"Content-Type": "application/json"
}
user_input = f"Topic: {topic}\nTone: {tone}\nTarget Duration: {duration}\nAction: Write a full YouTube script."
messages = [{"role": "system", "content": SYSTEM_PROMPT}]
messages.extend(chat_history[-6:])
messages.append({"role": "user", "content": user_input})
try:
response = requests.post(GROQ_API_URL, headers=headers, json={
"model": MODEL_NAME,
"messages": messages,
"temperature": hook_strength
}, timeout=30)
if response.status_code == 200:
full_reply = response.json()["choices"][0]["message"]["content"]
tts_script, scenes = parse_script(full_reply)
return full_reply, tts_script, scenes
else:
return f"Error {response.status_code}: {response.text}", "", ""
except Exception as e:
return f"Request failed: {str(e)}", "", ""
css = """
footer {visibility: hidden}
"""
with gr.Blocks() as demo:
gr.Markdown("# 🎬 ScriptForge AI: YouTube Script Master")
gr.Markdown("Transform your video ideas into high-retention, audience-first scripts. *Powered by GROQ*")
with gr.Row():
with gr.Column(scale=1):
topic = gr.Textbox(label="Video Topic/Description", placeholder="E.g., How to build a PC in 2025", lines=3)
tone = gr.Dropdown(
choices=["High Energy", "Storytelling", "Educational", "Minimalist", "Aggressive/Hype"],
label="Video Tone",
value="High Energy"
)
duration = gr.Dropdown(
choices=["Shorts (<60s)", "Standard (5-10 mins)", "Deep Dive (15+ mins)"],
label="Target Duration",
value="Standard (5-10 mins)"
)
hook_strength = gr.Slider(minimum=0.1, maximum=1.5, value=0.7, step=0.1, label="Hook Strength (Creativity)")
generate_btn = gr.Button("🚀 Generate Script", variant="primary")
clear = gr.Button("Clear")
with gr.Column(scale=2):
with gr.Tabs():
with gr.TabItem("Combined View"):
chatbot = gr.Chatbot(
value=[{"role": "assistant", "content": "Hi, my name is Script Forge: your YouTube script writer. Give me a topic so I can show my creativity."}],
height=500
)
with gr.TabItem("TTS Only (Dialogue)"):
tts_output = gr.Textbox(label="Copy this for Text-to-Speech", lines=20)
download_btn = gr.Button("💾 Download Script (.txt)")
download_file = gr.File(label="Download prepared file")
with gr.TabItem("Visuals Only (Shot List)"):
scenes_output = gr.Textbox(label="Video Scene Descriptions", lines=20)
state = gr.State([{"role": "assistant", "content": "Hi, my name is Script Forge: your YouTube script writer. Give me a topic so I can show my creativity."}])
def respond_wrapper(topic, tone, duration, hook_strength, chat_history):
full_reply, tts_script, scenes = query_groq(topic, tone, duration, hook_strength, chat_history)
chat_history.append({"role": "user", "content": topic})
chat_history.append({"role": "assistant", "content": full_reply})
return chat_history, tts_script, scenes
generate_btn.click(
respond_wrapper,
[topic, tone, duration, hook_strength, state],
[chatbot, tts_output, scenes_output]
)
download_btn.click(
save_to_file,
[tts_output],
[download_file]
)
clear.click(
lambda: (None, [{"role": "assistant", "content": "Hi, my name is Script Forge: your YouTube script writer. Give me a topic so I can show my creativity."}], "", "", None),
None,
[topic, chatbot, tts_output, scenes_output, download_file]
)
if __name__ == "__main__":
demo.launch(theme=gr.themes.Soft(), css=css)
|