Spaces:
Running
Running
Update app.py
Browse files
app.py
CHANGED
@@ -2980,106 +2980,106 @@ def download_screenplay(screenplay_text: str, format_type: str, title: str,
|
|
2980 |
|
2981 |
# Create Gradio interface
|
2982 |
def create_interface():
|
2983 |
-
|
2984 |
-
|
2985 |
-
|
2986 |
-
|
2987 |
-
|
2988 |
-
|
2989 |
-
|
2990 |
-
|
2991 |
-
|
2992 |
-
|
2993 |
-
|
2994 |
-
|
2995 |
-
|
2996 |
-
|
2997 |
-
|
2998 |
-
|
2999 |
-
|
3000 |
-
|
3001 |
-
|
3002 |
-
|
3003 |
-
|
3004 |
-
|
3005 |
-
|
3006 |
-
|
3007 |
-
|
3008 |
-
|
3009 |
-
|
3010 |
-
|
3011 |
-
|
3012 |
-
|
3013 |
-
|
3014 |
-
|
3015 |
-
|
3016 |
-
|
3017 |
-
|
3018 |
-
|
3019 |
-
|
3020 |
-
|
3021 |
-
|
3022 |
-
|
3023 |
-
|
3024 |
-
|
3025 |
-
|
3026 |
-
|
3027 |
-
|
3028 |
-
|
3029 |
-
|
3030 |
-
|
3031 |
-
|
3032 |
-
|
3033 |
-
|
3034 |
-
|
3035 |
-
|
3036 |
-
|
3037 |
-
|
3038 |
-
|
3039 |
-
|
3040 |
-
|
3041 |
-
|
3042 |
-
|
3043 |
-
|
3044 |
-
|
3045 |
-
|
3046 |
-
|
3047 |
-
|
3048 |
-
|
3049 |
-
|
3050 |
-
|
3051 |
-
|
3052 |
-
|
3053 |
-
|
3054 |
-
|
3055 |
-
|
3056 |
-
|
3057 |
-
|
3058 |
-
|
3059 |
-
|
3060 |
-
|
3061 |
-
|
3062 |
-
|
3063 |
-
|
3064 |
-
|
3065 |
-
|
3066 |
-
|
3067 |
-
|
3068 |
-
|
3069 |
-
|
3070 |
-
|
3071 |
-
|
3072 |
-
|
3073 |
-
|
3074 |
-
|
3075 |
-
|
3076 |
-
|
3077 |
-
|
3078 |
-
|
3079 |
-
|
3080 |
-
|
3081 |
-
|
3082 |
-
|
3083 |
gr.HTML("""
|
3084 |
<style>
|
3085 |
.badges-container {
|
@@ -3138,217 +3138,217 @@ def create_interface():
|
|
3138 |
</p>
|
3139 |
</div>
|
3140 |
""")
|
3141 |
-
|
3142 |
-
|
3143 |
-
|
3144 |
-
|
3145 |
-
|
3146 |
-
|
3147 |
-
|
3148 |
-
|
3149 |
-
|
3150 |
-
|
3151 |
-
|
|
|
3152 |
- A detective with memory loss must solve their own attempted murder
|
3153 |
- Two rival food truck owners forced to work together to save the city food festival
|
3154 |
- A space station AI develops consciousness during a critical mission
|
3155 |
- A family reunion turns into a murder mystery during a hurricane
|
3156 |
|
3157 |
The more specific your concept, the better the screenplay will be tailored to your vision.""",
|
3158 |
-
|
3159 |
-
|
3160 |
-
|
3161 |
-
|
3162 |
-
|
3163 |
-
|
3164 |
-
|
3165 |
-
|
3166 |
-
|
3167 |
-
|
3168 |
-
|
3169 |
-
|
3170 |
-
|
3171 |
-
|
3172 |
-
|
3173 |
-
|
3174 |
-
|
3175 |
-
|
3176 |
-
|
3177 |
-
|
3178 |
-
|
3179 |
-
|
3180 |
-
|
3181 |
-
|
3182 |
-
|
3183 |
-
|
3184 |
-
|
3185 |
-
|
3186 |
-
|
3187 |
-
|
3188 |
-
|
3189 |
-
|
3190 |
-
|
3191 |
-
|
3192 |
-
|
3193 |
-
|
3194 |
-
|
3195 |
-
|
3196 |
-
|
3197 |
-
|
3198 |
-
|
3199 |
-
|
3200 |
-
|
3201 |
-
|
3202 |
-
|
3203 |
-
|
3204 |
-
|
3205 |
-
|
3206 |
-
|
3207 |
-
|
3208 |
-
|
3209 |
-
|
3210 |
-
|
3211 |
-
|
3212 |
-
|
3213 |
-
|
3214 |
-
|
3215 |
-
|
3216 |
-
|
3217 |
-
|
3218 |
-
|
3219 |
-
|
3220 |
-
|
3221 |
-
|
3222 |
-
|
3223 |
-
|
3224 |
-
|
3225 |
-
|
3226 |
-
|
3227 |
-
|
3228 |
-
|
3229 |
-
|
3230 |
-
|
3231 |
-
|
3232 |
-
|
3233 |
-
|
3234 |
-
|
3235 |
-
|
3236 |
-
|
3237 |
-
|
3238 |
-
|
3239 |
-
|
3240 |
-
|
3241 |
-
|
3242 |
-
|
3243 |
-
|
3244 |
-
|
3245 |
-
|
3246 |
-
|
3247 |
-
|
3248 |
-
|
3249 |
-
|
3250 |
-
|
3251 |
-
|
3252 |
-
|
3253 |
-
|
3254 |
-
|
3255 |
-
|
3256 |
-
|
3257 |
-
|
3258 |
-
|
3259 |
-
|
3260 |
-
|
3261 |
-
|
3262 |
-
|
3263 |
-
|
3264 |
-
|
3265 |
-
|
3266 |
-
|
3267 |
-
|
3268 |
-
|
3269 |
-
|
3270 |
-
|
3271 |
-
|
3272 |
-
|
3273 |
-
|
3274 |
-
|
3275 |
-
|
3276 |
-
|
3277 |
-
|
3278 |
-
|
3279 |
-
|
3280 |
-
|
3281 |
-
|
3282 |
-
|
3283 |
-
|
3284 |
-
|
3285 |
-
|
3286 |
-
|
3287 |
-
|
3288 |
-
|
3289 |
-
|
3290 |
-
|
3291 |
-
|
3292 |
-
|
3293 |
-
|
3294 |
-
|
3295 |
-
|
3296 |
-
|
3297 |
-
|
3298 |
-
|
3299 |
-
|
3300 |
-
|
3301 |
-
|
3302 |
-
|
3303 |
-
|
3304 |
-
|
3305 |
-
|
3306 |
-
|
3307 |
-
|
3308 |
-
|
3309 |
-
|
3310 |
-
|
3311 |
-
|
3312 |
-
|
3313 |
-
|
3314 |
-
|
3315 |
-
|
3316 |
-
|
3317 |
-
|
3318 |
-
|
3319 |
-
|
3320 |
|
3321 |
# Main function
|
3322 |
if __name__ == "__main__":
|
3323 |
-
|
3324 |
-
|
3325 |
-
|
3326 |
-
|
3327 |
-
|
3328 |
-
|
3329 |
-
|
3330 |
-
|
3331 |
-
|
3332 |
-
|
3333 |
-
|
3334 |
-
|
3335 |
-
|
3336 |
-
|
3337 |
-
|
3338 |
-
|
3339 |
-
|
3340 |
-
|
3341 |
-
|
3342 |
-
|
3343 |
-
|
3344 |
-
|
3345 |
-
|
3346 |
-
|
3347 |
-
|
3348 |
-
|
3349 |
-
|
3350 |
-
|
3351 |
-
|
3352 |
-
|
3353 |
-
|
3354 |
-
|
|
|
2980 |
|
2981 |
# Create Gradio interface
|
2982 |
def create_interface():
|
2983 |
+
"""Create Gradio interface for screenplay generation"""
|
2984 |
+
|
2985 |
+
css = """
|
2986 |
+
.main-header {
|
2987 |
+
text-align: center;
|
2988 |
+
margin-bottom: 2rem;
|
2989 |
+
padding: 2rem;
|
2990 |
+
background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);
|
2991 |
+
border-radius: 10px;
|
2992 |
+
color: white;
|
2993 |
+
}
|
2994 |
+
|
2995 |
+
.header-title {
|
2996 |
+
font-size: 3rem;
|
2997 |
+
margin-bottom: 1rem;
|
2998 |
+
background: linear-gradient(45deg, #f39c12, #e74c3c);
|
2999 |
+
-webkit-background-clip: text;
|
3000 |
+
-webkit-text-fill-color: transparent;
|
3001 |
+
}
|
3002 |
+
|
3003 |
+
.header-description {
|
3004 |
+
font-size: 1.1rem;
|
3005 |
+
opacity: 0.9;
|
3006 |
+
line-height: 1.6;
|
3007 |
+
}
|
3008 |
+
|
3009 |
+
.type-selector {
|
3010 |
+
display: flex;
|
3011 |
+
gap: 1rem;
|
3012 |
+
margin: 1rem 0;
|
3013 |
+
}
|
3014 |
+
|
3015 |
+
.type-card {
|
3016 |
+
flex: 1;
|
3017 |
+
padding: 1rem;
|
3018 |
+
border: 2px solid #ddd;
|
3019 |
+
border-radius: 8px;
|
3020 |
+
cursor: pointer;
|
3021 |
+
transition: all 0.3s;
|
3022 |
+
}
|
3023 |
+
|
3024 |
+
.type-card:hover {
|
3025 |
+
border-color: #f39c12;
|
3026 |
+
transform: translateY(-2px);
|
3027 |
+
}
|
3028 |
+
|
3029 |
+
.type-card.selected {
|
3030 |
+
border-color: #e74c3c;
|
3031 |
+
background: #fff5f5;
|
3032 |
+
}
|
3033 |
+
|
3034 |
+
#stages-display {
|
3035 |
+
max-height: 600px;
|
3036 |
+
overflow-y: auto;
|
3037 |
+
padding: 1rem;
|
3038 |
+
background: #f8f9fa;
|
3039 |
+
border-radius: 8px;
|
3040 |
+
}
|
3041 |
+
|
3042 |
+
#screenplay-output {
|
3043 |
+
font-family: 'Courier New', monospace;
|
3044 |
+
white-space: pre-wrap;
|
3045 |
+
background: white;
|
3046 |
+
padding: 2rem;
|
3047 |
+
border: 1px solid #ddd;
|
3048 |
+
border-radius: 8px;
|
3049 |
+
max-height: 800px;
|
3050 |
+
overflow-y: auto;
|
3051 |
+
}
|
3052 |
+
|
3053 |
+
.genre-grid {
|
3054 |
+
display: grid;
|
3055 |
+
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
|
3056 |
+
gap: 0.5rem;
|
3057 |
+
margin: 1rem 0;
|
3058 |
+
}
|
3059 |
+
|
3060 |
+
.genre-btn {
|
3061 |
+
padding: 0.75rem;
|
3062 |
+
border: 2px solid #e0e0e0;
|
3063 |
+
background: white;
|
3064 |
+
border-radius: 8px;
|
3065 |
+
cursor: pointer;
|
3066 |
+
transition: all 0.3s;
|
3067 |
+
text-align: center;
|
3068 |
+
}
|
3069 |
+
|
3070 |
+
.genre-btn:hover {
|
3071 |
+
border-color: #f39c12;
|
3072 |
+
background: #fffbf0;
|
3073 |
+
}
|
3074 |
+
|
3075 |
+
.genre-btn.selected {
|
3076 |
+
border-color: #e74c3c;
|
3077 |
+
background: #fff5f5;
|
3078 |
+
font-weight: bold;
|
3079 |
+
}
|
3080 |
+
"""
|
3081 |
+
|
3082 |
+
with gr.Blocks(theme=gr.themes.Soft(), css=css, title="Screenplay Generator") as interface:
|
3083 |
gr.HTML("""
|
3084 |
<style>
|
3085 |
.badges-container {
|
|
|
3138 |
</p>
|
3139 |
</div>
|
3140 |
""")
|
3141 |
+
|
3142 |
+
# State management
|
3143 |
+
current_session_id = gr.State(None)
|
3144 |
+
|
3145 |
+
with gr.Tabs():
|
3146 |
+
# Main Writing Tab
|
3147 |
+
with gr.Tab("โ๏ธ Write Screenplay"):
|
3148 |
+
with gr.Row():
|
3149 |
+
with gr.Column(scale=3):
|
3150 |
+
query_input = gr.Textbox(
|
3151 |
+
label="Screenplay Concept",
|
3152 |
+
placeholder="""Describe your screenplay idea. For example:
|
3153 |
- A detective with memory loss must solve their own attempted murder
|
3154 |
- Two rival food truck owners forced to work together to save the city food festival
|
3155 |
- A space station AI develops consciousness during a critical mission
|
3156 |
- A family reunion turns into a murder mystery during a hurricane
|
3157 |
|
3158 |
The more specific your concept, the better the screenplay will be tailored to your vision.""",
|
3159 |
+
lines=6
|
3160 |
+
)
|
3161 |
+
|
3162 |
+
with gr.Column(scale=1):
|
3163 |
+
screenplay_type = gr.Radio(
|
3164 |
+
choices=list(SCREENPLAY_LENGTHS.keys()),
|
3165 |
+
value="movie",
|
3166 |
+
label="Screenplay Type",
|
3167 |
+
info="Choose your format"
|
3168 |
+
)
|
3169 |
+
|
3170 |
+
genre_select = gr.Dropdown(
|
3171 |
+
choices=list(GENRE_TEMPLATES.keys()),
|
3172 |
+
value="drama",
|
3173 |
+
label="Primary Genre",
|
3174 |
+
info="Select main genre"
|
3175 |
+
)
|
3176 |
+
|
3177 |
+
language_select = gr.Radio(
|
3178 |
+
choices=["English", "Korean"],
|
3179 |
+
value="English",
|
3180 |
+
label="Language"
|
3181 |
+
)
|
3182 |
+
|
3183 |
+
with gr.Row():
|
3184 |
+
random_btn = gr.Button("๐ฒ Random Concept", scale=1)
|
3185 |
+
clear_btn = gr.Button("๐๏ธ Clear", scale=1)
|
3186 |
+
submit_btn = gr.Button("๐ฌ Start Writing", variant="primary", scale=2)
|
3187 |
+
|
3188 |
+
status_text = gr.Textbox(
|
3189 |
+
label="Status",
|
3190 |
+
interactive=False,
|
3191 |
+
value="Ready to create your screenplay"
|
3192 |
+
)
|
3193 |
+
|
3194 |
+
# Session management
|
3195 |
+
with gr.Group():
|
3196 |
+
gr.Markdown("### ๐ Saved Projects")
|
3197 |
+
with gr.Row():
|
3198 |
+
session_dropdown = gr.Dropdown(
|
3199 |
+
label="Active Sessions",
|
3200 |
+
choices=[],
|
3201 |
+
interactive=True,
|
3202 |
+
scale=3
|
3203 |
+
)
|
3204 |
+
refresh_btn = gr.Button("๐", scale=1)
|
3205 |
+
resume_btn = gr.Button("๐ Load", scale=1)
|
3206 |
+
|
3207 |
+
# Output displays
|
3208 |
+
with gr.Row():
|
3209 |
+
with gr.Column():
|
3210 |
+
with gr.Tab("๐ญ Writing Progress"):
|
3211 |
+
stages_display = gr.Markdown(
|
3212 |
+
value="*Your screenplay journey will unfold here...*",
|
3213 |
+
elem_id="stages-display"
|
3214 |
+
)
|
3215 |
+
|
3216 |
+
with gr.Tab("๐ Screenplay"):
|
3217 |
+
screenplay_output = gr.Markdown(
|
3218 |
+
value="*Your formatted screenplay will appear here...*",
|
3219 |
+
elem_id="screenplay-output"
|
3220 |
+
)
|
3221 |
+
|
3222 |
+
with gr.Row():
|
3223 |
+
format_select = gr.Radio(
|
3224 |
+
choices=["PDF", "FDX", "FOUNTAIN", "TXT"],
|
3225 |
+
value="PDF",
|
3226 |
+
label="Export Format"
|
3227 |
+
)
|
3228 |
+
download_btn = gr.Button("๐ฅ Download Screenplay", variant="secondary")
|
3229 |
+
|
3230 |
+
download_file = gr.File(
|
3231 |
+
label="Download",
|
3232 |
+
visible=False
|
3233 |
+
)
|
3234 |
+
|
3235 |
+
# Examples
|
3236 |
+
gr.Examples(
|
3237 |
+
examples=[
|
3238 |
+
["A burned-out teacher discovers her students are being replaced by AI duplicates"],
|
3239 |
+
["Two funeral home employees accidentally release a ghost who helps them solve murders"],
|
3240 |
+
["A time-loop forces a wedding planner to relive the worst wedding until they find true love"],
|
3241 |
+
["An astronaut returns to Earth to find everyone has forgotten space exists"],
|
3242 |
+
["A support group for reformed villains must save the city when heroes disappear"],
|
3243 |
+
["A food critic loses their sense of taste and teams up with a street food vendor"]
|
3244 |
+
],
|
3245 |
+
inputs=query_input,
|
3246 |
+
label="๐ก Example Concepts"
|
3247 |
+
)
|
3248 |
+
|
3249 |
+
# Screenplay Library Tab
|
3250 |
+
with gr.Tab("๐ Concept Library"):
|
3251 |
+
gr.Markdown("""
|
3252 |
+
### ๐ฒ Random Screenplay Concepts
|
3253 |
+
|
3254 |
+
Browse through AI-generated screenplay concepts. Each concept includes a title, logline, and brief setup.
|
3255 |
+
""")
|
3256 |
+
|
3257 |
+
library_display = gr.HTML(
|
3258 |
+
value="<p>Library feature coming soon...</p>"
|
3259 |
+
)
|
3260 |
+
|
3261 |
+
# Event handlers
|
3262 |
+
def handle_submit(query, s_type, genre, lang, session_id):
|
3263 |
+
if not query:
|
3264 |
+
yield "", "", "โ Please enter a concept", session_id
|
3265 |
+
return
|
3266 |
+
|
3267 |
+
yield from process_query(query, s_type, genre, lang, session_id)
|
3268 |
+
|
3269 |
+
def handle_random(s_type, genre, lang):
|
3270 |
+
return generate_random_screenplay_theme(s_type, genre, lang)
|
3271 |
+
|
3272 |
+
def handle_download(screenplay_text, format_type, session_id):
|
3273 |
+
if not screenplay_text or not session_id:
|
3274 |
+
return gr.update(visible=False)
|
3275 |
+
|
3276 |
+
# Get title from database
|
3277 |
+
session = ScreenplayDatabase.get_session(session_id)
|
3278 |
+
title = session.get('title', 'Untitled') if session else 'Untitled'
|
3279 |
+
|
3280 |
+
file_path = download_screenplay(screenplay_text, format_type, title, session_id)
|
3281 |
+
if file_path and os.path.exists(file_path):
|
3282 |
+
return gr.update(value=file_path, visible=True)
|
3283 |
+
return gr.update(visible=False)
|
3284 |
+
|
3285 |
+
# Connect events
|
3286 |
+
submit_btn.click(
|
3287 |
+
fn=handle_submit,
|
3288 |
+
inputs=[query_input, screenplay_type, genre_select, language_select, current_session_id],
|
3289 |
+
outputs=[stages_display, screenplay_output, status_text, current_session_id]
|
3290 |
+
)
|
3291 |
+
|
3292 |
+
random_btn.click(
|
3293 |
+
fn=handle_random,
|
3294 |
+
inputs=[screenplay_type, genre_select, language_select],
|
3295 |
+
outputs=[query_input]
|
3296 |
+
)
|
3297 |
+
|
3298 |
+
clear_btn.click(
|
3299 |
+
fn=lambda: ("", "", "Ready to create your screenplay", None),
|
3300 |
+
outputs=[stages_display, screenplay_output, status_text, current_session_id]
|
3301 |
+
)
|
3302 |
+
|
3303 |
+
refresh_btn.click(
|
3304 |
+
fn=get_active_sessions,
|
3305 |
+
outputs=[session_dropdown]
|
3306 |
+
)
|
3307 |
+
|
3308 |
+
download_btn.click(
|
3309 |
+
fn=handle_download,
|
3310 |
+
inputs=[screenplay_output, format_select, current_session_id],
|
3311 |
+
outputs=[download_file]
|
3312 |
+
)
|
3313 |
+
|
3314 |
+
# Load sessions on start
|
3315 |
+
interface.load(
|
3316 |
+
fn=get_active_sessions,
|
3317 |
+
outputs=[session_dropdown]
|
3318 |
+
)
|
3319 |
+
|
3320 |
+
return interface
|
3321 |
|
3322 |
# Main function
|
3323 |
if __name__ == "__main__":
|
3324 |
+
logger.info("Screenplay Generator Starting...")
|
3325 |
+
logger.info("=" * 60)
|
3326 |
+
|
3327 |
+
# Environment check
|
3328 |
+
logger.info(f"API Endpoint: {API_URL}")
|
3329 |
+
logger.info("Screenplay Types Available:")
|
3330 |
+
for s_type, info in SCREENPLAY_LENGTHS.items():
|
3331 |
+
logger.info(f" - {s_type}: {info['description']}")
|
3332 |
+
logger.info(f"Genres: {', '.join(GENRE_TEMPLATES.keys())}")
|
3333 |
+
|
3334 |
+
if BRAVE_SEARCH_API_KEY:
|
3335 |
+
logger.info("Web search enabled for market research.")
|
3336 |
+
else:
|
3337 |
+
logger.warning("Web search disabled.")
|
3338 |
+
|
3339 |
+
logger.info("=" * 60)
|
3340 |
+
|
3341 |
+
# Initialize database
|
3342 |
+
logger.info("Initializing database...")
|
3343 |
+
ScreenplayDatabase.init_db()
|
3344 |
+
logger.info("Database initialization complete.")
|
3345 |
+
|
3346 |
+
# Create and launch interface
|
3347 |
+
interface = create_interface()
|
3348 |
+
|
3349 |
+
interface.launch(
|
3350 |
+
server_name="0.0.0.0",
|
3351 |
+
server_port=7860,
|
3352 |
+
share=False,
|
3353 |
+
debug=True
|
3354 |
+
)
|
|