Spaces:
Sleeping
Sleeping
| # pyrefly: ignore [missing-import] | |
| import streamlit as st | |
| import os | |
| import hashlib | |
| from manager import generate_tour_script | |
| from tts import generate_audio | |
| st.set_page_config(page_title="AI Audio Tour", page_icon="🎧", layout="centered") | |
| def get_cache_key(location, interests, duration, voice_style): | |
| data = f"{location}_{interests}_{duration}_{voice_style}".lower().encode('utf-8') | |
| return hashlib.md5(data).hexdigest() | |
| def main(): | |
| st.title("🎧 Hướng dẫn viên AI Audio Tour") | |
| st.write("Khám phá các địa danh với hướng dẫn viên AI cá nhân hóa.") | |
| # Sidebar | |
| st.sidebar.header("Cài đặt") | |
| api_key_input = st.sidebar.text_input("Gemini API Key", type="password", help="Lấy từ Google AI Studio") | |
| # Save API key to session state | |
| if api_key_input: | |
| st.session_state["api_key"] = api_key_input | |
| st.sidebar.success("Đã lưu API Key tạm thời.") | |
| api_key = st.session_state.get("api_key") or os.environ.get("GEMINI_API_KEY") | |
| voice_style = st.sidebar.selectbox("Giọng đọc", ["Nữ (Tiếng Việt)", "Nam (Tiếng Việt)", "English (Female)", "English (Male)"]) | |
| # Main content | |
| location = st.text_input("Địa danh (Location)", placeholder="Ví dụ: Hội An, Đà Lạt, Hồ Gươm...") | |
| interests_list = st.multiselect( | |
| "Chủ đề quan tâm (Interests)", | |
| options=["Lịch sử (History)", "Văn hóa (Culture)", "Ẩm thực (Culinary)", "Kiến trúc (Architecture)"], | |
| default=["Lịch sử (History)", "Văn hóa (Culture)"] | |
| ) | |
| interests = ", ".join(interests_list) | |
| duration = st.slider("Thời lượng dự kiến (phút)", min_value=5, max_value=60, value=15, step=5) | |
| if st.button("Tạo Audio Tour", type="primary"): | |
| if not location: | |
| st.warning("Vui lòng nhập Địa danh!") | |
| return | |
| if not api_key: | |
| st.warning("Vui lòng nhập Gemini API Key ở thanh bên (Sidebar)!") | |
| return | |
| cache_key = get_cache_key(location, interests, duration, voice_style) | |
| cache_dir = "cache" | |
| os.makedirs(cache_dir, exist_ok=True) | |
| script_file = os.path.join(cache_dir, f"{cache_key}.txt") | |
| audio_file = os.path.join(cache_dir, f"{cache_key}.mp3") | |
| # Check cache | |
| if os.path.exists(script_file) and os.path.exists(audio_file): | |
| st.success("Tải từ bộ nhớ đệm (Cache) thành công! Không tốn quota API.") | |
| with open(script_file, "r", encoding="utf-8") as f: | |
| final_script = f.read() | |
| st.audio(audio_file, format="audio/mp3") | |
| with open(audio_file, "rb") as f: | |
| st.download_button( | |
| label="Tải file âm thanh (.mp3)", | |
| data=f, | |
| file_name=f"AudioTour_{location}.mp3", | |
| mime="audio/mp3" | |
| ) | |
| st.markdown("### Kịch bản Tour") | |
| with st.container(height=300): | |
| st.write(final_script) | |
| return | |
| # Not in cache, generate fresh | |
| try: | |
| with st.spinner("Đang lên kế hoạch và thu thập thông tin từ các chuyên gia (Quá trình này có thể mất 1-2 phút)..."): | |
| final_script = generate_tour_script(api_key, location, interests, duration) | |
| with st.spinner("Đang chuyển đổi văn bản thành giọng nói..."): | |
| success = generate_audio(final_script, voice_style, audio_file) | |
| if success: | |
| # Save script to cache | |
| with open(script_file, "w", encoding="utf-8") as f: | |
| f.write(final_script) | |
| st.success("Hoàn thành! Chúc bạn có một chuyến đi vui vẻ.") | |
| st.audio(audio_file, format="audio/mp3") | |
| with open(audio_file, "rb") as f: | |
| st.download_button( | |
| label="Tải file âm thanh (.mp3)", | |
| data=f, | |
| file_name=f"AudioTour_{location}.mp3", | |
| mime="audio/mp3" | |
| ) | |
| st.markdown("### Kịch bản Tour") | |
| with st.container(height=300): | |
| st.write(final_script) | |
| else: | |
| st.error("Có lỗi xảy ra trong quá trình tạo Audio.") | |
| except Exception as e: | |
| st.error(f"Đã xảy ra lỗi: {e}") | |
| if __name__ == "__main__": | |
| main() | |