awacke1 commited on
Commit
ed39ce7
Β·
verified Β·
1 Parent(s): 1f91cdb

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +811 -0
app.py ADDED
@@ -0,0 +1,811 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # %% ───────────── IMPORTS ─────────────
2
+ import base64
3
+ import glob
4
+ import hashlib
5
+ import json
6
+ import os
7
+ import pandas as pd
8
+ import pytz
9
+ import random
10
+ import re
11
+ import shutil
12
+ import streamlit as st
13
+ import time
14
+ import traceback
15
+ import uuid
16
+ import zipfile
17
+ from PIL import Image
18
+ from azure.cosmos import CosmosClient, exceptions, PartitionKey
19
+ from datetime import datetime
20
+ from git import Repo
21
+ from github import Github
22
+ from gradio_client import Client, handle_file
23
+ import tempfile
24
+ import io
25
+ import requests
26
+ import numpy as np
27
+ from urllib.parse import quote
28
+
29
+ # %% ───────────── APP CONFIGURATION ─────────────
30
+ Site_Name = 'πŸ™ GitCosmos'
31
+ title = "πŸ™ GitCosmos"
32
+ helpURL = 'https://huggingface.co/awacke1'
33
+ bugURL = 'https://huggingface.co/spaces/awacke1/AzureCosmosDBUI/'
34
+ icons = 'πŸ™πŸŒŒπŸ’«'
35
+ st.set_page_config(
36
+ page_title=title,
37
+ page_icon=icons,
38
+ layout="wide",
39
+ initial_sidebar_state="auto",
40
+ menu_items={
41
+ 'Get Help': helpURL,
42
+ 'Report a bug': bugURL,
43
+ 'About': title
44
+ }
45
+ )
46
+
47
+ # Cosmos DB & App URLs
48
+ ENDPOINT = "https://acae-afd.documents.azure.com:443/"
49
+ DATABASE_NAME = os.environ.get("COSMOS_DATABASE_NAME")
50
+ CONTAINER_NAME = os.environ.get("COSMOS_CONTAINER_NAME")
51
+ Key = os.environ.get("Key")
52
+ LOCAL_APP_URL = "https://huggingface.co/spaces/awacke1/AzureCosmosDBUI"
53
+ CosmosDBUrl = 'https://portal.azure.com/#@AaronCWackergmail.onmicrosoft.com/resource/subscriptions/003fba60-5b3f-48f4-ab36-3ed11bc40816/resourceGroups/datasets/providers/Microsoft.DocumentDB/databaseAccounts/acae-afd/dataExplorer'
54
+
55
+ # TTL Constants
56
+ SEVEN_DAYS_IN_SECONDS = 7 * 24 * 60 * 60 # 604,800 seconds
57
+ THIRTY_DAYS_IN_SECONDS = 30 * 24 * 60 * 60 # 2,592,000 seconds
58
+
59
+ # %% ───────────── HELPER FUNCTIONS ─────────────
60
+ def get_download_link(file_path):
61
+ with open(file_path, "rb") as file:
62
+ contents = file.read()
63
+ b64 = base64.b64encode(contents).decode()
64
+ file_name = os.path.basename(file_path)
65
+ return f'<a href="data:file/txt;base64,{b64}" download="{file_name}">Download {file_name} πŸ“‚</a>'
66
+
67
+ def generate_unique_id():
68
+ timestamp = datetime.utcnow().strftime('%Y%m%d%H%M%S%f')
69
+ unique_uuid = str(uuid.uuid4())
70
+ return_value = f"{timestamp}-{unique_uuid}"
71
+ st.write('New ID: ' + return_value)
72
+ return return_value
73
+
74
+ def generate_filename(prompt, file_type):
75
+ central = pytz.timezone('US/Central')
76
+ safe_date_time = datetime.now(central).strftime("%m%d_%H%M")
77
+ safe_prompt = re.sub(r'\W+', '', prompt)[:90]
78
+ return f"{safe_date_time}{safe_prompt}.{file_type}"
79
+
80
+ def create_file(filename, prompt, response, should_save=True):
81
+ if not should_save:
82
+ return
83
+ with open(filename, 'w', encoding='utf-8') as file:
84
+ file.write(prompt + "\n\n" + response)
85
+
86
+ def load_file(file_name):
87
+ with open(file_name, "r", encoding='utf-8') as file:
88
+ content = file.read()
89
+ return content
90
+
91
+ def display_glossary_entity(k):
92
+ search_urls = {
93
+ "πŸš€": lambda k: f"/?q={k}",
94
+ "πŸ“–": lambda k: f"https://en.wikipedia.org/wiki/{quote(k)}",
95
+ "πŸ”": lambda k: f"https://www.google.com/search?q={quote(k)}",
96
+ "πŸŽ₯": lambda k: f"https://www.youtube.com/results?search_query={quote(k)}",
97
+ }
98
+ links_md = ' '.join([f"<a href='{url(k)}' target='_blank'>{emoji}</a>" for emoji, url in search_urls.items()])
99
+ st.markdown(f"{k} {links_md}", unsafe_allow_html=True)
100
+
101
+ def create_zip_of_files(files):
102
+ zip_name = "all_files.zip"
103
+ with zipfile.ZipFile(zip_name, 'w') as zipf:
104
+ for file in files:
105
+ zipf.write(file)
106
+ return zip_name
107
+
108
+ def get_video_html(video_path, width="100%"):
109
+ video_url = f"data:video/mp4;base64,{base64.b64encode(open(video_path, 'rb').read()).decode()}"
110
+ return f'''
111
+ <video width="{width}" controls autoplay loop>
112
+ <source src="{video_url}" type="video/mp4">
113
+ Your browser does not support video.
114
+ </video>
115
+ '''
116
+
117
+ def get_audio_html(audio_path, width="100%"):
118
+ audio_url = f"data:audio/mpeg;base64,{base64.b64encode(open(audio_path, 'rb').read()).decode()}"
119
+ return f'''
120
+ <audio controls style="width:{width}">
121
+ <source src="{audio_url}" type="audio/mpeg">
122
+ Your browser does not support audio.
123
+ </audio>
124
+ '''
125
+
126
+ def preprocess_text(text):
127
+ text = text.replace('\r\n', '\\n').replace('\r', '\\n').replace('\n', '\\n')
128
+ text = text.replace('"', '\\"')
129
+ text = re.sub(r'[\t]', ' ', text)
130
+ text = re.sub(r'[^\x00-\x7F]+', '', text)
131
+ return text.strip()
132
+
133
+ def format_item_age(timestamp):
134
+ current_time = int(time.time())
135
+ age_seconds = current_time - timestamp
136
+ age_days = age_seconds / (24 * 60 * 60)
137
+ return f"{age_seconds:,} seconds ({age_days:.2f} days)"
138
+
139
+ # %% ───────────── COSMOS DB FUNCTIONS ─────────────
140
+ def get_databases(client):
141
+ return [db['id'] for db in client.list_databases()]
142
+
143
+ def get_containers(database):
144
+ return [container['id'] for container in database.list_containers()]
145
+
146
+ def get_documents(container, limit=None):
147
+ query = "SELECT * FROM c ORDER BY c._ts DESC"
148
+ items = list(container.query_items(query=query, enable_cross_partition_query=True, max_item_count=limit))
149
+ for item in items:
150
+ if '_ts' in item:
151
+ item['age_info'] = format_item_age(item['_ts'])
152
+ return items
153
+
154
+ def insert_record(container, record):
155
+ try:
156
+ container.create_item(body=record)
157
+ return True, "Inserted! πŸŽ‰"
158
+ except exceptions.CosmosHttpResponseError as e:
159
+ return False, f"HTTP error: {str(e)} 🚨"
160
+ except Exception as e:
161
+ return False, f"Error: {str(e)} 😱"
162
+
163
+ def update_record(container, updated_record):
164
+ try:
165
+ container.upsert_item(body=updated_record)
166
+ return True, f"Updated {updated_record['id']} πŸ› οΈ"
167
+ except exceptions.CosmosHttpResponseError as e:
168
+ return False, f"HTTP error: {str(e)} 🚨"
169
+ except Exception as e:
170
+ return False, f"Error: {traceback.format_exc()} 😱"
171
+
172
+ def delete_record(container, record):
173
+ try:
174
+ if "id" not in record:
175
+ return False, "Record must contain an 'id' field. πŸ›‘"
176
+ doc_id = record["id"]
177
+ if "delete_log" not in st.session_state:
178
+ st.session_state.delete_log = []
179
+ st.session_state.delete_log.append(f"Attempting to delete document: {json.dumps(record, indent=2)}")
180
+ partition_key_value = doc_id
181
+ st.session_state.delete_log.append(f"Using ID and Partition Key: {partition_key_value}")
182
+ container.delete_item(item=doc_id, partition_key=partition_key_value)
183
+ success_msg = f"Record {doc_id} successfully deleted from Cosmos DB. πŸ—‘οΈ"
184
+ st.session_state.delete_log.append(success_msg)
185
+ return True, success_msg
186
+ except exceptions.CosmosResourceNotFoundError:
187
+ success_msg = f"Record {doc_id} not found in Cosmos DB (already deleted or never existed). πŸ—‘οΈ"
188
+ st.session_state.delete_log.append(success_msg)
189
+ return True, success_msg
190
+ except exceptions.CosmosHttpResponseError as e:
191
+ error_msg = f"HTTP error deleting {doc_id}: {str(e)}. 🚨"
192
+ st.session_state.delete_log.append(error_msg)
193
+ return False, error_msg
194
+ except Exception as e:
195
+ error_msg = f"Unexpected error deleting {doc_id}: {str(traceback.format_exc())}. 😱"
196
+ st.session_state.delete_log.append(error_msg)
197
+ return False, error_msg
198
+
199
+ def save_to_cosmos_db(container, query, response1, response2):
200
+ try:
201
+ if container:
202
+ timestamp = datetime.utcnow().strftime('%Y%m%d%H%M%S%f')
203
+ unique_uuid = str(uuid.uuid4())
204
+ new_id = f"{timestamp}-{unique_uuid}"
205
+ record = {
206
+ "id": new_id,
207
+ "name": new_id,
208
+ "query": query,
209
+ "response1": response1,
210
+ "response2": response2,
211
+ "timestamp": datetime.utcnow().isoformat(),
212
+ "type": "ai_response",
213
+ "version": "1.0"
214
+ }
215
+ container.create_item(body=record)
216
+ st.success(f"Saved: {record['id']}")
217
+ st.session_state.documents = get_documents(container)
218
+ else:
219
+ st.error("Cosmos container not initialized.")
220
+ except Exception as e:
221
+ st.error(f"Save error: {str(e)}")
222
+
223
+ def set_container_ttl(container_client, ttl_seconds):
224
+ try:
225
+ container_properties = container_client.read()
226
+ container_client.replace_container(
227
+ container_client.container_path,
228
+ partition_key=container_properties['partitionKey'],
229
+ default_ttl=ttl_seconds
230
+ )
231
+ return True, f"TTL set to {ttl_seconds:,} seconds ({ttl_seconds/(24*60*60):.2f} days)"
232
+ except Exception as e:
233
+ return False, f"Error setting TTL: {str(e)}"
234
+
235
+ def archive_current_container(database_name, container_name, client):
236
+ try:
237
+ base_dir = "./cosmos_archive_current_container"
238
+ if os.path.exists(base_dir):
239
+ shutil.rmtree(base_dir)
240
+ os.makedirs(base_dir)
241
+ db_client = client.get_database_client(database_name)
242
+ container_client = db_client.get_container_client(container_name)
243
+ items = list(container_client.read_all_items())
244
+ container_dir = os.path.join(base_dir, container_name)
245
+ os.makedirs(container_dir)
246
+ for item in items:
247
+ item_id = item.get('id', f"unknown_{datetime.now().strftime('%Y%m%d%H%M%S')}")
248
+ with open(os.path.join(container_dir, f"{item_id}.json"), 'w') as f:
249
+ json.dump(item, f, indent=2)
250
+ archive_name = f"{container_name}_archive_{datetime.now().strftime('%Y%m%d%H%M%S')}"
251
+ shutil.make_archive(archive_name, 'zip', base_dir)
252
+ return get_download_link(f"{archive_name}.zip")
253
+ except Exception as e:
254
+ return f"Archive error: {str(e)} 😒"
255
+
256
+ # %% ───────────── GITHUB FUNCTIONS ─────────────
257
+ def download_github_repo(url, local_path):
258
+ if os.path.exists(local_path):
259
+ shutil.rmtree(local_path)
260
+ Repo.clone_from(url, local_path)
261
+
262
+ def create_zip_file(source_dir, output_filename):
263
+ shutil.make_archive(output_filename, 'zip', source_dir)
264
+
265
+ def create_repo(g, repo_name):
266
+ user = g.get_user()
267
+ return user.create_repo(repo_name)
268
+
269
+ def push_to_github(local_path, repo, github_token):
270
+ repo_url = f"https://{github_token}@github.com/{repo.full_name}.git"
271
+ local_repo = Repo(local_path)
272
+ if 'origin' in [remote.name for remote in local_repo.remotes]:
273
+ origin = local_repo.remote('origin')
274
+ origin.set_url(repo_url)
275
+ else:
276
+ origin = local_repo.create_remote('origin', repo_url)
277
+ if not local_repo.heads:
278
+ local_repo.git.checkout('-b', 'main')
279
+ current_branch = 'main'
280
+ else:
281
+ current_branch = local_repo.active_branch.name
282
+ local_repo.git.add(A=True)
283
+ if local_repo.is_dirty():
284
+ local_repo.git.commit('-m', 'Initial commit')
285
+ origin.push(refspec=f'{current_branch}:{current_branch}')
286
+
287
+ # %% ───────────── FILE & MEDIA MANAGEMENT FUNCTIONS ─────────────
288
+ def display_saved_files_in_sidebar():
289
+ all_files = sorted([f for f in glob.glob("*.md") if not f.lower().startswith('readme')], reverse=True)
290
+ st.sidebar.markdown("## πŸ“ Files")
291
+ for file in all_files:
292
+ col1, col2, col3 = st.sidebar.columns([6, 2, 1])
293
+ with col1:
294
+ st.markdown(f"πŸ“„ {file}")
295
+ with col2:
296
+ st.sidebar.download_button(
297
+ label="⬇️",
298
+ data=open(file, 'rb').read(),
299
+ file_name=file
300
+ )
301
+ with col3:
302
+ if st.sidebar.button("πŸ—‘", key=f"delete_{file}"):
303
+ os.remove(file)
304
+ st.rerun()
305
+
306
+ def display_file_viewer(file_path):
307
+ content = load_file(file_path)
308
+ if content:
309
+ st.markdown("### πŸ“„ File Viewer")
310
+ st.markdown(f"**{file_path}**")
311
+ file_stats = os.stat(file_path)
312
+ st.markdown(f"**Mod:** {datetime.fromtimestamp(file_stats.st_mtime).strftime('%Y-%m-%d %H:%M:%S')} | **Size:** {file_stats.st_size} bytes")
313
+ st.markdown("---")
314
+ st.markdown(content)
315
+ st.download_button("⬇️", data=content, file_name=os.path.basename(file_path), mime="text/markdown")
316
+
317
+ def display_file_editor(file_path):
318
+ if 'file_content' not in st.session_state:
319
+ st.session_state.file_content = {}
320
+ if file_path not in st.session_state.file_content:
321
+ content = load_file(file_path)
322
+ if content is not None:
323
+ st.session_state.file_content[file_path] = content
324
+ else:
325
+ return
326
+ st.markdown("### ✏️ Edit File")
327
+ st.markdown(f"**Editing:** {file_path}")
328
+ md_tab, code_tab = st.tabs(["Markdown", "Code"])
329
+ with md_tab:
330
+ st.markdown(st.session_state.file_content[file_path])
331
+ with code_tab:
332
+ new_content = st.text_area("Edit:", value=st.session_state.file_content[file_path], height=400, key=f"editor_{hash(file_path)}")
333
+ col1, col2 = st.columns([1, 5])
334
+ with col1:
335
+ if st.button("πŸ’Ύ Save"):
336
+ if save_file_content(file_path, new_content):
337
+ st.session_state.file_content[file_path] = new_content
338
+ st.success("Saved! πŸŽ‰")
339
+ time.sleep(1)
340
+ st.rerun()
341
+ with col2:
342
+ st.download_button("⬇️", data=new_content, file_name=os.path.basename(file_path), mime="text/markdown")
343
+
344
+ def save_file_content(file_path, content):
345
+ try:
346
+ with open(file_path, 'w', encoding='utf-8') as file:
347
+ file.write(content)
348
+ return True
349
+ except Exception as e:
350
+ st.error(f"Save error: {str(e)}")
351
+ return False
352
+
353
+ def update_file_management_section():
354
+ if 'file_view_mode' not in st.session_state:
355
+ st.session_state.file_view_mode = None
356
+ if 'current_file' not in st.session_state:
357
+ st.session_state.current_file = None
358
+ if 'file_content' not in st.session_state:
359
+ st.session_state.file_content = {}
360
+ all_files = sorted(glob.glob("*.md"), reverse=True)
361
+ st.sidebar.title("πŸ“ Files")
362
+ if st.sidebar.button("πŸ—‘ Delete All"):
363
+ for file in all_files:
364
+ os.remove(file)
365
+ st.session_state.file_content = {}
366
+ st.session_state.current_file = None
367
+ st.session_state.file_view_mode = None
368
+ st.rerun()
369
+ if st.sidebar.button("⬇️ Download All"):
370
+ zip_file = create_zip_of_files(all_files)
371
+ st.sidebar.markdown(get_download_link(zip_file), unsafe_allow_html=True)
372
+ for file in all_files:
373
+ col1, col2, col3, col4 = st.sidebar.columns([1, 3, 1, 1])
374
+ with col1:
375
+ if st.button("🌐", key=f"view_{file}"):
376
+ st.session_state.current_file = file
377
+ st.session_state.file_view_mode = 'view'
378
+ if file not in st.session_state.file_content:
379
+ content = load_file(file)
380
+ if content is not None:
381
+ st.session_state.file_content[file] = content
382
+ st.rerun()
383
+ with col2:
384
+ st.markdown(get_download_link(file), unsafe_allow_html=True)
385
+ with col3:
386
+ if st.button("πŸ“‚", key=f"edit_{file}"):
387
+ st.session_state.current_file = file
388
+ st.session_state.file_view_mode = 'edit'
389
+ if file not in st.session_state.file_content:
390
+ content = load_file(file)
391
+ if content is not None:
392
+ st.session_state.file_content[file] = content
393
+ st.rerun()
394
+ with col4:
395
+ if st.button("πŸ—‘", key=f"delete_{file}"):
396
+ os.remove(file)
397
+ if file in st.session_state.file_content:
398
+ del st.session_state.file_content[file]
399
+ if st.session_state.current_file == file:
400
+ st.session_state.current_file = None
401
+ st.session_state.file_view_mode = None
402
+ st.rerun()
403
+ if st.session_state.current_file:
404
+ if st.session_state.file_view_mode == 'view':
405
+ display_file_viewer(st.session_state.current_file)
406
+ elif st.session_state.file_view_mode == 'edit':
407
+ display_file_editor(st.session_state.current_file)
408
+
409
+ # %% ───────────── VIDEO & AUDIO UI FUNCTIONS ─────────────
410
+ def validate_and_preprocess_image(file_data, target_size=(576, 1024)):
411
+ try:
412
+ st.write("Preprocessing image...")
413
+ if isinstance(file_data, bytes):
414
+ img = Image.open(io.BytesIO(file_data))
415
+ elif hasattr(file_data, 'read'):
416
+ if hasattr(file_data, 'seek'):
417
+ file_data.seek(0)
418
+ img = Image.open(file_data)
419
+ elif isinstance(file_data, Image.Image):
420
+ img = file_data
421
+ else:
422
+ raise ValueError(f"Unsupported input: {type(file_data)}")
423
+ if img.mode != 'RGB':
424
+ img = img.convert('RGB')
425
+ aspect_ratio = img.size[0] / img.size[1]
426
+ if aspect_ratio > target_size[0] / target_size[1]:
427
+ new_width = target_size[0]
428
+ new_height = int(new_width / aspect_ratio)
429
+ else:
430
+ new_height = target_size[1]
431
+ new_width = int(new_height * aspect_ratio)
432
+ new_width = (new_width // 2) * 2
433
+ new_height = (new_height // 2) * 2
434
+ resized_img = img.resize((new_width, new_height), Image.Resampling.LANCZOS)
435
+ final_img = Image.new('RGB', target_size, (255, 255, 255))
436
+ paste_x = (target_size[0] - new_width) // 2
437
+ paste_y = (target_size[1] - new_height) // 2
438
+ final_img.paste(resized_img, (paste_x, paste_y))
439
+ return final_img
440
+ except Exception as e:
441
+ st.error(f"Image error: {str(e)}")
442
+ return None
443
+
444
+ def add_video_generation_ui(container):
445
+ st.markdown("### πŸŽ₯ Video Gen")
446
+ col1, col2 = st.columns([2, 1])
447
+ with col1:
448
+ uploaded_file = st.file_uploader("Upload Image πŸ–ΌοΈ", type=['png', 'jpg', 'jpeg'])
449
+ with col2:
450
+ st.markdown("#### Params")
451
+ motion = st.slider("🌊 Motion", 1, 255, 127)
452
+ fps = st.slider("🎬 FPS", 1, 30, 6)
453
+ with st.expander("Advanced"):
454
+ use_custom = st.checkbox("Custom Seed")
455
+ seed = st.number_input("Seed", value=int(time.time() * 1000)) if use_custom else None
456
+ if uploaded_file is not None:
457
+ try:
458
+ file_data = uploaded_file.read()
459
+ preview1, preview2 = st.columns(2)
460
+ with preview1:
461
+ st.write("Original")
462
+ st.image(Image.open(io.BytesIO(file_data)), use_column_width=True)
463
+ with preview2:
464
+ proc_img = validate_and_preprocess_image(io.BytesIO(file_data))
465
+ if proc_img:
466
+ st.write("Processed")
467
+ st.image(proc_img, use_column_width=True)
468
+ else:
469
+ st.error("Preprocess failed")
470
+ return
471
+ if st.button("πŸŽ₯ Generate"):
472
+ with st.spinner("Generating video..."):
473
+ with tempfile.NamedTemporaryFile(suffix='.png', delete=False) as temp_file:
474
+ proc_img.save(temp_file.name, format='PNG')
475
+ try:
476
+ client = Client("awacke1/stable-video-diffusion", hf_token=os.environ.get("HUGGINGFACE_TOKEN"))
477
+ result = client.predict(
478
+ image=temp_file.name,
479
+ seed=seed if seed is not None else int(time.time() * 1000),
480
+ randomize_seed=seed is None,
481
+ motion_bucket_id=motion,
482
+ fps_id=fps,
483
+ api_name="/video"
484
+ )
485
+ if result and isinstance(result, tuple) and len(result) >= 1:
486
+ video_path = result[0].get('video') if isinstance(result[0], dict) else None
487
+ if video_path and os.path.exists(video_path):
488
+ video_filename = f"generated_video_{datetime.now().strftime('%Y%m%d_%H%M%S')}.mp4"
489
+ shutil.copy(video_path, video_filename)
490
+ st.success(f"Video generated! πŸŽ‰")
491
+ st.video(video_filename)
492
+ if container:
493
+ video_record = {
494
+ "id": generate_unique_id(),
495
+ "type": "generated_video",
496
+ "filename": video_filename,
497
+ "seed": seed if seed is not None else "random",
498
+ "motion": motion,
499
+ "fps": fps,
500
+ "timestamp": datetime.now().isoformat()
501
+ }
502
+ success, message = insert_record(container, video_record)
503
+ if success:
504
+ st.success("DB record saved!")
505
+ else:
506
+ st.error(f"DB error: {message}")
507
+ else:
508
+ st.error("Invalid result format")
509
+ else:
510
+ st.error("No result returned")
511
+ except Exception as e:
512
+ st.error(f"Video gen error: {str(e)}")
513
+ finally:
514
+ try:
515
+ os.unlink(temp_file.name)
516
+ st.write("Temp file removed")
517
+ except Exception as e:
518
+ st.warning(f"Cleanup error: {str(e)}")
519
+ except Exception as e:
520
+ st.error(f"Upload error: {str(e)}")
521
+
522
+ # %% ───────────── MAIN FUNCTION ─────────────
523
+ def main():
524
+ st.markdown("### πŸ™ GitCosmos - Cosmos & Git Hub")
525
+ if "chat_history" not in st.session_state:
526
+ st.session_state.chat_history = []
527
+
528
+ if Key:
529
+ st.session_state.primary_key = Key
530
+ st.session_state.logged_in = True
531
+ else:
532
+ st.error("Missing Cosmos Key πŸ”‘βŒ")
533
+ return
534
+
535
+ if st.session_state.logged_in:
536
+ try:
537
+ if st.session_state.get("client") is None:
538
+ st.session_state.client = CosmosClient(ENDPOINT, credential=st.session_state.primary_key)
539
+
540
+ st.sidebar.title("πŸ™ Navigator")
541
+ databases = get_databases(st.session_state.client)
542
+ selected_db = st.sidebar.selectbox("πŸ—ƒοΈ DB", databases)
543
+ st.markdown(CosmosDBUrl)
544
+
545
+ if selected_db != st.session_state.get("selected_database"):
546
+ st.session_state.selected_database = selected_db
547
+ st.session_state.selected_container = None
548
+ st.session_state.selected_document_id = None
549
+ st.session_state.current_index = 0
550
+ st.rerun()
551
+
552
+ if st.session_state.selected_database:
553
+ database = st.session_state.client.get_database_client(st.session_state.selected_database)
554
+ containers = get_containers(database)
555
+ selected_container = st.sidebar.selectbox("πŸ“ Container", containers)
556
+
557
+ if selected_container != st.session_state.get("selected_container"):
558
+ st.session_state.selected_container = selected_container
559
+ st.session_state.selected_document_id = None
560
+ st.session_state.current_index = 0
561
+ st.rerun()
562
+
563
+ if st.session_state.selected_container:
564
+ container = database.get_container_client(st.session_state.selected_container)
565
+
566
+ st.sidebar.subheader("⏳ TTL Configuration")
567
+ ttl_seconds = st.sidebar.slider(
568
+ "Set TTL (seconds)",
569
+ min_value=SEVEN_DAYS_IN_SECONDS,
570
+ max_value=THIRTY_DAYS_IN_SECONDS,
571
+ value=SEVEN_DAYS_IN_SECONDS,
572
+ step=24*60*60,
573
+ format="%d seconds (%d days)"
574
+ )
575
+ if st.sidebar.button("⏰ Set Container TTL"):
576
+ success, message = set_container_ttl(container, ttl_seconds)
577
+ if success:
578
+ st.sidebar.success(message)
579
+ else:
580
+ st.sidebar.error(message)
581
+
582
+ if st.sidebar.button("πŸ“¦ Export"):
583
+ download_link = archive_current_container(st.session_state.selected_database,
584
+ st.session_state.selected_container,
585
+ st.session_state.client)
586
+ if download_link.startswith('<a'):
587
+ st.markdown(download_link, unsafe_allow_html=True)
588
+ else:
589
+ st.error(download_link)
590
+
591
+ documents = get_documents(container)
592
+ total_docs = len(documents)
593
+ num_docs = st.slider("Docs", 1, 20, 1)
594
+ documents_to_display = documents[:num_docs] if total_docs > num_docs else documents
595
+ st.sidebar.info(f"Showing {len(documents_to_display)} docs")
596
+
597
+ view_options = ['Markdown', 'Code', 'Run AI', 'Clone', 'New']
598
+ selected_view = st.sidebar.selectbox("View", view_options, index=1)
599
+
600
+ if selected_view == 'Markdown':
601
+ st.markdown("#### πŸ“„ Markdown")
602
+ if documents:
603
+ doc = documents[st.session_state.current_index]
604
+ content = json.dumps(doc, indent=2)
605
+ st.markdown(f"```json\n{content}\n```")
606
+ if '_ts' in doc:
607
+ st.markdown(f"**Item Age:** {doc['age_info']}")
608
+ col_prev, col_next = st.columns(2)
609
+ with col_prev:
610
+ if st.button("⬅️") and st.session_state.current_index > 0:
611
+ st.session_state.current_index -= 1
612
+ st.rerun()
613
+ with col_next:
614
+ if st.button("➑️") and st.session_state.current_index < total_docs - 1:
615
+ st.session_state.current_index += 1
616
+ st.rerun()
617
+
618
+ elif selected_view == 'Code':
619
+ st.markdown("#### πŸ’» Code Editor")
620
+ if documents:
621
+ doc = documents[st.session_state.current_index]
622
+ if '_ts' in doc:
623
+ st.markdown(f"**Item Age:** {doc['age_info']}")
624
+ doc_str = st.text_area("Edit JSON", value=json.dumps(doc, indent=2),
625
+ height=300, key=f'code_{st.session_state.current_index}')
626
+ col_prev, col_next = st.columns(2)
627
+ with col_prev:
628
+ if st.button("⬅️") and st.session_state.current_index > 0:
629
+ st.session_state.current_index -= 1
630
+ st.rerun()
631
+ with col_next:
632
+ if st.button("➑️") and st.session_state.current_index < total_docs - 1:
633
+ st.session_state.current_index += 1
634
+ st.rerun()
635
+ col_save, col_delete = st.columns(2)
636
+ with col_save:
637
+ if st.button("πŸ’Ύ Save", key=f'save_{st.session_state.current_index}'):
638
+ try:
639
+ updated_doc = json.loads(doc_str)
640
+ container.upsert_item(body=updated_doc)
641
+ st.success(f"Saved {updated_doc['id']}")
642
+ st.rerun()
643
+ except Exception as e:
644
+ st.error(f"Save err: {str(e)}")
645
+ with col_delete:
646
+ if st.button("πŸ—‘οΈ Delete", key=f'delete_{st.session_state.current_index}'):
647
+ try:
648
+ current_doc = json.loads(doc_str)
649
+ success, message = delete_record(container, current_doc)
650
+ if success:
651
+ st.success(message)
652
+ st.rerun()
653
+ else:
654
+ st.error(message)
655
+ except Exception as e:
656
+ st.error(f"Delete err: {str(e)}")
657
+ if "delete_log" in st.session_state and st.session_state.delete_log:
658
+ st.subheader("Delete Log")
659
+ for log_entry in st.session_state.delete_log[-5:]:
660
+ st.write(log_entry)
661
+
662
+ elif selected_view == 'Run AI':
663
+ st.markdown("#### πŸ€– Run AI (stub)")
664
+ st.info("AI functionality not implemented.")
665
+
666
+ elif selected_view == 'Clone':
667
+ st.markdown("#### πŸ“„ Clone")
668
+ if documents:
669
+ doc = documents[st.session_state.current_index]
670
+ st.markdown(f"Original ID: {doc.get('id', '')}")
671
+ if '_ts' in doc:
672
+ st.markdown(f"**Original Age:** {doc['age_info']}")
673
+ new_id = st.text_input("New ID", value=generate_unique_id(), key='new_clone_id')
674
+ new_name = st.text_input("New Name", value=f"Clone_{new_id[:8]}", key='new_clone_name')
675
+ new_doc = {'id': new_id, 'name': new_name, **{k: v for k, v in doc.items() if k not in ['id', 'name', '_rid', '_self', '_etag', '_attachments', '_ts']}}
676
+ doc_str = st.text_area("Edit JSON", value=json.dumps(new_doc, indent=2), height=300, key='clone_preview')
677
+ col1, col2 = st.columns(2)
678
+ with col1:
679
+ if st.button("πŸ”„ Regenerate"):
680
+ new_id = generate_unique_id()
681
+ st.session_state.new_clone_id = new_id
682
+ st.rerun()
683
+ with col2:
684
+ if st.button("πŸ’Ύ Save Clone"):
685
+ try:
686
+ final_doc = json.loads(doc_str)
687
+ for field in ['_rid', '_self', '_etag', '_attachments', '_ts']:
688
+ final_doc.pop(field, None)
689
+ container.create_item(body=final_doc)
690
+ st.success(f"Cloned {final_doc['id']}")
691
+ st.rerun()
692
+ except Exception as e:
693
+ st.error(f"Clone err: {str(e)}")
694
+ col_prev, col_next = st.columns(2)
695
+ with col_prev:
696
+ if st.button("⬅️") and st.session_state.current_index > 0:
697
+ st.session_state.current_index -= 1
698
+ st.rerun()
699
+ with col_next:
700
+ if st.button("➑️") and st.session_state.current_index < total_docs - 1:
701
+ st.session_state.current_index += 1
702
+ st.rerun()
703
+
704
+ elif selected_view == 'New':
705
+ st.markdown("#### βž• New Doc")
706
+ if st.button("πŸ€– Auto-Gen"):
707
+ auto_doc = {
708
+ "id": generate_unique_id(),
709
+ "name": f"Auto {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}",
710
+ "content": "Auto-generated record.",
711
+ "timestamp": datetime.now().isoformat()
712
+ }
713
+ success, message = insert_record(container, auto_doc)
714
+ if success:
715
+ st.success(message)
716
+ st.rerun()
717
+ else:
718
+ st.error(message)
719
+ else:
720
+ new_id = st.text_input("ID", value=generate_unique_id(), key='new_id')
721
+ default_doc = {
722
+ "id": new_id,
723
+ "name": "New Doc",
724
+ "content": "",
725
+ "timestamp": datetime.now().isoformat()
726
+ }
727
+ new_doc_str = st.text_area("JSON", value=json.dumps(default_doc, indent=2), height=300)
728
+ if st.button("βž• Create"):
729
+ try:
730
+ cleaned = preprocess_text(new_doc_str)
731
+ new_doc = json.loads(cleaned)
732
+ new_doc['id'] = new_id
733
+ success, message = insert_record(container, new_doc)
734
+ if success:
735
+ st.success(f"Created {new_doc['id']}")
736
+ st.rerun()
737
+ else:
738
+ st.error(message)
739
+ except Exception as e:
740
+ st.error(f"Create err: {str(e)}")
741
+ st.subheader(f"πŸ“Š {st.session_state.selected_container}")
742
+ if documents_to_display:
743
+ df = pd.DataFrame(documents_to_display)
744
+ if '_ts' in df.columns:
745
+ df['age'] = df['_ts'].apply(format_item_age)
746
+ st.dataframe(df)
747
+ else:
748
+ st.info("No docs.")
749
+
750
+ st.subheader("πŸ™ GitHub Ops")
751
+ github_token = os.environ.get("GITHUB")
752
+ source_repo = st.text_input("Source Repo URL", value="https://github.com/AaronCWacker/AIExamples-8-24-Streamlit")
753
+ new_repo_name = st.text_input("New Repo Name", value=f"Clone-{datetime.now().strftime('%Y%m%d_%H%M%S')}")
754
+ col_g1, col_g2 = st.columns(2)
755
+ with col_g1:
756
+ if st.button("πŸ“₯ Clone Repo"):
757
+ if github_token and source_repo:
758
+ try:
759
+ local_path = f"./temp_repo_{datetime.now().strftime('%Y%m%d%H%M%S')}"
760
+ download_github_repo(source_repo, local_path)
761
+ zip_filename = f"{new_repo_name}.zip"
762
+ create_zip_file(local_path, zip_filename[:-4])
763
+ st.markdown(get_download_link(zip_filename), unsafe_allow_html=True)
764
+ st.success("Cloned! πŸŽ‰")
765
+ except Exception as e:
766
+ st.error(f"Clone err: {str(e)}")
767
+ finally:
768
+ if os.path.exists(local_path):
769
+ shutil.rmtree(local_path)
770
+ if os.path.exists(zip_filename):
771
+ os.remove(zip_filename)
772
+ else:
773
+ st.error("Missing token or URL πŸ”‘β“")
774
+ with col_g2:
775
+ if st.button("πŸ“€ Push Repo"):
776
+ if github_token and source_repo:
777
+ try:
778
+ g = Github(github_token)
779
+ new_repo = create_repo(g, new_repo_name)
780
+ local_path = f"./temp_repo_{datetime.now().strftime('%Y%m%d%H%M%S')}"
781
+ download_github_repo(source_repo, local_path)
782
+ push_to_github(local_path, new_repo, github_token)
783
+ st.success(f"Pushed to {new_repo.html_url} πŸš€")
784
+ except Exception as e:
785
+ st.error(f"Push err: {str(e)}")
786
+ finally:
787
+ if os.path.exists(local_path):
788
+ shutil.rmtree(local_path)
789
+ else:
790
+ st.error("Missing token or URL πŸ”‘β“")
791
+
792
+ update_file_management_section()
793
+
794
+ except exceptions.CosmosHttpResponseError as e:
795
+ st.error(f"Cosmos error: {str(e)} 🚨")
796
+ except Exception as e:
797
+ st.error(f"Error: {str(e)} 😱")
798
+
799
+ if st.session_state.logged_in and st.sidebar.button("πŸšͺ Logout"):
800
+ st.markdown("#### πŸšͺ Logout")
801
+ st.session_state.logged_in = False
802
+ st.session_state.selected_records = []
803
+ st.session_state.client = None
804
+ st.session_state.selected_database = None
805
+ st.session_state.selected_container = None
806
+ st.session_state.selected_document_id = None
807
+ st.session_state.current_index = 0
808
+ st.rerun()
809
+
810
+ if __name__ == "__main__":
811
+ main()