Update app.py
Browse files
app.py
CHANGED
@@ -53,12 +53,18 @@ def serve_wishes(path):
|
|
53 |
|
54 |
|
55 |
class SimpleDB:
|
56 |
-
|
57 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
58 |
self.wishes_path = wishes_path
|
59 |
-
os.makedirs(
|
60 |
-
self.reflections = self._load_json(reflections_path)
|
61 |
self.wishes = self._load_json(wishes_path)
|
|
|
62 |
|
63 |
def _load_json(self, file_path):
|
64 |
if not os.path.exists(file_path):
|
@@ -71,29 +77,28 @@ class SimpleDB:
|
|
71 |
print(f"Error loading {file_path}: {e}")
|
72 |
return []
|
73 |
|
74 |
-
def save_reflection(self, name, reflection, sentiment
|
75 |
-
|
76 |
-
|
77 |
-
|
78 |
reflection_data = {
|
79 |
-
"timestamp":
|
80 |
"name": name,
|
81 |
"reflection": reflection,
|
82 |
"sentiment": sentiment
|
83 |
}
|
84 |
-
|
85 |
-
self.reflections.append(reflection_data)
|
86 |
-
self._save_json(self.reflections_path, self.reflections)
|
87 |
return True
|
88 |
|
89 |
-
def save_wish(self, name, wish, emotion_data=None
|
90 |
-
|
91 |
-
|
|
|
92 |
wish_data = {
|
|
|
93 |
"name": name,
|
94 |
"wish": wish,
|
95 |
-
"emotion": emotion_data
|
96 |
-
"timestamp": timestamp
|
97 |
}
|
98 |
self.wishes.append(wish_data)
|
99 |
self._save_json(self.wishes_path, self.wishes)
|
@@ -108,11 +113,17 @@ class SimpleDB:
|
|
108 |
print(f"Error saving to {file_path}: {e}")
|
109 |
return False
|
110 |
|
111 |
-
def
|
112 |
-
|
|
|
113 |
|
114 |
def get_all_wishes(self):
|
115 |
-
|
|
|
|
|
|
|
|
|
|
|
116 |
|
117 |
# API 설정
|
118 |
HF_API_TOKEN = os.getenv("roots", "")
|
@@ -646,59 +657,142 @@ def create_interface():
|
|
646 |
with blessing_section:
|
647 |
gr.Markdown("### 축원의식을 시작하겠습니다")
|
648 |
gr.Markdown("'명짐 복짐 짊어지고 안가태평하시기를 비도발원 축원 드립니다'")
|
649 |
-
|
650 |
-
|
651 |
-
|
652 |
-
|
653 |
-
|
654 |
-
|
655 |
-
set_baseline_btn = gr.Button("축원 마치기", variant="primary")
|
656 |
-
baseline_status = gr.Markdown("")
|
657 |
-
|
658 |
-
# 4단계: 굿판 입장 안내
|
659 |
-
entry_guide_section = gr.Column(visible=False)
|
660 |
-
with entry_guide_section:
|
661 |
-
gr.Markdown("## 굿판으로 입장하기")
|
662 |
-
gr.Markdown("""
|
663 |
-
* 청신 탭으로 이동해 주세요.
|
664 |
-
* 부산광역시 동래구 온천장역에서 시작하면 더욱 깊은 경험을 시작할 수 있습니다.
|
665 |
-
* (본격적인 경험을 시작하기에 앞서 이동을 권장드립니다)
|
666 |
-
""")
|
667 |
-
enter_btn = gr.Button("청신 의식 시작하기", variant="primary")
|
668 |
-
|
669 |
-
with gr.TabItem("청신") as tab_listen:
|
670 |
-
gr.Markdown("## 청신 - 소리로 정화하기")
|
671 |
-
gr.Markdown("""
|
672 |
-
온천천의 소리를 들으며 마음을 정화해보세요.
|
673 |
-
|
674 |
-
💫 이 앱은 온천천의 사운드스케이프를 녹음하여 제작되었으며,
|
675 |
-
온천천 온천장역에서 장전역까지 걸으며 더 깊은 체험이 가능합니다.
|
676 |
-
""")
|
677 |
-
play_music_btn = gr.Button("온천천의 ���리 듣기", variant="secondary")
|
678 |
-
with gr.Row():
|
679 |
-
audio = gr.Audio(
|
680 |
-
value="assets/main_music.mp3",
|
681 |
-
type="filepath",
|
682 |
-
label="온천천의 소리",
|
683 |
-
interactive=False,
|
684 |
-
show_download_button=True,
|
685 |
-
visible=True
|
686 |
-
)
|
687 |
-
with gr.Column():
|
688 |
-
reflection_input = gr.Textbox(
|
689 |
-
label="지금 이 순간의 감상을 자유롭게 적어보세요",
|
690 |
-
lines=3,
|
691 |
-
max_lines=5
|
692 |
)
|
693 |
-
|
694 |
-
|
695 |
-
|
696 |
-
label="기록된 감상들",
|
697 |
-
value=[], # 초기값은 빈 리스트
|
698 |
-
interactive=False,
|
699 |
-
wrap=True,
|
700 |
-
row_count=(5, "dynamic") # 동적으로 행 수 조정
|
701 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
702 |
|
703 |
# 기원 탭
|
704 |
with gr.TabItem("기원") as tab_wish:
|
|
|
53 |
|
54 |
|
55 |
class SimpleDB:
|
56 |
+
"""데이터 저장 및 관리 클래스"""
|
57 |
+
|
58 |
+
def __init__(self, wishes_path="data/wishes.json"):
|
59 |
+
"""
|
60 |
+
영구 저장이 필요한 소원 데이터만 파일로 관리
|
61 |
+
Args:
|
62 |
+
wishes_path: 소원 데이터 저장 경로
|
63 |
+
"""
|
64 |
self.wishes_path = wishes_path
|
65 |
+
os.makedirs(os.path.dirname(wishes_path), exist_ok=True)
|
|
|
66 |
self.wishes = self._load_json(wishes_path)
|
67 |
+
self.session_reflections = [] # 임시 메모리에만 저장되는 감상
|
68 |
|
69 |
def _load_json(self, file_path):
|
70 |
if not os.path.exists(file_path):
|
|
|
77 |
print(f"Error loading {file_path}: {e}")
|
78 |
return []
|
79 |
|
80 |
+
def save_reflection(self, name, reflection, sentiment):
|
81 |
+
"""
|
82 |
+
감상은 세션 메모리에만 저장
|
83 |
+
"""
|
84 |
reflection_data = {
|
85 |
+
"timestamp": datetime.now().strftime("%H:%M:%S"),
|
86 |
"name": name,
|
87 |
"reflection": reflection,
|
88 |
"sentiment": sentiment
|
89 |
}
|
90 |
+
self.session_reflections.append(reflection_data)
|
|
|
|
|
91 |
return True
|
92 |
|
93 |
+
def save_wish(self, name, wish, emotion_data=None):
|
94 |
+
"""
|
95 |
+
소원은 영구 저장
|
96 |
+
"""
|
97 |
wish_data = {
|
98 |
+
"timestamp": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
|
99 |
"name": name,
|
100 |
"wish": wish,
|
101 |
+
"emotion": emotion_data
|
|
|
102 |
}
|
103 |
self.wishes.append(wish_data)
|
104 |
self._save_json(self.wishes_path, self.wishes)
|
|
|
113 |
print(f"Error saving to {file_path}: {e}")
|
114 |
return False
|
115 |
|
116 |
+
def get_session_reflections(self):
|
117 |
+
"""현재 세션의 감상만 반환"""
|
118 |
+
return self.session_reflections
|
119 |
|
120 |
def get_all_wishes(self):
|
121 |
+
"""저장된 모든 소원 반환"""
|
122 |
+
return sorted(self.wishes, key=lambda x: x["timestamp"], reverse=True)
|
123 |
+
|
124 |
+
def clear_session_data(self):
|
125 |
+
"""세션 데이터 초기화"""
|
126 |
+
self.session_reflections = []
|
127 |
|
128 |
# API 설정
|
129 |
HF_API_TOKEN = os.getenv("roots", "")
|
|
|
657 |
with blessing_section:
|
658 |
gr.Markdown("### 축원의식을 시작하겠습니다")
|
659 |
gr.Markdown("'명짐 복짐 짊어지고 안가태평하시기를 비도발원 축원 드립니다'")
|
660 |
+
with gr.Column() as recording_section:
|
661 |
+
baseline_audio = gr.Audio(
|
662 |
+
label="축원 문장 녹음하기",
|
663 |
+
sources=["microphone"],
|
664 |
+
type="numpy",
|
665 |
+
streaming=False
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
666 |
)
|
667 |
+
analysis_status = gr.Markdown(
|
668 |
+
"축원 문장을 녹음해주세요.",
|
669 |
+
visible=True
|
|
|
|
|
|
|
|
|
|
|
670 |
)
|
671 |
+
|
672 |
+
with gr.Column(visible=False) as analysis_results:
|
673 |
+
voice_analysis = gr.Markdown("")
|
674 |
+
set_baseline_btn = gr.Button(
|
675 |
+
"축원 마치기",
|
676 |
+
variant="primary",
|
677 |
+
visible=False
|
678 |
+
)
|
679 |
+
|
680 |
+
def handle_baseline_recording(audio, state):
|
681 |
+
"""기준 음성 분석"""
|
682 |
+
if audio is None:
|
683 |
+
return {
|
684 |
+
"state": state,
|
685 |
+
"status": "축원 문장을 녹음해주세요.",
|
686 |
+
"analysis_visible": False,
|
687 |
+
"button_visible": False,
|
688 |
+
"analysis_text": ""
|
689 |
+
}
|
690 |
+
|
691 |
+
try:
|
692 |
+
# 분석 중임을 표시
|
693 |
+
yield {
|
694 |
+
"state": state,
|
695 |
+
"status": "음성을 분석하고 있습니다...",
|
696 |
+
"analysis_visible": False,
|
697 |
+
"button_visible": False,
|
698 |
+
"analysis_text": ""
|
699 |
+
}
|
700 |
+
|
701 |
+
# 음성 분석
|
702 |
+
features = calculate_baseline_features(audio)
|
703 |
+
if features is None:
|
704 |
+
return {
|
705 |
+
"state": state,
|
706 |
+
"status": "음성 분석에 실패했습니다. 다시 시도해주세요.",
|
707 |
+
"analysis_visible": False,
|
708 |
+
"button_visible": False,
|
709 |
+
"analysis_text": ""
|
710 |
+
}
|
711 |
+
|
712 |
+
# 감정 분석
|
713 |
+
baseline_emotion = map_acoustic_to_emotion(features)
|
714 |
+
|
715 |
+
# 분석 결과 생성
|
716 |
+
analysis_text = (
|
717 |
+
f"기준 음성 분석 결과:\n"
|
718 |
+
f"- 감정: {baseline_emotion['primary']}\n"
|
719 |
+
f"- 에너지 레벨: {baseline_emotion['details']['energy_level']}\n"
|
720 |
+
f"- 말하기 속도: {baseline_emotion['details']['speech_rate']}\n"
|
721 |
+
f"- 음높이: {baseline_emotion['details']['pitch_variation']}\n"
|
722 |
+
f"이 음성을 기준으로 이후 소원을 비교 분석합니다."
|
723 |
+
)
|
724 |
+
|
725 |
+
# 상태 업데이트
|
726 |
+
new_state = {**state, "baseline_features": features, "analysis_complete": True}
|
727 |
+
|
728 |
+
return {
|
729 |
+
"state": new_state,
|
730 |
+
"status": "",
|
731 |
+
"analysis_visible": True,
|
732 |
+
"button_visible": True,
|
733 |
+
"analysis_text": analysis_text
|
734 |
+
}
|
735 |
+
|
736 |
+
except Exception as e:
|
737 |
+
return {
|
738 |
+
"state": state,
|
739 |
+
"status": f"오류가 발생했습니다: {str(e)}",
|
740 |
+
"analysis_visible": False,
|
741 |
+
"button_visible": False,
|
742 |
+
"analysis_text": ""
|
743 |
+
}
|
744 |
+
|
745 |
+
def handle_blessing_complete(state):
|
746 |
+
"""축원 완료 처리"""
|
747 |
+
if not state.get("analysis_complete"):
|
748 |
+
return state, "음성 분석이 완료되지 않았습니다.", gr.update(visible=True), gr.update(visible=False)
|
749 |
+
|
750 |
+
return state, "", gr.update(visible=False), gr.update(visible=True)
|
751 |
+
|
752 |
+
# 4단계: 굿판 입장 안내
|
753 |
+
entry_guide_section = gr.Column(visible=False)
|
754 |
+
with entry_guide_section:
|
755 |
+
gr.Markdown("## 굿판으로 입장하기")
|
756 |
+
gr.Markdown("""
|
757 |
+
* 청신 탭으로 이동해 주세요.
|
758 |
+
* 부산광역시 동래구 온천장역에서 시작하면 더욱 깊은 경험을 시작할 수 있습니다.
|
759 |
+
* (본격적인 경험을 시작하기에 앞서 이동을 권장드립니다)
|
760 |
+
""")
|
761 |
+
enter_btn = gr.Button("청신 의식 시작하기", variant="primary")
|
762 |
+
|
763 |
+
with gr.TabItem("청신") as tab_listen:
|
764 |
+
gr.Markdown("## 청신 - 소리로 정화하기")
|
765 |
+
gr.Markdown("""
|
766 |
+
온천천의 소리를 들으며 마음을 정화해보세요.
|
767 |
+
|
768 |
+
💫 이 앱은 온천천의 사운드스케이프를 녹음하여 제작되었으며,
|
769 |
+
온천천 온천장역에서 장전역까지 걸으며 더 깊은 체험이 가능합니다.
|
770 |
+
""")
|
771 |
+
play_music_btn = gr.Button("온천천의 소리 듣기", variant="secondary")
|
772 |
+
with gr.Row():
|
773 |
+
audio = gr.Audio(
|
774 |
+
value="assets/main_music.mp3",
|
775 |
+
type="filepath",
|
776 |
+
label="온천천의 소리",
|
777 |
+
interactive=False,
|
778 |
+
show_download_button=True,
|
779 |
+
visible=True
|
780 |
+
)
|
781 |
+
with gr.Column():
|
782 |
+
reflection_input = gr.Textbox(
|
783 |
+
label="지금 이 순간의 감상을 자유롭게 적어보세요",
|
784 |
+
lines=3,
|
785 |
+
max_lines=5
|
786 |
+
)
|
787 |
+
save_btn = gr.Button("감상 저장하기", variant="secondary")
|
788 |
+
reflections_display = gr.Dataframe(
|
789 |
+
headers=["시간", "감상", "감정 분석"],
|
790 |
+
label="기록된 감상들",
|
791 |
+
value=[], # 초기값은 빈 리스트
|
792 |
+
interactive=False,
|
793 |
+
wrap=True,
|
794 |
+
row_count=(5, "dynamic") # 동적으로 행 수 조정
|
795 |
+
)
|
796 |
|
797 |
# 기원 탭
|
798 |
with gr.TabItem("기원") as tab_wish:
|