discard column colors if item is selected (optional)
[ncmpcpp.git] / src / info.cpp
blob279935e1fc9dbe81d21f577fa3f3b51164c024d5
1 /***************************************************************************
2 * Copyright (C) 2008-2009 by Andrzej Rybczak *
3 * electricityispower@gmail.com *
4 * *
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. *
9 * *
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. *
14 * *
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 ***************************************************************************/
21 #include "info.h"
23 #ifdef HAVE_CURL_CURL_H
24 # include <fstream>
25 # ifdef WIN32
26 # include <io.h>
27 # else
28 # include <sys/stat.h>
29 # endif // WIN32
30 # include "curl/curl.h"
31 # include "helpers.h"
32 #endif
34 #include "browser.h"
35 #include "charset.h"
36 #include "global.h"
37 #include "media_library.h"
38 #include "playlist.h"
39 #include "playlist_editor.h"
40 #include "search_engine.h"
41 #include "status.h"
42 #include "tag_editor.h"
44 using Global::MainHeight;
45 using Global::MainStartY;
46 using Global::myScreen;
47 using Global::myOldScreen;
49 #ifdef HAVE_CURL_CURL_H
50 const std::string Info::Folder = home_path + HOME_FOLDER"artists";
51 bool Info::ArtistReady = 0;
53 #ifdef HAVE_PTHREAD_H
54 pthread_t *Info::Downloader = 0;
55 #endif // HAVE_PTHREAD_H
57 #endif // HAVE_CURL_CURL_H
59 Info *myInfo = new Info;
61 const Info::Metadata Info::Tags[] =
63 { "Title", &MPD::Song::GetTitle, &MPD::Song::SetTitle },
64 { "Artist", &MPD::Song::GetArtist, &MPD::Song::SetArtist },
65 { "Album Artist", &MPD::Song::GetAlbumArtist, &MPD::Song::SetAlbumArtist },
66 { "Album", &MPD::Song::GetAlbum, &MPD::Song::SetAlbum },
67 { "Year", &MPD::Song::GetDate, &MPD::Song::SetDate },
68 { "Track", &MPD::Song::GetTrack, &MPD::Song::SetTrack },
69 { "Genre", &MPD::Song::GetGenre, &MPD::Song::SetGenre },
70 { "Composer", &MPD::Song::GetComposer, &MPD::Song::SetComposer },
71 { "Performer", &MPD::Song::GetPerformer, &MPD::Song::SetPerformer },
72 { "Disc", &MPD::Song::GetDisc, &MPD::Song::SetDisc },
73 { "Comment", &MPD::Song::GetComment, &MPD::Song::SetComment },
74 { 0, 0, 0 }
77 void Info::Init()
79 w = new Scrollpad(0, MainStartY, COLS, MainHeight, "", Config.main_color, brNone);
80 isInitialized = 1;
83 void Info::Resize()
85 w->Resize(COLS, MainHeight);
86 w->MoveTo(0, MainStartY);
87 hasToBeResized = 0;
90 std::basic_string<my_char_t> Info::Title()
92 return TO_WSTRING(itsTitle);
95 #if defined(HAVE_CURL_CURL_H) && defined(HAVE_PTHREAD_H)
96 void Info::Update()
98 if (!ArtistReady)
99 return;
100 pthread_join(*Downloader, 0);
101 w->Flush();
102 w->Refresh();
103 delete Downloader;
104 Downloader = 0;
105 ArtistReady = 0;
107 #endif // HAVE_CURL_CURL_H && HAVE_PTHREAD_H
109 void Info::GetSong()
111 if (myScreen == this)
113 myOldScreen->SwitchTo();
115 else
117 if (!isInitialized)
118 Init();
120 MPD::Song *s = myScreen->CurrentSong();
122 if (!s)
123 return;
125 if (hasToBeResized)
126 Resize();
128 myOldScreen = myScreen;
129 myScreen = this;
130 Global::RedrawHeader = 1;
131 itsTitle = "Song info";
132 w->Clear();
133 w->Reset();
134 PrepareSong(*s);
135 w->Window::Clear();
136 w->Flush();
140 #ifdef HAVE_CURL_CURL_H
142 void Info::GetArtist()
144 if (myScreen == this)
146 myOldScreen->SwitchTo();
148 else
150 if (!isInitialized)
151 Init();
153 # ifdef HAVE_PTHREAD_H
154 if (Downloader && !ArtistReady)
156 ShowMessage("Artist info is being downloaded...");
157 return;
159 else if (ArtistReady)
160 Update();
161 # endif // HAVE_PTHREAD_H
163 MPD::Song *s = myScreen->CurrentSong();
165 if (!s && myScreen->ActiveWindow() != myLibrary->Artists)
166 return;
168 itsArtist = !s ? myLibrary->Artists->Current() : s->GetArtist();
170 if (itsArtist.empty())
171 return;
173 if (hasToBeResized)
174 Resize();
175 myOldScreen = myScreen;
176 myScreen = this;
177 Global::RedrawHeader = 1;
178 itsTitle = "Artist info - " + itsArtist;
179 w->Clear();
180 w->Reset();
181 static_cast<Window &>(*w) << "Fetching artist info...";
182 w->Window::Refresh();
183 # ifdef HAVE_PTHREAD_H
184 if (!Downloader)
185 # endif // HAVE_PTHREAD_H
187 locale_to_utf(itsArtist);
189 std::string file = itsArtist + ".txt";
190 ToLower(file);
191 EscapeUnallowedChars(file);
193 itsFilenamePath = Folder + "/" + file;
195 mkdir(Folder.c_str()
196 # ifndef WIN32
197 , 0755
198 # endif // !WIN32
201 std::ifstream input(itsFilenamePath.c_str());
202 if (input.is_open())
204 bool first = 1;
205 std::string line;
206 while (getline(input, line))
208 if (!first)
209 *w << "\n";
210 utf_to_locale(line);
211 *w << line;
212 first = 0;
214 input.close();
215 w->SetFormatting(fmtBold, U("\n\nSimilar artists:\n"), fmtBoldEnd, 0);
216 w->SetFormatting(Config.color2, U("\n * "), clEnd, 1);
217 // below is used so format won't be removed using RemoveFormatting() by accident.
218 w->ForgetFormatting();
219 w->Flush();
221 else
223 # ifdef HAVE_PTHREAD_H
224 Downloader = new pthread_t;
225 pthread_create(Downloader, 0, PrepareArtist, this);
226 # else
227 PrepareArtist(this);
228 w->Flush();
229 # endif // HAVE_PTHREAD_H
235 void *Info::PrepareArtist(void *screen_void_ptr)
237 Info *screen = static_cast<Info *>(screen_void_ptr);
239 char *c_artist = curl_easy_escape(0, screen->itsArtist.c_str(), screen->itsArtist.length());
240 std::string url = "http://ws.audioscrobbler.com/2.0/?method=artist.getinfo&artist=";
241 url += c_artist;
242 url += "&api_key=d94e5b6e26469a2d1ffae8ef20131b79";
244 std::string result;
245 CURLcode code;
247 pthread_mutex_lock(&Global::CurlLock);
248 CURL *info = curl_easy_init();
249 curl_easy_setopt(info, CURLOPT_URL, url.c_str());
250 curl_easy_setopt(info, CURLOPT_WRITEFUNCTION, write_data);
251 curl_easy_setopt(info, CURLOPT_WRITEDATA, &result);
252 curl_easy_setopt(info, CURLOPT_CONNECTTIMEOUT, 10);
253 curl_easy_setopt(info, CURLOPT_NOSIGNAL, 1);
254 code = curl_easy_perform(info);
255 curl_easy_cleanup(info);
256 pthread_mutex_unlock(&Global::CurlLock);
258 curl_free(c_artist);
260 if (code != CURLE_OK)
262 *screen->w << "Error while fetching artist's info: " << curl_easy_strerror(code);
263 ArtistReady = 1;
264 pthread_exit(0);
267 size_t a, b;
268 bool save = 1;
270 a = result.find("status=\"failed\"");
272 if (a != std::string::npos)
274 EscapeHtml(result);
275 *screen->w << "Last.fm returned an error message: " << result;
276 ArtistReady = 1;
277 pthread_exit(0);
280 std::vector<std::string> similar;
281 for (size_t i = result.find("<name>"); i != std::string::npos; i = result.find("<name>"))
283 result[i] = '.';
284 size_t j = result.find("</name>");
285 result[j] = '.';
286 i += static_strlen("<name>");
287 similar.push_back(result.substr(i, j-i));
288 EscapeHtml(similar.back());
290 std::vector<std::string> urls;
291 for (size_t i = result.find("<url>"); i != std::string::npos; i = result.find("<url>"))
293 result[i] = '.';
294 size_t j = result.find("</url>");
295 result[j] = '.';
296 i += static_strlen("<url>");
297 urls.push_back(result.substr(i, j-i));
300 bool parse_failed = 0;
301 if ((a = result.find("<content>")) != std::string::npos)
303 a += static_strlen("<content>");
304 if ((b = result.find("</content>")) == std::string::npos)
305 parse_failed = 1;
307 else
308 parse_failed = 1;
309 if (parse_failed)
311 *screen->w << "Error: Fetched data could not be parsed";
312 ArtistReady = 1;
313 pthread_exit(0);
316 if (a == b)
318 result = "No description available for this artist.";
319 save = 0;
321 else
323 a += static_strlen("<![CDATA[");
324 b -= static_strlen("]]>");
325 result = result.substr(a, b-a);
328 EscapeHtml(result);
329 Trim(result);
331 std::ostringstream filebuffer;
332 if (save)
333 filebuffer << result;
334 utf_to_locale(result);
335 *screen->w << result;
337 if (save)
338 filebuffer << "\n\nSimilar artists:\n";
339 *screen->w << fmtBold << "\n\nSimilar artists:\n" << fmtBoldEnd;
340 for (size_t i = 1; i < similar.size(); ++i)
342 if (save)
343 filebuffer << "\n * " << similar[i] << " (" << urls[i] << ")";
344 utf_to_locale(similar[i]);
345 utf_to_locale(urls[i]);
346 *screen->w << "\n" << Config.color2 << " * " << clEnd << similar[i] << " (" << urls[i] << ")";
349 if (save)
350 filebuffer << "\n\n" << urls.front();
351 utf_to_locale(urls.front());
352 *screen->w << "\n\n" << urls.front();
354 if (save)
356 std::ofstream output(screen->itsFilenamePath.c_str());
357 if (output.is_open())
359 output << filebuffer.str();
360 output.close();
363 ArtistReady = 1;
364 pthread_exit(0);
366 #endif // HVAE_CURL_CURL_H
368 void Info::PrepareSong(MPD::Song &s)
370 # ifdef HAVE_TAGLIB_H
371 std::string path_to_file;
372 if (s.isFromDB())
373 path_to_file += Config.mpd_music_dir;
374 path_to_file += s.GetFile();
375 TagLib::FileRef f(path_to_file.c_str());
376 if (!f.isNull())
377 s.SetComment(f.tag()->comment().to8Bit(1));
378 # endif // HAVE_TAGLIB_H
380 *w << fmtBold << Config.color1 << "Filename: " << fmtBoldEnd << Config.color2 << s.GetName() << "\n" << clEnd;
381 *w << fmtBold << "Directory: " << fmtBoldEnd << Config.color2;
382 ShowTag(*w, s.GetDirectory());
383 *w << "\n\n" << clEnd;
384 *w << fmtBold << "Length: " << fmtBoldEnd << Config.color2 << s.GetLength() << "\n" << clEnd;
385 # ifdef HAVE_TAGLIB_H
386 if (!f.isNull())
388 *w << fmtBold << "Bitrate: " << fmtBoldEnd << Config.color2 << f.audioProperties()->bitrate() << " kbps\n" << clEnd;
389 *w << fmtBold << "Sample rate: " << fmtBoldEnd << Config.color2 << f.audioProperties()->sampleRate() << " Hz\n" << clEnd;
390 *w << fmtBold << "Channels: " << fmtBoldEnd << Config.color2 << (f.audioProperties()->channels() == 1 ? "Mono" : "Stereo") << "\n" << clDefault;
392 # endif // HAVE_TAGLIB_H
393 *w << clDefault;
395 for (const Metadata *m = Tags; m->Name; ++m)
397 *w << fmtBold << "\n" << m->Name << ": " << fmtBoldEnd;
398 ShowTag(*w, s.GetTags(m->Get));