1 /***************************************************************************
2 * Copyright (C) 2008-2014 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"
33 #include "scrollpad.h"
36 #include "statusbar.h"
38 #include "screen_switcher.h"
39 #include "utility/string.h"
41 using Global::MainHeight
;
42 using Global::MainStartY
;
44 #ifdef HAVE_CURL_CURL_H
45 LyricsFetcher
**Lyrics::itsFetcher
= 0;
46 std::queue
<MPD::Song
*> Lyrics::itsToDownload
;
47 pthread_mutex_t
Lyrics::itsDIBLock
= PTHREAD_MUTEX_INITIALIZER
;
48 size_t Lyrics::itsWorkersNumber
= 0;
49 #endif // HAVE_CURL_CURL_H
54 : Screen(NC::Scrollpad(0, MainStartY
, COLS
, MainHeight
, "", Config
.main_color
, NC::Border::None
))
56 #ifdef HAVE_CURL_CURL_H
57 isReadyToTake(0), isDownloadInProgress(0),
58 #endif // HAVE_CURL_CURL_H
64 size_t x_offset
, width
;
65 getWindowResizeParams(x_offset
, width
);
66 w
.resize(width
, MainHeight
);
67 w
.moveTo(x_offset
, MainStartY
);
73 # ifdef HAVE_CURL_CURL_H
77 if (isDownloadInProgress
)
82 # endif // HAVE_CURL_CURL_H
92 void Lyrics::switchTo()
94 using Global::myScreen
;
97 # ifdef HAVE_CURL_CURL_H
98 // take lyrics if they were downloaded
102 if (isDownloadInProgress
|| itsWorkersNumber
> 0)
104 Statusbar::print("Lyrics are being downloaded...");
107 # endif // HAVE_CURL_CURL_H
109 auto s
= currentSong(myScreen
);
115 SwitchTo::execute(this);
121 Statusbar::print("Song must have both artist and title tag set");
124 switchToPreviousScreen();
127 std::wstring
Lyrics::title()
129 std::wstring result
= L
"Lyrics: ";
130 result
+= Scroller(ToWString(itsSong
.toString("{%a - %t}", ", ")), itsScrollBegin
, COLS
-result
.length()-(Config
.design
== Design::Alternative
? 2 : Global::VolumeState
.length()));
134 void Lyrics::spacePressed()
136 Config
.now_playing_lyrics
= !Config
.now_playing_lyrics
;
137 Statusbar::printf("Reload lyrics if song changes: %1%",
138 Config
.now_playing_lyrics
? "on" : "off"
142 #ifdef HAVE_CURL_CURL_H
143 void Lyrics::DownloadInBackground(const MPD::Song
&s
)
145 if (s
.empty() || s
.getArtist().empty() || s
.getTitle().empty())
148 std::string filename
= GenerateFilename(s
);
149 std::ifstream
f(filename
.c_str());
155 Statusbar::printf("Fetching lyrics for \"%1%\"...",
156 s
.toString(Config
.song_status_format_no_colors
, Config
.tags_separator
)
159 MPD::Song
*s_copy
= new MPD::Song(s
);
160 pthread_mutex_lock(&itsDIBLock
);
161 if (itsWorkersNumber
== itsMaxWorkersNumber
)
162 itsToDownload
.push(s_copy
);
168 pthread_attr_init(&attr
);
169 pthread_attr_setdetachstate(&attr
, PTHREAD_CREATE_DETACHED
);
170 pthread_create(&t
, &attr
, DownloadInBackgroundImpl
, s_copy
);
172 pthread_mutex_unlock(&itsDIBLock
);
175 void *Lyrics::DownloadInBackgroundImpl(void *void_ptr
)
177 MPD::Song
*s
= static_cast<MPD::Song
*>(void_ptr
);
178 DownloadInBackgroundImplHelper(*s
);
183 pthread_mutex_lock(&itsDIBLock
);
184 if (itsToDownload
.empty())
186 pthread_mutex_unlock(&itsDIBLock
);
191 s
= itsToDownload
.front();
193 pthread_mutex_unlock(&itsDIBLock
);
195 DownloadInBackgroundImplHelper(*s
);
199 pthread_mutex_lock(&itsDIBLock
);
201 pthread_mutex_unlock(&itsDIBLock
);
206 void Lyrics::DownloadInBackgroundImplHelper(const MPD::Song
&s
)
208 std::string artist
= Curl::escape(s
.getArtist());
209 std::string title
= Curl::escape(s
.getTitle());
211 LyricsFetcher::Result result
;
212 bool fetcher_defined
= itsFetcher
&& *itsFetcher
;
213 for (LyricsFetcher
**plugin
= fetcher_defined
? itsFetcher
: lyricsPlugins
; *plugin
!= 0; ++plugin
)
215 result
= (*plugin
)->fetch(artist
, title
);
221 if (result
.first
== true)
222 Save(GenerateFilename(s
), result
.second
);
225 void *Lyrics::Download()
227 std::string artist
= Curl::escape(itsSong
.getArtist());
228 std::string title_
= Curl::escape(itsSong
.getTitle());
230 LyricsFetcher::Result result
;
232 // if one of plugins is selected, try only this one,
233 // otherwise try all of them until one of them succeeds
234 bool fetcher_defined
= itsFetcher
&& *itsFetcher
;
235 for (LyricsFetcher
**plugin
= fetcher_defined
? itsFetcher
: lyricsPlugins
; *plugin
!= 0; ++plugin
)
237 w
<< "Fetching lyrics from " << NC::Format::Bold
<< (*plugin
)->name() << NC::Format::NoBold
<< "... ";
238 result
= (*plugin
)->fetch(artist
, title_
);
239 if (result
.first
== false)
240 w
<< NC::Color::Red
<< result
.second
<< NC::Color::End
<< '\n';
247 if (result
.first
== true)
249 Save(itsFilename
, result
.second
);
251 w
<< Charset::utf8ToLocale(result
.second
);
254 w
<< '\n' << "Lyrics weren't found.";
260 void *Lyrics::DownloadWrapper(void *this_ptr
)
262 return static_cast<Lyrics
*>(this_ptr
)->Download();
264 #endif // HAVE_CURL_CURL_H
266 std::string
Lyrics::GenerateFilename(const MPD::Song
&s
)
268 std::string filename
;
269 if (Config
.store_lyrics_in_song_dir
)
271 if (s
.isFromDatabase())
273 filename
= Config
.mpd_music_dir
;
275 filename
+= s
.getURI();
278 filename
= s
.getURI();
279 // replace song's extension with .txt
280 size_t dot
= filename
.rfind('.');
281 assert(dot
!= std::string::npos
);
282 filename
.resize(dot
);
287 std::string file
= s
.getArtist();
289 file
+= s
.getTitle();
291 removeInvalidCharsFromFilename(file
, Config
.generate_win32_compatible_filenames
);
292 filename
= Config
.lyrics_directory
;
301 # ifdef HAVE_CURL_CURL_H
302 if (isDownloadInProgress
)
304 # endif // HAVE_CURL_CURL_H
306 assert(!itsSong
.getArtist().empty());
307 assert(!itsSong
.getTitle().empty());
309 itsFilename
= GenerateFilename(itsSong
);
314 std::ifstream
input(itsFilename
.c_str());
319 while (std::getline(input
, line
))
323 w
<< Charset::utf8ToLocale(line
);
332 # ifdef HAVE_CURL_CURL_H
333 pthread_create(&itsDownloader
, 0, DownloadWrapper
, this);
334 isDownloadInProgress
= 1;
336 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.";
344 assert(Global::myScreen
== this);
346 if (Config
.external_editor
.empty())
348 Statusbar::print("Proper external_editor variable has to be set in configuration file");
352 Statusbar::print("Opening lyrics in external editor...");
355 if (Config
.use_console_editor
)
357 res
= system(("/bin/sh -c \"" + Config
.external_editor
+ " \\\"" + itsFilename
+ "\\\"\"").c_str());
359 // below is needed as screen gets cleared, but apparently
360 // ncurses doesn't know about it, so we need to reload main screen
366 res
= system(("nohup " + Config
.external_editor
+ " \"" + itsFilename
+ "\" > /dev/null 2>&1 &").c_str());
369 bool Lyrics::SetSong(const MPD::Song
&s
)
371 if (!s
.getArtist().empty() && !s
.getTitle().empty())
380 #ifdef HAVE_CURL_CURL_H
381 void Lyrics::Save(const std::string
&filename
, const std::string
&lyrics
)
383 std::ofstream
output(filename
.c_str());
384 if (output
.is_open())
391 void Lyrics::Refetch()
393 if (remove(itsFilename
.c_str()) && errno
!= ENOENT
)
395 const char msg
[] = "Couldn't remove \"%1%\": %2%";
396 Statusbar::printf(msg
, wideShorten(itsFilename
, COLS
-const_strlen(msg
)-25), strerror(errno
));
402 void Lyrics::ToggleFetcher()
404 if (itsFetcher
&& *itsFetcher
)
407 itsFetcher
= &lyricsPlugins
[0];
409 Statusbar::printf("Using lyrics database: %s", (*itsFetcher
)->name());
411 Statusbar::print("Using all lyrics databases");
416 assert(isReadyToTake
);
417 pthread_join(itsDownloader
, 0);
420 isDownloadInProgress
= 0;
423 #endif // HAVE_CURL_CURL_H