Support for skip_count and time_skipped file metadata

This commit is contained in:
ejurgensen 2018-08-18 22:56:27 +02:00
parent 5ee0b69d39
commit fde0a28142
15 changed files with 170 additions and 68 deletions

View File

@ -820,7 +820,8 @@ curl -X GET "http://localhost:3689/api/library/playlists/1/tracks"
"disc_number": 1, "disc_number": 1,
"length_ms": 223085, "length_ms": 223085,
"play_count": 2, "play_count": 2,
"last_time_played": "2018-02-23T10:31:20Z", "skip_count": 1,
"time_played": "2018-02-23T10:31:20Z",
"media_kind": "music", "media_kind": "music",
"data_kind": "file", "data_kind": "file",
"path": "/music/srv/Heinrich/Solange wir tanzen/01 Solange wir tanzen.mp3", "path": "/music/srv/Heinrich/Solange wir tanzen/01 Solange wir tanzen.mp3",

View File

@ -36,12 +36,14 @@ Where valid field-names (with their types) are:
* `data_kind` (enumeration) * `data_kind` (enumeration)
* `media_kind` (enumeration) * `media_kind` (enumeration)
* `play_count` (integer) * `play_count` (integer)
* `skip_count` (integer)
* `rating` (integer) * `rating` (integer)
* `year` (integer) * `year` (integer)
* `compilation` (integer) * `compilation` (integer)
* `time_added` (date) * `time_added` (date)
* `time_modified` (date) * `time_modified` (date)
* `time_played` (date) * `time_played` (date)
* `time_skipped` (date)
Valid operators include: Valid operators include:
* `is`, `includes`, `starts with` (string) * `is`, `includes`, `starts with` (string)

View File

@ -85,6 +85,7 @@ STRTAG : 'artist'
; ;
INTTAG : 'play_count' INTTAG : 'play_count'
| 'skip_count'
| 'rating' | 'rating'
| 'year' | 'year'
| 'compilation' | 'compilation'
@ -93,6 +94,7 @@ INTTAG : 'play_count'
DATETAG : 'time_added' DATETAG : 'time_added'
| 'time_modified' | 'time_modified'
| 'time_played' | 'time_played'
| 'time_skipped'
; ;
ENUMTAG : 'data_kind' ENUMTAG : 'data_kind'

View File

@ -27,6 +27,7 @@ struct dmap_query_field_map;
"daap.songdataurl", "f.url", 0 "daap.songdataurl", "f.url", 0
"daap.songdateadded", "f.time_added", 1 "daap.songdateadded", "f.time_added", 1
"daap.songdatemodified", "f.time_modified", 1 "daap.songdatemodified", "f.time_modified", 1
"daap.songlastskipdate", "f.time_skipped", 1
"daap.songdatereleased", "f.date_released", 1 "daap.songdatereleased", "f.date_released", 1
"daap.songdescription", "f.description", 0 "daap.songdescription", "f.description", 0
"daap.songdisccount", "f.total_discs", 1 "daap.songdisccount", "f.total_discs", 1
@ -40,6 +41,7 @@ struct dmap_query_field_map;
"daap.songtrackcount", "f.total_tracks", 1 "daap.songtrackcount", "f.total_tracks", 1
"daap.songtracknumber", "f.track", 1 "daap.songtracknumber", "f.track", 1
"daap.songuserplaycount", "f.play_count", 1 "daap.songuserplaycount", "f.play_count", 1
"daap.songuserskipcount", "f.skip_count", 1
"daap.songyear", "f.year", 1 "daap.songyear", "f.year", 1
"com.apple.itunes.mediakind", "f.media_kind", 1 "com.apple.itunes.mediakind", "f.media_kind", 1
"com.apple.itunes.extended-media-kind", "f.media_kind", 1 "com.apple.itunes.extended-media-kind", "f.media_kind", 1

123
src/db.c
View File

@ -158,6 +158,8 @@ static const struct col_type_map mfi_cols_map[] =
{ mfi_offsetof(virtual_path), DB_TYPE_STRING }, { mfi_offsetof(virtual_path), DB_TYPE_STRING },
{ mfi_offsetof(directory_id), DB_TYPE_INT }, { mfi_offsetof(directory_id), DB_TYPE_INT },
{ mfi_offsetof(date_released), DB_TYPE_INT }, { mfi_offsetof(date_released), DB_TYPE_INT },
{ mfi_offsetof(skip_count), DB_TYPE_INT },
{ mfi_offsetof(time_skipped), DB_TYPE_INT },
}; };
/* This list must be kept in sync with /* This list must be kept in sync with
@ -250,6 +252,8 @@ static const ssize_t dbmfi_cols_map[] =
dbmfi_offsetof(virtual_path), dbmfi_offsetof(virtual_path),
dbmfi_offsetof(directory_id), dbmfi_offsetof(directory_id),
dbmfi_offsetof(date_released), dbmfi_offsetof(date_released),
dbmfi_offsetof(skip_count),
dbmfi_offsetof(time_skipped),
}; };
/* This list must be kept in sync with /* This list must be kept in sync with
@ -2344,6 +2348,24 @@ db_file_inc_playcount(int id)
#undef Q_TMPL #undef Q_TMPL
} }
void
db_file_inc_skipcount(int id)
{
#define Q_TMPL "UPDATE files SET skip_count = skip_count + 1, time_skipped = %" PRIi64 " WHERE id = %d;"
char *query;
query = sqlite3_mprintf(Q_TMPL, (int64_t)time(NULL), id);
if (!query)
{
DPRINTF(E_LOG, L_DB, "Out of memory for query string\n");
return;
}
db_query_run(query, 1, 0);
#undef Q_TMPL
}
void void
db_file_ping(int id) db_file_ping(int id)
{ {
@ -2751,23 +2773,35 @@ db_file_fetch_byvirtualpath(const char *virtual_path)
int int
db_file_add(struct media_file_info *mfi) db_file_add(struct media_file_info *mfi)
{ {
#define Q_TMPL "INSERT INTO files (id, path, fname, title, artist, album, genre, comment, type, composer," \ #define Q_TMPL "INSERT INTO files (" \
" orchestra, conductor, grouping, url, bitrate, samplerate, song_length, file_size, year, track," \ " id, path, fname, title, artist, album, genre, comment, type," \
" total_tracks, disc, total_discs, bpm, compilation, artwork, rating, play_count, seek, data_kind, item_kind," \ " composer, orchestra, conductor, grouping, url, bitrate," \
" description, time_added, time_modified, time_played, db_timestamp, disabled, sample_count," \ " samplerate, song_length, file_size, year, track," \
" codectype, idx, has_video, contentrating, bits_per_sample, album_artist," \ " total_tracks, disc, total_discs, bpm, compilation, artwork," \
" media_kind, tv_series_name, tv_episode_num_str, tv_network_name, tv_episode_sort, tv_season_num, " \ " rating, play_count, seek, data_kind, item_kind, description," \
" songartistid, songalbumid, " \ " time_added, time_modified, time_played, db_timestamp," \
" title_sort, artist_sort, album_sort, composer_sort, album_artist_sort, virtual_path," \ " disabled, sample_count, codectype, idx, has_video," \
" directory_id, date_released) " \ " contentrating, bits_per_sample, album_artist, media_kind," \
" VALUES (NULL, '%q', '%q', TRIM(%Q), TRIM(%Q), TRIM(%Q), TRIM(%Q), TRIM(%Q), %Q, TRIM(%Q)," \ " tv_series_name, tv_episode_num_str, tv_network_name," \
" TRIM(%Q), TRIM(%Q), TRIM(%Q), %Q, %d, %d, %d, %" PRIi64 ", %d, %d," \ " tv_episode_sort, tv_season_num, songartistid, songalbumid," \
" %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d," \ " title_sort, artist_sort, album_sort, composer_sort," \
" %Q, %" PRIi64 ", %" PRIi64 ", %" PRIi64 ", %" PRIi64 ", %d, %" PRIi64 "," \ " album_artist_sort, virtual_path, directory_id, date_released,"\
" %Q, %d, %d, %d, %d, TRIM(%Q)," \ " skip_count, time_skipped" \
" %d, TRIM(%Q), TRIM(%Q), TRIM(%Q), %d, %d," \ ") VALUES (" \
" daap_songalbumid(LOWER(TRIM(%Q)), ''), daap_songalbumid(LOWER(TRIM(%Q)), LOWER(TRIM(%Q))), " \ " NULL, '%q', '%q', TRIM(%Q), TRIM(%Q), TRIM(%Q), TRIM(%Q), TRIM(%Q), %Q," \
" TRIM(%Q), TRIM(%Q), TRIM(%Q), TRIM(%Q), TRIM(%Q), TRIM(%Q), %d, %d);" " TRIM(%Q), TRIM(%Q), TRIM(%Q), TRIM(%Q), %Q, %d," \
" %d, %d, %" PRIi64 ", %d, %d," \
" %d, %d, %d, %d, %d, %d," \
" %d, %d, %d, %d, %d, %Q," \
" %" PRIi64 ", %" PRIi64 ", %" PRIi64 ", %" PRIi64 "," \
" %d, %" PRIi64 ", %Q, %d, %d," \
" %d, %d, TRIM(%Q), %d," \
" TRIM(%Q), TRIM(%Q), TRIM(%Q)," \
" %d, %d, daap_songalbumid(LOWER(TRIM(%Q)), ''), daap_songalbumid(LOWER(TRIM(%Q)), LOWER(TRIM(%Q)))," \
" TRIM(%Q), TRIM(%Q), TRIM(%Q), TRIM(%Q)," \
" TRIM(%Q), TRIM(%Q), %d, %d," \
" %d, %" PRIi64 "" \
");"
char *query; char *query;
char *errmsg; char *errmsg;
@ -2789,20 +2823,19 @@ db_file_add(struct media_file_info *mfi)
mfi->time_modified = mfi->db_timestamp; mfi->time_modified = mfi->db_timestamp;
query = sqlite3_mprintf(Q_TMPL, query = sqlite3_mprintf(Q_TMPL,
STR(mfi->path), STR(mfi->fname), mfi->title, mfi->artist, mfi->album, STR(mfi->path), STR(mfi->fname), mfi->title, mfi->artist, mfi->album, mfi->genre, mfi->comment, mfi->type,
mfi->genre, mfi->comment, mfi->type, mfi->composer, mfi->composer, mfi->orchestra, mfi->conductor, mfi->grouping, mfi->url, mfi->bitrate,
mfi->orchestra, mfi->conductor, mfi->grouping, mfi->url, mfi->bitrate, mfi->samplerate, mfi->song_length, mfi->file_size, mfi->year, mfi->track,
mfi->samplerate, mfi->song_length, mfi->file_size, mfi->year, mfi->track, mfi->total_tracks, mfi->disc, mfi->total_discs, mfi->bpm, mfi->compilation, mfi->artwork,
mfi->total_tracks, mfi->disc, mfi->total_discs, mfi->bpm, mfi->compilation, mfi->artwork, mfi->rating, mfi->play_count, mfi->seek, mfi->data_kind, mfi->item_kind, mfi->description,
mfi->rating, mfi->play_count, mfi->seek, mfi->data_kind, mfi->item_kind, (int64_t)mfi->time_added, (int64_t)mfi->time_modified, (int64_t)mfi->time_played, (int64_t)mfi->db_timestamp,
mfi->description, (int64_t)mfi->time_added, (int64_t)mfi->time_modified, mfi->disabled, mfi->sample_count, mfi->codectype, mfi->index, mfi->has_video,
(int64_t)mfi->time_played, (int64_t)mfi->db_timestamp, mfi->disabled, mfi->sample_count, mfi->contentrating, mfi->bits_per_sample, mfi->album_artist, mfi->media_kind,
mfi->codectype, mfi->index, mfi->has_video, mfi->tv_series_name, mfi->tv_episode_num_str, mfi->tv_network_name,
mfi->contentrating, mfi->bits_per_sample, mfi->album_artist, mfi->tv_episode_sort, mfi->tv_season_num, mfi->album_artist, mfi->album_artist, mfi->album,
mfi->media_kind, mfi->tv_series_name, mfi->tv_episode_num_str, mfi->title_sort, mfi->artist_sort, mfi->album_sort, mfi->composer_sort,
mfi->tv_network_name, mfi->tv_episode_sort, mfi->tv_season_num, mfi->album_artist_sort, mfi->virtual_path, mfi->directory_id, mfi->date_released,
mfi->album_artist, mfi->album_artist, mfi->album, mfi->title_sort, mfi->artist_sort, mfi->album_sort, mfi->skip_count, mfi->time_skipped);
mfi->composer_sort, mfi->album_artist_sort, mfi->virtual_path, mfi->directory_id, mfi->date_released);
if (!query) if (!query)
{ {
@ -2835,19 +2868,19 @@ int
db_file_update(struct media_file_info *mfi) db_file_update(struct media_file_info *mfi)
{ {
#define Q_TMPL "UPDATE files SET path = '%q', fname = '%q', title = TRIM(%Q), artist = TRIM(%Q), album = TRIM(%Q), genre = TRIM(%Q)," \ #define Q_TMPL "UPDATE files SET path = '%q', fname = '%q', title = TRIM(%Q), artist = TRIM(%Q), album = TRIM(%Q), genre = TRIM(%Q)," \
" comment = TRIM(%Q), type = %Q, composer = TRIM(%Q), orchestra = TRIM(%Q), conductor = TRIM(%Q), grouping = TRIM(%Q)," \ " comment = TRIM(%Q), type = %Q, composer = TRIM(%Q), orchestra = TRIM(%Q), conductor = TRIM(%Q), grouping = TRIM(%Q)," \
" url = %Q, bitrate = %d, samplerate = %d, song_length = %d, file_size = %" PRIi64 "," \ " url = %Q, bitrate = %d, samplerate = %d, song_length = %d, file_size = %" PRIi64 "," \
" year = %d, track = %d, total_tracks = %d, disc = %d, total_discs = %d, bpm = %d," \ " year = %d, track = %d, total_tracks = %d, disc = %d, total_discs = %d, bpm = %d," \
" compilation = %d, artwork = %d, rating = %d, seek = %d, data_kind = %d, item_kind = %d," \ " compilation = %d, artwork = %d, rating = %d, seek = %d, data_kind = %d, item_kind = %d," \
" description = %Q, time_modified = %" PRIi64 "," \ " description = %Q, time_modified = %" PRIi64 "," \
" db_timestamp = %" PRIi64 ", disabled = %" PRIi64 ", sample_count = %" PRIi64 "," \ " db_timestamp = %" PRIi64 ", disabled = %" PRIi64 ", sample_count = %" PRIi64 "," \
" codectype = %Q, idx = %d, has_video = %d," \ " codectype = %Q, idx = %d, has_video = %d," \
" bits_per_sample = %d, album_artist = TRIM(%Q)," \ " bits_per_sample = %d, album_artist = TRIM(%Q)," \
" media_kind = %d, tv_series_name = TRIM(%Q), tv_episode_num_str = TRIM(%Q)," \ " media_kind = %d, tv_series_name = TRIM(%Q), tv_episode_num_str = TRIM(%Q)," \
" tv_network_name = TRIM(%Q), tv_episode_sort = %d, tv_season_num = %d," \ " tv_network_name = TRIM(%Q), tv_episode_sort = %d, tv_season_num = %d," \
" songartistid = daap_songalbumid(LOWER(TRIM(%Q)), ''), songalbumid = daap_songalbumid(LOWER(TRIM(%Q)), LOWER(TRIM(%Q)))," \ " songartistid = daap_songalbumid(LOWER(TRIM(%Q)), ''), songalbumid = daap_songalbumid(LOWER(TRIM(%Q)), LOWER(TRIM(%Q)))," \
" title_sort = TRIM(%Q), artist_sort = TRIM(%Q), album_sort = TRIM(%Q), composer_sort = TRIM(%Q), album_artist_sort = TRIM(%Q)," \ " title_sort = TRIM(%Q), artist_sort = TRIM(%Q), album_sort = TRIM(%Q), composer_sort = TRIM(%Q), album_artist_sort = TRIM(%Q)," \
" virtual_path = TRIM(%Q), directory_id = %d, date_released = %d" \ " virtual_path = TRIM(%Q), directory_id = %d, date_released = %d, skip_count = %d, time_skipped = %" PRIi64 "" \
" WHERE id = %d;" " WHERE id = %d;"
char *query; char *query;
@ -2880,7 +2913,7 @@ db_file_update(struct media_file_info *mfi)
mfi->album_artist, mfi->album_artist, mfi->album, mfi->album_artist, mfi->album_artist, mfi->album,
mfi->title_sort, mfi->artist_sort, mfi->album_sort, mfi->title_sort, mfi->artist_sort, mfi->album_sort,
mfi->composer_sort, mfi->album_artist_sort, mfi->composer_sort, mfi->album_artist_sort,
mfi->virtual_path, mfi->directory_id, mfi->date_released, mfi->virtual_path, mfi->directory_id, mfi->date_released, mfi->skip_count, mfi->time_skipped,
mfi->id); mfi->id);
if (!query) if (!query)

View File

@ -219,6 +219,9 @@ struct media_file_info {
uint32_t directory_id; /* Id of directory */ uint32_t directory_id; /* Id of directory */
uint32_t date_released; uint32_t date_released;
uint32_t skip_count;
uint32_t time_skipped;
}; };
#define mfi_offsetof(field) offsetof(struct media_file_info, field) #define mfi_offsetof(field) offsetof(struct media_file_info, field)
@ -363,6 +366,8 @@ struct db_media_file_info {
char *virtual_path; char *virtual_path;
char *directory_id; char *directory_id;
char *date_released; char *date_released;
char *skip_count;
char *time_skipped;
}; };
#define dbmfi_offsetof(field) offsetof(struct db_media_file_info, field) #define dbmfi_offsetof(field) offsetof(struct db_media_file_info, field)
@ -557,6 +562,9 @@ db_files_get_count(void);
void void
db_file_inc_playcount(int id); db_file_inc_playcount(int id);
void
db_file_inc_skipcount(int id);
void void
db_file_ping(int id); db_file_ping(int id);

View File

@ -93,7 +93,9 @@
" album_artist_sort VARCHAR(1024) DEFAULT NULL COLLATE DAAP," \ " album_artist_sort VARCHAR(1024) DEFAULT NULL COLLATE DAAP," \
" virtual_path VARCHAR(4096) DEFAULT NULL," \ " virtual_path VARCHAR(4096) DEFAULT NULL," \
" directory_id INTEGER DEFAULT 0," \ " directory_id INTEGER DEFAULT 0," \
" date_released INTEGER DEFAULT 0" \ " date_released INTEGER DEFAULT 0," \
" skip_count INTEGER DEFAULT 0," \
" time_skipped INTEGER DEFAULT 0" \
");" ");"
#define T_PL \ #define T_PL \

View File

@ -26,7 +26,7 @@
* is a major upgrade. In other words minor version upgrades permit downgrading * is a major upgrade. In other words minor version upgrades permit downgrading
* forked-daapd after the database was upgraded. */ * forked-daapd after the database was upgraded. */
#define SCHEMA_VERSION_MAJOR 19 #define SCHEMA_VERSION_MAJOR 19
#define SCHEMA_VERSION_MINOR 8 #define SCHEMA_VERSION_MINOR 9
int int
db_init_indices(sqlite3 *hdl); db_init_indices(sqlite3 *hdl);

View File

@ -1651,6 +1651,23 @@ static const struct db_upgrade_query db_upgrade_v1908_queries[] =
}; };
#define U_V1909_ALTER_FILES_ADD_SKIP_COUNT \
"ALTER TABLE files ADD COLUMN skip_count INTEGER DEFAULT 0;"
#define U_V1909_ALTER_FILES_ADD_TIME_SKIPPED \
"ALTER TABLE files ADD COLUMN time_skipped INTEGER DEFAULT 0;"
#define U_V1909_SCVER_MINOR \
"UPDATE admin SET value = '09' WHERE key = 'schema_version_minor';"
static const struct db_upgrade_query db_upgrade_v1909_queries[] =
{
{ U_V1909_ALTER_FILES_ADD_SKIP_COUNT, "alter table files add column skip_count" },
{ U_V1909_ALTER_FILES_ADD_TIME_SKIPPED, "alter table files add column time_skipped" },
{ U_V1909_SCVER_MINOR, "set schema_version_minor to 09" },
};
int int
db_upgrade(sqlite3 *hdl, int db_ver) db_upgrade(sqlite3 *hdl, int db_ver)
{ {
@ -1663,7 +1680,7 @@ db_upgrade(sqlite3 *hdl, int db_ver)
switch (db_ver) switch (db_ver)
{ {
case 1000: case 1000:
ret = db_generic_upgrade(hdl, db_upgrade_v11_queries, sizeof(db_upgrade_v11_queries) / sizeof(db_upgrade_v11_queries[0])); ret = db_generic_upgrade(hdl, db_upgrade_v11_queries, ARRAY_SIZE(db_upgrade_v11_queries));
if (ret < 0) if (ret < 0)
return -1; return -1;
@ -1678,14 +1695,14 @@ db_upgrade(sqlite3 *hdl, int db_ver)
if (ret < 0) if (ret < 0)
return -1; return -1;
ret = db_generic_upgrade(hdl, db_upgrade_v12_queries, sizeof(db_upgrade_v12_queries) / sizeof(db_upgrade_v12_queries[0])); ret = db_generic_upgrade(hdl, db_upgrade_v12_queries, ARRAY_SIZE(db_upgrade_v12_queries));
if (ret < 0) if (ret < 0)
return -1; return -1;
/* FALLTHROUGH */ /* FALLTHROUGH */
case 1200: case 1200:
ret = db_generic_upgrade(hdl, db_upgrade_v13_queries, sizeof(db_upgrade_v13_queries) / sizeof(db_upgrade_v13_queries[0])); ret = db_generic_upgrade(hdl, db_upgrade_v13_queries, ARRAY_SIZE(db_upgrade_v13_queries));
if (ret < 0) if (ret < 0)
return -1; return -1;
@ -1696,7 +1713,7 @@ db_upgrade(sqlite3 *hdl, int db_ver)
if (ret < 0) if (ret < 0)
return -1; return -1;
ret = db_generic_upgrade(hdl, db_upgrade_v14_queries, sizeof(db_upgrade_v14_queries) / sizeof(db_upgrade_v14_queries[0])); ret = db_generic_upgrade(hdl, db_upgrade_v14_queries, ARRAY_SIZE(db_upgrade_v14_queries));
if (ret < 0) if (ret < 0)
return -1; return -1;
@ -1707,21 +1724,21 @@ db_upgrade(sqlite3 *hdl, int db_ver)
if (ret < 0) if (ret < 0)
return -1; return -1;
ret = db_generic_upgrade(hdl, db_upgrade_v15_queries, sizeof(db_upgrade_v15_queries) / sizeof(db_upgrade_v15_queries[0])); ret = db_generic_upgrade(hdl, db_upgrade_v15_queries, ARRAY_SIZE(db_upgrade_v15_queries));
if (ret < 0) if (ret < 0)
return -1; return -1;
/* FALLTHROUGH */ /* FALLTHROUGH */
case 1500: case 1500:
ret = db_generic_upgrade(hdl, db_upgrade_v1501_queries, sizeof(db_upgrade_v1501_queries) / sizeof(db_upgrade_v1501_queries[0])); ret = db_generic_upgrade(hdl, db_upgrade_v1501_queries, ARRAY_SIZE(db_upgrade_v1501_queries));
if (ret < 0) if (ret < 0)
return -1; return -1;
/* FALLTHROUGH */ /* FALLTHROUGH */
case 1501: case 1501:
ret = db_generic_upgrade(hdl, db_upgrade_v16_queries, sizeof(db_upgrade_v16_queries) / sizeof(db_upgrade_v16_queries[0])); ret = db_generic_upgrade(hdl, db_upgrade_v16_queries, ARRAY_SIZE(db_upgrade_v16_queries));
if (ret < 0) if (ret < 0)
return -1; return -1;
@ -1732,28 +1749,28 @@ db_upgrade(sqlite3 *hdl, int db_ver)
/* FALLTHROUGH */ /* FALLTHROUGH */
case 1600: case 1600:
ret = db_generic_upgrade(hdl, db_upgrade_v17_queries, sizeof(db_upgrade_v17_queries) / sizeof(db_upgrade_v17_queries[0])); ret = db_generic_upgrade(hdl, db_upgrade_v17_queries, ARRAY_SIZE(db_upgrade_v17_queries));
if (ret < 0) if (ret < 0)
return -1; return -1;
/* FALLTHROUGH */ /* FALLTHROUGH */
case 1700: case 1700:
ret = db_generic_upgrade(hdl, db_upgrade_v18_queries, sizeof(db_upgrade_v18_queries) / sizeof(db_upgrade_v18_queries[0])); ret = db_generic_upgrade(hdl, db_upgrade_v18_queries, ARRAY_SIZE(db_upgrade_v18_queries));
if (ret < 0) if (ret < 0)
return -1; return -1;
/* FALLTHROUGH */ /* FALLTHROUGH */
case 1800: case 1800:
ret = db_generic_upgrade(hdl, db_upgrade_v1801_queries, sizeof(db_upgrade_v1801_queries) / sizeof(db_upgrade_v1801_queries[0])); ret = db_generic_upgrade(hdl, db_upgrade_v1801_queries, ARRAY_SIZE(db_upgrade_v1801_queries));
if (ret < 0) if (ret < 0)
return -1; return -1;
/* FALLTHROUGH */ /* FALLTHROUGH */
case 1801: case 1801:
ret = db_generic_upgrade(hdl, db_upgrade_v1900_queries, sizeof(db_upgrade_v1900_queries) / sizeof(db_upgrade_v1900_queries[0])); ret = db_generic_upgrade(hdl, db_upgrade_v1900_queries, ARRAY_SIZE(db_upgrade_v1900_queries));
if (ret < 0) if (ret < 0)
return -1; return -1;
@ -1764,49 +1781,49 @@ db_upgrade(sqlite3 *hdl, int db_ver)
/* FALLTHROUGH */ /* FALLTHROUGH */
case 1900: case 1900:
ret = db_generic_upgrade(hdl, db_upgrade_v1901_queries, sizeof(db_upgrade_v1901_queries) / sizeof(db_upgrade_v1901_queries[0])); ret = db_generic_upgrade(hdl, db_upgrade_v1901_queries, ARRAY_SIZE(db_upgrade_v1901_queries));
if (ret < 0) if (ret < 0)
return -1; return -1;
/* FALLTHROUGH */ /* FALLTHROUGH */
case 1901: case 1901:
ret = db_generic_upgrade(hdl, db_upgrade_v1902_queries, sizeof(db_upgrade_v1902_queries) / sizeof(db_upgrade_v1902_queries[0])); ret = db_generic_upgrade(hdl, db_upgrade_v1902_queries, ARRAY_SIZE(db_upgrade_v1902_queries));
if (ret < 0) if (ret < 0)
return -1; return -1;
/* FALLTHROUGH */ /* FALLTHROUGH */
case 1902: case 1902:
ret = db_generic_upgrade(hdl, db_upgrade_v1903_queries, sizeof(db_upgrade_v1903_queries) / sizeof(db_upgrade_v1903_queries[0])); ret = db_generic_upgrade(hdl, db_upgrade_v1903_queries, ARRAY_SIZE(db_upgrade_v1903_queries));
if (ret < 0) if (ret < 0)
return -1; return -1;
/* FALLTHROUGH */ /* FALLTHROUGH */
case 1903: case 1903:
ret = db_generic_upgrade(hdl, db_upgrade_v1904_queries, sizeof(db_upgrade_v1904_queries) / sizeof(db_upgrade_v1904_queries[0])); ret = db_generic_upgrade(hdl, db_upgrade_v1904_queries, ARRAY_SIZE(db_upgrade_v1904_queries));
if (ret < 0) if (ret < 0)
return -1; return -1;
/* FALLTHROUGH */ /* FALLTHROUGH */
case 1904: case 1904:
ret = db_generic_upgrade(hdl, db_upgrade_v1905_queries, sizeof(db_upgrade_v1905_queries) / sizeof(db_upgrade_v1905_queries[0])); ret = db_generic_upgrade(hdl, db_upgrade_v1905_queries, ARRAY_SIZE(db_upgrade_v1905_queries));
if (ret < 0) if (ret < 0)
return -1; return -1;
/* FALLTHROUGH */ /* FALLTHROUGH */
case 1905: case 1905:
ret = db_generic_upgrade(hdl, db_upgrade_V1906_queries, sizeof(db_upgrade_V1906_queries) / sizeof(db_upgrade_V1906_queries[0])); ret = db_generic_upgrade(hdl, db_upgrade_V1906_queries, ARRAY_SIZE(db_upgrade_V1906_queries));
if (ret < 0) if (ret < 0)
return -1; return -1;
/* FALLTHROUGH */ /* FALLTHROUGH */
case 1906: case 1906:
ret = db_generic_upgrade(hdl, db_upgrade_V1907_queries, sizeof(db_upgrade_V1907_queries) / sizeof(db_upgrade_V1907_queries[0])); ret = db_generic_upgrade(hdl, db_upgrade_V1907_queries, ARRAY_SIZE(db_upgrade_V1907_queries));
if (ret < 0) if (ret < 0)
return -1; return -1;
@ -1817,6 +1834,13 @@ db_upgrade(sqlite3 *hdl, int db_ver)
if (ret < 0) if (ret < 0)
return -1; return -1;
/* FALLTHROUGH */
case 1908:
ret = db_generic_upgrade(hdl, db_upgrade_v1909_queries, ARRAY_SIZE(db_upgrade_v1909_queries));
if (ret < 0)
return -1;
break; break;
default: default:

View File

@ -85,7 +85,10 @@ static const struct dmap_field_map dfm_dmap_aseq = { -1,
static const struct dmap_field_map dfm_dmap_asfm = { dbmfi_offsetof(type), -1, -1 }; static const struct dmap_field_map dfm_dmap_asfm = { dbmfi_offsetof(type), -1, -1 };
static const struct dmap_field_map dfm_dmap_asgn = { dbmfi_offsetof(genre), -1, -1 }; static const struct dmap_field_map dfm_dmap_asgn = { dbmfi_offsetof(genre), -1, -1 };
static const struct dmap_field_map dfm_dmap_ashp = { dbmfi_offsetof(play_count), -1, -1 }; static const struct dmap_field_map dfm_dmap_ashp = { dbmfi_offsetof(play_count), -1, -1 };
static const struct dmap_field_map dfm_dmap_askd = { dbmfi_offsetof(time_skipped), -1, -1 };
static const struct dmap_field_map dfm_dmap_askp = { dbmfi_offsetof(skip_count), -1, -1 };
static const struct dmap_field_map dfm_dmap_aspc = { dbmfi_offsetof(play_count), -1, -1 }; static const struct dmap_field_map dfm_dmap_aspc = { dbmfi_offsetof(play_count), -1, -1 };
static const struct dmap_field_map dfm_dmap_aspl = { dbmfi_offsetof(time_played), -1, -1 };
static const struct dmap_field_map dfm_dmap_assp = { -1, -1, -1 }; static const struct dmap_field_map dfm_dmap_assp = { -1, -1, -1 };
static const struct dmap_field_map dfm_dmap_assr = { dbmfi_offsetof(samplerate), -1, -1 }; static const struct dmap_field_map dfm_dmap_assr = { dbmfi_offsetof(samplerate), -1, -1 };
static const struct dmap_field_map dfm_dmap_asst = { -1, -1, -1 }; static const struct dmap_field_map dfm_dmap_asst = { -1, -1, -1 };
@ -207,9 +210,12 @@ struct dmap_field;
"daap.songformat", "asfm", &dfm_dmap_asfm, DMAP_TYPE_STRING "daap.songformat", "asfm", &dfm_dmap_asfm, DMAP_TYPE_STRING
"daap.songgenre", "asgn", &dfm_dmap_asgn, DMAP_TYPE_STRING "daap.songgenre", "asgn", &dfm_dmap_asgn, DMAP_TYPE_STRING
"daap.songhasbeenplayed", "ashp", &dfm_dmap_ashp, DMAP_TYPE_UBYTE "daap.songhasbeenplayed", "ashp", &dfm_dmap_ashp, DMAP_TYPE_UBYTE
"daap.songlastskipdate", "askd", &dfm_dmap_askd, DMAP_TYPE_DATE
"daap.songuserskipcount", "askp", &dfm_dmap_askp, DMAP_TYPE_UINT
"daap.songkeywords", "asky", &dfm_dmap_asky, DMAP_TYPE_STRING "daap.songkeywords", "asky", &dfm_dmap_asky, DMAP_TYPE_STRING
"daap.songlongcontentdescription", "aslc", &dfm_dmap_aslc, DMAP_TYPE_STRING "daap.songlongcontentdescription", "aslc", &dfm_dmap_aslc, DMAP_TYPE_STRING
"daap.songuserplaycount", "aspc", &dfm_dmap_aspc, DMAP_TYPE_UINT "daap.songuserplaycount", "aspc", &dfm_dmap_aspc, DMAP_TYPE_UINT
"daap.songdateplayed", "aspl", &dfm_dmap_aspl, DMAP_TYPE_DATE
"daap.songrelativevolume", "asrv", &dfm_dmap_asrv, DMAP_TYPE_BYTE "daap.songrelativevolume", "asrv", &dfm_dmap_asrv, DMAP_TYPE_BYTE
"daap.sortartist", "assa", &dfm_dmap_assa, DMAP_TYPE_STRING "daap.sortartist", "assa", &dfm_dmap_assa, DMAP_TYPE_STRING
"daap.sortcomposer", "assc", &dfm_dmap_assc, DMAP_TYPE_STRING "daap.sortcomposer", "assc", &dfm_dmap_assc, DMAP_TYPE_STRING

View File

@ -186,7 +186,9 @@ track_to_json(struct db_media_file_info *dbmfi)
safe_json_add_int_from_string(item, "length_ms", dbmfi->song_length); safe_json_add_int_from_string(item, "length_ms", dbmfi->song_length);
safe_json_add_int_from_string(item, "play_count", dbmfi->play_count); safe_json_add_int_from_string(item, "play_count", dbmfi->play_count);
safe_json_add_int_from_string(item, "skip_count", dbmfi->skip_count);
safe_json_add_time_from_string(item, "time_played", dbmfi->time_played, true); safe_json_add_time_from_string(item, "time_played", dbmfi->time_played, true);
safe_json_add_time_from_string(item, "time_skipped", dbmfi->time_skipped, true);
safe_json_add_time_from_string(item, "time_added", dbmfi->time_added, true); safe_json_add_time_from_string(item, "time_added", dbmfi->time_added, true);
safe_json_add_time_from_string(item, "date_released", dbmfi->date_released, false); safe_json_add_time_from_string(item, "date_released", dbmfi->date_released, false);
safe_json_add_int_from_string(item, "seek_ms", dbmfi->seek); safe_json_add_int_from_string(item, "seek_ms", dbmfi->seek);

View File

@ -98,12 +98,14 @@ static const struct field_map rsp_fields[] =
{ "compilation", dbmfi_offsetof(compilation), F_DETAILED | F_FULL }, { "compilation", dbmfi_offsetof(compilation), F_DETAILED | F_FULL },
{ "rating", dbmfi_offsetof(rating), F_DETAILED | F_FULL }, { "rating", dbmfi_offsetof(rating), F_DETAILED | F_FULL },
{ "play_count", dbmfi_offsetof(play_count), F_DETAILED | F_FULL }, { "play_count", dbmfi_offsetof(play_count), F_DETAILED | F_FULL },
{ "skip_count", dbmfi_offsetof(skip_count), F_DETAILED | F_FULL },
{ "data_kind", dbmfi_offsetof(data_kind), F_DETAILED }, { "data_kind", dbmfi_offsetof(data_kind), F_DETAILED },
{ "item_kind", dbmfi_offsetof(item_kind), F_DETAILED }, { "item_kind", dbmfi_offsetof(item_kind), F_DETAILED },
{ "description", dbmfi_offsetof(description), F_DETAILED | F_FULL }, { "description", dbmfi_offsetof(description), F_DETAILED | F_FULL },
{ "time_added", dbmfi_offsetof(time_added), F_DETAILED | F_FULL }, { "time_added", dbmfi_offsetof(time_added), F_DETAILED | F_FULL },
{ "time_modified", dbmfi_offsetof(time_modified), F_DETAILED | F_FULL }, { "time_modified", dbmfi_offsetof(time_modified), F_DETAILED | F_FULL },
{ "time_played", dbmfi_offsetof(time_played), F_DETAILED | F_FULL }, { "time_played", dbmfi_offsetof(time_played), F_DETAILED | F_FULL },
{ "time_skipped", dbmfi_offsetof(time_skipped), F_DETAILED | F_FULL },
{ "db_timestamp", dbmfi_offsetof(db_timestamp), F_DETAILED }, { "db_timestamp", dbmfi_offsetof(db_timestamp), F_DETAILED },
{ "disabled", dbmfi_offsetof(disabled), F_ALWAYS }, { "disabled", dbmfi_offsetof(disabled), F_ALWAYS },
{ "sample_count", dbmfi_offsetof(sample_count), F_DETAILED }, { "sample_count", dbmfi_offsetof(sample_count), F_DETAILED },

View File

@ -128,6 +128,8 @@ static struct metadata_map md_map[] =
{ "Date Added", PLIST_DATE, mfi_offsetof(time_added) }, { "Date Added", PLIST_DATE, mfi_offsetof(time_added) },
{ "Play Date", PLIST_UINT, mfi_offsetof(time_played) }, { "Play Date", PLIST_UINT, mfi_offsetof(time_played) },
{ "Play Count", PLIST_UINT, mfi_offsetof(play_count) }, { "Play Count", PLIST_UINT, mfi_offsetof(play_count) },
{ "Skip Count", PLIST_UINT, mfi_offsetof(skip_count) },
{ "Skip Date", PLIST_DATE, mfi_offsetof(time_skipped) },
{ NULL, 0, 0 } { NULL, 0, 0 }
}; };

View File

@ -352,6 +352,14 @@ playcount_inc_cb(void *arg)
db_file_inc_playcount(*id); db_file_inc_playcount(*id);
} }
static void
skipcount_inc_cb(void *arg)
{
int *id = arg;
db_file_inc_skipcount(*id);
}
#ifdef LASTFM #ifdef LASTFM
// Callback from the worker thread (async operation as it may block) // Callback from the worker thread (async operation as it may block)
static void static void
@ -2219,6 +2227,7 @@ playback_next_bh(void *arg, int *retval)
{ {
struct player_source *ps; struct player_source *ps;
int ret; int ret;
int id;
uint32_t item_id; uint32_t item_id;
// The upper half is playback_pause, therefor the current playing item is // The upper half is playback_pause, therefor the current playing item is
@ -2230,11 +2239,16 @@ playback_next_bh(void *arg, int *retval)
return COMMAND_END; return COMMAND_END;
} }
item_id = cur_streaming->item_id;
// Only add to history if playback started // Only add to history if playback started
if (cur_streaming->output_start > cur_streaming->stream_start) if (cur_streaming->output_start > cur_streaming->stream_start)
history_add(cur_streaming->id, cur_streaming->item_id); {
history_add(cur_streaming->id, item_id);
item_id = cur_streaming->item_id; id = (int)cur_streaming->id;
worker_execute(skipcount_inc_cb, &id, sizeof(int), 5);
}
ps = source_next(); ps = source_next();
if (!ps) if (!ps)

View File

@ -36,12 +36,14 @@ struct rsp_query_field_map;
"compilation", RSP_TYPE_INT "compilation", RSP_TYPE_INT
"rating", RSP_TYPE_INT "rating", RSP_TYPE_INT
"play_count", RSP_TYPE_INT "play_count", RSP_TYPE_INT
"skip_count", RSP_TYPE_INT
"data_kind", RSP_TYPE_INT "data_kind", RSP_TYPE_INT
"item_kind", RSP_TYPE_INT "item_kind", RSP_TYPE_INT
"description", RSP_TYPE_STRING "description", RSP_TYPE_STRING
"time_added", RSP_TYPE_DATE "time_added", RSP_TYPE_DATE
"time_modified", RSP_TYPE_DATE "time_modified", RSP_TYPE_DATE
"time_played", RSP_TYPE_DATE "time_played", RSP_TYPE_DATE
"time_skipped", RSP_TYPE_DATE
"db_timestamp", RSP_TYPE_DATE "db_timestamp", RSP_TYPE_DATE
"sample_count", RSP_TYPE_INT "sample_count", RSP_TYPE_INT
"codectype", RSP_TYPE_STRING "codectype", RSP_TYPE_STRING