@@ -12,13 +12,13 @@ class AudioQuality(enum.Enum):
1212 NORMAL = 0x00
1313 HIGH = 0x01
1414 VERY_HIGH = 0x02
15+ LOSSLESS = 0x03
1516
1617 @staticmethod
1718 def get_quality (audio_format : AudioFile .Format ) -> AudioQuality :
1819 if audio_format in [
1920 AudioFile .MP3_96 ,
2021 AudioFile .OGG_VORBIS_96 ,
21- AudioFile .AAC_24_NORM ,
2222 ]:
2323 return AudioQuality .NORMAL
2424 if audio_format in [
@@ -35,6 +35,11 @@ def get_quality(audio_format: AudioFile.Format) -> AudioQuality:
3535 AudioFile .AAC_48 ,
3636 ]:
3737 return AudioQuality .VERY_HIGH
38+ if audio_format in [
39+ AudioFile .FLAC_FLAC ,
40+ AudioFile .FLAC_FLAC_24BIT ,
41+ ]:
42+ return AudioQuality .LOSSLESS
3843 raise RuntimeError ("Unknown format: {}" .format (audio_format ))
3944
4045 def get_matches (self ,
@@ -47,35 +52,71 @@ def get_matches(self,
4752 return file_list
4853
4954
50- class VorbisOnlyAudioQuality (AudioQualityPicker ):
51- logger = logging .getLogger ("Librespot:Player:VorbisOnlyAudioQuality" )
55+ class FormatOnlyAudioQuality (AudioQualityPicker ):
56+ # Generic quality picker; filters files by container format
57+
58+ logger = logging .getLogger ("Librespot:Player:FormatOnlyAudioQuality" )
5259 preferred : AudioQuality
60+ format_filter : SuperAudioFormat
5361
54- def __init__ (self , preferred : AudioQuality ):
62+ def __init__ (self , preferred : AudioQuality , format_filter : SuperAudioFormat ):
5563 self .preferred = preferred
64+ self .format_filter = format_filter
5665
5766 @staticmethod
58- def get_vorbis_file (files : typing .List [Metadata .AudioFile ]):
67+ def get_file_by_format (files : typing .List [Metadata .AudioFile ],
68+ format_type : SuperAudioFormat ) -> typing .Optional [Metadata .AudioFile ]:
5969 for file in files :
6070 if file .HasField ("format" ) and SuperAudioFormat .get (
61- file .format ) == SuperAudioFormat . VORBIS :
71+ file .format ) == format_type :
6272 return file
6373 return None
6474
65- def get_file (self , files : typing .List [Metadata .AudioFile ]):
66- matches : typing .List [Metadata .AudioFile ] = self .preferred .get_matches (
67- files )
68- vorbis : Metadata .AudioFile = VorbisOnlyAudioQuality .get_vorbis_file (
69- matches )
70- if vorbis is None :
71- vorbis : Metadata .AudioFile = VorbisOnlyAudioQuality .get_vorbis_file (
72- files )
73- if vorbis is not None :
75+ def get_file (self , files : typing .List [Metadata .AudioFile ]) -> typing .Optional [Metadata .AudioFile ]:
76+ quality_matches : typing .List [Metadata .AudioFile ] = self .preferred .get_matches (files )
77+
78+ selected_file = self .get_file_by_format (quality_matches , self .format_filter )
79+
80+ if selected_file is None :
81+ # Try using any file matching the format, regardless of quality
82+ selected_file = self .get_file_by_format (files , self .format_filter )
83+
84+ if selected_file is not None :
85+ # Found format match (different quality than preferred)
7486 self .logger .warning (
75- "Using {} because preferred {} couldn't be found." .format (
76- Metadata .AudioFile .Format .Name (vorbis .format ),
77- self .preferred ))
87+ "Using {} format file with {} quality because preferred {} quality couldn't be found." .format (
88+ self .format_filter .name ,
89+ AudioQuality .get_quality (selected_file .format ).name ,
90+ self .preferred .name ))
7891 else :
92+ available_formats = [SuperAudioFormat .get (f .format ).name
93+ for f in files if f .HasField ("format" )]
7994 self .logger .fatal (
80- "Couldn't find any Vorbis file, available: {}" )
81- return vorbis
95+ "Couldn't find any {} file. Available formats: {}" .format (
96+ self .format_filter .name ,
97+ ", " .join (set (available_formats )) if available_formats else "none" ))
98+
99+ return selected_file
100+
101+
102+ # Backward-compatible wrapper classes
103+
104+ class VorbisOnlyAudioQuality (FormatOnlyAudioQuality ):
105+ logger = logging .getLogger ("Librespot:Player:VorbisOnlyAudioQuality" )
106+
107+ def __init__ (self , preferred : AudioQuality ):
108+ super ().__init__ (preferred , SuperAudioFormat .VORBIS )
109+
110+ @staticmethod
111+ def get_vorbis_file (files : typing .List [Metadata .AudioFile ]) -> typing .Optional [Metadata .AudioFile ]:
112+ return FormatOnlyAudioQuality .get_file_by_format (files , SuperAudioFormat .VORBIS )
113+
114+ class LosslessOnlyAudioQuality (FormatOnlyAudioQuality ):
115+ logger = logging .getLogger ("Librespot:Player:LosslessOnlyAudioQuality" )
116+
117+ def __init__ (self , preferred : AudioQuality ):
118+ super ().__init__ (preferred , SuperAudioFormat .FLAC )
119+
120+ @staticmethod
121+ def get_flac_file (files : typing .List [Metadata .AudioFile ]) -> typing .Optional [Metadata .AudioFile ]:
122+ return FormatOnlyAudioQuality .get_file_by_format (files , SuperAudioFormat .FLAC )
0 commit comments