Cleanups. Delete+Move works now.
This commit is contained in:
parent
9361bd333f
commit
ce4ef44dcd
|
|
@ -1,4 +1,5 @@
|
||||||
#include "DirectoryNode.h"
|
#include "DirectoryNode.h"
|
||||||
|
#include "globals.h"
|
||||||
|
|
||||||
DirectoryNode::DirectoryNode(const String &nodeName)
|
DirectoryNode::DirectoryNode(const String &nodeName)
|
||||||
: name(nodeName), currentPlaying(nullptr)
|
: name(nodeName), currentPlaying(nullptr)
|
||||||
|
|
@ -465,7 +466,7 @@ String DirectoryNode::getCurrentPlayingFilePath() const
|
||||||
{
|
{
|
||||||
if (currentPlaying != nullptr)
|
if (currentPlaying != nullptr)
|
||||||
{
|
{
|
||||||
String filePath = "/" + name;
|
String filePath = name;
|
||||||
if (!filePath.endsWith("/"))
|
if (!filePath.endsWith("/"))
|
||||||
{
|
{
|
||||||
filePath += "/";
|
filePath += "/";
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
|
||||||
const String sys_dir = "system";
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,68 +0,0 @@
|
||||||
// HTML web page
|
|
||||||
const char index_html[] PROGMEM = R"rawliteral(
|
|
||||||
<!DOCTYPE HTML><html>
|
|
||||||
<head>
|
|
||||||
<title>HannaBox</title>
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<link rel='stylesheet' href='/style.css' type='text/css' media='all' />
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<h1>🎵 HannaBox 🎵</h1>
|
|
||||||
<span id="state"></span><br/><br/>
|
|
||||||
<span id="voltage"></span><br/>
|
|
||||||
<span id="uid"></span><br/>
|
|
||||||
<span id="heap"></span><br/>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<button class="prev-button" onclick="simpleGetCall('previous');""></button>
|
|
||||||
<button class="play-button" onclick="simpleGetCall('toggleplaypause');"></button>
|
|
||||||
<button class="next-button" onclick="simpleGetCall('next');"></button><br/><br/>
|
|
||||||
</div>
|
|
||||||
<div class="slidecontainer">
|
|
||||||
|
|
||||||
<input name="progress" type="range" min="0" max="100" value="0" class="slider" id="progressSlider"
|
|
||||||
onchange="postValue('progress',document.getElementById('progressSlider').value);lastChange = Date.now();userIsInteracting = false;"
|
|
||||||
oninput="userIsInteracting = true;"
|
|
||||||
>
|
|
||||||
<label for="progress" id="progressLabel"></label>
|
|
||||||
</div>
|
|
||||||
<div class="slidecontainer">
|
|
||||||
<input name="volume" type="range" min="0" max="15" value="7" class="slider" id="volumeSlider"
|
|
||||||
onchange="postValue('volume',document.getElementById('volumeSlider').value);lastChange = Date.now()"
|
|
||||||
>
|
|
||||||
<label for="volume">Vol</label>
|
|
||||||
</div>
|
|
||||||
<!--
|
|
||||||
<button onmouseup="simpleGetCall('stop');" ontouchend="simpleGetCall('stop');">Stop</button>
|
|
||||||
-->
|
|
||||||
|
|
||||||
<p class="playlist-container">
|
|
||||||
<h2>🎶 Playlist 🎶</h2>
|
|
||||||
%DIRECTORY%
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<form id="uploadForm" method="POST" action="/upload" enctype="multipart/form-data">
|
|
||||||
<input type="file" name="data" id="uploadFile" accept=".mp3,.wav,.flac,.m4a,.ogg"/>
|
|
||||||
<input type="submit" name="upload" value="Upload" title="Upload Audio File" id="uploadButton"/>
|
|
||||||
<div id="uploadStatus"></div>
|
|
||||||
<div id="uploadProgress" style="display: none;">
|
|
||||||
<div class="progress-bar">
|
|
||||||
<div class="progress-fill" id="progressFill"></div>
|
|
||||||
</div>
|
|
||||||
<span id="progressText">0%</span>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
|
|
||||||
<h2>Edit RFID Mapping</h2>
|
|
||||||
<form id="editMappingForm">
|
|
||||||
<label for="rfid">RFID:</label>
|
|
||||||
<input type="text" id="rfid" name="rfid" required><br>
|
|
||||||
<label for="song">Song:</label>
|
|
||||||
<input type="text" id="song" name="song" required><br>
|
|
||||||
<button type="button" onclick="editMapping()">Update Mapping</button>
|
|
||||||
</form>
|
|
||||||
|
|
||||||
<script src="/script.js"></script>
|
|
||||||
</body>
|
|
||||||
</html>)rawliteral";
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
// CSS content moved to SD card at /system/style.css
|
|
||||||
// This saves approximately 4KB of flash memory
|
|
||||||
const char css[] PROGMEM = "";
|
|
||||||
|
|
@ -1,93 +1,23 @@
|
||||||
#ifndef GLOBALS_H_
|
#ifndef GLOBALS_H_
|
||||||
#define GLOBALS_H_
|
#define GLOBALS_H_
|
||||||
|
|
||||||
void stop();
|
|
||||||
|
|
||||||
void start();
|
|
||||||
|
|
||||||
bool playFile(const char* filename, uint32_t resumeFilePos = 0);
|
|
||||||
|
|
||||||
void loop2(void* parameter);
|
|
||||||
|
|
||||||
void id_song_action(AsyncWebServerRequest *request);
|
|
||||||
|
|
||||||
void progress_action(AsyncWebServerRequest *request);
|
|
||||||
|
|
||||||
void volume_action(AsyncWebServerRequest *request);
|
|
||||||
|
|
||||||
boolean buttonPressed(const uint8_t pin);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Helper routine to dump a byte array as hex values to Serial.
|
|
||||||
*/
|
|
||||||
void dump_byte_array(byte *buffer, byte bufferSize)
|
|
||||||
{
|
|
||||||
for (byte i = 0; i < bufferSize; i++)
|
|
||||||
{
|
|
||||||
Serial.print(buffer[i] < 0x10 ? " 0" : " ");
|
|
||||||
Serial.print(buffer[i], HEX);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
String getRFIDString(byte uidByte[10])
|
|
||||||
{
|
|
||||||
String uidString = String(uidByte[0]) + " " + String(uidByte[1]) + " " +
|
|
||||||
String(uidByte[2]) + " " + String(uidByte[3]);
|
|
||||||
return uidString;
|
|
||||||
}
|
|
||||||
|
|
||||||
void writeFile(fs::FS &fs, const char * path, const char * message){
|
|
||||||
Serial.printf("Writing file: %s\n", path);
|
|
||||||
|
|
||||||
File file = fs.open(path, FILE_WRITE);
|
|
||||||
if(!file){
|
|
||||||
Serial.println("Failed to open file for writing");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if(file.print(message)){
|
|
||||||
Serial.println("File written");
|
|
||||||
} else {
|
|
||||||
Serial.println("Write failed");
|
|
||||||
}
|
|
||||||
file.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
unsigned long lastStart = 0;
|
|
||||||
|
|
||||||
unsigned long lastInteraction = 0;
|
|
||||||
|
|
||||||
boolean sleepSoundPlayed = false;
|
|
||||||
|
|
||||||
boolean startupSoundPlayed = false;
|
|
||||||
|
|
||||||
boolean continuousMode = false;
|
|
||||||
|
|
||||||
uint8_t buttontoignore = 0;
|
const String sys_dir = "system";
|
||||||
|
|
||||||
uint32_t lastVoltage = 0;
|
|
||||||
|
|
||||||
uint loopCounter = 0;
|
|
||||||
|
|
||||||
String lastUid = "";
|
|
||||||
|
|
||||||
uint16_t currentSongId = 0;
|
|
||||||
|
|
||||||
uint32_t currentSongSeconds = 0;
|
|
||||||
|
|
||||||
boolean continuePlaying = false;
|
|
||||||
|
|
||||||
boolean prepareSleepMode = false;
|
|
||||||
|
|
||||||
const String sleep_sound = "sleep.mp3";
|
const String sleep_sound = "sleep.mp3";
|
||||||
|
|
||||||
const String startup_sound = "start.mp3";
|
const String startup_sound = "start.mp3";
|
||||||
|
|
||||||
const String mapping_file = "/mapping.txt";
|
const String mapping_file = "mapping.txt";
|
||||||
|
|
||||||
const String progress_file = "progress.txt";
|
const String progress_file = "progress.txt";
|
||||||
|
|
||||||
std::map<String, String> rfid_map;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
const long sleepMessageDelay = 28000;
|
const long sleepMessageDelay = 28000;
|
||||||
|
|
|
||||||
43
src/main.cpp
43
src/main.cpp
|
|
@ -47,14 +47,9 @@
|
||||||
#define MAX_VOL 15
|
#define MAX_VOL 15
|
||||||
|
|
||||||
#include "globals.h"
|
#include "globals.h"
|
||||||
#include "WebContent.h"
|
#include "main.h"
|
||||||
#include "DirectoryNode.h"
|
#include "DirectoryNode.h"
|
||||||
|
|
||||||
#define SOUND_STARTUP "start.mp3"
|
|
||||||
#define SOUND_SLEEP "sleep.mp3"
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
File root;
|
File root;
|
||||||
File mp3File;
|
File mp3File;
|
||||||
|
|
@ -280,7 +275,7 @@ void handleDeleteFile(AsyncWebServerRequest *request) {
|
||||||
String filename = request->arg("filename");
|
String filename = request->arg("filename");
|
||||||
|
|
||||||
if (SD.exists(filename)) {
|
if (SD.exists(filename)) {
|
||||||
SD.remove(filename.c_str());
|
SD.remove(filename);
|
||||||
Serial.println("Deleted file: " + filename);
|
Serial.println("Deleted file: " + filename);
|
||||||
request->send(200, "text/plain", "File deleted successfully.");
|
request->send(200, "text/plain", "File deleted successfully.");
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -492,7 +487,7 @@ String getState()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Function to save the rfid_map to the mapping file
|
// Function to save the rfid_map to the mapping file
|
||||||
void saveMappingToFile(const char *filename) {
|
void saveMappingToFile(const String filename) {
|
||||||
File file = SD.open(filename, FILE_WRITE);
|
File file = SD.open(filename, FILE_WRITE);
|
||||||
if (file) {
|
if (file) {
|
||||||
for (const auto &pair : rfid_map) {
|
for (const auto &pair : rfid_map) {
|
||||||
|
|
@ -511,7 +506,7 @@ void editMapping(AsyncWebServerRequest *request) {
|
||||||
String rfid = request->getParam("rfid", true)->value();
|
String rfid = request->getParam("rfid", true)->value();
|
||||||
String song = request->getParam("song", true)->value();
|
String song = request->getParam("song", true)->value();
|
||||||
rfid_map[rfid] = song;
|
rfid_map[rfid] = song;
|
||||||
saveMappingToFile(mapping_file.c_str());
|
saveMappingToFile(getSysDir(mapping_file));
|
||||||
request->send(200, "text/plain", "Mapping updated");
|
request->send(200, "text/plain", "Mapping updated");
|
||||||
} else {
|
} else {
|
||||||
request->send(400, "text/plain", "Invalid parameters");
|
request->send(400, "text/plain", "Invalid parameters");
|
||||||
|
|
@ -752,7 +747,7 @@ void setup()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
String progressPath = "/"+sys_dir+"/"+progress_file;
|
String progressPath = getSysDir(progress_file);
|
||||||
|
|
||||||
continuePlaying = readSongProgress(progressPath.c_str());
|
continuePlaying = readSongProgress(progressPath.c_str());
|
||||||
|
|
||||||
|
|
@ -824,14 +819,14 @@ void setup()
|
||||||
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request)
|
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request)
|
||||||
{
|
{
|
||||||
webrequestActive = true;
|
webrequestActive = true;
|
||||||
String htmlPath = "/system/index.html";
|
String htmlPath = getSysDir("index.html");
|
||||||
if (SD.exists(htmlPath)) {
|
if (SD.exists(htmlPath)) {
|
||||||
AsyncWebServerResponse *response = request->beginResponse(SD, htmlPath, "text/html", false, processor);
|
AsyncWebServerResponse *response = request->beginResponse(SD, htmlPath, "text/html", false, processor);
|
||||||
response->addHeader("Content-Type", "text/html; charset=UTF-8");
|
response->addHeader("Content-Type", "text/html; charset=UTF-8");
|
||||||
request->send(response);
|
request->send(response);
|
||||||
} else {
|
} else {
|
||||||
// Fallback: serve minimal error if file not found
|
// Fallback: serve minimal error if file not found
|
||||||
request->send(200, "text/plain", "ERROR: /system/index.html on SD Card not found!");
|
request->send(404, "text/plain", "ERROR: /system/index.html on SD Card not found!");
|
||||||
}
|
}
|
||||||
webrequestActive = false;
|
webrequestActive = false;
|
||||||
|
|
||||||
|
|
@ -840,12 +835,12 @@ void setup()
|
||||||
server.on("/style.css", HTTP_GET, [](AsyncWebServerRequest *request)
|
server.on("/style.css", HTTP_GET, [](AsyncWebServerRequest *request)
|
||||||
{
|
{
|
||||||
webrequestActive = true;
|
webrequestActive = true;
|
||||||
String cssPath = "/system/style.css";
|
String cssPath = getSysDir("style.css");
|
||||||
if (SD.exists(cssPath)) {
|
if (SD.exists(cssPath)) {
|
||||||
request->send(SD, cssPath, "text/css");
|
request->send(SD, cssPath, "text/css");
|
||||||
} else {
|
} else {
|
||||||
// Fallback: serve minimal CSS if file not found
|
// Fallback: serve minimal CSS if file not found
|
||||||
request->send(200, "text/css", "body{font-family:Arial;text-align:center;padding:20px;}");
|
request->send(404, "text/plain", "ERROR: /system/style.css on SD Card not found!");
|
||||||
}
|
}
|
||||||
webrequestActive = false;
|
webrequestActive = false;
|
||||||
|
|
||||||
|
|
@ -856,12 +851,12 @@ void setup()
|
||||||
webrequestActive = true;
|
webrequestActive = true;
|
||||||
deactivateRFID();
|
deactivateRFID();
|
||||||
activateSD();
|
activateSD();
|
||||||
String jsPath = "/system/script.js";
|
String jsPath = getSysDir("script.js");
|
||||||
if (SD.exists(jsPath)) {
|
if (SD.exists(jsPath)) {
|
||||||
request->send(SD, jsPath, "application/javascript");
|
request->send(SD, jsPath, "application/javascript");
|
||||||
} else {
|
} else {
|
||||||
// Fallback: serve minimal JS if file not found
|
// Fallback: serve minimal JS if file not found
|
||||||
request->send(200, "application/javascript", "console.log('JavaScript file not found on SD card');");
|
request->send(404, "text/plain", "ERROR: /system/script.js on SD Card not found!");
|
||||||
}
|
}
|
||||||
webrequestActive = false;
|
webrequestActive = false;
|
||||||
|
|
||||||
|
|
@ -990,9 +985,9 @@ void volume_action(AsyncWebServerRequest *request)
|
||||||
request->send_P(200, "text/plain", "ok");
|
request->send_P(200, "text/plain", "ok");
|
||||||
}
|
}
|
||||||
|
|
||||||
String getStartupSoundDir() {
|
|
||||||
static String tempPath = "/"+sys_dir+"/"+startup_sound;
|
const String getSysDir(const String filename) {
|
||||||
return tempPath;
|
return "/"+sys_dir+"/"+filename;
|
||||||
}
|
}
|
||||||
|
|
||||||
void loop()
|
void loop()
|
||||||
|
|
@ -1022,7 +1017,7 @@ void loop()
|
||||||
playSongById(currentSongId,currentSongSeconds);
|
playSongById(currentSongId,currentSongSeconds);
|
||||||
} else if (!startupSoundPlayed) {
|
} else if (!startupSoundPlayed) {
|
||||||
startupSoundPlayed = true;
|
startupSoundPlayed = true;
|
||||||
playSongByPath(getStartupSoundDir().c_str());
|
playSongByPath(getSysDir(startup_sound));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1034,12 +1029,12 @@ void loop()
|
||||||
sleepSoundPlayed = true;
|
sleepSoundPlayed = true;
|
||||||
prepareSleepMode = true;
|
prepareSleepMode = true;
|
||||||
if (currentNode != NULL) {
|
if (currentNode != NULL) {
|
||||||
String progressPath = "/"+sys_dir+"/"+progress_file;
|
String progressPath = getSysDir(progress_file);
|
||||||
|
|
||||||
writeSongProgress(progressPath.c_str(),currentNode->getCurrentPlayingId(),currentNode->getSecondsPlayed());
|
writeSongProgress(progressPath.c_str(),currentNode->getCurrentPlayingId(),currentNode->getSecondsPlayed());
|
||||||
}
|
}
|
||||||
|
|
||||||
String tempPath = "/"+sys_dir+"/"+sleep_sound;
|
String tempPath = getSysDir(sleep_sound);
|
||||||
playSongByPath(tempPath.c_str());
|
playSongByPath(tempPath.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1067,7 +1062,7 @@ void loop()
|
||||||
vol++;
|
vol++;
|
||||||
}
|
}
|
||||||
audio.setVolume(vol);
|
audio.setVolume(vol);
|
||||||
playSongByPath(getStartupSoundDir().c_str());
|
playSongByPath(getSysDir(startup_sound));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1083,7 +1078,7 @@ void loop()
|
||||||
vol--;
|
vol--;
|
||||||
}
|
}
|
||||||
audio.setVolume(vol);
|
audio.setVolume(vol);
|
||||||
playSongByPath(getStartupSoundDir().c_str());
|
playSongByPath(getSysDir(startup_sound));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,85 @@
|
||||||
|
#ifndef MAIN_H_
|
||||||
|
#define MAIN_H_
|
||||||
|
|
||||||
|
void stop();
|
||||||
|
|
||||||
|
void start();
|
||||||
|
|
||||||
|
bool playFile(const char* filename, uint32_t resumeFilePos = 0);
|
||||||
|
|
||||||
|
void loop2(void* parameter);
|
||||||
|
|
||||||
|
void id_song_action(AsyncWebServerRequest *request);
|
||||||
|
|
||||||
|
void progress_action(AsyncWebServerRequest *request);
|
||||||
|
|
||||||
|
void volume_action(AsyncWebServerRequest *request);
|
||||||
|
|
||||||
|
boolean buttonPressed(const uint8_t pin);
|
||||||
|
|
||||||
|
const String getSysDir(const String filename);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper routine to dump a byte array as hex values to Serial.
|
||||||
|
*/
|
||||||
|
void dump_byte_array(byte *buffer, byte bufferSize)
|
||||||
|
{
|
||||||
|
for (byte i = 0; i < bufferSize; i++)
|
||||||
|
{
|
||||||
|
Serial.print(buffer[i] < 0x10 ? " 0" : " ");
|
||||||
|
Serial.print(buffer[i], HEX);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String getRFIDString(byte uidByte[10])
|
||||||
|
{
|
||||||
|
String uidString = String(uidByte[0]) + " " + String(uidByte[1]) + " " +
|
||||||
|
String(uidByte[2]) + " " + String(uidByte[3]);
|
||||||
|
return uidString;
|
||||||
|
}
|
||||||
|
|
||||||
|
void writeFile(fs::FS &fs, const char * path, const char * message){
|
||||||
|
Serial.printf("Writing file: %s\n", path);
|
||||||
|
|
||||||
|
File file = fs.open(path, FILE_WRITE);
|
||||||
|
if(!file){
|
||||||
|
Serial.println("Failed to open file for writing");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(file.print(message)){
|
||||||
|
Serial.println("File written");
|
||||||
|
} else {
|
||||||
|
Serial.println("Write failed");
|
||||||
|
}
|
||||||
|
file.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned long lastStart = 0;
|
||||||
|
|
||||||
|
unsigned long lastInteraction = 0;
|
||||||
|
|
||||||
|
boolean sleepSoundPlayed = false;
|
||||||
|
|
||||||
|
boolean startupSoundPlayed = false;
|
||||||
|
|
||||||
|
boolean continuousMode = false;
|
||||||
|
|
||||||
|
uint8_t buttontoignore = 0;
|
||||||
|
|
||||||
|
uint32_t lastVoltage = 0;
|
||||||
|
|
||||||
|
uint loopCounter = 0;
|
||||||
|
|
||||||
|
String lastUid = "";
|
||||||
|
|
||||||
|
uint16_t currentSongId = 0;
|
||||||
|
|
||||||
|
uint32_t currentSongSeconds = 0;
|
||||||
|
|
||||||
|
boolean continuePlaying = false;
|
||||||
|
|
||||||
|
boolean prepareSleepMode = false;
|
||||||
|
|
||||||
|
std::map<String, String> rfid_map;
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@ -75,13 +75,13 @@
|
||||||
<input type="text" id="moveFrom" placeholder="/oldname.mp3"/>
|
<input type="text" id="moveFrom" placeholder="/oldname.mp3"/>
|
||||||
<label for="moveTo">To:</label>
|
<label for="moveTo">To:</label>
|
||||||
<input type="text" id="moveTo" placeholder="/newname.mp3"/>
|
<input type="text" id="moveTo" placeholder="/newname.mp3"/>
|
||||||
<button class="action-btn" onclick="moveFile()">Move/Rename</button>
|
<button type="button" class="action-btn" onclick="moveFile()">Move/Rename</button>
|
||||||
</form>
|
</form>
|
||||||
<form class="form">
|
<form class="form">
|
||||||
<h3>Delete File</h3>
|
<h3>Delete File</h3>
|
||||||
<label for="deleteFile">Filename:</label>
|
<label for="deleteFileName">Filename:</label>
|
||||||
<input type="text" id="deleteFile" placeholder="/song.mp3"/>
|
<input type="text" id="deleteFileName" placeholder="/song.mp3"/>
|
||||||
<button class="action-btn" onclick="deleteFile()">Delete</button>
|
<button type="button" class="action-btn" onclick="deleteFileOnServer()">Delete</button>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -86,9 +86,10 @@ function displayState(state) {
|
||||||
document.getElementById("uid").innerHTML = 'Last NFC ID: '+state['uid'];
|
document.getElementById("uid").innerHTML = 'Last NFC ID: '+state['uid'];
|
||||||
|
|
||||||
/* ==== Autofill convenience fields ==== */
|
/* ==== Autofill convenience fields ==== */
|
||||||
if (state['filepath']) {
|
var fm = document.getElementById('fileManager');
|
||||||
|
if (state['filepath'] && fm.style.display == 'none') {
|
||||||
document.getElementById('moveFrom').value = state['filepath'];
|
document.getElementById('moveFrom').value = state['filepath'];
|
||||||
document.getElementById('deleteFile').value = state['filepath'];
|
document.getElementById('deleteFileName').value = state['filepath'];
|
||||||
document.getElementById('song').value = state['filepath'];
|
document.getElementById('song').value = state['filepath'];
|
||||||
}
|
}
|
||||||
if (state['uid']) {
|
if (state['uid']) {
|
||||||
|
|
@ -304,8 +305,8 @@ function moveFile() {
|
||||||
xhr.send();
|
xhr.send();
|
||||||
}
|
}
|
||||||
|
|
||||||
function deleteFile() {
|
function deleteFileOnServer() {
|
||||||
var filename = document.getElementById('deleteFile').value.trim();
|
var filename = document.getElementById('deleteFileName').value.trim();
|
||||||
if (!filename) {
|
if (!filename) {
|
||||||
alert('Please provide filename to delete.');
|
alert('Please provide filename to delete.');
|
||||||
return;
|
return;
|
||||||
|
|
|
||||||
|
|
@ -1,262 +0,0 @@
|
||||||
setInterval(getState, 4000);
|
|
||||||
setInterval(updateProgress, 500); // Update progress every second
|
|
||||||
|
|
||||||
// Get the <li> elements
|
|
||||||
var liElements = document.querySelectorAll('ul li');
|
|
||||||
|
|
||||||
var lastChange = 0;
|
|
||||||
|
|
||||||
var lastStateUpdateTime = Date.now();
|
|
||||||
var songStartTime = 0;
|
|
||||||
var currentSongLength = 0;
|
|
||||||
var isPlaying = false;
|
|
||||||
var userIsInteracting = false; // Flag to track user interaction with the slider
|
|
||||||
|
|
||||||
// Add click event listener to each <li> element
|
|
||||||
liElements.forEach(function(li) {
|
|
||||||
li.addEventListener('click', function() {
|
|
||||||
//var liText = this.innerText;
|
|
||||||
var id = this.dataset.id;
|
|
||||||
playSongById(id);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
function simpleGetCall(endpoint) {
|
|
||||||
var xhr = new XMLHttpRequest();
|
|
||||||
xhr.open("GET", "/" + endpoint, true);
|
|
||||||
|
|
||||||
xhr.onreadystatechange = function() {
|
|
||||||
if (xhr.readyState === 4) {
|
|
||||||
getState(); // Fetch the latest state right after the button action
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
xhr.send();
|
|
||||||
}
|
|
||||||
|
|
||||||
function postValue(endpoint,value) {
|
|
||||||
var xhr = new XMLHttpRequest();
|
|
||||||
xhr.open("POST", "/" + endpoint, true);
|
|
||||||
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
|
|
||||||
xhr.send("value="+encodeURIComponent(value));
|
|
||||||
|
|
||||||
xhr.onreadystatechange = function() {
|
|
||||||
if (xhr.readyState === 4) {
|
|
||||||
getState(); // Fetch the latest state right after the button action
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function getState() {
|
|
||||||
var xhr = new XMLHttpRequest();
|
|
||||||
xhr.onreadystatechange = function() {
|
|
||||||
if (xhr.readyState === 4) {
|
|
||||||
var state = JSON.parse(xhr.response);
|
|
||||||
isPlaying = state['playing'];
|
|
||||||
if (isPlaying) {
|
|
||||||
songStartTime = Date.now() - state['time'] * 1000;
|
|
||||||
currentSongLength = state['length'] * 1000;
|
|
||||||
}
|
|
||||||
lastStateUpdateTime = Date.now();
|
|
||||||
displayState(state);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
xhr.open("GET","/state", true);
|
|
||||||
xhr.send();
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateProgress() {
|
|
||||||
if (isPlaying && !userIsInteracting) { // Check if user is not interacting
|
|
||||||
var elapsedTime = Date.now() - songStartTime;
|
|
||||||
if (elapsedTime >= currentSongLength) {
|
|
||||||
elapsedTime = currentSongLength;
|
|
||||||
isPlaying = false; // Stop updating if the song has ended
|
|
||||||
}
|
|
||||||
var progressElement = document.getElementById('progressSlider');
|
|
||||||
progressElement.value = elapsedTime / 1000; // Convert to seconds
|
|
||||||
document.getElementById("progressLabel").innerHTML = Math.floor(elapsedTime / 1000);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function displayState(state) {
|
|
||||||
document.getElementById("state").innerHTML = state['title'];
|
|
||||||
document.getElementById("progressLabel").innerHTML = state['time'];
|
|
||||||
document.getElementById("voltage").innerHTML = state['voltage']+' mV';
|
|
||||||
document.getElementById("heap").innerHTML = state['heap']+' bytes free heap';
|
|
||||||
document.getElementById("uid").innerHTML = 'Last NFC ID: '+state['uid'];
|
|
||||||
var elements = document.getElementsByClassName('play-button');
|
|
||||||
var btn = elements[0];
|
|
||||||
|
|
||||||
if (state['playing']) {
|
|
||||||
btn.classList.add('paused');
|
|
||||||
} else {
|
|
||||||
btn.classList.remove('paused');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Date.now()-lastChange>1200) {
|
|
||||||
var progress = document.getElementById('progressSlider');
|
|
||||||
progress.value = state['time'];
|
|
||||||
progress.max = state['length'];
|
|
||||||
|
|
||||||
var volume = document.getElementById('volumeSlider');
|
|
||||||
volume.value = state['volume'];
|
|
||||||
}
|
|
||||||
updateProgress();
|
|
||||||
}
|
|
||||||
|
|
||||||
function playSongById(id) {
|
|
||||||
var url = "/playbyid";
|
|
||||||
var params = "id="+id;
|
|
||||||
var http = new XMLHttpRequest();
|
|
||||||
|
|
||||||
http.open("GET", url+"?"+params, true);
|
|
||||||
http.onreadystatechange = function()
|
|
||||||
{
|
|
||||||
if(http.readyState == 4 && http.status == 200) {
|
|
||||||
getState();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
http.send(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
function playNamedSong(song) {
|
|
||||||
var xhr = new XMLHttpRequest();
|
|
||||||
xhr.open("POST", "/playnamed");
|
|
||||||
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
|
|
||||||
//application/x-www-form-urlencoded
|
|
||||||
var body = song;
|
|
||||||
xhr.send("title="+encodeURIComponent(body));
|
|
||||||
}
|
|
||||||
|
|
||||||
function editMapping() {
|
|
||||||
var rfid = document.getElementById('rfid').value;
|
|
||||||
var song = document.getElementById('song').value;
|
|
||||||
var xhr = new XMLHttpRequest();
|
|
||||||
xhr.open("POST", "/edit_mapping", true);
|
|
||||||
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
|
|
||||||
xhr.send("rfid=" + encodeURIComponent(rfid) + "&song=" + encodeURIComponent(song));
|
|
||||||
xhr.onreadystatechange = function() {
|
|
||||||
if (xhr.readyState === 4 && xhr.status === 200) {
|
|
||||||
alert("Mapping updated successfully!");
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validate file before upload
|
|
||||||
function validateFile(file) {
|
|
||||||
var maxSize = 50 * 1024 * 1024; // 50MB limit
|
|
||||||
var allowedTypes = ['audio/mpeg', 'audio/wav', 'audio/flac', 'audio/mp4', 'audio/ogg'];
|
|
||||||
var allowedExtensions = ['.mp3', '.wav', '.flac', '.m4a', '.ogg'];
|
|
||||||
|
|
||||||
if (file.size > maxSize) {
|
|
||||||
return 'File too large. Maximum size is 50MB.';
|
|
||||||
}
|
|
||||||
|
|
||||||
var fileName = file.name.toLowerCase();
|
|
||||||
var hasValidExtension = allowedExtensions.some(ext => fileName.endsWith(ext));
|
|
||||||
|
|
||||||
if (!hasValidExtension) {
|
|
||||||
return 'Invalid file type. Only audio files (.mp3, .wav, .flac, .m4a, .ogg) are allowed.';
|
|
||||||
}
|
|
||||||
|
|
||||||
return null; // No error
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle form submission with AJAX to show upload status
|
|
||||||
document.getElementById('uploadForm').addEventListener('submit', function(event) {
|
|
||||||
event.preventDefault(); // Prevent the default form submit
|
|
||||||
|
|
||||||
var fileInput = document.getElementById('uploadFile');
|
|
||||||
var file = fileInput.files[0];
|
|
||||||
|
|
||||||
if (!file) {
|
|
||||||
alert('Please select a file to upload.');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var validationError = validateFile(file);
|
|
||||||
if (validationError) {
|
|
||||||
alert(validationError);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var form = event.target;
|
|
||||||
var formData = new FormData(form);
|
|
||||||
var uploadButton = document.getElementById('uploadButton');
|
|
||||||
var uploadStatus = document.getElementById('uploadStatus');
|
|
||||||
var uploadProgress = document.getElementById('uploadProgress');
|
|
||||||
var progressFill = document.getElementById('progressFill');
|
|
||||||
var progressText = document.getElementById('progressText');
|
|
||||||
|
|
||||||
// Disable upload button and show progress
|
|
||||||
uploadButton.disabled = true;
|
|
||||||
uploadButton.value = 'Uploading...';
|
|
||||||
uploadProgress.style.display = 'block';
|
|
||||||
uploadStatus.innerHTML = 'Preparing upload...';
|
|
||||||
|
|
||||||
var xhr = new XMLHttpRequest();
|
|
||||||
xhr.open('POST', '/upload', true);
|
|
||||||
|
|
||||||
xhr.upload.onloadstart = function() {
|
|
||||||
uploadStatus.innerHTML = 'Upload started...';
|
|
||||||
};
|
|
||||||
|
|
||||||
xhr.upload.onprogress = function(event) {
|
|
||||||
if (event.lengthComputable) {
|
|
||||||
var percentComplete = Math.round((event.loaded / event.total) * 100);
|
|
||||||
progressFill.style.width = percentComplete + '%';
|
|
||||||
progressText.innerHTML = percentComplete + '%';
|
|
||||||
uploadStatus.innerHTML = 'Uploading: ' + percentComplete + '% (' +
|
|
||||||
Math.round(event.loaded / 1024) + 'KB / ' +
|
|
||||||
Math.round(event.total / 1024) + 'KB)';
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
xhr.upload.onerror = function() {
|
|
||||||
uploadStatus.innerHTML = 'Upload failed due to network error.';
|
|
||||||
resetUploadForm();
|
|
||||||
};
|
|
||||||
|
|
||||||
xhr.upload.onabort = function() {
|
|
||||||
uploadStatus.innerHTML = 'Upload was cancelled.';
|
|
||||||
resetUploadForm();
|
|
||||||
};
|
|
||||||
|
|
||||||
xhr.onreadystatechange = function() {
|
|
||||||
if (xhr.readyState === 4) { // Request is done
|
|
||||||
if (xhr.status >= 200 && xhr.status < 300) { // Success status code range
|
|
||||||
uploadStatus.innerHTML = 'Upload completed successfully!';
|
|
||||||
progressFill.style.width = '100%';
|
|
||||||
progressText.innerHTML = '100%';
|
|
||||||
|
|
||||||
setTimeout(function() {
|
|
||||||
alert('File uploaded successfully!');
|
|
||||||
location.reload(); // Reload to get updated playlist
|
|
||||||
}, 1000);
|
|
||||||
} else {
|
|
||||||
var errorMsg = xhr.responseText || 'Unknown error occurred';
|
|
||||||
uploadStatus.innerHTML = 'Upload failed: ' + errorMsg;
|
|
||||||
alert('Upload failed: ' + errorMsg);
|
|
||||||
resetUploadForm();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
xhr.send(formData); // Send the form data using XMLHttpRequest
|
|
||||||
});
|
|
||||||
|
|
||||||
function resetUploadForm() {
|
|
||||||
var uploadButton = document.getElementById('uploadButton');
|
|
||||||
var uploadProgress = document.getElementById('uploadProgress');
|
|
||||||
var progressFill = document.getElementById('progressFill');
|
|
||||||
var progressText = document.getElementById('progressText');
|
|
||||||
|
|
||||||
uploadButton.disabled = false;
|
|
||||||
uploadButton.value = 'Upload';
|
|
||||||
uploadProgress.style.display = 'none';
|
|
||||||
progressFill.style.width = '0%';
|
|
||||||
progressText.innerHTML = '0%';
|
|
||||||
|
|
||||||
// Clear file input
|
|
||||||
document.getElementById('uploadFile').value = '';
|
|
||||||
}
|
|
||||||
Loading…
Reference in New Issue