From f6039466fb275f55be16113bbbf46872f90862c8 Mon Sep 17 00:00:00 2001 From: Miika Pekkarinen Date: Sun, 16 Dec 2007 21:10:26 +0000 Subject: Added file modify time field to the DB. Now metadata changes should be detected with database autoupdate enabled. Runtime statistics are not yet preserved. Preserving statistics over moving of files and altering metadata is going to be implemented next. IMPORTANT: Export database before upgrading. git-svn-id: svn://svn.rockbox.org/rockbox/trunk@15946 a1c6a512-1295-4272-9138-f99709370657 --- apps/tagcache.c | 195 +++++++++++++++++++++++++++++++++++++------------------- apps/tagcache.h | 19 +++++- 2 files changed, 147 insertions(+), 67 deletions(-) (limited to 'apps') diff --git a/apps/tagcache.c b/apps/tagcache.c index bf2df7cc0b..1f4521168e 100644 --- a/apps/tagcache.c +++ b/apps/tagcache.c @@ -85,7 +85,6 @@ #define do_timed_yield() do { } while(0) #endif - #ifndef __PCTOOL__ /* Tag Cache thread. */ static struct event_queue tagcache_queue; @@ -115,16 +114,18 @@ static const int unique_tags[] = { tag_artist, tag_album, tag_genre, tag_composer, tag_comment, tag_albumartist, tag_grouping }; /* Numeric tags (we can use these tags with conditional clauses). */ -static const int numeric_tags[] = { tag_year, tag_discnumber, tag_tracknumber, tag_length, - tag_bitrate, tag_playcount, tag_rating, tag_playtime, tag_lastplayed, tag_commitid, +static const int numeric_tags[] = { tag_year, tag_discnumber, + tag_tracknumber, tag_length, tag_bitrate, tag_playcount, tag_rating, + tag_playtime, tag_lastplayed, tag_commitid, tag_mtime, tag_virt_length_min, tag_virt_length_sec, tag_virt_playtime_min, tag_virt_playtime_sec, tag_virt_entryage, tag_virt_autoscore }; /* String presentation of the tags defined in tagcache.h. Must be in correct order! */ static const char *tags_str[] = { "artist", "album", "genre", "title", - "filename", "composer", "comment", "albumartist", "grouping", "year", "discnumber", "tracknumber", - "bitrate", "length", "playcount", "rating", "playtime", "lastplayed", "commitid" }; + "filename", "composer", "comment", "albumartist", "grouping", "year", + "discnumber", "tracknumber", "bitrate", "length", "playcount", "rating", + "playtime", "lastplayed", "commitid", "mtime" }; /* Status information of the tagcache. */ static struct tagcache_stat tc_stat; @@ -188,7 +189,7 @@ struct master_header { /* For the endianess correction */ static const char *tagfile_entry_ec = "ss"; -static const char *index_entry_ec = "llllllllllllllllllll"; /* (1 + TAG_COUNT) * l */ +static const char *index_entry_ec = "lllllllllllllllllllll"; /* (1 + TAG_COUNT) * l */ static const char *tagcache_header_ec = "lll"; static const char *master_header_ec = "llllll"; @@ -252,6 +253,8 @@ static int processed_dir_count; static volatile int write_lock; static volatile int read_lock; +static bool delete_entry(long idx_id); + const char* tagcache_tag_to_str(int tag) { return tags_str[tag]; @@ -328,6 +331,46 @@ static int open_tag_fd(struct tagcache_header *hdr, int tag, bool write) return fd; } +static int open_master_fd(struct master_header *hdr, bool write) +{ + int fd; + int rc; + + fd = open(TAGCACHE_FILE_MASTER, write ? O_RDWR : O_RDONLY); + if (fd < 0) + { + logf("master file open failed for R/W"); + tc_stat.ready = false; + return fd; + } + + tc_stat.econ = false; + + /* Check the header. */ + rc = read(fd, hdr, sizeof(struct master_header)); + if (hdr->tch.magic == TAGCACHE_MAGIC && rc == sizeof(struct master_header)) + { + /* Success. */ + return fd; + } + + /* Trying to read again, this time with endianess correction enabled. */ + lseek(fd, 0, SEEK_SET); + + rc = ecread(fd, hdr, 1, master_header_ec, true); + if (hdr->tch.magic != TAGCACHE_MAGIC || rc != sizeof(struct master_header)) + { + logf("header error"); + tc_stat.ready = false; + close(fd); + return -2; + } + + tc_stat.econ = true; + + return fd; +} + #ifndef __PCTOOL__ static bool do_timed_yield(void) { @@ -521,6 +564,8 @@ bool tagcache_find_index(struct tagcache_search *tcs, const char *filename) static bool get_index(int masterfd, int idxid, struct index_entry *idx, bool use_ram) { + bool localfd = false; + if (idxid < 0) { logf("Incorrect idxid: %d", idxid); @@ -540,15 +585,31 @@ static bool get_index(int masterfd, int idxid, (void)use_ram; #endif + if (masterfd < 0) + { + struct master_header tcmh; + + localfd = true; + masterfd = open_master_fd(&tcmh, false); + if (masterfd < 0) + return false; + } + lseek(masterfd, idxid * sizeof(struct index_entry) + sizeof(struct master_header), SEEK_SET); if (ecread(masterfd, idx, 1, index_entry_ec, tc_stat.econ) != sizeof(struct index_entry)) { logf("read error #3"); + if (localfd) + close(masterfd); + return false; } + if (localfd) + close(masterfd); + if (idx->flag & FLAG_DELETED) return false; @@ -1080,46 +1141,6 @@ static void remove_files(void) } -static int open_master_fd(struct master_header *hdr, bool write) -{ - int fd; - int rc; - - fd = open(TAGCACHE_FILE_MASTER, write ? O_RDWR : O_RDONLY); - if (fd < 0) - { - logf("master file open failed for R/W"); - tc_stat.ready = false; - return fd; - } - - tc_stat.econ = false; - - /* Check the header. */ - rc = read(fd, hdr, sizeof(struct master_header)); - if (hdr->tch.magic == TAGCACHE_MAGIC && rc == sizeof(struct master_header)) - { - /* Success. */ - return fd; - } - - /* Trying to read again, this time with endianess correction enabled. */ - lseek(fd, 0, SEEK_SET); - - rc = ecread(fd, hdr, 1, master_header_ec, true); - if (hdr->tch.magic != TAGCACHE_MAGIC || rc != sizeof(struct master_header)) - { - logf("header error"); - tc_stat.ready = false; - close(fd); - return -2; - } - - tc_stat.econ = true; - - return fd; -} - static bool check_all_headers(void) { struct master_header myhdr; @@ -1602,16 +1623,17 @@ static int check_if_empty(char **tag) entry.tag_length[tag] = check_if_empty(data); \ offset += entry.tag_length[tag] +static void add_tagcache(char *path, unsigned long mtime #if defined(HAVE_TC_RAMCACHE) && defined(HAVE_DIRCACHE) -static void add_tagcache(char *path, const struct dirent *dc) -#else -static void add_tagcache(char *path) + ,const struct dirent *dc #endif + ) { struct mp3entry id3; struct temp_file_entry entry; bool ret; int fd; + int idx_id = -1; char tracknumfix[3]; int offset = 0; int path_length = strlen(path); @@ -1637,16 +1659,40 @@ static void add_tagcache(char *path) #if defined(HAVE_TC_RAMCACHE) && defined(HAVE_DIRCACHE) if (tc_stat.ramcache && dircache_is_enabled()) { - if (find_entry_ram(path, dc) >= 0) - return ; + idx_id = find_entry_ram(path, dc); } else #endif { if (filenametag_fd >= 0) { - if (find_entry_disk(path) >= 0) - return ; + idx_id = find_entry_disk(path); + } + } + + /* Check if file has been modified. */ + if (idx_id >= 0) + { + struct index_entry idx; + + if (!get_index(-1, idx_id, &idx, true)) + { + logf("failed to retrieve index entry"); + return ; + } + + if ((unsigned long)idx.tag_seek[tag_mtime] == mtime) + { + /* No changes to file. */ + return ; + } + + /* Metadata might have been changed. Delete the entry. */ + logf("Re-adding: %s", path); + if (!delete_entry(idx_id)) + { + logf("delete_entry failed: %d", idx_id); + return ; } } @@ -1706,6 +1752,7 @@ static void add_tagcache(char *path) entry.tag_offset[tag_tracknumber] = id3.tracknum; entry.tag_offset[tag_length] = id3.length; entry.tag_offset[tag_bitrate] = id3.bitrate; + entry.tag_offset[tag_mtime] = mtime; /* String tags. */ has_albumartist = id3.albumartist != NULL @@ -3241,7 +3288,8 @@ bool tagcache_create_changelog(struct tagcache_search *tcs) static bool delete_entry(long idx_id) { - int fd; + int fd = -1; + /*int dbdel_fd = -1;*/ int tag, i; struct index_entry idx, myidx; struct master_header myhdr; @@ -3259,13 +3307,23 @@ static bool delete_entry(long idx_id) if ( (fd = open_master_fd(&myhdr, true) ) < 0) return false; + /* + TODO: Implement soon. + dbdel_fd = open(TAGCACHE_FILE_DELETED, O_RDWR | O_APPEND | O_CREAT); + if (dbdel_fd < 0) + { + logf("delete_entry(): DBDEL open failed"); + goto cleanup; + } + close(dbdel_fd); + dbdel_fd = -1; + */ lseek(fd, idx_id * sizeof(struct index_entry), SEEK_CUR); if (ecread(fd, &myidx, 1, index_entry_ec, tc_stat.econ) != sizeof(struct index_entry)) { logf("delete_entry(): read error"); - close(fd); - return false; + goto cleanup; } myidx.flag |= FLAG_DELETED; @@ -3274,8 +3332,7 @@ static bool delete_entry(long idx_id) != sizeof(struct index_entry)) { logf("delete_entry(): write_error"); - close(fd); - return false; + goto cleanup; } /* Now check which tags are no longer in use (if any) */ @@ -3298,8 +3355,7 @@ static bool delete_entry(long idx_id) != sizeof(struct index_entry)) { logf("delete_entry(): read error #2"); - close(fd); - return false; + goto cleanup; } idxp = &idx; } @@ -3318,6 +3374,7 @@ static bool delete_entry(long idx_id) } close(fd); + fd = -1; /* Now delete all tags no longer in use. */ for (tag = 0; tag < TAG_COUNT; tag++) @@ -3347,7 +3404,7 @@ static bool delete_entry(long idx_id) if (fd < 0) { logf("open failed"); - return false; + goto cleanup; } /* Skip the header block */ @@ -3368,6 +3425,14 @@ static bool delete_entry(long idx_id) } return true; + + cleanup: + if (fd >= 0) + close(fd); +/* if (dbdel_fd >= 0) + close(dbdel_fd); + */ + return false; } #ifndef __PCTOOL__ @@ -3846,11 +3911,13 @@ static bool check_dir(const char *dirname) else { tc_stat.curentry = curpath; + + /* Add a new entry to the temporary db file. */ + add_tagcache(curpath, (entry->wrtdate << 16) | entry->wrttime #if defined(HAVE_TC_RAMCACHE) && defined(HAVE_DIRCACHE) - add_tagcache(curpath, dir->internal_entry); -#else - add_tagcache(curpath); + , dir->internal_entry #endif + ); /* Wait until current path for debug screen is read and unset. */ while (tc_stat.syncscreen && tc_stat.curentry != NULL) diff --git a/apps/tagcache.h b/apps/tagcache.h index 39f0c61f7e..879cf66fbd 100644 --- a/apps/tagcache.h +++ b/apps/tagcache.h @@ -25,13 +25,13 @@ enum tag_type { tag_artist = 0, tag_album, tag_genre, tag_title, tag_filename, tag_composer, tag_comment, tag_albumartist, tag_grouping, tag_year, tag_discnumber, tag_tracknumber, tag_bitrate, tag_length, tag_playcount, tag_rating, - tag_playtime, tag_lastplayed, tag_commitid, + tag_playtime, tag_lastplayed, tag_commitid, tag_mtime, /* Virtual tags */ tag_virt_length_min, tag_virt_length_sec, tag_virt_playtime_min, tag_virt_playtime_sec, tag_virt_entryage, tag_virt_autoscore }; -#define TAG_COUNT 19 +#define TAG_COUNT 20 /* Maximum length of a single tag. */ #define TAG_MAXLEN (MAX_PATH*2) @@ -43,7 +43,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 0x5443480b +#define TAGCACHE_MAGIC 0x5443480c /* How much to allocate extra space for ramcache. */ #define TAGCACHE_RESERVE 32768 @@ -72,10 +72,23 @@ enum tag_type { tag_artist = 0, tag_album, tag_genre, tag_title, #define TAGCACHE_MAX_CLAUSES 32 /* Tag database files. */ + +/* Temporary database containing new tags to be committed to the main db. */ #define TAGCACHE_FILE_TEMP ROCKBOX_DIR "/database_tmp.tcd" + +/* Database containing deleted entries with runtime statistics. */ +#define TAGCACHE_FILE_DELETED ROCKBOX_DIR "/database_del.tcd" + +/* The main database master index and numeric data. */ #define TAGCACHE_FILE_MASTER ROCKBOX_DIR "/database_idx.tcd" + +/* The main database string data. */ #define TAGCACHE_FILE_INDEX ROCKBOX_DIR "/database_%d.tcd" + +/* ASCII dumpfile of the DB contents. */ #define TAGCACHE_FILE_CHANGELOG ROCKBOX_DIR "/database_changelog.txt" + +/* Serialized DB. */ #define TAGCACHE_STATEFILE ROCKBOX_DIR "/database_state.tcd" /* Flags */ -- cgit v1.2.3