Pratap2002 commited on
Commit
aabae34
Β·
verified Β·
1 Parent(s): 3b5da43

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +508 -368
app.py CHANGED
@@ -1,368 +1,508 @@
1
- import streamlit as st
2
- import argparse
3
- import requests
4
- import os
5
- from gtts import gTTS
6
- from PIL import Image, ImageDraw, ImageFont, ImageFilter
7
- from io import BytesIO
8
- import time
9
- import json
10
- from moviepy.editor import *
11
- from moviepy.video.tools.segmenting import findObjects
12
- from groq import Groq
13
- import numpy as np
14
- import random
15
- import base64
16
- #from huggingface_hub import InferenceClient
17
- import torch
18
- #from diffusers import CogVideoXImageToVideoPipeline
19
- #from diffusers.utils import export_to_video, load_image
20
- from scipy.ndimage import zoom
21
- from together import Together
22
-
23
- # Set page config
24
- st.set_page_config(
25
- page_title="AI Video Ad Generator",
26
- page_icon="πŸŽ₯",
27
- layout="wide"
28
- )
29
-
30
- # Add custom CSS
31
- st.markdown("""
32
- <style>
33
- .stButton>button {
34
- width: 100%;
35
- height: 3em;
36
- background-color: #FF4B4B;
37
- color: white;
38
- }
39
- .stTextInput>div>div>input {
40
- background-color: #f0f2f6;
41
- }
42
- </style>
43
- """, unsafe_allow_html=True)
44
-
45
- # API Keys
46
- GROQ_API_KEY = "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"
47
- TOGETHER_API_KEY = "dff7082474cf3265ac478ad65fb984f6f1bb773026ca099903291518bc76810a" # Replace with your Together AI API key
48
-
49
- def text_to_speech(text, output_path):
50
- tts = gTTS(text)
51
- tts.save(output_path)
52
-
53
- def generate_image(prompt, max_retries=3, delay=5):
54
- client = Together(api_key=TOGETHER_API_KEY)
55
-
56
- for attempt in range(max_retries):
57
- try:
58
- response = client.images.generate(
59
- prompt=prompt,
60
- model="black-forest-labs/FLUX.1-schnell-Free",
61
- width=1024,
62
- height=1024,
63
- steps=4,
64
- n=1,
65
- response_format="b64_json"
66
- )
67
-
68
- image_data = base64.b64decode(response.data[0].b64_json)
69
- image = Image.open(BytesIO(image_data))
70
-
71
- print(f"Successfully generated image for prompt: {prompt}")
72
- return image
73
- except Exception as e:
74
- print(f"Error generating image (attempt {attempt + 1}/{max_retries}): {e}")
75
- if attempt < max_retries - 1:
76
- wait_time = delay + random.uniform(0, 2)
77
- print(f"Waiting for {wait_time:.2f} seconds before retrying...")
78
- time.sleep(wait_time)
79
- else:
80
- print("Max retries reached. Returning default image.")
81
- return Image.new('RGB', (1024, 1024), color='red')
82
-
83
- def generate_script(product_description):
84
- client = Groq(api_key=GROQ_API_KEY)
85
- prompt = f"""Create a 20-second advertisement script based on this description: {product_description}
86
-
87
- Create 3-4 scenes that total exactly 20 seconds. For each scene include:
88
- 1. A detailed visual description for image generation
89
- 2. Voice-over narration text (break into individual words for timed display)
90
- 3. On-screen caption text
91
- 4. Duration in seconds (use only numbers, e.g. 5 not '5 seconds')
92
-
93
- Format each scene like this:
94
- SCENE 1
95
- Visual: [detailed description]
96
- Narration: [voice-over text]
97
- Caption: [on-screen text]
98
- Duration: [number]
99
- Format the output as JSON with this structure:
100
- {{
101
- "scenes": [
102
- {{
103
- "image_description": "detailed visual description",
104
- "narration": "voiceover text",
105
- "caption": "overlay text",
106
- "words": ["word1", "word2", "word3"]
107
- }}
108
- ]
109
- }}
110
-
111
- Make the scenes flow naturally and build excitement."""
112
-
113
- response = client.chat.completions.create(
114
- messages=[
115
- {"role": "system", "content": "You are a professional advertising scriptwriter who creates compelling visual and narrative scripts."},
116
- {"role": "user", "content": prompt}
117
- ],
118
- model="llama-3.1-70b-versatile",
119
- max_tokens=1000
120
- )
121
-
122
- try:
123
- script_data = json.loads(response.choices[0].message.content)
124
- for scene in script_data['scenes']:
125
- scene['words'] = scene['narration'].split()
126
- except json.JSONDecodeError:
127
- script_data = {
128
- "scenes": [
129
- {
130
- "image_description": f"Professional product shot of {product_description} on clean white background with dramatic lighting and some text on the image and do not show the playbutton signe in the image",
131
- "narration": f"Introducing the revolutionary {product_description}",
132
- "caption": "Discover Innovation",
133
- "words": [f"Introducing", "the", "revolutionary", product_description]
134
- },
135
- {
136
- "image_description": f"Close-up shot showing key features of {product_description} with hands demonstrating use and some text on the image and do not show the playbutton signe in the image",
137
- "narration": "Experience unmatched quality and performance",
138
- "caption": "Superior Features",
139
- "words": ["Experience", "unmatched", "quality", "and", "performance"]
140
- },
141
- {
142
- "image_description": f"Lifestyle shot of people using {product_description} in real-world setting, warm lighting and some text on the image and do not show the playbutton signe in the image",
143
- "narration": "Transform your daily life",
144
- "caption": "Real Results",
145
- "words": ["Transform", "your", "daily", "life"]
146
- },
147
- {
148
- "image_description": f"Dynamic product shot of {product_description} with call-to-action graphics and some text on the image and do not show the playbutton signe in the image",
149
- "narration": "Don't wait - get yours today!",
150
- "caption": "Order Now",
151
- "words": ["Don't", "wait", "get", "yours", "today"]
152
- }
153
- ]
154
- }
155
-
156
- return json.dumps(script_data)
157
-
158
- def create_text_image(text, size, font_color, font_path, font_size=80):
159
- padding = 40
160
- img = Image.new('RGBA', (size[0] + padding * 2, size[1] + padding * 2), (0, 0, 0, 0))
161
- draw = ImageDraw.Draw(img)
162
-
163
- try:
164
- font = ImageFont.truetype(font_path, font_size, encoding="unic")
165
- except IOError:
166
- print(f"Warning: Could not load {font_path}. Using default font.")
167
- font = ImageFont.load_default().font_variant(size=font_size)
168
-
169
- text_bbox = draw.textbbox((0, 0), text, font=font)
170
- text_width = text_bbox[2] - text_bbox[0]
171
- text_height = text_bbox[3] - text_bbox[1]
172
- x_text = (size[0] - text_width) / 2
173
- y_text = (size[1] - text_height) / 2
174
-
175
- # Draw text stroke
176
- stroke_color = "black"
177
- for adj in range(-3, 4):
178
- draw.text((x_text+adj, y_text), text, font=font, fill=stroke_color)
179
- draw.text((x_text, y_text+adj), text, font=font, fill=stroke_color)
180
-
181
- draw.text((x_text, y_text), text, font=font, fill=font_color)
182
-
183
- img = img.filter(ImageFilter.GaussianBlur(radius=1))
184
- return np.array(img)
185
-
186
- def image_to_video_ml(image, prompt, num_frames=49, fps=8):
187
- try:
188
- image_np = np.array(image) / 255.0
189
-
190
- def create_animation(frame):
191
- # Enhanced zoom animation
192
- zoom_factor = 1 + 0.15 * np.sin(frame * 2 * np.pi / num_frames)
193
- zoomed = zoom(image_np, (zoom_factor, zoom_factor, 1), order=1)
194
-
195
- # Center crop
196
- crop_start = [(zoomed.shape[i] - image_np.shape[i]) // 2 for i in range(2)]
197
- cropped = zoomed[crop_start[0]:crop_start[0]+image_np.shape[0],
198
- crop_start[1]:crop_start[1]+image_np.shape[1]]
199
-
200
- # Enhanced pan animation
201
- pan_x = int(80 * np.sin(frame * 2 * np.pi / num_frames))
202
- pan_y = int(80 * np.cos(frame * 2 * np.pi / num_frames))
203
- panned = np.roll(cropped, shift=(pan_y, pan_x), axis=(0, 1))
204
-
205
- # Add rotation
206
- angle = 5 * np.sin(frame * 2 * np.pi / num_frames)
207
- rotated = rotate(panned, angle, reshape=False)
208
-
209
- return rotated
210
-
211
- animated_frames = []
212
- for i in range(num_frames):
213
- frame = create_animation(i)
214
- if frame.shape[2] == 3:
215
- animated_frames.append(frame)
216
- else:
217
- print(f"Skipping frame {i} due to unexpected shape: {frame.shape}")
218
-
219
- if len(animated_frames) == 0:
220
- raise ValueError("No valid frames were generated")
221
-
222
- animated_frames = np.array(animated_frames)
223
- noise = np.random.normal(0, 0.03, animated_frames.shape)
224
- animated_frames = np.clip(animated_frames + noise, 0, 1)
225
- animated_frames = (animated_frames * 255).astype(np.uint8)
226
-
227
- temp_video_path = f"temp_video_{int(time.time())}.mp4"
228
- export_to_video(animated_frames, temp_video_path, fps=fps)
229
-
230
- return temp_video_path
231
- except Exception as e:
232
- print(f"Error in image_to_video_ml: {e}")
233
- return None
234
-
235
- def generate_video(product_description, output_path, duration, font_color, font_path):
236
- script_json = generate_script(product_description)
237
- script_data = json.loads(script_json)
238
- scenes = script_data['scenes']
239
-
240
- print(f"Generated script with {len(scenes)} scenes")
241
-
242
- full_narration = " ".join([scene['narration'] for scene in scenes])
243
- audio_path = "audio.mp3"
244
- text_to_speech(full_narration, audio_path)
245
-
246
- print(f"Generated audio: {audio_path}")
247
-
248
- scene_images = []
249
- for scene in scenes:
250
- image = generate_image(scene['image_description'])
251
- scene_images.append(image)
252
-
253
- print(f"Generated {len(scene_images)} images for scenes")
254
-
255
- video_clips = []
256
- scene_duration = duration / len(scenes)
257
- for i, (scene, image) in enumerate(zip(scenes, scene_images)):
258
- video_path = image_to_video_ml(image, scene['image_description'], num_frames=int(scene_duration * 24), fps=24)
259
- if video_path:
260
- video_clip = VideoFileClip(video_path)
261
- # Add flip effect
262
- if i % 2 == 0: # Alternate between horizontal and vertical flips
263
- video_clip = video_clip.fx(vfx.mirror_x)
264
- else:
265
- video_clip = video_clip.fx(vfx.mirror_y)
266
- video_clips.append(video_clip)
267
- else:
268
- # print(f"Failed to generate video for scene {i+1}. Using static image instead.")
269
- video_clip = ImageClip(np.array(image)).set_duration(scene_duration)
270
- video_clips.append(video_clip)
271
-
272
- # Create word-by-word text clips
273
- text_clips = []
274
- current_time = 0
275
- for i, scene in enumerate(scenes):
276
- words = scene['words']
277
- word_duration = scene_duration / len(words)
278
-
279
- for word in words:
280
- text_image = create_text_image(word, scene_images[0].size, font_color, font_path, font_size=100)
281
- text_clip = ImageClip(text_image).set_duration(word_duration)
282
- text_clip = text_clip.set_start(current_time)
283
- text_clip = text_clip.set_position('center')
284
-
285
- # Add random animation effect for each word
286
- effect = random.choice(['zoom', 'rotate', 'slide'])
287
- if effect == 'zoom':
288
- text_clip = text_clip.resize(lambda t: 1 + 0.3 * np.sin(t * 2 * np.pi))
289
- elif effect == 'rotate':
290
- text_clip = text_clip.rotate(lambda t: 20 * np.sin(t * 2 * np.pi))
291
- else: # slide
292
- text_clip = text_clip.set_position(lambda t: ('center', 540 + 50 * np.sin(t * 2 * np.pi)))
293
-
294
- text_clip = text_clip.fadein(0.2).fadeout(0.2)
295
- text_clips.append(text_clip)
296
- current_time += word_duration
297
-
298
- final_video = concatenate_videoclips(video_clips)
299
- final_video = CompositeVideoClip([final_video] + text_clips)
300
- audio_clip = AudioFileClip(audio_path)
301
- final_clip = final_video.set_audio(audio_clip)
302
-
303
- def vignette(image):
304
- y, x = np.ogrid[:image.shape[0], :image.shape[1]]
305
- center_x, center_y = image.shape[1] / 2, image.shape[0] / 2
306
- mask = ((x - center_x)**2 + (y - center_y)**2) > (center_x**2 + center_y**2)
307
- image[mask] = image[mask] * 0.8
308
- return image
309
-
310
- final_clip = final_clip.fl_image(vignette)
311
- final_clip.write_videofile(output_path, fps=24)
312
-
313
- os.remove(audio_path)
314
- for clip in video_clips:
315
- if hasattr(clip, 'filename') and os.path.exists(clip.filename):
316
- os.remove(clip.filename)
317
-
318
- print("Video generation completed successfully")
319
- return True
320
-
321
- def main():
322
- st.title("πŸŽ₯ AI Video Ad Generator")
323
- st.markdown("### Create professional video ads with AI")
324
-
325
- with st.container():
326
- col1, col2 = st.columns([2,1])
327
-
328
- with col1:
329
- product_description = st.text_area(
330
- "Product Description",
331
- placeholder="Enter a detailed description of your product and its key features...",
332
- height=150
333
- )
334
-
335
- with col2:
336
- output_path = st.text_input("Output Filename", value="ad.mp4")
337
- duration = st.slider("Video Duration (seconds)", min_value=10, max_value=60, value=20)
338
- font_color = st.color_picker("Text Color", value="#FFFFFF")
339
- font_path = st.selectbox("Font Style", ["arial.ttf", "times.ttf", "calibri.ttf"])
340
-
341
- if st.button("πŸš€ Generate Video Ad", type="primary"):
342
- if not product_description:
343
- st.error("Please enter a product description")
344
- return
345
-
346
- with st.spinner("Generating your video ad..."):
347
- success = generate_video(product_description, output_path, duration, font_color, font_path)
348
-
349
- if success:
350
- st.success(f"Video generated successfully!")
351
-
352
- # Display the video
353
- video_file = open(output_path, 'rb')
354
- video_bytes = video_file.read()
355
- st.video(video_bytes)
356
-
357
- # Download button
358
- st.download_button(
359
- label="Download Video",
360
- data=video_bytes,
361
- file_name=output_path,
362
- mime="video/mp4"
363
- )
364
- else:
365
- st.error("Failed to generate video. Please try again.")
366
-
367
- if __name__ == "__main__":
368
- main()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ from dataclasses import dataclass
3
+ from typing import List, Dict, Optional
4
+ from datetime import datetime
5
+ import google.generativeai as genai
6
+ import os
7
+ from dotenv import load_dotenv
8
+ from PIL import Image
9
+ from langchain_groq import ChatGroq
10
+ from langchain.schema import HumanMessage, SystemMessage
11
+ import torch
12
+ import numpy as np
13
+ import plotly.graph_objects as go
14
+ from fpdf import FPDF
15
+ import time
16
+
17
+ # Load environment variables
18
+ load_dotenv()
19
+
20
+ # Configure LLMs
21
+ llm = ChatGroq(
22
+ temperature=0.2,
23
+ groq_api_key=os.environ.get("GROQ_API_KEY"),
24
+ model_name="llama-3.1-70b-versatile"
25
+ )
26
+
27
+ genai.configure(api_key=os.getenv("GOOGLE_API_KEY"))
28
+ model = genai.GenerativeModel(
29
+ model_name="gemini-1.5-pro",
30
+ generation_config={
31
+ "temperature": 0.2,
32
+ "top_p": 0.8,
33
+ "top_k": 40,
34
+ "max_output_tokens": 2048,
35
+ }
36
+ )
37
+
38
+ # Initialize session state for realtime.py components
39
+ if 'chat_history' not in st.session_state:
40
+ st.session_state.chat_history = []
41
+ if 'user_data' not in st.session_state:
42
+ st.session_state.user_data = {
43
+ 'progress': [],
44
+ 'meal_logs': [],
45
+ 'workout_logs': []
46
+ }
47
+
48
+ # Code from acountable.py
49
+ @dataclass
50
+ class TransformationJourney:
51
+ user_id: str
52
+ goal_type: str # fitness, skill, personal growth etc
53
+ start_date: datetime
54
+ progress_images: List[Dict[str, str]] # [{date: image_path}]
55
+ feedback_history: List[Dict[str, str]] # [{date: feedback}]
56
+ chat_history: List[Dict[str, str]] # [{role: content}]
57
+
58
+ class TransformationCoach:
59
+ def __init__(self):
60
+ self.model = model
61
+ self.llm = llm
62
+
63
+ def analyze_image(self, image: Image) -> str:
64
+ prompt = """
65
+ As a transformation coach, analyze this image in relation to the user's goals.
66
+ Provide detailed observations about:
67
+ 1. Current state/progress visible in the image
68
+ 2. Areas of improvement
69
+ 3. Notable achievements
70
+ Be specific and objective in your analysis.
71
+ """
72
+ response = self.model.generate_content([prompt, image])
73
+ return response.text
74
+
75
+ def generate_feedback(self, analysis: str, goal_type: str) -> Dict[str, List[str]]:
76
+ prompt = f"""
77
+ Based on the following analysis of the user's progress image for their {goal_type} transformation:
78
+ {analysis}
79
+
80
+ Provide structured feedback in the following format:
81
+ 1. Specific "DO" recommendations (3-5 points)
82
+ 2. Specific "DON'T" recommendations (2-3 points)
83
+ 3. A motivational message
84
+ Make all feedback actionable and tailored to the analysis.
85
+ """
86
+ response = self.model.generate_content(prompt)
87
+ feedback_parts = response.text.split("\n\n")
88
+ return {
89
+ "dos": feedback_parts[0].split("\n")[1:],
90
+ "donts": feedback_parts[1].split("\n")[1:],
91
+ "motivation": feedback_parts[2]
92
+ }
93
+
94
+ def chat_response(self, question: str, context: List[Dict[str, str]], analysis: str = None) -> str:
95
+ prompt = f"""
96
+ You are a supportive transformation coach. Based on the following context:
97
+
98
+ Previous chat: {str(context)}
99
+ Latest analysis: {analysis if analysis else 'No recent analysis'}
100
+
101
+ User question: {question}
102
+
103
+ Provide a helpful, encouraging response focused on their transformation journey.
104
+ """
105
+ response = self.llm.invoke(prompt)
106
+ return response.content
107
+
108
+ # Functions from realtime.py
109
+ def load_llm():
110
+ api_key = os.environ.get("GROQ_API_KEY")
111
+ if api_key is None:
112
+ st.error("GROQ_API_KEY is not set in the environment variables.")
113
+ return None
114
+ llm = ChatGroq(
115
+ api_key=api_key,
116
+ model_name="llama-3.1-70b-versatile",
117
+ temperature=0.7,
118
+ max_tokens=2048
119
+ )
120
+ return llm
121
+
122
+ def analyze_image_realtime(image, task="meal"):
123
+ try:
124
+ if task == "meal":
125
+ prompt = """
126
+ As a nutrition expert, analyze this meal image and provide:
127
+ 1. Estimated caloric content
128
+ 2. Macro-nutrient breakdown
129
+ 3. Nutritional assessment
130
+ 4. Suggestions for improvement
131
+ 5. Any potential health concerns
132
+ Be specific and detailed in your analysis.
133
+ """
134
+ else: # workout form analysis
135
+ prompt = """
136
+ As a fitness expert, analyze this workout form and provide:
137
+ 1. Form assessment
138
+ 2. Potential injury risks
139
+ 3. Specific corrections needed
140
+ 4. Benefits of proper form
141
+ Be specific and detailed in your analysis.
142
+ """
143
+ response = model.generate_content([prompt, image])
144
+ return response.text
145
+ except Exception as e:
146
+ return f"Error analyzing image: {str(e)}"
147
+
148
+ def track_progress(metric, value, date=None):
149
+ if date is None:
150
+ date = datetime.now().strftime("%Y-%m-%d")
151
+
152
+ st.session_state.user_data['progress'].append({
153
+ 'date': date,
154
+ 'metric': metric,
155
+ 'value': value
156
+ })
157
+
158
+ def display_progress_chart():
159
+ if not st.session_state.user_data['progress']:
160
+ return
161
+
162
+ data = st.session_state.user_data['progress']
163
+ fig = go.Figure()
164
+
165
+ metrics = set(item['metric'] for item in data)
166
+ for metric in metrics:
167
+ metric_data = [item for item in data if item['metric'] == metric]
168
+ dates = [item['date'] for item in metric_data]
169
+ values = [item['value'] for item in metric_data]
170
+
171
+ fig.add_trace(go.Scatter(x=dates, y=values, name=metric))
172
+
173
+ fig.update_layout(title="Your Progress Over Time",
174
+ xaxis_title="Date",
175
+ yaxis_title="Value")
176
+ st.plotly_chart(fig)
177
+
178
+ # Functions from workout.py
179
+ def generate_meal_plan(weight, height, age, gender, activity_level, goal, dietary_restrictions, health_history, current_eating_habits, meal_preparation, budget, sleep):
180
+ """Generate personalized meal plan using LLM"""
181
+ prompt = f"""
182
+ Based on the following user data, generate a highly personalized and detailed daily meal plan with easily available ingredients:
183
+
184
+ User Profile:
185
+ - Weight: {weight} kg
186
+ - Height: {height} cm
187
+ - Age: {age}
188
+ - Gender: {gender}
189
+ - Activity Level: {activity_level}
190
+ - Goal: {goal}
191
+ - Dietary Restrictions: {', '.join(dietary_restrictions)}
192
+ - Health History: {health_history}
193
+ - Current Eating Habits: {current_eating_habits}
194
+ - Meal Preparation: {meal_preparation}
195
+ - Budget: {budget}
196
+ - Sleep: {sleep}
197
+
198
+ Generate a clear and structured meal plan with:
199
+
200
+ 1. Total Daily Calorie Requirements
201
+ 2. Macronutrient Distribution (in grams):
202
+ - Protein
203
+ - Carbohydrates
204
+ - Fats
205
+
206
+ Daily Meal Schedule:
207
+ [Detailed meal schedule content...]
208
+ """
209
+
210
+ response = llm.invoke(prompt)
211
+ return response.content
212
+
213
+ def generate_workout_plan(goal, activity_level, target_areas, workout_time, current_fitness_level, exercise_preferences):
214
+ """Generate personalized workout plan using LLM"""
215
+ prompt = f"""
216
+ Based on:
217
+ - Fitness Goal: {goal}
218
+ - Activity Level: {activity_level}
219
+ - Target Areas: {target_areas}
220
+ - Workout Time: {workout_time}
221
+ - Current Fitness Level: {current_fitness_level}
222
+ - Exercise Preferences: {exercise_preferences}
223
+
224
+ Generate a clear and structured weekly workout plan:
225
+ [Detailed workout plan content...]
226
+ """
227
+
228
+ response = llm.invoke(prompt)
229
+ return response.content
230
+
231
+ def create_pdf(name, meal_plan, workout_plan):
232
+ pdf = FPDF()
233
+ pdf.add_page()
234
+
235
+ pdf.set_font('Arial', 'B', 20)
236
+ pdf.cell(0, 10, f'Personalized Fitness Plan for {name}', ln=True, align='C')
237
+ pdf.ln(10)
238
+
239
+ pdf.set_font('Arial', 'B', 16)
240
+ pdf.cell(0, 10, 'Meal Plan', ln=True)
241
+ pdf.set_font('Arial', '', 12)
242
+ pdf.multi_cell(0, 10, meal_plan)
243
+
244
+ pdf.add_page()
245
+ pdf.set_font('Arial', 'B', 16)
246
+ pdf.cell(0, 10, 'Workout Plan', ln=True)
247
+ pdf.set_font('Arial', '', 12)
248
+ pdf.multi_cell(0, 10, workout_plan)
249
+
250
+ return pdf
251
+
252
+ def main():
253
+ st.set_page_config(page_title="AI Fitness Coach", layout="wide")
254
+
255
+ # Chat input needs to be outside tabs
256
+ user_input = st.chat_input("Type your message here...")
257
+ if user_input:
258
+ st.session_state.chat_history.append({"role": "user", "content": user_input})
259
+
260
+ # Process user input with LLM
261
+ llm = load_llm()
262
+ if llm is not None:
263
+ messages = [
264
+ SystemMessage(content="You are an expert health and fitness coach. Provide detailed and personalized advice."),
265
+ HumanMessage(content=user_input)
266
+ ]
267
+ response = llm(messages)
268
+
269
+ st.session_state.chat_history.append({"role": "assistant", "content": response.content})
270
+
271
+ tab1, tab2, tab3 = st.tabs(["Transformation Journey", "Realtime Tracking", "Workout & Meal Plans"])
272
+
273
+ with tab1:
274
+ # Implementation from acountable.py
275
+ st.title("Transformation Journey Tracker")
276
+
277
+ if 'coach' not in st.session_state:
278
+ st.session_state.coach = TransformationCoach()
279
+ if 'journey' not in st.session_state:
280
+ st.session_state.journey = None
281
+
282
+ # Initialize or load journey
283
+ if not st.session_state.journey:
284
+ with st.form("journey_setup"):
285
+ st.write("Let's start your transformation journey!")
286
+ user_id = st.text_input("Enter your user ID")
287
+ goal_type = st.selectbox(
288
+ "What type of transformation?",
289
+ ["Fitness", "Skill Development", "Personal Growth", "Other"]
290
+ )
291
+ if st.form_submit_button("Start Journey"):
292
+ st.session_state.journey = TransformationJourney(
293
+ user_id=user_id,
294
+ goal_type=goal_type,
295
+ start_date=datetime.now(),
296
+ progress_images=[],
297
+ feedback_history=[],
298
+ chat_history=[]
299
+ )
300
+
301
+ # Main interaction area
302
+ if st.session_state.journey:
303
+ st.subheader("Upload Progress Image")
304
+ uploaded_file = st.file_uploader("Choose an image...", type=["jpg", "jpeg", "png"], key="transform_uploader")
305
+
306
+ if uploaded_file:
307
+ image = Image.open(uploaded_file)
308
+ st.image(image, caption="Uploaded Image", use_column_width=True)
309
+
310
+ if st.button("Analyze and Get Feedback"):
311
+ with st.spinner("Analyzing your progress..."):
312
+ analysis = st.session_state.coach.analyze_image(image)
313
+ feedback = st.session_state.coach.generate_feedback(
314
+ analysis,
315
+ st.session_state.journey.goal_type
316
+ )
317
+
318
+ # Display feedback
319
+ st.subheader("Your Personalized Feedback")
320
+
321
+ st.write("🎯 DO:")
322
+ for do in feedback["dos"]:
323
+ st.write(f"βœ“ {do}")
324
+
325
+ st.write("⚠️ DON'T:")
326
+ for dont in feedback["donts"]:
327
+ st.write(f"βœ— {dont}")
328
+
329
+ st.write("πŸ’ͺ Motivation:")
330
+ st.write(feedback["motivation"])
331
+
332
+ # Store progress
333
+ current_time = datetime.now().strftime("%Y-%m-%d_%H-%M-%S")
334
+ image_path = f"progress_images/{st.session_state.journey.user_id}_{current_time}.jpg"
335
+ st.session_state.journey.progress_images.append({
336
+ current_time: image_path
337
+ })
338
+ st.session_state.journey.feedback_history.append({
339
+ current_time: feedback
340
+ })
341
+
342
+ # Chat interface
343
+ st.write("---")
344
+ st.subheader("Chat with Your Coach")
345
+
346
+ # Display chat history
347
+ for message in st.session_state.journey.chat_history:
348
+ if message["role"] == "user":
349
+ st.write("You: " + message["content"])
350
+ else:
351
+ st.write("Coach: " + message["content"])
352
+
353
+ # Chat input
354
+ user_question = st.text_input("Ask your coach anything:", key="transform_chat")
355
+ if st.button("Send", key="transform_send"):
356
+ if user_question:
357
+ # Add user message
358
+ st.session_state.journey.chat_history.append({
359
+ "role": "user",
360
+ "content": user_question
361
+ })
362
+
363
+ # Get coach's response
364
+ latest_analysis = st.session_state.coach.analyze_image(image) if uploaded_file else None
365
+ response = st.session_state.coach.chat_response(
366
+ user_question,
367
+ st.session_state.journey.chat_history,
368
+ latest_analysis
369
+ )
370
+
371
+ # Add coach's response
372
+ st.session_state.journey.chat_history.append({
373
+ "role": "coach",
374
+ "content": response
375
+ })
376
+
377
+ st.experimental_rerun()
378
+
379
+ with tab2:
380
+ # Implementation from realtime.py
381
+ st.title("AI Health Coach")
382
+
383
+ st.warning("""This system is for informational purposes only and is not a substitute for professional medical advice,
384
+ diagnosis, or treatment. Always seek the advice of your physician or other qualified health provider.""")
385
+
386
+ # Display chat history
387
+ for message in st.session_state.chat_history:
388
+ with st.chat_message(message["role"]):
389
+ st.write(message["content"])
390
+
391
+ # Image upload and analysis
392
+ uploaded_file = st.file_uploader("Upload an image for analysis (meal or workout form)", type=['png', 'jpg', 'jpeg'], key="realtime_uploader")
393
+ if uploaded_file:
394
+ image = Image.open(uploaded_file)
395
+ st.image(image, caption="Uploaded Image")
396
+
397
+ analysis_type = st.radio("What would you like to analyze?", ["Meal", "Workout Form"])
398
+ if st.button("Analyze Image", key="realtime_analyze"):
399
+ with st.spinner("Analyzing image..."):
400
+ results = analyze_image_realtime(image, analysis_type.lower())
401
+ st.write("Analysis Results:", results)
402
+
403
+ # Progress tracking section
404
+ with st.expander("Track Your Progress"):
405
+ metric = st.selectbox("Select metric to track", ["Weight", "Body Fat %", "Workout Duration", "Calories"])
406
+ value = st.number_input("Enter value", key="progress_value")
407
+ if st.button("Log Progress", key="log_progress"):
408
+ track_progress(metric, value)
409
+ st.success("Progress logged successfully!")
410
+
411
+ # Display progress charts
412
+ display_progress_chart()
413
+
414
+ with tab3:
415
+ # Implementation from workout.py
416
+ st.title("AI Fitness Coach")
417
+ st.markdown("<h1 style='text-align: center; color: #4CAF50;'>Welcome to Your Personalized Fitness Journey!</h1>", unsafe_allow_html=True)
418
+
419
+ # Get user inputs
420
+ st.header("Personal Information")
421
+
422
+ col1, col2, col3 = st.columns(3)
423
+
424
+ with col1:
425
+ name = st.text_input("What's your name?", placeholder="Enter your name")
426
+ age = st.number_input("What's your age?", min_value=1, max_value=120, value=25)
427
+ gender = st.selectbox("What's your gender?", ["Male", "Female"])
428
+ health_history = st.text_input("Do you have any medical conditions?")
429
+
430
+ with col2:
431
+ weight = st.number_input("What's your weight (in kg)?", min_value=1, value=70)
432
+ height = st.number_input("What's your height (in cm)?", min_value=1, value=170)
433
+ goal = st.selectbox(
434
+ "What's your fitness goal?",
435
+ ["Weight Loss", "Muscle Gain", "Maintenance", "General Fitness"],
436
+ index=0
437
+ )
438
+ activity_level = st.selectbox(
439
+ "What's your activity level?",
440
+ [
441
+ "Sedentary (little to no exercise)",
442
+ "Lightly Active (1-3 days/week)",
443
+ "Moderately Active (3-5 days/week)",
444
+ "Very Active (6-7 days/week)",
445
+ "Extra Active (very intense exercise daily)"
446
+ ]
447
+ )
448
+
449
+ with col3:
450
+ dietary_restrictions = st.multiselect(
451
+ "Do you have any dietary restrictions?",
452
+ ["None", "Vegetarian", "Vegan", "Gluten-Free", "Dairy-Free", "Keto"]
453
+ )
454
+ current_eating_habits = st.text_input("What does your current diet look like?")
455
+ meal_preparation = st.text_input("Do you cook your meals or rely on takeout?")
456
+ budget = st.number_input("What is your monthly budget for groceries/food?", min_value=0)
457
+
458
+ if st.button("Generate Plans", key="generate_plans"):
459
+ if not name or not goal or not age or not gender or not weight or not height or not activity_level:
460
+ st.error("Please fill in all required fields")
461
+ return
462
+
463
+ with st.spinner("Generating your personalized plans..."):
464
+ meal_plan = generate_meal_plan(
465
+ weight=weight,
466
+ height=height,
467
+ age=age,
468
+ gender=gender,
469
+ activity_level=activity_level,
470
+ goal=goal,
471
+ dietary_restrictions=dietary_restrictions if dietary_restrictions else ["None"],
472
+ health_history=health_history,
473
+ current_eating_habits=current_eating_habits,
474
+ meal_preparation=meal_preparation,
475
+ budget=budget,
476
+ sleep="8 hours" # Default value
477
+ )
478
+ workout_plan = generate_workout_plan(
479
+ goal=goal,
480
+ activity_level=activity_level,
481
+ target_areas="full body", # Default value
482
+ workout_time=60, # Default value
483
+ current_fitness_level="Beginner", # Default value
484
+ exercise_preferences="None" # Default value
485
+ )
486
+
487
+ # Create PDF
488
+ pdf = create_pdf(name, meal_plan, workout_plan)
489
+
490
+ # Display plans in two columns
491
+ col1, col2 = st.columns(2)
492
+ with col1:
493
+ st.subheader("Your Meal Plan")
494
+ st.write(meal_plan)
495
+ with col2:
496
+ st.subheader("Your Workout Plan")
497
+ st.write(workout_plan)
498
+
499
+ # Add download button
500
+ st.download_button(
501
+ label="Download Plan as PDF",
502
+ data=pdf.output(dest='S').encode('latin-1'),
503
+ file_name=f"fitness_plan_{name}.pdf",
504
+ mime="application/pdf"
505
+ )
506
+
507
+ if __name__ == "__main__":
508
+ main()