|
import dash |
|
from dash import dcc, html, Input, Output, State, callback |
|
import dash_bootstrap_components as dbc |
|
import base64 |
|
import io |
|
import google.generativeai as genai |
|
import numpy as np |
|
import edge_tts |
|
import asyncio |
|
import re |
|
import logging |
|
import json |
|
from dash.exceptions import PreventUpdate |
|
|
|
|
|
logging.basicConfig(level=logging.INFO) |
|
logger = logging.getLogger(__name__) |
|
|
|
|
|
app = dash.Dash(__name__, external_stylesheets=[dbc.themes.BOOTSTRAP]) |
|
|
|
|
|
genai.configure(api_key='YOUR_GEMINI_API_KEY') |
|
|
|
|
|
def generate_podcast_script(api_key, content, duration, num_hosts): |
|
|
|
|
|
async def text_to_speech(text, voice): |
|
|
|
|
|
async def render_podcast(api_key, script, voice1, voice2, num_hosts): |
|
|
|
|
|
async def get_voice_list(): |
|
|
|
|
|
|
|
language_names = { |
|
|
|
} |
|
|
|
|
|
app.layout = dbc.Container([ |
|
html.H1("AI Podcast Generator", className="my-4"), |
|
|
|
dbc.Card([ |
|
dbc.CardBody([ |
|
dbc.Input(id="api-key-input", type="password", placeholder="Enter your Gemini API Key"), |
|
dbc.Textarea(id="content-input", placeholder="Paste your content or upload a document", rows=5), |
|
dcc.Upload( |
|
id='document-upload', |
|
children=html.Div(['Drag and Drop or ', html.A('Select a File')]), |
|
style={ |
|
'width': '100%', |
|
'height': '60px', |
|
'lineHeight': '60px', |
|
'borderWidth': '1px', |
|
'borderStyle': 'dashed', |
|
'borderRadius': '5px', |
|
'textAlign': 'center', |
|
'margin': '10px 0' |
|
} |
|
), |
|
dcc.RadioItems( |
|
id="duration", |
|
options=[ |
|
{'label': '1-5 min', 'value': '1-5 min'}, |
|
{'label': '5-10 min', 'value': '5-10 min'}, |
|
{'label': '10-15 min', 'value': '10-15 min'} |
|
], |
|
value='1-5 min', |
|
inline=True |
|
), |
|
dcc.RadioItems( |
|
id="num-hosts", |
|
options=[ |
|
{'label': '1 host', 'value': 1}, |
|
{'label': '2 hosts', 'value': 2} |
|
], |
|
value=2, |
|
inline=True |
|
), |
|
dcc.Dropdown(id="lang1-select", options=[{'label': lang, 'value': lang} for lang in language_names.values()], value="English (United States)"), |
|
dcc.Dropdown(id="voice1-select"), |
|
html.Div([ |
|
dcc.Dropdown(id="lang2-select", options=[{'label': lang, 'value': lang} for lang in language_names.values()], value="English (United States)"), |
|
dcc.Dropdown(id="voice2-select") |
|
], id="second-voice-container"), |
|
dbc.Button("Generate Script", id="generate-btn", color="primary", className="mt-3"), |
|
dbc.Textarea(id="script-output", rows=10, className="mt-3"), |
|
dbc.Button("Render Podcast", id="render-btn", color="success", className="mt-3"), |
|
html.Div(id="audio-output") |
|
]) |
|
], className="mt-4") |
|
], fluid=True) |
|
|
|
|
|
@app.callback( |
|
Output("voice1-select", "options"), |
|
Input("lang1-select", "value") |
|
) |
|
def update_voice1_options(lang): |
|
selected_lang = next((key for key, value in language_names.items() if value == lang), None) |
|
voices = asyncio.run(get_voice_list()).get(selected_lang, []) |
|
return [{'label': voice, 'value': voice} for voice in voices] |
|
|
|
@app.callback( |
|
Output("voice2-select", "options"), |
|
Input("lang2-select", "value") |
|
) |
|
def update_voice2_options(lang): |
|
selected_lang = next((key for key, value in language_names.items() if value == lang), None) |
|
voices = asyncio.run(get_voice_list()).get(selected_lang, []) |
|
return [{'label': voice, 'value': voice} for voice in voices] |
|
|
|
@app.callback( |
|
Output("second-voice-container", "style"), |
|
Input("num-hosts", "value") |
|
) |
|
def update_second_voice_visibility(num_hosts): |
|
return {'display': 'block' if num_hosts == 2 else 'none'} |
|
|
|
@app.callback( |
|
Output("content-input", "value"), |
|
Input("document-upload", "contents"), |
|
State("document-upload", "filename") |
|
) |
|
def update_content(contents, filename): |
|
if contents is not None: |
|
content_type, content_string = contents.split(',') |
|
decoded = base64.b64decode(content_string) |
|
try: |
|
if 'csv' in filename: |
|
df = pd.read_csv(io.StringIO(decoded.decode('utf-8'))) |
|
return df.to_string() |
|
elif 'xls' in filename: |
|
df = pd.read_excel(io.BytesIO(decoded)) |
|
return df.to_string() |
|
elif 'txt' in filename: |
|
return decoded.decode('utf-8') |
|
else: |
|
return 'Unsupported file type' |
|
except Exception as e: |
|
return f'Error processing file: {str(e)}' |
|
return '' |
|
|
|
@app.callback( |
|
Output("script-output", "value"), |
|
Input("generate-btn", "n_clicks"), |
|
State("api-key-input", "value"), |
|
State("content-input", "value"), |
|
State("duration", "value"), |
|
State("num-hosts", "value") |
|
) |
|
def generate_script(n_clicks, api_key, content, duration, num_hosts): |
|
if n_clicks is None: |
|
raise PreventUpdate |
|
try: |
|
script = generate_podcast_script(api_key, content, duration, num_hosts) |
|
return script |
|
except Exception as e: |
|
return f"Error generating script: {str(e)}" |
|
|
|
@app.callback( |
|
Output("audio-output", "children"), |
|
Input("render-btn", "n_clicks"), |
|
State("api-key-input", "value"), |
|
State("script-output", "value"), |
|
State("voice1-select", "value"), |
|
State("voice2-select", "value"), |
|
State("num-hosts", "value") |
|
) |
|
def render_podcast_audio(n_clicks, api_key, script, voice1, voice2, num_hosts): |
|
if n_clicks is None: |
|
raise PreventUpdate |
|
try: |
|
sample_rate, audio_data = asyncio.run(render_podcast(api_key, script, voice1, voice2, num_hosts)) |
|
audio_base64 = base64.b64encode(audio_data.tobytes()).decode('utf-8') |
|
return html.Audio(src=f"data:audio/wav;base64,{audio_base64}", controls=True) |
|
except Exception as e: |
|
return html.Div(f"Error rendering podcast: {str(e)}", style={'color': 'red'}) |
|
|
|
if __name__ == '__main__': |
|
print("Starting the Dash application...") |
|
app.run(debug=True, host='0.0.0.0', port=7860) |
|
print("Dash application has finished running.") |