Fixed a bug that would cause no playback on newloy loaded books. Added .mobi support.

This commit is contained in:
Storm Dragon
2025-10-27 18:32:12 -04:00
parent 105c83a941
commit ecbddde9cd
4 changed files with 118 additions and 7 deletions

View File

@@ -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()

View File

@@ -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
View 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}")

View File

@@ -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: