From 45dfe2a36f03d1ada7036dedb50fb98d7c5421b2 Mon Sep 17 00:00:00 2001 From: Miika Pekkarinen Date: Sat, 15 Jul 2006 17:36:25 +0000 Subject: [PATCH] Initial runtimedb support for tagcache. Only for developers, statistical data will be lost in future until changelogs has been implemented. git-svn-id: svn://svn.rockbox.org/rockbox/trunk@10217 a1c6a512-1295-4272-9138-f99709370657 --- apps/playback.c | 20 +--- apps/settings_menu.c | 11 ++ apps/tagcache.c | 270 +++++++++++++++++++++++++++++-------------- apps/tagcache.h | 12 +- apps/tagnavi.config | 19 +-- apps/tagtree.c | 242 ++++++++++++++++++++++++++++++++------ 6 files changed, 426 insertions(+), 148 deletions(-) diff --git a/apps/playback.c b/apps/playback.c index 4fc0c33921..8ffd5889c5 100644 --- a/apps/playback.c +++ b/apps/playback.c @@ -2189,11 +2189,13 @@ void audio_thread(void) case Q_AUDIO_PLAY: logf("starting..."); + audio_clear_track_entries(true, false); audio_play_start((size_t)ev.data); break ; case Q_AUDIO_STOP: logf("audio_stop"); + audio_clear_track_entries(true, false); audio_stop_playback(); break ; @@ -2816,22 +2818,6 @@ void mpeg_id3_options(bool _v1first) } #ifdef ROCKBOX_HAS_LOGF -void test_buffer_event(struct mp3entry *id3, bool last_track) -{ - (void)id3; - (void)last_track; - - logf("be:%d%s", last_track, id3->path); -} - -void test_unbuffer_event(struct mp3entry *id3, bool last_track) -{ - (void)id3; - (void)last_track; - - logf("ube:%d%s", last_track, id3->path); -} - void test_track_changed_event(struct mp3entry *id3) { (void)id3; @@ -2854,8 +2840,6 @@ static void playback_init(void) #endif #ifdef ROCKBOX_HAS_LOGF - audio_set_track_buffer_event(test_buffer_event); - audio_set_track_unbuffer_event(test_unbuffer_event); audio_set_track_changed_event(test_track_changed_event); #endif diff --git a/apps/settings_menu.c b/apps/settings_menu.c index f3cae84743..f00612a180 100644 --- a/apps/settings_menu.c +++ b/apps/settings_menu.c @@ -1545,6 +1545,16 @@ static bool tagcache_autoupdate(void) return rc; } +static bool tagcache_runtimedb(void) +{ + bool rc = set_bool_options(str(LANG_RUNTIMEDB_ACTIVE), + &global_settings.runtimedb, + STR(LANG_ON), + STR(LANG_OFF), + NULL); + return rc; +} + static bool tagcache_settings_menu(void) { int m; @@ -1557,6 +1567,7 @@ static bool tagcache_settings_menu(void) { ID2P(LANG_TAGCACHE_AUTOUPDATE), tagcache_autoupdate }, { ID2P(LANG_TAGCACHE_FORCE_UPDATE), tagcache_rebuild }, { ID2P(LANG_TAGCACHE_UPDATE), tagcache_update }, + { ID2P(LANG_RUNTIMEDB_ACTIVE), tagcache_runtimedb }, }; m=menu_init( items, sizeof(items) / sizeof(*items), NULL, diff --git a/apps/tagcache.c b/apps/tagcache.c index affdc46a66..a5675850bf 100644 --- a/apps/tagcache.c +++ b/apps/tagcache.c @@ -58,7 +58,8 @@ static const int sorted_tags[] = { tag_artist, tag_album, tag_genre, tag_compose static const int unique_tags[] = { tag_artist, tag_album, tag_genre, tag_composer }; /* Numeric tags (we can use these tags with conditional clauses). */ -static const int numeric_tags[] = { tag_year, tag_tracknumber, tag_length, tag_bitrate }; +static const int numeric_tags[] = { tag_year, tag_tracknumber, tag_length, tag_bitrate, + tag_playcount, tag_playtime, tag_lastplayed, tag_virt_autoscore }; /* Status information of the tagcache. */ static struct tagcache_stat stat; @@ -179,8 +180,8 @@ bool tagcache_is_sorted_tag(int type) } #if defined(HAVE_TC_RAMCACHE) && defined(HAVE_DIRCACHE) -static struct index_entry *find_entry_ram(const char *filename, - const struct dircache_entry *dc) +static long find_entry_ram(const char *filename, + const struct dircache_entry *dc) { static long last_pos = 0; int counter = 0; @@ -188,7 +189,7 @@ static struct index_entry *find_entry_ram(const char *filename, /* Check if we tagcache is loaded into ram. */ if (!stat.ramcache) - return NULL; + return -1; if (dc == NULL) dc = dircache_get_entry_ptr(filename); @@ -196,7 +197,7 @@ static struct index_entry *find_entry_ram(const char *filename, if (dc == NULL) { logf("tagcache: file not found."); - return NULL; + return -1; } try_again: @@ -211,7 +212,7 @@ static struct index_entry *find_entry_ram(const char *filename, if (hdr->indices[i].tag_seek[tag_filename] == (long)dc) { last_pos = MAX(0, i - 3); - return &hdr->indices[i]; + return i; } if (++counter == 100) @@ -227,28 +228,28 @@ static struct index_entry *find_entry_ram(const char *filename, goto try_again; } - return NULL; + return -1; } #endif -static struct index_entry *find_entry_disk(const char *filename, bool retrieve) +static long find_entry_disk(const char *filename) { - static struct index_entry idx; static long last_pos = -1; long pos_history[POS_HISTORY_COUNT]; long pos_history_idx = 0; - struct tagcache_header tch; bool found = false; struct tagfile_entry tfe; - int masterfd, fd = filenametag_fd; + int fd; char buf[MAX_PATH]; int i; int pos = -1; + fd = filenametag_fd; if (fd < 0) { last_pos = -1; - return NULL; + if ( (fd = open(TAGCACHE_FILE_MASTER, O_RDONLY)) < 0) + return -1; } check_again: @@ -276,7 +277,7 @@ static struct index_entry *find_entry_disk(const char *filename, bool retrieve) logf("too long tag #1"); close(fd); last_pos = -1; - return NULL; + return -2; } if (read(fd, buf, tfe.tag_length) != tfe.tag_length) @@ -284,7 +285,7 @@ static struct index_entry *find_entry_disk(const char *filename, bool retrieve) logf("read error #2"); close(fd); last_pos = -1; - return NULL; + return -3; } if (!strcasecmp(filename, buf)) @@ -307,90 +308,106 @@ static struct index_entry *find_entry_disk(const char *filename, bool retrieve) logf("seek again"); goto check_again; } - //close(fd); - return NULL; - } - - if (!retrieve) - { - /* Just return a valid pointer without a valid entry. */ - return &idx; - } - - /* Found. Now read the index_entry (if requested). */ - masterfd = open(TAGCACHE_FILE_MASTER, O_RDONLY); - if (masterfd < 0) - { - logf("open fail"); - return NULL; - } - - if (read(fd, &tch, sizeof(struct tagcache_header)) != - sizeof(struct tagcache_header) || tch.magic != TAGCACHE_MAGIC) - { - logf("header error"); - return NULL; - } - - for (i = 0; i < tch.entry_count; i++) - { - if (read(masterfd, &idx, sizeof(struct index_entry)) != - sizeof(struct index_entry)) - { - logf("read error #3"); - close(fd); - return NULL; - } - if (idx.tag_seek[tag_filename] == pos) - break ; - } - close(masterfd); - - /* Not found? */ - if (i == tch.entry_count) - { - logf("not found!"); - return NULL; + if (fd != filenametag_fd) + close(fd); + return -4; } - return &idx; + if (fd != filenametag_fd) + close(fd); + + return tfe.idx_id; } -static long tagcache_get_seek(const struct tagcache_search *tcs, - int tag, int idxid) +bool tagcache_find_index(struct tagcache_search *tcs, const char *filename) { - struct index_entry idx; + long idx_id = -1; +#if defined(HAVE_TC_RAMCACHE) && defined(HAVE_DIRCACHE) + if (stat.ramcache && dircache_is_enabled()) + idx_id = find_entry_ram(filename, NULL); +#endif + + if (idx_id < 0) + idx_id = find_entry_disk(filename); + + if (idx_id < 0) + return false; + + if (!tagcache_search(tcs, tag_filename)) + return false; + + tcs->entry_count = 0; + tcs->idx_id = idx_id; + + return true; +} + +static bool tagcache_get_index(const struct tagcache_search *tcs, + int idxid, struct index_entry *idx) +{ #ifdef HAVE_TC_RAMCACHE if (tcs->ramsearch) { if (hdr->indices[idxid].flag & FLAG_DELETED) return false; - return hdr->indices[idxid].tag_seek[tag]; + memcpy(idx, &hdr->indices[idxid], sizeof(struct index_entry)); + return true; } #endif lseek(tcs->masterfd, idxid * sizeof(struct index_entry) + sizeof(struct tagcache_header), SEEK_SET); - if (read(tcs->masterfd, &idx, sizeof(struct index_entry)) != + if (read(tcs->masterfd, idx, sizeof(struct index_entry)) != sizeof(struct index_entry)) { logf("read error #3"); - return -4; + return false; } - return idx.tag_seek[tag]; + return true; +} + +static long check_virtual_tags(int tag, const struct index_entry *idx) +{ + long data = 0; + + switch (tag) + { + case tag_virt_autoscore: + if (idx->tag_seek[tag_length] == 0 + || idx->tag_seek[tag_playcount] == 0) + { + data = 0; + } + else + { + data = 100 * idx->tag_seek[tag_playtime] + / idx->tag_seek[tag_length] + / idx->tag_seek[tag_playcount]; + } + break; + + default: + data = idx->tag_seek[tag]; + } + + return data; } long tagcache_get_numeric(const struct tagcache_search *tcs, int tag) { + struct index_entry idx; if (!tagcache_is_numeric_tag(tag)) return -1; - return tagcache_get_seek(tcs, tag, tcs->idx_id); + if (!tagcache_get_index(tcs, tcs->idx_id, &idx)) + return -2; + + return check_virtual_tags(tag, &idx); } static bool check_against_clause(long numeric, const char *str, @@ -459,16 +476,20 @@ static bool build_lookup_list(struct tagcache_search *tcs) /* Go through all conditional clauses. */ for (j = 0; j < tcs->clause_count; j++) { - int seek = hdr->indices[i].tag_seek[tcs->clause[j]->tag]; + struct index_entry *idx = &hdr->indices[i]; + int seek; char *str = NULL; struct tagfile_entry *entry; - + + seek = check_virtual_tags(tcs->clause[j]->tag, idx); + if (!tagcache_is_numeric_tag(tcs->clause[j]->tag)) { entry = (struct tagfile_entry *)&hdr->tags[tcs->clause[j]->tag][seek]; str = entry->tag_data; } + if (!check_against_clause(seek, str, tcs->clause[j])) break ; } @@ -529,9 +550,11 @@ static bool build_lookup_list(struct tagcache_search *tcs) for (i = 0; i < tcs->clause_count; i++) { struct tagfile_entry tfe; - int seek = entry.tag_seek[tcs->clause[i]->tag]; + int seek; char str[256]; + seek = check_virtual_tags(tcs->clause[i]->tag, &entry); + memset(str, 0, sizeof str); if (!tagcache_is_numeric_tag(tcs->clause[i]->tag)) { @@ -852,9 +875,13 @@ bool tagcache_retrieve(struct tagcache_search *tcs, int idxid, char *buf, long size) { struct tagfile_entry tfe; + struct index_entry idx; long seek; - seek = tagcache_get_seek(tcs, tcs->type, idxid); + if (!tagcache_get_index(tcs, idxid, &idx)) + return false; + + seek = idx.tag_seek[tcs->type]; if (seek < 0) { logf("Retrieve failed"); @@ -974,12 +1001,15 @@ static long get_tag_numeric(const struct index_entry *entry, int tag) bool tagcache_fill_tags(struct mp3entry *id3, const char *filename) { struct index_entry *entry; + int idx_id; /* Find the corresponding entry in tagcache. */ - entry = find_entry_ram(filename, NULL); - if (entry == NULL || !stat.ramcache) + idx_id = find_entry_ram(filename, NULL); + if (idx_id < 0 || !stat.ramcache) return false; + entry = &hdr->indices[idx_id]; + id3->title = get_tag(entry, tag_title)->tag_data; id3->artist = get_tag(entry, tag_artist)->tag_data; id3->album = get_tag(entry, tag_album)->tag_data; @@ -1036,13 +1066,13 @@ static void add_tagcache(const char *path) #if defined(HAVE_TC_RAMCACHE) && defined(HAVE_DIRCACHE) if (stat.ramcache && dircache_is_enabled()) { - if (find_entry_ram(path, dc)) + if (find_entry_ram(path, dc) >= 0) return ; } else #endif { - if (find_entry_disk(path, false)) + if (find_entry_disk(path) >= 0) return ; } @@ -2051,35 +2081,101 @@ static void free_tempbuf(void) tempbuf_size = 0; } -static bool delete_entry(long idx_id) +static int open_master_fd(struct tagcache_header *hdr) { int fd; - int tag, i; - struct index_entry idx, myidx; - struct tagcache_header hdr; - char buf[MAX_PATH]; - int in_use[TAG_COUNT]; fd = open(TAGCACHE_FILE_MASTER, O_RDWR); if (fd < 0) { logf("master file open failed for R/W"); - return false; + return fd; } /* Check the header. */ - read(fd, &hdr, sizeof(struct tagcache_header)); - if (hdr.magic != TAGCACHE_MAGIC) + read(fd, hdr, sizeof(struct tagcache_header)); + if (hdr->magic != TAGCACHE_MAGIC) { logf("header error"); + close(fd); + return -2; + } + + return fd; +} + +bool tagcache_modify_numeric_entry(struct tagcache_search *tcs, + int tag, long data) +{ + struct index_entry idx; + struct tagcache_header myhdr; + + if (!tagcache_is_numeric_tag(tag)) + return false; + +#ifdef HAVE_TC_RAMCACHE + /* Update ram entries first. */ + if (tcs->ramsearch) + { + hdr->indices[tcs->idx_id].tag_seek[tag] = data; + hdr->indices[tcs->idx_id].flag |= FLAG_DIRTYNUM; + } +#endif + + /* And now update the db on disk also. */ + if (tcs->masterfd < 0) + { + if ( (tcs->masterfd = open_master_fd(&myhdr)) < 0) + return false; + } + + lseek(tcs->masterfd, tcs->idx_id * sizeof(struct index_entry) + + sizeof(struct tagcache_header), SEEK_SET); + if (read(tcs->masterfd, &idx, sizeof(struct index_entry)) + != sizeof(struct index_entry)) + { + logf("read error"); return false; } + idx.flag |= FLAG_DIRTYNUM; + idx.tag_seek[tag] = data; + + lseek(tcs->masterfd, -sizeof(struct index_entry), SEEK_CUR); + if (write(tcs->masterfd, &idx, sizeof(struct index_entry)) + != sizeof(struct index_entry)) + { + logf("write error"); + return false; + } + + return true; +} + +static bool delete_entry(long idx_id) +{ + int fd; + int tag, i; + struct index_entry idx, myidx; + struct tagcache_header myhdr; + char buf[MAX_PATH]; + int in_use[TAG_COUNT]; + +#ifdef HAVE_TC_RAMCACHE + /* At first mark the entry removed from ram cache. */ + if (hdr) + hdr->indices[idx_id].flag |= FLAG_DELETED; +#endif + + if ( (fd = open_master_fd(&myhdr) < 0) ) + return false; + lseek(fd, idx_id * sizeof(struct index_entry), SEEK_CUR); if (read(fd, &myidx, sizeof(struct index_entry)) != sizeof(struct index_entry)) { logf("read error"); + close(fd); return false; } @@ -2089,6 +2185,7 @@ static bool delete_entry(long idx_id) != sizeof(struct index_entry)) { logf("write error"); + close(fd); return false; } @@ -2097,7 +2194,7 @@ static bool delete_entry(long idx_id) in_use[tag] = 0; lseek(fd, sizeof(struct tagcache_header), SEEK_SET); - for (i = 0; i < hdr.entry_count; i++) + for (i = 0; i < myhdr.entry_count; i++) { if (read(fd, &idx, sizeof(struct index_entry)) != sizeof(struct index_entry)) @@ -2376,7 +2473,6 @@ static bool load_tagcache(void) { logf("Entry no longer valid."); logf("-> %s", buf); - idx->flag |= FLAG_DELETED; delete_entry(fe->idx_id); continue ; } @@ -2397,7 +2493,6 @@ static bool load_tagcache(void) { logf("Entry no longer valid."); logf("-> %s", buf); - idx->flag |= FLAG_DELETED; delete_entry(fe->idx_id); continue; } @@ -2808,6 +2903,7 @@ void tagcache_init(void) { stat.initialized = false; stat.commit_step = 0; + filenametag_fd = -1; queue_init(&tagcache_queue); create_thread(tagcache_thread, tagcache_stack, diff --git a/apps/tagcache.h b/apps/tagcache.h index a82f6e1634..04f9567936 100644 --- a/apps/tagcache.h +++ b/apps/tagcache.h @@ -23,9 +23,11 @@ enum tag_type { tag_artist = 0, tag_album, tag_genre, tag_title, tag_filename, tag_composer, tag_year, tag_tracknumber, - tag_bitrate, tag_length }; + tag_bitrate, tag_length, tag_playcount, tag_playtime, tag_lastplayed, + /* Virtual tags */ + tag_virt_autoscore }; -#define TAG_COUNT 10 +#define TAG_COUNT 13 /* Allow a little drift to the filename ordering (should not be too high/low). */ #define POS_HISTORY_COUNT 4 @@ -34,7 +36,7 @@ enum tag_type { tag_artist = 0, tag_album, tag_genre, tag_title, #define IDX_BUF_DEPTH 64 /* Tag Cache Header version 'TCHxx'. Increment when changing internal structures. */ -#define TAGCACHE_MAGIC 0x54434804 +#define TAGCACHE_MAGIC 0x54434805 /* How much to allocate extra space for ramcache. */ #define TAGCACHE_RESERVE 32768 @@ -68,6 +70,7 @@ enum tag_type { tag_artist = 0, tag_album, tag_genre, tag_title, /* Flags */ #define FLAG_DELETED 0x0001 /* Entry has been removed from db */ #define FLAG_DIRCACHE 0x0002 /* Filename is a dircache pointer */ +#define FLAG_DIRTYNUM 0x0004 /* Numeric data has been modified */ enum clause { clause_none, clause_is, clause_gt, clause_gteq, clause_lt, clause_lteq, clause_contains, clause_begins_with, clause_ends_with }; @@ -123,6 +126,7 @@ struct tagcache_search { bool tagcache_is_numeric_tag(int type); bool tagcache_is_unique_tag(int type); bool tagcache_is_sorted_tag(int type); +bool tagcache_find_index(struct tagcache_search *tcs, const char *filename); bool tagcache_search(struct tagcache_search *tcs, int tag); bool tagcache_search_add_filter(struct tagcache_search *tcs, int tag, int seek); @@ -133,6 +137,8 @@ bool tagcache_retrieve(struct tagcache_search *tcs, int idxid, char *buf, long size); void tagcache_search_finish(struct tagcache_search *tcs); long tagcache_get_numeric(const struct tagcache_search *tcs, int tag); +bool tagcache_modify_numeric_entry(struct tagcache_search *tcs, + int tag, long data); struct tagcache_stat* tagcache_get_stat(void); int tagcache_get_commit_step(void); diff --git a/apps/tagnavi.config b/apps/tagnavi.config index 2b00d59e1c..285f826425 100644 --- a/apps/tagnavi.config +++ b/apps/tagnavi.config @@ -1,11 +1,16 @@ -"Artists" artist : album : title -"Albums" album : title -"Genres" genre : artist : album : title -"Composers" composer : album : title +"Artists" artist : album : title = "%02d. %s" tracknum title +"Albums" album : title = "%02d. %s" tracknum title +"Genres" genre : artist : album : title = "%02d. %s" tracknum title +"Composers" composer : album : title = "%02d. %s" tracknum title "Tracks" title -"Search by artist" artist ? artist ~ "" : album : title -"Search by album" album ? album ~ "" : title +"Search by artist" artist ? artist ~ "" : album : title = "%02d. %s" tracknum title +"Search by album" album ? album ~ "" : title = "%02d. %s" tracknum title "Search by title" title ? title ~ "" -"Search by year" artist ? year = "" : album : title +"Search by year" artist ? year = "" : album : title = "%02d. %s" tracknum title +"Search by score" title = "(%3d) %s" autoscore title ? autoscore > "" +"Most played tracks" title = "(%d) %s" playcount title ? playcount > "1" +"Never played tracks" artist ? playcount == "0" : album : title = "%02d. %s" tracknum title +"Best tracks" title ? autoscore > "60" & playcount > "1" "Example 1" artist ? year >= "2000" & artist ^ "a" : album : title "Example 2" genre ? genre ~ "metal" : artist ? year >= "2000" : album ? year >= "2000" : title +"List played tracks" title = "(%3d/%d) %s" autoscore playcount title ? playcount > "0" diff --git a/apps/tagtree.c b/apps/tagtree.c index ded2426d41..396248d7ce 100644 --- a/apps/tagtree.c +++ b/apps/tagtree.c @@ -39,6 +39,7 @@ #include "gui/list.h" #include "buffer.h" #include "atoi.h" +#include "playback.h" #define FILE_SEARCH_INSTRUCTIONS ROCKBOX_DIR "/tagnavi.config" @@ -47,11 +48,28 @@ static int tagtree_play_folder(struct tree_context* c); static char searchstring[32]; #define MAX_TAGS 5 +/* + * "%3d. %s" autoscore title + * + * valid = true + * formatstr = "%-3d. %s" + * tags[0] = tag_autoscore + * tags[1] = tag_title + * tag_count = 2 + */ +struct display_format { + bool valid; + char formatstr[64]; + int tags[MAX_TAGS]; + int tag_count; +}; + struct search_instruction { char name[64]; int tagorder[MAX_TAGS]; int tagorder_count; struct tagcache_search_clause clause[MAX_TAGS][TAGCACHE_MAX_CLAUSES]; + struct display_format format[MAX_TAGS]; int clause_count[MAX_TAGS]; int result_seek[MAX_TAGS]; }; @@ -121,6 +139,8 @@ static int get_tag(int *tag) MATCH(tag, buf, "title", tag_title); MATCH(tag, buf, "tracknum", tag_tracknumber); MATCH(tag, buf, "year", tag_year); + MATCH(tag, buf, "playcount", tag_playcount); + MATCH(tag, buf, "autoscore", tag_virt_autoscore); logf("NO MATCH: %s\n", buf); if (buf[0] == '?') @@ -163,7 +183,7 @@ static int get_clause(int *condition) return 0; } -static bool add_clause(struct search_instruction *inst, +static bool add_clause(struct search_instruction *inst, int tag, int type, const char *str) { int len = strlen(str); @@ -206,14 +226,51 @@ static bool add_clause(struct search_instruction *inst, return true; } +static int get_format_str(struct display_format *fmt) +{ + int ret; + + memset(fmt, 0, sizeof(struct display_format)); + + if (get_token_str(fmt->formatstr, sizeof fmt->formatstr) < 0) + return -10; + + while (fmt->tag_count < MAX_TAGS) + { + ret = get_tag(&fmt->tags[fmt->tag_count]); + if (ret < 0) + return -11; + + if (ret == 0) + break; + + fmt->tag_count++; + } + + fmt->valid = true; + + return 1; +} + static int get_condition(struct search_instruction *inst) { + struct display_format format; + struct display_format *fmt = NULL; int tag; int condition; char buf[32]; - + switch (*strp) { + case '=': + if (get_format_str(&format) < 0) + { + logf("get_format_str() parser failed!"); + return -4; + } + fmt = &format; + break; + case '?': case ' ': case '&': @@ -225,6 +282,14 @@ static int get_condition(struct search_instruction *inst) return 0; } + if (fmt) + { + memcpy(&inst->format[inst->tagorder_count], fmt, + sizeof(struct display_format)); + } + else + inst->format[inst->tagorder_count].valid = false; + if (get_tag(&tag) <= 0) return -1; @@ -294,6 +359,62 @@ static int compare(const void *p1, const void *p2) return strncasecmp(e1->name, e2->name, MAX_PATH); } +static void tagtree_buffer_event(struct mp3entry *id3, bool last_track) +{ + (void)id3; + (void)last_track; + + logf("be:%d%s", last_track, id3->path); +} + +static void tagtree_unbuffer_event(struct mp3entry *id3, bool last_track) +{ + (void)last_track; + long playcount; + long playtime; + long lastplayed; + + /* Do not gather data unless proper setting has been enabled. */ + if (!global_settings.runtimedb) + return; + + /* Don't process unplayed tracks. */ + if (id3->elapsed == 0) + return; + + if (!tagcache_find_index(&tcs, id3->path)) + { + logf("tc stat: not found: %s", id3->path); + return; + } + + playcount = tagcache_get_numeric(&tcs, tag_playcount); + playtime = tagcache_get_numeric(&tcs, tag_playtime); + lastplayed = tagcache_get_numeric(&tcs, tag_lastplayed); + + playcount++; + + /* Ignore the last 15s (crossfade etc.) */ + playtime += MIN(id3->length, id3->elapsed + 15 * 1000); + + logf("ube:%s", id3->path); + logf("-> %d/%d/%d", last_track, playcount, playtime); + logf("-> %d/%d/%d", id3->elapsed, id3->length, MIN(id3->length, id3->elapsed + 15 * 1000)); + + /* lastplayed not yet supported. */ + + if (!tagcache_modify_numeric_entry(&tcs, tag_playcount, playcount) + || !tagcache_modify_numeric_entry(&tcs, tag_playtime, playtime) + || !tagcache_modify_numeric_entry(&tcs, tag_lastplayed, tag_lastplayed)) + { + logf("tc stat: modify failed!"); + tagcache_search_finish(&tcs); + return; + } + + tagcache_search_finish(&tcs); +} + void tagtree_init(void) { int fd; @@ -350,6 +471,9 @@ void tagtree_init(void) close(fd); audiobuf += sizeof(struct search_instruction) * si_count + 4; + + audio_set_track_buffer_event(tagtree_buffer_event); + audio_set_track_unbuffer_event(tagtree_unbuffer_event); } bool show_search_progress(bool init, int count) @@ -445,48 +569,100 @@ int retrieve_entries(struct tree_context *c, struct tagcache_search *tcs, while (tagcache_get_next(tcs)) { + struct display_format *fmt = &csi->format[extra]; + if (total_count++ < offset) continue; dptr->newtable = navibrowse; dptr->extraseek = tcs->result_seek; - if (!tcs->ramsearch || tag == tag_title) + if (tag == tag_title) + dptr->newtable = playtrack; + + if (!tcs->ramsearch || fmt->valid) { - int tracknum = -1; + char buf[MAX_PATH]; + int buf_pos = 0; + + if (fmt->valid) + { + char fmtbuf[8]; + bool read_format = false; + int fmtbuf_pos = 0; + int parpos = 0; + + memset(buf, 0, sizeof buf); + for (i = 0; fmt->formatstr[i] != '\0'; i++) + { + if (fmt->formatstr[i] == '%') + { + read_format = true; + fmtbuf_pos = 0; + if (parpos >= fmt->tag_count) + { + logf("too many format tags"); + return 0; + } + } + + if (read_format) + { + fmtbuf[fmtbuf_pos++] = fmt->formatstr[i]; + if (fmtbuf_pos >= (long)sizeof(fmtbuf)) + { + logf("format parse error"); + return 0; + } + + if (fmt->formatstr[i] == 's') + { + fmtbuf[fmtbuf_pos] = '\0'; + read_format = false; + snprintf(&buf[buf_pos], MAX_PATH - buf_pos, fmtbuf, tcs->result); + buf_pos += strlen(&buf[buf_pos]); + parpos++; + } + else if (fmt->formatstr[i] == 'd') + { + fmtbuf[fmtbuf_pos] = '\0'; + read_format = false; + snprintf(&buf[buf_pos], MAX_PATH - buf_pos, fmtbuf, + tagcache_get_numeric(tcs, fmt->tags[parpos])); + buf_pos += strlen(&buf[buf_pos]); + parpos++; + } + continue; + } + + buf[buf_pos++] = fmt->formatstr[i]; + + if (buf_pos - 1 >= (long)sizeof(buf)) + { + logf("buffer overflow"); + return 0; + } + } + + buf[buf_pos++] = '\0'; + } dptr->name = &c->name_buffer[namebufused]; - if (tag == tag_title) - { - dptr->newtable = playtrack; - if (c->currtable != allsubentries && c->dirlevel > 1) - tracknum = tagcache_get_numeric(tcs, tag_tracknumber); - } - - if (tracknum > 0) - { - snprintf(dptr->name, c->name_buffer_size - namebufused, "%02d. %s", - tracknum, tcs->result); - namebufused += strlen(dptr->name) + 1; - if (namebufused >= c->name_buffer_size) - { - logf("chunk mode #1: %d", current_entry_count); - c->dirfull = true; - sort = false; - break ; - } - } + if (fmt->valid) + namebufused += buf_pos; else - { namebufused += tcs->result_len; - if (namebufused >= c->name_buffer_size) - { - logf("chunk mode #2: %d", current_entry_count); - c->dirfull = true; - sort = false; - break ; - } - strcpy(dptr->name, tcs->result); + + if (namebufused >= c->name_buffer_size) + { + logf("chunk mode #2: %d", current_entry_count); + c->dirfull = true; + sort = false; + break ; } + if (fmt->valid) + strcpy(dptr->name, buf); + else + strcpy(dptr->name, tcs->result); } else dptr->name = tcs->result;