Skip to content

Commit 1a76c46

Browse files
committed
Deletion of single tracks through the web interface #163
1 parent a7c3f68 commit 1a76c46

File tree

2 files changed

+91
-7
lines changed

2 files changed

+91
-7
lines changed

src/configServer.cpp

+89-5
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ using namespace httpsserver;
4343

4444
static const char *const HTML_ENTITY_FAILED_CROSS = "❌";
4545
static const char *const HTML_ENTITY_OK_MARK = "✅";
46-
46+
static const char *const HTML_ENTITY_WASTEBASKED = "🗑";
4747
static const char *const HTTP_GET = "GET";
4848
static const char *const HTTP_POST = "POST";
4949

@@ -70,6 +70,7 @@ static const char* const header =
7070
// STYLE
7171
"<style>"
7272
"#file-input,input, button {width:100%;height:44px;border-radius:4px;margin:10px auto;font-size:15px;}"
73+
".small {height:12px;width:12px;margin:2px}"
7374
"input, button, a.back {background:#f1f1f1;border:0;padding:0;text-align:center;}"
7475
"body {background:#3498db;font-family:sans-serif;font-size:12px;color:#777}"
7576
"#file-input {padding:0 5px;border:1px solid #ddd;line-height:44px;text-align:left;display:block;cursor:pointer}"
@@ -437,6 +438,7 @@ static void handleMakeCurrentLocationPrivate(HTTPRequest * req, HTTPResponse * r
437438
static void handlePrivacy(HTTPRequest *req, HTTPResponse *res);
438439
static void handlePrivacyDeleteAction(HTTPRequest *req, HTTPResponse *res);
439440
static void handleSd(HTTPRequest *req, HTTPResponse *res);
441+
static void handleDeleteFiles(HTTPRequest *req, HTTPResponse *res);
440442
static void handleDelete(HTTPRequest *req, HTTPResponse *res);
441443
static void handleDeleteAction(HTTPRequest *req, HTTPResponse *res);
442444

@@ -481,6 +483,7 @@ void beginPages() {
481483
server->registerNode(new ResourceNode("/settings/privacy", HTTP_GET, handlePrivacy));
482484
server->registerNode(new ResourceNode("/privacy_delete", HTTP_GET, handlePrivacyDeleteAction));
483485
server->registerNode(new ResourceNode("/sd", HTTP_GET, handleSd));
486+
server->registerNode(new ResourceNode("/deleteFiles", HTTP_POST, handleDeleteFiles));
484487

485488
server->addMiddleware(&accessFilter);
486489
server->setDefaultHeader("Server", std::string("OBS/") + OBSVersion);
@@ -491,8 +494,8 @@ void beginPages() {
491494

492495

493496
void createHttpServer() {
494-
server = new HTTPSServer(&obsCert);
495-
insecureServer = new HTTPServer();
497+
server = new HTTPSServer(&obsCert, 443, 2);
498+
insecureServer = new HTTPServer(80, 1);
496499

497500
log_i("About to create pages.");
498501
beginPages();
@@ -1442,6 +1445,57 @@ static void handleFlashFileUpdateAction(HTTPRequest *req, HTTPResponse *res) {
14421445
sensorManager->attachInterrupts();
14431446
}
14441447

1448+
static void handleDeleteFiles(HTTPRequest *req, HTTPResponse * res) {
1449+
const auto params = extractParameters(req);
1450+
String path = getParameter(params, "path");
1451+
if (path != "trash") {
1452+
SD.mkdir("/trash");
1453+
}
1454+
1455+
String html = replaceDefault(header, "Delete Files");
1456+
html += "<h3>Deleting files</h3>";
1457+
html += "<div>In: " + ObsUtils::encodeForXmlText(path);
1458+
html += "</div><br /><div>";
1459+
sendHtml(res, html);
1460+
html.clear();
1461+
1462+
for (auto param : params) {
1463+
if (param.first == "delete") {
1464+
String file = param.second;
1465+
1466+
String fullName = path + (path.length() > 1 ? "/" : "") + file;
1467+
1468+
html += ObsUtils::encodeForXmlText(file) + " &#10140; ";
1469+
if (path != "trash") {
1470+
if (SD.rename(fullName, "/trash/" + file)) {
1471+
log_i("Moved '%s'.", fullName.c_str());
1472+
html += HTML_ENTITY_WASTEBASKED;
1473+
} else {
1474+
log_w("Failed to move '%s'.", fullName.c_str());
1475+
html += HTML_ENTITY_FAILED_CROSS;
1476+
}
1477+
} else {
1478+
if (SD.remove(fullName)) {
1479+
log_i("Deleted '%s'.", fullName.c_str());
1480+
html += HTML_ENTITY_WASTEBASKED;
1481+
} else {
1482+
log_w("Failed to delete '%s'.", fullName.c_str());
1483+
html += HTML_ENTITY_FAILED_CROSS;
1484+
}
1485+
}
1486+
html += "<br />\n";
1487+
res->print(html);
1488+
html.clear();
1489+
}
1490+
}
1491+
html += "</div>";
1492+
html += "<input type=button onclick=\"window.location.href='/sd?path="
1493+
+ ObsUtils::encodeForUrl(path) + "'\" class='btn' value='Back' />";
1494+
html += footer;
1495+
res->print(html);
1496+
}
1497+
1498+
14451499
static void handleSd(HTTPRequest *req, HTTPResponse *res) {
14461500
String path = getParameter(req, "path", "/");
14471501

@@ -1454,7 +1508,9 @@ static void handleSd(HTTPRequest *req, HTTPResponse *res) {
14541508

14551509
if (file.isDirectory()) {
14561510
String html = header;
1457-
html = replaceDefault(html, "SD Card Contents " + String(file.name()));
1511+
html = replaceDefault(html, "SD Card Contents " + String(file.name()), "/deleteFiles");
1512+
1513+
html += "<input type='hidden' name='path' value='" + ObsUtils::encodeForXmlAttribute(path) + "'/>";
14581514
html += "<ul class=\"directory-listing\">";
14591515
sendHtml(res, html);
14601516
html.clear();
@@ -1468,12 +1524,20 @@ static void handleSd(HTTPRequest *req, HTTPResponse *res) {
14681524
displayTest->drawWaitBar(5, counter++);
14691525

14701526
auto fileName = String(child.name());
1527+
auto fileTip = ObsUtils::encodeForXmlAttribute(
1528+
ObsUtils::dateTimeToString(child.getLastWrite())
1529+
+ " - " + ObsUtils::toScaledByteString(child.size()));
1530+
14711531
fileName = fileName.substring(int(fileName.lastIndexOf("/") + 1));
1532+
fileName = ObsUtils::encodeForXmlAttribute(fileName);
14721533
bool isDirectory = child.isDirectory();
14731534
html +=
14741535
("<li class=\""
14751536
+ String(isDirectory ? "directory" : "file")
1476-
+ "\"><a href=\"/sd?path="
1537+
+ "\" title='" + fileTip + "'>"
1538+
+ "<input class='small' type='checkbox' value='" + fileName + "' name='delete'"
1539+
+ String(isDirectory ? "disabled" : "")
1540+
+ "><a href=\"/sd?path="
14771541
+ String(child.name())
14781542
+ "\">"
14791543
+ String(isDirectory ? "&#x1F4C1;" : "&#x1F4C4;")
@@ -1491,6 +1555,26 @@ static void handleSd(HTTPRequest *req, HTTPResponse *res) {
14911555
}
14921556
file.close();
14931557
html += "</ul>";
1558+
1559+
if (path != "/") {
1560+
String back = path.substring(0, path.lastIndexOf('/'));
1561+
if (back.isEmpty()) {
1562+
back = "/";
1563+
}
1564+
html += "<input type=button onclick=\"window.location.href='/sd?path="
1565+
+ ObsUtils::encodeForUrl(back) + "'\" class='btn' value='Up' />";
1566+
} else {
1567+
html += "<input type=button onclick=\"window.location.href='/'\" "
1568+
"class='btn' value='Menu' />";
1569+
}
1570+
1571+
if (counter > 0) {
1572+
if (path == "/trash") {
1573+
html += "<hr /><input type='submit' class='btn' value='Delete Selected' />";
1574+
} else {
1575+
html += "<hr /><input type='submit' class='btn' value='Move to Trash' />";
1576+
}
1577+
}
14941578
html += footer;
14951579
res->print(html);
14961580
displayTest->clearProgressBar(5);

src/utils/obsutils.cpp

+2-2
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,8 @@ String ObsUtils::dateTimeToString(time_t theTime) {
4545
tm timeStruct;
4646
localtime_r(&theTime, &timeStruct);
4747
snprintf(date, sizeof(date),
48-
"%02d.%02d.%04dT%02d:%02d:%02dZ",
49-
timeStruct.tm_mday, timeStruct.tm_mon + 1, timeStruct.tm_year + 1900,
48+
"%04d-%02d-%02dT%02d:%02d:%02dZ",
49+
timeStruct.tm_year + 1900, timeStruct.tm_mon + 1, timeStruct.tm_mday,
5050
timeStruct.tm_hour, timeStruct.tm_min, timeStruct.tm_sec);
5151
return String(date);
5252
}

0 commit comments

Comments
 (0)