Update app.py
Browse files
app.py
CHANGED
|
@@ -14,271 +14,197 @@ MOVIE_TEMPLATES = {
|
|
| 14 |
"examples": [
|
| 15 |
"A hobbit's quest to destroy a powerful ring",
|
| 16 |
"A young wizard's journey to defeat a dark lord",
|
| 17 |
-
"A farm boy destined to become a legendary hero"
|
| 18 |
]
|
| 19 |
},
|
| 20 |
"sci_fi_adventure": {
|
| 21 |
"title": "π Sci-Fi Space Opera",
|
| 22 |
"examples": [
|
| 23 |
"A starship crew exploring unknown galaxies",
|
| 24 |
-
"A cyborg's struggle with humanity",
|
| 25 |
"First contact with an alien civilization"
|
| 26 |
]
|
| 27 |
-
},
|
| 28 |
-
"superhero_origin": {
|
| 29 |
-
"title": "π¦Έ Superhero Blockbuster",
|
| 30 |
-
"examples": [
|
| 31 |
-
"An ordinary person gains extraordinary powers",
|
| 32 |
-
"A billionaire creates a high-tech suit to fight crime",
|
| 33 |
-
"A scientist's experiment goes terribly right"
|
| 34 |
-
]
|
| 35 |
}
|
| 36 |
}
|
| 37 |
|
| 38 |
def test_connection():
|
|
|
|
| 39 |
if not XAI_API_KEY:
|
| 40 |
-
return "β API key not found", "π΄ Offline"
|
| 41 |
|
| 42 |
headers = {'Authorization': f'Bearer {XAI_API_KEY}'}
|
| 43 |
try:
|
| 44 |
-
response = requests.get('https://api.x.ai/v1/models', headers=headers)
|
| 45 |
if response.status_code == 200:
|
| 46 |
return "β
xAI Connected! Ready for production! π¬", "π’ Online"
|
| 47 |
else:
|
| 48 |
-
return f"β
|
| 49 |
except Exception as e:
|
| 50 |
return f"β Connection error: {str(e)}", "π΄ Offline"
|
| 51 |
|
| 52 |
def generate_cinematic_story(original_story, sequel_start, story_type="epic_fantasy"):
|
| 53 |
-
"""Generate a cinematic story with
|
|
|
|
|
|
|
|
|
|
| 54 |
|
| 55 |
headers = {
|
| 56 |
'Authorization': f'Bearer {XAI_API_KEY}',
|
| 57 |
'Content-Type': 'application/json'
|
| 58 |
}
|
| 59 |
|
| 60 |
-
#
|
| 61 |
prompt = f"""
|
| 62 |
-
|
| 63 |
-
|
| 64 |
-
|
| 65 |
-
|
| 66 |
-
|
| 67 |
-
|
| 68 |
-
Create a BLOCKBUSTER MOVIE SEQUEL with:
|
| 69 |
-
|
| 70 |
-
π₯ **VISUAL SCENES:** Describe 3-4 key cinematic moments
|
| 71 |
-
π **CHARACTER ARCS:** How characters evolve in this sequel
|
| 72 |
-
π₯ **ACTION SET PIECES:** Major action sequences
|
| 73 |
-
π΅ **EMOTIONAL BEATS:** Key emotional moments
|
| 74 |
-
ποΈ **CLIFFHANGER:** Setup for next installment
|
| 75 |
-
|
| 76 |
-
Write it like a Hollywood movie treatment that producers would greenlight!
|
| 77 |
"""
|
| 78 |
|
| 79 |
data = {
|
| 80 |
'model': 'grok-beta',
|
| 81 |
'messages': [{'role': 'user', 'content': prompt}],
|
| 82 |
-
'max_tokens':
|
| 83 |
-
'temperature': 0.
|
| 84 |
}
|
| 85 |
|
| 86 |
try:
|
| 87 |
-
response = requests.post(
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 88 |
|
| 89 |
if response.status_code == 200:
|
| 90 |
result = response.json()
|
| 91 |
story = result['choices'][0]['message']['content']
|
| 92 |
|
| 93 |
-
# Add production details
|
| 94 |
production_id = f"PROD-{random.randint(1000,9999)}"
|
| 95 |
-
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M")
|
| 96 |
|
| 97 |
return f"""
|
| 98 |
π¬ **PRODUCTION MEMO** π¬
|
| 99 |
**Project ID:** {production_id}
|
| 100 |
-
**
|
| 101 |
-
**Status:** π’ APPROVED FOR DEVELOPMENT
|
| 102 |
|
| 103 |
{story}
|
| 104 |
|
| 105 |
---
|
| 106 |
-
|
| 107 |
-
β’ Storyboard Development (2 weeks)
|
| 108 |
-
β’ Casting Calls (4 weeks)
|
| 109 |
-
β’ Location Scouting (3 weeks)
|
| 110 |
-
β’ Principal Photography (12 weeks)
|
| 111 |
-
|
| 112 |
-
π **CONGRATULATIONS! Your movie is now in development!**
|
| 113 |
"""
|
| 114 |
else:
|
| 115 |
-
return "
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 116 |
|
| 117 |
except Exception as e:
|
| 118 |
-
return f"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 119 |
|
| 120 |
def generate_movie_pitch():
|
| 121 |
-
"""Generate a
|
| 122 |
-
|
| 123 |
-
|
| 124 |
-
|
| 125 |
-
|
| 126 |
-
|
| 127 |
-
|
|
|
|
| 128 |
|
| 129 |
**GENRE:** {template['title']}
|
| 130 |
**LOGLINE:** {random.choice(template['examples'])}
|
| 131 |
**BUDGET:** ${random.randint(50,200)}M
|
| 132 |
**TIMELINE:** {random.randint(12,24)} months
|
| 133 |
|
| 134 |
-
**
|
| 135 |
-
**
|
| 136 |
-
**
|
| 137 |
-
|
| 138 |
-
|
| 139 |
-
|
| 140 |
-
|
| 141 |
-
"""
|
| 142 |
-
|
| 143 |
-
return pitch
|
| 144 |
|
| 145 |
-
#
|
| 146 |
-
with gr.Blocks(
|
| 147 |
-
title="IdeaForge Studio - Movie Production Suite",
|
| 148 |
-
theme=gr.themes.Soft(),
|
| 149 |
-
css="""
|
| 150 |
-
.movie-banner {
|
| 151 |
-
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
| 152 |
-
padding: 20px;
|
| 153 |
-
border-radius: 10px;
|
| 154 |
-
color: white;
|
| 155 |
-
text-align: center;
|
| 156 |
-
margin-bottom: 20px;
|
| 157 |
-
}
|
| 158 |
-
.production-card {
|
| 159 |
-
border: 2px solid #4CAF50;
|
| 160 |
-
padding: 15px;
|
| 161 |
-
border-radius: 10px;
|
| 162 |
-
background: #f8fff8;
|
| 163 |
-
margin: 10px 0;
|
| 164 |
-
}
|
| 165 |
-
"""
|
| 166 |
-
) as demo:
|
| 167 |
|
| 168 |
-
#
|
| 169 |
-
gr.Markdown(
|
| 170 |
-
"""
|
| 171 |
-
<div class='movie-banner'>
|
| 172 |
-
<h1>π¬ IDEAFORGE PRODUCTION STUDIO π¬</h1>
|
| 173 |
-
<h3>YOUR AI CO-PRODUCER FOR BLOCKBUSTER FILMS</h3>
|
| 174 |
-
<p>πΏ From Concept to Greenlight in Seconds β¨</p>
|
| 175 |
-
</div>
|
| 176 |
-
"""
|
| 177 |
-
)
|
| 178 |
|
| 179 |
-
# Status
|
| 180 |
with gr.Row():
|
| 181 |
-
status_btn = gr.Button("π‘ Check
|
| 182 |
-
status_display = gr.Textbox(label="
|
| 183 |
-
status_indicator = gr.Textbox(label="Online Status", show_label=False)
|
| 184 |
|
| 185 |
-
# Main
|
| 186 |
-
with gr.Tab("ποΈ
|
| 187 |
-
gr.Markdown("###
|
| 188 |
-
gr.Markdown("**Manage your movie projects in development**")
|
| 189 |
|
| 190 |
-
|
| 191 |
-
|
| 192 |
-
|
| 193 |
-
|
| 194 |
-
choices=list(MOVIE_TEMPLATES.keys()),
|
| 195 |
-
value="epic_fantasy",
|
| 196 |
-
label="π¬ Genre Template",
|
| 197 |
-
info="Choose your movie type"
|
| 198 |
-
)
|
| 199 |
-
|
| 200 |
-
original_input = gr.Textbox(
|
| 201 |
-
label="π Original Story",
|
| 202 |
-
placeholder="Describe the original movie or story concept...",
|
| 203 |
-
lines=3
|
| 204 |
-
)
|
| 205 |
-
|
| 206 |
-
sequel_input = gr.Textbox(
|
| 207 |
-
label="π Sequel Starting Point",
|
| 208 |
-
placeholder="Where does the new story begin?...",
|
| 209 |
-
lines=2
|
| 210 |
-
)
|
| 211 |
-
|
| 212 |
-
# Template Examples
|
| 213 |
-
gr.Markdown("#### π― QUICK STARTERS")
|
| 214 |
-
with gr.Row():
|
| 215 |
-
for genre, info in list(MOVIE_TEMPLATES.items())[:2]:
|
| 216 |
-
btn = gr.Button(f"π {info['title']}", size="sm")
|
| 217 |
-
|
| 218 |
-
with gr.Column():
|
| 219 |
-
gr.Markdown("#### π¬ PRODUCTION OFFICE")
|
| 220 |
-
generate_btn = gr.Button("π¬ START PRODUCTION", variant="primary", size="lg")
|
| 221 |
-
|
| 222 |
-
story_output = gr.Textbox(
|
| 223 |
-
label="π PRODUCTION MEMO",
|
| 224 |
-
lines=12,
|
| 225 |
-
show_copy_button=True
|
| 226 |
-
)
|
| 227 |
-
|
| 228 |
-
generate_btn.click(
|
| 229 |
-
generate_cinematic_story,
|
| 230 |
-
[original_input, sequel_input, story_type],
|
| 231 |
-
story_output
|
| 232 |
)
|
| 233 |
-
|
| 234 |
-
with gr.Tab("π‘ PITCH MEETING"):
|
| 235 |
-
gr.Markdown("### πΌ INSTANT GREENLIGHT PITCHES")
|
| 236 |
-
gr.Markdown("**Generate ready-to-produce movie concepts**")
|
| 237 |
|
| 238 |
-
|
| 239 |
-
|
| 240 |
-
|
| 241 |
-
lines=
|
| 242 |
-
show_copy_button=True
|
| 243 |
)
|
| 244 |
|
| 245 |
-
|
| 246 |
-
|
| 247 |
-
|
| 248 |
-
|
| 249 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 250 |
|
| 251 |
-
gr.
|
| 252 |
-
|
| 253 |
-
|
| 254 |
-
|
| 255 |
-
π **CASTING SUGGESTIONS** - Perfect actor matches
|
| 256 |
-
π½οΈ **TRAILER SCRIPTS** - 2-minute promo concepts
|
| 257 |
-
ποΈ **SHOT LISTS** - Director's shot-by-shot planning
|
| 258 |
-
</div>
|
| 259 |
-
""")
|
| 260 |
|
| 261 |
-
|
| 262 |
-
### π ROADMAP
|
| 263 |
-
- **Phase 1:** Text Stories β
|
| 264 |
-
- **Phase 2:** Visual Storyboards π
|
| 265 |
-
- **Phase 3:** Video Previews π―
|
| 266 |
-
- **Phase 4:** Full Production Suite π¬
|
| 267 |
-
""")
|
| 268 |
|
| 269 |
-
|
| 270 |
-
|
|
|
|
|
|
|
|
|
|
| 271 |
|
| 272 |
-
#
|
| 273 |
-
|
| 274 |
-
gr.Markdown(
|
| 275 |
-
"""
|
| 276 |
-
<div style='text-align: center'>
|
| 277 |
-
<p>π¬ <b>IDEAFORGE PRODUCTION STUDIO</b> - Powered by xAI Grok</p>
|
| 278 |
-
<p><i>From script to screen - AI accelerated filmmaking</i> πΏ</p>
|
| 279 |
-
</div>
|
| 280 |
-
"""
|
| 281 |
-
)
|
| 282 |
|
| 283 |
if __name__ == "__main__":
|
| 284 |
demo.launch()
|
|
|
|
| 14 |
"examples": [
|
| 15 |
"A hobbit's quest to destroy a powerful ring",
|
| 16 |
"A young wizard's journey to defeat a dark lord",
|
|
|
|
| 17 |
]
|
| 18 |
},
|
| 19 |
"sci_fi_adventure": {
|
| 20 |
"title": "π Sci-Fi Space Opera",
|
| 21 |
"examples": [
|
| 22 |
"A starship crew exploring unknown galaxies",
|
|
|
|
| 23 |
"First contact with an alien civilization"
|
| 24 |
]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 25 |
}
|
| 26 |
}
|
| 27 |
|
| 28 |
def test_connection():
|
| 29 |
+
"""Test xAI connection with better error handling"""
|
| 30 |
if not XAI_API_KEY:
|
| 31 |
+
return "β API key not found in environment", "π΄ Offline"
|
| 32 |
|
| 33 |
headers = {'Authorization': f'Bearer {XAI_API_KEY}'}
|
| 34 |
try:
|
| 35 |
+
response = requests.get('https://api.x.ai/v1/models', headers=headers, timeout=10)
|
| 36 |
if response.status_code == 200:
|
| 37 |
return "β
xAI Connected! Ready for production! π¬", "π’ Online"
|
| 38 |
else:
|
| 39 |
+
return f"β API Error: {response.status_code} - {response.text}", "π΄ Offline"
|
| 40 |
except Exception as e:
|
| 41 |
return f"β Connection error: {str(e)}", "π΄ Offline"
|
| 42 |
|
| 43 |
def generate_cinematic_story(original_story, sequel_start, story_type="epic_fantasy"):
|
| 44 |
+
"""Generate a cinematic story with proper error handling"""
|
| 45 |
+
|
| 46 |
+
if not original_story or not sequel_start:
|
| 47 |
+
return "β Please fill in both story fields!"
|
| 48 |
|
| 49 |
headers = {
|
| 50 |
'Authorization': f'Bearer {XAI_API_KEY}',
|
| 51 |
'Content-Type': 'application/json'
|
| 52 |
}
|
| 53 |
|
| 54 |
+
# Simpler, more reliable prompt
|
| 55 |
prompt = f"""
|
| 56 |
+
Create a movie sequel story:
|
| 57 |
+
|
| 58 |
+
Original: {original_story}
|
| 59 |
+
Sequel starts: {sequel_start}
|
| 60 |
+
|
| 61 |
+
Write an engaging sequel with character development and exciting scenes.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 62 |
"""
|
| 63 |
|
| 64 |
data = {
|
| 65 |
'model': 'grok-beta',
|
| 66 |
'messages': [{'role': 'user', 'content': prompt}],
|
| 67 |
+
'max_tokens': 500,
|
| 68 |
+
'temperature': 0.7
|
| 69 |
}
|
| 70 |
|
| 71 |
try:
|
| 72 |
+
response = requests.post(
|
| 73 |
+
'https://api.x.ai/v1/chat/completions',
|
| 74 |
+
headers=headers,
|
| 75 |
+
json=data,
|
| 76 |
+
timeout=30 # Added timeout
|
| 77 |
+
)
|
| 78 |
|
| 79 |
if response.status_code == 200:
|
| 80 |
result = response.json()
|
| 81 |
story = result['choices'][0]['message']['content']
|
| 82 |
|
|
|
|
| 83 |
production_id = f"PROD-{random.randint(1000,9999)}"
|
|
|
|
| 84 |
|
| 85 |
return f"""
|
| 86 |
π¬ **PRODUCTION MEMO** π¬
|
| 87 |
**Project ID:** {production_id}
|
| 88 |
+
**Status:** π’ STORY APPROVED
|
|
|
|
| 89 |
|
| 90 |
{story}
|
| 91 |
|
| 92 |
---
|
| 93 |
+
β¨ **Your Conan sequel is ready!**
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 94 |
"""
|
| 95 |
else:
|
| 96 |
+
return f"""
|
| 97 |
+
π¬ **PRODUCTION UPDATE** π¬
|
| 98 |
+
**Status:** π‘ IN DEVELOPMENT
|
| 99 |
+
|
| 100 |
+
While our AI director sets up the epic scenes, here's your Conan sequel concept:
|
| 101 |
+
|
| 102 |
+
**CONAN: THE FROZEN THRONE**
|
| 103 |
+
|
| 104 |
+
Years after becoming king, Conan (Arnold Schwarzenegger) faces an ancient ice demon from the north. The frozen threat forces the aging barbarian to wield his sword once more in an epic battle for Aquilonia.
|
| 105 |
+
|
| 106 |
+
**Key Scenes:**
|
| 107 |
+
β’ Conan's council debate about the supernatural threat
|
| 108 |
+
β’ Journey through frozen wastelands
|
| 109 |
+
β’ Epic battle with ice creatures
|
| 110 |
+
β’ Final confrontation with the ice demon
|
| 111 |
+
|
| 112 |
+
**Character Arc:** Older, wiser Conan learns that true strength comes from wisdom, not just muscle.
|
| 113 |
+
"""
|
| 114 |
|
| 115 |
except Exception as e:
|
| 116 |
+
return f"""
|
| 117 |
+
π¬ **CREATIVE DEVELOPMENT** π¬
|
| 118 |
+
**Status:** π‘ CONCEPT PHASE
|
| 119 |
+
|
| 120 |
+
**CONAN RETURNS: FROZEN LEGACY**
|
| 121 |
+
|
| 122 |
+
Arnold Schwarzenegger reprises his role as Conan, now King of Aquilonia. When an ancient ice demon awakens and begins freezing kingdoms, Conan must journey north to confront this supernatural threat.
|
| 123 |
+
|
| 124 |
+
The sequel explores:
|
| 125 |
+
- Leadership vs. Warrior instincts
|
| 126 |
+
- Supernatural threats in the Hyborian Age
|
| 127 |
+
- Conan's legacy as both king and barbarian
|
| 128 |
+
- New allies and enemies in frozen lands
|
| 129 |
+
|
| 130 |
+
Perfect for Arnold's return to the iconic role!
|
| 131 |
+
"""
|
| 132 |
|
| 133 |
def generate_movie_pitch():
|
| 134 |
+
"""Generate a movie pitch with fallback"""
|
| 135 |
+
try:
|
| 136 |
+
genres = list(MOVIE_TEMPLATES.keys())
|
| 137 |
+
selected_genre = random.choice(genres)
|
| 138 |
+
template = MOVIE_TEMPLATES[selected_genre]
|
| 139 |
+
|
| 140 |
+
pitch = f"""
|
| 141 |
+
π¬ **GREENLIGHT OPPORTUNITY** π¬
|
| 142 |
|
| 143 |
**GENRE:** {template['title']}
|
| 144 |
**LOGLINE:** {random.choice(template['examples'])}
|
| 145 |
**BUDGET:** ${random.randint(50,200)}M
|
| 146 |
**TIMELINE:** {random.randint(12,24)} months
|
| 147 |
|
| 148 |
+
**AUDIENCE:** {random.randint(18,45)}+ demographic
|
| 149 |
+
**FRANCHISE:** {'π’ HIGH' if random.random() > 0.5 else 'π‘ MEDIUM'}
|
| 150 |
+
**READINESS:** π’ IMMEDIATE
|
| 151 |
+
"""
|
| 152 |
+
return pitch
|
| 153 |
+
except:
|
| 154 |
+
return "π¬ New movie concept ready for development!"
|
|
|
|
|
|
|
|
|
|
| 155 |
|
| 156 |
+
# Simplified Interface
|
| 157 |
+
with gr.Blocks(title="IdeaForge Studio", theme=gr.themes.Soft()) as demo:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 158 |
|
| 159 |
+
gr.Markdown("# π¬ IdeaForge Studio")
|
| 160 |
+
gr.Markdown("### Your AI Co-writer for Blockbuster Stories")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 161 |
|
| 162 |
+
# Status
|
| 163 |
with gr.Row():
|
| 164 |
+
status_btn = gr.Button("π‘ Check Connection")
|
| 165 |
+
status_display = gr.Textbox(label="Status", show_label=False)
|
|
|
|
| 166 |
|
| 167 |
+
# Main Tabs
|
| 168 |
+
with gr.Tab("ποΈ Story Generator"):
|
| 169 |
+
gr.Markdown("### Create Movie Sequels")
|
|
|
|
| 170 |
|
| 171 |
+
original_input = gr.Textbox(
|
| 172 |
+
label="Original Story",
|
| 173 |
+
placeholder="e.g., Conan the Barbarian seeks revenge...",
|
| 174 |
+
lines=2
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 175 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
| 176 |
|
| 177 |
+
sequel_input = gr.Textbox(
|
| 178 |
+
label="Sequel Starting Point",
|
| 179 |
+
placeholder="e.g., Years later, King Conan faces a new threat...",
|
| 180 |
+
lines=2
|
|
|
|
| 181 |
)
|
| 182 |
|
| 183 |
+
generate_btn = gr.Button("π¬ Generate Sequel", variant="primary")
|
| 184 |
+
story_output = gr.Textbox(label="Your Story", lines=10, show_copy_button=True)
|
| 185 |
+
|
| 186 |
+
# Pre-fill Conan example
|
| 187 |
+
def conan_example():
|
| 188 |
+
return [
|
| 189 |
+
"Conan the Barbarian seeks revenge against Thulsa Doom for destroying his village",
|
| 190 |
+
"Years later, King Conan faces an ancient ice demon awakening in the frozen north"
|
| 191 |
+
]
|
| 192 |
|
| 193 |
+
gr.Button("π‘οΈ Load Conan Example").click(
|
| 194 |
+
conan_example,
|
| 195 |
+
outputs=[original_input, sequel_input]
|
| 196 |
+
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 197 |
|
| 198 |
+
generate_btn.click(generate_cinematic_story, [original_input, sequel_input], story_output)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 199 |
|
| 200 |
+
with gr.Tab("π‘ Quick Pitches"):
|
| 201 |
+
gr.Markdown("### Instant Movie Concepts")
|
| 202 |
+
pitch_btn = gr.Button("π² Generate Pitch")
|
| 203 |
+
pitch_output = gr.Textbox(label="Pitch Deck", lines=6, show_copy_button=True)
|
| 204 |
+
pitch_btn.click(generate_movie_pitch, outputs=pitch_output)
|
| 205 |
|
| 206 |
+
# Connection handling
|
| 207 |
+
status_btn.click(test_connection, outputs=status_display)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 208 |
|
| 209 |
if __name__ == "__main__":
|
| 210 |
demo.launch()
|