Merge branch '0.7.x'

This commit is contained in:
Andrzej Rybczak
2016-10-30 23:44:26 +01:00
18 changed files with 128 additions and 51 deletions

26
INSTALL
View File

@@ -25,22 +25,24 @@ it or regenerate `configure' using a newer version of `autoconf'.
The simplest way to compile this package is: The simplest way to compile this package is:
1. `cd' to the directory containing the package's source code and type 1. `cd' to the directory containing the package's source code.
`./configure' to configure the package for your system. If you're
using `csh' on an old version of System V, you might need to type
`sh ./configure' instead to prevent `csh' from trying to execute
`configure' itself.
Running `configure' takes a while. While running, it prints some For the next two commands, `csh' users will need to prefix them with
messages telling which features it is checking for. `sh '.
2. Type `make' to compile the package. 2. Run `./autogen.sh' to generate the `configure' script.
3. Type `make install' to install the programs and any data files and 3. Run `./configure' to configure the package for your system. This
documentation. will take a while. While running, it prints some messages
telling which features it is checking for.
4. You can remove the program binaries and object files from the 4. Run `make' to compile the package.
source code directory by typing `make clean'.
5. Type `make install' to install the programs and any data files
and documentation.
6. You can remove the program binaries and object files from the
source code directory by typing `make clean'.
Compilers and Options Compilers and Options
===================== =====================

9
NEWS
View File

@@ -2,6 +2,15 @@ ncmpcpp-0.8 (????-??-??)
* Configuration variable 'execute_on_player_state_change' was added. * Configuration variable 'execute_on_player_state_change' was added.
* Support for controlling whether ncmpcpp should display multiple tags as-is or make an effort to hide duplicate values (show_duplicate_tags configuration variable, enabled by default). * Support for controlling whether ncmpcpp should display multiple tags as-is or make an effort to hide duplicate values (show_duplicate_tags configuration variable, enabled by default).
ncmpcpp-0.7.6 (2016-10-30)
* Fixed assertion failure on trying to search backwards in an empty list.
* Updated installation instructions in INSTALL file.
* Make sure that stream of random numbers is not deterministic.
* Opening playlist editor when there is no MPD playlists directory no longer freezes the application.
* Added info about behavior of MPD_HOST and MPD_PORT environment variables to man page.
* Tilde will now be expanded to home directory in visualizer_fifo_path, execute_on_song_change and external_editor configuration variables.
* Fixed lyricwiki and justsomelyrics fetchers.
ncmpcpp-0.7.5 (2016-08-17) ncmpcpp-0.7.5 (2016-08-17)
* Action chains can be now used for seeking. * Action chains can be now used for seeking.
* Fixed fetching artist info from last.fm. * Fixed fetching artist info from last.fm.

View File

@@ -156,8 +156,3 @@ $AUTOMAKE --add-missing $AUTOMAKE_FLAGS || exit 1
echo " $AUTOCONF" echo " $AUTOCONF"
$AUTOCONF || exit 1 $AUTOCONF || exit 1
cd "$olddir"
if test x$NOCONFIGURE = x; then
"$srcdir"/configure "$@" || exit 1
fi

View File

@@ -59,10 +59,10 @@ Directory for storing ncmpcpp related files. Changing it is useful if you want t
Directory for storing downloaded lyrics. It defaults to ~/.lyrics since other MPD clients (eg. ncmpc) also use that location. Directory for storing downloaded lyrics. It defaults to ~/.lyrics since other MPD clients (eg. ncmpc) also use that location.
.TP .TP
.B mpd_host = HOST .B mpd_host = HOST
Connect to MPD running on specified host/unix socket. When HOST starts with a '/', it is assumed to be a unix socket. Connect to MPD running on specified host/unix socket. When HOST starts with a '/', it is assumed to be a unix socket. Note: MPD_HOST environment variable overrides this setting.
.TP .TP
.B mpd_port = PORT .B mpd_port = PORT
Connect to MPD on the specified port. Connect to MPD on the specified port. Note: MPD_PORT environment variable overrides this setting.
.TP .TP
.B mpd_music_dir = PATH .B mpd_music_dir = PATH
Search for files in specified directory. This is needed for tag editor to work. Search for files in specified directory. This is needed for tag editor to work.

View File

@@ -2124,11 +2124,15 @@ void AddRandomItems::run()
Statusbar::put() << "Number of random " << tag_type_str << "s: "; Statusbar::put() << "Number of random " << tag_type_str << "s: ";
number = fromString<unsigned>(wFooter->prompt()); number = fromString<unsigned>(wFooter->prompt());
} }
if (number && (rnd_type == 's' ? Mpd.AddRandomSongs(number) : Mpd.AddRandomTag(tag_type, number))) if (number > 0)
{ {
Statusbar::printf("%1% random %2%%3% added to playlist", bool success;
number, tag_type_str, number == 1 ? "" : "s" if (rnd_type == 's')
); success = Mpd.AddRandomSongs(number, Global::RNG);
else
success = Mpd.AddRandomTag(tag_type, number, Global::RNG);
if (success)
Statusbar::printf("%1% random %2%%3% added to playlist", number, tag_type_str, number == 1 ? "" : "s");
} }
} }

View File

@@ -38,4 +38,6 @@ bool SeekingInProgress = false;
std::string VolumeState; std::string VolumeState;
boost::posix_time::ptime Timer; boost::posix_time::ptime Timer;
std::mt19937 RNG;
} }

View File

@@ -22,6 +22,8 @@
#define NCMPCPP_GLOBAL_H #define NCMPCPP_GLOBAL_H
#include <boost/date_time/posix_time/posix_time_types.hpp> #include <boost/date_time/posix_time/posix_time_types.hpp>
#include <random>
#include "mpdpp.h" #include "mpdpp.h"
#include "screen.h" #include "screen.h"
@@ -58,6 +60,9 @@ extern std::string VolumeState;
// global timer // global timer
extern boost::posix_time::ptime Timer; extern boost::posix_time::ptime Timer;
// global RNG
extern std::mt19937 RNG;
} }
#endif // NCMPCPP_GLOBAL_H #endif // NCMPCPP_GLOBAL_H

View File

@@ -53,7 +53,7 @@ MPD::SongIterator getDatabaseIterator(MPD::Connection &mpd)
{ {
// If we can't get the database, display appropriate // If we can't get the database, display appropriate
// error message and reconnect with the MPD server. // error message and reconnect with the MPD server.
Statusbar::print("Unable to fetch the data, increase max_buffer_output_size in your MPD configuration file"); Statusbar::print("Unable to fetch the data, increase max_output_buffer_size in your MPD configuration file");
mpd.Disconnect(); mpd.Disconnect();
mpd.Connect(); mpd.Connect();
} }

View File

@@ -25,7 +25,9 @@
#include <cstdlib> #include <cstdlib>
#include <cstring> #include <cstring>
#include <boost/algorithm/string/join.hpp>
#include <boost/algorithm/string/replace.hpp> #include <boost/algorithm/string/replace.hpp>
#include <boost/algorithm/string/split.hpp>
#include <boost/algorithm/string/trim.hpp> #include <boost/algorithm/string/trim.hpp>
#include <boost/regex.hpp> #include <boost/regex.hpp>
@@ -105,6 +107,15 @@ std::vector<std::string> LyricsFetcher::getContent(const char *regex_, const std
void LyricsFetcher::postProcess(std::string &data) const void LyricsFetcher::postProcess(std::string &data) const
{ {
stripHtmlTags(data); stripHtmlTags(data);
// Remove indentation from each line and collapse multiple newlines into one.
std::vector<std::string> lines;
boost::split(lines, data, boost::is_any_of("\r\n"));
for (auto &line : lines)
boost::trim(line);
std::unique(lines.begin(), lines.end(), [](std::string &a, std::string &b) {
return a.empty() && b.empty();
});
data = boost::algorithm::join(lines, "\n");
boost::trim(data); boost::trim(data);
} }
@@ -126,7 +137,7 @@ LyricsFetcher::Result LyricwikiFetcher::fetch(const std::string &artist, const s
return result; return result;
} }
auto lyrics = getContent("<div class='lyricbox'>(.*?)<!--", data); auto lyrics = getContent("<div class='lyricbox'>(.*?)</div>", data);
if (lyrics.empty()) if (lyrics.empty())
{ {
@@ -224,6 +235,14 @@ void Sing365Fetcher::postProcess(std::string &data) const
/**********************************************************************/ /**********************************************************************/
void JustSomeLyricsFetcher::postProcess(std::string &data) const
{
data = unescapeHtmlUtf8(data);
LyricsFetcher::postProcess(data);
}
/**********************************************************************/
void MetrolyricsFetcher::postProcess(std::string &data) const void MetrolyricsFetcher::postProcess(std::string &data) const
{ {
// some of lyrics have both \n chars and <br />, html tags // some of lyrics have both \n chars and <br />, html tags

View File

@@ -108,7 +108,9 @@ struct JustSomeLyricsFetcher : public GoogleLyricsFetcher
virtual const char *name() const OVERRIDE { return "justsomelyrics.com"; } virtual const char *name() const OVERRIDE { return "justsomelyrics.com"; }
protected: protected:
virtual const char *regex() const OVERRIDE { return "<div class=\"content.*?</div>(.*?)</div>"; } virtual const char *regex() const OVERRIDE { return "<div class=\"content.*?</div>\\s*</div>(.*?)<div"; }
virtual void postProcess(std::string &data) const OVERRIDE;
}; };
struct AzLyricsFetcher : public GoogleLyricsFetcher struct AzLyricsFetcher : public GoogleLyricsFetcher

View File

@@ -353,13 +353,33 @@ template <typename ItemT> struct Menu : Window, List
Iterator current() { return Iterator(m_items.begin() + m_highlight); } Iterator current() { return Iterator(m_items.begin() + m_highlight); }
ConstIterator current() const { return ConstIterator(m_items.begin() + m_highlight); } ConstIterator current() const { return ConstIterator(m_items.begin() + m_highlight); }
ReverseIterator rcurrent() { return ReverseIterator(++current()); } ReverseIterator rcurrent() {
ConstReverseIterator rcurrent() const { return ReverseIterator(++current()); } if (empty())
return rend();
else
return ReverseIterator(++current());
}
ConstReverseIterator rcurrent() const {
if (empty())
return rend();
else
return ConstReverseIterator(++current());
}
ValueIterator currentV() { return ValueIterator(m_items.begin() + m_highlight); } ValueIterator currentV() { return ValueIterator(m_items.begin() + m_highlight); }
ConstValueIterator currentV() const { return ConstValueIterator(m_items.begin() + m_highlight); } ConstValueIterator currentV() const { return ConstValueIterator(m_items.begin() + m_highlight); }
ReverseValueIterator rcurrentV() { return ReverseValueIterator(++currentV()); } ReverseValueIterator rcurrentV() {
ConstReverseValueIterator rcurrentV() const { return ConstReverseValueIterator(++currentV()); } if (empty())
return rendV();
else
return ReverseValueIterator(++currentV());
}
ConstReverseValueIterator rcurrentV() const {
if (empty())
return rendV();
else
return ConstReverseValueIterator(++currentV());
}
Iterator begin() { return Iterator(m_items.begin()); } Iterator begin() { return Iterator(m_items.begin()); }
ConstIterator begin() const { return ConstIterator(m_items.begin()); } ConstIterator begin() const { return ConstIterator(m_items.begin()); }

View File

@@ -561,7 +561,7 @@ void Connection::Add(const std::string &path)
} }
} }
bool Connection::AddRandomTag(mpd_tag_type tag, size_t number) bool Connection::AddRandomTag(mpd_tag_type tag, size_t number, std::mt19937 &rng)
{ {
std::vector<std::string> tags( std::vector<std::string> tags(
std::make_move_iterator(GetList(tag)), std::make_move_iterator(GetList(tag)),
@@ -570,7 +570,7 @@ bool Connection::AddRandomTag(mpd_tag_type tag, size_t number)
if (number > tags.size()) if (number > tags.size())
return false; return false;
std::random_shuffle(tags.begin(), tags.end()); std::shuffle(tags.begin(), tags.end(), rng);
auto it = tags.begin(); auto it = tags.begin();
for (size_t i = 0; i < number && it != tags.end(); ++i) for (size_t i = 0; i < number && it != tags.end(); ++i)
{ {
@@ -588,7 +588,7 @@ bool Connection::AddRandomTag(mpd_tag_type tag, size_t number)
return true; return true;
} }
bool Connection::AddRandomSongs(size_t number) bool Connection::AddRandomSongs(size_t number, std::mt19937 &rng)
{ {
prechecksNoCommandsList(); prechecksNoCommandsList();
std::vector<std::string> files; std::vector<std::string> files;
@@ -609,7 +609,7 @@ bool Connection::AddRandomSongs(size_t number)
} }
else else
{ {
std::random_shuffle(files.begin(), files.end()); std::shuffle(files.begin(), files.end(), rng);
StartCommandsList(); StartCommandsList();
auto it = files.begin(); auto it = files.begin();
for (size_t i = 0; i < number && it != files.end(); ++i, ++it) for (size_t i = 0; i < number && it != files.end(); ++i, ++it)

View File

@@ -23,6 +23,7 @@
#include <cassert> #include <cassert>
#include <exception> #include <exception>
#include <random>
#include <set> #include <set>
#include <vector> #include <vector>
@@ -524,8 +525,8 @@ struct Connection
int AddSong(const std::string &, int = -1); // returns id of added song int AddSong(const std::string &, int = -1); // returns id of added song
int AddSong(const Song &, int = -1); // returns id of added song int AddSong(const Song &, int = -1); // returns id of added song
bool AddRandomTag(mpd_tag_type, size_t); bool AddRandomTag(mpd_tag_type, size_t, std::mt19937 &rng);
bool AddRandomSongs(size_t); bool AddRandomSongs(size_t number, std::mt19937 &rng);
void Add(const std::string &path); void Add(const std::string &path);
void Delete(unsigned int pos); void Delete(unsigned int pos);
void PlaylistDelete(const std::string &playlist, unsigned int pos); void PlaylistDelete(const std::string &playlist, unsigned int pos);

View File

@@ -89,7 +89,6 @@ int main(int argc, char **argv)
using Global::VolumeState; using Global::VolumeState;
using Global::Timer; using Global::Timer;
srand(time(nullptr));
std::setlocale(LC_ALL, ""); std::setlocale(LC_ALL, "");
std::locale::global(Charset::internalLocale()); std::locale::global(Charset::internalLocale());
@@ -131,6 +130,9 @@ int main(int argc, char **argv)
// initialize global timer // initialize global timer
Timer = boost::posix_time::microsec_clock::local_time(); Timer = boost::posix_time::microsec_clock::local_time();
// initialize global random number generator
Global::RNG.seed(std::random_device()());
// initialize playlist // initialize playlist
myPlaylist->switchTo(); myPlaylist->switchTo();

View File

@@ -147,13 +147,23 @@ void PlaylistEditor::update()
{ {
m_playlists_update_requested = false; m_playlists_update_requested = false;
size_t idx = 0; size_t idx = 0;
for (MPD::PlaylistIterator it = Mpd.GetPlaylists(), end; it != end; ++it, ++idx) try
{ {
if (idx < Playlists.size()) for (MPD::PlaylistIterator it = Mpd.GetPlaylists(), end; it != end; ++it, ++idx)
Playlists[idx].value() = std::move(*it); {
if (idx < Playlists.size())
Playlists[idx].value() = std::move(*it);
else
Playlists.addItem(std::move(*it));
};
}
catch (MPD::ServerError &e)
{
if (e.code() == MPD_SERVER_ERROR_SYSTEM) // no playlists directory
Statusbar::print(e.what());
else else
Playlists.addItem(std::move(*it)); throw;
}; }
if (idx < Playlists.size()) if (idx < Playlists.size())
Playlists.resizeList(idx); Playlists.resizeList(idx);
std::sort(Playlists.beginV(), Playlists.endV(), std::sort(Playlists.beginV(), Playlists.endV(),

View File

@@ -160,6 +160,12 @@ std::string adjust_directory(std::string s)
return s; return s;
} }
std::string adjust_path(std::string s)
{
expand_home(s);
return s;
}
// parser worker for buffer // parser worker for buffer
template <typename ValueT, typename TransformT> template <typename ValueT, typename TransformT>
option_parser::worker buffer(NC::Buffer &arg, ValueT &&value, TransformT &&map) option_parser::worker buffer(NC::Buffer &arg, ValueT &&value, TransformT &&map)
@@ -237,8 +243,8 @@ bool Configuration::read(const std::vector<std::string> &config_paths, bool igno
p.add("mpd_crossfade_time", assign_default( p.add("mpd_crossfade_time", assign_default(
crossfade_time, 5 crossfade_time, 5
)); ));
p.add("visualizer_fifo_path", assign_default( p.add("visualizer_fifo_path", assign_default<std::string>(
visualizer_fifo_path, "/tmp/mpd.fifo" visualizer_fifo_path, "/tmp/mpd.fifo", adjust_path
)); ));
p.add("visualizer_output_name", assign_default( p.add("visualizer_output_name", assign_default(
visualizer_output_name, "Visualizer feed" visualizer_output_name, "Visualizer feed"
@@ -372,8 +378,8 @@ bool Configuration::read(const std::vector<std::string> &config_paths, bool igno
song_columns_mode_format = columns_to_format(columns); song_columns_mode_format = columns_to_format(columns);
return v; return v;
})); }));
p.add("execute_on_song_change", assign_default( p.add("execute_on_song_change", assign_default<std::string>(
execute_on_song_change, "" execute_on_song_change, "", adjust_path
)); ));
p.add("execute_on_player_state_change", assign_default( p.add("execute_on_player_state_change", assign_default(
execute_on_player_state_change, "" execute_on_player_state_change, ""
@@ -647,8 +653,8 @@ bool Configuration::read(const std::vector<std::string> &config_paths, bool igno
boundsCheck(v, 1u, 3u); boundsCheck(v, 1u, 3u);
return --v; return --v;
})); }));
p.add("external_editor", assign_default( p.add("external_editor", assign_default<std::string>(
external_editor, "nano" external_editor, "nano", adjust_path
)); ));
p.add("use_console_editor", yes_no( p.add("use_console_editor", yes_no(
use_console_editor, true use_console_editor, true

View File

@@ -197,7 +197,7 @@ void SortPlaylistDialog::sort() const
quick_sort = [this, &song_cmp, &quick_sort, &iter_swap](Iterator first, Iterator last) { quick_sort = [this, &song_cmp, &quick_sort, &iter_swap](Iterator first, Iterator last) {
if (last-first > 1) if (last-first > 1)
{ {
Iterator pivot = first+rand()%(last-first); Iterator pivot = first+Global::RNG()%(last-first);
iter_swap(pivot, last-1); iter_swap(pivot, last-1);
pivot = last-1; pivot = last-1;

View File

@@ -66,7 +66,7 @@ void stripHtmlTags(std::string &s)
for (size_t i = s.find("<"); i != std::string::npos; i = s.find("<")) for (size_t i = s.find("<"); i != std::string::npos; i = s.find("<"))
{ {
size_t j = s.find(">", i)+1; size_t j = s.find(">", i)+1;
if (s.compare(i, j-i, "<p>") == 0 || s.compare(i, j-i, "</p>") == 0) if (s.compare(i, std::min(3ul, j-i), "<p ") == 0 || s.compare(i, j-i, "</p>") == 0)
s.replace(i, j-i, "\n"); s.replace(i, j-i, "\n");
else else
s.replace(i, j-i, ""); s.replace(i, j-i, "");
@@ -87,4 +87,4 @@ void stripHtmlTags(std::string &s)
else if (s[i] == '\t') else if (s[i] == '\t')
s[i] = ' '; s[i] = ' ';
} }
} }