Raykarr commited on
Commit
62eda63
Β·
verified Β·
1 Parent(s): 4d459e8

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +235 -0
app.py ADDED
@@ -0,0 +1,235 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ import ebooklib
3
+ from ebooklib import epub
4
+ from PIL import Image
5
+ import io
6
+ import markdown
7
+ import uuid
8
+ from datetime import datetime
9
+ from collections import OrderedDict
10
+
11
+ # Initialize session state variables
12
+ if 'chapters' not in st.session_state:
13
+ st.session_state.chapters = OrderedDict({"Chapter 1": ""})
14
+
15
+ if 'metadata' not in st.session_state:
16
+ st.session_state.metadata = {
17
+ "description": "",
18
+ "publisher": "Self Published",
19
+ "publication_date": datetime.now().strftime("%Y-%m-%d"),
20
+ "rights": "All rights reserved"
21
+ }
22
+
23
+ # Helper functions
24
+ def get_chapter_num(chapter_title):
25
+ try:
26
+ return int(chapter_title.split()[1])
27
+ except:
28
+ return float('inf')
29
+
30
+ def add_chapter():
31
+ chapter_numbers = [get_chapter_num(title) for title in st.session_state.chapters.keys()]
32
+ next_number = max(chapter_numbers) + 1 if chapter_numbers else 1
33
+
34
+ new_chapters = OrderedDict()
35
+ existing_chapters = sorted(st.session_state.chapters.items(), key=lambda x: get_chapter_num(x[0]))
36
+
37
+ for title, content in existing_chapters:
38
+ new_chapters[title] = content
39
+
40
+ new_chapters[f"Chapter {next_number}"] = ""
41
+ st.session_state.chapters = new_chapters
42
+
43
+ def remove_chapter(chapter_title):
44
+ if chapter_title in st.session_state.chapters:
45
+ new_chapters = OrderedDict()
46
+ for title, content in st.session_state.chapters.items():
47
+ if title != chapter_title:
48
+ new_chapters[title] = content
49
+ st.session_state.chapters = new_chapters
50
+
51
+ def update_chapter_content(chapter_title, new_content):
52
+ if chapter_title in st.session_state.chapters:
53
+ st.session_state.chapters[chapter_title] = new_content
54
+
55
+ def update_chapter_title(old_title, new_title):
56
+ if old_title in st.session_state.chapters and old_title != new_title:
57
+ content = st.session_state.chapters[old_title]
58
+ new_chapters = OrderedDict()
59
+ for title, chapter_content in st.session_state.chapters.items():
60
+ if title == old_title:
61
+ new_chapters[new_title] = content
62
+ else:
63
+ new_chapters[title] = chapter_content
64
+ st.session_state.chapters = new_chapters
65
+
66
+ def convert_markdown_to_html(markdown_text):
67
+ return markdown.markdown(markdown_text, extensions=['tables', 'fenced_code', 'footnotes'])
68
+
69
+ def create_epub(title, author, cover_image, chapters_content, metadata):
70
+ book = epub.EpubBook()
71
+
72
+ # Set metadata
73
+ book.set_identifier(str(uuid.uuid4()))
74
+ book.set_title(title)
75
+ book.set_language('en')
76
+ book.add_author(author)
77
+ book.add_metadata('DC', 'description', metadata['description'])
78
+ book.add_metadata('DC', 'publisher', metadata['publisher'])
79
+ book.add_metadata('DC', 'date', metadata['publication_date'])
80
+ book.add_metadata('DC', 'rights', metadata['rights'])
81
+
82
+ # Handle cover image
83
+ cover_page = None
84
+ if cover_image is not None:
85
+ cover_image_bytes = io.BytesIO()
86
+ cover_image.save(cover_image_bytes, format='PNG')
87
+ cover_image_bytes = cover_image_bytes.getvalue()
88
+
89
+ cover_item = epub.EpubItem(
90
+ uid='cover_image',
91
+ file_name='images/cover.png',
92
+ media_type='image/png',
93
+ content=cover_image_bytes
94
+ )
95
+ book.add_item(cover_item)
96
+
97
+ # Create cover page
98
+ cover_page = epub.EpubHtml(title='Cover', file_name='cover.xhtml')
99
+ cover_page.content = f'''<html><body><div style="text-align: center; padding: 0; margin: 0;">
100
+ <img src="images/cover.png" alt="cover"/></div></body></html>'''
101
+ book.add_item(cover_page)
102
+
103
+ # Set the cover page as the book's cover
104
+ book.set_cover("images/cover.png", cover_image_bytes)
105
+
106
+ # Create table of contents page
107
+ toc_page = epub.EpubHtml(title='Table of Contents', file_name='toc.xhtml')
108
+ toc_content = ['<h1>Table of Contents</h1>', '<nav>', '<ol>']
109
+
110
+ for chapter_title in chapters_content.keys():
111
+ toc_content.append(f'<li><a href="#{chapter_title}">{chapter_title}</a></li>')
112
+
113
+ toc_content.extend(['</ol>', '</nav>'])
114
+ toc_page.content = '\n'.join(toc_content)
115
+ book.add_item(toc_page)
116
+
117
+ # Create chapters
118
+ chapter_objects = []
119
+ for idx, (chapter_title, chapter_content) in enumerate(chapters_content.items(), 1):
120
+ html_content = convert_markdown_to_html(chapter_content)
121
+ chapter = epub.EpubHtml(
122
+ title=chapter_title,
123
+ file_name=f'chapter_{idx}.xhtml',
124
+ content=f'<h1 id="{chapter_title}">{chapter_title}</h1>\n{html_content}'
125
+ )
126
+ book.add_item(chapter)
127
+ chapter_objects.append(chapter)
128
+
129
+ # Add navigation
130
+ book.toc = chapter_objects
131
+ book.add_item(epub.EpubNcx())
132
+ book.add_item(epub.EpubNav())
133
+
134
+ # Add CSS
135
+ style = '''
136
+ body { font-family: "Crimson Text", Georgia, serif; line-height: 1.6; margin: 2em; }
137
+ h1, h2, h3 { color: #2c3e50; margin-top: 1.5em; }
138
+ p { text-align: justify; margin-bottom: 1em; }
139
+ img { max-width: 100%; display: block; margin: 1em auto; }
140
+ nav { margin: 2em 0; }
141
+ nav ol { list-style-type: none; padding-left: 0; }
142
+ nav ol li { margin-bottom: 0.5em; }
143
+ nav ol li a { color: #2c3e50; text-decoration: none; }
144
+ nav ol li a:hover { text-decoration: underline; }
145
+ '''
146
+ nav_css = epub.EpubItem(
147
+ uid="style",
148
+ file_name="style/main.css",
149
+ media_type="text/css",
150
+ content=style
151
+ )
152
+ book.add_item(nav_css)
153
+
154
+ # Set spine with correct order: cover -> toc -> chapters
155
+ spine = []
156
+ if cover_page:
157
+ spine.append(cover_page)
158
+ spine.append(toc_page)
159
+ spine.extend(chapter_objects)
160
+ book.spine = spine
161
+
162
+ return book
163
+
164
+ def main():
165
+ st.set_page_config(page_title="πŸ“š EPUB Creator", layout="wide")
166
+ st.title("πŸ“š EPUB Creator")
167
+
168
+ with st.expander("πŸ“‹ Book Details", expanded=True):
169
+ col1, col2 = st.columns(2)
170
+ with col1:
171
+ title = st.text_input("Book Title", "My Book")
172
+ author = st.text_input("Author Name")
173
+ with col2:
174
+ st.session_state.metadata["publisher"] = st.text_input("Publisher", value=st.session_state.metadata["publisher"])
175
+ st.session_state.metadata["description"] = st.text_area("Book Description", value=st.session_state.metadata["description"])
176
+
177
+ with st.expander("πŸ–ΌοΈ Cover Image", expanded=True):
178
+ cover_image = st.file_uploader("Upload cover image (PNG/JPG)", type=['png', 'jpg', 'jpeg'])
179
+ if cover_image:
180
+ st.image(cover_image, width=300)
181
+
182
+ with st.expander("πŸ“ Chapters", expanded=True):
183
+ st.button("βž• Add New Chapter", on_click=add_chapter)
184
+ for chapter_title in st.session_state.chapters.keys():
185
+ st.subheader(chapter_title)
186
+ new_title = st.text_input("Title", value=chapter_title, key=f"title_{chapter_title}")
187
+ if new_title != chapter_title:
188
+ update_chapter_title(chapter_title, new_title)
189
+ st.rerun()
190
+
191
+ content = st.text_area(
192
+ "Content (Markdown supported)",
193
+ value=st.session_state.chapters[chapter_title],
194
+ height=200,
195
+ key=f"content_{chapter_title}"
196
+ )
197
+
198
+ # Update content if changed
199
+ if content != st.session_state.chapters[chapter_title]:
200
+ update_chapter_content(chapter_title, content)
201
+
202
+ if len(st.session_state.chapters) > 1:
203
+ st.button("πŸ—‘οΈ Remove Chapter", key=f"remove_{chapter_title}", on_click=remove_chapter, args=(chapter_title,))
204
+ st.markdown("---")
205
+
206
+ with st.expander("πŸ‘οΈ Preview", expanded=True):
207
+ if st.session_state.chapters:
208
+ chapter_select = st.selectbox("Select chapter to preview", options=list(st.session_state.chapters.keys()))
209
+ st.markdown("### Preview")
210
+ st.markdown(st.session_state.chapters[chapter_select])
211
+
212
+ if st.button("πŸ“š Generate EPUB", type="primary"):
213
+ if not title or not author:
214
+ st.error("Please provide both title and author name!")
215
+ return
216
+
217
+ if not any(st.session_state.chapters.values()):
218
+ st.error("Please add some content to at least one chapter!")
219
+ return
220
+
221
+ try:
222
+ cover_img = Image.open(cover_image) if cover_image else None
223
+ book = create_epub(title, author, cover_img, st.session_state.chapters, st.session_state.metadata)
224
+
225
+ epub_bytes = io.BytesIO()
226
+ epub.write_epub(epub_bytes, book)
227
+
228
+ st.download_button("πŸ“₯ Download EPUB", data=epub_bytes.getvalue(), file_name=f"{title.lower().replace(' ', '_')}.epub", mime="application/epub+zip")
229
+ st.success("βœ… EPUB generated successfully!")
230
+
231
+ except Exception as e:
232
+ st.error(f"Error generating EPUB: {str(e)}")
233
+
234
+ if __name__ == "__main__":
235
+ main()