diff --git a/firmware/libs/buzz_proto/include/buzz_proto.h b/firmware/libs/buzz_proto/include/buzz_proto.h index c15fc09..c62f95a 100644 --- a/firmware/libs/buzz_proto/include/buzz_proto.h +++ b/firmware/libs/buzz_proto/include/buzz_proto.h @@ -40,6 +40,8 @@ enum buzz_data_type BUZZ_DATA_FILE_PUT = 0x21, BUZZ_DATA_TAGS_GET = 0x22, BUZZ_DATA_TAGS_PUT = 0x23, + BUZZ_DATA_RM_FILE = 0x24, + BUZZ_DATA_RENAME_FILE = 0x25, BUZZ_DATA_FW_UPDATE = 0x30, @@ -98,6 +100,23 @@ struct __attribute__((packed)) buzz_resp_fs_info uint8_t data[]; /* Pfadnamen */ }; +/* Payload für das Entfernen einer Datei */ +struct __attribute__((packed)) buzz_rm_file_payload +{ + uint8_t data_type; /* BUZZ_DATA_RM_FILE */ + uint8_t path_length; + char path[]; /* Variabler String ohne Null-Terminierung */ +}; + +/* Payload für das Umbenennen einer Datei */ +struct __attribute__((packed)) buzz_rename_file_payload +{ + uint8_t data_type; /* BUZZ_DATA_RENAME_FILE */ + uint8_t old_path_length; + uint8_t new_path_length; + char paths[]; /* Variabler String ohne Null-Terminierung */ +}; + /* Payload für das Credit-System (ACK) */ struct __attribute__((packed)) buzz_ack_payload { diff --git a/firmware/libs/buzz_proto/src/buzz_proto.c b/firmware/libs/buzz_proto/src/buzz_proto.c index d5a72c5..a2671ab 100644 --- a/firmware/libs/buzz_proto/src/buzz_proto.c +++ b/firmware/libs/buzz_proto/src/buzz_proto.c @@ -382,6 +382,113 @@ static void handle_file_get_request(struct buzz_frame_msg *msg, bool only_tags) } } +static void process_rm_request(struct buzz_frame_msg *msg) +{ + struct buzz_proto_header *hdr = (struct buzz_proto_header *)msg->data_ptr; + uint16_t payload_len = sys_le16_to_cpu(hdr->payload_length); + struct buzz_rm_file_payload *req = (struct buzz_rm_file_payload *)(msg->data_ptr + sizeof(*hdr)); + + if (payload_len < (sizeof(req->data_type) + sizeof(req->path_length))) + { + LOG_ERR("Invalid payload for RM_FILE request"); + send_error_frame(msg, EINVAL); + return; + } + + if ((sizeof(req->data_type) + sizeof(req->path_length) + req->path_length) != payload_len) + { + LOG_ERR("Path length in RM_FILE does not match payload length"); + send_error_frame(msg, EINVAL); + return; + } + + if (req->path_length >= sizeof(src_path)) + { + LOG_ERR("Path too long for RM_FILE request"); + send_error_frame(msg, ENAMETOOLONG); + return; + } + + memcpy(src_path, req->path, req->path_length); + src_path[req->path_length] = '\0'; + + int rc = fs_mgmt_pm_unlink(src_path); + if (rc != 0) + { + LOG_ERR("Failed to remove file '%s': %d", src_path, rc); + send_error_frame(msg, abs(rc)); + return; + } + + LOG_INF("File '%s' removed successfully", src_path); + + hdr->frame_type = BUZZ_FRAME_SUCCESS; + hdr->payload_length = sys_cpu_to_le16(sizeof(struct buzz_resp_success)); + + struct buzz_resp_success *resp_data = (struct buzz_resp_success *)(msg->data_ptr + sizeof(*hdr)); + resp_data->data_type = BUZZ_DATA_RM_FILE; + + if (msg->reply_cb) + { + msg->reply_cb(msg->data_ptr, sizeof(*hdr) + sizeof(*resp_data)); + } +} + +static void process_move_request(struct buzz_frame_msg *msg) +{ + struct buzz_proto_header *hdr = (struct buzz_proto_header *)msg->data_ptr; + uint16_t payload_len = sys_le16_to_cpu(hdr->payload_length); + struct buzz_rename_file_payload *req = (struct buzz_rename_file_payload *)(msg->data_ptr + sizeof(*hdr)); + + if (payload_len < (sizeof(req->data_type) + sizeof(req->old_path_length) + sizeof(req->new_path_length))) + { + LOG_ERR("Invalid payload for RENAME_FILE request"); + send_error_frame(msg, EINVAL); + return; + } + + if ((sizeof(req->data_type) + sizeof(req->old_path_length) + sizeof(req->new_path_length) + req->old_path_length + req->new_path_length) != payload_len) + { + LOG_ERR("Path lengths in RENAME_FILE do not match payload length"); + send_error_frame(msg, EINVAL); + return; + } + + if (req->old_path_length >= sizeof(src_path) || req->new_path_length >= sizeof(dst_path)) + { + LOG_ERR("Source or destination path too long for RENAME_FILE request"); + send_error_frame(msg, ENAMETOOLONG); + return; + } + + memcpy(src_path, req->paths, req->old_path_length); + src_path[req->old_path_length] = '\0'; + + memcpy(dst_path, req->paths + req->old_path_length, req->new_path_length); + dst_path[req->new_path_length] = '\0'; + + int rc = fs_mgmt_pm_rename(src_path, dst_path); + if (rc != 0) + { + LOG_ERR("Failed to rename file from '%s' to '%s': %d", src_path, dst_path, rc); + send_error_frame(msg, abs(rc)); + return; + } + + LOG_INF("File renamed from '%s' to '%s' successfully", src_path, dst_path); + + hdr->frame_type = BUZZ_FRAME_SUCCESS; + hdr->payload_length = sys_cpu_to_le16(sizeof(struct buzz_resp_success)); + + struct buzz_resp_success *resp_data = (struct buzz_resp_success *)(msg->data_ptr + sizeof(*hdr)); + resp_data->data_type = BUZZ_DATA_RENAME_FILE; + + if (msg->reply_cb) + { + msg->reply_cb(msg->data_ptr, sizeof(*hdr) + sizeof(*resp_data)); + } +} + static void process_file_get_stream(void) { uint8_t *buf = NULL; @@ -595,12 +702,22 @@ static void handle_request(struct buzz_frame_msg *msg) send_error_frame(msg, EBUSY); } break; - + case BUZZ_DATA_TAGS_GET: LOG_DBG("Received TAGS_GET Request"); handle_file_get_request(msg, true); break; + case BUZZ_DATA_RM_FILE: + LOG_DBG("Received RM_FILE Request"); + process_rm_request(msg); + break; + + case BUZZ_DATA_RENAME_FILE: + LOG_DBG("Received RENAME_FILE Request"); + process_move_request(msg); + break; + default: LOG_WRN("Unknown request data_type: 0x%02x", req_data->data_type); send_error_frame(msg, EINVAL); diff --git a/webpage/src/lib/sync.ts b/webpage/src/lib/sync.ts index 34678f8..137f760 100644 --- a/webpage/src/lib/sync.ts +++ b/webpage/src/lib/sync.ts @@ -40,7 +40,7 @@ export async function refreshRemote() { const audioFiles = await fetchDirectory(currentFsInfo?.audioPath || "/lfs/a"); let mappedAudio = audioFiles.map(mapToBuzzerFile); - + // Dateien sofort im UI anzeigen, bevor die Tags geladen sind buzzerAudioFiles.set([...mappedAudio]); @@ -52,7 +52,7 @@ export async function refreshRemote() { mappedAudio[i].sysTags = tags.sysTags; mappedAudio[i].metaTags = tags.metaTags; mappedAudio[i].tagsLoaded = true; - + // Store aktualisieren, um das UI pro Datei neu zu rendern buzzerAudioFiles.set([...mappedAudio]); } catch (error) { @@ -72,7 +72,7 @@ export async function refreshLocal() { isFetchingLocal.set(true); try { const dbFiles = await getLocalFiles(); - + // Paralleles Parsen aller Blobs in der lokalen Datenbank const files: BuzzerFile[] = await Promise.all(dbFiles.map(async (record) => { const { sysTags, metaTags } = await parseAudioFileTags(record.blob, record.name); @@ -87,7 +87,7 @@ export async function refreshLocal() { selected: false, }; })); - + localAudioFiles.set(files); } catch (error) { console.error("Fehler beim Laden der lokalen Datenbank:", error); @@ -119,11 +119,11 @@ export async function downloadSelectedFiles() { isFetchingRemote.set(true); try { -for (const file of files) { + for (const file of files) { console.debug(`Starte Download von: ${file.name}`); transferStats.update(s => ({ ...s, pendingFileName: file.name })); - + const fullPath = `${pathPrefix}/${file.name}`; await new Promise(r => setTimeout(r, SETTINGS.ui.transferUpdateIntervalMs)); // Kurze Verzögerung für UI-Update await getFile(fullPath); @@ -156,7 +156,7 @@ export async function deleteSelectedLocalFiles() { for (const file of selectedFiles) { await deleteLocalFile(file.name); } - + await refreshLocal(); } catch (error) { console.error("Fehler beim Löschen lokaler Dateien:", error); @@ -196,8 +196,8 @@ export async function uploadSelectedFiles() { } const fullPath = `${pathPrefix}/${file.name}`; - await new Promise(r => setTimeout(r, SETTINGS.ui.transferUpdateIntervalMs)); - + await new Promise(r => setTimeout(r, SETTINGS.ui.transferUpdateIntervalMs)); + await putFile(dbRecord.blob, fullPath, file.name); } @@ -205,7 +205,7 @@ export async function uploadSelectedFiles() { const avgSpeedKbs = ((totalBytes / 1024) / totalTimeSec).toFixed(1); addToast(` ${files.length === 1 ? "Eine Datei" : files.length + " Dateien"} erfolgreich hochgeladen. (${avgSpeedKbs} kB/s)`, "success"); - + // Buzzer-Ansicht nach erfolgreichem Upload aktualisieren refreshRemote(); } catch (error) {