1 /***************************************************************************
2 * Copyright (C) 2008-2013 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::msg("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::msg("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
.new_design
? 2 : Global::VolumeState
.length()));
140 void Lyrics::spacePressed()
142 Config
.now_playing_lyrics
= !Config
.now_playing_lyrics
;
143 Statusbar::msg("Reload lyrics if song changes: %s", Config
.now_playing_lyrics
? "On" : "Off");
146 #ifdef HAVE_CURL_CURL_H
147 void Lyrics::DownloadInBackground(const MPD::Song
&s
)
149 if (s
.empty() || s
.getArtist().empty() || s
.getTitle().empty())
152 std::string filename
= GenerateFilename(s
);
153 std::ifstream
f(filename
.c_str());
159 Statusbar::msg("Fetching lyrics for \"%s\"...", s
.toString(Config
.song_status_format_no_colors
, Config
.tags_separator
).c_str());
161 MPD::Song
*s_copy
= new MPD::Song(s
);
162 pthread_mutex_lock(&itsDIBLock
);
163 if (itsWorkersNumber
== itsMaxWorkersNumber
)
164 itsToDownload
.push(s_copy
);
170 pthread_attr_init(&attr
);
171 pthread_attr_setdetachstate(&attr
, PTHREAD_CREATE_DETACHED
);
172 pthread_create(&t
, &attr
, DownloadInBackgroundImpl
, s_copy
);
174 pthread_mutex_unlock(&itsDIBLock
);
177 void *Lyrics::DownloadInBackgroundImpl(void *void_ptr
)
179 MPD::Song
*s
= static_cast<MPD::Song
*>(void_ptr
);
180 DownloadInBackgroundImplHelper(*s
);
185 pthread_mutex_lock(&itsDIBLock
);
186 if (itsToDownload
.empty())
188 pthread_mutex_unlock(&itsDIBLock
);
193 s
= itsToDownload
.front();
195 pthread_mutex_unlock(&itsDIBLock
);
197 DownloadInBackgroundImplHelper(*s
);
201 pthread_mutex_lock(&itsDIBLock
);
203 pthread_mutex_unlock(&itsDIBLock
);
208 void Lyrics::DownloadInBackgroundImplHelper(const MPD::Song
&s
)
210 std::string artist
= Curl::escape(s
.getArtist());
211 std::string title
= Curl::escape(s
.getTitle());
213 LyricsFetcher::Result result
;
214 bool fetcher_defined
= itsFetcher
&& *itsFetcher
;
215 for (LyricsFetcher
**plugin
= fetcher_defined
? itsFetcher
: lyricsPlugins
; *plugin
!= 0; ++plugin
)
217 result
= (*plugin
)->fetch(artist
, title
);
223 if (result
.first
== true)
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 LyricsFetcher::Result result
;
234 // if one of plugins is selected, try only this one,
235 // otherwise try all of them until one of them succeeds
236 bool fetcher_defined
= itsFetcher
&& *itsFetcher
;
237 for (LyricsFetcher
**plugin
= fetcher_defined
? itsFetcher
: lyricsPlugins
; *plugin
!= 0; ++plugin
)
239 w
<< "Fetching lyrics from " << NC::Format::Bold
<< (*plugin
)->name() << NC::Format::NoBold
<< "... ";
240 result
= (*plugin
)->fetch(artist
, title_
);
241 if (result
.first
== false)
242 w
<< NC::Color::Red
<< result
.second
<< NC::Color::End
<< '\n';
249 if (result
.first
== true)
251 Save(itsFilename
, result
.second
);
253 w
<< Charset::utf8ToLocale(result
.second
);
256 w
<< '\n' << "Lyrics weren't found.";
262 void *Lyrics::DownloadWrapper(void *this_ptr
)
264 return static_cast<Lyrics
*>(this_ptr
)->Download();
266 #endif // HAVE_CURL_CURL_H
268 std::string
Lyrics::GenerateFilename(const MPD::Song
&s
)
270 std::string filename
;
271 if (Config
.store_lyrics_in_song_dir
)
273 if (s
.isFromDatabase())
275 filename
= Config
.mpd_music_dir
;
277 filename
+= s
.getURI();
280 filename
= s
.getURI();
281 // replace song's extension with .txt
282 size_t dot
= filename
.rfind('.');
283 assert(dot
!= std::string::npos
);
284 filename
.resize(dot
);
289 std::string file
= s
.getArtist();
291 file
+= s
.getTitle();
293 removeInvalidCharsFromFilename(file
, Config
.generate_win32_compatible_filenames
);
294 filename
= Config
.lyrics_directory
;
303 # ifdef HAVE_CURL_CURL_H
304 if (isDownloadInProgress
)
306 # endif // HAVE_CURL_CURL_H
308 assert(!itsSong
.getArtist().empty());
309 assert(!itsSong
.getTitle().empty());
311 itsFilename
= GenerateFilename(itsSong
);
313 CreateDir(Config
.lyrics_directory
);
318 std::ifstream
input(itsFilename
.c_str());
323 while (std::getline(input
, line
))
327 w
<< Charset::utf8ToLocale(line
);
336 # ifdef HAVE_CURL_CURL_H
337 pthread_create(&itsDownloader
, 0, DownloadWrapper
, this);
338 isDownloadInProgress
= 1;
340 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.";
348 assert(Global::myScreen
== this);
350 if (Config
.external_editor
.empty())
352 Statusbar::msg("Proper external_editor variable has to be set in configuration file");
356 Statusbar::msg("Opening lyrics in external editor...");
359 if (Config
.use_console_editor
)
361 res
= system(("/bin/sh -c \"" + Config
.external_editor
+ " \\\"" + itsFilename
+ "\\\"\"").c_str());
363 // below is needed as screen gets cleared, but apparently
364 // ncurses doesn't know about it, so we need to reload main screen
370 res
= system(("nohup " + Config
.external_editor
+ " \"" + itsFilename
+ "\" > /dev/null 2>&1 &").c_str());
373 #ifdef HAVE_CURL_CURL_H
374 void Lyrics::Save(const std::string
&filename
, const std::string
&lyrics
)
376 std::ofstream
output(filename
.c_str());
377 if (output
.is_open())
384 void Lyrics::Refetch()
386 if (remove(itsFilename
.c_str()) && errno
!= ENOENT
)
388 const char msg
[] = "Couldn't remove \"%ls\": %s";
389 Statusbar::msg(msg
, wideShorten(ToWString(itsFilename
), COLS
-const_strlen(msg
)-25).c_str(), strerror(errno
));
395 void Lyrics::ToggleFetcher()
397 if (itsFetcher
&& *itsFetcher
)
400 itsFetcher
= &lyricsPlugins
[0];
402 Statusbar::msg("Using lyrics database: %s", (*itsFetcher
)->name());
404 Statusbar::msg("Using all lyrics databases");
409 assert(isReadyToTake
);
410 pthread_join(itsDownloader
, 0);
413 isDownloadInProgress
= 0;
416 #endif // HAVE_CURL_CURL_H