Update app.py
Browse files
app.py
CHANGED
|
@@ -575,6 +575,7 @@ def safe_state_update(state, updates):
|
|
| 575 |
return state
|
| 576 |
|
| 577 |
def create_interface():
|
|
|
|
| 578 |
import base64
|
| 579 |
|
| 580 |
# initial_state 정의
|
|
@@ -617,7 +618,7 @@ def create_interface():
|
|
| 617 |
AUDIO_PLAYER_HTML = """
|
| 618 |
<div class="audio-player-container">
|
| 619 |
<audio id="mainAudio" preload="auto">
|
| 620 |
-
<source src="
|
| 621 |
Your browser does not support the audio element.
|
| 622 |
</audio>
|
| 623 |
<button id="playButton" onclick="togglePlay()" class="custom-audio-button">
|
|
@@ -631,6 +632,7 @@ def create_interface():
|
|
| 631 |
|
| 632 |
function togglePlay() {
|
| 633 |
if (!isPlaying) {
|
|
|
|
| 634 |
audioElement.play()
|
| 635 |
.then(() => {
|
| 636 |
isPlaying = true;
|
|
@@ -647,20 +649,17 @@ def create_interface():
|
|
| 647 |
}
|
| 648 |
}
|
| 649 |
|
| 650 |
-
|
| 651 |
-
|
| 652 |
-
|
| 653 |
-
audioElement.pause();
|
| 654 |
-
audioElement.currentTime = 0;
|
| 655 |
-
}
|
| 656 |
});
|
| 657 |
|
| 658 |
-
// 오디오 로딩 에러 처리
|
| 659 |
audioElement.addEventListener('error', function(e) {
|
| 660 |
console.error("Audio error:", e);
|
| 661 |
alert("음악 파일을 불러오는데 실패했습니다. 페이지를 새로고침해주세요.");
|
| 662 |
});
|
| 663 |
</script>
|
|
|
|
| 664 |
<style>
|
| 665 |
.audio-player-container {
|
| 666 |
margin: 20px 0;
|
|
@@ -696,33 +695,33 @@ def create_interface():
|
|
| 696 |
"""
|
| 697 |
|
| 698 |
css = """
|
| 699 |
-
/*
|
| 700 |
.gradio-container {
|
| 701 |
-
|
| 702 |
-
|
|
|
|
| 703 |
}
|
| 704 |
-
|
| 705 |
-
/* 모바일
|
| 706 |
@media (max-width: 600px) {
|
| 707 |
-
.
|
| 708 |
-
|
| 709 |
-
|
| 710 |
-
|
| 711 |
-
.desktop-logo { display: none !important; }
|
| 712 |
-
.mobile-logo {
|
| 713 |
-
width: 100% !important;
|
| 714 |
-
height: auto !important;
|
| 715 |
-
max-width: 300px !important;
|
| 716 |
-
margin: 0 auto !important;
|
| 717 |
-
display: block !important;
|
| 718 |
}
|
| 719 |
-
.gradio-button {
|
|
|
|
|
|
|
| 720 |
min-height: 44px !important;
|
| 721 |
-
margin: 10px 0 !important;
|
| 722 |
}
|
| 723 |
-
|
| 724 |
-
|
| 725 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 726 |
}
|
| 727 |
}
|
| 728 |
|
|
@@ -742,12 +741,69 @@ def create_interface():
|
|
| 742 |
margin: 0 auto;
|
| 743 |
display: block;
|
| 744 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 745 |
}
|
| 746 |
"""
|
| 747 |
|
| 748 |
with gr.Blocks(theme=gr.themes.Soft(), css=css) as app:
|
| 749 |
state = gr.State(value=initial_state)
|
| 750 |
-
|
| 751 |
with gr.Column(elem_classes="logo-container"):
|
| 752 |
gr.HTML(f"""
|
| 753 |
<img class="mobile-logo" src="data:image/png;base64,{mobile_logo}" alt="디지털 굿판 로고 모바일">
|
|
@@ -835,9 +891,7 @@ def create_interface():
|
|
| 835 |
# 기원 탭
|
| 836 |
with gr.TabItem("기원") as tab_wish:
|
| 837 |
gr.Markdown("## 기원 - 소원을 전해보세요")
|
| 838 |
-
|
| 839 |
-
# 상태 표시 추가
|
| 840 |
-
processing_status = gr.Markdown("", visible=False)
|
| 841 |
|
| 842 |
with gr.Row():
|
| 843 |
with gr.Column():
|
|
@@ -845,12 +899,13 @@ def create_interface():
|
|
| 845 |
label="소원을 나누고 싶은 마음을 말해주세요",
|
| 846 |
sources=["microphone"],
|
| 847 |
type="numpy",
|
| 848 |
-
streaming=False
|
|
|
|
| 849 |
)
|
| 850 |
with gr.Row():
|
| 851 |
clear_btn = gr.Button("녹음 지우기", variant="secondary")
|
| 852 |
analyze_btn = gr.Button("소원 분석하기", variant="primary")
|
| 853 |
-
|
| 854 |
with gr.Column():
|
| 855 |
transcribed_text = gr.Textbox(
|
| 856 |
label="인식된 텍스트",
|
|
@@ -1043,25 +1098,24 @@ def create_interface():
|
|
| 1043 |
print(f"Error saving wish: {e}")
|
| 1044 |
return "오류가 발생했습니다.", []
|
| 1045 |
|
| 1046 |
-
def safe_analyze_voice(audio_data,
|
| 1047 |
-
"""음성 분석 함수에 안전장치 추가"""
|
| 1048 |
if audio_data is None:
|
| 1049 |
-
return
|
| 1050 |
|
| 1051 |
try:
|
| 1052 |
-
|
| 1053 |
-
result = analyze_voice(audio_data,
|
| 1054 |
-
|
| 1055 |
return (*result, gr.update(visible=False))
|
| 1056 |
except Exception as e:
|
| 1057 |
print(f"Voice analysis error: {str(e)}")
|
| 1058 |
return (
|
| 1059 |
-
|
| 1060 |
"음성 분석 중 오류가 발생했습니다. 다시 시도해주세요.",
|
| 1061 |
"",
|
| 1062 |
"",
|
| 1063 |
"",
|
| 1064 |
-
gr.update(visible=
|
| 1065 |
)
|
| 1066 |
|
| 1067 |
|
|
@@ -1102,7 +1156,7 @@ def create_interface():
|
|
| 1102 |
analyze_btn.click(
|
| 1103 |
fn=safe_analyze_voice,
|
| 1104 |
inputs=[voice_input, state],
|
| 1105 |
-
outputs=[state, transcribed_text, voice_emotion, text_emotion, final_prompt,
|
| 1106 |
)
|
| 1107 |
|
| 1108 |
generate_btn.click(
|
|
|
|
| 575 |
return state
|
| 576 |
|
| 577 |
def create_interface():
|
| 578 |
+
db = SimpleDB() # DB 객체 초기화 추가
|
| 579 |
import base64
|
| 580 |
|
| 581 |
# initial_state 정의
|
|
|
|
| 618 |
AUDIO_PLAYER_HTML = """
|
| 619 |
<div class="audio-player-container">
|
| 620 |
<audio id="mainAudio" preload="auto">
|
| 621 |
+
<source src="assets/main_music.mp3" type="audio/mp3">
|
| 622 |
Your browser does not support the audio element.
|
| 623 |
</audio>
|
| 624 |
<button id="playButton" onclick="togglePlay()" class="custom-audio-button">
|
|
|
|
| 632 |
|
| 633 |
function togglePlay() {
|
| 634 |
if (!isPlaying) {
|
| 635 |
+
audioElement.load(); // 재생 전 로드 추가
|
| 636 |
audioElement.play()
|
| 637 |
.then(() => {
|
| 638 |
isPlaying = true;
|
|
|
|
| 649 |
}
|
| 650 |
}
|
| 651 |
|
| 652 |
+
audioElement.addEventListener('ended', function() {
|
| 653 |
+
isPlaying = false;
|
| 654 |
+
playButtonText.textContent = '재생';
|
|
|
|
|
|
|
|
|
|
| 655 |
});
|
| 656 |
|
|
|
|
| 657 |
audioElement.addEventListener('error', function(e) {
|
| 658 |
console.error("Audio error:", e);
|
| 659 |
alert("음악 파일을 불러오는데 실패했습니다. 페이지를 새로고침해주세요.");
|
| 660 |
});
|
| 661 |
</script>
|
| 662 |
+
|
| 663 |
<style>
|
| 664 |
.audio-player-container {
|
| 665 |
margin: 20px 0;
|
|
|
|
| 695 |
"""
|
| 696 |
|
| 697 |
css = """
|
| 698 |
+
/* 전체 컨테이너 width 제한 */
|
| 699 |
.gradio-container {
|
| 700 |
+
margin: 0 auto !important;
|
| 701 |
+
max-width: 800px !important;
|
| 702 |
+
padding: 1rem !important;
|
| 703 |
}
|
| 704 |
+
|
| 705 |
+
/* 모바일 뷰 */
|
| 706 |
@media (max-width: 600px) {
|
| 707 |
+
.container { padding: 10px !important; }
|
| 708 |
+
.gradio-row {
|
| 709 |
+
flex-direction: column !important;
|
| 710 |
+
gap: 10px !important;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 711 |
}
|
| 712 |
+
.gradio-button {
|
| 713 |
+
width: 100% !important;
|
| 714 |
+
margin: 5px 0 !important;
|
| 715 |
min-height: 44px !important;
|
|
|
|
| 716 |
}
|
| 717 |
+
.gradio-textbox { width: 100% !important; }
|
| 718 |
+
.gradio-audio { width: 100% !important; }
|
| 719 |
+
.gradio-image { width: 100% !important; }
|
| 720 |
+
#audio-recorder { width: 100% !important; }
|
| 721 |
+
#result-image { width: 100% !important; }
|
| 722 |
+
.gradio-dataframe {
|
| 723 |
+
overflow-x: auto !important;
|
| 724 |
+
max-width: 100% !important;
|
| 725 |
}
|
| 726 |
}
|
| 727 |
|
|
|
|
| 741 |
margin: 0 auto;
|
| 742 |
display: block;
|
| 743 |
}
|
| 744 |
+
/* 데스크탑에서 2단 컬럼 레이아웃 보완 */
|
| 745 |
+
.gradio-row {
|
| 746 |
+
gap: 20px !important;
|
| 747 |
+
}
|
| 748 |
+
.gradio-row > .gradio-column {
|
| 749 |
+
flex: 1 !important;
|
| 750 |
+
min-width: 0 !important;
|
| 751 |
+
}
|
| 752 |
+
}
|
| 753 |
+
|
| 754 |
+
/* 전반적인 UI 개선 */
|
| 755 |
+
.gradio-button {
|
| 756 |
+
transition: all 0.3s ease;
|
| 757 |
+
border-radius: 8px !important;
|
| 758 |
+
}
|
| 759 |
+
.gradio-button:active {
|
| 760 |
+
transform: scale(0.98);
|
| 761 |
+
}
|
| 762 |
+
|
| 763 |
+
/* 컴포넌트 간격 조정 */
|
| 764 |
+
.gradio-column > *:not(:last-child) {
|
| 765 |
+
margin-bottom: 1rem !important;
|
| 766 |
+
}
|
| 767 |
+
|
| 768 |
+
/* 데이터프레임 스타일링 */
|
| 769 |
+
.gradio-dataframe {
|
| 770 |
+
border: 1px solid #e0e0e0;
|
| 771 |
+
border-radius: 8px;
|
| 772 |
+
overflow: hidden;
|
| 773 |
+
}
|
| 774 |
+
|
| 775 |
+
/* 오디오 플레이어 컨테이너 */
|
| 776 |
+
.audio-player-container {
|
| 777 |
+
max-width: 800px;
|
| 778 |
+
margin: 20px auto;
|
| 779 |
+
padding: 0 1rem;
|
| 780 |
+
}
|
| 781 |
+
|
| 782 |
+
/* 탭 스타일링 */
|
| 783 |
+
.tabs {
|
| 784 |
+
max-width: 800px;
|
| 785 |
+
margin: 0 auto;
|
| 786 |
+
}
|
| 787 |
+
|
| 788 |
+
/* 마크다운 컨텐츠 */
|
| 789 |
+
.markdown-content {
|
| 790 |
+
max-width: 800px;
|
| 791 |
+
margin: 0 auto;
|
| 792 |
+
padding: 0 1rem;
|
| 793 |
+
}
|
| 794 |
+
|
| 795 |
+
/* 이미지 컨테이너 */
|
| 796 |
+
.gradio-image {
|
| 797 |
+
border-radius: 8px;
|
| 798 |
+
overflow: hidden;
|
| 799 |
+
max-width: 800px;
|
| 800 |
+
margin: 0 auto;
|
| 801 |
}
|
| 802 |
"""
|
| 803 |
|
| 804 |
with gr.Blocks(theme=gr.themes.Soft(), css=css) as app:
|
| 805 |
state = gr.State(value=initial_state)
|
| 806 |
+
processing_status = gr.State("")
|
| 807 |
with gr.Column(elem_classes="logo-container"):
|
| 808 |
gr.HTML(f"""
|
| 809 |
<img class="mobile-logo" src="data:image/png;base64,{mobile_logo}" alt="디지털 굿판 로고 모바일">
|
|
|
|
| 891 |
# 기원 탭
|
| 892 |
with gr.TabItem("기원") as tab_wish:
|
| 893 |
gr.Markdown("## 기원 - 소원을 전해보세요")
|
| 894 |
+
status_display = gr.Markdown("", visible=False) # 상태 표시용 컴포넌트
|
|
|
|
|
|
|
| 895 |
|
| 896 |
with gr.Row():
|
| 897 |
with gr.Column():
|
|
|
|
| 899 |
label="소원을 나누고 싶은 마음을 말해주세요",
|
| 900 |
sources=["microphone"],
|
| 901 |
type="numpy",
|
| 902 |
+
streaming=False,
|
| 903 |
+
elem_id="voice-input" # elem_id 추가
|
| 904 |
)
|
| 905 |
with gr.Row():
|
| 906 |
clear_btn = gr.Button("녹음 지우기", variant="secondary")
|
| 907 |
analyze_btn = gr.Button("소원 분석하기", variant="primary")
|
| 908 |
+
|
| 909 |
with gr.Column():
|
| 910 |
transcribed_text = gr.Textbox(
|
| 911 |
label="인식된 텍스트",
|
|
|
|
| 1098 |
print(f"Error saving wish: {e}")
|
| 1099 |
return "오류가 발생했습니다.", []
|
| 1100 |
|
| 1101 |
+
def safe_analyze_voice(audio_data, current_state):
|
|
|
|
| 1102 |
if audio_data is None:
|
| 1103 |
+
return current_state, "음성을 먼저 녹음해주세요.", "", "", "", gr.update(visible=True)
|
| 1104 |
|
| 1105 |
try:
|
| 1106 |
+
status_display.update(value="분석 중입니다...", visible=True)
|
| 1107 |
+
result = analyze_voice(audio_data, current_state)
|
| 1108 |
+
status_display.update(value="", visible=False)
|
| 1109 |
return (*result, gr.update(visible=False))
|
| 1110 |
except Exception as e:
|
| 1111 |
print(f"Voice analysis error: {str(e)}")
|
| 1112 |
return (
|
| 1113 |
+
current_state,
|
| 1114 |
"음성 분석 중 오류가 발생했습니다. 다시 시도해주세요.",
|
| 1115 |
"",
|
| 1116 |
"",
|
| 1117 |
"",
|
| 1118 |
+
gr.update(visible=True)
|
| 1119 |
)
|
| 1120 |
|
| 1121 |
|
|
|
|
| 1156 |
analyze_btn.click(
|
| 1157 |
fn=safe_analyze_voice,
|
| 1158 |
inputs=[voice_input, state],
|
| 1159 |
+
outputs=[state, transcribed_text, voice_emotion, text_emotion, final_prompt, status_display]
|
| 1160 |
)
|
| 1161 |
|
| 1162 |
generate_btn.click(
|