Spaces:
Sleeping
Sleeping
Commit
Β·
d53004d
1
Parent(s):
7006220
updated files
Browse files- ArchitectureDiagram.png +3 -0
- Demo/02_Tech_Deck.pdf +3 -0
- Demo/03_Cost_Sheet.xlsx +0 -0
- Demo/04_Team_Profile.pdf +3 -0
- Demo/05-Roadmap.pdf +3 -0
- Demo/06-End Slide.pdf +3 -0
- Demo/old-02_Tech_Deck.pdf +3 -0
- app/api/v1/audio.py +17 -12
- app/api/v1/image.py +21 -34
- app/api/v1/video.py +21 -13
- app/services/video_service.py +83 -56
- generated_audio/audio_46204c7f1cdd4d21b632318c1533798f.mp3 +3 -0
- generated_audio/audio_4e8cfaddd1e143a4a7a11a8baeafa178.mp3 +3 -0
- generated_audio/audio_5d3d0ac54e0e4b7ab91a88a5e6cb5a7a.mp3 +3 -0
- generated_audio/audio_6b4a3eaef6c745d4932063aba95541e5.mp3 +3 -0
- generated_audio/audio_772adc2bcf6a49e28757294a6157a71c.mp3 +3 -0
- generated_audio/audio_8d99b8f248d549f3806c1fe705c8a24c.mp3 +3 -0
- generated_audio/audio_8e0e1a375aa8415eb3be3689fdfc015e.mp3 +3 -0
- generated_audio/audio_d9f41d5ef87c4d088bd7cd230a75cd62.mp3 +3 -0
- generated_audio/audio_db2b96e4b94e4bcf8ab796d4fe483e41.mp3 +3 -0
- streamlit_ui - Copy.py +213 -0
- streamlit_ui.py +55 -138
ArchitectureDiagram.png
ADDED
![]() |
Git LFS Details
|
Demo/02_Tech_Deck.pdf
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:1b45c266eb01b16301621ee2e31ea8282cd1d72fff4bf959522b0b302f5b7d0e
|
3 |
+
size 10621
|
Demo/03_Cost_Sheet.xlsx
ADDED
Binary file (5.63 kB). View file
|
|
Demo/04_Team_Profile.pdf
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:e71ceecc9d5d85324fd31ed8b268e222977f8dfc1455cb44e260b2aa74ad5de0
|
3 |
+
size 73529
|
Demo/05-Roadmap.pdf
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:ea63aed5200371c1b3bc42cf06f9e4f1d3cded3afe53f27f5789de4cbccc352d
|
3 |
+
size 83787
|
Demo/06-End Slide.pdf
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:eaab3d85667480068276f4c7c45db0ce1a31c7b26227ac904eb5b8ad4e6cf0ca
|
3 |
+
size 3922333
|
Demo/old-02_Tech_Deck.pdf
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:8887e7d3825f880a83d1d4c94d1d9dbc3dbe3db290b6d4ee03c14a918b07bd45
|
3 |
+
size 1730
|
app/api/v1/audio.py
CHANGED
@@ -1,11 +1,9 @@
|
|
1 |
-
from fastapi import APIRouter, HTTPException,
|
|
|
2 |
from pydantic import BaseModel
|
3 |
-
from
|
4 |
-
|
5 |
-
import
|
6 |
-
import os # β
Also needed
|
7 |
-
from gtts import gTTS # β
Needed if you're calling it directly here
|
8 |
-
|
9 |
router = APIRouter()
|
10 |
|
11 |
class AudioRequest(BaseModel):
|
@@ -16,14 +14,21 @@ class AudioRequest(BaseModel):
|
|
16 |
@router.post("/generate")
|
17 |
def generate_audio_endpoint(payload: AudioRequest):
|
18 |
try:
|
|
|
19 |
filename = f"audio_{uuid.uuid4().hex}.mp3"
|
20 |
-
|
21 |
os.makedirs("generated_audio", exist_ok=True)
|
|
|
|
|
|
|
22 |
tts = gTTS(text=payload.text, lang=payload.language)
|
23 |
tts.save(file_path)
|
24 |
-
|
25 |
-
|
26 |
-
|
27 |
-
|
|
|
|
|
|
|
28 |
except Exception as e:
|
29 |
raise HTTPException(status_code=500, detail=str(e))
|
|
|
1 |
+
from fastapi import APIRouter, HTTPException, Body
|
2 |
+
from fastapi.responses import Response # β
add this
|
3 |
from pydantic import BaseModel
|
4 |
+
from gtts import gTTS
|
5 |
+
import uuid
|
6 |
+
import os
|
|
|
|
|
|
|
7 |
router = APIRouter()
|
8 |
|
9 |
class AudioRequest(BaseModel):
|
|
|
14 |
@router.post("/generate")
|
15 |
def generate_audio_endpoint(payload: AudioRequest):
|
16 |
try:
|
17 |
+
# β
Save inside generated/audio for consistency
|
18 |
filename = f"audio_{uuid.uuid4().hex}.mp3"
|
19 |
+
folder = "generated/audio"
|
20 |
os.makedirs("generated_audio", exist_ok=True)
|
21 |
+
file_path = f"generated_audio/{filename}" # β
match your video & image folders
|
22 |
+
|
23 |
+
# β
Generate TTS audio
|
24 |
tts = gTTS(text=payload.text, lang=payload.language)
|
25 |
tts.save(file_path)
|
26 |
+
|
27 |
+
# β
Return audio bytes for inline Streamlit playback
|
28 |
+
with open(file_path, "rb") as f:
|
29 |
+
audio_bytes = f.read()
|
30 |
+
|
31 |
+
return Response(content=audio_bytes, media_type="audio/mpeg")
|
32 |
+
|
33 |
except Exception as e:
|
34 |
raise HTTPException(status_code=500, detail=str(e))
|
app/api/v1/image.py
CHANGED
@@ -1,54 +1,41 @@
|
|
1 |
from fastapi import APIRouter, HTTPException, Depends, Body
|
|
|
2 |
from pydantic import BaseModel
|
3 |
from app.auth.auth import verify_token
|
4 |
-
|
5 |
-
from datetime import datetime
|
6 |
import os
|
|
|
|
|
|
|
|
|
7 |
|
|
|
8 |
router = APIRouter()
|
9 |
|
|
|
10 |
class ImageRequest(BaseModel):
|
11 |
prompt: str
|
12 |
style: str = "default"
|
13 |
|
14 |
-
|
15 |
-
|
16 |
-
filename: str
|
17 |
-
download_url: str
|
18 |
|
19 |
-
|
|
|
20 |
def generate_image_file_endpoint(
|
21 |
data: ImageRequest = Body(...),
|
22 |
token: str = Depends(verify_token)
|
23 |
):
|
24 |
-
|
25 |
-
|
26 |
-
filename = f"image_{datetime.now().strftime('%Y%m%d_%H%M%S')}.png"
|
27 |
-
folder = "generated/image"
|
28 |
-
os.makedirs(folder, exist_ok=True)
|
29 |
-
output_path = os.path.join(folder, filename)
|
30 |
|
31 |
try:
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
except:
|
38 |
-
font = ImageFont.load_default()
|
39 |
-
|
40 |
-
draw.text((20, 20), f"Prompt: {prompt}", fill="black", font=font)
|
41 |
-
draw.text((20, 60), f"Style: {style}", fill="gray", font=font)
|
42 |
-
|
43 |
-
img.save(output_path, format="PNG")
|
44 |
-
|
45 |
-
print(f"β
Image created: {output_path}, size = {os.path.getsize(output_path)} bytes")
|
46 |
-
return {
|
47 |
-
"message": "Image generated successfully",
|
48 |
-
"filename": filename,
|
49 |
-
"download_url": f"/api/v1/download?file_path=generated/image/{filename}"
|
50 |
-
}
|
51 |
|
52 |
except Exception as e:
|
53 |
-
print(f"β Image
|
54 |
-
raise HTTPException(status_code=500, detail=
|
|
|
1 |
from fastapi import APIRouter, HTTPException, Depends, Body
|
2 |
+
from fastapi.responses import Response
|
3 |
from pydantic import BaseModel
|
4 |
from app.auth.auth import verify_token
|
5 |
+
import requests
|
|
|
6 |
import os
|
7 |
+
from pydantic import BaseModel
|
8 |
+
from dotenv import load_dotenv
|
9 |
+
load_dotenv()
|
10 |
+
|
11 |
|
12 |
+
# β
Define router
|
13 |
router = APIRouter()
|
14 |
|
15 |
+
# β
Define Request schema
|
16 |
class ImageRequest(BaseModel):
|
17 |
prompt: str
|
18 |
style: str = "default"
|
19 |
|
20 |
+
UNSPLASH_ACCESS_KEY = os.getenv("UNSPLASH_ACCESS_KEY") # store this in .env
|
21 |
+
print(f"unsplash key is: {UNSPLASH_ACCESS_KEY}")
|
|
|
|
|
22 |
|
23 |
+
# β
Endpoint
|
24 |
+
@router.post("/generate")
|
25 |
def generate_image_file_endpoint(
|
26 |
data: ImageRequest = Body(...),
|
27 |
token: str = Depends(verify_token)
|
28 |
):
|
29 |
+
query = f"{data.prompt} {data.style}"
|
30 |
+
url = f"https://api.unsplash.com/photos/random?query={query}&client_id={UNSPLASH_ACCESS_KEY}&orientation=landscape"
|
|
|
|
|
|
|
|
|
31 |
|
32 |
try:
|
33 |
+
r = requests.get(url)
|
34 |
+
r.raise_for_status()
|
35 |
+
image_url = r.json()["urls"]["regular"]
|
36 |
+
img_data = requests.get(image_url).content
|
37 |
+
return Response(content=img_data, media_type="image/jpeg")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
38 |
|
39 |
except Exception as e:
|
40 |
+
print(f"β Image fetch failed: {str(e)}")
|
41 |
+
raise HTTPException(status_code=500, detail="Image generation failed.")
|
app/api/v1/video.py
CHANGED
@@ -1,13 +1,13 @@
|
|
1 |
# app/api/v1/video.py
|
2 |
from fastapi import APIRouter, HTTPException, Depends, Body
|
|
|
3 |
from pydantic import BaseModel
|
4 |
from app.services.video_service import generate_video_file
|
5 |
from app.auth.auth import verify_token
|
6 |
-
import
|
7 |
-
import os # β
Also needed
|
8 |
-
from gtts import gTTS # β
Needed if you're calling it directly here
|
9 |
from typing import Optional
|
10 |
|
|
|
11 |
router = APIRouter()
|
12 |
|
13 |
class VideoInput(BaseModel):
|
@@ -15,23 +15,31 @@ class VideoInput(BaseModel):
|
|
15 |
tone: str
|
16 |
domain: str
|
17 |
environment: str
|
18 |
-
transcript: Optional[str] = None
|
19 |
|
20 |
@router.post("/generate")
|
21 |
def generate_video_endpoint(
|
22 |
payload: VideoInput = Body(...),
|
23 |
-
token: str = Depends(verify_token)
|
|
|
24 |
try:
|
25 |
-
#
|
26 |
filename = generate_video_file(
|
27 |
-
script=payload.prompt,
|
28 |
-
duration=10 #
|
29 |
)
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
"
|
34 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
35 |
except Exception as e:
|
36 |
print("β Video generation error:", str(e))
|
37 |
raise HTTPException(status_code=500, detail=str(e))
|
|
|
1 |
# app/api/v1/video.py
|
2 |
from fastapi import APIRouter, HTTPException, Depends, Body
|
3 |
+
from fastapi.responses import FileResponse
|
4 |
from pydantic import BaseModel
|
5 |
from app.services.video_service import generate_video_file
|
6 |
from app.auth.auth import verify_token
|
7 |
+
import os
|
|
|
|
|
8 |
from typing import Optional
|
9 |
|
10 |
+
# β
Define router FIRST
|
11 |
router = APIRouter()
|
12 |
|
13 |
class VideoInput(BaseModel):
|
|
|
15 |
tone: str
|
16 |
domain: str
|
17 |
environment: str
|
18 |
+
transcript: Optional[str] = None
|
19 |
|
20 |
@router.post("/generate")
|
21 |
def generate_video_endpoint(
|
22 |
payload: VideoInput = Body(...),
|
23 |
+
token: str = Depends(verify_token)
|
24 |
+
):
|
25 |
try:
|
26 |
+
# Generate video file
|
27 |
filename = generate_video_file(
|
28 |
+
script=payload.prompt,
|
29 |
+
duration=10 # Optional: could be dynamic
|
30 |
)
|
31 |
+
video_path = os.path.join("generated/video", filename)
|
32 |
+
|
33 |
+
if not os.path.exists(video_path):
|
34 |
+
raise HTTPException(status_code=500, detail="Video not found")
|
35 |
+
|
36 |
+
# β
Return the actual file for Streamlit to play
|
37 |
+
return FileResponse(
|
38 |
+
video_path,
|
39 |
+
media_type="video/mp4",
|
40 |
+
filename=filename
|
41 |
+
)
|
42 |
+
|
43 |
except Exception as e:
|
44 |
print("β Video generation error:", str(e))
|
45 |
raise HTTPException(status_code=500, detail=str(e))
|
app/services/video_service.py
CHANGED
@@ -1,67 +1,94 @@
|
|
1 |
# app/services/video_service.py
|
2 |
|
3 |
-
import cv2
|
4 |
-
import numpy as np
|
5 |
import os
|
6 |
import uuid
|
7 |
-
import
|
8 |
from gtts import gTTS
|
9 |
from mutagen.mp3 import MP3
|
10 |
-
import
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
11 |
|
12 |
def generate_video_file(script: str, duration: int = None) -> str:
|
13 |
-
|
14 |
-
|
15 |
-
|
16 |
-
final_video_path = raw_video_path.replace(".mp4", "_final.mp4")
|
17 |
|
18 |
-
|
19 |
-
os.
|
20 |
-
|
21 |
|
22 |
-
# Generate audio
|
23 |
tts = gTTS(text=script, lang='en')
|
24 |
-
tts.save(
|
25 |
-
|
26 |
-
# Get
|
27 |
-
audio = MP3(
|
28 |
-
audio_duration = audio.info.length #
|
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 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
# app/services/video_service.py
|
2 |
|
|
|
|
|
3 |
import os
|
4 |
import uuid
|
5 |
+
import requests
|
6 |
from gtts import gTTS
|
7 |
from mutagen.mp3 import MP3
|
8 |
+
from moviepy.editor import ImageClip, concatenate_videoclips, AudioFileClip
|
9 |
+
from dotenv import load_dotenv
|
10 |
+
|
11 |
+
load_dotenv()
|
12 |
+
|
13 |
+
UNSPLASH_KEY = os.getenv("UNSPLASH_ACCESS_KEY")
|
14 |
+
UNSPLASH_API = "https://api.unsplash.com/photos/random"
|
15 |
+
|
16 |
+
def fetch_unsplash_images(query, count=3):
|
17 |
+
headers = {"Accept-Version": "v1", "Authorization": f"Client-ID {UNSPLASH_KEY}"}
|
18 |
+
urls = []
|
19 |
+
|
20 |
+
for _ in range(count):
|
21 |
+
r = requests.get(UNSPLASH_API, params={"query": query}, headers=headers)
|
22 |
+
if r.status_code == 200:
|
23 |
+
data = r.json()
|
24 |
+
if isinstance(data, dict):
|
25 |
+
urls.append(data["urls"]["regular"])
|
26 |
+
elif isinstance(data, list) and len(data) > 0:
|
27 |
+
urls.append(data[0]["urls"]["regular"])
|
28 |
+
return urls
|
29 |
|
30 |
def generate_video_file(script: str, duration: int = None) -> str:
|
31 |
+
os.makedirs("generated/video", exist_ok=True)
|
32 |
+
os.makedirs("generated/audio", exist_ok=True)
|
33 |
+
os.makedirs("generated/tmp", exist_ok=True)
|
|
|
34 |
|
35 |
+
video_filename = f"video_{uuid.uuid4().hex}.mp4"
|
36 |
+
video_path = os.path.join("generated/video", video_filename)
|
37 |
+
audio_path = f"generated/audio/audio_{uuid.uuid4().hex}.mp3"
|
38 |
|
39 |
+
# Step 1: Generate audio
|
40 |
tts = gTTS(text=script, lang='en')
|
41 |
+
tts.save(audio_path)
|
42 |
+
|
43 |
+
# Get audio duration (fallback if 0)
|
44 |
+
audio = MP3(audio_path)
|
45 |
+
audio_duration = max(audio.info.length, 3.0) # ensure at least 3s
|
46 |
+
|
47 |
+
# Step 2: Fetch Unsplash images
|
48 |
+
images = fetch_unsplash_images(script, count=3)
|
49 |
+
if not images:
|
50 |
+
raise Exception("No images found from Unsplash for the prompt")
|
51 |
+
|
52 |
+
# Step 3: Create slideshow clips
|
53 |
+
clips = []
|
54 |
+
per_image_duration = audio_duration / len(images)
|
55 |
+
tmp_files = []
|
56 |
+
|
57 |
+
for url in images:
|
58 |
+
img_data = requests.get(url).content
|
59 |
+
tmp_file = f"generated/tmp/tmp_{uuid.uuid4().hex}.jpg"
|
60 |
+
tmp_files.append(tmp_file)
|
61 |
+
|
62 |
+
with open(tmp_file, "wb") as f:
|
63 |
+
f.write(img_data)
|
64 |
+
|
65 |
+
clip = ImageClip(tmp_file).resize(height=720).set_duration(per_image_duration)
|
66 |
+
clips.append(clip)
|
67 |
+
|
68 |
+
# Step 4: Concatenate without negative padding
|
69 |
+
final_clip = concatenate_videoclips(clips, method="compose")
|
70 |
+
|
71 |
+
# Step 5: Force duration to match audio
|
72 |
+
final_clip = final_clip.set_duration(audio_duration)
|
73 |
+
|
74 |
+
# Step 6: Add audio
|
75 |
+
final_clip = final_clip.set_audio(AudioFileClip(audio_path))
|
76 |
+
|
77 |
+
# Step 7: Export video
|
78 |
+
final_clip.write_videofile(
|
79 |
+
video_path,
|
80 |
+
fps=24,
|
81 |
+
codec="libx264",
|
82 |
+
audio_codec="aac",
|
83 |
+
threads=4,
|
84 |
+
preset="ultrafast"
|
85 |
+
)
|
86 |
+
|
87 |
+
# Cleanup
|
88 |
+
for file in tmp_files:
|
89 |
+
try:
|
90 |
+
os.remove(file)
|
91 |
+
except:
|
92 |
+
pass
|
93 |
+
|
94 |
+
return video_filename
|
generated_audio/audio_46204c7f1cdd4d21b632318c1533798f.mp3
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:a752e374c3cd105ec751eb30df12b3a0939e0500e7e2487b41e4269050e44906
|
3 |
+
size 57216
|
generated_audio/audio_4e8cfaddd1e143a4a7a11a8baeafa178.mp3
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:7f2d93711bae321f7bfe7f698a0a5be8b0b6462c862cf196a95a621931089ba6
|
3 |
+
size 20160
|
generated_audio/audio_5d3d0ac54e0e4b7ab91a88a5e6cb5a7a.mp3
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:6924a2184a3a139ef4d6d9bc5e1e08d3b1b69d7e6904f8acb458d40cbc90fc73
|
3 |
+
size 9024
|
generated_audio/audio_6b4a3eaef6c745d4932063aba95541e5.mp3
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:343053419eba6ee94bd9d7fd54631336e97b693dea9775f2d57c6f342953cefb
|
3 |
+
size 9024
|
generated_audio/audio_772adc2bcf6a49e28757294a6157a71c.mp3
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:d7168fbdac599d9f1f20f13a88b11188cfca4bf03c6c5e83b10f0446f501a698
|
3 |
+
size 35328
|
generated_audio/audio_8d99b8f248d549f3806c1fe705c8a24c.mp3
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:2bbd01d120150bc2de7f4f6882d9194d8cf67a267ae3505dda8ddcc9b109701c
|
3 |
+
size 20160
|
generated_audio/audio_8e0e1a375aa8415eb3be3689fdfc015e.mp3
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:4c348b34bef828c56c5157933592943b8e23c02b00eb1f81cedde7e2b3a4dac7
|
3 |
+
size 72384
|
generated_audio/audio_d9f41d5ef87c4d088bd7cd230a75cd62.mp3
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:e0ba21c2e66aa0ee7d2fb7f5f7e7cc18948526f35cdcc458dc043e2ab16afef8
|
3 |
+
size 20160
|
generated_audio/audio_db2b96e4b94e4bcf8ab796d4fe483e41.mp3
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:d7168fbdac599d9f1f20f13a88b11188cfca4bf03c6c5e83b10f0446f501a698
|
3 |
+
size 35328
|
streamlit_ui - Copy.py
ADDED
@@ -0,0 +1,213 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# streamlit_ui.py
|
2 |
+
|
3 |
+
import streamlit as st
|
4 |
+
import requests
|
5 |
+
import base64
|
6 |
+
from PIL import Image
|
7 |
+
import io
|
8 |
+
import os
|
9 |
+
import tempfile
|
10 |
+
|
11 |
+
st.set_page_config(
|
12 |
+
page_title="Prompta - Text to Media Generator",
|
13 |
+
page_icon="ποΈ",
|
14 |
+
layout="wide",
|
15 |
+
initial_sidebar_state="expanded"
|
16 |
+
)
|
17 |
+
st.title("ποΈπΌοΈποΈ Prompta - Text to Media Generator")
|
18 |
+
|
19 |
+
# π οΈ Get Token FIRST
|
20 |
+
TOKEN = st.sidebar.text_input("π API Token", type="password")
|
21 |
+
HEADERS = {"Authorization": f"Bearer {TOKEN}"} if TOKEN else {}
|
22 |
+
|
23 |
+
# β
Display AFTER token is typed
|
24 |
+
if TOKEN:
|
25 |
+
st.sidebar.write("Using token:", TOKEN)
|
26 |
+
st.sidebar.write("Sending headers:", HEADERS)
|
27 |
+
else:
|
28 |
+
st.sidebar.warning("β οΈ Please enter a valid API token to use the app.")
|
29 |
+
|
30 |
+
API_BASE = "http://localhost:8000"
|
31 |
+
|
32 |
+
#API_BASE = "https://2255d6a4793d.ngrok-free.app"
|
33 |
+
|
34 |
+
def render_media(file_bytes, media_type, caption):
|
35 |
+
b64 = base64.b64encode(file_bytes).decode()
|
36 |
+
if media_type == "audio":
|
37 |
+
st.audio(f"data:audio/wav;base64,{b64}", format="audio/wav")
|
38 |
+
elif media_type == "video":
|
39 |
+
st.video(f"data:video/mp4;base64,{b64}")
|
40 |
+
elif media_type == "image":
|
41 |
+
try:
|
42 |
+
# Validate if it's a valid image
|
43 |
+
img = Image.open(io.BytesIO(file_bytes))
|
44 |
+
st.image(img, caption=caption)
|
45 |
+
except Exception as e:
|
46 |
+
st.warning("β οΈ Cannot render image. It may be corrupt or empty.")
|
47 |
+
st.code(str(e))
|
48 |
+
|
49 |
+
# Sidebar inputs
|
50 |
+
st.sidebar.header("π οΈ Settings")
|
51 |
+
#TOKEN = st.sidebar.text_input("π API Token", type="password")
|
52 |
+
#HEADERS = {"Authorization": f"Bearer {TOKEN}"} if TOKEN else {}
|
53 |
+
|
54 |
+
voice = st.selectbox("Choose voice", ["en", "hi", "te", "ta"])
|
55 |
+
voice_map = {
|
56 |
+
"en": "en-US",
|
57 |
+
"hi": "hi-IN",
|
58 |
+
"te": "te-IN",
|
59 |
+
"ta": "ta-IN"
|
60 |
+
}
|
61 |
+
|
62 |
+
tab = st.sidebar.radio("Select Task", ["Text to Audio", "Text to Image", "Text to Video"])
|
63 |
+
|
64 |
+
if tab == "Text to Audio":
|
65 |
+
st.subheader("π€ Text to Audio")
|
66 |
+
text = st.text_area("Enter text")
|
67 |
+
voice = st.selectbox("Choose language", ["English", "Hindi", "Telugu", "Tamil"])
|
68 |
+
voice_map = {
|
69 |
+
"English": ("en-US", "en"),
|
70 |
+
"Hindi": ("hi-IN", "hi"),
|
71 |
+
"Telugu": ("te-IN", "te"),
|
72 |
+
"Tamil": ("ta-IN", "ta")
|
73 |
+
}
|
74 |
+
voice_code, lang_code = voice_map[voice]
|
75 |
+
|
76 |
+
if st.button("π Generate Audio"):
|
77 |
+
with st.spinner("Generating audio..."):
|
78 |
+
r = requests.post(
|
79 |
+
f"{API_BASE}/api/v1/audio/generate",
|
80 |
+
json={
|
81 |
+
"text": text,
|
82 |
+
"voice": voice_code,
|
83 |
+
"language": lang_code
|
84 |
+
},
|
85 |
+
headers=HEADERS
|
86 |
+
)
|
87 |
+
if r.status_code == 200:
|
88 |
+
try:
|
89 |
+
data = r.json()
|
90 |
+
st.code(data, language="json") # Debug: show full JSON response in UI
|
91 |
+
|
92 |
+
if "download_url" in data:
|
93 |
+
download_url = f"{API_BASE}{data['download_url']}"
|
94 |
+
audio_resp = requests.get(download_url, headers=HEADERS)
|
95 |
+
if audio_resp.status_code == 200:
|
96 |
+
render_media(audio_resp.content, "audio", "Generated Audio")
|
97 |
+
else:
|
98 |
+
st.error("β Failed to download audio file.")
|
99 |
+
else:
|
100 |
+
st.error("β `download_url` not found in API response.")
|
101 |
+
st.code(data)
|
102 |
+
except Exception as e:
|
103 |
+
st.error("β Failed to parse API response.")
|
104 |
+
st.code(r.text)
|
105 |
+
st.exception(e)
|
106 |
+
else:
|
107 |
+
st.error(f"β Failed: {r.json().get('detail')}")
|
108 |
+
|
109 |
+
|
110 |
+
elif tab == "Text to Image":
|
111 |
+
st.subheader("πΌοΈ Text to Image")
|
112 |
+
prompt = st.text_area("Enter image prompt")
|
113 |
+
style = st.selectbox("Choose Style", ["sdxl", "deepfloyd", "kandinsky"])
|
114 |
+
|
115 |
+
if st.button("π§ Generate Image"):
|
116 |
+
with st.spinner("Generating image..."):
|
117 |
+
r = requests.post(
|
118 |
+
f"{API_BASE}/api/v1/image/generate",
|
119 |
+
json={"prompt": prompt, "style": style}, # β
correct key
|
120 |
+
headers=HEADERS
|
121 |
+
)
|
122 |
+
if r.status_code == 200:
|
123 |
+
try:
|
124 |
+
res_json = r.json()
|
125 |
+
download_url = res_json.get("download_url")
|
126 |
+
if not download_url:
|
127 |
+
st.error("No download URL returned.")
|
128 |
+
else:
|
129 |
+
download_full_url = f"{API_BASE}{download_url}"
|
130 |
+
image_response = requests.get(download_full_url, headers={"accept": "image/png"}, allow_redirects=True)
|
131 |
+
if image_response.status_code != 200:
|
132 |
+
st.error("β Failed to download image.")
|
133 |
+
st.code(image_response.text)
|
134 |
+
st.write("Status:", image_response.status_code)
|
135 |
+
st.write("Headers:", image_response.headers)
|
136 |
+
st.write(image_response.status_code, image_response.headers)
|
137 |
+
render_media(image_response.content, "image", "Generated Image")
|
138 |
+
except Exception as e:
|
139 |
+
st.error(f"β οΈ Failed to fetch/display image: {str(e)}")
|
140 |
+
st.code(r.text)
|
141 |
+
else:
|
142 |
+
try:
|
143 |
+
detail = r.json().get("detail")
|
144 |
+
except Exception:
|
145 |
+
detail = r.text # fallback to raw response text (may be empty or HTML)
|
146 |
+
|
147 |
+
st.error(f"β Failed: {detail}")
|
148 |
+
|
149 |
+
elif tab == "Text to Video":
|
150 |
+
st.subheader("ποΈ Text to Video")
|
151 |
+
prompt = st.text_area("Enter video prompt")
|
152 |
+
tone = st.selectbox("Tone", ["formal", "casual", "emotional", "documentary"])
|
153 |
+
domain = st.selectbox("Domain", ["health", "education", "governance", "entertainment"])
|
154 |
+
environment = st.selectbox("Environment", ["urban", "rural", "nature", "futuristic"])
|
155 |
+
|
156 |
+
transcript = st.text_area("Transcript (optional - for subtitles)", height=100)
|
157 |
+
enhance = st.checkbox("β¨ Add Subtitles and Background Music")
|
158 |
+
|
159 |
+
if st.button("π¬ Generate Video"):
|
160 |
+
with st.spinner("Generating video..."):
|
161 |
+
r = requests.post(
|
162 |
+
f"{API_BASE}/api/v1/video/generate",
|
163 |
+
json={"prompt": prompt, "tone": tone, "domain": domain, "environment": environment},
|
164 |
+
headers=HEADERS
|
165 |
+
)
|
166 |
+
if r.status_code == 200:
|
167 |
+
try:
|
168 |
+
data = r.json()
|
169 |
+
st.code(data, language="json")
|
170 |
+
|
171 |
+
download_url = data.get("download_url")
|
172 |
+
if not download_url:
|
173 |
+
st.error("β οΈ No download URL received.")
|
174 |
+
else:
|
175 |
+
full_video_url = f"{API_BASE}{download_url}"
|
176 |
+
video_response = requests.get(full_video_url, headers=HEADERS)
|
177 |
+
if video_response.status_code == 200:
|
178 |
+
video_bytes = video_response.content
|
179 |
+
st.write("π¦ Video size (bytes):", len(video_bytes))
|
180 |
+
|
181 |
+
if enhance and transcript:
|
182 |
+
with tempfile.NamedTemporaryFile(delete=False, suffix=".mp4") as tmp_vid:
|
183 |
+
tmp_vid.write(video_bytes)
|
184 |
+
tmp_vid_path = tmp_vid.name
|
185 |
+
|
186 |
+
srt_path = generate_srt_from_text(transcript, output_path="streamlit_subs.srt")
|
187 |
+
enhanced_path = "streamlit_final_video.mp4"
|
188 |
+
enhance_video_with_subtitles_and_bgm(
|
189 |
+
video_path=tmp_vid_path,
|
190 |
+
srt_path=srt_path,
|
191 |
+
bgm_path="default_bgm.mp3",
|
192 |
+
output_path=enhanced_path
|
193 |
+
)
|
194 |
+
|
195 |
+
with open(enhanced_path, "rb") as f:
|
196 |
+
render_media(f.read(), "video", "Enhanced Video")
|
197 |
+
else:
|
198 |
+
st.video(video_bytes)
|
199 |
+
else:
|
200 |
+
st.error("β Failed to download video.")
|
201 |
+
except Exception as e:
|
202 |
+
st.error("β Error parsing response or rendering video.")
|
203 |
+
st.code(r.text)
|
204 |
+
st.exception(e)
|
205 |
+
else:
|
206 |
+
try:
|
207 |
+
st.error(f"β Failed: {r.json().get('detail')}")
|
208 |
+
except:
|
209 |
+
st.error(f"β Failed: {r.text}")
|
210 |
+
|
211 |
+
|
212 |
+
st.sidebar.markdown("---")
|
213 |
+
st.sidebar.info("Built with β€οΈ for AI GovTech Challenge 2025")
|
streamlit_ui.py
CHANGED
@@ -1,12 +1,8 @@
|
|
1 |
# streamlit_ui.py
|
2 |
-
|
3 |
import streamlit as st
|
4 |
import requests
|
5 |
import base64
|
6 |
-
from PIL import Image
|
7 |
import io
|
8 |
-
import os
|
9 |
-
import tempfile
|
10 |
|
11 |
st.set_page_config(
|
12 |
page_title="Prompta - Text to Media Generator",
|
@@ -23,131 +19,95 @@ HEADERS = {"Authorization": f"Bearer {TOKEN}"} if TOKEN else {}
|
|
23 |
# β
Display AFTER token is typed
|
24 |
if TOKEN:
|
25 |
st.sidebar.write("Using token:", TOKEN)
|
26 |
-
st.sidebar.write("Sending headers:", HEADERS)
|
27 |
else:
|
28 |
st.sidebar.warning("β οΈ Please enter a valid API token to use the app.")
|
29 |
|
30 |
API_BASE = "http://localhost:8000"
|
31 |
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
|
38 |
-
|
39 |
-
if
|
40 |
-
st.audio(
|
41 |
-
elif
|
42 |
-
st.video(
|
43 |
-
elif
|
|
|
|
|
44 |
try:
|
45 |
-
#
|
46 |
-
|
47 |
-
|
48 |
-
|
49 |
-
|
50 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
51 |
|
|
|
|
|
|
|
52 |
st.sidebar.header("π οΈ Settings")
|
53 |
-
#TOKEN = st.sidebar.text_input("π API Token", type="password")
|
54 |
-
#HEADERS = {"Authorization": f"Bearer {TOKEN}"} if TOKEN else {}
|
55 |
-
|
56 |
-
voice = st.selectbox("Choose voice", ["en", "hi", "te", "ta"])
|
57 |
-
voice_map = {
|
58 |
-
"en": "en-US",
|
59 |
-
"hi": "hi-IN",
|
60 |
-
"te": "te-IN",
|
61 |
-
"ta": "ta-IN"
|
62 |
-
}
|
63 |
|
64 |
tab = st.sidebar.radio("Select Task", ["Text to Audio", "Text to Image", "Text to Video"])
|
65 |
|
|
|
|
|
|
|
66 |
if tab == "Text to Audio":
|
67 |
st.subheader("π€ Text to Audio")
|
68 |
text = st.text_area("Enter text")
|
69 |
-
voice = st.selectbox("Choose language", ["
|
70 |
-
voice_map = {
|
71 |
-
"English": ("en-US", "en"),
|
72 |
-
"Hindi": ("hi-IN", "hi"),
|
73 |
-
"Telugu": ("te-IN", "te"),
|
74 |
-
"Tamil": ("ta-IN", "ta")
|
75 |
-
}
|
76 |
-
voice_code, lang_code = voice_map[voice]
|
77 |
|
78 |
if st.button("π Generate Audio"):
|
79 |
with st.spinner("Generating audio..."):
|
80 |
r = requests.post(
|
81 |
-
f"{API_BASE}/api/v1/audio/generate",
|
82 |
-
json={
|
83 |
-
"text": text,
|
84 |
-
"voice": voice_code,
|
85 |
-
"language": lang_code
|
86 |
-
},
|
87 |
headers=HEADERS
|
88 |
)
|
89 |
if r.status_code == 200:
|
90 |
-
|
91 |
-
data = r.json()
|
92 |
-
st.code(data, language="json") # Debug: show full JSON response in UI
|
93 |
-
|
94 |
-
if "download_url" in data:
|
95 |
-
download_url = f"{API_BASE}{data['download_url']}"
|
96 |
-
audio_resp = requests.get(download_url, headers=HEADERS)
|
97 |
-
if audio_resp.status_code == 200:
|
98 |
-
render_media(audio_resp.content, "audio", "Generated Audio")
|
99 |
-
else:
|
100 |
-
st.error("β Failed to download audio file.")
|
101 |
-
else:
|
102 |
-
st.error("β `download_url` not found in API response.")
|
103 |
-
st.code(data)
|
104 |
-
except Exception as e:
|
105 |
-
st.error("β Failed to parse API response.")
|
106 |
-
st.code(r.text)
|
107 |
-
st.exception(e)
|
108 |
else:
|
109 |
-
st.error(f"β Failed: {r.json().get('detail')}")
|
110 |
-
|
111 |
|
|
|
|
|
|
|
112 |
elif tab == "Text to Image":
|
113 |
st.subheader("πΌοΈ Text to Image")
|
114 |
prompt = st.text_area("Enter image prompt")
|
115 |
-
style = st.selectbox("Choose
|
116 |
|
117 |
if st.button("π§ Generate Image"):
|
118 |
-
with st.spinner("Generating image..."):
|
119 |
r = requests.post(
|
120 |
-
f"{API_BASE}/api/v1/image/generate",
|
121 |
-
json={"prompt": prompt, "style": style},
|
122 |
headers=HEADERS
|
123 |
)
|
124 |
if r.status_code == 200:
|
125 |
-
|
126 |
-
res_json = r.json()
|
127 |
-
download_url = res_json.get("download_url")
|
128 |
-
if not download_url:
|
129 |
-
st.error("No download URL returned.")
|
130 |
-
else:
|
131 |
-
download_full_url = f"{API_BASE}{download_url}"
|
132 |
-
image_response = requests.get(download_full_url, headers={"accept": "image/png"}, allow_redirects=True)
|
133 |
-
if image_response.status_code != 200:
|
134 |
-
st.error("β Failed to download image.")
|
135 |
-
st.code(image_response.text)
|
136 |
-
st.write("Status:", image_response.status_code)
|
137 |
-
st.write("Headers:", image_response.headers)
|
138 |
-
st.write(image_response.status_code, image_response.headers)
|
139 |
-
render_media(image_response.content, "image", "Generated Image")
|
140 |
-
except Exception as e:
|
141 |
-
st.error(f"β οΈ Failed to fetch/display image: {str(e)}")
|
142 |
-
st.code(r.text)
|
143 |
else:
|
144 |
try:
|
145 |
-
|
146 |
except Exception:
|
147 |
-
|
148 |
-
|
149 |
-
st.error(f"β Failed: {detail}")
|
150 |
|
|
|
|
|
|
|
151 |
elif tab == "Text to Video":
|
152 |
st.subheader("ποΈ Text to Video")
|
153 |
prompt = st.text_area("Enter video prompt")
|
@@ -155,9 +115,6 @@ elif tab == "Text to Video":
|
|
155 |
domain = st.selectbox("Domain", ["health", "education", "governance", "entertainment"])
|
156 |
environment = st.selectbox("Environment", ["urban", "rural", "nature", "futuristic"])
|
157 |
|
158 |
-
transcript = st.text_area("Transcript (optional - for subtitles)", height=100)
|
159 |
-
enhance = st.checkbox("β¨ Add Subtitles and Background Music")
|
160 |
-
|
161 |
if st.button("π¬ Generate Video"):
|
162 |
with st.spinner("Generating video..."):
|
163 |
r = requests.post(
|
@@ -166,49 +123,9 @@ elif tab == "Text to Video":
|
|
166 |
headers=HEADERS
|
167 |
)
|
168 |
if r.status_code == 200:
|
169 |
-
|
170 |
-
data = r.json()
|
171 |
-
st.code(data, language="json")
|
172 |
-
|
173 |
-
download_url = data.get("download_url")
|
174 |
-
if not download_url:
|
175 |
-
st.error("β οΈ No download URL received.")
|
176 |
-
else:
|
177 |
-
full_video_url = f"{API_BASE}{download_url}"
|
178 |
-
video_response = requests.get(full_video_url, headers=HEADERS)
|
179 |
-
if video_response.status_code == 200:
|
180 |
-
video_bytes = video_response.content
|
181 |
-
st.write("π¦ Video size (bytes):", len(video_bytes))
|
182 |
-
|
183 |
-
if enhance and transcript:
|
184 |
-
with tempfile.NamedTemporaryFile(delete=False, suffix=".mp4") as tmp_vid:
|
185 |
-
tmp_vid.write(video_bytes)
|
186 |
-
tmp_vid_path = tmp_vid.name
|
187 |
-
|
188 |
-
srt_path = generate_srt_from_text(transcript, output_path="streamlit_subs.srt")
|
189 |
-
enhanced_path = "streamlit_final_video.mp4"
|
190 |
-
enhance_video_with_subtitles_and_bgm(
|
191 |
-
video_path=tmp_vid_path,
|
192 |
-
srt_path=srt_path,
|
193 |
-
bgm_path="default_bgm.mp3",
|
194 |
-
output_path=enhanced_path
|
195 |
-
)
|
196 |
-
|
197 |
-
with open(enhanced_path, "rb") as f:
|
198 |
-
render_media(f.read(), "video", "Enhanced Video")
|
199 |
-
else:
|
200 |
-
st.video(video_bytes)
|
201 |
-
else:
|
202 |
-
st.error("β Failed to download video.")
|
203 |
-
except Exception as e:
|
204 |
-
st.error("β Error parsing response or rendering video.")
|
205 |
-
st.code(r.text)
|
206 |
-
st.exception(e)
|
207 |
else:
|
208 |
-
|
209 |
-
st.error(f"β Failed: {r.json().get('detail')}")
|
210 |
-
except:
|
211 |
-
st.error(f"β Failed: {r.text}")
|
212 |
|
213 |
|
214 |
st.sidebar.markdown("---")
|
|
|
1 |
# streamlit_ui.py
|
|
|
2 |
import streamlit as st
|
3 |
import requests
|
4 |
import base64
|
|
|
5 |
import io
|
|
|
|
|
6 |
|
7 |
st.set_page_config(
|
8 |
page_title="Prompta - Text to Media Generator",
|
|
|
19 |
# β
Display AFTER token is typed
|
20 |
if TOKEN:
|
21 |
st.sidebar.write("Using token:", TOKEN)
|
|
|
22 |
else:
|
23 |
st.sidebar.warning("β οΈ Please enter a valid API token to use the app.")
|
24 |
|
25 |
API_BASE = "http://localhost:8000"
|
26 |
|
27 |
+
# ==================================================
|
28 |
+
# Unified media rendering
|
29 |
+
# ==================================================
|
30 |
+
def render_media(response, label):
|
31 |
+
content_type = response.headers.get("Content-Type", "")
|
32 |
+
file_bytes = response.content
|
33 |
+
|
34 |
+
if "audio" in content_type:
|
35 |
+
st.audio(file_bytes, format=content_type)
|
36 |
+
elif "video" in content_type:
|
37 |
+
st.video(file_bytes)
|
38 |
+
elif "image" in content_type:
|
39 |
+
st.image(file_bytes, caption=label, use_container_width=True)
|
40 |
+
else:
|
41 |
try:
|
42 |
+
# JSON fallback (video download_url case)
|
43 |
+
data = response.json()
|
44 |
+
if "download_url" in data:
|
45 |
+
video_url = f"{API_BASE}{data['download_url']}"
|
46 |
+
st.info("π₯ Downloading video from URL...")
|
47 |
+
video_resp = requests.get(video_url, headers=HEADERS)
|
48 |
+
if video_resp.status_code == 200:
|
49 |
+
st.video(video_resp.content)
|
50 |
+
else:
|
51 |
+
st.error(f"β Failed to download video from {video_url}")
|
52 |
+
else:
|
53 |
+
st.warning("β οΈ Unsupported media format or empty response.")
|
54 |
+
except Exception:
|
55 |
+
st.warning("β οΈ Unsupported media format or empty response.")
|
56 |
|
57 |
+
# ==================================================
|
58 |
+
# Sidebar Inputs
|
59 |
+
# ==================================================
|
60 |
st.sidebar.header("π οΈ Settings")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
61 |
|
62 |
tab = st.sidebar.radio("Select Task", ["Text to Audio", "Text to Image", "Text to Video"])
|
63 |
|
64 |
+
# ==================================================
|
65 |
+
# Text to Audio
|
66 |
+
# ==================================================
|
67 |
if tab == "Text to Audio":
|
68 |
st.subheader("π€ Text to Audio")
|
69 |
text = st.text_area("Enter text")
|
70 |
+
voice = st.selectbox("Choose voice/language", ["en-US", "hi-IN", "te-IN", "ta-IN"])
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
71 |
|
72 |
if st.button("π Generate Audio"):
|
73 |
with st.spinner("Generating audio..."):
|
74 |
r = requests.post(
|
75 |
+
f"{API_BASE}/api/v1/audio/generate",
|
76 |
+
json={"text": text, "voice": voice},
|
|
|
|
|
|
|
|
|
77 |
headers=HEADERS
|
78 |
)
|
79 |
if r.status_code == 200:
|
80 |
+
render_media(r, "Generated Audio")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
81 |
else:
|
82 |
+
st.error(f"β Failed: {r.json().get('detail', r.text)}")
|
|
|
83 |
|
84 |
+
# ==================================================
|
85 |
+
# Text to Image
|
86 |
+
# ==================================================
|
87 |
elif tab == "Text to Image":
|
88 |
st.subheader("πΌοΈ Text to Image")
|
89 |
prompt = st.text_area("Enter image prompt")
|
90 |
+
style = st.selectbox("Choose style", ["nature", "technology", "urban", "abstract"])
|
91 |
|
92 |
if st.button("π§ Generate Image"):
|
93 |
+
with st.spinner("Generating image from Unsplash..."):
|
94 |
r = requests.post(
|
95 |
+
f"{API_BASE}/api/v1/image/generate",
|
96 |
+
json={"prompt": prompt, "style": style},
|
97 |
headers=HEADERS
|
98 |
)
|
99 |
if r.status_code == 200:
|
100 |
+
render_media(r, "Generated Image")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
101 |
else:
|
102 |
try:
|
103 |
+
err = r.json().get('detail', 'Unknown error')
|
104 |
except Exception:
|
105 |
+
err = r.text
|
106 |
+
st.error(f"β Failed to fetch/display image: {err}")
|
|
|
107 |
|
108 |
+
# ==================================================
|
109 |
+
# Text to Video
|
110 |
+
# ==================================================
|
111 |
elif tab == "Text to Video":
|
112 |
st.subheader("ποΈ Text to Video")
|
113 |
prompt = st.text_area("Enter video prompt")
|
|
|
115 |
domain = st.selectbox("Domain", ["health", "education", "governance", "entertainment"])
|
116 |
environment = st.selectbox("Environment", ["urban", "rural", "nature", "futuristic"])
|
117 |
|
|
|
|
|
|
|
118 |
if st.button("π¬ Generate Video"):
|
119 |
with st.spinner("Generating video..."):
|
120 |
r = requests.post(
|
|
|
123 |
headers=HEADERS
|
124 |
)
|
125 |
if r.status_code == 200:
|
126 |
+
render_media(r, "Generated Video")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
127 |
else:
|
128 |
+
st.error(f"β Failed: {r.json().get('detail', r.text)}")
|
|
|
|
|
|
|
129 |
|
130 |
|
131 |
st.sidebar.markdown("---")
|