Fixed chapter parsing for m4b books. Added volume for audiobooks bound to 9 (decrese) and 0 (increase).

This commit is contained in:
Storm Dragon
2025-10-20 20:50:31 -04:00
parent f9564265fa
commit 409cb61f05
3 changed files with 115 additions and 28 deletions

View File

@@ -149,14 +149,9 @@ class AudioParser:
"""Extract chapter information from audio file"""
chapters = []
# Try MP4 chapter format (M4B, M4A)
if hasattr(audioFile, 'tags') and audioFile.tags:
# MP4 files store chapters in a special way
if hasattr(audioFile.tags, '_DictProxy__dict'):
tagsDict = audioFile.tags._DictProxy__dict
if 'chapters' in tagsDict:
mp4Chapters = tagsDict['chapters']
chapters = self._parse_mp4_chapters(mp4Chapters)
# Try MP4 chapter format (M4B, M4A) - check chapters attribute directly
if hasattr(audioFile, 'chapters') and audioFile.chapters:
chapters = self._parse_mp4_chapters(audioFile.chapters, audioFile.info.length)
# Try MP3 chapter format (ID3 CHAP frames)
if not chapters and hasattr(audioFile, 'tags'):
@@ -168,30 +163,41 @@ class AudioParser:
return chapters
def _parse_mp4_chapters(self, mp4Chapters):
"""Parse MP4 chapter list"""
def _parse_mp4_chapters(self, mp4Chapters, totalDuration):
"""
Parse MP4 chapter list
Args:
mp4Chapters: List of mutagen.mp4.Chapter objects
totalDuration: Total duration of audio file in seconds
Returns:
List of AudioChapter objects
"""
chapters = []
for i, chapterData in enumerate(mp4Chapters):
if isinstance(chapterData, tuple) and len(chapterData) >= 2:
startTime = chapterData[0] / 1000.0 # Convert ms to seconds
chapterTitle = chapterData[1] if chapterData[1] else f"Chapter {i + 1}"
for i, chapterObj in enumerate(mp4Chapters):
# Chapter objects have .start (float, seconds) and .title (string) attributes
startTime = chapterObj.start
chapterTitle = chapterObj.title if chapterObj.title else f"Chapter {i + 1}"
# Calculate duration (will be updated when we know the next chapter's start)
duration = 0.0
# Calculate duration (will be updated when we know the next chapter's start)
duration = 0.0
chapter = AudioChapter(
title=chapterTitle,
startTime=startTime,
duration=duration
)
chapters.append(chapter)
chapter = AudioChapter(
title=chapterTitle,
startTime=startTime,
duration=duration
)
chapters.append(chapter)
# Calculate durations based on next chapter's start time
for i in range(len(chapters) - 1):
chapters[i].duration = chapters[i + 1].startTime - chapters[i].startTime
# Last chapter duration will be set by total book duration later
# Last chapter duration extends to end of file
if chapters:
chapters[-1].duration = totalDuration - chapters[-1].startTime
return chapters

View File

@@ -165,6 +165,70 @@ class MpvPlayer:
"""Get current playback speed"""
return self.playbackSpeed
def increase_volume(self, step=5):
"""
Increase volume by step amount
Args:
step: Volume increase amount (default 5)
Returns:
Current volume level (0-200, allows software amplification)
"""
if not self.isInitialized or not self.player:
return 100
try:
# pylint: disable=no-member
currentVolume = self.player.volume if self.player.volume is not None else 100
# Allow up to 200% for software amplification (useful for quiet audio)
newVolume = min(200, currentVolume + step)
self.player.volume = newVolume
return int(newVolume)
except Exception as e:
print(f"Error increasing volume: {e}")
return 100
def decrease_volume(self, step=5):
"""
Decrease volume by step amount
Args:
step: Volume decrease amount (default 5)
Returns:
Current volume level (0-100)
"""
if not self.isInitialized or not self.player:
return 100
try:
# pylint: disable=no-member
currentVolume = self.player.volume if self.player.volume is not None else 100
newVolume = max(0, currentVolume - step)
self.player.volume = newVolume
return int(newVolume)
except Exception as e:
print(f"Error decreasing volume: {e}")
return 100
def get_volume(self):
"""
Get current volume level
Returns:
Current volume (0-200, 100 is normal, >100 is software amplification)
"""
if not self.isInitialized or not self.player:
return 100
try:
# pylint: disable=no-member
volume = self.player.volume
return int(volume) if volume is not None else 100
except:
return 100
def cleanup(self):
"""Cleanup resources"""
if self.isInitialized and self.player: