awacke1 commited on
Commit
8f04cdd
β€’
1 Parent(s): eddf77b

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +258 -0
app.py ADDED
@@ -0,0 +1,258 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ import base64
3
+ from datetime import datetime
4
+ import plotly.graph_objects as go
5
+ import cv2
6
+ import os
7
+ import pytz
8
+ import random
9
+ import re
10
+ import requests
11
+ from moviepy.editor import VideoFileClip
12
+ from PIL import Image
13
+ import glob
14
+ from audio_recorder_streamlit import audio_recorder
15
+ import json
16
+ from openai import OpenAI
17
+ from dotenv import load_dotenv
18
+
19
+ # Page config
20
+ st.set_page_config(
21
+ page_title="Bike Cinematic Universe 🎬",
22
+ page_icon="🚲",
23
+ layout="wide"
24
+ )
25
+
26
+ # Custom CSS with expanded styling
27
+ st.markdown("""
28
+ <style>
29
+ .main {
30
+ background: linear-gradient(to right, #1a1a1a, #2d2d2d);
31
+ color: #ffffff;
32
+ }
33
+ .stMarkdown {
34
+ font-family: 'Helvetica Neue', sans-serif;
35
+ }
36
+ .category-header {
37
+ background: linear-gradient(45deg, #2b5876, #4e4376);
38
+ padding: 20px;
39
+ border-radius: 10px;
40
+ margin: 10px 0;
41
+ }
42
+ .scene-card {
43
+ background: rgba(0,0,0,0.3);
44
+ padding: 15px;
45
+ border-radius: 8px;
46
+ margin: 10px 0;
47
+ border: 1px solid rgba(255,255,255,0.1);
48
+ }
49
+ .media-gallery {
50
+ display: grid;
51
+ gap: 1rem;
52
+ padding: 1rem;
53
+ }
54
+ .bike-card {
55
+ background: rgba(255,255,255,0.05);
56
+ border-radius: 10px;
57
+ padding: 15px;
58
+ transition: transform 0.3s;
59
+ }
60
+ .bike-card:hover {
61
+ transform: scale(1.02);
62
+ }
63
+ </style>
64
+ """, unsafe_allow_html=True)
65
+
66
+ # Load environment variables
67
+ load_dotenv()
68
+ client = OpenAI(api_key=os.getenv('OPENAI_API_KEY'))
69
+
70
+ # Bike Collections
71
+ bike_collections = {
72
+ "Celestial Collection 🌌": {
73
+ "Eclipse Vaulter": {
74
+ "prompt": """Cinematic shot of a sleek black mountain bike silhouetted against a total solar eclipse.
75
+ The corona creates an ethereal halo effect, with lens flares accentuating key points of the frame.
76
+ Dynamic composition shows the bike mid-leap, with stardust particles trailing behind.
77
+ Camera angle: Low angle, wide shot
78
+ Lighting: Dramatic rim lighting from eclipse
79
+ Color palette: Deep purples, cosmic blues, corona gold""",
80
+ "emoji": "πŸŒ‘"
81
+ },
82
+ "Starlight Leaper": {
83
+ "prompt": """A black bike performing an epic leap under a vast Milky Way galaxy.
84
+ Shimmering stars blanket the sky while the bike's wheels leave a trail of stardust.
85
+ Camera angle: Wide-angle upward shot
86
+ Lighting: Natural starlight with subtle rim lighting
87
+ Color palette: Deep blues, silver highlights, cosmic purples""",
88
+ "emoji": "✨"
89
+ },
90
+ "Moonlit Hopper": {
91
+ "prompt": """A sleek black bike mid-hop over a moonlit meadow.
92
+ Full moon illuminating misty surroundings with fireflies dancing around.
93
+ Camera angle: Side profile with slight low angle
94
+ Lighting: Soft moonlight with atmospheric fog
95
+ Color palette: Silver blues, soft whites, deep shadows""",
96
+ "emoji": "πŸŒ™"
97
+ }
98
+ },
99
+ "Nature-Inspired Collection 🌲": {
100
+ "Shadow Grasshopper": {
101
+ "prompt": """A black bike jumping between forest paths.
102
+ Dappled sunlight streams through the canopy, creating dynamic shadows.
103
+ Camera angle: Through-the-trees tracking shot
104
+ Lighting: Natural forest lighting with sun rays
105
+ Color palette: Forest greens, golden sunlight, deep shadows""",
106
+ "emoji": "πŸ¦—"
107
+ },
108
+ "Onyx Leapfrog": {
109
+ "prompt": """A bike with obsidian-black finish jumping over a sparkling creek.
110
+ Water reflection creates mirror effect with ripples from the leap.
111
+ Camera angle: Low angle from water level
112
+ Lighting: Golden hour side lighting
113
+ Color palette: Deep blacks, water blues, forest greens""",
114
+ "emoji": "🐸"
115
+ }
116
+ }
117
+ }
118
+
119
+ # File handling functions
120
+ def generate_filename(prompt, file_type):
121
+ """Generate a safe filename from prompt and timestamp"""
122
+ central = pytz.timezone('US/Central')
123
+ safe_date_time = datetime.now(central).strftime("%m%d_%H%M")
124
+ replaced_prompt = re.sub(r'[<>:"/\\|?*\n]', ' ', prompt)
125
+ safe_prompt = re.sub(r'\s+', ' ', replaced_prompt).strip()[:240]
126
+ return f"{safe_date_time}_{safe_prompt}.{file_type}"
127
+
128
+ def save_file(content, filename, is_binary=False):
129
+ """Save content to file with proper mode"""
130
+ mode = 'wb' if is_binary else 'w'
131
+ with open(filename, mode) as f:
132
+ f.write(content)
133
+ return filename
134
+
135
+ def process_video(video_path, seconds_per_frame=1):
136
+ """Extract frames and audio from video"""
137
+ base64Frames = []
138
+ video = cv2.VideoCapture(video_path)
139
+ total_frames = int(video.get(cv2.CAP_PROP_FRAME_COUNT))
140
+ fps = video.get(cv2.CAP_PROP_FPS)
141
+ frames_to_skip = int(fps * seconds_per_frame)
142
+
143
+ for frame_idx in range(0, total_frames, frames_to_skip):
144
+ video.set(cv2.CAP_PROP_POS_FRAMES, frame_idx)
145
+ success, frame = video.read()
146
+ if not success:
147
+ break
148
+ _, buffer = cv2.imencode(".jpg", frame)
149
+ base64Frames.append(base64.b64encode(buffer).decode("utf-8"))
150
+
151
+ video.release()
152
+
153
+ # Extract audio
154
+ base_video_path = os.path.splitext(video_path)[0]
155
+ audio_path = f"{base_video_path}.mp3"
156
+ try:
157
+ video_clip = VideoFileClip(video_path)
158
+ video_clip.audio.write_audiofile(audio_path)
159
+ video_clip.close()
160
+ except:
161
+ st.warning("No audio track found in video")
162
+ audio_path = None
163
+
164
+ return base64Frames, audio_path
165
+
166
+ def create_media_gallery():
167
+ st.header("🎬 Media Gallery")
168
+
169
+ tabs = st.tabs(["πŸ–ΌοΈ Images", "🎡 Audio", "πŸŽ₯ Video", "🎨 Scene Generator"])
170
+
171
+ with tabs[0]:
172
+ image_files = glob.glob("*.png") + glob.glob("*.jpg")
173
+ if image_files:
174
+ cols = st.columns(3)
175
+ for idx, image_file in enumerate(image_files):
176
+ with cols[idx % 3]:
177
+ st.image(image_file)
178
+ st.caption(os.path.basename(image_file))
179
+
180
+ with tabs[1]:
181
+ audio_files = glob.glob("*.mp3") + glob.glob("*.wav")
182
+ for audio_file in audio_files:
183
+ with st.expander(f"🎡 {os.path.basename(audio_file)}"):
184
+ st.audio(audio_file)
185
+
186
+ with tabs[2]:
187
+ video_files = glob.glob("*.mp4")
188
+ for video_file in video_files:
189
+ with st.expander(f"πŸŽ₯ {os.path.basename(video_file)}"):
190
+ st.video(video_file)
191
+
192
+ with tabs[3]:
193
+ for collection_name, bikes in bike_collections.items():
194
+ st.subheader(collection_name)
195
+ cols = st.columns(len(bikes))
196
+
197
+ for idx, (bike_name, details) in enumerate(bikes.items()):
198
+ with cols[idx]:
199
+ st.markdown(f"""
200
+ <div class='bike-card'>
201
+ <h3>{details['emoji']} {bike_name}</h3>
202
+ <p>{details['prompt']}</p>
203
+ </div>
204
+ """, unsafe_allow_html=True)
205
+
206
+ def main():
207
+ st.title("🚲 Bike Cinematic Universe")
208
+
209
+ # Main navigation
210
+ tab_main = st.radio("Choose Action:",
211
+ ["πŸ“Έ Upload Media", "🎬 View Gallery", "🎨 Generate Scene"],
212
+ horizontal=True)
213
+
214
+ if tab_main == "πŸ“Έ Upload Media":
215
+ col1, col2 = st.columns(2)
216
+
217
+ with col1:
218
+ # Image upload
219
+ uploaded_image = st.file_uploader("Upload Image", type=['png', 'jpg'])
220
+ if uploaded_image:
221
+ st.image(uploaded_image)
222
+ prompt = st.text_input("Image Description:")
223
+ if st.button("Process Image"):
224
+ filename = generate_filename(prompt, uploaded_image.type.split('/')[-1])
225
+ save_file(uploaded_image.getvalue(), filename, is_binary=True)
226
+ st.success(f"Saved as {filename}")
227
+
228
+ with col2:
229
+ # Audio/Video upload
230
+ uploaded_media = st.file_uploader("Upload Audio/Video", type=['mp3', 'wav', 'mp4'])
231
+ if uploaded_media:
232
+ if uploaded_media.type.startswith('audio'):
233
+ st.audio(uploaded_media)
234
+ else:
235
+ st.video(uploaded_media)
236
+ if st.button("Save Media"):
237
+ filename = generate_filename("media", uploaded_media.type.split('/')[-1])
238
+ save_file(uploaded_media.getvalue(), filename, is_binary=True)
239
+ st.success(f"Saved as {filename}")
240
+
241
+ elif tab_main == "🎬 View Gallery":
242
+ create_media_gallery()
243
+
244
+ else: # Generate Scene
245
+ st.header("🎨 Scene Generator")
246
+ selected_collection = st.selectbox("Choose Collection", list(bike_collections.keys()))
247
+ selected_bike = st.selectbox("Choose Bike", list(bike_collections[selected_collection].keys()))
248
+
249
+ bike_details = bike_collections[selected_collection][selected_bike]
250
+ st.markdown(f"""
251
+ <div class='scene-card'>
252
+ <h3>{bike_details['emoji']} {selected_bike}</h3>
253
+ <p>{bike_details['prompt']}</p>
254
+ </div>
255
+ """, unsafe_allow_html=True)
256
+
257
+ if __name__ == "__main__":
258
+ main()