Spaces:
Build error
Build error
Create app.py
Browse files
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()
|