11use  std:: { io,  time:: Duration } ; 
22
33use  symphonia:: { 
4+     core:: meta:: { Metadata ,  MetadataOptions } , 
5+     core:: probe:: { Hint ,  ProbedMetadata } , 
46    core:: { 
57        audio:: SampleBuffer , 
68        codecs:: { Decoder ,  DecoderOptions } , 
@@ -27,6 +29,18 @@ pub struct SymphoniaDecoder {
2729    format :  Box < dyn  FormatReader > , 
2830    decoder :  Box < dyn  Decoder > , 
2931    sample_buffer :  Option < SampleBuffer < f64 > > , 
32+     probed_metadata :  Option < ProbedMetadata > , 
33+ } 
34+ 
35+ #[ derive( Default ) ]  
36+ pub ( crate )  struct  LocalFileMetadata  { 
37+     pub  name :  String , 
38+     pub  language :  String , 
39+     pub  album :  String , 
40+     pub  artists :  String , 
41+     pub  album_artists :  String , 
42+     pub  number :  u32 , 
43+     pub  disc_number :  u32 , 
3044} 
3145
3246impl  SymphoniaDecoder  { 
@@ -94,20 +108,62 @@ impl SymphoniaDecoder {
94108            // We set the sample buffer when decoding the first full packet, 
95109            // whose duration is also the ideal sample buffer size. 
96110            sample_buffer :  None , 
111+ 
112+             probed_metadata :  None , 
97113        } ) 
98114    } 
99115
100-     pub  fn  normalisation_data ( & mut  self )  -> Option < NormalisationData >  { 
101-         let  mut  metadata = self . format . metadata ( ) ; 
116+     pub ( crate )  fn  new_with_probe < R > ( src :  R ,  extension :  Option < & str > )  -> DecoderResult < Self > 
117+     where 
118+         R :  MediaSource  + ' static , 
119+     { 
120+         let  mss = MediaSourceStream :: new ( Box :: new ( src) ,  Default :: default ( ) ) ; 
102121
103-         // Advance to the latest metadata revision. 
104-         // None means we hit the latest. 
105-         loop  { 
106-             if  metadata. pop ( ) . is_none ( )  { 
107-                 break ; 
108-             } 
122+         let  mut  hint = Hint :: new ( ) ; 
123+ 
124+         if  let  Some ( extension)  = extension { 
125+             hint. with_extension ( extension) ; 
126+         } 
127+ 
128+         let  format_opts:  FormatOptions  = Default :: default ( ) ; 
129+         let  metadata_opts:  MetadataOptions  = Default :: default ( ) ; 
130+         let  decoder_opts:  DecoderOptions  = Default :: default ( ) ; 
131+ 
132+         let  probed =
133+             symphonia:: default:: get_probe ( ) . format ( & hint,  mss,  & format_opts,  & metadata_opts) ?; 
134+ 
135+         let  format = probed. format ; 
136+ 
137+         let  track = format. default_track ( ) . ok_or_else ( || { 
138+             DecoderError :: SymphoniaDecoder ( "Could not retrieve default track" . into ( ) ) 
139+         } ) ?; 
140+ 
141+         let  decoder = symphonia:: default:: get_codecs ( ) . make ( & track. codec_params ,  & decoder_opts) ?; 
142+ 
143+         let  rate = decoder. codec_params ( ) . sample_rate . ok_or_else ( || { 
144+             DecoderError :: SymphoniaDecoder ( "Could not retrieve sample rate" . into ( ) ) 
145+         } ) ?; 
146+ 
147+         // TODO: The official client supports local files with sample rates other than 44,100 kHz. 
148+         // To play these accurately, we need to either resample the input audio, or introduce a way 
149+         // to change the player's current sample rate (likely by closing and re-opening the sink 
150+         // with new parameters). 
151+         if  rate != SAMPLE_RATE  { 
152+             return  Err ( DecoderError :: SymphoniaDecoder ( format ! ( 
153+                 "Unsupported sample rate: {rate}. Local files must have a sample rate of {SAMPLE_RATE} Hz." 
154+             ) ) ) ; 
109155        } 
110156
157+         Ok ( Self  { 
158+             format, 
159+             decoder, 
160+             sample_buffer :  None , 
161+             probed_metadata :  Some ( probed. metadata ) , 
162+         } ) 
163+     } 
164+ 
165+     pub  fn  normalisation_data ( & mut  self )  -> Option < NormalisationData >  { 
166+         let  metadata = self . metadata ( ) ?; 
111167        let  tags = metadata. current ( ) ?. tags ( ) ; 
112168
113169        if  tags. is_empty ( )  { 
@@ -131,6 +187,70 @@ impl SymphoniaDecoder {
131187        } 
132188    } 
133189
190+     pub ( crate )  fn  local_file_metadata ( & mut  self )  -> Option < LocalFileMetadata >  { 
191+         let  metadata = self . metadata ( ) ?; 
192+         let  tags = metadata. current ( ) ?. tags ( ) ; 
193+         let  mut  metadata = LocalFileMetadata :: default ( ) ; 
194+ 
195+         for  tag in  tags { 
196+             if  let  Value :: String ( value)  = & tag. value  { 
197+                 match  tag. std_key  { 
198+                     // We could possibly use mem::take here to avoid cloning, but that risks leaving 
199+                     // the audio item metadata in a bad state. 
200+                     Some ( StandardTagKey :: TrackTitle )  => metadata. name  = value. clone ( ) , 
201+                     Some ( StandardTagKey :: Language )  => metadata. language  = value. clone ( ) , 
202+                     Some ( StandardTagKey :: Artist )  => metadata. artists  = value. clone ( ) , 
203+                     Some ( StandardTagKey :: AlbumArtist )  => metadata. album_artists  = value. clone ( ) , 
204+                     Some ( StandardTagKey :: Album )  => metadata. album  = value. clone ( ) , 
205+                     Some ( StandardTagKey :: TrackNumber )  => { 
206+                         metadata. number  = value. parse :: < u32 > ( ) . unwrap_or_default ( ) 
207+                     } 
208+                     Some ( StandardTagKey :: DiscNumber )  => { 
209+                         metadata. disc_number  = value. parse :: < u32 > ( ) . unwrap_or_default ( ) 
210+                     } 
211+                     _ => ( ) , 
212+                 } 
213+             }  else  if  let  Value :: UnsignedInt ( value)  = & tag. value  { 
214+                 match  tag. std_key  { 
215+                     Some ( StandardTagKey :: TrackNumber )  => metadata. number  = * value as  u32 , 
216+                     Some ( StandardTagKey :: DiscNumber )  => metadata. disc_number  = * value as  u32 , 
217+                     _ => ( ) , 
218+                 } 
219+             }  else  if  let  Value :: SignedInt ( value)  = & tag. value  { 
220+                 match  tag. std_key  { 
221+                     Some ( StandardTagKey :: TrackNumber )  => metadata. number  = * value as  u32 , 
222+                     Some ( StandardTagKey :: DiscNumber )  => metadata. disc_number  = * value as  u32 , 
223+                     _ => ( ) , 
224+                 } 
225+             } 
226+         } 
227+ 
228+         Some ( metadata) 
229+     } 
230+ 
231+     fn  metadata ( & mut  self )  -> Option < Metadata >  { 
232+         let  mut  metadata = self . format . metadata ( ) ; 
233+ 
234+         // If we can't get metadata from the container, fall back to other tags found by probing. 
235+         // Note that this is only relevant for local files. 
236+         if  metadata. current ( ) . is_none ( ) 
237+             && let  Some ( ref  mut  probe_metadata)  = self . probed_metadata 
238+             && let  Some ( inner_probe_metadata)  = probe_metadata. get ( ) 
239+         { 
240+             metadata = inner_probe_metadata; 
241+         } 
242+ 
243+         // Advance to the latest metadata revision. 
244+         // None means we hit the latest. 
245+         loop  { 
246+             if  metadata. pop ( ) . is_none ( )  { 
247+                 break ; 
248+             } 
249+         } 
250+ 
251+         Some ( metadata) 
252+     } 
253+ 
134254    #[ inline]  
135255    fn  ts_to_ms ( & self ,  ts :  u64 )  -> u32  { 
136256        match  self . decoder . codec_params ( ) . time_base  { 
0 commit comments