2
2
MiniWebRadio -- Webradio receiver for ESP32
3
3
4
4
first release on 03/2017
5
- Version 2.4d , Mar 13 /2022
5
+ Version 2.5 , Mar 14 /2022
6
6
7
7
2.8" color display (320x240px) with controller ILI9341 or HX8347D (SPI) or
8
8
3.5" color display (480x320px) wiht controller ILI9486 or ILI9488 (SPI)
@@ -371,60 +371,41 @@ inline uint8_t getBrightness(){
371
371
return pref.getUShort (" brightness" );
372
372
}
373
373
/* **********************************************************************************************************************
374
- * A S C I I *
374
+ * U R L d e c o d e *
375
375
***********************************************************************************************************************/
376
- const char * UTF8toASCII (const char * str){
377
- uint16_t i = 0 , j = 0 ;
378
- char tab[96 ] = {
379
- 96 , 173 , 155 , 156 , 32 , 157 , 32 , 32 , 32 , 32 , 166 , 174 , 170 , 32 , 32 , 32 , 248 , 241 , 253 , 32 ,
380
- 32 , 230 , 32 , 250 , 32 , 32 , 167 , 175 , 172 , 171 , 32 , 168 , 32 , 32 , 32 , 32 , 142 , 143 , 146 , 128 ,
381
- 32 , 144 , 32 , 32 , 32 , 32 , 32 , 32 , 32 , 165 , 32 , 32 , 32 , 32 , 153 , 32 , 32 , 32 , 32 , 32 ,
382
- 154 , 32 , 32 , 225 , 133 , 160 , 131 , 32 , 132 , 134 , 145 , 135 , 138 , 130 , 136 , 137 , 141 , 161 , 140 , 139 ,
383
- 32 , 164 , 149 , 162 , 147 , 32 , 148 , 246 , 32 , 151 , 163 , 150 , 129 , 32 , 32 , 152
384
- };
385
- while ((str[i] != 0 ) && (j < 1020 )){
386
- _chbuf[j] = str[i];
387
- if (str[i] == 0xC2 ){ // compute unicode from utf8
388
- i++;
389
- if ((str[i] > 159 ) && (str[i] < 192 )) _chbuf[j] = tab[str[i] - 160 ];
390
- else _chbuf[j] = 32 ;
391
- }
392
- else if (str[i] == 0xC3 ){
393
- i++;
394
- if ((str[i] > 127 ) && (str[i] < 192 )) _chbuf[j] = tab[str[i] - 96 ];
395
- else _chbuf[j] = 32 ;
396
- }
397
- i++; j++;
398
- }
399
- _chbuf[j] = 0 ;
400
- return (_chbuf);
401
- }
402
- const char * ASCIItoUTF8 (const char * str){
403
- uint16_t i = 0 , j = 0 , uni = 0 ;
404
- uint16_t tab[128 ] = {
405
- 199 , 252 , 233 , 226 , 228 , 224 , 229 , 231 , 234 , 235 , 232 , 239 , 238 , 236 , 196 , 197 ,
406
- 201 , 230 , 198 , 244 , 246 , 242 , 251 , 249 , 255 , 214 , 220 , 162 , 163 , 165 , 8359 , 402 ,
407
- 225 , 237 , 243 , 250 , 241 , 209 , 170 , 186 , 191 , 8976 , 172 , 189 , 188 , 161 , 171 , 187 ,
408
- 9617 , 9618 , 9619 , 9474 , 9508 , 9569 , 9570 , 9558 , 9557 , 9571 , 9553 , 9559 , 9565 , 9564 , 9563 , 9488 ,
409
- 9492 , 9524 , 9516 , 9500 , 9472 , 9532 , 9566 , 9567 , 9562 , 9556 , 9577 , 9574 , 9568 , 9552 , 9580 , 9575 ,
410
- 9576 , 9572 , 9573 , 9561 , 9560 , 9554 , 9555 , 9579 , 9578 , 9496 , 9484 , 9608 , 9604 , 9612 , 9616 , 9600 ,
411
- 945 , 223 , 915 , 960 , 931 , 963 , 181 , 964 , 934 , 920 , 937 , 948 , 8734 , 966 , 949 , 8745 ,
412
- 8801 , 177 , 8805 , 8804 , 8992 , 8993 , 247 , 8776 , 176 , 8729 , 183 , 8730 , 8319 , 178 , 9632 , 160
413
- };
414
- while ((str[i] != 0 ) && (j < 1020 )){
415
- uni = str[i];
416
- if (uni >= 128 ){uni -= 128 ; uni = tab[uni];}
417
- // uni=UTF8fromASCII(str[i]);
418
- switch (uni){
419
- case 0 ... 127 :{_chbuf[j] = str[i]; i++; j++; break ;}
420
- case 160 ... 191 :{_chbuf[j] = 0xC2 ; _chbuf[j+1 ] = uni; j += 2 ; i++; break ;}
421
- case 192 ... 255 :{_chbuf[j] = 0xC3 ; _chbuf[j+1 ] = uni - 64 ; j += 2 ; i++; break ;}
422
- default :{_chbuf[j] = ' ' ; i++; j++; break ;} // ignore all other
376
+ // In m3u playlists, file names can be URL encoded.
377
+ // Since UTF-8 is always shorter than URI, the same memory is used for decoding
378
+ // e.g. Born%20On%20The%20B.mp3 --> Born On The B.mp3
379
+ // e.g. %D0%B8%D1%81%D0%BF%D1%8B%D1%82%D0%B0%D0%BD%D0%B8%D0%B5.mp3 --> испытание.mp3
380
+ void urldecode (char *str){
381
+ uint16_t p1 = 0 , p2 = 0 ;
382
+ char a, b;
383
+ while (str[p1]) {
384
+ if ((str[p1] == ' %' ) && ((a = str[p1 + 1 ]) && (b = str[p1 + 2 ])) && (isxdigit (a) && isxdigit (b))) {
385
+ if (a >= ' a' )
386
+ a -= ' a' -' A' ;
387
+ if (a >= ' A' )
388
+ a -= (' A' - 10 );
389
+ else
390
+ a -= ' 0' ;
391
+ if (b >= ' a' )
392
+ b -= ' a' -' A' ;
393
+ if (b >= ' A' )
394
+ b -= (' A' - 10 );
395
+ else
396
+ b -= ' 0' ;
397
+ str[p2++] = 16 *a+b;
398
+ p1+=3 ;
399
+ } else if (str[p1] == ' +' ) {
400
+ str[p2++] = ' ' ;
401
+ p1++;
402
+ } else {
403
+ str[p2++] = str[p1++];
423
404
}
424
405
}
425
- _chbuf[j] = 0 ;
426
- return _chbuf;
406
+ str[p2++] = ' \0 ' ;
427
407
}
408
+
428
409
/* **********************************************************************************************************************
429
410
* T I M E R *
430
411
***********************************************************************************************************************/
@@ -681,7 +662,7 @@ void showLogoAndStationName(){
681
662
}
682
663
display_info (SN_utf8.c_str (), _winName.x , _winName.y , TFT_CYAN, 10 , _winName.h );
683
664
684
- String logo = " /logo/" + String ( UTF8toASCII ( SN_ascii.c_str ()) ) +" .jpg" ;
665
+ String logo = " /logo/" + (String) SN_ascii.c_str () +" .jpg" ;
685
666
if (drawImage (logo.c_str (), 0 , _winName.y + 2 ) == false ){
686
667
drawImage (" /common/unknown.jpg" , 0 , _winName.y + 2 ); // if no draw unknown
687
668
}
@@ -1209,6 +1190,23 @@ const char* byte_to_binary(int8_t x){
1209
1190
}
1210
1191
return b;
1211
1192
}
1193
+ void trim (char *s) {
1194
+ // fb trim in place
1195
+ char *pe;
1196
+ char *p = s;
1197
+ while ( isspace (*p) ) p++; // left
1198
+ pe = p; // right
1199
+ while ( *pe != ' \0 ' ) pe++;
1200
+ do {
1201
+ pe--;
1202
+ } while ( (pe > p) && isspace (*pe) );
1203
+ if (p == s) {
1204
+ *++pe = ' \0 ' ;
1205
+ } else { // move
1206
+ while ( p <= pe ) *s++ = *p++;
1207
+ *s = ' \0 ' ;
1208
+ }
1209
+ }
1212
1210
bool startsWith (const char * base, const char * str) {
1213
1211
char c;
1214
1212
while ( (c = *str++) != ' \0 ' )
@@ -1263,7 +1261,7 @@ void SerialPrintflnCut(const char* item, const char* color, const char* str){
1263
1261
1264
1262
const char * scaleImage (const char * path){
1265
1263
if ((!endsWith (path, " bmp" )) && (!endsWith (path, " jpg" ))){ // not a image
1266
- return UTF8toASCII ( path) ;
1264
+ return path;
1267
1265
}
1268
1266
static char pathBuff[256 ];
1269
1267
memset (pathBuff, 0 , sizeof (pathBuff));
@@ -1280,7 +1278,7 @@ const char* scaleImage(const char* path){
1280
1278
else strcat (pathBuff, _prefix); // medium pic, 480x320px
1281
1279
strcat (pathBuff, path);
1282
1280
}
1283
- return UTF8toASCII ( pathBuff) ;
1281
+ return pathBuff;
1284
1282
}
1285
1283
inline uint8_t getvolume (){
1286
1284
return pref.getUShort (" volume" );
@@ -1414,7 +1412,7 @@ void savefile(const char* fileName, uint32_t contentLength){ //save the uploadfi
1414
1412
strcat (fn, _prefix);
1415
1413
if (!startsWith (fileName, " /" )) strcat (fn, " /" );
1416
1414
strcat (fn, fileName);
1417
- if (webSrv.uploadB64image (SD_MMC, UTF8toASCII (fn) , contentLength)){
1415
+ if (webSrv.uploadB64image (SD_MMC, fn , contentLength)){
1418
1416
SerialPrintfln (" save image " ANSI_ESC_CYAN " %s" ANSI_ESC_WHITE " to SD card was successfully" , fn);
1419
1417
webSrv.reply (" OK" );
1420
1418
}
@@ -1428,7 +1426,7 @@ void savefile(const char* fileName, uint32_t contentLength){ //save the uploadfi
1428
1426
else {
1429
1427
strcpy (fn, fileName);
1430
1428
}
1431
- if (webSrv.uploadfile (SD_MMC, UTF8toASCII (fn) , contentLength)){
1429
+ if (webSrv.uploadfile (SD_MMC, fn , contentLength)){
1432
1430
SerialPrintfln (" save file " ANSI_ESC_CYAN " %s" ANSI_ESC_WHITE " to SD card was successfully" , fn);
1433
1431
webSrv.reply (" OK" );
1434
1432
}
@@ -1486,16 +1484,16 @@ void audiotrack(const char* fileName, uint32_t resumeFilePos){
1486
1484
}
1487
1485
1488
1486
void processPlaylist (boolean first){
1489
- String f = " " ;
1490
1487
String t = " " ;
1491
1488
_playlistTime = millis ();
1492
1489
while (playlistFile.available () > 0 ){
1493
- f = playlistFile.readStringUntil (' \n ' );
1494
- if (f.length () < 5 ) continue ; // line is # or space or nothing, smallest filename "1.mp3" < 5
1495
- if (f.startsWith (" #" )) SerialPrintfln (" Playlist: " ANSI_ESC_GREEN " %s" , f.c_str ());
1496
- f.trim ();
1490
+ size_t bytesRead = playlistFile.readBytesUntil (' \n ' , _chbuf, 512 );
1491
+ _chbuf[bytesRead] = ' \0 ' ;
1492
+ if (bytesRead < 5 ) continue ; // line is # or space or nothing, smallest filename "1.mp3" < 5
1493
+ if (startsWith (_chbuf, " #" ))SerialPrintfln (" Playlist: " ANSI_ESC_GREEN " %s" , _chbuf);
1494
+ trim (_chbuf);
1497
1495
if (first){
1498
- if (f. startsWith (" #EXTM3U" )){
1496
+ if (startsWith (_chbuf, " #EXTM3U" )){
1499
1497
first = false ;
1500
1498
_f_playlistEnabled = true ;
1501
1499
continue ;
@@ -1506,34 +1504,35 @@ void processPlaylist(boolean first){
1506
1504
return ;
1507
1505
}
1508
1506
}
1509
- if (f. startsWith (" #EXTINF" )){
1510
- int8_t idx = f. indexOf (" ," );
1507
+ if (startsWith (_chbuf, " #EXTINF" )){
1508
+ int8_t idx = indexOf (" ," , _chbuf, 0 );
1511
1509
if (idx > 8 ){
1512
- t = f. substring ( idx + 1 ) ;
1510
+ t = _chbuf[ idx + 1 ] ;
1513
1511
t.trim ();
1514
1512
}
1515
1513
continue ;
1516
1514
}
1517
- if (!f. startsWith (" #" )){
1518
- if (f. startsWith (" http" )){
1519
- SerialPrintflnCut (" Playlist: " , ANSI_ESC_YELLOW, f. c_str () );
1515
+ if (!startsWith (_chbuf, " #" )){
1516
+ if (startsWith (_chbuf, " http" )){
1517
+ SerialPrintflnCut (" Playlist: " , ANSI_ESC_YELLOW, _chbuf );
1520
1518
clearFName ();
1521
1519
showVolumeBar ();
1522
1520
if (t.length () > 0 ){
1523
1521
showFileName (t.c_str ());
1524
1522
webSrv.send (" audiotrack=" + t);
1525
1523
}
1526
1524
else {
1527
- showFileName (f. c_str () );
1528
- webSrv.send (" audiotrack=" + f );
1525
+ showFileName (_chbuf );
1526
+ webSrv.send ((String) " audiotrack=" + _chbuf );
1529
1527
}
1530
1528
changeState (PLAYERico);
1531
- audioConnecttohost (f. c_str () );
1529
+ audioConnecttohost (_chbuf );
1532
1530
}
1533
1531
else {
1534
- SerialPrintfln (" Playlist: " ANSI_ESC_YELLOW " %s" , f.c_str ());
1535
- webSrv.send (" audiotrack=" + f);
1536
- audiotrack (f.c_str ());
1532
+ urldecode (_chbuf);
1533
+ SerialPrintfln (" Playlist: " ANSI_ESC_YELLOW " %s" , _chbuf);
1534
+ webSrv.send ((String)" audiotrack=" + _chbuf);
1535
+ audiotrack (_chbuf);
1537
1536
}
1538
1537
return ;
1539
1538
}
@@ -2374,7 +2373,8 @@ void WEBSRV_onCommand(const String cmd, const String param, const String arg){
2374
2373
2375
2374
if (cmd == " change_state" ){ changeState (param.toInt ()); return ;}
2376
2375
2377
- if (cmd == " stopfile" ){ _resumeFilePos = audioStopSong (); webSrv.send (" stopfile=audiofile stopped" ); return ;}
2376
+ if (cmd == " stopfile" ){ _resumeFilePos = audioStopSong (); webSrv.send (" stopfile=audiofile stopped" );
2377
+ if (playlistFile) playlistFile.close (); return ;}
2378
2378
2379
2379
if (cmd == " resumefile" ){ if (!_lastconnectedfile) webSrv.send (" resumefile=nothing to resume" );
2380
2380
else {audiotrack (_lastconnectedfile, _resumeFilePos);
0 commit comments