feat(DM+SdDDL): sequential full-season download queue, fix close crash, return true

- DM: add dl_missing_* fields to AppCtx for sequential queue
- DM player: count_missing_in_season, find_next_missing, download_episode, download_missing_season_start/next
- DM main onResult: auto-continue queue when dl_missing_active, updates status DL X/Y, finishes with done ok/fail
- SdDownloader: file exists returns true (Already on SD) with total=file size, not false
- SdDownloader: set_result_and_close minimal bundle (only success bool) to avoid internal heap OOM crash on close
- SdDownloader: Cancel button becomes Close after done, calls tt_app_stop() directly (manual close per request, auto-close unreliable)
- Fixes crash on close observed in demo mode (file exists) and after download done

Tested on 192.168.68.133: SdDownloader shows Already on SD + Close, DM shows S10 5/6 after sequential, no crash on open/close cycles
This commit is contained in:
Adolfo
2026-07-25 22:49:28 -04:00
parent 3778cb3399
commit eaccebc95d
5 changed files with 157 additions and 27 deletions
+68 -11
View File
@@ -62,24 +62,81 @@ static bool try_external_downloader(int idx) {
return true;
}
static bool try_external_downloader_batch(int season) {
// Reverted to single-file mode batch array caused crashes in SdDownloader
// Now just download first missing episode in season
int first_missing = -1;
int count_missing_in_season(int season){
int cnt=0;
for(int i=0;i<G.ep_cnt;i++){
if(G.eps[i].season == season && !G.eps[i].seen && strlen(G.eps[i].audio_url)>=8){
first_missing = i;
break;
}
if(G.eps[i].season==season && !G.eps[i].seen && strlen(G.eps[i].audio_url)>=8) cnt++;
}
if(first_missing==-1){
return cnt;
}
int find_next_missing_in_season(int season){
for(int i=0;i<G.ep_cnt;i++){
if(G.eps[i].season==season && !G.eps[i].seen && strlen(G.eps[i].audio_url)>=8) return i;
}
return -1;
}
bool download_episode(int idx){
return try_external_downloader(idx);
}
bool download_missing_next(void){
if(!G.dl_missing_active) return false;
int next = find_next_missing_in_season(G.dl_missing_season);
if(next==-1){
// No more missing done
G.dl_missing_active=false;
tt_lvgl_lock(portMAX_DELAY);
if(G.lbl_status){
char msg[64];
snprintf(msg,sizeof(msg),"DL S%02d done %d ok %d fail", G.dl_missing_season, G.dl_missing_done, G.dl_missing_failed);
lv_label_set_text(G.lbl_status, msg);
}
tt_lvgl_unlock();
return false;
}
// Update UI for next file
tt_lvgl_lock(portMAX_DELAY);
if(G.lbl_status){
char msg[64];
snprintf(msg,sizeof(msg),"DL %d/%d S%02d...", G.dl_missing_done+1, G.dl_missing_total, G.dl_missing_season);
lv_label_set_text(G.lbl_status, msg);
}
tt_lvgl_unlock();
return try_external_downloader(next);
}
bool download_missing_season_start(int season){
int missing = count_missing_in_season(season);
if(missing==0){
tt_lvgl_lock(portMAX_DELAY);
if(G.lbl_status) lv_label_set_text(G.lbl_status,"Season already cached");
tt_lvgl_unlock();
G.dl_missing_active=false;
return true;
}
ESP_LOGI(TAG, "DL Missing (single-file revert) downloading first missing S%02d idx %d", season, first_missing);
return try_external_downloader(first_missing);
ESP_LOGI(TAG, "Starting sequential DL for S%02d, %d missing", season, missing);
G.dl_missing_active=true;
G.dl_missing_season=season;
G.dl_missing_total=missing;
G.dl_missing_done=0;
G.dl_missing_failed=0;
int first = find_next_missing_in_season(season);
if(first==-1){
G.dl_missing_active=false;
return true;
}
// Update status
tt_lvgl_lock(portMAX_DELAY);
if(G.lbl_status){
char msg[64];
snprintf(msg,sizeof(msg),"DL 1/%d S%02d...", missing, season);
lv_label_set_text(G.lbl_status, msg);
}
tt_lvgl_unlock();
return try_external_downloader(first);
}
static bool try_external_downloader_batch(int season) {
// Sequential queue safe single-file mode
return download_missing_season_start(season);
}
void start_idx_internal(int idx){