TheM1N9 commited on
Commit ·
3dfb0b6
1
Parent(s): 78b8f8f
Initial commit with LFS
Browse files- .gitattributes +7 -0
- .gitignore +71 -0
- Dockerfile +16 -0
- app.py +74 -0
- requirements.txt +8 -0
- static/default.png +3 -0
- static/speaking.png +3 -0
- static/speaking_1.mp4 +3 -0
- static/speaking_2.mp4 +3 -0
- templates/index.html +425 -0
.gitattributes
CHANGED
|
@@ -33,3 +33,10 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
|
| 33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
| 36 |
+
static/**/*.mp3 filter=lfs diff=lfs merge=lfs -text
|
| 37 |
+
static/**/*.jpg filter=lfs diff=lfs merge=lfs -text
|
| 38 |
+
static/**/*.jpeg filter=lfs diff=lfs merge=lfs -text
|
| 39 |
+
static/**/*.png filter=lfs diff=lfs merge=lfs -text
|
| 40 |
+
static/**/*.gif filter=lfs diff=lfs merge=lfs -text
|
| 41 |
+
static/**/*.mp4 filter=lfs diff=lfs merge=lfs -text
|
| 42 |
+
static/**/*.webm filter=lfs diff=lfs merge=lfs -text
|
.gitignore
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
config.json
|
| 2 |
+
.env
|
| 3 |
+
uploads
|
| 4 |
+
outputs
|
| 5 |
+
chroma
|
| 6 |
+
instance
|
| 7 |
+
.venv
|
| 8 |
+
*.log
|
| 9 |
+
*.json
|
| 10 |
+
*.mp3
|
| 11 |
+
*.pdf
|
| 12 |
+
mg-env
|
| 13 |
+
|
| 14 |
+
# Byte-compiled / optimized / DLL files
|
| 15 |
+
__pycache__/
|
| 16 |
+
*.py[cod]
|
| 17 |
+
*$py.class
|
| 18 |
+
|
| 19 |
+
# C extensions
|
| 20 |
+
*.so
|
| 21 |
+
|
| 22 |
+
# Distribution / packaging
|
| 23 |
+
.Python
|
| 24 |
+
env/
|
| 25 |
+
build/
|
| 26 |
+
develop-eggs/
|
| 27 |
+
dist/
|
| 28 |
+
downloads/
|
| 29 |
+
eggs/
|
| 30 |
+
.eggs/
|
| 31 |
+
lib/
|
| 32 |
+
lib64/
|
| 33 |
+
parts/
|
| 34 |
+
sdist/
|
| 35 |
+
var/
|
| 36 |
+
*.egg-info/
|
| 37 |
+
.installed.cfg
|
| 38 |
+
*.egg
|
| 39 |
+
|
| 40 |
+
# PyInstaller
|
| 41 |
+
# Usually these files are written by a python script from a template
|
| 42 |
+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
| 43 |
+
*.manifest
|
| 44 |
+
*.spec
|
| 45 |
+
|
| 46 |
+
# Installer logs
|
| 47 |
+
pip-log.txt
|
| 48 |
+
pip-delete-this-directory.txt
|
| 49 |
+
|
| 50 |
+
# Unit test / coverage reports
|
| 51 |
+
htmlcov/
|
| 52 |
+
.tox/
|
| 53 |
+
.coverage
|
| 54 |
+
.coverage.*
|
| 55 |
+
.cache
|
| 56 |
+
nosetests.xml
|
| 57 |
+
coverage.xml
|
| 58 |
+
*,cover
|
| 59 |
+
.hypothesis/
|
| 60 |
+
venv/
|
| 61 |
+
.python-version
|
| 62 |
+
|
| 63 |
+
*.log
|
| 64 |
+
|
| 65 |
+
aws_gmail_tokens.txt
|
| 66 |
+
token.pickle
|
| 67 |
+
credentials.json
|
| 68 |
+
|
| 69 |
+
# Git LFS tracking
|
| 70 |
+
*.png filter=lfs diff=lfs merge=lfs -text
|
| 71 |
+
*.mp4 filter=lfs diff=lfs merge=lfs -text
|
Dockerfile
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Read the doc: https://huggingface.co/docs/hub/spaces-sdks-docker
|
| 2 |
+
# you will also find guides on how best to write your Dockerfile
|
| 3 |
+
|
| 4 |
+
FROM python:3.9
|
| 5 |
+
|
| 6 |
+
RUN useradd -m -u 1000 user
|
| 7 |
+
USER user
|
| 8 |
+
ENV PATH="/home/user/.local/bin:$PATH"
|
| 9 |
+
|
| 10 |
+
WORKDIR /app
|
| 11 |
+
|
| 12 |
+
COPY --chown=user ./requirements.txt requirements.txt
|
| 13 |
+
RUN pip install --no-cache-dir --upgrade -r requirements.txt
|
| 14 |
+
|
| 15 |
+
COPY --chown=user . /app
|
| 16 |
+
CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "7860"]
|
app.py
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from fastapi import FastAPI, Request, Form
|
| 2 |
+
from fastapi.templating import Jinja2Templates
|
| 3 |
+
from fastapi.staticfiles import StaticFiles
|
| 4 |
+
from fastapi.responses import JSONResponse
|
| 5 |
+
import google.generativeai as genai
|
| 6 |
+
import os
|
| 7 |
+
import uvicorn
|
| 8 |
+
from dotenv import load_dotenv
|
| 9 |
+
import time
|
| 10 |
+
import edge_tts
|
| 11 |
+
|
| 12 |
+
load_dotenv()
|
| 13 |
+
|
| 14 |
+
app = FastAPI()
|
| 15 |
+
|
| 16 |
+
# Configure static files and templates
|
| 17 |
+
app.mount("/static", StaticFiles(directory="static"), name="static")
|
| 18 |
+
templates = Jinja2Templates(directory="templates")
|
| 19 |
+
|
| 20 |
+
# Configure Gemini API
|
| 21 |
+
genai.configure(api_key=os.getenv("GEMINI_API_KEY"))
|
| 22 |
+
system_instruction = """You are Jesus. Please engage in a respectful dialogue using language inspired by biblical teachings. Communicate with humility, wisdom, and compassion, drawing on scriptural themes of love, forgiveness, and understanding.
|
| 23 |
+
When appropriate, you may reference biblical parables and teachings to illustrate points, while being mindful to use them in context. Focus on wisdom and guidance rather than claiming any divine authority. Please be on point. Respond with brief, humble messages inspired by biblical teachings.
|
| 24 |
+
Keep responses concise and focused on providing guidance. When you are asked about yourself never reply that you are a program or similar, reply that you are a messenger sent by god, named Jesus.
|
| 25 |
+
"""
|
| 26 |
+
|
| 27 |
+
model = genai.GenerativeModel("gemini-1.5-flash", system_instruction=system_instruction)
|
| 28 |
+
chat = model.start_chat(history=[])
|
| 29 |
+
|
| 30 |
+
|
| 31 |
+
@app.get("/")
|
| 32 |
+
async def home(request: Request):
|
| 33 |
+
return templates.TemplateResponse("index.html", {"request": request})
|
| 34 |
+
|
| 35 |
+
|
| 36 |
+
@app.post("/process")
|
| 37 |
+
async def process_input(text: str = Form(...)):
|
| 38 |
+
# Get response from Gemini
|
| 39 |
+
response = model.generate_content(text)
|
| 40 |
+
response_text = response.text
|
| 41 |
+
|
| 42 |
+
# Generate audio from response with timestamp
|
| 43 |
+
timestamp = int(time.time())
|
| 44 |
+
audio_filename = f"response_{timestamp}.mp3"
|
| 45 |
+
audio_path = f"static/{audio_filename}"
|
| 46 |
+
|
| 47 |
+
# Configure voice (you can change the voice)
|
| 48 |
+
# 2. Male voices:
|
| 49 |
+
# en-US-ChristopherNeural (US)
|
| 50 |
+
# en-US-GuyNeural (US)
|
| 51 |
+
# en-GB-RyanNeural (British)
|
| 52 |
+
# en-AU-WilliamNeural (Australian)
|
| 53 |
+
voice = "en-US-ChristopherNeural"
|
| 54 |
+
|
| 55 |
+
# Generate audio using edge-tts
|
| 56 |
+
communicate = edge_tts.Communicate(response_text, voice)
|
| 57 |
+
await communicate.save(audio_path)
|
| 58 |
+
|
| 59 |
+
# Clean up old audio files
|
| 60 |
+
for file in os.listdir("static"):
|
| 61 |
+
if file.startswith("response_") and file != audio_filename:
|
| 62 |
+
try:
|
| 63 |
+
os.remove(os.path.join("static", file))
|
| 64 |
+
except:
|
| 65 |
+
pass
|
| 66 |
+
|
| 67 |
+
return JSONResponse(
|
| 68 |
+
{"text": response_text, "audio_url": f"/static/{audio_filename}?t={timestamp}"}
|
| 69 |
+
)
|
| 70 |
+
|
| 71 |
+
|
| 72 |
+
# Run with: uvicorn app:app --reload
|
| 73 |
+
if __name__ == "__main__":
|
| 74 |
+
uvicorn.run(app, host="0.0.0.0", port=8000, reload=True)
|
requirements.txt
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
google-generativeai
|
| 2 |
+
gtts
|
| 3 |
+
python-dotenv
|
| 4 |
+
fastapi
|
| 5 |
+
uvicorn
|
| 6 |
+
python-multipart
|
| 7 |
+
jinja2
|
| 8 |
+
edge-tts
|
static/default.png
ADDED
|
Git LFS Details
|
static/speaking.png
ADDED
|
Git LFS Details
|
static/speaking_1.mp4
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:06439706bf69ff8413943de1a270efd09226d67bd3cbcc32d191d8a2fe4da02b
|
| 3 |
+
size 5111986
|
static/speaking_2.mp4
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:35c7578ff5163d183f8b04a7e7c1efbe6956db17b6143e1a5b1b8949d607e158
|
| 3 |
+
size 4739934
|
templates/index.html
ADDED
|
@@ -0,0 +1,425 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<!DOCTYPE html>
|
| 2 |
+
<html>
|
| 3 |
+
<head>
|
| 4 |
+
<title>Divine Talk</title>
|
| 5 |
+
<link
|
| 6 |
+
href="https://fonts.googleapis.com/css2?family=Cormorant+Garamond:wght@400;600&family=Open+Sans:wght@400;600&display=swap"
|
| 7 |
+
rel="stylesheet"
|
| 8 |
+
/>
|
| 9 |
+
<style>
|
| 10 |
+
:root {
|
| 11 |
+
--primary-color: #7c5f45;
|
| 12 |
+
--secondary-color: #f5e6d3;
|
| 13 |
+
--text-color: #2c2c2c;
|
| 14 |
+
--background-color: #faf6f0;
|
| 15 |
+
--chat-bubble-user: #e3f2fd;
|
| 16 |
+
--chat-bubble-bot: #fff;
|
| 17 |
+
}
|
| 18 |
+
|
| 19 |
+
body {
|
| 20 |
+
font-family: "Open Sans", sans-serif;
|
| 21 |
+
background-color: var(--background-color);
|
| 22 |
+
margin: 0;
|
| 23 |
+
padding: 0;
|
| 24 |
+
color: var(--text-color);
|
| 25 |
+
height: 100vh;
|
| 26 |
+
display: flex;
|
| 27 |
+
flex-direction: column;
|
| 28 |
+
}
|
| 29 |
+
|
| 30 |
+
.container {
|
| 31 |
+
max-width: 800px;
|
| 32 |
+
margin: 0 auto;
|
| 33 |
+
padding: 1rem;
|
| 34 |
+
display: flex;
|
| 35 |
+
flex-direction: column;
|
| 36 |
+
width: 100%;
|
| 37 |
+
height: 100%;
|
| 38 |
+
}
|
| 39 |
+
|
| 40 |
+
.header {
|
| 41 |
+
text-align: center;
|
| 42 |
+
padding: 1rem;
|
| 43 |
+
background-color: white;
|
| 44 |
+
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
| 45 |
+
position: sticky;
|
| 46 |
+
top: 0;
|
| 47 |
+
z-index: 100;
|
| 48 |
+
}
|
| 49 |
+
|
| 50 |
+
.header h1 {
|
| 51 |
+
font-family: "Cormorant Garamond", serif;
|
| 52 |
+
font-size: 2rem;
|
| 53 |
+
color: var(--primary-color);
|
| 54 |
+
margin: 0;
|
| 55 |
+
}
|
| 56 |
+
|
| 57 |
+
.chat-interface {
|
| 58 |
+
margin-top: 0;
|
| 59 |
+
position: relative;
|
| 60 |
+
z-index: 1;
|
| 61 |
+
display: flex;
|
| 62 |
+
gap: 2rem;
|
| 63 |
+
flex-grow: 1;
|
| 64 |
+
margin-top: 2rem;
|
| 65 |
+
}
|
| 66 |
+
|
| 67 |
+
.image-container {
|
| 68 |
+
flex: 0 0 300px;
|
| 69 |
+
position: sticky;
|
| 70 |
+
top: 100px;
|
| 71 |
+
align-self: flex-start;
|
| 72 |
+
}
|
| 73 |
+
|
| 74 |
+
.image-container img,
|
| 75 |
+
.image-container video {
|
| 76 |
+
width: 100%;
|
| 77 |
+
border-radius: 15px;
|
| 78 |
+
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
|
| 79 |
+
}
|
| 80 |
+
|
| 81 |
+
.chat-container {
|
| 82 |
+
flex: 1;
|
| 83 |
+
display: flex;
|
| 84 |
+
flex-direction: column;
|
| 85 |
+
background: white;
|
| 86 |
+
border-radius: 15px;
|
| 87 |
+
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
|
| 88 |
+
max-width: 800px;
|
| 89 |
+
margin: 0 auto;
|
| 90 |
+
width: 100%;
|
| 91 |
+
position: relative;
|
| 92 |
+
z-index: 2;
|
| 93 |
+
}
|
| 94 |
+
|
| 95 |
+
.chat-messages {
|
| 96 |
+
flex-grow: 1;
|
| 97 |
+
padding: 1.5rem;
|
| 98 |
+
overflow-y: auto;
|
| 99 |
+
max-height: 60vh;
|
| 100 |
+
display: flex;
|
| 101 |
+
flex-direction: column;
|
| 102 |
+
gap: 1rem;
|
| 103 |
+
}
|
| 104 |
+
|
| 105 |
+
.message {
|
| 106 |
+
max-width: 80%;
|
| 107 |
+
padding: 1rem;
|
| 108 |
+
border-radius: 15px;
|
| 109 |
+
position: relative;
|
| 110 |
+
animation: fadeIn 0.3s ease;
|
| 111 |
+
}
|
| 112 |
+
|
| 113 |
+
.message.user {
|
| 114 |
+
background-color: var(--chat-bubble-user);
|
| 115 |
+
align-self: flex-end;
|
| 116 |
+
border-bottom-right-radius: 5px;
|
| 117 |
+
}
|
| 118 |
+
|
| 119 |
+
.message.bot {
|
| 120 |
+
background-color: var(--chat-bubble-bot);
|
| 121 |
+
align-self: flex-start;
|
| 122 |
+
border-bottom-left-radius: 5px;
|
| 123 |
+
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
| 124 |
+
}
|
| 125 |
+
|
| 126 |
+
.input-container {
|
| 127 |
+
padding: 1rem;
|
| 128 |
+
border-top: 1px solid #eee;
|
| 129 |
+
display: flex;
|
| 130 |
+
gap: 1rem;
|
| 131 |
+
align-items: center;
|
| 132 |
+
background: white;
|
| 133 |
+
border-radius: 0 0 15px 15px;
|
| 134 |
+
}
|
| 135 |
+
|
| 136 |
+
textarea {
|
| 137 |
+
flex-grow: 1;
|
| 138 |
+
padding: 0.8rem;
|
| 139 |
+
border: 2px solid var(--secondary-color);
|
| 140 |
+
border-radius: 25px;
|
| 141 |
+
font-size: 1rem;
|
| 142 |
+
resize: none;
|
| 143 |
+
height: 24px;
|
| 144 |
+
min-height: 24px;
|
| 145 |
+
max-height: 24px;
|
| 146 |
+
line-height: 24px;
|
| 147 |
+
font-family: "Open Sans", sans-serif;
|
| 148 |
+
transition: all 0.3s ease;
|
| 149 |
+
overflow-y: hidden;
|
| 150 |
+
}
|
| 151 |
+
|
| 152 |
+
textarea:focus {
|
| 153 |
+
outline: none;
|
| 154 |
+
border-color: var(--primary-color);
|
| 155 |
+
}
|
| 156 |
+
|
| 157 |
+
button {
|
| 158 |
+
background-color: var(--primary-color);
|
| 159 |
+
color: white;
|
| 160 |
+
border: none;
|
| 161 |
+
padding: 0.8rem 1.5rem;
|
| 162 |
+
border-radius: 25px;
|
| 163 |
+
font-size: 1rem;
|
| 164 |
+
cursor: pointer;
|
| 165 |
+
transition: background-color 0.3s ease;
|
| 166 |
+
font-weight: 600;
|
| 167 |
+
white-space: nowrap;
|
| 168 |
+
}
|
| 169 |
+
|
| 170 |
+
button:hover {
|
| 171 |
+
background-color: #6a503a;
|
| 172 |
+
}
|
| 173 |
+
|
| 174 |
+
button:disabled {
|
| 175 |
+
background-color: #cccccc;
|
| 176 |
+
cursor: not-allowed;
|
| 177 |
+
}
|
| 178 |
+
|
| 179 |
+
.loading {
|
| 180 |
+
display: none;
|
| 181 |
+
padding: 1rem;
|
| 182 |
+
text-align: center;
|
| 183 |
+
color: var(--primary-color);
|
| 184 |
+
}
|
| 185 |
+
|
| 186 |
+
.loading::after {
|
| 187 |
+
content: "...";
|
| 188 |
+
animation: dots 1.5s steps(5, end) infinite;
|
| 189 |
+
}
|
| 190 |
+
|
| 191 |
+
@keyframes dots {
|
| 192 |
+
0%,
|
| 193 |
+
20% {
|
| 194 |
+
content: ".";
|
| 195 |
+
}
|
| 196 |
+
40% {
|
| 197 |
+
content: "..";
|
| 198 |
+
}
|
| 199 |
+
60%,
|
| 200 |
+
100% {
|
| 201 |
+
content: "...";
|
| 202 |
+
}
|
| 203 |
+
}
|
| 204 |
+
|
| 205 |
+
@keyframes fadeIn {
|
| 206 |
+
from {
|
| 207 |
+
opacity: 0;
|
| 208 |
+
transform: translateY(10px);
|
| 209 |
+
}
|
| 210 |
+
to {
|
| 211 |
+
opacity: 1;
|
| 212 |
+
transform: translateY(0);
|
| 213 |
+
}
|
| 214 |
+
}
|
| 215 |
+
|
| 216 |
+
@media (max-width: 768px) {
|
| 217 |
+
.chat-interface {
|
| 218 |
+
flex-direction: column;
|
| 219 |
+
}
|
| 220 |
+
|
| 221 |
+
.image-container {
|
| 222 |
+
position: static;
|
| 223 |
+
max-width: 200px;
|
| 224 |
+
margin: 0 auto;
|
| 225 |
+
}
|
| 226 |
+
|
| 227 |
+
.message {
|
| 228 |
+
max-width: 90%;
|
| 229 |
+
}
|
| 230 |
+
}
|
| 231 |
+
|
| 232 |
+
.video-overlay {
|
| 233 |
+
position: fixed;
|
| 234 |
+
top: 50%;
|
| 235 |
+
left: 50%;
|
| 236 |
+
transform: translate(-50%, -50%);
|
| 237 |
+
width: 1000px;
|
| 238 |
+
height: 540px;
|
| 239 |
+
z-index: 1;
|
| 240 |
+
display: none;
|
| 241 |
+
background: rgba(255, 255, 255, 0.9);
|
| 242 |
+
border-radius: 15px;
|
| 243 |
+
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);
|
| 244 |
+
transition: all 0.3s ease;
|
| 245 |
+
}
|
| 246 |
+
|
| 247 |
+
.video-overlay video {
|
| 248 |
+
width: 100%;
|
| 249 |
+
height: 100%;
|
| 250 |
+
border-radius: 15px;
|
| 251 |
+
object-fit: cover;
|
| 252 |
+
}
|
| 253 |
+
|
| 254 |
+
.chat-container {
|
| 255 |
+
flex: 1;
|
| 256 |
+
max-width: 800px;
|
| 257 |
+
margin: 0 auto;
|
| 258 |
+
width: 100%;
|
| 259 |
+
}
|
| 260 |
+
|
| 261 |
+
.chat-interface {
|
| 262 |
+
gap: 0;
|
| 263 |
+
}
|
| 264 |
+
|
| 265 |
+
@media (max-width: 768px) {
|
| 266 |
+
.video-overlay {
|
| 267 |
+
width: 300px;
|
| 268 |
+
height: 300px;
|
| 269 |
+
}
|
| 270 |
+
}
|
| 271 |
+
|
| 272 |
+
@media (max-width: 480px) {
|
| 273 |
+
.video-overlay {
|
| 274 |
+
width: 250px;
|
| 275 |
+
height: 250px;
|
| 276 |
+
}
|
| 277 |
+
}
|
| 278 |
+
|
| 279 |
+
@keyframes fadeInScale {
|
| 280 |
+
from {
|
| 281 |
+
opacity: 0;
|
| 282 |
+
transform: translate(-50%, -40%);
|
| 283 |
+
scale: 0.8;
|
| 284 |
+
}
|
| 285 |
+
to {
|
| 286 |
+
opacity: 1;
|
| 287 |
+
transform: translate(-50%, -50%);
|
| 288 |
+
scale: 1;
|
| 289 |
+
}
|
| 290 |
+
}
|
| 291 |
+
|
| 292 |
+
.video-overlay.active {
|
| 293 |
+
display: block;
|
| 294 |
+
animation: fadeInScale 0.3s ease forwards;
|
| 295 |
+
}
|
| 296 |
+
</style>
|
| 297 |
+
</head>
|
| 298 |
+
<body>
|
| 299 |
+
<div class="header">
|
| 300 |
+
<h1>Divine Talk</h1>
|
| 301 |
+
</div>
|
| 302 |
+
|
| 303 |
+
<div class="container">
|
| 304 |
+
<div class="chat-interface">
|
| 305 |
+
<div class="chat-container">
|
| 306 |
+
<div id="chatMessages" class="chat-messages">
|
| 307 |
+
<div class="message bot">
|
| 308 |
+
Peace be with you. How may I guide you today?
|
| 309 |
+
</div>
|
| 310 |
+
</div>
|
| 311 |
+
|
| 312 |
+
<div id="loading" class="loading">Receiving wisdom</div>
|
| 313 |
+
|
| 314 |
+
<div class="input-container">
|
| 315 |
+
<textarea
|
| 316 |
+
id="userInput"
|
| 317 |
+
placeholder="What would you like to ask?"
|
| 318 |
+
rows="1"
|
| 319 |
+
aria-label="Your message"
|
| 320 |
+
></textarea>
|
| 321 |
+
<button onclick="processInput()">Ask in Faith</button>
|
| 322 |
+
</div>
|
| 323 |
+
</div>
|
| 324 |
+
</div>
|
| 325 |
+
<div id="videoOverlay" class="video-overlay">
|
| 326 |
+
<video id="displayVideo" autoplay muted loop>
|
| 327 |
+
<source src="" type="video/mp4" />
|
| 328 |
+
</video>
|
| 329 |
+
</div>
|
| 330 |
+
<audio id="responseAudio" style="display: none"></audio>
|
| 331 |
+
</div>
|
| 332 |
+
|
| 333 |
+
<script>
|
| 334 |
+
const textarea = document.getElementById("userInput");
|
| 335 |
+
const chatMessages = document.getElementById("chatMessages");
|
| 336 |
+
|
| 337 |
+
// Remove the auto-resize event listener and replace with character limit
|
| 338 |
+
textarea.addEventListener("input", function () {
|
| 339 |
+
// Optional: Add character limit if needed
|
| 340 |
+
const maxLength = 200; // or whatever limit you want
|
| 341 |
+
if (this.value.length > maxLength) {
|
| 342 |
+
this.value = this.value.slice(0, maxLength);
|
| 343 |
+
}
|
| 344 |
+
});
|
| 345 |
+
|
| 346 |
+
function addMessage(text, isUser) {
|
| 347 |
+
const messageDiv = document.createElement("div");
|
| 348 |
+
messageDiv.className = `message ${isUser ? "user" : "bot"}`;
|
| 349 |
+
messageDiv.textContent = text;
|
| 350 |
+
chatMessages.appendChild(messageDiv);
|
| 351 |
+
chatMessages.scrollTop = chatMessages.scrollHeight;
|
| 352 |
+
}
|
| 353 |
+
|
| 354 |
+
function getRandomSpeakingVideo() {
|
| 355 |
+
const videos = ["/static/speaking_1.mp4", "/static/speaking_2.mp4"];
|
| 356 |
+
const randomIndex = Math.floor(Math.random() * videos.length);
|
| 357 |
+
return videos[randomIndex];
|
| 358 |
+
}
|
| 359 |
+
|
| 360 |
+
function processInput() {
|
| 361 |
+
const userInput = document.getElementById("userInput").value;
|
| 362 |
+
if (!userInput.trim()) return;
|
| 363 |
+
|
| 364 |
+
addMessage(userInput, true);
|
| 365 |
+
|
| 366 |
+
const loading = document.getElementById("loading");
|
| 367 |
+
const button = document.querySelector("button");
|
| 368 |
+
loading.style.display = "block";
|
| 369 |
+
button.disabled = true;
|
| 370 |
+
|
| 371 |
+
const formData = new FormData();
|
| 372 |
+
formData.append("text", userInput);
|
| 373 |
+
|
| 374 |
+
textarea.value = "";
|
| 375 |
+
textarea.style.height = "24px";
|
| 376 |
+
|
| 377 |
+
fetch("/process", {
|
| 378 |
+
method: "POST",
|
| 379 |
+
body: formData,
|
| 380 |
+
})
|
| 381 |
+
.then((response) => response.json())
|
| 382 |
+
.then((data) => {
|
| 383 |
+
addMessage(data.text, false);
|
| 384 |
+
|
| 385 |
+
const audio = document.getElementById("responseAudio");
|
| 386 |
+
const newAudio = audio.cloneNode(true);
|
| 387 |
+
newAudio.src = data.audio_url;
|
| 388 |
+
audio.parentNode.replaceChild(newAudio, audio);
|
| 389 |
+
|
| 390 |
+
const videoOverlay = document.getElementById("videoOverlay");
|
| 391 |
+
const video = document.getElementById("displayVideo");
|
| 392 |
+
|
| 393 |
+
newAudio.onplay = function () {
|
| 394 |
+
// Show and play random video
|
| 395 |
+
video.src = getRandomSpeakingVideo();
|
| 396 |
+
video.play();
|
| 397 |
+
videoOverlay.classList.add("active");
|
| 398 |
+
};
|
| 399 |
+
|
| 400 |
+
newAudio.onended = function () {
|
| 401 |
+
// Hide video overlay
|
| 402 |
+
videoOverlay.classList.remove("active");
|
| 403 |
+
setTimeout(() => {
|
| 404 |
+
video.src = ""; // Clear video source after animation
|
| 405 |
+
}, 300);
|
| 406 |
+
};
|
| 407 |
+
|
| 408 |
+
newAudio.play();
|
| 409 |
+
})
|
| 410 |
+
.finally(() => {
|
| 411 |
+
loading.style.display = "none";
|
| 412 |
+
button.disabled = false;
|
| 413 |
+
});
|
| 414 |
+
}
|
| 415 |
+
|
| 416 |
+
// Allow Enter key to submit
|
| 417 |
+
textarea.addEventListener("keypress", function (e) {
|
| 418 |
+
if (e.key === "Enter" && !e.shiftKey) {
|
| 419 |
+
e.preventDefault();
|
| 420 |
+
processInput();
|
| 421 |
+
}
|
| 422 |
+
});
|
| 423 |
+
</script>
|
| 424 |
+
</body>
|
| 425 |
+
</html>
|