Spaces:
Running
on
L40S
Running
on
L40S
🎨 Redesign from AnyCoder
Browse filesThis Pull Request contains a redesigned version of the app with:
- ✨ Modern, mobile-friendly design
- 🎯 Minimal, clean components
- 📱 Responsive layout
- 🚀 Improved user experience
Generated by [AnyCoder](https://huggingface.co/spaces/akhaliq/anycoder)
app.py
CHANGED
|
@@ -1,284 +1,197 @@
|
|
| 1 |
-
import
|
| 2 |
-
import
|
| 3 |
-
from
|
| 4 |
-
import
|
| 5 |
-
import time
|
| 6 |
-
import re
|
| 7 |
import os
|
| 8 |
-
|
| 9 |
-
import
|
| 10 |
-
import
|
| 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 |
-
def
|
| 92 |
-
|
| 93 |
-
|
| 94 |
-
|
| 95 |
-
params = {k:v for k,v in params.items() if v is not None}
|
| 96 |
-
vocal_structs = ['[verse]', '[chorus]', '[bridge]']
|
| 97 |
-
sample_rate = MODEL.cfg.sample_rate
|
| 98 |
|
| 99 |
-
|
| 100 |
-
|
| 101 |
-
|
| 102 |
-
if len(paragraphs) < 1:
|
| 103 |
-
return None, json.dumps("Lyrics can not be left blank")
|
| 104 |
-
paragraphs_norm = []
|
| 105 |
-
vocal_flag = False
|
| 106 |
-
for para in paragraphs:
|
| 107 |
-
lines = para.splitlines()
|
| 108 |
-
struct_tag = lines[0].strip().lower()
|
| 109 |
-
if struct_tag not in STRUCTS:
|
| 110 |
-
return None, json.dumps(f"Segments should start with a structure tag in {STRUCTS}")
|
| 111 |
-
if struct_tag in vocal_structs:
|
| 112 |
-
vocal_flag = True
|
| 113 |
-
if len(lines) < 2 or not [line.strip() for line in lines[1:] if line.strip()]:
|
| 114 |
-
return None, json.dumps("The following segments require lyrics: [verse], [chorus], [bridge]")
|
| 115 |
-
else:
|
| 116 |
-
new_para_list = []
|
| 117 |
-
for line in lines[1:]:
|
| 118 |
-
new_para_list.append(re.sub(r"[^\w\s\[\]\-\u4e00-\u9fff\u3040-\u309f\u30a0-\u30ff\uac00-\ud7af\u00c0-\u017f]", "", line))
|
| 119 |
-
new_para_str = f"{struct_tag} {'.'.join(new_para_list)}"
|
| 120 |
-
else:
|
| 121 |
-
if len(lines) > 1:
|
| 122 |
-
return None, json.dumps("The following segments should not contain lyrics: [intro], [intro-short], [intro-medium], [inst], [inst-short], [inst-medium], [outro], [outro-short], [outro-medium]")
|
| 123 |
-
else:
|
| 124 |
-
new_para_str = struct_tag
|
| 125 |
-
paragraphs_norm.append(new_para_str)
|
| 126 |
-
if not vocal_flag:
|
| 127 |
-
return None, json.dumps(f"The lyric must contain at least one of the following structures: {vocal_structs}")
|
| 128 |
-
lyric_norm = " ; ".join(paragraphs_norm)
|
| 129 |
-
|
| 130 |
-
# format prompt
|
| 131 |
-
if prompt_audio is not None:
|
| 132 |
-
genre = None
|
| 133 |
-
description = None
|
| 134 |
-
elif description is not None and description != "":
|
| 135 |
-
genre = None
|
| 136 |
-
|
| 137 |
-
progress(0.0, "Start Generation")
|
| 138 |
-
start = time.time()
|
| 139 |
|
| 140 |
-
|
| 141 |
-
|
| 142 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 143 |
|
| 144 |
-
|
| 145 |
-
|
| 146 |
-
|
| 147 |
-
|
| 148 |
-
|
| 149 |
-
|
| 150 |
-
|
| 151 |
-
"
|
| 152 |
-
"
|
|
|
|
|
|
|
|
|
|
|
|
|
| 153 |
}
|
| 154 |
-
|
| 155 |
-
filepath = save_as_flac(sample_rate, audio_data)
|
| 156 |
-
return filepath, json.dumps(input_config, indent=2)
|
| 157 |
-
|
| 158 |
|
| 159 |
-
#
|
| 160 |
-
|
| 161 |
-
|
| 162 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 163 |
|
| 164 |
-
|
| 165 |
-
|
| 166 |
-
|
| 167 |
-
|
| 168 |
-
lines=5,
|
| 169 |
-
max_lines=15,
|
| 170 |
-
value=EXAMPLE_LYRICS,
|
| 171 |
-
info="Each paragraph represents a segment starting with a structure tag and ending with a blank line, each line is a sentence without punctuation, segments [intro], [inst], [outro] should not contain lyrics, while [verse], [chorus], and [bridge] require lyrics.",
|
| 172 |
-
placeholder="""Lyric Format
|
| 173 |
-
'''
|
| 174 |
-
[structure tag]
|
| 175 |
-
lyrics
|
| 176 |
|
| 177 |
-
[structure tag]
|
| 178 |
-
lyrics
|
| 179 |
-
'''
|
| 180 |
-
1. One paragraph represents one segments, starting with a structure tag and ending with a blank line
|
| 181 |
-
2. One line represents one sentence, punctuation is not recommended inside the sentence
|
| 182 |
-
3. The following segments should not contain lyrics: [intro-short], [intro-medium], [inst-short], [inst-medium], [outro-short], [outro-medium]
|
| 183 |
-
4. The following segments require lyrics: [verse], [chorus], [bridge]
|
| 184 |
-
"""
|
| 185 |
-
)
|
| 186 |
-
|
| 187 |
-
with gr.Tabs(elem_id="extra-tabs"):
|
| 188 |
-
with gr.Tab("Genre Select"):
|
| 189 |
-
genre = gr.Radio(
|
| 190 |
-
choices=["Auto", "Pop", "R&B", "Dance", "Jazz", "Folk", "Rock", "Chinese Style", "Chinese Tradition", "Metal", "Reggae", "Chinese Opera"],
|
| 191 |
-
label="Genre Select(Optional)",
|
| 192 |
-
value="Auto",
|
| 193 |
-
interactive=True,
|
| 194 |
-
elem_id="single-select-radio"
|
| 195 |
-
)
|
| 196 |
-
with gr.Tab("Audio Prompt"):
|
| 197 |
-
prompt_audio = gr.Audio(
|
| 198 |
-
label="Prompt Audio (Optional)",
|
| 199 |
-
type="filepath",
|
| 200 |
-
elem_id="audio-prompt"
|
| 201 |
-
)
|
| 202 |
-
with gr.Tab("Text Prompt"):
|
| 203 |
-
gr.Markdown("For detailed usage, please refer to [here](https://github.com/tencent-ailab/SongGeneration?tab=readme-ov-file#-description-input-format)")
|
| 204 |
-
description = gr.Textbox(
|
| 205 |
-
label="Song Description (Optional)",
|
| 206 |
-
info="Describe the gender, timbre, genre, emotion, instrument and bpm of the song. Only English is supported currently.",
|
| 207 |
-
placeholder="female, dark, pop, sad, piano and drums, the bpm is 125.",
|
| 208 |
-
lines=1,
|
| 209 |
-
max_lines=2
|
| 210 |
-
)
|
| 211 |
-
|
| 212 |
-
with gr.Accordion("Advanced Config", open=False):
|
| 213 |
-
cfg_coef = gr.Slider(
|
| 214 |
-
label="CFG Coefficient",
|
| 215 |
-
minimum=0.1,
|
| 216 |
-
maximum=3.0,
|
| 217 |
-
step=0.1,
|
| 218 |
-
value=1.5,
|
| 219 |
-
interactive=True,
|
| 220 |
-
elem_id="cfg-coef",
|
| 221 |
-
)
|
| 222 |
-
temperature = gr.Slider(
|
| 223 |
-
label="Temperature",
|
| 224 |
-
minimum=0.1,
|
| 225 |
-
maximum=2.0,
|
| 226 |
-
step=0.1,
|
| 227 |
-
value=0.8,
|
| 228 |
-
interactive=True,
|
| 229 |
-
elem_id="temperature",
|
| 230 |
-
)
|
| 231 |
-
# top_k = gr.Slider(
|
| 232 |
-
# label="Top-K",
|
| 233 |
-
# minimum=1,
|
| 234 |
-
# maximum=100,
|
| 235 |
-
# step=1,
|
| 236 |
-
# value=50,
|
| 237 |
-
# interactive=True,
|
| 238 |
-
# elem_id="top_k",
|
| 239 |
-
# )
|
| 240 |
-
with gr.Row():
|
| 241 |
-
generate_btn = gr.Button("Generate Song", variant="primary")
|
| 242 |
-
generate_bgm_btn = gr.Button("Generate Pure Music", variant="primary")
|
| 243 |
-
|
| 244 |
-
with gr.Column():
|
| 245 |
-
output_audio = gr.Audio(label="Generated Song", type="filepath")
|
| 246 |
-
output_json = gr.JSON(label="Generated Info")
|
| 247 |
-
|
| 248 |
-
# # 示例按钮
|
| 249 |
-
# examples = gr.Examples(
|
| 250 |
-
# examples=[
|
| 251 |
-
# ["male, bright, rock, happy, electric guitar and drums, the bpm is 150."],
|
| 252 |
-
# ["female, warm, jazz, romantic, synthesizer and piano, the bpm is 100."]
|
| 253 |
-
# ],
|
| 254 |
-
# inputs=[description],
|
| 255 |
-
# label="Text Prompt examples"
|
| 256 |
-
# )
|
| 257 |
-
|
| 258 |
-
# examples = gr.Examples(
|
| 259 |
-
# examples=[
|
| 260 |
-
# "[intro-medium]\n\n[verse]\n在这个疯狂的世界里\n谁不渴望一点改变\n在爱情面前\n我们都显得那么不安全\n你紧紧抱着我\n告诉我再靠近一点\n别让这璀璨的夜晚白白浪费\n我那迷茫的眼睛\n看不见未来的路\n在情感消散之前\n我们对爱的渴望永不熄灭\n你给我留下一句誓言\n想知道我们的爱是否能持续到永远\n[chorus]\n\n约定在那最后的夜晚\n不管命运如何摆布\n我们的心是否依然如初\n我会穿上红衬衫\n带着摇滚的激情\n回到我们初遇的地方\n约定在那最后的夜晚\n就算全世界都变了样\n我依然坚守诺言\n铭记这一天\n你永远是我心中的爱恋\n\n[outro-medium]\n",
|
| 261 |
-
# "[intro-short]\n\n[verse]\nThrough emerald canyons where fireflies dwell\nCerulean berries kiss morning's first swell\nCrystalline dew crowns each Vitamin Dawn's confection dissolves slowly on me\nAmbrosia breezes through honeycomb vines\nNature's own candy in Fibonacci lines\n[chorus] Blueberry fruit so sweet\n takes you higher\n can't be beat\n In your lungs\n it starts to swell\n You're under its spell\n [verse] Resin of sunlight in candied retreat\nMarmalade moonbeams melt under bare feet\nNectar spirals bloom chloroplast champagne\nPhotosynthesis sings through my veins\nChlorophyll rhythms pulse warm in my blood\nThe forest's green pharmacy floods every bud[chorus] Blueberry fruit so sweet\n takes you higher\n can't be beat\n In your lungs\n it starts to swell\n You're under its spell\n feel the buzz\n ride the wave\n Limey me\n blueberry\n your mind's enslaved\n In the haze\n lose all time\n floating free\n feeling fine\n Blueberry\n fruit so sweet\n takes you higher\n can't be beat\n In your lungs\n it starts to swell\n cry\n You're under its spell\n\n[outro-short]\n",
|
| 262 |
-
# ],
|
| 263 |
-
# inputs=[lyric],
|
| 264 |
-
# label="Lyrics examples",
|
| 265 |
-
# )
|
| 266 |
-
|
| 267 |
-
# 生成按钮点击事件
|
| 268 |
-
generate_btn.click(
|
| 269 |
-
fn=generate_song,
|
| 270 |
-
inputs=[lyric, description, prompt_audio, genre, cfg_coef, temperature, gr.State(50)],
|
| 271 |
-
outputs=[output_audio, output_json]
|
| 272 |
-
)
|
| 273 |
-
generate_bgm_btn.click(
|
| 274 |
-
fn=generate_song,
|
| 275 |
-
inputs=[lyric, description, prompt_audio, genre, cfg_coef, temperature, gr.State(50), gr.State("bgm")],
|
| 276 |
-
outputs=[output_audio, output_json]
|
| 277 |
-
)
|
| 278 |
-
|
| 279 |
-
|
| 280 |
-
# 启动应用
|
| 281 |
if __name__ == "__main__":
|
| 282 |
-
|
| 283 |
-
demo.launch(server_name="0.0.0.0", server_port=7860)
|
| 284 |
-
|
|
|
|
| 1 |
+
from fastapi import FastAPI, HTTPException
|
| 2 |
+
from fastapi.responses import HTMLResponse
|
| 3 |
+
from fastapi.staticfiles import StaticFiles
|
| 4 |
+
import uvicorn
|
|
|
|
|
|
|
| 5 |
import os
|
| 6 |
+
from datetime import datetime
|
| 7 |
+
import json
|
| 8 |
+
from typing import Dict, List
|
| 9 |
+
|
| 10 |
+
app = FastAPI(title="Docker Demo App", version="1.0.0")
|
| 11 |
+
|
| 12 |
+
# Sample data store (in-memory)
|
| 13 |
+
data_store: Dict[str, Dict] = {}
|
| 14 |
+
|
| 15 |
+
@app.get("/", response_class=HTMLResponse)
|
| 16 |
+
async def root():
|
| 17 |
+
"""Main page with the anycoder attribution"""
|
| 18 |
+
html_content = """
|
| 19 |
+
<!DOCTYPE html>
|
| 20 |
+
<html>
|
| 21 |
+
<head>
|
| 22 |
+
<title>Docker App on HuggingFace</title>
|
| 23 |
+
<style>
|
| 24 |
+
body {
|
| 25 |
+
font-family: Arial, sans-serif;
|
| 26 |
+
max-width: 800px;
|
| 27 |
+
margin: 0 auto;
|
| 28 |
+
padding: 20px;
|
| 29 |
+
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
| 30 |
+
color: white;
|
| 31 |
+
min-height: 100vh;
|
| 32 |
+
}
|
| 33 |
+
.container {
|
| 34 |
+
background: rgba(255, 255, 255, 0.1);
|
| 35 |
+
padding: 30px;
|
| 36 |
+
border-radius: 10px;
|
| 37 |
+
backdrop-filter: blur(10px);
|
| 38 |
+
}
|
| 39 |
+
h1 {
|
| 40 |
+
text-align: center;
|
| 41 |
+
margin-bottom: 30px;
|
| 42 |
+
}
|
| 43 |
+
.endpoint {
|
| 44 |
+
background: rgba(255, 255, 255, 0.2);
|
| 45 |
+
padding: 15px;
|
| 46 |
+
margin: 10px 0;
|
| 47 |
+
border-radius: 5px;
|
| 48 |
+
}
|
| 49 |
+
.method {
|
| 50 |
+
display: inline-block;
|
| 51 |
+
padding: 3px 8px;
|
| 52 |
+
border-radius: 3px;
|
| 53 |
+
font-weight: bold;
|
| 54 |
+
margin-right: 10px;
|
| 55 |
+
}
|
| 56 |
+
.get { background: #61affe; }
|
| 57 |
+
.post { background: #49cc90; }
|
| 58 |
+
.delete { background: #f93e3e; }
|
| 59 |
+
a {
|
| 60 |
+
color: #ffd700;
|
| 61 |
+
text-decoration: none;
|
| 62 |
+
}
|
| 63 |
+
a:hover {
|
| 64 |
+
text-decoration: underline;
|
| 65 |
+
}
|
| 66 |
+
.footer {
|
| 67 |
+
margin-top: 40px;
|
| 68 |
+
text-align: center;
|
| 69 |
+
font-size: 14px;
|
| 70 |
+
}
|
| 71 |
+
</style>
|
| 72 |
+
</head>
|
| 73 |
+
<body>
|
| 74 |
+
<div class="container">
|
| 75 |
+
<h1>🐳 Docker Application on HuggingFace Spaces</h1>
|
| 76 |
+
<p>Welcome! This is a Docker-based FastAPI application running on HuggingFace Spaces.</p>
|
| 77 |
+
|
| 78 |
+
<h2>Available Endpoints:</h2>
|
| 79 |
+
|
| 80 |
+
<div class="endpoint">
|
| 81 |
+
<span class="method get">GET</span>
|
| 82 |
+
<strong>/health</strong> - Health check endpoint
|
| 83 |
+
</div>
|
| 84 |
+
|
| 85 |
+
<div class="endpoint">
|
| 86 |
+
<span class="method get">GET</span>
|
| 87 |
+
<strong>/items</strong> - List all items
|
| 88 |
+
</div>
|
| 89 |
+
|
| 90 |
+
<div class="endpoint">
|
| 91 |
+
<span class="method post">POST</span>
|
| 92 |
+
<strong>/items</strong> - Create a new item
|
| 93 |
+
</div>
|
| 94 |
+
|
| 95 |
+
<div class="endpoint">
|
| 96 |
+
<span class="method get">GET</span>
|
| 97 |
+
<strong>/items/{item_id}</strong> - Get a specific item
|
| 98 |
+
</div>
|
| 99 |
+
|
| 100 |
+
<div class="endpoint">
|
| 101 |
+
<span class="method delete">DELETE</span>
|
| 102 |
+
<strong>/items/{item_id}</strong> - Delete an item
|
| 103 |
+
</div>
|
| 104 |
+
|
| 105 |
+
<div class="endpoint">
|
| 106 |
+
<span class="method get">GET</span>
|
| 107 |
+
<strong>/info</strong> - System information
|
| 108 |
+
</div>
|
| 109 |
+
|
| 110 |
+
<div class="endpoint">
|
| 111 |
+
<span class="method get">GET</span>
|
| 112 |
+
<strong>/docs</strong> - Interactive API documentation
|
| 113 |
+
</div>
|
| 114 |
+
|
| 115 |
+
<div class="footer">
|
| 116 |
+
<p>Built with <a href="https://huggingface.co/spaces/akhaliq/anycoder" target="_blank">anycoder</a></p>
|
| 117 |
+
<p>Deployed with Docker on HuggingFace Spaces</p>
|
| 118 |
+
</div>
|
| 119 |
+
</div>
|
| 120 |
+
</body>
|
| 121 |
+
</html>
|
| 122 |
+
"""
|
| 123 |
+
return html_content
|
| 124 |
+
|
| 125 |
+
@app.get("/health")
|
| 126 |
+
async def health_check():
|
| 127 |
+
"""Health check endpoint"""
|
| 128 |
+
return {
|
| 129 |
+
"status": "healthy",
|
| 130 |
+
"timestamp": datetime.utcnow().isoformat(),
|
| 131 |
+
"version": "1.0.0"
|
| 132 |
+
}
|
| 133 |
|
| 134 |
+
@app.get("/items", response_model=List[Dict])
|
| 135 |
+
async def list_items():
|
| 136 |
+
"""List all items"""
|
| 137 |
+
return list(data_store.values())
|
| 138 |
|
| 139 |
+
@app.post("/items")
|
| 140 |
+
async def create_item(item: Dict):
|
| 141 |
+
"""Create a new item"""
|
| 142 |
+
if "id" not in item:
|
| 143 |
+
raise HTTPException(status_code=400, detail="Item must have an 'id' field")
|
|
|
|
|
|
|
|
|
|
| 144 |
|
| 145 |
+
item_id = str(item["id"])
|
| 146 |
+
if item_id in data_store:
|
| 147 |
+
raise HTTPException(status_code=409, detail="Item with this ID already exists")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 148 |
|
| 149 |
+
item["created_at"] = datetime.utcnow().isoformat()
|
| 150 |
+
data_store[item_id] = item
|
| 151 |
+
return {"message": "Item created successfully", "item": item}
|
| 152 |
+
|
| 153 |
+
@app.get("/items/{item_id}")
|
| 154 |
+
async def get_item(item_id: str):
|
| 155 |
+
"""Get a specific item"""
|
| 156 |
+
if item_id not in data_store:
|
| 157 |
+
raise HTTPException(status_code=404, detail="Item not found")
|
| 158 |
+
return data_store[item_id]
|
| 159 |
+
|
| 160 |
+
@app.delete("/items/{item_id}")
|
| 161 |
+
async def delete_item(item_id: str):
|
| 162 |
+
"""Delete an item"""
|
| 163 |
+
if item_id not in data_store:
|
| 164 |
+
raise HTTPException(status_code=404, detail="Item not found")
|
| 165 |
|
| 166 |
+
deleted_item = data_store.pop(item_id)
|
| 167 |
+
return {"message": "Item deleted successfully", "item": deleted_item}
|
| 168 |
+
|
| 169 |
+
@app.get("/info")
|
| 170 |
+
async def system_info():
|
| 171 |
+
"""Get system information"""
|
| 172 |
+
return {
|
| 173 |
+
"app_name": "Docker Demo App",
|
| 174 |
+
"environment": os.getenv("ENVIRONMENT", "development"),
|
| 175 |
+
"python_version": os.sys.version,
|
| 176 |
+
"docker": True,
|
| 177 |
+
"platform": os.name,
|
| 178 |
+
"total_items": len(data_store)
|
| 179 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
| 180 |
|
| 181 |
+
# Add some sample data on startup
|
| 182 |
+
@app.on_event("startup")
|
| 183 |
+
async def startup_event():
|
| 184 |
+
"""Initialize with sample data"""
|
| 185 |
+
sample_items = [
|
| 186 |
+
{"id": 1, "name": "Sample Item 1", "description": "This is a sample item"},
|
| 187 |
+
{"id": 2, "name": "Sample Item 2", "description": "Another sample item"},
|
| 188 |
+
{"id": 3, "name": "Docker Demo", "description": "Running in Docker container"}
|
| 189 |
+
]
|
| 190 |
|
| 191 |
+
for item in sample_items:
|
| 192 |
+
item_id = str(item["id"])
|
| 193 |
+
item["created_at"] = datetime.utcnow().isoformat()
|
| 194 |
+
data_store[item_id] = item
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 195 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 196 |
if __name__ == "__main__":
|
| 197 |
+
uvicorn.run(app, host="0.0.0.0", port=7860)
|
|
|
|
|
|