osnarayana commited on
Commit
a38b4f9
·
0 Parent(s):

Initial commit: media-gen-api with Streamlit UI

Browse files
Files changed (43) hide show
  1. media-gen-api/.devcontainer/devcontainer.json +33 -0
  2. media-gen-api/.gitignore +44 -0
  3. media-gen-api/.vs/VSWorkspaceState.json +6 -0
  4. media-gen-api/.vs/ai_gov_comm/FileContentIndex/09e5affd-6ff5-45b8-b6bb-c44b5b94954f.vsidx +0 -0
  5. media-gen-api/.vs/ai_gov_comm/v17/.wsuo +0 -0
  6. media-gen-api/.vs/ai_gov_comm/v17/DocumentLayout.json +12 -0
  7. media-gen-api/Dockerfile +34 -0
  8. media-gen-api/PRIVACY_POLICY.md +38 -0
  9. media-gen-api/Procfile +1 -0
  10. media-gen-api/Project_structure.docx +0 -0
  11. media-gen-api/README.md +94 -0
  12. media-gen-api/app/__init__.py +0 -0
  13. media-gen-api/app/api/__init__.py +0 -0
  14. media-gen-api/app/api/v1/__init__.py +0 -0
  15. media-gen-api/app/api/v1/audio.py +27 -0
  16. media-gen-api/app/api/v1/download.py +23 -0
  17. media-gen-api/app/api/v1/image.py +19 -0
  18. media-gen-api/app/api/v1/metrics.py +24 -0
  19. media-gen-api/app/api/v1/ppt.py +24 -0
  20. media-gen-api/app/api/v1/utils.py +9 -0
  21. media-gen-api/app/api/v1/video.py +20 -0
  22. media-gen-api/app/auth/auth.py +32 -0
  23. media-gen-api/app/core/config.py +6 -0
  24. media-gen-api/app/db.py +7 -0
  25. media-gen-api/app/main.py +70 -0
  26. media-gen-api/app/models.py +14 -0
  27. media-gen-api/app/services/audio_service.py +38 -0
  28. media-gen-api/app/services/image_service.py +38 -0
  29. media-gen-api/app/services/ppt_service.py +34 -0
  30. media-gen-api/app/services/video_service.py +35 -0
  31. media-gen-api/assets/default.jpg +0 -0
  32. media-gen-api/assets/logo_watermark.png +0 -0
  33. media-gen-api/backend/media_gen.py +187 -0
  34. media-gen-api/docs/index.html +62 -0
  35. media-gen-api/docs/privacy.html +68 -0
  36. media-gen-api/nixpacks.toml +19 -0
  37. media-gen-api/openapi.json +1 -0
  38. media-gen-api/packages.txt +3 -0
  39. media-gen-api/requirements.txt +19 -0
  40. media-gen-api/screenshots/generated_audio_sample.png +0 -0
  41. media-gen-api/screenshots/home_page.png +0 -0
  42. media-gen-api/scripts/preload_content.py +17 -0
  43. media-gen-api/streamlit_ui.py +75 -0
media-gen-api/.devcontainer/devcontainer.json ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "Python 3",
3
+ // Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile
4
+ "image": "mcr.microsoft.com/devcontainers/python:1-3.11-bullseye",
5
+ "customizations": {
6
+ "codespaces": {
7
+ "openFiles": [
8
+ "README.md",
9
+ "app.py"
10
+ ]
11
+ },
12
+ "vscode": {
13
+ "settings": {},
14
+ "extensions": [
15
+ "ms-python.python",
16
+ "ms-python.vscode-pylance"
17
+ ]
18
+ }
19
+ },
20
+ "updateContentCommand": "[ -f packages.txt ] && sudo apt update && sudo apt upgrade -y && sudo xargs apt install -y <packages.txt; [ -f requirements.txt ] && pip3 install --user -r requirements.txt; pip3 install --user streamlit; echo '✅ Packages installed and Requirements met'",
21
+ "postAttachCommand": {
22
+ "server": "streamlit run app.py --server.enableCORS false --server.enableXsrfProtection false"
23
+ },
24
+ "portsAttributes": {
25
+ "8501": {
26
+ "label": "Application",
27
+ "onAutoForward": "openPreview"
28
+ }
29
+ },
30
+ "forwardPorts": [
31
+ 8501
32
+ ]
33
+ }
media-gen-api/.gitignore ADDED
@@ -0,0 +1,44 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Python
2
+ *.pyc
3
+ __pycache__/
4
+ *.pyo
5
+ *.pyd
6
+ *.log
7
+
8
+ # Virtual env
9
+ .venv/
10
+ venv/
11
+ ENV/
12
+ env/
13
+
14
+ # Environment secrets
15
+ .env
16
+
17
+ # Streamlit cache
18
+ .streamlit/
19
+ .metadata/
20
+
21
+ # Pytest cache
22
+ .pytest_cache/
23
+
24
+ # VS Code / PyCharm / IDE files
25
+ .vscode/
26
+ .idea/
27
+ .project
28
+ .pydevproject
29
+
30
+ # RStudio / Mac / Windows artifacts
31
+ .Rhistory
32
+ .DS_Store
33
+ Thumbs.db
34
+
35
+ # Output folders
36
+ outputs/
37
+
38
+
39
+ logs/
40
+ __pycache__/
41
+ outputs/
42
+ generated/
43
+ *.pyc
44
+ *.log
media-gen-api/.vs/VSWorkspaceState.json ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ {
2
+ "ExpandedNodes": [
3
+ ""
4
+ ],
5
+ "PreviewInSolutionExplorer": false
6
+ }
media-gen-api/.vs/ai_gov_comm/FileContentIndex/09e5affd-6ff5-45b8-b6bb-c44b5b94954f.vsidx ADDED
Binary file (84.3 kB). View file
 
media-gen-api/.vs/ai_gov_comm/v17/.wsuo ADDED
Binary file (8.19 kB). View file
 
media-gen-api/.vs/ai_gov_comm/v17/DocumentLayout.json ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "Version": 1,
3
+ "WorkspaceRootPath": "C:\\Users\\fimba\\OneDrive\\Desktop\\PythonProjects\\ai_gov_comm\\",
4
+ "Documents": [],
5
+ "DocumentGroupContainers": [
6
+ {
7
+ "Orientation": 0,
8
+ "VerticalTabListWidth": 256,
9
+ "DocumentGroups": []
10
+ }
11
+ ]
12
+ }
media-gen-api/Dockerfile ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Use stable Python image
2
+ FROM python:3.10
3
+
4
+ # Install OS dependencies for Pillow and others
5
+ RUN apt-get update && apt-get install -y \
6
+ build-essential \
7
+ libjpeg-dev \
8
+ zlib1g-dev \
9
+ libpng-dev \
10
+ libfreetype6-dev \
11
+ && rm -rf /var/lib/apt/lists/*
12
+
13
+ # Set working directory
14
+ WORKDIR /app
15
+
16
+ # Copy requirement files first for caching
17
+ COPY requirements.txt ./
18
+
19
+ # Install Python dependencies
20
+ RUN pip install --no-cache-dir -r requirements.txt
21
+
22
+ # Copy the app files
23
+ COPY . /app
24
+
25
+ # Expose port (the port is dynamic and set by the platform, but 8000 is a good fallback)
26
+ EXPOSE 8000
27
+
28
+ # Create a script to start Streamlit with dynamic PORT
29
+ RUN echo '#!/bin/bash\n\
30
+ export PORT=${PORT:-8000}\n\
31
+ streamlit run app.py --server.port=$PORT --server.address=0.0.0.0' > /start.sh && chmod +x /start.sh
32
+
33
+ # Run the script
34
+ CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]
media-gen-api/PRIVACY_POLICY.md ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ ---
3
+
4
+ ### ✅ `PRIVACY_POLICY.md`
5
+
6
+ ```markdown
7
+ # 🔐 Privacy Policy for OSNarayana Media Generator
8
+
9
+ Effective Date: July 19, 2025
10
+
11
+ Your privacy is important to us. This app is built to prioritize user data security and transparency.
12
+
13
+ ## 1. What We Collect
14
+
15
+ - **No personal data** is collected, stored, or shared by this application.
16
+ - All prompts, audio, and media are **processed locally** or via **user-provided API keys** to external services (e.g., ElevenLabs, Unsplash).
17
+
18
+ ## 2. API Usage
19
+
20
+ - If you use external services (like ElevenLabs or Unsplash), you are subject to their respective [Terms of Service](https://www.elevenlabs.io/terms) and [Privacy Policies](https://www.elevenlabs.io/privacy).
21
+ - Your API keys are stored **locally** in your environment file (`.env`) and never uploaded.
22
+
23
+ ## 3. Data Storage
24
+
25
+ - Generated images, audio, and videos are stored **only on your local machine** under the `outputs/` directory.
26
+ - You can delete any generated content at your discretion.
27
+
28
+ ## 4. Analytics & Ads
29
+
30
+ - This app contains **no ads**, **no tracking**, and **no analytics**.
31
+
32
+ ## 5. Changes to Policy
33
+
34
+ Any updates to this policy will be reflected in this file on the [GitHub repository](https://github.com/your-username/osnarayana-media-generator).
35
+
36
+ ## 6. Contact
37
+
38
+ For questions or issues, contact: **[email protected]**
media-gen-api/Procfile ADDED
@@ -0,0 +1 @@
 
 
1
+ web: streamlit run app.py --server.port=8000 --server.address=0.0.0.0
media-gen-api/Project_structure.docx ADDED
Binary file (14 kB). View file
 
media-gen-api/README.md ADDED
@@ -0,0 +1,94 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # 🎙️ Media Generation API
2
+
3
+ A FastAPI-based backend to generate audio, images, video, and PPT from user inputs.
4
+ Supports BLEU/CLIP metrics, token-based authentication, and stores metadata in SQLite/Postgres.
5
+
6
+ A modular, RESTful FastAPI solution that converts text input into:
7
+ - 🎥 Video
8
+ - 🖼️ Image/Graphics
9
+ - 🔊 Audio
10
+
11
+
12
+ ---
13
+
14
+ ## 🚀 Features
15
+
16
+ - Text → Video: Tone, domain, and environment-aware video generation.
17
+ - Text → Audio: Context-aware voice synthesis with emotional tone and language support.
18
+ - Text → Graphics: Visual generation using parameter-based prompts.
19
+ - BLEU/CLIP metrics for prompt-output fidelity.
20
+ - Token-based authentication for secure API use.
21
+ - Dockerized for easy deployment
22
+ - Optional Streamlit/React UI
23
+ - Swagger UI: `http://localhost:8000/docs`
24
+
25
+ ---
26
+
27
+ ### 📁 Project Structure
28
+ media-gen-api/
29
+ ├── app/
30
+ │ ├── api/v1/ # Versioned API endpoints
31
+ │ ├── auth/ # Token-based auth
32
+ │ ├── services/ # Core media generation logic
33
+ │ └── main.py # FastAPI entry point
34
+ ├── tests/ # Unit/integration tests
35
+ ├── requirements.txt
36
+ └── README.md
37
+
38
+ ---
39
+
40
+ ## 📦 Installation
41
+ 🚀 Run Locally
42
+ 1. Clone repo & create virtual environment
43
+
44
+ git clone https://github.com/yourorg/media-gen-api.git
45
+ cd media-gen-api
46
+ python -m venv .venv
47
+ source .venv/bin/activate # On Windows: .venv\Scripts\activate
48
+
49
+ 2. Install dependencies
50
+
51
+ pip install -r requirements.txt
52
+
53
+ 3. Run the API
54
+
55
+ uvicorn app.main:app --reload
56
+
57
+ Access docs: http://127.0.0.1:8000/docs
58
+
59
+ ---
60
+ ### 🔐 Authentication
61
+ Use Bearer <your_token> in the Authorize button or headers.
62
+
63
+ ---
64
+ ### 📡 API Endpoints Summary
65
+ | Endpoint | Method | Description |
66
+ |--------------------------|--------|---------------------------|
67
+ | /api/v1/audio/generate | POST | Generate audio from text |
68
+ | /api/v1/image/generate | POST | Generate image from text |
69
+ | /api/v1/video/generate | POST | Generate video from text |
70
+ | /api/v1/download | GET | Download generated file |
71
+
72
+ ---
73
+ ###📦 Deployment (Streamlit/Optional UI)
74
+ Option 1: Run with Streamlit (for demo)
75
+ streamlit run streamlit_ui.py
76
+
77
+ Option 2: Docker (Production-ready)
78
+ docker build -t media-gen-api .
79
+ docker run -p 8000:8000 media-gen-api
80
+
81
+ ---
82
+ ### 📊 Metrics Logging (Optional)
83
+ - BLEU score and CLIPScore (WIP)
84
+ - Latency, GPU/CPU tracking
85
+ - Log file: logs/generation.log
86
+
87
+ ---
88
+ #### 📋 Submission Checklist
89
+ - ✅ RESTful modular architecture
90
+ - ✅ Multi-format (MP4, PNG, WAV)
91
+ - ✅ Token Auth + Swagger UI
92
+ - ✅ Compatible with DD/PIB via API
93
+ - ✅ Streamlit demo app (optional)
94
+
media-gen-api/app/__init__.py ADDED
File without changes
media-gen-api/app/api/__init__.py ADDED
File without changes
media-gen-api/app/api/v1/__init__.py ADDED
File without changes
media-gen-api/app/api/v1/audio.py ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # app/api/v1/audio.py
2
+ from fastapi import APIRouter, HTTPException, Query, Depends, Request
3
+ from pydantic import BaseModel
4
+ from app.services.audio_service import generate_audio_file # ✅ Only this import
5
+ from app.auth.auth import verify_token
6
+
7
+
8
+ router = APIRouter()
9
+
10
+ class AudioInput(BaseModel):
11
+ text: str
12
+ voice: str = "default"
13
+ language: str = "en"
14
+
15
+ @router.post("/generate", dependencies=[Depends(verify_token)])
16
+ def generate_audio_endpoint(payload: AudioInput):
17
+ try:
18
+ file_path = generate_audio_file(payload.text, payload.voice, payload.language)
19
+
20
+ return {
21
+ "status": "success",
22
+ "type": "audio",
23
+ "file_path": file_path,
24
+ "download_url": f"/api/v1/download?file_path={file_path}"
25
+ }
26
+ except Exception as e:
27
+ raise HTTPException(status_code=500, detail=str(e))
media-gen-api/app/api/v1/download.py ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from fastapi import APIRouter, HTTPException, Query, Depends, Request
2
+ from fastapi.responses import FileResponse
3
+ from app.auth.auth import verify_token
4
+ import os
5
+
6
+ router = APIRouter()
7
+
8
+ @router.get("/", dependencies=[Depends(verify_token)])
9
+ def download_file(file_path: str = Query(..., description="Relative path from project root")):
10
+ # Sanitize the input path
11
+ file_path = os.path.normpath(file_path)
12
+
13
+ # Absolute path (from project root)
14
+ abs_path = os.path.join(os.getcwd(), file_path)
15
+
16
+ if not os.path.isfile(abs_path):
17
+ raise HTTPException(status_code=404, detail="File not found")
18
+
19
+ return FileResponse(
20
+ path=abs_path,
21
+ filename=os.path.basename(abs_path),
22
+ media_type='application/octet-stream'
23
+ )
media-gen-api/app/api/v1/image.py ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from fastapi import APIRouter, HTTPException, Query, Depends, Request
2
+ from pydantic import BaseModel
3
+ from app.services.image_service import generate_image_file
4
+ from app.auth.auth import verify_token
5
+
6
+ router = APIRouter()
7
+
8
+ class ImageInput(BaseModel):
9
+ prompt: str
10
+ style: str = "default"
11
+
12
+ @router.post("/generate", dependencies=[Depends(verify_token)]) # ✅ FIX: POST, not GET
13
+ def generate_image(payload: ImageInput):
14
+ filename = generate_image_file(payload.prompt, payload.style)
15
+ return {
16
+ "message": "Image generated successfully",
17
+ "filename": filename,
18
+ "download_url": f"/api/v1/download?file_path=generated/image/{filename}"
19
+ }
media-gen-api/app/api/v1/metrics.py ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # app/api/v1/metrics.py
2
+ from fastapi import APIRouter, HTTPException, Query, Depends, Request
3
+ from typing import List
4
+ from sklearn.metrics import accuracy_score
5
+ from nltk.translate.bleu_score import sentence_bleu
6
+ from sentence_transformers import SentenceTransformer, util
7
+ from app.auth.auth import verify_token
8
+
9
+ router = APIRouter()
10
+ model = SentenceTransformer("clip-ViT-B-32") # for CLIP-like semantic score
11
+
12
+ @router.post("/evaluate/bleu", dependencies=[Depends(verify_token)])
13
+ def compute_bleu(reference: str, candidate: str):
14
+ ref_tokens = [reference.split()]
15
+ cand_tokens = candidate.split()
16
+ score = sentence_bleu(ref_tokens, cand_tokens)
17
+ return {"metric": "BLEU", "score": score}
18
+
19
+ @router.post("/evaluate/clipscore")
20
+ def compute_clip_score(reference: str, candidate: str):
21
+ ref_emb = model.encode(reference, convert_to_tensor=True)
22
+ cand_emb = model.encode(candidate, convert_to_tensor=True)
23
+ score = util.cos_sim(ref_emb, cand_emb).item()
24
+ return {"metric": "CLIPScore", "score": score}
media-gen-api/app/api/v1/ppt.py ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # app/api/v1/ppt.py
2
+ from fastapi import APIRouter, HTTPException, Query, Depends, Request
3
+ from pydantic import BaseModel
4
+ from typing import List
5
+ from app.services.ppt_service import generate_ppt_file
6
+ from app.auth.auth import verify_token
7
+
8
+ router = APIRouter()
9
+
10
+ class Slide(BaseModel):
11
+ title: str
12
+ content: str
13
+
14
+ class PPTInput(BaseModel):
15
+ slides: List[Slide]
16
+
17
+ @router.post("/generate", dependencies=[Depends(verify_token)])
18
+ def generate_ppt(payload: PPTInput):
19
+ filename = generate_ppt_file([slide.dict() for slide in payload.slides])
20
+ return {
21
+ "message": "PPT generated successfully",
22
+ "filename": filename,
23
+ "download_url": f"/api/v1/download?file_path=generated/ppt/{filename}"
24
+ }
media-gen-api/app/api/v1/utils.py ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ # app/api/v1/utils.py
2
+ from fastapi.responses import FileResponse
3
+
4
+ def download_file(file_path: str):
5
+ return FileResponse(
6
+ path=file_path,
7
+ filename=file_path.split("/")[-1],
8
+ media_type="application/octet-stream"
9
+ )
media-gen-api/app/api/v1/video.py ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from fastapi import APIRouter, HTTPException, Query, Depends, Request
2
+ from pydantic import BaseModel
3
+ from app.services.video_service import generate_video_file
4
+ from app.auth.auth import verify_token
5
+
6
+
7
+ router = APIRouter()
8
+
9
+ class VideoInput(BaseModel):
10
+ script: str
11
+ duration: int = 10
12
+
13
+ @router.post("/generate", dependencies=[Depends(verify_token)]) # ✅ FIX: POST, not GET
14
+ def generate_video(payload: VideoInput):
15
+ filename = generate_video_file(payload.script, payload.duration)
16
+ return {
17
+ "message": "Video generated successfully",
18
+ "filename": filename,
19
+ "download_url": f"/api/v1/download?file_path=generated/video/{filename}"
20
+ }
media-gen-api/app/auth/auth.py ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from fastapi import Request, HTTPException, status
2
+ from fastapi import Depends, HTTPException, status
3
+ from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
4
+
5
+
6
+ # Replace with a more secure method later (e.g., from .env)
7
+ API_TOKEN = "my_secure_token_123" # we can generate a random one using uuid or secrets
8
+
9
+ def verify_token(request: Request):
10
+ token = request.headers.get("Authorization")
11
+ if not token or token.replace("Bearer ", "") != API_TOKEN:
12
+ raise HTTPException(
13
+ status_code=status.HTTP_401_UNAUTHORIZED,
14
+ detail="Invalid or missing token",
15
+ )
16
+ return request
17
+
18
+ security = HTTPBearer()
19
+
20
+ VALID_TOKENS = {"token123", "mysecuretoken"} # Your valid tokens
21
+
22
+ def auth_required(
23
+ credentials: HTTPAuthorizationCredentials = Depends(security),
24
+ ):
25
+ token = credentials.credentials
26
+ if token not in VALID_TOKENS:
27
+ raise HTTPException(
28
+ status_code=status.HTTP_401_UNAUTHORIZED,
29
+ detail="Invalid or missing token"
30
+ )
31
+ return token
32
+
media-gen-api/app/core/config.py ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ import os
2
+
3
+ class Settings:
4
+ API_KEY: str = os.getenv("API_KEY", "dummy-api-key")
5
+
6
+ settings = Settings()
media-gen-api/app/db.py ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ from sqlalchemy import create_engine
2
+ from sqlalchemy.orm import sessionmaker
3
+
4
+ DATABASE_URL = "sqlite:///./media_gen.db"
5
+
6
+ engine = create_engine(DATABASE_URL, connect_args={"check_same_thread": False})
7
+ SessionLocal = sessionmaker(bind=engine, autoflush=False)
media-gen-api/app/main.py ADDED
@@ -0,0 +1,70 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # app/main.py
2
+ from fastapi import FastAPI
3
+ from app.api.v1 import audio, video, image, ppt, metrics, download
4
+ import logging
5
+ import os
6
+ import sentry_sdk
7
+ from fastapi import FastAPI, Depends
8
+ from fastapi.security import HTTPBearer
9
+ from app.api.v1.audio import router as audio_router
10
+ from app.api.v1.video import router as video_router
11
+ from app.api.v1.image import router as image_router
12
+ from app.api.v1.ppt import router as ppt_router
13
+ from app.api.v1.metrics import router as metrics_router
14
+ from app.api.v1.download import router as download_router
15
+
16
+ from fastapi.openapi.utils import get_openapi
17
+ from fastapi.security import HTTPBearer
18
+
19
+
20
+
21
+ # Create logs directory if it doesn't exist
22
+ os.makedirs("logs", exist_ok=True)
23
+
24
+ app = FastAPI(
25
+ title="Media Generator API",
26
+ description="Generate audio, video, image, and PPT from text",
27
+ version="1.0.0"
28
+ )
29
+
30
+ app.include_router(audio_router, prefix="/api/v1/audio", tags=["Audio"])
31
+ app.include_router(video_router, prefix="/api/v1/video", tags=["Video"])
32
+ app.include_router(image_router, prefix="/api/v1/image", tags=["Image"])
33
+ app.include_router(ppt_router, prefix="/api/v1/ppt", tags=["PPT"])
34
+ app.include_router(metrics_router, prefix="/api/v1/metrics", tags=["Metrics"])
35
+ app.include_router(download_router, prefix="/api/v1/download", tags=["Download"])
36
+
37
+ logging.basicConfig(
38
+ level=logging.INFO,
39
+ format="%(asctime)s [%(levelname)s] %(message)s",
40
+ handlers=[logging.FileHandler("logs/app.log"), logging.StreamHandler()]
41
+ )
42
+
43
+ sentry_sdk.init(dsn="https://a1d84892719d25dca7c04804931d2e82@o4509752680775680.ingest.de.sentry.io/4509752685166672", traces_sample_rate=1.0)
44
+
45
+
46
+ security = HTTPBearer()
47
+
48
+
49
+ def custom_openapi():
50
+ if app.openapi_schema:
51
+ return app.openapi_schema
52
+ openapi_schema = get_openapi(
53
+ title="Media Generator API",
54
+ version="1.0.0",
55
+ description="Generate audio, video, image, and PPT from text",
56
+ routes=app.routes,
57
+ )
58
+ openapi_schema["components"]["securitySchemes"] = {
59
+ "HTTPBearer": {
60
+ "type": "http",
61
+ "scheme": "bearer"
62
+ }
63
+ }
64
+ for path in openapi_schema["paths"].values():
65
+ for method in path.values():
66
+ method["security"] = [{"HTTPBearer": []}]
67
+ app.openapi_schema = openapi_schema
68
+ return app.openapi_schema
69
+
70
+ app.openapi = custom_openapi
media-gen-api/app/models.py ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from sqlalchemy import Column, Integer, String, DateTime
2
+ from sqlalchemy.ext.declarative import declarative_base
3
+ from datetime import datetime
4
+
5
+ Base = declarative_base()
6
+
7
+ class MediaGeneration(Base):
8
+ __tablename__ = "media_generations"
9
+
10
+ id = Column(Integer, primary_key=True, index=True)
11
+ media_type = Column(String)
12
+ prompt = Column(String)
13
+ file_path = Column(String)
14
+ timestamp = Column(DateTime, default=datetime.utcnow)
media-gen-api/app/services/audio_service.py ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # app/services/audio_service.py
2
+ from gtts import gTTS
3
+ import os
4
+ from datetime import datetime
5
+ from app.db import SessionLocal
6
+ from app.models import MediaGeneration
7
+ import logging
8
+ logger = logging.getLogger(__name__)
9
+
10
+ def generate_audio_file(text: str, voice: str = "default", language: str = "en") -> str:
11
+ try:
12
+ tts = gTTS(text=text, lang=language, slow=False)
13
+ timestamp = datetime.now().strftime("%Y%m%d%H%M%S")
14
+ filename = f"audio_{timestamp}.mp3"
15
+ output_dir = "generated/audio"
16
+ os.makedirs(output_dir, exist_ok=True)
17
+ file_path = os.path.join(output_dir, filename)
18
+ tts.save(file_path)
19
+ logger.info(f"Generated Audio: {filename}")
20
+ return file_path
21
+ except:
22
+ logger.error(f"Audio Generation Failed: {str(e)}")
23
+ raise
24
+
25
+
26
+ from app.db import SessionLocal
27
+ from app.models import MediaGeneration
28
+
29
+ def save_metadata(media_type, prompt, file_path):
30
+ db = SessionLocal()
31
+ record = MediaGeneration(
32
+ media_type=media_type,
33
+ prompt=prompt,
34
+ file_path=file_path,
35
+ )
36
+ db.add(record)
37
+ db.commit()
38
+ db.close()
media-gen-api/app/services/image_service.py ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # app/services/image_service.py
2
+ import os
3
+ from datetime import datetime
4
+ from app.db import SessionLocal
5
+ from app.models import MediaGeneration
6
+ import logging
7
+ logger = logging.getLogger(__name__)
8
+
9
+
10
+ def generate_image_file(prompt: str, style: str = "default") -> str:
11
+ try:
12
+ # Simulate saving a generated image file
13
+ filename = f"image_{datetime.now().strftime('%Y%m%d_%H%M%S')}.png"
14
+ folder = "generated/image"
15
+ os.makedirs(folder, exist_ok=True)
16
+
17
+ # Placeholder: Simulate image generation by writing prompt text to a file
18
+ with open(os.path.join(folder, filename), "w") as f:
19
+ f.write(f"Prompt: {prompt}\nStyle: {style}")
20
+ logger.info(f"Generated Image: {filename}")
21
+ return filename
22
+ except:
23
+ logger.error(f"Image Geneartion failed: {str(e)}")
24
+ raise
25
+
26
+ from app.db import SessionLocal
27
+ from app.models import MediaGeneration
28
+
29
+ def save_metadata(media_type, prompt, file_path):
30
+ db = SessionLocal()
31
+ record = MediaGeneration(
32
+ media_type=media_type,
33
+ prompt=prompt,
34
+ file_path=file_path,
35
+ )
36
+ db.add(record)
37
+ db.commit()
38
+ db.close()
media-gen-api/app/services/ppt_service.py ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # app/services/ppt_service.py
2
+ import os
3
+ from datetime import datetime
4
+ from app.db import SessionLocal
5
+ from app.models import MediaGeneration
6
+ import logging
7
+ logger = logging.getLogger(__name__)
8
+
9
+ def generate_ppt_file(slides: list[dict]) -> str:
10
+ try:
11
+ filename = f"ppt_{datetime.now().strftime('%Y%m%d_%H%M%S')}.ppt"
12
+ folder = "generated/ppt"
13
+ os.makedirs(folder, exist_ok=True)
14
+
15
+ with open(os.path.join(folder, filename), "w") as f:
16
+ for i, slide in enumerate(slides, 1):
17
+ f.write(f"Slide {i}:\nTitle: {slide['title']}\nContent: {slide['content']}\n\n")
18
+ logger.info(f"Generated PPT: {filename}")
19
+ return filename
20
+ except:
21
+ logger.error(f"PPT Generation failed: {str(e)}")
22
+ raise
23
+
24
+
25
+ def save_metadata(media_type, prompt, file_path):
26
+ db = SessionLocal()
27
+ record = MediaGeneration(
28
+ media_type=media_type,
29
+ prompt=prompt,
30
+ file_path=file_path,
31
+ )
32
+ db.add(record)
33
+ db.commit()
34
+ db.close()
media-gen-api/app/services/video_service.py ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # app/services/video_service.py
2
+ import os
3
+ from datetime import datetime
4
+ from app.db import SessionLocal
5
+ from app.models import MediaGeneration
6
+ import logging
7
+ logger = logging.getLogger(__name__)
8
+
9
+ def generate_video_file(script: str, duration: int = 10) -> str:
10
+ try:
11
+ # Simulate saving a generated video file
12
+ filename = f"video_{datetime.now().strftime('%Y%m%d_%H%M%S')}.mp4"
13
+ folder = "generated/video"
14
+ os.makedirs(folder, exist_ok=True)
15
+
16
+ # Placeholder: Simulate video generation by writing script info to a file
17
+ with open(os.path.join(folder, filename), "w") as f:
18
+ f.write(f"Script: {script}\nDuration: {duration} seconds")
19
+ logger.info(f"Generated Video: {filename}")
20
+ return filename
21
+ except:
22
+ logger.error(f"Video generation failed: {str(e)}")
23
+ raise
24
+
25
+
26
+ def save_metadata(media_type, prompt, file_path):
27
+ db = SessionLocal()
28
+ record = MediaGeneration(
29
+ media_type=media_type,
30
+ prompt=prompt,
31
+ file_path=file_path,
32
+ )
33
+ db.add(record)
34
+ db.commit()
35
+ db.close()
media-gen-api/assets/default.jpg ADDED
media-gen-api/assets/logo_watermark.png ADDED
media-gen-api/backend/media_gen.py ADDED
@@ -0,0 +1,187 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # ✅ Updated media_gen.py with file logging + UI debug toggle
2
+ import os
3
+ import re
4
+ import logging
5
+ import streamlit as st
6
+ import requests
7
+ from PIL import Image, UnidentifiedImageError
8
+ from io import BytesIO
9
+ from dotenv import load_dotenv
10
+ from moviepy.editor import ImageClip, AudioFileClip
11
+ from elevenlabs import generate, save, set_api_key
12
+ from googletrans import Translator
13
+ from PIL import ImageEnhance, Image
14
+ import tempfile
15
+
16
+ # Load env vars
17
+ load_dotenv()
18
+
19
+ # Logging setup
20
+ logging.basicConfig(
21
+ filename="app.log",
22
+ level=logging.INFO,
23
+ format="%(asctime)s [%(levelname)s] %(message)s",
24
+ )
25
+
26
+ # Constants
27
+ OUTPUT_DIR = "outputs"
28
+ DEFAULT_IMAGE = "assets/fallback.jpg"
29
+ WATERMARK_PATH = "assets/logo_watermark.png"
30
+ UNSPLASH_ACCESS_KEY = os.getenv("UNSPLASH_ACCESS_KEY")
31
+
32
+ os.makedirs("outputs/audio", exist_ok=True)
33
+ os.makedirs("outputs/images", exist_ok=True)
34
+ os.makedirs("outputs/videos", exist_ok=True)
35
+
36
+ def translate_text(text, target_lang):
37
+ return Translator().translate(text, dest=target_lang).text
38
+
39
+ def sanitize_filename(text):
40
+ return re.sub(r'\W+', '_', text).lower()[:50]
41
+
42
+ def apply_watermark(image_path, watermark_path=WATERMARK_PATH):
43
+ try:
44
+ base = Image.open(image_path).convert("RGBA")
45
+ watermark = Image.open(watermark_path).convert("RGBA").resize((100, 100))
46
+ base.paste(watermark, (base.width - 110, base.height - 110), watermark)
47
+ base.convert("RGB").save(image_path)
48
+ except Exception as e:
49
+ logging.error(f"Watermarking failed: {e}")
50
+ st.write(f"❌ Watermarking failed: {e}")
51
+
52
+ def use_fallback_image(prompt, add_watermark=False):
53
+ try:
54
+ fallback_path = DEFAULT_IMAGE
55
+ output_path = f"outputs/images/{sanitize_filename(prompt)}.jpg"
56
+ with Image.open(fallback_path) as img:
57
+ img.save(output_path)
58
+ if add_watermark:
59
+ apply_watermark(output_path)
60
+ return output_path
61
+ except UnidentifiedImageError:
62
+ logging.error("Could not open fallback image.")
63
+ st.write("❌ Could not open fallback image.")
64
+ return None
65
+
66
+ def generate_gtts_fallback(prompt, output_path, lang="en", debug_mode=False):
67
+ try:
68
+ from gtts import gTTS
69
+ tts = gTTS(text=prompt, lang=lang)
70
+ tts.save(output_path)
71
+ logging.info(f"gTTS fallback audio saved to {output_path}")
72
+ if debug_mode:
73
+ st.write(f"✅ Fallback audio (gTTS) saved to {output_path}")
74
+ return output_path
75
+ except Exception as e:
76
+ logging.error(f"gTTS fallback failed: {e}")
77
+ st.write(f"❌ gTTS fallback failed: {str(e)}")
78
+ return None
79
+
80
+
81
+ def generate_image(prompt, file_tag, add_watermark=False, dark_mode=False, debug_mode=False):
82
+ try:
83
+ # Enhance prompt if dark mode is enabled
84
+ if dark_mode:
85
+ prompt += " at night, dark theme, low light, moody lighting"
86
+
87
+ url = f"https://api.unsplash.com/photos/random?query={requests.utils.quote(prompt)}&client_id={UNSPLASH_ACCESS_KEY}"
88
+ response = requests.get(url, timeout=10)
89
+ response.raise_for_status()
90
+ image_url = response.json()["urls"]["regular"]
91
+ image_response = requests.get(image_url, timeout=10)
92
+ image_response.raise_for_status()
93
+
94
+ output_path = f"outputs/images/{sanitize_filename(prompt)}.jpg"
95
+ img = Image.open(BytesIO(image_response.content))
96
+ img.convert("RGB").save(output_path)
97
+
98
+ if add_watermark:
99
+ apply_watermark(output_path)
100
+
101
+ return output_path
102
+
103
+ except Exception as e:
104
+ logging.error(f"Image generation failed: {e}")
105
+ st.write("🔁 Unsplash failed. Using fallback.")
106
+ st.write(f"❌ Image generation failed: {e}")
107
+ return use_fallback_image(prompt, add_watermark=add_watermark)
108
+
109
+
110
+ # ✅ Updated generate_audio with proper language handling
111
+
112
+ def generate_audio(prompt, output_path, debug_mode=False, lang="en"):
113
+ try:
114
+ api_key = os.getenv("ELEVEN_API_KEY") or st.secrets.get("ELEVEN_API_KEY", None)
115
+
116
+ # Use gTTS for non-English languages
117
+ if lang != "en":
118
+ if debug_mode:
119
+ st.write(f"🌐 Non-English language selected: {lang}. Using gTTS.")
120
+ return generate_gtts_fallback(prompt, output_path, lang=lang, debug_mode=debug_mode)
121
+
122
+ if api_key:
123
+ if debug_mode:
124
+ st.write(f"✅ ELEVEN_API_KEY loaded: {api_key[:4]}...****")
125
+
126
+ set_api_key(api_key)
127
+ if debug_mode:
128
+ st.write(f"🎧 Generating audio for prompt: {prompt}")
129
+
130
+ try:
131
+ audio = generate(text=prompt, voice="Aria", model="eleven_monolingual_v1")
132
+ save(audio, output_path)
133
+ logging.info(f"Audio saved successfully to {output_path}")
134
+
135
+ if debug_mode:
136
+ st.write(f"🔍 File exists after save? {os.path.exists(output_path)}")
137
+ st.write(f"✅ Audio saved successfully to {output_path}")
138
+ return output_path
139
+
140
+ except Exception as e:
141
+ logging.warning(f"ElevenLabs failed: {e}")
142
+ if debug_mode:
143
+ st.write(f"⚠️ ElevenLabs failed: {str(e)}")
144
+ st.write("🔁 Falling back to gTTS...")
145
+ return generate_gtts_fallback(prompt, output_path, lang=lang, debug_mode=debug_mode)
146
+
147
+ else:
148
+ logging.warning("ELEVEN_API_KEY not found")
149
+ if debug_mode:
150
+ st.write("❌ ELEVEN_API_KEY not found. Falling back to gTTS.")
151
+ return generate_gtts_fallback(prompt, output_path, lang=lang, debug_mode=debug_mode)
152
+
153
+ except Exception as e:
154
+ logging.error(f"Exception during audio generation setup: {e}")
155
+ if debug_mode:
156
+ st.write(f"❌ Exception during audio generation setup: {str(e)}")
157
+ st.write("🔁 Falling back to gTTS...")
158
+ return generate_gtts_fallback(prompt, output_path, lang=lang, debug_mode=debug_mode)
159
+
160
+
161
+ def generate_video(prompt, image_path, audio_path, output_path, add_watermark=False, dark_mode=False):
162
+ try:
163
+ # If dark_mode, darken the image temporarily
164
+ if dark_mode:
165
+ with Image.open(image_path) as img:
166
+ enhancer = ImageEnhance.Brightness(img)
167
+ darker_img = enhancer.enhance(0.5) # Reduce brightness to 50%
168
+
169
+ # Save to a temporary file
170
+ with tempfile.NamedTemporaryFile(delete=False, suffix=".jpg") as tmp:
171
+ temp_image_path = tmp.name
172
+ darker_img.save(temp_image_path)
173
+ image_path = temp_image_path
174
+
175
+ audio_clip = AudioFileClip(audio_path)
176
+ image_clip = ImageClip(image_path).set_duration(audio_clip.duration).resize(height=720)
177
+ video = image_clip.set_audio(audio_clip)
178
+
179
+ output_path = f"outputs/videos/{sanitize_filename(prompt)}.mp4"
180
+ video.write_videofile(output_path, fps=24, codec="libx264", audio_codec="aac", verbose=False, logger=None)
181
+ return output_path
182
+
183
+ except Exception as e:
184
+ logging.error(f"Video generation failed: {e}")
185
+ st.write(f"❌ Video generation failed: {e}")
186
+ return None
187
+
media-gen-api/docs/index.html ADDED
@@ -0,0 +1,62 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
6
+ <meta name="description" content="OSN Media Generator - Built by O.S.Narayana. Create media content using AI with no ads or in-app purchases."/>
7
+ <title>OSN Media Generator</title>
8
+ <style>
9
+ body {
10
+ font-family: Arial, sans-serif;
11
+ background-color: #f3f4f6;
12
+ color: #111827;
13
+ text-align: center;
14
+ padding: 50px 20px;
15
+ }
16
+ .container {
17
+ max-width: 600px;
18
+ margin: auto;
19
+ background: #ffffff;
20
+ padding: 30px;
21
+ border-radius: 12px;
22
+ box-shadow: 0 4px 12px rgba(0,0,0,0.1);
23
+ }
24
+ h1 {
25
+ color: #1f2937;
26
+ }
27
+ a {
28
+ color: #2563eb;
29
+ text-decoration: none;
30
+ }
31
+ a:hover {
32
+ text-decoration: underline;
33
+ }
34
+ .footer {
35
+ margin-top: 40px;
36
+ font-size: 0.9em;
37
+ color: #6b7280;
38
+ }
39
+ </style>
40
+ </head>
41
+ <body>
42
+ <div class="container">
43
+ <h1>🎮 OSN Media Generator</h1>
44
+ <p>
45
+ Welcome to the official homepage of <strong>OSN Media Generator</strong>, created by <strong>O.S.Narayana</strong>.
46
+ </p>
47
+ <p>
48
+ This app helps you generate audio, images, and videos using AI tools like ElevenLabs, Unsplash, and Streamlit.
49
+ </p>
50
+ <p>
51
+ 👉 Try the app now: <br>
52
+ <a href="https://osnarayana-media-generator.streamlit.app/" target="_blank">Launch OSN Media Generator</a>
53
+ </p>
54
+ <p>
55
+ 📄 View our <a href="privacy.html" target="_blank">Privacy Policy</a>
56
+ </p>
57
+ <div class="footer">
58
+ &copy; 2025 OSN Media. Built with 💗 by O.S.Narayana.
59
+ </div>
60
+ </div>
61
+ </body>
62
+ </html>
media-gen-api/docs/privacy.html ADDED
@@ -0,0 +1,68 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Privacy Policy - OSN Media Generator</title>
7
+ <meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" />
8
+ <meta http-equiv="Pragma" content="no-cache" />
9
+ <meta http-equiv="Expires" content="0" />
10
+ <style>
11
+ body {
12
+ font-family: Arial, sans-serif;
13
+ margin: 2rem;
14
+ padding: 0;
15
+ background-color: #f9f9f9;
16
+ color: #333;
17
+ }
18
+ h1, h2, h3 { color: #222; }
19
+ a { color: #1a73e8; text-decoration: none; }
20
+ a:hover { text-decoration: underline; }
21
+ code { background-color: #eee; padding: 2px 4px; border-radius: 4px; }
22
+
23
+ </style>
24
+ </head>
25
+ <body>
26
+ <h1>🔐 Privacy Policy for OSN Media Generator</h1>
27
+
28
+ <p>Effective Date: July 19, 2025</p>
29
+
30
+ <p>Your privacy is important to us. This app is built to prioritize user data security and transparency.</p>
31
+
32
+ <h2>1. What We Collect</h2>
33
+
34
+ <ul>
35
+ <li><strong>No personal data</strong> is collected, stored, or shared by this application.</li>
36
+ <li>All prompts, audio, and media are <strong>processed locally</strong> or via <strong>user-provided API keys</strong> to external services (e.g., ElevenLabs, Unsplash).</li>
37
+ </ul>
38
+
39
+ <h2>2. API Usage</h2>
40
+
41
+ <ul>
42
+ <li>If you use external services (like ElevenLabs or Unsplash), you are subject to their respective <a href="https://www.elevenlabs.io/terms">Terms of Service</a> and <a href="https://www.elevenlabs.io/privacy">Privacy Policies</a>.</li>
43
+ <li>Your API keys are stored <strong>locally</strong> in your environment file (<code>.env</code>) and never uploaded.</li>
44
+ </ul>
45
+
46
+ <h2>3. Data Storage</h2>
47
+
48
+ <ul>
49
+ <li>Generated images, audio, and videos are stored <strong>only on your local machine</strong> under the <code>outputs/</code> directory.</li>
50
+ <li>You can delete any generated content at your discretion.</li>
51
+ </ul>
52
+
53
+ <h2>4. Analytics &amp; Ads</h2>
54
+
55
+ <ul>
56
+ <li>This app contains <strong>no ads</strong>, <strong>no tracking</strong>, and <strong>no analytics</strong>.</li>
57
+ </ul>
58
+
59
+ <h2>5. Changes to Policy</h2>
60
+
61
+ <p>Any updates to this policy will be reflected in this file on the <a href="https://github.com/sivaogeti/osnarayana-media-generator">GitHub repository</a>.</p>
62
+
63
+ <h2>6. Contact</h2>
64
+
65
+ <p>For questions or issues, contact: <strong>[email protected]</strong></p>
66
+
67
+ </body>
68
+ </html>
media-gen-api/nixpacks.toml ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [phases.setup]
2
+ nixpkgs = [
3
+ "python311",
4
+ "zlib",
5
+ "libjpeg",
6
+ "gcc",
7
+ "pkg-config"
8
+ ]
9
+
10
+ [phases.build]
11
+ cmds = [
12
+ "python -m venv /opt/venv",
13
+ ". /opt/venv/bin/activate",
14
+ "pip install --upgrade pip",
15
+ "pip install -r requirements.txt"
16
+ ]
17
+
18
+ [start]
19
+ cmd = "streamlit run app.py --server.port 8000 --server.address 0.0.0.0"
media-gen-api/openapi.json ADDED
@@ -0,0 +1 @@
 
 
1
+ {"openapi":"3.1.0","info":{"title":"Media Generator API","description":"Generate audio, video, image, and PPT from text","version":"1.0.0"},"paths":{"/api/v1/audio/generate":{"post":{"tags":["Audio"],"summary":"Generate Audio Endpoint","operationId":"generate_audio_endpoint_api_v1_audio_generate_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AudioInput"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/video/generate":{"post":{"tags":["Video"],"summary":"Generate Video","operationId":"generate_video_api_v1_video_generate_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VideoInput"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/image/generate":{"post":{"tags":["Image"],"summary":"Generate Image","operationId":"generate_image_api_v1_image_generate_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ImageInput"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/ppt/generate":{"post":{"tags":["PPT"],"summary":"Generate Ppt","operationId":"generate_ppt_api_v1_ppt_generate_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PPTInput"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/metrics/evaluate/bleu":{"post":{"tags":["Metrics"],"summary":"Compute Bleu","operationId":"compute_bleu_api_v1_metrics_evaluate_bleu_post","parameters":[{"name":"reference","in":"query","required":true,"schema":{"type":"string","title":"Reference"}},{"name":"candidate","in":"query","required":true,"schema":{"type":"string","title":"Candidate"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/metrics/evaluate/clipscore":{"post":{"tags":["Metrics"],"summary":"Compute Clip Score","operationId":"compute_clip_score_api_v1_metrics_evaluate_clipscore_post","parameters":[{"name":"reference","in":"query","required":true,"schema":{"type":"string","title":"Reference"}},{"name":"candidate","in":"query","required":true,"schema":{"type":"string","title":"Candidate"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/download/":{"get":{"tags":["Download"],"summary":"Download File","operationId":"download_file_api_v1_download__get","parameters":[{"name":"file_path","in":"query","required":true,"schema":{"type":"string","description":"Relative path from project root","title":"File Path"},"description":"Relative path from project root"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}}},"components":{"schemas":{"AudioInput":{"properties":{"text":{"type":"string","title":"Text"},"voice":{"type":"string","title":"Voice","default":"default"},"language":{"type":"string","title":"Language","default":"en"}},"type":"object","required":["text"],"title":"AudioInput"},"HTTPValidationError":{"properties":{"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"type":"array","title":"Detail"}},"type":"object","title":"HTTPValidationError"},"ImageInput":{"properties":{"prompt":{"type":"string","title":"Prompt"},"style":{"type":"string","title":"Style","default":"default"}},"type":"object","required":["prompt"],"title":"ImageInput"},"PPTInput":{"properties":{"slides":{"items":{"$ref":"#/components/schemas/Slide"},"type":"array","title":"Slides"}},"type":"object","required":["slides"],"title":"PPTInput"},"Slide":{"properties":{"title":{"type":"string","title":"Title"},"content":{"type":"string","title":"Content"}},"type":"object","required":["title","content"],"title":"Slide"},"ValidationError":{"properties":{"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"type":"array","title":"Location"},"msg":{"type":"string","title":"Message"},"type":{"type":"string","title":"Error Type"}},"type":"object","required":["loc","msg","type"],"title":"ValidationError"},"VideoInput":{"properties":{"script":{"type":"string","title":"Script"},"duration":{"type":"integer","title":"Duration","default":10}},"type":"object","required":["script"],"title":"VideoInput"}},"securitySchemes":{"HTTPBearer":{"type":"http","scheme":"bearer"}}}}
media-gen-api/packages.txt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ ffmpeg
2
+ libjpeg-dev
3
+ zlib1g-dev
media-gen-api/requirements.txt ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ streamlit
2
+ requests
3
+ python-dotenv
4
+ gtts
5
+ moviepy==1.0.3
6
+ google-api-python-client
7
+ google-auth-httplib2
8
+ google-auth-oauthlib
9
+ google-auth
10
+ pillow==9.5.0
11
+ googletrans==4.0.0-rc1
12
+ elevenlabs
13
+ deep_translator
14
+ gTTS
15
+ sentry-sdk
16
+ scikit-learn
17
+ nltk
18
+ sentence_transformers
19
+ sqlalchemy
media-gen-api/screenshots/generated_audio_sample.png ADDED
media-gen-api/screenshots/home_page.png ADDED
media-gen-api/scripts/preload_content.py ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import shutil
3
+ from datetime import datetime
4
+
5
+ SOURCE_DIR = "generated"
6
+ ARCHIVE_DIR = f"archive_{datetime.now().strftime('%Y%m%d_%H%M')}"
7
+
8
+ def archive_all():
9
+ if not os.path.exists(SOURCE_DIR):
10
+ print("No content to archive.")
11
+ return
12
+
13
+ shutil.copytree(SOURCE_DIR, ARCHIVE_DIR)
14
+ print(f"Archived to {ARCHIVE_DIR}")
15
+
16
+ if __name__ == "__main__":
17
+ archive_all()
media-gen-api/streamlit_ui.py ADDED
@@ -0,0 +1,75 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # streamlit_ui.py
2
+ import streamlit as st
3
+ import requests
4
+ import base64
5
+
6
+ st.set_page_config(page_title="AI Media Generator", layout="wide")
7
+ st.title("🎙️🖼️🎞️ AI Media Generator")
8
+
9
+ API_BASE = "http://localhost:8000" # change if deployed
10
+
11
+ # Helper to play audio/video in Streamlit
12
+ def render_media(file_bytes, media_type, label):
13
+ b64 = base64.b64encode(file_bytes).decode()
14
+ if media_type == "audio":
15
+ st.audio(f"data:audio/wav;base64,{b64}", format="audio/wav")
16
+ elif media_type == "video":
17
+ st.video(f"data:video/mp4;base64,{b64}")
18
+ elif media_type == "image":
19
+ st.image(file_bytes, caption=label, use_column_width=True)
20
+
21
+ # Sidebar inputs
22
+ st.sidebar.header("🛠️ Settings")
23
+ TOKEN = st.sidebar.text_input("🔑 API Token", type="password")
24
+ HEADERS = {"Authorization": f"Bearer {TOKEN}"} if TOKEN else {}
25
+
26
+ # Tabs
27
+ tab = st.sidebar.radio("Select Task", ["Text to Audio", "Text to Image", "Text to Video"])
28
+
29
+ if tab == "Text to Audio":
30
+ st.subheader("🎤 Text to Audio")
31
+ text = st.text_area("Enter text")
32
+ voice = st.selectbox("Choose voice/language", ["en-US", "hi-IN", "te-IN", "ta-IN"])
33
+
34
+ if st.button("🔊 Generate Audio"):
35
+ with st.spinner("Generating audio..."):
36
+ r = requests.post(f"{API_BASE}/audio/generate", json={"text": text, "voice": voice}, headers=HEADERS)
37
+ if r.status_code == 200:
38
+ render_media(r.content, "audio", "Generated Audio")
39
+ else:
40
+ st.error(f"❌ Failed: {r.json().get('detail')}")
41
+
42
+ elif tab == "Text to Image":
43
+ st.subheader("🖼️ Text to Image")
44
+ prompt = st.text_area("Enter image prompt")
45
+ model = st.selectbox("Choose model", ["sdxl", "deepfloyd", "kandinsky"])
46
+
47
+ if st.button("🧠 Generate Image"):
48
+ with st.spinner("Generating image..."):
49
+ r = requests.post(f"{API_BASE}/image/generate", json={"prompt": prompt, "model": model}, headers=HEADERS)
50
+ if r.status_code == 200:
51
+ render_media(r.content, "image", "Generated Image")
52
+ else:
53
+ st.error(f"❌ Failed: {r.json().get('detail')}")
54
+
55
+ elif tab == "Text to Video":
56
+ st.subheader("🎞️ Text to Video")
57
+ prompt = st.text_area("Enter video prompt")
58
+ tone = st.selectbox("Tone", ["formal", "casual", "emotional", "documentary"])
59
+ domain = st.selectbox("Domain", ["health", "education", "governance", "entertainment"])
60
+ environment = st.selectbox("Environment", ["urban", "rural", "nature", "futuristic"])
61
+
62
+ if st.button("🎬 Generate Video"):
63
+ with st.spinner("Generating video..."):
64
+ r = requests.post(
65
+ f"{API_BASE}/video/generate",
66
+ json={"prompt": prompt, "tone": tone, "domain": domain, "environment": environment},
67
+ headers=HEADERS
68
+ )
69
+ if r.status_code == 200:
70
+ render_media(r.content, "video", "Generated Video")
71
+ else:
72
+ st.error(f"❌ Failed: {r.json().get('detail')}")
73
+
74
+ st.sidebar.markdown("---")
75
+ st.sidebar.info("Built with ❤️ for AI GovTech Challenge 2025")