Johan713 commited on
Commit
5015449
·
verified ·
1 Parent(s): 3e79474

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +1380 -0
app.py ADDED
@@ -0,0 +1,1380 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ import requests
3
+ import json
4
+ import os
5
+ import pyaudio as pyaudio
6
+ import base64
7
+ from io import BytesIO
8
+ import speech_recognition as sr
9
+ from elevenlabs import play, stream, save
10
+ from elevenlabs.client import ElevenLabs
11
+ from elevenlabs import Voice, VoiceSettings
12
+ from together import Together
13
+ import tempfile
14
+ from audio_recorder_streamlit import audio_recorder
15
+ from PIL import Image
16
+ import random
17
+ import string
18
+ import av
19
+ import time
20
+ import io
21
+ import queue
22
+ from docx import Document
23
+ from fpdf import FPDF
24
+ import pytesseract
25
+ import plotly.express as px
26
+ import pandas as pd
27
+ from pdf2image import convert_from_path
28
+ import PyPDF2
29
+ import docx2txt
30
+ from streamlit_option_menu import option_menu
31
+ import nltk
32
+ from nltk.tokenize import sent_tokenize
33
+ import networkx as nx
34
+ import speech_recognition as sr
35
+ from streamlit_webrtc import webrtc_streamer, WebRtcMode, RTCConfiguration
36
+ import matplotlib.pyplot as plt
37
+ import plotly.graph_objs as go
38
+ from plotly.subplots import make_subplots
39
+
40
+ nltk.download('punkt')
41
+
42
+ from streamlit_agraph import agraph, Node, Edge, Config
43
+ import pandas as pd
44
+ from wordcloud import WordCloud
45
+ import numpy as np
46
+ import threading
47
+ import pydub
48
+ from sklearn.feature_extraction.text import TfidfVectorizer
49
+ from sklearn.metrics.pairwise import cosine_similarity
50
+ import math
51
+ import wave
52
+
53
+ from reportlab.lib.pagesizes import letter
54
+ from reportlab.platypus import SimpleDocTemplate, Paragraph
55
+ from reportlab.lib.styles import getSampleStyleSheet
56
+
57
+ # API Keys
58
+ XI_API_KEY = "sk_44be09a8808c06fec48cc005a1f0443b1ef30711a06cb23c"
59
+ TOGETHER_API_KEY = "291a366758796aaf86fe851b1b0ce2278ef75c750930e9f5d949e6b38bd208de"
60
+
61
+ # Initialize clients
62
+ elevenlabs_client = ElevenLabs(api_key=XI_API_KEY)
63
+ together_client = Together(api_key=TOGETHER_API_KEY)
64
+
65
+
66
+ # Predefined characters (expanded)
67
+ predefined_characters = {
68
+ "Naruto Uzumaki": {
69
+ "description": "Protagonist of the Naruto series. An optimistic and determined ninja with the goal of becoming Hokage.",
70
+ "voice_id": " rKe56TKNeWFVS5tXg0nO",
71
+ "image": "https://i.pinimg.com/originals/2e/f7/0d/2ef70d5217b530dfb766a45d9babbb5e.jpg"
72
+ },
73
+ "Sherlock Holmes": {
74
+ "description": "Brilliant detective known for his logical reasoning and observational skills.",
75
+ "voice_id": "VR6AewLTigWG4xSOukaG",
76
+ "image": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcSFNrsQ14wE7vKH_46oN-tCF3YwGyjo9fbLMuTpvm3ENf_10JcTHqoIyxkl_EDgpmGnEXs&usqp=CAU"
77
+ },
78
+ "Elizabeth Bennet": {
79
+ "description": "Protagonist of Pride and Prejudice, known for her intelligence and wit.",
80
+ "voice_id": "EXAVITQu4vr4xnSDxMaL",
81
+ "image": "https://www.indiependent.co.uk/wp-content/uploads/2015/08/elizabeth-bennet.jpg"
82
+ }
83
+ }
84
+
85
+ # Initialize session state
86
+ if "characters" not in st.session_state or not st.session_state.characters:
87
+ st.session_state.characters = predefined_characters.copy()
88
+ if "current_character" not in st.session_state:
89
+ st.session_state.current_character = None
90
+ if "messages" not in st.session_state:
91
+ st.session_state.messages = []
92
+ if "book_details" not in st.session_state:
93
+ st.session_state.book_details = {}
94
+
95
+ # Fiction genres
96
+ fiction_genres = [
97
+ "Fantasy", "Science Fiction", "Mystery", "Thriller", "Romance", "Historical Fiction",
98
+ "Horror", "Adventure", "Contemporary Fiction", "Dystopian", "Young Adult"
99
+ ]
100
+
101
+ # Non-fiction genres
102
+ non_fiction_genres = [
103
+ "Biography", "Autobiography", "Memoir", "Self-help", "History", "Science",
104
+ "Philosophy", "Psychology", "Business", "Travel", "True Crime"
105
+ ]
106
+
107
+ def create_character(name, description, voice_file, image_file=None):
108
+ voice = elevenlabs_client.clone(
109
+ name=name,
110
+ description=description,
111
+ files=[voice_file]
112
+ )
113
+
114
+ image_path = None
115
+ if image_file:
116
+ characters_dir = "characters"
117
+ os.makedirs(characters_dir, exist_ok=True)
118
+ image_path = os.path.join(characters_dir, f"{name.lower().replace(' ', '_')}.jpg")
119
+ with Image.open(image_file) as img:
120
+ img.save(image_path)
121
+
122
+ st.session_state.characters[name] = {
123
+ "description": description,
124
+ "voice_id": voice.voice_id,
125
+ "image": image_path
126
+ }
127
+ return voice
128
+
129
+ def generate_ai_response(character, user_message, language):
130
+ response = together_client.chat.completions.create(
131
+ model="meta-llama/Meta-Llama-3.1-8B-Instruct-Turbo",
132
+ messages=[
133
+ {"role": "system", "content": f"You are {character}. {st.session_state.characters[character]['description']} Respond in {language}."},
134
+ {"role": "user", "content": user_message}
135
+ ],
136
+ max_tokens=1024,
137
+ temperature=0.7,
138
+ top_p=0.7,
139
+ top_k=50,
140
+ repetition_penalty=1,
141
+ stop=["<|eot_id|>", "<|eom_id|>"]
142
+ )
143
+ return response.choices[0].message.content
144
+
145
+ def text_to_speech(text, voice_id):
146
+ audio = elevenlabs_client.generate(
147
+ text=text,
148
+ voice=Voice(voice_id=voice_id),
149
+ model="eleven_multilingual_v2"
150
+ )
151
+ return audio
152
+
153
+ def speech_to_text(audio_bytes):
154
+ recognizer = sr.Recognizer()
155
+ with sr.AudioFile(BytesIO(audio_bytes)) as source:
156
+ audio = recognizer.record(source)
157
+ try:
158
+ text = recognizer.recognize_google(audio)
159
+ return text
160
+ except sr.UnknownValueError:
161
+ return "Sorry, I couldn't understand the audio."
162
+ except sr.RequestError:
163
+ return "Sorry, there was an error processing the audio."
164
+
165
+ def process_audio(frame):
166
+ sound = frame.to_ndarray()
167
+ sound = sound.astype(np.int16)
168
+ return sound.tobytes()
169
+
170
+ def split_text(text, chunk_size=5000):
171
+ """Split the text into chunks of approximately chunk_size characters."""
172
+ chunks = []
173
+ current_chunk = []
174
+ current_size = 0
175
+ for sentence in nltk.sent_tokenize(text):
176
+ sentence_size = len(sentence)
177
+ if current_size + sentence_size > chunk_size and current_chunk:
178
+ chunks.append(' '.join(current_chunk))
179
+ current_chunk = []
180
+ current_size = 0
181
+ current_chunk.append(sentence)
182
+ current_size += sentence_size
183
+ if current_chunk:
184
+ chunks.append(' '.join(current_chunk))
185
+ return chunks
186
+
187
+ def generate_audio_chunk(text, voice_id, headers):
188
+ """Generate audio for a single chunk of text."""
189
+ audio_data = {
190
+ "text": text,
191
+ "model_id": "eleven_monolingual_v1",
192
+ "voice_settings": {
193
+ "stability": 0.5,
194
+ "similarity_boost": 0.5
195
+ }
196
+ }
197
+ response = requests.post(f"https://api.elevenlabs.io/v1/text-to-speech/{voice_id}", json=audio_data, headers=headers)
198
+ if response.status_code == 200:
199
+ return response.content
200
+ else:
201
+ raise Exception(f"Error generating audio: {response.text}")
202
+
203
+ def convert_to_audiobook(text, language, voice_id):
204
+ audio = elevenlabs_client.generate(
205
+ text=text,
206
+ voice=Voice(voice_id=voice_id),
207
+ model="eleven_multilingual_v2"
208
+ )
209
+ return audio
210
+ def generate_book_chapter(book_details, chapter_index):
211
+ chapter = book_details['chapters'][chapter_index]
212
+ chapter_name = chapter['name']
213
+
214
+ if book_details['genre_type'] == 'Fiction':
215
+ chapter_prompt = f"Write chapter {chapter_index + 1} titled '{chapter_name}' for a {book_details['genre']} book. The book is about: {book_details['description']}. This chapter should include the following scenes: {', '.join(chapter['scenes'])}. Maintain consistency with the overall story and characters."
216
+
217
+ chapter_content = ""
218
+ for scene_index, scene in enumerate(chapter['scenes']):
219
+ scene_prompt = f"Write scene {scene_index + 1} for chapter {chapter_index + 1} titled '{chapter_name}'. The scene should include the following details: {scene}"
220
+ scene_text = together_client.chat.completions.create(
221
+ model="meta-llama/Meta-Llama-3.1-8B-Instruct-Turbo",
222
+ messages=[
223
+ {"role": "system", "content": f"You are an expert {book_details['genre']} writer."},
224
+ {"role": "user", "content": scene_prompt}
225
+ ],
226
+ max_tokens=6048,
227
+ temperature=0.7
228
+ )
229
+ chapter_content += f"## Scene {scene_index + 1}\n\n{scene_text.choices[0].message.content}\n\n"
230
+ else:
231
+ chapter_prompt = f"Write chapter {chapter_index + 1} titled '{chapter_name}' for a {book_details['genre']} non-fiction book. The book is about: {book_details['description']}. This chapter should cover the following parts: {', '.join(chapter['parts'])}. Ensure the content is informative and well-structured."
232
+
233
+ chapter_content = ""
234
+ for part_index, part in enumerate(chapter['parts']):
235
+ part_prompt = f"Write part {part_index + 1} for chapter {chapter_index + 1} titled '{chapter_name}'. The part should cover the following details: {part}"
236
+ part_text = together_client.chat.completions.create(
237
+ model="meta-llama/Meta-Llama-3.1-8B-Instruct-Turbo",
238
+ messages=[
239
+ {"role": "system", "content": f"You are an expert {book_details['genre']} writer."},
240
+ {"role": "user", "content": part_prompt}
241
+ ],
242
+ max_tokens=6048,
243
+ temperature=0.7
244
+ )
245
+ chapter_content += f"## Part {part_index + 1}\n\n{part_text.choices[0].message.content}\n\n"
246
+
247
+ return chapter_content
248
+ def generate_book_introduction(book_details):
249
+ intro_prompt = f"Write an engaging introduction for a {book_details['genre']} {'fiction' if book_details['genre_type'] == 'Fiction' else 'non-fiction'} book titled '{book_details['title']}' by {book_details['author']}. The book is about: {book_details['description']}."
250
+
251
+ intro_content = together_client.chat.completions.create(
252
+ model="meta-llama/Meta-Llama-3.1-8B-Instruct-Turbo",
253
+ messages=[
254
+ {"role": "system", "content": "You are an expert book introduction writer."},
255
+ {"role": "user", "content": intro_prompt}
256
+ ],
257
+ max_tokens=1024,
258
+ temperature=0.7
259
+ )
260
+ return intro_content.choices[0].message.content
261
+
262
+ def generate_table_of_contents(book_details):
263
+ toc = f"# Table of Contents\n\nIntroduction\n\n"
264
+ for i, chapter in enumerate(book_details['chapters'], 1):
265
+ toc += f"{i}. {chapter['name']}\n"
266
+ return toc
267
+
268
+ def save_book_formats(content, title):
269
+ # Save as TXT
270
+ with open(f"{title}.txt", "w", encoding="utf-8") as f:
271
+ f.write(content)
272
+
273
+ # Save as DOCX
274
+ doc = Document()
275
+ doc.add_heading(title, 0)
276
+ for paragraph in content.split('\n'):
277
+ doc.add_paragraph(paragraph)
278
+ doc.save(f"{title}.docx")
279
+
280
+ # Save as PDF using reportlab
281
+ pdf_filename = f"{title}.pdf"
282
+ doc = SimpleDocTemplate(pdf_filename, pagesize=letter)
283
+ styles = getSampleStyleSheet()
284
+ flowables = []
285
+
286
+ for paragraph in content.split('\n'):
287
+ p = Paragraph(paragraph, styles['Normal'])
288
+ flowables.append(p)
289
+
290
+ doc.build(flowables)
291
+
292
+ def extract_text_from_file(file):
293
+ file_extension = os.path.splitext(file.name)[1].lower()
294
+
295
+ if file_extension == '.pdf':
296
+ pdf_reader = PyPDF2.PdfReader(file)
297
+ text = ""
298
+ for page in pdf_reader.pages:
299
+ text += page.extract_text()
300
+ elif file_extension == '.txt':
301
+ text = file.getvalue().decode('utf-8')
302
+ elif file_extension in ['.doc', '.docx']:
303
+ text = docx2txt.process(file)
304
+ else:
305
+ raise ValueError("Unsupported file format")
306
+
307
+ return text
308
+
309
+ # Character Arc Visualizer
310
+ def character_arc_visualizer():
311
+ st.header("Character Arc Visualizer")
312
+ st.write("Visualize character development throughout your story.")
313
+
314
+ if 'character_arcs' not in st.session_state:
315
+ st.session_state.character_arcs = {}
316
+
317
+ # Input for character arc data
318
+ character_name = st.text_input("Enter character name")
319
+ arc_points = st.text_area("Enter arc points (format: Chapter,Development Level)")
320
+
321
+ if st.button("Add Character Arc"):
322
+ if character_name and arc_points:
323
+ arc_data = [tuple(point.split(',')) for point in arc_points.split('\n') if point.strip()]
324
+ st.session_state.character_arcs[character_name] = arc_data
325
+ st.success(f"Arc for {character_name} added successfully!")
326
+
327
+ # Visualize character arcs
328
+ if st.session_state.character_arcs:
329
+ st.subheader("Character Arcs")
330
+ fig = go.Figure()
331
+
332
+ for character, arc in st.session_state.character_arcs.items():
333
+ chapters, levels = zip(*arc)
334
+ fig.add_trace(go.Scatter(x=chapters, y=levels, mode='lines+markers', name=character))
335
+
336
+ fig.update_layout(title="Character Development Arcs",
337
+ xaxis_title="Story Progression",
338
+ yaxis_title="Character Development Level")
339
+ st.plotly_chart(fig)
340
+
341
+ # World-Building Assistant
342
+ def world_building_assistant():
343
+ st.header("World-Building Assistant")
344
+ st.write("Develop and maintain consistent rules, cultures, and environments for your fictional world with AI assistance.")
345
+
346
+ if 'world_elements' not in st.session_state:
347
+ st.session_state.world_elements = {}
348
+
349
+ # Input for world elements
350
+ element_category = st.selectbox("Element Category", ["Geography", "Culture", "Magic System", "Technology", "History", "Politics"])
351
+
352
+ # Custom inputs for each category
353
+ if element_category == "Geography":
354
+ element_name = st.text_input("Location Name")
355
+ climate = st.selectbox("Climate", ["Tropical", "Temperate", "Arctic", "Desert", "Mediterranean"])
356
+ terrain = st.multiselect("Terrain Features", ["Mountains", "Forests", "Rivers", "Oceans", "Plains", "Islands"])
357
+ prompt = f"Create a detailed description for a {climate} {', '.join(terrain)} region named {element_name}."
358
+
359
+ elif element_category == "Culture":
360
+ element_name = st.text_input("Culture Name")
361
+ social_structure = st.selectbox("Social Structure", ["Hierarchical", "Egalitarian", "Clan-based", "Caste System"])
362
+ values = st.multiselect("Core Values", ["Honor", "Knowledge", "Nature", "Technology", "Spirituality", "Warfare"])
363
+ prompt = f"Describe the {social_structure} culture of {element_name}, emphasizing their focus on {', '.join(values)}."
364
+
365
+ elif element_category == "Magic System":
366
+ element_name = st.text_input("Magic System Name")
367
+ source = st.selectbox("Source of Magic", ["Natural Elements", "Divine", "Inner Energy", "Artifacts", "Otherworldly"])
368
+ limitations = st.multiselect("Limitations", ["Physical toll", "Rare resources", "Specific bloodline", "Years of study", "Unpredictable"])
369
+ prompt = f"Detail the {source}-based magic system called {element_name}, including its {', '.join(limitations)} as limitations."
370
+
371
+ elif element_category == "Technology":
372
+ element_name = st.text_input("Technology Name")
373
+ tech_level = st.slider("Technology Level", 1, 10, 5)
374
+ focus = st.multiselect("Technological Focus", ["Energy", "Transportation", "Communication", "Warfare", "Medicine", "AI"])
375
+ prompt = f"Describe the level {tech_level} technology {element_name}, focusing on advancements in {', '.join(focus)}."
376
+
377
+ elif element_category == "History":
378
+ element_name = st.text_input("Historical Event/Era Name")
379
+ time_frame = st.selectbox("Time Frame", ["Ancient", "Medieval", "Renaissance", "Industrial", "Modern", "Futuristic"])
380
+ event_type = st.selectbox("Event Type", ["War", "Discovery", "Cultural Revolution", "Natural Disaster", "Technological Breakthrough"])
381
+ prompt = f"Narrate the {time_frame} {event_type} known as {element_name} and its impact on the world."
382
+
383
+ elif element_category == "Politics":
384
+ element_name = st.text_input("Political System/Faction Name")
385
+ gov_type = st.selectbox("Government Type", ["Monarchy", "Democracy", "Oligarchy", "Theocracy", "Anarchy"])
386
+ key_issues = st.multiselect("Key Political Issues", ["Resource scarcity", "Technological regulation", "Civil rights", "Environmental concerns", "Foreign relations"])
387
+ prompt = f"Explain the {gov_type} political system of {element_name}, addressing their stance on {', '.join(key_issues)}."
388
+
389
+ if st.button("Generate World Element"):
390
+ if element_name:
391
+ with st.spinner("Generating world element..."):
392
+ response = together_client.chat.completions.create(
393
+ model="meta-llama/Meta-Llama-3.1-8B-Instruct-Turbo",
394
+ messages=[
395
+ {"role": "system", "content": "You are an expert world-building assistant for fiction writers."},
396
+ {"role": "user", "content": prompt}
397
+ ],
398
+ max_tokens=5000,
399
+ temperature=0.7
400
+ )
401
+ element_description = response.choices[0].message.content
402
+
403
+ if element_category not in st.session_state.world_elements:
404
+ st.session_state.world_elements[element_category] = {}
405
+ st.session_state.world_elements[element_category][element_name] = element_description
406
+ st.success(f"{element_name} added to {element_category} successfully!")
407
+
408
+ # Display world elements
409
+ if st.session_state.world_elements:
410
+ st.subheader("Your World")
411
+ for category, elements in st.session_state.world_elements.items():
412
+ st.write(f"**{category}**")
413
+ for name, description in elements.items():
414
+ with st.expander(name):
415
+ st.write(description)
416
+ if st.button(f"Regenerate {name}", key=f"regen_{category}_{name}"):
417
+ with st.spinner(f"Regenerating {name}..."):
418
+ response = together_client.chat.completions.create(
419
+ model="meta-llama/Meta-Llama-3.1-8B-Instruct-Turbo",
420
+ messages=[
421
+ {"role": "system", "content": "You are an expert world-building assistant for fiction writers."},
422
+ {"role": "user", "content": f"Rewrite and improve the following world element description for {category}: {description}"}
423
+ ],
424
+ max_tokens=500,
425
+ temperature=0.7
426
+ )
427
+ new_description = response.choices[0].message.content
428
+ st.session_state.world_elements[category][name] = new_description
429
+ st.experimental_rerun()
430
+
431
+ # AI-powered world consistency check
432
+ if st.button("Check World Consistency"):
433
+ if st.session_state.world_elements:
434
+ all_elements = "\n".join([f"{cat}: {name} - {desc}" for cat, elements in st.session_state.world_elements.items() for name, desc in elements.items()])
435
+ with st.spinner("Analyzing world consistency..."):
436
+ response = together_client.chat.completions.create(
437
+ model="meta-llama/Meta-Llama-3.1-8B-Instruct-Turbo",
438
+ messages=[
439
+ {"role": "system", "content": "You are an expert world-building consultant for fiction writers."},
440
+ {"role": "user", "content": f"Analyze the following world elements for consistency and provide suggestions for improvement:\n\n{all_elements}"}
441
+ ],
442
+ max_tokens=1000,
443
+ temperature=0.7
444
+ )
445
+ consistency_analysis = response.choices[0].message.content
446
+ st.subheader("World Consistency Analysis")
447
+ st.write(consistency_analysis)
448
+ else:
449
+ st.warning("Add some world elements before checking consistency.")
450
+
451
+ # Export world-building data
452
+ if st.session_state.world_elements:
453
+ if st.button("Export World Data"):
454
+ export_data = {category: {name: desc for name, desc in elements.items()} for category, elements in st.session_state.world_elements.items()}
455
+ st.download_button(
456
+ label="Download World Data as JSON",
457
+ data=json.dumps(export_data, indent=2),
458
+ file_name="world_building_data.json",
459
+ mime="application/json"
460
+ )
461
+
462
+ def get_audio_data():
463
+ CHUNK = 1024
464
+ FORMAT = pyaudio.paInt16
465
+ CHANNELS = 1
466
+ RATE = 44100
467
+ RECORD_SECONDS = 0.1
468
+
469
+ p = pyaudio.PyAudio()
470
+
471
+ stream = p.open(format=FORMAT,
472
+ channels=CHANNELS,
473
+ rate=RATE,
474
+ input=True,
475
+ frames_per_buffer=CHUNK)
476
+
477
+ frames = []
478
+
479
+ for i in range(0, int(RATE / CHUNK * RECORD_SECONDS)):
480
+ data = stream.read(CHUNK)
481
+ frames.append(np.frombuffer(data, dtype=np.int16))
482
+
483
+ stream.stop_stream()
484
+ stream.close()
485
+ p.terminate()
486
+
487
+ return np.concatenate(frames)
488
+
489
+ def update_audio_chart(chart, audio_data):
490
+ chart.plotly_chart(go.Figure(data=go.Scatter(y=audio_data, mode='lines')), use_container_width=True)
491
+
492
+ def create_radar_chart(stats):
493
+ df = pd.DataFrame(dict(
494
+ r=list(stats.values()),
495
+ theta=list(stats.keys())
496
+ ))
497
+ fig = px.line_polar(df, r='r', theta='theta', line_close=True)
498
+ fig.update_traces(fill='toself')
499
+ fig.update_layout(
500
+ polar=dict(
501
+ radialaxis=dict(visible=True, range=[0, 10])
502
+ ),
503
+ showlegend=False
504
+ )
505
+ return fig
506
+
507
+ def main():
508
+ st.set_page_config(page_title="AUDSOL - AI Automation for Writers", page_icon="📚", layout="wide")
509
+
510
+ menu = ["Home", "Create Character", "Chat with Characters", "Generate Book", "Convert to Audiobook",
511
+ "Book Outline Generator", "Character Development Workshop", "Writing Prompts Generator", "Character Arc Visualizer", "World-Building Assistant", "Interactive Character Board"]
512
+ choice = st.sidebar.selectbox("Menu", menu)
513
+
514
+ if choice == "Home":
515
+ st.title("AUDSOL - AI Automation for Writers")
516
+ st.write("Welcome to AUDSOL, your AI-powered writing assistant!")
517
+
518
+ st.header("Unlock Your Writing Potential with AI")
519
+ st.write("""
520
+ AUDSOL is designed to revolutionize your writing process and help you generate thousands of dollars per month through KDP self-publishing. Our AI-powered tools streamline your workflow, boost creativity, and enhance your productivity.
521
+ """)
522
+
523
+ st.subheader("Key Features:")
524
+ col1, col2 = st.columns(2)
525
+
526
+ with col1:
527
+ st.markdown("""
528
+ - **AI Character Creation**: Bring your characters to life with unique voices and personalities.
529
+ - **Interactive Character Chats**: Develop your characters through dynamic conversations.
530
+ - **Automated Book Generation**: Create full-length books with AI assistance.
531
+ - **Audiobook Conversion**: Transform your text into professional audiobooks.
532
+ """)
533
+
534
+ with col2:
535
+ st.markdown("""
536
+ - **Book Outline Generator**: Craft detailed outlines for your next bestseller.
537
+ - **Character Development Workshop**: Deep dive into character creation and evolution.
538
+ - **Writing Prompts Generator**: Spark your creativity with AI-generated prompts.
539
+ - **Multi-format Export**: Save your work in TXT, DOCX, and PDF formats.
540
+ """)
541
+
542
+ st.header("How AUDSOL Boosts Your KDP Earnings")
543
+ st.write("""
544
+ 1. **Rapid Content Creation**: Generate high-quality books faster than ever before.
545
+ 2. **Diverse Genre Expertise**: Our AI adapts to any genre, helping you tap into lucrative markets.
546
+ 3. **Consistent Output**: Maintain a steady publishing schedule to build your author brand.
547
+ 4. **Enhanced Quality**: AI-assisted editing and character development improve your storytelling.
548
+ 5. **Audiobook Integration**: Easily create audiobooks to diversify your income streams.
549
+ """)
550
+
551
+ st.header("Get Started Today!")
552
+ st.write("""
553
+ Explore our features using the sidebar menu and start transforming your writing career. With AUDSOL, you're not just writing—you're crafting your path to financial success in the world of self-publishing.
554
+ """)
555
+
556
+ elif choice == "Create Character":
557
+ st.header("Create a New Character")
558
+ col1, col2 = st.columns(2)
559
+
560
+ with col1:
561
+ new_name = st.text_input("Character Name")
562
+ new_description = st.text_area("Short Character Description (500 chars)", max_chars=500)
563
+ new_voice_file = st.file_uploader("Upload Voice Sample (MP3)", type=["mp3"])
564
+
565
+ with col2:
566
+ new_detailed_description = st.text_area("Detailed Character Description")
567
+ new_image_file = st.file_uploader("Upload Character Image (Optional)", type=["jpg", "png"])
568
+
569
+ if st.button("Create Character") and new_name and new_description and new_voice_file:
570
+ with st.spinner("Creating character..."):
571
+ with tempfile.NamedTemporaryFile(delete=False, suffix=".mp3") as tmp_file:
572
+ tmp_file.write(new_voice_file.getvalue())
573
+ tmp_file_path = tmp_file.name
574
+ create_character(new_name, new_description, tmp_file_path, new_image_file)
575
+ os.unlink(tmp_file_path)
576
+ st.success(f"Character '{new_name}' created successfully!")
577
+
578
+ elif choice == "Chat with Characters":
579
+ st.header("Chat with Characters")
580
+
581
+ if 'messages' not in st.session_state:
582
+ st.session_state.messages = []
583
+
584
+ if 'audio_transcription' not in st.session_state:
585
+ st.session_state.audio_transcription = None
586
+
587
+ if 'voice_input_stage' not in st.session_state:
588
+ st.session_state.voice_input_stage = 'ready'
589
+
590
+ if 'audio_bytes' not in st.session_state:
591
+ st.session_state.audio_bytes = None
592
+
593
+ col1, col2 = st.columns([1, 3])
594
+
595
+ with col1:
596
+ st.subheader("Characters")
597
+ for character in st.session_state.characters:
598
+ if st.button(character, key=f"btn_{character}"):
599
+ st.session_state.current_character = character
600
+ st.session_state.messages = []
601
+ st.session_state.audio_transcription = None
602
+ st.session_state.voice_input_stage = 'ready'
603
+ st.session_state.audio_bytes = None
604
+
605
+ with col2:
606
+ if 'current_character' in st.session_state and st.session_state.current_character in st.session_state.characters:
607
+ current_char = st.session_state.characters[st.session_state.current_character]
608
+ st.subheader(f"Chatting with {st.session_state.current_character}")
609
+ if "image" in current_char and current_char["image"]:
610
+ st.image(current_char["image"], width=200)
611
+
612
+ # Display chat history
613
+ for message in st.session_state.messages:
614
+ with st.chat_message(message["role"]):
615
+ st.write(message["content"])
616
+ if "audio" in message:
617
+ st.audio(message["audio"])
618
+
619
+ language = st.selectbox("Select Language", ["English", "Spanish", "French", "German", "Chinese", "Japanese"])
620
+ input_method = st.radio("Choose input method:", ("Text", "Voice"))
621
+
622
+ user_input = None
623
+ if input_method == "Text":
624
+ user_input = st.chat_input("Type your message here...")
625
+ else:
626
+ st.write("Voice Input Process:")
627
+
628
+ if st.session_state.voice_input_stage == 'ready':
629
+ if st.button("Start Recording"):
630
+ st.session_state.voice_input_stage = 'recording'
631
+ st.experimental_rerun()
632
+
633
+ elif st.session_state.voice_input_stage == 'recording':
634
+ st.write("Recording... Click 'Stop Recording' when finished.")
635
+ audio_bytes = audio_recorder(key="voice_recorder", icon_size="2x")
636
+ if audio_bytes:
637
+ st.session_state.audio_bytes = audio_bytes
638
+ if st.button("Stop Recording"):
639
+ if st.session_state.audio_bytes:
640
+ st.session_state.voice_input_stage = 'transcribing'
641
+ st.experimental_rerun()
642
+ else:
643
+ st.warning("No audio recorded. Please try again.")
644
+ st.session_state.voice_input_stage = 'ready'
645
+
646
+ elif st.session_state.voice_input_stage == 'transcribing':
647
+ st.write("Transcribing audio...")
648
+ with st.spinner("Processing..."):
649
+ if st.session_state.audio_bytes:
650
+ with tempfile.NamedTemporaryFile(delete=False, suffix=".wav") as tmp_file:
651
+ tmp_file.write(st.session_state.audio_bytes)
652
+ tmp_file_path = tmp_file.name
653
+
654
+ recognizer = sr.Recognizer()
655
+ with sr.AudioFile(tmp_file_path) as source:
656
+ audio_data = recognizer.record(source)
657
+
658
+ try:
659
+ transcription = recognizer.recognize_google(audio_data)
660
+ st.session_state.audio_transcription = transcription
661
+ user_input = transcription
662
+ except sr.UnknownValueError:
663
+ st.warning("Speech recognition could not understand the audio. Please try again.")
664
+ st.session_state.voice_input_stage = 'ready'
665
+ except sr.RequestError as e:
666
+ st.error(f"Could not request results from speech recognition service. Please check your internet connection and try again. Error: {e}")
667
+ st.session_state.voice_input_stage = 'ready'
668
+
669
+ os.unlink(tmp_file_path)
670
+ else:
671
+ st.error("No audio data available. Please record your message again.")
672
+ st.session_state.voice_input_stage = 'ready'
673
+
674
+ if user_input:
675
+ # Add user message to chat history
676
+ st.session_state.messages.append({"role": "user", "content": user_input})
677
+
678
+ # Display user message
679
+ with st.chat_message("user"):
680
+ st.write(user_input)
681
+
682
+ with st.spinner("Generating response..."):
683
+ ai_response = generate_ai_response(st.session_state.current_character, user_input, language)
684
+ audio = text_to_speech(ai_response, current_char["voice_id"])
685
+
686
+ with tempfile.NamedTemporaryFile(delete=False, suffix=".mp3") as tmp_file:
687
+ save(audio, tmp_file.name)
688
+ audio_path = tmp_file.name
689
+
690
+ # Add AI response to chat history
691
+ st.session_state.messages.append({
692
+ "role": "assistant",
693
+ "content": ai_response,
694
+ "audio": audio_path
695
+ })
696
+
697
+ # Display AI response
698
+ with st.chat_message("assistant"):
699
+ st.write(ai_response)
700
+ st.audio(audio_path)
701
+
702
+ # Reset for next interaction
703
+ st.session_state.audio_transcription = None
704
+ st.session_state.audio_bytes = None
705
+ st.session_state.voice_input_stage = 'ready'
706
+ st.experimental_rerun()
707
+
708
+ else:
709
+ st.info("Please select a character to start chatting.")
710
+
711
+ elif choice == "Generate Book":
712
+ st.header("Generate a Book")
713
+
714
+ genre_type = st.radio("Choose genre type:", ("Fiction", "Non-fiction"))
715
+
716
+ if genre_type == "Fiction":
717
+ genre = st.selectbox("Select Fiction Genre", fiction_genres)
718
+ else:
719
+ genre = st.selectbox("Select Non-fiction Genre", non_fiction_genres)
720
+
721
+ title = st.text_input("Book Title")
722
+ author = st.text_input("Author Name")
723
+ description = st.text_area("Book Description")
724
+
725
+ num_chapters = st.number_input("Number of Chapters", min_value=1, value=5)
726
+
727
+ chapters = []
728
+ for i in range(num_chapters):
729
+ st.subheader(f"Chapter {i+1}")
730
+ chapter_name = st.text_input(f"Chapter {i+1} Name")
731
+ if genre_type == "Fiction":
732
+ num_scenes = st.number_input(f"Number of Scenes in Chapter {i+1}", min_value=1, value=3)
733
+ scenes = []
734
+ for j in range(num_scenes):
735
+ scene_description = st.text_area(f"Scene {j+1} Description (Chapter {i+1})")
736
+ scenes.append(scene_description)
737
+ chapters.append({"name": chapter_name, "scenes": scenes})
738
+ else:
739
+ chapter_description = st.text_area(f"Chapter {i+1} Description")
740
+ num_parts = st.number_input(f"Number of Parts in Chapter {i+1}", min_value=1, value=3)
741
+ parts = []
742
+ for j in range(num_parts):
743
+ part_description = st.text_area(f"Part {j+1} Description (Chapter {i+1})")
744
+ parts.append(part_description)
745
+ chapters.append({"name": chapter_name, "description": chapter_description, "parts": parts})
746
+
747
+ if st.button("Generate Book"):
748
+ book_details = {
749
+ "genre_type": genre_type,
750
+ "genre": genre,
751
+ "title": title,
752
+ "author": author,
753
+ "description": description,
754
+ "chapters": chapters
755
+ }
756
+
757
+ st.session_state.book_details = book_details
758
+ st.session_state.generated_book = ""
759
+
760
+ with st.spinner("Generating book introduction..."):
761
+ introduction = generate_book_introduction(book_details)
762
+ st.session_state.generated_book += f"# {title}\nBy {author}\n\n{introduction}\n\n"
763
+
764
+ toc = generate_table_of_contents(book_details)
765
+ st.session_state.generated_book += f"{toc}\n\n"
766
+
767
+ for i, chapter in enumerate(chapters):
768
+ with st.spinner(f"Generating Chapter {i+1}: {chapter['name']}..."):
769
+ chapter_content = generate_book_chapter(book_details, i)
770
+ st.session_state.generated_book += f"# Chapter {i+1}: {chapter['name']}\n\n{chapter_content}\n\n"
771
+ st.success(f"Chapter {i+1} generated successfully!")
772
+
773
+ st.success("Book generated successfully!")
774
+ st.text_area("Generated Book", st.session_state.generated_book, height=300)
775
+
776
+ save_book_formats(st.session_state.generated_book, title)
777
+
778
+ st.download_button(
779
+ label="Download as TXT",
780
+ data=st.session_state.generated_book,
781
+ file_name=f"{title}.txt",
782
+ mime="text/plain"
783
+ )
784
+
785
+ with open(f"{title}.docx", "rb") as docx_file:
786
+ st.download_button(
787
+ label="Download as DOCX",
788
+ data=docx_file,
789
+ file_name=f"{title}.docx",
790
+ mime="application/vnd.openxmlformats-officedocument.wordprocessingml.document"
791
+ )
792
+
793
+ with open(f"{title}.pdf", "rb") as pdf_file:
794
+ st.download_button(
795
+ label="Download as PDF",
796
+ data=pdf_file,
797
+ file_name=f"{title}.pdf",
798
+ mime="application/pdf"
799
+ )
800
+
801
+ elif choice == "Convert to Audiobook":
802
+ st.header("Convert to Audiobook")
803
+ st.write("Transform your written work into an engaging audiobook.")
804
+
805
+ col1, col2 = st.columns([2, 1])
806
+
807
+ with col1:
808
+ upload_type = st.radio("Choose input method:", ("Upload File", "Paste Text"), horizontal=True)
809
+
810
+ if upload_type == "Upload File":
811
+ uploaded_file = st.file_uploader("Upload your book file", type=['txt', 'pdf', 'doc', 'docx'])
812
+ if uploaded_file:
813
+ with st.spinner("Extracting text from file..."):
814
+ text = extract_text_from_file(uploaded_file)
815
+ st.success("Text extracted successfully!")
816
+ else:
817
+ text = st.text_area("Enter your book text here", height=300, placeholder="Paste your book content here...")
818
+
819
+ if 'text' in locals() and text:
820
+ st.write("Preview:")
821
+ st.info(text[:500] + "..." if len(text) > 500 else text)
822
+
823
+ with col2:
824
+ st.subheader("Audiobook Settings")
825
+ language = st.selectbox("Language", ["English", "Spanish", "French", "German", "Chinese", "Japanese"])
826
+
827
+ voice_option = st.radio("Voice Option:", ("Predefined", "Clone Your Voice"))
828
+
829
+ if voice_option == "Predefined":
830
+ available_voices = [
831
+ ("21m00Tcm4TlvDq8ikWAM", "Rachel"),
832
+ ("2EiwWnXFnvU5JabPnv8n", "Domi"),
833
+ ("AZnzlk1XvdvUeBnXmlld", "Bella"),
834
+ ("EXAVITQu4vr4xnSDxMaL", "Antoni"),
835
+ ("ErXwobaYiN019PkySvjV", "Elli"),
836
+ ("MF3mGyEYCl7XYWbV9V6O", "Adam"),
837
+ ("TxGEqnHWrfWFTfGW9XjX", "Sam"),
838
+ ("VR6AewLTigWG4xSOukaG", "Josh"),
839
+ ("pNInz6obpgDQGcFmaJgB", "Arnold"),
840
+ ("yoZ06aMxZJJ28mfd3POQ", "Emma"),
841
+ ("ZQe5CZNOzWyzPSCn5a3c", "Ava"),
842
+ ("jBpfuIE2acCO8z3wKNLl", "Bella"),
843
+ ("onwK4e9ZLuTAKqWW03F9", "Dorothy"),
844
+ ("g5CIjZEefAph4nQFvHAz", "Josh"),
845
+ ("wViXBPUzp2ZZixB1xQuM", "Arnold"),
846
+ ("zrHiDhphv9ZnVXBqCLjz", "Charlotte"),
847
+ ("t0jbNlBVZ17f02VDIeMI", "Matilda"),
848
+ ("piTKgcLEGmPE4e6mEKli", "Matthew"),
849
+ ("RErgWrLnpU47n1wBUr3G", "James"),
850
+ ("N2lVS1w4EtoT3dr4eOWO", "Joseph"),
851
+ ("ODq5zmih8GrVes37Dizd", "Jeremy"),
852
+ ("SOYHLrjzK2X1ezoPC6cr", "Michael"),
853
+ ("TX3LPaxmHKxFdv7VOQHJ", "Robert"),
854
+ ("flq6f7yk4E4fJM5XTYNq", "Olivia"),
855
+ ("z9fAnlkpzviPz146aGWa", "Sophia"),
856
+ ("3KehXGbKd6Gu5T9fjnVK", "Ethan"),
857
+ ("IKne3meq5aSn9XLyUdCD", "Charlie"),
858
+ ("LcfcDJNUP1GQjkzn1xUU", "Amelia"),
859
+ ("G4i3RVLNXz6PiE5QAgCu", "Lily"),
860
+ ("XB0fDUnXU5powFXDhCwa", "Jackson"),
861
+ ("xVe4PmtsSGVPkKo5V1gB", "Chloe"),
862
+ ("YD5YTsM3TLX99VqWelFo", "Madison"),
863
+ ("jsCqWAovK2LkecY7zXl4", "Ryan"),
864
+ ("oWAxZDx7w5VEj9dCyTzz", "Luna"),
865
+ ("1vXVqeHAitMxEfPe88RV", "Maverick"),
866
+ ("2eGXxRfOxHPE1ZPCIzNv", "Zoe"),
867
+ ("tL2Ybu6R2XEfqotJ1UWp", "Thomas"),
868
+ ("XrExE9yKIg1WjnnlVkGX", "Scarlett"),
869
+ ("Yko7PKHZNXotIFUBG7I9", "Daniel"),
870
+ ("R2wc5f0yV8dVe4Y24eDC", "Hannah"),
871
+ ("qNAGtHKepPLQO6GXVImS", "Liam"),
872
+ ("h0ZXKKEOGPMlAb7yFQf0", "Isabella"),
873
+ ("Jb5FM5xWS8mhCk0DHjmx", "Grace"),
874
+ ("pOnD5fLH3IhEBY5YOHP4", "Noah"),
875
+ ("r5U4RGJuA4kh2Umi8P3J", "Oliver"),
876
+ ("x8GtTQIVJ5dxmYN4vffj", "Emily"),
877
+ ("j9FdgHYQX3nG7qjc9VVG", "Sophie"),
878
+ ("C8lN8YDucvTvl6yKiokH", "William"),
879
+ ("JM6niiVGNJTotuzPD5kd", "Evelyn"),
880
+ ("7JrRqOwEPGAznN7Uf8hU", "Benjamin")
881
+ ]
882
+
883
+ selected_voice = st.selectbox(
884
+ "Select a voice",
885
+ options=available_voices,
886
+ format_func=lambda x: f"{x[1]} (ID: {x[0]})"
887
+ )
888
+
889
+ if selected_voice:
890
+ voice_id, voice_name = selected_voice
891
+ st.write(f"Selected Voice: {voice_name}")
892
+ st.write(f"Voice ID: {voice_id}")
893
+ else:
894
+ voice_sample = st.file_uploader("Upload your voice sample (MP3)", type=["mp3"])
895
+ if voice_sample:
896
+ with st.spinner("Cloning voice..."):
897
+ with tempfile.NamedTemporaryFile(delete=False, suffix=".mp3") as tmp_file:
898
+ tmp_file.write(voice_sample.getvalue())
899
+ tmp_file_path = tmp_file.name
900
+
901
+ cloned_voice = elevenlabs_client.clone(
902
+ name="Custom Voice",
903
+ description="Custom cloned voice",
904
+ files=[tmp_file_path]
905
+ )
906
+ # No need to unlink here; we will do it later
907
+ voice_id = cloned_voice.voice_id
908
+ st.success("Voice cloned successfully!")
909
+
910
+ speed = st.slider("Narration Speed", min_value=0.5, max_value=2.0, value=1.0, step=0.1)
911
+
912
+ if st.button("Convert to Audiobook", type="primary"):
913
+ if 'text' in locals() and text and 'voice_id' in locals():
914
+ chunks = split_text(text)
915
+ total_chunks = len(chunks)
916
+
917
+ headers = {
918
+ "Accept": "audio/mpeg",
919
+ "Content-Type": "application/json",
920
+ "xi-api-key": XI_API_KEY
921
+ }
922
+
923
+ all_audio_data = BytesIO()
924
+
925
+ progress_bar = st.progress(0)
926
+ status_text = st.empty()
927
+
928
+ for i, chunk in enumerate(chunks, 1):
929
+ status_text.text(f"Converting chunk {i}/{total_chunks}...")
930
+ try:
931
+ audio_content = generate_audio_chunk(chunk, voice_id, headers)
932
+ all_audio_data.write(audio_content)
933
+ progress_bar.progress(i / total_chunks)
934
+ except Exception as e:
935
+ st.error(f"Error processing chunk {i}: {str(e)}")
936
+ break
937
+
938
+ if all_audio_data.tell() > 0:
939
+ all_audio_data.seek(0)
940
+ st.success("Audiobook created successfully!")
941
+ st.audio(all_audio_data, format='audio/mp3')
942
+
943
+ st.download_button(
944
+ label="Download Complete Audiobook",
945
+ data=all_audio_data,
946
+ file_name="complete_audiobook.mp3",
947
+ mime="audio/mpeg",
948
+ key="download_complete_audiobook"
949
+ )
950
+ else:
951
+ st.error("Failed to generate the complete audiobook.")
952
+ else:
953
+ st.warning("Please provide text and select a voice before converting.")
954
+
955
+ # New feature: Book Outline Generator
956
+ elif choice == "Book Outline Generator":
957
+ st.header("Detailed Book Outline Generator")
958
+ st.write("Create a comprehensive, chapter-by-chapter outline for your next bestseller.")
959
+
960
+ col1, col2 = st.columns([2, 1])
961
+
962
+ with col1:
963
+ title = st.text_input("Book Title", placeholder="Enter your book title")
964
+ main_idea = st.text_area("Main Idea or Concept", placeholder="Describe the central theme or concept of your book in detail", height=150)
965
+ target_audience = st.text_input("Target Audience", placeholder="Who is your book for? Be specific about demographics, interests, etc.")
966
+ key_themes = st.text_area("Key Themes or Topics", placeholder="List the main themes or topics you want to cover in your book", height=100)
967
+
968
+ with col2:
969
+ genre_type = st.radio("Genre Type:", ("Fiction", "Non-fiction"))
970
+ if genre_type == "Fiction":
971
+ genre = st.selectbox("Fiction Genre", fiction_genres)
972
+ protagonist = st.text_input("Protagonist", placeholder="Describe your main character")
973
+ setting = st.text_input("Setting", placeholder="Where and when does your story take place?")
974
+ else:
975
+ genre = st.selectbox("Non-fiction Genre", non_fiction_genres)
976
+ expertise_level = st.select_slider("Reader's Expertise Level", options=["Beginner", "Intermediate", "Advanced", "Expert"])
977
+
978
+ desired_length = st.number_input("Estimated Word Count", min_value=10000, value=80000, step=5000, help="Approximate length of your book")
979
+ num_chapters = st.number_input("Number of Chapters", min_value=5, max_value=50, value=15)
980
+
981
+ if st.button("Generate Detailed Outline", type="primary"):
982
+ if title and main_idea and target_audience and genre and key_themes:
983
+ with st.spinner("Crafting your comprehensive book outline..."):
984
+ if genre_type == "Fiction":
985
+ outline_prompt = f"""Generate an extremely detailed book outline for a {genre} novel titled '{title}'.
986
+ Main idea: {main_idea}
987
+ Target audience: {target_audience}
988
+ Key themes: {key_themes}
989
+ Protagonist: {protagonist}
990
+ Setting: {setting}
991
+ Estimated length: {desired_length} words
992
+ Number of chapters: {num_chapters}
993
+
994
+ For each chapter, provide:
995
+ 1. A compelling chapter title
996
+ 2. A detailed synopsis (200-300 words)
997
+ 3. Key plot points or events
998
+ 4. Character development and interactions
999
+ 5. Setting details and atmosphere
1000
+ 6. Themes explored in the chapter
1001
+ 7. Any foreshadowing or plot twists
1002
+ 8. Estimated word count for the chapter
1003
+
1004
+ Additionally, include:
1005
+ - An engaging prologue idea
1006
+ - A captivating epilogue concept
1007
+ - Suggestions for potential subplots
1008
+ - Ideas for symbolic elements or motifs throughout the book
1009
+
1010
+ Make the outline as comprehensive and detailed as possible, using the maximum available tokens."""
1011
+ else:
1012
+ outline_prompt = f"""Generate an extremely detailed book outline for a {genre} non-fiction book titled '{title}'.
1013
+ Main idea: {main_idea}
1014
+ Target audience: {target_audience} (Expertise level: {expertise_level})
1015
+ Key themes or topics: {key_themes}
1016
+ Estimated length: {desired_length} words
1017
+ Number of chapters: {num_chapters}
1018
+
1019
+ For each chapter, provide:
1020
+ 1. An informative chapter title
1021
+ 2. A detailed chapter summary (200-300 words)
1022
+ 3. Main concepts or arguments presented
1023
+ 4. Supporting evidence, data, or examples to include
1024
+ 5. Potential expert quotes or case studies to research
1025
+ 6. Practical applications or exercises for readers
1026
+ 7. Key takeaways from the chapter
1027
+ 8. Estimated word count for the chapter
1028
+
1029
+ Additionally, include:
1030
+ - An attention-grabbing introduction outline
1031
+ - A powerful conclusion and call-to-action outline
1032
+ - Ideas for sidebars, infographics, or illustrations
1033
+ - Suggestions for further reading or resources
1034
+
1035
+ Make the outline as comprehensive and detailed as possible, using the maximum available tokens."""
1036
+
1037
+ outline = together_client.chat.completions.create(
1038
+ model="meta-llama/Meta-Llama-3.1-8B-Instruct-Turbo",
1039
+ messages=[
1040
+ {"role": "system", "content": "You are a professional book outliner and developmental editor with extensive experience in creating detailed, chapter-by-chapter outlines for bestselling books across various genres."},
1041
+ {"role": "user", "content": outline_prompt}
1042
+ ],
1043
+ max_tokens=30000, # Increased to maximum allowed
1044
+ temperature=0.7
1045
+ )
1046
+
1047
+ st.session_state.book_outline = outline.choices[0].message.content
1048
+
1049
+ st.success("Comprehensive book outline generated successfully!")
1050
+
1051
+ # Display the outline in a structured and visually appealing way
1052
+ st.subheader("Your Detailed Book Outline")
1053
+ outline_lines = st.session_state.book_outline.split('\n')
1054
+ chapter_count = 0
1055
+ for line in outline_lines:
1056
+ if line.strip().startswith('Chapter'):
1057
+ chapter_count += 1
1058
+ st.markdown(f"<h3 style='color: #1e90ff;'>{line.strip()}</h3>", unsafe_allow_html=True)
1059
+ elif any(section in line for section in ['Synopsis:', 'Summary:', 'Plot Points:', 'Main Concepts:', 'Character Development:', 'Supporting Evidence:', 'Setting:', 'Practical Applications:', 'Themes:', 'Key Takeaways:', 'Foreshadowing:', 'Estimated Word Count:']):
1060
+ st.markdown(f"<h4 style='color: #32cd32;'>{line.strip()}</h4>", unsafe_allow_html=True)
1061
+ else:
1062
+ st.write(line.strip())
1063
+
1064
+ st.info(f"Total Chapters: {chapter_count}")
1065
+
1066
+ st.download_button(
1067
+ label="Download Detailed Outline",
1068
+ data=st.session_state.book_outline,
1069
+ file_name=f"{title.replace(' ', '_').lower()}_detailed_outline.txt",
1070
+ mime="text/plain",
1071
+ key="download_detailed_outline"
1072
+ )
1073
+ else:
1074
+ st.warning("Please fill in all the required fields to generate a detailed outline.")
1075
+
1076
+ # New feature: Character Development Workshop
1077
+ elif choice == "Character Development Workshop":
1078
+ st.header("Character Development Workshop")
1079
+ st.write("Bring your characters to life with our in-depth development tools.")
1080
+
1081
+ col1, col2 = st.columns([1, 1])
1082
+
1083
+ with col1:
1084
+ character_name = st.text_input("Character Name", placeholder="Enter character's name")
1085
+ character_role = st.selectbox("Character Role", ["Protagonist", "Antagonist", "Supporting Character", "Mentor", "Love Interest", "Sidekick"])
1086
+ character_age = st.number_input("Age", min_value=0, max_value=250, value=30)
1087
+ character_occupation = st.text_input("Occupation", placeholder="Character's job or main activity")
1088
+
1089
+ with col2:
1090
+ character_background = st.text_area("Background", placeholder="Brief history or backstory", height=100)
1091
+ character_goals = st.text_area("Goals", placeholder="What does the character want to achieve?", height=80)
1092
+ character_fears = st.text_area("Fears or Weaknesses", placeholder="What holds the character back?", height=80)
1093
+
1094
+ col3, col4 = st.columns([1, 1])
1095
+
1096
+ with col3:
1097
+ physical_attributes = st.text_area("Physical Attributes", placeholder="Describe appearance, mannerisms, etc.", height=100)
1098
+
1099
+ with col4:
1100
+ personality_traits = st.text_area("Personality Traits", placeholder="List key personality characteristics", height=100)
1101
+
1102
+ if st.button("Develop Character", type="primary"):
1103
+ if character_name and character_role and character_background:
1104
+ with st.spinner("Crafting your character profile..."):
1105
+ character_prompt = f"""
1106
+ Create a detailed character profile for {character_name}:
1107
+ - Age: {character_age}
1108
+ - Occupation: {character_occupation}
1109
+ - Role: {character_role}
1110
+ - Background: {character_background}
1111
+ - Goals: {character_goals}
1112
+ - Fears/Weaknesses: {character_fears}
1113
+ - Physical Attributes: {physical_attributes}
1114
+ - Personality Traits: {personality_traits}
1115
+
1116
+ Expand on these details to create a rich, multi-dimensional character. Include potential character arc, quirks, and how they might interact with other characters or drive the plot forward.
1117
+ """
1118
+
1119
+ character_profile = together_client.chat.completions.create(
1120
+ model="meta-llama/Meta-Llama-3.1-8B-Instruct-Turbo",
1121
+ messages=[
1122
+ {"role": "system", "content": "You are a professional character developer for novels and screenplays, skilled in creating complex, believable characters."},
1123
+ {"role": "user", "content": character_prompt}
1124
+ ],
1125
+ max_tokens=6500,
1126
+ temperature=0.7
1127
+ )
1128
+
1129
+ st.session_state.character_profile = character_profile.choices[0].message.content
1130
+
1131
+ st.success("Character profile developed successfully!")
1132
+
1133
+ # Display the character profile in a more structured way
1134
+ st.subheader(f"{character_name}'s Character Profile")
1135
+ profile_lines = st.session_state.character_profile.split('\n')
1136
+ for line in profile_lines:
1137
+ if ':' in line:
1138
+ key, value = line.split(':', 1)
1139
+ st.markdown(f"**{key.strip()}:** {value.strip()}")
1140
+ else:
1141
+ st.write(line.strip())
1142
+
1143
+ st.download_button(
1144
+ label="Download Character Profile",
1145
+ data=st.session_state.character_profile,
1146
+ file_name=f"{character_name.replace(' ', '_').lower()}_profile.txt",
1147
+ mime="text/plain",
1148
+ key="download_character_profile"
1149
+ )
1150
+ else:
1151
+ st.warning("Please fill in at least the character's name, role, and background to generate a profile.")
1152
+
1153
+ # New feature: Writing Prompts Generator
1154
+ elif choice == "Writing Prompts Generator":
1155
+ st.header("Writing Prompts Generator")
1156
+ st.write("Spark your creativity with custom writing prompts.")
1157
+
1158
+ col1, col2 = st.columns([1, 1])
1159
+
1160
+ with col1:
1161
+ prompt_type = st.selectbox("Prompt Type", ["General", "Sci-Fi", "Fantasy", "Romance", "Mystery", "Historical", "Horror", "Thriller"])
1162
+ prompt_length = st.slider("Prompt Complexity", min_value=1, max_value=5, value=3, help="1: Simple, 5: Elaborate")
1163
+
1164
+ with col2:
1165
+ specific_elements = st.multiselect("Include Specific Elements", ["Character", "Setting", "Conflict", "Theme", "Plot Twist"])
1166
+ writing_style = st.selectbox("Writing Style", ["Any", "Descriptive", "Dialogue-heavy", "Action-packed", "Introspective", "Humorous"])
1167
+
1168
+ mood = st.select_slider("Mood", options=["Dark", "Neutral", "Light"], value="Neutral")
1169
+
1170
+ if st.button("Generate Writing Prompt", type="primary"):
1171
+ with st.spinner("Crafting your writing prompt..."):
1172
+ prompt_request = f"""
1173
+ Generate a {prompt_type.lower()} writing prompt.
1174
+ Complexity: {prompt_length}/5
1175
+ Include these elements: {', '.join(specific_elements)}
1176
+ Writing style: {writing_style}
1177
+ Mood: {mood}
1178
+
1179
+ The prompt should inspire a short story or scene that a writer can immediately start working on.
1180
+ """
1181
+
1182
+ generated_prompt = together_client.chat.completions.create(
1183
+ model="meta-llama/Meta-Llama-3.1-8B-Instruct-Turbo",
1184
+ messages=[
1185
+ {"role": "system", "content": "You are a creative writing prompt generator, skilled in crafting inspiring and thought-provoking prompts for writers."},
1186
+ {"role": "user", "content": prompt_request}
1187
+ ],
1188
+ max_tokens=1024,
1189
+ temperature=0.9
1190
+ )
1191
+
1192
+ st.session_state.writing_prompt = generated_prompt.choices[0].message.content
1193
+
1194
+ st.success("Writing prompt generated successfully!")
1195
+
1196
+ # Display the writing prompt in an attractive format
1197
+ st.markdown("### Your Writing Prompt")
1198
+ st.info(st.session_state.writing_prompt)
1199
+
1200
+ col3, col4 = st.columns([1, 1])
1201
+ with col3:
1202
+ if st.button("Generate Another Prompt"):
1203
+ st.experimental_rerun()
1204
+ with col4:
1205
+ st.download_button(
1206
+ label="Save Prompt",
1207
+ data=st.session_state.writing_prompt,
1208
+ file_name="writing_prompt.txt",
1209
+ mime="text/plain",
1210
+ key="save_writing_prompt"
1211
+ )
1212
+
1213
+ elif choice == "Interactive Character Board":
1214
+ st.header("Interactive Character Board")
1215
+
1216
+ # Initialize session state variables
1217
+ if "character_board" not in st.session_state:
1218
+ st.session_state.character_board = []
1219
+ if "custom_stats" not in st.session_state:
1220
+ st.session_state.custom_stats = ["Intelligence", "Strength", "Speed", "Durability", "Energy Projection", "Fighting Skills"]
1221
+
1222
+ # Character creation form
1223
+ st.subheader("Add New Character")
1224
+ with st.form("character_form"):
1225
+ new_name = st.text_input("Character Name")
1226
+ new_description = st.text_area("Character Description", max_chars=1000)
1227
+ new_image = st.file_uploader("Character Image", type=["jpg", "png", "jpeg"])
1228
+
1229
+ st.subheader("Character Stats")
1230
+ stats = {}
1231
+ for stat in st.session_state.custom_stats:
1232
+ stats[stat] = st.slider(f"{stat}", 0, 10, 5)
1233
+
1234
+ background = st.text_area("Character Background")
1235
+ abilities = st.text_area("Special Abilities")
1236
+ weaknesses = st.text_area("Weaknesses")
1237
+ relationships = st.text_area("Relationships")
1238
+ additional_info = st.text_area("Additional Information")
1239
+
1240
+ submit_button = st.form_submit_button("Add Character")
1241
+
1242
+ if submit_button:
1243
+ if new_name and new_description:
1244
+ character_data = {
1245
+ "name": new_name,
1246
+ "description": new_description,
1247
+ "stats": stats,
1248
+ "background": background,
1249
+ "abilities": abilities,
1250
+ "weaknesses": weaknesses,
1251
+ "relationships": relationships,
1252
+ "additional_info": additional_info,
1253
+ }
1254
+
1255
+ if new_image:
1256
+ image = Image.open(new_image)
1257
+ img_byte_arr = io.BytesIO()
1258
+ image.save(img_byte_arr, format='PNG')
1259
+ character_data["image"] = img_byte_arr.getvalue()
1260
+
1261
+ st.session_state.character_board.append(character_data)
1262
+
1263
+ if new_stat and new_stat not in st.session_state.custom_stats:
1264
+ st.session_state.custom_stats.append(new_stat)
1265
+
1266
+ st.success(f"Character '{new_name}' added successfully!")
1267
+ else:
1268
+ st.warning("Please provide at least a name and description for the character.")
1269
+
1270
+ # Stat management
1271
+ with st.expander("Manage Stats"):
1272
+ st.subheader("Current Stats")
1273
+ for stat in st.session_state.custom_stats:
1274
+ col1, col2 = st.columns([3, 1])
1275
+ col1.write(stat)
1276
+ if col2.button("Remove", key=f"remove_{stat}"):
1277
+ st.session_state.custom_stats.remove(stat)
1278
+ st.experimental_rerun()
1279
+
1280
+ new_stat = st.text_input("Add New Stat")
1281
+ if st.button("Add Stat"):
1282
+ if new_stat and new_stat not in st.session_state.custom_stats:
1283
+ st.session_state.custom_stats.append(new_stat)
1284
+ st.experimental_rerun()
1285
+ else:
1286
+ st.warning("Stat already exists or is empty.")
1287
+
1288
+ # Display character cards
1289
+ st.subheader("Character Cards")
1290
+ if st.session_state.character_board:
1291
+ for idx, character in enumerate(st.session_state.character_board):
1292
+ with st.expander(character["name"], expanded=True):
1293
+ col1, col2 = st.columns([1, 1])
1294
+ with col1:
1295
+ if "image" in character:
1296
+ st.image(character["image"], use_column_width=True)
1297
+ st.write(f"**Description:** {character['description']}")
1298
+ st.write(f"**Background:** {character['background']}")
1299
+ st.write(f"**Special Abilities:** {character['abilities']}")
1300
+ st.write(f"**Weaknesses:** {character['weaknesses']}")
1301
+ st.write(f"**Relationships:** {character['relationships']}")
1302
+ if character["additional_info"]:
1303
+ st.write(f"**Additional Info:** {character['additional_info']}")
1304
+ with col2:
1305
+ st.subheader("Character Stats")
1306
+ fig = create_radar_chart(character["stats"])
1307
+ st.plotly_chart(fig, use_container_width=True)
1308
+
1309
+ if st.button(f"Delete {character['name']}", key=f"delete_{idx}"):
1310
+ st.session_state.character_board.pop(idx)
1311
+ st.experimental_rerun()
1312
+ else:
1313
+ st.info("No characters added yet. Use the form above to add characters to the board.")
1314
+
1315
+ elif choice == "Character Arc Visualizer":
1316
+ character_arc_visualizer()
1317
+
1318
+ elif choice == "World-Building Assistant":
1319
+ world_building_assistant()
1320
+
1321
+ # Optional: Add a feature to view saved prompts
1322
+ if st.checkbox("View Saved Prompts"):
1323
+ if "saved_prompts" not in st.session_state:
1324
+ st.session_state.saved_prompts = []
1325
+
1326
+ if st.session_state.writing_prompt and st.button("Add Current Prompt to Saved"):
1327
+ st.session_state.saved_prompts.append(st.session_state.writing_prompt)
1328
+ st.success("Prompt added to saved list!")
1329
+
1330
+ if st.session_state.saved_prompts:
1331
+ for i, prompt in enumerate(st.session_state.saved_prompts):
1332
+ st.text_area(f"Saved Prompt {i+1}", prompt, height=100, key=f"saved_prompt_{i}")
1333
+ else:
1334
+ st.info("No saved prompts yet. Generate and save some prompts to see them here!")
1335
+
1336
+ # Sidebar for quick access to generated content
1337
+ with st.sidebar:
1338
+ st.header("Quick Access")
1339
+ if "generated_book" in st.session_state:
1340
+ if st.button("View Generated Book"):
1341
+ st.session_state.current_view = "generated_book"
1342
+ if "book_outline" in st.session_state:
1343
+ if st.button("View Book Outline"):
1344
+ st.session_state.current_view = "book_outline"
1345
+ if "character_profile" in st.session_state:
1346
+ if st.button("View Character Profile"):
1347
+ st.session_state.current_view = "character_profile"
1348
+ if "writing_prompt" in st.session_state:
1349
+ if st.button("View Writing Prompt"):
1350
+ st.session_state.current_view = "writing_prompt"
1351
+ if "story_branches" in st.session_state:
1352
+ if st.button("View Interactive Story"):
1353
+ st.session_state.current_view = "interactive_story"
1354
+ if "character_arcs" in st.session_state:
1355
+ if st.button("View Character Arcs"):
1356
+ st.session_state.current_view = "character_arcs"
1357
+ if "world_elements" in st.session_state:
1358
+ if st.button("View World-Building"):
1359
+ st.session_state.current_view = "world_building"
1360
+
1361
+ # Display the selected content in the main area
1362
+ if "current_view" in st.session_state:
1363
+ if st.session_state.current_view == "generated_book":
1364
+ st.header("Generated Book")
1365
+ st.text_area("Book Content", st.session_state.generated_book, height=400)
1366
+ elif st.session_state.current_view == "book_outline":
1367
+ st.header("Book Outline")
1368
+ st.text_area("Outline", st.session_state.book_outline, height=400)
1369
+ elif st.session_state.current_view == "character_profile":
1370
+ st.header("Character Profile")
1371
+ st.text_area("Profile", st.session_state.character_profile, height=400)
1372
+ elif st.session_state.current_view == "writing_prompt":
1373
+ st.header("Writing Prompt")
1374
+ st.text_area("Prompt", st.session_state.writing_prompt, height=200)
1375
+ elif st.session_state.current_view == "character_arcs":
1376
+ character_arc_visualizer()
1377
+ elif st.session_state.current_view == "world_building":
1378
+ world_building_assistant()
1379
+ if __name__ == "__main__":
1380
+ main()