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
85 const MPD::Song s
= myPlaylist
->nowPlayingSong();
86 if (!s
.empty() && !s
.getArtist().empty() && !s
.getTitle().empty())
97 void Lyrics::switchTo()
99 using Global::myScreen
;
100 if (myScreen
!= this)
102 # ifdef HAVE_CURL_CURL_H
103 // take lyrics if they were downloaded
107 if (isDownloadInProgress
|| itsWorkersNumber
> 0)
109 Statusbar::print("Lyrics are being downloaded...");
112 # endif // HAVE_CURL_CURL_H
114 const MPD::Song
*s
= currentSong(myScreen
);
118 if (!s
->getArtist().empty() && !s
->getTitle().empty())
120 SwitchTo::execute(this);
127 Statusbar::print("Song must have both artist and title tag set");
130 switchToPreviousScreen();
133 std::wstring
Lyrics::title()
135 std::wstring result
= L
"Lyrics: ";
136 result
+= Scroller(ToWString(itsSong
.toString("{%a - %t}", ", ")), itsScrollBegin
, COLS
-result
.length()-(Config
.design
== Design::Alternative
? 2 : Global::VolumeState
.length()));
140 void Lyrics::spacePressed()
142 Config
.now_playing_lyrics
= !Config
.now_playing_lyrics
;
143 Statusbar::printf("Reload lyrics if song changes: %1%",
144 Config
.now_playing_lyrics
? "on" : "off"
148 #ifdef HAVE_CURL_CURL_H
149 void Lyrics::DownloadInBackground(const MPD::Song
&s
)
151 if (s
.empty() || s
.getArtist().empty() || s
.getTitle().empty())
154 std::string filename
= GenerateFilename(s
);
155 std::ifstream
f(filename
.c_str());
161 Statusbar::printf("Fetching lyrics for \"%1%\"...",
162 s
.toString(Config
.song_status_format_no_colors
, Config
.tags_separator
)
165 MPD::Song
*s_copy
= new MPD::Song(s
);
166 pthread_mutex_lock(&itsDIBLock
);
167 if (itsWorkersNumber
== itsMaxWorkersNumber
)
168 itsToDownload
.push(s_copy
);
174 pthread_attr_init(&attr
);
175 pthread_attr_setdetachstate(&attr
, PTHREAD_CREATE_DETACHED
);
176 pthread_create(&t
, &attr
, DownloadInBackgroundImpl
, s_copy
);
178 pthread_mutex_unlock(&itsDIBLock
);
181 void *Lyrics::DownloadInBackgroundImpl(void *void_ptr
)
183 MPD::Song
*s
= static_cast<MPD::Song
*>(void_ptr
);
184 DownloadInBackgroundImplHelper(*s
);
189 pthread_mutex_lock(&itsDIBLock
);
190 if (itsToDownload
.empty())
192 pthread_mutex_unlock(&itsDIBLock
);
197 s
= itsToDownload
.front();
199 pthread_mutex_unlock(&itsDIBLock
);
201 DownloadInBackgroundImplHelper(*s
);
205 pthread_mutex_lock(&itsDIBLock
);
207 pthread_mutex_unlock(&itsDIBLock
);
212 void Lyrics::DownloadInBackgroundImplHelper(const MPD::Song
&s
)
214 std::string artist
= Curl::escape(s
.getArtist());
215 std::string title
= Curl::escape(s
.getTitle());
217 LyricsFetcher::Result result
;
218 bool fetcher_defined
= itsFetcher
&& *itsFetcher
;
219 for (LyricsFetcher
**plugin
= fetcher_defined
? itsFetcher
: lyricsPlugins
; *plugin
!= 0; ++plugin
)
221 result
= (*plugin
)->fetch(artist
, title
);
227 if (result
.first
== true)
228 Save(GenerateFilename(s
), result
.second
);
231 void *Lyrics::Download()
233 std::string artist
= Curl::escape(itsSong
.getArtist());
234 std::string title_
= Curl::escape(itsSong
.getTitle());
236 LyricsFetcher::Result result
;
238 // if one of plugins is selected, try only this one,
239 // otherwise try all of them until one of them succeeds
240 bool fetcher_defined
= itsFetcher
&& *itsFetcher
;
241 for (LyricsFetcher
**plugin
= fetcher_defined
? itsFetcher
: lyricsPlugins
; *plugin
!= 0; ++plugin
)
243 w
<< "Fetching lyrics from " << NC::Format::Bold
<< (*plugin
)->name() << NC::Format::NoBold
<< "... ";
244 result
= (*plugin
)->fetch(artist
, title_
);
245 if (result
.first
== false)
246 w
<< NC::Color::Red
<< result
.second
<< NC::Color::End
<< '\n';
253 if (result
.first
== true)
255 Save(itsFilename
, result
.second
);
257 w
<< Charset::utf8ToLocale(result
.second
);
260 w
<< '\n' << "Lyrics weren't found.";
266 void *Lyrics::DownloadWrapper(void *this_ptr
)
268 return static_cast<Lyrics
*>(this_ptr
)->Download();
270 #endif // HAVE_CURL_CURL_H
272 std::string
Lyrics::GenerateFilename(const MPD::Song
&s
)
274 std::string filename
;
275 if (Config
.store_lyrics_in_song_dir
)
277 if (s
.isFromDatabase())
279 filename
= Config
.mpd_music_dir
;
281 filename
+= s
.getURI();
284 filename
= s
.getURI();
285 // replace song's extension with .txt
286 size_t dot
= filename
.rfind('.');
287 assert(dot
!= std::string::npos
);
288 filename
.resize(dot
);
293 std::string file
= s
.getArtist();
295 file
+= s
.getTitle();
297 removeInvalidCharsFromFilename(file
, Config
.generate_win32_compatible_filenames
);
298 filename
= Config
.lyrics_directory
;
307 # ifdef HAVE_CURL_CURL_H
308 if (isDownloadInProgress
)
310 # endif // HAVE_CURL_CURL_H
312 assert(!itsSong
.getArtist().empty());
313 assert(!itsSong
.getTitle().empty());
315 itsFilename
= GenerateFilename(itsSong
);
320 std::ifstream
input(itsFilename
.c_str());
325 while (std::getline(input
, line
))
329 w
<< Charset::utf8ToLocale(line
);
338 # ifdef HAVE_CURL_CURL_H
339 pthread_create(&itsDownloader
, 0, DownloadWrapper
, this);
340 isDownloadInProgress
= 1;
342 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.";
350 assert(Global::myScreen
== this);
352 if (Config
.external_editor
.empty())
354 Statusbar::print("Proper external_editor variable has to be set in configuration file");
358 Statusbar::print("Opening lyrics in external editor...");
361 if (Config
.use_console_editor
)
363 res
= system(("/bin/sh -c \"" + Config
.external_editor
+ " \\\"" + itsFilename
+ "\\\"\"").c_str());
365 // below is needed as screen gets cleared, but apparently
366 // ncurses doesn't know about it, so we need to reload main screen
372 res
= system(("nohup " + Config
.external_editor
+ " \"" + itsFilename
+ "\" > /dev/null 2>&1 &").c_str());
375 #ifdef HAVE_CURL_CURL_H
376 void Lyrics::Save(const std::string
&filename
, const std::string
&lyrics
)
378 std::ofstream
output(filename
.c_str());
379 if (output
.is_open())
386 void Lyrics::Refetch()
388 if (remove(itsFilename
.c_str()) && errno
!= ENOENT
)
390 const char msg
[] = "Couldn't remove \"%1%\": %2%";
391 Statusbar::printf(msg
, wideShorten(itsFilename
, COLS
-const_strlen(msg
)-25), strerror(errno
));
397 void Lyrics::ToggleFetcher()
399 if (itsFetcher
&& *itsFetcher
)
402 itsFetcher
= &lyricsPlugins
[0];
404 Statusbar::printf("Using lyrics database: %s", (*itsFetcher
)->name());
406 Statusbar::print("Using all lyrics databases");
411 assert(isReadyToTake
);
412 pthread_join(itsDownloader
, 0);
415 isDownloadInProgress
= 0;
418 #endif // HAVE_CURL_CURL_H