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
This commit is contained in:
parent
e60cb43a98
commit
45dfe2a36f
6 changed files with 426 additions and 148 deletions
|
@ -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
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
270
apps/tagcache.c
270
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,
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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"
|
||||
|
|
242
apps/tagtree.c
242
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;
|
||||
|
|
Loading…
Reference in a new issue