@@ -184,6 +184,62 @@ bool ESP32_VS1053_Stream::_escapeUrl(const char *url, size_t len)
184184 return true ;
185185}
186186
187+ bool ESP32_VS1053_Stream::_isPlaylistContentType ()
188+ {
189+ const String contentType = _http->header (CONTENT_TYPE);
190+ const char *ct = contentType.c_str ();
191+
192+ return strcasestr (ct, " audio/x-scpls" ) ||
193+ strcasestr (ct, " audio/scpls" ) ||
194+ strcasestr (ct, " audio/x-mpegurl" ) ||
195+ strcasestr (ct, " application/x-mpegurl" ) ||
196+ strcasestr (ct, " audio/mpegurl" );
197+ }
198+
199+ const char *ESP32_VS1053_Stream::_parsePlaylist ()
200+ {
201+ WiFiClient *stream = _http->getStreamPtr ();
202+ if (!stream)
203+ {
204+ log_e (" No stream handle" );
205+ return nullptr ;
206+ }
207+
208+ char *line = reinterpret_cast <char *>(_localbuffer);
209+
210+ while (stream->connected () && stream->available ())
211+ {
212+ size_t len = stream->readBytesUntil (' \n ' , line, VS1053_MAX_URL_LENGTH - 1 );
213+
214+ if (len == 0 )
215+ continue ;
216+
217+ line[len] = ' \0 ' ;
218+
219+ // Handle truncated lines
220+ if (len == VS1053_MAX_URL_LENGTH - 1 )
221+ {
222+ int c;
223+ while (stream->available () && (c = stream->read ()) != ' \n ' )
224+ ;
225+ }
226+
227+ // Skip comments (M3U, EXTINF, etc.)
228+ if (line[0 ] == ' #' || line[0 ] == ' \0 ' )
229+ continue ;
230+
231+ // Find URL
232+ char *newUrl = strstr (line, " http" );
233+ if (newUrl)
234+ {
235+ strtok (newUrl, " \r\n ;?" );
236+ return newUrl;
237+ }
238+ }
239+
240+ return nullptr ;
241+ }
242+
187243bool ESP32_VS1053_Stream::isChipConnected ()
188244{
189245 return _vs1053 ? _vs1053->isChipConnected () : false ;
@@ -219,13 +275,6 @@ bool ESP32_VS1053_Stream::connectToHost(const char *url, const char *username,
219275 return false ;
220276 }
221277
222- if (strstr (url, " ./" ))
223- { // hacky solution: some items on radio-browser.info has
224- // non resolving names that contain './' in their hostname
225- log_e (" Invalid url not started" );
226- return false ;
227- }
228-
229278 bool needsEscape = false ;
230279 for (size_t i = 0 ; i < length; ++i)
231280 {
@@ -285,57 +334,25 @@ bool ESP32_VS1053_Stream::connectToHost(const char *url, const char *username,
285334 [[fallthrough]];
286335 case 200 :
287336 {
288- const String contentType = _http->header (CONTENT_TYPE);
289- const char *ct = contentType.c_str ();
290- if (strcasestr (ct, " audio/x-scpls" ) ||
291- strcasestr (ct, " audio/scpls" ) ||
292- strcasestr (ct, " audio/x-mpegurl" ) ||
293- strcasestr (ct, " application/x-mpegurl" ) ||
294- strcasestr (ct, " audio/mpegurl" ))
337+ if (_isPlaylistContentType ())
295338 {
296- log_d (" url %s is a playlist" , url);
297339 if (!_canRedirect ())
298340 {
299- stopSong ();
341+ _eofStream ();
342+ _redirectCount = 0 ;
300343 return false ;
301344 }
302- WiFiClient *stream = _http->getStreamPtr ();
303- if (!stream)
345+
346+ const char *newUrl = _parsePlaylist ();
347+ if (newUrl)
304348 {
305- log_e ( " No stream handle " );
349+ log_i ( " playlist redirection to: %s " , newUrl );
306350 stopSong ();
307- return false ;
308- }
309-
310- char *line = reinterpret_cast <char *>(_localbuffer);
311-
312- while (stream->connected () && stream->available ())
313- {
314- size_t len = stream->readBytesUntil (' \n ' , line, VS1053_MAX_URL_LENGTH - 1 );
315-
316- if (len == 0 )
317- continue ;
318-
319- line[len] = ' \0 ' ;
320-
321- // Detect truncated line (no newline encountered)
322- if (len == VS1053_MAX_URL_LENGTH - 1 )
323- {
324- int c;
325- // Flush until end-of-line to avoid corrupt next read
326- while (stream->available () && (c = stream->read ()) != ' \n ' )
327- ;
328- }
329-
330- char *newUrl = strstr (line, " http" );
331- if (newUrl)
332- {
333- strtok (newUrl, " \r\n ;?" );
334- stopSong ();
335- log_i (" playlist %s reconnects to: %s" , url, newUrl);
336- return connectToHost (newUrl, username, pwd, offset);
337- }
351+ return connectToHost (newUrl, username, pwd, offset);
338352 }
353+ _eofStream ();
354+ _redirectCount = 0 ;
355+ return false ;
339356 }
340357
341358 if (_stationCallback && !_http->header (ICY_NAME).equals (" " ))
@@ -353,7 +370,7 @@ bool ESP32_VS1053_Stream::connectToHost(const char *url, const char *username,
353370 _vs1053->startSong ();
354371 }
355372 _streamStallStartMS = 0 ;
356- log_d (" redirected %i times to %s" , _redirectCount, url);
373+ log_i (" redirected %i times to %s" , _redirectCount, url);
357374 _redirectCount = 0 ;
358375 _vs1053->setVolume (_volume);
359376 return true ;
@@ -365,30 +382,23 @@ bool ESP32_VS1053_Stream::connectToHost(const char *url, const char *username,
365382 {
366383 if (!_canRedirect ())
367384 {
368- stopSong ();
385+ _eofStream ();
386+ _redirectCount = 0 ;
369387 return false ;
370388 }
371389
372390 if (!_http->hasHeader (LOCATION))
373391 {
374392 log_e (" No location header redirecting from %s" , url);
375- stopSong ();
393+ _eofStream ();
394+ _redirectCount = 0 ;
376395 return false ;
377396 }
378397
379398 const String location = _http->header (LOCATION);
380399
381- // hacky solution: some items on radio-browser.info
382- // have non-resolving names containing "./" in the hostname
383- if (location.indexOf (" ./" ) != -1 )
384- {
385- log_e (" Invalid url %s redirecting from %s" , location, url);
386- stopSong ();
387- return false ;
388- }
389-
390400 stopSong ();
391- log_d (" %i redirection to: %s" , HTTPresult, location.c_str ());
401+ log_i (" %i redirection to: %s" , HTTPresult, location.c_str ());
392402 return connectToHost (location.c_str (), username, pwd, 0 );
393403 }
394404
0 commit comments