return ret;
}
-// 累积并解析SSE数据(补充实现)
-void ln_sse_accumulate_and_dispatch(int cmd, const char *data, int data_len,
- char *acc, int *acc_len, int acc_size) {
- if (!data || data_len <= 0 || !acc || !acc_len || *acc_len >= acc_size) return;
-
- // 累积数据
- int copy_len = (acc_size - *acc_len - 1) < data_len ? (acc_size - *acc_len - 1) : data_len;
- memcpy(acc + *acc_len, data, copy_len);
- *acc_len += copy_len;
- acc[*acc_len] = '\0';
-
- // 分割SSE事件(按"\n\n"分割)
- char *ptr = acc;
- while ((ptr = strstr(ptr, "\n\n")) != NULL) {
- int event_len = ptr - acc;
- if (event_len > 0) {
- // 提取data字段(去掉"date: "前缀)
- char *data_start = strstr(acc, "data: ");
- if (data_start) {
- data_start += 6; // 跳过"data: "
- int json_len = (acc + event_len) - data_start;
- char *json = (char *)malloc(json_len + 1);
- if (json) {
- memcpy(json, data_start, json_len);
- json[json_len] = '\0';
- // 投递解析后的JSON数据
- do_ln_http_server_recv_data(cmd, json, json_len);
- free(json);
- }
- }
- }
- // 移动指针处理剩余数据
- *acc_len -= (ptr - acc + 2);
- memmove(acc, ptr + 2, *acc_len);
- acc[*acc_len] = '\0';
- ptr = acc;
- }
-}
-
/* 计算文件MD5(小写hex)*/
static int ln_calc_file_md5_lower_hex(const char *file_path, char out_hex[33])
{
OSAL_MSGQ_WAIT_FOREVER);
if (ret != OSAL_SUCCESS)
{
- printf("[RECV_TASK] 消息读取失败: %d\n", ret);
continue;
}
// 消息有效性检查
if (!mail.data || mail.len <= 0)
{
- printf("[RECV_TASK] 无效消息: data=%p, len=%u\n", mail.data, mail.len);
continue;
}
if (recv_data.data) free(recv_data.data);
}
}
+
+
+/**
+ * 高级MP3下载函数,支持自定义保存目录和文件名
+ * @param audio_url 音频文件URL
+ * @param save_dir 保存目录
+ * @param filename 文件名(不包含路径)
+ * @return ERRCODE_SUCC 成功,ERRCODE_FAIL 失败
+ */
+static uint32_t ln_download_mp3_file_advanced(const char *audio_url, const char *save_dir, const char *filename)
+{
+ if (!audio_url || !save_dir || !filename)
+ {
+ printf("[SSE] download_mp3_advanced: invalid parameters\n");
+ return ERRCODE_FAIL;
+ }
+
+ // 构建完整保存路径
+ char save_path[512];
+ snprintf(save_path, sizeof(save_path), "%s/%s", save_dir, filename);
+
+ // 确保目录存在
+ if (access(save_dir, F_OK) == -1)
+ {
+ uapi_create_folder(save_dir);
+ }
+
+ printf("[SSE] downloading MP3: %s -> %s\n", audio_url, save_path);
+
+ /* CAT1 网络保活处理 */
+ cat1_api_add_cat1_vote();
+ cat1_api_wakeup_cat1(1);
+ cat1_api_keep_wakeup(1);
+ cat1_api_disable_cat1_cmd(0);
+ LOS_Msleep(300);
+
+ HttpClient client = {0};
+ HttpClientData client_data = {0};
+ int ret = -1;
+ size_t total_written = 0;
+
+ // 打开文件准备写入
+ FILE *fd = fopen(save_path, "wb");
+ if (!fd)
+ {
+ printf("[SSE] fopen failed for %s\n", save_path);
+ return ERRCODE_FAIL;
+ }
+
+ HTTPC_RESULT prep = ln_http_prepare_auto(&client_data, 2048, LN_CLIENT_PREPARE_SIZE * 2);
+ if (prep != HTTP_SUCCESS)
+ {
+ printf("[SSE] prepare failed: %d\n", prep);
+ fclose(fd);
+ return ERRCODE_FAIL;
+ }
+
+ // 建立连接
+ ret = HttpClientConn(&client, audio_url);
+ if (ret != 0)
+ {
+ printf("[SSE] connection failed, ret=0x%x\n", ret);
+ fclose(fd);
+ HttpClientUnprepare(&client_data);
+ return ERRCODE_FAIL;
+ }
+
+ // 发送GET请求
+ ret = HttpClientSend(&client, audio_url, HTTP_GET, &client_data);
+ if (ret != 0)
+ {
+ printf("[SSE] send request failed, ret=0x%x\n", ret);
+ fclose(fd);
+ HttpClientClse(&client);
+ HttpClientUnprepare(&client_data);
+ return ERRCODE_FAIL;
+ }
+
+ // 接收响应数据
+ do
+ {
+ HTTPC_RESULT recv_res = HttpClientRecvResponse(&client, &client_data);
+ if (recv_res == HTTP_SUCCESS || recv_res == HTTP_EAGAIN)
+ {
+ if (client_data.contentBlockLen > 0)
+ {
+ size_t written = fwrite(client_data.responseBuf, 1, client_data.contentBlockLen, fd);
+ if (written != client_data.contentBlockLen)
+ {
+ printf("[SSE] write failed, written=%zu, expected=%d\n",
+ written, client_data.contentBlockLen);
+ fclose(fd);
+ HttpClientClse(&client);
+ HttpClientUnprepare(&client_data);
+ return ERRCODE_FAIL;
+ }
+ total_written += written;
+ }
+ }
+ else
+ {
+ printf("[SSE] receive ended (code: %d)\n", recv_res);
+ break;
+ }
+ } while (client_data.isMore);
+
+ fclose(fd);
+ HttpClientUnprepare(&client_data);
+ HttpClientClse(&client);
+
+ printf("[SSE] download completed, total bytes: %zu\n", total_written);
+ return ERRCODE_SUCC;
+}
+
+/**
+ * 解析JSON数据,下载MP3并推送消息
+ * @param json_data JSON数据字符串
+ * @param interface_type 接口类型
+ * @param segment_index 片段索引
+ * @return ERRCODE_SUCC 成功,ERRCODE_FAIL 失败
+ */
+uint32_t ln_parse_and_process_json_data(const char *json_data, int interface_type, int segment_index)
+{
+ if (!json_data)
+ {
+ printf("[JSON_PARSE] invalid json_data\n");
+ return ERRCODE_FAIL;
+ }
+
+ printf("[JSON_PARSE] parsing JSON data (interface_type=%d, segment=%d): %s\n",
+ interface_type, segment_index, json_data);
+
+ // 解析JSON数据
+ cJSON *root = cJSON_Parse(json_data);
+ if (!root)
+ {
+ printf("[JSON_PARSE] JSON parse failed: %s\n", json_data);
+ return ERRCODE_FAIL;
+ }
+
+ // 检查是否有错误
+ cJSON *errorCode = cJSON_GetObjectItem(root, "errorCode");
+ if (errorCode && !cJSON_IsNull(errorCode))
+ {
+ printf("[JSON_PARSE] server error: %s\n",
+ cJSON_IsString(errorCode) ? errorCode->valuestring : "unknown");
+ cJSON_Delete(root);
+ return ERRCODE_FAIL;
+ }
+
+ // 获取data字段
+ cJSON *data = cJSON_GetObjectItem(root, "data");
+ if (!data || !cJSON_IsObject(data))
+ {
+ printf("[JSON_PARSE] no data field found\n");
+ cJSON_Delete(root);
+ return ERRCODE_FAIL;
+ }
+
+ // 根据接口类型获取音频URL
+ char *audio_url = NULL;
+ if (interface_type == LN_HTTP_SERVICE_CMD_SSE_RECV)
+ {
+ // 语音对话接口:使用 audioUrl 字段
+ cJSON *audioUrl = cJSON_GetObjectItem(data, "audioUrl");
+ if (audioUrl && cJSON_IsString(audioUrl))
+ {
+ audio_url = audioUrl->valuestring;
+ }
+ }
+ else if (interface_type == LN_HTTP_RECV_STORY_CHOOSE || interface_type == LN_HTTP_RECV_STORY_RANDOM)
+ {
+ // 故事接口:使用 storyAudioUrl 字段
+ cJSON *storyAudioUrl = cJSON_GetObjectItem(data, "storyAudioUrl");
+ if (storyAudioUrl && cJSON_IsString(storyAudioUrl))
+ {
+ audio_url = storyAudioUrl->valuestring;
+ }
+ }
+ else
+ {
+ // 默认尝试 audioUrl 字段
+ cJSON *audioUrl = cJSON_GetObjectItem(data, "audioUrl");
+ if (audioUrl && cJSON_IsString(audioUrl))
+ {
+ audio_url = audioUrl->valuestring;
+ }
+ }
+
+ // 获取textContent
+ cJSON *textContent = cJSON_GetObjectItem(data, "textContent");
+ char *text_content = NULL;
+ if (textContent && cJSON_IsString(textContent))
+ {
+ text_content = textContent->valuestring;
+ }
+
+ // 获取intention
+ cJSON *intention = cJSON_GetObjectItem(data, "intention");
+ char *intention_str = NULL;
+ if (intention && cJSON_IsString(intention))
+ {
+ intention_str = intention->valuestring;
+ }
+
+ // 获取storyTheme(故事接口特有)
+ cJSON *storyTheme = cJSON_GetObjectItem(data, "storyTheme");
+ char *story_theme = NULL;
+ if (storyTheme && cJSON_IsString(storyTheme))
+ {
+ story_theme = storyTheme->valuestring;
+ }
+
+ // 获取storyContent(故事接口特有)
+ cJSON *storyContent = cJSON_GetObjectItem(data, "storyContent");
+ char *story_content = NULL;
+ if (storyContent && cJSON_IsString(storyContent))
+ {
+ story_content = storyContent->valuestring;
+ }
+
+ // 创建推送数据
+ cJSON *push_data = cJSON_CreateObject();
+
+ // 根据接口类型添加相应字段
+ if (text_content && strlen(text_content) > 0)
+ {
+ cJSON_AddStringToObject(push_data, "textContent", text_content);
+ }
+
+ if (story_content && strlen(story_content) > 0)
+ {
+ cJSON_AddStringToObject(push_data, "storyContent", story_content);
+ }
+
+ if (intention_str && strlen(intention_str) > 0)
+ {
+ cJSON_AddStringToObject(push_data, "intention", intention_str);
+ }
+
+ if (story_theme && strlen(story_theme) > 0)
+ {
+ cJSON_AddStringToObject(push_data, "storyTheme", story_theme);
+ }
+
+ // 推送到相应的接收线程
+ char *json_string = cJSON_Print(push_data);
+ if (json_string)
+ {
+ printf("[JSON_PARSE] pushing data to interface_type=%d: %s\n", interface_type, json_string);
+ do_ln_http_server_recv_data(interface_type, json_string, strlen(json_string));
+ free(json_string);
+ }
+ cJSON_Delete(push_data);
+
+ // 下载音频文件
+ if (audio_url && strlen(audio_url) > 0)
+ {
+ char save_dir[256];
+ char filename[128];
+
+ // 根据接口类型确定保存目录和文件名
+ switch (interface_type)
+ {
+ case LN_HTTP_SERVICE_CMD_SSE_RECV:
+ snprintf(save_dir, sizeof(save_dir), "%s/speech", LN_DATA_PATH);
+ snprintf(filename, sizeof(filename), "speech_audio_%d.mp3", segment_index);
+ break;
+ case LN_HTTP_RECV_STORY_CHOOSE:
+ snprintf(save_dir, sizeof(save_dir), "%s/story/choose", LN_DATA_PATH);
+ snprintf(filename, sizeof(filename), "story_choose_%d.mp3", segment_index);
+ break;
+ case LN_HTTP_RECV_STORY_RANDOM:
+ snprintf(save_dir, sizeof(save_dir), "%s/story/random", LN_DATA_PATH);
+ snprintf(filename, sizeof(filename), "story_random_%d.mp3", segment_index);
+ break;
+ default:
+ snprintf(save_dir, sizeof(save_dir), "%s", LN_DATA_PATH);
+ snprintf(filename, sizeof(filename), "audio_%d.mp3", segment_index);
+ break;
+ }
+
+ printf("[JSON_PARSE] found audio URL (interface_type=%d): %s\n", interface_type, audio_url);
+ printf("[JSON_PARSE] downloading audio to: %s/%s\n", save_dir, filename);
+
+ uint32_t download_result = ln_download_mp3_file_advanced(audio_url, save_dir, filename);
+ if (download_result == ERRCODE_SUCC)
+ {
+ printf("[JSON_PARSE] audio downloaded successfully: %s/%s\n", save_dir, filename);
+ }
+ else
+ {
+ printf("[JSON_PARSE] audio download failed\n");
+ }
+ }
+ else
+ {
+ printf("[JSON_PARSE] no audio URL found for interface_type=%d, skipping download\n", interface_type);
+ }
+
+ cJSON_Delete(root);
+ return ERRCODE_SUCC;
+}
+
+/**
+ * 从文件中解析data数据,下载MP3并推送消息
+ * @param file_path 文件路径
+ * @param interface_type 接口类型
+ * @return ERRCODE_SUCC 成功,ERRCODE_FAIL 失败
+ */
+uint32_t ln_parse_file_and_process_data(const char *file_path, int interface_type)
+{
+ if (!file_path)
+ {
+ printf("[FILE_PARSE] invalid file path\n");
+ return ERRCODE_FAIL;
+ }
+
+ printf("[FILE_PARSE] processing file: %s (interface_type=%d)\n", file_path, interface_type);
+
+ FILE *fp = fopen(file_path, "r");
+ if (!fp)
+ {
+ printf("[FILE_PARSE] failed to open file: %s\n", file_path);
+ return ERRCODE_FAIL;
+ }
+
+ char line[2048];
+ int line_count = 0;
+ int success_count = 0;
+ int error_count = 0;
+
+ while (fgets(line, sizeof(line), fp))
+ {
+ line_count++;
+
+ // 去除行尾的换行符
+ size_t len = strlen(line);
+ if (len > 0 && line[len - 1] == '\n')
+ {
+ line[len - 1] = '\0';
+ len--;
+ }
+ if (len > 0 && line[len - 1] == '\r')
+ {
+ line[len - 1] = '\0';
+ len--;
+ }
+
+ // 跳过空行
+ if (len == 0)
+ {
+ continue;
+ }
+
+ // 检查是否是SSE数据格式 (data: {...})
+ if (strncmp(line, "data:", 5) == 0)
+ {
+ char *json_start = line + 5;
+ // 跳过可能的空格
+ while (*json_start == ' ' || *json_start == '\t')
+ {
+ json_start++;
+ }
+
+ printf("[FILE_PARSE] line %d: processing JSON: %s\n", line_count, json_start);
+
+ uint32_t result = ln_parse_and_process_json_data(json_start, interface_type, line_count);
+ if (result == ERRCODE_SUCC)
+ {
+ success_count++;
+ }
+ else
+ {
+ error_count++;
+ }
+ }
+ else
+ {
+ printf("[FILE_PARSE] line %d: invalid format (not SSE data): %s\n", line_count, line);
+ error_count++;
+ }
+ }
+
+ fclose(fp);
+
+ printf("[FILE_PARSE] processing completed: total lines=%d, success=%d, errors=%d\n",
+ line_count, success_count, error_count);
+
+ return (error_count == 0) ? ERRCODE_SUCC : ERRCODE_FAIL;
+}
+