From: luozheng <1732036298@qq.com> Date: Mon, 20 Oct 2025 08:31:47 +0000 (+0800) Subject: 表情下载,屏保下载,日程提醒音频下载支持断点续传,减少下载时间 X-Git-Tag: V1.0.9_20251128_194606_LN~197^2~1 X-Git-Url: http://git.snicker.com.cn/?a=commitdiff_plain;h=f0fed221dccb085f983aeaff899f8c9f54e40b64;p=mirrors%2FFD230.git 表情下载,屏保下载,日程提醒音频下载支持断点续传,减少下载时间 --- diff --git a/code/sdk/zhihui/leiniao/imsl_ln/ln_api.c b/code/sdk/zhihui/leiniao/imsl_ln/ln_api.c index 013cc2412..7ccd43b73 100644 --- a/code/sdk/zhihui/leiniao/imsl_ln/ln_api.c +++ b/code/sdk/zhihui/leiniao/imsl_ln/ln_api.c @@ -404,7 +404,6 @@ int uapi_ln_download_file(const char *url, const char *save_path) WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD] 开始下载: %s -> %s", url, save_path); g_ln_is_file_downloading = 1; - uapi_pm_add_sleep_veto(PM_ID_DEBUG); HttpClient client = {0}; HttpClientData client_data = {0}; @@ -416,7 +415,6 @@ int uapi_ln_download_file(const char *url, const char *save_path) { WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD] fopen failed for %s", save_path); g_ln_is_file_downloading = 0; - uapi_pm_remove_sleep_veto(PM_ID_DEBUG); return ERRCODE_FAIL; } @@ -426,7 +424,6 @@ int uapi_ln_download_file(const char *url, const char *save_path) WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD] prepare failed: %d", prep); fclose(fd); g_ln_is_file_downloading = 0; - uapi_pm_remove_sleep_veto(PM_ID_DEBUG); return ERRCODE_FAIL; } @@ -438,7 +435,6 @@ int uapi_ln_download_file(const char *url, const char *save_path) fclose(fd); HttpClientUnprepare(&client_data); g_ln_is_file_downloading = 0; - uapi_pm_remove_sleep_veto(PM_ID_DEBUG); return ERRCODE_FAIL; } @@ -451,7 +447,6 @@ int uapi_ln_download_file(const char *url, const char *save_path) HttpClientClse(&client); HttpClientUnprepare(&client_data); g_ln_is_file_downloading = 0; - uapi_pm_remove_sleep_veto(PM_ID_DEBUG); return ERRCODE_FAIL; } @@ -473,7 +468,6 @@ int uapi_ln_download_file(const char *url, const char *save_path) fclose(fd); HttpClientClse(&client); HttpClientUnprepare(&client_data); - uapi_pm_remove_sleep_veto(PM_ID_DEBUG); return ERRCODE_FAIL; } total_written += written; @@ -510,7 +504,6 @@ int uapi_ln_download_file(const char *url, const char *save_path) { WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "删除不完整文件成功\r\n"); } - uapi_pm_remove_sleep_veto(PM_ID_DEBUG); } HttpClientUnprepare(&client_data); @@ -518,7 +511,6 @@ int uapi_ln_download_file(const char *url, const char *save_path) fclose(fd); g_ln_is_file_downloading = 0; WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD] 下载过程结束,ret=%d", ret); - uapi_pm_remove_sleep_veto(PM_ID_DEBUG); return ret; } @@ -531,6 +523,7 @@ int uapi_ln_download_with_retry(const char *url, const char *save_path, int max_ return ERRCODE_FAIL; } WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD] 开始下载: %s -> %s", url, save_path); + uapi_pm_add_sleep_veto(PM_ID_DEBUG); int ret = uapi_ln_download_file(url, save_path); if (ERRCODE_SUCC != ret) { @@ -545,6 +538,7 @@ int uapi_ln_download_with_retry(const char *url, const char *save_path, int max_ } } } + uapi_pm_remove_sleep_veto(PM_ID_DEBUG); return ret; } @@ -561,13 +555,6 @@ int uapi_ln_download_file_resume(const char *url, const char *save_path) return ERRCODE_FAIL; } - if (g_ln_is_file_downloading == 1) - { - WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD_RESUME] 已有文件正在下载中"); - return ERRCODE_FAIL; - } - g_ln_is_file_downloading = 1; - long long resume_offset = 0; /* 判断是否存在部分文件,计算续传偏移 */ { @@ -589,12 +576,16 @@ int uapi_ln_download_file_resume(const char *url, const char *save_path) int ret = -1; size_t total_written = 0; + if(resume_offset > 0) + { + WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD_RESUME] 断点续传,resume_offset=%lld", resume_offset); + } + /* 以追加模式打开(若服务端返回200将切换为覆盖写)*/ FILE *fd = fopen(save_path, resume_offset > 0 ? "ab+" : "wb+"); if (!fd) { WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD_RESUME] fopen failed for %s", save_path); - g_ln_is_file_downloading = 0; return ERRCODE_FAIL; } @@ -603,7 +594,6 @@ int uapi_ln_download_file_resume(const char *url, const char *save_path) { WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD_RESUME] prepare failed: %d", prep); fclose(fd); - g_ln_is_file_downloading = 0; return ERRCODE_FAIL; } @@ -616,7 +606,6 @@ int uapi_ln_download_file_resume(const char *url, const char *save_path) { HttpClientUnprepare(&client_data); fclose(fd); - g_ln_is_file_downloading = 0; return ERRCODE_FAIL; } int hdr = snprintf(header_buf, 128, "Range: bytes=%lld-\r\n", resume_offset); @@ -625,7 +614,6 @@ int uapi_ln_download_file_resume(const char *url, const char *save_path) free(header_buf); HttpClientUnprepare(&client_data); fclose(fd); - g_ln_is_file_downloading = 0; return ERRCODE_FAIL; } HttpClientSetCustomHeader(&client, header_buf); @@ -640,7 +628,6 @@ int uapi_ln_download_file_resume(const char *url, const char *save_path) if (header_buf) free(header_buf); fclose(fd); HttpClientUnprepare(&client_data); - g_ln_is_file_downloading = 0; return ERRCODE_FAIL; } @@ -652,7 +639,6 @@ int uapi_ln_download_file_resume(const char *url, const char *save_path) fclose(fd); HttpClientClse(&client); HttpClientUnprepare(&client_data); - g_ln_is_file_downloading = 0; return ERRCODE_FAIL; } @@ -666,7 +652,6 @@ int uapi_ln_download_file_resume(const char *url, const char *save_path) fclose(fd); HttpClientClse(&client); HttpClientUnprepare(&client_data); - g_ln_is_file_downloading = 0; return ERRCODE_FAIL; } @@ -682,7 +667,6 @@ int uapi_ln_download_file_resume(const char *url, const char *save_path) if (header_buf) free(header_buf); HttpClientClse(&client); HttpClientUnprepare(&client_data); - g_ln_is_file_downloading = 0; return ERRCODE_FAIL; } resume_offset = 0; @@ -700,7 +684,6 @@ int uapi_ln_download_file_resume(const char *url, const char *save_path) fclose(fd); HttpClientClse(&client); HttpClientUnprepare(&client_data); - g_ln_is_file_downloading = 0; return ERRCODE_FAIL; } total_written += written; @@ -724,10 +707,10 @@ int uapi_ln_download_file_resume(const char *url, const char *save_path) fclose(fd); HttpClientClse(&client); HttpClientUnprepare(&client_data); - g_ln_is_file_downloading = 0; return ERRCODE_FAIL; } total_written += written; + WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD_RESUME] write first block success, written=%zu", total_written); } } else @@ -754,7 +737,44 @@ int uapi_ln_download_file_resume(const char *url, const char *save_path) HttpClientUnprepare(&client_data); HttpClientClse(&client); fclose(fd); + return ret; +} +/* 断点续传下载函数封装:带重试,不做UI通知与资源释放 */ +int uapi_ln_download_file_resume_with_retry(const char *url, const char *save_path, int max_retries) +{ + if (url == NULL || save_path == NULL || (max_retries <= 0 || max_retries > 10 )) + { + WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD_RESUME_WITH_RETRY] 参数无效"); + return ERRCODE_FAIL; + } + WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD_RESUME_WITH_RETRY] 开始下载: %s -> %s", url, save_path); + if (uapi_ln_is_file_downloading() == 1) + { + WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD_RESUME] 已有文件正在下载中"); + return ERRCODE_FAIL; + } + g_ln_is_file_downloading = 1; + + //添加睡眠投票 + uapi_pm_add_sleep_veto(PM_ID_DEBUG); + + int ret = uapi_ln_download_file_resume(url, save_path); + if (ERRCODE_SUCC != ret) + { + for (int i = 1; i <= max_retries; i++) + { + WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD_RESUME_WITH_RETRY] 重试 %d/%d", i, max_retries); + ret = uapi_ln_download_file_resume(url, save_path); + if (ERRCODE_SUCC == ret) + { + WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD_RESUME_WITH_RETRY] 重试成功 %d", i); + break; + } + } + } g_ln_is_file_downloading = 0; + //移除睡眠投票 + uapi_pm_remove_sleep_veto(PM_ID_DEBUG); return ret; } /* 自适应申请HttpClient缓冲(保证header>=2048),失败则逐步扩容重试 */ diff --git a/code/sdk/zhihui/leiniao/imsl_ln/ln_api.h b/code/sdk/zhihui/leiniao/imsl_ln/ln_api.h index 5df754cd4..a3285c10e 100644 --- a/code/sdk/zhihui/leiniao/imsl_ln/ln_api.h +++ b/code/sdk/zhihui/leiniao/imsl_ln/ln_api.h @@ -234,6 +234,7 @@ int uapi_ln_download_file(const char *url, const char *save_path); int uapi_ln_download_with_retry(const char *url, const char *save_path, int max_retries); /* 带断点续传的下载:支持 Range 续传;失败不删除部分文件 */ int uapi_ln_download_file_resume(const char *url, const char *save_path); +int uapi_ln_download_file_resume_with_retry(const char *url, const char *save_path, int max_retries); /* 设置ws音频播放状态 */ void uapi_ln_set_audio_play_status(int status); /* 获取ws音频播放状态 */ diff --git a/code/sdk/zhihui/leiniao/imsl_ln/ln_api_device_info.c b/code/sdk/zhihui/leiniao/imsl_ln/ln_api_device_info.c index a5ff2c8ae..2c3105881 100644 --- a/code/sdk/zhihui/leiniao/imsl_ln/ln_api_device_info.c +++ b/code/sdk/zhihui/leiniao/imsl_ln/ln_api_device_info.c @@ -5,6 +5,7 @@ #include #include #include "wearable_log.h" +#include "custom_utils.h" LnCharacterConfig g_ln_character_config = {0}; @@ -122,9 +123,11 @@ void ln_download_screen_image(void) { const char *url = g_ln_pending_screen_image_url; const char *save_path = LN_DEVICE_SCREEN_IMAGE_FILE; - WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DEVICE_CHARACTER] 屏保下载URL: %s, 保存路径: %s", url, save_path); - errcode_t ret = uapi_ln_download_with_retry(url, save_path, 3); - if (ret == ERRCODE_SUCC) + //如果本地有文件先删除文件 + uapi_delete_files(LN_DEVICE_SCREEN_IMAGE_FILE); + WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "[DEVICE_CHARACTER] 屏保下载URL: %s, 保存路径: %s", url, save_path); + int ret = uapi_ln_download_file_resume_with_retry(url, save_path, 5); + if (ret == 0) { WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DEVICE_CHARACTER] 屏保下载成功,通知去切换屏保"); push_download_success_notification(DOWNLOAD_FILE_TYPE_SCREEN_IMAGE); diff --git a/code/sdk/zhihui/leiniao/imsl_ln/ln_api_emoji.c b/code/sdk/zhihui/leiniao/imsl_ln/ln_api_emoji.c index c527355ab..bf9763b6e 100644 --- a/code/sdk/zhihui/leiniao/imsl_ln/ln_api_emoji.c +++ b/code/sdk/zhihui/leiniao/imsl_ln/ln_api_emoji.c @@ -16,8 +16,9 @@ /* 表情信息结构体定义 */ typedef struct { - char *emotion; // 心情类型(动态分配) - char *emojiUrl; // 表情文件URL(动态分配) + char *emotion; // 心情类型(动态分配) + char *emojiUrl; // 表情文件URL(动态分配) + long long size; // 表情文件大小(字节,支持断点续传判断;0 表示未知) } EmotionEmoji; /* 表情包配置结构体 */ @@ -421,7 +422,7 @@ static void ln_emoji_download(void *arg) continue; } - // 下载并缓存 + // 下载并缓存(支持断点续传:当目标文件存在且小于应有大小时,从断点续传) char save_path[MAX_PATH_LEN] = {0}; ln_gen_emoji_cache_path(pkg, &pkg->emojis[i], save_path, sizeof(save_path)); /* 确保保存目录存在 */ @@ -438,7 +439,7 @@ static void ln_emoji_download(void *arg) uapi_create_folder(dir_buf); } } - if (uapi_ln_download_with_retry(pkg->emojis[i].emojiUrl, save_path, 3) != ERRCODE_SUCC) + if (uapi_ln_download_file_resume_with_retry(pkg->emojis[i].emojiUrl, save_path, 3) != ERRCODE_SUCC) { all_success = 0; failed_count++; @@ -615,9 +616,29 @@ void ln_parse_download_emoji(const char *json_buf, int json_len) { cJSON *emotion = cJSON_GetObjectItem(item, "emotion"); cJSON *emojiUrl = cJSON_GetObjectItem(item, "emojiUrl"); + cJSON *size_node = cJSON_GetObjectItem(item, "size"); g_emoji_package.emojis[i].emotion = ln_safe_strdup((emotion && cJSON_IsString(emotion)) ? emotion->valuestring : ""); g_emoji_package.emojis[i].emojiUrl = ln_safe_strdup((emojiUrl && cJSON_IsString(emojiUrl)) ? emojiUrl->valuestring : ""); + /* 解析单个表情大小(字节) */ + long long emoji_size = 0; + if (size_node) + { + if (cJSON_IsNumber(size_node)) + { + emoji_size = (long long)size_node->valuedouble; + } + else if (cJSON_IsString(size_node) && size_node->valuestring) + { + emoji_size = atoll(size_node->valuestring); + } + } + g_emoji_package.emojis[i].size = (emoji_size > 0) ? emoji_size : 0; + WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, " emoji %d: emotion=%s, url=%s, size=%lld", + i + 1, + g_emoji_package.emojis[i].emotion, + g_emoji_package.emojis[i].emojiUrl, + g_emoji_package.emojis[i].size); } } } diff --git a/code/sdk/zhihui/leiniao/imsl_ln/ln_api_schedule.c b/code/sdk/zhihui/leiniao/imsl_ln/ln_api_schedule.c index ca1ea1bbe..9b79fde49 100644 --- a/code/sdk/zhihui/leiniao/imsl_ln/ln_api_schedule.c +++ b/code/sdk/zhihui/leiniao/imsl_ln/ln_api_schedule.c @@ -205,7 +205,8 @@ void ln_parse_schedule_reminder(const char *json_buf, int json_len) { // 目前是立即提醒的所以直接下载并播放 WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[JSON_PARSE] download audio:执行下载并播放\n"); - int ret = uapi_ln_download_with_retry(g_schedule_reminder.reminderAudioUrl, LN_REMINDER_AUDIO_FILE, 2); + uapi_delete_files(LN_REMINDER_AUDIO_FILE); + int ret = uapi_ln_download_file_resume_with_retry(g_schedule_reminder.reminderAudioUrl, LN_REMINDER_AUDIO_FILE, 3); if (ret == ERRCODE_SUCC) { WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[JSON_PARSE] download audio:下载成功\n");