Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 62 additions & 21 deletions librespot/audio/decoders.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,13 @@ class AudioQuality(enum.Enum):
NORMAL = 0x00
HIGH = 0x01
VERY_HIGH = 0x02
LOSSLESS = 0x03

@staticmethod
def get_quality(audio_format: AudioFile.Format) -> AudioQuality:
if audio_format in [
AudioFile.MP3_96,
AudioFile.OGG_VORBIS_96,
AudioFile.AAC_24_NORM,
]:
return AudioQuality.NORMAL
if audio_format in [
Expand All @@ -35,7 +35,12 @@ def get_quality(audio_format: AudioFile.Format) -> AudioQuality:
AudioFile.AAC_48,
]:
return AudioQuality.VERY_HIGH
raise RuntimeError("Unknown format: {}".format(format))
if audio_format in [
AudioFile.FLAC_FLAC,
AudioFile.FLAC_FLAC_24BIT,
]:
return AudioQuality.LOSSLESS
raise RuntimeError("Unknown format: {}".format(audio_format))

def get_matches(self,
files: typing.List[AudioFile]) -> typing.List[AudioFile]:
Expand All @@ -47,35 +52,71 @@ def get_matches(self,
return file_list


class VorbisOnlyAudioQuality(AudioQualityPicker):
logger = logging.getLogger("Librespot:Player:VorbisOnlyAudioQuality")
class FormatOnlyAudioQuality(AudioQualityPicker):
# Generic quality picker; filters files by container format

logger = logging.getLogger("Librespot:Player:FormatOnlyAudioQuality")
preferred: AudioQuality
format_filter: SuperAudioFormat

def __init__(self, preferred: AudioQuality):
def __init__(self, preferred: AudioQuality, format_filter: SuperAudioFormat):
self.preferred = preferred
self.format_filter = format_filter

@staticmethod
def get_vorbis_file(files: typing.List[Metadata.AudioFile]):
def get_file_by_format(files: typing.List[Metadata.AudioFile],
format_type: SuperAudioFormat) -> typing.Optional[Metadata.AudioFile]:
for file in files:
if file.HasField("format") and SuperAudioFormat.get(
file.format) == SuperAudioFormat.VORBIS:
file.format) == format_type:
return file
return None

def get_file(self, files: typing.List[Metadata.AudioFile]):
matches: typing.List[Metadata.AudioFile] = self.preferred.get_matches(
files)
vorbis: Metadata.AudioFile = VorbisOnlyAudioQuality.get_vorbis_file(
matches)
if vorbis is None:
vorbis: Metadata.AudioFile = VorbisOnlyAudioQuality.get_vorbis_file(
files)
if vorbis is not None:
def get_file(self, files: typing.List[Metadata.AudioFile]) -> typing.Optional[Metadata.AudioFile]:
quality_matches: typing.List[Metadata.AudioFile] = self.preferred.get_matches(files)

selected_file = self.get_file_by_format(quality_matches, self.format_filter)

if selected_file is None:
# Try using any file matching the format, regardless of quality
selected_file = self.get_file_by_format(files, self.format_filter)

if selected_file is not None:
# Found format match (different quality than preferred)
self.logger.warning(
"Using {} because preferred {} couldn't be found.".format(
Metadata.AudioFile.Format.Name(vorbis.format),
self.preferred))
"Using {} format file with {} quality because preferred {} quality couldn't be found.".format(
self.format_filter.name,
AudioQuality.get_quality(selected_file.format).name,
self.preferred.name))
else:
available_formats = [SuperAudioFormat.get(f.format).name
for f in files if f.HasField("format")]
self.logger.fatal(
"Couldn't find any Vorbis file, available: {}")
return vorbis
"Couldn't find any {} file. Available formats: {}".format(
self.format_filter.name,
", ".join(set(available_formats)) if available_formats else "none"))

return selected_file


# Backward-compatible wrapper classes

class VorbisOnlyAudioQuality(FormatOnlyAudioQuality):
logger = logging.getLogger("Librespot:Player:VorbisOnlyAudioQuality")

def __init__(self, preferred: AudioQuality):
super().__init__(preferred, SuperAudioFormat.VORBIS)

@staticmethod
def get_vorbis_file(files: typing.List[Metadata.AudioFile]) -> typing.Optional[Metadata.AudioFile]:
return FormatOnlyAudioQuality.get_file_by_format(files, SuperAudioFormat.VORBIS)

class LosslessOnlyAudioQuality(FormatOnlyAudioQuality):
logger = logging.getLogger("Librespot:Player:LosslessOnlyAudioQuality")

def __init__(self, preferred: AudioQuality):
super().__init__(preferred, SuperAudioFormat.FLAC)

@staticmethod
def get_flac_file(files: typing.List[Metadata.AudioFile]) -> typing.Optional[Metadata.AudioFile]:
return FormatOnlyAudioQuality.get_file_by_format(files, SuperAudioFormat.FLAC)
7 changes: 6 additions & 1 deletion librespot/audio/format.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ class SuperAudioFormat(enum.Enum):
MP3 = 0x00
VORBIS = 0x01
AAC = 0x02
FLAC = 0x03

@staticmethod
def get(audio_format: Metadata.AudioFile.Format):
Expand All @@ -26,7 +27,11 @@ def get(audio_format: Metadata.AudioFile.Format):
if audio_format in [
Metadata.AudioFile.Format.AAC_24,
Metadata.AudioFile.Format.AAC_48,
Metadata.AudioFile.Format.AAC_24_NORM,
]:
return SuperAudioFormat.AAC
if audio_format in [
Metadata.AudioFile.Format.FLAC_FLAC,
Metadata.AudioFile.Format.FLAC_FLAC_24BIT,
]:
return SuperAudioFormat.FLAC
raise RuntimeError("Unknown audio format: {}".format(audio_format))
Loading
Loading