Skip to content

Commit 8896c6c

Browse files
Refactor playlist code and parsing (#103)
1 parent bca6fa3 commit 8896c6c

2 files changed

Lines changed: 75 additions & 63 deletions

File tree

src/ESP32_VS1053_Stream.cpp

Lines changed: 73 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -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+
187243
bool 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

src/ESP32_VS1053_Stream.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,8 @@ class ESP32_VS1053_Stream
116116
void _eofStream();
117117
bool _canRedirect();
118118
bool _escapeUrl(const char *url, size_t len);
119+
bool _isPlaylistContentType();
120+
const char *_parsePlaylist();
119121
void _handleStream(WiFiClient *stream);
120122
void _handleChunkedStream(WiFiClient *stream);
121123
void _handleLocalFile();

0 commit comments

Comments
 (0)