std::string Lyrics::GenerateFilename(const MPD::Song &s) { std::string filename; if (Config.store_lyrics_in_song_dir) { if (s.isFromDatabase()) { filename = Config.mpd_music_dir; filename += "/"; filename += s.getURI(); } else filename = s.getURI(); // replace song's extension with .txt size_t dot = filename.rfind('.'); assert(dot != std::string::npos); filename.resize(dot); filename += ".txt"; } else { std::string file = s.getArtist(); file += " - "; file += s.getTitle(); file += ".txt"; removeInvalidCharsFromFilename(file); filename = Config.lyrics_directory; filename += "/"; filename += file; } return filename; }
std::string Lyrics::GenerateFilename(const MPD::Song &s) { std::string filename; if (Config.store_lyrics_in_song_dir) { if (s.isFromDB()) { filename = Config.mpd_music_dir; filename += "/"; filename += s.GetFile(); } else filename = s.GetFile(); // replace song's extension with .txt size_t dot = filename.rfind('.'); assert(dot != std::string::npos); filename.resize(dot); filename += ".txt"; } else { std::string file = locale_to_utf_cpy(s.GetArtist()); file += " - "; file += locale_to_utf_cpy(s.GetTitle()); file += ".txt"; EscapeUnallowedChars(file); filename = Config.lyrics_directory; filename += "/"; filename += file; } return filename; }
void Lyrics::update() { # ifdef HAVE_CURL_CURL_H if (isReadyToTake) Take(); if (isDownloadInProgress) { w.flush(); w.refresh(); } # endif // HAVE_CURL_CURL_H if (ReloadNP) { const MPD::Song s = myPlaylist->nowPlayingSong(); if (!s.empty() && !s.getArtist().empty() && !s.getTitle().empty()) { drawHeader(); itsScrollBegin = 0; itsSong = s; Load(); } ReloadNP = 0; } }
void Lyrics::DownloadInBackground(const MPD::Song &s) { if (s.empty() || s.getArtist().empty() || s.getTitle().empty()) return; std::string filename = GenerateFilename(s); std::ifstream f(filename.c_str()); if (f.is_open()) { f.close(); return; } Statusbar::msg("Fetching lyrics for \"%s\"...", s.toString(Config.song_status_format_no_colors, Config.tags_separator).c_str()); MPD::Song *s_copy = new MPD::Song(s); pthread_mutex_lock(&itsDIBLock); if (itsWorkersNumber == itsMaxWorkersNumber) itsToDownload.push(s_copy); else { ++itsWorkersNumber; pthread_t t; pthread_attr_t attr; pthread_attr_init(&attr); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); pthread_create(&t, &attr, DownloadInBackgroundImpl, s_copy); } pthread_mutex_unlock(&itsDIBLock); }
void Display::Tags(const MPD::Song &s, void *data, Menu<MPD::Song> *menu) { size_t i = static_cast<Menu<std::string> *>(data)->Choice(); if (i < 11) { ShowTag(*menu, s.GetTags(SongInfo::Tags[i].Get)); } else if (i == 12) { if (s.GetNewName().empty()) *menu << s.GetName(); else *menu << s.GetName() << Config.color2 << " -> " << clEnd << s.GetNewName(); } }
void Lyrics::DownloadInBackgroundImplHelper(const MPD::Song &s) { std::string artist = Curl::escape(s.getArtist()); std::string title = Curl::escape(s.getTitle()); LyricsFetcher::Result result; bool fetcher_defined = itsFetcher && *itsFetcher; for (LyricsFetcher **plugin = fetcher_defined ? itsFetcher : lyricsPlugins; *plugin != 0; ++plugin) { result = (*plugin)->fetch(artist, title); if (result.first) break; if (fetcher_defined) break; } if (result.first == true) Save(GenerateFilename(s), result.second); }
void Storage::loadNewSong(MPD::Song s) { // save current file currentArtist->saveArtist(filePath); if (Config::GetInstance()->isRemoteStorageEnabled()) { // save to shared storage if (remoteSaveArtist(filePath)) { currentArtist->setSynced(true); } else { currentArtist->setSynced(false); } // save with sync flags currentArtist->saveArtist(filePath); } // clear current Artist currentArtist->clear(); // load new data to storage filePath = createArtistFilePath(s.GetArtist()); currentArtist->name.set(s.GetArtist()); currentArtist->album.set(s.GetAlbum()); // check if file exists // search in local temp currentArtist->loadArtistFromFile(filePath); // if is enabled remote sharing, share if (Config::GetInstance()->isRemoteStorageEnabled()) { // remote load artist std::string remoteArtistContent = remoteLoadArtist(s.GetArtist()); currentArtist->loadArtistFromRemoteContent(remoteArtistContent); } // classificate artist currentArtist->loadClassificator(); currentArtist->classificateArtist(); loadWidgets(); }
void Browser::LocateSong(const MPD::Song &s) { if (s.GetDirectory().empty()) return; itsBrowseLocally = !s.isFromDB(); if (myScreen != this) SwitchTo(); if (itsBrowsedDir != s.GetDirectory()) GetDirectory(s.GetDirectory()); for (size_t i = 0; i < w->Size(); ++i) { if ((*w)[i].type == itSong && s.GetHash() == (*w)[i].song->GetHash()) { w->Highlight(i); break; } } }
void Lyrics::fetchInBackground(const MPD::Song &s) { auto consumer = [this] { std::string lyrics_file; while (true) { MPD::Song qs; { auto queue = m_shared_queue.acquire(); assert(queue->first); if (queue->second.empty()) { queue->first = false; break; } lyrics_file = lyricsFilename(queue->second.front()); if (!boost::filesystem::exists(lyrics_file)) qs = queue->second.front(); queue->second.pop(); } if (!qs.empty()) { auto lyrics = downloadLyrics(qs, nullptr, m_fetcher); if (lyrics) saveLyrics(lyrics_file, *lyrics); } } }; auto queue = m_shared_queue.acquire(); queue->second.push(s); // Start the consumer if it's not running. if (!queue->first) { std::thread t(consumer); t.detach(); queue->first = true; } }
void Browser::LocateSong(const MPD::Song &s) { if (s.getDirectory().empty()) return; itsBrowseLocally = !s.isFromDatabase(); if (myScreen != this) switchTo(); if (itsBrowsedDir != s.getDirectory()) GetDirectory(s.getDirectory()); for (size_t i = 0; i < w.size(); ++i) { if (w[i].value().type == itSong && s.getHash() == w[i].value().song->getHash()) { w.highlight(i); break; } } drawHeader(); }
void Browser::locateSong(const MPD::Song &s) { if (s.getDirectory().empty()) throw std::runtime_error("Song's directory is empty"); m_local_browser = !s.isFromDatabase(); if (myScreen != this) switchTo(); // change to relevant directory if (m_current_directory != s.getDirectory()) { getDirectory(s.getDirectory()); drawHeader(); } // highlight the item auto begin = w.beginV(), end = w.endV(); auto it = std::find(begin, end, MPD::Item(s)); if (it != end) w.highlight(it-begin); }
bool addSongToPlaylist(const MPD::Song &s, bool play, size_t position) { bool result = false; if (Config.ncmpc_like_songs_adding && myPlaylist->checkForSong(s)) { auto &w = myPlaylist->main(); if (play) { auto song = std::find(w.beginV(), w.endV(), s); assert(song != w.endV()); Mpd.PlayID(song->getID()); result = true; } else { Mpd.StartCommandsList(); for (auto it = w.rbeginV(); it != w.rendV(); ++it) if (*it == s) Mpd.Delete(it->getPosition()); Mpd.CommitCommandsList(); // we return false in this case } } else { position = std::min(position, Mpd.GetPlaylistLength()); int id = Mpd.AddSong(s, position); if (id >= 0) { Statusbar::msg("Added to playlist: %s", s.toString(Config.song_status_format_no_colors, Config.tags_separator).c_str() ); if (play) Mpd.PlayID(id); result = true; } } return result; }
bool MPD::Song::SendQueue() { ExtractQueue(); if (!myHandshake.OK()) return false; Log(llInfo, "Submitting songs..."); string result, postdata; CURLcode code; postdata = "s="; postdata += myHandshake.SessionID; for (std::deque<string>::const_iterator it = Song::SubmitQueue.begin(); it != Song::SubmitQueue.end(); it++) postdata += *it; Log(llVerbose, "URL: %s", myHandshake.SubmissionURL.c_str()); Log(llVerbose, "Post data: %s", postdata.c_str()); CURL *submission = curl_easy_init(); curl_easy_setopt(submission, CURLOPT_URL, myHandshake.SubmissionURL.c_str()); curl_easy_setopt(submission, CURLOPT_POST, 1); curl_easy_setopt(submission, CURLOPT_POSTFIELDS, postdata.c_str()); curl_easy_setopt(submission, CURLOPT_WRITEFUNCTION, write_data); curl_easy_setopt(submission, CURLOPT_WRITEDATA, &result); curl_easy_setopt(submission, CURLOPT_CONNECTTIMEOUT, curl_queue_connecttimeout); curl_easy_setopt(submission, CURLOPT_TIMEOUT, curl_queue_timeout); curl_easy_setopt(submission, CURLOPT_DNS_CACHE_TIMEOUT, 0); curl_easy_setopt(submission, CURLOPT_NOPROGRESS, 1); curl_easy_setopt(submission, CURLOPT_NOSIGNAL, 1); code = curl_easy_perform(submission); curl_easy_cleanup(submission); IgnoreNewlines(result); if (result == "OK") { Log(llInfo, "Number of submitted songs: %d", Song::SubmitQueue.size()); SubmitQueue.clear(); std::ofstream f(Config.file_cache.c_str(), std::ios::trunc); f.close(); NowPlayingNotify = s.Data && !s.isStream(); return true; } else { if (result.empty()) { Log(llError, "Error while submitting songs: %s", curl_easy_strerror(code)); } else { Log(llError, "Audioscrobbler returned status %s", result.c_str()); // BADSESSION or FAILED was returned, handshake needs resetting. myHandshake.Clear(); Log(llVerbose, "Handshake reset"); } return false; } }
void Display::Songs(const MPD::Song &s, void *data, Menu<MPD::Song> *menu) { if (!s.Localized()) const_cast<MPD::Song *>(&s)->Localize(); bool is_now_playing = menu == myPlaylist->Items && (menu->isFiltered() ? s.GetPosition() : menu->CurrentlyDrawedPosition()) == size_t(myPlaylist->NowPlaying); if (is_now_playing) *menu << Config.now_playing_prefix; assert(data); bool separate_albums = false; if (Config.playlist_separate_albums && menu->CurrentlyDrawedPosition()+1 < menu->Size()) { MPD::Song *next = static_cast<ScreenFormat *>(data)->screen->GetSong(menu->CurrentlyDrawedPosition()+1); if (next && next->GetAlbum() != s.GetAlbum()) separate_albums = true; } if (separate_albums) { *menu << fmtUnderline; mvwhline(menu->Raw(), menu->Y(), 0, ' ', menu->GetWidth()); } bool discard_colors = Config.discard_colors_if_item_is_selected && menu->isSelected(menu->CurrentlyDrawedPosition()); std::string line = s.toString(*static_cast<ScreenFormat *>(data)->format, "$"); for (std::string::const_iterator it = line.begin(); it != line.end(); ++it) { if (*it == '$') { if (++it == line.end()) // end of format { *menu << '$'; break; } else if (isdigit(*it)) // color { if (!discard_colors) *menu << Color(*it-'0'); } else if (*it == 'R') // right align { basic_buffer<my_char_t> buf; buf << U(" "); String2Buffer(TO_WSTRING(line.substr(it-line.begin()+1)), buf); if (discard_colors) buf.RemoveFormatting(); if (is_now_playing) buf << Config.now_playing_suffix; *menu << XY(menu->GetWidth()-buf.Str().length()-(menu->isSelected(menu->CurrentlyDrawedPosition()) ? Config.selected_item_suffix_length : 0), menu->Y()) << buf; if (separate_albums) *menu << fmtUnderlineEnd; return; } else // not a color nor right align, just a random character *menu << *--it; } else if (*it == MPD::Song::FormatEscapeCharacter) { // treat '$' as a normal character if song format escape char is prepended to it if (++it == line.end() || *it != '$') --it; *menu << *it; } else *menu << *it; } if (is_now_playing) *menu << Config.now_playing_suffix; if (separate_albums) *menu << fmtUnderlineEnd; }
void Display::SongsInColumns(const MPD::Song &s, void *data, Menu<MPD::Song> *menu) { if (!s.Localized()) const_cast<MPD::Song *>(&s)->Localize(); /// FIXME: This function is pure mess, it needs to be /// rewritten and unified with Display::Columns() a bit. bool is_now_playing = menu == myPlaylist->Items && (menu->isFiltered() ? s.GetPosition() : menu->CurrentlyDrawedPosition()) == size_t(myPlaylist->NowPlaying); if (is_now_playing) *menu << Config.now_playing_prefix; if (Config.columns.empty()) return; assert(data); bool separate_albums = false; if (Config.playlist_separate_albums && menu->CurrentlyDrawedPosition()+1 < menu->Size()) { MPD::Song *next = static_cast<ScreenFormat *>(data)->screen->GetSong(menu->CurrentlyDrawedPosition()+1); if (next && next->GetAlbum() != s.GetAlbum()) separate_albums = true; } if (separate_albums) *menu << fmtUnderline; std::vector<Column>::const_iterator next2last, last, it; size_t where = 0; int width; bool last_fixed = Config.columns.back().fixed; if (Config.columns.size() > 1) next2last = Config.columns.end()-2; last = Config.columns.end()-1; bool discard_colors = Config.discard_colors_if_item_is_selected && menu->isSelected(menu->CurrentlyDrawedPosition()); for (it = Config.columns.begin(); it != Config.columns.end(); ++it) { if (where) { menu->GotoXY(where, menu->Y()); *menu << ' '; if (!discard_colors && (it-1)->color != clDefault) *menu << clEnd; } if (it == Config.columns.end()-1) width = menu->GetWidth()-where; else if (last_fixed && it == next2last) width = menu->GetWidth()-where-1-(++next2last)->width; else width = it->width*(it->fixed ? 1 : menu->GetWidth()/100.0); MPD::Song::GetFunction get = 0; std::string tag; for (size_t i = 0; i < it->type.length(); ++i) { get = toGetFunction(it->type[i]); tag = get ? s.GetTags(get) : ""; if (!tag.empty()) break; } if (!discard_colors && it->color != clDefault) *menu << it->color; whline(menu->Raw(), 32, menu->GetWidth()-where); // last column might need to be shrinked to make space for np/sel suffixes if (it == last) { if (menu->isSelected(menu->CurrentlyDrawedPosition())) width -= Config.selected_item_suffix_length; if (is_now_playing) width -= Config.now_playing_suffix_length; } if (it->right_alignment) { if (width > 0 && (!tag.empty() || it->display_empty_tag)) { int x, y; menu->GetXY(x, y); my_string_t wtag = TO_WSTRING(tag.empty() ? Config.empty_tag : tag).substr(0, width-!!x); *menu << XY(x+width-Window::Length(wtag)-!!x, y) << wtag; } } else { if (it == last) { if (width > 0) { my_string_t str; if (!tag.empty()) str = TO_WSTRING(tag).substr(0, width-1); else if (it->display_empty_tag) str = TO_WSTRING(Config.empty_tag).substr(0, width-1); *menu << str; } } else { if (!tag.empty()) *menu << tag; else if (it->display_empty_tag) *menu << Config.empty_tag; } } where += width; } if (!discard_colors && (--it)->color != clDefault) *menu << clEnd; if (is_now_playing) *menu << Config.now_playing_suffix; if (separate_albums) *menu << fmtUnderlineEnd; }
void MediaLibrary::LocateSong(const MPD::Song &s) { std::string primary_tag = s.get(Config.media_lib_primary_tag); if (primary_tag.empty()) { std::string item_type = boost::locale::to_lower( tagTypeToString(Config.media_lib_primary_tag)); Statusbar::printf("Can't use this function because the song has no %s tag", item_type); return; } if (!s.isFromDatabase()) { Statusbar::print("Song is not from the database"); return; } if (myScreen != this) switchTo(); Statusbar::put() << "Jumping to song..."; Global::wFooter->refresh(); if (!hasTwoColumns) { if (Tags.empty()) update(); if (!MoveToTag(Tags, primary_tag)) { // The tag could not be found. Since this was called from an existing // song, the tag should exist in the library, but it was not listed by // list/listallinfo. This is the case with some players where it is not // possible to list all of the library, e.g. mopidy with mopidy-spotify. // To workaround this we simply insert the missing tag. Tags.addItem(PrimaryTag(primary_tag, s.getMTime())); std::sort(Tags.beginV(), Tags.endV(), SortPrimaryTags()); Tags.refresh(); MoveToTag(Tags, primary_tag); } Albums.clear(); } if (Albums.empty()) update(); // When you locate a song in the media library, if no albums or no songs // are found, set the active column to the previous one (tags if no albums, // and albums if no songs). This makes sure that the active column is not // empty, which may make it impossible to move out of. // // The problem was if you highlight some song in the rightmost column in // the media browser and then go to some other window and select locate // song. If the tag or album it looked up in the media library was // empty, the selection would stay in the songs column while it was empty. // This made the selection impossible to change. // // This only is a problem if a song has some tag or album for which the // find command doesn't return any results. This should never really happen // unless there is some inconsistency in the player. However, it may // happen, so we need to handle it. // // Note: We don't want to return when no albums are found in two column // mode. In this case, we try to insert the album, as we do with tags when // they are not found. if (hasTwoColumns || !Albums.empty()) { if (!MoveToAlbum(Albums, primary_tag, s)) { // The album could not be found, insert it if in two column mode. // See comment about tags not found above. This is the equivalent // for two column mode. Albums.addItem(AlbumEntry( Album(primary_tag, s.getAlbum(), s.getDate(), s.getMTime()) )); std::sort(Albums.beginV(), Albums.endV(), SortAlbumEntries()); Albums.refresh(); MoveToAlbum(Albums, primary_tag, s); } Songs.clear(); update(); if (!Songs.empty()) { if (s != Songs.current()->value()) { auto begin = Songs.beginV(), end = Songs.endV(); auto it = std::find(begin, end, s); if (it != end) Songs.highlight(it-begin); } nextColumn(); nextColumn(); } else // invalid album was added, clear the list Albums.clear(); } else // invalid tag was added, clear the list Tags.clear(); refresh(); }
void MediaLibrary::LocateSong(const MPD::Song &s) { std::string primary_tag = s.get(Config.media_lib_primary_tag); if (primary_tag.empty()) { std::string item_type = boost::locale::to_lower( tagTypeToString(Config.media_lib_primary_tag)); Statusbar::msg("Can't use this function because the song has no %s tag set", item_type.c_str()); return; } if (myScreen != this) switchTo(); Statusbar::put() << "Jumping to song..."; Global::wFooter->refresh(); if (!hasTwoColumns) { Tags.showAll(); if (Tags.empty()) update(); if (primary_tag != Tags.current().value().tag()) { for (size_t i = 0; i < Tags.size(); ++i) { if (primary_tag == Tags[i].value().tag()) { Tags.highlight(i); Albums.clear(); Songs.clear(); break; } } } } Albums.showAll(); if (Albums.empty()) update(); std::string album = s.getAlbum(); std::string date = s.getDate(); if ((hasTwoColumns && Albums.current().value().entry().tag() != primary_tag) || album != Albums.current().value().entry().album() || date != Albums.current().value().entry().date()) { for (size_t i = 0; i < Albums.size(); ++i) { if ((!hasTwoColumns || Albums[i].value().entry().tag() == primary_tag) && album == Albums[i].value().entry().album() && date == Albums[i].value().entry().date()) { Albums.highlight(i); Songs.clear(); break; } } } Songs.showAll(); if (Songs.empty()) update(); if (s.getHash() != Songs.current().value().getHash()) { for (size_t i = 0; i < Songs.size(); ++i) { if (s.getHash() == Songs[i].value().getHash()) { Songs.highlight(i); break; } } } Tags.setHighlightColor(Config.main_highlight_color); Albums.setHighlightColor(Config.main_highlight_color); Songs.setHighlightColor(Config.active_column_color); w = &Songs; refresh(); }
void updatePlayer(MPD::Client *, MPD::StatusChanges changed, void *) { if (changed.SongID) { if (MPD::Client::GetInstance()->isPlaying()) { #if DEBUG std::cout << "Song changed" << std::endl; #endif MPD::Song song = MPD::Client::GetInstance()->GetCurrentSong(); // set song parameters to storage Storage::GetInstance()->loadNewSong(song); // run agents AgentManager::GetInstance()->songChanged(); // set text widgets GUI::MainWindow::GetInstance()->setSongLabel(song.GetArtist() + " - " + song.GetTitle()); GUI::MainWindow::GetInstance()->setArtist(song.GetArtist()); GUI::MainWindow::GetInstance()->setTitle(song.GetTitle()); GUI::MainWindow::GetInstance()->setAlbum(song.GetAlbum()); GUI::MainWindow::GetInstance()->setGenre(song.GetGenre()); GUI::MainWindow::GetInstance()->setTimeScale(MPD::Client::GetInstance()->GetElapsedTime(), MPD::Client::GetInstance()->GetTotalTime()); } } if (changed.ElapsedTime) { // if (!Config::GetInstance()->isAgentsEnabled()) { // AgentManager::GetInstance()->killAgents(); // } // // AgentManager::GetInstance()->isSourcesChanged(); // load new info to widgets GUI::MainWindow::GetInstance()->articlesWidget->updateArticlesWidget(); GUI::MainWindow::GetInstance()->slideshowWidget->updateSlideshowWidget(); // GUI::MainWindow::GetInstance()->coverWidget->updateCoverWidget(); GUI::MainWindow::GetInstance()->setTimeScale(MPD::Client::GetInstance()->GetElapsedTime(), MPD::Client::GetInstance()->GetTotalTime()); if (MPD::Client::GetInstance()->isPlaying()) { MPD::Song song = MPD::Client::GetInstance()->GetCurrentSong(); GUI::MainWindow::GetInstance()->setSongLabel(song.GetArtist() + " - " + song.GetTitle()); GUI::MainWindow::GetInstance()->setStatusBar(_("IMPC Playing: ") + song.GetFile()); } } if (changed.PlayerState) { MPD::PlayerState s = MPD::Client::GetInstance()->GetState(); if (s == MPD::psPlay) { GUI::MainWindow::GetInstance()->on_play(); GUI::MainWindow::GetInstance()->setPlayButtonActive(true); GUI::MainWindow::GetInstance()->setBitrate(MPD::Client::GetInstance()->GetBitrate()); } else if (s == MPD::psPause) { GUI::MainWindow::GetInstance()->on_pause(); GUI::MainWindow::GetInstance()->setPlayButtonActive(false); } else if (s == MPD::psStop) { GUI::MainWindow::GetInstance()->on_stop(); GUI::MainWindow::GetInstance()->articlesWidget->clearArticlesWidget(); GUI::MainWindow::GetInstance()->slideshowWidget->clearSlide(); } } if (changed.DBUpdating) { GUI::MainWindow::GetInstance()->artistsWidget->reload(); } if (changed.Volume) { // we set default volume GUI::MainWindow::GetInstance()->setVolume((double) MPD::Client::GetInstance()->GetVolume()); } AgentManager::GetInstance()->checkIfAgentsEnabled(); }