1 /***************************************************************************
2 * Copyright (C) 2008-2016 by Andrzej Rybczak *
3 * electricityispower@gmail.com *
5 * This program is free software; you can redistribute it and/or modify *
6 * it under the terms of the GNU General Public License as published by *
7 * the Free Software Foundation; either version 2 of the License, or *
8 * (at your option) any later version. *
10 * This program is distributed in the hope that it will be useful, *
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 * GNU General Public License for more details. *
15 * You should have received a copy of the GNU General Public License *
16 * along with this program; if not, write to the *
17 * Free Software Foundation, Inc., *
18 * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. *
19 ***************************************************************************/
28 #include "curl_handle.h"
29 #include "format_impl.h"
34 #include "scrollpad.h"
37 #include "statusbar.h"
39 #include "screen_switcher.h"
40 #include "utility/string.h"
42 using Global::MainHeight
;
43 using Global::MainStartY
;
45 #ifdef HAVE_CURL_CURL_H
46 LyricsFetcher
*Lyrics::itsFetcher
= nullptr;
47 std::queue
<MPD::Song
*> Lyrics::itsToDownload
;
48 pthread_mutex_t
Lyrics::itsDIBLock
= PTHREAD_MUTEX_INITIALIZER
;
49 size_t Lyrics::itsWorkersNumber
= 0;
50 #endif // HAVE_CURL_CURL_H
55 : Screen(NC::Scrollpad(0, MainStartY
, COLS
, MainHeight
, "", Config
.main_color
, NC::Border()))
57 #ifdef HAVE_CURL_CURL_H
58 isReadyToTake(0), isDownloadInProgress(0),
59 #endif // HAVE_CURL_CURL_H
65 size_t x_offset
, width
;
66 getWindowResizeParams(x_offset
, width
);
67 w
.resize(width
, MainHeight
);
68 w
.moveTo(x_offset
, MainStartY
);
74 # ifdef HAVE_CURL_CURL_H
78 if (isDownloadInProgress
)
83 # endif // HAVE_CURL_CURL_H
93 void Lyrics::switchTo()
95 using Global::myScreen
;
98 # ifdef HAVE_CURL_CURL_H
99 // take lyrics if they were downloaded
103 if (isDownloadInProgress
|| itsWorkersNumber
> 0)
105 Statusbar::print("Lyrics are being downloaded...");
108 # endif // HAVE_CURL_CURL_H
110 auto s
= currentSong(myScreen
);
116 SwitchTo::execute(this);
122 Statusbar::print("Song must have both artist and title tag set");
125 switchToPreviousScreen();
128 std::wstring
Lyrics::title()
130 std::wstring result
= L
"Lyrics: ";
133 Format::stringify
<wchar_t>(Format::parse(L
"%a - %t"), &itsSong
),
135 COLS
-result
.length()-(Config
.design
== Design::Alternative
? 2 : Global::VolumeState
.length())
140 #ifdef HAVE_CURL_CURL_H
141 void Lyrics::DownloadInBackground(const MPD::Song
&s
)
143 if (s
.empty() || s
.getArtist().empty() || s
.getTitle().empty())
146 std::string filename
= GenerateFilename(s
);
147 std::ifstream
f(filename
.c_str());
153 Statusbar::printf("Fetching lyrics for \"%1%\"...",
154 Format::stringify
<char>(Config
.song_status_format
, &s
)
157 MPD::Song
*s_copy
= new MPD::Song(s
);
158 pthread_mutex_lock(&itsDIBLock
);
159 if (itsWorkersNumber
== itsMaxWorkersNumber
)
160 itsToDownload
.push(s_copy
);
166 pthread_attr_init(&attr
);
167 pthread_attr_setdetachstate(&attr
, PTHREAD_CREATE_DETACHED
);
168 pthread_create(&t
, &attr
, DownloadInBackgroundImpl
, s_copy
);
170 pthread_mutex_unlock(&itsDIBLock
);
173 void *Lyrics::DownloadInBackgroundImpl(void *void_ptr
)
175 MPD::Song
*s
= static_cast<MPD::Song
*>(void_ptr
);
176 DownloadInBackgroundImplHelper(*s
);
181 pthread_mutex_lock(&itsDIBLock
);
182 if (itsToDownload
.empty())
184 pthread_mutex_unlock(&itsDIBLock
);
189 s
= itsToDownload
.front();
191 pthread_mutex_unlock(&itsDIBLock
);
193 DownloadInBackgroundImplHelper(*s
);
197 pthread_mutex_lock(&itsDIBLock
);
199 pthread_mutex_unlock(&itsDIBLock
);
204 void Lyrics::DownloadInBackgroundImplHelper(const MPD::Song
&s
)
206 std::string artist
= Curl::escape(s
.getArtist());
207 std::string title
= Curl::escape(s
.getTitle());
209 LyricsFetcher::Result result
;
211 if (itsFetcher
== nullptr)
213 for (auto &fetcher
: Config
.lyrics_fetchers
)
215 result
= fetcher
->fetch(artist
, title
);
221 itsFetcher
->fetch(artist
, title
);
224 Save(GenerateFilename(s
), result
.second
);
227 void *Lyrics::Download()
229 std::string artist
= Curl::escape(itsSong
.getArtist());
230 std::string title_
= Curl::escape(itsSong
.getTitle());
232 auto fetch_lyrics
= [&](auto &fetcher
) {
233 w
<< "Fetching lyrics from "
236 << NC::Format::NoBold
<< "... ";
237 auto result
= fetcher
->fetch(artist
, title_
);
238 if (result
.first
== false)
248 LyricsFetcher::Result result
;
250 if (itsFetcher
== nullptr)
252 for (auto &fetcher
: Config
.lyrics_fetchers
)
254 result
= fetch_lyrics(fetcher
);
260 result
= fetch_lyrics(itsFetcher
);
264 Save(itsFilename
, result
.second
);
266 w
<< Charset::utf8ToLocale(result
.second
);
269 w
<< '\n' << "Lyrics weren't found.";
275 void *Lyrics::DownloadWrapper(void *this_ptr
)
277 return static_cast<Lyrics
*>(this_ptr
)->Download();
279 #endif // HAVE_CURL_CURL_H
281 std::string
Lyrics::GenerateFilename(const MPD::Song
&s
)
283 std::string filename
;
284 if (Config
.store_lyrics_in_song_dir
)
286 if (s
.isFromDatabase())
288 filename
= Config
.mpd_music_dir
;
290 filename
+= s
.getURI();
293 filename
= s
.getURI();
294 // replace song's extension with .txt
295 size_t dot
= filename
.rfind('.');
296 assert(dot
!= std::string::npos
);
297 filename
.resize(dot
);
302 std::string file
= s
.getArtist();
304 file
+= s
.getTitle();
306 removeInvalidCharsFromFilename(file
, Config
.generate_win32_compatible_filenames
);
307 filename
= Config
.lyrics_directory
;
316 # ifdef HAVE_CURL_CURL_H
317 if (isDownloadInProgress
)
319 # endif // HAVE_CURL_CURL_H
321 assert(!itsSong
.getArtist().empty());
322 assert(!itsSong
.getTitle().empty());
324 itsFilename
= GenerateFilename(itsSong
);
329 std::ifstream
input(itsFilename
.c_str());
334 while (std::getline(input
, line
))
336 // Remove carriage returns as they mess up the display.
337 line
.erase(std::remove(line
.begin(), line
.end(), '\r'), line
.end());
340 w
<< Charset::utf8ToLocale(line
);
349 # ifdef HAVE_CURL_CURL_H
350 pthread_create(&itsDownloader
, 0, DownloadWrapper
, this);
351 isDownloadInProgress
= 1;
353 w
<< "Local lyrics not found. As ncmpcpp has been compiled without curl support, you can put appropriate lyrics into " << Config
.lyrics_directory
<< " directory (file syntax is \"$ARTIST - $TITLE.txt\") or recompile ncmpcpp with curl support.";
361 assert(Global::myScreen
== this);
363 if (Config
.external_editor
.empty())
365 Statusbar::print("Proper external_editor variable has to be set in configuration file");
369 Statusbar::print("Opening lyrics in external editor...");
372 if (Config
.use_console_editor
)
374 res
= system(("/bin/sh -c \"" + Config
.external_editor
+ " \\\"" + itsFilename
+ "\\\"\"").c_str());
376 // below is needed as screen gets cleared, but apparently
377 // ncurses doesn't know about it, so we need to reload main screen
383 res
= system(("nohup " + Config
.external_editor
+ " \"" + itsFilename
+ "\" > /dev/null 2>&1 &").c_str());
386 bool Lyrics::SetSong(const MPD::Song
&s
)
388 if (!s
.getArtist().empty() && !s
.getTitle().empty())
397 #ifdef HAVE_CURL_CURL_H
398 void Lyrics::Save(const std::string
&filename
, const std::string
&lyrics
)
400 std::ofstream
output(filename
.c_str());
401 if (output
.is_open())
408 void Lyrics::Refetch()
410 if (remove(itsFilename
.c_str()) && errno
!= ENOENT
)
412 const char msg
[] = "Couldn't remove \"%1%\": %2%";
413 Statusbar::printf(msg
, wideShorten(itsFilename
, COLS
-const_strlen(msg
)-25), strerror(errno
));
419 void Lyrics::ToggleFetcher()
421 if (itsFetcher
!= nullptr)
423 auto fetcher
= std::find_if(Config
.lyrics_fetchers
.begin(),
424 Config
.lyrics_fetchers
.end(),
425 [](auto &f
) { return f
.get() == itsFetcher
; });
426 assert(fetcher
!= Config
.lyrics_fetchers
.end());
428 if (fetcher
!= Config
.lyrics_fetchers
.end())
429 itsFetcher
= fetcher
->get();
431 itsFetcher
= nullptr;
435 assert(!Config
.lyrics_fetchers
.empty());
436 itsFetcher
= Config
.lyrics_fetchers
[0].get();
439 if (itsFetcher
!= nullptr)
440 Statusbar::printf("Using lyrics fetcher: %s", itsFetcher
->name());
442 Statusbar::print("Using all lyrics fetchers");
447 assert(isReadyToTake
);
448 pthread_join(itsDownloader
, 0);
451 isDownloadInProgress
= 0;
454 #endif // HAVE_CURL_CURL_H