Index: apps/cuesheet.c =================================================================== --- apps/cuesheet.c (revision 28940) +++ apps/cuesheet.c (working copy) @@ -319,7 +319,14 @@ bool curr_cuesheet_skip(struct cuesheet *cue, int direction, unsigned long curr_pos) { int track = cue_find_current_track(cue, curr_pos); - + bool prevent_skipback = false; + +#ifdef HAVE_TAGCACHE + /* Protect resume position by disallowing skipback to 00:00 of + current track? */ + prevent_skipback = disallow_skipback(audio_current_track()); +#endif + if (direction >= 0 && track == cue->track_count - 1) { /* we want to get out of the cuesheet */ @@ -334,8 +341,9 @@ to previous cuesheet segment. If skipping backward after DEFAULT_SKIP_TRESH seconds have elapsed, skip to the start of the current cuesheet segment */ - if (direction == 1 || - ((curr_pos - cue->tracks[track].offset) < DEFAULT_SKIP_TRESH)) + if (direction == 1 + || ((curr_pos - cue->tracks[track].offset) < DEFAULT_SKIP_TRESH) + || prevent_skipback) { track += direction; } Index: apps/lang/english.lang =================================================================== --- apps/lang/english.lang (revision 28940) +++ apps/lang/english.lang (working copy) @@ -12685,3 +12685,185 @@ *: "Filesize" + + id: LANG_AUTORESUME + desc: resume settings menu + user: core + + *: "Automatic resume" + + + *: "Automatic resume" + + + *: "Automatic resume" + + + + id: LANG_AUTORESUME_ENABLE + desc: resume settings menu + user: core + + *: "Enable automatic resume" + + + *: "Enable automatic resume" + + + *: "Enable automatic resume" + + + + id: LANG_AUTORESUME_ENABLE_YES + desc: resume settings menu + user: core + + *: "Yes (requires initialized database)" + + + *: "Yes (requires initialized database)" + + + *: "Yes (requires initialized database)" + + + + id: LANG_AUTORESUME_AUTOMATIC + desc: resume on automatic track change + user: core + + *: "Resume on automatic track change" + + + *: "Resume on automatic track change" + + + *: "Resume on automatic track change" + + + + id: LANG_AUTORESUME_ALL_RESUMABLE + desc: autoresume: all resumable tracks + user: core + + *: "All resumable tracks" + + + *: "All resumable tracks" + + + *: "All resumable tracks" + + + + id: LANG_AUTORESUME_SKIPBACK + desc: Skipback settings menu to enable/disable resume + user: core + + *: "Allow skip back to intro" + + + *: "Allow skip back to intro" + + + *: "Allow skip back to intro" + + + + id: LANG_AUTORESUME_SKIPBACK_ALWAYS + desc: Skipback settings menu to enable/disable resume + user: core + + *: "Always (Looses resume position)" + + + *: "Always (Looses resume position)" + + + *: "Always. Looses resume position" + + + + id: LANG_AUTORESUME_SKIPBACK_NONRESUME + desc: Skipback settings menu to enable/disable resume + user: core + + *: "Non-resumable tracks only" + + + *: "Non-resumable tracks only" + + + *: "Non-resumable tracks only" + + + + id: LANG_AUTORESUME_CUSTOM_MENU + desc: custom resume filter menu + user: core + + *: "Resumable tracks" + + + *: "Resumable tracks" + + + *: "Resumable tracks" + + + + id: LANG_AUTORESUME_CUSTOM + desc: enable custom resume filter (based on filename/genre tag)? + user: core + + *: "Enable customization" + + + *: "Enable customization" + + + *: "Enable customization" + + + + id: LANG_AUTORESUME_CUSTOM_NO + desc: enable custom resume filter (based on filename/genre tag)? + user: core + + *: "No (resume all tracks)" + + + *: "No (resume all tracks)" + + + *: "No (resume all tracks)" + + + + id: LANG_AUTORESUME_PATHSUB + desc: custom resume filter: filename + user: core + + *: "Path substrings (comma-separated)" + + + *: "Path substrings (comma-separated)" + + + *: "Path substrings (comma-separated)" + + + + id: LANG_AUTORESUME_TAGSUB + desc: custom resume filter: genre tag + user: core + + *: "Genre tag substrings (comma-separated)" + + + *: "Genre tag substrings (comma-separated)" + + + *: "Genre tag substrings (comma-separated)" + + Index: apps/gui/wps.c =================================================================== --- apps/gui/wps.c (revision 28940) +++ apps/gui/wps.c (working copy) @@ -465,7 +465,13 @@ static void prev_track(unsigned long skip_thresh) { struct wps_state *state = skin_get_global_state(); - if (state->id3->elapsed < skip_thresh) + bool prevent_skipback = false; + +#ifdef HAVE_TAGCACHE + prevent_skipback = disallow_skipback(state->id3); +#endif + + if (state->id3->elapsed < skip_thresh || prevent_skipback) { audio_prev(); return; Index: apps/settings.h =================================================================== --- apps/settings.h (revision 28940) +++ apps/settings.h (working copy) @@ -125,6 +125,10 @@ /* scrollbar visibility/position */ enum { SCROLLBAR_OFF = 0, SCROLLBAR_LEFT, SCROLLBAR_RIGHT }; +/* autoresume settings */ +enum { AUTORESUME_SKIPBACK_ALWAYS = 0, AUTORESUME_SKIPBACK_NONRESUME, + AUTORESUME_SKIPBACK_NEVER }; + /* Alarm settings */ #ifdef HAVE_RTC_ALARM enum { ALARM_START_WPS = 0, @@ -576,6 +580,14 @@ bool tagcache_ram; /* load tagcache to ram? */ #endif bool tagcache_autoupdate; /* automatically keep tagcache in sync? */ + bool autoresume_enable; /* enable autoupdate feature? */ + int autoresume_automatic; /* resume next track? -1=never, + 0=always, other = if longer than .. min */ + int autoresume_skipback; /* skipback for resumable tracks? 0=do skip back, + 1=only for nonresumable tracks, 2=always */ + bool autoresume_custom; /* resume only for files matching file/tag below? */ + unsigned char autoresume_pathsub[MAX_PATHNAME+1]; /* comma-separated list */ + unsigned char autoresume_tagsub[MAX_PATHNAME+1]; /* dito */ bool runtimedb; /* runtime database active? */ #endif /* HAVE_TAGCACHE */ Index: apps/menus/settings_menu.c =================================================================== --- apps/menus/settings_menu.c (revision 28940) +++ apps/menus/settings_menu.c (working copy) @@ -28,6 +28,7 @@ #include "action.h" #include "settings.h" #include "menu.h" +#include "keyboard.h" #include "sound_menu.h" #include "exported_menus.h" #include "tree.h" @@ -367,6 +368,45 @@ /***********************************/ /***********************************/ +/* AUTORESUME MENU */ +#ifdef HAVE_TAGCACHE + +static void edit_name_buf(char *textbuf) +{ + kbd_input (textbuf, MAX_PATHNAME+1); +} + +MENUITEM_SETTING(autoresume_enable, &global_settings.autoresume_enable, NULL); +#if 0 +MENUITEM_SETTING(autoresume_automatic, &global_settings.autoresume_automatic, + NULL); +MENUITEM_SETTING(autoresume_skipback, &global_settings.autoresume_skipback, + NULL); + +MENUITEM_SETTING(autoresume_custom, &global_settings.autoresume_custom, NULL); +MENUITEM_FUNCTION(autoresume_pathsub, MENU_FUNC_USEPARAM, + ID2P(LANG_AUTORESUME_PATHSUB), + edit_name_buf, &global_settings.autoresume_pathsub, NULL, + Icon_NOICON); +MENUITEM_FUNCTION(autoresume_tagsub, MENU_FUNC_USEPARAM, + ID2P(LANG_AUTORESUME_TAGSUB), + edit_name_buf, &global_settings.autoresume_tagsub, NULL, + Icon_NOICON); + +MAKE_MENU(autoresume_custom_menu, ID2P(LANG_AUTORESUME_CUSTOM_MENU), + 0, Icon_NOICON, + &autoresume_custom, &autoresume_pathsub, &autoresume_tagsub); +#endif +MAKE_MENU(autoresume_menu, ID2P(LANG_AUTORESUME), + 0, Icon_NOICON, + &autoresume_enable); + + +#endif /* HAVE_TAGCACHE */ +/* AUTORESUME MENU */ +/***********************************/ + +/***********************************/ /* VOICE MENU */ static int talk_callback(int action,const struct menu_item_ex *this_item); MENUITEM_SETTING(talk_menu_item, &global_settings.talk_menu, NULL); @@ -436,7 +476,11 @@ &tagcache_menu, #endif &display_menu, &system_menu, - &bookmark_settings_menu, &browse_langs, &voice_settings_menu, + &bookmark_settings_menu, +#ifdef HAVE_TAGCACHE + &autoresume_menu, +#endif + &browse_langs, &voice_settings_menu, #ifdef HAVE_HOTKEY &hotkey_menu, #endif Index: apps/metadata.c =================================================================== --- apps/metadata.c (revision 28940) +++ apps/metadata.c (working copy) @@ -25,6 +25,7 @@ #include "debug.h" #include "logf.h" +#include "settings.h" #include "cuesheet.h" #include "metadata.h" @@ -419,3 +420,66 @@ memcpy(dest, orig, sizeof(struct mp3entry)); adjust_mp3entry(dest, dest, orig); } + +#ifdef HAVE_TAGCACHE + +enum { AUTORESUMABLE_UNKNOWN = 0, AUTORESUMABLE_TRUE, AUTORESUMABLE_FALSE }; + +bool autoresumable(struct mp3entry *id3) +{ + unsigned char search[MAX_PATHNAME+1]; + char *saveptr, *substr; + bool is_resumable; + + if (id3->autoresumable) /* result cached? */ + return id3->autoresumable == AUTORESUMABLE_TRUE; + + is_resumable = true; + + if (! global_settings.autoresume_custom) + goto out; + + strcpy(search, global_settings.autoresume_pathsub); + + for (substr = strtok_r(search, ",", &saveptr); + substr; + substr = strtok_r(NULL, ",", &saveptr)) + { + if (id3->path && strcasestr(id3->path, substr)) + goto out; + } + + strcpy(search, global_settings.autoresume_tagsub); + + for (substr = strtok_r(search, ",", &saveptr); + substr; + substr = strtok_r(NULL, ",", &saveptr)) + { + if (id3->genre_string && strcasestr(id3->genre_string, substr)) + goto out; + } + + is_resumable = false; + + out: + /* cache result */ + id3->autoresumable = + is_resumable ? AUTORESUMABLE_TRUE : AUTORESUMABLE_FALSE; + + logf("autoresumable: %s with genre %s is%s resumable", + id3->path, + id3->genre_string ? id3->genre_string : "(NULL)", + is_resumable ? "" : " not"); + + return is_resumable; +} + +bool disallow_skipback(struct mp3entry *id3) +{ + return global_settings.autoresume_enable + && global_settings.autoresume_skipback != AUTORESUME_SKIPBACK_ALWAYS + && (global_settings.autoresume_skipback == AUTORESUME_SKIPBACK_NEVER + || autoresumable(id3)); +} + +#endif /* HAVE_TAGCACHE */ Index: apps/metadata.h =================================================================== --- apps/metadata.h (revision 28940) +++ apps/metadata.h (working copy) @@ -247,6 +247,8 @@ int index; /* playlist index */ #ifdef HAVE_TAGCACHE + unsigned char autoresumable; /* caches result of autoresumable() */ + /* runtime database fields */ long tagcache_idx; /* 0=invalid, otherwise idx+1 */ int rating; @@ -279,10 +281,16 @@ bool mp3info(struct mp3entry *entry, const char *filename); void adjust_mp3entry(struct mp3entry *entry, void *dest, const void *orig); void copy_mp3entry(struct mp3entry *dest, const struct mp3entry *orig); + #if CONFIG_CODEC == SWCODEC void strip_tags(int handle_id); #endif +#ifdef HAVE_TAGCACHE +bool autoresumable(struct mp3entry *id3); +bool disallow_skipback(struct mp3entry *id3); #endif +#endif + Index: apps/settings_list.c =================================================================== --- apps/settings_list.c (revision 28940) +++ apps/settings_list.c (working copy) @@ -332,6 +332,33 @@ return TALK_ID(value, UNIT_SEC); } +#ifdef HAVE_TAGCACHE +static const char* formatter_autoresume_automatic(char *buffer, + size_t buffer_size, + int val, const char *unit) +{ + (void)unit; + if (val == -1) + return str(LANG_NEVER); + else if (val == 0) + return str(LANG_AUTORESUME_ALL_RESUMABLE); + else + snprintf(buffer, buffer_size, "%d min", val); + return buffer; +} + +static int32_t getlang_autoresume_automatic(int value, int unit) +{ + (void)unit; + if (value == -1) + return LANG_NEVER; + else if (value == 0) + return LANG_AUTORESUME_ALL_RESUMABLE; + else + return TALK_ID(value, UNIT_MIN); +} +#endif + #ifdef HAVE_BACKLIGHT #ifdef SIMULATOR #define DEFAULT_BACKLIGHT_TIMEOUT 0 @@ -1257,6 +1284,30 @@ ID2P(LANG_RANDOM)), #ifdef HAVE_TAGCACHE + BOOL_SETTING(0, autoresume_enable, LANG_AUTORESUME_ENABLE, false, + "autoresume enable", off_on, + LANG_AUTORESUME_ENABLE_YES, LANG_SET_BOOL_NO, NULL), + + TABLE_SETTING(F_ALLOW_ARBITRARY_VALS, autoresume_automatic, + LANG_AUTORESUME_AUTOMATIC, 0, "autoresume next track", + "never,all,5m,10m,15m,20m,25m,30m", + UNIT_MIN, formatter_autoresume_automatic, + getlang_autoresume_automatic, NULL, + 8, -1,0,5,10,15,20,25,30), + CHOICE_SETTING(0, autoresume_skipback, LANG_AUTORESUME_SKIPBACK, + AUTORESUME_SKIPBACK_NONRESUME, + "autoresume skipback", "always,nonresumable,never", NULL, 3, + ID2P(LANG_AUTORESUME_SKIPBACK_ALWAYS), + ID2P(LANG_AUTORESUME_SKIPBACK_NONRESUME), + ID2P(LANG_NEVER)), + BOOL_SETTING(0, autoresume_custom, LANG_AUTORESUME_CUSTOM, false, + "autoresume custom filter", off_on, + LANG_SET_BOOL_YES, LANG_AUTORESUME_CUSTOM_NO, NULL), + TEXT_SETTING(0, autoresume_pathsub, "autoresume path substring", + "podcast,audiobook", NULL, NULL), + TEXT_SETTING(0, autoresume_tagsub, "autoresume tag substring", + "podcast,audiobook", NULL, NULL), + OFFON_SETTING(0, runtimedb, LANG_RUNTIMEDB_ACTIVE, false, "gather runtime data", NULL), #endif Index: apps/tagcache.c =================================================================== --- apps/tagcache.c (revision 28940) +++ apps/tagcache.c (working copy) @@ -195,7 +195,7 @@ /** Note: This should be (1 + TAG_COUNT) amount of l's. */ -static const char *index_entry_ec = "lllllllllllllllllllll"; +static const char *index_entry_ec = "llllllllllllllllllllll"; static const char *tagcache_header_ec = "lll"; static const char *master_header_ec = "llllll"; @@ -1695,6 +1695,13 @@ if (id3->bitrate == 0) id3->bitrate = 1; + if (global_settings.autoresume_enable) + { + id3->offset = get_tag_numeric(entry, tag_lastoffset, idx_id); + logf("tagcache_fill_tags: Set offset for %s to %lX\n", + id3->title, id3->offset); + } + return true; } #endif @@ -2315,6 +2322,7 @@ tmpdb_copy_tag(tag_playtime); tmpdb_copy_tag(tag_lastplayed); tmpdb_copy_tag(tag_commitid); + tmpdb_copy_tag(tag_lastoffset); /* Avoid processing this entry again. */ idx.flag |= FLAG_RESURRECTED; Index: apps/tagcache.h =================================================================== --- apps/tagcache.h (revision 28940) +++ apps/tagcache.h (working copy) @@ -32,7 +32,7 @@ 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_mtime, + tag_playtime, tag_lastplayed, tag_commitid, tag_mtime, tag_lastoffset, /* Real tags end here, count them. */ TAG_COUNT, /* Virtual tags */ @@ -50,7 +50,7 @@ #define IDX_BUF_DEPTH 64 /* Tag Cache Header version 'TCHxx'. Increment when changing internal structures. */ -#define TAGCACHE_MAGIC 0x5443480d +#define TAGCACHE_MAGIC 0x5443480e /* How much to allocate extra space for ramcache. */ #define TAGCACHE_RESERVE 32768 @@ -103,6 +103,7 @@ (1LU << tag_tracknumber) | (1LU << tag_length) | (1LU << tag_bitrate) | \ (1LU << tag_playcount) | (1LU << tag_rating) | (1LU << tag_playtime) | \ (1LU << tag_lastplayed) | (1LU << tag_commitid) | (1LU << tag_mtime) | \ + (1LU << tag_lastoffset) | \ (1LU << tag_virt_length_min) | (1LU << tag_virt_length_sec) | \ (1LU << tag_virt_playtime_min) | (1LU << tag_virt_playtime_sec) | \ (1LU << tag_virt_entryage) | (1LU << tag_virt_autoscore)) Index: apps/playback.c =================================================================== --- apps/playback.c (revision 28940) +++ apps/playback.c (working copy) @@ -992,14 +992,34 @@ /* Update track info after successful a codec track change */ static void audio_update_trackinfo(void) { + bool resume = false; + /* Load the curent track's metadata into curtrack_id3 */ if (CUR_TI->id3_hid >= 0) copy_mp3entry(thistrack_id3, bufgetid3(CUR_TI->id3_hid)); /* Reset current position */ thistrack_id3->elapsed = 0; - thistrack_id3->offset = 0; +#ifdef HAVE_TAGCACHE + /* Ignoring resume position for automatic track change if so configured */ + resume = global_settings.autoresume_enable && + (!automatic_skip /* Resume all manually selected tracks */ + || (global_settings.autoresume_automatic >= 0 /* Not never resume? */ + && autoresumable(thistrack_id3) /* Pass Resume filter? */ + && (global_settings.autoresume_automatic == 0 /* Always resume? */ + || thistrack_id3->length /* Long track? */ + >= global_settings.autoresume_automatic * 60UL * 1000UL))); +#endif + + if (!resume) + { + thistrack_id3->offset = 0; + } + + logf("audio_update_trackinfo: Set offset for %s to %lX\n", + thistrack_id3->title, thistrack_id3->offset); + /* Update the codec API */ ci.filesize = CUR_TI->filesize; ci.id3 = thistrack_id3; @@ -1210,6 +1230,9 @@ { copy_mp3entry(thistrack_id3, id3); thistrack_id3->offset = offset; + logf("audio_load_track: set offset for %s to %lX\n", + thistrack_id3->title, + offset); } else memset(thistrack_id3, 0, sizeof(struct mp3entry)); @@ -1415,8 +1438,14 @@ return; } - /* All required data is now available for the codec. */ - tracks[track_widx].taginfo_ready = true; + /* All required data is now available for the codec -- unless the + autoresume feature is in effect. In the latter case, the codec + must wait until after PLAYBACK_EVENT_TRACK_BUFFER, which may + generate a resume position. */ +#ifdef HAVE_TAGCACHE + if (! global_settings.autoresume_enable) +#endif + tracks[track_widx].taginfo_ready = true; if (start_play) { @@ -1424,10 +1453,17 @@ buf_request_buffer_handle(tracks[track_widx].audio_hid); } + send_event(PLAYBACK_EVENT_TRACK_BUFFER, track_id3); + +#ifdef HAVE_TAGCACHE + /* In case the autoresume feature has been enabled, finally all + required data is available for the codec. */ + if (global_settings.autoresume_enable) + tracks[track_widx].taginfo_ready = true; +#endif + track_widx = (track_widx + 1) & MAX_TRACK_MASK; - send_event(PLAYBACK_EVENT_TRACK_BUFFER, track_id3); - /* load next track */ LOGFQUEUE("audio > audio Q_AUDIO_FILL_BUFFER"); queue_post(&audio_queue, Q_AUDIO_FILL_BUFFER, 0); Index: apps/tagtree.c =================================================================== --- apps/tagtree.c (revision 28940) +++ apps/tagtree.c (working copy) @@ -53,6 +53,8 @@ #include "storage.h" #include "dir.h" +#define str_or_empty(x) (x ? x : "(NULL)") + #define FILE_SEARCH_INSTRUCTIONS ROCKBOX_DIR "/tagnavi.config" static int tagtree_play_folder(struct tree_context* c); @@ -168,6 +170,8 @@ static struct tree_context *tc; +extern bool automatic_skip; /* Who initiated in-progress skip? (C/A-) */ + static int get_token_str(char *buf, int size) { /* Find the start. */ @@ -239,6 +243,7 @@ MATCH(tag, buf, "playcount", tag_playcount); MATCH(tag, buf, "rating", tag_rating); MATCH(tag, buf, "lastplayed", tag_lastplayed); + MATCH(tag, buf, "lastoffset", tag_lastoffset); MATCH(tag, buf, "commitid", tag_commitid); MATCH(tag, buf, "entryage", tag_virt_entryage); MATCH(tag, buf, "autoscore", tag_virt_autoscore); @@ -647,7 +652,7 @@ struct mp3entry *id3 = (struct mp3entry*)data; /* Do not gather data unless proper setting has been enabled. */ - if (!global_settings.runtimedb) + if (!global_settings.runtimedb && !global_settings.autoresume_enable) return; logf("be:%s", id3->path); @@ -661,13 +666,31 @@ return; } - id3->playcount = tagcache_get_numeric(&tcs, tag_playcount); - if (!id3->rating) - id3->rating = tagcache_get_numeric(&tcs, tag_rating); - id3->lastplayed = tagcache_get_numeric(&tcs, tag_lastplayed); - id3->score = tagcache_get_numeric(&tcs, tag_virt_autoscore) / 10; - id3->playtime = tagcache_get_numeric(&tcs, tag_playtime); + if (global_settings.runtimedb) + { + id3->playcount = tagcache_get_numeric(&tcs, tag_playcount); + if (!id3->rating) + id3->rating = tagcache_get_numeric(&tcs, tag_rating); + id3->lastplayed = tagcache_get_numeric(&tcs, tag_lastplayed); + id3->score = tagcache_get_numeric(&tcs, tag_virt_autoscore) / 10; + id3->playtime = tagcache_get_numeric(&tcs, tag_playtime); + + logf("-> %ld/%ld", id3->playcount, id3->playtime); + } + if (global_settings.autoresume_enable) + { + /* Load current file resume offset if not already defined (by + another resume mechanism) */ + if (id3->offset == 0) + { + id3->offset = tagcache_get_numeric(&tcs, tag_lastoffset); + + logf("tagtree_buffer_event: Set offset for %s to %lX\n", + str_or_empty(id3->title), id3->offset); + } + } + /* Store our tagcache index pointer. */ id3->tagcache_idx = tcs.idx_id+1; @@ -676,16 +699,14 @@ static void tagtree_track_finish_event(void *data) { - long playcount; - long playtime; long lastplayed; long tagcache_idx; struct mp3entry *id3 = (struct mp3entry*)data; /* Do not gather data unless proper setting has been enabled. */ - if (!global_settings.runtimedb) + if (!global_settings.runtimedb && !global_settings.autoresume_enable) { - logf("runtimedb gathering not enabled"); + logf("runtimedb gathering and autoresume not enabled"); return; } @@ -704,7 +725,6 @@ return; } - playcount = id3->playcount + 1; lastplayed = tagcache_increase_serial(); if (lastplayed < 0) { @@ -712,17 +732,39 @@ return; } - /* Ignore the last 15s (crossfade etc.) */ - playtime = id3->playtime + MIN(id3->length, id3->elapsed + 15 * 1000); - - logf("ube:%s", id3->path); - logf("-> %ld/%ld", playcount, playtime); - logf("-> %ld/%ld/%ld", id3->elapsed, id3->length, MIN(id3->length, id3->elapsed + 15 * 1000)); + if (global_settings.runtimedb) + { + long playcount; + long playtime; - /* Queue the updates to the tagcache system. */ - tagcache_update_numeric(tagcache_idx, tag_playcount, playcount); - tagcache_update_numeric(tagcache_idx, tag_playtime, playtime); - tagcache_update_numeric(tagcache_idx, tag_lastplayed, lastplayed); + playcount = id3->playcount + 1; + + /* Ignore the last 15s (crossfade etc.) */ + playtime = id3->playtime + MIN(id3->length, id3->elapsed + 15 * 1000); + + logf("ube:%s", id3->path); + logf("-> %ld/%ld", playcount, playtime); + logf("-> %ld/%ld/%ld", id3->elapsed, id3->length, + MIN(id3->length, id3->elapsed + 15 * 1000)); + + /* Queue the updates to the tagcache system. */ + tagcache_update_numeric(tagcache_idx, tag_playcount, playcount); + tagcache_update_numeric(tagcache_idx, tag_playtime, playtime); + tagcache_update_numeric(tagcache_idx, tag_lastplayed, lastplayed); + } + + if (global_settings.autoresume_enable + /* do not update resume info when manually skipping across a track */ + && (automatic_skip || (id3->elapsed >= 15UL * 1000UL))) + { + unsigned long offset + = automatic_skip || !autoresumable(id3) ? 0 : id3->offset; + + tagcache_update_numeric(tagcache_idx, tag_lastoffset, offset); + + logf("tagtree_track_finish_event: Save offset for %s: %lX", + str_or_empty(id3->title), offset); + } } bool tagtree_export(void)