Epub now uses spine order for book navigation and headings are included in paragraph navigation.
This commit is contained in:
109
bookstorm.py
109
bookstorm.py
@@ -50,6 +50,7 @@ from src.audiobookshelf_client import AudiobookshelfClient
|
||||
from src.audiobookshelf_menu import AudiobookshelfMenu
|
||||
from src.server_link_manager import ServerLinkManager
|
||||
from src.bookmarks_menu import BookmarksMenu
|
||||
from src.wav_exporter import WavExporter
|
||||
|
||||
|
||||
class BookReader:
|
||||
@@ -2595,7 +2596,8 @@ def main():
|
||||
|
||||
# Handle export mode
|
||||
if args.wav:
|
||||
return export_to_wav(bookPath, config, args.outputDir)
|
||||
exporter = WavExporter(config)
|
||||
return exporter.export(bookPath, args.outputDir)
|
||||
|
||||
# Interactive reading mode
|
||||
config.set_last_book(bookPath)
|
||||
@@ -2614,110 +2616,5 @@ def main():
|
||||
return 0
|
||||
|
||||
|
||||
def export_to_wav(bookPath, config, outputDir=None):
|
||||
"""
|
||||
Export book to WAV files split by chapter
|
||||
|
||||
Args:
|
||||
bookPath: Path to book file
|
||||
config: ConfigManager instance
|
||||
outputDir: Output directory (optional)
|
||||
|
||||
Returns:
|
||||
Exit code
|
||||
"""
|
||||
from src.daisy_parser import DaisyParser
|
||||
from src.epub_parser import EpubParser
|
||||
from src.pdf_parser import PdfParser
|
||||
from src.txt_parser import TxtParser
|
||||
from src.tts_engine import TtsEngine
|
||||
import wave
|
||||
|
||||
print(f"Exporting book to WAV: {bookPath}")
|
||||
|
||||
# Parse book using appropriate parser
|
||||
bookPath = Path(bookPath)
|
||||
suffix = bookPath.suffix.lower()
|
||||
|
||||
if suffix in ['.epub']:
|
||||
parser = EpubParser()
|
||||
elif suffix in ['.zip']:
|
||||
parser = DaisyParser()
|
||||
elif suffix in ['.pdf']:
|
||||
parser = PdfParser()
|
||||
elif suffix in ['.txt']:
|
||||
parser = TxtParser()
|
||||
else:
|
||||
print(f"Error: Unsupported book format: {suffix}")
|
||||
return 1
|
||||
|
||||
try:
|
||||
book = parser.parse(bookPath)
|
||||
except Exception as e:
|
||||
print(f"Error parsing book: {e}")
|
||||
return 1
|
||||
|
||||
# Determine output directory
|
||||
if outputDir is None:
|
||||
bookName = Path(bookPath).stem
|
||||
outputDir = Path(f"./{bookName}_audio")
|
||||
else:
|
||||
outputDir = Path(outputDir)
|
||||
|
||||
outputDir.mkdir(parents=True, exist_ok=True)
|
||||
print(f"Output directory: {outputDir}")
|
||||
|
||||
# Initialize TTS engine
|
||||
readerEngine = config.get_reader_engine()
|
||||
if readerEngine == 'speechd':
|
||||
print("Error: WAV export requires piper-tts. Set reader_engine=piper in config.")
|
||||
return 1
|
||||
|
||||
voiceModel = config.get_voice_model()
|
||||
tts = TtsEngine(voiceModel)
|
||||
|
||||
print(f"Using voice: {voiceModel}")
|
||||
print(f"Chapters: {book.get_total_chapters()}")
|
||||
print()
|
||||
|
||||
# Export each chapter
|
||||
for chapterIdx in range(book.get_total_chapters()):
|
||||
chapter = book.get_chapter(chapterIdx)
|
||||
if not chapter:
|
||||
continue
|
||||
|
||||
chapterNum = chapterIdx + 1
|
||||
print(f"Exporting Chapter {chapterNum}/{book.get_total_chapters()}: {chapter.title}")
|
||||
|
||||
# Combine all paragraphs in chapter
|
||||
chapterText = "\n\n".join(chapter.paragraphs)
|
||||
|
||||
# Generate audio
|
||||
try:
|
||||
wavData = tts.text_to_wav_data(chapterText)
|
||||
if not wavData:
|
||||
print(f" Warning: No audio generated for chapter {chapterNum}")
|
||||
continue
|
||||
|
||||
# Save to file
|
||||
sanitizedTitle = "".join(c for c in chapter.title if c.isalnum() or c in (' ', '-', '_')).strip()
|
||||
if not sanitizedTitle:
|
||||
sanitizedTitle = f"Chapter_{chapterNum}"
|
||||
|
||||
outputFile = outputDir / f"{chapterNum:03d}_{sanitizedTitle}.wav"
|
||||
with open(outputFile, 'wb') as f:
|
||||
f.write(wavData)
|
||||
|
||||
print(f" Saved: {outputFile.name}")
|
||||
|
||||
except Exception as e:
|
||||
print(f" Error generating audio for chapter {chapterNum}: {e}")
|
||||
continue
|
||||
|
||||
parser.cleanup()
|
||||
print(f"\nExport complete! Files saved to: {outputDir}")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(main())
|
||||
|
||||
Reference in New Issue
Block a user