media library: add support for sorting by mtime

This commit is contained in:
Matthew Hague
2012-09-20 12:32:48 +02:00
committed by Andrzej Rybczak
parent add40d542d
commit b487f10f10
15 changed files with 336 additions and 65 deletions

View File

@@ -431,6 +431,9 @@
#def_key "m"
# move_selected_items_up
#
#def_key "m"
# toggle_media_library_sort_mode
#
#def_key "n"
# move_sort_order_down
#

View File

@@ -437,6 +437,8 @@
#
#media_library_display_empty_tag = "yes"
#
#media_library_sort_by_mtime = "no"
#
#enable_window_title = "yes"
#
##

View File

@@ -1397,13 +1397,13 @@ void EditLibraryTag::Run()
Statusbar::lock();
Statusbar::put() << NC::fmtBold << tagTypeToString(Config.media_lib_primary_tag) << NC::fmtBoldEnd << ": ";
std::string new_tag = wFooter->getString(myLibrary->Tags.current().value());
std::string new_tag = wFooter->getString(myLibrary->Tags.current().value().tag());
Statusbar::unlock();
if (!new_tag.empty() && new_tag != myLibrary->Tags.current().value())
if (!new_tag.empty() && new_tag != myLibrary->Tags.current().value().tag())
{
Statusbar::msg("Updating tags...");
Mpd.StartSearch(1);
Mpd.AddSearch(Config.media_lib_primary_tag, myLibrary->Tags.current().value());
Mpd.AddSearch(Config.media_lib_primary_tag, myLibrary->Tags.current().value().tag());
MPD::MutableSong::SetFunction set = tagTypeToSetFunction(Config.media_lib_primary_tag);
assert(set);
bool success = true;
@@ -2162,12 +2162,15 @@ void ToggleLibraryTagType::Run()
myLibrary->Tags.setTitle(Config.titles_visibility ? item_type + "s" : "");
myLibrary->Tags.reset();
item_type = lowercase(item_type);
std::string and_mtime = Config.media_library_sort_by_mtime ?
" and mtime" :
"";
if (myLibrary->Columns() == 2)
{
myLibrary->Songs.clear();
myLibrary->Albums.reset();
myLibrary->Albums.clear();
myLibrary->Albums.setTitle(Config.titles_visibility ? "Albums (sorted by " + item_type + ")" : "");
myLibrary->Albums.setTitle(Config.titles_visibility ? "Albums (sorted by " + item_type + and_mtime + ")" : "");
myLibrary->Albums.display();
}
else
@@ -2179,6 +2182,16 @@ void ToggleLibraryTagType::Run()
}
}
bool ToggleMediaLibrarySortMode::canBeRun() const
{
return myScreen == myLibrary;
}
void ToggleMediaLibrarySortMode::Run()
{
myLibrary->toggleMTimeSort();
}
bool RefetchLyrics::canBeRun() const
{
# ifdef HAVE_CURL_CURL_H
@@ -2295,7 +2308,7 @@ void ShowArtistInfo::Run()
{
assert(!myLibrary->Tags.empty());
assert(Config.media_lib_primary_tag == MPD_TAG_ARTIST);
artist = myLibrary->Tags.current().value();
artist = myLibrary->Tags.current().value().tag();
}
else
{
@@ -2660,6 +2673,7 @@ void populateActions()
insertAction(new AddRandomItems());
insertAction(new ToggleBrowserSortMode());
insertAction(new ToggleLibraryTagType());
insertAction(new ToggleMediaLibrarySortMode());
insertAction(new RefetchLyrics());
insertAction(new RefetchArtistInfo());
insertAction(new SetSelectedItemsPriority());

View File

@@ -43,7 +43,7 @@ enum ActionType
aCropMainPlaylist, aCropPlaylist, aClearMainPlaylist, aClearPlaylist, aSortPlaylist, aReversePlaylist,
aApplyFilter, aFind, aFindItemForward, aFindItemBackward, aNextFoundItem,
aPreviousFoundItem, aToggleFindMode, aToggleReplayGainMode, aToggleSpaceMode, aToggleAddMode,
aToggleMouse, aToggleBitrateVisibility, aAddRandomItems, aToggleBrowserSortMode, aToggleLibraryTagType,
aToggleMouse, aToggleBitrateVisibility, aAddRandomItems, aToggleBrowserSortMode, aToggleLibraryTagType, aToggleMediaLibrarySortMode,
aRefetchLyrics, aRefetchArtistInfo, aSetSelectedItemsPriority, aFilterPlaylistOnPriorities,
aShowSongInfo, aShowArtistInfo,
aShowLyrics, aQuit, aNextScreen, aPreviousScreen, aShowHelp, aShowPlaylist, aShowBrowser, aChangeBrowseMode,
@@ -914,6 +914,16 @@ protected:
virtual void Run();
};
struct ToggleMediaLibrarySortMode : public Action
{
ToggleMediaLibrarySortMode()
: Action(aToggleMediaLibrarySortMode, "toggle_media_library_sort_mode") { }
protected:
virtual bool canBeRun() const;
virtual void Run();
};
struct RefetchLyrics : public Action
{
RefetchLyrics() : Action(aRefetchLyrics, "refetch_lyrics") { }

View File

@@ -529,6 +529,7 @@ void BindingsConfiguration::generateDefaults()
{
bind(k, aMoveSortOrderUp);
bind(k, aMoveSelectedItemsUp);
bind(k, aToggleMediaLibrarySortMode);
}
if (notBound(k = stringToKey("n")))
{

View File

@@ -35,7 +35,7 @@ extern BaseScreen *myScreen;
extern BaseScreen *myLockedScreen;
// points at inactive screen, if locking was enabled and two screens are displayed
extern BaseScreen *myInactiveScreen;
extern BaseScreen *myInactiveScreen;
// header window (above main window)
extern NC::Window *wHeader;

View File

@@ -325,6 +325,7 @@ void Help::GetKeybindings()
# endif // HAVE_TAGLIB_H
KeyDesc(aEditLibraryTag, "Edit tag (left column)/album (middle/right column)");
KeyDesc(aToggleLibraryTagType, "Toggle type of tag used in left column");
KeyDesc(aToggleMediaLibrarySortMode, "Toggle sort mode");
KeysSection("Playlist editor");
KeyDesc(aPreviousColumn, "Previous column");

View File

@@ -66,12 +66,12 @@ typedef MediaLibrary::SearchConstraints SearchConstraints;
std::string AlbumToString(const SearchConstraints &sc);
std::string SongToString(const MPD::Song &s);
bool TagEntryMatcher(const Regex &rx, const std::string &tag);
bool TagEntryMatcher(const Regex &rx, const MPD::TagMTime &tagmtime);
bool AlbumEntryMatcher(const Regex &rx, const NC::Menu<SearchConstraints>::Item &item, bool filter);
bool SongEntryMatcher(const Regex &rx, const MPD::Song &s);
void DisplayAlbums(NC::Menu<SearchConstraints> &menu);
void DisplayPrimaryTags(NC::Menu<std::string> &menu);
void DisplayPrimaryTags(NC::Menu<MPD::TagMTime> &menu);
bool SortSongsByTrack(const MPD::Song &a, const MPD::Song &b);
@@ -101,14 +101,34 @@ class SortSearchConstraints {
public:
SortSearchConstraints() : m_cmp(std::locale(), Config.ignore_leading_the) { }
bool operator()(const SearchConstraints &a, const SearchConstraints &b) const {
int result;
result = m_cmp(a.PrimaryTag, b.PrimaryTag);
if (result != 0)
return result < 0;
result = m_cmp(a.Date, b.Date);
if (result != 0)
return result < 0;
return m_cmp(a.Album, b.Album) < 0;
if (Config.media_library_sort_by_mtime)
{
return a.MTime > b.MTime;
}
else
{
int result;
result = m_cmp(a.PrimaryTag, b.PrimaryTag);
if (result != 0)
return result < 0;
result = m_cmp(a.Date, b.Date);
if (result != 0)
return result < 0;
return m_cmp(a.Album, b.Album) < 0;
}
}
};
class ArtistSorting {
LocaleStringComparison m_cmp;
public:
ArtistSorting() : m_cmp(std::locale(), Config.ignore_leading_the) { }
bool operator()(const MPD::TagMTime &a,
const MPD::TagMTime &b) const {
if (Config.media_library_sort_by_mtime)
return a.mtime() > b.mtime();
else
return m_cmp(a.tag(), b.tag()) < 0;
}
};
@@ -123,7 +143,7 @@ MediaLibrary::MediaLibrary()
itsRightColWidth = COLS-COLS/3*2-1;
itsRightColStartX = itsLeftColWidth+itsMiddleColWidth+2;
Tags = NC::Menu<std::string>(0, MainStartY, itsLeftColWidth, MainHeight, Config.titles_visibility ? tagTypeToString(Config.media_lib_primary_tag) + "s" : "", Config.main_color, NC::brNone);
Tags = NC::Menu<MPD::TagMTime>(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);
@@ -209,18 +229,74 @@ 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.GetList(Config.media_lib_primary_tag);
std::sort(list.begin(), list.end(),
LocaleBasedSorting(std::locale(), Config.ignore_leading_the));
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->empty() && !Config.media_library_display_empty_tag)
if (it->tag().empty() && !Config.media_library_display_empty_tag)
continue;
Tags.addItem(*it);
}
@@ -234,22 +310,26 @@ void MediaLibrary::update()
// and slows down the whole process.
Mpd.BlockIdle(true);
Albums.reset();
Mpd.StartFieldSearch(MPD_TAG_ALBUM);
Mpd.AddSearch(Config.media_lib_primary_tag, Tags.current().value());
auto albums = Mpd.CommitSearchTags();
for (auto album = albums.begin(); album != albums.end(); ++album)
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)
{
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());
Mpd.AddSearch(MPD_TAG_ALBUM, *album);
Mpd.AddSearch(Config.media_lib_primary_tag,
Tags.current().value().tag());
Mpd.AddSearch(MPD_TAG_ALBUM, album);
auto dates = Mpd.CommitSearchTags();
for (auto date = dates.begin(); date != dates.end(); ++date)
Albums.addItem(SearchConstraints(*album, *date));
Albums.addItem(SearchConstraints(album, *date, mtime));
}
else
Albums.addItem(SearchConstraints(*album, ""));
Albums.addItem(SearchConstraints(album, "", mtime));
}
if (!Albums.empty())
std::sort(Albums.beginV(), Albums.endV(), SortSearchConstraints());
@@ -270,30 +350,33 @@ void MediaLibrary::update()
auto artists = Mpd.GetList(Config.media_lib_primary_tag);
for (auto artist = artists.begin(); artist != artists.end(); ++artist)
{
Mpd.StartFieldSearch(MPD_TAG_ALBUM);
Mpd.StartFieldSearchMTime(MPD_TAG_ALBUM, Config.media_library_sort_by_mtime);
Mpd.AddSearch(Config.media_lib_primary_tag, *artist);
auto albums = Mpd.CommitSearchTags();
for (auto album = albums.begin(); album != albums.end(); ++album)
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)
{
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.AddSearch(MPD_TAG_ALBUM, album);
auto dates = Mpd.CommitSearchTags();
for (auto date = dates.begin(); date != dates.end(); ++date)
Albums.addItem(SearchConstraints(*artist, *album, *date));
Albums.addItem(SearchConstraints(*artist, album, *date, mtime));
}
else
Albums.addItem(SearchConstraints(*artist, *album, *artist));
Albums.addItem(SearchConstraints(*artist, album, *artist, mtime));
}
else
Albums.addItem(SearchConstraints(*artist, *album, ""));
Albums.addItem(SearchConstraints(*artist, album, "", mtime));
}
}
Mpd.BlockIdle(0);
if (!Albums.empty())
std::sort(Albums.beginV(), Albums.endV(), SortSearchConstraints());
Albums.refresh();
@@ -311,7 +394,9 @@ void MediaLibrary::update()
Songs.reset();
Mpd.StartSearch(1);
Mpd.AddSearch(Config.media_lib_primary_tag, hasTwoColumns ? Albums.current().value().PrimaryTag : Tags.current().value());
Mpd.AddSearch(Config.media_lib_primary_tag,
hasTwoColumns ? Albums.current().value().PrimaryTag :
Tags.current().value().tag());
if (Albums.current().value().Date != AllTracksMarker)
{
Mpd.AddSearch(MPD_TAG_ALBUM, Albums.current().value().Album);
@@ -471,7 +556,7 @@ std::string MediaLibrary::currentFilter()
{
std::string filter;
if (isActiveWindow(Tags))
filter = RegexFilter<std::string>::currentFilter(Tags);
filter = RegexFilter<MPD::TagMTime>::currentFilter(Tags);
else if (isActiveWindow(Albums))
filter = RegexItemFilter<SearchConstraints>::currentFilter(Albums);
else if (isActiveWindow(Songs))
@@ -484,7 +569,7 @@ void MediaLibrary::applyFilter(const std::string &filter)
if (isActiveWindow(Tags))
{
Tags.showAll();
auto rx = RegexFilter<std::string>(filter, Config.regex_type, TagEntryMatcher);
auto rx = RegexFilter<MPD::TagMTime>(filter, Config.regex_type, TagEntryMatcher);
Tags.filter(Tags.begin(), Tags.end(), rx);
}
else if (isActiveWindow(Albums))
@@ -514,7 +599,7 @@ bool MediaLibrary::search(const std::string &constraint)
bool result = false;
if (isActiveWindow(Tags))
{
auto rx = RegexFilter<std::string>(constraint, Config.regex_type, TagEntryMatcher);
auto rx = RegexFilter<MPD::TagMTime>(constraint, Config.regex_type, TagEntryMatcher);
result = Tags.search(Tags.begin(), Tags.end(), rx);
}
else if (isActiveWindow(Albums))
@@ -596,10 +681,10 @@ MPD::SongList MediaLibrary::getSelectedSongs()
};
for (auto it = Tags.begin(); it != Tags.end(); ++it)
if (it->isSelected())
tag_handler(it->value());
tag_handler(it->value().tag());
// if no item is selected, add current one
if (result.empty() && !Tags.empty())
tag_handler(Tags.current().value());
tag_handler(Tags.current().value().tag());
}
else if (isActiveWindow(Albums))
{
@@ -612,7 +697,8 @@ MPD::SongList MediaLibrary::getSelectedSongs()
if (hasTwoColumns)
Mpd.AddSearch(Config.media_lib_primary_tag, sc.PrimaryTag);
else
Mpd.AddSearch(Config.media_lib_primary_tag, Tags.current().value());
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);
auto songs = Mpd.CommitSearchSongs();
@@ -726,7 +812,10 @@ void MediaLibrary::toggleColumnsMode()
if (Config.titles_visibility)
{
std::string item_type = lowercase(tagTypeToString(Config.media_lib_primary_tag));
Albums.setTitle("Albums (sorted by " + item_type + ")");
std::string and_mtime = Config.media_library_sort_by_mtime ?
" and mtime" :
"";
Albums.setTitle("Albums (sorted by " + item_type + and_mtime + ")");
}
else
Albums.setTitle("");
@@ -792,11 +881,11 @@ void MediaLibrary::LocateSong(const MPD::Song &s)
Tags.showAll();
if (Tags.empty())
update();
if (primary_tag != Tags.current().value())
if (primary_tag != Tags.current().value().tag())
{
for (size_t i = 0; i < Tags.size(); ++i)
{
if (primary_tag == Tags[i].value())
if (primary_tag == Tags[i].value().tag())
{
Tags.highlight(i);
Albums.clear();
@@ -866,7 +955,7 @@ void MediaLibrary::AddToPlaylist(bool add_n_play)
|| (isActiveWindow(Albums) && Albums.current().value().Date == AllTracksMarker))
{
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().c_str());
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());
@@ -915,9 +1004,9 @@ std::string SongToString(const MPD::Song &s)
return s.toString(Config.song_library_format, Config.tags_separator);
}
bool TagEntryMatcher(const Regex &rx, const std::string &tag)
bool TagEntryMatcher(const Regex &rx, const MPD::TagMTime &tagmtime)
{
return rx.match(tag);
return rx.match(tagmtime.tag());
}
bool AlbumEntryMatcher(const Regex &rx, const NC::Menu<SearchConstraints>::Item &item, bool filter)
@@ -939,9 +1028,9 @@ void DisplayAlbums(NC::Menu<SearchConstraints> &menu)
menu << AlbumToString(menu.drawn()->value());
}
void DisplayPrimaryTags(NC::Menu<std::string> &menu)
void DisplayPrimaryTags(NC::Menu<MPD::TagMTime> &menu)
{
const std::string &tag = menu.drawn()->value();
const std::string &tag = menu.drawn()->value().tag();
if (tag.empty())
menu << Config.empty_tag;
else
@@ -952,7 +1041,7 @@ void DisplayPrimaryTags(NC::Menu<std::string> &menu)
bool SortSongsByTrack(const MPD::Song &a, const MPD::Song &b)
{
int cmp = a.getDisc().compare(b.getDisc());
int cmp = a.getDisc().compare(a.getDisc());
if (cmp != 0)
return cmp < 0;
return a.getTrack() < b.getTrack();

View File

@@ -73,21 +73,30 @@ struct MediaLibrary: Screen<NC::Window *>, Filterable, HasColumns, HasSongs, Sea
int Columns();
void LocateSong(const MPD::Song &);
ProxySongList songsProxyList();
// mtimes
bool hasMTimes();
void toggleMTimeSort();
struct SearchConstraints
{
SearchConstraints() { }
SearchConstraints(const std::string &tag, const std::string &album, const std::string &date)
: PrimaryTag(tag), Album(album), Date(date) { }
SearchConstraints(const std::string &album, const std::string &date)
: Album(album), Date(date) { }
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;
NC::Menu<std::string> Tags;
bool operator<(const SearchConstraints &a) const;
bool hasMTime() { return MTime != 0; }
};
NC::Menu<MPD::TagMTime> Tags;
NC::Menu<SearchConstraints> Albums;
NC::Menu<MPD::Song> Songs;

View File

@@ -21,6 +21,7 @@
#include <cassert>
#include <cstdlib>
#include <algorithm>
#include <map>
#include "charset.h"
#include "error.h"
@@ -1140,6 +1141,52 @@ StringList Connection::GetList(mpd_tag_type type)
return result;
}
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<std::string, time_t> 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)
@@ -1154,6 +1201,18 @@ 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
{
@@ -1211,6 +1270,50 @@ StringList Connection::CommitSearchTags()
return result;
}
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<std::string, time_t> 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;
}
ItemList Connection::GetDirectory(const std::string &path)
{
ItemList result;

View File

@@ -93,6 +93,35 @@ 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<TagMTime> TagMTimeList;
typedef std::vector<Item> ItemList;
typedef std::vector<std::string> StringList;
typedef std::vector<Output> OutputList;
@@ -213,14 +242,17 @@ 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;
SongList CommitSearchSongs();
StringList CommitSearchTags();
TagMTimeList CommitSearchTagsMTime();
StringList GetPlaylists();
StringList GetList(mpd_tag_type);
TagMTimeList GetListMTime(mpd_tag_type, bool);
ItemList GetDirectory(const std::string &);
SongList GetDirectoryRecursive(const std::string &);
SongList GetSongs(const std::string &);
@@ -240,7 +272,7 @@ private:
int GoBusy();
int CheckForErrors();
mpd_connection *itsConnection;
bool isCommandsListEnabled;
@@ -272,6 +304,7 @@ private:
void *itsErrorHandlerUserdata;
mpd_tag_type itsSearchedField;
bool itsSearchFieldMTime;
};
}
@@ -279,4 +312,3 @@ private:
extern MPD::Connection Mpd;
#endif

View File

@@ -89,7 +89,7 @@ private:
};
void replaceTag(mpd_tag_type tag_type, std::string &&orig_value,
const std::string &value, unsigned idx);
const std::string &value, unsigned idx);
template <typename F>
std::string getTag(mpd_tag_type tag_type, F orig_value, unsigned idx) const {

View File

@@ -239,6 +239,7 @@ void Configuration::SetDefaults()
new_design = false;
visualizer_use_wave = true;
visualizer_in_stereo = false;
media_library_sort_by_mtime = false;
tag_editor_extended_numeration = false;
media_library_display_date = true;
media_library_display_empty_tag = true;
@@ -953,6 +954,10 @@ void Configuration::Read()
if (!v.empty())
media_lib_primary_tag = charToTagType(v[0]);
}
else if (name == "media_library_sort_by_mtime")
{
media_library_sort_by_mtime = v == "yes";
}
else
std::cout << "Unknown option: " << name << ", ignoring.\n";
}

View File

@@ -177,6 +177,7 @@ struct Configuration
bool new_design;
bool visualizer_use_wave;
bool visualizer_in_stereo;
bool media_library_sort_by_mtime;
bool tag_editor_extended_numeration;
bool media_library_display_date;
bool media_library_display_empty_tag;

View File

@@ -82,8 +82,9 @@ struct Song
static const char FormatEscapeCharacter = 1;
const char *getTag(mpd_tag_type type, unsigned idx = 0) const;
private:
const char *getTag(mpd_tag_type type, unsigned idx) const;
std::string ParseFormat(std::string::const_iterator &it, const std::string &tags_separator,
const std::string &escape_chars) const;