Fixed chapter parsing for m4b books. Added volume for audiobooks bound to 9 (decrese) and 0 (increase).
This commit is contained in:
@@ -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
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user