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(
|