summaryrefslogtreecommitdiff
path: root/apps
diff options
context:
space:
mode:
authorMiika Pekkarinen <miipekk@ihme.org>2006-07-15 17:36:25 +0000
committerMiika Pekkarinen <miipekk@ihme.org>2006-07-15 17:36:25 +0000
commit45dfe2a36f03d1ada7036dedb50fb98d7c5421b2 (patch)
tree866a57b677dce40ef5b772e1514a9ec6a35f60ea /apps
parente60cb43a982e03b062bd42dbe260abca299fe14d (diff)
downloadrockbox-45dfe2a36f03d1ada7036dedb50fb98d7c5421b2.zip
rockbox-45dfe2a36f03d1ada7036dedb50fb98d7c5421b2.tar.gz
rockbox-45dfe2a36f03d1ada7036dedb50fb98d7c5421b2.tar.bz2
rockbox-45dfe2a36f03d1ada7036dedb50fb98d7c5421b2.tar.xz
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
Diffstat (limited to 'apps')
-rw-r--r--apps/playback.c20
-rw-r--r--apps/settings_menu.c11
-rw-r--r--apps/tagcache.c264
-rw-r--r--apps/tagcache.h12
-rw-r--r--apps/tagnavi.config19
-rw-r--r--apps/tagtree.c236
6 files changed, 420 insertions, 142 deletions
diff --git a/apps/playback.c b/apps/playback.c
index 4fc0c33..8ffd588 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 f3cae84..f00612a 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 affdc46..a567585 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 (fd != filenametag_fd)
+ close(fd);
+ return -4;
}
- if (!retrieve)
- {
- /* Just return a valid pointer without a valid entry. */
- return &idx;
- }
+ if (fd != filenametag_fd)
+ close(fd);
+
+ return tfe.idx_id;
+}
+
+bool tagcache_find_index(struct tagcache_search *tcs, const char *filename)
+{
+ long idx_id = -1;
- /* Found. Now read the index_entry (if requested). */
- masterfd = open(TAGCACHE_FILE_MASTER, O_RDONLY);
- if (masterfd < 0)
- {
- logf("open fail");
- return NULL;
- }
+#if defined(HAVE_TC_RAMCACHE) && defined(HAVE_DIRCACHE)
+ if (stat.ramcache && dircache_is_enabled())
+ idx_id = find_entry_ram(filename, NULL);
+#endif
- if (read(fd, &tch, sizeof(struct tagcache_header)) !=
- sizeof(struct tagcache_header) || tch.magic != TAGCACHE_MAGIC)
- {
- logf("header error");
- return NULL;
- }
+ if (idx_id < 0)
+ idx_id = find_entry_disk(filename);
- 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);
+ if (idx_id < 0)
+ return false;
- /* Not found? */
- if (i == tch.entry_count)
- {
- logf("not found!");
- return NULL;
- }
+ if (!tagcache_search(tcs, tag_filename))
+ return false;
- return &idx;
+ tcs->entry_count = 0;
+ tcs->idx_id = idx_id;
+
+ return true;
}
-static long tagcache_get_seek(const struct tagcache_search *tcs,
- int tag, int idxid)
+static bool tagcache_get_index(const struct tagcache_search *tcs,
+ int idxid, struct index_entry *idx)
{
- 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 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 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 a82f6e1..04f9567 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 2b00d59..285f826 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 ded2426..396248d 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;
- 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)
+ if (fmt->valid)
{
- snprintf(dptr->name, c->name_buffer_size - namebufused, "%02d. %s",
- tracknum, tcs->result);
- namebufused += strlen(dptr->name) + 1;
- if (namebufused >= c->name_buffer_size)
+ 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++)
{
- logf("chunk mode #1: %d", current_entry_count);
- c->dirfull = true;
- sort = false;
- break ;
+ 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 (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;