| 
					
				 | 
			
			
				@@ -164,47 +164,57 @@ esp_err_t cspotPlayer::handleGET(httpd_req_t *request) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     httpd_resp_set_hdr(request, "Content-type", "application/json"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    httpd_resp_set_hdr(request, "Content-length", std::to_string(body.size()).c_str()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     httpd_resp_send(request, body.c_str(), body.size()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     return ESP_OK; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 esp_err_t cspotPlayer::handlePOST(httpd_req_t *request) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-   cJSON* response= cJSON_CreateObject(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-   cJSON_AddNumberToObject(response, "status", 101); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-   cJSON_AddStringToObject(response, "statusString", "ERROR-OK"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-   cJSON_AddNumberToObject(response, "spotifyError", 0); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    // get body if any (add '\0' at the end if used as string) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	if (request->content_len) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		char* body = (char*) calloc(1, request->content_len + 1); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		int size = httpd_req_recv(request, body, request->content_len); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        // I know this is very crude and unsafe... 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        url_decode(body); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        char *key = strtok(body, "&"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        std::map<std::string, std::string> queryMap; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        while (key) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            char *value = strchr(key, '='); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            *value++ = '\0'; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            queryMap[key] = value; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            key = strtok(NULL, "&"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        }; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        free(body); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        // Pass user's credentials to the blob and give the token 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        blob->loadZeroconfQuery(queryMap); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        clientConnected.give(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    cJSON* response= cJSON_CreateObject(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    // try a command that will tell us if the sink is available */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    if (cmdHandler(CSPOT_BUSY)) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        cJSON_AddNumberToObject(response, "status", 101); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        cJSON_AddStringToObject(response, "statusString", "OK"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        cJSON_AddNumberToObject(response, "spotifyError", 0); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        // get body if any (add '\0' at the end if used as string) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        if (request->content_len) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            char* body = (char*) calloc(1, request->content_len + 1); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            int size = httpd_req_recv(request, body, request->content_len); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            // I know this is very crude and unsafe... 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            url_decode(body); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            char *key = strtok(body, "&"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            std::map<std::string, std::string> queryMap; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            while (key) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                char *value = strchr(key, '='); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                *value++ = '\0'; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                queryMap[key] = value; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                key = strtok(NULL, "&"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            }; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            free(body); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            // Pass user's credentials to the blob and give the token 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            blob->loadZeroconfQuery(queryMap); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            clientConnected.give(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } else { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        cJSON_AddNumberToObject(response, "status", 104); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        cJSON_AddStringToObject(response, "statusString", "ERROR-NOT-IMPLEMENTED"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        cJSON_AddNumberToObject(response, "spotifyError", 501); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+         
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        httpd_resp_set_status(request, "501 Not Implemented");       
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        CSPOT_LOG(info, "sink is busy, can't accept request"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     char *responseStr = cJSON_PrintUnformatted(response); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     cJSON_Delete(response); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    httpd_resp_set_hdr(request, "Content-type", "application/json"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     esp_err_t rc = httpd_resp_send(request, responseStr, strlen(responseStr)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     free(responseStr); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -224,7 +234,7 @@ void cspotPlayer::eventHandler(std::unique_ptr<cspot::SpircHandler::Event> event 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         spirc->setRemoteVolume(volume); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         cmdHandler(CSPOT_START, 44100); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        CSPOT_LOG(info, "restart"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        CSPOT_LOG(info, "(re)start playing"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         break; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     case cspot::SpircHandler::EventType::PLAY_PAUSE: { 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -288,22 +298,47 @@ void cspotPlayer::command(cspot_event_t event) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     if (!spirc) return; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     // switch...case consume a ton of extra .rodata 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    if (event == CSPOT_PREV) spirc->previousSong(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    else if (event == CSPOT_NEXT) spirc->nextSong(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    else if (event == CSPOT_TOGGLE)	spirc->setPause(!chunker->isPaused); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    else if (event == CSPOT_STOP || event == CSPOT_PAUSE) spirc->setPause(true); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    else if (event == CSPOT_PLAY) spirc->setPause(false); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    else if (event == CSPOT_DISC) spirc->disconnect(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    else if (event == CSPOT_VOLUME_UP) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    switch (event) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    // nextSong/previousSong come back through cspot::event as a FLUSH 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    case CSPOT_PREV: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        spirc->previousSong(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        break; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    case CSPOT_NEXT: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        spirc->nextSong(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        break; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    // setPause comes back through cspot::event with PLAY/PAUSE 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    case CSPOT_TOGGLE: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        spirc->setPause(!chunker->isPaused); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        break; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    case CSPOT_STOP: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    case CSPOT_PAUSE: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        spirc->setPause(true); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        break; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    case CSPOT_PLAY: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        spirc->setPause(false); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        break; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    // calling spirc->disconnect() might have been logical but it does not 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    // generate any cspot::event, so we need to manually force exiting player 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    // loop through chunker which will eventually do the disconnect 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    case CSPOT_DISC: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        cmdHandler(CSPOT_DISC); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        chunker->teardown(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        break; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    // spirc->setRemoteVolume does not generate a cspot::event so call cmdHandler 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    case CSPOT_VOLUME_UP: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         volume += (UINT16_MAX / 50); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         volume = std::min(volume, UINT16_MAX); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         cmdHandler(CSPOT_VOLUME, volume); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         spirc->setRemoteVolume(volume); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    } else if (event == CSPOT_VOLUME_DOWN) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        break; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    case CSPOT_VOLUME_DOWN: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         volume -= (UINT16_MAX / 50); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         volume = std::max(volume, 0); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         cmdHandler(CSPOT_VOLUME, volume); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         spirc->setRemoteVolume(volume); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        break; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    default: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        break; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -371,10 +406,10 @@ void cspotPlayer::runTask() { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 }, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 [this](const uint8_t* data, size_t bytes) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                     return dataHandler(data, bytes); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-             }); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-              
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-             // set volume at connection 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-             cmdHandler(CSPOT_VOLUME, volume); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            }); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            // set volume at connection 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            cmdHandler(CSPOT_VOLUME, volume); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             // exit when player has stopped (received a DISC) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             while (chunker->isRunning) { 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -425,7 +460,6 @@ void cspotPlayer::runTask() { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 /**************************************************************************************** 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  * API to create and start a cspot instance 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 struct cspot_s* cspot_create(const char *name, httpd_handle_t server, int port, cspot_cmd_cb_t cmd_cb, cspot_data_cb_t data_cb) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	bell::setDefaultLogger(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     player = new cspotPlayer(name, server, port, cmd_cb, data_cb); 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -436,7 +470,6 @@ struct cspot_s* cspot_create(const char *name, httpd_handle_t server, int port, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 /**************************************************************************************** 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  * Commands sent by local buttons/actions 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 bool cspot_cmd(struct cspot_s* ctx, cspot_event_t event, void *param) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     player->command(event); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	return true; 
			 |