text/plain
•
4.71 KB
•
211 lines
#include "playlist.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "audio_player.h"
#include "bookmark.h"
#include "lvgl.h"
#include "storage.h"
#include "ui.h"
#define PLAYLIST_BUF_SIZE 4096
playlist_t playlist;
void playlist_load(void) {
memset(&playlist, 0, sizeof(playlist));
storage_file_t f = storage_open(PLAYLIST_FILE, "r");
if (!f) {
return;
}
char buf[PLAYLIST_BUF_SIZE];
size_t n = storage_read(f, buf, sizeof(buf) - 1);
storage_close(f);
buf[n] = '\0';
// Parse line by line
char* line = buf;
while (line && *line) {
char* eol = strchr(line, '\n');
size_t line_len = eol ? (size_t)(eol - line) : strlen(line);
// Null-terminate line temporarily
char saved = line[line_len];
line[line_len] = '\0';
// Parse index = N
if (strncmp(line, "index", 5) == 0) {
char* eq = strchr(line, '=');
if (eq) {
playlist.index = atoi(eq + 1);
}
}
// Parse type = "music" or type = "audiobook"
else if (strncmp(line, "type", 4) == 0 &&
playlist.count < PLAYLIST_MAX_ENTRIES) {
char* eq = strchr(line, '=');
if (eq) {
char* q1 = strchr(eq, '"');
if (q1) {
q1++;
if (strncmp(q1, "audiobook", 9) == 0) {
playlist.entries[playlist.count].type =
PLAYLIST_AUDIOBOOK;
} else {
playlist.entries[playlist.count].type = PLAYLIST_MUSIC;
}
}
}
}
// Parse path = "/some/path"
else if (strncmp(line, "path", 4) == 0 &&
playlist.count < PLAYLIST_MAX_ENTRIES) {
char* eq = strchr(line, '=');
if (eq) {
char* q1 = strchr(eq, '"');
if (q1) {
q1++;
char* q2 = strchr(q1, '"');
if (q2) {
size_t plen = (size_t)(q2 - q1);
if (plen < STORAGE_MAX_PATH) {
memcpy(playlist.entries[playlist.count].path, q1,
plen);
playlist.entries[playlist.count].path[plen] = '\0';
playlist.count++;
}
}
}
}
}
line[line_len] = saved;
if (!eol) {
break;
}
line = eol + 1;
}
// Clamp index
if (playlist.count > 0) {
if (playlist.index < 0) {
playlist.index = 0;
}
if (playlist.index >= playlist.count) {
playlist.index = playlist.count - 1;
}
} else {
playlist.index = 0;
}
}
void playlist_save(void) {
storage_mkdir(DATA_DIR);
char buf[PLAYLIST_BUF_SIZE];
int pos = 0;
pos += snprintf(buf + pos, sizeof(buf) - (size_t)pos,
"[playlist]\nindex = %d\n\n", playlist.index);
for (int i = 0; i < playlist.count && pos < (int)sizeof(buf) - 128; i++) {
const char* type_str = playlist.entries[i].type == PLAYLIST_AUDIOBOOK
? "audiobook"
: "music";
pos += snprintf(buf + pos, sizeof(buf) - (size_t)pos,
"[[entry]]\ntype = \"%s\"\npath = \"%s\"\n\n", type_str,
playlist.entries[i].path);
}
storage_file_t f = storage_open(PLAYLIST_FILE, "w");
if (f) {
storage_write(f, buf, (size_t)pos);
storage_close(f);
}
}
void playlist_add(const char* path, playlist_type_t type) {
if (playlist.count >= PLAYLIST_MAX_ENTRIES) {
return;
}
playlist.entries[playlist.count].type = type;
strncpy(playlist.entries[playlist.count].path, path, STORAGE_MAX_PATH - 1);
playlist.entries[playlist.count].path[STORAGE_MAX_PATH - 1] = '\0';
playlist.count++;
}
void playlist_clear(void) {
memset(&playlist, 0, sizeof(playlist));
}
playlist_entry_t* playlist_current(void) {
if (playlist.count == 0) {
return NULL;
}
if (playlist.index < 0 || playlist.index >= playlist.count) {
return NULL;
}
return &playlist.entries[playlist.index];
}
bool playlist_next(void) {
if (playlist.index + 1 >= playlist.count) {
return false;
}
playlist.index++;
return true;
}
bool playlist_prev(void) {
if (playlist.index <= 0) {
return false;
}
playlist.index--;
return true;
}
bool playlist_is_empty(void) {
return playlist.count == 0;
}
int playlist_count(void) {
return playlist.count;
}
void playlist_set_index(int i) {
if (i >= 0 && i < playlist.count) {
playlist.index = i;
}
}
void playlist_tick(void) {
static uint32_t last_slot = 0;
playlist_entry_t* entry = playlist_current();
if (!entry || entry->type != PLAYLIST_AUDIOBOOK) {
return;
}
if (audio_player_get_state() != AUDIO_STATE_PLAYING) {
return;
}
uint32_t slot = lv_tick_get() / PLAYLIST_BOOKMARK_INTERVAL_MS;
if (slot == last_slot) {
return;
}
last_slot = slot;
uint32_t pos = audio_player_get_position_ms();
if (pos > 0 && app_state.last_played_path[0]) {
bookmark_save(app_state.last_played_path, pos);
}
// Also save chapter bookmark for directory audiobooks
if (app_state.audiobook_dir[0]) {
const char* filename = strrchr(app_state.last_played_path, '/');
if (filename) {
bookmark_save_string(app_state.audiobook_dir, filename + 1);
}
}
}