From 7bf43a9712de54b76acec0a0bb441947fbf36ad1 Mon Sep 17 00:00:00 2001 From: jcorporation Date: Thu, 5 Oct 2023 19:27:22 +0200 Subject: [PATCH] Add "added" timestamp to song database - added is set to current time, if a new song is added to the database. - GetAdded falls back to mtime. Code for proxy plugin is missing, this needs a patch for libmpdclient. closes #378 --- src/SongPrint.cxx | 6 ++++++ src/SongSave.cxx | 11 +++++++++++ src/command/DatabaseCommands.cxx | 3 +++ src/command/QueueCommands.cxx | 3 +++ src/db/VHelper.cxx | 7 +++++++ src/db/plugins/simple/Song.cxx | 4 ++++ src/db/plugins/simple/Song.hxx | 7 +++++++ src/db/update/UpdateSong.cxx | 1 + src/playlist/PlaylistSong.cxx | 1 + src/queue/PlaylistUpdate.cxx | 1 + src/queue/Print.cxx | 11 +++++++++++ src/song/DetachedSong.cxx | 2 ++ src/song/DetachedSong.hxx | 18 ++++++++++++++++++ src/song/Filter.hxx | 5 +++++ src/song/LightSong.hxx | 6 ++++++ 15 files changed, 86 insertions(+) diff --git a/src/SongPrint.cxx b/src/SongPrint.cxx index 835669d27..207aa7b13 100644 --- a/src/SongPrint.cxx +++ b/src/SongPrint.cxx @@ -77,6 +77,9 @@ song_print_info(Response &r, const LightSong &song, bool base) noexcept if (!IsNegative(song.mtime)) time_print(r, "Last-Modified", song.mtime); + if (!IsNegative(song.added)) + time_print(r, "Added", song.added); + if (song.audio_format.IsDefined()) r.Fmt(FMT_STRING("Format: {}\n"), song.audio_format); @@ -100,6 +103,9 @@ song_print_info(Response &r, const DetachedSong &song, bool base) noexcept if (!IsNegative(song.GetLastModified())) time_print(r, "Last-Modified", song.GetLastModified()); + if (!IsNegative(song.GetAdded())) + time_print(r, "Added", song.GetAdded()); + if (const auto &f = song.GetAudioFormat(); f.IsDefined()) r.Fmt(FMT_STRING("Format: {}\n"), f); diff --git a/src/SongSave.cxx b/src/SongSave.cxx index 79bbd4f49..68686aee6 100644 --- a/src/SongSave.cxx +++ b/src/SongSave.cxx @@ -22,6 +22,7 @@ #include #define SONG_MTIME "mtime" +#define SONG_ADDED "added" #define SONG_END "song_end" static void @@ -54,6 +55,10 @@ song_save(BufferedOutputStream &os, const Song &song) if (!IsNegative(song.mtime)) os.Fmt(FMT_STRING(SONG_MTIME ": {}\n"), std::chrono::system_clock::to_time_t(song.mtime)); + + if (!IsNegative(song.added)) + os.Fmt(FMT_STRING(SONG_ADDED ": {}\n"), + std::chrono::system_clock::to_time_t(song.added)); os.Write(SONG_END "\n"); } @@ -69,6 +74,10 @@ song_save(BufferedOutputStream &os, const DetachedSong &song) if (!IsNegative(song.GetLastModified())) os.Fmt(FMT_STRING(SONG_MTIME ": {}\n"), std::chrono::system_clock::to_time_t(song.GetLastModified())); + + if (!IsNegative(song.GetAdded())) + os.Fmt(FMT_STRING(SONG_ADDED ": {}\n"), + std::chrono::system_clock::to_time_t(song.GetAdded())); os.Write(SONG_END "\n"); } @@ -109,6 +118,8 @@ song_load(LineReader &file, const char *uri, tag.SetHasPlaylist(StringIsEqual(value, "yes")); } else if (StringIsEqual(line, SONG_MTIME)) { song.SetLastModified(std::chrono::system_clock::from_time_t(atoi(value))); + } else if (StringIsEqual(line, SONG_ADDED)) { + song.SetAdded(std::chrono::system_clock::from_time_t(atoi(value))); } else if (StringIsEqual(line, "Range")) { char *endptr; diff --git a/src/command/DatabaseCommands.cxx b/src/command/DatabaseCommands.cxx index 51cece639..8b317cd1d 100644 --- a/src/command/DatabaseCommands.cxx +++ b/src/command/DatabaseCommands.cxx @@ -51,6 +51,9 @@ ParseSortTag(const char *s) if (StringIsEqualIgnoreCase(s, "Last-Modified")) return TagType(SORT_TAG_LAST_MODIFIED); + if (StringIsEqualIgnoreCase(s, "Added")) + return TagType(SORT_TAG_ADDED); + TagType tag = tag_name_parse_i(s); if (tag == TAG_NUM_OF_ITEM_TYPES) throw ProtocolError(ACK_ERROR_ARG, "Unknown sort tag"); diff --git a/src/command/QueueCommands.cxx b/src/command/QueueCommands.cxx index 7282ab12a..00516909b 100644 --- a/src/command/QueueCommands.cxx +++ b/src/command/QueueCommands.cxx @@ -276,6 +276,9 @@ ParseSortTag(const char *s) if (StringIsEqualIgnoreCase(s, "Last-Modified")) return TagType(SORT_TAG_LAST_MODIFIED); + if (StringIsEqualIgnoreCase(s, "Added")) + return TagType(SORT_TAG_ADDED); + if (StringIsEqualIgnoreCase(s, "prio")) return TagType(SORT_TAG_PRIO); diff --git a/src/db/VHelper.cxx b/src/db/VHelper.cxx index 93674a86c..92c826abb 100644 --- a/src/db/VHelper.cxx +++ b/src/db/VHelper.cxx @@ -60,6 +60,13 @@ DatabaseVisitorHelper::Commit() ? a.GetLastModified() > b.GetLastModified() : a.GetLastModified() < b.GetLastModified(); }); + else if (sort == TagType(SORT_TAG_ADDED)) + std::stable_sort(songs.begin(), songs.end(), + [descending](const DetachedSong &a, const DetachedSong &b){ + return descending + ? a.GetAdded() > b.GetAdded() + : a.GetAdded() < b.GetAdded(); + }); else std::stable_sort(songs.begin(), songs.end(), [sort, descending](const DetachedSong &a, diff --git a/src/db/plugins/simple/Song.cxx b/src/db/plugins/simple/Song.cxx index f7b742407..e6e8d961f 100644 --- a/src/db/plugins/simple/Song.cxx +++ b/src/db/plugins/simple/Song.cxx @@ -19,6 +19,7 @@ Song::Song(DetachedSong &&other, Directory &_parent) noexcept filename(other.GetURI()), tag(std::move(other.WritableTag())), mtime(other.GetLastModified()), + added(other.GetAdded()), start_time(other.GetStartTime()), end_time(other.GetEndTime()), audio_format(other.GetAudioFormat()) @@ -117,6 +118,9 @@ Song::Export() const noexcept dest.mtime = IsNegative(mtime) && target_song != nullptr ? target_song->mtime : mtime; + dest.added = IsNegative(added) && target_song != nullptr + ? target_song->added + : added; dest.start_time = start_time.IsZero() && target_song != nullptr ? target_song->start_time : start_time; diff --git a/src/db/plugins/simple/Song.hxx b/src/db/plugins/simple/Song.hxx index 0342f28c2..0ce3a6431 100644 --- a/src/db/plugins/simple/Song.hxx +++ b/src/db/plugins/simple/Song.hxx @@ -56,6 +56,13 @@ struct Song : IntrusiveListHook<> { std::chrono::system_clock::time_point mtime = std::chrono::system_clock::time_point::min(); + /** + * The time stamp when the file was added. A negative + * value means that this is unknown/unavailable. + */ + std::chrono::system_clock::time_point added = + std::chrono::system_clock::time_point::min(); + /** * Start of this sub-song within the file. */ diff --git a/src/db/update/UpdateSong.cxx b/src/db/update/UpdateSong.cxx index 1d695822e..7cd919ba3 100644 --- a/src/db/update/UpdateSong.cxx +++ b/src/db/update/UpdateSong.cxx @@ -50,6 +50,7 @@ try { } new_song->mark = true; + new_song->added = std::chrono::system_clock::now(); { const ScopeDatabaseLock protect; diff --git a/src/playlist/PlaylistSong.cxx b/src/playlist/PlaylistSong.cxx index cb21018c2..96fae4ea9 100644 --- a/src/playlist/PlaylistSong.cxx +++ b/src/playlist/PlaylistSong.cxx @@ -25,6 +25,7 @@ merge_song_metadata(DetachedSong &add, const DetachedSong &base) noexcept } add.SetLastModified(base.GetLastModified()); + add.SetAdded(base.GetAdded()); if (add.GetStartTime().IsZero()) { add.SetStartTime(base.GetStartTime()); diff --git a/src/queue/PlaylistUpdate.cxx b/src/queue/PlaylistUpdate.cxx index 3de084410..35a35c81a 100644 --- a/src/queue/PlaylistUpdate.cxx +++ b/src/queue/PlaylistUpdate.cxx @@ -33,6 +33,7 @@ UpdatePlaylistSong(const Database &db, DetachedSong &song) } song.SetLastModified(original->mtime); + song.SetAdded(original->added); song.SetTag(original->tag); db.ReturnSong(original); diff --git a/src/queue/Print.cxx b/src/queue/Print.cxx index 019012e60..80418b6a7 100644 --- a/src/queue/Print.cxx +++ b/src/queue/Print.cxx @@ -127,6 +127,17 @@ PrintSortedQueue(Response &r, const Queue &queue, return a.GetLastModified() < b.GetLastModified(); }); + else if (sort == TagType(SORT_TAG_ADDED)) + std::stable_sort(v.begin(), v.end(), + [&queue, descending](unsigned a_pos, unsigned b_pos){ + if (descending) + std::swap(a_pos, b_pos); + + const auto &a = queue.Get(a_pos); + const auto &b = queue.Get(b_pos); + + return a.GetAdded() < b.GetAdded(); + }); else if (sort == TagType(SORT_TAG_PRIO)) std::stable_sort(v.begin(), v.end(), [&queue, descending](unsigned a_pos, unsigned b_pos){ diff --git a/src/song/DetachedSong.cxx b/src/song/DetachedSong.cxx index d4f809b5e..b280c90d4 100644 --- a/src/song/DetachedSong.cxx +++ b/src/song/DetachedSong.cxx @@ -11,6 +11,7 @@ DetachedSong::DetachedSong(const LightSong &other) noexcept real_uri(other.real_uri != nullptr ? other.real_uri : ""), tag(other.tag), mtime(other.mtime), + added(other.added), start_time(other.start_time), end_time(other.end_time), audio_format(other.audio_format) {} @@ -21,6 +22,7 @@ DetachedSong::operator LightSong() const noexcept result.directory = nullptr; result.real_uri = real_uri.empty() ? nullptr : real_uri.c_str(); result.mtime = mtime; + result.added = added; result.start_time = start_time; result.end_time = end_time; return result; diff --git a/src/song/DetachedSong.hxx b/src/song/DetachedSong.hxx index fb4700b16..debf52315 100644 --- a/src/song/DetachedSong.hxx +++ b/src/song/DetachedSong.hxx @@ -7,6 +7,7 @@ #include "tag/Tag.hxx" #include "pcm/AudioFormat.hxx" #include "Chrono.hxx" +#include "time/ChronoUtil.hxx" #include #include @@ -58,6 +59,13 @@ class DetachedSong { std::chrono::system_clock::time_point mtime = std::chrono::system_clock::time_point::min(); + /** + * The time stamp when the file was added to db. A negative + * value means that this is unknown/unavailable. + */ + std::chrono::system_clock::time_point added = + std::chrono::system_clock::time_point::min(); + /** * Start of this sub-song within the file. */ @@ -212,6 +220,16 @@ public: mtime = _value; } + std::chrono::system_clock::time_point GetAdded() const noexcept { + return IsNegative(added) + ? mtime + : added; + } + + void SetAdded(std::chrono::system_clock::time_point _value) noexcept { + added = _value; + } + SongTime GetStartTime() const noexcept { return start_time; } diff --git a/src/song/Filter.hxx b/src/song/Filter.hxx index da0ad9e93..b48f91b57 100644 --- a/src/song/Filter.hxx +++ b/src/song/Filter.hxx @@ -21,6 +21,11 @@ */ #define SORT_TAG_PRIO (TAG_NUM_OF_ITEM_TYPES + 4) +/** + * Special value for the db_selection_print() sort parameter. + */ +#define SORT_TAG_ADDED (TAG_NUM_OF_ITEM_TYPES + 5) + enum TagType : uint8_t; struct LightSong; diff --git a/src/song/LightSong.hxx b/src/song/LightSong.hxx index 7f540a397..e4328128c 100644 --- a/src/song/LightSong.hxx +++ b/src/song/LightSong.hxx @@ -51,6 +51,12 @@ struct LightSong { */ std::chrono::system_clock::time_point mtime = std::chrono::system_clock::time_point::min(); + /** + * The time stamp when the file was added. A negative + * value means that this is unknown/unavailable. + */ + std::chrono::system_clock::time_point added = std::chrono::system_clock::time_point::min(); + /** * Start of this sub-song within the file. */