Skip to content

Commit 75479a0

Browse files
committed
Containerizes app, configures AI API keys, and centralizes app settings.
1 parent e5dac35 commit 75479a0

File tree

8 files changed

+135
-52
lines changed

8 files changed

+135
-52
lines changed

.env.example

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# API Keys for Podcast Generator
2+
# Copy this file to .env and fill in your actual API keys
3+
4+
# ElevenLabs API Key (get one at https://elevenlabs.io)
5+
ELEVENLABS_API_KEY=your_elevenlabs_api_key_here
6+
7+
# Google Gemini API Key (get one at https://ai.google.dev)
8+
GEMINI_API_KEY=your_gemini_api_key_here

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,6 @@ _version.py
2222
*.DS_Store
2323
Podcast_generator.egg-info/
2424
instance/*
25+
26+
# Environment variables (contains API keys)
27+
.env

README.md

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
# 🎙️ Podcast Generator
22
[![Buy Me a Coffee](https://img.shields.io/badge/Buy_Me_a_Coffee-FFDD00?style=flat&logo=buy-me-a-coffee&logoColor=000000)](https://www.buymeacoffee.com/laurentftech)
3+
[![Docker Hub](https://img.shields.io/docker/v/gandulf78/podcast_generator?label=docker&logo=docker)](https://hub.docker.com/r/gandulf78/podcast_generator)
4+
[![Docker Pulls](https://img.shields.io/docker/pulls/gandulf78/podcast_generator)](https://hub.docker.com/r/gandulf78/podcast_generator)
35

46
If you enjoy this project and want to support my work, feel free to [buy me a coffee](https://www.buymeacoffee.com/laurentftech) ☕. Thank you for your support!
57

@@ -235,6 +237,56 @@ pip install .[demo]
235237

236238
---
237239

240+
## 🐳 Docker Deployment
241+
242+
You can run Podcast Generator as a web service using Docker. This provides a REST API and web interface.
243+
244+
### Setting up API Keys
245+
246+
First, create a `.env` file with your API keys:
247+
248+
```bash
249+
# Copy the example file
250+
cp .env.example .env
251+
252+
# Edit .env and add your API keys
253+
# ELEVENLABS_API_KEY=your_actual_key_here
254+
# GEMINI_API_KEY=your_actual_key_here
255+
```
256+
257+
### Using Docker Hub (Recommended)
258+
259+
**Option 1: Using docker-compose (easiest)**
260+
261+
```bash
262+
# Pull and start the container (reads .env automatically)
263+
docker-compose -f docker-compose.prod.yml up -d
264+
```
265+
266+
**Option 2: Using docker run**
267+
268+
```bash
269+
# Pull the latest image
270+
docker pull gandulf78/podcast_generator:latest
271+
272+
# Run the container (load .env file)
273+
docker run -d -p 8000:8000 \
274+
-v $(pwd)/config:/app/config \
275+
-v $(pwd)/logs:/app/logs \
276+
--env-file .env \
277+
gandulf78/podcast_generator:latest
278+
```
279+
280+
The web interface will be available at `http://localhost:8000`
281+
282+
### Building from Source
283+
284+
```bash
285+
docker-compose up --build
286+
```
287+
288+
---
289+
238290
## 👨‍💻 For Developers
239291
To contribute to the project, run the code, or create your own build, please refer to the full developer guide:
240292
➡️ [DEVELOPERS.md](docs/DEVELOPERS.md)

app.py

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from flask import Flask, render_template, request, jsonify, send_from_directory
22
from generate_podcast import generate, PODCAST_SCRIPT, setup_logging, validate_speakers, update_elevenlabs_quota
3-
from utils import sanitize_text, get_asset_path
3+
from utils import sanitize_text, get_asset_path, get_app_data_dir
4+
from config import AVAILABLE_VOICES, DEFAULT_APP_SETTINGS
45
from create_demo import create_html_demo_whisperx
56
import os
67
import tempfile
@@ -38,16 +39,14 @@
3839

3940

4041
def get_settings_path():
41-
from gui import get_app_data_dir
4242
return os.path.join(get_app_data_dir(), "settings.json")
4343

4444
def load_settings():
4545
try:
4646
with open(get_settings_path(), 'r') as f:
4747
return json.load(f)
4848
except (FileNotFoundError, json.JSONDecodeError):
49-
from gui import PodcastGeneratorApp
50-
return PodcastGeneratorApp.DEFAULT_APP_SETTINGS
49+
return DEFAULT_APP_SETTINGS
5150

5251
def save_settings(settings):
5352
with open(get_settings_path(), 'w') as f:
@@ -111,7 +110,7 @@ def update_settings():
111110

112111
@app.route('/api/voices', methods=['GET'])
113112
def get_voices():
114-
from gui import AVAILABLE_VOICES as gemini_voices
113+
gemini_voices = AVAILABLE_VOICES
115114
elevenlabs_voices = []
116115
api_key = os.environ.get("ELEVENLABS_API_KEY")
117116
if api_key:

config.py

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,49 @@
99
"account": "gemini_api_key",
1010
"url": "https://aistudio.google.com/app/apikey"
1111
}
12+
}
13+
14+
# Gemini available voices
15+
AVAILABLE_VOICES = {
16+
"Zephyr": "Bright",
17+
"Puck": "Upbeat",
18+
"Charon": "Informative",
19+
"Kore": "Firm",
20+
"Fenrir": "Excitable",
21+
"Leda": "Youthful",
22+
"Orus": "Firm",
23+
"Aoede": "Breezy",
24+
"Callirrhoe": "Easy-going",
25+
"Autonoe": "Bright",
26+
"Enceladus": "Breathy",
27+
"Iapetus": "Clear",
28+
"Umbriel": "Easy-going",
29+
"Algieba": "Smooth",
30+
"Despina": "Smooth",
31+
"Erinome": "Clear",
32+
"Algenib": "Gravelly",
33+
"Rasalgethi": "Informative",
34+
"Laomedeia": "Upbeat",
35+
"Achernar": "Soft",
36+
"Alnilam": "Firm",
37+
"Schedar": "Even",
38+
"Gacrux": "Mature",
39+
"Pulcherrima": "Forward",
40+
"Achird": "Friendly",
41+
"Zubenelgenubi": "Casual",
42+
"Vindemiatrix": "Gentle",
43+
"Sadachbia": "Lively",
44+
"Sadaltager": "Knowledgeable",
45+
"Sulafat": "Warm"
46+
}
47+
48+
# Default application settings
49+
DEFAULT_APP_SETTINGS = {
50+
"tts_provider": "elevenlabs",
51+
"speaker_voices": {"John": "Schedar - Even", "Samantha": "Zephyr - Bright"},
52+
"speaker_voices_elevenlabs": {
53+
"John": {"id": "TX3LPaxmHKxFdv7VOQHJ", "display_name": "Liam - Male, Young, american"},
54+
"Samantha": {"id": "cgSgspJ2msm6clMCkdW9", "display_name": "Jessica - Female, Young, american"}
55+
},
56+
"elevenlabs_quota_cache": None
1257
}

docker-compose.prod.yml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
services:
2+
podcast-generator:
3+
image: gandulf78/podcast_generator:latest
4+
ports:
5+
- "8000:8000"
6+
volumes:
7+
- ./config:/app/config
8+
- ./logs:/app/logs
9+
environment:
10+
# These variables are loaded from the .env file
11+
# Copy .env.example to .env and fill in your API keys
12+
- ELEVENLABS_API_KEY=${ELEVENLABS_API_KEY}
13+
- GEMINI_API_KEY=${GEMINI_API_KEY}
14+
restart: always

docker-compose.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,7 @@ services:
66
volumes:
77
- ./config:/app/config
88
- ./logs:/app/logs
9+
environment:
10+
- ELEVENLABS_API_KEY=${ELEVENLABS_API_KEY}
11+
- GEMINI_API_KEY=${GEMINI_API_KEY}
912
restart: always

gui.py

Lines changed: 6 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
from generate_podcast import validate_speakers, update_elevenlabs_quota
4141
from utils import get_asset_path, sanitize_app_settings_for_backend, find_ffplay_path, get_app_data_dir, sanitize_text
4242
from create_demo import create_html_demo_whisperx
43+
from config import AVAILABLE_VOICES, DEFAULT_APP_SETTINGS
4344

4445
# --- Versioning ---
4546
# This file is automatically generated by setuptools-scm
@@ -49,55 +50,13 @@
4950
# Fallback développement: quand _version.py n'est pas encore généré
5051
__version__ = "0.0.0-dev"
5152

52-
AVAILABLE_VOICES = {
53-
"Zephyr": "Bright",
54-
"Puck": "Upbeat",
55-
"Charon": "Informative",
56-
"Kore": "Firm",
57-
"Fenrir": "Excitable",
58-
"Leda": "Youthful",
59-
"Orus": "Firm",
60-
"Aoede": "Breezy",
61-
"Callirrhoe": "Easy-going",
62-
"Autonoe": "Bright",
63-
"Enceladus": "Breathy",
64-
"Iapetus": "Clear",
65-
"Umbriel": "Easy-going",
66-
"Algieba": "Smooth",
67-
"Despina": "Smooth",
68-
"Erinome": "Clear",
69-
"Algenib": "Gravelly",
70-
"Rasalgethi": "Informative",
71-
"Laomedeia": "Upbeat",
72-
"Achernar": "Soft",
73-
"Alnilam": "Firm",
74-
"Schedar": "Even",
75-
"Gacrux": "Mature",
76-
"Pulcherrima": "Forward",
77-
"Achird": "Friendly",
78-
"Zubenelgenubi": "Casual",
79-
"Vindemiatrix": "Gentle",
80-
"Sadachbia": "Lively",
81-
"Sadaltager": "Knowledgeable",
82-
"Sulafat": "Warm"
83-
}
84-
8553

8654
def get_app_version() -> str:
8755
"""Gets the application version from the _version.py file."""
8856
return __version__
8957

9058

9159
class PodcastGeneratorApp:
92-
DEFAULT_APP_SETTINGS = {
93-
"tts_provider": "elevenlabs",
94-
"speaker_voices": {"John": "Schedar - Even", "Samantha": "Zephyr - Bright"},
95-
"speaker_voices_elevenlabs": {
96-
"John": {"id": "TX3LPaxmHKxFdv7VOQHJ", "display_name": "Liam - Male, Young, american"},
97-
"Samantha": {"id": "cgSgspJ2msm6clMCkdW9", "display_name": "Jessica - Female, Young, american"}
98-
},
99-
"elevenlabs_quota_cache": None
100-
}
10160

10261
def __init__(self, root: tk.Tk, generate_func, logger, api_key: str, default_script: str = ""):
10362
self.root = root
@@ -811,10 +770,10 @@ def load_settings(self):
811770
settings = json.load(f)
812771
# Load the settings as they are, preserving the rich dictionary for ElevenLabs.
813772
# The data will be flattened only when needed (right before generation).
814-
tts_provider = settings.get("tts_provider", self.DEFAULT_APP_SETTINGS["tts_provider"])
815-
speaker_voices = settings.get("speaker_voices", self.DEFAULT_APP_SETTINGS["speaker_voices"].copy())
773+
tts_provider = settings.get("tts_provider", DEFAULT_APP_SETTINGS["tts_provider"])
774+
speaker_voices = settings.get("speaker_voices", DEFAULT_APP_SETTINGS["speaker_voices"].copy())
816775
speaker_voices_elevenlabs = settings.get("speaker_voices_elevenlabs",
817-
self.DEFAULT_APP_SETTINGS["speaker_voices_elevenlabs"].copy())
776+
DEFAULT_APP_SETTINGS["speaker_voices_elevenlabs"].copy())
818777
elevenlabs_quota_cache = settings.get("elevenlabs_quota_cache", None)
819778

820779
return {
@@ -825,7 +784,7 @@ def load_settings(self):
825784
}
826785
except (FileNotFoundError, json.JSONDecodeError):
827786
# Returns default app settings if the file does not exist or is corrupt
828-
return json.loads(json.dumps(self.DEFAULT_APP_SETTINGS))
787+
return json.loads(json.dumps(DEFAULT_APP_SETTINGS))
829788

830789
def save_settings(self, settings_to_save):
831790
"""Saves the settings to the JSON file."""
@@ -934,7 +893,7 @@ def _show_settings_window(self):
934893
current_settings=self.app_settings,
935894
save_callback=self.save_settings,
936895
close_callback=self.on_settings_window_close,
937-
default_settings=self.DEFAULT_APP_SETTINGS,
896+
default_settings=DEFAULT_APP_SETTINGS,
938897
preloaded_elevenlabs_voices=self.elevenlabs_voices_cache,
939898
play_gemini_sample_callback=self.play_gemini_voice_sample,
940899
play_elevenlabs_sample_callback=self.play_elevenlabs_voice_sample

0 commit comments

Comments
 (0)