Spaces:
Running
Running
Update app.py
Browse files
app.py
CHANGED
@@ -1235,6 +1235,34 @@ def create_interface():
|
|
1235 |
box-shadow: 0 10px 30px rgba(0,0,0,0.3);
|
1236 |
}
|
1237 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1238 |
.act-button {
|
1239 |
min-height: 80px;
|
1240 |
font-size: 1.1rem;
|
@@ -1258,7 +1286,7 @@ def create_interface():
|
|
1258 |
margin: 15px 0;
|
1259 |
}
|
1260 |
"""
|
1261 |
-
|
1262 |
with gr.Blocks(theme=gr.themes.Soft(), css=css, title="AI ์๋๋ฆฌ์ค ์๊ฐ") as interface:
|
1263 |
gr.HTML("""
|
1264 |
<div class="main-header">
|
@@ -1402,6 +1430,7 @@ def create_interface():
|
|
1402 |
session_id = system.current_session_id
|
1403 |
yield progress, status, formatted, planning_data, session_id
|
1404 |
|
|
|
1405 |
def handle_act_writing(act_name, session_id, planning_data, previous_acts):
|
1406 |
"""๋ง๋ณ ์๋๋ฆฌ์ค ์์ฑ - generator ๋ํผ"""
|
1407 |
if not session_id or not planning_data:
|
@@ -1410,6 +1439,7 @@ def create_interface():
|
|
1410 |
|
1411 |
try:
|
1412 |
system = ScreenplayGenerationSystem()
|
|
|
1413 |
|
1414 |
# Generator๋ฅผ ์ํํ๋ฉฐ yield
|
1415 |
for act_progress, status_msg in system.generate_act(
|
@@ -1417,46 +1447,179 @@ def create_interface():
|
|
1417 |
):
|
1418 |
progress_html = create_act_progress_display(act_progress)
|
1419 |
screenplay_display = format_screenplay_display(act_progress.content)
|
|
|
1420 |
|
1421 |
yield progress_html, status_msg, screenplay_display
|
|
|
|
|
|
|
|
|
1422 |
|
1423 |
except Exception as e:
|
1424 |
logger.error(f"Act writing error: {e}")
|
1425 |
yield f"<div>โ ์ค๋ฅ ๋ฐ์: {str(e)}</div>", f"์ค๋ฅ: {str(e)}", ""
|
1426 |
-
|
1427 |
def format_screenplay_display(content):
|
|
|
1428 |
if not content:
|
1429 |
return "*์์ฑ ์ค...*"
|
1430 |
-
|
1431 |
-
|
1432 |
-
formatted = content.replace("INT.", "**INT.**").replace("EXT.", "**EXT.**")
|
1433 |
-
# ์บ๋ฆญํฐ๋ช
๊ฐ์กฐ (๋๋ฌธ์๋ก ๋ ์ค)
|
1434 |
-
lines = formatted.split('\n')
|
1435 |
result = []
|
|
|
|
|
1436 |
for line in lines:
|
1437 |
-
|
1438 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1439 |
else:
|
1440 |
result.append(line)
|
1441 |
-
|
1442 |
-
|
1443 |
-
|
1444 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1445 |
full = "# ๐ฌ ์ ์ฒด ์๋๋ฆฌ์ค\n\n"
|
1446 |
|
1447 |
-
|
1448 |
-
|
1449 |
-
|
1450 |
-
|
1451 |
-
|
1452 |
-
full += "## ์ 2๋งB - ๋ณต์กํ\n\n" + act2b + "\n\n---\n\n"
|
1453 |
-
if act3 and act3 != "*3๋ง์ด ์ฌ๊ธฐ์ ํ์๋ฉ๋๋ค...*":
|
1454 |
-
full += "## ์ 3๋ง - ํด๊ฒฐ\n\n" + act3 + "\n\n"
|
1455 |
|
1456 |
-
|
1457 |
-
|
1458 |
-
|
1459 |
-
""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1460 |
if not full_screenplay or full_screenplay == "*์ ์ฒด ์๋๋ฆฌ์ค๊ฐ ์์ฑ๋๋ฉด ์ฌ๊ธฐ์ ํ์๋ฉ๋๋ค...*":
|
1461 |
return None
|
1462 |
|
@@ -1464,8 +1627,16 @@ def create_interface():
|
|
1464 |
filename = f"screenplay_{timestamp}.txt"
|
1465 |
|
1466 |
with open(filename, 'w', encoding='utf-8') as f:
|
1467 |
-
# ๋งํฌ๋ค์ด ์ ๊ฑฐ
|
1468 |
-
clean_text = full_screenplay
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1469 |
f.write(clean_text)
|
1470 |
|
1471 |
return filename
|
@@ -1562,15 +1733,16 @@ def create_interface():
|
|
1562 |
|
1563 |
# ๋ค์ด๋ก๋
|
1564 |
download_btn.click(
|
1565 |
-
fn=save_to_file,
|
1566 |
-
inputs=[full_output],
|
1567 |
outputs=[download_file]
|
1568 |
).then(
|
1569 |
-
lambda x: gr.update(visible=True) if x else gr.update(visible=False),
|
1570 |
inputs=[download_file],
|
1571 |
outputs=[download_file]
|
1572 |
)
|
1573 |
-
|
|
|
1574 |
return interface
|
1575 |
|
1576 |
# ๋ฉ์ธ ์คํ
|
|
|
1235 |
box-shadow: 0 10px 30px rgba(0,0,0,0.3);
|
1236 |
}
|
1237 |
|
1238 |
+
/* ์๋๋ฆฌ์ค ์ถ๋ ฅ ์คํ์ผ */
|
1239 |
+
.screenplay-output {
|
1240 |
+
font-family: 'Courier New', monospace;
|
1241 |
+
font-size: 14px;
|
1242 |
+
line-height: 1.6;
|
1243 |
+
white-space: pre-wrap;
|
1244 |
+
background: #f8f9fa;
|
1245 |
+
padding: 20px;
|
1246 |
+
border-radius: 8px;
|
1247 |
+
}
|
1248 |
+
|
1249 |
+
.screenplay-output h3 {
|
1250 |
+
color: #2c3e50;
|
1251 |
+
margin: 20px 0 10px 0;
|
1252 |
+
font-size: 16px;
|
1253 |
+
font-weight: bold;
|
1254 |
+
}
|
1255 |
+
|
1256 |
+
.screenplay-output strong {
|
1257 |
+
color: #34495e;
|
1258 |
+
font-weight: bold;
|
1259 |
+
}
|
1260 |
+
|
1261 |
+
.screenplay-output em {
|
1262 |
+
font-style: italic;
|
1263 |
+
color: #7f8c8d;
|
1264 |
+
}
|
1265 |
+
|
1266 |
.act-button {
|
1267 |
min-height: 80px;
|
1268 |
font-size: 1.1rem;
|
|
|
1286 |
margin: 15px 0;
|
1287 |
}
|
1288 |
"""
|
1289 |
+
|
1290 |
with gr.Blocks(theme=gr.themes.Soft(), css=css, title="AI ์๋๋ฆฌ์ค ์๊ฐ") as interface:
|
1291 |
gr.HTML("""
|
1292 |
<div class="main-header">
|
|
|
1430 |
session_id = system.current_session_id
|
1431 |
yield progress, status, formatted, planning_data, session_id
|
1432 |
|
1433 |
+
# 4. ์ด๋ฒคํธ ํธ๋ค๋ฌ ์์ - act ๋ด์ฉ ์ ์ฅ ๊ฐ์
|
1434 |
def handle_act_writing(act_name, session_id, planning_data, previous_acts):
|
1435 |
"""๋ง๋ณ ์๋๋ฆฌ์ค ์์ฑ - generator ๋ํผ"""
|
1436 |
if not session_id or not planning_data:
|
|
|
1439 |
|
1440 |
try:
|
1441 |
system = ScreenplayGenerationSystem()
|
1442 |
+
final_content = "" # ์ต์ข
์ฝํ
์ธ ์ ์ฅ
|
1443 |
|
1444 |
# Generator๋ฅผ ์ํํ๋ฉฐ yield
|
1445 |
for act_progress, status_msg in system.generate_act(
|
|
|
1447 |
):
|
1448 |
progress_html = create_act_progress_display(act_progress)
|
1449 |
screenplay_display = format_screenplay_display(act_progress.content)
|
1450 |
+
final_content = act_progress.content # ์๋ณธ ์ฝํ
์ธ ์ ์ฅ
|
1451 |
|
1452 |
yield progress_html, status_msg, screenplay_display
|
1453 |
+
|
1454 |
+
# ์๋ฃ ํ ์๋ณธ ์ฝํ
์ธ ๋ฐํ
|
1455 |
+
if final_content:
|
1456 |
+
yield progress_html, f"โ
{act_name} ์์ฑ ๋ฐ ์ ์ฅ๋จ", screenplay_display
|
1457 |
|
1458 |
except Exception as e:
|
1459 |
logger.error(f"Act writing error: {e}")
|
1460 |
yield f"<div>โ ์ค๋ฅ ๋ฐ์: {str(e)}</div>", f"์ค๋ฅ: {str(e)}", ""
|
1461 |
+
|
1462 |
def format_screenplay_display(content):
|
1463 |
+
"""์๋๋ฆฌ์ค ํ์ ํฌ๋งทํ
- ๊ฐ์ ๋ ๋ฒ์ """
|
1464 |
if not content:
|
1465 |
return "*์์ฑ ์ค...*"
|
1466 |
+
|
1467 |
+
lines = content.split('\n')
|
|
|
|
|
|
|
1468 |
result = []
|
1469 |
+
scene_number = 0
|
1470 |
+
|
1471 |
for line in lines:
|
1472 |
+
stripped = line.strip()
|
1473 |
+
|
1474 |
+
# ๋น ์ค ์ ์ง
|
1475 |
+
if not stripped:
|
1476 |
+
result.append("")
|
1477 |
+
continue
|
1478 |
+
|
1479 |
+
# ์ฌ ํค๋ ๊ฐ์ง ๋ฐ ํฌ๋งทํ
(INT. ๋๋ EXT.๋ก ์์)
|
1480 |
+
if stripped.startswith(('INT.', 'EXT.', 'INT ', 'EXT ')):
|
1481 |
+
scene_number += 1
|
1482 |
+
# ์ฌ ๊ตฌ๋ถ์ ์ถ๊ฐ
|
1483 |
+
result.append("\n" + "="*80)
|
1484 |
+
result.append(f"### ์ฌ {scene_number}")
|
1485 |
+
result.append(f"**{stripped}**")
|
1486 |
+
result.append("-"*40)
|
1487 |
+
# ์บ๋ฆญํฐ๋ช
๊ฐ์ง (๋๋ฌธ์๋ก๋ง ๋ ์งง์ ์ค)
|
1488 |
+
elif stripped.isupper() and len(stripped.split()) <= 3 and not any(c.isdigit() for c in stripped):
|
1489 |
+
# ์บ๋ฆญํฐ๋ช
๊ฐ์กฐ
|
1490 |
+
result.append(f"\n**{stripped}**")
|
1491 |
+
# ๊ดํธ๋ก ๋ ์ง์๋ฌธ (์ฐ๊ธฐ ์ง์)
|
1492 |
+
elif stripped.startswith('(') and stripped.endswith(')'):
|
1493 |
+
result.append(f"*{stripped}*")
|
1494 |
+
# ํธ๋์ง์
(CUT TO:, FADE IN: ๋ฑ)
|
1495 |
+
elif any(trans in stripped.upper() for trans in ['CUT TO:', 'FADE IN:', 'FADE OUT:', 'DISSOLVE TO:']):
|
1496 |
+
result.append(f"\n_{stripped}_\n")
|
1497 |
+
# ์ผ๋ฐ ํ
์คํธ
|
1498 |
else:
|
1499 |
result.append(line)
|
1500 |
+
|
1501 |
+
# ์ต์ข
์ ๋ฆฌ
|
1502 |
+
formatted = '\n'.join(result)
|
1503 |
+
|
1504 |
+
# ์ฐ์๋ ๋น ์ค ์ ๊ฑฐ
|
1505 |
+
while '\n\n\n' in formatted:
|
1506 |
+
formatted = formatted.replace('\n\n\n', '\n\n')
|
1507 |
+
|
1508 |
+
return formatted
|
1509 |
+
|
1510 |
+
# 2. combine_all_acts ํจ์ ์์ - State ๊ฐ ์ฌ๋ฐ๋ฅด๊ฒ ์ฒ๋ฆฌ
|
1511 |
+
def combine_all_acts(act1_state, act2a_state, act2b_state, act3_state):
|
1512 |
+
"""๋ชจ๋ ๋ง ํฉ์น๊ธฐ - State ๊ฐ ์ฒ๋ฆฌ ๊ฐ์ """
|
1513 |
full = "# ๐ฌ ์ ์ฒด ์๋๋ฆฌ์ค\n\n"
|
1514 |
|
1515 |
+
# State ๊ฐ์ฒด์ธ ๊ฒฝ์ฐ value ์์ฑ ์ ๊ทผ
|
1516 |
+
act1 = act1_state.value if hasattr(act1_state, 'value') else act1_state
|
1517 |
+
act2a = act2a_state.value if hasattr(act2a_state, 'value') else act2a_state
|
1518 |
+
act2b = act2b_state.value if hasattr(act2b_state, 'value') else act2b_state
|
1519 |
+
act3 = act3_state.value if hasattr(act3_state, 'value') else act3_state
|
|
|
|
|
|
|
1520 |
|
1521 |
+
# ์ค์ ์ฝํ
์ธ ๊ฐ ์๋์ง ํ์ธ
|
1522 |
+
has_content = False
|
1523 |
+
|
1524 |
+
if act1 and act1 != "*1๋ง์ด ์ฌ๊ธฐ์ ํ์๋ฉ๋๋ค...*" and act1 != "":
|
1525 |
+
# ๋งํฌ๋ค์ด ์ ๊ฑฐํ ์๋ณธ ํ
์คํธ ์ถ์ถ
|
1526 |
+
clean_act1 = act1.replace("**", "").replace("###", "").replace("="*80, "").replace("-"*40, "")
|
1527 |
+
full += "## ์ 1๋ง - ์ค์ \n\n" + clean_act1 + "\n\n" + "="*80 + "\n\n"
|
1528 |
+
has_content = True
|
1529 |
+
|
1530 |
+
if act2a and act2a != "*2๋งA๊ฐ ์ฌ๊ธฐ์ ํ์๋ฉ๋๋ค...*" and act2a != "":
|
1531 |
+
clean_act2a = act2a.replace("**", "").replace("###", "").replace("="*80, "").replace("-"*40, "")
|
1532 |
+
full += "## ์ 2๋งA - ์์น\n\n" + clean_act2a + "\n\n" + "="*80 + "\n\n"
|
1533 |
+
has_content = True
|
1534 |
+
|
1535 |
+
if act2b and act2b != "*2๋งB๊ฐ ์ฌ๊ธฐ์ ํ์๋ฉ๋๋ค...*" and act2b != "":
|
1536 |
+
clean_act2b = act2b.replace("**", "").replace("###", "").replace("="*80, "").replace("-"*40, "")
|
1537 |
+
full += "## ์ 2๋งB - ๋ณต์กํ\n\n" + clean_act2b + "\n\n" + "="*80 + "\n\n"
|
1538 |
+
has_content = True
|
1539 |
+
|
1540 |
+
if act3 and act3 != "*3๋ง์ด ์ฌ๊ธฐ์ ํ์๋ฉ๋๋ค...*" and act3 != "":
|
1541 |
+
clean_act3 = act3.replace("**", "").replace("###", "").replace("="*80, "").replace("-"*40, "")
|
1542 |
+
full += "## ์ 3๋ง - ํด๊ฒฐ\n\n" + clean_act3 + "\n\n"
|
1543 |
+
has_content = True
|
1544 |
+
|
1545 |
+
if not has_content:
|
1546 |
+
return "*์ ์ฒด ์๋๋ฆฌ์ค๊ฐ ์์ฑ๋๋ฉด ์ฌ๊ธฐ์ ํ์๋ฉ๋๋ค...*"
|
1547 |
+
|
1548 |
+
return full
|
1549 |
+
|
1550 |
+
# 3. save_to_file ํจ์ ์์ - ๋ฐ์ดํฐ๋ฒ ์ด์ค์์ ์ง์ ๊ฐ์ ธ์ค๊ธฐ
|
1551 |
+
def save_to_file(full_screenplay, session_id):
|
1552 |
+
"""์๋๋ฆฌ์ค๋ฅผ ํ์ผ๋ก ์ ์ฅ - ๊ฐ์ ๋ ๋ฒ์ """
|
1553 |
+
|
1554 |
+
# ์ธ์
ID๊ฐ ์์ผ๋ฉด DB์์ ์ง์ ๊ฐ์ ธ์ค๊ธฐ
|
1555 |
+
if session_id:
|
1556 |
+
try:
|
1557 |
+
with ScreenplayDatabase.get_db() as conn:
|
1558 |
+
cursor = conn.cursor()
|
1559 |
+
row = cursor.execute(
|
1560 |
+
'''SELECT user_query, title, logline, genre, screenplay_type,
|
1561 |
+
act1_content, act2a_content, act2b_content, act3_content
|
1562 |
+
FROM screenplay_sessions WHERE session_id = ?''',
|
1563 |
+
(session_id,)
|
1564 |
+
).fetchone()
|
1565 |
+
|
1566 |
+
if row:
|
1567 |
+
# ํ์ผ ๋ด์ฉ ๊ตฌ์ฑ
|
1568 |
+
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
1569 |
+
filename = f"screenplay_{timestamp}.txt"
|
1570 |
+
|
1571 |
+
with open(filename, 'w', encoding='utf-8') as f:
|
1572 |
+
# ํค๋ ์ ๋ณด
|
1573 |
+
f.write("="*80 + "\n")
|
1574 |
+
f.write("์๋๋ฆฌ์ค\n")
|
1575 |
+
f.write("="*80 + "\n\n")
|
1576 |
+
|
1577 |
+
if row['title']:
|
1578 |
+
f.write(f"์ ๋ชฉ: {row['title']}\n")
|
1579 |
+
if row['genre']:
|
1580 |
+
f.write(f"์ฅ๋ฅด: {row['genre']}\n")
|
1581 |
+
if row['screenplay_type']:
|
1582 |
+
f.write(f"ํ์: {row['screenplay_type']}\n")
|
1583 |
+
if row['logline']:
|
1584 |
+
f.write(f"๋ก๊ทธ๋ผ์ธ: {row['logline']}\n")
|
1585 |
+
|
1586 |
+
f.write("\n" + "="*80 + "\n\n")
|
1587 |
+
|
1588 |
+
# ๊ฐ ๋ง ๋ด์ฉ ์ถ๊ฐ
|
1589 |
+
if row['act1_content']:
|
1590 |
+
f.write("์ 1๋ง - ์ค์ \n")
|
1591 |
+
f.write("="*80 + "\n\n")
|
1592 |
+
f.write(row['act1_content'])
|
1593 |
+
f.write("\n\n" + "="*80 + "\n\n")
|
1594 |
+
|
1595 |
+
if row['act2a_content']:
|
1596 |
+
f.write("์ 2๋งA - ์์น\n")
|
1597 |
+
f.write("="*80 + "\n\n")
|
1598 |
+
f.write(row['act2a_content'])
|
1599 |
+
f.write("\n\n" + "="*80 + "\n\n")
|
1600 |
+
|
1601 |
+
if row['act2b_content']:
|
1602 |
+
f.write("์ 2๋งB - ๋ณต์กํ\n")
|
1603 |
+
f.write("="*80 + "\n\n")
|
1604 |
+
f.write(row['act2b_content'])
|
1605 |
+
f.write("\n\n" + "="*80 + "\n\n")
|
1606 |
+
|
1607 |
+
if row['act3_content']:
|
1608 |
+
f.write("์ 3๋ง - ํด๊ฒฐ\n")
|
1609 |
+
f.write("="*80 + "\n\n")
|
1610 |
+
f.write(row['act3_content'])
|
1611 |
+
f.write("\n\n")
|
1612 |
+
|
1613 |
+
# ์์ฑ ์ ๋ณด
|
1614 |
+
f.write("\n" + "="*80 + "\n")
|
1615 |
+
f.write(f"์์ฑ์ผ: {datetime.now().strftime('%Y๋
%m์ %d์ผ %H:%M')}\n")
|
1616 |
+
f.write("="*80 + "\n")
|
1617 |
+
|
1618 |
+
return filename
|
1619 |
+
except Exception as e:
|
1620 |
+
logger.error(f"DB์์ ํ์ผ ์ ์ฅ ์ค ์ค๋ฅ: {e}")
|
1621 |
+
|
1622 |
+
# DB ์ ๊ทผ ์คํจ์ ์ ๋ฌ๋ฐ์ ๋ด์ฉ์ผ๋ก ์ ์ฅ
|
1623 |
if not full_screenplay or full_screenplay == "*์ ์ฒด ์๋๋ฆฌ์ค๊ฐ ์์ฑ๋๋ฉด ์ฌ๊ธฐ์ ํ์๋ฉ๋๋ค...*":
|
1624 |
return None
|
1625 |
|
|
|
1627 |
filename = f"screenplay_{timestamp}.txt"
|
1628 |
|
1629 |
with open(filename, 'w', encoding='utf-8') as f:
|
1630 |
+
# ๋ชจ๋ ๋งํฌ๋ค์ด ๋ฐ ํฌ๋งทํ
์ ๊ฑฐ
|
1631 |
+
clean_text = full_screenplay
|
1632 |
+
clean_text = clean_text.replace("**", "")
|
1633 |
+
clean_text = clean_text.replace("###", "")
|
1634 |
+
clean_text = clean_text.replace("#", "")
|
1635 |
+
clean_text = clean_text.replace("="*80, "-"*80)
|
1636 |
+
clean_text = clean_text.replace("-"*40, "")
|
1637 |
+
clean_text = clean_text.replace("_", "")
|
1638 |
+
clean_text = clean_text.replace("*", "")
|
1639 |
+
|
1640 |
f.write(clean_text)
|
1641 |
|
1642 |
return filename
|
|
|
1733 |
|
1734 |
# ๋ค์ด๋ก๋
|
1735 |
download_btn.click(
|
1736 |
+
fn=lambda full, sid: save_to_file(full, sid),
|
1737 |
+
inputs=[full_output, current_session_id], # session_id ์ถ๊ฐ
|
1738 |
outputs=[download_file]
|
1739 |
).then(
|
1740 |
+
lambda x: gr.update(visible=True, value=x) if x else gr.update(visible=False),
|
1741 |
inputs=[download_file],
|
1742 |
outputs=[download_file]
|
1743 |
)
|
1744 |
+
|
1745 |
+
|
1746 |
return interface
|
1747 |
|
1748 |
# ๋ฉ์ธ ์คํ
|