osnarayana commited on
Commit
d53004d
Β·
1 Parent(s): 7006220

updated files

Browse files
ArchitectureDiagram.png ADDED

Git LFS Details

  • SHA256: 939a8dca28847fc7ed65376c5f54f1badac80b13fe3be7685afafb507587f7d9
  • Pointer size: 132 Bytes
  • Size of remote file: 1.65 MB
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, Depends, Body
 
2
  from pydantic import BaseModel
3
- from app.services.audio_service import generate_audio_file
4
- from app.auth.auth import verify_token
5
- import uuid # βœ… Add this
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
- file_path = f"generated_audio/{filename}"
21
  os.makedirs("generated_audio", exist_ok=True)
 
 
 
22
  tts = gTTS(text=payload.text, lang=payload.language)
23
  tts.save(file_path)
24
- return {
25
- "file_path": file_path,
26
- "download_url": f"/api/v1/download?file_path={file_path}"
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
- from PIL import Image, ImageDraw, ImageFont
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
- class ImageResponse(BaseModel):
15
- message: str
16
- filename: str
17
- download_url: str
18
 
19
- @router.post("/generate", response_model=ImageResponse)
 
20
  def generate_image_file_endpoint(
21
  data: ImageRequest = Body(...),
22
  token: str = Depends(verify_token)
23
  ):
24
- prompt = data.prompt
25
- style = data.style
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
- img = Image.new("RGB", (768, 512), color="white")
33
- draw = ImageDraw.Draw(img)
34
-
35
- try:
36
- font = ImageFont.truetype("arial.ttf", 20)
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 generation failed: {str(e)}")
54
- raise HTTPException(status_code=500, detail=str(e))
 
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 uuid # βœ… Add this
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 # βœ… make optional
19
 
20
  @router.post("/generate")
21
  def generate_video_endpoint(
22
  payload: VideoInput = Body(...),
23
- token: str = Depends(verify_token)):
 
24
  try:
25
- # Use `payload.prompt` as the script
26
  filename = generate_video_file(
27
- script=payload.prompt, # πŸ‘ˆ mapping prompt to script
28
- duration=10 # Or dynamically set based on text length
29
  )
30
- return {
31
- "message": "Video generated successfully",
32
- "filename": filename,
33
- "download_url": f"/api/v1/download?file_path=generated/video/{filename}"
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 math
8
  from gtts import gTTS
9
  from mutagen.mp3 import MP3
10
- import subprocess
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
11
 
12
  def generate_video_file(script: str, duration: int = None) -> str:
13
- # Paths
14
- audio_filename = f"generated/audio/audio_{uuid.uuid4().hex}.mp3"
15
- raw_video_path = f"generated/video/video_{uuid.uuid4().hex}.mp4"
16
- final_video_path = raw_video_path.replace(".mp4", "_final.mp4")
17
 
18
- # Ensure directories exist
19
- os.makedirs(os.path.dirname(audio_filename), exist_ok=True)
20
- os.makedirs(os.path.dirname(raw_video_path), exist_ok=True)
21
 
22
- # Generate audio
23
  tts = gTTS(text=script, lang='en')
24
- tts.save(audio_filename)
25
-
26
- # Get accurate audio duration
27
- audio = MP3(audio_filename)
28
- audio_duration = audio.info.length # e.g., 5.98 seconds
29
-
30
- # Video specs
31
- fps = 25 # Higher FPS = smoother video
32
- total_frames = int(audio_duration * fps)
33
- width, height = 640, 480
34
- fourcc = cv2.VideoWriter_fourcc(*'mp4v')
35
- out = cv2.VideoWriter(raw_video_path, fourcc, fps, (width, height))
36
-
37
- # Create each frame
38
- for _ in range(total_frames):
39
- frame = np.ones((height, width, 3), dtype=np.uint8) * 255
40
- cv2.putText(frame, script, (30, height // 2), cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0, 0, 0), 2)
41
- out.write(frame)
42
-
43
- out.release()
44
-
45
- # Merge audio and video
46
- ffmpeg_cmd = [
47
- "ffmpeg",
48
- "-y",
49
- "-i", raw_video_path,
50
- "-i", audio_filename,
51
- "-c:v", "libx264",
52
- "-c:a", "aac",
53
- "-crf", "23",
54
- "-c:a", "aac",
55
- "-b:a", "128k",
56
- "-shortest",
57
- "-movflags", "+faststart", # πŸ‘ˆ crucial for browser playback
58
- final_video_path
59
- ]
60
-
61
- try:
62
- subprocess.run(ffmpeg_cmd, check=True)
63
- except subprocess.CalledProcessError as e:
64
- print(f"FFmpeg failed: {e}")
65
- return None
66
-
67
- return os.path.basename(final_video_path)
 
 
 
 
 
 
 
 
 
 
 
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
- API_BASE = "http://localhost:8000"
35
- #API_BASE = "https://2255d6a4793d.ngrok-free.app"
36
-
37
- def render_media(file_bytes, media_type, caption):
38
- b64 = base64.b64encode(file_bytes).decode()
39
- if media_type == "audio":
40
- st.audio(f"data:audio/wav;base64,{b64}", format="audio/wav")
41
- elif media_type == "video":
42
- st.video(f"data:video/mp4;base64,{b64}")
43
- elif media_type == "image":
 
 
44
  try:
45
- # Validate if it's a valid image
46
- img = Image.open(io.BytesIO(file_bytes))
47
- st.image(img, caption=caption)
48
- except Exception as e:
49
- st.warning("⚠️ Cannot render image. It may be corrupt or empty.")
50
- st.code(str(e))
 
 
 
 
 
 
 
 
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", ["English", "Hindi", "Telugu", "Tamil"])
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
- try:
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 Style", ["sdxl", "deepfloyd", "kandinsky"])
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}, # βœ… correct key
122
  headers=HEADERS
123
  )
124
  if r.status_code == 200:
125
- try:
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
- detail = r.json().get("detail")
146
  except Exception:
147
- detail = r.text # fallback to raw response text (may be empty or HTML)
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
- try:
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
- try:
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("---")