Update app.py
Browse files
app.py
CHANGED
@@ -618,7 +618,7 @@ def create_interface():
|
|
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/
|
622 |
Your browser does not support the audio element.
|
623 |
</audio>
|
624 |
<button id="playButton" onclick="togglePlay()" class="custom-audio-button">
|
@@ -632,7 +632,7 @@ def create_interface():
|
|
632 |
|
633 |
function togglePlay() {
|
634 |
if (!isPlaying) {
|
635 |
-
audioElement.
|
636 |
audioElement.play()
|
637 |
.then(() => {
|
638 |
isPlaying = true;
|
@@ -648,18 +648,8 @@ def create_interface():
|
|
648 |
playButtonText.textContent = '재생';
|
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;
|
@@ -704,6 +694,16 @@ def create_interface():
|
|
704 |
|
705 |
/* 모바일 뷰 */
|
706 |
@media (max-width: 600px) {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
707 |
.container { padding: 10px !important; }
|
708 |
.gradio-row {
|
709 |
flex-direction: column !important;
|
@@ -733,13 +733,15 @@ def create_interface():
|
|
733 |
max-width: 800px;
|
734 |
margin: 0 auto;
|
735 |
}
|
736 |
-
.
|
737 |
-
|
738 |
-
|
739 |
-
|
740 |
-
|
741 |
-
|
742 |
-
|
|
|
|
|
743 |
}
|
744 |
/* 데스크탑에서 2단 컬럼 레이아웃 보완 */
|
745 |
.gradio-row {
|
@@ -952,6 +954,23 @@ def create_interface():
|
|
952 |
interactive=False,
|
953 |
wrap=True
|
954 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
955 |
|
956 |
# 이벤트 핸들러들
|
957 |
def handle_name_submit(name, state):
|
@@ -1025,24 +1044,24 @@ def create_interface():
|
|
1025 |
return state, []
|
1026 |
|
1027 |
try:
|
1028 |
-
current_time = datetime.now().strftime("%H:%M:%S")
|
|
|
|
|
1029 |
if text_analyzer:
|
1030 |
sentiment = text_analyzer(text)[0]
|
1031 |
sentiment_text = f"{sentiment['label']} ({sentiment['score']:.2f})"
|
1032 |
-
db.save_reflection(state.get("user_name", "익명"), text, sentiment)
|
1033 |
else:
|
1034 |
sentiment_text = "분석 불가"
|
1035 |
-
|
1036 |
-
|
1037 |
-
|
1038 |
-
reflections = state.get("reflections", [])
|
1039 |
-
reflections.append(new_reflection)
|
1040 |
-
state = safe_state_update(state, {"reflections": reflections})
|
1041 |
|
1042 |
-
|
|
|
|
|
1043 |
except Exception as e:
|
1044 |
print(f"Error saving reflection: {e}")
|
1045 |
-
return state,
|
1046 |
|
1047 |
def save_reflection_fixed(text, state):
|
1048 |
if not text.strip():
|
@@ -1098,24 +1117,34 @@ def create_interface():
|
|
1098 |
print(f"Error saving wish: {e}")
|
1099 |
return "오류가 발생했습니다.", []
|
1100 |
|
1101 |
-
def safe_analyze_voice(audio_data,
|
1102 |
if audio_data is None:
|
1103 |
-
return
|
1104 |
|
1105 |
try:
|
1106 |
-
|
1107 |
-
|
1108 |
-
|
1109 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1110 |
except Exception as e:
|
1111 |
print(f"Voice analysis error: {str(e)}")
|
1112 |
return (
|
1113 |
-
|
1114 |
"음성 분석 중 오류가 발생했습니다. 다시 시도해주세요.",
|
1115 |
"",
|
1116 |
"",
|
1117 |
"",
|
1118 |
-
gr.update(visible=
|
1119 |
)
|
1120 |
|
1121 |
|
|
|
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/mpeg">
|
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.currentTime = 0; // 재생 시작 시 처음부터
|
636 |
audioElement.play()
|
637 |
.then(() => {
|
638 |
isPlaying = true;
|
|
|
648 |
playButtonText.textContent = '재생';
|
649 |
}
|
650 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
651 |
</script>
|
652 |
+
"""
|
653 |
<style>
|
654 |
.audio-player-container {
|
655 |
margin: 20px 0;
|
|
|
694 |
|
695 |
/* 모바일 뷰 */
|
696 |
@media (max-width: 600px) {
|
697 |
+
.mobile-logo {
|
698 |
+
display: none !important;
|
699 |
+
}
|
700 |
+
.desktop-logo {
|
701 |
+
display: block !important;
|
702 |
+
width: 100%;
|
703 |
+
height: auto;
|
704 |
+
max-width: 800px;
|
705 |
+
margin: 0 auto;
|
706 |
+
}
|
707 |
.container { padding: 10px !important; }
|
708 |
.gradio-row {
|
709 |
flex-direction: column !important;
|
|
|
733 |
max-width: 800px;
|
734 |
margin: 0 auto;
|
735 |
}
|
736 |
+
.desktop-logo {
|
737 |
+
display: none !important;
|
738 |
+
}
|
739 |
+
.mobile-logo {
|
740 |
+
display: block !important;
|
741 |
+
width: 100% !important;
|
742 |
+
height: auto !important;
|
743 |
+
max-width: 300px !important;
|
744 |
+
margin: 0 auto !important;
|
745 |
}
|
746 |
/* 데스크탑에서 2단 컬럼 레이아웃 보완 */
|
747 |
.gradio-row {
|
|
|
954 |
interactive=False,
|
955 |
wrap=True
|
956 |
)
|
957 |
+
with gr.TabItem("프로젝트 소개") as tab_intro:
|
958 |
+
gr.Markdown("""
|
959 |
+
# 디지털 굿판 프로젝트
|
960 |
+
|
961 |
+
본 사업은 전통 굿의 요소와 현대 기술을 결합해 참여자들이 자연과 깊이 연결되며 내면을 탐색하고 치유하는 경험을 제공하는 다원예술 프로젝트입니다.
|
962 |
+
|
963 |
+
금샘과 온천천의 생명 창조 신화를 배경으로, 참여자들은 자연의 소리에 귀를 기울이고, 신화에 몰입하며, 감정을 표현하는 과정에서 개인적 및 공동체적 치유의 여정을 걷습니다.
|
964 |
+
|
965 |
+
AI와 사운드스케이프 같은 현대 기술, 장소성, 신화적 요소가 어우러져 삶과 예술, 자연과 기술이 조화를 이루는 체험 공간을 창조합니다.
|
966 |
+
|
967 |
+
이 프로젝트는 현대 사회의 삶에 대한 근본적인 질문을 던지며 새로운 문화적 지향점을 모색합니다.
|
968 |
+
|
969 |
+
## 크레딧
|
970 |
+
- **기획**: 루츠리딤, 전승아
|
971 |
+
- **음악**: 루츠리딤 (이광혁)
|
972 |
+
- **미디어아트**: 송지훈
|
973 |
+
""")
|
974 |
|
975 |
# 이벤트 핸들러들
|
976 |
def handle_name_submit(name, state):
|
|
|
1044 |
return state, []
|
1045 |
|
1046 |
try:
|
1047 |
+
current_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
1048 |
+
name = state.get("user_name", "익명")
|
1049 |
+
|
1050 |
if text_analyzer:
|
1051 |
sentiment = text_analyzer(text)[0]
|
1052 |
sentiment_text = f"{sentiment['label']} ({sentiment['score']:.2f})"
|
|
|
1053 |
else:
|
1054 |
sentiment_text = "분석 불가"
|
1055 |
+
|
1056 |
+
# DB에 저장
|
1057 |
+
db.save_reflection(name, text, sentiment_text)
|
|
|
|
|
|
|
1058 |
|
1059 |
+
# 화면에는 현재 사용자의 입력만 표시
|
1060 |
+
return state, [[current_time, text, sentiment_text]]
|
1061 |
+
|
1062 |
except Exception as e:
|
1063 |
print(f"Error saving reflection: {e}")
|
1064 |
+
return state, []
|
1065 |
|
1066 |
def save_reflection_fixed(text, state):
|
1067 |
if not text.strip():
|
|
|
1117 |
print(f"Error saving wish: {e}")
|
1118 |
return "오류가 발생했습니다.", []
|
1119 |
|
1120 |
+
def safe_analyze_voice(audio_data, state):
|
1121 |
if audio_data is None:
|
1122 |
+
return state, "음성을 먼저 녹음해주세요.", "", "", "", gr.update(visible=False)
|
1123 |
|
1124 |
try:
|
1125 |
+
processing_status = gr.Markdown("분석 중입니다...", visible=True)
|
1126 |
+
|
1127 |
+
# 음성 데이터 전처리 확인
|
1128 |
+
sr, y = audio_data
|
1129 |
+
if len(y) == 0:
|
1130 |
+
return state, "음성이 감지되지 않았습니다.", "", "", "", gr.update(visible=False)
|
1131 |
+
|
1132 |
+
result = analyze_voice(audio_data, state)
|
1133 |
+
if result[1] == "음성 인식 실패":
|
1134 |
+
return state, "음성 인식에 실패했습니다. 다시 시도해주세요.", "", "", "", gr.update(visible=False)
|
1135 |
+
|
1136 |
+
processing_status.update(visible=False)
|
1137 |
+
return result
|
1138 |
+
|
1139 |
except Exception as e:
|
1140 |
print(f"Voice analysis error: {str(e)}")
|
1141 |
return (
|
1142 |
+
state,
|
1143 |
"음성 분석 중 오류가 발생했습니다. 다시 시도해주세요.",
|
1144 |
"",
|
1145 |
"",
|
1146 |
"",
|
1147 |
+
gr.update(visible=False)
|
1148 |
)
|
1149 |
|
1150 |
|