Fixed a bug that would cause no playback on newloy loaded books. Added .mobi support.
This commit is contained in:
@@ -35,6 +35,7 @@ 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.mobi_parser import MobiParser
|
||||
from src.audio_parser import AudioParser
|
||||
from src.folder_audiobook_parser import FolderAudiobookParser
|
||||
from src.bookmark_manager import BookmarkManager
|
||||
@@ -122,7 +123,7 @@ class BookReader:
|
||||
booksDir = libraryDir
|
||||
else:
|
||||
booksDir = self.config.get_books_directory()
|
||||
supportedFormats = ['.zip', '.epub', '.pdf', '.txt', '.m4b', '.m4a', '.mp3']
|
||||
supportedFormats = ['.zip', '.epub', '.mobi', '.pdf', '.txt', '.m4b', '.m4a', '.mp3']
|
||||
self.bookSelector = BookSelector(booksDir, supportedFormats, self.speechEngine)
|
||||
|
||||
# Initialize sleep timer menu
|
||||
@@ -220,6 +221,9 @@ class BookReader:
|
||||
if suffix in ['.epub']:
|
||||
self.parser = EpubParser()
|
||||
self.book = self.parser.parse(self.bookPath)
|
||||
elif suffix in ['.mobi']:
|
||||
self.parser = MobiParser()
|
||||
self.book = self.parser.parse(self.bookPath)
|
||||
elif suffix in ['.zip']:
|
||||
# Assume DAISY format for zip files
|
||||
self.parser = DaisyParser()
|
||||
|
||||
@@ -5,6 +5,7 @@ lxml>=4.6.0
|
||||
mutagen>=1.45.0
|
||||
pypdf
|
||||
mpv
|
||||
requests>=2.25.0
|
||||
|
||||
# Braille display support (optional)
|
||||
# Note: These are system packages, not pip packages
|
||||
@@ -14,5 +15,6 @@ mpv
|
||||
# Optional dependencies
|
||||
# piper-tts: Install separately with voice models to /usr/share/piper-voices/
|
||||
# ffmpeg: Install via system package manager for M4B/M4A support
|
||||
# calibre: Install via system package manager for MOBI support (provides ebook-convert)
|
||||
# brltty: System daemon for Braille display hardware
|
||||
# liblouis: Braille translation library
|
||||
|
||||
101
src/mobi_parser.py
Normal file
101
src/mobi_parser.py
Normal file
@@ -0,0 +1,101 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
MOBI Parser
|
||||
|
||||
Parses MOBI/Mobipocket format ebooks and extracts text content.
|
||||
Uses Calibre's ebook-convert to convert MOBI to EPUB, then parses the EPUB.
|
||||
"""
|
||||
|
||||
import tempfile
|
||||
import shutil
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
|
||||
from src.book import Book, Chapter
|
||||
from src.epub_parser import EpubParser
|
||||
|
||||
|
||||
class MobiParser:
|
||||
"""Parser for MOBI/Mobipocket format ebooks"""
|
||||
|
||||
def __init__(self):
|
||||
"""Initialize MOBI parser"""
|
||||
self.tempDir = None
|
||||
self.epubParser = None
|
||||
|
||||
def parse(self, mobiPath):
|
||||
"""
|
||||
Parse MOBI file
|
||||
|
||||
Args:
|
||||
mobiPath: Path to MOBI file
|
||||
|
||||
Returns:
|
||||
Book object
|
||||
|
||||
Raises:
|
||||
Exception: If parsing fails
|
||||
"""
|
||||
mobiPath = Path(mobiPath)
|
||||
|
||||
# Create temp directory for conversion
|
||||
self.tempDir = tempfile.mkdtemp(prefix='bookstorm_mobi_')
|
||||
tempPath = Path(self.tempDir)
|
||||
|
||||
try:
|
||||
# Convert MOBI to EPUB using Calibre's ebook-convert
|
||||
epubPath = tempPath / f"{mobiPath.stem}.epub"
|
||||
|
||||
# Check if ebook-convert is available
|
||||
convertCmd = ['ebook-convert', str(mobiPath), str(epubPath)]
|
||||
|
||||
try:
|
||||
# Run ebook-convert with error capture
|
||||
result = subprocess.run(
|
||||
convertCmd,
|
||||
capture_output=True, # pylint: disable=subprocess-run-check
|
||||
text=True,
|
||||
timeout=60
|
||||
)
|
||||
|
||||
if result.returncode != 0:
|
||||
raise Exception(f"ebook-convert failed: {result.stderr}")
|
||||
|
||||
except FileNotFoundError:
|
||||
raise Exception(
|
||||
"Calibre's ebook-convert tool is required for MOBI support. "
|
||||
"Install Calibre: sudo pacman -S calibre (Arch) or "
|
||||
"sudo apt install calibre (Debian/Ubuntu)"
|
||||
)
|
||||
except subprocess.TimeoutExpired:
|
||||
raise Exception("MOBI conversion timed out (>60 seconds)")
|
||||
|
||||
# Verify the EPUB was created
|
||||
if not epubPath.exists():
|
||||
raise Exception("Failed to convert MOBI to EPUB")
|
||||
|
||||
# Parse the converted EPUB
|
||||
self.epubParser = EpubParser()
|
||||
book = self.epubParser.parse(epubPath)
|
||||
|
||||
# Set the original MOBI path as the book path
|
||||
book.path = mobiPath
|
||||
|
||||
return book
|
||||
|
||||
except Exception as e:
|
||||
raise Exception(f"Error parsing MOBI: {e}")
|
||||
|
||||
def cleanup(self):
|
||||
"""Clean up temporary files"""
|
||||
# Clean up EPUB parser
|
||||
if self.epubParser:
|
||||
self.epubParser.cleanup()
|
||||
|
||||
# Clean up our temp directory
|
||||
if self.tempDir and Path(self.tempDir).exists():
|
||||
try:
|
||||
shutil.rmtree(self.tempDir)
|
||||
except Exception as e:
|
||||
print(f"Warning: Could not remove temp directory: {e}")
|
||||
@@ -382,14 +382,18 @@ class MpvPlayer:
|
||||
return False
|
||||
|
||||
try:
|
||||
# Seek to start position (including 0)
|
||||
# Always seek to ensure we're at the right position, even for position 0
|
||||
if startPosition >= 0:
|
||||
self.player.seek(startPosition, reference='absolute')
|
||||
|
||||
# Start playback
|
||||
# Start playback first (this ensures mpv begins loading/decoding)
|
||||
self.player.pause = False
|
||||
self.isPaused = False
|
||||
|
||||
# Only seek if we have a non-zero start position
|
||||
# Seeking to position 0 immediately after load can fail if file isn't ready
|
||||
if startPosition > 0:
|
||||
# Wait a moment for file to be ready before seeking
|
||||
import time
|
||||
time.sleep(0.1)
|
||||
self.player.seek(startPosition, reference='absolute')
|
||||
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
|
||||
Reference in New Issue
Block a user