From 1e335fae028f19eee2c7d729127f1a303394589d Mon Sep 17 00:00:00 2001 From: Andrzej Rybczak Date: Tue, 2 Oct 2012 02:21:56 +0200 Subject: [PATCH] media library: optimize update --- src/actions.cpp | 6 +- src/media_library.cpp | 406 +++++++++++++++++++----------------------- src/media_library.h | 73 +++++--- src/mpdpp.cpp | 102 ----------- src/mpdpp.h | 32 ---- src/song.cpp | 41 +++-- src/song.h | 4 +- 7 files changed, 259 insertions(+), 405 deletions(-) diff --git a/src/actions.cpp b/src/actions.cpp index b23d2b19..fb89c39c 100644 --- a/src/actions.cpp +++ b/src/actions.cpp @@ -1453,9 +1453,9 @@ void EditLibraryAlbum::Run() Statusbar::lock(); Statusbar::put() << NC::fmtBold << "Album: " << NC::fmtBoldEnd; - std::string new_album = wFooter->getString(myLibrary->Albums.current().value().Album); + std::string new_album = wFooter->getString(myLibrary->Albums.current().value().entry().album()); Statusbar::unlock(); - if (!new_album.empty() && new_album != myLibrary->Albums.current().value().Album) + if (!new_album.empty() && new_album != myLibrary->Albums.current().value().entry().album()) { bool success = 1; Statusbar::msg("Updating tags..."); @@ -2193,7 +2193,7 @@ bool ToggleMediaLibrarySortMode::canBeRun() const void ToggleMediaLibrarySortMode::Run() { - myLibrary->toggleMTimeSort(); + myLibrary->toggleSortMode(); } bool RefetchLyrics::canBeRun() const diff --git a/src/media_library.cpp b/src/media_library.cpp index 141a56ac..a5ec5f2b 100644 --- a/src/media_library.cpp +++ b/src/media_library.cpp @@ -55,31 +55,28 @@ size_t itsMiddleColStartX; size_t itsRightColWidth; size_t itsRightColStartX; -// this string marks the position in middle column that works as "All tracks" option. it's -// assigned to Date in SearchConstraint class since date normally cannot contain other chars -// than ciphers and -'s (0x7f is interpreted as backspace keycode, so it's quite safe to assume -// that it won't appear in any tag, let alone date). -const std::string AllTracksMarker = "\x7f_\x7f_\x7f"; +typedef MediaLibrary::AlbumEntry AlbumEntry; -typedef MediaLibrary::SearchConstraints SearchConstraints; - -std::string AlbumToString(const SearchConstraints &sc); +std::string AlbumToString(const AlbumEntry &ae); std::string SongToString(const MPD::Song &s); -bool TagEntryMatcher(const Regex &rx, const MPD::TagMTime &tagmtime); -bool AlbumEntryMatcher(const Regex &rx, const NC::Menu::Item &item, bool filter); +bool TagEntryMatcher(const Regex &rx, const MediaLibrary::PrimaryTag &tagmtime); +bool AlbumEntryMatcher(const Regex &rx, const NC::Menu::Item &item, bool filter); bool SongEntryMatcher(const Regex &rx, const MPD::Song &s); -void DisplayAlbums(NC::Menu &menu); -void DisplayPrimaryTags(NC::Menu &menu); +void DisplayAlbums(NC::Menu &menu); +void DisplayPrimaryTags(NC::Menu &menu); bool SortSongsByTrack(const MPD::Song &a, const MPD::Song &b); struct SortAllTracks { static const std::array GetFuns; + LocaleStringComparison m_cmp; + public: SortAllTracks() : m_cmp(std::locale(), Config.ignore_leading_the) { } + bool operator()(const MPD::Song &a, const MPD::Song &b) { for (auto get = GetFuns.begin(); get != GetFuns.end(); ++get) { int ret = m_cmp(a.getTags(*get, Config.tags_separator), @@ -90,41 +87,51 @@ public: return a.getTrack() < b.getTrack(); } }; + const std::array SortAllTracks::GetFuns = {{ &MPD::Song::getDate, &MPD::Song::getAlbum, &MPD::Song::getDisc }}; -class SortSearchConstraints { +class SortAlbumEntries { + typedef MediaLibrary::Album Album; + LocaleStringComparison m_cmp; + public: - SortSearchConstraints() : m_cmp(std::locale(), Config.ignore_leading_the) { } - bool operator()(const SearchConstraints &a, const SearchConstraints &b) const { + SortAlbumEntries() : m_cmp(std::locale(), Config.ignore_leading_the) { } + + bool operator()(const AlbumEntry &a, const AlbumEntry &b) const { + return (*this)(a.entry(), b.entry()); + } + + bool operator()(const Album &a, const Album &b) const { if (Config.media_library_sort_by_mtime) - { - return a.MTime > b.MTime; - } + return a.mtime() > b.mtime(); else { int result; - result = m_cmp(a.PrimaryTag, b.PrimaryTag); + result = m_cmp(a.tag(), b.tag()); if (result != 0) return result < 0; - result = m_cmp(a.Date, b.Date); + result = m_cmp(a.date(), b.date()); if (result != 0) return result < 0; - return m_cmp(a.Album, b.Album) < 0; + return m_cmp(a.album(), b.album()) < 0; } } }; -class ArtistSorting { +class SortPrimaryTags { + typedef MediaLibrary::PrimaryTag PrimaryTag; + LocaleStringComparison m_cmp; + public: - ArtistSorting() : m_cmp(std::locale(), Config.ignore_leading_the) { } - bool operator()(const MPD::TagMTime &a, - const MPD::TagMTime &b) const { + SortPrimaryTags() : m_cmp(std::locale(), Config.ignore_leading_the) { } + + bool operator()(const PrimaryTag &a, const PrimaryTag &b) const { if (Config.media_library_sort_by_mtime) return a.mtime() > b.mtime(); else @@ -143,7 +150,7 @@ MediaLibrary::MediaLibrary() itsRightColWidth = COLS-COLS/3*2-1; itsRightColStartX = itsLeftColWidth+itsMiddleColWidth+2; - Tags = NC::Menu(0, MainStartY, itsLeftColWidth, MainHeight, Config.titles_visibility ? tagTypeToString(Config.media_lib_primary_tag) + "s" : "", Config.main_color, NC::brNone); + Tags = NC::Menu(0, MainStartY, itsLeftColWidth, MainHeight, Config.titles_visibility ? tagTypeToString(Config.media_lib_primary_tag) + "s" : "", Config.main_color, NC::brNone); Tags.setHighlightColor(Config.active_column_color); Tags.cyclicScrolling(Config.use_cyclic_scrolling); Tags.centeredCursor(Config.centered_cursor); @@ -151,7 +158,7 @@ MediaLibrary::MediaLibrary() Tags.setSelectedSuffix(Config.selected_item_suffix); Tags.setItemDisplayer(DisplayPrimaryTags); - Albums = NC::Menu(itsMiddleColStartX, MainStartY, itsMiddleColWidth, MainHeight, Config.titles_visibility ? "Albums" : "", Config.main_color, NC::brNone); + Albums = NC::Menu(itsMiddleColStartX, MainStartY, itsMiddleColWidth, MainHeight, Config.titles_visibility ? "Albums" : "", Config.main_color, NC::brNone); Albums.setHighlightColor(Config.main_highlight_color); Albums.cyclicScrolling(Config.use_cyclic_scrolling); Albums.centeredCursor(Config.centered_cursor); @@ -229,194 +236,119 @@ std::wstring MediaLibrary::title() return L"Media library"; } -bool MediaLibrary::hasMTimes() -{ - bool has = false; - if (hasTwoColumns && !Albums.empty()) - has = Albums.current().value().hasMTime(); - else if (!hasTwoColumns && !Tags.empty()) - has = Tags.current().value().hasMTime(); - return has; -} - -void MediaLibrary::toggleMTimeSort() -{ - Config.media_library_sort_by_mtime = !Config.media_library_sort_by_mtime; - if (Config.media_library_sort_by_mtime) - Statusbar::msg("Sorting library by: Modification time"); - else - Statusbar::msg("Sorting library by: Name"); - - if (!hasMTimes() && Config.media_library_sort_by_mtime) - { - Tags.clear(); - Albums.clear(); - Songs.clear(); - } - else - { - if (!hasTwoColumns) - { - std::sort(Tags.beginV(), Tags.endV(), ArtistSorting()); - Tags.refresh(); - Albums.clear(); - Songs.clear(); - } - else - { - std::sort(Albums.beginV(), Albums.endV(), SortSearchConstraints()); - Albums.refresh(); - Songs.clear(); - } - } - - if (hasTwoColumns) - { - if (Config.titles_visibility) - { - std::string item_type = lowercase(tagTypeToString(Config.media_lib_primary_tag)); - std::string and_mtime = Config.media_library_sort_by_mtime ? - " and mtime" : - ""; - Albums.setTitle("Albums (sorted by " + item_type + and_mtime + ")"); - } - } - update(); -} - void MediaLibrary::update() { - if (!hasTwoColumns && Tags.reallyEmpty()) - { - Albums.clear(); - Songs.clear(); - auto list = Mpd.GetListMTime(Config.media_lib_primary_tag, - Config.media_library_sort_by_mtime); - - std::sort(list.begin(), list.end(), ArtistSorting()); - for (auto it = list.begin(); it != list.end(); ++it) - { - if (it->tag().empty() && !Config.media_library_display_empty_tag) - continue; - Tags.addItem(*it); - } - Tags.refresh(); - } + Mpd.BlockIdle(true); - if (!hasTwoColumns && !Tags.empty() && Albums.reallyEmpty() && Songs.reallyEmpty()) + if (hasTwoColumns) { - // idle has to be blocked for now since it would be enabled and - // disabled a few times by each mpd command, which makes no sense - // and slows down the whole process. - Mpd.BlockIdle(true); - Albums.reset(); - Mpd.StartFieldSearchMTime(MPD_TAG_ALBUM, Config.media_library_sort_by_mtime); - Mpd.AddSearch(Config.media_lib_primary_tag, Tags.current().value().tag()); - auto albums = Mpd.CommitSearchTagsMTime(); - for (auto tagmtime = albums.begin(); tagmtime != albums.end(); ++tagmtime) + if (Albums.reallyEmpty()) { - const std::string &album = tagmtime->tag(); - time_t mtime = tagmtime->mtime(); - if (Config.media_library_display_date) - { - Mpd.StartFieldSearch(MPD_TAG_DATE); - - Mpd.AddSearch(Config.media_lib_primary_tag, - Tags.current().value().tag()); - Mpd.AddSearch(MPD_TAG_ALBUM, album); - Mpd.CommitSearchTags([this, &album, &mtime](std::string &&date) { - Albums.addItem(SearchConstraints(album, date, mtime)); - }); - } - else - Albums.addItem(SearchConstraints(album, "", mtime)); - } - if (!Albums.empty()) - std::sort(Albums.beginV(), Albums.endV(), SortSearchConstraints()); - if (Albums.size() > 1) - { - Albums.addSeparator(); - Albums.addItem(SearchConstraints("", AllTracksMarker)); - } - Albums.refresh(); - Mpd.BlockIdle(false); - } - else if (hasTwoColumns && Albums.reallyEmpty()) - { - Songs.clear(); - Albums << NC::XY(0, 0) << "Fetching albums..."; - Albums.Window::refresh(); - Mpd.BlockIdle(true); - MPD::StringList artists; - Mpd.GetList(Config.media_lib_primary_tag, [&artists](std::string &&artist) { - artists.push_back(artist); - }); - for (auto artist = artists.begin(); artist != artists.end(); ++artist) - { - Mpd.StartFieldSearchMTime(MPD_TAG_ALBUM, Config.media_library_sort_by_mtime); - Mpd.AddSearch(Config.media_lib_primary_tag, *artist); - auto albums = Mpd.CommitSearchTagsMTime(); - for (auto am = albums.begin(); am != albums.end(); ++am) - { - const std::string &album = am->tag(); - time_t mtime = am->mtime(); - if (Config.media_library_display_date) + Songs.clear(); + std::map, time_t> albums; + Mpd.GetDirectoryRecursive("/", [&albums](MPD::Song &&s) { + unsigned idx = 0; + std::string tag = s.get(Config.media_lib_primary_tag, idx); + do { - if (Config.media_lib_primary_tag != MPD_TAG_DATE) - { - Mpd.StartFieldSearch(MPD_TAG_DATE); - Mpd.AddSearch(Config.media_lib_primary_tag, *artist); - Mpd.AddSearch(MPD_TAG_ALBUM, album); - Mpd.CommitSearchTags([this, &artist, &album, &mtime](std::string &&date) { - Albums.addItem(SearchConstraints(*artist, album, date, mtime)); - }); - } + auto key = std::make_tuple(tag, s.getAlbum(), s.getDate()); + auto it = albums.find(key); + if (it == albums.end()) + albums[key] = s.getMTime(); else - Albums.addItem(SearchConstraints(*artist, album, *artist, mtime)); + it->second = s.getMTime(); } - else - Albums.addItem(SearchConstraints(*artist, album, "", mtime)); - } + while (!(tag = s.get(Config.media_lib_primary_tag, ++idx)).empty()); + }); + for (auto it = albums.begin(); it != albums.end(); ++it) + Albums.addItem(AlbumEntry(Album( + std::move(std::get<0>(it->first)), + std::move(std::get<1>(it->first)), + std::move(std::get<2>(it->first)), + it->second))); + std::sort(Albums.beginV(), Albums.endV(), SortAlbumEntries()); + Albums.refresh(); } - Mpd.BlockIdle(0); - - if (!Albums.empty()) - std::sort(Albums.beginV(), Albums.endV(), SortSearchConstraints()); - Albums.refresh(); } - - if (!hasTwoColumns && !Tags.empty() && isActiveWindow(Albums) && Albums.reallyEmpty()) + else { - Albums.setHighlightColor(Config.main_highlight_color); - Tags.setHighlightColor(Config.active_column_color); - w = &Tags; + if (Tags.reallyEmpty()) + { + Albums.clear(); + Songs.clear(); + std::map tags; + Mpd.GetDirectoryRecursive("/", [&tags](MPD::Song &&s) { + unsigned idx = 0; + std::string tag = s.get(Config.media_lib_primary_tag, idx); + do + { + auto it = tags.find(tag); + if (it == tags.end()) + tags[tag] = s.getMTime(); + else + it->second = std::max(it->second, s.getMTime()); + } + while (!(tag = s.get(Config.media_lib_primary_tag, ++idx)).empty()); + }); + for (auto it = tags.begin(); it != tags.end(); ++it) + Tags.addItem(PrimaryTag(std::move(it->first), it->second)); + Tags.refresh(); + } + + if (!Tags.empty() && Albums.reallyEmpty()) + { + auto &primary_tag = Tags.current().value().tag(); + Mpd.StartSearch(true); + Mpd.AddSearch(Config.media_lib_primary_tag, primary_tag); + std::map, time_t> albums; + Mpd.CommitSearchSongs([&albums](MPD::Song &&s) { + auto key = std::make_tuple(s.getAlbum(), s.getDate()); + auto it = albums.find(key); + if (it == albums.end()) + albums[key] = s.getMTime(); + else + it->second = s.getMTime(); + }); + for (auto it = albums.begin(); it != albums.end(); ++it) + Albums.addItem(AlbumEntry(Album( + primary_tag, + std::move(std::get<0>(it->first)), + std::move(std::get<1>(it->first)), + it->second))); + std::sort(Albums.beginV(), Albums.endV(), SortAlbumEntries()); + if (albums.size() > 1) + { + Albums.addSeparator(); + Albums.addItem(AlbumEntry::mkAllTracksEntry(primary_tag)); + } + Albums.refresh(); + } } - if (!(hasTwoColumns ? Albums.empty() : Tags.empty()) && Songs.reallyEmpty()) + if (!Albums.empty() && Songs.reallyEmpty()) { Songs.reset(); - - Mpd.StartSearch(1); - Mpd.AddSearch(Config.media_lib_primary_tag, - hasTwoColumns ? Albums.current().value().PrimaryTag : - Tags.current().value().tag()); - if (Albums.current().value().Date != AllTracksMarker) + auto &album = Albums.current().value(); + Mpd.StartSearch(true); + Mpd.AddSearch(Config.media_lib_primary_tag, album.entry().tag()); + if (!album.isAllTracksEntry()) { - Mpd.AddSearch(MPD_TAG_ALBUM, Albums.current().value().Album); - if (Config.media_library_display_date) - Mpd.AddSearch(MPD_TAG_DATE, Albums.current().value().Date); + Mpd.AddSearch(MPD_TAG_ALBUM, album.entry().album()); + Mpd.AddSearch(MPD_TAG_DATE, album.entry().date()); } Mpd.CommitSearchSongs([this](MPD::Song &&s) { Songs.addItem(s, myPlaylist->checkForSong(s)); }); - if (Albums.current().value().Date == AllTracksMarker) + if (album.isAllTracksEntry()) std::sort(Songs.beginV(), Songs.endV(), SortAllTracks()); else std::sort(Songs.beginV(), Songs.endV(), SortSongsByTrack); Songs.refresh(); } + + Mpd.BlockIdle(false); } void MediaLibrary::enterPressed() @@ -437,7 +369,7 @@ void MediaLibrary::spacePressed() } else if (isActiveWindow(Albums)) { - if (Albums.current().value().Date != AllTracksMarker) + if (Albums.current().value().isAllTracksEntry()) { size_t i = Albums.choice(); Albums.at(i).setSelected(!Albums.at(i).isSelected()); @@ -559,9 +491,9 @@ std::string MediaLibrary::currentFilter() { std::string filter; if (isActiveWindow(Tags)) - filter = RegexFilter::currentFilter(Tags); + filter = RegexFilter::currentFilter(Tags); else if (isActiveWindow(Albums)) - filter = RegexItemFilter::currentFilter(Albums); + filter = RegexItemFilter::currentFilter(Albums); else if (isActiveWindow(Songs)) filter = RegexFilter::currentFilter(Songs); return filter; @@ -572,14 +504,14 @@ void MediaLibrary::applyFilter(const std::string &filter) if (isActiveWindow(Tags)) { Tags.showAll(); - auto rx = RegexFilter(filter, Config.regex_type, TagEntryMatcher); + auto rx = RegexFilter(filter, Config.regex_type, TagEntryMatcher); Tags.filter(Tags.begin(), Tags.end(), rx); } else if (isActiveWindow(Albums)) { Albums.showAll(); auto fun = std::bind(AlbumEntryMatcher, _1, _2, true); - auto rx = RegexItemFilter(filter, Config.regex_type, fun); + auto rx = RegexItemFilter(filter, Config.regex_type, fun); Albums.filter(Albums.begin(), Albums.end(), rx); } else if (isActiveWindow(Songs)) @@ -602,13 +534,13 @@ bool MediaLibrary::search(const std::string &constraint) bool result = false; if (isActiveWindow(Tags)) { - auto rx = RegexFilter(constraint, Config.regex_type, TagEntryMatcher); + auto rx = RegexFilter(constraint, Config.regex_type, TagEntryMatcher); result = Tags.search(Tags.begin(), Tags.end(), rx); } else if (isActiveWindow(Albums)) { auto fun = std::bind(AlbumEntryMatcher, _1, _2, false); - auto rx = RegexItemFilter(constraint, Config.regex_type, fun); + auto rx = RegexItemFilter(constraint, Config.regex_type, fun); result = Albums.search(Albums.begin(), Albums.end(), rx); } else if (isActiveWindow(Songs)) @@ -698,12 +630,12 @@ MPD::SongList MediaLibrary::getSelectedSongs() auto &sc = it->value(); Mpd.StartSearch(true); if (hasTwoColumns) - Mpd.AddSearch(Config.media_lib_primary_tag, sc.PrimaryTag); + Mpd.AddSearch(Config.media_lib_primary_tag, sc.entry().tag()); else Mpd.AddSearch(Config.media_lib_primary_tag, Tags.current().value().tag()); - Mpd.AddSearch(MPD_TAG_ALBUM, sc.Album); - Mpd.AddSearch(MPD_TAG_DATE, sc.Date); + Mpd.AddSearch(MPD_TAG_ALBUM, sc.entry().album()); + Mpd.AddSearch(MPD_TAG_DATE, sc.entry().date()); size_t begin = result.size(); Mpd.CommitSearchSongs([&result](MPD::Song &&s) { result.push_back(s); @@ -817,13 +749,9 @@ void MediaLibrary::toggleColumnsMode() if (Config.titles_visibility) { std::string item_type = lowercase(tagTypeToString(Config.media_lib_primary_tag)); - std::string and_mtime = Config.media_library_sort_by_mtime ? - " and mtime" : - ""; + std::string and_mtime = Config.media_library_sort_by_mtime ? " and mtime" : ""; Albums.setTitle("Albums (sorted by " + item_type + and_mtime + ")"); } - else - Albums.setTitle(""); } else Albums.setTitle(Config.titles_visibility ? "Albums" : ""); @@ -845,6 +773,33 @@ ProxySongList MediaLibrary::songsProxyList() }); } +void MediaLibrary::toggleSortMode() +{ + Config.media_library_sort_by_mtime = !Config.media_library_sort_by_mtime; + Statusbar::msg("Sorting library by: %s", + Config.media_library_sort_by_mtime ? "Modification time" : "Name"); + if (hasTwoColumns) + { + std::sort(Albums.beginV(), Albums.endV(), SortAlbumEntries()); + Albums.refresh(); + Songs.clear(); + if (Config.titles_visibility) + { + std::string item_type = lowercase(tagTypeToString(Config.media_lib_primary_tag)); + std::string and_mtime = Config.media_library_sort_by_mtime ? " and mtime" : ""; + Albums.setTitle("Albums (sorted by " + item_type + and_mtime + ")"); + } + } + else + { + std::sort(Tags.beginV(), Tags.endV(), SortPrimaryTags()); + Tags.refresh(); + Albums.clear(); + Songs.clear(); + } + update(); +} + void MediaLibrary::LocateSong(const MPD::Song &s) { std::string primary_tag; @@ -907,15 +862,15 @@ void MediaLibrary::LocateSong(const MPD::Song &s) std::string album = s.getAlbum(); std::string date = s.getDate(); - if ((hasTwoColumns && Albums.current().value().PrimaryTag != primary_tag) - || album != Albums.current().value().Album - || date != Albums.current().value().Date) + if ((hasTwoColumns && Albums.current().value().entry().tag() != primary_tag) + || album != Albums.current().value().entry().album() + || date != Albums.current().value().entry().date()) { for (size_t i = 0; i < Albums.size(); ++i) { - if ((!hasTwoColumns || Albums[i].value().PrimaryTag == primary_tag) - && album == Albums[i].value().Album - && date == Albums[i].value().Date) + if ((!hasTwoColumns || Albums[i].value().entry().tag() == primary_tag) + && album == Albums[i].value().entry().album() + && date == Albums[i].value().entry().date()) { Albums.highlight(i); Songs.clear(); @@ -957,13 +912,14 @@ void MediaLibrary::AddToPlaylist(bool add_n_play) if (addSongsToPlaylist(list, add_n_play)) { if ((!Tags.empty() && isActiveWindow(Tags)) - || (isActiveWindow(Albums) && Albums.current().value().Date == AllTracksMarker)) + || (isActiveWindow(Albums) && Albums.current().value().isAllTracksEntry())) { std::string tag_type = lowercase(tagTypeToString(Config.media_lib_primary_tag)); Statusbar::msg("Songs with %s = \"%s\" added", tag_type.c_str(), Tags.current().value().tag().c_str()); } else if (isActiveWindow(Albums)) - Statusbar::msg("Songs from album \"%s\" added", Albums.current().value().Album.c_str()); + Statusbar::msg("Songs from album \"%s\" added", + Albums.current().value().entry().album().c_str()); } } @@ -982,24 +938,24 @@ void MediaLibrary::AddToPlaylist(bool add_n_play) namespace {// -std::string AlbumToString(const SearchConstraints &sc) +std::string AlbumToString(const AlbumEntry &ae) { std::string result; - if (sc.Date == AllTracksMarker) + if (ae.isAllTracksEntry()) result = "All tracks"; else { if (hasTwoColumns) { - if (sc.PrimaryTag.empty()) + if (ae.entry().tag().empty()) result += Config.empty_tag; else - result += sc.PrimaryTag; + result += ae.entry().tag(); result += " - "; } - if (Config.media_lib_primary_tag != MPD_TAG_DATE && !sc.Date.empty()) - result += "(" + sc.Date + ") "; - result += sc.Album.empty() ? "" : sc.Album; + if (Config.media_lib_primary_tag != MPD_TAG_DATE && !ae.entry().date().empty()) + result += "(" + ae.entry().date() + ") "; + result += ae.entry().album().empty() ? "" : ae.entry().album(); } return result; } @@ -1009,14 +965,14 @@ std::string SongToString(const MPD::Song &s) return s.toString(Config.song_library_format, Config.tags_separator); } -bool TagEntryMatcher(const Regex &rx, const MPD::TagMTime &tagmtime) +bool TagEntryMatcher(const Regex &rx, const MediaLibrary::PrimaryTag &pt) { - return rx.match(tagmtime.tag()); + return rx.match(pt.tag()); } -bool AlbumEntryMatcher(const Regex &rx, const NC::Menu::Item &item, bool filter) +bool AlbumEntryMatcher(const Regex &rx, const NC::Menu::Item &item, bool filter) { - if (item.isSeparator() || item.value().Date == AllTracksMarker) + if (item.isSeparator() || item.value().isAllTracksEntry()) return filter; return rx.match(AlbumToString(item.value())); } @@ -1028,12 +984,12 @@ bool SongEntryMatcher(const Regex &rx, const MPD::Song &s) /***********************************************************************/ -void DisplayAlbums(NC::Menu &menu) +void DisplayAlbums(NC::Menu &menu) { menu << AlbumToString(menu.drawn()->value()); } -void DisplayPrimaryTags(NC::Menu &menu) +void DisplayPrimaryTags(NC::Menu &menu) { const std::string &tag = menu.drawn()->value().tag(); if (tag.empty()) @@ -1046,7 +1002,7 @@ void DisplayPrimaryTags(NC::Menu &menu) bool SortSongsByTrack(const MPD::Song &a, const MPD::Song &b) { - int cmp = a.getDisc().compare(a.getDisc()); + int cmp = a.getDisc().compare(b.getDisc()); if (cmp != 0) return cmp < 0; return a.getTrack() < b.getTrack(); diff --git a/src/media_library.h b/src/media_library.h index 68ceb7ae..f7adba6c 100644 --- a/src/media_library.h +++ b/src/media_library.h @@ -73,31 +73,60 @@ struct MediaLibrary: Screen, Filterable, HasColumns, HasSongs, Sea int Columns(); void LocateSong(const MPD::Song &); ProxySongList songsProxyList(); + void toggleSortMode(); - // mtimes - bool hasMTimes(); - void toggleMTimeSort(); - - struct SearchConstraints + struct PrimaryTag { - SearchConstraints() { } - SearchConstraints(const std::string &tag, const std::string &album, const std::string &date) : PrimaryTag(tag), Album(album), Date(date), MTime(0) { } - SearchConstraints(const std::string &album, const std::string &date) : Album(album), Date(date), MTime(0) { } - SearchConstraints(const std::string &tag, const std::string &album, const std::string &date, time_t mtime) : PrimaryTag(tag), Album(album), Date(date), MTime(mtime) { } - SearchConstraints(const std::string &album, const std::string &date, time_t mtime) : Album(album), Date(date), MTime(mtime) { } - - std::string PrimaryTag; - std::string Album; - std::string Date; - time_t MTime; - - bool operator<(const SearchConstraints &a) const; - - bool hasMTime() { return MTime != 0; } + PrimaryTag(std::string tag_, time_t mtime_) + : m_tag(std::move(tag_)), m_mtime(mtime_) { } + + const std::string &tag() const { return m_tag; } + time_t mtime() const { return m_mtime; } + + private: + std::string m_tag; + time_t m_mtime; }; - - NC::Menu Tags; - NC::Menu Albums; + + struct Album + { + Album(std::string tag_, std::string album_, std::string date_, time_t mtime_) + : m_tag(std::move(tag_)), m_album(std::move(album_)) + , m_date(std::move(date_)), m_mtime(mtime_) { } + + const std::string &tag() const { return m_tag; } + const std::string &album() const { return m_album; } + const std::string &date() const { return m_date; } + time_t mtime() const { return m_mtime; } + + private: + std::string m_tag; + std::string m_album; + std::string m_date; + time_t m_mtime; + }; + + struct AlbumEntry + { + AlbumEntry() : m_all_tracks_entry(false), m_album("", "", "", 0) { } + AlbumEntry(Album album_) : m_all_tracks_entry(false), m_album(album_) { } + + const Album &entry() const { return m_album; } + bool isAllTracksEntry() const { return m_all_tracks_entry; } + + static AlbumEntry mkAllTracksEntry(std::string tag) { + auto result = AlbumEntry(Album(tag, "", "", 0)); + result.m_all_tracks_entry = true; + return result; + } + + private: + bool m_all_tracks_entry; + Album m_album; + }; + + NC::Menu Tags; + NC::Menu Albums; NC::Menu Songs; protected: diff --git a/src/mpdpp.cpp b/src/mpdpp.cpp index 3d1b5ddc..fb0801dc 100644 --- a/src/mpdpp.cpp +++ b/src/mpdpp.cpp @@ -1139,52 +1139,6 @@ void Connection::GetList(mpd_tag_type type, StringConsumer f) GoIdle(); } -TagMTimeList Connection::GetListMTime(mpd_tag_type type, bool get_mtime) -{ - TagMTimeList result; - if (!itsConnection) - return result; - assert(!isCommandsListEnabled); - GoBusy(); - - if (!get_mtime) - { - mpd_search_db_tags(itsConnection, type); - mpd_search_commit(itsConnection); - while (mpd_pair *item = mpd_recv_pair_tag(itsConnection, type)) - { - result.push_back(TagMTime(item->value)); - mpd_return_pair(itsConnection, item); - } - mpd_response_finish(itsConnection); - } - else - { - mpd_send_list_all_meta(itsConnection, "/"); - std::map max_mtimes; - while (mpd_song *s = mpd_recv_song(itsConnection)) - { - Song song(s); - const std::string &tag = song.getTag(type); - time_t mtime = song.getMTime(); - auto mt = max_mtimes.find(tag); - if (mt == max_mtimes.end()) - max_mtimes.insert(std::make_pair(tag, mtime)); - else - mt->second = std::max(mt->second, mtime); - } - mpd_response_finish(itsConnection); - - for (auto it = max_mtimes.begin(); it != max_mtimes.end(); ++it) - { - result.push_back(TagMTime(it->first, it->second)); - } - } - - GoIdle(); - return result; -} - void Connection::StartSearch(bool exact_match) { if (itsConnection) @@ -1199,18 +1153,6 @@ void Connection::StartFieldSearch(mpd_tag_type item) mpd_search_db_tags(itsConnection, item); } } -void Connection::StartFieldSearchMTime(mpd_tag_type item, bool get_mtime) -{ - if (itsConnection) - { - itsSearchedField = item; - itsSearchFieldMTime = get_mtime; - if (!get_mtime) - mpd_search_db_tags(itsConnection, item); - else - mpd_search_db_songs(itsConnection, 1); - } -} void Connection::AddSearch(mpd_tag_type item, const std::string &str) const { @@ -1264,50 +1206,6 @@ void Connection::CommitSearchTags(StringConsumer f) GoIdle(); } -TagMTimeList Connection::CommitSearchTagsMTime() -{ - TagMTimeList result; - if (!itsConnection) - return result; - - assert(!isCommandsListEnabled); - GoBusy(); - mpd_search_commit(itsConnection); - - if (!itsSearchFieldMTime) - { - while (mpd_pair *tag = mpd_recv_pair_tag(itsConnection, itsSearchedField)) - { - result.push_back(TagMTime(tag->value)); - mpd_return_pair(itsConnection, tag); - } - } - else - { - std::map max_mtimes; - while (mpd_song *s = mpd_recv_song(itsConnection)) - { - Song song(s); - const std::string &tag = song.getTag(itsSearchedField); - time_t mtime = song.getMTime(); - auto mt = max_mtimes.find(tag); - if (mt == max_mtimes.end()) - max_mtimes.insert(std::make_pair(tag, mtime)); - else - mt->second = std::max(mt->second, mtime); - } - - for (auto it = max_mtimes.begin(); it != max_mtimes.end(); ++it) - { - result.push_back(TagMTime(it->first, it->second)); - } - } - mpd_response_finish(itsConnection); - GoIdle(); - - return result; -} - void Connection::GetDirectory(const std::string &directory, ItemConsumer f) { if (!itsConnection) diff --git a/src/mpdpp.h b/src/mpdpp.h index f312a294..800eac4d 100644 --- a/src/mpdpp.h +++ b/src/mpdpp.h @@ -93,35 +93,6 @@ private: bool m_enabled; }; -struct TagMTime -{ - TagMTime(const std::string &tag_) : m_tag(tag_), m_mtime(0) { } - TagMTime(const std::string &tag_, time_t mtime_) : m_tag(tag_), m_mtime(mtime_) { } - - const std::string &tag() const { return m_tag; } - time_t mtime() const { return m_mtime; } - - void set_mtime(time_t mtime_) - { - m_mtime = mtime_; - } - - void set_tag(std::string tag_) - { - m_tag = tag_; - } - - bool hasMTime() - { - return (m_mtime != 0); - } - -private: - std::string m_tag; - time_t m_mtime; -}; - -typedef std::vector TagMTimeList; typedef std::vector ItemList; typedef std::vector StringList; typedef std::vector OutputList; @@ -245,17 +216,14 @@ public: void StartSearch(bool); void StartFieldSearch(mpd_tag_type); - void StartFieldSearchMTime(mpd_tag_type, bool); void AddSearch(mpd_tag_type, const std::string &) const; void AddSearchAny(const std::string &str) const; void AddSearchURI(const std::string &str) const; void CommitSearchSongs(SongConsumer f); void CommitSearchTags(StringConsumer f); - TagMTimeList CommitSearchTagsMTime(); void GetPlaylists(StringConsumer f); void GetList(mpd_tag_type type, StringConsumer f); - TagMTimeList GetListMTime(mpd_tag_type, bool); void GetDirectory(const std::string &directory, ItemConsumer f); void GetDirectoryRecursive(const std::string &directory, SongConsumer f); void GetSongs(const std::string &directory, SongConsumer f); diff --git a/src/song.cpp b/src/song.cpp index 372e4cc5..7894224d 100644 --- a/src/song.cpp +++ b/src/song.cpp @@ -43,10 +43,13 @@ size_t calc_hash(const char* s, unsigned seed = 0) namespace MPD {// -const char *Song::getTag(mpd_tag_type type, unsigned idx) const +std::string Song::get(mpd_tag_type type, unsigned idx) const { + std::string result; const char *tag = mpd_song_get_tag(m_song.get(), type, idx); - return tag ? tag : ""; + if (tag) + result = tag; + return result; } Song::Song(mpd_song *s) @@ -96,31 +99,31 @@ std::string Song::getDirectory(unsigned idx) const std::string Song::getArtist(unsigned idx) const { assert(m_song); - return getTag(MPD_TAG_ARTIST, idx); + return get(MPD_TAG_ARTIST, idx); } std::string Song::getTitle(unsigned idx) const { assert(m_song); - return getTag(MPD_TAG_TITLE, idx); + return get(MPD_TAG_TITLE, idx); } std::string Song::getAlbum(unsigned idx) const { assert(m_song); - return getTag(MPD_TAG_ALBUM, idx); + return get(MPD_TAG_ALBUM, idx); } std::string Song::getAlbumArtist(unsigned idx) const { assert(m_song); - return getTag(MPD_TAG_ALBUM_ARTIST, idx); + return get(MPD_TAG_ALBUM_ARTIST, idx); } std::string Song::getTrack(unsigned idx) const { assert(m_song); - std::string track = getTag(MPD_TAG_TRACK, idx); + std::string track = get(MPD_TAG_TRACK, idx); if ((track.length() == 1 && track[0] != '0') || (track.length() > 3 && track[1] == '/')) track = "0"+track; @@ -140,37 +143,37 @@ std::string Song::getTrackNumber(unsigned idx) const std::string Song::getDate(unsigned idx) const { assert(m_song); - return getTag(MPD_TAG_DATE, idx); + return get(MPD_TAG_DATE, idx); } std::string Song::getGenre(unsigned idx) const { assert(m_song); - return getTag(MPD_TAG_GENRE, idx); + return get(MPD_TAG_GENRE, idx); } std::string Song::getComposer(unsigned idx) const { assert(m_song); - return getTag(MPD_TAG_COMPOSER, idx); + return get(MPD_TAG_COMPOSER, idx); } std::string Song::getPerformer(unsigned idx) const { assert(m_song); - return getTag(MPD_TAG_PERFORMER, idx); + return get(MPD_TAG_PERFORMER, idx); } std::string Song::getDisc(unsigned idx) const { assert(m_song); - return getTag(MPD_TAG_DISC, idx); + return get(MPD_TAG_DISC, idx); } std::string Song::getComment(unsigned idx) const { assert(m_song); - return getTag(MPD_TAG_COMMENT, idx); + return get(MPD_TAG_COMMENT, idx); } std::string Song::getLength(unsigned idx) const @@ -318,7 +321,7 @@ std::string Song::ParseFormat(std::string::const_iterator &it, const std::string { std::string result; bool has_some_tags = 0; - MPD::Song::GetFunction get = 0; + MPD::Song::GetFunction get_fun = 0; while (*++it != '}') { while (*it == '{') @@ -345,21 +348,21 @@ std::string Song::ParseFormat(std::string::const_iterator &it, const std::string if (*it == '%') { result += *it; - get = 0; + get_fun = 0; } else - get = charToGetFunction(*it); + get_fun = charToGetFunction(*it); - if (get) + if (get_fun) { - std::string tag = getTags(get, tags_separator); + std::string tag = getTags(get_fun, tags_separator); if (!escape_chars.empty()) // prepend format escape character to all given chars to escape { for (size_t i = 0; i < escape_chars.length(); ++i) for (size_t j = 0; (j = tag.find(escape_chars[i], j)) != std::string::npos; j += 2) tag.replace(j, 1, std::string(1, FormatEscapeCharacter) + escape_chars[i]); } - if (!tag.empty() && (get != &MPD::Song::getLength || getDuration() > 0)) + if (!tag.empty() && (get_fun != &MPD::Song::getLength || getDuration() > 0)) { if (delimiter && tag.size() > delimiter) tag = ToString(wideShorten(ToWString(tag), delimiter)); diff --git a/src/song.h b/src/song.h index 16a99c7f..3151428d 100644 --- a/src/song.h +++ b/src/song.h @@ -39,6 +39,8 @@ struct Song Song(mpd_song *s); + std::string get(mpd_tag_type type, unsigned idx = 0) const; + virtual std::string getURI(unsigned idx = 0) const; virtual std::string getName(unsigned idx = 0) const; virtual std::string getDirectory(unsigned idx = 0) const; @@ -81,8 +83,6 @@ struct Song static bool isFormatOk(const std::string &type, const std::string &fmt); static const char FormatEscapeCharacter = 1; - - const char *getTag(mpd_tag_type type, unsigned idx = 0) const; private: std::string ParseFormat(std::string::const_iterator &it, const std::string &tags_separator,