From fde0a281429bd992c7f2733d8ed1655e241c10f5 Mon Sep 17 00:00:00 2001 From: ejurgensen Date: Sat, 18 Aug 2018 22:56:27 +0200 Subject: [PATCH] Support for skip_count and time_skipped file metadata --- README_JSON_API.md | 3 +- README_SMARTPL.md | 2 + src/SMARTPL.g | 2 + src/daap_query.gperf | 2 + src/db.c | 123 ++++++++++++++++++++----------- src/db.h | 8 ++ src/db_init.c | 4 +- src/db_init.h | 2 +- src/db_upgrade.c | 60 ++++++++++----- src/dmap_fields.gperf | 6 ++ src/httpd_jsonapi.c | 2 + src/httpd_rsp.c | 2 + src/library/filescanner_itunes.c | 2 + src/player.c | 18 ++++- src/rsp_query.gperf | 2 + 15 files changed, 170 insertions(+), 68 deletions(-) diff --git a/README_JSON_API.md b/README_JSON_API.md index 81b5fc1d..346126aa 100644 --- a/README_JSON_API.md +++ b/README_JSON_API.md @@ -820,7 +820,8 @@ curl -X GET "http://localhost:3689/api/library/playlists/1/tracks" "disc_number": 1, "length_ms": 223085, "play_count": 2, - "last_time_played": "2018-02-23T10:31:20Z", + "skip_count": 1, + "time_played": "2018-02-23T10:31:20Z", "media_kind": "music", "data_kind": "file", "path": "/music/srv/Heinrich/Solange wir tanzen/01 Solange wir tanzen.mp3", diff --git a/README_SMARTPL.md b/README_SMARTPL.md index ee0a84d2..a95f969c 100644 --- a/README_SMARTPL.md +++ b/README_SMARTPL.md @@ -36,12 +36,14 @@ Where valid field-names (with their types) are: * `data_kind` (enumeration) * `media_kind` (enumeration) * `play_count` (integer) +* `skip_count` (integer) * `rating` (integer) * `year` (integer) * `compilation` (integer) * `time_added` (date) * `time_modified` (date) * `time_played` (date) +* `time_skipped` (date) Valid operators include: * `is`, `includes`, `starts with` (string) diff --git a/src/SMARTPL.g b/src/SMARTPL.g index 2b16ec29..f87bcafb 100644 --- a/src/SMARTPL.g +++ b/src/SMARTPL.g @@ -85,6 +85,7 @@ STRTAG : 'artist' ; INTTAG : 'play_count' + | 'skip_count' | 'rating' | 'year' | 'compilation' @@ -93,6 +94,7 @@ INTTAG : 'play_count' DATETAG : 'time_added' | 'time_modified' | 'time_played' + | 'time_skipped' ; ENUMTAG : 'data_kind' diff --git a/src/daap_query.gperf b/src/daap_query.gperf index e97febcd..2bd87697 100644 --- a/src/daap_query.gperf +++ b/src/daap_query.gperf @@ -27,6 +27,7 @@ struct dmap_query_field_map; "daap.songdataurl", "f.url", 0 "daap.songdateadded", "f.time_added", 1 "daap.songdatemodified", "f.time_modified", 1 +"daap.songlastskipdate", "f.time_skipped", 1 "daap.songdatereleased", "f.date_released", 1 "daap.songdescription", "f.description", 0 "daap.songdisccount", "f.total_discs", 1 @@ -40,6 +41,7 @@ struct dmap_query_field_map; "daap.songtrackcount", "f.total_tracks", 1 "daap.songtracknumber", "f.track", 1 "daap.songuserplaycount", "f.play_count", 1 +"daap.songuserskipcount", "f.skip_count", 1 "daap.songyear", "f.year", 1 "com.apple.itunes.mediakind", "f.media_kind", 1 "com.apple.itunes.extended-media-kind", "f.media_kind", 1 diff --git a/src/db.c b/src/db.c index cdc2e2dc..267d8b73 100644 --- a/src/db.c +++ b/src/db.c @@ -158,6 +158,8 @@ static const struct col_type_map mfi_cols_map[] = { mfi_offsetof(virtual_path), DB_TYPE_STRING }, { mfi_offsetof(directory_id), 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 @@ -250,6 +252,8 @@ static const ssize_t dbmfi_cols_map[] = dbmfi_offsetof(virtual_path), dbmfi_offsetof(directory_id), dbmfi_offsetof(date_released), + dbmfi_offsetof(skip_count), + dbmfi_offsetof(time_skipped), }; /* This list must be kept in sync with @@ -2344,6 +2348,24 @@ db_file_inc_playcount(int id) #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 db_file_ping(int id) { @@ -2751,23 +2773,35 @@ db_file_fetch_byvirtualpath(const char *virtual_path) int db_file_add(struct media_file_info *mfi) { -#define Q_TMPL "INSERT INTO files (id, path, fname, title, artist, album, genre, comment, type, composer," \ - " orchestra, conductor, grouping, url, bitrate, samplerate, song_length, file_size, year, track," \ - " total_tracks, disc, total_discs, bpm, compilation, artwork, rating, play_count, seek, data_kind, item_kind," \ - " description, time_added, time_modified, time_played, db_timestamp, disabled, sample_count," \ - " codectype, idx, has_video, contentrating, bits_per_sample, album_artist," \ - " media_kind, tv_series_name, tv_episode_num_str, tv_network_name, tv_episode_sort, tv_season_num, " \ - " songartistid, songalbumid, " \ - " title_sort, artist_sort, album_sort, composer_sort, album_artist_sort, virtual_path," \ - " directory_id, date_released) " \ - " VALUES (NULL, '%q', '%q', TRIM(%Q), TRIM(%Q), TRIM(%Q), TRIM(%Q), TRIM(%Q), %Q, 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);" +#define Q_TMPL "INSERT INTO files (" \ + " id, path, fname, title, artist, album, genre, comment, type," \ + " composer, orchestra, conductor, grouping, url, bitrate," \ + " samplerate, song_length, file_size, year, track," \ + " total_tracks, disc, total_discs, bpm, compilation, artwork," \ + " rating, play_count, seek, data_kind, item_kind, description," \ + " time_added, time_modified, time_played, db_timestamp," \ + " disabled, sample_count, codectype, idx, has_video," \ + " contentrating, bits_per_sample, album_artist, media_kind," \ + " tv_series_name, tv_episode_num_str, tv_network_name," \ + " tv_episode_sort, tv_season_num, songartistid, songalbumid," \ + " title_sort, artist_sort, album_sort, composer_sort," \ + " album_artist_sort, virtual_path, directory_id, date_released,"\ + " skip_count, time_skipped" \ + ") VALUES (" \ + " NULL, '%q', '%q', TRIM(%Q), TRIM(%Q), TRIM(%Q), TRIM(%Q), TRIM(%Q), %Q," \ + " 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 *errmsg; @@ -2789,20 +2823,19 @@ db_file_add(struct media_file_info *mfi) mfi->time_modified = mfi->db_timestamp; query = sqlite3_mprintf(Q_TMPL, - STR(mfi->path), STR(mfi->fname), mfi->title, mfi->artist, mfi->album, - mfi->genre, mfi->comment, mfi->type, mfi->composer, - mfi->orchestra, mfi->conductor, mfi->grouping, mfi->url, mfi->bitrate, - 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->rating, mfi->play_count, mfi->seek, mfi->data_kind, mfi->item_kind, - mfi->description, (int64_t)mfi->time_added, (int64_t)mfi->time_modified, - (int64_t)mfi->time_played, (int64_t)mfi->db_timestamp, mfi->disabled, mfi->sample_count, - mfi->codectype, mfi->index, mfi->has_video, - mfi->contentrating, mfi->bits_per_sample, mfi->album_artist, - mfi->media_kind, mfi->tv_series_name, mfi->tv_episode_num_str, - mfi->tv_network_name, mfi->tv_episode_sort, mfi->tv_season_num, - mfi->album_artist, mfi->album_artist, mfi->album, mfi->title_sort, mfi->artist_sort, mfi->album_sort, - mfi->composer_sort, mfi->album_artist_sort, mfi->virtual_path, mfi->directory_id, mfi->date_released); + STR(mfi->path), STR(mfi->fname), mfi->title, mfi->artist, mfi->album, mfi->genre, mfi->comment, mfi->type, + mfi->composer, mfi->orchestra, mfi->conductor, mfi->grouping, mfi->url, mfi->bitrate, + 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->rating, mfi->play_count, mfi->seek, mfi->data_kind, mfi->item_kind, mfi->description, + (int64_t)mfi->time_added, (int64_t)mfi->time_modified, (int64_t)mfi->time_played, (int64_t)mfi->db_timestamp, + mfi->disabled, mfi->sample_count, mfi->codectype, mfi->index, mfi->has_video, + mfi->contentrating, mfi->bits_per_sample, mfi->album_artist, mfi->media_kind, + mfi->tv_series_name, mfi->tv_episode_num_str, mfi->tv_network_name, + mfi->tv_episode_sort, mfi->tv_season_num, mfi->album_artist, mfi->album_artist, mfi->album, + mfi->title_sort, mfi->artist_sort, mfi->album_sort, mfi->composer_sort, + mfi->album_artist_sort, mfi->virtual_path, mfi->directory_id, mfi->date_released, + mfi->skip_count, mfi->time_skipped); if (!query) { @@ -2835,19 +2868,19 @@ int 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)," \ - " 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 "," \ - " 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," \ - " description = %Q, time_modified = %" PRIi64 "," \ - " db_timestamp = %" PRIi64 ", disabled = %" PRIi64 ", sample_count = %" PRIi64 "," \ - " codectype = %Q, idx = %d, has_video = %d," \ - " bits_per_sample = %d, album_artist = 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," \ - " 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)," \ - " virtual_path = TRIM(%Q), directory_id = %d, date_released = %d" \ + " 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 "," \ + " 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," \ + " description = %Q, time_modified = %" PRIi64 "," \ + " db_timestamp = %" PRIi64 ", disabled = %" PRIi64 ", sample_count = %" PRIi64 "," \ + " codectype = %Q, idx = %d, has_video = %d," \ + " bits_per_sample = %d, album_artist = 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," \ + " 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)," \ + " virtual_path = TRIM(%Q), directory_id = %d, date_released = %d, skip_count = %d, time_skipped = %" PRIi64 "" \ " WHERE id = %d;" char *query; @@ -2880,7 +2913,7 @@ db_file_update(struct media_file_info *mfi) mfi->album_artist, mfi->album_artist, mfi->album, mfi->title_sort, mfi->artist_sort, mfi->album_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); if (!query) diff --git a/src/db.h b/src/db.h index 7ca79a59..b4248f09 100644 --- a/src/db.h +++ b/src/db.h @@ -219,6 +219,9 @@ struct media_file_info { uint32_t directory_id; /* Id of directory */ uint32_t date_released; + + uint32_t skip_count; + uint32_t time_skipped; }; #define mfi_offsetof(field) offsetof(struct media_file_info, field) @@ -363,6 +366,8 @@ struct db_media_file_info { char *virtual_path; char *directory_id; char *date_released; + char *skip_count; + char *time_skipped; }; #define dbmfi_offsetof(field) offsetof(struct db_media_file_info, field) @@ -557,6 +562,9 @@ db_files_get_count(void); void db_file_inc_playcount(int id); +void +db_file_inc_skipcount(int id); + void db_file_ping(int id); diff --git a/src/db_init.c b/src/db_init.c index c8101259..4f1aa248 100644 --- a/src/db_init.c +++ b/src/db_init.c @@ -93,7 +93,9 @@ " album_artist_sort VARCHAR(1024) DEFAULT NULL COLLATE DAAP," \ " virtual_path VARCHAR(4096) DEFAULT NULL," \ " 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 \ diff --git a/src/db_init.h b/src/db_init.h index ce430015..48fdd06c 100644 --- a/src/db_init.h +++ b/src/db_init.h @@ -26,7 +26,7 @@ * is a major upgrade. In other words minor version upgrades permit downgrading * forked-daapd after the database was upgraded. */ #define SCHEMA_VERSION_MAJOR 19 -#define SCHEMA_VERSION_MINOR 8 +#define SCHEMA_VERSION_MINOR 9 int db_init_indices(sqlite3 *hdl); diff --git a/src/db_upgrade.c b/src/db_upgrade.c index 14dbdec3..17c48faf 100644 --- a/src/db_upgrade.c +++ b/src/db_upgrade.c @@ -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 db_upgrade(sqlite3 *hdl, int db_ver) { @@ -1663,7 +1680,7 @@ db_upgrade(sqlite3 *hdl, int db_ver) switch (db_ver) { 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) return -1; @@ -1678,14 +1695,14 @@ db_upgrade(sqlite3 *hdl, int db_ver) if (ret < 0) 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) return -1; /* FALLTHROUGH */ 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) return -1; @@ -1696,7 +1713,7 @@ db_upgrade(sqlite3 *hdl, int db_ver) if (ret < 0) 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) return -1; @@ -1707,21 +1724,21 @@ db_upgrade(sqlite3 *hdl, int db_ver) if (ret < 0) 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) return -1; /* FALLTHROUGH */ 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) return -1; /* FALLTHROUGH */ 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) return -1; @@ -1732,28 +1749,28 @@ db_upgrade(sqlite3 *hdl, int db_ver) /* FALLTHROUGH */ 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) return -1; /* FALLTHROUGH */ 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) return -1; /* FALLTHROUGH */ 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) return -1; /* FALLTHROUGH */ 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) return -1; @@ -1764,49 +1781,49 @@ db_upgrade(sqlite3 *hdl, int db_ver) /* FALLTHROUGH */ 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) return -1; /* FALLTHROUGH */ 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) return -1; /* FALLTHROUGH */ 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) return -1; /* FALLTHROUGH */ 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) return -1; /* FALLTHROUGH */ 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) return -1; /* FALLTHROUGH */ 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) return -1; /* FALLTHROUGH */ 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) return -1; @@ -1817,6 +1834,13 @@ db_upgrade(sqlite3 *hdl, int db_ver) if (ret < 0) 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; default: diff --git a/src/dmap_fields.gperf b/src/dmap_fields.gperf index 0119f021..42768610 100644 --- a/src/dmap_fields.gperf +++ b/src/dmap_fields.gperf @@ -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_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_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_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_assr = { dbmfi_offsetof(samplerate), -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.songgenre", "asgn", &dfm_dmap_asgn, DMAP_TYPE_STRING "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.songlongcontentdescription", "aslc", &dfm_dmap_aslc, DMAP_TYPE_STRING "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.sortartist", "assa", &dfm_dmap_assa, DMAP_TYPE_STRING "daap.sortcomposer", "assc", &dfm_dmap_assc, DMAP_TYPE_STRING diff --git a/src/httpd_jsonapi.c b/src/httpd_jsonapi.c index 3b0358b1..7d4e1784 100644 --- a/src/httpd_jsonapi.c +++ b/src/httpd_jsonapi.c @@ -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, "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_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, "date_released", dbmfi->date_released, false); safe_json_add_int_from_string(item, "seek_ms", dbmfi->seek); diff --git a/src/httpd_rsp.c b/src/httpd_rsp.c index b4cf0cbe..d0a428b1 100644 --- a/src/httpd_rsp.c +++ b/src/httpd_rsp.c @@ -98,12 +98,14 @@ static const struct field_map rsp_fields[] = { "compilation", dbmfi_offsetof(compilation), F_DETAILED | F_FULL }, { "rating", dbmfi_offsetof(rating), 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 }, { "item_kind", dbmfi_offsetof(item_kind), F_DETAILED }, { "description", dbmfi_offsetof(description), 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_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 }, { "disabled", dbmfi_offsetof(disabled), F_ALWAYS }, { "sample_count", dbmfi_offsetof(sample_count), F_DETAILED }, diff --git a/src/library/filescanner_itunes.c b/src/library/filescanner_itunes.c index e270d785..c36f4bfb 100644 --- a/src/library/filescanner_itunes.c +++ b/src/library/filescanner_itunes.c @@ -128,6 +128,8 @@ static struct metadata_map md_map[] = { "Date Added", PLIST_DATE, mfi_offsetof(time_added) }, { "Play Date", PLIST_UINT, mfi_offsetof(time_played) }, { "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 } }; diff --git a/src/player.c b/src/player.c index c5626efb..b39ce4bc 100644 --- a/src/player.c +++ b/src/player.c @@ -352,6 +352,14 @@ playcount_inc_cb(void *arg) db_file_inc_playcount(*id); } +static void +skipcount_inc_cb(void *arg) +{ + int *id = arg; + + db_file_inc_skipcount(*id); +} + #ifdef LASTFM // Callback from the worker thread (async operation as it may block) static void @@ -2219,6 +2227,7 @@ playback_next_bh(void *arg, int *retval) { struct player_source *ps; int ret; + int id; uint32_t item_id; // 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; } + item_id = cur_streaming->item_id; + // Only add to history if playback started 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(); if (!ps) diff --git a/src/rsp_query.gperf b/src/rsp_query.gperf index 7c3e5d3e..8068adbf 100644 --- a/src/rsp_query.gperf +++ b/src/rsp_query.gperf @@ -36,12 +36,14 @@ struct rsp_query_field_map; "compilation", RSP_TYPE_INT "rating", RSP_TYPE_INT "play_count", RSP_TYPE_INT +"skip_count", RSP_TYPE_INT "data_kind", RSP_TYPE_INT "item_kind", RSP_TYPE_INT "description", RSP_TYPE_STRING "time_added", RSP_TYPE_DATE "time_modified", RSP_TYPE_DATE "time_played", RSP_TYPE_DATE +"time_skipped", RSP_TYPE_DATE "db_timestamp", RSP_TYPE_DATE "sample_count", RSP_TYPE_INT "codectype", RSP_TYPE_STRING