File size: 8,878 Bytes
0a161b3 d447614 b38a110 0a161b3 c475e1b d447614 0a161b3 d447614 0a161b3 39318a3 0a161b3 d447614 0a161b3 d447614 0a161b3 d447614 0a161b3 d447614 b38a110 0a161b3 d447614 0a161b3 d447614 0a161b3 b38a110 0a161b3 b38a110 0a161b3 d447614 0a161b3 b38a110 d447614 0a161b3 d447614 0a161b3 d447614 0a161b3 d447614 0a161b3 c475e1b 9c10d09 b38a110 9c10d09 963bae6 d447614 a3bfbf8 c475e1b b38a110 d447614 a3bfbf8 9c10d09 a3bfbf8 d447614 c475e1b 9c10d09 b38a110 a3bfbf8 c475e1b d447614 a3bfbf8 d447614 be945e1 9c10d09 a3bfbf8 d447614 9c10d09 a3bfbf8 c475e1b 39318a3 f32b751 39318a3 f32b751 b5f7bb8 f32b751 0a161b3 d447614 0a161b3 d447614 0a161b3 d447614 b38a110 0a161b3 d447614 b38a110 0a161b3 b38a110 0a161b3 77854c5 b38a110 77854c5 b38a110 77854c5 d447614 b38a110 d447614 0a161b3 77854c5 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 | import os
import assemblyai as aai
import gradio as gr
import requests
import time
from supabase import create_client, Client
# --------------------------------------------------
# Load secrets from environment
# --------------------------------------------------
ASSEMBLYAI_API_KEY = os.environ.get("ASSEMBLYAI_API_KEY")
SUPABASE_URL = os.environ.get("SUPABASE_URL")
SUPABASE_KEY = os.environ.get("SUPABASE_KEY")
WEBHOOK_URL = os.environ.get(
"N8N_WEBHOOK_URL",
"http://13.48.132.18:5678/webhook-test/report-writing"
)
if not ASSEMBLYAI_API_KEY:
raise RuntimeError("ASSEMBLYAI_API_KEY not set")
if not SUPABASE_URL or not SUPABASE_KEY:
raise RuntimeError("SUPABASE_URL or SUPABASE_KEY not set")
# --------------------------------------------------
# Initialize clients
# --------------------------------------------------
aai.settings.api_key = ASSEMBLYAI_API_KEY
supabase: Client = create_client(SUPABASE_URL, SUPABASE_KEY)
# --------------------------------------------------
# In-memory session
# --------------------------------------------------
INACTIVITY_TIMEOUT = 30 * 60 # 30 minutes in seconds
session = {"user": None, "user_id": None, "last_activity": None}
# --------------------------------------------------
# Check for existing Supabase session on startup
# --------------------------------------------------
def restore_session():
try:
user = supabase.auth.get_user()
if user and user.user:
session["user"] = user.user.email
session["user_id"] = user.user.id
return True
except Exception as e:
print(f"β οΈ Session restore error: {e}")
return False
# Restore session on app startup
restore_session()
# --------------------------------------------------
# Inactivity timeout check
# --------------------------------------------------
def check_inactivity():
"""Check if user has been inactive for 30 mins, auto-logout if true"""
if session["user_id"] is None:
return False
if session["last_activity"] is None:
return False
elapsed = time.time() - session["last_activity"]
if elapsed > INACTIVITY_TIMEOUT:
# Auto logout
try:
supabase.auth.sign_out()
except:
pass
session["user"] = None
session["user_id"] = None
session["last_activity"] = None
return True # Timed out
return False
def update_activity():
"""Update last activity timestamp"""
session["last_activity"] = time.time()
# --------------------------------------------------
# Auth functions
# --------------------------------------------------
def signup(email, password):
try:
res = supabase.auth.sign_up({"email": email, "password": password})
if res.user:
session["user"] = res.user.email
session["user_id"] = res.user.id
session["last_activity"] = time.time()
return (
"β
Signup successful.",
gr.update(visible=False), # hide auth
gr.update(visible=True) # show transcription
)
return "β Signup failed.", gr.update(), gr.update()
except Exception as e:
return f"β Signup error: {str(e)}", gr.update(), gr.update()
def login(email, password):
try:
res = supabase.auth.sign_in_with_password({"email": email, "password": password})
if res.user:
session["user"] = res.user.email
session["user_id"] = res.user.id
session["last_activity"] = time.time()
return (
"β
Login successful.",
gr.update(visible=False), # hide auth
gr.update(visible=True) # show transcription
)
return "β Invalid credentials.", gr.update(), gr.update()
except Exception as e:
return f"β Login error: {str(e)}", gr.update(), gr.update()
def logout():
try:
supabase.auth.sign_out()
session["user"] = None
session["user_id"] = None
return (
"β
Logged out successfully.",
gr.update(visible=True), # show auth
gr.update(visible=False) # hide transcription
)
except Exception as e:
return f"β Logout error: {str(e)}", gr.update(), gr.update()
# --------------------------------------------------
# Transcription logic
# --------------------------------------------------
def transcribe(audio_path: str):
transcriber = aai.Transcriber()
transcript = transcriber.transcribe(audio_path)
if transcript.status == aai.TranscriptStatus.error:
raise RuntimeError(transcript.error)
return transcript.text, transcript.audio_duration
def transcribe_audio(audio_file):
# Check for inactivity timeout
if check_inactivity():
return "β Session expired due to inactivity. Please log in again."
if session["user_id"] is None:
return "β Please log in."
if audio_file is None:
return "β οΈ Please upload an audio file."
try:
# Update activity timestamp
update_activity()
# Transcribe audio
text, duration_seconds = transcribe(audio_file)
minutes_used = max(1, int(duration_seconds // 60))
# Save transcription
supabase.table("transcriptions").insert({
"user_id": session["user_id"],
"user_email": session["user"],
"audio_duration": minutes_used,
"transcript": text
}).execute()
# Update users_usage
existing = supabase.table("users_usage").select("minutes_used").eq("user_id", session["user_id"]).execute()
if existing.data:
supabase.table("users_usage").update({
"minutes_used": existing.data[0]["minutes_used"] + minutes_used
}).eq("user_id", session["user_id"]).execute()
else:
supabase.table("users_usage").insert({
"user_id": session["user_id"],
"minutes_used": minutes_used
}).execute()
# Send to N8N webhook using GET
try:
payload = {
"transcriptions": text,
"user_email": session["user"]
}
resp = requests.get(WEBHOOK_URL, params=payload)
if not resp.ok:
print(f"β οΈ Webhook failed: {resp.status_code} {resp.text}")
except Exception as e:
print(f"β οΈ Webhook error: {e}")
return text
except Exception as e:
return f"β Error: {str(e)}"
# --------------------------------------------------
# Gradio UI
# --------------------------------------------------
with gr.Blocks() as app:
# π AUTH SECTION
with gr.Column(visible=not restore_session()) as auth_section:
gr.Markdown("## π Login or Signup")
email = gr.Textbox(label="Email")
password = gr.Textbox(label="Password", type="password")
auth_status = gr.Textbox(label="Status")
with gr.Row():
login_btn = gr.Button("Login")
signup_btn = gr.Button("Signup")
# ποΈ TRANSCRIPTION SECTION (hidden initially)
with gr.Column(visible=restore_session()) as transcription_section:
gr.Markdown("## ποΈ AI Transcription")
gr.Markdown("Upload audio β get transcript")
with gr.Row():
logout_btn = gr.Button("πͺ Logout", scale=1)
audio_input = gr.Audio(type="filepath", label="Upload Audio")
output_text = gr.Textbox(label="Transcript", lines=10)
transcribe_btn = gr.Button("Transcribe")
transcribe_btn.click(transcribe_audio, audio_input, output_text)
# π EMBEDDED N8N FORM
gr.Markdown("## π Site Report Form")
gr.HTML(
'<iframe src="http://13.48.132.18:5678/form/SITEREPORT" '
'width="100%" height="700" frameborder="0" '
'allow="same-origin" allowfullscreen '
'style="border: 1px solid #ccc; border-radius: 5px; background: white;"></iframe>'
)
logout_status = gr.Textbox(label="Status", visible=False)
# --------------------------------------------------
# Button bindings
login_btn.click(
login,
[email, password],
[auth_status, auth_section, transcription_section]
)
signup_btn.click(
signup,
[email, password],
[auth_status, auth_section, transcription_section]
)
logout_btn.click(
logout,
None,
[logout_status, transcription_section, auth_section]
)
# --------------------------------------------------
# Launch
# --------------------------------------------------
app.launch(
server_name="0.0.0.0",
server_port=7860,
show_error=True
) |